lu-lowcode-package-form 0.9.30 → 0.9.32

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.
@@ -0,0 +1,302 @@
1
+ import React, { forwardRef, useEffect } from "react";
2
+ import { Form, Row, Col, message } from "antd";
3
+
4
+ import { debounce } from 'lodash';
5
+ import { evalFormula } from '../../utils/formula'
6
+
7
+
8
+ function batchElements(elements, groupSize) {
9
+ const groupedElements = [];
10
+ let tempArray = [];
11
+
12
+ const fillWithReactElement = (size, array) => {
13
+ const missingElementsCount = size - array.length;
14
+ array.push(...new Array(missingElementsCount).fill(React.createElement('div')));
15
+ };
16
+
17
+ for (const element of elements) {
18
+ const { _componentName } = element?.props || {};
19
+ const componentName = element.type?.displayName || _componentName;
20
+ if (componentName && componentName.startsWith('Layout.')) {
21
+ if (tempArray.length > 0) {
22
+ fillWithReactElement(groupSize, tempArray);
23
+ groupedElements.push(tempArray);
24
+ tempArray = [];
25
+ }
26
+ groupedElements.push([element]);
27
+ } else {
28
+ tempArray.push(element);
29
+ if (tempArray.length === groupSize) {
30
+ groupedElements.push(tempArray);
31
+ tempArray = [];
32
+ }
33
+ }
34
+ }
35
+
36
+ if (tempArray.length > 0) {
37
+ fillWithReactElement(groupSize, tempArray);
38
+ groupedElements.push(tempArray);
39
+ }
40
+
41
+ return groupedElements;
42
+ }
43
+
44
+
45
+ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) => {
46
+ const [form] = Form.useForm();
47
+ const [formContent, setFormContent] = React.useState(null);
48
+ const dependencyMap = React.useRef(null);
49
+
50
+ React.useImperativeHandle(ref, () => ({
51
+ formRef: form,
52
+ initializeFieldVisibility,
53
+ }), []);
54
+
55
+ useEffect(() => {
56
+ initializeDependencyMap();
57
+ setFormContent(renderChildren());
58
+ }, [children, cols]);
59
+
60
+ const initializeDependencyMap = () => {
61
+ const fields = [];
62
+ function traverse(currentNode) {
63
+ var componentName = currentNode.type?.displayName || currentNode.props?._componentName;
64
+ const { props } = currentNode;
65
+ if (componentName && (componentName.startsWith('Field.') || componentName.startsWith('Layout.'))) {
66
+ fields.push(currentNode);
67
+ }
68
+
69
+ if (props?.children) {
70
+ let children = React.Children.toArray(props?.children)
71
+ children.forEach(child => traverse(child));
72
+ }
73
+ }
74
+ dependencyMap.current = new Map();
75
+ const childrenArray = React.Children.toArray(children);
76
+ for (let index = 0; index < childrenArray.length; index++) {
77
+ const element = childrenArray[index];
78
+ traverse(element)
79
+ }
80
+ fields.forEach(child => {
81
+ const { componentId, __id, ...props } = child?.props;
82
+ const identifier = componentId || __id;
83
+ dependencyMap.current.set(identifier, {
84
+ children: childrenArray.filter(item => item.props.withId === identifier || item.props.withIds?.some(item => {
85
+ return identifier == item || item.startsWith(identifier + '.')
86
+ })),
87
+ show: true,
88
+ });
89
+ });
90
+ console.log("dependencyMap", dependencyMap.current)
91
+ initializeFieldVisibility();
92
+ };
93
+
94
+ // 初始化字段的级联关系
95
+ const initializeFieldVisibility = (reloadFields = false) => {
96
+ const fieldValues = form.getFieldsValue();
97
+ for (let key of dependencyMap.current.keys()) {
98
+ handleFieldsWith(key, fieldValues);
99
+ }
100
+ if (reloadFields) setFormContent(renderChildren());
101
+ };
102
+
103
+ // 计算字段级联关系
104
+ const handleFieldsWith = (identifier, fieldValues) => {
105
+ let needRefresh = false;
106
+ if (dependencyMap.current.has(identifier)) {
107
+ const dependentChildren = dependencyMap.current.get(identifier).children;
108
+ dependentChildren.forEach(child => {
109
+ if (child.props.withFunc && typeof child.props.withFunc === 'function') {
110
+ needRefresh = handleFieldsVisible(fieldValues, child)
111
+ }
112
+
113
+ if (child.props.withFill)
114
+ handleFieldsWithFill(fieldValues, child)
115
+
116
+ });
117
+ }
118
+ return needRefresh;
119
+ };
120
+
121
+ // 处理级联显示隐藏 @return {boolean} 是否需要重新渲染表单的字段
122
+ const handleFieldsVisible = (fieldValues, child) => {
123
+ let needRefresh = false;
124
+ const childShouldBeVisible = child.props.withFunc(fieldValues);
125
+ const childIdentifier = child.props.componentId || child.props.__id;
126
+ if (dependencyMap.current.has(childIdentifier)) {
127
+ const childData = dependencyMap.current.get(childIdentifier);
128
+ if (childData.show !== childShouldBeVisible) {
129
+ childData.show = childShouldBeVisible;
130
+ dependencyMap.current.set(childIdentifier, childData);
131
+ needRefresh = true;
132
+ }
133
+ }
134
+ return needRefresh
135
+ }
136
+ // 处理级联数据源
137
+ // 处理级联填充
138
+ const handleFieldsWithFill = async (fieldValues, child) => {
139
+ const withFill = child.props.withFill;
140
+ const withDataFetch = child.props.withDataFetch;
141
+ console.log("withFill", withFill)
142
+ console.log("fieldValues", fieldValues)
143
+ console.log("child", child)
144
+
145
+ let withDatas = [];
146
+ // 先处理依赖数据
147
+ if (withFill?.withData && withFill?.withData.length > 0 && withDataFetch && typeof withDataFetch === 'function') {
148
+ for (let index = 0; index < withFill?.withData.length; index++) {
149
+ const element = withFill?.withData[index];
150
+ let params = {}
151
+ params.tableName = element.withTable.table_name
152
+ params.filter = {}
153
+ for (let index = 0; index < element.withCondition.length; index++) {
154
+ const { value: condition_value, column: condition_column } = element.withCondition[index];
155
+ params.filter[condition_column.column_name] = getParamValue(condition_value.group_key, condition_value.field_key, fieldValues, withDatas)
156
+ }
157
+
158
+ // 访问接口获取数据
159
+ const response = await withDataFetch(params)
160
+ if (response.code === 0 && response.data.list) {
161
+ withDatas.push({
162
+ id: element.id,
163
+ data: response.data.list
164
+ })
165
+ }
166
+ }
167
+ }
168
+
169
+ // 构造计算公式
170
+ let formula;
171
+ if (withFill.value && withFill.value.length > 0) {
172
+ formula = withFill.value.map(item => {
173
+ let result = "";
174
+ const { insert, attributes } = item
175
+ if (typeof insert !== "string") {
176
+ if (insert?.span && attributes && attributes.tagKey && attributes.id) {
177
+ result = getParamValue(attributes.tagKey, attributes.id, fieldValues, withDatas)
178
+ if (Array.isArray(result)) result = JSON.stringify(result)
179
+ else if (result) result = `"${result}"`
180
+ }
181
+ }
182
+ else result = insert
183
+ return result
184
+ })
185
+ }
186
+ console.log("formula", formula)
187
+ if (formula && formula.length > 0) {
188
+ const childIdentifier = child.props.componentId || child.props.__id;
189
+ form.setFieldValue(childIdentifier, evalFormula(formula))
190
+ }
191
+ }
192
+
193
+ const getParamValue = (tagKey, id, fieldValues, withDatas) => {
194
+ let result = "";
195
+ // 从当前表单字段取值
196
+ if (tagKey == "fieldsValue") {
197
+ if (id.indexOf(".") >= 0) {
198
+ const [parentKey, childKey] = id.split(".");
199
+ const parentValue = fieldValues?.[parentKey] || [];
200
+ if (Array.isArray(parentValue))
201
+ result = parentValue.map(item => {
202
+ return item?.[childKey] || ""
203
+ })
204
+ else result = parentValue?.[childKey] || ""
205
+ }
206
+ else result = fieldValues?.[id] || ""
207
+ }
208
+ // 从依赖数据取值
209
+ else {
210
+ let withData = withDatas.find(item => item.id === tagKey)
211
+ if (withData && withData.data && withData.data.length > 0) {
212
+ // 暂时只取一条数据,后续再想 sum 函数等问题
213
+ result = withData.data[0]?.[id] || ""
214
+ }
215
+ }
216
+ return result
217
+ }
218
+
219
+ const handleFieldsChange = debounce((changedFields, allFields) => {
220
+ const fieldValues = form.getFieldsValue();
221
+ let needRefresh = false;
222
+ changedFields.forEach(field => {
223
+ if (field.name && field.name.length > 0) {
224
+ needRefresh = handleFieldsWith(field.name[0], fieldValues);
225
+ }
226
+ });
227
+
228
+ if (needRefresh) {
229
+ setFormContent(renderChildren());
230
+ }
231
+ }, 200);
232
+
233
+ const renderChildren = () => {
234
+ const childrenArray = React.Children.toArray(children);
235
+ console.log("childrenArray", childrenArray)
236
+ const groupedChildren = batchElements(
237
+ childrenArray.filter(child => {
238
+ const identifier = child.props.componentId || child.props.__id;
239
+ return dependencyMap.current.has(identifier) && dependencyMap.current.get(identifier).show;
240
+ }),
241
+ cols
242
+ );
243
+
244
+ return groupedChildren.map((group, index) => (
245
+ <Row key={`row-${index}`} gutter={[24, 24]}>
246
+ {group.map((child, index) => {
247
+ const { componentId, __id, _componentName, ...props } = child.props;
248
+ const componentName = child.type?.displayName || _componentName;
249
+ const identifier = componentId || __id;
250
+ const isLayoutComponent = componentName && componentName.startsWith('Layout.');
251
+ const rules = []
252
+ if (props.isRequired)
253
+ rules.push({ required: true, message: `${props.label}必须填写` });
254
+ if (props.rules)
255
+ if (Array.isArray(props.rules)) {
256
+ const pattern = props.rules.join("|")
257
+ rules.push({ pattern: new RegExp(pattern), message: props.rulesFailMessage ? props.rulesFailMessage : `${props.label}格式错误` })
258
+ }
259
+ else {
260
+ rules.push({ pattern: new RegExp(props.rules), message: props.rulesFailMessage ? props.rulesFailMessage : `${props.label}格式错误` })
261
+ }
262
+ return (
263
+ <Col key={identifier || `col-${index}`} span={isLayoutComponent ? 24 : 24 / group.length} style={{ marginBottom: 0 }}>
264
+ {isLayoutComponent && child}
265
+ {!isLayoutComponent && <Form.Item
266
+ style={{ marginBottom: 0 }}
267
+ label=""
268
+ name={identifier}
269
+ rules={rules}
270
+ >
271
+ {child}
272
+ </Form.Item>
273
+ }
274
+
275
+ </Col>
276
+ );
277
+ })}
278
+ </Row>
279
+ ));
280
+ };
281
+
282
+ return (
283
+ <Form form={form} className={"form-container fp-0 fw-full fh-full box-border fflex fflex-col fgap-y-2" + (mode == "desgin" ? " fp-6" : "")} onFieldsChange={handleFieldsChange}>
284
+ {formContent}
285
+ </Form>
286
+ );
287
+ });
288
+
289
+ export function withWrap(Component) {
290
+ return forwardRef((props, ref) => <Component {...props} ref={ref} forwardedRef={ref} />);
291
+ }
292
+
293
+ export class FormContainerClass extends React.PureComponent {
294
+ render() {
295
+ const { forwardedRef, ...otherProps } = this.props;
296
+ return <FormContainer {...otherProps} ref={forwardedRef} />
297
+ }
298
+ }
299
+ const FormContainerWrapper = withWrap(({ forwardedRef, ...props }) => <FormContainer {...props} ref={forwardedRef} />);
300
+
301
+ export { LayoutFormRow, LayoutFormGroupTitle } from './layout';
302
+ export { FormContainer, FormContainerWrapper };
@@ -59,16 +59,26 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
59
59
 
