@tmagic/form 1.0.0-beta.1

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 (94) hide show
  1. package/dist/style.css +161 -0
  2. package/dist/tmagic-form.es.js +3749 -0
  3. package/dist/tmagic-form.es.js.map +1 -0
  4. package/dist/tmagic-form.umd.js +3790 -0
  5. package/dist/tmagic-form.umd.js.map +1 -0
  6. package/dist/types/src/Form.vue.d.ts +304 -0
  7. package/dist/types/src/FormDialog.vue.d.ts +641 -0
  8. package/dist/types/src/containers/Container.vue.d.ts +69 -0
  9. package/dist/types/src/containers/Fieldset.vue.d.ts +56 -0
  10. package/dist/types/src/containers/GroupList.vue.d.ts +56 -0
  11. package/dist/types/src/containers/GroupListItem.vue.d.ts +72 -0
  12. package/dist/types/src/containers/Panel.vue.d.ts +41 -0
  13. package/dist/types/src/containers/Row.vue.d.ts +42 -0
  14. package/dist/types/src/containers/Step.vue.d.ts +45 -0
  15. package/dist/types/src/containers/Table.vue.d.ts +1401 -0
  16. package/dist/types/src/containers/Tabs.vue.d.ts +45 -0
  17. package/dist/types/src/fields/Cascader.vue.d.ts +1748 -0
  18. package/dist/types/src/fields/Checkbox.vue.d.ts +59 -0
  19. package/dist/types/src/fields/CheckboxGroup.vue.d.ts +56 -0
  20. package/dist/types/src/fields/Date.vue.d.ts +58 -0
  21. package/dist/types/src/fields/DateTime.vue.d.ts +57 -0
  22. package/dist/types/src/fields/Daterange.vue.d.ts +57 -0
  23. package/dist/types/src/fields/Display.vue.d.ts +3 -0
  24. package/dist/types/src/fields/DynamicField.vue.d.ts +76 -0
  25. package/dist/types/src/fields/Hidden.vue.d.ts +3 -0
  26. package/dist/types/src/fields/Link.vue.d.ts +1365 -0
  27. package/dist/types/src/fields/Number.vue.d.ts +59 -0
  28. package/dist/types/src/fields/RadioGroup.vue.d.ts +55 -0
  29. package/dist/types/src/fields/Select.vue.d.ts +975 -0
  30. package/dist/types/src/fields/Switch.vue.d.ts +59 -0
  31. package/dist/types/src/fields/Text.vue.d.ts +61 -0
  32. package/dist/types/src/fields/Textarea.vue.d.ts +56 -0
  33. package/dist/types/src/fields/Time.vue.d.ts +54 -0
  34. package/dist/types/src/index.d.ts +36 -0
  35. package/dist/types/src/schema.d.ts +495 -0
  36. package/dist/types/src/shims-vue.d.ts +6 -0
  37. package/dist/types/src/utils/config.d.ts +3 -0
  38. package/dist/types/src/utils/fieldProps.d.ts +21 -0
  39. package/dist/types/src/utils/form.d.ts +8 -0
  40. package/dist/types/src/utils/useAddField.d.ts +1 -0
  41. package/dist/types/src/vite-env.d.ts +1 -0
  42. package/package.json +51 -0
  43. package/src/Form.vue +188 -0
  44. package/src/FormDialog.vue +163 -0
  45. package/src/containers/Container.vue +263 -0
  46. package/src/containers/Fieldset.vue +138 -0
  47. package/src/containers/GroupList.vue +152 -0
  48. package/src/containers/GroupListItem.vue +159 -0
  49. package/src/containers/Panel.vue +106 -0
  50. package/src/containers/Row.vue +68 -0
  51. package/src/containers/Step.vue +82 -0
  52. package/src/containers/Table.vue +624 -0
  53. package/src/containers/Tabs.vue +176 -0
  54. package/src/fields/Cascader.vue +147 -0
  55. package/src/fields/Checkbox.vue +67 -0
  56. package/src/fields/CheckboxGroup.vue +44 -0
  57. package/src/fields/ColorPicker.vue +38 -0
  58. package/src/fields/Date.vue +51 -0
  59. package/src/fields/DateTime.vue +59 -0
  60. package/src/fields/Daterange.vue +75 -0
  61. package/src/fields/Display.vue +34 -0
  62. package/src/fields/DynamicField.vue +109 -0
  63. package/src/fields/Hidden.vue +29 -0
  64. package/src/fields/Link.vue +112 -0
  65. package/src/fields/Number.vue +53 -0
  66. package/src/fields/RadioGroup.vue +33 -0
  67. package/src/fields/Select.vue +393 -0
  68. package/src/fields/Switch.vue +65 -0
  69. package/src/fields/Text.vue +134 -0
  70. package/src/fields/Textarea.vue +62 -0
  71. package/src/fields/Time.vue +48 -0
  72. package/src/index.ts +127 -0
  73. package/src/schema.ts +638 -0
  74. package/src/shims-vue.d.ts +6 -0
  75. package/src/theme/date-time.scss +7 -0
  76. package/src/theme/fieldset.scss +24 -0
  77. package/src/theme/form-dialog.scss +13 -0
  78. package/src/theme/form.scss +30 -0
  79. package/src/theme/group-list.scss +23 -0
  80. package/src/theme/index.scss +9 -0
  81. package/src/theme/link.scss +3 -0
  82. package/src/theme/panel.scss +18 -0
  83. package/src/theme/select.scss +3 -0
  84. package/src/theme/table.scss +69 -0
  85. package/src/theme/tabs.scss +25 -0
  86. package/src/utils/config.ts +27 -0
  87. package/src/utils/containerProps.ts +50 -0
  88. package/src/utils/createForm.ts +25 -0
  89. package/src/utils/fieldProps.ts +39 -0
  90. package/src/utils/form.ts +266 -0
  91. package/src/utils/useAddField.ts +40 -0
  92. package/src/vite-env.d.ts +1 -0
  93. package/tsconfig.json +9 -0
  94. package/vite.config.ts +27 -0
