@hzab/list-render 1.8.1 → 1.8.3-beta1

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 CHANGED
@@ -1,3 +1,14 @@
1
+ # @hzab/list-render@1.9.0
2
+
3
+ feat: 列表渲染支持 tag 模式、前缀 Node (前缀圆点)
4
+ feat: Switch 渲染模式优化,使用 前缀 Node (前缀圆点)
5
+ feat: 新增、编辑、详情弹窗支持抽屉模式
6
+
7
+ # @hzab/list-render@1.8.2
8
+
9
+ fix: query schema props scope 模式问题修复
10
+ fix: query allowClear 允许外部配置
11
+
1
12
  # @hzab/list-render@1.6.2
2
13
 
3
14
  fix: 监听 model query 更改
package/README.md CHANGED
@@ -74,9 +74,11 @@ const listDM = useMemo(
74
74
  | tableProps | Object | | {} | 直接传给 Table 的 props,相关 API 可直接参考 antd table 组件 |
75
75
  | fetchOnEdit | Boolean | | true | 展示编辑弹框时,是否会调用一次详情接口进行回填;若为 false,则会使用表格列表接口返回的 row 数据进行回填 |
76
76
  | fetchById | Boolean | | true | 编辑中的详情请求,是否使用 id 作为入参的 key |
77
- | dialogConf | Object | | {} | dialog 配置对象 |
78
- | dialogDetailProps | Object | | {} | dialog descriptions 配置对象 |
79
- | dialogFormProps | Object | | {} | dialog fromRender 配置对象 |
77
+ | modalMode | string | | dialog | 新增/编辑表单、详情 展示模式: dialog drawer |
78
+ | modalConf | Object | | {} | modal/Drawer 配置对象 |
79
+ | modalDetailProps | Object | | {} | modal descriptions 配置对象 |
80
+ | modalFormProps | Object | | {} | modal/drawer fromRender 配置对象 |
81
+ | modalProps | Object | | {} | modal/drawer 配置对象 |
80
82
  | schemaScope | Object | | {} | formRender schemaScope props |
81
83
  | components | Object | | {} | formRender components props 自定义组件 |
82
84
  | detailComponents | Object | | {} | descriptions components props 自定义组件 |
@@ -89,28 +91,29 @@ const listDM = useMemo(
89
91
  | onCreateSuc | Function | | - | 新增成功返回的回调 |
90
92
  | onEditSuc | Function | | - | 编辑成功返回的回调 |
91
93
  | onDelSuc | Function | | - | 删除成功返回的回调 |
92
- | onFormDialogClose | Function | | - | 表单弹窗关闭回调 |
93
- | dialogFormMount | Function | | - | 新增、编辑弹窗 Form 渲染完成回调 |
94
+ | onFormModalClose | Function | | - | 表单弹窗关闭回调 |
95
+ | modalFormMount | Function | | - | 新增、编辑弹窗 Form 渲染完成回调 |
94
96
  | msgConf | Object | | {} | 新增、编辑、删除、列表查询,详情查询的报错 msg 提示设置 |
95
97
  | i18n | Object | | {} | 文案配置 |
96
98
  | queryFormInitialValues | Object | | {} | 列表上方查询 Form 默认值 |
97
99
  | queryFormIsExtendModelQuery | Boolean | | false | 列表上方查询 Form 默认值是否继承 data-model.query 置 |
100
+ | useFormData | boolean | 否 | | 是否使用 form data 提交数据 |
98
101
 
99
102
  - fetchOnEdit 展示编辑弹框时,是否会调用一次详情接口进行回填(某些场景下,列表接口只返回部分部分字段,只有详情接口会返回全部字段);若为 false,则会使用表格列表接口返回的 row 数据进行回填
100
103
 
101
104
  #### tableConf
102
105
 
103
- | 属性名称 | 属性类型 | 必须 | 默认值 | 描述 |
104
- | --------------- | ------------- | ---- | --------- | -------------------------------------------------------------------------------------------- |
105
- | colConf | Object | | {} | 指定各列的配置(比如列宽),key 为字段的 name。可以指定名为 “\_$actions”的字段来设置“操作”列 |
106
- | rowSelection | Object | | {} | 选择功能的配置。参考 antd table rowSelection 参数 |
107
- | scroll | Object | | {} | 表格是否可滚动,也可以指定滚动区域的宽、高。参考 antd table scroll 参数 |
108
- | expandable | Object | | {} | 配置展开属性。参考 antd table expandable 参数 |
109
- | onRow | Object | | {} | 设置行属性。参考 antd table onRow 参数 |
110
- | orderColType | string | | - | 序号列数据类型:page(按当前页的序号)、all(按所有页的序号) |
111
- | orderColWidth | string/number | | - | 序号列 width 的参数 |
112
- | tableEmptyValue | string/number | | undefined | table 列表空值展示数 |
113
- | isTableSortXIdex | Boolean | | undefined | table 列表列排序是否按照 x-index 排序 |
106
+ | 属性名称 | 属性类型 | 必须 | 默认值 | 描述 |
107
+ | ---------------- | ------------- | ---- | --------- | -------------------------------------------------------------------------------------------- |
108
+ | colConf | Object | | {} | 指定各列的配置(比如列宽),key 为字段的 name。可以指定名为 “\_$actions”的字段来设置“操作”列 |
109
+ | rowSelection | Object | | {} | 选择功能的配置。参考 antd table rowSelection 参数 |
110
+ | scroll | Object | | {} | 表格是否可滚动,也可以指定滚动区域的宽、高。参考 antd table scroll 参数 |
111
+ | expandable | Object | | {} | 配置展开属性。参考 antd table expandable 参数 |
112
+ | onRow | Object | | {} | 设置行属性。参考 antd table onRow 参数 |
113
+ | orderColType | string | | - | 序号列数据类型:page(按当前页的序号)、all(按所有页的序号) |
114
+ | orderColWidth | string/number | | - | 序号列 width 的参数 |
115
+ | tableEmptyValue | string/number | | undefined | table 列表空值展示数 |
116
+ | isTableSortXIdex | Boolean | | undefined | table 列表列排序是否按照 x-index 排序 |
114
117
 
115
118
  ##### tableConf.colConf[xxx]
116
119
 
@@ -159,7 +162,7 @@ const listDM = useMemo(
159
162
  | ------------------ | -------- | ---- | ------ | ------------------------------------ |
160
163
  | headerActionPrefix | Function | 否 | | 新增按钮左侧插槽 |
161
164
  | headerActionSuffix | Function | 否 | | 新增按钮右侧插槽 |
162
- | HeaderOthersSuffix | Function | 否 | | 表格和搜索项之间的插槽 |
165
+ | HeaderOthersSuffix | Function | 否 | | 表格和搜索项之间的插槽 |
163
166
  | tableActionsSlot | Function | 否 | | 操作列插槽,会覆盖操作列 |
164
167
  | actionPrefixSlot | Function | 否 | | 操作列 编辑按钮左侧插槽 |
165
168
  | actionCenterSlot | Function | 否 | | 操作列 编辑、删除按钮中间插槽 |
@@ -203,7 +206,6 @@ const Slots = {
203
206
  | cancelText | string | 否 | | 弹窗底部取消按钮文案 |
204
207
  | footer | Array | 否 | | 自定义弹窗底部按钮 |
205
208
  | beforeSubmit | Function | 否 | | 提交前的回调, return false; 表示拦截,不进行请求。 |
206
- | useFormData | boolean | 否 | | 是否使用 form data 提交数据 |
207
209
 
208
210
  #### paginationConf
209
211
 
@@ -223,16 +225,16 @@ const Slots = {
223
225
 
224
226
  - 可使用 ref 获取并触发执行
225
227
 
226
- | 函数名 | 参数 | 说明 |
227
- | ------------- | ----- | -------------------------------------------- |
228
- | onSearch | query | 重置页码至 1,并刷新列表 |
229
- | getList | query | 获取当前页列表数据 |
230
- | forceUpdate | - | 强制重渲染列表,解决枚举数据渲染不正常的问题 |
231
- | formDialogRef | - | 新增、编辑 弹窗 form-dialog 的 ref |
232
- | queryRef | - | 筛选条件 query-render 的 ref |
233
- | onCreate | - | 手动触发新增按钮相关操作 |
234
- | onEdit | row | 手动触发编辑按钮相关操作 |
235
- | onDel | row | 手动触发删除按钮相关操作 |
228
+ | 函数名 | 参数 | 说明 |
229
+ | ------------ | ----- | -------------------------------------------- |
230
+ | onSearch | query | 重置页码至 1,并刷新列表 |
231
+ | getList | query | 获取当前页列表数据 |
232
+ | forceUpdate | - | 强制重渲染列表,解决枚举数据渲染不正常的问题 |
233
+ | formModalRef | - | 新增、编辑 弹窗 form-modal 的 ref |
234
+ | queryRef | - | 筛选条件 query-render 的 ref |
235
+ | onCreate | - | 手动触发新增按钮相关操作 |
236
+ | onEdit | row | 手动触发编辑按钮相关操作 |
237
+ | onDel | row | 手动触发删除按钮相关操作 |
236
238
 
237
239
  # Schema
238
240
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hzab/list-render",
3
- "version": "1.8.1",
3
+ "version": "1.8.3-beta1",
4
4
  "description": "",
5
5
  "main": "src",
6
6
  "scripts": {
@@ -20,10 +20,10 @@
20
20
  "license": "ISC",
21
21
  "devDependencies": {
22
22
  "@ant-design/icons": "^4.8.1",
23
- "@hzab/data-model": "^1.5.0",
23
+ "@hzab/data-model": "^1.6.0",
24
24
  "@hzab/form-render": "^1.1.5",
25
25
  "@hzab/schema-descriptions": "^1.0.0",
26
- "@hzab/webpack-config": "0.0.12",
26
+ "@hzab/webpack-config": "^0.7.2",
27
27
  "@types/react": "^17.0.62",
28
28
  "@types/react-dom": "^17.0.20",
29
29
  "antd": "^4.24.12",
@@ -0,0 +1,112 @@
1
+ import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react";
2
+
3
+ import { Modal, Drawer, Button } from "antd";
4
+ import Descriptions from "@hzab/schema-descriptions";
5
+
6
+ import "./index.less";
7
+
8
+ let _formData = null;
9
+
10
+ function DetailModal(props, parentRef) {
11
+ const { modalMode, Slots = {}, modalConf = {}, modalProps = {} } = props;
12
+ const [open, setOpen] = useState(false);
13
+ const [data, setData] = useState({});
14
+ const formRef = useRef();
15
+ function show(formData = props.formInitialValues) {
16
+ setOpen(true);
17
+ // 处理 formRef.current 为 undefined 的问题
18
+ setData(formData);
19
+ }
20
+
21
+ function close() {
22
+ props.onClose && props.onClose();
23
+ setOpen(false);
24
+ formRef.current?.formRender?.reset();
25
+ }
26
+
27
+ useImperativeHandle(parentRef, () => ({
28
+ show,
29
+ close,
30
+ cancel: close,
31
+ formRef,
32
+ }));
33
+
34
+ let footer = undefined;
35
+ const options = {
36
+ close,
37
+ form: formRef.current?.formRender,
38
+ scenario: "detail",
39
+ };
40
+ if (modalConf?.footer) {
41
+ footer = typeof modalConf?.footer === "function" ? modalConf.footer({ ...options, options }) : modalConf?.footer;
42
+ } else {
43
+ footer = [];
44
+
45
+ if (Slots.modalFooterPre) {
46
+ footer.push(<Slots.modalFooterPre key="pre" options={options} />);
47
+ }
48
+
49
+ footer.push(
50
+ <Button key="confirm" onClick={close}>
51
+ {modalConf.okText || "关 闭"}
52
+ </Button>,
53
+ );
54
+
55
+ if (Slots.modalFooterSuffix) {
56
+ footer.push(<Slots.modalFooterSuffix key="suffix" options={options} />);
57
+ }
58
+ }
59
+
60
+ /**
61
+ * 解决 show 函数中 formRef.current 为 undefined 的问题
62
+ */
63
+ function didMount() {
64
+ props.modalFormMount && props.modalFormMount();
65
+ if (_formData) {
66
+ setData(_formData);
67
+ _formData = null;
68
+ }
69
+ }
70
+
71
+ const CModal = modalMode === "drawer" ? Drawer : Modal;
72
+ const _modalProps = {
73
+ className: "detail-modal",
74
+ wrapClassName: "detail-modal",
75
+ title: "详情",
76
+ visible: open,
77
+ open: open,
78
+ onClose: close,
79
+ onCancel: close,
80
+ footer: footer,
81
+ maskClosable: modalProps.maskClosable || false,
82
+ width: modalConf?.width ?? 720,
83
+ // 解决弹窗不销毁,表单远程数据没有更新的问题
84
+ destroyOnClose: true,
85
+ ...modalProps,
86
+ };
87
+
88
+ return (
89
+ <CModal {..._modalProps}>
90
+ <Descriptions
91
+ {...props.detailProps}
92
+ components={props.components}
93
+ schema={props.schema}
94
+ schemaScope={{
95
+ scenario: "detail",
96
+ ...(props.schemaScope || {}),
97
+ }}
98
+ data={data}
99
+ />
100
+ <DidMount didMount={didMount} />
101
+ </CModal>
102
+ );
103
+ }
104
+
105
+ function DidMount(props) {
106
+ useEffect(() => {
107
+ props.didMount();
108
+ }, []);
109
+ return null;
110
+ }
111
+
112
+ export default forwardRef(DetailModal);
@@ -0,0 +1,2 @@
1
+ .detail-modal {
2
+ }
@@ -0,0 +1,8 @@
1
+ .form-modal {
2
+ .ant-drawer-footer,
3
+ ant-modal-footer {
4
+ .ant-btn + .ant-btn {
5
+ margin-left: 12px;
6
+ }
7
+ }
8
+ }
@@ -1,6 +1,7 @@
1
1
  import { forwardRef, useEffect, useImperativeHandle, useRef, useState, useMemo } from "react";
2
+ import _ from "lodash";
2
3
 
3
- import { Modal, Button, message } from "antd";
4
+ import { Modal, Drawer, Button, message } from "antd";
4
5
 
5
6
  import FormRender from "@hzab/form-render";
6
7
 
@@ -8,13 +9,29 @@ import "./index.less";
8
9
 
9
10
  let _formData = null;
10
11
 
11
- function FormDialog(props, parentRef) {
12
- const { Slots = {}, dialogConf = {}, modalProps = {} } = props;
12
+ export interface IFormRender {
13
+ setValues: Function;
14
+ reset: Function;
15
+ validate: Function;
16
+ values: Object;
17
+ }
18
+ export interface IFormRef {
19
+ formRender: IFormRender;
20
+ }
21
+
22
+ /**
23
+ * 表单弹层,包含弹窗、抽屉两种模式
24
+ * @param props
25
+ * @param parentRef
26
+ * @returns
27
+ */
28
+ export function FormModal(props, parentRef) {
29
+ const { modalMode, Slots = {}, modalConf = {}, modalProps = {} } = props;
13
30
  const [loading, setLoading] = useState(false);
14
31
  const [title, setTitle] = useState("新增");
15
32
  const [scenario, setScenario] = useState("create");
16
33
  const [open, setOpen] = useState(false);
17
- const formRef = useRef();
34
+ const formRef = useRef<IFormRef>();
18
35
 
19
36
  const FormSlot = useMemo(() => props.Slots?.FormSlot, [props.Slots?.FormSlot]);
20
37
 
@@ -48,8 +65,8 @@ function FormDialog(props, parentRef) {
48
65
  function onOk() {
49
66
  validate().then(async () => {
50
67
  const submitForm = _.cloneDeep(await formRef.current?.formRender?.values);
51
- if (dialogConf.beforeSubmit) {
52
- const isContinue = await dialogConf.beforeSubmit(submitForm, {
68
+ if (modalConf.beforeSubmit) {
69
+ const isContinue = await modalConf.beforeSubmit(submitForm, {
53
70
  cancel: close,
54
71
  formRef,
55
72
  scenario,
@@ -74,7 +91,7 @@ function FormDialog(props, parentRef) {
74
91
  * 校验表单
75
92
  * @returns
76
93
  */
77
- function validate(hideMessage) {
94
+ function validate(hideMessage = false) {
78
95
  return new Promise((resolve, reject) => {
79
96
  formRef.current?.formRender
80
97
  ?.validate()
@@ -98,33 +115,33 @@ function FormDialog(props, parentRef) {
98
115
  validate,
99
116
  scenario,
100
117
  };
101
- if (dialogConf?.footer) {
102
- footer = typeof dialogConf?.footer === "function" ? dialogConf.footer({ ...options, options }) : dialogConf?.footer;
118
+ if (modalConf?.footer) {
119
+ footer = typeof modalConf?.footer === "function" ? modalConf.footer({ ...options, options }) : modalConf?.footer;
103
120
  } else {
104
121
  footer = [];
105
122
 
106
- if (Slots.dialogFooterPre) {
107
- footer.push(<Slots.dialogFooterPre key="pre" options={options} />);
123
+ if (Slots.modalFooterPre) {
124
+ footer.push(<Slots.modalFooterPre key="pre" options={options} />);
108
125
  }
109
126
 
110
127
  footer.push(
111
128
  <Button key="cancel" onClick={close}>
112
- {dialogConf.cancelText || "取 消"}
129
+ {modalConf.cancelText || "取 消"}
113
130
  </Button>,
114
131
  );
115
132
 
116
- if (Slots.dialogFooterCenter) {
117
- footer.push(<Slots.dialogFooterCenter key="center" options={options} />);
133
+ if (Slots.modalFooterCenter) {
134
+ footer.push(<Slots.modalFooterCenter key="center" options={options} />);
118
135
  }
119
136
 
120
137
  footer.push(
121
138
  <Button key="confirm" type="primary" onClick={onOk} loading={loading}>
122
- {dialogConf.okText || "确 定"}
139
+ {modalConf.okText || "确 定"}
123
140
  </Button>,
124
141
  );
125
142
 
126
- if (Slots.dialogFooterSuffix) {
127
- footer.push(<Slots.dialogFooterSuffix key="suffix" options={options} />);
143
+ if (Slots.modalFooterSuffix) {
144
+ footer.push(<Slots.modalFooterSuffix key="suffix" options={options} />);
128
145
  }
129
146
  }
130
147
 
@@ -132,27 +149,33 @@ function FormDialog(props, parentRef) {
132
149
  * 解决 show 函数中 formRef.current 为 undefined 的问题
133
150
  */
134
151
  function didMount() {
135
- props.dialogFormMount && props.dialogFormMount();
152
+ props.modalFormMount && props.modalFormMount();
136
153
  if (_formData) {
137
154
  formRef.current?.formRender?.setValues(_formData);
138
155
  _formData = null;
139
156
  }
140
157
  }
141
158
 
159
+ const CModal = modalMode === "drawer" ? Drawer : Modal;
160
+ const _modalProps = {
161
+ className: "form-modal",
162
+ wrapClassName: "form-modal",
163
+ title: title,
164
+ visible: open,
165
+ open: open,
166
+ onClose: close,
167
+ onCancel: close,
168
+ onOk: onOk,
169
+ footer: footer,
170
+ maskClosable: modalProps.maskClosable || false,
171
+ width: modalConf?.width ?? 720,
172
+ // 解决弹窗不销毁,表单远程数据没有更新的问题
173
+ destroyOnClose: true,
174
+ ...modalProps,
175
+ };
176
+
142
177
  return (
143
- <Modal
144
- wrapClassName="form-dialog"
145
- title={title}
146
- visible={open}
147
- onCancel={close}
148
- onOk={onOk}
149
- footer={footer}
150
- {...modalProps}
151
- maskClosable={modalProps.maskClosable || false}
152
- width={dialogConf?.width}
153
- // 解决弹窗不销毁,表单远程数据没有更新的问题
154
- destroyOnClose
155
- >
178
+ <CModal {..._modalProps}>
156
179
  {FormSlot ? (
157
180
  <FormSlot {...props} ref={formRef} scenario={scenario} schema={props.schema?.schema} />
158
181
  ) : (
@@ -168,7 +191,7 @@ function FormDialog(props, parentRef) {
168
191
  ></FormRender>
169
192
  )}
170
193
  <DidMount didMount={didMount} />
171
- </Modal>
194
+ </CModal>
172
195
  );
173
196
  }
174
197
 
@@ -179,4 +202,4 @@ function DidMount(props) {
179
202
  return null;
180
203
  }
181
204
 
182
- export default forwardRef(FormDialog);
205
+ export default forwardRef(FormModal);
@@ -1,7 +1,8 @@
1
+ import _ from "lodash";
1
2
  import { getFieldMap } from "./utils";
2
3
 
3
4
  export const handleQuerySchema = (opt) => {
4
- const { schema, search, filters, onSearch, replaceComList } = opt || {};
5
+ const { schema, search, filters, onSearch, replaceComList, schemaScope } = opt || {};
5
6
  const queryProperties = {};
6
7
  let index = 0;
7
8
  if (search) {
@@ -10,13 +11,13 @@ export const handleQuerySchema = (opt) => {
10
11
  index += 1;
11
12
  }
12
13
 
13
- const fieldMap = getFieldMap(schema);
14
+ const fieldMap = getFieldMap(_.cloneDeep(schema));
14
15
  filters?.forEach((key) => {
15
16
  const item = fieldMap[key];
16
17
  if (item) {
17
18
  const xComponent = item["x-component"];
18
19
  const replaceItem = replaceComList?.find((it) => it.name === key || (!it.name && it.component === xComponent));
19
- // TODO: 优化
20
+
20
21
  const itemConf = {
21
22
  ...item,
22
23
  ...replaceItem,
@@ -25,11 +26,17 @@ export const handleQuerySchema = (opt) => {
25
26
  "x-display": "visible",
26
27
  };
27
28
 
28
- if (!itemConf["x-component-props"]) {
29
+ // 处理 props 不存在、从 scope 中获取的情况
30
+ const comProps = itemConf["x-component-props"];
31
+ if (!comProps) {
29
32
  itemConf["x-component-props"] = {};
33
+ } else if (isScopeKey(comProps)) {
34
+ itemConf["x-component-props"] = getValByScope(comProps, schemaScope) || {};
30
35
  }
31
36
 
32
- itemConf["x-component-props"].allowClear = true;
37
+ if (typeof itemConf["x-component-props"].allowClear !== "boolean") {
38
+ itemConf["x-component-props"].allowClear = true;
39
+ }
33
40
 
34
41
  handleInput(itemConf, opt);
35
42
  handleSelect(itemConf, opt);
@@ -173,7 +180,7 @@ export const handleShortcutSubmit = (conf, opt) => {
173
180
 
174
181
  // 输入框
175
182
  if (isEnterSubmit && xComponent === "Input") {
176
- const onPressEnter = xComponentProps?.onPressEnter;
183
+ const onPressEnter = getSchemaVal(xComponentProps?.onPressEnter, opt);
177
184
  conf["x-component-props"].onPressEnter = function (e, ...args) {
178
185
  e?.preventDefault && e?.preventDefault();
179
186
  // 阻止默认事件,解决单个 input 提交问题
@@ -184,12 +191,12 @@ export const handleShortcutSubmit = (conf, opt) => {
184
191
  // 清除事件
185
192
  handleInputClear(conf, opt);
186
193
  } else if (isChangeSubmit && onChangeList.includes(xComponent)) {
187
- conf["x-component-props"].onChange = handleHoc(xComponentProps?.onChange, onSearch);
194
+ conf["x-component-props"].onChange = handleHoc(xComponentProps?.onChange, onSearch, opt);
188
195
  }
189
196
  // 处理自定义组件快捷提交逻辑
190
197
  const item = customSubmitList?.find((it) => it.component === xComponent);
191
198
  if (item) {
192
- conf["x-component-props"][item.event] = handleHoc(xComponentProps[item.event], onSearch);
199
+ conf["x-component-props"][item.event] = handleHoc(xComponentProps[item.event], onSearch, opt);
193
200
  }
194
201
  };
195
202
 
@@ -199,11 +206,14 @@ export const handleShortcutSubmit = (conf, opt) => {
199
206
  * @param cb
200
207
  * @returns
201
208
  */
202
- export const handleHoc = (source, cb) => {
209
+ export const handleHoc = (source, cb, opt) => {
203
210
  return async function (...args) {
211
+ const { schemaScope } = opt;
212
+ let _source = getSchemaVal(source, schemaScope);
213
+
204
214
  let res = null;
205
- if (source) {
206
- res = await source(...args);
215
+ if (_source) {
216
+ res = await _source(...args);
207
217
  }
208
218
  cb && cb(...args);
209
219
  return res;
@@ -217,8 +227,8 @@ export const handleHoc = (source, cb) => {
217
227
  * @returns
218
228
  */
219
229
  export const handleInputClear = (conf, opt) => {
220
- const { onSearch } = opt || {};
221
- const onChange = conf["x-component-props"]?.onChange;
230
+ const { onSearch, schemaScope } = opt || {};
231
+ const onChange = getSchemaVal(conf["x-component-props"]?.onChange, schemaScope);
222
232
  conf["x-component-props"].onChange = function (e, ...args) {
223
233
  const res = onChange && onChange(e, ...args);
224
234
  if (e.type === "click" && e.target?.value === "") {
@@ -229,4 +239,56 @@ export const handleInputClear = (conf, opt) => {
229
239
  return conf;
230
240
  };
231
241
 
242
+ /**
243
+ * 是否是 scope key
244
+ * @param strKey
245
+ * @returns
246
+ */
247
+ export const isScopeKey = (strKey) => {
248
+ if (typeof strKey !== "string") {
249
+ return false;
250
+ }
251
+ const key = strKey.trim();
252
+ return key.startsWith("{{") && key.endsWith("}}");
253
+ };
254
+
255
+ /**
256
+ * 获取 scope key
257
+ * @param strKey
258
+ * @returns
259
+ */
260
+ export const getScopeKey = (strKey) => {
261
+ if (!isScopeKey(strKey)) {
262
+ return "";
263
+ }
264
+ const key = strKey.trim();
265
+ return key.replace(/^\{\{/, "").replace(/\}\}$/, "");
266
+ };
267
+
268
+ /**
269
+ * 通过字符串获取 schemaScope 中的数据
270
+ * @param strKey
271
+ * @param schemaScope
272
+ * @returns
273
+ */
274
+ export const getSchemaVal = (strKey, schemaScope = {}) => {
275
+ const val = getValByScope(strKey, schemaScope);
276
+ if (!_.isNil(val) && val !== "") {
277
+ return val;
278
+ }
279
+ return strKey;
280
+ };
281
+
282
+ /**
283
+ * 通过字符串获取 schemaScope 中的数据
284
+ * @param strKey
285
+ * @param schemaScope
286
+ * @returns
287
+ */
288
+ export const getValByScope = (strKey, schemaScope = {}) => {
289
+ if (isScopeKey(strKey)) {
290
+ return schemaScope && schemaScope[getScopeKey(strKey)];
291
+ }
292
+ };
293
+
232
294
  export default handleQuerySchema;
@@ -1,3 +1,4 @@
1
+ import { Schema } from "@formily/json-schema";
1
2
  import _ from "lodash";
2
3
  import dayjs from "dayjs";
3
4
  import advancedFormat from "dayjs/plugin/advancedFormat";
@@ -23,8 +24,9 @@ const dateFormatEnum = {
23
24
  * @returns
24
25
  */
25
26
  export function getVal(field = {}, data = {}, opt = {}) {
27
+ const {} = opt;
26
28
  let val = _.get(data, field.name);
27
- const { } = field || {};
29
+ const {} = field || {};
28
30
  const xComponent = field["x-component"];
29
31
  const xComponentProps = field["x-component-props"] || {};
30
32
 
@@ -89,9 +91,11 @@ export function getDateVal(val, format) {
89
91
  return dayjs(val).format(format);
90
92
  }
91
93
 
92
- export function getFieldList(_schema, fieldList = [], opt = {},isTableSortXIdex = false) {
94
+ export function getFieldList(_schema, fieldList = [], opt = {}, isTableSortXIdex = false) {
93
95
  const schema = _schema?.schema || _schema;
94
96
 
97
+ // 解决 schema 字符串可执行代码、变量等
98
+ const properties = Schema.getOrderProperties(schema);
95
99
  const { boxList = [] } = opt || {};
96
100
  let _boxList = ["FormGrid", "FormGrid.GridColumn", "Card"];
97
101
  if (Array.isArray(boxList)) {
@@ -101,10 +105,10 @@ export function getFieldList(_schema, fieldList = [], opt = {},isTableSortXIdex
101
105
  }
102
106
 
103
107
  schema?.properties &&
104
- Object.keys(schema?.properties).forEach((key) => {
105
- const field = schema?.properties[key];
108
+ properties.forEach((item) => {
109
+ const field = item.schema;
106
110
  if (!field.name) {
107
- field.name = key;
111
+ field.name = item.key;
108
112
  }
109
113
  const componentName = field["x-component"];
110
114
  if (_boxList.includes(componentName)) {
@@ -142,3 +146,53 @@ export function objToFormData(data) {
142
146
  });
143
147
  return formData;
144
148
  }
149
+
150
+ /**
151
+ * 根据 val 获取对应的 options 项数据
152
+ * @param {string|number|boolean} val
153
+ * @param {Object} field
154
+ */
155
+ export const getFieldOptItByVal = function (val, field, opt) {
156
+ if (!field) {
157
+ return {};
158
+ }
159
+ const options = Array.isArray(field.enum)
160
+ ? field.enum
161
+ : Array.isArray(field.options)
162
+ ? field.options
163
+ : field.dataSource || [];
164
+ return getOptItByVal(val, options, { fieldNames: field.fieldNames, ...opt });
165
+ };
166
+
167
+ /**
168
+ * 根据 val 获取对应的 options 项数据,并进行数据归一化处理
169
+ * value, label, children
170
+ * @param {string|number|boolean} val
171
+ * @param {Object} field
172
+ */
173
+ export const getOptItByVal = function (val, options, opt) {
174
+ const { fieldNames } = opt || {};
175
+ const { value = "value", label = "label", children = "children" } = fieldNames || {};
176
+ for (let i = 0; i < options.length; i++) {
177
+ const it = options[i];
178
+ if (it?.[value] === val) {
179
+ return {
180
+ ...it,
181
+ value: it?.[value],
182
+ label: it?.[label],
183
+ children: it?.[children],
184
+ };
185
+ }
186
+ // 递归处理
187
+ const _children = it?.[children];
188
+ if (Array.isArray(_children) && _children.length > 0) {
189
+ const child = getOptItByVal(val, _children, opt);
190
+ return {
191
+ ...child,
192
+ value: child?.[value],
193
+ label: child?.[label],
194
+ children: child?.[children],
195
+ };
196
+ }
197
+ }
198
+ };
@@ -0,0 +1,9 @@
1
+ .table-cell-prefix-box {
2
+ .prefix-node {
3
+ display: inline-block;
4
+ width: 8px;
5
+ height: 8px;
6
+ margin-right: 8px;
7
+ border-radius: 50%;
8
+ }
9
+ }
@@ -0,0 +1,61 @@
1
+ import { ReactNode } from "react";
2
+ import _ from "lodash";
3
+
4
+ import { getFieldOptItByVal } from "../../common/utils";
5
+
6
+ import "./index.less";
7
+
8
+ export interface IPrefixNodeProps {
9
+ showPrefixNode?: boolean | Object;
10
+ value: any;
11
+ field?: Object;
12
+ }
13
+
14
+ export interface ITagOption {
15
+ value: string | number | boolean;
16
+ label: string | number;
17
+ children?: [ITagOption];
18
+ color: string;
19
+ icon: ReactNode | string;
20
+ }
21
+
22
+ export const defaultSwitchEnum = [
23
+ {
24
+ value: true,
25
+ label: "是",
26
+ color: "#00b42a",
27
+ },
28
+ {
29
+ value: false,
30
+ label: "否",
31
+ color: "#c9cdd4",
32
+ },
33
+ ];
34
+
35
+ export const PrefixNode = (props) => {
36
+ const { value, field } = props;
37
+ let showVal = Array.isArray(value) ? value : [value];
38
+
39
+ const _field = { enum: defaultSwitchEnum, ...field };
40
+
41
+ return showVal?.map((it, i) => {
42
+ const option = getFieldOptItByVal(it, _field) || {};
43
+ const PreNode = option.PrefixNode;
44
+ console.log(it, option);
45
+
46
+ return (
47
+ <div key={it + "" + i} className="table-cell-prefix-box">
48
+ {PreNode ? (
49
+ <PreNode />
50
+ ) : option.icon ? (
51
+ option.icon
52
+ ) : (
53
+ <div className="prefix-node" style={{ backgroundColor: option.color }}></div>
54
+ )}
55
+ {option.label || it}
56
+ </div>
57
+ );
58
+ });
59
+ };
60
+
61
+ export default PrefixNode;
@@ -0,0 +1,3 @@
1
+ .table-cell-tag {
2
+ margin-bottom: 8px;
3
+ }
@@ -0,0 +1,37 @@
1
+ import { ReactNode } from "react";
2
+ import { Tag } from "antd";
3
+ import _ from "lodash";
4
+
5
+ import { getFieldOptItByVal } from "../../common/utils";
6
+
7
+ import "./index.less";
8
+
9
+ export interface ITagsProps {
10
+ showTags?: boolean | Object;
11
+ value: any;
12
+ field?: Object;
13
+ }
14
+
15
+ export interface ITagOption {
16
+ value: string | number | boolean;
17
+ label: string | number;
18
+ children?: [ITagOption];
19
+ color: string;
20
+ icon: ReactNode | string;
21
+ }
22
+
23
+ export const Tags = (props) => {
24
+ const { value, field } = props;
25
+ let showVal = Array.isArray(value) ? value : [value];
26
+
27
+ return showVal?.map((it, i) => {
28
+ const option = getFieldOptItByVal(it, field) || {};
29
+ return (
30
+ <Tag key={it + "" + i} className="table-cell-tag" icon={option.icon} color={option.color}>
31
+ {option.label || it}
32
+ </Tag>
33
+ );
34
+ });
35
+ };
36
+
37
+ export default Tags;
@@ -9,8 +9,8 @@ import _ from "lodash";
9
9
  import QueryRender from "./query-render";
10
10
  import Pagination from "./pagination-render";
11
11
  import TableRender from "./table-render";
12
- import FormDialog from "./form-dialog";
13
- import DetailDialog from "./detail-dialog";
12
+ import FormModal from "./FormModal";
13
+ import DetailModal from "./DetailModal";
14
14
 
15
15
  import { objToFormData } from "./common/utils";
16
16
 
@@ -23,8 +23,17 @@ const ListRender = forwardRef(function (props, parentRef) {
23
23
  queryFormIsExtendModelQuery = false,
24
24
  queryFormInitialValues = {},
25
25
  dialogConf = {},
26
+ modalConf = {},
26
27
  /** 编辑接口使用 patch 发起请求 */
27
28
  isPatchUpdate = false,
29
+ /**
30
+ * 表单、详情 展示模式: dialog drawer
31
+ */
32
+ modalMode = "dialog",
33
+ /**
34
+ * 表单提交是否使用 FormData 格式
35
+ */
36
+ useFormData: _useFormData,
28
37
  } = props;
29
38
  const { createText = props.createText } = i18n || {};
30
39
  const [total, setTotal] = useState(0);
@@ -32,18 +41,20 @@ const ListRender = forwardRef(function (props, parentRef) {
32
41
  const [formState, setFormState] = useState("");
33
42
  const [rowId, setRowId] = useState(0);
34
43
  const [listLoading, setListLoading] = useState(false);
35
- const formDialogRef = useRef();
36
- const detailDialogRef = useRef();
44
+ const formModalRef = useRef();
45
+ const detailModalRef = useRef();
37
46
  const queryRef = useRef();
38
47
  const modelQueryRef = useRef({});
39
48
  const formQueryRef = useRef({});
40
49
  const paginationQueryRef = useRef({ pageNum: 1, pageSize: 10 });
50
+ const useFormData = _useFormData ?? dialogConf.useFormData ?? modalConf?.useFormData;
41
51
 
42
52
  useImperativeHandle(parentRef, () => ({
43
53
  getList,
44
54
  onSearch,
45
55
  forceUpdate,
46
- formDialogRef,
56
+ formModalRef,
57
+ formDialogRef: formModalRef,
47
58
  queryRef,
48
59
  onCreate,
49
60
  onEdit,
@@ -69,7 +80,6 @@ const ListRender = forwardRef(function (props, parentRef) {
69
80
  model.query = {};
70
81
  }
71
82
  modelQueryRef.current = model?.query;
72
-
73
83
  }, [model?.query]);
74
84
 
75
85
  useEffect(() => {
@@ -110,7 +120,7 @@ const ListRender = forwardRef(function (props, parentRef) {
110
120
  ..._q,
111
121
  ..._q1,
112
122
  ..._q2,
113
- ..._q3
123
+ ..._q3,
114
124
  };
115
125
 
116
126
  if (mergedQueries.$timerange !== undefined) {
@@ -119,7 +129,6 @@ const ListRender = forwardRef(function (props, parentRef) {
119
129
 
120
130
  model.query = mergedQueries;
121
131
 
122
-
123
132
  model
124
133
  ?.getList(mergedQueries)
125
134
  .then((res) => {
@@ -145,8 +154,8 @@ const ListRender = forwardRef(function (props, parentRef) {
145
154
  getList();
146
155
  }
147
156
 
148
- function onSearch(quer,source) {
149
- const query = source === "queryRender" ? {...quer} :{...formQueryRef.current, ...quer};
157
+ function onSearch(quer, source) {
158
+ const query = source === "queryRender" ? { ...quer } : { ...formQueryRef.current, ...quer };
150
159
  if (model && !model.query) {
151
160
  model.query = {};
152
161
  }
@@ -167,12 +176,12 @@ const ListRender = forwardRef(function (props, parentRef) {
167
176
 
168
177
  function onCreate() {
169
178
  setFormState("create");
170
- formDialogRef.current.show();
179
+ formModalRef.current.show();
171
180
  }
172
181
 
173
182
  async function onCreateSubmit(data) {
174
183
  let _data = typeof model?.createMap === "function" ? model.createMap(data) : data;
175
- if (dialogConf.useFormData) {
184
+ if (useFormData) {
176
185
  _data = objToFormData(_data);
177
186
  }
178
187
  return model
@@ -213,7 +222,7 @@ const ListRender = forwardRef(function (props, parentRef) {
213
222
 
214
223
  function handleDetail(data, id) {
215
224
  setRowId(id);
216
- detailDialogRef.current.show(data);
225
+ detailModalRef.current.show(data);
217
226
  }
218
227
 
219
228
  function onEdit(row, idx) {
@@ -241,13 +250,11 @@ const ListRender = forwardRef(function (props, parentRef) {
241
250
  function handleEdit(data, id) {
242
251
  setRowId(id);
243
252
  setFormState("edit");
244
- formDialogRef.current.show(data, "编辑", "edit");
253
+ formModalRef.current.show(data, "编辑", "edit");
245
254
  }
246
255
 
247
256
  async function onEditSubmit(data) {
248
257
  let _data = data;
249
- console.log("isPatchUpdate", isPatchUpdate);
250
-
251
258
  if (isPatchUpdate) {
252
259
  if (model?.patchMap === "function") {
253
260
  _data = model.patchMap(data);
@@ -256,7 +263,7 @@ const ListRender = forwardRef(function (props, parentRef) {
256
263
  _data = model.updateMap(data);
257
264
  }
258
265
 
259
- if (dialogConf.useFormData) {
266
+ if (useFormData) {
260
267
  _data = objToFormData(_data);
261
268
  }
262
269
  const request = isPatchUpdate ? model?.patch : model?.update;
@@ -289,6 +296,12 @@ const ListRender = forwardRef(function (props, parentRef) {
289
296
 
290
297
  const { Slots = {} } = props;
291
298
 
299
+ const _modalConf = { ...props.dialogConf, ...modalConf };
300
+ const formProps = { ...props.dialogFormProps, ...props.modalFormProps };
301
+ const onFormModalClose = props.onFormDialogClose ?? props.onFormModalClose;
302
+ const modalProps = { ...props.dialogProps, ...props.modalProps };
303
+ const modalFormMount = props.dialogFormMount ?? props.modalFormMount;
304
+
292
305
  return (
293
306
  <div className={`list-render ${props.className}`}>
294
307
  <div className={`list-header ${props.verticalHeader ? "vertical-header" : ""}`}>
@@ -302,9 +315,9 @@ const ListRender = forwardRef(function (props, parentRef) {
302
315
  queryFormInitialValues={
303
316
  queryFormIsExtendModelQuery
304
317
  ? {
305
- ...queryFormInitialValues,
306
- ...model.query,
307
- }
318
+ ...queryFormInitialValues,
319
+ ...model.query,
320
+ }
308
321
  : queryFormInitialValues
309
322
  }
310
323
  onSearch={onSearch}
@@ -329,9 +342,7 @@ const ListRender = forwardRef(function (props, parentRef) {
329
342
  )}
330
343
  </div>
331
344
  </div>
332
- {Slots.HeaderOthersSuffix && (
333
- <Slots.HeaderOthersSuffix onSearch={onSearch} getList={getList} />
334
- )}
345
+ {Slots.HeaderOthersSuffix && <Slots.HeaderOthersSuffix onSearch={onSearch} getList={getList} />}
335
346
  <TableRender
336
347
  idKey={idKey}
337
348
  schema={schema?.schema}
@@ -361,33 +372,37 @@ const ListRender = forwardRef(function (props, parentRef) {
361
372
  i18n={i18n}
362
373
  />
363
374
  ) : null}
364
- <FormDialog
365
- ref={formDialogRef}
375
+
376
+ <FormModal
377
+ ref={formModalRef}
366
378
  schema={schema}
367
- dialogConf={props.dialogConf}
368
379
  formInitialValues={props.formInitialValues}
369
- onClose={props.onFormDialogClose}
370
- onSubmit={formState === "edit" ? onEditSubmit : onCreateSubmit}
371
- Slots={props.Slots}
372
- modalProps={props.dialogModalProps}
373
- formProps={props.dialogFormProps}
374
380
  components={props.components}
375
381
  schemaScope={props.schemaScope}
376
- dialogFormMount={props.dialogFormMount}
382
+ formProps={formProps}
383
+ modalMode={modalMode}
384
+ modalConf={_modalConf}
385
+ onClose={onFormModalClose}
386
+ onSubmit={formState === "edit" ? onEditSubmit : onCreateSubmit}
387
+ Slots={props.Slots}
388
+ modalProps={modalProps}
389
+ modalFormMount={modalFormMount}
377
390
  i18n={i18n}
378
391
  />
379
- <DetailDialog
380
- ref={detailDialogRef}
392
+
393
+ <DetailModal
394
+ ref={detailModalRef}
381
395
  schema={schema}
382
- dialogConf={props.dialogConf}
383
396
  formInitialValues={props.formInitialValues}
384
- onClose={props.onFormDialogClose}
385
- Slots={props.Slots}
386
- detailProps={props.dialogDetailProps}
387
- modalProps={props.dialogModalProps}
388
397
  components={props.detailComponents}
389
398
  schemaScope={props.schemaScope}
390
- dialogFormMount={props.dialogFormMount}
399
+ detailProps={props.dialogDetailProps || props.modalDetailProps}
400
+ modalMode={modalMode}
401
+ modalConf={_modalConf}
402
+ onClose={onFormModalClose}
403
+ Slots={props.Slots}
404
+ modalProps={modalProps}
405
+ modalFormMount={modalFormMount}
391
406
  />
392
407
  </div>
393
408
  );
@@ -18,6 +18,7 @@ function QueryRender(props, parentRef) {
18
18
  setSchema(
19
19
  handleQuerySchema({
20
20
  ...props?.config,
21
+ schemaScope: props.schemaScope,
21
22
  schema: _.cloneDeep(props.schema),
22
23
  search: props.search,
23
24
  filters: _.cloneDeep(props.filters),
@@ -48,12 +49,12 @@ function QueryRender(props, parentRef) {
48
49
 
49
50
  if (!beforeQuerySearchResult) return;
50
51
  }
51
- return props.onSearch && props.onSearch(query, 'queryRender');
52
+ return props.onSearch && props.onSearch(query, "queryRender");
52
53
  }
53
54
 
54
55
  function onReset() {
55
56
  formRef.current?.formRender?.reset();
56
- props.onSearch && props.onSearch(_.cloneDeep(formRef?.current?.formRender?.values), 'queryRender');
57
+ props.onSearch && props.onSearch(_.cloneDeep(formRef?.current?.formRender?.values), "queryRender");
57
58
  }
58
59
 
59
60
  return (
@@ -1,8 +1,12 @@
1
1
  import { useEffect, useState, useMemo, useCallback, useRef } from "react";
2
2
  import { Table, Button, Popconfirm, Tooltip } from "antd";
3
+ import { Schema } from "@formily/json-schema";
3
4
  import { QuestionCircleOutlined } from "@ant-design/icons";
5
+ import _ from "lodash";
4
6
 
5
7
  import { FormilyField } from "../components/Formily/FormilyField";
8
+ import Tags from "../components/Tags";
9
+ import PrefixNode from "../components/PrefixNode";
6
10
  // getColRender 使用 observer 包裹一层 column 的 render 函数,解决数据无法响应的问题
7
11
  import { getColRender } from "../common/formily-utils";
8
12
  import { getVal, getFieldList } from "../common/utils";
@@ -15,7 +19,7 @@ const scenario = "table-render";
15
19
  function TableRender(props) {
16
20
  const { config = {}, query = {}, i18n } = props;
17
21
  const { tableDel = "删除", tableEdit = "编辑", tableDelTip = "确认删除该项?", tableDetail = "详情" } = i18n || {};
18
- const { orderColType, orderColWidth, tableEmptyValue, isTableSortXIdex = false} = config || {};
22
+ const { orderColType, orderColWidth, tableEmptyValue, isTableSortXIdex = false } = config || {};
19
23
  const [columns, setColumns] = useState([]);
20
24
 
21
25
  const formilyRef = useRef({
@@ -58,6 +62,7 @@ function TableRender(props) {
58
62
  const fieldSchemas = formilyRef.current?.fields;
59
63
  if (field.inTable !== false) {
60
64
  const { name, title } = field;
65
+ const comName = field["x-component"];
61
66
 
62
67
  const decoratorProps = field["x-decorator-props"] || {};
63
68
  let _title = title;
@@ -93,11 +98,13 @@ function TableRender(props) {
93
98
  };
94
99
  } else {
95
100
  colRender = function (text, record, index, ...args) {
96
- const { width, ellipsis, emptyValue } = _colConf || {};
101
+ const { width, ellipsis, emptyValue, showTags, showPrefixNode } = _colConf || {};
97
102
  const schemaDefaultValue = fieldSchemas[name]?.componentProps?.emptyValue;
98
103
  const defaultValue = emptyValue ?? schemaDefaultValue ?? tableEmptyValue ?? "";
99
104
 
100
- let val = getVal({ ...field, ...fieldSchemas[name] }, record, { fieldSchema: fieldSchemas?.[name] });
105
+ let val = getVal({ ...field, ...fieldSchemas[name] }, record, {
106
+ fieldSchema: fieldSchemas?.[name],
107
+ });
101
108
 
102
109
  if (val === "" || val === undefined || val === null) {
103
110
  val = defaultValue;
@@ -115,13 +122,37 @@ function TableRender(props) {
115
122
  </Tooltip>
116
123
  );
117
124
  }
125
+ // 使用 tag 渲染内容
126
+ if (showTags) {
127
+ // field 通过 fieldSchemas 获取最新的 field 数据
128
+ return (
129
+ <Tags
130
+ value={_.get(record, name)}
131
+ config={typeof showTags === "object" ? showTags : undefined}
132
+ field={{ ...field, ...fieldSchemas?.[name] }}
133
+ />
134
+ );
135
+ }
136
+ // 使用 前缀节点 渲染内容
137
+ if (showPrefixNode || (showPrefixNode !== false && comName === "Switch")) {
138
+ // field 通过 fieldSchemas 获取最新的 field 数据
139
+ return (
140
+ <PrefixNode
141
+ value={_.get(record, name)}
142
+ config={typeof showPrefixNode === "object" ? showPrefixNode : undefined}
143
+ field={{ ...field, ...fieldSchemas?.[name] }}
144
+ />
145
+ );
146
+ }
147
+
118
148
  return content;
119
149
  };
120
150
  }
121
151
 
122
152
  columns.push({
123
153
  ..._colConf,
124
- onCell: (record, rowIndex) => (_colConf?.onCell?.({...record, _field: { ...field, ...(fieldSchemas?.[name] || {}) }}, rowIndex) || {}),
154
+ onCell: (record, rowIndex) =>
155
+ _colConf?.onCell?.({ ...record, _field: { ...field, ...(fieldSchemas?.[name] || {}) } }, rowIndex) || {},
125
156
  title: _title,
126
157
  key: name,
127
158
  dataIndex: name,
@@ -153,6 +184,7 @@ function TableRender(props) {
153
184
  //详情按钮权限
154
185
  const _hasDetail =
155
186
  hasDetail && typeof hasDetail === "function" ? hasDetail(record, index) : hasDetail !== false;
187
+ console.log("_hasDetail", hasDetail, _hasDetail);
156
188
 
157
189
  //删除按钮权限
158
190
  const _hasDel = hasDel && typeof hasDel === "function" ? hasDel(record, index) : hasDel !== false;
@@ -1,2 +0,0 @@
1
- .form-dialog {
2
- }