af-mobile-client-vue3 1.0.54

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.
Files changed (156) hide show
  1. package/.editorconfig +38 -0
  2. package/.env +7 -0
  3. package/.env.development +4 -0
  4. package/.env.envoiceShow +7 -0
  5. package/.env.production +7 -0
  6. package/.husky/commit-msg +1 -0
  7. package/.husky/pre-commit +1 -0
  8. package/.vscode/extensions.json +7 -0
  9. package/.vscode/settings.json +61 -0
  10. package/LICENSE +21 -0
  11. package/README.md +181 -0
  12. package/af-example-mobile-vue-web.iml +9 -0
  13. package/build/vite/index.ts +91 -0
  14. package/build/vite/vconsole.ts +44 -0
  15. package/eslint.config.js +7 -0
  16. package/index.html +17 -0
  17. package/mock/data.ts +20 -0
  18. package/mock/index.ts +5 -0
  19. package/mock/modules/prose.mock.ts +16 -0
  20. package/mock/modules/user.mock.ts +152 -0
  21. package/netlify.toml +12 -0
  22. package/package.json +107 -0
  23. package/public/favicon-dark.svg +4 -0
  24. package/public/favicon.ico +0 -0
  25. package/public/favicon.svg +4 -0
  26. package/public/pwa-192x192.png +0 -0
  27. package/public/pwa-512x512.png +0 -0
  28. package/public/safari-pinned-tab.svg +32 -0
  29. package/scripts/verifyCommit.js +19 -0
  30. package/src/App.vue +43 -0
  31. package/src/api/mock/index.ts +30 -0
  32. package/src/api/user/index.ts +40 -0
  33. package/src/assets/common/default-user-profile.png +0 -0
  34. package/src/assets/img/apps/apply-web.png +0 -0
  35. package/src/assets/img/apps/example-web.png +0 -0
  36. package/src/assets/img/apps/iot-web.png +0 -0
  37. package/src/assets/img/apps/linepatrol-web.png +0 -0
  38. package/src/assets/img/apps/monitor-web.png +0 -0
  39. package/src/assets/img/apps/oa-web.png +0 -0
  40. package/src/assets/img/apps/revenue-web.png +0 -0
  41. package/src/assets/img/apps/safe-check-web.png +0 -0
  42. package/src/assets/img/component/logo.png +0 -0
  43. package/src/assets/img/home/banner1.png +0 -0
  44. package/src/assets/img/home/banner2.png +0 -0
  45. package/src/assets/img/home/banner3.png +0 -0
  46. package/src/assets/img/home/banner4.png +0 -0
  47. package/src/assets/img/home/notice/icon.png +0 -0
  48. package/src/assets/img/user/login/background-shadow-1.svg +20 -0
  49. package/src/assets/img/user/login/logo-background.svg +20 -0
  50. package/src/assets/img/user/login/logo.png +0 -0
  51. package/src/assets/img/user/my/exit-login.png +0 -0
  52. package/src/assets/img/user/my/setting-arrow.png +0 -0
  53. package/src/assets/img/user/my/setting.png +0 -0
  54. package/src/bootstrap.ts +32 -0
  55. package/src/components/core/App/MicroAppView.vue +59 -0
  56. package/src/components/core/BeautifulLoading/index.vue +47 -0
  57. package/src/components/core/NavBar/index.vue +12 -0
  58. package/src/components/core/SvgIcon/index.vue +61 -0
  59. package/src/components/core/Tabbar/index.vue +38 -0
  60. package/src/components/core/Uploader/index.vue +104 -0
  61. package/src/components/core/XMultiSelect/index.vue +196 -0
  62. package/src/components/core/XSelect/index.vue +130 -0
  63. package/src/components/data/XBadge/index.vue +85 -0
  64. package/src/components/data/XCellDetail/index.vue +106 -0
  65. package/src/components/data/XCellList/index.vue +358 -0
  66. package/src/components/data/XCellListFilter/index.vue +392 -0
  67. package/src/components/data/XForm/index.vue +127 -0
  68. package/src/components/data/XFormItem/index.vue +472 -0
  69. package/src/components/data/XReportForm/XReportFormJsonRender.vue +220 -0
  70. package/src/components/data/XReportForm/index.vue +1058 -0
  71. package/src/components/layout/NormalDataLayout/index.vue +70 -0
  72. package/src/components/layout/TabBarLayout/index.vue +40 -0
  73. package/src/components.d.ts +53 -0
  74. package/src/enums/requestEnum.ts +25 -0
  75. package/src/env.d.ts +16 -0
  76. package/src/font-style/PingFangSC-Regular.woff2 +0 -0
  77. package/src/font-style/font.css +4 -0
  78. package/src/hooks/useCommon.ts +9 -0
  79. package/src/hooks/useLogin.ts +97 -0
  80. package/src/icons/svg/bird.svg +1 -0
  81. package/src/icons/svg/check-in.svg +33 -0
  82. package/src/icons/svg/dark.svg +5 -0
  83. package/src/icons/svg/github.svg +5 -0
  84. package/src/icons/svg/light.svg +5 -0
  85. package/src/icons/svg/link.svg +5 -0
  86. package/src/icons/svg/loadError.svg +1 -0
  87. package/src/icons/svg/notFound.svg +1 -0
  88. package/src/icons/svgo.yml +22 -0
  89. package/src/layout/PageLayout.vue +51 -0
  90. package/src/layout/SingleLayout.vue +35 -0
  91. package/src/locales/en-US.json +25 -0
  92. package/src/locales/zh-CN.json +25 -0
  93. package/src/main.ts +48 -0
  94. package/src/plugins/AppData.ts +38 -0
  95. package/src/plugins/GetLoginInfoService.ts +10 -0
  96. package/src/plugins/index.ts +11 -0
  97. package/src/router/README.md +8 -0
  98. package/src/router/guards.ts +60 -0
  99. package/src/router/index.ts +60 -0
  100. package/src/router/invoiceRoutes.ts +33 -0
  101. package/src/router/routes.ts +84 -0
  102. package/src/services/api/Login.ts +6 -0
  103. package/src/services/api/common.ts +98 -0
  104. package/src/services/api/index.ts +7 -0
  105. package/src/services/api/manage.ts +8 -0
  106. package/src/services/restTools.ts +37 -0
  107. package/src/settings.ts +1 -0
  108. package/src/stores/index.ts +7 -0
  109. package/src/stores/modules/cachedView.ts +31 -0
  110. package/src/stores/modules/counter.ts +19 -0
  111. package/src/stores/modules/routeTransitionName.ts +26 -0
  112. package/src/stores/modules/setting.ts +28 -0
  113. package/src/stores/modules/user.ts +180 -0
  114. package/src/stores/mutation-type.ts +7 -0
  115. package/src/styles/app.less +67 -0
  116. package/src/styles/login.less +81 -0
  117. package/src/typing.ts +3 -0
  118. package/src/utils/Storage.ts +124 -0
  119. package/src/utils/authority-utils.ts +87 -0
  120. package/src/utils/common.ts +41 -0
  121. package/src/utils/crypto.ts +39 -0
  122. package/src/utils/dataUtil.ts +42 -0
  123. package/src/utils/dictUtil.ts +51 -0
  124. package/src/utils/http/index.ts +158 -0
  125. package/src/utils/i18n.ts +41 -0
  126. package/src/utils/indexedDB.ts +180 -0
  127. package/src/utils/local-storage.ts +9 -0
  128. package/src/utils/mobileUtil.ts +26 -0
  129. package/src/utils/progress.ts +19 -0
  130. package/src/utils/routerUtil.ts +271 -0
  131. package/src/utils/set-page-title.ts +7 -0
  132. package/src/utils/validate.ts +6 -0
  133. package/src/views/chat/index.vue +153 -0
  134. package/src/views/common/LoadError.vue +64 -0
  135. package/src/views/common/NotFound.vue +68 -0
  136. package/src/views/component/EvaluateRecordView/index.vue +40 -0
  137. package/src/views/component/XCellDetailView/index.vue +216 -0
  138. package/src/views/component/XCellListView/index.vue +36 -0
  139. package/src/views/component/XFormView/index.vue +478 -0
  140. package/src/views/component/XReportFormIframeView/index.vue +45 -0
  141. package/src/views/component/XReportFormView/index.vue +295 -0
  142. package/src/views/component/index.vue +111 -0
  143. package/src/views/component/menu.vue +117 -0
  144. package/src/views/component/notice.vue +46 -0
  145. package/src/views/component/topNav.vue +36 -0
  146. package/src/views/invoiceShow/index.vue +62 -0
  147. package/src/views/user/login/ForgetPasswordForm.vue +93 -0
  148. package/src/views/user/login/LoginForm.vue +145 -0
  149. package/src/views/user/login/LoginTitle.vue +68 -0
  150. package/src/views/user/login/LoginWave.vue +109 -0
  151. package/src/views/user/login/index.vue +22 -0
  152. package/src/views/user/my/index.vue +230 -0
  153. package/src/vue-router.d.ts +9 -0
  154. package/tsconfig.json +43 -0
  155. package/uno.config.ts +32 -0
  156. package/vite.config.ts +110 -0
