gy-ui-plus 1.0.9 → 1.0.10

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