n20-common-lib 3.1.2 → 3.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "n20-common-lib",
3
- "version": "3.1.2",
3
+ "version": "3.1.4",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "vue-cli-service serve",
@@ -70,7 +70,7 @@
70
70
  <!-- 内容头部工具栏 -->
71
71
  <div class="main-toolbar">
72
72
  <div class="toolbar-left">
73
- <div>
73
+ <div class="toolbar-left-top">
74
74
  <h2 class="report-title">{{ currentReport.name }}</h2>
75
75
  <div class="add-to-home-btn" @click="handleAddToHome">
76
76
  <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
@@ -81,6 +81,7 @@
81
81
  </div>
82
82
  <div class="report-stats">
83
83
  <span class="stat-item">计算耗时:{{ calculateTime }} ms</span>
84
+ <span class="stat-divider"></span>
84
85
  <span class="stat-item">数据条数:{{ dataCount }}</span>
85
86
  </div>
86
87
  </div>
@@ -95,42 +96,10 @@
95
96
 
96
97
  <div class="action-buttons">
97
98
  <div class="action-btn" @click="handleRefresh" title="刷新">
98
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
99
- <path
100
- d="M13 2.5V6.5H9M1 11.5V7.5H5"
101
- stroke="currentColor"
102
- stroke-width="1.5"
103
- stroke-linecap="round"
104
- stroke-linejoin="round"
105
- />
106
- <path
107
- d="M13 2.5C11.5 1 9.5 0.5 7 1C4.5 1.5 2.5 2.5 1.5 4.5C0.5 6.5 0.5 9 2 11C3.5 13 5.5 14 7.5 14C9.5 14 11 13 12 12"
108
- stroke="currentColor"
109
- stroke-width="1.5"
110
- stroke-linecap="round"
111
- />
112
- </svg>
99
+ <i class="n20-icon-shuaxin"></i>
113
100
  </div>
114
- <div class="action-btn" @click="handleExport" title="导出">
115
- <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
116
- <path
117
- d="M3 9L7 13L11 9"
118
- stroke="currentColor"
119
- stroke-width="1.5"
120
- stroke-linecap="round"
121
- stroke-linejoin="round"
122
- />
123
- <path d="M7 3V11" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" />
124
- <path
125
- d="M1 9V11C1 11.5523 1.44772 12 2 12H12C12.5523 12 13 11.5523 13 11V9"
126
- stroke="currentColor"
127
- stroke-width="1.5"
128
- stroke-linecap="round"
129
- />
130
- </svg>
131
- </div>
132
- <div class="action-btn" title="折叠" @click="handleToggleRightSidebar">
133
- <i :class="isRightSidebarCollapsed ? 'v3-icon-move-left' : 'v3-icon-move-right'" class="pointer"></i>
101
+ <div class="action-btn" @click="handleMore" title="更多">
102
+ <i class="v3-icon-more"></i>
134
103
  </div>
135
104
  </div>
136
105
  </div>
@@ -464,10 +433,7 @@ export default {
464
433
  // 图表实例
465
434
  chartInstance: null,
466
435
  // 下拉框显示状态
467
- showChartTypeDropdown: false,
468
- showDimensionDropdown: false,
469
- showMetricDropdown: false,
470
- showAggregateDropdown: false,
436
+ showCustomAggregateDropdown: false,
471
437
  // 左侧边栏是否折叠
472
438
  isLeftSidebarCollapsed: false,
473
439
  // 右侧边栏是否折叠
@@ -806,11 +772,20 @@ export default {
806
772
  statType: 'group',
807
773
  chartType: 'bar',
808
774
  dimensions: [],
809
- metrics: [],
810
- aggregateType: 'sum'
775
+ metrics: [] // 每项格式: { key: '字段名', aggregateType: 'sum' }
811
776
  }
812
777
  },
813
778
 
779
+ // 迁移旧格式报表数据(metrics 从 string[] 迁移为 { key, aggregateType }[])
780
+ migrateReportFormat(report) {
781
+ if (report.metrics && report.metrics.length > 0 && typeof report.metrics[0] === 'string') {
782
+ const aggregateType = report.aggregateType || 'sum'
783
+ report.metrics = report.metrics.map((key) => ({ key, aggregateType }))
784
+ }
785
+ delete report.aggregateType
786
+ return report
787
+ },
788
+
814
789
  // 生成唯一ID
