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
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
ref="mainToolbar"
|
|
25
25
|
:report-name="currentReport.name"
|
|
26
26
|
:calculate-time="calculateTime"
|
|
27
|
-
:data-count="
|
|
27
|
+
:data-count="rawDataCount"
|
|
28
28
|
:view-mode="viewMode"
|
|
29
29
|
:view-mode-options="viewModeOptions"
|
|
30
30
|
:is-cross-mode="isCrossMode"
|
|
@@ -58,6 +58,8 @@
|
|
|
58
58
|
:data-count="dataCount"
|
|
59
59
|
:dimension-label="getFirstDimensionLabel"
|
|
60
60
|
:is-cross-mode="isCrossMode"
|
|
61
|
+
:row-dimension-label="getRowDimensionLabel"
|
|
62
|
+
:column-dimension-label="getColumnDimensionLabel"
|
|
61
63
|
:table-columns="tableColumns"
|
|
62
64
|
:cross-table-columns="crossTableColumns"
|
|
63
65
|
:table-data="paginatedTableData"
|
|
@@ -190,8 +192,6 @@ export default {
|
|
|
190
192
|
reportList: [],
|
|
191
193
|
// 当前选中的报表索引
|
|
192
194
|
currentReportIndex: 0,
|
|
193
|
-
// 视图模式: chart / table
|
|
194
|
-
viewMode: 'chart',
|
|
195
195
|
// 计算耗时
|
|
196
196
|
calculateTime: 0,
|
|
197
197
|
// 数据条数
|
|
@@ -237,6 +237,11 @@ export default {
|
|
|
237
237
|
return this.data || []
|
|
238
238
|
},
|
|
239
239
|
|
|
240
|
+
// 原始数据条数(用于工具栏显示)
|
|
241
|
+
rawDataCount() {
|
|
242
|
+
return this.actualData.length
|
|
243
|
+
},
|
|
244
|
+
|
|
240
245
|
visibleC: {
|
|
241
246
|
get() {
|
|
242
247
|
return this.visible
|
|
@@ -260,6 +265,16 @@ export default {
|
|
|
260
265
|
return this.currentReport.statType === 'cross'
|
|
261
266
|
},
|
|
262
267
|
|
|
268
|
+
// 视图模式:从报表配置中读取,交叉统计强制为 table
|
|
269
|
+
viewMode() {
|
|
270
|
+
// 交叉统计模式强制使用表格视图
|
|
271
|
+
if (this.isCrossMode) {
|
|
272
|
+
return 'table'
|
|
273
|
+
}
|
|
274
|
+
// 从报表配置中读取,默认为 chart
|
|
275
|
+
return this.currentReport.viewMode || 'chart'
|
|
276
|
+
},
|
|
277
|
+
|
|
263
278
|
// 是否有数据
|
|
264
279
|
hasData() {
|
|
265
280
|
return this.processedData.length > 0
|
|
@@ -488,7 +503,7 @@ export default {
|
|
|
488
503
|
return cols
|
|
489
504
|
},
|
|
490
505
|
|
|
491
|
-
//
|
|
506
|
+
// 交叉表列定义(支持多级表头)
|
|
492
507
|
crossTableColumns() {
|
|
493
508
|
const { rowDimension, columnDimension, metrics: rawMetrics } = this.currentReport
|
|
494
509
|
const metrics = (rawMetrics || []).filter((m) => m && m.prop)
|
|
@@ -496,6 +511,7 @@ export default {
|
|
|
496
511
|
|
|
497
512
|
const cols = []
|
|
498
513
|
|
|
514
|
+
// 行维度列(第一列)
|
|
499
515
|
const rowDimDef = this.columns.find((c) => c.prop === rowDimension)
|
|
500
516
|
cols.push({
|
|
501
517
|
key: rowDimension,
|
|
@@ -504,22 +520,55 @@ export default {
|
|
|
504
520
|
isPrimary: true
|
|
505
521
|
})
|
|
506
522
|
|
|
507
|
-
|
|
523
|
+
// 获取列维度的唯一值,使用 getEnumDisplayValue 处理枚举和对象类型
|
|
524
|
+
const uniqueColumnValues = [
|
|
525
|
+
...new Set(
|
|
526
|
+
this.actualData.map((item) => this.getEnumDisplayValue(item, columnDimension))
|
|
527
|
+
)
|
|
528
|
+
]
|
|
508
529
|
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
530
|
+
// 如果有多个指标,使用多级表头结构
|
|
531
|
+
if (metrics.length > 1) {
|
|
532
|
+
// 每个列维度值作为一个父列
|
|
533
|
+
uniqueColumnValues.forEach((colValue) => {
|
|
534
|
+
const safeColValue = String(colValue)
|
|
535
|
+
const children = metrics.map((metric) => {
|
|
536
|
+
const metricDef = this.columns.find((c) => c.prop === metric.prop)
|
|
537
|
+
const colMetricKey = `${safeColValue}_${metric.prop}`
|
|
538
|
+
return {
|
|
539
|
+
key: colMetricKey,
|
|
540
|
+
title: metricDef ? metricDef.label : metric.prop,
|
|
541
|
+
type: 'metric',
|
|
542
|
+
aggregateType: metric.aggregateType,
|
|
543
|
+
columnValue: colValue,
|
|
544
|
+
metricKey: metric.prop
|
|
545
|
+
}
|
|
546
|
+
})
|
|
513
547
|
cols.push({
|
|
514
|
-
key:
|
|
515
|
-
title:
|
|
516
|
-
type: '
|
|
517
|
-
|
|
518
|
-
columnValue: colValue,
|
|
519
|
-
metricKey: metric.prop
|
|
548
|
+
key: `group_${safeColValue}`,
|
|
549
|
+
title: colValue,
|
|
550
|
+
type: 'group',
|
|
551
|
+
children
|
|
520
552
|
})
|
|
521
553
|
})
|
|
522
|
-
}
|
|
554
|
+
} else {
|
|
555
|
+
// 单个指标时,使用原来的扁平结构
|
|
556
|
+
uniqueColumnValues.forEach((colValue) => {
|
|
557
|
+
metrics.forEach((metric) => {
|
|
558
|
+
const metricDef = this.columns.find((c) => c.prop === metric.prop)
|
|
559
|
+
const safeColValue = String(colValue)
|
|
560
|
+
const colMetricKey = `${safeColValue}_${metric.prop}`
|
|
561
|
+
cols.push({
|
|
562
|
+
key: colMetricKey,
|
|
563
|
+
title: `${colValue} - ${metricDef ? metricDef.label : metric.prop}`,
|
|
564
|
+
type: 'metric',
|
|
565
|
+
aggregateType: metric.aggregateType,
|
|
566
|
+
columnValue: colValue,
|
|
567
|
+
metricKey: metric.prop
|
|
568
|
+
})
|
|
569
|
+
})
|
|
570
|
+
})
|
|
571
|
+
}
|
|
523
572
|
|
|
524
573
|
return cols
|
|
525
574
|
},
|
|
@@ -557,6 +606,22 @@ export default {
|
|
|
557
606
|
return dimDef ? dimDef.label : dim
|
|
558
607
|
},
|
|
559
608
|
|
|
609
|
+
// 获取行维度标签(交叉统计用)
|
|
610
|
+
getRowDimensionLabel() {
|
|
611
|
+
const dim = this.currentReport.rowDimension
|
|
612
|
+
if (!dim) return '未选择'
|
|
613
|
+
const dimDef = this.columns.find((c) => c.prop === dim)
|
|
614
|
+
return dimDef ? dimDef.label : dim
|
|
615
|
+
},
|
|
616
|
+
|
|
617
|
+
// 获取列维度标签(交叉统计用)
|
|
618
|
+
getColumnDimensionLabel() {
|
|
619
|
+
const dim = this.currentReport.columnDimension
|
|
620
|
+
if (!dim) return '未选择'
|
|
621
|
+
const dimDef = this.columns.find((c) => c.prop === dim)
|
|
622
|
+
return dimDef ? dimDef.label : dim
|
|
623
|
+
},
|
|
624
|
+
|
|
560
625
|
// 图表配置
|
|
561
626
|
chartOption() {
|
|
562
627
|
if (!this.hasData) return {}
|
|
@@ -736,6 +801,27 @@ export default {
|
|
|
736
801
|
},
|
|
737
802
|
|
|
738
803
|
watch: {
|
|
804
|
+
// 弹窗打开时重新加载数据
|
|
805
|
+
visible(newVal) {
|
|
806
|
+
if (newVal) {
|
|
807
|
+
// 弹窗打开时重新加载持久化数据
|
|
808
|
+
this.loadFromStorage()
|
|
809
|
+
// 如果没有报表则新建
|
|
810
|
+
if (this.reportList.length === 0) {
|
|
811
|
+
this.addNewReport()
|
|
812
|
+
}
|
|
813
|
+
// 初始化图表
|
|
814
|
+
if (this.viewMode === 'chart') {
|
|
815
|
+
this.$nextTick(() => {
|
|
816
|
+
this.initChart()
|
|
817
|
+
})
|
|
818
|
+
}
|
|
819
|
+
} else {
|
|
820
|
+
// 弹窗关闭时保存数据
|
|
821
|
+
this.saveToStorage()
|
|
822
|
+
}
|
|
823
|
+
},
|
|
824
|
+
|
|
739
825
|
actualData: {
|
|
740
826
|
handler(newData) {
|
|
741
827
|
if (newData && newData.length > 0) {
|
|
@@ -754,7 +840,10 @@ export default {
|
|
|
754
840
|
columns: {
|
|
755
841
|
handler(newColumns) {
|
|
756
842
|
if (newColumns && newColumns.length > 0) {
|
|
757
|
-
|
|
843
|
+
// 仅在首次初始化或没有配置时调用
|
|
844
|
+
if (!this.currentReport.selectedDimension && !this.currentReport.rowDimension) {
|
|
845
|
+
this.initDefaultConfig()
|
|
846
|
+
}
|
|
758
847
|
}
|
|
759
848
|
},
|
|
760
849
|
immediate: true
|
|
@@ -804,6 +893,7 @@ export default {
|
|
|
804
893
|
immediate: true
|
|
805
894
|
},
|
|
806
895
|
|
|
896
|
+
// 视图模式变化时初始化图表
|
|
807
897
|
viewMode(newVal) {
|
|
808
898
|
if (newVal === 'chart') {
|
|
809
899
|
this.$nextTick(() => {
|
|
@@ -811,6 +901,7 @@ export default {
|
|
|
811
901
|
})
|
|
812
902
|
}
|
|
813
903
|
},
|
|
904
|
+
|
|
814
905
|
chartOption: {
|
|
815
906
|
deep: true,
|
|
816
907
|
handler() {
|
|
@@ -917,17 +1008,30 @@ export default {
|
|
|
917
1008
|
// 初始化默认配置
|
|
918
1009
|
initDefaultConfig() {
|
|
919
1010
|
if (!this.currentReport) return
|
|
920
|
-
if (this.currentReport.selectedDimension) {
|
|
921
|
-
return
|
|
922
|
-
}
|
|
923
1011
|
|
|
924
1012
|
const allColumns = this.availableDimensions
|
|
925
1013
|
const numericCols = this.availableMetrics
|
|
926
1014
|
|
|
927
|
-
|
|
1015
|
+
// 分组统计模式:初始化分组维度
|
|
1016
|
+
if (
|
|
1017
|
+
this.currentReport.statType === 'group' &&
|
|
1018
|
+
!this.currentReport.selectedDimension &&
|
|
1019
|
+
allColumns.length > 0
|
|
1020
|
+
) {
|
|
928
1021
|
this.currentReport.selectedDimension = allColumns[0].prop
|
|
929
1022
|
}
|
|
930
1023
|
|
|
1024
|
+
// 交叉统计模式:初始化行列维度
|
|
1025
|
+
if (this.currentReport.statType === 'cross') {
|
|
1026
|
+
if (!this.currentReport.rowDimension && allColumns.length > 0) {
|
|
1027
|
+
this.currentReport.rowDimension = allColumns[0].prop
|
|
1028
|
+
}
|
|
1029
|
+
if (!this.currentReport.columnDimension && allColumns.length > 1) {
|
|
1030
|
+
this.currentReport.columnDimension = allColumns[1].prop
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
|
|
1034
|
+
// 初始化指标(仅当没有任何指标时)
|
|
931
1035
|
if (numericCols.length > 0 && this.currentReport.metrics.length === 0) {
|
|
932
1036
|
this.currentReport.metrics = [
|
|
933
1037
|
{
|
|
@@ -948,12 +1052,21 @@ export default {
|
|
|
948
1052
|
selectedDimension: null,
|
|
949
1053
|
rowDimension: null,
|
|
950
1054
|
columnDimension: null,
|
|
951
|
-
metrics: []
|
|
1055
|
+
metrics: [],
|
|
1056
|
+
// 添加视图模式持久化
|
|
1057
|
+
viewMode: 'chart'
|
|
952
1058
|
}
|
|
953
1059
|
},
|
|
954
1060
|
|
|
955
1061
|
// 迁移旧格式报表数据
|
|
956
1062
|
migrateReportFormat(report) {
|
|
1063
|
+
if (!report) return this.getDefaultReport()
|
|
1064
|
+
|
|
1065
|
+
// 确保 statType 存在,默认为 'group'
|
|
1066
|
+
if (!report.statType) {
|
|
1067
|
+
report.statType = 'group'
|
|
1068
|
+
}
|
|
1069
|
+
|
|
957
1070
|
if (!report.metrics) {
|
|
958
1071
|
report.metrics = []
|
|
959
1072
|
}
|
|
@@ -963,7 +1076,7 @@ export default {
|
|
|
963
1076
|
if (typeof m === 'string') {
|
|
964
1077
|
return { prop: m, aggregateType: report.aggregateType || 'sum' }
|
|
965
1078
|
}
|
|
966
|
-
if (m.key && !m.prop) {
|
|
1079
|
+
if (m && m.key && !m.prop) {
|
|
967
1080
|
return { prop: m.key, aggregateType: m.aggregateType || 'sum' }
|
|
968
1081
|
}
|
|
969
1082
|
return m
|
|
@@ -981,6 +1094,14 @@ export default {
|
|
|
981
1094
|
if (!report.selectedDimension) {
|
|
982
1095
|
report.selectedDimension = null
|
|
983
1096
|
}
|
|
1097
|
+
// 确保图表类型存在
|
|
1098
|
+
if (!report.chartType) {
|
|
1099
|
+
report.chartType = 'bar'
|
|
1100
|
+
}
|
|
1101
|
+
// 确保 viewMode 存在,交叉统计默认为 table,分组统计默认为 chart
|
|
1102
|
+
if (!report.viewMode) {
|
|
1103
|
+
report.viewMode = report.statType === 'cross' ? 'table' : 'chart'
|
|
1104
|
+
}
|
|
984
1105
|
delete report.aggregateType
|
|
985
1106
|
delete report.dimensions
|
|
986
1107
|
return report
|
|
@@ -1051,7 +1172,15 @@ export default {
|
|
|
1051
1172
|
|
|
1052
1173
|
// ========== MainToolbar 事件处理 ==========
|
|
1053
1174
|
handleViewModeChange(val) {
|
|
1054
|
-
|
|
1175
|
+
// 将视图模式保存到报表配置中
|
|
1176
|
+
this.currentReport.viewMode = val
|
|
1177
|
+
this.saveToStorage()
|
|
1178
|
+
// 切换到图表视图时初始化图表
|
|
1179
|
+
if (val === 'chart') {
|
|
1180
|
+
this.$nextTick(() => {
|
|
1181
|
+
this.initChart()
|
|
1182
|
+
})
|
|
1183
|
+
}
|
|
1055
1184
|
},
|
|
1056
1185
|
|
|
1057
1186
|
handleRefresh() {
|
|
@@ -1143,13 +1272,17 @@ export default {
|
|
|
1143
1272
|
handleStatTypeChange(type) {
|
|
1144
1273
|
this.currentReport.statType = type
|
|
1145
1274
|
if (type === 'cross') {
|
|
1146
|
-
//
|
|
1275
|
+
// 切换到交叉统计时清除分组维度,并强制使用表格视图
|
|
1147
1276
|
this.currentReport.selectedDimension = null
|
|
1148
|
-
this.viewMode = 'table'
|
|
1277
|
+
this.currentReport.viewMode = 'table'
|
|
1149
1278
|
} else {
|
|
1150
1279
|
// 切换到分组统计时清除行列维度
|
|
1151
1280
|
this.currentReport.rowDimension = null
|
|
1152
1281
|
this.currentReport.columnDimension = null
|
|
1282
|
+
// 如果之前是表格视图,可以保持;否则默认图表视图
|
|
1283
|
+
if (!this.currentReport.viewMode || this.currentReport.viewMode === 'table') {
|
|
1284
|
+
this.currentReport.viewMode = 'chart'
|
|
1285
|
+
}
|
|
1153
1286
|
}
|
|
1154
1287
|
this.saveToStorage()
|
|
1155
1288
|
},
|
|
@@ -1234,11 +1367,18 @@ export default {
|
|
|
1234
1367
|
if (saved) {
|
|
1235
1368
|
const configs = JSON.parse(saved)
|
|
1236
1369
|
if (Array.isArray(configs) && configs.length > 0) {
|
|
1370
|
+
// 迁移格式并加载报表列表
|
|
1237
1371
|
this.reportList = configs.map((report) => this.migrateReportFormat(report))
|
|
1372
|
+
// 确保 currentReportIndex 有效
|
|
1373
|
+
if (this.currentReportIndex >= this.reportList.length) {
|
|
1374
|
+
this.currentReportIndex = 0
|
|
1375
|
+
}
|
|
1238
1376
|
}
|
|
1239
1377
|
}
|
|
1240
1378
|
} catch (e) {
|
|
1241
1379
|
console.warn('Failed to load pivot configs from storage:', e)
|
|
1380
|
+
// 加载失败时清空报表列表
|
|
1381
|
+
this.reportList = []
|
|
1242
1382
|
}
|
|
1243
1383
|
},
|
|
1244
1384
|
|
|
@@ -213,11 +213,12 @@
|
|
|
213
213
|
|
|
214
214
|
<script>
|
|
215
215
|
import advancedQuery from './advancedQuery.vue'
|
|
216
|
+
|
|
217
|
+
import axios from '../../utils/axios.js'
|
|
216
218
|
import ClAdvancedFilter from '../AdvancedFilter/index.vue'
|
|
217
|
-
import ClInputSearch from '../InputSearch/index.vue'
|
|
218
|
-
import ClDragList from '../DragList/index.vue'
|
|
219
219
|
import ClDialog from '../Dialog/index.vue'
|
|
220
|
-
import
|
|
220
|
+
import ClDragList from '../DragList/index.vue'
|
|
221
|
+
import ClInputSearch from '../InputSearch/index.vue'
|
|
221
222
|
|
|
222
223
|
const VIEW_TYPE = { ADVANCED: '0', BASIC: '1' }
|
|
223
224
|
|
|
@@ -299,13 +300,21 @@ export default {
|
|
|
299
300
|
|
|
300
301
|
getInitialSearchValue() {
|
|
301
302
|
const obj = {}
|
|
302
|
-
//
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
303
|
+
// 为 slot 类型筛选项初始化值
|
|
304
|
+
this.filterList
|
|
305
|
+
.filter((item) => item.type === 'slot')
|
|
306
|
+
.forEach((item) => {
|
|
307
|
+
// 单值绑定
|
|
308
|
+
if (item.value) {
|
|
309
|
+
obj[item.value] = undefined
|
|
310
|
+
}
|
|
311
|
+
// 多值绑定(slotFields 数组)
|
|
312
|
+
if (item.slotFields?.length) {
|
|
313
|
+
item.slotFields.forEach((field) => {
|
|
314
|
+
obj[field] = undefined
|
|
315
|
+
})
|
|
316
|
+
}
|
|
317
|
+
})
|
|
309
318
|
return obj
|
|
310
319
|
},
|
|
311
320
|
filterObj() {
|
|
@@ -411,27 +420,38 @@ export default {
|
|
|
411
420
|
handleClear() {
|
|
412
421
|
// 收集 required 项的当前值,清空时保留
|
|
413
422
|
const preserved = {}
|
|
414
|
-
this.filterList
|
|
415
|
-
|
|
416
|
-
|
|
423
|
+
this.filterList
|
|
424
|
+
.filter((item) => item.required)
|
|
425
|
+
.forEach((item) => {
|
|
426
|
+
// 日期范围类型
|
|
417
427
|
if (item.startDate && this.searchValue[item.startDate] !== undefined) {
|
|
418
428
|
preserved[item.startDate] = this.searchValue[item.startDate]
|
|
419
429
|
}
|
|
420
430
|
if (item.endDate && this.searchValue[item.endDate] !== undefined) {
|
|
421
431
|
preserved[item.endDate] = this.searchValue[item.endDate]
|
|
422
432
|
}
|
|
423
|
-
|
|
433
|
+
// 数字范围类型
|
|
424
434
|
if (item.startValue && this.searchValue[item.startValue] !== undefined) {
|
|
425
435
|
preserved[item.startValue] = this.searchValue[item.startValue]
|
|
426
436
|
}
|
|
427
437
|
if (item.endValue && this.searchValue[item.endValue] !== undefined) {
|
|
428
438
|
preserved[item.endValue] = this.searchValue[item.endValue]
|
|
429
439
|
}
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
440
|
+
// 普通单值
|
|
441
|
+
if (item.value && this.searchValue[item.value] !== undefined) {
|
|
442
|
+
preserved[item.value] = this.searchValue[item.value]
|
|
443
|
+
}
|
|
444
|
+
// slot 多值绑定
|
|
445
|
+
if (item.slotFields?.length) {
|
|
446
|
+
item.slotFields.forEach((field) => {
|
|
447
|
+
if (this.searchValue[field] !== undefined) {
|
|
448
|
+
preserved[field] = this.searchValue[field]
|
|
449
|
+
}
|
|
450
|
+
})
|
|
451
|
+
}
|
|
452
|
+
})
|
|
453
|
+
|
|
454
|
+
this.searchValue = { ...this.initialValue, ...this.getInitialSearchValue, ...preserved }
|
|
435
455
|
// 直接构建 payload,绕过 filterObj 避免 initialValue 重新注入
|
|
436
456
|
this.$emit(
|
|
437
457
|
'filter',
|