@xbeeant/form-engine-designer 0.0.2 → 0.0.4

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.
Files changed (137) hide show
  1. package/dist/es/Canvas.js +42 -46
  2. package/dist/es/Canvas.js.map +1 -1
  3. package/dist/es/Designer.js +121 -98
  4. package/dist/es/Designer.js.map +1 -1
  5. package/dist/es/DesignerContext.js +86 -73
  6. package/dist/es/DesignerContext.js.map +1 -1
  7. package/dist/es/Palette.js +3 -3
  8. package/dist/es/Palette.js.map +1 -1
  9. package/dist/es/PropertyPanel.js +97 -97
  10. package/dist/es/PropertyPanel.js.map +1 -1
  11. package/dist/es/catalog.js +19 -11
  12. package/dist/es/catalog.js.map +1 -1
  13. package/dist/es/designer-context.js +110 -0
  14. package/dist/es/designer-context.js.map +1 -0
  15. package/dist/es/history.js +73 -0
  16. package/dist/es/history.js.map +1 -0
  17. package/dist/es/index.js +3 -3
  18. package/dist/es/property/basic-property.js +6 -0
  19. package/dist/es/property/basic-property.js.map +1 -1
  20. package/dist/es/property-panel.js +294 -0
  21. package/dist/es/property-panel.js.map +1 -0
  22. package/dist/es/resizable-panel.js +103 -0
  23. package/dist/es/resizable-panel.js.map +1 -0
  24. package/dist/es/schema-utils.js +286 -0
  25. package/dist/es/schema-utils.js.map +1 -0
  26. package/dist/es/widgets/BindEditor.js +60 -49
  27. package/dist/es/widgets/BindEditor.js.map +1 -1
  28. package/dist/es/widgets/CodeEditor.js +41 -49
  29. package/dist/es/widgets/CodeEditor.js.map +1 -1
  30. package/dist/es/widgets/DefaultEditor.js +25 -30
  31. package/dist/es/widgets/DefaultEditor.js.map +1 -1
  32. package/dist/es/widgets/DependenciesEditor.js +21 -21
  33. package/dist/es/widgets/DependenciesEditor.js.map +1 -1
  34. package/dist/es/widgets/EnumEditor.js +25 -31
  35. package/dist/es/widgets/EnumEditor.js.map +1 -1
  36. package/dist/es/widgets/ExpressionSwitch.js +32 -30
  37. package/dist/es/widgets/ExpressionSwitch.js.map +1 -1
  38. package/dist/es/widgets/ReactionsEditor.js +103 -103
  39. package/dist/es/widgets/ReactionsEditor.js.map +1 -1
  40. package/dist/es/widgets/RemoteDataEditor.js +63 -63
  41. package/dist/es/widgets/RemoteDataEditor.js.map +1 -1
  42. package/dist/es/widgets/ValidateEditor.js +49 -49
  43. package/dist/es/widgets/ValidateEditor.js.map +1 -1
  44. package/dist/es/widgets/bind-editor.js +78 -0
  45. package/dist/es/widgets/bind-editor.js.map +1 -0
  46. package/dist/es/widgets/code-editor.js +76 -0
  47. package/dist/es/widgets/code-editor.js.map +1 -0
  48. package/dist/es/widgets/default-editor.js +50 -0
  49. package/dist/es/widgets/default-editor.js.map +1 -0
  50. package/dist/es/widgets/dependencies-editor.js +36 -0
  51. package/dist/es/widgets/dependencies-editor.js.map +1 -0
  52. package/dist/es/widgets/enum-editor.js +43 -0
  53. package/dist/es/widgets/enum-editor.js.map +1 -0
  54. package/dist/es/widgets/expression-builder.js +367 -0
  55. package/dist/es/widgets/expression-builder.js.map +1 -0
  56. package/dist/es/widgets/expression-model.js +340 -0
  57. package/dist/es/widgets/expression-model.js.map +1 -0
  58. package/dist/es/widgets/expression-switch.js +49 -0
  59. package/dist/es/widgets/expression-switch.js.map +1 -0
  60. package/dist/es/widgets/index.js +13 -13
  61. package/dist/es/widgets/index.js.map +1 -1
  62. package/dist/es/widgets/options-editor.js +102 -0
  63. package/dist/es/widgets/options-editor.js.map +1 -0
  64. package/dist/es/widgets/reactions-editor.js +401 -0
  65. package/dist/es/widgets/reactions-editor.js.map +1 -0
  66. package/dist/es/widgets/reactions-model.js +107 -0
  67. package/dist/es/widgets/reactions-model.js.map +1 -0
  68. package/dist/es/widgets/remote-data-editor.js +314 -0
  69. package/dist/es/widgets/remote-data-editor.js.map +1 -0
  70. package/dist/es/widgets/use-form-data-fields.js +16 -0
  71. package/dist/es/widgets/use-form-data-fields.js.map +1 -0
  72. package/dist/es/widgets/validate-editor.js +118 -0
  73. package/dist/es/widgets/validate-editor.js.map +1 -0
  74. package/package.json +4 -5
  75. package/dist/es/react/src/components/FormController.js +0 -154
  76. package/dist/es/react/src/components/FormController.js.map +0 -1
  77. package/dist/es/react/src/components/NexusField.js +0 -99
  78. package/dist/es/react/src/components/NexusField.js.map +0 -1
  79. package/dist/es/react/src/components/NexusForm.js +0 -121
  80. package/dist/es/react/src/components/NexusForm.js.map +0 -1
  81. package/dist/es/react/src/components/NexusFormProvider.js +0 -19
  82. package/dist/es/react/src/components/NexusFormProvider.js.map +0 -1
  83. package/dist/es/react/src/components/NexusLayout.js +0 -49
  84. package/dist/es/react/src/components/NexusLayout.js.map +0 -1
  85. package/dist/es/react/src/components/NexusObject.js +0 -65
  86. package/dist/es/react/src/components/NexusObject.js.map +0 -1
  87. package/dist/es/react/src/contexts/FieldInheritContext.js +0 -6
  88. package/dist/es/react/src/contexts/FieldInheritContext.js.map +0 -1
  89. package/dist/es/react/src/contexts/GridContext.js +0 -6
  90. package/dist/es/react/src/contexts/GridContext.js.map +0 -1
  91. package/dist/es/react/src/contexts/LayoutConfigContext.js +0 -6
  92. package/dist/es/react/src/contexts/LayoutConfigContext.js.map +0 -1
  93. package/dist/es/react/src/contexts/NexusContext.js +0 -13
  94. package/dist/es/react/src/contexts/NexusContext.js.map +0 -1
  95. package/dist/es/react/src/hooks/useForm.js +0 -10
  96. package/dist/es/react/src/hooks/useForm.js.map +0 -1
  97. package/dist/es/react/src/utils/renderTreeNode.js +0 -18
  98. package/dist/es/react/src/utils/renderTreeNode.js.map +0 -1
  99. package/dist/es/react/src/utils/resolveColSpan.js +0 -10
  100. package/dist/es/react/src/utils/resolveColSpan.js.map +0 -1
  101. package/dist/es/schemaUtils.test.js +0 -188
  102. package/dist/es/schemaUtils.test.js.map +0 -1
  103. package/dist/es/vite-env.d.js +0 -2
  104. package/dist/es/vite-env.d.js.map +0 -1
  105. package/dist/es/widgets/expressionModel.test.js +0 -193
  106. package/dist/es/widgets/expressionModel.test.js.map +0 -1
  107. package/src/Canvas.tsx +0 -1111
  108. package/src/Designer.css +0 -206
  109. package/src/Designer.tsx +0 -254
  110. package/src/DesignerContext.tsx +0 -192
  111. package/src/Palette.tsx +0 -261
  112. package/src/PropertyPanel.tsx +0 -486
  113. package/src/ResizablePanel.tsx +0 -210
  114. package/src/catalog.ts +0 -526
  115. package/src/index.ts +0 -25
  116. package/src/property/basic-property.ts +0 -126
  117. package/src/schemaUtils.test.ts +0 -261
  118. package/src/schemaUtils.ts +0 -627
  119. package/src/types.ts +0 -38
  120. package/src/vite-env.d.ts +0 -1
  121. package/src/widgets/BindEditor.tsx +0 -105
  122. package/src/widgets/CodeEditor.tsx +0 -114
  123. package/src/widgets/DefaultEditor.tsx +0 -73
  124. package/src/widgets/DependenciesEditor.tsx +0 -56
  125. package/src/widgets/EnumEditor.tsx +0 -66
  126. package/src/widgets/ExpressionBuilder.tsx +0 -509
  127. package/src/widgets/ExpressionSwitch.tsx +0 -62
  128. package/src/widgets/OptionsEditor.tsx +0 -141
  129. package/src/widgets/ReactionsEditor.tsx +0 -532
  130. package/src/widgets/RemoteDataEditor.tsx +0 -382
  131. package/src/widgets/ValidateEditor.tsx +0 -162
  132. package/src/widgets/expressionModel.test.ts +0 -262
  133. package/src/widgets/expressionModel.ts +0 -573
  134. package/src/widgets/index.ts +0 -45
  135. package/src/widgets/reactionsModel.ts +0 -190
  136. package/src/widgets/useFormDataFields.ts +0 -21
  137. /package/dist/es/{form-engine-designer.css → index.css} +0 -0