60
60
  const initializeDependencyMap = () => {
61
61
  const fields = [];
62
- function traverse(currentNode) {
62
+ function traverse(currentNode,parentNode = null) {
63
63
  var componentName = currentNode.type?.displayName || currentNode.props?._componentName;
64
64
  const { props } = currentNode;
65
65
  if (componentName && (componentName.startsWith('Field.') || componentName.startsWith('Layout.'))) {
66
- fields.push(currentNode);
66
+ let identifier = props?.componentId || props?.__id;
67
+ let withIds = []
68
+ if(parentNode && parentNode?.props) {
69
+ identifier = `${parentNode?.props?.componentId || parentNode?.props?.__id}.${identifier}`
70
+ }
71
+ if (props?.withId)
72
+ withIds.push(props?.withId)
73
+ if (props?.withIds)
74
+ withIds = [...withIds,...props?.withIds]
75
+
76
+ fields.push({identifier, withIds,component: currentNode});
67
77
  }
68
78
 
69
79
  if (props?.children) {
70
80
  let children = React.Children.toArray(props?.children)
71
- children.forEach(child => traverse(child));
81
+ children.forEach(child => traverse(child,(componentName && (componentName.startsWith('Field.')))?currentNode:null));
72
82
  }
73
83
  }
74
84
  dependencyMap.current = new Map();
@@ -77,13 +87,14 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
77
87
  const element = childrenArray[index];
78
88
  traverse(element)
79
89
  }
80
- fields.forEach(child => {
81
- const { componentId, __id, ...props } = child?.props;
82
- const identifier = componentId || __id;
90
+ console.log("fields", fields)
91
+ fields.forEach(field => {
92
+ const { identifier } = field;
83
93
  dependencyMap.current.set(identifier, {
84
- children: childrenArray.filter(item => item.props.withId === identifier || item.props.withIds?.some(item => {
85
- return identifier == item || item.startsWith(identifier + '.')
86
- })),
94
+ // children: childrenArray.filter(item => item.props.withId === identifier || item.props.withIds?.some(item => {
95
+ // return identifier == item || item.startsWith(identifier + '.')
96
+ // })),
97
+ children: fields.filter(item=>item.withIds.some(item=>item == identifier)),
87
98
  show: true,
88
99
  });
89
100
  });
@@ -103,15 +114,22 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
103
114
  // 计算字段级联关系
104
115
  const handleFieldsWith = (identifier, fieldValues) => {
105
116
  let needRefresh = false;
117
+ let parentIdentifier = [];
118
+ if (Array.isArray(identifier)) {
119
+ parentIdentifier = [...(identifier.slice(0,-1))]
120
+ identifier = identifier.filter(item=>typeof item == "string").join(".")
121
+ }
106
122
  if (dependencyMap.current.has(identifier)) {
107
123
  const dependentChildren = dependencyMap.current.get(identifier).children;
124
+ console.log("dependentChildren", dependentChildren)
125
+
108
126
  dependentChildren.forEach(child => {
109
- if (child.props.withFunc && typeof child.props.withFunc === 'function') {
110
- needRefresh = handleFieldsVisible(fieldValues, child)
127
+ if (child.component.props.withFunc && typeof child.component.props.withFunc === 'function') {
128
+ needRefresh = handleFieldsVisible(fieldValues, child,parentIdentifier)
111
129
  }
112
130
 
113
- if (child.props.withFill)
114
- handleFieldsWithFill(fieldValues, child)
131
+ if (child.component.props.withFill)
132
+ handleFieldsWithFill(fieldValues, child,parentIdentifier)
115
133
 
116
134
  });
117
135
  }
@@ -119,10 +137,10 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
119
137
  };
120
138
 
121
139
  // 处理级联显示隐藏 @return {boolean} 是否需要重新渲染表单的字段
122
- const handleFieldsVisible = (fieldValues, child) => {
140
+ const handleFieldsVisible = (fieldValues, child,parentIdentifier) => {
123
141
  let needRefresh = false;
124
- const childShouldBeVisible = child.props.withFunc(fieldValues);
125
- const childIdentifier = child.props.componentId || child.props.__id;
142
+ const childShouldBeVisible = child?.component?.props?.withFunc(fieldValues);
143
+ const childIdentifier = child?.identifier;
126
144
  if (dependencyMap.current.has(childIdentifier)) {
127
145
  const childData = dependencyMap.current.get(childIdentifier);
128
146
  if (childData.show !== childShouldBeVisible) {
@@ -135,13 +153,19 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
135
153
  }
136
154
  // 处理级联数据源
137
155
  // 处理级联填充
138
- const handleFieldsWithFill = async (fieldValues, child) => {
139
- const withFill = child.props.withFill;
140
- const withDataFetch = child.props.withDataFetch;
141
- console.log("withFill", withFill)
142
- console.log("fieldValues", fieldValues)
143
- console.log("child", child)
144
-
156
+ const handleFieldsWithFill = async (fieldValues, child,parentIdentifier) => {
157
+ const withFill = child?.component?.props.withFill;
158
+ const withDataFetch = child?.component?.props.withDataFetch;
159
+ let withFillIndex = 0
160
+ let withFillGroup = ""
161
+ let childIdentifier = child.identifier;
162
+ if (parentIdentifier && Array.isArray(parentIdentifier) && parentIdentifier.length > 1){
163
+ withFillGroup = parentIdentifier.filter(item=>typeof item == "string").join(".")
164
+ withFillIndex = parentIdentifier[parentIdentifier.length-1]
165
+ if(childIdentifier.startsWith(`${withFillGroup}.`)) {
166
+ childIdentifier = [...parentIdentifier, childIdentifier.replace(`${withFillGroup}.`,"")]
167
+ }
168
+ }
145
169
  let withDatas = [];
146
170
  // 先处理依赖数据
147
171
  if (withFill?.withData && withFill?.withData.length > 0 && withDataFetch && typeof withDataFetch === 'function') {
@@ -174,8 +198,14 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
174
198
  const { insert, attributes } = item
175
199
  if (typeof insert !== "string") {
176
200
  if (insert?.span && attributes && attributes.tagKey && attributes.id) {
201
+
177
202
  result = getParamValue(attributes.tagKey, attributes.id, fieldValues, withDatas)
178
- if (Array.isArray(result)) result = JSON.stringify(result)
203
+ if (Array.isArray(result)){
204
+ if (Array.isArray(childIdentifier) && result.length > withFillIndex) {
205
+ result = result[withFillIndex]
206
+ }
207
+ else result = JSON.stringify(result)
208
+ }
179
209
  else if (result) result = `"${result}"`
180
210
  }
181
211
  }
@@ -183,10 +213,10 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
183
213
  return result
184
214
  })
185
215
  }
186
- console.log("formula", formula)
187
216
  if (formula && formula.length > 0) {
188
- const childIdentifier = child.props.componentId || child.props.__id;
189
- form.setFieldValue(childIdentifier, evalFormula(formula))
217
+ const formulaResult = evalFormula(formula);
218
+ form.setFieldValue(childIdentifier,formulaResult)
219
+ handleFieldsWith(childIdentifier, form.getFieldsValue())
190
220
  }
191
221
  }
192
222
 
@@ -219,9 +249,10 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
219
249
  const handleFieldsChange = debounce((changedFields, allFields) => {
220
250
  const fieldValues = form.getFieldsValue();
221
251
  let needRefresh = false;
252
+ console.log("changedFields",changedFields)
222
253
  changedFields.forEach(field => {
223
254
  if (field.name && field.name.length > 0) {
224
- needRefresh = handleFieldsWith(field.name[0], fieldValues);
255
+ needRefresh = handleFieldsWith(field.name, fieldValues);
225
256
  }
226
257
  });
227
258
 
@@ -236,7 +267,7 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
236
267
  const groupedChildren = batchElements(
237
268
  childrenArray.filter(child => {
238
269
  const identifier = child.props.componentId || child.props.__id;
239
- return dependencyMap.current.has(identifier) && dependencyMap.current.get(identifier).show;
270
+ return !dependencyMap.current.has(identifier) || dependencyMap.current.get(identifier)?.show;
240
271
  }),
241
272
  cols
242
273
  );
@@ -248,6 +279,7 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
248
279
  const componentName = child.type?.displayName || _componentName;
249
280
  const identifier = componentId || __id;
250
281
  const isLayoutComponent = componentName && componentName.startsWith('Layout.');
282
+ const isTable = componentName && componentName == 'Field.Table';
251
283
  const rules = []
252
284
  if (props.isRequired)
253
285
  rules.push({ required: true, message: `${props.label}必须填写` });
@@ -261,18 +293,17 @@ const FormContainer = forwardRef(({ cols = 1, children, mode = "view" }, ref) =>
261
293
  }
262
294
  return (
263
295
  <Col key={identifier || `col-${index}`} span={isLayoutComponent ? 24 : 24 / group.length} style={{ marginBottom: 0 }}>
264
- {isLayoutComponent ? (
265
- child
266
- ) : (
267
- <Form.Item
268
- style={{ marginBottom: 0 }}
269
- label=""
270
- name={identifier}
271
- rules={rules}
272
- >
273
- {child}
274
- </Form.Item>
275
- )}
296
+ {(isLayoutComponent || isTable) && child}
297
+ {!isLayoutComponent && <Form.Item
298
+ style={{ marginBottom: 0 }}
299
+ label=""
300
+ name={identifier}
301
+ rules={rules}
302
+ >
303
+ {child}
304
+ </Form.Item>
305
+ }
306
+
276
307
  </Col>
277
308
  );
278
309
  })}
@@ -11,7 +11,6 @@ const LayoutFormRow = ({ children, layout }) => {
11
11
 
12
12
  const colSpans = useMemo(() => getColSpan(layout), [layout]);
13
13
 
14
- console.log("colSpans", colSpans)
15
14
 
16
15
  const formItems = useMemo(() => {
17
16
  return React.Children.toArray(children).map((child, index) => {