@lx-frontend/wrap-element-ui 1.0.1-beta.2 → 1.0.1-beta.4

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 (46) hide show
  1. package/README.md +45 -45
  2. package/package.json +15 -16
  3. package/src/components/AddMembers/index.vue +157 -157
  4. package/src/components/AuditSteps/index.vue +140 -140
  5. package/src/components/DemoComponent/index.vue +20 -20
  6. package/src/components/EditableTable/README.md +147 -147
  7. package/src/components/EditableTable/index.less +724 -716
  8. package/src/components/EditableTable/index.vue +914 -841
  9. package/src/components/EditableTable/types.ts +116 -94
  10. package/src/components/EditableTable/useCellHover.ts +71 -71
  11. package/src/components/EditableTable/useColumnHeaderOperation.ts +325 -187
  12. package/src/components/EditableTable/useDefaultOperation.ts +95 -95
  13. package/src/components/EditableTable/useDragSort.ts +290 -291
  14. package/src/components/EditableTable/usePagination.ts +30 -33
  15. package/src/components/EditableTable/useRowBgColor.ts +50 -50
  16. package/src/components/EditableTable/useViewSetting.ts +119 -67
  17. package/src/components/Ellipsis/MultilineEllipsis.vue +141 -141
  18. package/src/components/Ellipsis/index.vue +119 -119
  19. package/src/components/LxTable/index.vue +296 -296
  20. package/src/components/PopoverForm/index.vue +66 -66
  21. package/src/components/SearchForm/index.vue +243 -243
  22. package/src/components/SearchSelect/index.vue +153 -153
  23. package/src/components/index.ts +24 -24
  24. package/src/components/singleMessage/index.ts +44 -44
  25. package/dist/AddMembers/index.vue.d.ts +0 -31
  26. package/dist/AuditSteps/index.vue.d.ts +0 -46
  27. package/dist/DemoComponent/index.vue.d.ts +0 -2
  28. package/dist/EditableTable/index.vue.d.ts +0 -186
  29. package/dist/EditableTable/types.d.ts +0 -123
  30. package/dist/EditableTable/useCellHover.d.ts +0 -11
  31. package/dist/EditableTable/useColumnHeaderOperation.d.ts +0 -106
  32. package/dist/EditableTable/useDefaultOperation.d.ts +0 -22
  33. package/dist/EditableTable/useDragSort.d.ts +0 -15
  34. package/dist/EditableTable/usePagination.d.ts +0 -13
  35. package/dist/EditableTable/useRowBgColor.d.ts +0 -16
  36. package/dist/EditableTable/useViewSetting.d.ts +0 -58
  37. package/dist/Ellipsis/MultilineEllipsis.vue.d.ts +0 -91
  38. package/dist/Ellipsis/index.vue.d.ts +0 -89
  39. package/dist/LxTable/index.vue.d.ts +0 -2
  40. package/dist/PopoverForm/index.vue.d.ts +0 -50
  41. package/dist/SearchForm/index.vue.d.ts +0 -105
  42. package/dist/SearchSelect/index.vue.d.ts +0 -53
  43. package/dist/index.css +0 -1
  44. package/dist/index.d.ts +0 -12
  45. package/dist/index.mjs +0 -40826
  46. package/dist/singleMessage/index.d.ts +0 -4
