vue2-client 1.8.125 → 1.8.126

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/CHANGELOG.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # Change Log
2
2
  > 所有关于本项目的变化都在该文档里。
3
3
 
4
+ **1.8.126 -2024-4-9 @江超**
5
+ - 现在相同配置名的key不会重复发起请求了
6
+
4
7
  **1.8.124 - 1.8.125 -2024-4-1 @陈博晨**
5
8
  - 修复路由bug, 修复axios响应拦截器, 还原一些物采平台需要的组件
6
9
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vue2-client",
3
- "version": "1.8.125",
3
+ "version": "1.8.126",
4
4
  "private": false,
5
5
  "scripts": {
6
6
  "serve": "SET NODE_OPTIONS=--openssl-legacy-provider && vue-cli-service serve --no-eslint",
@@ -31,9 +31,11 @@
31
31
  "enquire.js": "^2.1.6",
32
32
  "file-saver": "^2.0.5",
33
33
  "highlight.js": "^11.7.0",
34
+ "html2canvas": "^1.4.1",
34
35
  "js-base64": "^3.7.5",
35
36
  "js-cookie": "^2.2.1",
36
37
  "jsencrypt": "^3.3.2",
38
+ "jspdf": "^2.5.1",
37
39
  "lodash.clonedeep": "^4.5.0",
38
40
  "lodash.get": "^4.4.2",
39
41
  "mockjs": "^1.1.0",
@@ -1,128 +1,470 @@
1
- <template>
2
- <div class="reportMain">
3
- <h2 class="reportTitle" v-html="config.title"></h2>
4
- <table class="reportTable">
5
- <tbody>
6
- <template v-for="(row, rowIndex) in config.columns">
7
- <template v-if="row[0].type !== 'inputColumns'">
8
- <tr :key="rowIndex">
9
- <td v-for="(cell, cellIndex) in row" :key="cellIndex" :colspan="cell.colSpan ? cell.colSpan : undefined" :rowspan="cell.rowSpan ? cell.rowSpan : undefined">
10
- <template v-if="cell.type === 'column'">
11
- {{ cell.text }}
12
- </template>
13
- <template v-else-if="cell.type === 'value'">
14
- {{ cell.value }}
15
- </template>
16
- <template v-else-if="cell.type === 'input'">
17
- <a-input v-model="data[cell.dataIndex]" />
18
- </template>
19
- <template v-else-if="cell.type === 'inputs'">
20
- <a-input v-model="data[cell.dataIndex]" />
21
- </template>
22
- </td>
23
- </tr>
24
- </template>
25
- <template v-else>
26
- <tr v-for="(item, definitionIndex) in data[row[0].dataIndex]" :key="row[0].dataIndex + definitionIndex">
27
- <td v-for="(cell, cellIndex) in row[0].definition" :key="cellIndex" :colspan="cell.colSpan ? cell.colSpan : undefined" :rowspan="cell.rowSpan ? cell.rowSpan : undefined">
28
- <template v-if="cell.type === 'column'">
29
- {{ cell.text }}
30
- </template>
31
- <template v-else-if="cell.type === 'value'">
32
- {{ cell.value }}
33
- </template>
34
- <template v-else-if="cell.type === 'input'">
35
- <a-input v-model="data[row[0].dataIndex][definitionIndex][cell.dataIndex]" />
36
- </template>
37
- <template v-else-if="cell.type === 'inputs'">
38
- <a-input v-model="data[row[0].dataIndex][definitionIndex][cell.dataIndex]" />
39
- </template>
40
- </td>
41
- </tr>
42
- <tr :key="'dataAction' + rowIndex">
43
- <td :colspan="maxColSpan">
44
- <a-button-group>
45
- <a-button @click="addData(data[row[0].dataIndex])"><a-icon type="plus"/></a-button>
46
- <a-button @click="removeData(data[row[0].dataIndex])"><a-icon type="minus"/></a-button>
47
- </a-button-group>
48
- </td>
49
- </tr>
50
- </template>
51
- </template>
52
- </tbody>
53
- </table>
54
- </div>
55
- </template>
56
-
57
- <script>
58
- export default {
59
- name: 'XReport',
60
- props: {
61
- config: {
62
- type: Object,
63
- required: true
64
- }
65
- },
66
- data () {
67
- return {
68
- data: this.config.data,
69
- maxColSpan: 12
70
- }
71
- },
72
- methods: {
73
- removeData (dataItem) {
74
- if (dataItem.length === 0) {
75
- this.$message.warn('已经没有更多了')
76
- return
77
- }
78
- dataItem.pop()
79
- },
80
- addData (dataItem) {
81
- dataItem.push({
82
- no: '',
83
- sort: '',
84
- content: '',
85
- finishTime: ''
86
- })
87
- }
88
- },
89
- }
90
- </script>
91
-
92
- <style lang="less" scoped>
93
- .reportMain {
94
- width: 800px;
95
- text-align: center;
96
- margin: 0 auto;
97
- font-size: 16px;
98
- color: #000;
99
- background-color: #fff;
100
- padding: 15px;
101
- border-radius: 8px;
102
-
103
- .reportTitle {
104
- font-weight: bold;
105
- }
106
-
107
- .reportTable {
108
- width: 100%;
109
- border-collapse: collapse;
110
- table-layout:fixed;
111
- word-break:break-all;
112
- }
113
-
114
- td {
115
- border: 1px solid #000;
116
- padding: 8px;
117
- }
118
-
119
- input {
120
- width: 100%;
121
- box-sizing: border-box;
122
- }
123
-
124
- .innerTable {
125
- width: 784px;
126
- }
127
- }
128
- </style>
1
+ <template>
2
+ <div>
3
+ <!-- 用以包裹整个页面 -->
4
+ <a-card>
5
+ <!-- 切换菜单 -->
6
+ <a-radio-group v-model="type" default-value="a" button-style="solid" @change="tabChanged">
7
+ <a-radio-button value="design">
8
+ 设计
9
+ </a-radio-button>
10
+ <a-radio-button value="display">
11
+ 预览
12
+ </a-radio-button>
13
+ </a-radio-group>
14
+ <!-- 主体表格 -->
15
+ <XReportDesign
16
+ v-if="scanFinish"
17
+ :config="type === 'display' ? originalConfig : activeConfig"
18
+ :slot-config-name="type === 'display' ? undefined : activatedSlotName"
19
+ :for-display="type === 'display'"
20
+ id="printReady">
21
+ </XReportDesign>
22
+ <!-- 导出按钮 -->
23
+ <template v-if="type === 'display'">
24
+ <div class="tools">
25
+ <div class="toolsItem" @click="exportPDF">
26
+ <a-button type="primary">
27
+ 导出PDF
28
+ </a-button>
29
+ </div>
30
+ </div>
31
+ </template>
32
+ </a-card>
33
+ </div>
34
+ </template>
35
+
36
+ <script>
37
+ // 转PDF用
38
+ import HtmlToPdf from '@vue2-client/utils/htmlToPDF'
39
+ import { getConfigByName } from '@vue2-client/services/api/common'
40
+ import XReportDesign from '@vue2-client/base-client/components/common/XReport/XReportDesign.vue'
41
+
42
+ export default {
43
+ name: 'XReport',
44
+ props: {
45
+ configName: {
46
+ type: String,
47
+ required: true
48
+ },
49
+ activatedSlotName: {
50
+ type: String,
51
+ default: undefined
52
+ }
53
+ },
54
+ components: {
55
+ XReportDesign
56
+ },
57
+ data () {
58
+ return {
59
+ config: undefined,
60
+ type: 'design',
61
+ maxColSpan: 12,
62
+ // 定义是否完成配置的扫描,未完成不要渲染子组件
63
+ scanFinish: false,
64
+ // 当前激活的配置文件
65
+ activeConfig: null,
66
+ // 原始配置文件
67
+ // 用于展示。某些情况下“设计页”中的内容仅为“预览页”表格其中的一部分
68
+ originalConfig: null,
69
+ // 扫描到的配置
70
+ configFromWeb: {},
71
+ timer: undefined
72
+ }
73
+ },
74
+ methods: {
75
+ // 检查slot是否在配置文件中包含,如果没有包含,则视为非法获取
76
+ checkSlotDefine (config) {
77
+ const slotsDeclare = config.slotsDeclare
78
+ const total = slotsDeclare.length
79
+ let count = 0
80
+ slotsDeclare.forEach(declare => {
81
+ config.columns.forEach(row => {
82
+ row.forEach(cell => {
83
+ if (cell.slotConfig === declare) {
84
+ count++
85
+ }
86
+ })
87
+ })
88
+ })
89
+
90
+ return count === total
91
+ },
92
+ // 切换了标签页
93
+ tabChanged (key) {
94
+ this.scanFinish = false
95
+ this.originalConfig.data = { ...this.originalConfig.data, ...this.config.data }
96
+ this.config.data = this.originalConfig.data
97
+ this.$nextTick(() => {
98
+ this.scanFinish = true
99
+ })
100
+ },
101
+ // 获取当前日期,为保存文件命名用
102
+ getDate () {
103
+ const currentDate = new Date()
104
+
105
+ const year = currentDate.getFullYear()
106
+ const month = String(currentDate.getMonth() + 1).padStart(2, '0')
107
+ const day = String(currentDate.getDate()).padStart(2, '0')
108
+
109
+ const formattedDate = `${year}_${month}_${day}`
110
+
111
+ return formattedDate
112
+ },
113
+ // 导出PDF
114
+ exportPDF () {
115
+ const date = this.getDate()
116
+ let title = this.config.title
117
+ title = title.replace(/<[^>]+>/g, '')
118
+ const fileName = date + '' + title
119
+ HtmlToPdf.getPdf(fileName, '#printReady')
120
+ },
121
+ // 用于分割配置中的colums,将需要处理的数组提取出来
122
+ formatConfigRow () {
123
+ for (let i = 0; i < this.config.columns.length; i++) {
124
+ // 对原始数组进行递归,依次将该位置拆分为三个部分,当前处理位置之前的,当前处理位置,当前处理位置之后的
125
+ const before = this.config.columns.slice(0, i)
126
+ const after = this.config.columns.slice(i + 1, this.config.columns.length)
127
+
128
+ // 将当前处理的数组交给处理的方法
129
+ const x = this.checkRow(this.config.columns[i])
130
+
131
+ const newArr = []
132
+
133
+ // 拼接之前的数组
134
+ if (before.length > 0) {
135
+ if (before.length >= 1) {
136
+ before.forEach(item => {
137
+ newArr.push(item)
138
+ })
139
+ } else {
140
+ newArr.push(before)
141
+ }
142
+ }
143
+
144
+ // 拼接不需要更改当前节点处理完成的数组
145
+ newArr.push(x.old)
146
+
147
+ // 如果处理了新加的数据,拼接
148
+ if (x.add.length > 0) {
149
+ for (let j = 0; j < x.add.length; j++) {
150
+ if (x.add[j]) {
151
+ newArr.push(x.add[j])
152
+ i++
153
+ }
154
+ }
155
+ }
156
+
157
+ // 拼接之后的数组
158
+ if (after.length > 0) {
159
+ if (after.length >= 1) {
160
+ after.forEach(item => {
161
+ newArr.push(item)
162
+ })
163
+ } else {
164
+ newArr.push(after)
165
+ }
166
+ }
167
+
168
+ this.config.columns = newArr
169
+ }
170
+ },
171
+ // 处理colums数组,为声明了rowspan的单元格,自动匹配格式
172
+ checkRow (rowArr) {
173
+ // 不需要更改的数据
174
+ const original = []
175
+ // 需要更改新加的数据
176
+ const addArr = []
177
+ // 统计rowspan出现的总和
178
+ let count = 0
179
+ // 统计声明列需要的rowspan总数
180
+ let total = 0
181
+ // 是否为声明行
182
+ let titleCellFlag = false
183
+ // 是否为声明行后第一行
184
+ let firstSubLine = false
185
+ // 需要处理的行,新的index值
186
+ let subRowIndex = 0
187
+ // 新生成的行,临时存储
188
+ const waitForAddArr = []
189
+ // 用于记录声明行的colspan避免格式错误
190
+ let preColSpan = 0
191
+ // 用于统计循环次数,判断是否是最后一次
192
+ let forEachCount = 0
193
+
194
+ // 标记所有数据
195
+ rowArr.forEach(cell => {
196
+ forEachCount++
197
+ // 如果该行没有rowspan则默认其为1,不要影响统计结果
198
+ if (!cell.rowSpan) {
199
+ cell.rowSpan = 0
200
+ }
201
+
202
+ if (cell.text && total !== 0) { // 如果遇到了下一个声明行,证明rowspan少了一行,需要补充一个占位格
203
+ const nullObj = { type: 'placeHolderColumn', order: subRowIndex, noBoarder: true, needSplit: true, colSpan: preColSpan, dontShowRow: true }
204
+ subRowIndex++
205
+ waitForAddArr.push(nullObj)
206
+ total = 0
207
+ count = 0
208
+ titleCellFlag = false
209
+ firstSubLine = false
210
+ } else if ((total !== count + cell.rowSpan) && forEachCount === rowArr.length) {
211
+ // 如果没有遇到了下一个声明行,但已经是当前行最后一个数据,也证明rowspan少了一行,需要补充一个占位格
212
+ const nullObj = { type: 'placeHolderColumn', order: subRowIndex, noBoarder: true, needSplit: true, colSpan: preColSpan, dontShowRow: true }
213
+ subRowIndex++
214
+ waitForAddArr.push(nullObj)
215
+ total = 0
216
+ count = 0
217
+ titleCellFlag = false
218
+ firstSubLine = false
219
+ }
220
+
221
+ // 判断是否为声明行
222
+ if (cell.text && total === 0) {
223
+ // 将声明行声明的rowspan作为总数,判断下方rowspan相加是否等于声明行声明的数量
224
+ total = cell.rowSpan
225
+ titleCellFlag = false
226
+ firstSubLine = true
227
+ subRowIndex = 1
228
+ cell.show = true
229
+ cell.showRowSpan = total
230
+ } else if (cell.rowSpan > 0 && !titleCellFlag && firstSubLine) { // 判断是否为声明行后首行,因为首行不需要移动
231
+ count += cell.rowSpan
232
+ firstSubLine = false
233
+ cell.noBoarder = true
234
+ cell.show = true
235
+ cell.showRowSpan = total
236
+ } else if (cell.rowSpan > 0 && !titleCellFlag && !firstSubLine) { // 既非声明行,也非首行,需要移动
237
+ count += cell.rowSpan
238
+ // cell.type = 'notShow'
239
+ cell.needSplit = true
240
+ cell.order = subRowIndex
241
+ cell.dontShowRow = true
242
+ subRowIndex++
243
+
244
+ // 如果之前添加过空行补充位置,刚好最后一位还有内容,将其互换
245
+ if (forEachCount === rowArr.length && !waitForAddArr[waitForAddArr.length - 1].dataIndex) {
246
+ waitForAddArr[waitForAddArr.length - 1].order += 1
247
+ cell.order -= 1
248
+ waitForAddArr.push(cell)
249
+ } else {
250
+ waitForAddArr.push(cell)
251
+ }
252
+ }
253
+
254
+ // 如果count和total相等了,证明已经处理完成。将计数器还原
255
+ if (count === total) {
256
+ total = 0
257
+ count = 0
258
+ titleCellFlag = false
259
+ firstSubLine = false
260
+ }
261
+ // 保存上一个的colspan,保持生成的格子与原格式一致
262
+ preColSpan = cell.colSpan
263
+ })
264
+
265
+ // 将所有不需要移动的放入original
266
+ rowArr.forEach(cell => {
267
+ if (cell.needSplit !== true) {
268
+ original.push(cell)
269
+ }
270
+ })
271
+
272
+ // 增加新的数组
273
+ waitForAddArr.forEach(cell => {
274
+ const target = cell.order
275
+ // if (cell.type === 'notShow') {
276
+ // cell.type = 'inputs'
277
+ // }
278
+ cell.noBoarder = true
279
+ if (addArr[target] === undefined) {
280
+ const temp = []
281
+ temp.push(cell)
282
+ addArr[target] = temp
283
+ } else if (addArr[target].length > 0) {
284
+ addArr[target].push(cell)
285
+ }
286
+ })
287
+
288
+ // 如果没有新增,将单元格边框设置为显示
289
+ if (addArr.length < 1) {
290
+ original.forEach(cell => {
291
+ if (cell.type === 'input' || cell.type === 'inputs') {
292
+ cell.noBoarder = false
293
+ }
294
+ })
295
+ }
296
+
297
+ return {
298
+ old: original,
299
+ add: addArr
300
+ }
301
+ },
302
+ // 扫描配置,如果有插槽则拼接插槽
303
+ scanConfigSlot (config) {
304
+ const columnsArr = config.columns
305
+ for (let i = 0; i < columnsArr.length; i++) {
306
+ for (let j = 0; j < columnsArr[i].length; j++) {
307
+ // 如果发现type为slot,开始匹配对应的slot配置文件
308
+ if (columnsArr[i][j].type === 'slot') {
309
+ const targetName = columnsArr[i][j].slotConfig
310
+ // 找不到目标插槽配置
311
+ if (!this.configFromWeb[targetName] || !this.configFromWeb[targetName].columns) {
312
+ console.error('无法找到目标插槽的配置!')
313
+ return
314
+ }
315
+
316
+ // 替换columns,合并data
317
+ config.columns[i] = []
318
+ const before = config.columns.slice(0, i)
319
+ const after = config.columns.slice(i + 1, config.columns.length)
320
+
321
+ const addArr = []
322
+ for (let k = 0; k < this.configFromWeb[targetName].columns.length; k++) {
323
+ const temp = []
324
+ this.configFromWeb[targetName].columns[k].forEach(cell => {
325
+ temp.push(cell)
326
+ })
327
+ addArr.push(temp)
328
+ }
329
+
330
+ const newArr = []
331
+ // 拼接之前的数组
332
+ if (before.length > 0) {
333
+ if (before.length >= 1) {
334
+ before.forEach(item => {
335
+ newArr.push(item)
336
+ })
337
+ } else {
338
+ newArr.push(before)
339
+ }
340
+ }
341
+
342
+ addArr.forEach(arr => {
343
+ newArr.push(arr)
344
+ })
345
+
346
+ // 拼接之后的数组
347
+ if (after.length > 0) {
348
+ if (after.length >= 1) {
349
+ after.forEach(item => {
350
+ newArr.push(item)
351
+ })
352
+ } else {
353
+ newArr.push(after)
354
+ }
355
+ }
356
+
357
+ config.columns = newArr
358
+ if (this.configFromWeb[targetName].slotsDeclare) {
359
+ config.slotsDeclare = this.configFromWeb[targetName].slotsDeclare
360
+ } else {
361
+ config.slotsDeclare = []
362
+ }
363
+
364
+ config.data = { ...config.data, ...this.configFromWeb[targetName].data }
365
+ this.configFromWeb = {}
366
+ }
367
+ }
368
+ }
369
+ this.config = config
370
+ },
371
+ // 扫描所有插槽名
372
+ scanConfigName (config, resut) {
373
+ if (config.slotsDeclare) {
374
+ config.slotsDeclare.forEach(name => {
375
+ resut.push(name)
376
+ })
377
+ }
378
+ },
379
+ // 获取插槽
380
+ getConfigAndJoin (config, outerLock) {
381
+ // 检查主配置插槽声明是否合法
382
+ const check = this.checkSlotDefine(config)
383
+ const waitForDownloadSlotName = []
384
+ if (check) {
385
+ // 扫描主配置中声明的插槽名
386
+ this.scanConfigName(config, waitForDownloadSlotName)
387
+
388
+ const total = waitForDownloadSlotName.length
389
+ let count = 0
390
+
391
+ // 挨个获取插槽
392
+ waitForDownloadSlotName.forEach(configName => {
393
+ getConfigByName(configName, undefined, res => {
394
+ this.configFromWeb[configName] = res
395
+ count++
396
+ })
397
+ })
398
+
399
+ const timer = setInterval(() => {
400
+ console.log('插槽下载进度,当前:' + count + '/' + total)
401
+ if (count >= total) {
402
+ clearInterval(timer)
403
+ this.scanConfigSlot(config)
404
+ if (config.slotsDeclare.length > 0) {
405
+ const lock = { status: true }
406
+ this.getConfigAndJoin(config, lock)
407
+ const innerTimer = setInterval(() => {
408
+ if (!lock.status) {
409
+ clearInterval(innerTimer)
410
+ outerLock.status = false
411
+ }
412
+ }, 100)
413
+ } else {
414
+ outerLock.status = false
415
+ }
416
+ }
417
+ }, 100)
418
+ } else {
419
+ console.error('插槽配置有误!')
420
+ outerLock.status = false
421
+ }
422
+ }
423
+ },
424
+ beforeMount () {
425
+ // 获取主配置
426
+ getConfigByName(this.configName, undefined, res => {
427
+ this.config = res
428
+
429
+ const lock = { status: true }
430
+
431
+ this.getConfigAndJoin(this.config, lock)
432
+
433
+ this.timer = setInterval(() => {
434
+ if (!lock.status) {
435
+ clearInterval(this.timer)
436
+ console.log('拼接完成', this.config)
437
+ this.originalConfig = Object.assign({}, this.config)
438
+ // 扫描配置文件中有没有rowSpan,进行格式化调整
439
+ this.formatConfigRow(this.config)
440
+ this.activeConfig = this.config
441
+ this.$nextTick(() => {
442
+ this.scanFinish = true
443
+ })
444
+ }
445
+ }, 100)
446
+
447
+ // setTimeout(() => {
448
+ // console.log('拼接完成', this.config)
449
+ // this.originalConfig = Object.assign({}, this.config)
450
+ // // 扫描配置文件中有没有rowSpan,进行格式化调整
451
+ // this.formatConfigRow(this.config)
452
+ // this.activeConfig = this.config
453
+ // this.$nextTick(() => {
454
+ // this.scanFinish = true
455
+ // })
456
+ // }, 1000)
457
+ })
458
+ }
459
+ }
460
+ </script>
461
+
462
+ <style lang="less" scoped>
463
+ .tools{
464
+ text-align: center;
465
+ cursor: pointer;
466
+ .toolsItem{
467
+ display: inline-block;
468
+ }
469
+ }
470
+ </style>