gy-ui-plus 1.0.6 → 1.0.8

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 (38) hide show
  1. package/README.md +3 -3
  2. package/dist/gy-ui-plus.es.js +1764 -1839
  3. package/dist/gy-ui-plus.umd.js +2 -2
  4. package/package.json +12 -11
  5. package/packages/button/src/index.vue +41 -0
  6. package/packages/button/src/type.ts +11 -0
  7. package/packages/index.ts +5 -3
  8. package/packages/layout-page/index.ts +4 -0
  9. package/packages/layout-page/src/index.vue +75 -0
  10. package/packages/layout-page/style/index.ts +1 -0
  11. package/packages/layout-page/style/layout-page.scss +37 -0
  12. package/packages/table/index.ts +5 -0
  13. package/packages/table/src/GyTableColumn.vue +100 -0
  14. package/packages/table/src/index.vue +1073 -0
  15. package/packages/table/src/operator.vue +203 -0
  16. package/packages/table/src/renderCol.vue +20 -0
  17. package/packages/table/src/renderHeader.vue +18 -0
  18. package/packages/table/src/singleEdit.vue +352 -0
  19. package/packages/table/src/singleEditCell.vue +301 -0
  20. package/packages/table/src/tableProps.ts +146 -0
  21. package/packages/{gy-table → table}/src/useExpose.ts +15 -15
  22. package/packages/table/style/index.ts +1 -0
  23. package/packages/table/style/table.scss +348 -0
  24. package/dist/gy-ui-plus.css +0 -1
  25. package/packages/gy-button/src/index.vue +0 -29
  26. package/packages/gy-table/index.ts +0 -8
  27. package/packages/gy-table/src/TTableColumn.vue +0 -140
  28. package/packages/gy-table/src/index.vue +0 -1205
  29. package/packages/gy-table/src/operator.vue +0 -246
  30. package/packages/gy-table/src/renderCol.vue +0 -24
  31. package/packages/gy-table/src/renderHeader.vue +0 -18
  32. package/packages/gy-table/src/singleEdit.vue +0 -406
  33. package/packages/gy-table/src/singleEditCell.vue +0 -337
  34. package/packages/gy-table/src/tableProps.ts +0 -174
  35. package/packages/gy-table/style/table.scss +0 -368
  36. /package/packages/{gy-button → button}/index.ts +0 -0
  37. /package/packages/{gy-table → table}/src/ColumnSet.vue +0 -0
  38. /package/packages/{gy-table → table}/src/useVirtualized.ts +0 -0