@@ -1,842 +1,915 @@
1
- <template>
2
- <div class="editable-table">
3
- <div class="view-setting">
4
- <div class="view-setting__btn-wrapper">
5
- <div
6
- class="view-setting__btn btn-pointer"
7
- @click="handleViewSettingShow"
8
- >
9
- <i class="el-icon-setting" />
10
- <div class="view-setting__btn-text">
11
- 显示设置
12
- </div>
13
- </div>
14
- </div>
15
-
16
- <el-dialog
17
- title="显示设置"
18
- :visible.sync="viewSettingVisible"
19
- width="750px"
20
- :close-on-click-modal="false"
21
- :append-to-body="true"
22
- custom-class="view-setting__dialog"
23
- >
24
- <div class="view-setting__content">
25
- <div class="view-setting__content-left">
26
- <div class="view-setting__content-left-title">
27
- 表头设置
28
- </div>
29
- <div class="view-setting__checkbox-wrapper">
30
- <el-checkbox-group v-model="columnsToBeShown">
31
- <el-checkbox
32
- v-for="item in columnConfig"
33
- :key="item.label"
34
- :label="item.prop"
35
- :disabled="item.isAlwaysShow"
36
- >
37
- <div class="view-setting__content-left-item">
38
- {{ item.label }}
39
- </div>
40
- </el-checkbox>
41
- </el-checkbox-group>
42
- </div>
43
- </div>
44
- <div class="view-setting__content-right">
45
- <div class="view-setting__content-right-title">
46
- 已选择
47
- <div class="view-setting__selected-count">
48
- {{ columnsToBeShown.length }}
49
- </div>
50
- </div>
51
- <div class="view-setting__content-right-frize">
52
- 冻结前
53
- <el-input
54
- v-model="leftFixedColumnCount"
55
- class="view-setting__content-right-input"
56
- />
57
-
58
- </div>
59
- <div class="view-setting__content-right-selected">
60
- <div
61
- v-for="(item, index) in viewSettingDragSortOptions"
62
- :key="item.prop"
63
- class="view-setting__selected-item view-setting-draggable-item"
64
- >
65
- <div class="view-setting__selected-item-left">
66
- <div
67
- class="view-setting-drag-target view-setting__icon-wrapper"
68
- :data-index="index"
69
- >
70
- <div
71
- class="view-setting-drag-target editable-table-drag-icon"
72
- :data-index="index"
73
- />
74
- </div>
75
- <div class="view-setting__selected-item-name">
76
- {{ item.label }}
77
- </div>
78
- </div>
79
- <div
80
- :class="['view-setting__selected-item-close', item.isAlwaysShow ? 'view-setting__selected-item-close--disabled' : '']"
81
- @click="handleColumnClose(item)"
82
- >
83
- <i class="el-icon-close" />
84
- </div>
85
- </div>
86
- </div>
87
- </div>
88
- </div>
89
- <template #footer>
90
- <el-button @click="handleViewSettingClose">
91
- 取消
92
- </el-button>
93
- <el-button
94
- type="primary"
95
- @click="handleViewSettingConfirm"
96
- >
97
- 确认
98
- </el-button>
99
- </template>
100
- </el-dialog>
101
- </div>
102
-
103
- <!-- 列表展示,属性透传,列编辑 -->
104
- <el-table
105
- ref="tableDomRef"
106
- v-loading="loading"
107
- :data="dataList"
108
- :row-style="setRowStyle"
109
- :row-class-name="setRowClassName"
110
- :cell-class-name="setCellClassName"
111
- :show-summary="summaryList.length > 0"
112
- :summary-method="tableSummaryMethod"
113
- v-bind="$attrs"
114
- border
115
- @selection-change="handleSelectionChange"
116
- @cell-mouse-enter="debouncedHoverHandler"
117
- @header-dragend="doTableLayout"
118
- >
119
- <el-table-column
120
- v-if="rowDragAble"
121
- width="30px"
122
- class-name="editable-table__drag-cell no-inner-cell-border"
123
- :fixed="leftFixedColumnCount > 0 ? 'left' : ''"
124
- >
125
- <template #default="scope">
126
- <div
127
- class="row-drag-target editable-table__drag-icon"
128
- :data-index="scope.$index"
129
- @mousedown="currScope = scope"
130
- >
131
- <div
132
- :data-index="scope.$index"
133
- class="row-drag-target editable-table-drag-icon"
134
- />
135
- </div>
136
- </template>
137
- </el-table-column>
138
- <!-- 展开行 -->
139
- <el-table-column
140
- v-if="hasExpandRow"
141
- width="30px"
142
- type="expand"
143
- :fixed="leftFixedColumnCount > 0 ? 'left' : ''"
144
- class-name="no-inner-cell-border"
145
- >
146
- <template #default="scope">
147
- <slot
148
- name="expand"
149
- v-bind="scope"
150
- />
151
- </template>
152
- </el-table-column>
153
- <!-- 选择列 -->
154
- <el-table-column
155
- v-if="hasSelectionColumn"
156
- width="45px"
157
- align="center"
158
- type="selection"
159
- :fixed="leftFixedColumnCount > 0 ? 'left' : ''"
160
- class-name="no-inner-cell-border"
161
- />
162
- <!-- 编号列 -->
163
- <el-table-column
164
- v-if="hasIndexColumn"
165
- min-width="30px"
166
- type="index"
167
- :fixed="leftFixedColumnCount > 0 ? 'left' : ''"
168
- class-name="no-inner-cell-border"
169
- />
170
- <!-- 颜色选择列 -->
171
- <el-table-column
172
- v-if="colorList && colorList.length > 0"
173
- width="22px"
174
- class-name="editable-table__color-column no-inner-cell-border"
175
- :fixed="leftFixedColumnCount > 0 ? 'left' : ''"
176
- >
177
- <template #header>
178
- <div class="editable-table__color-icon" />
179
- </template>
180
- <template #default="scope">
181
- <el-popover
182
- ref="colorPopoverRef"
183
- placement="right"
184
- trigger="click"
185
- popper-class="color-popover"
186
- >
187
- <div class="color-list">
188
- <div
189
- v-for="color in colorList"
190
- :key="color.id"
191
- class="color-list__item"
192
- :style="{ backgroundColor: color.sampleColor }"
193
- @click="handleColorChange(color.id, scope)"
194
- >
195
- <span :style="{color: color.textColor}">{{ color.name }}</span>
196
- </div>
197
- </div>
198
- <template slot="reference">
199
- <!-- 没有这个包裹的div标签,在拖动换行之后,无法调起颜色选择弹窗 -->
200
- <div>
201
- <div
202
- v-if="isDefaultColor(scope.row.colorId)"
203
- class="editable-table__color-icon"
204
- />
205
- <div
206
- v-else
207
- class="editable-table__selected-color"
208
- :style="{ backgroundColor: getColorById(scope.row.colorId, 'sample') }"
209
- />
210
- </div>
211
- </template>
212
- </el-popover>
213
- </template>
214
- </el-table-column>
215
- <!-- 这里的key很重要,必须保证每次生成的key都不一样,这样列排序功能才能生效,否则,即使actualColumns数组元素顺序发生变化,列顺序也不会改变。原因未知 -->
216
- <el-table-column
217
- v-for="(column, index) in actualColumns"
218
- :key="column.prop + index"
219
- resizable
220
- class-name="editable-table__data-column"
221
- :filtered-value="filteredValue[column.prop] || []"
222
- v-bind="getColumnBindProps(column)"
223
- >
224
- <template
225
- v-if="showColumnHeadSortIcon(column)"
226
- #header="scope"
227
- >
228
- <el-popover
229
- ref="sortFilterPopoverRef"
230
- placement="bottom"
231
- trigger="click"
232
- popper-class="editable-table__sort-filter"
233
- :data-prop="column.prop"
234
- @show="handleHeaderPopoverShow(column)"
235
- >
236
- <template slot="reference">
237
- <!-- 筛选中,或排序中,高亮 -->
238
- <span :class="['editable-table__sort-reference', isColumnHeadActive(column) && 'editable-table__sort-reference--active']">
239
- {{ column.label }}
240
- <div :class="['editable-table__sort-icon', isColumnHeadActive(column) && 'editable-table__sort-icon--active']" />
241
- </span>
242
- </template>
243
- <div class="sort-filter">
244
- <div class="sort-filter__column-title">
245
- {{ column.label }}
246
- </div>
247
- <div
248
- v-if="column.isColumnSortable"
249
- class="sort-filter__sort"
250
- >
251
- <div class="sort-filter__sort-title">
252
- 排序
253
- </div>
254
- <div class="sort-filter__sort-btns">
255
- <el-button
256
- :class="['sort-filter__sort-btn', tempSortingColumn?.prop === column.prop && tempSortType === 'ascending' && 'sort-filter__sort-btn--active']"
257
- @click="handleSort('ascending', column)"
258
- >
259
- 升序
260
- </el-button>
261
- <el-button
262
- :class="['sort-filter__sort-btn', tempSortingColumn?.prop === column.prop && tempSortType === 'descending' && 'sort-filter__sort-btn--active']"
263
- @click="handleSort('descending', column)"
264
- >
265
- 降序
266
- </el-button>
267
- </div>
268
- </div>
269
- <div
270
- v-if="column.isColumnSearchAble"
271
- class="sort-filter__search"
272
- >
273
- <div class="sort-filter__search-title">
274
- 搜索
275
- </div>
276
- <el-input
277
- v-model="tempSearchValue[column.prop]"
278
- class="sort-filter__search-input"
279
- placeholder="请输入内容"
280
- />
281
- </div>
282
- <div
283
- v-if="column.filters && column.filters.length > 0"
284
- class="sort-filter__filter"
285
- >
286
- <div class="sort-filter__filter-title">
287
- 筛选
288
- </div>
289
- <el-checkbox-group
290
- v-model="tempFilteredValue[column.prop]"
291
- class="sort-filter__filter-checkbox-group"
292
- >
293
- <el-checkbox
294
- v-for="item in column.filters"
295
- :key="item.value"
296
- :label="item.value"
297
- class="sort-filter__filter-checkbox"
298
- >
299
- <slot
300
- :name="column.prop + '-filter-item'"
301
- v-bind="item"
302
- >
303
- {{ item.text }}
304
- </slot>
305
- </el-checkbox>
306
- </el-checkbox-group>
307
- </div>
308
- <div
309
- v-if="column.summary"
310
- class="sort-filter__filter"
311
- >
312
- <div class="sort-filter__filter-title">
313
- 统计
314
- </div>
315
- <el-checkbox-group
316
- v-model="tempSummaryList"
317
- class="sort-filter__filter-checkbox-group"
318
- >
319
- <el-checkbox
320
- :label="column.prop"
321
- class="sort-filter__filter-checkbox"
322
- >
323
- <slot
324
- :name="column.prop + '-summay-item'"
325
- v-bind="column"
326
- >
327
- {{ column.label }}
328
- </slot>
329
- </el-checkbox>
330
- </el-checkbox-group>
331
- </div>
332
- <div class="sort-filter__footer">
333
- <el-button
334
- class="sort-filter__reset-btn"
335
- @click="handleHeaderOperationReset(column, scope)"
336
- >
337
- 重置
338
- </el-button>
339
- <el-button
340
- class="sort-filter__confirm-btn"
341
- type="primary"
342
- @click="handleHeaderOperationConfirm(scope)"
343
- >
344
- 确定
345
- </el-button>
346
- </div>
347
- </div>
348
- </el-popover>
349
- </template>
350
- <!-- 默认操作按钮,defaultOperations属性不为空数组时展示。编辑状态下隐藏 -->
351
- <template
352
- v-if="column.prop === '$$operation'"
353
- #default="scope"
354
- >
355
- <el-popover
356
- v-if="editingRowIndex !== scope.$index"
357
- ref="operationPopoverRef"
358
- placement="bottom"
359
- trigger="click"
360
- popper-class="operation-popover"
361
- >
362
- <div
363
- slot="reference"
364
- class="operation-popover__operation-reference btn-pointer"
365
- >
366
- <el-button :class="['operation-popover__operation-btn', hoveringCellInfo.rowIndex === scope.$index && 'operation-popover__operation-btn--active']">
367
- 操作
368
- </el-button>
369
- </div>
370
- <div class="operation-popover__operation">
371
- <div
372
- v-if="defaultOperations.includes('delete')"
373
- class="operation-popover__operation-item btn-pointer"
374
- @click="handleDelete(scope.row, scope.$index)"
375
- >
376
- 删除
377
- </div>
378
- <div
379
- v-if="defaultOperations.includes('edit')"
380
- class="operation-popover__operation-item btn-pointer"
381
- @click="handleEdit(scope)"
382
- >
383
- 编辑
384
- </div>
385
- <div
386
- v-if="defaultOperations.includes('top')"
387
- class="operation-popover__operation-item btn-pointer"
388
- @click="handleRowPinToTop(scope)"
389
- >
390
- 置顶
391
- </div>
392
- <slot
393
- name="custom-operation"
394
- v-bind="scope"
395
- />
396
- </div>
397
- </el-popover>
398
- <div
399
- v-else
400
- class="operation-popover__save-cancel"
401
- >
402
- <div
403
- class="btn-pointer operation-popover__btn"
404
- @click="handleEditSave(scope.row)"
405
- >
406
- 保存
407
- </div>
408
- <div
409
- class="btn-pointer operation-popover__btn"
410
- @click="handleEditCancel(scope.row)"
411
- >
412
- 取消
413
- </div>
414
- </div>
415
- </template>
416
- <!-- 默认渲染、非编辑态,不需要特殊处理,el-table负责渲染 -->
417
- <!-- 自定义插槽 -->
418
- <template
419
- v-else-if="column.slotName"
420
- #default="scope"
421
- >
422
- <!-- 非编辑态 -->
423
- <slot
424
- v-if="scope.$index !== editingRowIndex"
425
- v-bind="scope"
426
- :name="column.slotName"
427
- />
428
- <!-- 编辑态 -->
429
- <template v-else-if="scope.$index === editingRowIndex">
430
- <!-- 自定义编辑 -->
431
- <slot
432
- v-if="column.editSlotName"
433
- v-bind="{scope, column}"
434
- :name="column.editSlotName"
435
- />
436
- <!-- 内置编辑类型 -->
437
- <div v-else-if="column.editType">
438
- <div v-if="column.editType === 'input'">
439
- <el-input
440
- v-model="editingRowData[column.prop]"
441
- clearable
442
- />
443
- </div>
444
- <div v-if="column.editType === 'select'">
445
- <el-select v-model="editingRowData[column.prop]">
446
- <el-option
447
- v-for="item in column.selectOptions"
448
- :key="item.label"
449
- :label="item.label"
450
- :value="item.value"
451
- />
452
- </el-select>
453
- </div>
454
- <el-date-picker
455
- v-if="column.editType === 'date'"
456
- v-model="editingRowData[column.prop]"
457
- type="date"
458
- placeholder="选择日期"
459
- />
460
- </div>
461
- <!-- 不支持编辑 -->
462
- <slot
463
- v-else
464
- v-bind="scope"
465
- :name="column.slotName"
466
- />
467
- </template>
468
- </template>
469
- <!-- 默认渲染列,编辑态 -->
470
- <template
471
- v-else-if="editingRowIndex !== -1"
472
- #default="scope"
473
- >
474
- <!-- 当前行编辑中,内置编辑类型 -->
475
- <template v-if="editingRowIndex === scope.$index && column.editType">
476
- <div v-if="column.editType === 'input'">
477
- <el-input
478
- v-model="editingRowData[column.prop]"
479
- clearable
480
- type="text"
481
- />
482
- </div>
483
- <div v-if="column.editType === 'select'">
484
- <el-select v-model="editingRowData[column.prop]">
485
- <el-option
486
- v-for="item in column.selectOptions"
487
- :key="item.value"
488
- :label="item.label"
489
- :value="item.value"
490
- />
491
- </el-select>
492
- </div>
493
- <div v-if="column.editType === 'date'">
494
- <el-date-picker
495
- v-model="editingRowData[column.prop]"
496
- type="date"
497
- placeholder="选择日期"
498
- />
499
- </div>
500
- </template>
501
- <!-- 当前行编辑中,自定义编辑类型 -->
502
- <slot
503
- v-else-if="column.editSlotName && scope.$index === editingRowIndex"
504
- v-bind="scope"
505
- :name="column.editSlotName"
506
- />
507
- <!-- 当前行非编辑中 -->
508
- <template v-else>
509
- {{ column.formatter ? column.formatter(scope.row, column, scope.row[column.prop], scope.$index) : scope.row[column.prop] }}
510
- </template>
511
- </template>
512
- <!-- hover状态渲染 -->
513
- <template
514
- v-else-if="column.hoverSlotName"
515
- #default="scope"
516
- >
517
- <slot
518
- v-if="scope.$index === hoveringCellInfo.rowIndex && column.prop === hoveringCellInfo.columnProperty"
519
- v-bind="scope"
520
- :name="column.hoverSlotName"
521
- />
522
- <slot
523
- v-else-if="column.slotName"
524
- v-bind="scope"
525
- :name="column.slotName"
526
- />
527
- <template v-else>
528
- {{ column.formatter ? column.formatter(scope.row, column, scope.row[column.prop], scope.$index) : scope.row[column.prop] }}
529
- </template>
530
- </template>
531
- </el-table-column>
532
- </el-table>
533
- <div class="pagination-wrap">
534
- <div>共{{ total }}项数据</div>
535
- <el-pagination
536
- background
537
- layout="sizes, prev, pager, next, jumper"
538
- :page-sizes="[10, 15, 30, 60, 100]"
539
- :page-size.sync="pageSize"
540
- :pager-count="11"
541
- :current-page.sync="currentPage"
542
- :total="total"
543
- @size-change="handlePageSizeChange"
544
- @current-change="handleCurrPageChange"
545
- />
546
- </div>
547
- </div>
548
- </template>
549
-
550
- <script lang="ts" setup>
551
- import { computed, nextTick, ref, watch } from 'vue';
552
- import { Message } from 'element-ui'
553
- import usePagination from './usePagination';
554
- import useCellHover from './useCellHover';
555
- import useViewSetting from './useViewSetting';
556
- import useRowBgColor from './useRowBgColor';
557
- import useDefaultOperation from './useDefaultOperation'
558
- import useColumnHeaderOperation from './useColumnHeaderOperation';
559
- import useDragSort from './useDragSort';
560
- import { ITableDataItem, IColumnConfig, IDefaultOperationType, IColorList } from './types';
561
-
562
- // defineProps泛型参数如果从外部传入,编译报错
563
- interface IProps {
564
- /** 表格数据 */
565
- dataList: ITableDataItem[];
566
- /** 列配置 */
567
- columnConfig: IColumnConfig[];
568
- /** 是否展示展开行 */
569
- hasExpandRow?: boolean;
570
- /** 是否展示序号 */
571
- hasIndexColumn?: boolean;
572
- /** 是否展示选择列 */
573
- hasSelectionColumn?: boolean;
574
- /** 是否支持行拖拽 */
575
- rowDragAble?: boolean;
576
- /** 表格总条数 */
577
- total: number;
578
- /** 自定义列操作,当前支持行编辑(edit),删除(delete),置顶(top) */
579
- defaultOperations?: IDefaultOperationType[];
580
- /** 行背景色列表 */
581
- colorList?: IColorList;
582
- /** 左侧固定列数 */
583
- leftFixedCount?: number;
584
- /** 性能优化参数,调整拖拽的范围 */
585
- dragSemiRange?: number;
586
- /** 是否显示加载 */
587
- loading?: boolean;
588
- }
589
-
590
- interface IEmits {
591
- /** 行选中 */
592
- (e: 'selection-change', selection: any): void
593
- /** 修改行背景色 */
594
- (e: 'row-bg-change', param: {colorId: number; row: ITableDataItem; rowIndex: number}): void
595
- /** 行拖拽放置事件 */
596
- (e: 'row-drag-drop', param: { row: any; fromIndex: number; toIndex: number; page: number; size: number;}): void
597
- /** 行删除 */
598
- (e: 'row-delete', param: { row: any; index: number; page: number; size: number; }): void
599
- /** 点击编辑按钮立即触发 */
600
- (e: 'row-edit', param: { row: any, index: number; page: number; size: number;}): void
601
- /** 行置顶 */
602
- (e: 'row-pin-to-top', param: { row: any, rawIndex: number; page: number; size: number;}): void
603
- /** 行编辑保存 */
604
- (e: 'row-edit-save', param: { page: number; size: number; row: any; changedData: Record<string, any>; }): void
605
- /** 行编辑取消 */
606
- (e: 'row-edit-cancel', param: { row: any; page: number; size: number;}): void
607
- /** 页码改变 */
608
- (e: 'page-change', param: { page: number, size: number }): void
609
- }
610
-
611
- const props = withDefaults(defineProps<IProps>(), {
612
- dataList: () => [],
613
- columnConfig: () => [],
614
- hasExpandRow: false,
615
- hasIndexColumn: false,
616
- hasSelectionColumn: false,
617
- rowDragAble: false,
618
- total: 0,
619
- defaultOperations: () => [],
620
- colorList: () => [],
621
- leftFixedCount: 1,
622
- dragSemiRange: 15,
623
- loading: false
624
- })
625
-
626
- // 同defineProps一样,不支持泛型参数从外部导入
627
- const emit = defineEmits<IEmits>()
628
-
629
- const showingColumns = ref<string[]>([]); // 表格中实际展示的列
630
-
631
- const actualColumns = computed(() => {
632
- const res: IColumnConfig[] = [];
633
- let cnt = leftFixedColumnCount.value
634
-
635
- // 列排序和列过滤
636
- for (const prop of showingColumns.value) {
637
- const rawItem = props.columnConfig.find(c => c.prop === prop) ?? {} as IColumnConfig;
638
- const item: IColumnConfig = {
639
- ...rawItem,
640
- isColumnSortable: rawItem.sortable,
641
- sortable: inSorting.value ? rawItem.sortable : false
642
- };
643
- if (item) {
644
- if (cnt > 0) {
645
- item.fixed = 'left';
646
- // eslint-disable-next-line no-plusplus
647
- cnt--;
648
- } else {
649
- item.fixed = undefined;
650
- }
651
- res.push(item);
652
- }
653
- }
654
-
655
- // 使用默认操作项,添加默认操作列。该列在编辑模式下隐藏
656
- if (props.defaultOperations && props.defaultOperations.length > 0) {
657
- res.push({
658
- label: '操作',
659
- prop: '$$operation',
660
- 'min-width': '100px',
661
- fixed: 'right'
662
- });
663
- }
664
-
665
- return res;
666
- })
667
-
668
- const sortFilterPopoverRef = ref<any>(null);
669
- const tableDomRef = ref<any>(null);
670
- const currScope = ref<any>(null);
671
-
672
- /************ 分页相关 *************/
673
- const beforePageChange = () => {
674
- searchValue.value = {};
675
- }
676
- const {
677
- pageSize,
678
- currentPage,
679
- handleCurrPageChange,
680
- handlePageSizeChange
681
- } = usePagination({
682
- emit,
683
- beforePageChange
684
- })
685
-
686
- /************ 表格单元格hover事件相关 ************ */
687
- const {
688
- hoveringCellInfo,
689
- setCellClassName,
690
- debouncedHoverHandler
691
- } = useCellHover(tableDomRef)
692
-
693
- /************ 行背景色设置相关 ************ */
694
- const {
695
- isDefaultColor,
696
- getColorById,
697
- setRowStyle,
698
- handleColorChange,
699
- colorPopoverRef
700
- } = useRowBgColor({
701
- colorList: props.colorList,
702
- emit
703
- })
704
-
705
- /************ 默认的操作相关 ************ */
706
- const {
707
- operationPopoverRef,
708
- editingRowData,
709
- editingRowIndex,
710
- handleDelete,
711
- handleEdit,
712
- handleEditSave,
713
- handleEditCancel,
714
- handleRowPinToTop,
715
- closeAllExpandedRows
716
- } = useDefaultOperation({
717
- emit,
718
- tableDomRef,
719
- pageSize,
720
- currentPage,
721
- hasExpandRow: props.hasExpandRow
722
- })
723
-
724
- /************ 显示设置相关 ************ */
725
- const {
726
- viewSettingDragSortOptions,
727
- columnsToBeShown,
728
- viewSettingVisible,
729
- leftFixedColumnCount,
730
- handleViewSettingShow,
731
- handleViewSettingClose,
732
- handleViewSettingConfirm
733
- } = useViewSetting({
734
- tableDomRef,
735
- showingColumns,
736
- actualColumns,
737
- props
738
- })
739
-
740
- /************ 列头部操作相关 ************ */
741
- const {
742
- isColumnHeadActive,
743
- handleSort,
744
- handleHeaderPopoverShow,
745
- handleHeaderOperationConfirm,
746
- handleHeaderOperationReset,
747
- summaryList,
748
- tableSummaryMethod,
749
- filteredValue,
750
- showColumnHeadSortIcon,
751
- tempSortingColumn,
752
- tempSearchValue,
753
- tempFilteredValue,
754
- tempSummaryList,
755
- tempSortType,
756
- sortingColumn,
757
- isColumnFiltering,
758
- searchValue,
759
- inSorting
760
- } = useColumnHeaderOperation({
761
- tableDomRef,
762
- sortFilterPopoverRef,
763
- props
764
- })
765
-
766
- /************ 表格行拖拽和显示设置列拖拽 ************ */
767
- const beforeDragStart = () => {
768
- // 如果有列存在排序,不允许拖拽
769
- if (sortingColumn.value) {
770
- Message.warning('已有列正在排序,不允许拖拽。');
771
- return false;
772
- }
773
-
774
- // 如果有列存在筛选,不允许拖拽
775
- if (isColumnFiltering.value) {
776
- Message.warning('已有列正在筛选,不允许拖拽。');
777
- return false;
778
- }
779
-
780
- editingRowIndex.value = -1; // 关闭编辑状态
781
-
782
- return true
783
- }
784
-
785
- useDragSort({
786
- emit,
787
- props,
788
- viewSettingDragSortOptions,
789
- beforeDragStart,
790
- currentPage,
791
- pageSize,
792
- currScope,
793
- tableDomRef,
794
- })
795
-
796
- const doTableLayout = async () => {
797
- await nextTick();
798
- tableDomRef.value?.doLayout();
799
- }
800
-
801
- watch(
802
- () => props.columnConfig,
803
- (val) => {
804
- showingColumns.value = val.map(c => c.prop);
805
- },
806
- { immediate: true }
807
- )
808
-
809
- // 过滤出自定义属性,将其它属性全部透传给 el-table-column
810
- const getColumnBindProps = (column: IColumnConfig) => {
811
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
812
- const { editAble, editType, slotName, inputType, options, ...rest } = column;
813
- return rest;
814
- }
815
-
816
- const setRowClassName = (scope) => {
817
- return `
818
- custom-row-classname
819
- custom-row-classname-${scope.rowIndex}
820
- ${scope.row.isPinned ? 'custom-row-classname-pinned' : ''}
821
- `
822
- }
823
-
824
- const handleSelectionChange = (e) => {
825
- emit('selection-change', e);
826
- }
827
-
828
- const handleColumnClose = (item) => {
829
- if (item.isAlwaysShow) return;
830
- columnsToBeShown.value = columnsToBeShown.value.filter(c => c !== item.prop);
831
- }
832
-
833
- defineExpose({
834
- closeAllExpandedRows,
835
- elTableRef: tableDomRef
836
- })
837
-
838
- </script>
839
-
840
- <style lang="less">
841
- @import './index.less';
1
+ <template>
2
+ <div class="editable-table">
3
+ <div class="view-setting">
4
+ <div
5
+ v-if="!hideViewSettingBtn"
6
+ class="view-setting__btn-wrapper"
7
+ >
8
+ <div
9
+ class="view-setting__btn btn-pointer"
10
+ @click="handleViewSettingShow"
11
+ >
12
+ <i class="el-icon-setting" />
13
+ <div class="view-setting__btn-text">
14
+ 显示设置
15
+ </div>
16
+ </div>
17
+ </div>
18
+
19
+ <el-dialog
20
+ title="显示设置"
21
+ :visible.sync="viewSettingVisible"
22
+ width="750px"
23
+ top="12vh"
24
+ :close-on-click-modal="false"
25
+ :append-to-body="true"
26
+ custom-class="view-setting__dialog"
27
+ >
28
+ <div class="view-setting__content">
29
+ <div class="view-setting__content-left">
30
+ <div class="view-setting__content-left-title">
31
+ 表头设置
32
+ </div>
33
+ <div class="view-setting__checkbox-wrapper">
34
+ <el-checkbox-group v-model="columnsToBeShown">
35
+ <el-checkbox
36
+ v-for="item in columnConfig"
37
+ :key="item.label"
38
+ :label="item.prop"
39
+ :disabled="item.isAlwaysShow"
40
+ >
41
+ <div class="view-setting__content-left-item">
42
+ {{ item.label }}
43
+ </div>
44
+ </el-checkbox>
45
+ </el-checkbox-group>
46
+ </div>
47
+ </div>
48
+ <div class="view-setting__content-right">
49
+ <div class="view-setting__content-right-title">
50
+ 已选择
51
+ <div class="view-setting__selected-count">
52
+ {{ columnsToBeShown.length }}
53
+ </div>
54
+ </div>
55
+ <div class="view-setting__content-right-frize">
56
+ 冻结前
57
+ <el-input
58
+ class="view-setting__content-right-input"
59
+ :value="tempLeftFixedColumnCount"
60
+ @input="handleInputTempLeftFixedColumnCount"
61
+ />
62
+
63
+ </div>
64
+ <div class="view-setting__content-right-selected">
65
+ <div
66
+ v-for="(item, index) in viewSettingDragSortOptions"
67
+ :key="item.prop"
68
+ class="view-setting__selected-item view-setting-draggable-item"
69
+ >
70
+ <div class="view-setting__selected-item-left">
71
+ <div
72
+ class="view-setting-drag-target view-setting__icon-wrapper"
73
+ :data-index="index"
74
+ >
75
+ <div
76
+ class="view-setting-drag-target editable-table-drag-icon"
77
+ :data-index="index"
78
+ />
79
+ </div>
80
+ <div class="view-setting__selected-item-name">
81
+ {{ item.label }}
82
+ </div>
83
+ </div>
84
+ <div
85
+ :class="['view-setting__selected-item-close', item.isAlwaysShow ? 'view-setting__selected-item-close--disabled' : '']"
86
+ @click="handleColumnClose(item)"
87
+ >
88
+ <i class="el-icon-close" />
89
+ </div>
90
+ </div>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ <template #footer>
95
+ <el-button @click="handleViewSettingClose">
96
+ 取消
97
+ </el-button>
98
+ <el-button
99
+ type="primary"
100
+ @click="handleViewSettingConfirm"
101
+ >
102
+ 确认
103
+ </el-button>
104
+ </template>
105
+ </el-dialog>
106
+ </div>
107
+
108
+ <!-- 列表展示,属性透传,列编辑 -->
109
+ <el-table
110
+ ref="tableDomRef"
111
+ v-loading="loading"
112
+ :data="dataList"
113
+ :row-style="setRowStyle"
114
+ :row-class-name="setRowClassName"
115
+ :cell-class-name="setCellClassName"
116
+ :show-summary="summaryList.length > 0"
117
+ :summary-method="tableSummaryMethod"
118
+ v-bind="$attrs"
119
+ border
120
+ @selection-change="handleSelectionChange"
121
+ @cell-mouse-enter="debouncedHoverHandler"
122
+ @header-dragend="doTableLayout"
123
+ >
124
+ <el-table-column
125
+ v-if="rowDragAble"
126
+ width="30px"
127
+ class-name="editable-table__drag-cell no-inner-cell-border"
128
+ :fixed="leftFixedColumnCount > 0 ? 'left' : ''"
129
+ >
130
+ <template #default="scope">
131
+ <div
132
+ class="row-drag-target editable-table__drag-icon"
133
+ :data-index="scope.$index"
134
+ @mousedown="currScope = scope"
135
+ >
136
+ <div
137
+ :data-index="scope.$index"
138
+ class="row-drag-target editable-table-drag-icon"
139
+ />
140
+ </div>
141
+ </template>
142
+ </el-table-column>
143
+ <!-- 展开行 -->
144
+ <el-table-column
145
+ v-if="hasExpandRow"
146
+ width="30px"
147
+ type="expand"
148
+ :fixed="leftFixedColumnCount > 0 ? 'left' : ''"
149
+ class-name="no-inner-cell-border"
150
+ >
151
+ <template #default="scope">
152
+ <slot
153
+ name="expand"
154
+ v-bind="scope"
155
+ />
156
+ </template>
157
+ </el-table-column>
158
+ <!-- 选择列 -->
159
+ <el-table-column
160
+ v-if="hasSelectionColumn"
161
+ width="45px"
162
+ align="center"
163
+ type="selection"
164
+ :fixed="leftFixedColumnCount > 0 ? 'left' : ''"
165
+ class-name="no-inner-cell-border"
166
+ />
167
+ <!-- 编号列 -->
168
+ <el-table-column
169
+ v-if="hasIndexColumn"
170
+ min-width="30px"
171
+ type="index"
172
+ :fixed="leftFixedColumnCount > 0 ? 'left' : ''"
173
+ class-name="no-inner-cell-border"
174
+ />
175
+ <!-- 颜色选择列 -->
176
+ <el-table-column
177
+ v-if="colorList && colorList.length > 0"
178
+ width="22px"
179
+ class-name="editable-table__color-column no-inner-cell-border"
180
+ :fixed="leftFixedColumnCount > 0 ? 'left' : ''"
181
+ >
182
+ <template #header>
183
+ <div class="editable-table__color-icon" />
184
+ </template>
185
+ <template #default="scope">
186
+ <el-popover
187
+ ref="colorPopoverRef"
188
+ placement="right"
189
+ trigger="click"
190
+ popper-class="color-popover"
191
+ >
192
+ <div class="color-list">
193
+ <div
194
+ v-for="color in colorList"
195
+ :key="color.id"
196
+ class="color-list__item"
197
+ :style="{ backgroundColor: color.sampleColor }"
198
+ @click="handleColorChange(color.id, scope)"
199
+ >
200
+ <span :style="{color: color.textColor}">{{ color.name }}</span>
201
+ </div>
202
+ </div>
203
+ <template slot="reference">
204
+ <!-- 没有这个包裹的div标签,在拖动换行之后,无法调起颜色选择弹窗 -->
205
+ <div>
206
+ <div
207
+ v-if="isDefaultColor(scope.row.colorId)"
208
+ class="editable-table__color-icon"
209
+ />
210
+ <div
211
+ v-else
212
+ class="editable-table__selected-color"
213
+ :style="{ backgroundColor: getColorById(scope.row.colorId, 'sample') }"
214
+ />
215
+ </div>
216
+ </template>
217
+ </el-popover>
218
+ </template>
219
+ </el-table-column>
220
+ <!-- 这里的key很重要,必须保证每次生成的key都不一样,这样列排序功能才能生效,否则,即使actualColumns数组元素顺序发生变化,列顺序也不会改变。原因未知 -->
221
+ <el-table-column
222
+ v-for="(column, index) in actualColumns"
223
+ :key="column.prop + index"
224
+ resizable
225
+ class-name="editable-table__data-column"
226
+ :filtered-value="Array.isArray(filteredValue[column.prop]) ? filteredValue[column.prop] : []"
227
+ v-bind="getColumnBindProps(column)"
228
+ >
229
+ <template
230
+ v-if="showColumnHeadSortIcon(column)"
231
+ #header="scope"
232
+ >
233
+ <el-popover
234
+ ref="sortFilterPopoverRef"
235
+ placement="bottom"
236
+ trigger="click"
237
+ popper-class="editable-table__sort-filter"
238
+ :data-prop="column.prop"
239
+ @show="handleHeaderPopoverShow(column)"
240
+ >
241
+ <template slot="reference">
242
+ <!-- 筛选中,或排序中,高亮 -->
243
+ <span :class="['editable-table__sort-reference', isColumnHeadActive(column) && 'editable-table__sort-reference--active']">
244
+ {{ column.label }}
245
+ <div :class="['editable-table__sort-icon', isColumnHeadActive(column) && 'editable-table__sort-icon--active']" />
246
+ </span>
247
+ </template>
248
+ <div class="sort-filter">
249
+ <div class="sort-filter__column-title">
250
+ {{ column.label }}
251
+ </div>
252
+ <div
253
+ v-if="column.isColumnSortable"
254
+ class="sort-filter__sort"
255
+ >
256
+ <div class="sort-filter__sort-title">
257
+ 排序
258
+ </div>
259
+ <div class="sort-filter__sort-btns">
260
+ <el-button
261
+ :class="['sort-filter__sort-btn', tempSortingColumn?.prop === column.prop && tempSortType === 'ascending' && 'sort-filter__sort-btn--active']"
262
+ @click="handleSort('ascending', column)"
263
+ >
264
+ 升序
265
+ </el-button>
266
+ <el-button
267
+ :class="['sort-filter__sort-btn', tempSortingColumn?.prop === column.prop && tempSortType === 'descending' && 'sort-filter__sort-btn--active']"
268
+ @click="handleSort('descending', column)"
269
+ >
270
+ 降序
271
+ </el-button>
272
+ </div>
273
+ </div>
274
+ <div
275
+ v-if="column.search && !Array.isArray(column.search)"
276
+ class="sort-filter__search"
277
+ >
278
+ <div class="sort-filter__search-title">
279
+ 搜索
280
+ </div>
281
+ <el-input
282
+ v-model="tempSearchValue[column.prop]"
283
+ class="sort-filter__search-input"
284
+ placeholder="请输入内容"
285
+ />
286
+ </div>
287
+
288
+ <div
289
+ v-if="column.search && Array.isArray(column.search)"
290
+ class="sort-filter__search"
291
+ style="display: flex;flex-direction: column;gap: 12px;"
292
+ >
293
+ <div
294
+ v-for="item in column.search"
295
+ :key="item.prop"
296
+ >
297
+ <div class="sort-filter__search-title">
298
+ {{ item.label }}
299
+ </div>
300
+ <el-input
301
+ v-model="tempSearchValue[item.prop]"
302
+ class="sort-filter__search-input"
303
+ placeholder="请输入内容"
304
+ />
305
+ </div>
306
+ </div>
307
+
308
+ <div
309
+ v-if="column.filters && ((Array.isArray(column.filters) ? column.filters : column.filters.options).length > 0)"
310
+ class="sort-filter__filter"
311
+ >
312
+ <div class="sort-filter__filter-title">
313
+ 筛选
314
+ </div>
315
+ <el-checkbox-group
316
+ v-if="column.filters && (Array.isArray(column.filters) || column.filters.type === 'checkbox')"
317
+ v-model="tempFilteredValue[column.prop]"
318
+ class="sort-filter__filter-checkbox-group"
319
+ >
320
+ <el-checkbox
321
+ v-for="item in (Array.isArray(column.filters) ? column.filters : column.filters.options)"
322
+ :key="item.value"
323
+ :label="item.value"
324
+ class="sort-filter__filter-checkbox"
325
+ >
326
+ <slot
327
+ :name="column.prop + '-filter-item'"
328
+ v-bind="item"
329
+ >
330
+ {{ item.text }}
331
+ </slot>
332
+ </el-checkbox>
333
+ </el-checkbox-group>
334
+
335
+ <el-radio-group
336
+ v-if="column.filters && !Array.isArray(column.filters) && column.filters.type === 'radio'"
337
+ v-model="tempFilteredValue[column.prop]"
338
+ style="display: flex;flex-direction: column;gap: 6px;"
339
+ >
340
+ <el-radio
341
+ v-for="item in column.filters.options"
342
+ :key="item.value"
343
+ :label="item.value"
344
+ >
345
+ <slot
346
+ :name="column.prop + '-filter-item'"
347
+ v-bind="item"
348
+ >
349
+ {{ item.text }}
350
+ </slot>
351
+ </el-radio>
352
+ </el-radio-group>
353
+ </div>
354
+ <div
355
+ v-if="column.summary"
356
+ class="sort-filter__filter"
357
+ >
358
+ <div class="sort-filter__filter-title">
359
+ 统计
360
+ </div>
361
+ <el-checkbox-group
362
+ v-model="tempSummaryList"
363
+ class="sort-filter__filter-checkbox-group"
364
+ >
365
+ <el-checkbox
366
+ :label="column.prop"
367
+ class="sort-filter__filter-checkbox"
368
+ >
369
+ <slot
370
+ :name="column.prop + '-summay-item'"
371
+ v-bind="column"
372
+ >
373
+ {{ column.label }}
374
+ </slot>
375
+ </el-checkbox>
376
+ </el-checkbox-group>
377
+ </div>
378
+ <div class="sort-filter__footer">
379
+ <el-button
380
+ class="sort-filter__reset-btn"
381
+ @click="handleHeaderOperationReset(column, scope)"
382
+ >
383
+ 重置
384
+ </el-button>
385
+ <el-button
386
+ class="sort-filter__confirm-btn"
387
+ type="primary"
388
+ @click="handleHeaderOperationConfirm(column, scope)"
389
+ >
390
+ 确定
391
+ </el-button>
392
+ </div>
393
+ </div>
394
+ </el-popover>
395
+ </template>
396
+ <!-- 默认操作按钮,defaultOperations属性不为空数组时展示。编辑状态下隐藏 -->
397
+ <template
398
+ v-if="column.prop === '$$operation'"
399
+ #default="scope"
400
+ >
401
+ <el-popover
402
+ v-if="editingRowIndex !== scope.$index"
403
+ ref="operationPopoverRef"
404
+ placement="bottom"
405
+ trigger="click"
406
+ popper-class="operation-popover"
407
+ >
408
+ <div
409
+ slot="reference"
410
+ class="operation-popover__operation-reference btn-pointer"
411
+ >
412
+ <el-button :class="['operation-popover__operation-btn', hoveringCellInfo.rowIndex === scope.$index && 'operation-popover__operation-btn--active']">
413
+ 操作
414
+ </el-button>
415
+ </div>
416
+ <div class="operation-popover__operation">
417
+ <div
418
+ v-if="defaultOperations.includes('delete')"
419
+ class="operation-popover__operation-item btn-pointer"
420
+ @click="handleDelete(scope.row, scope.$index)"
421
+ >
422
+ 删除
423
+ </div>
424
+ <div
425
+ v-if="defaultOperations.includes('edit')"
426
+ class="operation-popover__operation-item btn-pointer"
427
+ @click="handleEdit(scope)"
428
+ >
429
+ 编辑
430
+ </div>
431
+ <div
432
+ v-if="defaultOperations.includes('top')"
433
+ class="operation-popover__operation-item btn-pointer"
434
+ @click="handleRowPinToTop(scope)"
435
+ >
436
+ 置顶
437
+ </div>
438
+ <slot
439
+ name="custom-operation"
440
+ v-bind="scope"
441
+ />
442
+ </div>
443
+ </el-popover>
444
+ <div
445
+ v-else
446
+ class="operation-popover__save-cancel"
447
+ >
448
+ <div
449
+ class="btn-pointer operation-popover__btn"
450
+ @click="handleEditSave(scope.row)"
451
+ >
452
+ 保存
453
+ </div>
454
+ <div
455
+ class="btn-pointer operation-popover__btn"
456
+ @click="handleEditCancel(scope.row)"
457
+ >
458
+ 取消
459
+ </div>
460
+ </div>
461
+ </template>
462
+ <!-- 默认渲染、非编辑态,不需要特殊处理,el-table负责渲染 -->
463
+ <!-- 自定义插槽 -->
464
+ <template
465
+ v-else-if="column.slotName"
466
+ #default="scope"
467
+ >
468
+ <!-- 非编辑态 -->
469
+ <slot
470
+ v-if="scope.$index !== editingRowIndex"
471
+ v-bind="scope"
472
+ :name="column.slotName"
473
+ />
474
+ <!-- 编辑态 -->
475
+ <template v-else-if="scope.$index === editingRowIndex">
476
+ <!-- 自定义编辑 -->
477
+ <slot
478
+ v-if="column.editSlotName"
479
+ v-bind="{scope, column}"
480
+ :name="column.editSlotName"
481
+ />
482
+ <!-- 内置编辑类型 -->
483
+ <div v-else-if="column.editType">
484
+ <div v-if="column.editType === 'input'">
485
+ <el-input
486
+ v-model="editingRowData[column.prop]"
487
+ clearable
488
+ />
489
+ </div>
490
+ <div v-if="column.editType === 'select'">
491
+ <el-select v-model="editingRowData[column.prop]">
492
+ <el-option
493
+ v-for="item in column.selectOptions"
494
+ :key="item.label"
495
+ :label="item.label"
496
+ :value="item.value"
497
+ />
498
+ </el-select>
499
+ </div>
500
+ <el-date-picker
501
+ v-if="column.editType === 'date'"
502
+ v-model="editingRowData[column.prop]"
503
+ type="date"
504
+ placeholder="选择日期"
505
+ />
506
+ </div>
507
+ <!-- 不支持编辑 -->
508
+ <slot
509
+ v-else
510
+ v-bind="scope"
511
+ :name="column.slotName"
512
+ />
513
+ </template>
514
+ </template>
515
+ <!-- 默认渲染列,编辑态 -->
516
+ <template
517
+ v-else-if="editingRowIndex !== -1"
518
+ #default="scope"
519
+ >
520
+ <!-- 当前行编辑中,内置编辑类型 -->
521
+ <template v-if="editingRowIndex === scope.$index && column.editType">
522
+ <div v-if="column.editType === 'input'">
523
+ <el-input
524
+ v-model="editingRowData[column.prop]"
525
+ clearable
526
+ type="text"
527
+ />
528
+ </div>
529
+ <div v-if="column.editType === 'select'">
530
+ <el-select v-model="editingRowData[column.prop]">
531
+ <el-option
532
+ v-for="item in column.selectOptions"
533
+ :key="item.value"
534
+ :label="item.label"
535
+ :value="item.value"
536
+ />
537
+ </el-select>
538
+ </div>
539
+ <div v-if="column.editType === 'date'">
540
+ <el-date-picker
541
+ v-model="editingRowData[column.prop]"
542
+ type="date"
543
+ placeholder="选择日期"
544
+ />
545
+ </div>
546
+ </template>
547
+ <!-- 当前行编辑中,自定义编辑类型 -->
548
+ <slot
549
+ v-else-if="column.editSlotName && scope.$index === editingRowIndex"
550
+ v-bind="scope"
551
+ :name="column.editSlotName"
552
+ />
553
+ <!-- 当前行非编辑中 -->
554
+ <template v-else>
555
+ {{ column.formatter ? column.formatter(scope.row, column, scope.row[column.prop], scope.$index) : scope.row[column.prop] }}
556
+ </template>
557
+ </template>
558
+ <!-- hover状态渲染 -->
559
+ <template
560
+ v-else-if="column.hoverSlotName"
561
+ #default="scope"
562
+ >
563
+ <slot
564
+ v-if="scope.$index === hoveringCellInfo.rowIndex && column.prop === hoveringCellInfo.columnProperty"
565
+ v-bind="scope"
566
+ :name="column.hoverSlotName"
567
+ />
568
+ <slot
569
+ v-else-if="column.slotName"
570
+ v-bind="scope"
571
+ :name="column.slotName"
572
+ />
573
+ <template v-else>
574
+ {{ column.formatter ? column.formatter(scope.row, column, scope.row[column.prop], scope.$index) : scope.row[column.prop] }}
575
+ </template>
576
+ </template>
577
+ </el-table-column>
578
+ </el-table>
579
+ <div class="pagination-wrap">
580
+ <div>共{{ total }}项数据</div>
581
+ <el-pagination
582
+ background
583
+ layout="sizes, prev, pager, next, jumper"
584
+ :page-sizes="[10, 15, 30, 60, 100]"
585
+ :page-size.sync="pageSize"
586
+ :pager-count="11"
587
+ :current-page="currentPage"
588
+ :total="total"
589
+ @size-change="handlePageSizeChange"
590
+ @current-change="handleCurrPageChange"
591
+ />
592
+ </div>
593
+ </div>
594
+ </template>
595
+
596
+ <script lang="ts" setup>
597
+ import { computed, nextTick, ref, watch } from 'vue';
598
+ import { Message } from 'element-ui'
599
+ import usePagination from './usePagination';
600
+ import useCellHover from './useCellHover';
601
+ import useViewSetting from './useViewSetting';
602
+ import useRowBgColor from './useRowBgColor';
603
+ import useDefaultOperation from './useDefaultOperation'
604
+ import useColumnHeaderOperation from './useColumnHeaderOperation';
605
+ import useDragSort from './useDragSort';
606
+ import { ITableDataItem, IColumnConfig, IDefaultOperationType, IColorList } from './types';
607
+
608
+ // defineProps泛型参数如果从外部传入,编译报错
609
+ interface IProps {
610
+ /** 表格数据 */
611
+ dataList: ITableDataItem[];
612
+ /** 列配置 */
613
+ columnConfig: IColumnConfig[];
614
+ /** 是否展示展开行 */
615
+ hasExpandRow?: boolean;
616
+ /** 是否展示序号 */
617
+ hasIndexColumn?: boolean;
618
+ /** 是否展示选择列 */
619
+ hasSelectionColumn?: boolean;
620
+ /** 是否支持行拖拽 */
621
+ rowDragAble?: boolean;
622
+ /** 表格总条数 */
623
+ total: number;
624
+ /** 自定义列操作,当前支持行编辑(edit),删除(delete),置顶(top) */
625
+ defaultOperations?: IDefaultOperationType[];
626
+ /** 行背景色列表 */
627
+ colorList?: IColorList;
628
+ /** 左侧固定列数 */
629
+ leftFixedCount?: number;
630
+ /** 性能优化参数,调整拖拽的范围 */
631
+ dragSemiRange?: number;
632
+ /** 是否显示加载 */
633
+ loading?: boolean;
634
+ /** 是否隐藏显示设置按钮 */
635
+ hideViewSettingBtn?: boolean
636
+ /** 设置的缓存的key */
637
+ settingStorgeKey?: string
638
+ /** 前端排序,默认关闭 */
639
+ localSort?: boolean
640
+ /** 前端过滤,默认关闭 */
641
+ localFilter?: boolean
642
+ /** 页码 */
643
+ currentPage: number
644
+ }
645
+
646
+ interface IEmits {
647
+ /** 行选中 */
648
+ (e: 'selection-change', selection: any): void
649
+ /** 修改行背景色 */
650
+ (e: 'row-bg-change', param: {colorId: number; row: ITableDataItem; rowIndex: number}): void
651
+ /** 行拖拽放置事件 */
652
+ (e: 'row-drag-drop', param: { row: any; fromIndex: number; toIndex: number; page: number; size: number;}): void
653
+ /** 行删除 */
654
+ (e: 'row-delete', param: { row: any; index: number; page: number; size: number; }): void
655
+ /** 点击编辑按钮立即触发 */
656
+ (e: 'row-edit', param: { row: any, index: number; page: number; size: number;}): void
657
+ /** 行置顶 */
658
+ (e: 'row-pin-to-top', param: { row: any, rawIndex: number; page: number; size: number;}): void
659
+ /** 行编辑保存 */
660
+ (e: 'row-edit-save', param: { page: number; size: number; row: any; changedData: Record<string, any>; }): void
661
+ /** 行编辑取消 */
662
+ (e: 'row-edit-cancel', param: { row: any; page: number; size: number;}): void
663
+ /** 页码改变 */
664
+ (e: 'page-change', param: { page: number, size: number }): void
665
+ /** 查询 */
666
+ (e: 'search', param: Record<string, any>): void
667
+ /** 排序 */
668
+ (e: 'sort-change', param: { order: 'descending' | 'ascending' | null, prop: string }): void
669
+ }
670
+
671
+ const props = withDefaults(defineProps<IProps>(), {
672
+ dataList: () => [],
673
+ columnConfig: () => [],
674
+ hasExpandRow: false,
675
+ hasIndexColumn: false,
676
+ hasSelectionColumn: false,
677
+ rowDragAble: false,
678
+ total: 0,
679
+ defaultOperations: () => [],
680
+ colorList: () => [],
681
+ leftFixedCount: 1,
682
+ dragSemiRange: 15,
683
+ loading: false,
684
+ hideViewSettingBtn: false,
685
+ settingStorgeKey: '',
686
+ localSort: false,
687
+ localFilter: false,
688
+ currentPage: 1,
689
+ })
690
+
691
+ // 同defineProps一样,不支持泛型参数从外部导入
692
+ const emit = defineEmits<IEmits>()
693
+
694
+ const showingColumns = ref<string[]>([]); // 表格中实际展示的列
695
+
696
+ const actualColumns = computed(() => {
697
+ const res: IColumnConfig[] = [];
698
+ let cnt = leftFixedColumnCount.value
699
+
700
+ // 列排序和列过滤
701
+ for (const prop of showingColumns.value) {
702
+ const rawItem = props.columnConfig.find(c => c.prop === prop) ?? {} as IColumnConfig;
703
+ const item: IColumnConfig = {
704
+ ...rawItem,
705
+ isColumnSortable: rawItem.sortable,
706
+ sortable: inSorting.value ? rawItem.sortable : false
707
+ };
708
+ if (cnt > 0) {
709
+ item.fixed = 'left';
710
+ // eslint-disable-next-line no-plusplus
711
+ cnt--;
712
+ } else {
713
+ item.fixed = undefined;
714
+ }
715
+ res.push(item);
716
+ }
717
+
718
+ // 使用默认操作项,添加默认操作列。该列在编辑模式下隐藏
719
+ if (props.defaultOperations && props.defaultOperations.length > 0) {
720
+ res.push({
721
+ label: '操作',
722
+ prop: '$$operation',
723
+ 'min-width': '100px',
724
+ fixed: 'right'
725
+ });
726
+ }
727
+
728
+ return res;
729
+ })
730
+
731
+ const sortFilterPopoverRef = ref<any>(null);
732
+ const tableDomRef = ref<any>(null);
733
+ const currScope = ref<any>(null);
734
+
735
+ /************ 分页相关 *************/
736
+ const beforePageChange = () => {
737
+ searchValue.value = {};
738
+ }
739
+ const {
740
+ pageSize,
741
+ handleCurrPageChange,
742
+ handlePageSizeChange,
743
+ } = usePagination({
744
+ emit,
745
+ beforePageChange
746
+ })
747
+
748
+ /************ 表格单元格hover事件相关 ************ */
749
+ const {
750
+ hoveringCellInfo,
751
+ setCellClassName,
752
+ debouncedHoverHandler
753
+ } = useCellHover(tableDomRef)
754
+
755
+ /************ 行背景色设置相关 ************ */
756
+ const {
757
+ isDefaultColor,
758
+ getColorById,
759
+ setRowStyle,
760
+ handleColorChange,
761
+ colorPopoverRef
762
+ } = useRowBgColor({
763
+ colorList: props.colorList,
764
+ emit
765
+ })
766
+
767
+ /************ 默认的操作相关 ************ */
768
+ const {
769
+ operationPopoverRef,
770
+ editingRowData,
771
+ editingRowIndex,
772
+ handleDelete,
773
+ handleEdit,
774
+ handleEditSave,
775
+ handleEditCancel,
776
+ handleRowPinToTop,
777
+ closeAllExpandedRows
778
+ } = useDefaultOperation({
779
+ emit,
780
+ tableDomRef,
781
+ pageSize,
782
+ props,
783
+ hasExpandRow: props.hasExpandRow
784
+ })
785
+
786
+ /************ 显示设置相关 ************ */
787
+ const {
788
+ viewSettingDragSortOptions,
789
+ columnsToBeShown,
790
+ viewSettingVisible,
791
+ leftFixedColumnCount,
792
+ tempLeftFixedColumnCount,
793
+ handleInputTempLeftFixedColumnCount,
794
+ handleViewSettingShow,
795
+ handleViewSettingClose,
796
+ handleViewSettingConfirm
797
+ } = useViewSetting({
798
+ tableDomRef,
799
+ showingColumns,
800
+ actualColumns,
801
+ props
802
+ })
803
+
804
+ /************ 列头部操作相关 ************ */
805
+ const {
806
+ setSort,
807
+ clearSort,
808
+ setSearchParams,
809
+ isColumnHeadActive,
810
+ handleSort,
811
+ handleHeaderPopoverShow,
812
+ handleHeaderOperationConfirm,
813
+ handleHeaderOperationReset,
814
+ summaryList,
815
+ tableSummaryMethod,
816
+ filteredValue,
817
+ showColumnHeadSortIcon,
818
+ tempSortingColumn,
819
+ tempSearchValue,
820
+ tempFilteredValue,
821
+ tempSummaryList,
822
+ tempSortType,
823
+ sortingColumn,
824
+ isColumnFiltering,
825
+ searchValue,
826
+ inSorting,
827
+ } = useColumnHeaderOperation({
828
+ tableDomRef,
829
+ sortFilterPopoverRef,
830
+ props,
831
+ emit,
832
+ showingColumns,
833
+ })
834
+
835
+ /************ 表格行拖拽和显示设置列拖拽 ************ */
836
+ const beforeDragStart = () => {
837
+ // 如果有列存在排序,不允许拖拽
838
+ if (sortingColumn.value) {
839
+ Message.warning('已有列正在排序,不允许拖拽。');
840
+ return false;
841
+ }
842
+
843
+ // 如果有列存在筛选,不允许拖拽
844
+ if (isColumnFiltering.value) {
845
+ Message.warning('已有列正在筛选,不允许拖拽。');
846
+ return false;
847
+ }
848
+
849
+ editingRowIndex.value = -1; // 关闭编辑状态
850
+
851
+ return true
852
+ }
853
+
854
+ useDragSort({
855
+ emit,
856
+ props,
857
+ viewSettingDragSortOptions,
858
+ beforeDragStart,
859
+ pageSize,
860
+ currScope,
861
+ tableDomRef,
862
+ })
863
+
864
+ const doTableLayout = async () => {
865
+ await nextTick();
866
+ tableDomRef.value?.doLayout();
867
+ }
868
+
869
+ // 过滤出自定义属性,将其它属性全部透传给 el-table-column
870
+ const getColumnBindProps = (column: IColumnConfig) => {
871
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
872
+ const { editAble, editType, slotName, inputType, options, filters, ...rest } = column;
873
+ return rest;
874
+ }
875
+
876
+ const setRowClassName = (scope) => {
877
+ return `
878
+ custom-row-classname
879
+ custom-row-classname-${scope.rowIndex}
880
+ ${scope.row.isPinned ? 'custom-row-classname-pinned' : ''}
881
+ `
882
+ }
883
+
884
+ const handleSelectionChange = (e) => {
885
+ emit('selection-change', e);
886
+ }
887
+
888
+ const handleColumnClose = (item) => {
889
+ if (item.isAlwaysShow) return;
890
+ columnsToBeShown.value = columnsToBeShown.value.filter(c => c !== item.prop);
891
+ }
892
+
893
+ defineExpose({
894
+ closeAllExpandedRows,
895
+ openViewSetting: handleViewSettingShow,
896
+ elTableRef: tableDomRef,
897
+ setSort,
898
+ clearSort,
899
+ setSearchParams,
900
+ })
901
+
902
+ // loading 结束和页码变化时滚动到顶部
903
+ watch([
904
+ () => props.loading,
905
+ () => props.currentPage,
906
+ ], ([loading]) => {
907
+ if (loading) return;
908
+ tableDomRef.value.$el.querySelector('.el-table__body-wrapper').scrollTop = 0
909
+ })
910
+
911
+ </script>
912
+
913
+ <style lang="less">
914
+ @import './index.less';
842
915
  </style>