@sparqjs/schema-ui-element-plus 0.1.0
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/dist/adapter/action-types.d.ts +5 -0
- package/dist/adapter/action-types.d.ts.map +1 -0
- package/dist/adapter/action-types.js +2 -0
- package/dist/adapter/action-types.js.map +1 -0
- package/dist/adapter/action.d.ts +17 -0
- package/dist/adapter/action.d.ts.map +1 -0
- package/dist/adapter/action.js +67 -0
- package/dist/adapter/action.js.map +1 -0
- package/dist/adapter/audit.d.ts +6 -0
- package/dist/adapter/audit.d.ts.map +1 -0
- package/dist/adapter/audit.js +67 -0
- package/dist/adapter/audit.js.map +1 -0
- package/dist/adapter/container.d.ts +19 -0
- package/dist/adapter/container.d.ts.map +1 -0
- package/dist/adapter/container.js +114 -0
- package/dist/adapter/container.js.map +1 -0
- package/dist/adapter/context.d.ts +17 -0
- package/dist/adapter/context.d.ts.map +1 -0
- package/dist/adapter/context.js +30 -0
- package/dist/adapter/context.js.map +1 -0
- package/dist/adapter/field.d.ts +44 -0
- package/dist/adapter/field.d.ts.map +1 -0
- package/dist/adapter/field.js +704 -0
- package/dist/adapter/field.js.map +1 -0
- package/dist/adapter/index.d.ts +7 -0
- package/dist/adapter/index.d.ts.map +1 -0
- package/dist/adapter/index.js +539 -0
- package/dist/adapter/index.js.map +1 -0
- package/dist/adapter/layout.d.ts +31 -0
- package/dist/adapter/layout.d.ts.map +1 -0
- package/dist/adapter/layout.js +226 -0
- package/dist/adapter/layout.js.map +1 -0
- package/dist/adapter/types.d.ts +67 -0
- package/dist/adapter/types.d.ts.map +1 -0
- package/dist/adapter/types.js +2 -0
- package/dist/adapter/types.js.map +1 -0
- package/dist/icon-registry.d.ts +3 -0
- package/dist/icon-registry.d.ts.map +1 -0
- package/dist/icon-registry.js +82 -0
- package/dist/icon-registry.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/preset/data-adapter.d.ts +38 -0
- package/dist/preset/data-adapter.d.ts.map +1 -0
- package/dist/preset/data-adapter.js +51 -0
- package/dist/preset/data-adapter.js.map +1 -0
- package/dist/preset/index.d.ts +59 -0
- package/dist/preset/index.d.ts.map +1 -0
- package/dist/preset/index.js +80 -0
- package/dist/preset/index.js.map +1 -0
- package/dist/ui-kit.d.ts +56 -0
- package/dist/ui-kit.d.ts.map +1 -0
- package/dist/ui-kit.js +398 -0
- package/dist/ui-kit.js.map +1 -0
- package/package.json +38 -0
- package/src/style.css +483 -0
|
@@ -0,0 +1,704 @@
|
|
|
1
|
+
import { SCHEMA_COMPONENT_CASCADER, SCHEMA_COMPONENT_CHECKBOX, SCHEMA_COMPONENT_COLOR, SCHEMA_COMPONENT_DATE_PICKER, SCHEMA_COMPONENT_DATE_RANGE_PICKER, SCHEMA_COMPONENT_DATETIME_PICKER, SCHEMA_COMPONENT_FILES, SCHEMA_COMPONENT_ICON, SCHEMA_COMPONENT_ICON_INPUT, SCHEMA_COMPONENT_IMAGE, SCHEMA_COMPONENT_KEY_VALUE, SCHEMA_COMPONENT_MULTI_SELECT, SCHEMA_COMPONENT_NUMBER, SCHEMA_COMPONENT_PASSWORD, SCHEMA_COMPONENT_RADIO, SCHEMA_COMPONENT_RATE, SCHEMA_COMPONENT_RELATION_PICKER, SCHEMA_COMPONENT_RICH_TEXT, SCHEMA_COMPONENT_SELECT, SCHEMA_COMPONENT_SELECT_TREE, SCHEMA_COMPONENT_SLIDER, SCHEMA_COMPONENT_SWITCH, SCHEMA_COMPONENT_TEXT, SCHEMA_COMPONENT_TEXTAREA, SCHEMA_COMPONENT_TIME_PICKER, SCHEMA_COMPONENT_TIME_RANGE_PICKER, SCHEMA_UI_CASCADER, SCHEMA_UI_CHECKBOX, SCHEMA_UI_CHECKBOX_GROUP, SCHEMA_UI_COLOR_PICKER, SCHEMA_UI_DATE_PICKER, SCHEMA_UI_NUMBER, SCHEMA_UI_OPTION, SCHEMA_UI_OPTION_GROUP, SCHEMA_UI_RADIO, SCHEMA_UI_RADIO_GROUP, SCHEMA_UI_RATE, SCHEMA_UI_SELECT, SCHEMA_UI_SLIDER, SCHEMA_UI_SWITCH, SCHEMA_UI_TEXT, SCHEMA_UI_TEXTAREA, SCHEMA_UI_TIME_PICKER, SCHEMA_UI_TREE_SELECT, resolveValidationDateTimeRuleValue, formatRangeValidationEndpoint, resolveRangeValidationParams, resolveValidationIntegerRuleValue, resolveValidationNumberRuleValue, resolveValidationTimeStringRuleValue, } from '@sparqjs/schema-core';
|
|
2
|
+
import { formatDisplayValue } from '@sparqjs/schema-vue';
|
|
3
|
+
import { InfoFilled } from '@element-plus/icons-vue';
|
|
4
|
+
import { ElTooltip } from 'element-plus';
|
|
5
|
+
import { h } from 'vue';
|
|
6
|
+
/**
|
|
7
|
+
* 渲染详情模式下的单个字段。
|
|
8
|
+
*
|
|
9
|
+
* 渲染器提供自定义内容时优先使用 `context.contentNode`。
|
|
10
|
+
* `context.binding` 作为兜底值来源。
|
|
11
|
+
*/
|
|
12
|
+
export function renderDetailField(context) {
|
|
13
|
+
return h('div', {
|
|
14
|
+
...context.node.raw.props,
|
|
15
|
+
class: ['sparq-detail-field', context.node.raw.props?.class],
|
|
16
|
+
'data-sparq-detail-field': context.node.field ?? context.node.id,
|
|
17
|
+
'data-sparq-element-plus-detail-field': context.node.id,
|
|
18
|
+
}, [
|
|
19
|
+
h('span', {
|
|
20
|
+
class: 'sparq-detail-field__label',
|
|
21
|
+
'data-slot': 'label',
|
|
22
|
+
}, renderFieldLabel(context)),
|
|
23
|
+
h('strong', {
|
|
24
|
+
class: 'sparq-detail-field__value',
|
|
25
|
+
'data-slot': 'value',
|
|
26
|
+
}, context.contentNode ?? formatDisplayValue(context.binding?.value)),
|
|
27
|
+
context.helpNode,
|
|
28
|
+
]);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* 渲染字段节点对应的输入控件。
|
|
32
|
+
*
|
|
33
|
+
* `context.node` 提供组件 key、字段 schema 和数据源。
|
|
34
|
+
* `context.disabled` 和 `context.readonly` 存在时会覆盖字段 schema 标记。
|
|
35
|
+
*/
|
|
36
|
+
export function renderFieldContent(context) {
|
|
37
|
+
return renderControl(context, {
|
|
38
|
+
componentKey: context.node.componentKey,
|
|
39
|
+
field: context.node.field,
|
|
40
|
+
label: resolveFieldLabel(context.node),
|
|
41
|
+
fieldSchema: context.node.fieldSchema,
|
|
42
|
+
props: context.node.props ?? context.node.raw.props,
|
|
43
|
+
disabled: context.disabled ?? context.node.fieldSchema?.disabled,
|
|
44
|
+
readonly: context.readonly ?? context.node.fieldSchema?.readonly,
|
|
45
|
+
placeholder: context.placeholder ?? context.node.placeholder,
|
|
46
|
+
nodeSlots: context.node.raw.slots,
|
|
47
|
+
options: context.options,
|
|
48
|
+
binding: context.binding,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 构建搜索项使用的表单绑定对象。
|
|
53
|
+
*/
|
|
54
|
+
export function createSearchItemBinding(context) {
|
|
55
|
+
return {
|
|
56
|
+
value: context.form?.values[context.item.field],
|
|
57
|
+
onChange(value) {
|
|
58
|
+
context.form?.setValue?.(context.item.field, value);
|
|
59
|
+
},
|
|
60
|
+
onBlur() {
|
|
61
|
+
context.form?.setTouched?.(context.item.field);
|
|
62
|
+
},
|
|
63
|
+
errors: context.form?.errors?.[context.item.field] ?? [],
|
|
64
|
+
loading: context.form?.loading?.[context.item.field] ?? false,
|
|
65
|
+
options: context.options ?? [],
|
|
66
|
+
optionLoading: context.form?.optionLoading?.[context.item.field] ?? false,
|
|
67
|
+
optionError: context.form?.optionErrors?.[context.item.field],
|
|
68
|
+
loadOptions: context.item.fieldSchema
|
|
69
|
+
? (input) => {
|
|
70
|
+
const optionInput = normalizeOptionsLoadInput(input);
|
|
71
|
+
return (context.form?.loadOptions?.({
|
|
72
|
+
runtime: context.runtime,
|
|
73
|
+
field: context.item.fieldSchema,
|
|
74
|
+
fieldName: context.item.field,
|
|
75
|
+
dataSource: context.item.dataSource,
|
|
76
|
+
resource: context.resource,
|
|
77
|
+
view: context.state.viewName,
|
|
78
|
+
payload: context.payload,
|
|
79
|
+
keyword: optionInput.keyword,
|
|
80
|
+
values: optionInput.values,
|
|
81
|
+
parentValue: optionInput.parentValue,
|
|
82
|
+
}) ?? Promise.resolve([]));
|
|
83
|
+
}
|
|
84
|
+
: undefined,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* 构建表格筛选项使用的表单绑定对象。
|
|
89
|
+
*
|
|
90
|
+
* `context.filter.field` 可能为空,因此返回的回调必须保持空操作安全。
|
|
91
|
+
*/
|
|
92
|
+
export function createTableFilterBinding(context) {
|
|
93
|
+
const field = context.filter.field;
|
|
94
|
+
return {
|
|
95
|
+
value: field ? context.form?.values[field] : undefined,
|
|
96
|
+
onChange(value) {
|
|
97
|
+
if (field) {
|
|
98
|
+
context.form?.setValue?.(field, value);
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
onBlur() {
|
|
102
|
+
if (field) {
|
|
103
|
+
context.form?.setTouched?.(field);
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
errors: field ? (context.form?.errors?.[field] ?? []) : [],
|
|
107
|
+
loading: field ? (context.form?.loading?.[field] ?? false) : false,
|
|
108
|
+
options: context.options ?? [],
|
|
109
|
+
optionLoading: field ? (context.form?.optionLoading?.[field] ?? false) : false,
|
|
110
|
+
optionError: field ? context.form?.optionErrors?.[field] : undefined,
|
|
111
|
+
loadOptions: field && context.filter.fieldSchema
|
|
112
|
+
? (input) => {
|
|
113
|
+
const optionInput = normalizeOptionsLoadInput(input);
|
|
114
|
+
return (context.form?.loadOptions?.({
|
|
115
|
+
runtime: context.runtime,
|
|
116
|
+
field: context.filter.fieldSchema,
|
|
117
|
+
fieldName: field,
|
|
118
|
+
dataSource: context.filter.dataSource,
|
|
119
|
+
resource: context.resource,
|
|
120
|
+
view: context.state.viewName,
|
|
121
|
+
payload: context.payload,
|
|
122
|
+
keyword: optionInput.keyword,
|
|
123
|
+
values: optionInput.values,
|
|
124
|
+
parentValue: optionInput.parentValue,
|
|
125
|
+
}) ?? Promise.resolve([]));
|
|
126
|
+
}
|
|
127
|
+
: undefined,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* 解析字段节点的可见标签。
|
|
132
|
+
*
|
|
133
|
+
* `raw.title` 优先于 `fieldSchema.label`,最后回退到字段名。
|
|
134
|
+
*/
|
|
135
|
+
export function resolveFieldLabel(node) {
|
|
136
|
+
return node.raw.title ?? node.fieldSchema?.label ?? node.field;
|
|
137
|
+
}
|
|
138
|
+
/** 将字段标题与可聚焦的补充提示组合为统一标签内容。 */
|
|
139
|
+
export function renderFieldLabel(context) {
|
|
140
|
+
const label = resolveFieldLabel(context.node);
|
|
141
|
+
return renderFieldLabelContent(label, context.tip, context.node.id);
|
|
142
|
+
}
|
|
143
|
+
/** 为 UI adapter 内的自定义字段控件复用标题提示交互。 */
|
|
144
|
+
export function renderFieldLabelContent(label, tip, fieldId) {
|
|
145
|
+
if (!tip) {
|
|
146
|
+
return label;
|
|
147
|
+
}
|
|
148
|
+
return h('span', { class: 'sparq-field-label' }, [
|
|
149
|
+
h('span', { class: 'sparq-field-label__text' }, label),
|
|
150
|
+
h(ElTooltip, {
|
|
151
|
+
content: tip,
|
|
152
|
+
placement: 'top',
|
|
153
|
+
popperClass: 'sparq-field-tip-popper',
|
|
154
|
+
trigger: ['hover', 'focus'],
|
|
155
|
+
}, {
|
|
156
|
+
default: () => h('span', {
|
|
157
|
+
class: 'sparq-field-label__tip-trigger',
|
|
158
|
+
role: 'button',
|
|
159
|
+
tabindex: 0,
|
|
160
|
+
'aria-label': `${label ?? '字段'}提示`,
|
|
161
|
+
'data-sparq-field-tip-trigger': fieldId,
|
|
162
|
+
}, [h(InfoFilled)]),
|
|
163
|
+
}),
|
|
164
|
+
]);
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* 根据标准化字段输入渲染一个 Element Plus 控件。
|
|
168
|
+
*
|
|
169
|
+
* `input` 携带组件 key、绑定、字段 schema 和可选数据源。
|
|
170
|
+
*/
|
|
171
|
+
export function renderControl(context, input) {
|
|
172
|
+
const componentKey = input.componentKey ?? SCHEMA_COMPONENT_TEXT;
|
|
173
|
+
const props = createControlProps(input, componentKey);
|
|
174
|
+
const options = resolveControlOptions(input);
|
|
175
|
+
switch (componentKey) {
|
|
176
|
+
case SCHEMA_COMPONENT_TEXTAREA:
|
|
177
|
+
return context.ui.render(SCHEMA_UI_TEXTAREA, createTextareaProps(props));
|
|
178
|
+
case SCHEMA_COMPONENT_PASSWORD:
|
|
179
|
+
return context.ui.render(SCHEMA_UI_TEXT, {
|
|
180
|
+
...props,
|
|
181
|
+
type: 'password',
|
|
182
|
+
showPassword: true,
|
|
183
|
+
}, createTextInputSlots(input.nodeSlots));
|
|
184
|
+
case SCHEMA_COMPONENT_NUMBER:
|
|
185
|
+
return context.ui.render(SCHEMA_UI_NUMBER, props);
|
|
186
|
+
case SCHEMA_COMPONENT_RELATION_PICKER:
|
|
187
|
+
if (isTreeRelation(input)) {
|
|
188
|
+
return context.ui.render(SCHEMA_UI_TREE_SELECT, createRelationTreeSelectProps(input, props, options));
|
|
189
|
+
}
|
|
190
|
+
return context.ui.render(SCHEMA_UI_SELECT, createRelationSelectProps(input, props), {
|
|
191
|
+
default: () => renderOptions(context.ui, options),
|
|
192
|
+
});
|
|
193
|
+
case SCHEMA_COMPONENT_SELECT:
|
|
194
|
+
return context.ui.render(SCHEMA_UI_SELECT, createSelectProps(input, props), {
|
|
195
|
+
default: () => renderOptions(context.ui, options),
|
|
196
|
+
});
|
|
197
|
+
case SCHEMA_COMPONENT_SELECT_TREE:
|
|
198
|
+
return context.ui.render(SCHEMA_UI_TREE_SELECT, {
|
|
199
|
+
...props,
|
|
200
|
+
data: toTreeSelectOptions(options),
|
|
201
|
+
props: {
|
|
202
|
+
label: 'label',
|
|
203
|
+
value: 'value',
|
|
204
|
+
children: 'children',
|
|
205
|
+
},
|
|
206
|
+
renderAfterExpand: false,
|
|
207
|
+
});
|
|
208
|
+
case SCHEMA_COMPONENT_CASCADER:
|
|
209
|
+
return context.ui.render(SCHEMA_UI_CASCADER, {
|
|
210
|
+
...props,
|
|
211
|
+
options: toCascaderOptions(options),
|
|
212
|
+
props: {
|
|
213
|
+
multiple: input.fieldSchema?.multiple,
|
|
214
|
+
emitPath: false,
|
|
215
|
+
checkStrictly: true,
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
case SCHEMA_COMPONENT_MULTI_SELECT:
|
|
219
|
+
return context.ui.render(SCHEMA_UI_SELECT, {
|
|
220
|
+
...createSelectProps(input, props),
|
|
221
|
+
multiple: true,
|
|
222
|
+
}, {
|
|
223
|
+
default: () => renderOptions(context.ui, options),
|
|
224
|
+
});
|
|
225
|
+
case SCHEMA_COMPONENT_RADIO:
|
|
226
|
+
return context.ui.render(SCHEMA_UI_RADIO_GROUP, props, {
|
|
227
|
+
default: () => renderRadioOptions(context.ui, options),
|
|
228
|
+
});
|
|
229
|
+
case SCHEMA_COMPONENT_CHECKBOX:
|
|
230
|
+
return context.ui.render(SCHEMA_UI_CHECKBOX_GROUP, props, {
|
|
231
|
+
default: () => renderCheckboxOptions(context.ui, options),
|
|
232
|
+
});
|
|
233
|
+
case SCHEMA_COMPONENT_SWITCH:
|
|
234
|
+
return context.ui.render(SCHEMA_UI_SWITCH, props);
|
|
235
|
+
case SCHEMA_COMPONENT_RATE:
|
|
236
|
+
return context.ui.render(SCHEMA_UI_RATE, props);
|
|
237
|
+
case SCHEMA_COMPONENT_DATETIME_PICKER:
|
|
238
|
+
return context.ui.render(SCHEMA_UI_DATE_PICKER, withDateTimeControlDefaults(input, props, {
|
|
239
|
+
type: 'datetime',
|
|
240
|
+
format: 'YYYY-MM-DD HH:mm:ss',
|
|
241
|
+
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
|
242
|
+
}));
|
|
243
|
+
case SCHEMA_COMPONENT_DATE_PICKER:
|
|
244
|
+
return context.ui.render(SCHEMA_UI_DATE_PICKER, withDateTimeControlDefaults(input, props, {
|
|
245
|
+
type: 'date',
|
|
246
|
+
format: 'YYYY-MM-DD',
|
|
247
|
+
valueFormat: 'YYYY-MM-DD',
|
|
248
|
+
}));
|
|
249
|
+
case SCHEMA_COMPONENT_DATE_RANGE_PICKER:
|
|
250
|
+
return context.ui.render(SCHEMA_UI_DATE_PICKER, withDateTimeControlDefaults(input, props, {
|
|
251
|
+
type: 'daterange',
|
|
252
|
+
format: 'YYYY-MM-DD',
|
|
253
|
+
valueFormat: 'YYYY-MM-DD',
|
|
254
|
+
}));
|
|
255
|
+
case SCHEMA_COMPONENT_TIME_PICKER:
|
|
256
|
+
return context.ui.render(SCHEMA_UI_TIME_PICKER, withDateTimeControlDefaults(input, props, {
|
|
257
|
+
format: 'HH:mm:ss',
|
|
258
|
+
valueFormat: 'HH:mm:ss',
|
|
259
|
+
}));
|
|
260
|
+
case SCHEMA_COMPONENT_TIME_RANGE_PICKER:
|
|
261
|
+
return context.ui.render(SCHEMA_UI_TIME_PICKER, withDateTimeControlDefaults(input, props, {
|
|
262
|
+
format: 'HH:mm:ss',
|
|
263
|
+
valueFormat: 'HH:mm:ss',
|
|
264
|
+
isRange: true,
|
|
265
|
+
}));
|
|
266
|
+
case SCHEMA_COMPONENT_SLIDER:
|
|
267
|
+
return context.ui.render(SCHEMA_UI_SLIDER, props);
|
|
268
|
+
case SCHEMA_COMPONENT_COLOR:
|
|
269
|
+
return context.ui.render(SCHEMA_UI_COLOR_PICKER, props);
|
|
270
|
+
case SCHEMA_COMPONENT_TEXT:
|
|
271
|
+
return context.ui.render(SCHEMA_UI_TEXT, props, createTextInputSlots(input.nodeSlots));
|
|
272
|
+
case SCHEMA_COMPONENT_RICH_TEXT:
|
|
273
|
+
case SCHEMA_COMPONENT_ICON:
|
|
274
|
+
case SCHEMA_COMPONENT_ICON_INPUT:
|
|
275
|
+
case SCHEMA_COMPONENT_KEY_VALUE:
|
|
276
|
+
case SCHEMA_COMPONENT_IMAGE:
|
|
277
|
+
case SCHEMA_COMPONENT_FILES:
|
|
278
|
+
throw new Error(`Element Plus UI adapter has not implemented control "${componentKey}" yet.`);
|
|
279
|
+
default:
|
|
280
|
+
throw new Error(`Element Plus UI adapter does not support control "${componentKey}".`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
function createSelectProps(input, props) {
|
|
284
|
+
const options = input.fieldSchema?.options;
|
|
285
|
+
if (options?.mode !== 'static' || options.value_policy !== 'open') {
|
|
286
|
+
return props;
|
|
287
|
+
}
|
|
288
|
+
return {
|
|
289
|
+
...props,
|
|
290
|
+
filterable: true,
|
|
291
|
+
allowCreate: true,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
function createTextInputSlots(slots) {
|
|
295
|
+
if (!slots) {
|
|
296
|
+
return undefined;
|
|
297
|
+
}
|
|
298
|
+
const renderedSlots = Object.fromEntries(['prefix', 'suffix', 'prepend', 'append']
|
|
299
|
+
.map((name) => [name, renderStaticSlotContent(slots[name])])
|
|
300
|
+
.filter(([, content]) => content !== undefined)
|
|
301
|
+
.map(([name, content]) => [name, () => content]));
|
|
302
|
+
return Object.keys(renderedSlots).length ? renderedSlots : undefined;
|
|
303
|
+
}
|
|
304
|
+
function renderStaticSlotContent(content) {
|
|
305
|
+
if (!content) {
|
|
306
|
+
return undefined;
|
|
307
|
+
}
|
|
308
|
+
if (content.type === 'text') {
|
|
309
|
+
return content.text;
|
|
310
|
+
}
|
|
311
|
+
if (content.type === 'icon') {
|
|
312
|
+
return h('span', { class: 'sparq-input-slot-icon', 'data-sparq-icon': content.icon }, content.icon);
|
|
313
|
+
}
|
|
314
|
+
return undefined;
|
|
315
|
+
}
|
|
316
|
+
function createControlProps(input, componentKey) {
|
|
317
|
+
const validationProps = createValidationControlProps(input, componentKey);
|
|
318
|
+
return {
|
|
319
|
+
...validationProps,
|
|
320
|
+
...input.props,
|
|
321
|
+
...createEffectiveConstraintProps(componentKey, validationProps, input.props),
|
|
322
|
+
modelValue: input.binding?.value,
|
|
323
|
+
'onUpdate:modelValue': input.binding?.onChange,
|
|
324
|
+
onBlur: input.binding?.onBlur,
|
|
325
|
+
placeholder: input.placeholder ?? stringProp(input.props?.placeholder),
|
|
326
|
+
disabled: input.disabled ?? input.props?.disabled,
|
|
327
|
+
readonly: input.readonly ?? input.props?.readonly,
|
|
328
|
+
loading: input.binding?.loading,
|
|
329
|
+
'data-sparq-element-plus-control': input.field ?? componentKey,
|
|
330
|
+
'data-component': componentKey,
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
function createTextareaProps(props) {
|
|
334
|
+
if (props.autosize !== true) {
|
|
335
|
+
return props;
|
|
336
|
+
}
|
|
337
|
+
const rows = typeof props.rows === 'number' && props.rows > 0 ? props.rows : undefined;
|
|
338
|
+
const next = {
|
|
339
|
+
...props,
|
|
340
|
+
autosize: rows ? { minRows: rows } : true,
|
|
341
|
+
};
|
|
342
|
+
delete next.rows;
|
|
343
|
+
return next;
|
|
344
|
+
}
|
|
345
|
+
function createEffectiveConstraintProps(componentKey, validationProps, componentProps) {
|
|
346
|
+
if (componentKey !== SCHEMA_COMPONENT_NUMBER &&
|
|
347
|
+
componentKey !== SCHEMA_COMPONENT_RATE &&
|
|
348
|
+
componentKey !== SCHEMA_COMPONENT_SLIDER) {
|
|
349
|
+
return {};
|
|
350
|
+
}
|
|
351
|
+
return omitUndefined({
|
|
352
|
+
min: stricterMinimum(validationProps.min, componentProps?.min),
|
|
353
|
+
max: stricterMaximum(validationProps.max, componentProps?.max),
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
function stricterMinimum(left, right) {
|
|
357
|
+
const values = [left, right].filter((value) => typeof value === 'number');
|
|
358
|
+
return values.length ? Math.max(...values) : undefined;
|
|
359
|
+
}
|
|
360
|
+
function stricterMaximum(left, right) {
|
|
361
|
+
const values = [left, right].filter((value) => typeof value === 'number');
|
|
362
|
+
return values.length ? Math.min(...values) : undefined;
|
|
363
|
+
}
|
|
364
|
+
function withControlDefaults(props, defaults) {
|
|
365
|
+
return {
|
|
366
|
+
...defaults,
|
|
367
|
+
...props,
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
function withDateTimeControlDefaults(input, props, defaults) {
|
|
371
|
+
const merged = withControlDefaults(props, defaults);
|
|
372
|
+
if (!input.fieldSchema?.type || input.fieldSchema.type === 'string') {
|
|
373
|
+
return merged;
|
|
374
|
+
}
|
|
375
|
+
return {
|
|
376
|
+
...merged,
|
|
377
|
+
valueFormat: defaults.valueFormat,
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
function createValidationControlProps(input, componentKey) {
|
|
381
|
+
switch (componentKey) {
|
|
382
|
+
case SCHEMA_COMPONENT_NUMBER:
|
|
383
|
+
case SCHEMA_COMPONENT_RATE:
|
|
384
|
+
case SCHEMA_COMPONENT_SLIDER:
|
|
385
|
+
return createNumericValidationProps(input);
|
|
386
|
+
case SCHEMA_COMPONENT_TEXT:
|
|
387
|
+
case SCHEMA_COMPONENT_TEXTAREA:
|
|
388
|
+
case SCHEMA_COMPONENT_PASSWORD:
|
|
389
|
+
return createTextValidationProps(input);
|
|
390
|
+
case SCHEMA_COMPONENT_DATE_PICKER:
|
|
391
|
+
case SCHEMA_COMPONENT_DATETIME_PICKER:
|
|
392
|
+
return createDateValidationProps(input);
|
|
393
|
+
case SCHEMA_COMPONENT_DATE_RANGE_PICKER:
|
|
394
|
+
return input.fieldSchema?.type === 'json'
|
|
395
|
+
? createDateRangeValidationProps(input)
|
|
396
|
+
: createDateValidationProps(input);
|
|
397
|
+
case SCHEMA_COMPONENT_TIME_PICKER:
|
|
398
|
+
return createTimeValidationProps(input);
|
|
399
|
+
case SCHEMA_COMPONENT_TIME_RANGE_PICKER:
|
|
400
|
+
return input.fieldSchema?.type === 'json'
|
|
401
|
+
? createTimeRangeValidationProps(input)
|
|
402
|
+
: createTimeValidationProps(input);
|
|
403
|
+
default:
|
|
404
|
+
return {};
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
function createNumericValidationProps(input) {
|
|
408
|
+
const min = resolveValidationNumberRuleValue(findValidationRule(input, 'min'), input.fieldSchema);
|
|
409
|
+
const max = resolveValidationNumberRuleValue(findValidationRule(input, 'max'), input.fieldSchema);
|
|
410
|
+
return omitUndefined({ min, max });
|
|
411
|
+
}
|
|
412
|
+
function createTextValidationProps(input) {
|
|
413
|
+
const minlength = resolveValidationIntegerRuleValue(findValidationRule(input, 'min_length'));
|
|
414
|
+
const maxlength = resolveValidationIntegerRuleValue(findValidationRule(input, 'max_length'));
|
|
415
|
+
return omitUndefined({ minlength, maxlength });
|
|
416
|
+
}
|
|
417
|
+
function createDateValidationProps(input) {
|
|
418
|
+
const min = resolveValidationDateTimeRuleValue(findValidationRule(input, 'min'), input.fieldSchema);
|
|
419
|
+
const max = resolveValidationDateTimeRuleValue(findValidationRule(input, 'max'), input.fieldSchema);
|
|
420
|
+
if (min === undefined && max === undefined) {
|
|
421
|
+
return {};
|
|
422
|
+
}
|
|
423
|
+
return {
|
|
424
|
+
disabledDate(date) {
|
|
425
|
+
const value = date.getTime();
|
|
426
|
+
return (min !== undefined && value < min) || (max !== undefined && value > max);
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
function createTimeValidationProps(input) {
|
|
431
|
+
const min = resolveValidationTimeStringRuleValue(findValidationRule(input, 'min'), input.fieldSchema);
|
|
432
|
+
const max = resolveValidationTimeStringRuleValue(findValidationRule(input, 'max'), input.fieldSchema);
|
|
433
|
+
if (!min && !max) {
|
|
434
|
+
return {};
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
selectableRange: `${min ?? '00:00:00'} - ${max ?? '23:59:59'}`,
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
function createDateRangeValidationProps(input) {
|
|
441
|
+
const rangeRule = findValidationRule(input, 'range');
|
|
442
|
+
const resolved = resolveRangeValidationParams(rangeRule?.params);
|
|
443
|
+
if (!resolved.value || resolved.value.spanUnit !== 'day') {
|
|
444
|
+
return {};
|
|
445
|
+
}
|
|
446
|
+
const { min, max } = resolved.value;
|
|
447
|
+
if (min === undefined && max === undefined) {
|
|
448
|
+
return {};
|
|
449
|
+
}
|
|
450
|
+
return {
|
|
451
|
+
disabledDate(date) {
|
|
452
|
+
const value = Date.UTC(date.getFullYear(), date.getMonth(), date.getDate());
|
|
453
|
+
return (min !== undefined && value < min) || (max !== undefined && value > max);
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
function createTimeRangeValidationProps(input) {
|
|
458
|
+
const rangeRule = findValidationRule(input, 'range');
|
|
459
|
+
const resolved = resolveRangeValidationParams(rangeRule?.params);
|
|
460
|
+
if (!resolved.value || resolved.value.spanUnit !== 'minute') {
|
|
461
|
+
return {};
|
|
462
|
+
}
|
|
463
|
+
const min = resolved.value.min === undefined ? '00:00:00' : formatRangeValidationEndpoint(resolved.value.min, 'minute');
|
|
464
|
+
const max = resolved.value.max === undefined ? '23:59:59' : formatRangeValidationEndpoint(resolved.value.max, 'minute');
|
|
465
|
+
if (resolved.value.min === undefined && resolved.value.max === undefined) {
|
|
466
|
+
return {};
|
|
467
|
+
}
|
|
468
|
+
return {
|
|
469
|
+
selectableRange: `${min} - ${max}`,
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
function findValidationRule(input, type) {
|
|
473
|
+
return input.fieldSchema?.validation?.find((rule) => normalizeRuleType(rule.type) === type);
|
|
474
|
+
}
|
|
475
|
+
function normalizeRuleType(type) {
|
|
476
|
+
return String(type ?? '')
|
|
477
|
+
.trim()
|
|
478
|
+
.toLowerCase();
|
|
479
|
+
}
|
|
480
|
+
function stringProp(value) {
|
|
481
|
+
return typeof value === 'string' ? value : undefined;
|
|
482
|
+
}
|
|
483
|
+
function omitUndefined(input) {
|
|
484
|
+
return Object.fromEntries(Object.entries(input).filter(([, value]) => value !== undefined));
|
|
485
|
+
}
|
|
486
|
+
function createRelationSelectProps(input, props) {
|
|
487
|
+
return {
|
|
488
|
+
...props,
|
|
489
|
+
filterable: true,
|
|
490
|
+
remote: true,
|
|
491
|
+
clearable: true,
|
|
492
|
+
multiple: input.fieldSchema?.multiple,
|
|
493
|
+
reserveKeyword: true,
|
|
494
|
+
loading: input.binding?.optionLoading ?? props.loading,
|
|
495
|
+
remoteMethod(keyword) {
|
|
496
|
+
void input.binding?.loadOptions?.({ keyword }).catch(() => undefined);
|
|
497
|
+
},
|
|
498
|
+
onVisibleChange(visible) {
|
|
499
|
+
if (visible && shouldLoadInitialOptions(input)) {
|
|
500
|
+
void input.binding
|
|
501
|
+
?.loadOptions?.({
|
|
502
|
+
keyword: '',
|
|
503
|
+
values: selectedValues(input.binding?.value),
|
|
504
|
+
})
|
|
505
|
+
.catch(() => undefined);
|
|
506
|
+
}
|
|
507
|
+
},
|
|
508
|
+
};
|
|
509
|
+
}
|
|
510
|
+
function createRelationTreeSelectProps(input, props, options) {
|
|
511
|
+
const loadMode = relationLoadMode(input);
|
|
512
|
+
const lazy = loadMode === 'tree' && options.length > 0;
|
|
513
|
+
return {
|
|
514
|
+
...props,
|
|
515
|
+
data: toTreeSelectOptions(options, lazy),
|
|
516
|
+
filterable: true,
|
|
517
|
+
clearable: true,
|
|
518
|
+
multiple: input.fieldSchema?.multiple,
|
|
519
|
+
loading: input.binding?.optionLoading ?? props.loading,
|
|
520
|
+
lazy,
|
|
521
|
+
props: {
|
|
522
|
+
label: 'label',
|
|
523
|
+
value: 'value',
|
|
524
|
+
children: 'children',
|
|
525
|
+
isLeaf: 'isLeaf',
|
|
526
|
+
},
|
|
527
|
+
load(node, resolve) {
|
|
528
|
+
if (!lazy) {
|
|
529
|
+
resolve([]);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (!node.level) {
|
|
533
|
+
resolve(toTreeSelectOptions(options, lazy));
|
|
534
|
+
return;
|
|
535
|
+
}
|
|
536
|
+
const parentValue = node.data?.value;
|
|
537
|
+
const loading = input.binding?.loadOptions?.({ parentValue }) ?? Promise.resolve([]);
|
|
538
|
+
void loading.then((items) => {
|
|
539
|
+
resolve(toTreeSelectOptions(items.map(toElementPlusOption), lazy));
|
|
540
|
+
});
|
|
541
|
+
},
|
|
542
|
+
onVisibleChange(visible) {
|
|
543
|
+
if (visible && shouldLoadTreeRelationOptions(input, loadMode)) {
|
|
544
|
+
const loadInput = loadMode === 'tree' ? {} : undefined;
|
|
545
|
+
void input.binding?.loadOptions?.(loadInput);
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
function shouldLoadInitialOptions(input) {
|
|
551
|
+
if (input.binding?.optionLoading) {
|
|
552
|
+
return false;
|
|
553
|
+
}
|
|
554
|
+
const options = input.options ?? input.binding?.options ?? [];
|
|
555
|
+
return options.length === 0;
|
|
556
|
+
}
|
|
557
|
+
function shouldLoadTreeRelationOptions(input, loadMode) {
|
|
558
|
+
if (input.binding?.optionLoading) {
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
if (loadMode === 'all') {
|
|
562
|
+
return true;
|
|
563
|
+
}
|
|
564
|
+
return shouldLoadInitialOptions(input);
|
|
565
|
+
}
|
|
566
|
+
function isTreeRelation(input) {
|
|
567
|
+
const reference = input.fieldSchema?.options?.reference;
|
|
568
|
+
return Boolean(reference?.parent_field || reference?.load_mode === 'tree');
|
|
569
|
+
}
|
|
570
|
+
function relationLoadMode(input) {
|
|
571
|
+
const reference = input.fieldSchema?.options?.reference;
|
|
572
|
+
if (reference?.load_mode === 'tree') {
|
|
573
|
+
return 'tree';
|
|
574
|
+
}
|
|
575
|
+
if (reference?.load_mode === 'paged') {
|
|
576
|
+
return 'paged';
|
|
577
|
+
}
|
|
578
|
+
if (reference?.load_mode === 'all' || reference?.parent_field) {
|
|
579
|
+
return 'all';
|
|
580
|
+
}
|
|
581
|
+
return 'paged';
|
|
582
|
+
}
|
|
583
|
+
function selectedValues(value) {
|
|
584
|
+
if (value === undefined || value === null || value === '') {
|
|
585
|
+
return undefined;
|
|
586
|
+
}
|
|
587
|
+
return Array.isArray(value) ? value : [value];
|
|
588
|
+
}
|
|
589
|
+
function normalizeOptionsLoadInput(input) {
|
|
590
|
+
if (typeof input === 'string' || input === undefined) {
|
|
591
|
+
return {
|
|
592
|
+
keyword: input,
|
|
593
|
+
};
|
|
594
|
+
}
|
|
595
|
+
return input;
|
|
596
|
+
}
|
|
597
|
+
function renderOptions(ui, options) {
|
|
598
|
+
const entries = [];
|
|
599
|
+
const groups = new Map();
|
|
600
|
+
for (const option of options) {
|
|
601
|
+
if (!option.group) {
|
|
602
|
+
entries.push(option);
|
|
603
|
+
continue;
|
|
604
|
+
}
|
|
605
|
+
const key = `${typeof option.group.value}:${String(option.group.value)}`;
|
|
606
|
+
const existing = groups.get(key);
|
|
607
|
+
if (existing) {
|
|
608
|
+
existing.options.push(option);
|
|
609
|
+
continue;
|
|
610
|
+
}
|
|
611
|
+
const entry = { group: option.group, options: [option] };
|
|
612
|
+
groups.set(key, entry);
|
|
613
|
+
entries.push(entry);
|
|
614
|
+
}
|
|
615
|
+
return entries.map((entry) => {
|
|
616
|
+
if ('options' in entry) {
|
|
617
|
+
return ui.render(SCHEMA_UI_OPTION_GROUP, { label: entry.group.label }, { default: () => entry.options.map((option) => renderOption(ui, option)) });
|
|
618
|
+
}
|
|
619
|
+
return renderOption(ui, entry);
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
function renderOption(ui, option) {
|
|
623
|
+
return ui.render(SCHEMA_UI_OPTION, {
|
|
624
|
+
label: option.label,
|
|
625
|
+
value: option.value,
|
|
626
|
+
disabled: option.disabled,
|
|
627
|
+
...option.props,
|
|
628
|
+
}, option.description
|
|
629
|
+
? {
|
|
630
|
+
default: () => [
|
|
631
|
+
h('span', option.label),
|
|
632
|
+
h('span', {
|
|
633
|
+
style: {
|
|
634
|
+
marginLeft: '8px',
|
|
635
|
+
color: 'var(--el-text-color-secondary)',
|
|
636
|
+
fontSize: 'var(--el-font-size-extra-small)',
|
|
637
|
+
},
|
|
638
|
+
}, option.description),
|
|
639
|
+
],
|
|
640
|
+
}
|
|
641
|
+
: undefined);
|
|
642
|
+
}
|
|
643
|
+
function renderRadioOptions(ui, options) {
|
|
644
|
+
return options.map((option) => ui.render(SCHEMA_UI_RADIO, {
|
|
645
|
+
label: option.value,
|
|
646
|
+
value: option.value,
|
|
647
|
+
disabled: option.disabled,
|
|
648
|
+
...option.props,
|
|
649
|
+
}, {
|
|
650
|
+
default: () => option.label,
|
|
651
|
+
}));
|
|
652
|
+
}
|
|
653
|
+
function renderCheckboxOptions(ui, options) {
|
|
654
|
+
return options.map((option) => ui.render(SCHEMA_UI_CHECKBOX, {
|
|
655
|
+
label: option.value,
|
|
656
|
+
value: option.value,
|
|
657
|
+
disabled: option.disabled,
|
|
658
|
+
...option.props,
|
|
659
|
+
}, {
|
|
660
|
+
default: () => option.label,
|
|
661
|
+
}));
|
|
662
|
+
}
|
|
663
|
+
function toTreeSelectOptions(options, lazy = false) {
|
|
664
|
+
return options.map((option) => ({
|
|
665
|
+
value: option.value,
|
|
666
|
+
label: option.label,
|
|
667
|
+
disabled: option.disabled,
|
|
668
|
+
parent_value: option.parent_value,
|
|
669
|
+
has_children: option.has_children,
|
|
670
|
+
path_labels: option.path_labels,
|
|
671
|
+
meta: option.meta,
|
|
672
|
+
isLeaf: lazy && option.has_children !== undefined ? !option.has_children : undefined,
|
|
673
|
+
...option.props,
|
|
674
|
+
children: option.children ? toTreeSelectOptions(option.children, lazy) : undefined,
|
|
675
|
+
}));
|
|
676
|
+
}
|
|
677
|
+
function toCascaderOptions(options) {
|
|
678
|
+
return options.map((option) => ({
|
|
679
|
+
value: option.value,
|
|
680
|
+
label: option.label,
|
|
681
|
+
disabled: option.disabled,
|
|
682
|
+
...option.props,
|
|
683
|
+
children: option.children ? toCascaderOptions(option.children) : undefined,
|
|
684
|
+
}));
|
|
685
|
+
}
|
|
686
|
+
function resolveControlOptions(input) {
|
|
687
|
+
return (input.options ?? []).map(toElementPlusOption);
|
|
688
|
+
}
|
|
689
|
+
function toElementPlusOption(option) {
|
|
690
|
+
return {
|
|
691
|
+
label: option.label,
|
|
692
|
+
value: option.value,
|
|
693
|
+
disabled: option.disabled,
|
|
694
|
+
description: option.description,
|
|
695
|
+
group: option.group,
|
|
696
|
+
children: option.children?.map(toElementPlusOption),
|
|
697
|
+
parent_value: option.parent_value,
|
|
698
|
+
has_children: option.has_children,
|
|
699
|
+
path_labels: option.path_labels,
|
|
700
|
+
meta: option.meta,
|
|
701
|
+
props: option.props,
|
|
702
|
+
};
|
|
703
|
+
}
|
|
704
|
+
//# sourceMappingURL=field.js.map
|