@hzab/list-render 1.9.5 → 1.9.7-beta
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +13 -0
- package/README.md +63 -49
- package/package.json +3 -3
- package/src/card-render/index.jsx +123 -0
- package/src/card-render/index.less +25 -0
- package/src/common/utils.js +23 -0
- package/src/components/PrefixNode/index.tsx +0 -2
- package/src/index.less +1 -2
- package/src/list-render.jsx +81 -24
- package/src/pagination-render/index.jsx +2 -6
- package/src/table-render/index.jsx +63 -4
- package/src/table-render/index.less +24 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,16 @@
|
|
|
1
|
+
# @hzab/list-render@1.9.8
|
|
2
|
+
|
|
3
|
+
feat: 新增 layout 类型,card 用于卡片格式渲染,支持自定义渲染
|
|
4
|
+
|
|
5
|
+
# @hzab/list-render@1.9.7
|
|
6
|
+
|
|
7
|
+
fix: 修复 分页配置时数据覆盖问题
|
|
8
|
+
|
|
9
|
+
# @hzab/list-render@1.9.6
|
|
10
|
+
|
|
11
|
+
feat: 新增列筛选,筛选项显隐
|
|
12
|
+
fix: 修复 分页配置
|
|
13
|
+
|
|
1
14
|
# @hzab/list-render@1.9.5
|
|
2
15
|
|
|
3
16
|
fix: 修复 datePicker 传入 showTime 为 true 时显示时分秒
|
package/README.md
CHANGED
|
@@ -49,55 +49,58 @@ const listDM = useMemo(
|
|
|
49
49
|
|
|
50
50
|
### InfoPanel Attributes
|
|
51
51
|
|
|
52
|
-
| 属性名称 | 属性类型 | 必须 | 默认值 | 描述
|
|
53
|
-
| --------------------------- | ---------------- | ---- | --------------- |
|
|
54
|
-
|
|
|
55
|
-
|
|
|
56
|
-
|
|
|
57
|
-
|
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
|
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
|
|
|
74
|
-
|
|
|
75
|
-
|
|
|
76
|
-
|
|
|
77
|
-
|
|
|
78
|
-
|
|
|
79
|
-
|
|
|
80
|
-
|
|
|
81
|
-
|
|
|
82
|
-
|
|
|
83
|
-
|
|
|
84
|
-
|
|
|
85
|
-
|
|
|
86
|
-
|
|
|
87
|
-
|
|
|
88
|
-
|
|
|
89
|
-
|
|
|
90
|
-
|
|
|
91
|
-
|
|
|
92
|
-
|
|
|
93
|
-
|
|
|
94
|
-
|
|
|
95
|
-
|
|
|
96
|
-
|
|
|
97
|
-
|
|
|
98
|
-
|
|
|
99
|
-
|
|
|
100
|
-
|
|
|
52
|
+
| 属性名称 | 属性类型 | 必须 | 默认值 | 描述 |
|
|
53
|
+
| --------------------------- | ---------------- | ---- | --------------- | -------------------------------------------------------------------------------------------------------------- |
|
|
54
|
+
| layout | string | 否 | default | 列表渲染类型格式 |
|
|
55
|
+
| className | string | 否 | - | 外层 div className |
|
|
56
|
+
| idKey | string | 否 | id | 唯一值字段的 key |
|
|
57
|
+
| schema | Object | 是 | - | 字段描述文件,包含各个字段的信息 |
|
|
58
|
+
| model | Object | 是 | - | 数据模型,包含 CURD 接口信息,传入 DataModel 的实例 |
|
|
59
|
+
| isPatchUpdate | boolean | 否 | false | 编辑提交接口是否使用 patch 发起请求 |
|
|
60
|
+
| list | Array | | - | 本地数据源 |
|
|
61
|
+
| closeAutoRequest | Boolean | | false | 是否关闭加载完毕后自动发起请求。true 时组件 didMount 不自动发起请求 |
|
|
62
|
+
| hasQuery | Boolean | | true | 是否包含搜索、筛选框、搜索按钮等 |
|
|
63
|
+
| verticalHeader | Boolean | | false | 搜索项和新增按钮是否处于不同的行等 |
|
|
64
|
+
| search | String | | - | 传入空字符串时,不包含搜索框;传入非空字符串时,显示搜索框,同时传入的字符串作为搜索框的占位符 |
|
|
65
|
+
| filters | Array | | [] | 字符串数组,可以包含要筛选的字段 key 值(schema 中的 name),或者字符串 '$timerange'(时间范围筛选专用) |
|
|
66
|
+
| queryConf | Object | | {} | 设置 query 参数的 key |
|
|
67
|
+
| createText | String/ReactNote | | 新增 | 新增按钮文案 |
|
|
68
|
+
| hasCreate | Boolean | | true | 是否显示新增按钮 |
|
|
69
|
+
| hasAction | Boolean | | true | 是否在表格的最右增加一个“操作”列;hasAction 为 true 时,下面的 hasEdit/hasDel 才会生效 |
|
|
70
|
+
| hasEdit | Boolean/Function | | true | 是否显示编辑按钮,可传入回调控制当前行是否显示 |
|
|
71
|
+
| hasDel | Boolean/Function | | true | 是否显示删除按钮,可传入回调控制当前行是否显示 |
|
|
72
|
+
| hasDetail | Boolean/Function | | true | 是否显示详情按钮,可传入回调控制当前行是否显示 |
|
|
73
|
+
| hasDelTips | String/Function | | "确认删除该项?" | 删除按钮自定义提示,可传入回调根据当前行数据显示对应提示 |
|
|
74
|
+
| tableConf | Object | | {} | Table 相关配置 |
|
|
75
|
+
| tableProps | Object | | {} | 直接传给 Table 的 props,相关 API 可直接参考 antd table 组件 |
|
|
76
|
+
| cardConf | Object | | {} | Card 相关配置 |
|
|
77
|
+
| cardProps | Object | | {} | 直接传给 cardRender 的 props,因内部渲染使用的的是详情组件,相关 API 可直接参考 @hzab/schema-descriptions 组件 |
|
|
78
|
+
| fetchOnEdit | Boolean | | true | 展示编辑弹框时,是否会调用一次详情接口进行回填;若为 false,则会使用表格列表接口返回的 row 数据进行回填 |
|
|
79
|
+
| fetchById | Boolean | | true | 编辑中的详情请求,是否使用 id 作为入参的 key |
|
|
80
|
+
| modalMode | string | | dialog | 新增/编辑表单、详情 展示模式: dialog drawer |
|
|
81
|
+
| modalConf | Object | | {} | modal/Drawer 配置对象 |
|
|
82
|
+
| modalDetailProps | Object | | {} | modal descriptions 配置对象 |
|
|
83
|
+
| modalFormProps | Object | | {} | modal/drawer fromRender 配置对象 |
|
|
84
|
+
| modalProps | Object | | {} | modal/drawer 配置对象 |
|
|
85
|
+
| schemaScope | Object | | {} | formRender schemaScope props |
|
|
86
|
+
| components | Object | | {} | formRender components props 自定义组件 |
|
|
87
|
+
| detailComponents | Object | | {} | descriptions components props 自定义组件 |
|
|
88
|
+
| hasPagination | Boolean | | true | 是否显示分页 |
|
|
89
|
+
| paginationConf | Object | | {} | 可自定义 Pagination props,进行 pagination 相关设置 |
|
|
90
|
+
| formInitialValues | Object | | {} | 给新增、编辑对话框中的表单增加默认值 |
|
|
91
|
+
| Slots | Object | | {} | 组件插槽 |
|
|
92
|
+
| getFieldListOpt | Object | | {} | getFieldList opt 参数 |
|
|
93
|
+
| onGetListEnd | Function | | - | 请求列表成功返回的回调 |
|
|
94
|
+
| onCreateSuc | Function | | - | 新增成功返回的回调 |
|
|
95
|
+
| onEditSuc | Function | | - | 编辑成功返回的回调 |
|
|
96
|
+
| onDelSuc | Function | | - | 删除成功返回的回调 |
|
|
97
|
+
| onFormModalClose | Function | | - | 表单弹窗关闭回调 |
|
|
98
|
+
| modalFormMount | Function | | - | 新增、编辑弹窗 Form 渲染完成回调 |
|
|
99
|
+
| msgConf | Object | | {} | 新增、编辑、删除、列表查询,详情查询的报错 msg 提示设置 |
|
|
100
|
+
| i18n | Object | | {} | 文案配置 |
|
|
101
|
+
| queryFormInitialValues | Object | | {} | 列表上方查询 Form 默认值 |
|
|
102
|
+
| queryFormIsExtendModelQuery | Boolean | | false | 列表上方查询 Form 默认值是否继承 data-model.query 置 |
|
|
103
|
+
| useFormData | boolean | 否 | | 是否使用 form data 提交数据 |
|
|
101
104
|
|
|
102
105
|
- fetchOnEdit 展示编辑弹框时,是否会调用一次详情接口进行回填(某些场景下,列表接口只返回部分部分字段,只有详情接口会返回全部字段);若为 false,则会使用表格列表接口返回的 row 数据进行回填
|
|
103
106
|
|
|
@@ -122,6 +125,17 @@ const listDM = useMemo(
|
|
|
122
125
|
| ellipsis | boolean/Object | | - | 当前列是否超出隐藏,true 或 { showTitle: true } 开启超出隐藏 |
|
|
123
126
|
| emptyValue | string/number | | undefined | table 列表单列空值展示 |
|
|
124
127
|
|
|
128
|
+
#### cardConf
|
|
129
|
+
|
|
130
|
+
| 属性名称 | 属性类型 | 必须 | 默认值 | 描述 |
|
|
131
|
+
| -------------- | -------- | ---- | ------ | -------------- |
|
|
132
|
+
| CardItemRender | JSX | | | 自定义渲染卡片 |
|
|
133
|
+
| |
|
|
134
|
+
|
|
135
|
+
##### cardConf.colConf[xxx]
|
|
136
|
+
|
|
137
|
+
| 属性名称 | 属性类型 | 必须 | 默认值 | 描述 |
|
|
138
|
+
|
|
125
139
|
#### queryConf
|
|
126
140
|
|
|
127
141
|
| 属性名称 | 属性类型 | 必须 | 默认值 | 描述 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hzab/list-render",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.7-beta",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "src",
|
|
6
6
|
"scripts": {
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"devDependencies": {
|
|
22
22
|
"@ant-design/icons": "^4.8.1",
|
|
23
23
|
"@hzab/data-model": "^1.7.4",
|
|
24
|
-
"@hzab/form-render": "^1.
|
|
25
|
-
"@hzab/schema-descriptions": "^1.
|
|
24
|
+
"@hzab/form-render": "^1.6.12",
|
|
25
|
+
"@hzab/schema-descriptions": "^1.3.0",
|
|
26
26
|
"@hzab/webpack-config": "^0.7.2",
|
|
27
27
|
"@types/react": "^17.0.62",
|
|
28
28
|
"@types/react-dom": "^17.0.20",
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { useEffect, useState, useMemo, useCallback, useRef } from "react";
|
|
2
|
+
import { Table, Button, Popconfirm, Tooltip, Checkbox, Popover } from "antd";
|
|
3
|
+
import { QuestionCircleOutlined, FilterOutlined, SettingOutlined } from "@ant-design/icons";
|
|
4
|
+
import _ from "lodash";
|
|
5
|
+
import DetailRender from "@hzab/schema-descriptions";
|
|
6
|
+
import { removeInTableFalseFields } from "../common/utils";
|
|
7
|
+
import "./index.less";
|
|
8
|
+
import { use } from "react";
|
|
9
|
+
|
|
10
|
+
const scenario = "table-render";
|
|
11
|
+
|
|
12
|
+
function CardRender(props) {
|
|
13
|
+
const [schema, setSchema] = useState({});
|
|
14
|
+
|
|
15
|
+
const { config = {}, cardProps = {}, query = {}, i18n, onEdit, onDel, onSearch, getList, Slots = {} } = props;
|
|
16
|
+
const { tableDel = "删除", tableEdit = "编辑", tableDelTip = "确认删除该项?", tableDetail = "详情" } = i18n || {};
|
|
17
|
+
const { columns = 3, CardItemRender, hasEdit, hasDel, hasDelTips, hasDetail = false } = config || {};
|
|
18
|
+
|
|
19
|
+
const functionProps = { onEdit: onEdit, onDel: onDel, onSearch, getList };
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
const schemas = removeInTableFalseFields(props?.schema);
|
|
23
|
+
setSchema(schemas);
|
|
24
|
+
}, [props?.schema, props?.schema?.schema]);
|
|
25
|
+
|
|
26
|
+
const getColRender = (record, index) => {
|
|
27
|
+
const _colConf = props.config?.colConf?._$actions || {};
|
|
28
|
+
|
|
29
|
+
const slotProps = { record, index, ...functionProps };
|
|
30
|
+
|
|
31
|
+
if (Slots?.tableActionsSlot) {
|
|
32
|
+
return <Slots.tableActionsSlot {...slotProps} />;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
//编辑按钮权限
|
|
36
|
+
const _hasEdit = hasEdit && typeof hasEdit === "function" ? hasEdit(record, index) : hasEdit !== false;
|
|
37
|
+
|
|
38
|
+
//详情按钮权限
|
|
39
|
+
const _hasDetail = hasDetail && typeof hasDetail === "function" ? hasDetail(record, index) : hasDetail !== false;
|
|
40
|
+
|
|
41
|
+
//删除按钮权限
|
|
42
|
+
const _hasDel = hasDel && typeof hasDel === "function" ? hasDel(record, index) : hasDel !== false;
|
|
43
|
+
|
|
44
|
+
//删除按钮提示
|
|
45
|
+
const delTips =
|
|
46
|
+
hasDelTips && typeof hasDelTips === "function" ? hasDelTips(record, index) : hasDelTips || tableDelTip;
|
|
47
|
+
|
|
48
|
+
return (
|
|
49
|
+
<div style={{ width: _colConf?.width, maxWidth: "100%" }}>
|
|
50
|
+
{Slots?.actionPrefixSlot && <Slots.actionPrefixSlot {...slotProps} />}
|
|
51
|
+
{_hasDetail ? (
|
|
52
|
+
<Button
|
|
53
|
+
type="link"
|
|
54
|
+
onClick={() => {
|
|
55
|
+
props.onDetail && props.onDetail(record, index);
|
|
56
|
+
}}
|
|
57
|
+
>
|
|
58
|
+
{tableDetail}
|
|
59
|
+
</Button>
|
|
60
|
+
) : null}
|
|
61
|
+
{_hasEdit ? (
|
|
62
|
+
<Button
|
|
63
|
+
type="link"
|
|
64
|
+
onClick={() => {
|
|
65
|
+
props.onEdit && props.onEdit(record, index);
|
|
66
|
+
}}
|
|
67
|
+
>
|
|
68
|
+
{tableEdit}
|
|
69
|
+
</Button>
|
|
70
|
+
) : null}
|
|
71
|
+
{Slots?.actionCenterSlot && <Slots.actionCenterSlot {...slotProps} />}
|
|
72
|
+
{_hasDel ? (
|
|
73
|
+
<Popconfirm
|
|
74
|
+
placement="topRight"
|
|
75
|
+
title={delTips}
|
|
76
|
+
onConfirm={() => {
|
|
77
|
+
props.onDel && props.onDel(record, index);
|
|
78
|
+
}}
|
|
79
|
+
>
|
|
80
|
+
<Button type="link" danger>
|
|
81
|
+
{tableDel}
|
|
82
|
+
</Button>
|
|
83
|
+
</Popconfirm>
|
|
84
|
+
) : null}
|
|
85
|
+
{Slots?.actionSuffixSlot && <Slots.actionSuffixSlot {...slotProps} />}
|
|
86
|
+
</div>
|
|
87
|
+
);
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
const ItemRender = ({ item, index }) => {
|
|
91
|
+
return CardItemRender ? (
|
|
92
|
+
<CardItemRender item={item} index={index} functionProps={functionProps} />
|
|
93
|
+
) : (
|
|
94
|
+
<DetailRender
|
|
95
|
+
config={props.config}
|
|
96
|
+
schema={schema}
|
|
97
|
+
bordered={false}
|
|
98
|
+
data={item}
|
|
99
|
+
schemaScope={{ scenario: "table-render", ...props.schemaScope }}
|
|
100
|
+
components={props.components}
|
|
101
|
+
{...cardProps}
|
|
102
|
+
/>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
return (
|
|
106
|
+
<div className="card-render-wrap" style={{ gridTemplateColumns: `repeat(${columns}, 1fr` }}>
|
|
107
|
+
{props.list?.map((item, index) => {
|
|
108
|
+
return (
|
|
109
|
+
<div key={item["id"]} className="item-render-card">
|
|
110
|
+
{props.hasAction !== false ? (
|
|
111
|
+
<div className="item-render-card-title">{getColRender(item, index)}</div>
|
|
112
|
+
) : null}
|
|
113
|
+
<div className="item-render-card-content">
|
|
114
|
+
<ItemRender item={item} index={index} />
|
|
115
|
+
</div>
|
|
116
|
+
</div>
|
|
117
|
+
);
|
|
118
|
+
})}
|
|
119
|
+
</div>
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export default CardRender;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
.card-render-wrap {
|
|
2
|
+
display: grid;
|
|
3
|
+
grid-gap: 12px;
|
|
4
|
+
padding: 0 12px;
|
|
5
|
+
|
|
6
|
+
.item-render-card {
|
|
7
|
+
border: 1px solid #f0f0f0;
|
|
8
|
+
&:hover {
|
|
9
|
+
border: 1px solid var(--ant-primary-color);
|
|
10
|
+
box-shadow: 0 0 10px var(--ant-primary-color);
|
|
11
|
+
.item-render-card-title {
|
|
12
|
+
border-color: var(--ant-primary-color);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
.item-render-card-title {
|
|
16
|
+
padding: 8px 0px;
|
|
17
|
+
margin-bottom: 10px;
|
|
18
|
+
text-align: right;
|
|
19
|
+
border-bottom: 1px solid #f0f0f0;
|
|
20
|
+
}
|
|
21
|
+
.item-render-card-content {
|
|
22
|
+
padding: 0 12px;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
package/src/common/utils.js
CHANGED
|
@@ -196,3 +196,26 @@ export const getOptItByVal = function (val, options, opt) {
|
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
};
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* 移除 schema.properties 中包含 "inTable": false 的字段
|
|
202
|
+
* @param {Object} data - 原始数据对象
|
|
203
|
+
* @returns {Object} - 处理后的新对象
|
|
204
|
+
*/
|
|
205
|
+
export const removeInTableFalseFields = (data) => {
|
|
206
|
+
// 深拷贝原始数据
|
|
207
|
+
const result = _.cloneDeep(data);
|
|
208
|
+
|
|
209
|
+
// 检查是否存在 schema 和 properties
|
|
210
|
+
if (result?.schema?.properties) {
|
|
211
|
+
// 遍历properties中的所有属性
|
|
212
|
+
Object.keys(result.schema.properties).forEach((key) => {
|
|
213
|
+
if (result.schema.properties[key].inTable === false) {
|
|
214
|
+
// 删除整个包含inTable:false的属性
|
|
215
|
+
delete result.schema.properties[key];
|
|
216
|
+
}
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
return result;
|
|
221
|
+
};
|
|
@@ -41,8 +41,6 @@ export const PrefixNode = (props) => {
|
|
|
41
41
|
return showVal?.map((it, i) => {
|
|
42
42
|
const option = getFieldOptItByVal(it, _field) || {};
|
|
43
43
|
const PreNode = option.PrefixNode;
|
|
44
|
-
console.log(it, option);
|
|
45
|
-
|
|
46
44
|
return (
|
|
47
45
|
<div key={it + "" + i} className="table-cell-prefix-box">
|
|
48
46
|
{PreNode ? (
|
package/src/index.less
CHANGED
package/src/list-render.jsx
CHANGED
|
@@ -10,6 +10,7 @@ import axios, { getCancelTokenSource } from "@hzab/data-model/src/axios";
|
|
|
10
10
|
import QueryRender from "./query-render";
|
|
11
11
|
import Pagination from "./pagination-render";
|
|
12
12
|
import TableRender from "./table-render";
|
|
13
|
+
import CardRender from "./card-render";
|
|
13
14
|
import FormModal from "./FormModal";
|
|
14
15
|
import DetailModal from "./DetailModal";
|
|
15
16
|
|
|
@@ -17,8 +18,15 @@ import { objToFormData } from "./common/utils";
|
|
|
17
18
|
|
|
18
19
|
import "./index.less";
|
|
19
20
|
|
|
21
|
+
const pageSizeOptionMap = {
|
|
22
|
+
default: [10, 20, 50, 100],
|
|
23
|
+
card: [9, 18, 54, 99],
|
|
24
|
+
};
|
|
25
|
+
|
|
20
26
|
const ListRender = forwardRef(function (props, parentRef) {
|
|
21
27
|
const {
|
|
28
|
+
/** 渲染类型 */
|
|
29
|
+
layout = "default",
|
|
22
30
|
idKey = "id",
|
|
23
31
|
i18n,
|
|
24
32
|
queryFormIsExtendModelQuery = false,
|
|
@@ -36,6 +44,11 @@ const ListRender = forwardRef(function (props, parentRef) {
|
|
|
36
44
|
*/
|
|
37
45
|
useFormData: _useFormData,
|
|
38
46
|
} = props;
|
|
47
|
+
const pageSizeOptions = props.paginationConf?.pageSizeOptions || pageSizeOptionMap[props.layout] || [10, 20, 50, 100];
|
|
48
|
+
// const [pageSizeOptions, setPageSizeOptions] = useState(
|
|
49
|
+
// props.paginationConf?.pageSizeOptions || pageSizeOptionMap[props.layout] || [10, 20, 50, 100],
|
|
50
|
+
// );
|
|
51
|
+
|
|
39
52
|
const { createText = props.createText } = i18n || {};
|
|
40
53
|
const [total, setTotal] = useState(0);
|
|
41
54
|
const [list, setList] = useState([]);
|
|
@@ -47,9 +60,10 @@ const ListRender = forwardRef(function (props, parentRef) {
|
|
|
47
60
|
const queryRef = useRef();
|
|
48
61
|
const modelQueryRef = useRef({});
|
|
49
62
|
const formQueryRef = useRef({});
|
|
50
|
-
const paginationQueryRef = useRef({ pageNum: 1, pageSize:
|
|
63
|
+
const paginationQueryRef = useRef({ pageNum: 1, pageSize: pageSizeOptions[0] });
|
|
51
64
|
const useFormData = _useFormData ?? dialogConf.useFormData ?? modalConf?.useFormData;
|
|
52
65
|
const getListSourceRef = useRef();
|
|
66
|
+
const [showSearch, setShowSearch] = useState(true);
|
|
53
67
|
|
|
54
68
|
useImperativeHandle(parentRef, () => ({
|
|
55
69
|
getList,
|
|
@@ -65,12 +79,22 @@ const ListRender = forwardRef(function (props, parentRef) {
|
|
|
65
79
|
|
|
66
80
|
const { schema = {}, config = {}, model = {}, msgConf = {} } = props;
|
|
67
81
|
|
|
82
|
+
// useEffect(() => {
|
|
83
|
+
// const list = props.paginationConf?.pageSizeOptions || pageSizeOptionMap[props.layout] || [10, 20, 50, 100];
|
|
84
|
+
// setPageSizeOptions(list);
|
|
85
|
+
// paginationQueryRef.current = { ...paginationQueryRef.current, pageSize: list[0] };
|
|
86
|
+
// }, [layout]);
|
|
87
|
+
|
|
68
88
|
useEffect(() => {
|
|
69
89
|
if (model) {
|
|
70
90
|
if (!model.query) {
|
|
71
91
|
model.query = {};
|
|
72
92
|
} else {
|
|
73
93
|
modelQueryRef.current = model?.query;
|
|
94
|
+
paginationQueryRef.current = {
|
|
95
|
+
pageNum: model?.query?.pageNum ?? paginationQueryRef.current?.pageNum,
|
|
96
|
+
pageSize: model?.query?.pageSize ?? paginationQueryRef.current?.pageSize,
|
|
97
|
+
};
|
|
74
98
|
}
|
|
75
99
|
model.query.pageNum = 1;
|
|
76
100
|
}
|
|
@@ -100,7 +124,8 @@ const ListRender = forwardRef(function (props, parentRef) {
|
|
|
100
124
|
if (!model?.getList && Array.isArray(props.list)) {
|
|
101
125
|
setListLoading(true);
|
|
102
126
|
const { list } = props;
|
|
103
|
-
const { pageNum = 1, pageSize =
|
|
127
|
+
const { pageNum = 1, pageSize = pageSizeOptions[0] } = paginationQueryRef.current || {};
|
|
128
|
+
|
|
104
129
|
setList(list.slice(pageSize * (pageNum - 1), pageNum * pageSize));
|
|
105
130
|
setTotal(list.length);
|
|
106
131
|
props.onGetListEnd && props.onGetListEnd({ list, pagination: { pageNum, pageSize } });
|
|
@@ -182,7 +207,7 @@ const ListRender = forwardRef(function (props, parentRef) {
|
|
|
182
207
|
paginationQueryRef.current = { ...paginationQueryRef.current, pageNum: 1 };
|
|
183
208
|
|
|
184
209
|
if (model && model.query && !model.query.pageSize) {
|
|
185
|
-
model.query.pageSize =
|
|
210
|
+
model.query.pageSize = pageSizeOptions[0];
|
|
186
211
|
}
|
|
187
212
|
formQueryRef.current = query;
|
|
188
213
|
model.query = Object.assign(model.query, query);
|
|
@@ -324,10 +349,14 @@ const ListRender = forwardRef(function (props, parentRef) {
|
|
|
324
349
|
const modalProps = { ...props.dialogProps, ...props.modalProps };
|
|
325
350
|
const modalFormMount = props.dialogFormMount ?? props.modalFormMount;
|
|
326
351
|
|
|
352
|
+
const handleSetShowSearch = () => {
|
|
353
|
+
setShowSearch(!showSearch);
|
|
354
|
+
};
|
|
355
|
+
|
|
327
356
|
return (
|
|
328
357
|
<div className={`list-render ${props.className}`}>
|
|
329
358
|
<div className={`list-header ${props.verticalHeader ? "vertical-header" : ""}`}>
|
|
330
|
-
{props.hasQuery !== false ? (
|
|
359
|
+
{props.hasQuery !== false && showSearch ? (
|
|
331
360
|
<QueryRender
|
|
332
361
|
ref={queryRef}
|
|
333
362
|
schema={schema}
|
|
@@ -365,32 +394,60 @@ const ListRender = forwardRef(function (props, parentRef) {
|
|
|
365
394
|
</div>
|
|
366
395
|
</div>
|
|
367
396
|
{Slots.HeaderOthersSuffix && <Slots.HeaderOthersSuffix onSearch={onSearch} getList={getList} />}
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
397
|
+
{layout === "card" ? (
|
|
398
|
+
<CardRender
|
|
399
|
+
idKey={idKey}
|
|
400
|
+
schema={schema}
|
|
401
|
+
list={list}
|
|
402
|
+
config={props.cardConf}
|
|
403
|
+
cardProps={props.cardProps}
|
|
404
|
+
hasAction={props.hasAction}
|
|
405
|
+
query={model?.query}
|
|
406
|
+
Slots={props.Slots}
|
|
407
|
+
onEdit={onEdit}
|
|
408
|
+
onDel={onDel}
|
|
409
|
+
onDetail={onDetail}
|
|
410
|
+
onSearch={onSearch}
|
|
411
|
+
getList={getList}
|
|
412
|
+
loading={listLoading}
|
|
413
|
+
components={props.components}
|
|
414
|
+
schemaScope={props.schemaScope}
|
|
415
|
+
i18n={i18n}
|
|
416
|
+
/>
|
|
417
|
+
) : null}
|
|
418
|
+
|
|
419
|
+
{layout === "default" ? (
|
|
420
|
+
<TableRender
|
|
421
|
+
idKey={idKey}
|
|
422
|
+
schema={schema?.schema}
|
|
423
|
+
list={list}
|
|
424
|
+
config={props.tableConf}
|
|
425
|
+
hasAction={props.hasAction}
|
|
426
|
+
query={model?.query}
|
|
427
|
+
Slots={props.Slots}
|
|
428
|
+
onEdit={onEdit}
|
|
429
|
+
onDel={onDel}
|
|
430
|
+
onDetail={onDetail}
|
|
431
|
+
onSearch={onSearch}
|
|
432
|
+
getList={getList}
|
|
433
|
+
setShowSearch={handleSetShowSearch}
|
|
434
|
+
loading={listLoading}
|
|
435
|
+
tableProps={props.tableProps}
|
|
436
|
+
getFieldListOpt={props.getFieldListOpt}
|
|
437
|
+
components={props.components}
|
|
438
|
+
schemaScope={props.schemaScope}
|
|
439
|
+
i18n={i18n}
|
|
440
|
+
/>
|
|
441
|
+
) : null}
|
|
442
|
+
|
|
388
443
|
{props.hasPagination !== false ? (
|
|
389
444
|
<Pagination
|
|
445
|
+
layout={layout}
|
|
390
446
|
onChange={onPageChange}
|
|
391
447
|
total={total}
|
|
392
448
|
query={{ ...model?.query, ...paginationQueryRef.current }}
|
|
393
449
|
config={props.paginationConf}
|
|
450
|
+
pageSizeOptions={pageSizeOptions}
|
|
394
451
|
i18n={i18n}
|
|
395
452
|
/>
|
|
396
453
|
) : null}
|
|
@@ -3,12 +3,8 @@ import { Pagination } from "antd";
|
|
|
3
3
|
|
|
4
4
|
import "./index.less";
|
|
5
5
|
|
|
6
|
-
const pageSizeOptions = [10, 20, 50, 100];
|
|
7
|
-
|
|
8
6
|
function PaginationRender(props) {
|
|
9
|
-
const [pageSize, setPageSize] = useState(
|
|
10
|
-
props.query?.pageSize || (props.config?.pageSizeOptions ? props.config?.pageSizeOptions[0] : pageSizeOptions[0]),
|
|
11
|
-
);
|
|
7
|
+
const [pageSize, setPageSize] = useState(props.query?.pageSize || pageSizeOptions[0]);
|
|
12
8
|
const [pageNum, setPageNumber] = useState(props.query?.pageNum || 1);
|
|
13
9
|
|
|
14
10
|
useEffect(() => {
|
|
@@ -43,7 +39,7 @@ function PaginationRender(props) {
|
|
|
43
39
|
current={pageNum}
|
|
44
40
|
pageSize={pageSize}
|
|
45
41
|
onChange={onChange}
|
|
46
|
-
pageSizeOptions={props.
|
|
42
|
+
pageSizeOptions={props.pageSizeOptions}
|
|
47
43
|
total={props.total}
|
|
48
44
|
showTotal={(total) => `总条数 ${total} 条`}
|
|
49
45
|
showSizeChanger
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useEffect, useState, useMemo, useCallback, useRef } from "react";
|
|
2
|
-
import { Table, Button, Popconfirm, Tooltip } from "antd";
|
|
2
|
+
import { Table, Button, Popconfirm, Tooltip, Checkbox, Popover } from "antd";
|
|
3
3
|
import { Schema } from "@formily/json-schema";
|
|
4
|
-
import { QuestionCircleOutlined } from "@ant-design/icons";
|
|
4
|
+
import { QuestionCircleOutlined, FilterOutlined, SettingOutlined } from "@ant-design/icons";
|
|
5
5
|
import _ from "lodash";
|
|
6
6
|
|
|
7
7
|
import { FormilyField } from "../components/Formily/FormilyField";
|
|
@@ -19,8 +19,16 @@ const scenario = "table-render";
|
|
|
19
19
|
function TableRender(props) {
|
|
20
20
|
const { config = {}, query = {}, i18n } = props;
|
|
21
21
|
const { tableDel = "删除", tableEdit = "编辑", tableDelTip = "确认删除该项?", tableDetail = "详情" } = i18n || {};
|
|
22
|
-
const {
|
|
22
|
+
const {
|
|
23
|
+
orderColType,
|
|
24
|
+
orderColWidth,
|
|
25
|
+
tableEmptyValue,
|
|
26
|
+
isShowTableFilter = false,
|
|
27
|
+
isTableSortXIdex = false,
|
|
28
|
+
} = config || {};
|
|
29
|
+
|
|
23
30
|
const [columns, setColumns] = useState([]);
|
|
31
|
+
const [columnList, setColumnList] = useState([]);
|
|
24
32
|
|
|
25
33
|
const formilyRef = useRef({
|
|
26
34
|
fields: {},
|
|
@@ -184,7 +192,6 @@ function TableRender(props) {
|
|
|
184
192
|
//详情按钮权限
|
|
185
193
|
const _hasDetail =
|
|
186
194
|
hasDetail && typeof hasDetail === "function" ? hasDetail(record, index) : hasDetail !== false;
|
|
187
|
-
console.log("_hasDetail", hasDetail, _hasDetail);
|
|
188
195
|
|
|
189
196
|
//删除按钮权限
|
|
190
197
|
const _hasDel = hasDel && typeof hasDel === "function" ? hasDel(record, index) : hasDel !== false;
|
|
@@ -238,6 +245,7 @@ function TableRender(props) {
|
|
|
238
245
|
}
|
|
239
246
|
|
|
240
247
|
setColumns(columns);
|
|
248
|
+
setColumnList(columns);
|
|
241
249
|
}, [props.schema, props.config]);
|
|
242
250
|
|
|
243
251
|
// 解决 FormilyField 中无相关参数报错的问题。自定义参数都从这里传入
|
|
@@ -245,8 +253,59 @@ function TableRender(props) {
|
|
|
245
253
|
scenario: scenario,
|
|
246
254
|
};
|
|
247
255
|
|
|
256
|
+
const onChange = (e, col) => {
|
|
257
|
+
if (e.target.checked) {
|
|
258
|
+
const list = [...columns, col];
|
|
259
|
+
// 创建映射表提高查找效率
|
|
260
|
+
const columnIndexMap = new Map(columnList.map((item, index) => [item.key, index]));
|
|
261
|
+
setColumns(list.sort((a, b) => columnIndexMap.get(a.key) - columnIndexMap.get(b.key)));
|
|
262
|
+
} else {
|
|
263
|
+
setColumns(columns.filter((item) => item.key !== col.key));
|
|
264
|
+
}
|
|
265
|
+
};
|
|
266
|
+
|
|
248
267
|
return (
|
|
249
268
|
<div className="table-render-wrap">
|
|
269
|
+
{isShowTableFilter ? (
|
|
270
|
+
<div className="table-render-wrap-header">
|
|
271
|
+
<div
|
|
272
|
+
className="table-filter"
|
|
273
|
+
onClick={() => {
|
|
274
|
+
props.setShowSearch && props.setShowSearch();
|
|
275
|
+
}}
|
|
276
|
+
>
|
|
277
|
+
<FilterOutlined className="table-filter-icon" />
|
|
278
|
+
显示过滤项
|
|
279
|
+
</div>
|
|
280
|
+
<Popover
|
|
281
|
+
placement="bottomRight"
|
|
282
|
+
content={
|
|
283
|
+
<div className="table-popover-content">
|
|
284
|
+
{columnList.map((col) => {
|
|
285
|
+
if (col.key === "_$actions") {
|
|
286
|
+
return null;
|
|
287
|
+
}
|
|
288
|
+
return (
|
|
289
|
+
<Checkbox
|
|
290
|
+
key={col.key}
|
|
291
|
+
checked={columns.findIndex((item) => item.key === col.key) !== -1}
|
|
292
|
+
onChange={(e) => onChange(e, col)}
|
|
293
|
+
>
|
|
294
|
+
{col.title}
|
|
295
|
+
</Checkbox>
|
|
296
|
+
);
|
|
297
|
+
})}
|
|
298
|
+
</div>
|
|
299
|
+
}
|
|
300
|
+
trigger="click"
|
|
301
|
+
>
|
|
302
|
+
<div className="table-filter">
|
|
303
|
+
<SettingOutlined className="table-filter-icon" />
|
|
304
|
+
显示列
|
|
305
|
+
</div>
|
|
306
|
+
</Popover>
|
|
307
|
+
</div>
|
|
308
|
+
) : null}
|
|
250
309
|
<Table
|
|
251
310
|
className="table-render"
|
|
252
311
|
rowKey={props.idKey || "id"}
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
.table-render-wrap {
|
|
2
|
+
.table-render-wrap-header {
|
|
3
|
+
padding: 0 12px;
|
|
4
|
+
display: flex;
|
|
5
|
+
justify-content: flex-end;
|
|
6
|
+
align-items: center;
|
|
7
|
+
margin-bottom: 12px;
|
|
8
|
+
.table-filter {
|
|
9
|
+
font-size: 16px;
|
|
10
|
+
cursor: pointer;
|
|
11
|
+
.table-filter-icon {
|
|
12
|
+
margin: 0 4px 0 10px;
|
|
13
|
+
cursor: pointer;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
2
17
|
.inline-block-max-w {
|
|
3
18
|
display: inline-block;
|
|
4
19
|
max-width: 100%;
|
|
@@ -15,3 +30,12 @@
|
|
|
15
30
|
text-overflow: ellipsis;
|
|
16
31
|
}
|
|
17
32
|
}
|
|
33
|
+
.table-popover-content {
|
|
34
|
+
max-width: 400px;
|
|
35
|
+
display: flex;
|
|
36
|
+
flex-direction: column;
|
|
37
|
+
.ant-checkbox-wrapper {
|
|
38
|
+
margin-bottom: 8px;
|
|
39
|
+
margin-left: 0 !important;
|
|
40
|
+
}
|
|
41
|
+
}
|