n20-common-lib 3.1.5 → 3.1.6
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.
- package/package.json +2 -1
- package/src/assets/css/pivot.scss +58 -137
- package/src/components/AdvancedFilter/index.vue +35 -23
- package/src/components/Pivot/ChartView.vue +2 -6
- package/src/components/Pivot/TableView.vue +165 -87
- package/src/components/Pivot/index.vue +166 -26
- package/src/components/ProFilterView/index.vue +39 -19
- package/src/index.js +93 -91
- package/style/index.css +1 -1
- package/theme/blue.css +1 -1
- package/theme/cctcRed.css +1 -1
- package/theme/green.css +1 -1
- package/theme/lightBlue.css +1 -1
- package/theme/orange.css +1 -1
- package/theme/purple.css +1 -1
- package/theme/red.css +1 -1
- package/theme/yellow.css +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n20-common-lib",
|
|
3
|
-
"version": "3.1.
|
|
3
|
+
"version": "3.1.6",
|
|
4
4
|
"private": false,
|
|
5
5
|
"scripts": {
|
|
6
6
|
"serve": "vue-cli-service serve",
|
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"devDependencies": {
|
|
75
75
|
"@babel/plugin-proposal-optional-chaining": "^7.14.5",
|
|
76
76
|
"@babel/plugin-transform-flow-comments": "^7.14.5",
|
|
77
|
+
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
|
|
77
78
|
"@vue/cli-plugin-babel": "~4.5.0",
|
|
78
79
|
"@vue/cli-plugin-eslint": "~4.5.0",
|
|
79
80
|
"@vue/cli-plugin-unit-jest": "^5.0.8",
|
|
@@ -33,6 +33,8 @@
|
|
|
33
33
|
/* 左侧边栏 */
|
|
34
34
|
.pivot-sidebar-left {
|
|
35
35
|
width: 280px;
|
|
36
|
+
min-width: 280px;
|
|
37
|
+
flex-shrink: 0;
|
|
36
38
|
background: #f2f3f5;
|
|
37
39
|
border-right: 1px solid #e5e6eb;
|
|
38
40
|
display: flex;
|
|
@@ -257,6 +259,7 @@
|
|
|
257
259
|
/* 主内容区域 */
|
|
258
260
|
.pivot-main {
|
|
259
261
|
flex: 1;
|
|
262
|
+
min-width: 0;
|
|
260
263
|
display: flex;
|
|
261
264
|
flex-direction: column;
|
|
262
265
|
position: relative;
|
|
@@ -484,6 +487,7 @@
|
|
|
484
487
|
width: 100%;
|
|
485
488
|
height: 100%;
|
|
486
489
|
position: relative;
|
|
490
|
+
overflow: hidden;
|
|
487
491
|
}
|
|
488
492
|
|
|
489
493
|
.chart-wrapper {
|
|
@@ -528,81 +532,6 @@
|
|
|
528
532
|
min-height: 0;
|
|
529
533
|
}
|
|
530
534
|
|
|
531
|
-
.table-container {
|
|
532
|
-
width: 100%;
|
|
533
|
-
height: 100%;
|
|
534
|
-
overflow: auto;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
.pivot-table {
|
|
538
|
-
width: 100%;
|
|
539
|
-
min-width: 769px;
|
|
540
|
-
border-collapse: collapse;
|
|
541
|
-
table-layout: fixed;
|
|
542
|
-
font-size: 14px;
|
|
543
|
-
|
|
544
|
-
th,
|
|
545
|
-
td {
|
|
546
|
-
padding: 10px 16px;
|
|
547
|
-
border-right: 1px solid #e5e6eb;
|
|
548
|
-
border-bottom: 1px solid #e5e6eb;
|
|
549
|
-
height: 57px;
|
|
550
|
-
box-sizing: border-box;
|
|
551
|
-
}
|
|
552
|
-
|
|
553
|
-
th {
|
|
554
|
-
background: #f2f3f5;
|
|
555
|
-
font-weight: 500;
|
|
556
|
-
color: #1d2129;
|
|
557
|
-
position: sticky;
|
|
558
|
-
top: 0;
|
|
559
|
-
z-index: 1;
|
|
560
|
-
text-align: left;
|
|
561
|
-
height: 40px;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
th.metric-header {
|
|
565
|
-
text-align: right;
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
td.dimension-cell {
|
|
569
|
-
text-align: left;
|
|
570
|
-
color: #1d2129;
|
|
571
|
-
vertical-align: middle;
|
|
572
|
-
}
|
|
573
|
-
|
|
574
|
-
td.metric-cell {
|
|
575
|
-
text-align: right;
|
|
576
|
-
vertical-align: middle;
|
|
577
|
-
|
|
578
|
-
.cell-content {
|
|
579
|
-
display: flex;
|
|
580
|
-
flex-direction: column;
|
|
581
|
-
align-items: flex-end;
|
|
582
|
-
justify-content: center;
|
|
583
|
-
height: 100%;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
.cell-value {
|
|
587
|
-
font-family: 'Open Sans', sans-serif;
|
|
588
|
-
font-size: 14px;
|
|
589
|
-
line-height: 22px;
|
|
590
|
-
color: #1d2129;
|
|
591
|
-
}
|
|
592
|
-
|
|
593
|
-
.cell-count {
|
|
594
|
-
font-family: 'PingFang SC', sans-serif;
|
|
595
|
-
font-size: 12px;
|
|
596
|
-
line-height: 20px;
|
|
597
|
-
color: #86909c;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
tbody tr:hover {
|
|
602
|
-
background: #f7f8fa;
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
|
|
606
535
|
.empty-state {
|
|
607
536
|
width: 100%;
|
|
608
537
|
height: 100%;
|
|
@@ -615,6 +544,8 @@
|
|
|
615
544
|
/* 右侧边栏 */
|
|
616
545
|
.pivot-sidebar-right {
|
|
617
546
|
width: 280px;
|
|
547
|
+
min-width: 280px;
|
|
548
|
+
flex-shrink: 0;
|
|
618
549
|
background: #fff;
|
|
619
550
|
border-left: 1px solid #e5e6eb;
|
|
620
551
|
display: flex;
|
|
@@ -1067,6 +998,8 @@
|
|
|
1067
998
|
display: flex;
|
|
1068
999
|
flex-direction: column;
|
|
1069
1000
|
height: 100%;
|
|
1001
|
+
width: 100%;
|
|
1002
|
+
overflow: hidden;
|
|
1070
1003
|
}
|
|
1071
1004
|
|
|
1072
1005
|
.table-header {
|
|
@@ -1088,88 +1021,76 @@
|
|
|
1088
1021
|
font-family: 'PingFang SC', sans-serif;
|
|
1089
1022
|
}
|
|
1090
1023
|
|
|
1091
|
-
/*
|
|
1092
|
-
.
|
|
1024
|
+
/* vxe-table 表格样式 */
|
|
1025
|
+
.table-container {
|
|
1093
1026
|
flex: 1;
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
.cross-pivot-table {
|
|
1027
|
+
min-height: 0;
|
|
1028
|
+
overflow: hidden;
|
|
1098
1029
|
width: 100%;
|
|
1099
|
-
min-width: 100%;
|
|
1100
|
-
border-collapse: collapse;
|
|
1101
|
-
table-layout: fixed;
|
|
1102
1030
|
}
|
|
1103
1031
|
|
|
1104
|
-
.
|
|
1105
|
-
|
|
1106
|
-
border-right: 1px solid #e5e6eb;
|
|
1107
|
-
border-bottom: 1px solid #e5e6eb;
|
|
1108
|
-
padding: 9px 16px;
|
|
1109
|
-
font-size: 14px;
|
|
1110
|
-
font-weight: 500;
|
|
1111
|
-
color: #1d2129;
|
|
1112
|
-
text-align: center;
|
|
1113
|
-
height: 40px;
|
|
1114
|
-
box-sizing: border-box;
|
|
1115
|
-
}
|
|
1032
|
+
.pivot-vxe-table {
|
|
1033
|
+
font-family: 'PingFang SC', sans-serif;
|
|
1116
1034
|
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1035
|
+
/* 表头样式 */
|
|
1036
|
+
.vxe-table--header-wrapper {
|
|
1037
|
+
.vxe-header--column {
|
|
1038
|
+
background: #f2f3f5;
|
|
1039
|
+
font-weight: 500;
|
|
1040
|
+
color: #1d2129;
|
|
1041
|
+
font-size: 14px;
|
|
1042
|
+
border-right: 1px solid #e5e6eb;
|
|
1043
|
+
}
|
|
1122
1044
|
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
}
|
|
1045
|
+
/* 多级表头样式 */
|
|
1046
|
+
.vxe-header--row {
|
|
1047
|
+
.vxe-header--column {
|
|
1048
|
+
border-bottom: 1px solid #e5e6eb;
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1128
1051
|
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
height: 57px;
|
|
1137
|
-
box-sizing: border-box;
|
|
1138
|
-
}
|
|
1052
|
+
/* 多级表头父级样式 */
|
|
1053
|
+
.vxe-header--column.col--group {
|
|
1054
|
+
background: #f7f8fa;
|
|
1055
|
+
text-align: center;
|
|
1056
|
+
border-bottom: 1px solid #e5e6eb;
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1139
1059
|
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1060
|
+
/* 单元格样式 */
|
|
1061
|
+
.vxe-body--column {
|
|
1062
|
+
font-size: 14px;
|
|
1063
|
+
color: #1d2129;
|
|
1064
|
+
border-right: 1px solid #e5e6eb;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
/* 悬停行样式 */
|
|
1068
|
+
.vxe-body--row.row--hover {
|
|
1069
|
+
background: #f7f8fa;
|
|
1070
|
+
}
|
|
1145
1071
|
}
|
|
1146
1072
|
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1073
|
+
/* 指标单元格内容 */
|
|
1074
|
+
.metric-cell-content {
|
|
1075
|
+
display: flex;
|
|
1076
|
+
flex-direction: column;
|
|
1077
|
+
align-items: flex-end;
|
|
1078
|
+
justify-content: center;
|
|
1079
|
+
height: 100%;
|
|
1152
1080
|
}
|
|
1153
1081
|
|
|
1154
|
-
.
|
|
1155
|
-
display: block;
|
|
1082
|
+
.metric-value {
|
|
1156
1083
|
font-family: 'Open Sans', sans-serif;
|
|
1157
1084
|
font-size: 14px;
|
|
1158
|
-
font-weight: 400;
|
|
1159
|
-
color: #1d2129;
|
|
1160
1085
|
line-height: 22px;
|
|
1086
|
+
color: #1d2129;
|
|
1161
1087
|
}
|
|
1162
1088
|
|
|
1163
|
-
.
|
|
1164
|
-
display: block;
|
|
1089
|
+
.metric-count {
|
|
1165
1090
|
font-family: 'PingFang SC', sans-serif;
|
|
1166
1091
|
font-size: 12px;
|
|
1167
|
-
color: #86909c;
|
|
1168
1092
|
line-height: 20px;
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
.cross-data-row:hover td {
|
|
1172
|
-
background: #f7f8fa;
|
|
1093
|
+
color: #86909c;
|
|
1173
1094
|
}
|
|
1174
1095
|
|
|
1175
1096
|
/* 分页组件 */
|
|
@@ -109,13 +109,15 @@
|
|
|
109
109
|
<script>
|
|
110
110
|
// import formItemInput from './form-item-input.vue'
|
|
111
111
|
import XEUtils from 'xe-utils'
|
|
112
|
-
|
|
113
|
-
import ClDialog from '../Dialog/index.vue'
|
|
114
|
-
import InputSearch from '../InputSearch/index.vue'
|
|
112
|
+
|
|
115
113
|
import filterItem from './filterItem.vue'
|
|
116
114
|
import formItemRender from './formItemRender.vue'
|
|
117
115
|
import { getOnlyKey } from './utils'
|
|
118
116
|
|
|
117
|
+
import axios from '../../utils/axios.js'
|
|
118
|
+
import ClDialog from '../Dialog/index.vue'
|
|
119
|
+
import InputSearch from '../InputSearch/index.vue'
|
|
120
|
+
|
|
119
121
|
const prefixCls = 'n20-advanced-filter'
|
|
120
122
|
export default {
|
|
121
123
|
name: 'AdvancedFilter',
|
|
@@ -334,6 +336,9 @@ export default {
|
|
|
334
336
|
if (rangeTypes.includes(item.type)) {
|
|
335
337
|
return hasRange(item.startDate, item.endDate) ? this.prefixCls + '-active' : ''
|
|
336
338
|
}
|
|
339
|
+
if (item.slotFields && item.slotFields.length > 0) {
|
|
340
|
+
return item.slotFields.some((field) => hasValue(this.model[field])) ? this.prefixCls + '-active' : ''
|
|
341
|
+
}
|
|
337
342
|
return hasValue(this.model[item.value]) ? this.prefixCls + '-active' : ''
|
|
338
343
|
},
|
|
339
344
|
handleClose(item) {
|
|
@@ -397,34 +402,41 @@ export default {
|
|
|
397
402
|
this.$emit('clear')
|
|
398
403
|
},
|
|
399
404
|
setModelData(list) {
|
|
400
|
-
|
|
405
|
+
// 收集当前选中列表中的所有字段
|
|
406
|
+
const selectedFields = new Set()
|
|
401
407
|
list.forEach((res) => {
|
|
402
|
-
|
|
403
|
-
if (res.
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
if (res.startDate && res.endDate) {
|
|
408
|
-
arr.push(res.startDate)
|
|
409
|
-
arr.push(res.endDate)
|
|
410
|
-
}
|
|
411
|
-
// 收集数字范围类型的字段
|
|
412
|
-
if (res.startValue && res.endValue) {
|
|
413
|
-
arr.push(res.startValue)
|
|
414
|
-
arr.push(res.endValue)
|
|
415
|
-
}
|
|
416
|
-
// 收集插槽类型可能的自定义字段(slotFields 数组)
|
|
408
|
+
if (res.value) selectedFields.add(res.value)
|
|
409
|
+
if (res.startDate) selectedFields.add(res.startDate)
|
|
410
|
+
if (res.endDate) selectedFields.add(res.endDate)
|
|
411
|
+
if (res.startValue) selectedFields.add(res.startValue)
|
|
412
|
+
if (res.endValue) selectedFields.add(res.endValue)
|
|
417
413
|
if (res.type === 'slot' && Array.isArray(res?.slotFields)) {
|
|
418
414
|
res?.slotFields.forEach((field) => {
|
|
419
|
-
if (field
|
|
420
|
-
arr.push(field)
|
|
421
|
-
}
|
|
415
|
+
if (field) selectedFields.add(field)
|
|
422
416
|
})
|
|
423
417
|
}
|
|
424
418
|
})
|
|
419
|
+
|
|
420
|
+
// 收集 filterList 中定义的所有字段
|
|
421
|
+
const allDefinedFields = new Set()
|
|
422
|
+
this.filterList.forEach((item) => {
|
|
423
|
+
if (item.value) allDefinedFields.add(item.value)
|
|
424
|
+
if (item.startDate) allDefinedFields.add(item.startDate)
|
|
425
|
+
if (item.endDate) allDefinedFields.add(item.endDate)
|
|
426
|
+
if (item.startValue) allDefinedFields.add(item.startValue)
|
|
427
|
+
if (item.endValue) allDefinedFields.add(item.endValue)
|
|
428
|
+
if (item.type === 'slot' && Array.isArray(item?.slotFields)) {
|
|
429
|
+
item?.slotFields.forEach((field) => {
|
|
430
|
+
if (field) allDefinedFields.add(field)
|
|
431
|
+
})
|
|
432
|
+
}
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
// 只清除:在 filterList 中定义但不在选中列表中的字段
|
|
425
436
|
for (const key in this.model) {
|
|
426
437
|
if (Object.hasOwnProperty.call(this.model, key)) {
|
|
427
|
-
|
|
438
|
+
// 仅当该字段在 filterList 中定义且未被选中时才清除
|
|
439
|
+
if (allDefinedFields.has(key) && !selectedFields.has(key)) {
|
|
428
440
|
delete this.model[key]
|
|
429
441
|
}
|
|
430
442
|
}
|
|
@@ -2,10 +2,6 @@
|
|
|
2
2
|
<!-- 图表视图 -->
|
|
3
3
|
<div class="chart-view">
|
|
4
4
|
<div v-if="hasData" class="chart-wrapper">
|
|
5
|
-
<div class="chart-header">
|
|
6
|
-
<div class="chart-title">数据概览</div>
|
|
7
|
-
<div class="chart-desc">按维度「{{ dimensionLabel }}」聚合后的结果,共 {{ dataCount }} 条</div>
|
|
8
|
-
</div>
|
|
9
5
|
<div v-if="unitLabel" class="chart-unit-label">{{ unitLabel }}</div>
|
|
10
6
|
<div ref="chartContainer" class="chart-container"></div>
|
|
11
7
|
</div>
|
|
@@ -16,8 +12,8 @@
|
|
|
16
12
|
</template>
|
|
17
13
|
|
|
18
14
|
<script>
|
|
19
|
-
import Empty from '../Empty/index.vue'
|
|
20
15
|
import importG from '../../utils/importGlobal.js'
|
|
16
|
+
import Empty from '../Empty/index.vue'
|
|
21
17
|
|
|
22
18
|
export default {
|
|
23
19
|
name: 'ChartView',
|
|
@@ -106,4 +102,4 @@ export default {
|
|
|
106
102
|
this.destroyChart()
|
|
107
103
|
}
|
|
108
104
|
}
|
|
109
|
-
</script>
|
|
105
|
+
</script>
|
|
@@ -3,96 +3,96 @@
|
|
|
3
3
|
<div class="table-view">
|
|
4
4
|
<div v-if="hasData" class="table-wrapper">
|
|
5
5
|
<!-- 数据概览头部 -->
|
|
6
|
-
<div class="table-header">
|
|
6
|
+
<!-- <div class="table-header">
|
|
7
7
|
<div class="table-title">数据概览</div>
|
|
8
|
-
<div class="table-desc"
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
<thead>
|
|
16
|
-
<tr>
|
|
17
|
-
<th
|
|
18
|
-
v-for="col in tableColumns"
|
|
19
|
-
:key="col.key"
|
|
20
|
-
:class="col.type === 'metric' ? 'metric-header' : 'dimension-header'"
|
|
21
|
-
:style="col.width ? { width: col.width } : null"
|
|
22
|
-
>
|
|
23
|
-
{{ col.title }}
|
|
24
|
-
</th>
|
|
25
|
-
</tr>
|
|
26
|
-
</thead>
|
|
27
|
-
<tbody>
|
|
28
|
-
<tr v-for="(row, index) in tableData" :key="index">
|
|
29
|
-
<td
|
|
30
|
-
v-for="col in tableColumns"
|
|
31
|
-
:key="col.key"
|
|
32
|
-
:class="col.type === 'metric' ? 'metric-cell' : 'dimension-cell'"
|
|
33
|
-
:style="col.width ? { width: col.width } : null"
|
|
34
|
-
>
|
|
35
|
-
<template v-if="col.type === 'metric'">
|
|
36
|
-
<div class="cell-content">
|
|
37
|
-
<span v-if="col.aggregateType === 'count'" class="cell-count"
|
|
38
|
-
>{{ formatCount(row._groupCount) }} 笔</span
|
|
39
|
-
>
|
|
40
|
-
<span v-else class="cell-value">{{ formatNumber(row[col.key]) }}</span>
|
|
41
|
-
</div>
|
|
42
|
-
</template>
|
|
43
|
-
<template v-else>
|
|
44
|
-
{{ row[col.key] }}
|
|
45
|
-
</template>
|
|
46
|
-
</td>
|
|
47
|
-
</tr>
|
|
48
|
-
</tbody>
|
|
49
|
-
</table>
|
|
8
|
+
<div class="table-desc">
|
|
9
|
+
<template v-if="isCrossMode">
|
|
10
|
+
按行维度「{{ rowDimensionLabel }}」、列维度「{{ columnDimensionLabel }}」交叉分析后的结果,共 {{ dataCount }} 条
|
|
11
|
+
</template>
|
|
12
|
+
<template v-else>
|
|
13
|
+
按维度「{{ dimensionLabel }}」聚合后的结果,共 {{ dataCount }} 条
|
|
14
|
+
</template>
|
|
50
15
|
</div>
|
|
51
|
-
</
|
|
16
|
+
</div> -->
|
|
52
17
|
|
|
53
|
-
<!--
|
|
54
|
-
<
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
18
|
+
<!-- 表格容器 -->
|
|
19
|
+
<div class="table-container">
|
|
20
|
+
<vxe-table
|
|
21
|
+
ref="vxeTable"
|
|
22
|
+
:data="tableData"
|
|
23
|
+
:height="'100%'"
|
|
24
|
+
:scroll-y="{ enabled: true, gt: 0 }"
|
|
25
|
+
:scroll-x="{ enabled: true, gt: 0 }"
|
|
26
|
+
:row-config="{ isHover: true }"
|
|
27
|
+
:column-config="{ resizable: true }"
|
|
28
|
+
show-header-overflow
|
|
29
|
+
show-overflow
|
|
30
|
+
auto-resize
|
|
31
|
+
class="pivot-vxe-table"
|
|
32
|
+
>
|
|
33
|
+
<!-- 动态列渲染 - 支持多级表头 -->
|
|
34
|
+
<template v-for="col in vxeColumns">
|
|
35
|
+
<!-- 分组列(多级表头) -->
|
|
36
|
+
<vxe-colgroup
|
|
37
|
+
v-if="col.children && col.children.length > 0"
|
|
38
|
+
:key="col.key"
|
|
39
|
+
:title="col.title"
|
|
40
|
+
:header-align="col.headerAlign || 'center'"
|
|
41
|
+
>
|
|
42
|
+
<vxe-column
|
|
43
|
+
v-for="childCol in col.children"
|
|
44
|
+
:key="childCol.key"
|
|
45
|
+
:field="childCol.key"
|
|
46
|
+
:title="childCol.title"
|
|
47
|
+
:min-width="childCol.minWidth"
|
|
48
|
+
:align="childCol.align"
|
|
49
|
+
:header-align="childCol.headerAlign"
|
|
50
|
+
>
|
|
51
|
+
<template #default="{ row }">
|
|
52
|
+
<div class="metric-cell-content">
|
|
53
|
+
<span v-if="childCol.aggregateType === 'count'" class="metric-count">
|
|
54
|
+
{{ formatCount(row._groupCount) }} 笔
|
|
55
|
+
</span>
|
|
56
|
+
<span v-else class="metric-value">{{ formatNumber(row[childCol.key]) }}</span>
|
|
57
|
+
</div>
|
|
58
|
+
</template>
|
|
59
|
+
</vxe-column>
|
|
60
|
+
</vxe-colgroup>
|
|
61
|
+
|
|
62
|
+
<!-- 普通列 -->
|
|
63
|
+
<vxe-column
|
|
64
|
+
v-else
|
|
65
|
+
:key="col.field"
|
|
66
|
+
:field="col.field"
|
|
67
|
+
:title="col.title"
|
|
68
|
+
:min-width="col.minWidth"
|
|
69
|
+
:align="col.align"
|
|
70
|
+
:header-align="col.headerAlign"
|
|
71
|
+
>
|
|
72
|
+
<template #default="{ row }">
|
|
73
|
+
<!-- 维度列:直接显示值 -->
|
|
74
|
+
<template v-if="col.type === 'dimension'">
|
|
75
|
+
{{ row[col.field] }}
|
|
76
|
+
</template>
|
|
77
|
+
<!-- 指标列:根据聚合类型显示 -->
|
|
78
|
+
<template v-else-if="col.type === 'metric'">
|
|
79
|
+
<div class="metric-cell-content">
|
|
80
|
+
<span v-if="col.aggregateType === 'count'" class="metric-count">
|
|
81
|
+
{{ formatCount(row._groupCount) }} 笔
|
|
82
|
+
</span>
|
|
83
|
+
<span v-else class="metric-value">{{ formatNumber(row[col.field]) }}</span>
|
|
84
|
+
</div>
|
|
85
|
+
</template>
|
|
86
|
+
</template>
|
|
87
|
+
</vxe-column>
|
|
88
|
+
</template>
|
|
89
|
+
|
|
90
|
+
<!-- 空状态 -->
|
|
91
|
+
<template #empty>
|
|
92
|
+
<Empty type="noData2" :height="200" :width="200" content="暂无数据" />
|
|
93
|
+
</template>
|
|
94
|
+
</vxe-table>
|
|
95
|
+
</div>
|
|
96
96
|
|
|
97
97
|
<!-- 分页组件 -->
|
|
98
98
|
<div class="cross-pagination">
|
|
@@ -165,6 +165,16 @@ export default {
|
|
|
165
165
|
type: Boolean,
|
|
166
166
|
default: false
|
|
167
167
|
},
|
|
168
|
+
// 行维度标签(交叉统计用)
|
|
169
|
+
rowDimensionLabel: {
|
|
170
|
+
type: String,
|
|
171
|
+
default: ''
|
|
172
|
+
},
|
|
173
|
+
// 列维度标签(交叉统计用)
|
|
174
|
+
columnDimensionLabel: {
|
|
175
|
+
type: String,
|
|
176
|
+
default: ''
|
|
177
|
+
},
|
|
168
178
|
// 表格列定义
|
|
169
179
|
tableColumns: {
|
|
170
180
|
type: Array,
|
|
@@ -211,6 +221,67 @@ export default {
|
|
|
211
221
|
showPageSizeDropdown: false
|
|
212
222
|
}
|
|
213
223
|
},
|
|
224
|
+
computed: {
|
|
225
|
+
// 转换为 vxe-table 列格式(支持多级表头)
|
|
226
|
+
vxeColumns() {
|
|
227
|
+
// 计算列最小宽度的辅助函数
|
|
228
|
+
const calcMinWidth = (title, isDimension) => {
|
|
229
|
+
// 基础宽度:标题字符数 * 14px + 左右 padding 32px
|
|
230
|
+
const titleWidth = (title?.length || 0) * 14 + 32
|
|
231
|
+
// 维度列最小 120px,指标列最小 100px
|
|
232
|
+
const baseMin = isDimension ? 120 : 100
|
|
233
|
+
return Math.max(titleWidth, baseMin)
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (this.isCrossMode) {
|
|
237
|
+
return this.crossTableColumns.map((col) => {
|
|
238
|
+
// 分组列(多级表头)
|
|
239
|
+
if (col.type === 'group' && col.children) {
|
|
240
|
+
return {
|
|
241
|
+
key: col.key,
|
|
242
|
+
title: col.title,
|
|
243
|
+
type: 'group',
|
|
244
|
+
headerAlign: 'center',
|
|
245
|
+
// 递归处理子列
|
|
246
|
+
children: col.children.map((childCol) => ({
|
|
247
|
+
field: childCol.key,
|
|
248
|
+
title: childCol.title,
|
|
249
|
+
minWidth: calcMinWidth(childCol.title, false),
|
|
250
|
+
align: 'right',
|
|
251
|
+
headerAlign: 'right',
|
|
252
|
+
type: childCol.type,
|
|
253
|
+
aggregateType: childCol.aggregateType
|
|
254
|
+
}))
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
// 普通列
|
|
258
|
+
const isDimension = col.type === 'dimension'
|
|
259
|
+
return {
|
|
260
|
+
field: col.key,
|
|
261
|
+
title: col.title,
|
|
262
|
+
minWidth: calcMinWidth(col.title, isDimension),
|
|
263
|
+
align: isDimension ? 'left' : 'right',
|
|
264
|
+
headerAlign: isDimension ? 'left' : 'right',
|
|
265
|
+
type: col.type,
|
|
266
|
+
aggregateType: col.aggregateType
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
} else {
|
|
270
|
+
return this.tableColumns.map((col) => {
|
|
271
|
+
const isDimension = col.type === 'dimension'
|
|
272
|
+
return {
|
|
273
|
+
field: col.key,
|
|
274
|
+
title: col.title,
|
|
275
|
+
minWidth: calcMinWidth(col.title, isDimension),
|
|
276
|
+
align: isDimension ? 'left' : 'right',
|
|
277
|
+
headerAlign: isDimension ? 'left' : 'right',
|
|
278
|
+
type: col.type,
|
|
279
|
+
aggregateType: col.aggregateType
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
},
|
|
214
285
|
methods: {
|
|
215
286
|
// 格式化数值显示
|
|
216
287
|
formatNumber(val) {
|
|
@@ -257,6 +328,13 @@ export default {
|
|
|
257
328
|
// 关闭下拉菜单(供外部调用)
|
|
258
329
|
closeDropdown() {
|
|
259
330
|
this.showPageSizeDropdown = false
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
// 刷新表格(供外部调用)
|
|
334
|
+
refreshTable() {
|
|
335
|
+
if (this.$refs.vxeTable) {
|
|
336
|
+
this.$refs.vxeTable.refreshColumn()
|
|
337
|
+
}
|
|
260
338
|
}
|
|
261
339
|
}
|
|
262
340
|
}
|