@@ -1,1205 +0,0 @@
1
- <template>
2
- <div
3
- class="t-table"
4
- ref="TTableBox"
5
- v-loading="tableLoading"
6
- :element-loading-text="loadingTxt"
7
- >
8
- <div
9
- class="header_wrap"
10
- :style="{
11
- paddingBottom:
12
- tableTitle ||
13
- title ||
14
- isShow('title') ||
15
- isShow('toolbar') ||
16
- isSlotToolbar ||
17
- columnSetting
18
- ? '10px'
19
- : 0,
20
- }"
21
- >
22
- <div
23
- class="header_title"
24
- v-if="tableTitle || title || $slots.title || isSlotTitle"
25
- >
26
- <template v-if="$slots.title || isSlotTitle">
27
- <slot name="title" />
28
- </template>
29
- <template v-else>
30
- <span v-if="tableTitle">{{ tableTitle }}</span>
31
- <span v-else>{{ title }}</span>
32
- </template>
33
- </div>
34
- <div class="toolbar_top">
35
- <!-- 表格外操作 -->
36
- <slot name="toolbar"></slot>
37
- <!--列设置按钮-->
38
- <div
39
- class="header_right_wrap"
40
- :style="{
41
- marginLeft: isShow('toolbar') || isSlotToolbar ? '12px' : 0,
42
- }"
43
- >
44
- <slot name="btn" />
45
- <column-set
46
- v-if="columnSetting && !isTableHeader"
47
- v-bind="$attrs"
48
- :title="title || tableTitle"
49
- :columns="renderColumns"
50
- ref="columnSetRef"
51
- @columnSetting="(v) => (state.columnSet = v)"
52
- />
53
- </div>
54
- </div>
55
- </div>
56
- <div class="title-tip" v-if="isShow('titleTip')">
57
- <slot name="titleTip" />
58
- </div>
59
- <el-table
60
- ref="TTable"
61
- :data="state.tableData"
62
- :class="{
63
- cursor: isCopy,
64
- row_sort: isRowSort,
65
- row_sort_none: isRowSortIcon,
66
- tree_style: isTree,
67
- highlightCurrentRow: highlightCurrentRow,
68
- radioStyle: radioStyleClass,
69
- multile_head_column: isTableHeader,
70
- t_table_use_virtual: useVirtual,
71
- }"
72
- v-bind="$attrs"
73
- height="100%"
74
- :highlight-current-row="highlightCurrentRow"
75
- :border="border || table.border || isTableBorder || useVirtual"
76
- @cell-dblclick="cellDblclick"
77
- @row-click="rowClick"
78
- >
79
- <el-table-column
80
- v-if="isRowSortIcon"
81
- v-bind="{
82
- width: rowSortIconBind.width || 55,
83
- 'min-width': rowSortIconBind['min-width'] || rowSortIconBind.minWidth,
84
- label: rowSortIconBind.label || '拖动',
85
- fixed: rowSortIconBind.fixed,
86
- align: rowSortIconBind.align || align,
87
- ...rowSortIconBind,
88
- }"
89
- >
90
- <template #default>
91
- <el-icon
92
- class="row_drag"
93
- :color="rowSortIconBind.color"
94
- :size="rowSortIconBind.size"
95
- >
96
- <Rank />
97
- </el-icon>
98
- </template>
99
- </el-table-column>
100
- <!-- 复选框/单选框/序列号 -->
101
- <template v-if="!Array.isArray(table.firstColumn) && table.firstColumn">
102
- <!-- 复选框 -->
103
- <el-table-column
104
- v-if="table.firstColumn.type === 'selection'"
105
- v-bind="{
106
- type: 'selection',
107
- width: table.firstColumn.width || 55,
108
- label: table.firstColumn.label,
109
- fixed: table.firstColumn.fixed,
110
- align: table.firstColumn.align || align,
111
- 'reserve-selection': table.firstColumn.isPaging || false,
112
- selectable: table.firstColumn.selectable,
113
- ...table.firstColumn.bind,
114
- }"
115
- />
116
- <el-table-column
117
- v-else
118
- v-bind="{
119
- type: table.firstColumn.type,
120
- width: table.firstColumn.width || 55,
121
- label:
122
- table.firstColumn.label ||
123
- (table.firstColumn.type === 'radio' && '单选') ||
124
- (table.firstColumn.type === 'index' && '序号') ||
125
- (table.firstColumn.type === 'expand' && '') ||
126
- '',
127
- fixed: table.firstColumn.fixed,
128
- align: table.firstColumn.align || align,
129
- ...table.firstColumn.bind,
130
- }"
131
- >
132
- <template
133
- #default="scope"
134
- v-if="table.firstColumn.type !== 'selection'"
135
- >
136
- <el-radio
137
- v-if="table.firstColumn.type === 'radio'"
138
- v-model="radioVal"
139
- :label="scope.$index + 1"
140
- :disabled="scope.row.isRadioDisabled"
141
- @click="radioHandleChange(scope.row, scope.$index + 1)"
142
- ></el-radio>
143
- <template v-if="table.firstColumn.type === 'index'">
144
- <span v-if="isPaginationCumulative && isShowPagination">{{
145
- (paginationData.pageNo - 1) * paginationData.pageSize +
146
- scope.$index +
147
- 1
148
- }}</span>
149
- <span v-else>{{ scope.$index + 1 }}</span>
150
- </template>
151
- <template v-if="table.firstColumn.type === 'expand'">
152
- <slot name="expand" :scope="scope"></slot>
153
- </template>
154
- </template>
155
- </el-table-column>
156
- </template>
157
- <template v-if="Array.isArray(table.firstColumn)">
158
- <template v-for="(item, index) in table.firstColumn">
159
- <!-- 复选框 -->
160
- <el-table-column
161
- :key="index + 1"
162
- v-if="item.type === 'selection'"
163
- v-bind="{
164
- type: 'selection',
165
- width: item.width || 55,
166
- label: item.label,
167
- fixed: item.fixed,
168
- align: item.align || align,
169
- 'reserve-selection': item.isPaging || false,
170
- selectable: item.selectable,
171
- ...item.bind,
172
- }"
173
- />
174
- <el-table-column
175
- v-else
176
- :key="index + 'k'"
177
- v-bind="{
178
- type: item.type,
179
- width: item.width || 55,
180
- label:
181
- item.label ||
182
- (item.type === 'radio' && '单选') ||
183
- (item.type === 'index' && '序号') ||
184
- (item.type === 'expand' && '') ||
185
- '',
186
- fixed: item.fixed,
187
- align: item.align || align,
188
- ...item.bind,
189
- }"
190
- >
191
- <template #default="scope" v-if="item.type !== 'selection'">
192
- <el-radio
193
- v-if="item.type === 'radio'"
194
- v-model="radioVal"
195
- :label="scope.$index + 1"
196
- :disabled="scope.row.isRadioDisabled"
197
- @click="radioHandleChange(scope.row, scope.$index + 1)"
198
- ></el-radio>
199
- <template v-if="item.type === 'index'">
200
- <span v-if="isPaginationCumulative && isShowPagination">{{
201
- (paginationData.pageNo - 1) * paginationData.pageSize +
202
- scope.$index +
203
- 1
204
- }}</span>
205
- <span v-else>{{ scope.$index + 1 }}</span>
206
- </template>
207
- <template v-if="item.type === 'expand'">
208
- <slot name="expand" :scope="scope"></slot>
209
- </template>
210
- </template>
211
- </el-table-column>
212
- </template>
213
- </template>
214
- <!-- 主体内容 -->
215
- <template v-for="(item, index) in renderColumns">
216
- <template v-if="!item.children">
217
- <!-- 常规列 -->
218
- <el-table-column
219
- v-if="
220
- typeof item.isShowCol == 'function'
221
- ? item.isShowCol(item)
222
- : !item.isShowCol
223
- "
224
- :key="index + 'i'"
225
- :type="item.type"
226
- :label="item.label"
227
- :prop="item.prop"
228
- :min-width="item['min-width'] || item.minWidth"
229
- :width="item.width"
230
- :sortable="item.sortable || item.sort || sortable"
231
- :align="item.align || align"
232
- :fixed="item.fixed"
233
- :formatter="item.formatter"
234
- v-bind="
235
- typeof item.bind == 'function'
236
- ? item.bind(item)
237
- : {
238
- 'show-overflow-tooltip': true,
239
- ...item.bind,
240
- }
241
- "
242
- >
243
- <template
244
- #header
245
- v-if="
246
- item.headerRequired ||
247
- item.renderHeader ||
248
- item.isClickEdit ||
249
- item.isToolTip
250
- "
251
- >
252
- <render-header
253
- v-if="item.renderHeader"
254
- :column="item"
255
- :render="item.renderHeader"
256
- />
257
- <div
258
- style="display: inline"
259
- v-if="item.headerRequired && emptyDataRequired"
260
- >
261
- <span style="color: #f56c6c; font-size: 16px; margin-right: 3px"
262
- >*</span
263
- >
264
- <span>{{ item.label }}</span>
265
- </div>
266
- <div
267
- v-if="item.isClickEdit"
268
- class="click_edit"
269
- :style="{
270
- justifyContent: item.editIconAlign || align || 'center',
271
- }"
272
- >
273
- <span>{{ item.label }}</span>
274
- <el-icon
275
- v-if="!item.isShowEditIcon"
276
- v-bind="{ size: 14, ...item.editIconBind }"
277
- >
278
- <Edit />
279
- </el-icon>
280
- </div>
281
- <div
282
- v-if="item.isToolTip"
283
- class="click_edit"
284
- :style="{
285
- justifyContent: item.editIconAlign || align || 'center',
286
- }"
287
- >
288
- <span>{{ item.label }}</span>
289
- <el-tooltip
290
- v-if="item.toolTip"
291
- class="box-item"
292
- effect="dark"
293
- :content="item.toolTip"
294
- placement="top"
295
- >
296
- <el-icon
297
- v-bind="{
298
- size: 14,
299
- ...item.editIconBind,
300
- }"
301
- >
302
- <QuestionFilled />
303
- </el-icon>
304
- </el-tooltip>
305
- </div>
306
- </template>
307
- <template #default="scope">
308
- <!-- render渲染 -->
309
- <template v-if="item.render">
310
- <render-col
311
- :column="item"
312
- :row="scope.row"
313
- :render="item.render"
314
- :index="scope.$index"
315
- />
316
- </template>
317
- <!-- 自定义插槽 -->
318
- <template v-if="item.slotName">
319
- <slot :name="item.slotName" :scope="scope"></slot>
320
- </template>
321
- <!-- 单个单元格编辑 -->
322
- <template v-if="item.canEdit">
323
- <el-form
324
- :model="state.tableData[scope.$index]"
325
- :rules="isEditRules ? table.rules : {}"
326
- class="t_edit_cell_form"
327
- :class="{
328
- t_edit_cell_form_rules: isEditRules,
329
- }"
330
- :ref="(el:any) => handleRef(el, scope,item)"
331
- @submit.prevent
332
- :disabled="isDisabledForm"
333
- >
334
- <single-edit-cell
335
- :configEdit="item.configEdit"
336
- v-model="scope.row[item.prop]"
337
- :prop="item.prop"
338
- :scope="scope"
339
- :indexColumns="index"
340
- :ref="(el:any) => handleEditTableRef(el, scope,item)"
341
- @handleEvent="handleEvent($event, scope.$index)"
342
- @keyup-handle="handleKeyup"
343
- v-bind="$attrs"
344
- >
345
- <template
346
- v-for="(_index, name) in slots"
347
- v-slot:[name]="data"
348
- >
349
- <slot :name="name" v-bind="data"></slot>
350
- </template>
351
- </single-edit-cell>
352
- </el-form>
353
- </template>
354
- <!-- 单击单元格编辑 -->
355
- <template v-if="item.isClickEdit">
356
- <single-edit
357
- :isClickEdit="item.isClickEdit"
358
- :configEdit="item.configEdit"
359
- v-model="scope.row[scope.column.property]"
360
- v-bind="$attrs"
361
- ref="editClickCell"
362
- >
363
- <template
364
- v-for="(_index, name) in slots"
365
- v-slot:[name]="data"
366
- >
367
- <slot :name="name" v-bind="data"></slot>
368
- </template>
369
- </single-edit>
370
- </template>
371
- <!-- 字典过滤 -->
372
- <template v-if="item.filters && item.filters.list">
373
- {{
374
- constantEscape(
375
- scope.row[item.prop],
376
- table.listTypeInfo[item.filters.list],
377
- item.filters.key || "value",
378
- item.filters.label || "label"
379
- )
380
- }}
381
- </template>
382
- <div
383
- v-if="
384
- !item.render &&
385
- !item.slotName &&
386
- !item.canEdit &&
387
- !item.filters &&
388
- !item.isClickEdit &&
389
- !item.formatter
390
- "
391
- >
392
- <span>{{ scope.row[item.prop] }}</span>
393
- </div>
394
- </template>
395
- </el-table-column>
396
- </template>
397
- <!-- 表头合并单元格 -->
398
- <t-table-column
399
- v-else
400
- :key="index + 'm'"
401
- :item="item"
402
- :align="align"
403
- v-bind="$attrs"
404
- :sortable="sortable"
405
- >
406
- <template v-for="(_index, name) in slots" v-slot:[name]="data">
407
- <slot :name="name" v-bind="data"></slot>
408
- </template>
409
- </t-table-column>
410
- </template>
411
- <slot></slot>
412
- <Operator
413
- :table="table"
414
- :btnPermissions="btnPermissions"
415
- :tableData="state.tableData"
416
- :align="align"
417
- />
418
- <template v-for="(_index, name) in slots" v-slot:[name]="data">
419
- <slot :name="name" v-bind="data"></slot>
420
- </template>
421
- </el-table>
422
- <!-- 分页器 -->
423
- <div
424
- v-if="state.tableData && state.tableData.length && isShowPagination"
425
- class="pagination_wrap"
426
- >
427
- <el-pagination
428
- v-if="state.tableData && state.tableData.length && isShowPagination"
429
- v-model:current-page="paginationData.pageNo"
430
- @current-change="handlesCurrentChange"
431
- @change="handlePageChange"
432
- :page-sizes="table.pageSizes || [10, 20, 50, 100]"
433
- v-model:page-size="paginationData.pageSize"
434
- :layout="table.layout || 'total,sizes, prev, pager, next, jumper'"
435
- :prev-text="table.prevText"
436
- :next-text="table.nextText"
437
- :total="paginationData.total || 0"
438
- :size="table.size || 'small'"
439
- v-bind="$attrs"
440
- background
441
- >
442
- <slot name="pagination"></slot>
443
- </el-pagination>
444
- </div>
445
-
446
- <!-- 表格底部按钮 -->
447
- <footer
448
- class="handle_wrap"
449
- :style="{ textAlign: footerBtnAlign as any }"
450
- v-if="isShowFooterBtn && state.tableData && state.tableData.length > 0"
451
- >
452
- <slot name="footer" />
453
- <div v-if="!slots.footer">
454
- <el-button type="primary" @click="save">{{ saveBtnTxt }}</el-button>
455
- </div>
456
- </footer>
457
- </div>
458
- </template>
459
-
460
- <script setup lang="ts">
461
- import {
462
- computed,
463
- ref,
464
- watch,
465
- useSlots,
466
- reactive,
467
- onMounted,
468
- onUpdated,
469
- onBeforeUnmount,
470
- } from "vue";
471
- import { Rank, Edit, QuestionFilled } from "@element-plus/icons-vue";
472
- import { ElMessage } from "element-plus";
473
- import Sortable from "sortablejs";
474
- // @ts-ignore
475
- import TTableColumn from "./TTableColumn.vue";
476
- import SingleEditCell from "./singleEditCell.vue";
477
- import SingleEdit from "./singleEdit.vue";
478
- import ColumnSet from "./ColumnSet.vue";
479
- import RenderCol from "./renderCol.vue";
480
- import Operator from "./operator.vue";
481
- import RenderHeader from "./renderHeader.vue";
482
- // 虚拟滚动
483
- import { useVirtualized } from "./useVirtualized";
484
- const {
485
- scrollContainerEl,
486
- updateRenderedItemCache,
487
- updateOffset,
488
- getDom,
489
- saveDATA,
490
- getItemHeightFromCache,
491
- } = useVirtualized();
492
- import { useExpose } from "./useExpose";
493
- const {
494
- TTable,
495
- clearSelection,
496
- getSelectionRows,
497
- toggleRowSelection,
498
- toggleAllSelection,
499
- toggleRowExpansion,
500
- setCurrentRow,
501
- clearSort,
502
- clearFilter,
503
- doLayout,
504
- sort,
505
- scrollTo,
506
- setScrollTop,
507
- setScrollLeft,
508
- } = useExpose();
509
- import { tableProps } from "./tableProps";
510
- const props = defineProps(tableProps);
511
- defineOptions({
512
- name: "TTable",
513
- });
514
- // 初始化数据
515
- let state = reactive({
516
- tableData: props.tableData,
517
- columnSet: [],
518
- copyTableData: [], // 键盘事件
519
- });
520
- // 空数据时表头是否显示校验红点
521
- const emptyDataRequired = computed(() => {
522
- if (props.isEmptyDataRequired) {
523
- return state.tableData.length > 0;
524
- } else {
525
- return true;
526
- }
527
- });
528
- // 单选框
529
- const radioVal = ref<number | any>("");
530
- // 判断单选选中及取消选中
531
- const forbidden = ref(true);
532
- // 获取t-table ref
533
- const TTableBox = ref<HTMLElement | any>(null);
534
- // 获取columnSet Ref
535
- const columnSetRef = ref<HTMLElement | any>(null);
536
- // 获取form ref
537
- const formRef = ref({});
538
- // 动态form ref
539
- const handleRef = (
540
- el: any,
541
- scope: { $index: number; column: { property: string } },
542
- item: { prop: any }
543
- ) => {
544
- if (el) {
545
- formRef.value[
546
- `formRef-${scope.$index}-${item.prop || scope.column.property}`
547
- ] = el;
548
- }
549
- };
550
- // 获取所有单元格编辑组件 ref
551
- const editTableRef: any = ref({});
552
- // 动态单元格编辑组件 ref
553
- const handleEditTableRef = (
554
- el: any,
555
- scope: { $index: number; column: { property: string } },
556
- item: { prop: any }
557
- ) => {
558
- if (el) {
559
- editTableRef.value[
560
- `singleEditRef-${scope.$index}-${item.prop || scope.column.property}`
561
- ] = el;
562
- }
563
- };
564
- // 抛出事件
565
- const emits = defineEmits([
566
- "save",
567
- "page-change",
568
- "page-size-change",
569
- "handleEvent",
570
- "radioChange",
571
- "rowSort",
572
- "validateError",
573
- "update:paginationData",
574
- ]);
575
- // 获取所有插槽
576
- const slots = useSlots();
577
- watch(
578
- () => props.tableData,
579
- (val) => {
580
- // console.log(111, val)
581
- if (props.useVirtual) {
582
- saveDATA.value = val;
583
- updateRenderData(0);
584
- } else {
585
- state.tableData = val;
586
- }
587
- },
588
- { deep: true }
589
- );
590
- watch(
591
- () => props.isRowSort,
592
- (val) => {
593
- if (val) {
594
- initSort();
595
- }
596
- }
597
- );
598
- onMounted(() => {
599
- // 设置默认选中项(单选)
600
- if (props.defaultRadioCol) {
601
- defaultRadioSelect(props.defaultRadioCol);
602
- }
603
- initSort();
604
- if (props.useVirtual) {
605
- saveDATA.value = props.tableData;
606
- getDom();
607
- scrollContainerEl.value?.addEventListener("scroll", handleScroll);
608
- }
609
- });
610
- // 更新实际渲染数据
611
- const updateRenderData = (scrollTop: number) => {
612
- let startIndex = 0;
613
- let offsetHeight = 0;
614
- for (let i = 0; i < saveDATA.value.length; i++) {
615
- offsetHeight += getItemHeightFromCache(i);
616
- if (offsetHeight >= scrollTop) {
617
- startIndex = i;
618
- break;
619
- }
620
- }
621
- // 计算得出的渲染数据
622
- state.tableData = saveDATA.value.slice(
623
- startIndex,
624
- startIndex + props.virtualShowSize
625
- );
626
- // 缓存最新的列表项高度
627
- updateRenderedItemCache(startIndex);
628
- // 更新偏移值
629
- updateOffset(offsetHeight - getItemHeightFromCache(startIndex));
630
- };
631
- // 滚动事件
632
- const handleScroll = (e: any) => {
633
- // 渲染正确的数据
634
- updateRenderData(e.target.scrollTop);
635
- // console.log("滚动事件---handleScroll")
636
- };
637
- // 移除滚动事件
638
- onBeforeUnmount(() => {
639
- // console.log("移除滚动事件")
640
- if (props.useVirtual) {
641
- scrollContainerEl.value?.removeEventListener("scroll", handleScroll);
642
- }
643
- });
644
- onUpdated(() => {
645
- TTable.value.doLayout();
646
- });
647
- // 默认选中(单选项)---index必须是大于等于1(且只能默认选中第一页的数据)
648
- const defaultRadioSelect = (index: number | any) => {
649
- radioVal.value = index;
650
- emits("radioChange", state.tableData[index - 1], radioVal.value);
651
- };
652
- // 行拖拽
653
- const initSort = () => {
654
- if (!props.isRowSort) return;
655
- const el = TTableBox.value?.querySelector(".el-table__body-wrapper tbody");
656
- // console.log('3333', el)
657
- const handle = props.isRowSortIcon ? ".row_drag" : ".el-table__row";
658
- Sortable.create(el, {
659
- animation: 150, // 动画
660
- handle, // 指定拖拽目标,点击此目标才可拖拽元素(此例中设置操作按钮拖拽)
661
- // filter: '.disabled', // 指定不可拖动的类名(el-table中可通过row-class-name设置行的class)
662
- // dragClass: 'dragClass', // 设置拖拽样式类名
663
- // ghostClass: 'ghostClass', // 设置拖拽停靠样式类名
664
- // chosenClass: 'chosenClass', // 设置选中样式类名
665
- onEnd: (evt: { oldIndex: any; newIndex: any }) => {
666
- // console.log("拖拽结束---11", evt.oldIndex, evt.newIndex)
667
- const curRow = state.tableData.splice(evt.oldIndex, 1)[0];
668
- state.tableData.splice(evt.newIndex, 0, curRow);
669
- emits("rowSort", state.tableData, evt.oldIndex, evt.newIndex);
670
- },
671
- });
672
- };
673
-
674
- // 过滤字典
675
- /**
676
- * 下拉数据回显中文过滤器
677
- * @param [String,Number] value 需要转中文的key值
678
- * @param {String} list 数据源
679
- * @param [String,Number] key 数据源的key字段(默认:value)
680
- * @param {String} label 数据源的label字段(默认:label)
681
- */
682
- const constantEscape = (
683
- value: any,
684
- list: any[],
685
- key: string | number,
686
- label: string | number
687
- ) => {
688
- const res = list.find((item) => {
689
- return item[key] === value;
690
- });
691
- return res && res[label];
692
- };
693
- // // 第一列单选显示类
694
- const radioStyleClass = computed(() => {
695
- if (Array.isArray(props.table.firstColumn)) {
696
- return props.table.firstColumn.some(
697
- (item: { type: string }) => item.type === "radio"
698
- );
699
- } else {
700
- return props.table.firstColumn && props.table.firstColumn.type === "radio";
701
- }
702
- });
703
- // 单元格编辑是否存在校验
704
- const isEditRules = computed(() => {
705
- return (
706
- (props.table.rules && Object.keys(props.table.rules).length > 0) ||
707
- props.columns.some((item: any) => item?.configEdit?.rules)
708
- );
709
- });
710
- // 所有列(表头数据)
711
- const renderColumns = computed(() => {
712
- if (state.columnSet.length === 0) {
713
- return props.columns;
714
- }
715
- const columnByProp: any = props.columns.reduce((acc: any, cur: any) => {
716
- acc[cur.prop] = cur;
717
- return acc;
718
- }, {});
719
- return state.columnSet
720
- .filter((cur: any) => !cur.hidden)
721
- .map((cur: any) => columnByProp[cur.prop]);
722
- });
723
- // 判断是否是多级表头
724
- const isTableHeader = computed(() => {
725
- return renderColumns.value.some((item: any) => item.children);
726
- });
727
- // 判断如果有表头合并就自动开启单元格缩放
728
- const isTableBorder = computed(() => {
729
- return props.columns.some((item: any) => item.children);
730
- });
731
- // 单元格编辑键盘事件
732
- const handleKeyup = (
733
- event: { keyCode: number },
734
- index: number,
735
- key: string
736
- ) => {
737
- if (!props.isKeyup) return;
738
- const copyTableData = JSON.parse(JSON.stringify(state.tableData));
739
- const doms = document.getElementsByClassName(key);
740
- const focusNextElement = (nextIndex: number) => {
741
- const nextDom =
742
- doms[nextIndex]?.getElementsByTagName("input")[0] ||
743
- doms[nextIndex]?.getElementsByTagName("textarea")[0];
744
- if (nextDom) nextDom.focus();
745
- };
746
- switch (event.keyCode) {
747
- case 38: // 向上键
748
- if (!index) index = copyTableData.length;
749
- focusNextElement(index - 1);
750
- break;
751
- case 40: // 向下键
752
- if (index === copyTableData.length - 1) index = -1;
753
- focusNextElement(index + 1);
754
- break;
755
- case 13: // 回车键
756
- let keyName = props.columns.map((val: any) => val.prop);
757
- let num = keyName.indexOf(key);
758
- if (num === -1) {
759
- num = 0;
760
- } else if (num === keyName.length - 1) {
761
- if (index === state.copyTableData.length - 1) {
762
- index = 0;
763
- } else {
764
- ++index;
765
- }
766
- } else {
767
- ++num;
768
- }
769
- let doms = document.getElementsByClassName(keyName[num]);
770
- if (doms.length) {
771
- let dom =
772
- doms[index].getElementsByTagName("input")[0] ||
773
- doms[index].getElementsByTagName("textarea")[0];
774
- dom.focus();
775
- }
776
- break;
777
- }
778
- };
779
-
780
- // forbidden取值(选择单选或取消单选)
781
- const isForbidden = () => {
782
- forbidden.value = false;
783
- setTimeout(() => {
784
- forbidden.value = true;
785
- }, 0);
786
- };
787
- // 单选抛出事件radioChange
788
- const radioClick = (row: any, index: any) => {
789
- forbidden.value = !forbidden.value;
790
- const isCurrentlySelected = radioVal.value === index;
791
- if (isCurrentlySelected) {
792
- radioVal.value = null;
793
- } else {
794
- radioVal.value = index;
795
- }
796
- isForbidden();
797
- emits("radioChange", radioVal.value ? row : null, radioVal.value);
798
- };
799
-
800
- // 点击单选框单元格触发事件
801
- const radioHandleChange = (row: any, index: any) => {
802
- if (row?.isRadioDisabled) return;
803
- if (props.rowClickRadio) {
804
- return;
805
- }
806
- radioClick(row, index);
807
- };
808
- // 点击某行事件
809
- const rowClick = (row: any) => {
810
- if (row.isRadioDisabled) return;
811
- if (!props.rowClickRadio) {
812
- return;
813
- }
814
- radioClick(row, state.tableData.indexOf(row) + 1);
815
- };
816
- // 清除单选框选中状态
817
- const clearRadioHandle = () => {
818
- radioVal.value = null;
819
- TTable.value.setCurrentRow(-1);
820
- };
821
- // 复制内容到剪切板
822
- const copyToClipboard = async (text: any) => {
823
- // 确保传入的内容是字符串类型
824
- if (typeof text !== "string" || text.trim() === "") {
825
- throw new Error("无效的复制内容");
826
- }
827
- try {
828
- // 使用现代剪贴板 API 进行复制
829
- await navigator.clipboard.writeText(text);
830
- } catch (error) {
831
- // 捕获并抛出具体的错误信息
832
- if (
833
- (error as any).name === "NotAllowedError" ||
834
- (error as any).name === "SecurityError"
835
- ) {
836
- throw new Error("复制失败:权限被拒绝");
837
- } else {
838
- throw new Error("复制失败:浏览器不支持或发生未知错误");
839
- }
840
- }
841
- };
842
-
843
- // 显示消息提示
844
- const showMessage = (type: "success" | "error", message: string) => {
845
- if (type === "success") {
846
- ElMessage.success(message);
847
- } else {
848
- ElMessage.error(message);
849
- }
850
- };
851
-
852
- // 双击复制单元格内容
853
- const cellDblclick = async (
854
- row: { [x: string]: any },
855
- column: { property: string | number }
856
- ) => {
857
- if (!props.isCopy) {
858
- return false;
859
- }
860
- const value = row[column.property];
861
- try {
862
- // 调用复制函数
863
- await copyToClipboard(String(value)); // 确保值转换为字符串
864
- showMessage("success", "复制成功");
865
- } catch (error: any) {
866
- // 捕获并显示错误信息
867
- showMessage("error", error.message || "复制失败");
868
- }
869
- };
870
- // 判断是否使用了某个插槽
871
- const isShow = (name: string) => {
872
- return Object.keys(slots).includes(name);
873
- };
874
-
875
- // 整行编辑返回数据
876
- // const save = () => {
877
- // if (!isEditRules.value) {
878
- // emits("save", state.tableData)
879
- // return
880
- // }
881
- // // 表单规则校验
882
- // let successLength = 0
883
- // let rulesList: string[] = []
884
- // let rulesError: (string | number)[] = []
885
- // let propError: string[] = []
886
- // let propLabelError = [] as any
887
- // // 获取所有的form ref
888
- // const refList = Object.keys(formRef.value).filter(item => item.includes("formRef"))
889
- // // 获取单独设置规则项
890
- // const arr = renderColumns.value
891
- // .filter((val: { configEdit: { rules: any } }) => {
892
- // if (val.configEdit?.rules) {
893
- // return val
894
- // }
895
- // })
896
- // .map((item: { prop: any }) => item.prop)
897
- // // 获取整体设置规则
898
- // const arr1 = (props.table.rules && Object.keys(props.table.rules)) ?? []
899
- // // 获取最终设置了哪些规则(其值是设置的--prop)
900
- // const newArr = [...arr, ...arr1]
901
- // // 最终需要校验的ref
902
- // newArr.map(val => {
903
- // refList.map((item: any) => {
904
- // if (typeof item === "string" && item.includes(val)) {
905
- // rulesList.push(item)
906
- // }
907
- // })
908
- // })
909
- // // console.log('最终需要校验的数据', rulesList, formRef.value)
910
- // // 表单都校验
911
- // rulesList.map((val: string | number) => {
912
- // formRef.value[val].validate((valid: boolean) => {
913
- // if (valid) {
914
- // successLength = successLength + 1
915
- // } else {
916
- // rulesError.push(val)
917
- // }
918
- // })
919
- // })
920
- // setTimeout(() => {
921
- // // 所有表单都校验成功
922
- // if (successLength == rulesList.length) {
923
- // if (isEditRules.value) {
924
- // console.log("所有表单都校验成功--", state.tableData)
925
- // if (props.isSelfSave) {
926
- // return state.tableData
927
- // } else {
928
- // emits("save", state.tableData)
929
- // }
930
- // }
931
- // } else {
932
- // // 校验未通过的prop
933
- // rulesError.map(item => {
934
- // newArr.map(val => {
935
- // if (typeof item === "string" && item.includes(val)) {
936
- // propError.push(val)
937
- // }
938
- // })
939
- // })
940
- // // 去重获取校验未通过的prop--label
941
- // Array.from(new Set(propError)).map(item => {
942
- // renderColumns.value.map((val: { prop: string; label: string }) => {
943
- // if (item === val.prop) {
944
- // propLabelError.push(val.label)
945
- // }
946
- // })
947
- // })
948
- // console.log("校验未通过的prop--label", propLabelError)
949
- // emits("validateError", propLabelError)
950
- // }
951
- // }, 300)
952
- // }
953
- const save = (): Promise<any> => {
954
- return new Promise((resolve) => {
955
- if (!isEditRules.value) {
956
- emits("save", state.tableData);
957
- resolve(state.tableData);
958
- return;
959
- }
960
-
961
- // 表单规则校验
962
- let successLength = 0;
963
- let rulesList: string[] = [];
964
- let rulesError: (string | number)[] = [];
965
- let propError: string[] = [];
966
- let propLabelError = [] as any;
967
-
968
- // 获取所有的form ref
969
- const refList = Object.keys(formRef.value).filter((item) =>
970
- item.includes("formRef")
971
- );
972
-
973
- // 获取单独设置规则项
974
- const arr = renderColumns.value
975
- .filter((val: { configEdit: { rules: any } }) => {
976
- if (val.configEdit?.rules) {
977
- return val;
978
- }
979
- })
980
- .map((item: { prop: any }) => item.prop);
981
-
982
- // 获取整体设置规则
983
- const arr1 = (props.table.rules && Object.keys(props.table.rules)) ?? [];
984
-
985
- // 获取最终设置了哪些规则(其值是设置的--prop)
986
- const newArr = [...arr, ...arr1];
987
-
988
- // 最终需要校验的ref
989
- newArr.map((val) => {
990
- refList.map((item: any) => {
991
- if (typeof item === "string" && item.includes(val)) {
992
- rulesList.push(item);
993
- }
994
- });
995
- });
996
-
997
- // 表单都校验
998
- rulesList.map((val: string | number) => {
999
- formRef.value[val].validate((valid: boolean) => {
1000
- if (valid) {
1001
- successLength = successLength + 1;
1002
- } else {
1003
- rulesError.push(val);
1004
- }
1005
- });
1006
- });
1007
-
1008
- setTimeout(() => {
1009
- // 所有表单都校验成功
1010
- if (successLength == rulesList.length) {
1011
- if (isEditRules.value) {
1012
- emits("save", state.tableData);
1013
- resolve(state.tableData);
1014
- }
1015
- } else {
1016
- // 校验未通过的prop
1017
- rulesError.map((item) => {
1018
- newArr.map((val) => {
1019
- if (typeof item === "string" && item.includes(val)) {
1020
- propError.push(val);
1021
- }
1022
- });
1023
- });
1024
-
1025
- // 去重获取校验未通过的prop--label
1026
- Array.from(new Set(propError)).map((item) => {
1027
- renderColumns.value.map((val: { prop: string; label: string }) => {
1028
- if (item === val.prop) {
1029
- propLabelError.push(val.label);
1030
- }
1031
- });
1032
- });
1033
-
1034
- console.log("校验未通过的prop--label", propLabelError);
1035
- emits("validateError", propLabelError);
1036
- }
1037
- }, 300);
1038
- });
1039
- };
1040
- // 单个编辑事件
1041
- const handleEvent = ({ type, val }: any, index: any) => {
1042
- emits("handleEvent", type, val, index);
1043
- };
1044
- // 当前页码
1045
- const handlesCurrentChange = (val: any) => {
1046
- emits("page-change", val);
1047
- };
1048
- /** current-page 或 page-size 更改 */
1049
- const handlePageChange = (pageNo: number, pageSize: number) => {
1050
- const tableConfig = { ...props.table, pageNo, pageSize };
1051
- emits("update:paginationData", tableConfig);
1052
- emits("page-size-change", { pageNo, pageSize });
1053
- };
1054
- /**
1055
- * 公共方法
1056
- */
1057
- // 单元格编辑调用save方法返回数据
1058
- const saveMethod = (callback: (arg0: any) => any) => {
1059
- if (!isEditRules.value) {
1060
- callback && callback(state.tableData);
1061
- return;
1062
- }
1063
- // 表单规则校验
1064
- let successLength = 0;
1065
- let rulesList: any = [];
1066
- let rulesError: any = [];
1067
- let propError: any = [];
1068
- let propLabelError: any = [];
1069
- // 获取所有的form ref
1070
- const refList = Object.keys(formRef.value).filter((item) =>
1071
- item.includes("formRef")
1072
- );
1073
- // 获取单独设置规则项
1074
- const arr = renderColumns.value
1075
- .filter((val: { configEdit: { rules: any } }) => {
1076
- if (val.configEdit?.rules) {
1077
- return val;
1078
- }
1079
- })
1080
- .map((item: { prop: any }) => item.prop);
1081
- // 获取整体设置规则
1082
- const arr1 = (props.table.rules && Object.keys(props.table.rules)) ?? [];
1083
- // 获取最终设置了哪些规则(其值是设置的--prop)
1084
- const newArr = [...arr, ...arr1];
1085
- // 最终需要校验的ref
1086
- newArr.map((val) => {
1087
- refList.map((item: any) => {
1088
- if (item.includes(val)) {
1089
- rulesList.push(item);
1090
- }
1091
- });
1092
- });
1093
- // console.log('最终需要校验的数据', rulesList, formRef.value)
1094
- // 表单都校验
1095
- rulesList.map((val: string | number) => {
1096
- formRef.value[val].validate((valid: any) => {
1097
- if (valid) {
1098
- successLength = successLength + 1;
1099
- } else {
1100
- rulesError.push(val);
1101
- }
1102
- });
1103
- });
1104
- setTimeout(() => {
1105
- // 所有表单都校验成功
1106
- if (successLength == rulesList.length) {
1107
- if (isEditRules.value) {
1108
- // console.log('所有表单都校验成功--', state.tableData)
1109
- callback && callback(state.tableData);
1110
- }
1111
- } else {
1112
- // 校验未通过的prop
1113
- rulesError.map((item: string | any[]) => {
1114
- newArr.map((val) => {
1115
- if (item.includes(val)) {
1116
- propError.push(val);
1117
- }
1118
- });
1119
- });
1120
- // 去重获取校验未通过的prop--label
1121
- Array.from(new Set(propError)).map((item) => {
1122
- renderColumns.value.map((val: { prop: unknown; label: any }) => {
1123
- if (item === val.prop) {
1124
- propLabelError.push(val.label);
1125
- }
1126
- });
1127
- });
1128
- console.log("校验未通过的prop--label", propLabelError);
1129
- emits("validateError", propLabelError);
1130
- }
1131
- }, 300);
1132
- };
1133
- // 清空校验规则
1134
- const clearValidate = () => {
1135
- const refList = Object.keys(formRef.value).filter((item) =>
1136
- item.includes("formRef")
1137
- );
1138
- refList.length > 0 &&
1139
- refList.map((val) => {
1140
- formRef.value[val].clearValidate();
1141
- });
1142
- };
1143
- // 表单进行重置并移除校验结果
1144
- const resetFields = () => {
1145
- const refList = Object.keys(formRef.value).filter((item) =>
1146
- item.includes("formRef")
1147
- );
1148
- refList.length > 0 &&
1149
- refList.map((val) => {
1150
- formRef.value[val].resetFields();
1151
- });
1152
- // 重置下拉表格
1153
- const refEditList = Object.keys(editTableRef.value).filter((item) =>
1154
- item.includes("singleEditRef")
1155
- );
1156
- refEditList.length > 0 &&
1157
- refEditList.map((val) => {
1158
- editTableRef.value[val].resetTselectTableFields();
1159
- });
1160
- };
1161
- // 重置下拉表格--单元格编辑
1162
- const resetTselectTable = () => {
1163
- // 重置下拉表格
1164
- const refEditList = Object.keys(editTableRef.value).filter((item) =>
1165
- item.includes("singleEditRef")
1166
- );
1167
- refEditList.length > 0 &&
1168
- refEditList.map((val) => {
1169
- editTableRef.value[val].resetTselectTableFields();
1170
- });
1171
- };
1172
- // 获取columnSet缓存数据
1173
- const reSetColumnSet = () => {
1174
- return columnSetRef.value?.reSetColumnSet();
1175
- };
1176
- // 暴露方法出去
1177
- defineExpose({
1178
- defaultRadioSelect,
1179
- clearSelection,
1180
- getSelectionRows,
1181
- toggleRowSelection,
1182
- toggleAllSelection,
1183
- toggleRowExpansion,
1184
- setCurrentRow,
1185
- clearSort,
1186
- clearFilter,
1187
- doLayout,
1188
- sort,
1189
- scrollTo,
1190
- setScrollTop,
1191
- setScrollLeft,
1192
- state,
1193
- radioVal,
1194
- clearValidate,
1195
- resetFields,
1196
- save,
1197
- saveMethod,
1198
- reSetColumnSet,
1199
- clearRadioHandle,
1200
- resetTselectTable,
1201
- });
1202
- </script>
1203
- <style lang="scss" scoped>
1204
- @use "../style/table.scss";
1205
- </style>