n20-common-lib 3.1.4 → 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.
@@ -0,0 +1,240 @@
1
+ # Pivot 数据透视组件使用说明
2
+
3
+ ## 组件概述
4
+
5
+ Pivot 是一个数据透视分析组件,以 Dialog 弹窗形式呈现。支持对表格数据进行多维度聚合分析,提供分组统计和交叉统计两种模式,可视化方式包括图表视图和表格视图。
6
+
7
+ ## 基础用法
8
+
9
+ ```vue
10
+ <template>
11
+ <div>
12
+ <el-button @click="showPivot = true">打开数据透视</el-button>
13
+ <Pivot
14
+ :visible.sync="showPivot"
15
+ :data="tableData"
16
+ :columns="columns"
17
+ :row-fields="rowFields"
18
+ :col-fields="colFields"
19
+ :value-fields="valueFields"
20
+ :chart-type="chartType"
21
+ @refresh="handleRefresh"
22
+ @export="handleExport"
23
+ @add-to-home="handleAddToHome"
24
+ />
25
+ </div>
26
+ </template>
27
+
28
+ <script>
29
+ export default {
30
+ data() {
31
+ return {
32
+ showPivot: false,
33
+ // 数据源
34
+ tableData: [
35
+ { region: '华东', product: '产品A', amount: 10000, count: 50 },
36
+ { region: '华东', product: '产品B', amount: 15000, count: 60 },
37
+ { region: '华南', product: '产品A', amount: 8000, count: 40 },
38
+ { region: '华南', product: '产品B', amount: 12000, count: 55 },
39
+ // ...更多数据
40
+ ],
41
+ // 列定义
42
+ columns: [
43
+ { key: 'region', label: '地区', type: 'string' },
44
+ { key: 'product', label: '产品', type: 'string' },
45
+ { key: 'amount', label: '金额', type: 'number' },
46
+ { key: 'count', label: '数量', type: 'number' }
47
+ ],
48
+ // 行维度(分组统计时使用)
49
+ rowFields: ['region'],
50
+ // 列维度(交叉统计时使用)
51
+ colFields: ['region', 'product'],
52
+ // 计算指标
53
+ valueFields: ['amount', 'count'],
54
+ // 图表类型
55
+ chartType: 'bar'
56
+ }
57
+ },
58
+ methods: {
59
+ handleRefresh() {
60
+ console.log('刷新数据')
61
+ },
62
+ handleExport(data) {
63
+ console.log('导出数据', data)
64
+ },
65
+ handleAddToHome(report) {
66
+ console.log('添加到首页', report)
67
+ }
68
+ }
69
+ }
70
+ </script>
71
+ ```
72
+
73
+ ## Props 参数
74
+
75
+ | 参数 | 说明 | 类型 | 默认值 | 必填 |
76
+ |------|------|------|--------|------|
77
+ | visible | 控制弹窗显示,支持 .sync 修饰符 | Boolean | false | 是 |
78
+ | data | 数据源,原始表格数据 | Array | [] | 是 |
79
+ | columns | 列定义配置 | Array | [] | 是 |
80
+ | rowFields | 行维度字段(分组统计使用) | Array | [] | 否 |
81
+ | colFields | 列维度字段(交叉统计使用) | Array | [] | 否 |
82
+ | valueFields | 计算指标字段 | Array | [] | 否 |
83
+ | chartType | 图表类型 | String | 'bar' | 否 |
84
+ | width | 组件宽度 | Number/String | 1360 | 否 |
85
+ | height | 组件高度 | Number/String | 820 | 否 |
86
+
87
+ ### columns 列定义格式
88
+
89
+ ```javascript
90
+ // 维度字段(字符串类型)
91
+ { key: 'region', label: '地区', type: 'string' }
92
+
93
+ // 指标字段(数值类型)
94
+ { key: 'amount', label: '金额', type: 'number' }
95
+
96
+ // 日期字段(作为维度)
97
+ { key: 'date', label: '日期', type: 'date' }
98
+ ```
99
+
100
+ > **自动推断**:如果未指定 rowFields/colFields/valueFields,组件会根据 columns 中的 type 自动推断:
101
+ > - `type: 'string' | 'date'` → 作为维度字段
102
+ > - `type: 'number'` → 作为指标字段
103
+
104
+ ### chartType 图表类型
105
+
106
+ | 值 | 说明 |
107
+ |----|------|
108
+ | 'bar' | 柱状图(默认) |
109
+ | 'line' | 折线图 |
110
+ | 'pie' | 饼图 |
111
+ | 'area' | 面积图 |
112
+
113
+ ## Events 事件
114
+
115
+ | 事件名 | 说明 | 回调参数 |
116
+ |--------|------|----------|
117
+ | update:visible | 弹窗显示状态变化 | (visible: Boolean) |
118
+ | refresh | 点击刷新按钮 | - |
119
+ | export | 导出数据 | (data: Array) |
120
+ | add-to-home | 添加到首页 | (report: Object) |
121
+
122
+ ## 功能说明
123
+
124
+ ### 1. 统计模式
125
+
126
+ #### 分组统计(默认)
127
+ - 按选定的维度字段进行分组
128
+ - 对每个分组内的指标进行聚合计算
129
+ - 支持图表视图和表格视图
130
+
131
+ #### 交叉统计
132
+ - 按行维度和列维度进行交叉分析
133
+ - 仅支持表格视图
134
+ - 切换到交叉统计时会自动切换为表格视图
135
+
136
+ ### 2. 聚合方式
137
+
138
+ 每个指标可独立设置聚合类型:
139
+
140
+ | 类型 | 说明 |
141
+ |------|------|
142
+ | sum | 求和(默认) |
143
+ | count | 计数 |
144
+ | avg | 平均值 |
145
+ | max | 最大值 |
146
+ | min | 最小值 |
147
+
148
+ ### 3. 多报表管理
149
+
150
+ - **新建报表**:点击左侧边栏顶部的 "+" 按钮
151
+ - **重命名**:悬停报表项 → 点击更多 → 选择重命名
152
+ - **删除**:悬停报表项 → 点击更多 → 选择删除
153
+ - **切换报表**:点击报表项即可切换
154
+
155
+ > 报表配置会自动保存到 localStorage,刷新页面后保留。
156
+
157
+ ### 4. 数据导出
158
+
159
+ 点击工具栏的"更多"按钮 → 选择"导出",可将当前透视结果导出为 CSV 文件。
160
+
161
+ ### 5. 分页功能
162
+
163
+ 表格视图支持分页,可切换每页显示条数(10/20/50/100),支持跳转指定页码。
164
+
165
+ ## 界面布局
166
+
167
+ ```
168
+ ┌────────────────────────────────────────────────────────────────┐
169
+ │ 数据透视分析中心 │
170
+ ├──────────────┬─────────────────────────────────┬──────────────┤
171
+ │ 我的报表清单 │ 主内容区域 │ 维度配置 │
172
+ │ (280px) │ │ (280px) │
173
+ │ │ ┌─────────────────────────────┐ │ │
174
+ │ + 新建报表 │ │ 报表标题 | 添加到首页 │ │ 报表标题 │
175
+ │ │ │ 计算耗时 | 数据条数 │ │ 统计类型 │
176
+ │ ● 报表1 │ ├─────────────────────────────┤ │ 展示形式 │
177
+ │ ○ 报表2 │ │ │ │ 分组维度 │
178
+ │ ○ 报表3 │ │ 图表视图 / 表格视图 │ │ 计算指标 │
179
+ │ │ │ │ │ │
180
+ │ │ └─────────────────────────────┘ │ │
181
+ └──────────────┴─────────────────────────────────┴──────────────┘
182
+ ```
183
+
184
+ ## 使用示例
185
+
186
+ ### 示例1:基础分组统计
187
+
188
+ ```vue
189
+ <Pivot
190
+ :visible.sync="showPivot"
191
+ :data="salesData"
192
+ :columns="[
193
+ { key: 'region', label: '地区', type: 'string' },
194
+ { key: 'amount', label: '销售额', type: 'number' }
195
+ ]"
196
+ />
197
+ ```
198
+
199
+ ### 示例2:交叉统计分析
200
+
201
+ ```vue
202
+ <Pivot
203
+ :visible.sync="showPivot"
204
+ :data="salesData"
205
+ :columns="[
206
+ { key: 'region', label: '地区', type: 'string' },
207
+ { key: 'product', label: '产品', type: 'string' },
208
+ { key: 'amount', label: '销售额', type: 'number' }
209
+ ]"
210
+ :col-fields="['region', 'product']"
211
+ />
212
+ ```
213
+
214
+ ### 示例3:自定义图表类型
215
+
216
+ ```vue
217
+ <Pivot
218
+ :visible.sync="showPivot"
219
+ :data="salesData"
220
+ :columns="columns"
221
+ chart-type="pie"
222
+ />
223
+ ```
224
+
225
+ ## 注意事项
226
+
227
+ 1. **数据量限制**:当前版本未实现虚拟滚动,大数据量(>10000条)时可能存在性能问题
228
+ 2. **交叉统计无图表**:交叉统计模式仅支持表格视图,不渲染图表
229
+ 3. **配置持久化**:报表配置保存在 localStorage,清除浏览器缓存会丢失
230
+ 4. **列定义必填**:columns 参数必须正确配置,否则无法正确识别维度和指标字段
231
+
232
+ ## 依赖组件
233
+
234
+ | 组件 | 说明 |
235
+ |------|------|
236
+ | Dialog | 弹窗容器 |
237
+ | ViewToggle | 视图切换控件 |
238
+ | Empty | 空状态展示 |
239
+ | echarts | 图表渲染(按需加载) |
240
+ | xe-utils | 数据聚合计算 |
@@ -0,0 +1,171 @@
1
+ # 说明文档
2
+
3
+ ## 项目概述
4
+
5
+ **项目名称**: Vue2 数据透视组件开发
6
+ **开发框架**: Vue 2.6.11
7
+
8
+ ## 项目规划
9
+
10
+ ### 开发目标
11
+ 基于当前工程设计开发一个功能完善的Vue2数据透视组件,实现以下核心功能:
12
+ 1. 将表格数据转换为可交互的透视表,支持多维度数据透视
13
+ 2. 支持多种图表类型(柱状图、折线图、饼图等)的切换展示
14
+ 3. 提供条件聚合功能(计算指标),允许用户自定义聚合方式(求和、平均值、计数等)
15
+ 5. 提供详细的API文档说明数据格式要求和配置参数
16
+
17
+ ## 实施方案
18
+
19
+ ### 技术栈选择
20
+ - **核心框架**: Vue 2.6.11
21
+ - **图表库**: ECharts 5.3.3(项目已有依赖)
22
+ - **表格组件**: VXE-Table 3.20.10(项目已有依赖)
23
+ - **UI组件**: Element UI 2.15.6(项目已有依赖)
24
+ - **工具库**: XE-Utils 4.0.2(项目已有依赖)
25
+
26
+ ### 核心功能模块设计
27
+
28
+ #### 1. 透视表模块
29
+ - **功能描述**: 将原始表格数据按维度进行透视转换
30
+ - **技术实现**:
31
+ - 多维度分组算法
32
+ - 动态列生成
33
+ - 实时数据计算
34
+ - CSV数据导出
35
+
36
+ #### 2. 图表展示模块
37
+ - **功能描述**: 集成ECharts实现多种图表类型展示
38
+ - **支持图表类型**:
39
+ - 柱状图(bar)
40
+ - 折线图(line)
41
+ - 饼图(pie)
42
+ - 面积图(area)
43
+ - **技术实现**:
44
+ - ECharts配置封装
45
+ - 动态类型切换
46
+ - 响应式适配
47
+ - 交互式数据提示
48
+
49
+ #### 3. 聚合配置模块
50
+ - **功能描述**: 提供可视化的聚合配置界面
51
+ - **支持的聚合方式**:
52
+ - 求和(sum)
53
+ - 平均值(avg)
54
+ - 计数(count)
55
+ - 最大值(max)
56
+ - 最小值(min)
57
+ - **筛选条件**:
58
+ - 支持多种操作符(==, !=, >, <, >=, <=, includes)
59
+ - 多条件组合筛选
60
+ - 实时更新结果
61
+
62
+
63
+ ## 进度记录
64
+
65
+
66
+ ## 使用指南
67
+
68
+
69
+ ### 组件Props说明
70
+
71
+ | 属性 | 类型 | 必填 | 默认值 | 说明 |
72
+ |-----|------|-----|--------|------|
73
+ | visible | Boolean | 否 | false | 控制弹窗显示 |
74
+ | data | Array | 是 | [] | 原始表格数据(对象数组) |
75
+ | dataSource | Array | 否 | null | 数据源(向后兼容,优先使用data) |
76
+ | columns | Array | 是 | [] | 原始表格列配置(对象数组) |
77
+ | rowFields | Array | 否 | [] | 行维度字段数组(分组统计模式) |
78
+ | colFields | Array | 否 | [] | 列维度字段数组(交叉统计模式) |
79
+ | valueFields | Array | 否 | [] | 计算指标数组(数值型) |
80
+ | chartType | String | 否 | 'bar' | 图表类型:bar/line/pie/horizontalBar |
81
+
82
+ ### 事件说明
83
+
84
+ | 事件名 | 参数 | 说明 |
85
+ |-------|------|------|
86
+ | update:visible | Boolean | 弹窗显示状态变化 |
87
+ | refresh | - | 刷新数据 |
88
+ | export | Array | 导出数据 |
89
+ | add-to-home | Object | 添加到首页 |
90
+ | close | - | 关闭弹窗 |
91
+
92
+ ## 核心功能特性
93
+
94
+ ### 📊 透视表功能
95
+
96
+
97
+ ### 📈 图表展示
98
+
99
+ ### 🔢 条件聚合
100
+
101
+
102
+ ### 🔍 筛选功能
103
+
104
+ ## 技术实现要点
105
+
106
+ ### 1. 数据透视算法
107
+ 分组统计,根据展现形式,分组维度,计算指标进行透视表数据转换:
108
+ 交叉统计,根据行维度,列维度,计算指标进行透视表数据转换:
109
+
110
+
111
+ ### 2. 图表集成
112
+ 封装ECharts配置,支持动态类型切换:
113
+ - 根据图表展示形式生成不同配置
114
+ - 提取透视表数据转换为图表数据
115
+
116
+ ### 3. 计算指标
117
+ 实现多种计算指标的计算逻辑:
118
+ - 根据计算指标进行数据计算
119
+ - 按统计类型进行数据转换
120
+ - 应用计算指标的计算结果
121
+ - 支持多字段同时计算
122
+
123
+ ### 4. 性能优化
124
+ - 使用防抖处理窗口调整事件
125
+ - 按需更新图表和表格
126
+ - 组件销毁时清理资源
127
+ - 避免不必要的重复计算
128
+
129
+ ## 项目文件清单
130
+
131
+
132
+
133
+ ## 注意事项
134
+
135
+ 1. **数据类型要求**
136
+ - 聚合字段必须是数值类型
137
+ - 数据格式必须是对象数组
138
+ - 确保字段名与配置一致
139
+
140
+ 2. **性能考虑**
141
+ - 建议单次处理数据不超过10,000条
142
+ - 避免过多的行/列维度
143
+
144
+ 3. **浏览器兼容**
145
+ - 支持现代浏览器最新版本
146
+ - 需要支持ES6语法
147
+ - 需要Canvas支持
148
+
149
+ 4. **依赖安装**
150
+ - 确保所有必需依赖已安装
151
+ - 检查依赖版本兼容性
152
+ - 使用npm或yarn安装
153
+
154
+ ## 后续优化方向
155
+
156
+ ### 性能优化
157
+ - [ ] 实现虚拟滚动支持大数据量
158
+ - [ ] 使用Web Worker后台计算
159
+ - [ ] 实现数据分页加载
160
+ - [ ] 优化大数据集渲染性能
161
+
162
+ ### 功能扩展
163
+ - [ ] 支持自定义计算指标
164
+ - [ ] 添加更多图表类型(雷达图、散点图等)
165
+ - [ ] 实现数据钻取功能
166
+ - [ ] 增强排序和筛选功能
167
+ - [ ] 支持配置模板保存
168
+ - [ ] 添加数据刷新机制
169
+
170
+ ### 用户体验
171
+ - [ ] 添加快捷操作面板
@@ -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 axios from '../../utils/axios.js'
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
- // 遍历 filterList,为每个 type 为 slot 且有 value 字段的项初始化值
303
- // 注意:跳过无 value 的 slot(如 startDate/endDate 多值绑定),这些由父组件在 slot 内部管理
304
- this.filterList.forEach((item) => {
305
- if (item.type === 'slot' && item.value) {
306
- obj[item.value] = undefined
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.forEach((item) => {
415
- if (!item.required) return
416
- if (item.type === 'daterange' || item.type === 'datetimerange' || item.type === 'monthrange') {
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
- } else if (item.type === 'numberrange') {
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
- } else if (item.value && this.searchValue[item.value] !== undefined) {
431
- preserved[item.value] = this.searchValue[item.value]
432
- }
433
- })
434
- this.searchValue = { ...this.getInitialSearchValue, ...preserved }
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',
@@ -396,8 +396,6 @@ import { saveTransform } from '../../ShowColumn/index.vue'
396
396
  import OperateBtns from '../../TableProOperateColumn/OperateBtns.vue'
397
397
  import axios from '../../../utils/axios'
398
398
 
399
- import { $lc } from '../../../utils/i18n/index.js'
400
-
401
399
  const renderer = {
402
400
  name: 'renderer',
403
401
  props: {
@@ -612,7 +610,7 @@ export default {
612
610
  // 当操作列已通过模板固定渲染时,过滤掉 checkColumns 中的操作列,避免重复
613
611
  const cols =
614
612
  this.isOperateFixed && this.btnList && this.btnList.length > 0
615
- ? this.checkColumns.filter((col) => !(col.static && col.label === $lc('操作')))
613
+ ? this.checkColumns.filter((col) => !(col.static && col.label === this.$lc('操作')))
616
614
  : this.checkColumns
617
615
  if (this.isAutoWidth) {
618
616
  return this.calcColumnWidth(cols)
@@ -726,7 +724,7 @@ export default {
726
724
  // 排除模板已固定渲染的操作列,避免保存重复数据
727
725
  const cols =
728
726
  this.isOperateFixed && this.btnList && this.btnList.length > 0
729
- ? this.checkColumns.filter((col) => !(col.static && col.label === $lc('操作')))
727
+ ? this.checkColumns.filter((col) => !(col.static && col.label === this.$lc('操作')))
730
728
  : this.checkColumns
731
729
  const columns = saveTransform(cols, this.labelKey, this.isFilter)
732
730
  axios.post(
@@ -1329,7 +1327,7 @@ export default {
1329
1327
 
1330
1328
  const calcColumns = columns.map((column) => {
1331
1329
  // 操作列固定宽度
1332
- if (column.static && column.label === $lc('操作') && !column.width && !column.minWidth) {
1330
+ if (column.static && column.label === this.$lc('操作') && !column.width && !column.minWidth) {
1333
1331
  column.width = OPERATE_WIDTH
1334
1332
  }
1335
1333
  // 复选框列固定宽度