@@ -0,0 +1,1058 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ Button as vanButton,
4
+ Cell as vanCell,
5
+ CellGroup as vanCellGroup,
6
+ Collapse as vanCollapse,
7
+ CollapseItem as vanCollapseItem,
8
+ Field as vanField,
9
+ Form as vanForm,
10
+ NavBar as vanNavBar,
11
+ Skeleton as vanSkeleton,
12
+ } from 'vant'
13
+ import { defineEmits, nextTick, reactive, ref, watch } from 'vue'
14
+ import { getConfigByNameWithoutIndexedDB } from '@af-mobile-client-vue3/services/api/common'
15
+ import Uploader from '@af-mobile-client-vue3/components/core/Uploader/index.vue'
16
+ import XReportFormJsonRender from '@af-mobile-client-vue3/components/data/XReportForm/XReportFormJsonRender.vue'
17
+
18
+ // ------------------------- 类型定义 -------------------------
19
+ interface configDefine {
20
+ data?: any
21
+ designMode?: string
22
+ title?: string | undefined
23
+ subTitle?: Array<cell> | undefined
24
+ columns?: Array<any>
25
+ slotsDeclare?: Array<string> | undefined
26
+ tempData?: any
27
+ }
28
+
29
+ interface getConfigLock {
30
+ status: boolean
31
+ }
32
+
33
+ interface cell {
34
+ type: string
35
+ dataIndex: string
36
+ format: string | undefined
37
+ }
38
+
39
+ // ------------------------- 数据 -------------------------
40
+ const props = defineProps({
41
+ // 配置名
42
+ configName: String,
43
+ // 是否将动态行的label显示在标题上
44
+ showInputColumnsLabelOnTitle: { default: false },
45
+ slotName: String,
46
+ showNav: { default: true },
47
+ localConfig: { default: undefined },
48
+ configData: { default: undefined },
49
+ })
50
+ const emit = defineEmits(['saveConfig'])
51
+ // 原始配置
52
+ const activatedConfig: configDefine = reactive({})
53
+ // 激活的配置名
54
+ const activatedConfigName = ref('')
55
+ // 下载的配置
56
+ const configFromWeb = reactive({})
57
+ // 定时器
58
+ let timer: NodeJS.Timeout
59
+ // 控制组件渲染
60
+ const scanFinish = ref(false)
61
+ // 折叠面板当前激活的值
62
+ const activeCollapseName = ref('副标题')
63
+
64
+ watch(() => props.configName, () => {
65
+ // 这里是你的处理逻辑
66
+ initComponent()
67
+ })
68
+
69
+ // ------------------------- 方法 -------------------------
70
+ // 为inputs类型的数据,整理格式,供form展示用
71
+ function formatInputs(cell: cell): string[] {
72
+ const format = cell.format
73
+ const arr = format.split('{}')
74
+ const result = []
75
+ const reg = /[\u4E00-\u9FA5a-zA-Z]/g
76
+ arr.forEach((str) => {
77
+ if (str.length >= 1 && str.match(reg)) {
78
+ const strs = str.match(reg)
79
+ result.push(strs.join(''))
80
+ }
81
+ })
82
+ if (!activatedConfig.data[cell.dataIndex][result.length - 1]) {
83
+ let dataKeySet = Object.keys(activatedConfig.data)
84
+ dataKeySet = dataKeySet.filter((key) => {
85
+ return key.startsWith(cell.dataIndex)
86
+ })
87
+ dataKeySet.sort()
88
+ const target = dataKeySet[0]
89
+ dataKeySet.shift()
90
+ dataKeySet.forEach((key) => {
91
+ activatedConfig.data[key].forEach((item) => {
92
+ activatedConfig.data[target].push(item)
93
+ })
94
+ })
95
+
96
+ for (let i = 0; i < result.length; i++) {
97
+ if (!activatedConfig.data[cell.dataIndex][i])
98
+ activatedConfig.data[cell.dataIndex][i] = undefined
99
+ }
100
+ }
101
+ return result
102
+ }
103
+
104
+ // 左上角返回按钮
105
+ function onClickLeft(): void {
106
+ history.back()
107
+ }
108
+
109
+ // 提交按钮
110
+ function onSubmit(): void {
111
+ const keys = Object.keys(activatedConfig.tempData)
112
+ keys.forEach((key) => {
113
+ changeDeepObject(activatedConfig.data, key, activatedConfig.tempData[key])
114
+ })
115
+ console.warn('保存', activatedConfig)
116
+ emit('saveConfig', activatedConfig)
117
+ }
118
+
119
+ // // 动态行新增
120
+ // function addInputColumns(dataIndex: string, define: Array<any>): void {
121
+ // const temp = {}
122
+ // define.forEach((def) => {
123
+ // let begin = def.initialValue
124
+ // if (def.type === 'increment') {
125
+ // if (activatedConfig.data[dataIndex].length > 0) {
126
+ // begin = activatedConfig.data[dataIndex][activatedConfig.data[dataIndex].length - 1][def.dataIndex]
127
+ // begin = Number.parseInt(begin)
128
+ // }
129
+ // temp[def.dataIndex] = begin + def.step
130
+ // }
131
+ // else {
132
+ // temp[def.dataIndex] = ''
133
+ // }
134
+ // })
135
+ // activatedConfig.data[dataIndex].push(temp)
136
+ // }
137
+ //
138
+ // // 动态行删除
139
+ // function removeInputColumns(dataIndex: string) {
140
+ // activatedConfig.data[dataIndex].pop()
141
+ // }
142
+
143
+ // 扫描所有插槽名
144
+ function scanConfigName(config: configDefine, resut: Array<string>): void {
145
+ if (config.slotsDeclare) {
146
+ config.slotsDeclare.forEach((name) => {
147
+ resut.push(name)
148
+ })
149
+ }
150
+ }
151
+
152
+ // 检查slot是否在配置文件中包含,如果没有包含,则视为非法获取
153
+ function checkSlotDefine(config: configDefine): boolean {
154
+ const slotsDeclare = config.slotsDeclare
155
+ const total = slotsDeclare.length
156
+ let count = 0
157
+ slotsDeclare.forEach((declare) => {
158
+ config.columns.forEach((row: Array<any>) => {
159
+ row.forEach((cell) => {
160
+ if (cell.slotConfig === declare)
161
+ count++
162
+ })
163
+ })
164
+ })
165
+
166
+ return count === total
167
+ }
168
+
169
+ // 扫描配置,如果有插槽则拼接插槽
170
+ function scanConfigSlot(config: configDefine): void {
171
+ const columnsArr: Array<any> = config.columns
172
+ for (let i = 0; i < columnsArr.length; i++) {
173
+ for (let j = 0; j < columnsArr[i].length; j++) {
174
+ // 如果发现type为slot,开始匹配对应的slot配置文件
175
+ if (columnsArr[i][j].type === 'slot') {
176
+ const targetName = columnsArr[i][j].slotConfig
177
+ // 找不到目标插槽配置
178
+ if (!configFromWeb[targetName] || !configFromWeb[targetName].columns)
179
+ return
180
+
181
+ // 替换columns,合并data
182
+ config.columns[i] = []
183
+ const before = config.columns.slice(0, i)
184
+ let after = config.columns.slice(i + 1, config.columns.length)
185
+
186
+ const addArr = []
187
+
188
+ if (configFromWeb[targetName].cardMode === true) {
189
+ const temp = [{
190
+ type: 'card',
191
+ content: [],
192
+ label: configFromWeb[targetName].title,
193
+ }]
194
+ for (let k = 0; k < configFromWeb[targetName].columns.length; k++) {
195
+ configFromWeb[targetName].columns[k].forEach((cell) => {
196
+ temp[0].content.push(cell)
197
+ })
198
+ }
199
+ addArr.push(temp)
200
+ }
201
+ else {
202
+ for (let k = 0; k < configFromWeb[targetName].columns.length; k++) {
203
+ const temp = []
204
+ configFromWeb[targetName].columns[k].forEach((cell) => {
205
+ temp.push(cell)
206
+ })
207
+ addArr.push(temp)
208
+ }
209
+ }
210
+
211
+ const newArr = []
212
+ // 拼接之前的数组
213
+ if (before.length > 0) {
214
+ if (before.length >= 1) {
215
+ before.forEach((item) => {
216
+ newArr.push(item)
217
+ })
218
+ }
219
+ else {
220
+ newArr.push(before)
221
+ }
222
+ }
223
+
224
+ addArr.forEach((arr) => {
225
+ newArr.push(arr)
226
+ })
227
+
228
+ // 拼接之后的数组
229
+ if (after.length === 1) {
230
+ if (after[0].type === 'slot' || after[0][0].type === 'slot')
231
+ after = []
232
+ }
233
+ if (after.length > 0) {
234
+ if (after.length >= 1) {
235
+ after.forEach((item) => {
236
+ newArr.push(item)
237
+ })
238
+ }
239
+ else {
240
+ newArr.push(after)
241
+ }
242
+ }
243
+
244
+ config.columns = newArr
245
+ if (configFromWeb[targetName].slotsDeclare)
246
+ config.slotsDeclare = configFromWeb[targetName].slotsDeclare
247
+
248
+ else
249
+ config.slotsDeclare = []
250
+
251
+ if (config.data.images && configFromWeb[targetName].data.images) {
252
+ config.data.images = { ...config.data.images, ...configFromWeb[targetName].data.images }
253
+ delete configFromWeb[targetName].data.images
254
+ }
255
+ config.data = { ...config.data, ...configFromWeb[targetName].data }
256
+ }
257
+ }
258
+ }
259
+ Object.assign(activatedConfig, config)
260
+ }
261
+
262
+ // 获取并拼装插槽
263
+ function getConfigAndJoin(config: configDefine, outerLock: getConfigLock): void {
264
+ // 检查主配置插槽声明是否合法
265
+ const check = checkSlotDefine(config)
266
+ const waitForDownloadSlotName = []
267
+ if (check) {
268
+ // 扫描主配置中声明的插槽名
269
+ scanConfigName(config, waitForDownloadSlotName)
270
+
271
+ const total = waitForDownloadSlotName.length
272
+ let count = 0
273
+
274
+ // 挨个获取插槽
275
+ waitForDownloadSlotName.forEach((configName) => {
276
+ getConfigByNameWithoutIndexedDB(configName).then((res) => {
277
+ configFromWeb[configName] = res
278
+ count++
279
+ })
280
+ })
281
+
282
+ const timer = setInterval(() => {
283
+ if (count >= total) {
284
+ clearInterval(timer)
285
+ scanConfigSlot(config)
286
+ if (config.slotsDeclare.length > 0) {
287
+ const lock: getConfigLock = { status: true }
288
+ getConfigAndJoin(config, lock)
289
+ const innerTimer = setInterval(() => {
290
+ if (!lock.status) {
291
+ clearInterval(innerTimer)
292
+ outerLock.status = false
293
+ }
294
+ }, 100)
295
+ }
296
+ else {
297
+ outerLock.status = false
298
+ }
299
+ }
300
+ }, 100)
301
+ }
302
+ else {
303
+ outerLock.status = false
304
+ }
305
+ }
306
+
307
+ function deserializeFunctionAndRun(functionStr, value) {
308
+ // eslint-disable-next-line no-eval
309
+ const fun = eval(`(${functionStr})`)
310
+ return fun(value, activatedConfig)
311
+ }
312
+
313
+ function deserializeFunctionAndRunWithConfig(functionStr) {
314
+ // eslint-disable-next-line no-eval
315
+ const fun = eval(`(${functionStr})`)
316
+ return fun(activatedConfig)
317
+ }
318
+
319
+ // 将table配置的表格转换为适合Form展示的格式
320
+ function formatConfigToForm(config: configDefine): void {
321
+ const formColumns: Array<object> = []
322
+ const labels: Array<string> = []
323
+ for (let i = 0; i < config.columns.length; i++) {
324
+ let tempObj: {
325
+ customFunction?: any
326
+ inputColumns?: boolean
327
+ dataIndex: string
328
+ label: string
329
+ listType?: string
330
+ listLength?: number
331
+ valueText?: string
332
+ type: string | undefined
333
+ format?: string
334
+ definition?: Array<any>
335
+ editable?: boolean
336
+ inputReadOnly?: boolean
337
+ content?: Array<any>
338
+ text?: string
339
+ } = {
340
+ label: '',
341
+ type: undefined,
342
+ dataIndex: '',
343
+ }
344
+
345
+ const row: Array<any> = config.columns[i]
346
+ let listArr = []
347
+ for (let j = 0; j < row.length; j++) {
348
+ switch (row[j].type) {
349
+ case 'column' :
350
+ if (row[j].customFunctionForDynamicDataIndex)
351
+ row[j].dataIndex = deserializeFunctionAndRunWithConfig(row[j].customFunctionForDynamicDataIndex)
352
+ if (j === row.length - 1) {
353
+ if (row[j].customFunction) {
354
+ tempObj.customFunction = row[j].customFunction
355
+ tempObj.label = deserializeFunctionAndRun(row[j].customFunction, row[j].dataIndex)
356
+ }
357
+ else {
358
+ labels.push(tempObj.label)
359
+ labels.push(row[j].text)
360
+ tempObj = {
361
+ label: '',
362
+ type: undefined,
363
+ dataIndex: '',
364
+ }
365
+ }
366
+ }
367
+ else if (tempObj.label && tempObj.dataIndex) {
368
+ formColumns.push(tempObj)
369
+ tempObj = {
370
+ label: '',
371
+ type: undefined,
372
+ dataIndex: '',
373
+ }
374
+ }
375
+ else if (tempObj.customFunction) {
376
+ formColumns.push(tempObj)
377
+ tempObj = {
378
+ label: '',
379
+ type: undefined,
380
+ dataIndex: '',
381
+ }
382
+ }
383
+ else if (tempObj.label && !tempObj.dataIndex) {
384
+ labels.push(tempObj.label)
385
+ tempObj = {
386
+ label: '',
387
+ type: undefined,
388
+ dataIndex: '',
389
+ }
390
+ }
391
+ if (row[j].customFunction) {
392
+ tempObj.customFunction = row[j].customFunction
393
+ tempObj.label = deserializeFunctionAndRun(row[j].customFunction, row[j].dataIndex)
394
+ }
395
+ else {
396
+ tempObj.label = row[j].text
397
+ }
398
+ break
399
+ case 'value' :
400
+ tempObj.valueText = row[j].value
401
+ tempObj.editable = false
402
+ tempObj.type = 'showValue'
403
+ break
404
+ case 'input' :
405
+ if (row[j].customFunctionForDynamicDataIndex)
406
+ row[j].dataIndex = deserializeFunctionAndRunWithConfig(row[j].customFunctionForDynamicDataIndex)
407
+ tempObj.dataIndex = row[j].dataIndex
408
+ tempObj.type = 'input'
409
+ if (row[j].inputReadOnly)
410
+ tempObj.inputReadOnly = true
411
+ break
412
+ case 'curDateInput' :
413
+ tempObj.dataIndex = row[j].dataIndex
414
+ tempObj.text = row[j].text
415
+ tempObj.type = 'curDateInput'
416
+ break
417
+ case 'inputs' :
418
+ if (!tempObj.format)
419
+ tempObj.format = ''
420
+ if (!tempObj.dataIndex)
421
+ tempObj.dataIndex = row[j].dataIndex
422
+ tempObj.format += row[j].format
423
+ tempObj.type = 'inputs'
424
+ if (row[j].inputReadOnly)
425
+ tempObj.inputReadOnly = true
426
+ break
427
+ case 'images' :
428
+ tempObj.dataIndex = row[j].dataIndex
429
+ tempObj.type = 'images'
430
+ break
431
+ case 'inputColumns' :
432
+ tempObj.label = ''
433
+ tempObj.dataIndex = row[j].dataIndex
434
+ tempObj.definition = row[j].definition
435
+ if (labels.length > 0) {
436
+ for (let k = 0; k < tempObj.definition.length; k++) {
437
+ if (labels[k].length > 1) {
438
+ tempObj.definition[k].label = labels[k]
439
+ if (props.showInputColumnsLabelOnTitle) { // 动态行label展示在标题
440
+ if (k === tempObj.definition.length - 1)
441
+ tempObj.label = `${tempObj.label} ${labels[k].replace(/\s*/g, '')}`
442
+ else
443
+ tempObj.label = `${tempObj.label} ${labels[k].replace(/\s*/g, '')} :`
444
+ }
445
+ else { // 动态行仅展示为’动态行‘
446
+ tempObj.label = '动态行'
447
+ }
448
+ }
449
+ else {
450
+ tempObj.definition[k].label = ''
451
+ }
452
+ }
453
+ }
454
+ tempObj.type = 'inputColumns'
455
+ break
456
+ case 'list' :
457
+ tempObj.valueText = row[j].listHead
458
+ tempObj.type = 'list'
459
+ tempObj.listType = row[j].listType
460
+ tempObj.dataIndex = row[j].dataIndex
461
+ if (row[j].content)
462
+ tempObj.content = row[j].content
463
+ tempObj.listLength = row[j].listLength
464
+ listArr.push(tempObj)
465
+ tempObj = {
466
+ label: '',
467
+ type: undefined,
468
+ dataIndex: '',
469
+ }
470
+ }
471
+ }
472
+ if (listArr.length > 0)
473
+ formColumns.push({ type: 'list', content: listArr })
474
+ else if (tempObj.type || tempObj.label)
475
+ formColumns.push(tempObj)
476
+ listArr = []
477
+ tempObj = {
478
+ label: '',
479
+ type: undefined,
480
+ dataIndex: '',
481
+ }
482
+ }
483
+ activatedConfig.columns = formColumns
484
+ }
485
+
486
+ // 获取子组件更新的file列表
487
+ function updateFile(files, index) {
488
+ files.forEach((file) => {
489
+ if (file.content)
490
+ delete file.content
491
+ if (file.file)
492
+ delete file.file
493
+ if (file.objectUrl)
494
+ delete file.objectUrl
495
+ })
496
+ if (index.includes('@@@'))
497
+ changeDeepObjectDeepCopy(activatedConfig.data.images, index, files)
498
+ else
499
+ activatedConfig.data.images[index] = files
500
+ }
501
+
502
+ // function handleInputDeepChange(event, dataIndex) {
503
+ // this.$forceUpdate()
504
+ // }
505
+ function getDeepObject(obj, strPath) {
506
+ const arr = strPath.split('@@@')
507
+ let result = obj[arr[0]]
508
+ arr.shift()
509
+ try {
510
+ while (arr.length > 0) {
511
+ result = result[arr[0]]
512
+ arr.shift()
513
+ }
514
+ }
515
+ catch (e) {
516
+ result = undefined
517
+ }
518
+ return result
519
+ }
520
+
521
+ function changeDeepObject(obj, strPath, newVal) {
522
+ const arr = strPath.split('@@@')
523
+ if (arr.length === 1) {
524
+ obj[arr[0]] = newVal
525
+ }
526
+ else {
527
+ let result = obj[arr[0]]
528
+ arr.shift()
529
+ while (arr.length > 1) {
530
+ result = result[arr[0]]
531
+ arr.shift()
532
+ }
533
+ result[arr[0]] = newVal
534
+ }
535
+ }
536
+
537
+ function changeDeepObjectDeepCopy(obj, strPath, newVal) {
538
+ const arr = strPath.split('@@@')
539
+ if (arr.length === 1) {
540
+ obj[arr[0]] = newVal
541
+ }
542
+ else {
543
+ let result = obj[arr[0]]
544
+ arr.shift()
545
+ while (arr.length > 1) {
546
+ result = result[arr[0]]
547
+ arr.shift()
548
+ }
549
+ Object.assign(result[arr[0]], newVal)
550
+ }
551
+ }
552
+
553
+ // ------------------------- 初始化 -------------------------
554
+ initComponent()
555
+
556
+ function initComponent() {
557
+ if (props.localConfig !== undefined) {
558
+ Object.assign(activatedConfig, props.localConfig)
559
+
560
+ if (activatedConfig.designMode === 'json') {
561
+ activatedConfig.data = {}
562
+ Object.assign(activatedConfig.data, props.configData)
563
+ nextTick(() => {
564
+ scanFinish.value = true
565
+ })
566
+ return
567
+ }
568
+ else {
569
+ const lock = { status: true }
570
+ if (activatedConfig.slotsDeclare) {
571
+ getConfigAndJoin(activatedConfig, lock)
572
+ timer = setInterval(() => {
573
+ if (!lock.status) {
574
+ clearInterval(timer)
575
+ // 将配置文件格式更改为更利于Form的组织形式
576
+ formatConfigToForm(activatedConfig)
577
+ nextTick(() => {
578
+ scanFinish.value = true
579
+ })
580
+ }
581
+ }, 100)
582
+ }
583
+ else {
584
+ formatConfigToForm(activatedConfig)
585
+ nextTick(() => {
586
+ scanFinish.value = true
587
+ })
588
+ }
589
+ if (props.configData !== undefined)
590
+ Object.assign(activatedConfig.data, props.configData)
591
+
592
+ activatedConfig.columns.forEach((row) => {
593
+ row.forEach((item) => {
594
+ if (item.dataIndex && item.dataIndex.includes('@@@')) {
595
+ if (!activatedConfig.tempData) {
596
+ activatedConfig.tempData = {}
597
+ activatedConfig.tempData[item.dataIndex] = getDeepObject(activatedConfig.data, item.dataIndex)
598
+ if (item.type === 'images')
599
+ activatedConfig.tempData[item.dataIndex] = getDeepObject(activatedConfig.data.images, item.dataIndex)
600
+ }
601
+ else {
602
+ activatedConfig.tempData[item.dataIndex] = getDeepObject(activatedConfig.data, item.dataIndex)
603
+ if (item.type === 'images')
604
+ activatedConfig.tempData[item.dataIndex] = getDeepObject(activatedConfig.data.images, item.dataIndex)
605
+ }
606
+ }
607
+ })
608
+ })
609
+ console.warn('初始化完成,配置:', activatedConfig)
610
+ }
611
+ }
612
+
613
+ if (props.slotName)
614
+ activatedConfigName.value = props.slotName
615
+
616
+ else
617
+ activatedConfigName.value = props.configName
618
+
619
+ getConfigByNameWithoutIndexedDB(activatedConfigName.value).then((result) => {
620
+ Object.assign(activatedConfig, result)
621
+ if (activatedConfig.designMode === 'json') {
622
+ if (props.configData === undefined) {
623
+ console.error('无法找到json配置的数据')
624
+ }
625
+ else {
626
+ activatedConfig.data = {}
627
+ Object.assign(activatedConfig.data, props.configData)
628
+ nextTick(() => {
629
+ scanFinish.value = true
630
+ })
631
+ }
632
+ }
633
+ else {
634
+ const lock = { status: true }
635
+ if (activatedConfig.slotsDeclare) {
636
+ getConfigAndJoin(activatedConfig, lock)
637
+ timer = setInterval(() => {
638
+ if (!lock.status) {
639
+ clearInterval(timer)
640
+ // 将配置文件格式更改为更利于Form的组织形式
641
+ formatConfigToForm(activatedConfig)
642
+ nextTick(() => {
643
+ scanFinish.value = true
644
+ })
645
+ }
646
+ }, 100)
647
+ }
648
+ else {
649
+ formatConfigToForm(activatedConfig)
650
+ nextTick(() => {
651
+ scanFinish.value = true
652
+ })
653
+ }
654
+ if (props.configData !== undefined)
655
+ Object.assign(activatedConfig.data, props.configData)
656
+ }
657
+
658
+ activatedConfig.columns.forEach((row) => {
659
+ row.forEach((item) => {
660
+ if (item.dataIndex && item.dataIndex.includes('@@@')) {
661
+ if (!activatedConfig.tempData) {
662
+ activatedConfig.tempData = {}
663
+ activatedConfig.tempData[item.dataIndex] = getDeepObject(activatedConfig.data, item.dataIndex)
664
+ if (item.type === 'images')
665
+ activatedConfig.tempData[item.dataIndex] = getDeepObject(activatedConfig.data.images, item.dataIndex)
666
+ }
667
+ else {
668
+ activatedConfig.tempData[item.dataIndex] = getDeepObject(activatedConfig.data, item.dataIndex)
669
+ if (item.type === 'images')
670
+ activatedConfig.tempData[item.dataIndex] = getDeepObject(activatedConfig.data.images, item.dataIndex)
671
+ }
672
+ }
673
+ })
674
+ })
675
+ }).catch((e) => {
676
+ console.error(e)
677
+ })
678
+ }
679
+ function getNow() {
680
+ const date = new Date()
681
+ const year = date.getFullYear()
682
+ const month = String(date.getMonth() + 1).padStart(2, '0')
683
+ const day = String(date.getDate()).padStart(2, '0')
684
+ const hours = String(date.getHours()).padStart(2, '0')
685
+ const minutes = String(date.getMinutes()).padStart(2, '0')
686
+ const seconds = String(date.getSeconds()).padStart(2, '0')
687
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
688
+ }
689
+ </script>
690
+
691
+ <template>
692
+ <!-- 标题栏 -->
693
+ <van-nav-bar
694
+ v-if="showNav"
695
+ title="动态报表表单"
696
+ left-text="返回"
697
+ left-arrow
698
+ @click-left="onClickLeft"
699
+ />
700
+ <div v-if="scanFinish" class="main">
701
+ <template v-if="activatedConfig.designMode === 'json'">
702
+ <XReportFormJsonRender :json-config="activatedConfig" />
703
+ </template>
704
+ <template v-else>
705
+ <!-- 标题 -->
706
+ <h2 v-if="activatedConfig.title" class="title" v-html="activatedConfig.title" />
707
+ <!-- 副标题 -->
708
+ <div v-if="activatedConfig.subTitle && activatedConfig.subTitle.length > 0" class="form_item">
709
+ <van-collapse v-model="activeCollapseName" accordion>
710
+ <van-collapse-item title="副标题" name="副标题">
711
+ <van-form>
712
+ <van-cell-group inset>
713
+ <template v-for="(cell, index) in activatedConfig.subTitle" :key="index">
714
+ <template v-if="cell.type === 'inputs'">
715
+ <template v-for="(item, index) in formatInputs(cell)" :key="index">
716
+ <van-field
717
+ v-model="activatedConfig.data[cell.dataIndex][index]"
718
+ :label="`${item}:`"
719
+ clearable
720
+ type="textarea"
721
+ rows="1"
722
+ autosize
723
+ :placeholder="`请输入${item}`"
724
+ />
725
+ </template>
726
+ </template>
727
+ <template v-else>
728
+ <van-field
729
+ v-model="activatedConfig.data[cell.dataIndex]"
730
+ :label="`${cell.format}:`"
731
+ clearable
732
+ type="textarea"
733
+ rows="1"
734
+ autosize
735
+ :placeholder="`请输入${cell.format}`"
736
+ />
737
+ </template>
738
+ </template>
739
+ </van-cell-group>
740
+ </van-form>
741
+ </van-collapse-item>
742
+ </van-collapse>
743
+ </div>
744
+ <!-- 表单项 -->
745
+ <div class="form_item">
746
+ <van-collapse v-model="activeCollapseName" accordion>
747
+ <van-collapse-item title="表单项" name="表单项">
748
+ <van-form>
749
+ <template v-for="(row, index) in activatedConfig.columns" :key="index">
750
+ <!-- value属性 -->
751
+ <van-cell-group v-if="row.type === 'showValue'" inset class="cell_group">
752
+ <van-cell center :title="row.label" :value="row.valueText" />
753
+ </van-cell-group>
754
+ <!-- input -->
755
+ <van-cell-group v-else-if="row.type === 'input'" inset class="cell_group" :title="row.label">
756
+ <template v-if="row.inputReadOnly === true">
757
+ <template v-if="row.dataIndex.includes('@@@')">
758
+ <van-field
759
+ v-model="activatedConfig.tempData[row.dataIndex]"
760
+ :label="`${row.label}:`"
761
+ :placeholder="`请输入${row.label}`"
762
+ clearable
763
+ type="textarea"
764
+ rows="1"
765
+ :disabled="true"
766
+ autosize
767
+ />
768
+ </template>
769
+ <template v-else>
770
+ <van-field
771
+ v-model="activatedConfig.data[row.dataIndex]"
772
+ :label="`${row.label}:`"
773
+ :placeholder="`请输入${row.label}`"
774
+ clearable
775
+ type="textarea"
776
+ rows="1"
777
+ :disabled="true"
778
+ autosize
779
+ />
780
+ </template>
781
+ </template>
782
+ <template v-else>
783
+ <template v-if="row.dataIndex.includes('@@@')">
784
+ <van-field
785
+ v-model="activatedConfig.tempData[row.dataIndex]"
786
+ :label="`${row.label}:`"
787
+ :placeholder="`请输入${row.label}`"
788
+ clearable
789
+ type="textarea"
790
+ rows="1"
791
+ autosize
792
+ />
793
+ </template>
794
+ <template v-else>
795
+ <van-field
796
+ v-model="activatedConfig.data[row.dataIndex]"
797
+ :label="`${row.label}:`"
798
+ :placeholder="`请输入${row.label}`"
799
+ clearable
800
+ type="textarea"
801
+ rows="1"
802
+ autosize
803
+ />
804
+ </template>
805
+ </template>
806
+ </van-cell-group>
807
+ <!-- curDateInput -->
808
+ <van-cell-group v-else-if="row.type === 'curDateInput'" inset style="margin-top: 4vh">
809
+ <van-field
810
+ v-model="activatedConfig.data[row.dataIndex]"
811
+ :label="row.valueText ? `${row.valueText}:` : ''"
812
+ :placeholder="row.valueText ? `请点击右侧按钮设置${row.valueText}` : '请点击右侧按钮设置数据'"
813
+ readonly
814
+ type="textarea"
815
+ rows="1"
816
+ autosize
817
+ >
818
+ <template #button>
819
+ <van-button v-if="!activatedConfig.data[row.dataIndex]" size="small" type="primary" @click="activatedConfig.data[row.dataIndex] = getNow()">
820
+ {{ row.text }}
821
+ </van-button>
822
+ </template>
823
+ </van-field>
824
+ </van-cell-group>
825
+ <!-- inputs -->
826
+ <van-cell-group v-else-if="row.type === 'inputs'" inset :title="row.label">
827
+ <template v-if="row.inputReadOnly === true">
828
+ <template v-for="(item, index) in formatInputs(row)" :key="index">
829
+ <van-field
830
+ v-model="activatedConfig.data[row.dataIndex][index]"
831
+ :label="`${item}:`"
832
+ :placeholder="`请输入${item}`"
833
+ clearable
834
+ type="textarea"
835
+ rows="1"
836
+ :disabled="true"
837
+ autosize
838
+ />
839
+ </template>
840
+ </template>
841
+ <template v-else>
842
+ <template v-for="(item, index) in formatInputs(row)" :key="index">
843
+ <van-field
844
+ v-model="activatedConfig.data[row.dataIndex][index]"
845
+ :label="`${item}:`"
846
+ :placeholder="`请输入${item}`"
847
+ clearable
848
+ type="textarea"
849
+ rows="1"
850
+ autosize
851
+ />
852
+ </template>
853
+ </template>
854
+ </van-cell-group>
855
+ <!-- images -->
856
+ <van-cell-group v-else-if="row.type === 'images'" inset :title="row.label">
857
+ <template v-if="row.dataIndex.includes('@@@')">
858
+ <Uploader
859
+ upload-mode="oss"
860
+ :image-list="activatedConfig.tempData[row.dataIndex]"
861
+ :outer-index="row.dataIndex"
862
+ authority="admin"
863
+ @update-file-list="updateFile"
864
+ />
865
+ </template>
866
+ <template v-else>
867
+ <Uploader
868
+ upload-mode="oss"
869
+ :image-list="activatedConfig.data.images[row.dataIndex]"
870
+ :outer-index="row.dataIndex"
871
+ authority="admin"
872
+ @update-file-list="updateFile"
873
+ />
874
+ </template>
875
+ </van-cell-group>
876
+ <!-- 动态行 -->
877
+ <template v-else-if="row.type === 'inputColumns'">
878
+ <!-- <van-divider class="divider"> -->
879
+ <!-- {{ row.label }} -->
880
+ <!-- </van-divider> -->
881
+ <van-cell-group inset :title="row.label" style="background-color: #eff2f5">
882
+ <van-cell-group v-for="(item, index) in activatedConfig.data[row.dataIndex]" :key="index" inset class="my-cell-group" style="margin: 0 0 10px 0">
883
+ <div v-for="(def, defIndex) in row.definition" :key="defIndex">
884
+ <van-field
885
+ v-if="def.type === 'curDateInput'"
886
+ v-model="activatedConfig.data[row.dataIndex][index][def.dataIndex]"
887
+ :label="`${def.label}:`"
888
+ type="textarea"
889
+ rows="1"
890
+ autosize
891
+ :placeholder="`请点击右侧按钮设置${def.label}`"
892
+ readonly
893
+ >
894
+ <template #button>
895
+ <van-button v-show="activatedConfig.data[row.dataIndex][index][def.dataIndex] === ''" size="small" type="primary" @click="activatedConfig.data[row.dataIndex][index][def.dataIndex] = getNow()">
896
+ {{ def.text }}
897
+ </van-button>
898
+ </template>
899
+ </van-field>
900
+ <van-field
901
+ v-else
902
+ v-model="activatedConfig.data[row.dataIndex][index][def.dataIndex]"
903
+ :label="`${def.label}:`"
904
+ type="textarea"
905
+ rows="1"
906
+ autosize
907
+ :placeholder="`请输入${def.label}`"
908
+ clearable
909
+ />
910
+ </div>
911
+ </van-cell-group>
912
+ </van-cell-group>
913
+
914
+ <!-- <div class="button_group"> -->
915
+ <!-- <van-button class="input_columns_button" plain type="primary" @click="addInputColumns(row.dataIndex, row.definition)"> -->
916
+ <!-- <van-icon name="plus" /> -->
917
+ <!-- </van-button> -->
918
+ <!-- <van-button class="input_columns_button" plain type="primary" @click="removeInputColumns(row.dataIndex)"> -->
919
+ <!-- <van-icon name="minus" /> -->
920
+ <!-- </van-button> -->
921
+ <!-- </div> -->
922
+ </template>
923
+ <!-- list -->
924
+ <template v-else-if="row.type === 'list'">
925
+ <template v-for="(listIndex) in row.content[0].listLength" :key="listIndex">
926
+ <div style="margin-top: 3vh; margin-bottom: 3vh">
927
+ <van-cell-group inset :title="row.label" style="background-color: #eff2f5">
928
+ <template v-for="(cell, cellIndex) in row.content" :key="listIndex + cellIndex">
929
+ <template v-if="cell.listType === 'input'">
930
+ <van-field
931
+ v-model="activatedConfig.data[cell.dataIndex][listIndex]"
932
+ :label="cell.valueText"
933
+ type="textarea"
934
+ autosize
935
+ />
936
+ </template>
937
+ <template v-else-if="cell.listType === 'value'">
938
+ <van-field
939
+ :disabled="true"
940
+ :label="cell.valueText"
941
+ type="textarea"
942
+ :placeholder="cell.content[listIndex - 1]"
943
+ autosize
944
+ />
945
+ </template>
946
+ </template>
947
+ </van-cell-group>
948
+ </div>
949
+ </template>
950
+ </template>
951
+ </template>
952
+ </van-form>
953
+ </van-collapse-item>
954
+ </van-collapse>
955
+ </div>
956
+ <!-- 提交按钮 -->
957
+ <div class="submit_button">
958
+ <van-button round block type="primary" native-type="submit" @click="onSubmit">
959
+ 提交
960
+ </van-button>
961
+ </div>
962
+ </template>
963
+ </div>
964
+ <!-- 骨架屏 -->
965
+ <div v-else class="skeleton">
966
+ <van-skeleton title :row="5" />
967
+ </div>
968
+ </template>
969
+
970
+ <style scoped lang="less">
971
+ .main{
972
+ padding-top: 4vh;
973
+ width: 100vw;
974
+ height: 100vh;
975
+ background-color: #eff2f5;
976
+
977
+ .title{
978
+ padding-bottom: 2vh;
979
+ color: rgb(50, 50, 51);
980
+ text-align: center;
981
+ margin: 0 0 3vh;
982
+ }
983
+
984
+ .text_box{
985
+ margin-top: 2vh;
986
+ margin-bottom: 2vh;
987
+ }
988
+
989
+ .main_text{
990
+ padding-left: 16px;
991
+ font-weight: 400;
992
+ line-height: 1.6;
993
+ margin: 0 0 40px;
994
+ color: #969799;
995
+ font-size: 14px;
996
+ }
997
+
998
+ .show_value_item{
999
+ text-align: center;
1000
+ font-size: 1.2em;
1001
+ }
1002
+
1003
+ .cell_group{
1004
+ //margin-top: 2vh;
1005
+ //margin-bottom: 2vh;
1006
+ }
1007
+
1008
+ .form_item{
1009
+ margin-top: 2vh;
1010
+ }
1011
+
1012
+ .button_group{
1013
+ text-align: center;
1014
+ margin-top: 3vh;
1015
+ margin-bottom: 3vh;
1016
+ }
1017
+
1018
+ .button_group>:first-child {
1019
+ margin-right: 3vw;
1020
+ }
1021
+
1022
+ .divider{
1023
+ color: #1989fa;
1024
+ border-color: #1989fa;
1025
+ padding: 0 16px
1026
+ }
1027
+
1028
+ .submit_button{
1029
+ background-color: #eff2f5;
1030
+ padding: 5vh;
1031
+ }
1032
+ }
1033
+
1034
+ .skeleton{
1035
+ margin-top: 5vh
1036
+ }
1037
+ .my-cell-group{
1038
+ margin: 0 0 10px 0
1039
+ }
1040
+ :deep(.van-collapse-item__content) {
1041
+ background-color: #eff2f5;
1042
+ padding: 10px 0;
1043
+ }
1044
+ :deep(.van-cell-group__title){
1045
+ padding-top: 10px;
1046
+ padding-bottom: 10px;
1047
+ }
1048
+ :deep(.van-field__label) {
1049
+ font-weight: 600;
1050
+ }
1051
+ :deep(.van-uploader__wrapper){
1052
+ padding: 10px;
1053
+ display: flex;
1054
+ flex-wrap: wrap;
1055
+ justify-content: space-between; /* 水平方向上左右分散对齐 */
1056
+ align-items: center; /* 垂直方向上上下中心对齐 */
1057
+ }
1058
+ </style>