815
790
  generateId() {
816
791
  return 'report_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9)
@@ -941,7 +916,8 @@ export default {
941
916
  if (saved) {
942
917
  const configs = JSON.parse(saved)
943
918
  if (Array.isArray(configs) && configs.length > 0) {
944
- this.reportList = configs
919
+ // 迁移旧格式数据
920
+ this.reportList = configs.map((report) => this.migrateReportFormat(report))
945
921
  }
946
922
  }
947
923
  } catch (e) {
@@ -1377,18 +1353,24 @@ export default {
1377
1353
  }
1378
1354
 
1379
1355
  .main-toolbar {
1380
- height: 50px;
1381
- padding: 0 16px;
1382
1356
  display: flex;
1383
1357
  align-items: center;
1384
1358
  justify-content: space-between;
1359
+ padding: 6px 16px;
1385
1360
  border-bottom: 1px solid #e5e6eb;
1386
1361
  }
1387
1362
 
1388
1363
  .toolbar-left {
1364
+ display: flex;
1365
+ flex-direction: column;
1366
+ justify-content: center;
1367
+ gap: 4px;
1368
+ }
1369
+
1370
+ .toolbar-left-top {
1389
1371
  display: flex;
1390
1372
  align-items: center;
1391
- gap: 12px;
1373
+ gap: 8px;
1392
1374
  }
1393
1375
 
1394
1376
  .report-title {
@@ -1420,8 +1402,7 @@ export default {
1420
1402
  .report-stats {
1421
1403
  display: flex;
1422
1404
  align-items: center;
1423
- gap: 16px;
1424
- margin-left: 16px;
1405
+ gap: 8px;
1425
1406
  }
1426
1407
 
1427
1408
  .stat-item {
@@ -1429,10 +1410,16 @@ export default {
1429
1410
  color: #4e5969;
1430
1411
  }
1431
1412
 
1413
+ .stat-divider {
1414
+ width: 0;
1415
+ height: 12px;
1416
+ border-left: 1px solid #e5e6eb;
1417
+ }
1418
+
1432
1419
  .toolbar-right {
1433
1420
  display: flex;
1434
1421
  align-items: center;
1435
- gap: 16px;
1422
+ gap: 8px;
1436
1423
  }
1437
1424
 
1438
1425
  .action-buttons {
@@ -101,6 +101,7 @@
101
101
  v-if="filter.value"
102
102
  :name="filter.slotName"
103
103
  :model="model"
104
+ :item="filter"
104
105
  :value="searchValue[filter.value]"
105
106
  :input="(val) => handleSlotInput(filter.value, val)"
106
107
  ></slot>
@@ -335,22 +336,20 @@ export default {
335
336
  if (!newVal) return
336
337
 
337
338
  // 智能合并:保留用户已手动修改的值
338
- // 策略:只有当 searchValue 的值等于初始值时,才用 newVal 更新
339
- const mergedValue = { ...this.searchValue }
340
-
341
- Object.keys(newVal || {}).forEach((key) => {
342
- // 只有当前值与初始值相同时才更新(说明用户没修改过)
343
- // 或者 searchValue 中没有这个字段
344
- if (this.searchValue[key] === undefined || this.searchValue[key] === this.getInitialSearchValue[key]) {
345
- this.$set(mergedValue, key, newVal[key])
346
- }
347
- // 否则保留用户已修改的值
348
- })
349
-
350
- this.searchValue = mergedValue
339
+ this.searchValue = newVal
351
340
  },
352
341
  deep: true,
353
342
  immediate: false
343
+ },
344
+ // filterId 变化时重新请求视图列表并重置状态
345
+ filterId: {
346
+ handler(newVal, oldVal) {
347
+ if (newVal && newVal !== oldVal) {
348
+ this.resetState()
349
+ this.getFilterList()
350
+ }
351
+ },
352
+ immediate: false
354
353
  }
355
354
  },
356
355
  mounted() {
@@ -362,13 +361,6 @@ export default {
362
361
  this.searchValue = { ...this.getInitialSearchValue, ...this.initialValue }
363
362
  },
364
363
  methods: {
365
- /**
366
- * 供外部调用更新 GroupData
367
- * @param {Array} data - 筛选条件数据
368
- */
369
- setGroupData(data) {
370
- this.$refs.filter.mackData(data || [])
371
- },
372
364
  // 处理 slot 类型字段的输入事件,同时更新 searchValue 和 initialValue
373
365
  handleSlotInput(fieldName, val) {
374
366
  // 防御性检查:如果 fieldName 无效,不执行任何操作
@@ -394,7 +386,7 @@ export default {
394
386
  })
395
387
  },
396
388
  changeFn(value) {
397
- let obj = value
389
+ const obj = { ...value }
398
390
  const filter = this.filterList.find((item) => item.value === value.field)
399
391
  if (filter?.type === 'daterange') {
400
392
  obj.value = {
@@ -452,16 +444,6 @@ export default {
452
444
  'clear'
453
445
  )
454
446
  },
455
- isEnter() {
456
- this.$rulesValidateForm('ruleValidate', (valid) => {
457
- if (!valid) {
458
- console.log('校验不通过')
459
- this.$message.warning('校验不通过')
460
- } else {
461
- this.$message.warning('校验通过')
462
- }
463
- })
464
- },
465
447
  // 通过bussId拿取视图列表
466
448
  getFilterList(viewId) {
467
449
  // 如果没有 bussId,不获取视图列表
@@ -469,11 +451,10 @@ export default {
469
451
  return
470
452
  }
471
453
 
472
- let list = []
473
454
  axios
474
455
  .get(`/bems/query/viewColumn/getViewInfo`, { bussId: this.bussId })
475
456
  .then((res) => {
476
- list = res.data.map((item) => {
457
+ const list = (res.data || []).map((item) => {
477
458
  item.keyIds = this.safeParse(item.keyIds, [])
478
459
  return item
479
460
  })
@@ -522,7 +503,6 @@ export default {
522
503
  // 修改
523
504
  edit(item, isRefresh = false) {
524
505
  this.isRefresh = isRefresh
525
- console.log(item)
526
506
  this.viewPopoverVisible = false
527
507
  this.visible = true
528
508
  this.isAdd = false
@@ -642,7 +622,6 @@ export default {
642
622
  },
643
623
  // 选中视图
644
624
  handleSelect(item) {
645
- console.log(item)
646
625
  this.viewPopoverVisible = false
647
626
  this.selectItem = item
648
627
  this.selectedItem = item.viewName
@@ -648,7 +648,7 @@ export function saveTransform(list, labelKey, isFilter) {
648
648
  // 遍历列配置列表,将每列转换为存储格式
649
649
  list.forEach((c) => {
650
650
  // 普通列:有prop属性、无子列、非新增列、无宽度设置
651
- if (c.prop && !c.children && !c.isNew && !c.width & !c.isNew) {
651
+ if (c.prop && !c.children && !c.isNew && !c.width && !c.isNew) {
652
652
  const entry = { _colKey: 'prop', _colVal: c.prop }
653
653
  if (c.fixed) entry.fixed = c.fixed
654
654
  listN.push(entry)
@@ -281,7 +281,12 @@
281
281
  </div>
282
282
  </template>
283
283
  <template #default="{ row, $rowIndex }">
284
- <OperateBtns :btn-list="btnList" :row="row" :get-operate-btns="getOperateBtns" @command="(c) => handleOperateCommand(c, row, $rowIndex)" />
284
+ <OperateBtns
285
+ :btn-list="btnList"
286
+ :row="row"
287
+ :get-operate-btns="getOperateBtns"
288
+ @command="(c) => handleOperateCommand(c, row, $rowIndex)"
289
+ />
285
290
  </template>
286
291
  </vxe-column>
287
292
  <template #empty>
@@ -526,7 +531,8 @@ export default {
526
531
  },
527
532
  // 悬浮按钮组隐藏延迟(毫秒)
528
533
  hoverBtnsHideDelay: {
529
- type: Number
534
+ type: Number,
535
+ default: 100
530
536
  },
531
537
  // 悬浮按钮组最大显示数量,超过则收起到"更多"下拉菜单
532
538
  hoverBtnsMaxShow: {
@@ -603,10 +609,15 @@ export default {
603
609
  computed: {
604
610
  _columns: {
605
611
  get() {
612
+ // 当操作列已通过模板固定渲染时,过滤掉 checkColumns 中的操作列,避免重复
613
+ const cols =
614
+ this.isOperateFixed && this.btnList && this.btnList.length > 0
615
+ ? this.checkColumns.filter((col) => !(col.static && col.label === $lc('操作')))
616
+ : this.checkColumns
606
617
  if (this.isAutoWidth) {
607
- return this.calcColumnWidth(this.checkColumns)
618
+ return this.calcColumnWidth(cols)
608
619
  } else {
609
- return this.checkColumns
620
+ return cols
610
621
  }
611
622
  },
612
623
  set(val) {
@@ -693,8 +704,14 @@ export default {
693
704
  this.dialogVisible = true
694
705
  this.isExport = true
695
706
  },
696
- resizableChange({ resizeWidth, columnIndex }) {
697
- this.checkColumns[columnIndex].width = resizeWidth
707
+ resizableChange({ resizeWidth, column, columnIndex }) {
708
+ // 通过 prop 匹配 checkColumns 中的列,避免 _columns 过滤后索引错位
709
+ const targetCol = column?.property
710
+ ? this.checkColumns.find((c) => c.prop === column.property)
711
+ : this.checkColumns[columnIndex]
712
+ if (targetCol) {
713
+ targetCol.width = resizeWidth
714
+ }
698
715
  if (this.showColumn && this.pageId) {
699
716
  this.saveColumns()
700
717
  }
@@ -706,7 +723,12 @@ export default {
706
723
  // 保存列配置到后端(固定列、列宽调整等场景复用)
707
724
  saveColumns() {
708
725
  const userNo = sessionStorage.getItem('userNo')
709
- const columns = saveTransform(this.checkColumns, this.labelKey, this.isFilter)
726
+ // 排除模板已固定渲染的操作列,避免保存重复数据
727
+ const cols =
728
+ this.isOperateFixed && this.btnList && this.btnList.length > 0
729
+ ? this.checkColumns.filter((col) => !(col.static && col.label === $lc('操作')))
730
+ : this.checkColumns
731
+ const columns = saveTransform(cols, this.labelKey, this.isFilter)
710
732
  axios.post(
711
733
  `/bems/prod_1.0/user/pageHabit?t=${Math.random()}`,
712
734
  {
@@ -723,6 +745,7 @@ export default {
723
745
  this.isExport = false
724
746
  } else {
725
747
  this.checkColumns = list
748
+ this.colsKey++
726
749
  }
727
750
  },
728
751
  async getColumns() {
@@ -941,21 +964,6 @@ export default {
941
964
  // 得手动触发
942
965
  this.handleSelectionChange()
943
966
  },
944
- // 获取被合并的从属行索引集合
945
- getMergedRowIndexes() {
946
- const mergedIndexes = new Set()
947
- if (this.mergeCells && this.mergeCells.length > 0) {
948
- this.mergeCells.forEach((cell) => {
949
- if (cell.rowspan > 1) {
950
- // 从 row+1 到 row+rowspan-1 都是被合并的从属行
951
- for (let i = 1; i < cell.rowspan; i++) {
952
- mergedIndexes.add(cell.row + i)
953
- }
954
- }
955
- })
956
- }
957
- return mergedIndexes
958
- },
959
967
  toggleAll($table, disabled) {
960
968
  if (disabled) {
961
969
  return false
@@ -1316,18 +1324,9 @@ export default {
1316
1324
  const HOVER_BTNS_WIDTH = 356 // 悬浮按钮组宽度
1317
1325
  const MIN_LABEL_LENGTH = 10 // 最小标签长度
1318
1326
 
1319
- // 获取容器宽度
1320
- let wrapperEl = this.$refs.vxeTable?.$el
1321
- if (!wrapperEl) wrapperEl = document.querySelector('div#app')
1322
- if (!wrapperEl) return columns
1323
-
1324
- const { width: containerWidth } = wrapperEl.getBoundingClientRect()
1325
-
1326
1327
  // 解析宽度值(支持数字、"100"、"100px"格式)
1327
1328
  const parseWidth = (value) => (typeof value === 'number' ? value : parseInt(value, 10) || 0)
1328
1329
 
1329
- // 计算列宽并统计总宽度
1330
- let totalWidth = 0
1331
1330
  const calcColumns = columns.map((column) => {
1332
1331
  // 操作列固定宽度
1333
1332
  if (column.static && column.label === $lc('操作') && !column.width && !column.minWidth) {
@@ -1346,7 +1345,6 @@ export default {
1346
1345
  const widthValue = column.width || column.minWidth || column['min-width'] || 0
1347
1346
  const baseWidth = parseWidth(widthValue)
1348
1347
  column._baseWidth_ = baseWidth
1349
- totalWidth += baseWidth
1350
1348
  return column
1351
1349
  })
1352
1350