@@ -0,0 +1,624 @@
1
+ <template>
2
+ <div ref="mTable" class="m-fields-table" :class="{ 'm-fields-table-item-extra': config.itemExtra }">
3
+ <span v-if="config.extra" style="color: rgba(0, 0, 0, 0.45)" v-html="config.extra"></span>
4
+ <div class="el-form-item__content">
5
+ <el-tooltip content="拖拽可排序" placement="left-start" :disabled="config.dropSort !== true">
6
+ <el-table
7
+ v-if="model[modelName]"
8
+ ref="elTable"
9
+ style="width: 100%"
10
+ :data="model[modelName]"
11
+ :border="config.border"
12
+ :max-height="config.maxHeight"
13
+ :default-expand-all="true"
14
+ :key="updateKey"
15
+ @select="selectHandle"
16
+ @sort-change="sortChange"
17
+ >
18
+ <el-table-column v-if="config.itemExtra" :fixed="'left'" width="30" type="expand">
19
+ <template v-slot="scope">
20
+ <span v-html="itemExtra(config.itemExtra, scope.$index)" class="m-form-tip"></span>
21
+ </template>
22
+ </el-table-column>
23
+
24
+ <el-table-column label="操作" width="50" :fixed="config.fixed === false ? undefined : 'left'">
25
+ <template v-slot="scope">
26
+ <el-button
27
+ v-show="showDelete(scope.$index + 1 + pagecontext * pagesize - 1)"
28
+ style="color: #f56c6c"
29
+ type="text"
30
+ :icon="Delete"
31
+ @click="removeHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
32
+ ></el-button>
33
+ </template>
34
+ </el-table-column>
35
+
36
+ <el-table-column v-if="sort && model[modelName] && model[modelName].length > 1" label="排序" width="70">
37
+ <template v-slot="scope">
38
+ <el-tooltip
39
+ content="点击上移,双击置顶"
40
+ placement="top"
41
+ v-if="scope.$index + 1 + pagecontext * pagesize - 1 !== 0"
42
+ >
43
+ <el-button
44
+ plain
45
+ size="small"
46
+ :icon="ArrowUp"
47
+ type="text"
48
+ @click="upHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
49
+ @dblclick="topHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
50
+ ></el-button>
51
+ </el-tooltip>
52
+ <el-tooltip
53
+ content="点击下移,双击置底"
54
+ placement="top"
55
+ v-if="scope.$index + 1 + pagecontext * pagesize - 1 !== model[modelName].length - 1"
56
+ >
57
+ <el-button
58
+ plain
59
+ size="small"
60
+ :icon="ArrowDown"
61
+ type="text"
62
+ @click="downHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
63
+ @dblclick="bottomHandler(scope.$index + 1 + pagecontext * pagesize - 1)"
64
+ ></el-button>
65
+ </el-tooltip>
66
+ </template>
67
+ </el-table-column>
68
+
69
+ <el-table-column
70
+ v-if="selection"
71
+ align="center"
72
+ header-align="center"
73
+ type="selection"
74
+ width="45"
75
+ ></el-table-column>
76
+
77
+ <el-table-column width="50" label="序号">
78
+ <template v-slot="scope">{{ scope.$index + 1 + pagecontext * pagesize }}</template>
79
+ </el-table-column>
80
+
81
+ <template v-for="(column, index) in config.items">
82
+ <el-table-column
83
+ v-if="column.type !== 'hidden' && display(column.display)"
84
+ :prop="column.name"
85
+ :width="column.width"
86
+ :label="column.label"
87
+ :sortable="column.sortable"
88
+ :sort-orders="['ascending', 'descending']"
89
+ :key="column[mForm?.keyProp || '__key'] ?? index"
90
+ :class-name="config.dropSort === true ? 'el-table__column--dropable' : ''"
91
+ >
92
+ <template #default="scope">
93
+ <m-form-container
94
+ v-if="scope.$index > -1"
95
+ labelWidth="0"
96
+ :prop="getProp(scope.$index)"
97
+ :rules="column.rules"
98
+ :config="makeConfig(column)"
99
+ :model="scope.row"
100
+ :size="size"
101
+ @change="$emit('change', model[modelName])"
102
+ ></m-form-container>
103
+ </template>
104
+ </el-table-column>
105
+ </template>
106
+ </el-table>
107
+ </el-tooltip>
108
+ <slot></slot>
109
+ <el-button v-if="addable" size="small" type="primary" plain @click="newHandler()">添加</el-button> &nbsp;
110
+ <el-button icon="el-icon-s-grid" size="small" @click="toggleMode" v-if="enableToggleMode && !isFullscreen"
111
+ >展开配置</el-button
112
+ >
113
+ <el-button icon="el-icon-s-grid" size="small" @click="toggleFullscreen" v-if="config.enableFullscreen !== false">
114
+ {{ isFullscreen ? '退出全屏' : '全屏编辑' }}
115
+ </el-button>
116
+ <el-upload
117
+ v-if="importable"
118
+ ref="excelBtn"
119
+ action="/noop"
120
+ :on-change="excelHandler"
121
+ :auto-upload="false"
122
+ style="display: inline-block"
123
+ >
124
+ <el-button size="small" type="success" plain>导入EXCEL</el-button> </el-upload
125
+ >&nbsp;
126
+ <el-button v-if="importable" size="small" type="warning" plain @click="clearHandler()">清空</el-button>
127
+ </div>
128
+
129
+ <div class="bottom" style="text-align: right">
130
+ <el-pagination
131
+ layout="total, sizes, prev, pager, next, jumper"
132
+ :hide-on-single-page="model[modelName].length < pagesize"
133
+ :current-page="pagecontext + 1"
134
+ :page-sizes="[pagesize, 60, 120, 300]"
135
+ :page-size="pagesize"
136
+ :total="model[modelName].length"
137
+ @size-change="handleSizeChange"
138
+ @current-change="handleCurrentChange"
139
+ >
140
+ </el-pagination>
141
+ </div>
142
+ </div>
143
+ </template>
144
+
145
+ <script lang="ts">
146
+ /* eslint-disable no-param-reassign */
147
+ import { computed, defineComponent, inject, onMounted, PropType, ref, toRefs } from 'vue';
148
+ import { ArrowDown, ArrowUp, Delete } from '@element-plus/icons';
149
+ import { ElMessage, ElTable, ElUpload } from 'element-plus';
150
+ import { cloneDeep } from 'lodash-es';
151
+ import Sortable, { SortableEvent } from 'sortablejs';
152
+
153
+ import { asyncLoadJs, sleep } from '@tmagic/utils';
154
+
155
+ import { ColumnConfig, FormState, SortProp, TableConfig } from '../schema';
156
+ import { display, initValue } from '../utils/form';
157
+
158
+ export type UploadStatus = 'ready' | 'uploading' | 'success' | 'fail';
159
+
160
+ export interface ElFile extends File {
161
+ uid: number;
162
+ }
163
+
164
+ export type UploadFile = {
165
+ name: string;
166
+ percentage?: number;
167
+ status: UploadStatus;
168
+ size: number;
169
+ response?: unknown;
170
+ uid: number;
171
+ url?: string;
172
+ raw: ElFile;
173
+ };
174
+
175
+ let loadedAMapJS = false; // 是否加载完js
176
+ let firstLoadingAMapJS = true; // 否是第一次请求
177
+
178
+ export default defineComponent({
179
+ name: 'm-form-table',
180
+
181
+ props: {
182
+ name: {
183
+ type: String,
184
+ required: true,
185
+ },
186
+
187
+ model: {
188
+ type: Object,
189
+ required: true,
190
+ default: () => ({}),
191
+ },
192
+
193
+ config: {
194
+ type: Object as PropType<TableConfig>,
195
+ required: true,
196
+ default: () => ({}),
197
+ },
198
+
199
+ prop: {
200
+ type: String,
201
+ },
202
+
203
+ labelWidth: {
204
+ type: String,
205
+ },
206
+
207
+ sort: {
208
+ type: Boolean,
209
+ default: false,
210
+ },
211
+
212
+ sortKey: {
213
+ type: String,
214
+ default: '',
215
+ },
216
+
217
+ text: {
218
+ type: String,
219
+ },
220
+
221
+ size: String,
222
+
223
+ enableToggleMode: {
224
+ type: Boolean,
225
+ default: true,
226
+ },
227
+ },
228
+
229
+ emits: ['change', 'select'],
230
+
231
+ setup(props, { emit }) {
232
+ let timer: NodeJS.Timeout | null = null;
233
+ const mForm = inject<FormState | undefined>('mForm');
234
+
235
+ const elTable = ref<InstanceType<typeof ElTable>>();
236
+ const excelBtn = ref<InstanceType<typeof ElUpload>>();
237
+ const mTable = ref<HTMLDivElement>();
238
+
239
+ const pagesize = ref(10);
240
+ const pagecontext = ref(0);
241
+ const updateKey = ref(1);
242
+ const isFullscreen = ref(false);
243
+
244
+ const modelName = computed(() => props.name || props.config.name || '');
245
+
246
+ const sortChange = ({ prop, order }: SortProp) => {
247
+ if (order === 'ascending') {
248
+ props.model[modelName.value] = props.model[modelName.value].sort((a: any, b: any) => a[prop] - b[prop]);
249
+ } else if (order === 'descending') {
250
+ props.model[modelName.value] = props.model[modelName.value].sort((a: any, b: any) => b[prop] - a[prop]);
251
+ }
252
+ };
253
+
254
+ const foreUpdate = () => {
255
+ updateKey.value += 1;
256
+ setTimeout(() => rowDrop());
257
+ };
258
+
259
+ const swapArray = (index1: number, index2: number) => {
260
+ [props.model[modelName.value][index1]] = props.model[modelName.value].splice(
261
+ index2,
262
+ 1,
263
+ props.model[modelName.value][index1],
264
+ );
265
+ if (props.sortKey) {
266
+ for (let i = props.model[modelName.value].length - 1, v = 0; i >= 0; i--, v++) {
267
+ props.model[modelName.value][v][props.sortKey] = i;
268
+ }
269
+ }
270
+ mForm?.$emit('field-change', props.prop, props.model[modelName.value]);
271
+ };
272
+
273
+ const rowDrop = () => {
274
+ const tableEl = elTable.value?.$el;
275
+ if (tableEl?.querySelectorAll('.el-table__body > tbody')) {
276
+ // eslint-disable-next-line prefer-destructuring
277
+ const el = tableEl.querySelectorAll('.el-table__body > tbody')[0];
278
+ const sortable = Sortable.create(el, {
279
+ onEnd: ({ newIndex, oldIndex }: SortableEvent) => {
280
+ if (typeof newIndex === 'undefined') return;
281
+ if (typeof oldIndex === 'undefined') return;
282
+ swapArray(newIndex, oldIndex);
283
+ emit('change', props.model[modelName.value]);
284
+ foreUpdate();
285
+ sortable.destroy();
286
+ mForm?.$emit('field-change', props.prop, props.model[modelName.value]);
287
+ sleep(1000).then(() => {
288
+ const eltrs = tableEl.querySelectorAll('.el-table__body > tbody > tr');
289
+ if (eltrs?.[newIndex]) {
290
+ eltrs[newIndex].style.backgroundColor = 'rgba(243, 89, 59, 0.2)';
291
+ sleep(1000).then(() => {
292
+ eltrs[newIndex].style.backgroundColor = '';
293
+ });
294
+ }
295
+ });
296
+ },
297
+ });
298
+ }
299
+ };
300
+
301
+ const newHandler = async (row?: any) => {
302
+ if (props.config.max && props.model[modelName.value].length >= props.config.max) {
303
+ ElMessage.error(`最多新增配置不能超过${props.config.max}条`);
304
+ return;
305
+ }
306
+
307
+ const columns = props.config.items;
308
+ const enumValues = props.config.enum || [];
309
+ let enumV = [];
310
+ const { length } = props.model[modelName.value];
311
+ const key = props.config.key || 'id';
312
+ let inputs: any = {};
313
+
314
+ if (enumValues.length) {
315
+ if (length >= enumValues.length) {
316
+ return;
317
+ }
318
+ enumV = enumValues.filter((item) => {
319
+ let i = 0;
320
+ for (; i < length; i++) {
321
+ if (item[key] === props.model[modelName.value][i][key]) {
322
+ break;
323
+ }
324
+ }
325
+ return i === length;
326
+ });
327
+
328
+ if (enumV.length > 0) {
329
+ // eslint-disable-next-line prefer-destructuring
330
+ inputs = enumV[0];
331
+ }
332
+ } else if (Array.isArray(row)) {
333
+ columns.forEach((column, index) => {
334
+ column.name && (inputs[column.name] = row[index]);
335
+ });
336
+ } else {
337
+ if (typeof props.config.defaultAdd === 'function') {
338
+ inputs = await props.config.defaultAdd(mForm, {
339
+ model: props.model[modelName.value],
340
+ formValue: mForm?.values,
341
+ });
342
+ } else if (props.config.defaultAdd) {
343
+ inputs = props.config.defaultAdd;
344
+ }
345
+
346
+ inputs = await initValue(mForm, {
347
+ config: columns,
348
+ initValues: inputs,
349
+ });
350
+ }
351
+
352
+ if (props.sortKey && length) {
353
+ inputs[props.sortKey] = props.model[modelName.value][length - 1][props.sortKey] - 1;
354
+ }
355
+
356
+ props.model[modelName.value].push(inputs);
357
+ emit('change', props.model[modelName.value]);
358
+ };
359
+
360
+ if (!loadedAMapJS && firstLoadingAMapJS && !(globalThis as any).XLSX) {
361
+ firstLoadingAMapJS = false; // 马上置为false 只让第一个created组件去请求js
362
+ asyncLoadJs('https://cdn.bootcdn.net/ajax/libs/xlsx/0.17.0/xlsx.full.min.js')
363
+ .then(() => {
364
+ loadedAMapJS = true;
365
+ })
366
+ .catch(() => {
367
+ firstLoadingAMapJS = true; // 出错置为true 下次进来还是能重新请求js
368
+ });
369
+ }
370
+
371
+ onMounted(() => {
372
+ if (props.config.defautSort) {
373
+ sortChange(props.config.defautSort);
374
+ }
375
+
376
+ if (props.sort && props.sortKey) {
377
+ props.model[modelName.value].sort((a: any, b: any) => b[props.sortKey] - a[props.sortKey]);
378
+ }
379
+
380
+ if (props.config.dropSort) {
381
+ rowDrop();
382
+ }
383
+ });
384
+
385
+ return {
386
+ mForm,
387
+ pagesize,
388
+ pagecontext,
389
+ foreUpdate,
390
+ isFullscreen,
391
+ elTable,
392
+ mTable,
393
+ excelBtn,
394
+ modelName,
395
+ updateKey,
396
+ ArrowDown,
397
+ ArrowUp,
398
+ Delete,
399
+
400
+ data: computed(() =>
401
+ props.model[modelName.value].filter(
402
+ (item: any, index: number) =>
403
+ index >= pagecontext.value * pagesize.value && index + 1 <= (pagecontext.value + 1) * pagesize.value,
404
+ ),
405
+ ),
406
+ addable: computed(() => {
407
+ if (!props.model[modelName.value].length) {
408
+ return true;
409
+ }
410
+ if (typeof props.config.addable === 'function') {
411
+ return props.config.addable(mForm, {
412
+ model: props.model[modelName.value],
413
+ formValue: mForm?.values,
414
+ prop: props.prop,
415
+ });
416
+ }
417
+ return typeof props.config.addable === 'undefined' ? true : props.config.addable;
418
+ }),
419
+ selection: computed(() => {
420
+ if (typeof props.config.selection === 'function') {
421
+ return props.config.selection(mForm, { model: props.model[modelName.value] });
422
+ }
423
+ return props.config.selection;
424
+ }),
425
+ importable: computed(() => {
426
+ if (typeof props.config.importable === 'function') {
427
+ return props.config.importable(mForm, {
428
+ formValue: mForm?.values,
429
+ model: props.model[modelName.value],
430
+ });
431
+ }
432
+ return typeof props.config.importable === 'undefined' ? false : props.config.importable;
433
+ }),
434
+
435
+ sortChange,
436
+
437
+ display(fuc: any) {
438
+ return display(mForm, fuc, props);
439
+ },
440
+
441
+ itemExtra(fuc: any, index: number) {
442
+ if (typeof fuc === 'function') {
443
+ return fuc(mForm, {
444
+ values: mForm?.initValues,
445
+ model: props.model,
446
+ formValue: mForm ? mForm.values : props.model,
447
+ prop: props.prop,
448
+ index,
449
+ });
450
+ }
451
+
452
+ return fuc;
453
+ },
454
+
455
+ newHandler,
456
+
457
+ removeHandler(index: number) {
458
+ props.model[modelName.value].splice(index, 1);
459
+ emit('change', props.model[modelName.value]);
460
+ },
461
+
462
+ selectHandle(selection: any, row: any) {
463
+ if (typeof props.config.selection === 'string' && props.config.selection === 'single') {
464
+ elTable.value?.clearSelection();
465
+ elTable.value?.toggleRowSelection(row, true);
466
+ }
467
+ emit('select', selection, row);
468
+ if (typeof props.config.onSelect === 'function') {
469
+ props.config.onSelect(mForm, { selection, row });
470
+ }
471
+ },
472
+
473
+ toggleRowSelection(row: any, selected: boolean) {
474
+ elTable.value?.toggleRowSelection.call(elTable.value, row, selected);
475
+ },
476
+
477
+ makeConfig(config: ColumnConfig) {
478
+ const newConfig = cloneDeep(config);
479
+ delete newConfig.display;
480
+ return newConfig;
481
+ },
482
+
483
+ upHandler(index: number) {
484
+ if (timer) {
485
+ clearTimeout(timer);
486
+ }
487
+
488
+ timer = setTimeout(() => {
489
+ swapArray(index, index - 1);
490
+ }, 300);
491
+ },
492
+
493
+ topHandler(index: number) {
494
+ if (timer) {
495
+ clearTimeout(timer);
496
+ }
497
+
498
+ // 首先判断当前元素需要上移几个位置,置底移动到数组的第一位
499
+ const moveNum = index;
500
+
501
+ // 循环出需要一个一个上移的次数
502
+ for (let i = 0; i < moveNum; i++) {
503
+ swapArray(index, index - 1);
504
+ index -= 1;
505
+ }
506
+ },
507
+
508
+ downHandler(index: number) {
509
+ if (timer) {
510
+ clearTimeout(timer);
511
+ }
512
+
513
+ timer = setTimeout(() => {
514
+ swapArray(index, index + 1);
515
+ }, 300);
516
+ },
517
+
518
+ bottomHandler(index: number) {
519
+ if (timer) {
520
+ clearTimeout(timer);
521
+ }
522
+
523
+ // 首先判断当前元素需要上移几个位置,置底移动到数组的第一位
524
+ const moveNum = props.model[modelName.value].length - 1 - index;
525
+
526
+ // 循环出需要一个一个上移的次数
527
+ for (let i = 0; i < moveNum; i++) {
528
+ swapArray(index, index + 1);
529
+ index += 1;
530
+ }
531
+ },
532
+
533
+ // 希望支持单行可控制是否显示删除按钮,不会影响现有逻辑
534
+ showDelete(index: number) {
535
+ const deleteFunc = props.config.delete;
536
+ if (deleteFunc && typeof deleteFunc === 'function') {
537
+ return deleteFunc(props.model[modelName.value], index, mForm?.values);
538
+ }
539
+ return true;
540
+ },
541
+
542
+ clearHandler() {
543
+ const len = props.model[modelName.value].length;
544
+ props.model[modelName.value].splice(0, len);
545
+ mForm?.$emit('field-change', props.prop, props.model[modelName.value]);
546
+ },
547
+
548
+ excelHandler(file: UploadFile) {
549
+ if (!file || !file.raw) {
550
+ return false;
551
+ }
552
+ const reader = new FileReader();
553
+ reader.onload = () => {
554
+ const data = reader.result;
555
+ const pdata = (globalThis as any).XLSX.read(data, { type: 'array' });
556
+ pdata.SheetNames.forEach((sheetName: string) => {
557
+ const arr = (globalThis as any).XLSX.utils.sheet_to_json(pdata.Sheets[sheetName], { header: 1 });
558
+ if (arr?.[0]) {
559
+ arr.forEach((row: any) => {
560
+ newHandler(row);
561
+ });
562
+ }
563
+ setTimeout(() => {
564
+ excelBtn.value?.clearFiles();
565
+ }, 300);
566
+ });
567
+ };
568
+ reader.readAsArrayBuffer(file.raw);
569
+
570
+ return false;
571
+ },
572
+
573
+ handleSizeChange(val: number) {
574
+ pagesize.value = val;
575
+ },
576
+
577
+ handleCurrentChange(val: number) {
578
+ pagecontext.value = val - 1;
579
+ },
580
+
581
+ toggleMode() {
582
+ const calcLabelWidth = (label: string) => {
583
+ if (!label) return '0px';
584
+ const zhLength = label.match(/[^\x00-\xff]/g)?.length || 0;
585
+ const chLength = label.length - zhLength;
586
+ return `${Math.max(chLength * 8 + zhLength * 20, 80)}px`;
587
+ };
588
+
589
+ // 切换为groupList的形式
590
+ props.config.type = 'groupList';
591
+ props.config.enableToggleMode = true;
592
+ props.config.tableItems = props.config.items;
593
+ props.config.items =
594
+ props.config.groupItems ||
595
+ props.config.items.map((item: any) => {
596
+ const text = item.text || item.label;
597
+ const labelWidth = calcLabelWidth(text);
598
+ return {
599
+ ...item,
600
+ text,
601
+ labelWidth,
602
+ span: item.span || 12,
603
+ };
604
+ });
605
+ },
606
+
607
+ toggleFullscreen() {
608
+ if (isFullscreen.value) {
609
+ mTable.value?.classList.remove('fixed');
610
+ isFullscreen.value = false;
611
+ } else {
612
+ mTable.value?.classList.add('fixed');
613
+ isFullscreen.value = true;
614
+ }
615
+ },
616
+
617
+ getProp(index: number) {
618
+ const { prop } = toRefs(props);
619
+ return `${prop.value}${prop.value ? '.' : ''}${index + 1 + pagecontext.value * pagesize.value - 1}`;
620
+ },
621
+ };
622
+ },
623
+ });
624
+ </script>