@@ -1,382 +0,0 @@
1
- // ============================================================================
2
- // RemoteDataEditor — 可视化编辑 remoteData 远程数据配置
3
- // 表单化配置代替手写 JSON:
4
- // - 接口地址 / 请求方式(GET | POST)
5
- // - 响应字段映射(数据数组路径 / 值字段 / 文案字段)
6
- // - 请求参数(key-value 行编辑)
7
- // - 请求头(key-value 行编辑,可选)
8
- // - 缓存键 / 超时(可选)
9
- // 值格式:RemoteDataConfig(见 @xbeeant/form-engine-ui 的 _shared.tsx)
10
- // ============================================================================
11
-
12
- import type { WidgetProps } from '@xbeeant/form-engine-ui';
13
- import {
14
- Button,
15
- Collapse,
16
- Form,
17
- Input,
18
- InputNumber,
19
- Select,
20
- Space,
21
- Tooltip,
22
- } from 'antd';
23
- import { useEffect, useRef, useState } from 'react';
24
- import { genId } from './reactionsModel';
25
-
26
- interface KeyValueRow {
27
- id: string;
28
- key: string;
29
- value: string;
30
- }
31
-
32
- interface RemoteDataForm {
33
- url?: string;
34
- method?: 'GET' | 'POST';
35
- dataPath?: string;
36
- valueKey?: string;
37
- labelKey?: string;
38
- params?: KeyValueRow[];
39
- headers?: KeyValueRow[];
40
- cacheKey?: string;
41
- timeout?: number;
42
- }
43
-
44
- /** Record → key-value 行 */
45
- function recordToRows(
46
- value: Record<string, unknown> | undefined,
47
- ): KeyValueRow[] {
48
- if (!value || typeof value !== 'object') {
49
- return [];
50
- }
51
- return Object.entries(value).map(([key, v]) => ({
52
- id: genId(),
53
- key,
54
- value: typeof v === 'string' ? v : JSON.stringify(v),
55
- }));
56
- }
57
-
58
- /** key-value 行 → Record(空 key 或空 value 的行丢弃) */
59
- function rowsToRecord(
60
- rows: KeyValueRow[] | undefined,
61
- ): Record<string, unknown> | undefined {
62
- if (!rows) {
63
- return undefined;
64
- }
65
- const result: Record<string, unknown> = {};
66
- for (const row of rows) {
67
- const key = row.key.trim();
68
- if (!key) {
69
- continue;
70
- }
71
- const raw = row.value.trim();
72
- if (!raw) {
73
- continue;
74
- }
75
- // 尝试解析 JSON(数字 / 布尔 / 对象),失败保留字符串
76
- try {
77
- result[key] = JSON.parse(raw);
78
- } catch {
79
- result[key] = raw;
80
- }
81
- }
82
- return Object.keys(result).length > 0 ? result : undefined;
83
- }
84
-
85
- /** RemoteDataConfig → 表单 */
86
- function configToForm(value: unknown): RemoteDataForm {
87
- const cfg = (value ?? {}) as Record<string, any>;
88
- const rf = (cfg.responseField ?? {}) as Record<string, any>;
89
- const params = cfg.params;
90
- return {
91
- url: cfg.url,
92
- method: cfg.method,
93
- dataPath: rf.data,
94
- valueKey: rf.value,
95
- labelKey: rf.label,
96
- params:
97
- params && typeof params === 'object' ? recordToRows(params) : undefined,
98
- headers: recordToRows(cfg.headers),
99
- cacheKey: cfg.cacheKey,
100
- timeout: cfg.timeout,
101
- };
102
- }
103
-
104
- /** 表单 → RemoteDataConfig */
105
- function formToConfig(
106
- form: RemoteDataForm,
107
- ): Record<string, unknown> | undefined {
108
- const url = form.url?.trim();
109
- if (!url) {
110
- return undefined;
111
- }
112
- const params = rowsToRecord(form.params);
113
- const headers = rowsToRecord(form.headers);
114
- const config: Record<string, unknown> = {
115
- url,
116
- method: form.method || 'GET',
117
- responseField: {
118
- data: form.dataPath?.trim() || 'data',
119
- value: form.valueKey?.trim() || 'value',
120
- label: form.labelKey?.trim() || 'label',
121
- },
122
- };
123
- if (params) {
124
- config.params = params;
125
- }
126
- if (headers) {
127
- config.headers = headers;
128
- }
129
- if (form.cacheKey?.trim()) {
130
- config.cacheKey = form.cacheKey.trim();
131
- }
132
- if (form.timeout != null) {
133
- config.timeout = form.timeout;
134
- }
135
- return config;
136
- }
137
-
138
- /** key-value 行编辑器(参数 / 请求头共用) */
139
- function KeyValueRows({
140
- rows,
141
- onChange,
142
- valuePlaceholder,
143
- keyPlaceholder,
144
- }: {
145
- rows: KeyValueRow[];
146
- onChange: (rows: KeyValueRow[]) => void;
147
- valuePlaceholder: string;
148
- keyPlaceholder: string;
149
- }) {
150
- return (
151
- <div style={{ width: '100%' }}>
152
- {rows.map((row) => (
153
- <div
154
- key={row.id}
155
- style={{
156
- display: 'flex',
157
- gap: 6,
158
- alignItems: 'center',
159
- marginBottom: 6,
160
- }}
161
- >
162
- <Input
163
- size='small'
164
- value={row.key}
165
- placeholder={keyPlaceholder}
166
- onChange={(e) =>
167
- onChange(
168
- rows.map((r) =>
169
- r.id === row.id ? { ...r, key: e.target.value } : r,
170
- ),
171
- )
172
- }
173
- />
174
- <Input
175
- size='small'
176
- value={row.value}
177
- placeholder={valuePlaceholder}
178
- onChange={(e) =>
179
- onChange(
180
- rows.map((r) =>
181
- r.id === row.id ? { ...r, value: e.target.value } : r,
182
- ),
183
- )
184
- }
185
- />
186
- <Button
187
- size='small'
188
- type='text'
189
- danger
190
- onClick={() => onChange(rows.filter((r) => r.id !== row.id))}
191
- >
192
-
193
- </Button>
194
- </div>
195
- ))}
196
- <Button
197
- size='small'
198
- type='dashed'
199
- block
200
- onClick={() => onChange([...rows, { id: genId(), key: '', value: '' }])}
201
- >
202
- + 添加
203
- </Button>
204
- </div>
205
- );
206
- }
207
-
208
- export function RemoteDataEditor({ value, onChange, title }: WidgetProps) {
209
- const [form, setForm] = useState<RemoteDataForm>(() => configToForm(value));
210
- const prevValueRef = useRef(value);
211
- // 自身 onChange 引发的 value 回传不应触发重建(否则每次输入都被重置)
212
- const lastEmittedRef = useRef(value);
213
-
214
- useEffect(() => {
215
- if (prevValueRef.current !== value) {
216
- prevValueRef.current = value;
217
- if (value !== lastEmittedRef.current) {
218
- setForm(configToForm(value));
219
- }
220
- }
221
- }, [value]);
222
-
223
- const commit = (next: RemoteDataForm) => {
224
- setForm(next);
225
- const emitted = formToConfig(next);
226
- lastEmittedRef.current = emitted;
227
- onChange(emitted);
228
- };
229
-
230
- const patch = (partial: Partial<RemoteDataForm>) => {
231
- commit({ ...form, ...partial });
232
- };
233
-
234
- return (
235
- <Form.Item layout={'vertical'} label={title} style={{ width: '100%' }}>
236
- <Space orientation='vertical' style={{ width: '100%' }} size={8}>
237
- {/* 请求地址 + 方式 */}
238
- <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
239
- <Input
240
- size='small'
241
- placeholder='接口地址(必填),如 /api/options'
242
- value={form.url}
243
- onChange={(e) => patch({ url: e.target.value })}
244
- style={{ flex: 1 }}
245
- />
246
- <Select
247
- size='small'
248
- style={{ width: 80 }}
249
- value={form.method}
250
- onChange={(v) => patch({ method: v })}
251
- options={[
252
- { value: 'GET', label: 'GET' },
253
- { value: 'POST', label: 'POST' },
254
- ]}
255
- />
256
- </div>
257
-
258
- {/* 响应字段映射 */}
259
- <div style={{ fontSize: 11, color: '#999' }}>响应字段映射</div>
260
- <div style={{ display: 'flex', gap: 6, alignItems: 'center' }}>
261
- <Tooltip title='响应数据数组的路径,如 data.list / data.result.records'>
262
- <Input
263
- size='small'
264
- placeholder='数据路径(默认 data)'
265
- value={form.dataPath}
266
- onChange={(e) => patch({ dataPath: e.target.value })}
267
- style={{ flex: 1 }}
268
- />
269
- </Tooltip>
270
- <Tooltip title='作为选项值(value)的字段名'>
271
- <Input
272
- size='small'
273
- placeholder='值字段'
274
- value={form.valueKey}
275
- onChange={(e) => patch({ valueKey: e.target.value })}
276
- style={{ flex: 1 }}
277
- />
278
- </Tooltip>
279
- <Tooltip title='作为选项文案(label)的字段名'>
280
- <Input
281
- size='small'
282
- placeholder='文案字段'
283
- value={form.labelKey}
284
- onChange={(e) => patch({ labelKey: e.target.value })}
285
- style={{ flex: 1 }}
286
- />
287
- </Tooltip>
288
- </div>
289
-
290
- {/* 请求参数 / 请求头 / 高级 */}
291
- <Collapse
292
- size='small'
293
- style={{ width: '100%', background: '#fafafa' }}
294
- items={[
295
- {
296
- key: 'params',
297
- label: '请求参数(可选)',
298
- children: (
299
- <KeyValueRows
300
- rows={form.params ?? []}
301
- onChange={(rows) => patch({ params: rows })}
302
- keyPlaceholder='参数名'
303
- valuePlaceholder='参数值(数字/布尔自动识别)'
304
- />
305
- ),
306
- },
307
- {
308
- key: 'headers',
309
- label: '请求头(可选)',
310
- children: (
311
- <KeyValueRows
312
- rows={form.headers ?? []}
313
- onChange={(rows) => patch({ headers: rows })}
314
- keyPlaceholder='请求头名'
315
- valuePlaceholder='如 Bearer xxxx'
316
- />
317
- ),
318
- },
319
- {
320
- key: 'advanced',
321
- label: '高级',
322
- children: (
323
- <Space
324
- orientation='vertical'
325
- style={{ width: '100%' }}
326
- size={8}
327
- >
328
- <div
329
- style={{ display: 'flex', gap: 6, alignItems: 'center' }}
330
- >
331
- <span
332
- style={{
333
- fontSize: 12,
334
- color: '#666',
335
- whiteSpace: 'nowrap',
336
- }}
337
- >
338
- 缓存键
339
- </span>
340
- <Input
341
- size='small'
342
- placeholder='默认按字段路径缓存'
343
- value={form.cacheKey}
344
- onChange={(e) => patch({ cacheKey: e.target.value })}
345
- />
346
- </div>
347
- <div
348
- style={{ display: 'flex', gap: 6, alignItems: 'center' }}
349
- >
350
- <span
351
- style={{
352
- fontSize: 12,
353
- color: '#666',
354
- whiteSpace: 'nowrap',
355
- }}
356
- >
357
- 超时(秒)
358
- </span>
359
- <InputNumber
360
- size='small'
361
- min={1}
362
- value={form.timeout}
363
- onChange={(v) => patch({ timeout: v ?? undefined })}
364
- placeholder='默认 10'
365
- style={{ width: 120 }}
366
- />
367
- </div>
368
- </Space>
369
- ),
370
- },
371
- ]}
372
- />
373
-
374
- <div style={{ fontSize: 11, color: '#ccc' }}>
375
- 提示:字段值变化后选项自动刷新;POST 时参数放入请求体
376
- </div>
377
- </Space>
378
- </Form.Item>
379
- );
380
- }
381
-
382
- export const remoteDataEditorWidget = RemoteDataEditor;
@@ -1,162 +0,0 @@
1
- // ============================================================================
2
- // ValidateEditor — 可视化编辑 validate 校验规则
3
- // 每行一条规则:「规则名称 + 校验表达式」(表达式需返回 boolean)
4
- // 与 schema 的 ValidateSchema({ key: Expression })互相转换
5
- // ============================================================================
6
-
7
- import type { WidgetProps } from '@xbeeant/form-engine-ui';
8
- import { Button, Form, Input } from 'antd';
9
- import { useEffect, useRef, useState } from 'react';
10
- import { ExpressionBuilder } from './ExpressionBuilder';
11
- import { genId } from './reactionsModel';
12
- import { useFormDataFields } from './useFormDataFields';
13
-
14
- interface ValidateRow {
15
- id: string;
16
- key: string;
17
- expr: string;
18
- }
19
-
20
- /** ValidateSchema 对象 → 规则行 */
21
- function validateToRows(value: unknown): ValidateRow[] {
22
- if (!value || typeof value !== 'object') {
23
- return [];
24
- }
25
- return Object.entries(value as Record<string, unknown>).map(
26
- ([key, expr]) => ({ id: genId(), key, expr: String(expr ?? '') }),
27
- );
28
- }
29
-
30
- /** 规则行 → ValidateSchema 对象(空表达式 / 空 key 的行丢弃) */
31
- function rowsToValidate(
32
- rows: ValidateRow[],
33
- ): Record<string, string> | undefined {
34
- const result: Record<string, string> = {};
35
- for (const row of rows) {
36
- const key = row.key.trim();
37
- const expr = row.expr.trim();
38
- if (!key || !expr || expr === '{{ }}') {
39
- continue;
40
- }
41
- result[key] = expr;
42
- }
43
- return Object.keys(result).length > 0 ? result : undefined;
44
- }
45
-
46
- export function ValidateEditor({ value, onChange, title }: WidgetProps) {
47
- const fields = useFormDataFields();
48
- const [rows, setRows] = useState<ValidateRow[]>(() => validateToRows(value));
49
- const prevValueRef = useRef(value);
50
- // 自身 onChange 引发的 value 回传不应触发重建(否则每次输入都被重置)
51
- const lastEmittedRef = useRef(value);
52
-
53
- useEffect(() => {
54
- if (prevValueRef.current !== value) {
55
- prevValueRef.current = value;
56
- if (value !== lastEmittedRef.current) {
57
- setRows(validateToRows(value));
58
- }
59
- }
60
- }, [value]);
61
-
62
- const commit = (next: ValidateRow[]) => {
63
- setRows(next);
64
- const emitted = rowsToValidate(next);
65
- lastEmittedRef.current = emitted;
66
- onChange(emitted);
67
- };
68
-
69
- const updateRow = (id: string, patch: Partial<ValidateRow>) => {
70
- commit(rows.map((r) => (r.id === id ? { ...r, ...patch } : r)));
71
- };
72
-
73
- const removeRow = (id: string) => {
74
- commit(rows.filter((r) => r.id !== id));
75
- };
76
-
77
- const addRow = () => {
78
- commit([...rows, { id: genId(), key: '', expr: '{{ }}' }]);
79
- };
80
-
81
- return (
82
- <Form.Item layout={'vertical'} label={title} style={{ width: '100%' }}>
83
- {rows.length === 0 ? (
84
- <div
85
- style={{
86
- fontSize: 11,
87
- color: '#999',
88
- background: '#fafafa',
89
- border: '1px dashed #e0e0e0',
90
- borderRadius: 6,
91
- padding: '6px 8px',
92
- marginBottom: 4,
93
- }}
94
- >
95
- 点击下方「添加校验规则」,组合 校验名称 + 表达式(变量 / 操作符 /
96
- 值)。表达式需返回 boolean,可用变量:$self.value、formData。
97
- </div>
98
- ) : (
99
- rows.map((row, i) => (
100
- <div
101
- key={row.id}
102
- style={{
103
- border: '1px solid #e8e8e8',
104
- borderRadius: 8,
105
- padding: 8,
106
- marginBottom: 8,
107
- background: '#fff',
108
- }}
109
- >
110
- <div
111
- style={{
112
- display: 'flex',
113
- alignItems: 'center',
114
- justifyContent: 'space-between',
115
- marginBottom: 6,
116
- }}
117
- >
118
- <span style={{ fontSize: 12, fontWeight: 600, color: '#333' }}>
119
- 校验规则 {i + 1}
120
- </span>
121
- <Button size='small' danger onClick={() => removeRow(row.id)}>
122
- 删除
123
- </Button>
124
- </div>
125
- <div style={{ marginBottom: 6 }}>
126
- <div style={{ fontSize: 11, color: '#666', marginBottom: 2 }}>
127
- 规则名称(key)
128
- </div>
129
- <Input
130
- size='small'
131
- placeholder='如 match / len'
132
- value={row.key}
133
- onChange={(e) => updateRow(row.id, { key: e.target.value })}
134
- />
135
- </div>
136
- <div>
137
- <div style={{ fontSize: 11, color: '#666', marginBottom: 2 }}>
138
- 校验表达式(需返回 boolean)
139
- </div>
140
- <ExpressionBuilder
141
- value={row.expr}
142
- onChange={(s) => updateRow(row.id, { expr: s })}
143
- fields={fields}
144
- showSelf
145
- placeholder='{{ $self.value.length >= 6 }}'
146
- />
147
- </div>
148
- </div>
149
- ))
150
- )}
151
-
152
- <Button size='small' type='dashed' block onClick={addRow}>
153
- + 添加校验规则
154
- </Button>
155
- <div style={{ fontSize: 11, color: '#999', marginTop: 4 }}>
156
- 提示:依赖字段(formData.xxx)变化时会自动联动重校验。
157
- </div>
158
- </Form.Item>
159
- );
160
- }
161
-
162
- export const validateEditorWidget = ValidateEditor;