el-plus-crud 0.0.94 → 0.0.95

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,897 +1,897 @@
1
- <template>
2
- <div class="el-plus-table-content">
3
- <!-- <div class="select-items" v-if="type === 'selection' && selectRowList && selectRowList.length > 0">
4
- <span class="title">已选择({{ selectRowList.length }}个): </span>
5
- <el-tag v-for="(item, i) in selectRowList" :key="item.id" class="select-item" size="small" closable @close="() => handelRemoveSelect(i, item)">
6
- {{ item[tableConfig.showSelectNameKey || 'name'] }}
7
- </el-tag>
8
- </div> -->
9
- <EleTabletHeader ref="tableHeaderRef" v-if="Object.keys(tableConfig?.toolbar || {}).length || tableConfig.tbName" v-model="toolFormData" :tbName="tableConfig.tbName" :column="tableConfig?.column || []" :size="size" :isShowRefresh="isShowRefresh" :loading="loading" :toolbar="tableConfig.toolbar" :isDialog="isDialog" @query="handelTopQuery" />
10
-
11
- <!-- 中部的Tabs -->
12
- <div class="table-tabs-panel" v-if="tableConfig.tabConf">
13
- <el-radio-group v-model="tableTabVal" size="default" @change="handelTabChange">
14
- <el-radio-button v-for="(item, i) in tableConfig.tabConf?.tabs" :key="i" :label="item.value" :loading="true">
15
- {{ item.label }}
16
- <template v-if="loadingTab">
17
- <el-icon class="is-loading"><Loading /></el-icon>
18
- </template>
19
- <template v-else>
20
- {{ getTabLabel(item) }}
21
- </template>
22
- </el-radio-button>
23
- </el-radio-group>
24
- </div>
25
-
26
- <!-- 中间列表 -->
27
- <div class="el-plus-table-main" v-loading="loading">
28
- <template v-if="isDIYMain">
29
- <slot name="main" :tableData="tableData"></slot>
30
- </template>
31
- <!-- 这里开始是表格内容 -->
32
- <el-table
33
- ref="elPlusTableRef"
34
- v-else
35
- style="width: 100%"
36
- height="100%"
37
- :maxHeight="tableConfig.maxHeight || 'auto'"
38
- v-bind="tableConfig.tableAttr"
39
- :class="{ 'big-h-bar': tableConfig?.tableAttr?.bigHBar, 'big-v-bar': tableConfig.tableAttr?.bigVBar }"
40
- :data="tableData"
41
- :row-key="rowKey"
42
- lazy
43
- :load="loadExpandData"
44
- :size="size"
45
- :row-class-name="initRowClassName"
46
- @select="handelTableSelect"
47
- @select-all="handelTableSelectAll"
48
- @expand-change="handelTableExpandChange"
49
- :treeProps="treeProps"
50
- :span-method="handelSpanMethod"
51
- >
52
- <!-- 复选框 -->
53
- <el-table-column v-if="type === 'selection'" type="selection" width="55px" :selectable="selectable" header-align="center" align="center" />
54
- <!-- 下标 -->
55
- <el-table-column v-if="isIndex" type="index" width="60" label="序号" :headerAlign="headerAlign" />
56
- <!-- 首列 -->
57
- <template v-if="useSlots().firstColumn">
58
- <slot name="firstColumn" />
59
- </template>
60
- <!-- 一级或多级列 -->
61
- <ElPlusTableColumn v-for="(item, i) in headerColumns" :key="i" :item="item" :size="size"></ElPlusTableColumn>
62
-
63
- <!-- 空 -->
64
- <template v-if="!loading && loadingStatus === 2" #empty>
65
- <el-empty v-if="isEmptyImg" :description="nullText" />
66
- <span v-else>{{ nullText }}</span>
67
- </template>
68
- <!-- 合计行 -->
69
- <template v-if="tableConfig.summaryConf?.prop" #append>
70
- <div :style="tableConfig.summaryConf?.hstyle" class="summary-row" v-if="summaryList && summaryList.length > 0">
71
- <div class="summary-item" v-for="(item, i) in summaryList" :key="i">
72
- <span>{{ item.label || '合计' }}:</span>
73
- <p>{{ item.value || 0 }}</p>
74
- </div>
75
- </div>
76
- </template>
77
- </el-table>
78
- </div>
79
- <div class="bottom-page-static-info" v-if="isPager || tableConfig.statistic">
80
- <el-pagination class="page-info" small @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="+pageInfo.current" :page-sizes="[5, 10, 20, 50, 100]" :page-size="pageInfo.size" layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total" />
81
- </div>
82
- <slot name="bottom"></slot>
83
- </div>
84
- </template>
85
- <script lang="ts">
86
- export default {
87
- name: 'ElPlusTable',
88
- inheritAttrs: false,
89
- customOptions: {}
90
- }
91
- </script>
92
- <script lang="ts" setup>
93
- import { ref, reactive, onMounted, computed, watch, nextTick, useSlots, inject, provide } from 'vue'
94
- import EleTabletHeader from './components/header.vue'
95
- import ElPlusTableColumn from './ElPlusTableColumn.vue'
96
- import { handelListColumn } from './util'
97
- import { cloneDeep } from 'lodash'
98
- import { Loading } from '@element-plus/icons-vue'
99
- import type { TableColumnCtx } from 'element-plus'
100
- import { ICRUDConfig, ITableConfig, ITableTabItem, ITreeProps } from 'types'
101
-
102
- interface SpanMethodProps {
103
- row: any
104
- column: TableColumnCtx<any>
105
- rowIndex: number
106
- columnIndex: number
107
- }
108
-
109
- const defaultConf = inject('defaultConf') as ICRUDConfig
110
- const format = inject('format') as any
111
-
112
- const emits = defineEmits(['getUrlConsumerIds', 'selection', 'select', 'selectAll', 'update:modelValue', 'tabChange', 'expandChange', 'inited'])
113
- const props = withDefaults(
114
- defineProps<{
115
- tableConfig: ITableConfig
116
- // 外部数据
117
- modelValue?: Array<any> | null
118
- // 列表类型 默认为index
119
- type?: string
120
- // 是否显示第一列的Index下标
121
- isIndex?: boolean
122
- // 是否分页
123
- isPager?: boolean
124
- // 每页显示条数
125
- pageSize?: number
126
- // 是否在弹框中
127
- isDialog?: boolean
128
- // 空占位文本
129
- nullText?: string
130
- // 是否显示暂无数据的图片
131
- isEmptyImg?: boolean
132
- // 是否显示刷新文本
133
- isShowRefresh?: boolean
134
- // rowKey默认为id
135
- rowKey?: string
136
- // 自定义table主体
137
- isDIYMain?: boolean
138
- // 默认选中项
139
- selectList?: Array<any> | []
140
- // 默认列宽
141
- colMinWidth?: string
142
- // 表头文本排列方式
143
- headerAlign?: 'left' | 'right' | 'center'
144
- }>(),
145
- {
146
- modelValue: null,
147
- type: 'index',
148
- isIndex: true,
149
- isPager: true,
150
- pageSize: 10,
151
- isDialog: false,
152
- nullText: '暂无数据',
153
- isShowRefresh: true,
154
- rowKey: 'id',
155
- isEmptyImg: true,
156
- isDIYMain: false,
157
- selectList: () => [],
158
- colMinWidth: 'auto',
159
- headerAlign: 'left'
160
- }
161
- )
162
-
163
- // 合并行算法
164
- const needSpanCellIndex = ref([] as any[])
165
- const handelSpanMethod = ref(null as unknown as Function)
166
-
167
- const elPlusTableRef = ref()
168
-
169
- // 顶部Tab数据
170
- const tabStatic = ref({} as any)
171
- const tableTabVal = ref(props.tableConfig?.tabConf?.tabs[props.tableConfig?.tabConf?.default || 0].value ?? '')
172
- // const tableTabVal = computed(() => {
173
- // if (props.tableConfig?.tabConf) {
174
- // if (props.tableConfig?.tabConf?.defaultVal !== undefined && props.tableConfig?.tabConf?.defaultVal !== null) {
175
- // return props.tableConfig?.tabConf?.defaultVal
176
- // }
177
- // return props.tableConfig?.tabConf?.tabs[props.tableConfig?.tabConf?.default || 0].value ?? ''
178
- // }
179
- // return ''
180
- // })
181
- const getTabLabel = computed(() => (item: ITableTabItem) => {
182
- if (item.key) {
183
- return tabStatic.value[item.key] || 0
184
- }
185
- return ''
186
- })
187
-
188
- const selectable = computed(() => (row: any, index: number) => {
189
- if (typeof props.tableConfig?.tableAttr?.selectable === 'function') {
190
- return props.tableConfig?.tableAttr?.selectable(row, index)
191
- }
192
- return true
193
- })
194
-
195
- const tableHeaderRef = ref()
196
-
197
- // 加载
198
- const loading = ref(false)
199
- const loadingTab = ref(!!props.tableConfig?.tabConf?.fetch)
200
- const listLoading = ref(false)
201
- const size = defaultConf.size || 'default'
202
-
203
- // 顶部查询条件数据缓存
204
- const topQueryData = ref({} as any)
205
-
206
- // 数据
207
- let toolFormData = reactive({} as any)
208
- const tableData = ref((props.modelValue || []) as any[])
209
- const haveClassRowList = reactive([])
210
-
211
- // 0:未加载; 1:加载中;2:加载完成
212
- const loadingStatus = ref(0)
213
-
214
- // 保存所有的选中行
215
- const allSelectRowList = reactive((cloneDeep(props.selectList) || []) as any[])
216
- // 记录树形展开的数组的下标
217
- const treeIndexList = reactive([] as any[][])
218
-
219
- // 分页信息
220
- const pageInfo = reactive({
221
- current: !props.isDialog && toolFormData.current ? parseInt(toolFormData.current as any) : 1,
222
- total: 0,
223
- size: !props.isDialog && toolFormData.size ? parseInt(toolFormData.size as any) : props.pageSize
224
- })
225
- // 数型解析
226
- const treeProps = (props.tableConfig?.explan?.treeProps || { children: 'children', hasChildren: 'hasChildren' }) as ITreeProps
227
-
228
- // 处理后的列显示
229
- const headerColumns = computed(() => {
230
- const tempList = handelListColumn(props.tableConfig?.column, defaultConf, props.tableConfig?.tbName || '', props.headerAlign, props.isDialog ? 'auto' : props.colMinWidth)
231
- // 这里重构一下合并行算法
232
- // 获取所有列
233
- const allColumn = getColumList(tempList)
234
- // 查询是否有合并行属性
235
- needSpanCellIndex.value = []
236
-
237
- const tempColList = allColumn
238
- .map((item, i) => {
239
- // 合并行
240
- if (item.isRowSpan) {
241
- needSpanCellIndex.value.push(i)
242
- let value = undefined as any
243
- let first = 0
244
- let count = 1
245
- // 这里修改data数据
246
- tableData.value?.map((row, j) => {
247
- if (value !== row[item.prop]) {
248
- if (count > 1 && j > 0) {
249
- // 这里要设置之前的数据合并行数
250
- tableData.value[first]['rowSpan_' + i] = count
251
- }
252
- first = j
253
- count = 1
254
- value = row[item.prop]
255
- } else {
256
- count += 1
257
- row['rowSpan_' + i] = 0
258
- }
259
- if (j === tableData.value.length - 1) {
260
- // 这里要设置之前的数据合并行数
261
- tableData.value[first]['rowSpan_' + i] = count
262
- }
263
- })
264
- }
265
- // 返回需要合并列的列数据
266
- return item.isColSpan ? i : null
267
- })
268
- .filter((item) => item !== null) as number[]
269
-
270
- if (tempColList.length) {
271
- // 这里开始合并列-遍历行数据
272
- tableData.value?.map((row, j) => {
273
- let value = undefined as any
274
- let first = 0
275
- let count = 1
276
- // 遍历表头
277
- tempColList.map((val: number, i: number) => {
278
- // 不存在时才添加
279
- if (!needSpanCellIndex.value.includes(val)) {
280
- needSpanCellIndex.value.push(val)
281
- }
282
- // 如果值不等 或者 列序号已经中断
283
- if (value !== row[allColumn[val].prop] || (i > 0 && val - 1 !== tempColList[i - 1])) {
284
- if (count > 1) {
285
- // 这里要设置之前的数据合并行数
286
- row['colSpan_' + first] = count
287
- }
288
- first = val
289
- count = 1
290
- value = row[allColumn[val].prop]
291
- } else {
292
- count += 1
293
- row['colSpan_' + val] = 0
294
- }
295
- // 如果是最后一个
296
- if (i === tempColList.length - 1) {
297
- // 这里要设置之前的数据合并行数
298
- row['colSpan_' + first] = count
299
- }
300
- })
301
- })
302
- }
303
- if (needSpanCellIndex.value.length > 0) {
304
- handelSpanMethod.value = ({ row, column, columnIndex }: SpanMethodProps) => {
305
- let tempColumnIndex = columnIndex
306
- // 这里要排除默认的列
307
- if (props.type === 'selection') tempColumnIndex -= 1
308
- if (props.isIndex) tempColumnIndex -= 1
309
- if (useSlots().firstColumn) tempColumnIndex -= 1
310
- if (needSpanCellIndex.value.includes(tempColumnIndex)) {
311
- return { rowspan: row['rowSpan_' + tempColumnIndex] ?? 1, colspan: row['colSpan_' + tempColumnIndex] ?? 1 }
312
- }
313
- }
314
- }
315
- return tempList
316
- })
317
-
318
- // 合计行数据
319
- const summaryList = computed(() => {
320
- const tempList = [] as any[]
321
- if (props.tableConfig.summaryConf?.vif) {
322
- if (typeof props.tableConfig.summaryConf?.vif === 'boolean') {
323
- if (!props.tableConfig.summaryConf?.vif) {
324
- return tempList
325
- }
326
- } else {
327
- if (!props.tableConfig.summaryConf?.vif()) {
328
- return tempList
329
- }
330
- }
331
- }
332
- if (props.tableConfig.summaryConf?.prop) {
333
- const propList = props.tableConfig.summaryConf.prop.split(',')
334
- const labelList = props.tableConfig.summaryConf?.label?.split(',') || []
335
- // 遍历
336
- propList.map((prop, i: number) => {
337
- let value = ''
338
- if (props.tableConfig.summaryConf?.sumFn) {
339
- value = props.tableConfig.summaryConf?.sumFn(tableData.value, allSelectRowList)
340
- } else {
341
- value = format.yuan(tableData.value.reduce((total: number, current) => (total += current[prop]), 0))
342
- }
343
- tempList.push({ label: labelList[i], value })
344
- })
345
- }
346
- return tempList
347
- })
348
-
349
- /**
350
- * 获取所有显示的列信息
351
- * @param list
352
- */
353
- function getColumList(list: Array<any>): Array<any> {
354
- const tempList = [] as any
355
- list?.map((item) => {
356
- if (item.children?.length) {
357
- tempList.push(...getColumList(item.children))
358
- } else {
359
- if (item.__vif) {
360
- tempList.push(item)
361
- }
362
- }
363
- })
364
- return tempList
365
- }
366
-
367
- /**
368
- * Tab切换
369
- * @param val
370
- */
371
- async function handelTabChange(val: string | number | boolean) {
372
- // 这里直接重新查询
373
- reload(true)
374
-
375
- // 通知父类 第二个参数是回调
376
- await emits('tabChange', val, initCol)
377
-
378
- // 这里触发下initCol
379
- nextTick(() => {
380
- initCol()
381
- })
382
- }
383
-
384
- /**
385
- * 加载自身展开的数据
386
- * @param row
387
- * @param treeNode
388
- * @param resolve
389
- */
390
- function loadExpandData(row: any, treeNode: any, resolve: any) {
391
- const postData = getListQueryData()
392
- postData[props.tableConfig?.explan?.idName || 'parentId'] = row.id
393
- props.tableConfig.fetch &&
394
- props.tableConfig.fetch(postData).then((pageInfo) => {
395
- resolve(pageInfo?.[props.tableConfig?.fetchMap?.list || 'records'])
396
- })
397
- }
398
-
399
- /**
400
- * 初始化每行的颜色
401
- * @param data
402
- */
403
- function initRowClassName(data: any) {
404
- if (haveClassRowList.length > data.rowIndex) {
405
- return haveClassRowList[data.rowIndex * 1]
406
- }
407
- return ''
408
- }
409
-
410
- /**
411
- * 处理单个选中的情况
412
- * @param selection
413
- * @param row
414
- */
415
- function handelTableSelect(selection: any[], item: any) {
416
- checkAndRemove(item, !selection.some((i) => i[props.rowKey] === item[props.rowKey]))
417
- // 通知父类
418
- emits('select', selection, item)
419
- emits('selection', cloneDeep(allSelectRowList))
420
- }
421
-
422
- /**
423
- * 处理全选
424
- * @param selection
425
- * @param row
426
- */
427
- function handelTableSelectAll(selection: any[]) {
428
- const isRemove = !(selection && selection.length > 0)
429
- ;(isRemove ? tableData.value : selection).map((item) => {
430
- checkAndRemove(item, isRemove)
431
- })
432
- // 通知父类
433
- emits('selectAll', selection, isRemove)
434
- emits('selection', cloneDeep(allSelectRowList))
435
- }
436
-
437
- /**
438
- * 处理行展开
439
- * @param row
440
- * @param expanded
441
- */
442
- function handelTableExpandChange(row: any, expanded: boolean) {
443
- // console.log('row: ', row, expanded)
444
- emits('expandChange', row, expanded)
445
- }
446
-
447
- /**
448
- * 选中或者取消
449
- * @param item
450
- */
451
- function checkAndRemove(item: any, isRemove: boolean = false) {
452
- // 校验是否已经有了
453
- const index = allSelectRowList.findIndex((i) => i[props.rowKey] === item[props.rowKey])
454
- if (isRemove) {
455
- if (index >= 0) {
456
- allSelectRowList.splice(index, 1)
457
- }
458
- } else {
459
- if (index < 0) {
460
- allSelectRowList.push(item)
461
- }
462
- }
463
- }
464
-
465
- /**
466
- * 外部修改选中项
467
- * @param item
468
- */
469
- function changeSelect(itemList: any[], isSelect: boolean = false) {
470
- if (itemList && itemList.length) {
471
- itemList.map((item) => {
472
- checkAndRemove(
473
- allSelectRowList.find((i) => i[props.rowKey] === item[props.rowKey]),
474
- !isSelect
475
- )
476
- })
477
- }
478
- // 这里要刷新一下tab的选中
479
- refreshTableSelect()
480
- }
481
-
482
- /**
483
- * 重置选中
484
- */
485
- function resetSelect() {
486
- allSelectRowList.splice(0, allSelectRowList.length, ...props.selectList)
487
- }
488
-
489
- /**
490
- * 封装分页查询条件
491
- */
492
- async function getListQueryData() {
493
- let queryMap = {
494
- // 封装查询条件
495
- // ...route.query,
496
- // ...tableHeaderRef.value.getData(),
497
- ...topQueryData.value,
498
- ...(typeof props.tableConfig.queryMap === 'function' ? await props.tableConfig.queryMap() : props.tableConfig.queryMap),
499
- t_: new Date().getTime()
500
- } as any
501
- if (props.isPager) {
502
- // 封装分页信息
503
- queryMap.current = pageInfo.current
504
- queryMap.size = pageInfo.size
505
- }
506
- // 这里处理一下列表Tab的查询条件
507
- if (props.tableConfig?.tabConf && props.tableConfig?.tabConf.prop) {
508
- queryMap[props.tableConfig?.tabConf.prop] = tableTabVal.value
509
- }
510
-
511
- queryMap = handelQueryData(queryMap)
512
- if (!props.isDialog) {
513
- // setQuery(route.path, queryMap)
514
- }
515
- // 这里是导出的权限判定-增加登陆信息
516
- if (props.tableConfig?.toolbar?.export?.isAuth) {
517
- // TODO GET Auth
518
- }
519
- return queryMap
520
- }
521
-
522
- /**
523
- * 处理下请求参数
524
- * @param queryMap
525
- */
526
- function handelQueryData(queryMap: object) {
527
- if (!queryMap) return {}
528
- const tempMap = {}
529
- Object.keys(queryMap).map((key) => {
530
- if (queryMap[key] !== undefined && queryMap[key] !== null && queryMap[key] !== '') {
531
- tempMap[key] = queryMap[key]
532
- }
533
- })
534
- return tempMap
535
- }
536
-
537
- /**
538
- * 处理显示
539
- * @param rowInfo
540
- */
541
- // function handelHeaderVIf(rowInfo: any) {
542
- // let vif = false
543
- // if (rowInfo.children && rowInfo.children.length > 0) {
544
- // vif = rowInfo.children.some((item: any) => {
545
- // return !(item.vif === false)
546
- // })
547
- // }
548
- // return vif
549
- // }
550
-
551
- /**
552
- * 处理列的显示
553
- * @param item
554
- */
555
- // function handelColumnVIf(item: any) {
556
- // if (item.vif !== undefined && item.vif !== null && item.vif !== '') {
557
- // if (typeof item.vif === 'boolean') return item.vif
558
- // if (typeof item.vif === 'function') return item.vif(item)
559
- // return !!item.vif
560
- // }
561
- // return true
562
- // }
563
-
564
- /**
565
- * 处理分页工具中pageSize变化
566
- * @param val
567
- */
568
- function handleSizeChange(val: number) {
569
- // 回到第一页
570
- pageInfo.current = 1
571
- pageInfo.size = val
572
- loadData(true)
573
- }
574
-
575
- /**
576
- * 处理分页工具中current变化
577
- * @param val
578
- */
579
- function handleCurrentChange(val: number) {
580
- if (pageInfo.current !== val) {
581
- pageInfo.current = val
582
- loadData(false)
583
- }
584
- }
585
-
586
- /**
587
- * 处理行展开
588
- * @param list
589
- * @param pIndexList
590
- */
591
- function handelTreeIndex(list: any[], pIndexList: number[]) {
592
- let tempList = []
593
- if (list && list.length > 0) {
594
- list.map((item, i) => {
595
- treeIndexList.push([...pIndexList, i])
596
- tempList = item[treeProps.children] as any[]
597
- if (tempList && tempList.length > 0) {
598
- handelTreeIndex(tempList, [i])
599
- }
600
- })
601
- }
602
- }
603
-
604
- /**
605
- * 真正调用查询的接口
606
- * @param isInit
607
- */
608
- async function loadData(isInit: Boolean) {
609
- if (!props.tableConfig.fetch) {
610
- loadingStatus.value = 2
611
- // if (props.modelValue) {
612
- // tableData.splice(0, tableData.length, ...props.modelValue)
613
- // }
614
- return false
615
- }
616
- if (loadingStatus.value === 1 || loading.value) return false
617
- loadingStatus.value = 1
618
- loading.value = true
619
- if (isInit) {
620
- pageInfo.current = 1
621
- }
622
- let postData = await getListQueryData()
623
- // 这里处理一下外部数据格式化
624
- if (props.tableConfig?.toolbar?.formConfig?.beforeRequest) {
625
- postData = props.tableConfig?.toolbar?.formConfig?.beforeRequest(JSON.parse(JSON.stringify(postData))) || postData
626
- }
627
- let dataPage = ((await props.tableConfig.fetch(postData)) || {}) as any
628
- // 这里要进行赋值操作-同时要转换相关key
629
- if (Array.isArray(dataPage)) {
630
- dataPage = { [props.tableConfig?.fetchMap?.list || 'records']: dataPage }
631
- }
632
- try {
633
- let dataResult = [] as any
634
- if (props.isPager) {
635
- pageInfo.total = dataPage[props.tableConfig?.fetchMap?.total || 'total'] * 1 || 0
636
- pageInfo.current = dataPage[props.tableConfig?.fetchMap?.current || 'current'] || 1
637
- dataResult = dataPage[props.tableConfig?.fetchMap?.list || 'records'] || null
638
- } else {
639
- dataResult = dataPage[props.tableConfig?.fetchMap?.list || 'records'] || null
640
- }
641
- tableData.value = dataResult
642
- if (isInit) {
643
- // 调用父类init
644
- emits('inited', tableData)
645
- }
646
- // 如果是树形结构
647
- if (props.type === 'expand') {
648
- treeIndexList.splice(0, treeIndexList.length)
649
- handelTreeIndex(tableData.value, [])
650
- }
651
- listLoading.value = false
652
- nextTick(() => {
653
- // 遍历以及选中当前页面数据
654
- refreshTableSelect()
655
- })
656
- } catch (e) {
657
- // eslint-disable-next-line no-console
658
- console.log('error: ', e)
659
- }
660
- loadingStatus.value = 2
661
- loading.value = false
662
- }
663
-
664
- /**
665
- * 渲染列表的选中项
666
- */
667
- function refreshTableSelect() {
668
- if (tableData.value && tableData.value.length > 0 && props.type === 'selection') {
669
- tableData.value.map((item: any) => {
670
- elPlusTableRef.value!.toggleRowSelection(
671
- item,
672
- allSelectRowList.some((i) => i[props.rowKey] === item[props.rowKey])
673
- )
674
- })
675
- }
676
- }
677
-
678
- /**
679
- * 重新加载
680
- */
681
- async function reload(isTab: boolean = false) {
682
- await loadData(true)
683
- // 这里判断一下Tab
684
- if (!isTab && props.tableConfig?.tabConf && props.tableConfig?.tabConf.fetch) {
685
- tabStatic.value = await props.tableConfig?.tabConf.fetch(Object.assign({}, await getListQueryData(), props.tableConfig?.tabConf.queryMap))
686
- loadingTab.value = false
687
- }
688
- return tableData.value
689
- }
690
-
691
- /**
692
- * 处理顶部条件表单筛选
693
- */
694
- function handelTopQuery() {
695
- topQueryData.value = cloneDeep(tableHeaderRef.value.getData())
696
- reload()
697
- }
698
-
699
- /**
700
- * 初始化列
701
- */
702
- function initCol() {
703
- tableHeaderRef.value?.initCol()
704
- }
705
-
706
- // 监听父类数据变更
707
- watch(
708
- () => props.modelValue,
709
- (data) => {
710
- // console.log('data: ', JSON.parse(JSON.stringify(data)))
711
- if (!props.tableConfig.fetch) {
712
- if (JSON.parse(JSON.stringify(data)) !== JSON.parse(JSON.stringify(tableData.value))) {
713
- loadingStatus.value = 2
714
- tableData.value.splice(0, tableData.value.length, ...(data || []))
715
- }
716
- }
717
- },
718
- { deep: true }
719
- )
720
-
721
- watch(
722
- () => props.selectList,
723
- (val: any[]) => {
724
- allSelectRowList.splice(0, allSelectRowList.length, ...val)
725
- // 遍历以及选中当前页面数据
726
- refreshTableSelect()
727
- },
728
- { deep: true }
729
- )
730
-
731
- // 这里注入插槽的reload事件
732
- if (props.isDIYMain) {
733
- // 只有isDIYMain=true时才注册事件
734
- provide('reloadTable', reload)
735
- }
736
-
737
- onMounted(() => {
738
- reload()
739
- })
740
-
741
- defineExpose({ reload, tableData, changeSelect, resetSelect, initCol })
742
- </script>
743
- <style lang="scss">
744
- .el-plus-table-content {
745
- background-color: #ffffff;
746
- border-radius: 5px;
747
- padding: 10px;
748
- width: 100%;
749
- max-height: 100%;
750
- overflow: hidden;
751
- display: flex;
752
- flex-direction: column;
753
- position: relative;
754
- box-sizing: border-box;
755
- .th-required {
756
- position: relative;
757
- &::before {
758
- content: '*';
759
- position: absolute;
760
- left: -10px;
761
- font-size: 16px;
762
- top: 2px;
763
- color: #ff3b30;
764
- font-weight: bold;
765
- }
766
- }
767
- .select-items {
768
- display: flex;
769
- flex-wrap: wrap;
770
- align-items: center;
771
-
772
- .title {
773
- font-size: 18px;
774
- font-weight: bold;
775
- margin-right: 20px;
776
- }
777
-
778
- .select-item {
779
- margin: 5px 10px 5px 0;
780
-
781
- &:last-child {
782
- margin-right: 0;
783
- }
784
- }
785
- }
786
-
787
- .table-tabs-panel {
788
- margin-bottom: 16px;
789
- }
790
- .summary-row {
791
- width: 100%;
792
- background-color: var(--el-table-row-hover-bg-color);
793
- color: var(--el-table-text-color);
794
- padding: 16px 20px;
795
- line-height: 24px;
796
- height: 40px;
797
- display: flex;
798
- position: relative;
799
- .summary-item {
800
- display: flex;
801
- align-items: center;
802
- margin-right: 30px;
803
- & > span {
804
- margin-right: 20px;
805
- }
806
- }
807
- }
808
-
809
- .el-plus-table-main {
810
- width: 100%;
811
- flex: 1;
812
- max-height: 100%;
813
- display: flex;
814
- flex-direction: column;
815
- .el-table {
816
- .cell {
817
- display: flex;
818
- }
819
- .is-center {
820
- .cell {
821
- align-items: center;
822
- justify-content: center;
823
- }
824
- }
825
- }
826
- .big-h-bar {
827
- .el-scrollbar {
828
- .is-horizontal {
829
- height: 12px !important;
830
- }
831
- .el-table__body {
832
- padding-bottom: 16px;
833
- }
834
- }
835
- }
836
- .big-v-bar {
837
- .el-scrollbar {
838
- .is-vertical {
839
- width: 12px !important;
840
- }
841
- }
842
- }
843
- }
844
-
845
- .pager-statistic {
846
- width: 100%;
847
- position: fixed;
848
- height: 32px;
849
- line-height: 32px;
850
- bottom: 0;
851
- text-align: right;
852
- min-width: 900px;
853
-
854
- .statistic-item {
855
- display: inline-block;
856
- }
857
- }
858
-
859
- .el-cascader__label {
860
- width: 300px !important;
861
- }
862
-
863
- .bottom-page-static-info {
864
- display: flex;
865
- flex-wrap: wrap;
866
- justify-content: center;
867
- align-items: center;
868
- min-height: 41px;
869
- margin-top: 10px;
870
-
871
- .page-info {
872
- margin-right: 30px;
873
- }
874
- }
875
-
876
- .common-list-export-excel {
877
- background: #20a0ff;
878
- text-decoration: none;
879
- color: #ffffff;
880
- font-size: 12px;
881
- height: 29px;
882
- line-height: 30px;
883
- display: block;
884
- margin-top: 5px;
885
- border-radius: 3px;
886
- padding: 0 10px;
887
- }
888
-
889
- .common-list-export-excel:hover {
890
- background: #58b6ff;
891
- }
892
-
893
- .el-table__empty-block {
894
- height: auto !important;
895
- }
896
- }
897
- </style>
1
+ <template>
2
+ <div class="el-plus-table-content">
3
+ <!-- <div class="select-items" v-if="type === 'selection' && selectRowList && selectRowList.length > 0">
4
+ <span class="title">已选择({{ selectRowList.length }}个): </span>
5
+ <el-tag v-for="(item, i) in selectRowList" :key="item.id" class="select-item" size="small" closable @close="() => handelRemoveSelect(i, item)">
6
+ {{ item[tableConfig.showSelectNameKey || 'name'] }}
7
+ </el-tag>
8
+ </div> -->
9
+ <EleTabletHeader ref="tableHeaderRef" v-if="Object.keys(tableConfig?.toolbar || {}).length || tableConfig.tbName" v-model="toolFormData" :tbName="tableConfig.tbName" :column="tableConfig?.column || []" :size="size" :isShowRefresh="isShowRefresh" :loading="loading" :toolbar="tableConfig.toolbar" :isDialog="isDialog" @query="handelTopQuery" />
10
+
11
+ <!-- 中部的Tabs -->
12
+ <div class="table-tabs-panel" v-if="tableConfig.tabConf">
13
+ <el-radio-group v-model="tableTabVal" size="default" @change="handelTabChange">
14
+ <el-radio-button v-for="(item, i) in tableConfig.tabConf?.tabs" :key="i" :label="item.value" :loading="true">
15
+ {{ item.label }}
16
+ <template v-if="loadingTab">
17
+ <el-icon class="is-loading"><Loading /></el-icon>
18
+ </template>
19
+ <template v-else>
20
+ {{ getTabLabel(item) }}
21
+ </template>
22
+ </el-radio-button>
23
+ </el-radio-group>
24
+ </div>
25
+
26
+ <!-- 中间列表 -->
27
+ <div class="el-plus-table-main" v-loading="loading">
28
+ <template v-if="isDIYMain">
29
+ <slot name="main" :tableData="tableData"></slot>
30
+ </template>
31
+ <!-- 这里开始是表格内容 -->
32
+ <el-table
33
+ ref="elPlusTableRef"
34
+ v-else
35
+ style="width: 100%"
36
+ height="100%"
37
+ :maxHeight="tableConfig.maxHeight || 'auto'"
38
+ v-bind="tableConfig.tableAttr"
39
+ :class="{ 'big-h-bar': tableConfig?.tableAttr?.bigHBar, 'big-v-bar': tableConfig.tableAttr?.bigVBar }"
40
+ :data="tableData"
41
+ :row-key="rowKey"
42
+ lazy
43
+ :load="loadExpandData"
44
+ :size="size"
45
+ :row-class-name="initRowClassName"
46
+ @select="handelTableSelect"
47
+ @select-all="handelTableSelectAll"
48
+ @expand-change="handelTableExpandChange"
49
+ :treeProps="treeProps"
50
+ :span-method="handelSpanMethod"
51
+ >
52
+ <!-- 复选框 -->
53
+ <el-table-column v-if="type === 'selection'" type="selection" width="55px" :selectable="selectable" header-align="center" align="center" />
54
+ <!-- 下标 -->
55
+ <el-table-column v-if="isIndex" type="index" width="60" label="序号" :headerAlign="headerAlign" />
56
+ <!-- 首列 -->
57
+ <template v-if="useSlots().firstColumn">
58
+ <slot name="firstColumn" />
59
+ </template>
60
+ <!-- 一级或多级列 -->
61
+ <ElPlusTableColumn v-for="(item, i) in headerColumns" :key="i" :item="item" :size="size"></ElPlusTableColumn>
62
+
63
+ <!-- 空 -->
64
+ <template v-if="!loading && loadingStatus === 2" #empty>
65
+ <el-empty v-if="isEmptyImg" :description="nullText" />
66
+ <span v-else>{{ nullText }}</span>
67
+ </template>
68
+ <!-- 合计行 -->
69
+ <template v-if="tableConfig.summaryConf?.prop" #append>
70
+ <div :style="tableConfig.summaryConf?.hstyle" class="summary-row" v-if="summaryList && summaryList.length > 0">
71
+ <div class="summary-item" v-for="(item, i) in summaryList" :key="i">
72
+ <span>{{ item.label || '合计' }}:</span>
73
+ <p>{{ item.value || 0 }}</p>
74
+ </div>
75
+ </div>
76
+ </template>
77
+ </el-table>
78
+ </div>
79
+ <div class="bottom-page-static-info" v-if="isPager || tableConfig.statistic">
80
+ <el-pagination class="page-info" small @size-change="handleSizeChange" @current-change="handleCurrentChange" :current-page="+pageInfo.current" :page-sizes="[5, 10, 20, 50, 100]" :page-size="pageInfo.size" layout="total, sizes, prev, pager, next, jumper" :total="pageInfo.total" />
81
+ </div>
82
+ <slot name="bottom"></slot>
83
+ </div>
84
+ </template>
85
+ <script lang="ts">
86
+ export default {
87
+ name: 'ElPlusTable',
88
+ inheritAttrs: false,
89
+ customOptions: {}
90
+ }
91
+ </script>
92
+ <script lang="ts" setup>
93
+ import { ref, reactive, onMounted, computed, watch, nextTick, useSlots, inject, provide } from 'vue'
94
+ import EleTabletHeader from './components/header.vue'
95
+ import ElPlusTableColumn from './ElPlusTableColumn.vue'
96
+ import { handelListColumn } from './util'
97
+ import { cloneDeep } from 'lodash'
98
+ import { Loading } from '@element-plus/icons-vue'
99
+ import type { TableColumnCtx } from 'element-plus'
100
+ import { ICRUDConfig, ITableConfig, ITableTabItem, ITreeProps } from 'types'
101
+
102
+ interface SpanMethodProps {
103
+ row: any
104
+ column: TableColumnCtx<any>
105
+ rowIndex: number
106
+ columnIndex: number
107
+ }
108
+
109
+ const defaultConf = inject('defaultConf') as ICRUDConfig
110
+ const format = inject('format') as any
111
+
112
+ const emits = defineEmits(['getUrlConsumerIds', 'selection', 'select', 'selectAll', 'update:modelValue', 'tabChange', 'expandChange', 'inited'])
113
+ const props = withDefaults(
114
+ defineProps<{
115
+ tableConfig: ITableConfig
116
+ // 外部数据
117
+ modelValue?: Array<any> | null
118
+ // 列表类型 默认为index
119
+ type?: string
120
+ // 是否显示第一列的Index下标
121
+ isIndex?: boolean
122
+ // 是否分页
123
+ isPager?: boolean
124
+ // 每页显示条数
125
+ pageSize?: number
126
+ // 是否在弹框中
127
+ isDialog?: boolean
128
+ // 空占位文本
129
+ nullText?: string
130
+ // 是否显示暂无数据的图片
131
+ isEmptyImg?: boolean
132
+ // 是否显示刷新文本
133
+ isShowRefresh?: boolean
134
+ // rowKey默认为id
135
+ rowKey?: string
136
+ // 自定义table主体
137
+ isDIYMain?: boolean
138
+ // 默认选中项
139
+ selectList?: Array<any> | []
140
+ // 默认列宽
141
+ colMinWidth?: string
142
+ // 表头文本排列方式
143
+ headerAlign?: 'left' | 'right' | 'center'
144
+ }>(),
145
+ {
146
+ modelValue: null,
147
+ type: 'index',
148
+ isIndex: true,
149
+ isPager: true,
150
+ pageSize: 10,
151
+ isDialog: false,
152
+ nullText: '暂无数据',
153
+ isShowRefresh: true,
154
+ rowKey: 'id',
155
+ isEmptyImg: true,
156
+ isDIYMain: false,
157
+ selectList: () => [],
158
+ colMinWidth: 'auto',
159
+ headerAlign: 'left'
160
+ }
161
+ )
162
+
163
+ // 合并行算法
164
+ const needSpanCellIndex = ref([] as any[])
165
+ const handelSpanMethod = ref(null as unknown as Function)
166
+
167
+ const elPlusTableRef = ref()
168
+
169
+ // 顶部Tab数据
170
+ const tabStatic = ref({} as any)
171
+ const tableTabVal = ref(props.tableConfig?.tabConf?.tabs[props.tableConfig?.tabConf?.default || 0].value ?? '')
172
+ // const tableTabVal = computed(() => {
173
+ // if (props.tableConfig?.tabConf) {
174
+ // if (props.tableConfig?.tabConf?.defaultVal !== undefined && props.tableConfig?.tabConf?.defaultVal !== null) {
175
+ // return props.tableConfig?.tabConf?.defaultVal
176
+ // }
177
+ // return props.tableConfig?.tabConf?.tabs[props.tableConfig?.tabConf?.default || 0].value ?? ''
178
+ // }
179
+ // return ''
180
+ // })
181
+ const getTabLabel = computed(() => (item: ITableTabItem) => {
182
+ if (item.key) {
183
+ return tabStatic.value[item.key] || 0
184
+ }
185
+ return ''
186
+ })
187
+
188
+ const selectable = computed(() => (row: any, index: number) => {
189
+ if (typeof props.tableConfig?.tableAttr?.selectable === 'function') {
190
+ return props.tableConfig?.tableAttr?.selectable(row, index)
191
+ }
192
+ return true
193
+ })
194
+
195
+ const tableHeaderRef = ref()
196
+
197
+ // 加载
198
+ const loading = ref(false)
199
+ const loadingTab = ref(!!props.tableConfig?.tabConf?.fetch)
200
+ const listLoading = ref(false)
201
+ const size = defaultConf.size || 'default'
202
+
203
+ // 顶部查询条件数据缓存
204
+ const topQueryData = ref({} as any)
205
+
206
+ // 数据
207
+ let toolFormData = reactive({} as any)
208
+ const tableData = ref((props.modelValue || []) as any[])
209
+ const haveClassRowList = reactive([])
210
+
211
+ // 0:未加载; 1:加载中;2:加载完成
212
+ const loadingStatus = ref(0)
213
+
214
+ // 保存所有的选中行
215
+ const allSelectRowList = reactive((cloneDeep(props.selectList) || []) as any[])
216
+ // 记录树形展开的数组的下标
217
+ const treeIndexList = reactive([] as any[][])
218
+
219
+ // 分页信息
220
+ const pageInfo = reactive({
221
+ current: !props.isDialog && toolFormData.current ? parseInt(toolFormData.current as any) : 1,
222
+ total: 0,
223
+ size: !props.isDialog && toolFormData.size ? parseInt(toolFormData.size as any) : props.pageSize
224
+ })
225
+ // 数型解析
226
+ const treeProps = (props.tableConfig?.explan?.treeProps || { children: 'children', hasChildren: 'hasChildren' }) as ITreeProps
227
+
228
+ // 处理后的列显示
229
+ const headerColumns = computed(() => {
230
+ const tempList = handelListColumn(props.tableConfig?.column, defaultConf, props.tableConfig?.tbName || '', props.headerAlign, props.isDialog ? 'auto' : props.colMinWidth)
231
+ // 这里重构一下合并行算法
232
+ // 获取所有列
233
+ const allColumn = getColumList(tempList)
234
+ // 查询是否有合并行属性
235
+ needSpanCellIndex.value = []
236
+
237
+ const tempColList = allColumn
238
+ .map((item, i) => {
239
+ // 合并行
240
+ if (item.isRowSpan) {
241
+ needSpanCellIndex.value.push(i)
242
+ let value = undefined as any
243
+ let first = 0
244
+ let count = 1
245
+ // 这里修改data数据
246
+ tableData.value?.map((row, j) => {
247
+ if (value !== row[item.prop]) {
248
+ if (count > 1 && j > 0) {
249
+ // 这里要设置之前的数据合并行数
250
+ tableData.value[first]['rowSpan_' + i] = count
251
+ }
252
+ first = j
253
+ count = 1
254
+ value = row[item.prop]
255
+ } else {
256
+ count += 1
257
+ row['rowSpan_' + i] = 0
258
+ }
259
+ if (j === tableData.value.length - 1) {
260
+ // 这里要设置之前的数据合并行数
261
+ tableData.value[first]['rowSpan_' + i] = count
262
+ }
263
+ })
264
+ }
265
+ // 返回需要合并列的列数据
266
+ return item.isColSpan ? i : null
267
+ })
268
+ .filter((item) => item !== null) as number[]
269
+
270
+ if (tempColList.length) {
271
+ // 这里开始合并列-遍历行数据
272
+ tableData.value?.map((row, j) => {
273
+ let value = undefined as any
274
+ let first = 0
275
+ let count = 1
276
+ // 遍历表头
277
+ tempColList.map((val: number, i: number) => {
278
+ // 不存在时才添加
279
+ if (!needSpanCellIndex.value.includes(val)) {
280
+ needSpanCellIndex.value.push(val)
281
+ }
282
+ // 如果值不等 或者 列序号已经中断
283
+ if (value !== row[allColumn[val].prop] || (i > 0 && val - 1 !== tempColList[i - 1])) {
284
+ if (count > 1) {
285
+ // 这里要设置之前的数据合并行数
286
+ row['colSpan_' + first] = count
287
+ }
288
+ first = val
289
+ count = 1
290
+ value = row[allColumn[val].prop]
291
+ } else {
292
+ count += 1
293
+ row['colSpan_' + val] = 0
294
+ }
295
+ // 如果是最后一个
296
+ if (i === tempColList.length - 1) {
297
+ // 这里要设置之前的数据合并行数
298
+ row['colSpan_' + first] = count
299
+ }
300
+ })
301
+ })
302
+ }
303
+ if (needSpanCellIndex.value.length > 0) {
304
+ handelSpanMethod.value = ({ row, column, columnIndex }: SpanMethodProps) => {
305
+ let tempColumnIndex = columnIndex
306
+ // 这里要排除默认的列
307
+ if (props.type === 'selection') tempColumnIndex -= 1
308
+ if (props.isIndex) tempColumnIndex -= 1
309
+ if (useSlots().firstColumn) tempColumnIndex -= 1
310
+ if (needSpanCellIndex.value.includes(tempColumnIndex)) {
311
+ return { rowspan: row['rowSpan_' + tempColumnIndex] ?? 1, colspan: row['colSpan_' + tempColumnIndex] ?? 1 }
312
+ }
313
+ }
314
+ }
315
+ return tempList
316
+ })
317
+
318
+ // 合计行数据
319
+ const summaryList = computed(() => {
320
+ const tempList = [] as any[]
321
+ if (props.tableConfig.summaryConf?.vif) {
322
+ if (typeof props.tableConfig.summaryConf?.vif === 'boolean') {
323
+ if (!props.tableConfig.summaryConf?.vif) {
324
+ return tempList
325
+ }
326
+ } else {
327
+ if (!props.tableConfig.summaryConf?.vif()) {
328
+ return tempList
329
+ }
330
+ }
331
+ }
332
+ if (props.tableConfig.summaryConf?.prop) {
333
+ const propList = props.tableConfig.summaryConf.prop.split(',')
334
+ const labelList = props.tableConfig.summaryConf?.label?.split(',') || []
335
+ // 遍历
336
+ propList.map((prop, i: number) => {
337
+ let value = ''
338
+ if (props.tableConfig.summaryConf?.sumFn) {
339
+ value = props.tableConfig.summaryConf?.sumFn(tableData.value, allSelectRowList)
340
+ } else {
341
+ value = format.yuan(tableData.value.reduce((total: number, current) => (total += current[prop]), 0))
342
+ }
343
+ tempList.push({ label: labelList[i], value })
344
+ })
345
+ }
346
+ return tempList
347
+ })
348
+
349
+ /**
350
+ * 获取所有显示的列信息
351
+ * @param list
352
+ */
353
+ function getColumList(list: Array<any>): Array<any> {
354
+ const tempList = [] as any
355
+ list?.map((item) => {
356
+ if (item.children?.length) {
357
+ tempList.push(...getColumList(item.children))
358
+ } else {
359
+ if (item.__vif) {
360
+ tempList.push(item)
361
+ }
362
+ }
363
+ })
364
+ return tempList
365
+ }
366
+
367
+ /**
368
+ * Tab切换
369
+ * @param val
370
+ */
371
+ async function handelTabChange(val: string | number | boolean) {
372
+ // 这里直接重新查询
373
+ reload(true)
374
+
375
+ // 通知父类 第二个参数是回调
376
+ await emits('tabChange', val, initCol)
377
+
378
+ // 这里触发下initCol
379
+ nextTick(() => {
380
+ initCol()
381
+ })
382
+ }
383
+
384
+ /**
385
+ * 加载自身展开的数据
386
+ * @param row
387
+ * @param treeNode
388
+ * @param resolve
389
+ */
390
+ function loadExpandData(row: any, treeNode: any, resolve: any) {
391
+ const postData = getListQueryData()
392
+ postData[props.tableConfig?.explan?.idName || 'parentId'] = row.id
393
+ props.tableConfig.fetch &&
394
+ props.tableConfig.fetch(postData).then((pageInfo) => {
395
+ resolve(pageInfo?.[props.tableConfig?.fetchMap?.list || 'records'])
396
+ })
397
+ }
398
+
399
+ /**
400
+ * 初始化每行的颜色
401
+ * @param data
402
+ */
403
+ function initRowClassName(data: any) {
404
+ if (haveClassRowList.length > data.rowIndex) {
405
+ return haveClassRowList[data.rowIndex * 1]
406
+ }
407
+ return ''
408
+ }
409
+
410
+ /**
411
+ * 处理单个选中的情况
412
+ * @param selection
413
+ * @param row
414
+ */
415
+ function handelTableSelect(selection: any[], item: any) {
416
+ checkAndRemove(item, !selection.some((i) => i[props.rowKey] === item[props.rowKey]))
417
+ // 通知父类
418
+ emits('select', selection, item)
419
+ emits('selection', cloneDeep(allSelectRowList))
420
+ }
421
+
422
+ /**
423
+ * 处理全选
424
+ * @param selection
425
+ * @param row
426
+ */
427
+ function handelTableSelectAll(selection: any[]) {
428
+ const isRemove = !(selection && selection.length > 0)
429
+ ;(isRemove ? tableData.value : selection).map((item) => {
430
+ checkAndRemove(item, isRemove)
431
+ })
432
+ // 通知父类
433
+ emits('selectAll', selection, isRemove)
434
+ emits('selection', cloneDeep(allSelectRowList))
435
+ }
436
+
437
+ /**
438
+ * 处理行展开
439
+ * @param row
440
+ * @param expanded
441
+ */
442
+ function handelTableExpandChange(row: any, expanded: boolean) {
443
+ // console.log('row: ', row, expanded)
444
+ emits('expandChange', row, expanded)
445
+ }
446
+
447
+ /**
448
+ * 选中或者取消
449
+ * @param item
450
+ */
451
+ function checkAndRemove(item: any, isRemove: boolean = false) {
452
+ // 校验是否已经有了
453
+ const index = allSelectRowList.findIndex((i) => i[props.rowKey] === item[props.rowKey])
454
+ if (isRemove) {
455
+ if (index >= 0) {
456
+ allSelectRowList.splice(index, 1)
457
+ }
458
+ } else {
459
+ if (index < 0) {
460
+ allSelectRowList.push(item)
461
+ }
462
+ }
463
+ }
464
+
465
+ /**
466
+ * 外部修改选中项
467
+ * @param item
468
+ */
469
+ function changeSelect(itemList: any[], isSelect: boolean = false) {
470
+ if (itemList && itemList.length) {
471
+ itemList.map((item) => {
472
+ checkAndRemove(
473
+ allSelectRowList.find((i) => i[props.rowKey] === item[props.rowKey]),
474
+ !isSelect
475
+ )
476
+ })
477
+ }
478
+ // 这里要刷新一下tab的选中
479
+ refreshTableSelect()
480
+ }
481
+
482
+ /**
483
+ * 重置选中
484
+ */
485
+ function resetSelect() {
486
+ allSelectRowList.splice(0, allSelectRowList.length, ...props.selectList)
487
+ }
488
+
489
+ /**
490
+ * 封装分页查询条件
491
+ */
492
+ async function getListQueryData() {
493
+ let queryMap = {
494
+ // 封装查询条件
495
+ // ...route.query,
496
+ // ...tableHeaderRef.value.getData(),
497
+ ...topQueryData.value,
498
+ ...(typeof props.tableConfig.queryMap === 'function' ? await props.tableConfig.queryMap() : props.tableConfig.queryMap),
499
+ t_: new Date().getTime()
500
+ } as any
501
+ if (props.isPager) {
502
+ // 封装分页信息
503
+ queryMap.current = pageInfo.current
504
+ queryMap.size = pageInfo.size
505
+ }
506
+ // 这里处理一下列表Tab的查询条件
507
+ if (props.tableConfig?.tabConf && props.tableConfig?.tabConf.prop) {
508
+ queryMap[props.tableConfig?.tabConf.prop] = tableTabVal.value
509
+ }
510
+
511
+ queryMap = handelQueryData(queryMap)
512
+ if (!props.isDialog) {
513
+ // setQuery(route.path, queryMap)
514
+ }
515
+ // 这里是导出的权限判定-增加登陆信息
516
+ if (props.tableConfig?.toolbar?.export?.isAuth) {
517
+ // TODO GET Auth
518
+ }
519
+ return queryMap
520
+ }
521
+
522
+ /**
523
+ * 处理下请求参数
524
+ * @param queryMap
525
+ */
526
+ function handelQueryData(queryMap: object) {
527
+ if (!queryMap) return {}
528
+ const tempMap = {}
529
+ Object.keys(queryMap).map((key) => {
530
+ if (queryMap[key] !== undefined && queryMap[key] !== null && queryMap[key] !== '') {
531
+ tempMap[key] = queryMap[key]
532
+ }
533
+ })
534
+ return tempMap
535
+ }
536
+
537
+ /**
538
+ * 处理显示
539
+ * @param rowInfo
540
+ */
541
+ // function handelHeaderVIf(rowInfo: any) {
542
+ // let vif = false
543
+ // if (rowInfo.children && rowInfo.children.length > 0) {
544
+ // vif = rowInfo.children.some((item: any) => {
545
+ // return !(item.vif === false)
546
+ // })
547
+ // }
548
+ // return vif
549
+ // }
550
+
551
+ /**
552
+ * 处理列的显示
553
+ * @param item
554
+ */
555
+ // function handelColumnVIf(item: any) {
556
+ // if (item.vif !== undefined && item.vif !== null && item.vif !== '') {
557
+ // if (typeof item.vif === 'boolean') return item.vif
558
+ // if (typeof item.vif === 'function') return item.vif(item)
559
+ // return !!item.vif
560
+ // }
561
+ // return true
562
+ // }
563
+
564
+ /**
565
+ * 处理分页工具中pageSize变化
566
+ * @param val
567
+ */
568
+ function handleSizeChange(val: number) {
569
+ // 回到第一页
570
+ pageInfo.current = 1
571
+ pageInfo.size = val
572
+ loadData(true)
573
+ }
574
+
575
+ /**
576
+ * 处理分页工具中current变化
577
+ * @param val
578
+ */
579
+ function handleCurrentChange(val: number) {
580
+ if (pageInfo.current !== val) {
581
+ pageInfo.current = val
582
+ loadData(false)
583
+ }
584
+ }
585
+
586
+ /**
587
+ * 处理行展开
588
+ * @param list
589
+ * @param pIndexList
590
+ */
591
+ function handelTreeIndex(list: any[], pIndexList: number[]) {
592
+ let tempList = []
593
+ if (list && list.length > 0) {
594
+ list.map((item, i) => {
595
+ treeIndexList.push([...pIndexList, i])
596
+ tempList = item[treeProps.children] as any[]
597
+ if (tempList && tempList.length > 0) {
598
+ handelTreeIndex(tempList, [i])
599
+ }
600
+ })
601
+ }
602
+ }
603
+
604
+ /**
605
+ * 真正调用查询的接口
606
+ * @param isInit
607
+ */
608
+ async function loadData(isInit: Boolean) {
609
+ if (!props.tableConfig.fetch) {
610
+ loadingStatus.value = 2
611
+ // if (props.modelValue) {
612
+ // tableData.splice(0, tableData.length, ...props.modelValue)
613
+ // }
614
+ return false
615
+ }
616
+ if (loadingStatus.value === 1 || loading.value) return false
617
+ loadingStatus.value = 1
618
+ loading.value = true
619
+ if (isInit) {
620
+ pageInfo.current = 1
621
+ }
622
+ let postData = await getListQueryData()
623
+ // 这里处理一下外部数据格式化
624
+ if (props.tableConfig?.toolbar?.formConfig?.beforeRequest) {
625
+ postData = props.tableConfig?.toolbar?.formConfig?.beforeRequest(JSON.parse(JSON.stringify(postData))) || postData
626
+ }
627
+ let dataPage = ((await props.tableConfig.fetch(postData)) || {}) as any
628
+ // 这里要进行赋值操作-同时要转换相关key
629
+ if (Array.isArray(dataPage)) {
630
+ dataPage = { [props.tableConfig?.fetchMap?.list || 'records']: dataPage }
631
+ }
632
+ try {
633
+ let dataResult = [] as any
634
+ if (props.isPager) {
635
+ pageInfo.total = dataPage[props.tableConfig?.fetchMap?.total || 'total'] * 1 || 0
636
+ pageInfo.current = dataPage[props.tableConfig?.fetchMap?.current || 'current'] || 1
637
+ dataResult = dataPage[props.tableConfig?.fetchMap?.list || 'records'] || null
638
+ } else {
639
+ dataResult = dataPage[props.tableConfig?.fetchMap?.list || 'records'] || null
640
+ }
641
+ tableData.value = dataResult
642
+ if (isInit) {
643
+ // 调用父类init
644
+ emits('inited', tableData)
645
+ }
646
+ // 如果是树形结构
647
+ if (props.type === 'expand') {
648
+ treeIndexList.splice(0, treeIndexList.length)
649
+ handelTreeIndex(tableData.value, [])
650
+ }
651
+ listLoading.value = false
652
+ nextTick(() => {
653
+ // 遍历以及选中当前页面数据
654
+ refreshTableSelect()
655
+ })
656
+ } catch (e) {
657
+ // eslint-disable-next-line no-console
658
+ console.log('error: ', e)
659
+ }
660
+ loadingStatus.value = 2
661
+ loading.value = false
662
+ }
663
+
664
+ /**
665
+ * 渲染列表的选中项
666
+ */
667
+ function refreshTableSelect() {
668
+ if (tableData.value && tableData.value.length > 0 && props.type === 'selection') {
669
+ tableData.value.map((item: any) => {
670
+ elPlusTableRef.value!.toggleRowSelection(
671
+ item,
672
+ allSelectRowList.some((i) => i[props.rowKey] === item[props.rowKey])
673
+ )
674
+ })
675
+ }
676
+ }
677
+
678
+ /**
679
+ * 重新加载
680
+ */
681
+ async function reload(isTab: boolean = false) {
682
+ await loadData(true)
683
+ // 这里判断一下Tab
684
+ if (!isTab && props.tableConfig?.tabConf && props.tableConfig?.tabConf.fetch) {
685
+ tabStatic.value = await props.tableConfig?.tabConf.fetch(Object.assign({}, await getListQueryData(), props.tableConfig?.tabConf.queryMap))
686
+ loadingTab.value = false
687
+ }
688
+ return tableData.value
689
+ }
690
+
691
+ /**
692
+ * 处理顶部条件表单筛选
693
+ */
694
+ function handelTopQuery() {
695
+ topQueryData.value = cloneDeep(tableHeaderRef.value.getData())
696
+ reload()
697
+ }
698
+
699
+ /**
700
+ * 初始化列
701
+ */
702
+ function initCol() {
703
+ tableHeaderRef.value?.initCol()
704
+ }
705
+
706
+ // 监听父类数据变更
707
+ watch(
708
+ () => props.modelValue,
709
+ (data) => {
710
+ // console.log('data: ', JSON.parse(JSON.stringify(data)))
711
+ if (!props.tableConfig.fetch) {
712
+ if (JSON.parse(JSON.stringify(data)) !== JSON.parse(JSON.stringify(tableData.value))) {
713
+ loadingStatus.value = 2
714
+ tableData.value.splice(0, tableData.value.length, ...(data || []))
715
+ }
716
+ }
717
+ },
718
+ { deep: true }
719
+ )
720
+
721
+ watch(
722
+ () => props.selectList,
723
+ (val: any[]) => {
724
+ allSelectRowList.splice(0, allSelectRowList.length, ...val)
725
+ // 遍历以及选中当前页面数据
726
+ refreshTableSelect()
727
+ },
728
+ { deep: true }
729
+ )
730
+
731
+ // 这里注入插槽的reload事件
732
+ if (props.isDIYMain) {
733
+ // 只有isDIYMain=true时才注册事件
734
+ provide('reloadTable', reload)
735
+ }
736
+
737
+ onMounted(() => {
738
+ reload()
739
+ })
740
+
741
+ defineExpose({ reload, tableData, changeSelect, resetSelect, initCol })
742
+ </script>
743
+ <style lang="scss">
744
+ .el-plus-table-content {
745
+ background-color: #ffffff;
746
+ border-radius: 5px;
747
+ padding: 10px;
748
+ width: 100%;
749
+ max-height: 100%;
750
+ overflow: hidden;
751
+ display: flex;
752
+ flex-direction: column;
753
+ position: relative;
754
+ box-sizing: border-box;
755
+ .th-required {
756
+ position: relative;
757
+ &::before {
758
+ content: '*';
759
+ position: absolute;
760
+ left: -10px;
761
+ font-size: 16px;
762
+ top: 2px;
763
+ color: #ff3b30;
764
+ font-weight: bold;
765
+ }
766
+ }
767
+ .select-items {
768
+ display: flex;
769
+ flex-wrap: wrap;
770
+ align-items: center;
771
+
772
+ .title {
773
+ font-size: 18px;
774
+ font-weight: bold;
775
+ margin-right: 20px;
776
+ }
777
+
778
+ .select-item {
779
+ margin: 5px 10px 5px 0;
780
+
781
+ &:last-child {
782
+ margin-right: 0;
783
+ }
784
+ }
785
+ }
786
+
787
+ .table-tabs-panel {
788
+ margin-bottom: 16px;
789
+ }
790
+ .summary-row {
791
+ width: 100%;
792
+ background-color: var(--el-table-row-hover-bg-color);
793
+ color: var(--el-table-text-color);
794
+ padding: 16px 20px;
795
+ line-height: 24px;
796
+ height: 40px;
797
+ display: flex;
798
+ position: relative;
799
+ .summary-item {
800
+ display: flex;
801
+ align-items: center;
802
+ margin-right: 30px;
803
+ & > span {
804
+ margin-right: 20px;
805
+ }
806
+ }
807
+ }
808
+
809
+ .el-plus-table-main {
810
+ width: 100%;
811
+ flex: 1;
812
+ max-height: 100%;
813
+ display: flex;
814
+ flex-direction: column;
815
+ .el-table {
816
+ .cell {
817
+ display: flex;
818
+ }
819
+ .is-center {
820
+ .cell {
821
+ align-items: center;
822
+ justify-content: center;
823
+ }
824
+ }
825
+ }
826
+ .big-h-bar {
827
+ .el-scrollbar {
828
+ .is-horizontal {
829
+ height: 12px !important;
830
+ }
831
+ .el-table__body {
832
+ padding-bottom: 16px;
833
+ }
834
+ }
835
+ }
836
+ .big-v-bar {
837
+ .el-scrollbar {
838
+ .is-vertical {
839
+ width: 12px !important;
840
+ }
841
+ }
842
+ }
843
+ }
844
+
845
+ .pager-statistic {
846
+ width: 100%;
847
+ position: fixed;
848
+ height: 32px;
849
+ line-height: 32px;
850
+ bottom: 0;
851
+ text-align: right;
852
+ min-width: 900px;
853
+
854
+ .statistic-item {
855
+ display: inline-block;
856
+ }
857
+ }
858
+
859
+ .el-cascader__label {
860
+ width: 300px !important;
861
+ }
862
+
863
+ .bottom-page-static-info {
864
+ display: flex;
865
+ flex-wrap: wrap;
866
+ justify-content: center;
867
+ align-items: center;
868
+ min-height: 41px;
869
+ margin-top: 10px;
870
+
871
+ .page-info {
872
+ margin-right: 30px;
873
+ }
874
+ }
875
+
876
+ .common-list-export-excel {
877
+ background: #20a0ff;
878
+ text-decoration: none;
879
+ color: #ffffff;
880
+ font-size: 12px;
881
+ height: 29px;
882
+ line-height: 30px;
883
+ display: block;
884
+ margin-top: 5px;
885
+ border-radius: 3px;
886
+ padding: 0 10px;
887
+ }
888
+
889
+ .common-list-export-excel:hover {
890
+ background: #58b6ff;
891
+ }
892
+
893
+ .el-table__empty-block {
894
+ height: auto !important;
895
+ }
896
+ }
897
+ </style>