@tmagic/form 1.4.8 → 1.4.9
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/package.json +5 -5
- package/src/Form.vue +201 -0
- package/src/FormBox.vue +120 -0
- package/src/FormDialog.vue +173 -0
- package/src/FormDrawer.vue +159 -0
- package/src/containers/Col.vue +52 -0
- package/src/containers/Container.vue +408 -0
- package/src/containers/Fieldset.vue +124 -0
- package/src/containers/GroupList.vue +139 -0
- package/src/containers/GroupListItem.vue +135 -0
- package/src/containers/Panel.vue +99 -0
- package/src/containers/Row.vue +54 -0
- package/src/containers/Step.vue +82 -0
- package/src/containers/Table.vue +648 -0
- package/src/containers/Tabs.vue +226 -0
- package/src/fields/Cascader.vue +131 -0
- package/src/fields/Checkbox.vue +58 -0
- package/src/fields/CheckboxGroup.vue +43 -0
- package/src/fields/ColorPicker.vue +30 -0
- package/src/fields/Date.vue +38 -0
- package/src/fields/DateTime.vue +47 -0
- package/src/fields/Daterange.vue +99 -0
- package/src/fields/Display.vue +21 -0
- package/src/fields/DynamicField.vue +90 -0
- package/src/fields/Hidden.vue +16 -0
- package/src/fields/Link.vue +86 -0
- package/src/fields/Number.vue +49 -0
- package/src/fields/NumberRange.vue +50 -0
- package/src/fields/RadioGroup.vue +28 -0
- package/src/fields/Select.vue +449 -0
- package/src/fields/Switch.vue +59 -0
- package/src/fields/Text.vue +170 -0
- package/src/fields/Textarea.vue +46 -0
- package/src/fields/Time.vue +34 -0
- package/src/fields/Timerange.vue +76 -0
- package/src/index.ts +139 -0
- package/src/schema.ts +757 -0
- package/src/shims-vue.d.ts +6 -0
- package/src/theme/date-time.scss +7 -0
- package/src/theme/fieldset.scss +28 -0
- package/src/theme/form-box.scss +13 -0
- package/src/theme/form-dialog.scss +13 -0
- package/src/theme/form-drawer.scss +11 -0
- package/src/theme/form.scss +43 -0
- package/src/theme/group-list.scss +23 -0
- package/src/theme/index.scss +14 -0
- package/src/theme/link.scss +3 -0
- package/src/theme/number-range.scss +8 -0
- package/src/theme/panel.scss +24 -0
- package/src/theme/select.scss +3 -0
- package/src/theme/table.scss +70 -0
- package/src/theme/tabs.scss +27 -0
- package/src/theme/text.scss +6 -0
- package/src/utils/config.ts +27 -0
- package/src/utils/containerProps.ts +50 -0
- package/src/utils/form.ts +268 -0
- package/src/utils/useAddField.ts +40 -0
|
@@ -0,0 +1,449 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TMagicSelect
|
|
3
|
+
v-if="model"
|
|
4
|
+
v-model="model[name]"
|
|
5
|
+
v-loading="loading"
|
|
6
|
+
class="m-select"
|
|
7
|
+
ref="tMagicSelect"
|
|
8
|
+
:clearable="typeof config.clearable !== 'undefined' ? config.clearable : true"
|
|
9
|
+
:filterable="typeof config.filterable !== 'undefined' ? config.filterable : true"
|
|
10
|
+
:popper-class="`m-select-popper ${popperClass}`"
|
|
11
|
+
:size="size"
|
|
12
|
+
:remote="remote"
|
|
13
|
+
:placeholder="config.placeholder"
|
|
14
|
+
:multiple="config.multiple"
|
|
15
|
+
:value-key="config.valueKey || 'value'"
|
|
16
|
+
:allow-create="config.allowCreate"
|
|
17
|
+
:disabled="disabled"
|
|
18
|
+
:remote-method="config.remote && remoteMethod"
|
|
19
|
+
@change="changeHandler"
|
|
20
|
+
@visible-change="visibleHandler"
|
|
21
|
+
>
|
|
22
|
+
<template v-if="config.group">
|
|
23
|
+
<component
|
|
24
|
+
v-for="(group, index) in (options as SelectGroupOption[])"
|
|
25
|
+
:key="index"
|
|
26
|
+
:is="optionGroupComponent?.component || 'el-option-group'"
|
|
27
|
+
v-bind="
|
|
28
|
+
optionGroupComponent?.props({
|
|
29
|
+
label: group.label,
|
|
30
|
+
disabled: group.disabled,
|
|
31
|
+
}) || {
|
|
32
|
+
label: group.label,
|
|
33
|
+
disabled: group.disabled,
|
|
34
|
+
}
|
|
35
|
+
"
|
|
36
|
+
>
|
|
37
|
+
<component
|
|
38
|
+
v-for="(item, index) in group.options"
|
|
39
|
+
:is="optionComponent?.component || 'el-option'"
|
|
40
|
+
:key="index"
|
|
41
|
+
v-bind="
|
|
42
|
+
optionComponent?.props({
|
|
43
|
+
label: item.label || item.text,
|
|
44
|
+
value: item.value,
|
|
45
|
+
disabled: item.disabled,
|
|
46
|
+
}) || {
|
|
47
|
+
label: item.label || item.text,
|
|
48
|
+
value: item.value,
|
|
49
|
+
disabled: item.disabled,
|
|
50
|
+
}
|
|
51
|
+
"
|
|
52
|
+
>
|
|
53
|
+
</component>
|
|
54
|
+
</component>
|
|
55
|
+
</template>
|
|
56
|
+
<template v-else>
|
|
57
|
+
<component
|
|
58
|
+
v-for="option in (options as SelectOption[])"
|
|
59
|
+
class="tmagic-design-option"
|
|
60
|
+
:key="config.valueKey ? option.value[config.valueKey] : option.value"
|
|
61
|
+
:is="optionComponent?.component || 'el-option'"
|
|
62
|
+
v-bind="
|
|
63
|
+
optionComponent?.props({
|
|
64
|
+
label: option.text,
|
|
65
|
+
value: option.value,
|
|
66
|
+
disabled: option.disabled,
|
|
67
|
+
}) || {
|
|
68
|
+
label: option.text,
|
|
69
|
+
value: option.value,
|
|
70
|
+
disabled: option.disabled,
|
|
71
|
+
}
|
|
72
|
+
"
|
|
73
|
+
>
|
|
74
|
+
</component>
|
|
75
|
+
</template>
|
|
76
|
+
<div v-loading="true" v-if="moreLoadingVisible"></div>
|
|
77
|
+
</TMagicSelect>
|
|
78
|
+
</template>
|
|
79
|
+
|
|
80
|
+
<script lang="ts" setup>
|
|
81
|
+
import { inject, nextTick, onBeforeMount, ref, watch, watchEffect } from 'vue';
|
|
82
|
+
|
|
83
|
+
import { getConfig as getDesignConfig, TMagicSelect } from '@tmagic/design';
|
|
84
|
+
import { getValueByKeyPath } from '@tmagic/utils';
|
|
85
|
+
|
|
86
|
+
import type { FieldProps, FormState, SelectConfig, SelectGroupOption, SelectOption } from '../schema';
|
|
87
|
+
import { getConfig } from '../utils/config';
|
|
88
|
+
import { useAddField } from '../utils/useAddField';
|
|
89
|
+
|
|
90
|
+
defineOptions({
|
|
91
|
+
name: 'MFormSelect',
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const props = defineProps<FieldProps<SelectConfig>>();
|
|
95
|
+
|
|
96
|
+
const emit = defineEmits(['change']);
|
|
97
|
+
|
|
98
|
+
const optionComponent = getDesignConfig('components')?.option;
|
|
99
|
+
const optionGroupComponent = getDesignConfig('components')?.optionGroup;
|
|
100
|
+
|
|
101
|
+
if (!props.model) throw new Error('不能没有model');
|
|
102
|
+
useAddField(props.prop);
|
|
103
|
+
|
|
104
|
+
const tMagicSelect = ref<InstanceType<typeof TMagicSelect>>();
|
|
105
|
+
const mForm = inject<FormState | undefined>('mForm');
|
|
106
|
+
const options = ref<SelectOption[] | SelectGroupOption[]>([]);
|
|
107
|
+
const localOptions = ref<SelectOption[] | SelectGroupOption[]>([]);
|
|
108
|
+
const loading = ref(false);
|
|
109
|
+
const moreLoadingVisible = ref(false);
|
|
110
|
+
const total = ref(0);
|
|
111
|
+
const pgIndex = ref(0);
|
|
112
|
+
const pgSize = ref(20);
|
|
113
|
+
const query = ref('');
|
|
114
|
+
const remoteData = ref<any[]>([]);
|
|
115
|
+
const remote = ref(true);
|
|
116
|
+
|
|
117
|
+
const equalValue = (value: any, v: any): boolean => {
|
|
118
|
+
if (typeof v === 'object') {
|
|
119
|
+
const key = props.config.valueKey || 'value';
|
|
120
|
+
return v[key] === value[key];
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return value === v;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const mapOptions = (data: any[]) => {
|
|
127
|
+
const {
|
|
128
|
+
option = {
|
|
129
|
+
text: 'text',
|
|
130
|
+
value: 'value',
|
|
131
|
+
},
|
|
132
|
+
} = props.config;
|
|
133
|
+
const { text = 'text', value = 'value' } = option;
|
|
134
|
+
|
|
135
|
+
return data.map((item) => ({
|
|
136
|
+
text: typeof text === 'function' ? text(item) : item[text],
|
|
137
|
+
value: typeof value === 'function' ? value(item) : item[value],
|
|
138
|
+
}));
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
const getOptions = async () => {
|
|
142
|
+
if (!props.model) return [];
|
|
143
|
+
|
|
144
|
+
if (localOptions.value.length) {
|
|
145
|
+
return localOptions.value;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
loading.value = true;
|
|
149
|
+
|
|
150
|
+
let items: SelectOption[] | SelectGroupOption[] = [];
|
|
151
|
+
|
|
152
|
+
const { option } = props.config;
|
|
153
|
+
|
|
154
|
+
if (!option) return [];
|
|
155
|
+
|
|
156
|
+
const { root = '', totalKey = 'total' } = option;
|
|
157
|
+
let { body = {}, url } = option;
|
|
158
|
+
|
|
159
|
+
if (typeof url === 'function') {
|
|
160
|
+
url = await url(mForm, { model: props.model, formValue: mForm?.values });
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
let postOptions: Record<string, any> = {
|
|
164
|
+
method: option.method || 'POST',
|
|
165
|
+
url,
|
|
166
|
+
cache: option.cache,
|
|
167
|
+
timeout: option.timeout,
|
|
168
|
+
mode: option.mode,
|
|
169
|
+
headers: option.headers || {},
|
|
170
|
+
json: option.json || false,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
if (typeof body === 'function') {
|
|
174
|
+
body = body(mForm, {
|
|
175
|
+
model: props.model,
|
|
176
|
+
formValue: mForm?.values,
|
|
177
|
+
formValues: mForm?.values,
|
|
178
|
+
config: props.config,
|
|
179
|
+
}) as Record<string, any>;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
body.query = query.value;
|
|
183
|
+
body.pgSize = pgSize.value;
|
|
184
|
+
body.pgIndex = pgIndex.value;
|
|
185
|
+
|
|
186
|
+
postOptions.data = body;
|
|
187
|
+
|
|
188
|
+
const requestFuc = getConfig('request') as Function;
|
|
189
|
+
|
|
190
|
+
if (typeof option.beforeRequest === 'function') {
|
|
191
|
+
postOptions = option.beforeRequest(mForm, postOptions, {
|
|
192
|
+
model: props.model,
|
|
193
|
+
formValue: mForm?.values,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (option.method?.toLocaleLowerCase() === 'jsonp') {
|
|
198
|
+
postOptions.jsonpCallback = option.jsonpCallback || 'callback';
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let res = await requestFuc(postOptions);
|
|
202
|
+
|
|
203
|
+
if (typeof option.afterRequest === 'function') {
|
|
204
|
+
res = option.afterRequest(mForm, res, {
|
|
205
|
+
model: props.model,
|
|
206
|
+
formValue: mForm?.values,
|
|
207
|
+
formValues: mForm?.values,
|
|
208
|
+
config: props.config,
|
|
209
|
+
postOptions,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const optionsData = getValueByKeyPath(root, res);
|
|
214
|
+
|
|
215
|
+
const resTotal = globalThis.parseInt(getValueByKeyPath(totalKey, res), 10);
|
|
216
|
+
if (resTotal > 0) {
|
|
217
|
+
total.value = resTotal;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
remoteData.value = remoteData.value.concat(optionsData);
|
|
221
|
+
if (optionsData) {
|
|
222
|
+
if (typeof option.item === 'function') {
|
|
223
|
+
items = option.item(optionsData);
|
|
224
|
+
} else if (optionsData.map) {
|
|
225
|
+
items = mapOptions(optionsData);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
loading.value = false;
|
|
230
|
+
|
|
231
|
+
// 多选过滤时会导致已选的选项显示不了,所以要把已选的选项保留不要过滤没了
|
|
232
|
+
const selectedOptions: SelectOption[] | SelectGroupOption[] = [];
|
|
233
|
+
if (props.config.multiple && props.model[props.name]) {
|
|
234
|
+
options.value.forEach((o: any) => {
|
|
235
|
+
const isInclude = props.model?.[props.name].includes(o.value);
|
|
236
|
+
if (isInclude && !(items as any[]).find((op: any) => op.value === o.value)) {
|
|
237
|
+
selectedOptions.push(o);
|
|
238
|
+
}
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
return pgIndex.value === 0 ? (selectedOptions as any[]).concat(items) : (options.value as any).concat(items);
|
|
243
|
+
};
|
|
244
|
+
|
|
245
|
+
const getInitLocalOption = async () => {
|
|
246
|
+
if (!props.model) return [];
|
|
247
|
+
|
|
248
|
+
const value = props.model[props.name];
|
|
249
|
+
const { config } = props;
|
|
250
|
+
localOptions.value = await getOptions();
|
|
251
|
+
|
|
252
|
+
remote.value = false;
|
|
253
|
+
|
|
254
|
+
if (config.group) {
|
|
255
|
+
if (config.multiple && value.findIndex) {
|
|
256
|
+
return (localOptions.value as SelectGroupOption[]).filter(
|
|
257
|
+
(group) => group.options.findIndex((item) => value.find((v: any) => equalValue(item.value, v)) > -1) > -1,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
return (localOptions.value as SelectGroupOption[]).filter(
|
|
262
|
+
(group) => group.options.findIndex((item) => equalValue(item.value, value)) > -1,
|
|
263
|
+
);
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (config.multiple && value.findIndex) {
|
|
267
|
+
return (localOptions.value as any[]).filter((item) => value.findIndex((v: any) => equalValue(item.value, v)) > -1);
|
|
268
|
+
}
|
|
269
|
+
return (localOptions.value as any[]).filter((item) => equalValue(item.value, value));
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
const getInitOption = async () => {
|
|
273
|
+
if (!props.model) return [];
|
|
274
|
+
|
|
275
|
+
const { option } = props.config;
|
|
276
|
+
|
|
277
|
+
if (!option) return [];
|
|
278
|
+
|
|
279
|
+
const { root = '', initRoot = '' } = option;
|
|
280
|
+
let { initBody = {} } = option;
|
|
281
|
+
|
|
282
|
+
let options: SelectOption[] | SelectGroupOption[] = [];
|
|
283
|
+
|
|
284
|
+
let url = option.initUrl;
|
|
285
|
+
if (!url) {
|
|
286
|
+
return getInitLocalOption();
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (typeof url === 'function') {
|
|
290
|
+
url = await url(mForm, { model: props.model, formValue: mForm?.values });
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (typeof initBody === 'function') {
|
|
294
|
+
initBody = initBody(mForm, {
|
|
295
|
+
model: props.model,
|
|
296
|
+
formValue: mForm?.values,
|
|
297
|
+
formValues: mForm?.values,
|
|
298
|
+
config: props.config,
|
|
299
|
+
}) as Record<string, any>;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
let postOptions: Record<string, any> = {
|
|
303
|
+
method: option.method || 'POST',
|
|
304
|
+
url,
|
|
305
|
+
data: {
|
|
306
|
+
id: props.model[props.name],
|
|
307
|
+
...initBody,
|
|
308
|
+
},
|
|
309
|
+
mode: option.mode,
|
|
310
|
+
headers: option.headers || {},
|
|
311
|
+
json: option.json || false,
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
if (typeof option.beforeInitRequest === 'function') {
|
|
315
|
+
postOptions = option.beforeInitRequest(mForm, postOptions, {
|
|
316
|
+
model: props.model,
|
|
317
|
+
formValue: mForm?.values,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (option.method?.toLocaleLowerCase() === 'jsonp') {
|
|
322
|
+
postOptions.jsonpCallback = option.jsonpCallback || 'callback';
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const requestFuc = getConfig('request') as Function;
|
|
326
|
+
let res = await requestFuc(postOptions);
|
|
327
|
+
|
|
328
|
+
if (typeof option.afterRequest === 'function') {
|
|
329
|
+
res = option.afterRequest(mForm, res, {
|
|
330
|
+
model: props.model,
|
|
331
|
+
formValue: mForm?.values,
|
|
332
|
+
formValues: mForm?.values,
|
|
333
|
+
config: props.config,
|
|
334
|
+
postOptions,
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
let initData = getValueByKeyPath(initRoot || root, res);
|
|
339
|
+
if (initData) {
|
|
340
|
+
if (!Array.isArray(initData)) {
|
|
341
|
+
initData = [initData];
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (typeof option.item === 'function') {
|
|
345
|
+
options = option.item(initData);
|
|
346
|
+
} else if (initData.map) {
|
|
347
|
+
options = mapOptions(initData);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return options;
|
|
352
|
+
};
|
|
353
|
+
|
|
354
|
+
if (typeof props.config.options === 'function') {
|
|
355
|
+
watchEffect(() => {
|
|
356
|
+
typeof props.config.options === 'function' &&
|
|
357
|
+
Promise.resolve(
|
|
358
|
+
props.config.options(mForm, {
|
|
359
|
+
model: props.model,
|
|
360
|
+
prop: props.prop,
|
|
361
|
+
formValues: mForm?.values,
|
|
362
|
+
formValue: mForm?.values,
|
|
363
|
+
config: props.config,
|
|
364
|
+
}),
|
|
365
|
+
).then((data) => {
|
|
366
|
+
options.value = data;
|
|
367
|
+
});
|
|
368
|
+
});
|
|
369
|
+
} else if (Array.isArray(props.config.options)) {
|
|
370
|
+
watchEffect(() => {
|
|
371
|
+
options.value = props.config.options as SelectOption[] | SelectGroupOption[];
|
|
372
|
+
});
|
|
373
|
+
} else if (props.config.option) {
|
|
374
|
+
onBeforeMount(() => {
|
|
375
|
+
if (!props.model) return;
|
|
376
|
+
const v = props.model[props.name];
|
|
377
|
+
if (Array.isArray(v) ? v.length : typeof v !== 'undefined') {
|
|
378
|
+
getInitOption().then((data) => {
|
|
379
|
+
options.value = data;
|
|
380
|
+
});
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
if (props.config.remote) {
|
|
386
|
+
const unWatch = watch(
|
|
387
|
+
() => tMagicSelect.value?.scrollbarWrap,
|
|
388
|
+
(scrollbarWrap) => {
|
|
389
|
+
if (!scrollbarWrap) {
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
nextTick(() => unWatch());
|
|
394
|
+
|
|
395
|
+
scrollbarWrap.addEventListener('scroll', async (e: Event) => {
|
|
396
|
+
const el = e.currentTarget as HTMLDivElement;
|
|
397
|
+
if (moreLoadingVisible.value) {
|
|
398
|
+
return;
|
|
399
|
+
}
|
|
400
|
+
if (el.scrollHeight - el.clientHeight - el.scrollTop > 1) {
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
403
|
+
if (total.value <= options.value.length) {
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
moreLoadingVisible.value = true;
|
|
407
|
+
pgIndex.value += 1;
|
|
408
|
+
options.value = await getOptions();
|
|
409
|
+
moreLoadingVisible.value = false;
|
|
410
|
+
});
|
|
411
|
+
},
|
|
412
|
+
{
|
|
413
|
+
immediate: true,
|
|
414
|
+
},
|
|
415
|
+
);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
const popperClass = mForm?.popperClass;
|
|
419
|
+
|
|
420
|
+
const changeHandler = (value: any) => {
|
|
421
|
+
emit('change', value);
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const visibleHandler = async (visible: boolean) => {
|
|
425
|
+
if (!visible) return;
|
|
426
|
+
|
|
427
|
+
if (!props.config.remote) return;
|
|
428
|
+
if (query.value && tMagicSelect.value) {
|
|
429
|
+
tMagicSelect.value.setQuery(query.value);
|
|
430
|
+
tMagicSelect.value.setPreviousQuery(query.value);
|
|
431
|
+
tMagicSelect.value.setSelectedLabel(query.value);
|
|
432
|
+
} else if (options.value.length <= (props.config.multiple ? props.model?.[props.name].length : 1)) {
|
|
433
|
+
options.value = await getOptions();
|
|
434
|
+
}
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
const remoteMethod = async (q: string) => {
|
|
438
|
+
if (!localOptions.value.length) {
|
|
439
|
+
query.value = q;
|
|
440
|
+
pgIndex.value = 0;
|
|
441
|
+
options.value = await getOptions();
|
|
442
|
+
// 多选时如果过滤选项会导致已选好的标签异常,需要重新刷新一下el-select的状态
|
|
443
|
+
if (props.config.multiple)
|
|
444
|
+
setTimeout(() => {
|
|
445
|
+
tMagicSelect.value?.setSelected();
|
|
446
|
+
}, 0);
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
</script>
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TMagicSwitch
|
|
3
|
+
v-model="model[name]"
|
|
4
|
+
:size="size"
|
|
5
|
+
:activeValue="activeValue"
|
|
6
|
+
:inactiveValue="inactiveValue"
|
|
7
|
+
:disabled="disabled"
|
|
8
|
+
@change="changeHandler"
|
|
9
|
+
></TMagicSwitch>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<script lang="ts" setup>
|
|
13
|
+
import { computed } from 'vue';
|
|
14
|
+
|
|
15
|
+
import { TMagicSwitch } from '@tmagic/design';
|
|
16
|
+
|
|
17
|
+
import type { FieldProps, SwitchConfig } from '../schema';
|
|
18
|
+
import { useAddField } from '../utils/useAddField';
|
|
19
|
+
|
|
20
|
+
defineOptions({
|
|
21
|
+
name: 'MFormSwitch',
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const props = defineProps<FieldProps<SwitchConfig>>();
|
|
25
|
+
|
|
26
|
+
const emit = defineEmits<{
|
|
27
|
+
change: [value: any];
|
|
28
|
+
}>();
|
|
29
|
+
|
|
30
|
+
useAddField(props.prop);
|
|
31
|
+
|
|
32
|
+
const changeHandler = (value: any) => {
|
|
33
|
+
emit('change', value);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const activeValue = computed(() => {
|
|
37
|
+
if (typeof props.config.activeValue === 'undefined') {
|
|
38
|
+
if (props.config.filter === 'number') {
|
|
39
|
+
return 1;
|
|
40
|
+
}
|
|
41
|
+
} else {
|
|
42
|
+
return props.config.activeValue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return true;
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const inactiveValue = computed(() => {
|
|
49
|
+
if (typeof props.config.inactiveValue === 'undefined') {
|
|
50
|
+
if (props.config.filter === 'number') {
|
|
51
|
+
return 0;
|
|
52
|
+
}
|
|
53
|
+
} else {
|
|
54
|
+
return props.config.inactiveValue;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return false;
|
|
58
|
+
});
|
|
59
|
+
</script>
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<TMagicPopover :visible="popoverVisible" width="220px">
|
|
3
|
+
<template #reference>
|
|
4
|
+
<TMagicInput
|
|
5
|
+
v-model="model[name]"
|
|
6
|
+
clearable
|
|
7
|
+
:size="size"
|
|
8
|
+
:placeholder="config.placeholder"
|
|
9
|
+
:disabled="disabled"
|
|
10
|
+
@change="changeHandler"
|
|
11
|
+
@input="inputHandler"
|
|
12
|
+
@keyup="keyUpHandler($event)"
|
|
13
|
+
>
|
|
14
|
+
<template #append v-if="appendConfig">
|
|
15
|
+
<TMagicButton
|
|
16
|
+
v-if="appendConfig.type === 'button'"
|
|
17
|
+
style="color: #409eff"
|
|
18
|
+
:size="size"
|
|
19
|
+
@click.prevent="buttonClickHandler"
|
|
20
|
+
>
|
|
21
|
+
{{ appendConfig.text }}
|
|
22
|
+
</TMagicButton>
|
|
23
|
+
</template>
|
|
24
|
+
</TMagicInput>
|
|
25
|
+
</template>
|
|
26
|
+
|
|
27
|
+
<div class="m-form-item__content">
|
|
28
|
+
<div class="m-form-validate__warning">输入内容前后有空格,是否移除空格?</div>
|
|
29
|
+
<div style="display: flex; justify-content: flex-end">
|
|
30
|
+
<TMagicButton link size="small" @click="popoverVisible = false">保持原样</TMagicButton>
|
|
31
|
+
<TMagicButton type="primary" size="small" @click="confirmTrimHandler">移除空格</TMagicButton>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
</TMagicPopover>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script lang="ts" setup>
|
|
38
|
+
import { computed, inject, ref } from 'vue';
|
|
39
|
+
|
|
40
|
+
import { TMagicButton, TMagicInput, TMagicPopover } from '@tmagic/design';
|
|
41
|
+
import { isNumber } from '@tmagic/utils';
|
|
42
|
+
|
|
43
|
+
import type { FieldProps, FormState, TextConfig } from '../schema';
|
|
44
|
+
import { useAddField } from '../utils/useAddField';
|
|
45
|
+
|
|
46
|
+
defineOptions({
|
|
47
|
+
name: 'MFormText',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const props = defineProps<FieldProps<TextConfig>>();
|
|
51
|
+
|
|
52
|
+
const emit = defineEmits<{
|
|
53
|
+
change: [value: string];
|
|
54
|
+
input: [value: string];
|
|
55
|
+
}>();
|
|
56
|
+
|
|
57
|
+
useAddField(props.prop);
|
|
58
|
+
|
|
59
|
+
const mForm = inject<FormState | undefined>('mForm');
|
|
60
|
+
|
|
61
|
+
const appendConfig = computed(() => {
|
|
62
|
+
if (typeof props.config.append === 'string') {
|
|
63
|
+
return {
|
|
64
|
+
text: props.config.append,
|
|
65
|
+
type: 'button',
|
|
66
|
+
handler: undefined,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (props.config.append && typeof props.config.append === 'object') {
|
|
71
|
+
if (props.config.append.value === 0) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return props.config.append;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return false;
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
const popoverVisible = ref(false);
|
|
82
|
+
|
|
83
|
+
const confirmTrimHandler = () => {
|
|
84
|
+
emit('change', props.model[props.name].trim() || '');
|
|
85
|
+
popoverVisible.value = false;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const checkWhiteSpace = (value: unknown) => {
|
|
89
|
+
if (typeof value === 'string' && !props.config.trim) {
|
|
90
|
+
popoverVisible.value = value.trim() !== value;
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
const changeHandler = (value: string) => {
|
|
95
|
+
emit('change', value);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const inputHandler = (v: string) => {
|
|
99
|
+
checkWhiteSpace(v);
|
|
100
|
+
emit('input', v);
|
|
101
|
+
mForm?.$emit('field-input', props.prop, v);
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const buttonClickHandler = () => {
|
|
105
|
+
if (!appendConfig.value) return;
|
|
106
|
+
if (typeof appendConfig.value.handler === 'function') {
|
|
107
|
+
appendConfig.value.handler(mForm, {
|
|
108
|
+
model: props.model,
|
|
109
|
+
values: mForm?.values,
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
};
|
|
113
|
+
|
|
114
|
+
const keyUpHandler = ($event: KeyboardEvent) => {
|
|
115
|
+
if (!props.model) return;
|
|
116
|
+
if (!props.name) return;
|
|
117
|
+
|
|
118
|
+
const arrowUp = $event.key === 'ArrowUp';
|
|
119
|
+
const arrowDown = $event.key === 'ArrowDown';
|
|
120
|
+
|
|
121
|
+
if (!arrowUp && !arrowDown) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const value = props.model[props.name];
|
|
126
|
+
let num;
|
|
127
|
+
let unit;
|
|
128
|
+
if (isNumber(value)) {
|
|
129
|
+
num = +value;
|
|
130
|
+
} else {
|
|
131
|
+
value.replace(/^([0-9.]+)([a-z%]+)$/, ($0: string, $1: string, $2: string) => {
|
|
132
|
+
num = +$1;
|
|
133
|
+
unit = $2;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (num === undefined) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const ctrl = navigator.platform.match('Mac') ? $event.metaKey : $event.ctrlKey;
|
|
142
|
+
const shift = $event.shiftKey;
|
|
143
|
+
const alt = $event.altKey;
|
|
144
|
+
|
|
145
|
+
if (arrowUp) {
|
|
146
|
+
if (ctrl) {
|
|
147
|
+
num += 100;
|
|
148
|
+
} else if (alt) {
|
|
149
|
+
num = (num * 10000 + 1000) / 10000;
|
|
150
|
+
} else if (shift) {
|
|
151
|
+
num = num + 10;
|
|
152
|
+
} else {
|
|
153
|
+
num += 1;
|
|
154
|
+
}
|
|
155
|
+
} else if (arrowDown) {
|
|
156
|
+
if (ctrl) {
|
|
157
|
+
num -= 100;
|
|
158
|
+
} else if (alt) {
|
|
159
|
+
num = (num * 10000 - 1000) / 10000;
|
|
160
|
+
} else if (shift) {
|
|
161
|
+
num -= 10;
|
|
162
|
+
} else {
|
|
163
|
+
num -= 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
props.model[props.name] = `${num}${unit || ''}`;
|
|
168
|
+
emit('change', props.model[props.name]);
|
|
169
|
+
};
|
|
170
|
+
</script>
|