sh-view 2.8.1 → 2.8.3

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 (91) hide show
  1. package/.eslintrc.js +25 -20
  2. package/other.js +8 -8
  3. package/package.json +9 -6
  4. package/packages/components/index.js +91 -91
  5. package/packages/components/sh-alert/alert.ts +30 -0
  6. package/packages/components/sh-alert/index.vue +143 -168
  7. package/packages/components/sh-badge/index.vue +242 -242
  8. package/packages/components/sh-calendar/index.vue +650 -650
  9. package/packages/components/sh-card/index.vue +148 -148
  10. package/packages/components/sh-code-editor/index.vue +19 -19
  11. package/packages/components/sh-col/index.vue +92 -92
  12. package/packages/components/sh-corner/index.vue +230 -230
  13. package/packages/components/sh-count-to/index.vue +131 -131
  14. package/packages/components/sh-date/index.vue +301 -301
  15. package/packages/components/sh-drawer/index.vue +579 -579
  16. package/packages/components/sh-drawer/scrollbar.js +78 -78
  17. package/packages/components/sh-empty/index.vue +42 -42
  18. package/packages/components/sh-form/js/props.js +76 -76
  19. package/packages/components/sh-form/js/useForm.js +229 -229
  20. package/packages/components/sh-header/index.vue +261 -260
  21. package/packages/components/sh-icon/css/default/ionicons.svg +869 -869
  22. package/packages/components/sh-icon/css/font/iconfont.json +247 -247
  23. package/packages/components/sh-icon/index.vue +41 -41
  24. package/packages/components/sh-image/index.vue +133 -133
  25. package/packages/components/sh-list/index.vue +146 -146
  26. package/packages/components/sh-loading/index.vue +53 -53
  27. package/packages/components/sh-modal/index.vue +188 -188
  28. package/packages/components/sh-noticebar/index.vue +215 -215
  29. package/packages/components/sh-poptip/index.vue +597 -597
  30. package/packages/components/sh-progress/index.vue +276 -276
  31. package/packages/components/sh-pull-refresh/index.vue +289 -289
  32. package/packages/components/sh-result/index.vue +114 -114
  33. package/packages/components/sh-row/index.vue +66 -66
  34. package/packages/components/sh-split/components/trigger.vue +33 -33
  35. package/packages/components/sh-split/index.vue +342 -342
  36. package/packages/components/sh-table/components/importModal.vue +363 -363
  37. package/packages/components/sh-table/components/sh-column.vue +68 -68
  38. package/packages/components/sh-table/js/excel_to_json.js +313 -313
  39. package/packages/components/sh-table/js/props.js +305 -305
  40. package/packages/components/sh-table/js/tableMethods.js +167 -167
  41. package/packages/components/sh-table/js/useTable.js +636 -636
  42. package/packages/components/sh-table/table.vue +217 -217
  43. package/packages/components/sh-tabs/index.vue +426 -426
  44. package/packages/components/sh-tag/index.vue +168 -168
  45. package/packages/components/sh-toolbar/index.vue +182 -182
  46. package/packages/components/sh-tree/components/table-tree.vue +289 -289
  47. package/packages/components/sh-tree/mixin/treeProps.js +122 -122
  48. package/packages/components/sh-upload/index.vue +535 -535
  49. package/packages/components/sh-water-fall/index.vue +80 -80
  50. package/packages/components/sh-water-mark/index.vue +96 -96
  51. package/packages/css/index.js +4 -4
  52. package/packages/directive/index.js +19 -19
  53. package/packages/directive/module/click-out.js +14 -14
  54. package/packages/directive/module/draggable.js +42 -42
  55. package/packages/directive/module/line-clamp.js +22 -22
  56. package/packages/directive/module/prevent-click.js +18 -18
  57. package/packages/directive/module/resize.js +14 -14
  58. package/packages/directive/module/ripple.js +166 -166
  59. package/packages/index.js +39 -39
  60. package/packages/mixin/index.js +86 -86
  61. package/packages/other/sh-cron-modal/components/cron-content.vue +294 -294
  62. package/packages/other/sh-cron-modal/index.vue +81 -81
  63. package/packages/other/sh-cron-modal/mixin/cron-emits.js +1 -1
  64. package/packages/other/sh-cron-modal/mixin/cron-props.js +9 -9
  65. package/packages/other/sh-cron-modal/tabs/cron-week-box.vue +126 -126
  66. package/packages/other/sh-menu/index.vue +326 -326
  67. package/packages/other/sh-menu/menu-group-content.vue +136 -136
  68. package/packages/other/sh-menu/menu-item-content.vue +71 -71
  69. package/packages/other/sh-menu-card/index.vue +250 -250
  70. package/packages/other/sh-menu-card/menu-box.vue +87 -87
  71. package/packages/other/sh-preview/components/sh-excel.vue +163 -163
  72. package/packages/other/sh-preview/js/data-hook.js +41 -41
  73. package/packages/other/sh-preview/js/data-props.js +15 -15
  74. package/packages/other/sh-system-tip/index.vue +115 -115
  75. package/packages/utils/resize.js +69 -70
  76. package/packages/utils/transfer-queue.js +12 -12
  77. package/packages/vxeTable/index.js +193 -184
  78. package/packages/vxeTable/plugins/export.js +450 -450
  79. package/packages/vxeTable/render/cell/vxe-render-img.vue +27 -27
  80. package/packages/vxeTable/render/cell/vxe-render-table.vue +51 -51
  81. package/packages/vxeTable/render/cell/vxe-render-time.vue +44 -44
  82. package/packages/vxeTable/render/cell/vxe-render-tree.vue +70 -70
  83. package/packages/vxeTable/render/filters/vxe-filter-input.vue +26 -26
  84. package/packages/vxeTable/render/filters/vxe-filter-time.vue +26 -26
  85. package/packages/vxeTable/render/globalRenders.jsx +514 -514
  86. package/packages/vxeTable/render/mixin/cell-hooks.js +198 -198
  87. package/packages/vxeTable/render/mixin/cell-props.js +23 -23
  88. package/packages/vxeTable/render/mixin/filter-hooks.js +46 -46
  89. package/tsconfig.json +25 -0
  90. package/types/component.d.ts +1 -0
  91. package/types/index.ts +0 -0
@@ -1,363 +1,363 @@
1
- <template>
2
- <sh-modal v-bind="modalConfig" :loading="modalLoading" @close="onModalClose">
3
- <div class="import-template-top">
4
- <transition name="fade">
5
- <sh-progress v-if="showProgress" :percent="progressPercent" :stroke-width="2">
6
- <template v-if="progressPercent === 100"><span>上传成功</span></template>
7
- </sh-progress>
8
- </transition>
9
- </div>
10
- <div class="import-template-head">
11
- <sh-button :size="size" @click="handleDownloadTemplateBtn">下载导入模板</sh-button>
12
- <sh-button :size="size" status="primary" @click="handleImportFileBtn">选择导入文件</sh-button>
13
- <div class="floatright">
14
- <sh-poptip v-if="importErrorData.length > 0" trigger="click" title="错误信息" placement="bottom-end">
15
- <sh-button :size="size" status="danger">
16
- 共发现 <strong>{{ importErrorData.length }}</strong> 条不符合规范数据
17
- </sh-button>
18
- <template #content>
19
- <div class="import-template-error-list">
20
- <div v-for="(errData, errDataIndex) in importErrorData" :key="errDataIndex" class="error-item">
21
- 第 <strong class="red">{{ errData.rowIndex }}</strong> 行,错误:<span class="red">{{ errData.errMsg }}</span>
22
- </div>
23
- </div>
24
- </template>
25
- </sh-poptip>
26
- </div>
27
- </div>
28
- <div class="import-template-body">
29
- <sh-table ref="shtable" v-bind="tableConfig"></sh-table>
30
- </div>
31
- <div class="import-template-foot">
32
- <div class="foot-left"></div>
33
- <div class="foot-right">
34
- <sh-button :size="size" @click="onModalClose">取消</sh-button>
35
- <sh-button :size="size" status="primary" @click="handleImportDataBtn('all')">全部导入</sh-button>
36
- <sh-button v-if="tableGlobalConfig.selectType" :size="size" status="warning" @click="handleImportDataBtn('select')">导入选中</sh-button>
37
- </div>
38
- </div>
39
- </sh-modal>
40
- </template>
41
-
42
- <script>
43
- import { defineComponent, computed, getCurrentInstance, ref, onBeforeMount } from 'vue'
44
- import * as ExcelJS from 'exceljs'
45
- export default defineComponent({
46
- name: 'TableImport',
47
- props: {
48
- modalConfig: {
49
- type: Object,
50
- default() {
51
- return {}
52
- }
53
- },
54
- columns: {
55
- type: Array
56
- },
57
- height: {
58
- type: [Number, String],
59
- default: 360
60
- },
61
- needValidate: {
62
- type: Boolean
63
- },
64
- importRules: {
65
- type: Object,
66
- default() {
67
- return {}
68
- }
69
- },
70
- importConfig: {
71
- type: Object,
72
- default() {
73
- return {}
74
- }
75
- },
76
- globalConfig: {
77
- type: Object,
78
- default() {
79
- return {}
80
- }
81
- },
82
- size: {
83
- type: String,
84
- default: 'mini' // medium / small / mini
85
- },
86
- importFileCallback: {
87
- type: Function
88
- },
89
- importFileFinished: {
90
- type: Function
91
- },
92
- downloadTemplateCallback: {
93
- type: Function
94
- },
95
- downloadTemplateFinished: {
96
- type: Function
97
- }
98
- },
99
- emits: ['confirm', 'cancel'],
100
- setup(props, context) {
101
- const { proxy } = getCurrentInstance()
102
- const { $vUtils, $vTable } = proxy
103
- const { emit, slots } = context
104
- const shtable = ref()
105
-
106
- const importTableData = ref([])
107
- const importErrorData = ref([])
108
- const modalLoading = ref(false)
109
- const showProgress = ref(false)
110
- const progressPercent = ref(0)
111
-
112
- const tableGlobalConfig = computed(() => Object.assign({ seq: true, title: true, tableName: '导入预览' }, props.globalConfig))
113
- const tableColumns = computed(() => props.columns.filter(item => !(['seq', 'checkbox', 'radio'].includes(item.type) || item.renderName === '$vGlobalOption')))
114
- const tableConfig = computed(() => {
115
- return {
116
- height: props.height,
117
- size: props.size,
118
- editRules: props.importRules,
119
- columns: tableColumns.value,
120
- data: importTableData.value,
121
- globalConfig: tableGlobalConfig.value
122
- }
123
- })
124
- // 初始化
125
- const initCreated = () => {
126
- showProgress.value = false
127
- progressPercent.value = 0
128
- importTableData.value = []
129
- }
130
- // 弹窗关闭
131
- const onModalClose = () => {
132
- $vUtils.set(props.modalConfig, 'modelValue', false)
133
- emit('cancel')
134
- }
135
- // 确认导入数据按钮
136
- const handleImportDataBtn = async (type = 'all') => {
137
- let importData = type === 'all' ? importTableData.value : shtable.value.getSelectionData()
138
- if (!importData || !Array.isArray(importData) || importData.length < 1) {
139
- proxy.msginfo('未导入数据')
140
- return false
141
- }
142
- if (props.needValidate) {
143
- let validateErrMap = await handleImportDataValidate(importData)
144
- if (validateErrMap) {
145
- proxy.msgerror('导入校验失败,请检查数据')
146
- return validateErrMap
147
- }
148
- }
149
- emit('confirm', importData)
150
- onModalClose()
151
- }
152
- // 导入数据验证
153
- const handleImportDataValidate = async rows => {
154
- return shtable.value.validate(rows)
155
- }
156
- // 下载导入模板按钮
157
- const handleDownloadTemplateBtn = () => {
158
- const parentTable = shtable.value.tableRef.value
159
- if (typeof props.downloadTemplateCallback === 'function') {
160
- props.downloadTemplateCallback()
161
- }
162
- let defaultOption = {
163
- filename: '模板',
164
- sheetName: props.globalConfig?.filename || '模板',
165
- type: 'xlsx',
166
- original: false,
167
- download: true,
168
- message: false,
169
- useStyle: false,
170
- columns: parentTable.getColumns().filter(item => item.type !== 'seq'),
171
- data: [],
172
- afterExportMethod: options => {
173
- handleDownloadTemplateFinished(options)
174
- }
175
- }
176
- parentTable.exportData(Object.assign(defaultOption, props.importConfig))
177
- }
178
- // 下载导入模板成功回调
179
- const handleDownloadTemplateFinished = options => {
180
- if (typeof props.downloadTemplateFinished === 'function') {
181
- props.downloadTemplateFinished(options)
182
- }
183
- proxy.msgsuccess('下载模板完成!')
184
- }
185
- // 选择导入文件按钮
186
- const handleImportFileBtn = async () => {
187
- try {
188
- const { file } = await $vTable.readFile({
189
- types: ['xlsx', 'xls']
190
- })
191
- if (typeof props.importFileCallback === 'function') {
192
- props.importFileCallback(file)
193
- }
194
- let fileRes = await readFile(file)
195
- let { results, errDatas } = dealImportTableData(tableColumns.value, fileRes)
196
- importTableData.value = results
197
- importErrorData.value = errDatas
198
- setTimeout(() => {
199
- showProgress.value = false
200
- }, 2000)
201
- if (typeof props.importFileFinished === 'function') {
202
- props.importFileFinished(options)
203
- }
204
- proxy.msgsuccess('导入完成!')
205
- } catch (e) {
206
- proxy.msgerror(e.message || e)
207
- }
208
- }
209
- // 读取文件
210
- const readFile = async file => {
211
- return new Promise((resolve, reject) => {
212
- const reader = new FileReader()
213
- reader.readAsArrayBuffer(file)
214
- reader.onloadstart = e => {
215
- modalLoading.value = true
216
- showProgress.value = true
217
- }
218
- reader.onprogress = e => {
219
- progressPercent.value = Math.round((e.loaded / e.total) * 100)
220
- }
221
- reader.onerror = e => {
222
- reject(new Error('文件读取出错'))
223
- }
224
- reader.onload = e => {
225
- modalLoading.value = false
226
- const workbook = new ExcelJS.Workbook()
227
- const data = e.target.result
228
- workbook.xlsx.load(data).then(wb => {
229
- const firstSheet = wb.worksheets[0]
230
- if (!firstSheet) {
231
- reject(new Error('文件读取出错'))
232
- }
233
- let headerRowCount = 1
234
- if (firstSheet.views?.length) {
235
- const frozenViews = firstSheet.views.filter(vm => vm.state === 'frozen')
236
- if (frozenViews.length) headerRowCount = frozenViews[0].ySplit
237
- }
238
- const sheetValues = firstSheet.getSheetValues()
239
- const dataValues = sheetValues.filter(a => !!a).slice(headerRowCount)
240
- resolve(dataValues)
241
- })
242
- }
243
- })
244
- }
245
- // 根据表格读取数据生成表格数据
246
- const dealImportTableData = (columns, records) => {
247
- let fields = generateColumnsAll(columns)
248
- let errDatas = []
249
- let results = records.map((row, rowIndex) => {
250
- const item = {}
251
- row.forEach((cellValue, cIndex) => {
252
- let column = fields[cIndex - 1]
253
- const { field, title, renderName, renderProps } = column
254
- let fieldValue = cellValue
255
- let split = renderProps?.split || ','
256
- let valueKey = renderName === '$vTree' ? renderProps?.nodeKey || 'id' : 'value'
257
- let labelKey = renderProps?.labelField || 'label'
258
- switch (renderName) {
259
- case '$vSelect':
260
- case '$vSwitch':
261
- case '$vCheckgroup':
262
- case '$vRadiogroup':
263
- case '$vTree':
264
- if (cellValue && renderProps.options && Array.isArray(renderProps.options)) {
265
- // 如果匹配到选择项则保留,否则删除掉不规范数据
266
- // 判断导入值为数据源的key or value 去匹配
267
- if (renderProps.multiple || ['$vSelect', '$vCheckgroup', '$vTree'].includes(renderName)) {
268
- let oriArray = cellValue ? cellValue.split(split).filter(_ => _ && _ !== 'undefined') : []
269
- let values = oriArray.map(orv => {
270
- let opt = renderProps.options.find(opt => String(orv) === String(opt[valueKey]) || String(orv) === opt[labelKey])
271
- return opt[valueKey]
272
- })
273
- fieldValue = values.join(split)
274
- } else {
275
- let opt = renderProps.options.find(opt => String(cellValue) === String(opt[valueKey]) || String(cellValue) === opt[labelKey])
276
- fieldValue = opt[valueKey]
277
- }
278
- if (!fieldValue || fieldValue.includes(undefined)) {
279
- errDatas.push({ rowIndex: rowIndex + 1, errMsg: `字段【${title}】不符合数据源规范,已被清空` })
280
- fieldValue = ''
281
- }
282
- }
283
- break
284
- default:
285
- break
286
- }
287
- $vUtils.set(item, field, fieldValue)
288
- })
289
- return item
290
- })
291
- return { results, errDatas }
292
- }
293
- // 生成全表头
294
- const generateColumnsAll = (columns, resultColumns = []) => {
295
- columns.forEach((column, index) => {
296
- if (column.children && Array.isArray(column.children) && column.children.length) {
297
- generateColumnsAll(column.children, resultColumns)
298
- } else {
299
- if (column.field) {
300
- resultColumns.push({
301
- title: column.title,
302
- field: column.field,
303
- renderName: column.renderName,
304
- renderProps: column.renderProps
305
- })
306
- }
307
- }
308
- })
309
- return resultColumns
310
- }
311
-
312
- onBeforeMount(() => {
313
- initCreated()
314
- })
315
-
316
- return {
317
- shtable,
318
- modalLoading,
319
- showProgress,
320
- progressPercent,
321
- importErrorData,
322
- tableConfig,
323
- tableGlobalConfig,
324
- onModalClose,
325
- handleDownloadTemplateBtn,
326
- handleImportFileBtn,
327
- handleImportDataBtn
328
- }
329
- }
330
- })
331
- </script>
332
-
333
- <style lang="scss" scoped>
334
- .import-template-head {
335
- + .import-template-body,
336
- + .import-template-foot {
337
- margin-top: 10px;
338
- }
339
- .floatright {
340
- float: right;
341
- }
342
- }
343
- .import-template-body {
344
- + .import-template-foot,
345
- + .import-template-error {
346
- margin-top: 10px;
347
- }
348
- }
349
- .import-template-foot {
350
- display: flex;
351
- align-items: center;
352
- justify-content: space-between;
353
- }
354
- .import-template-error-list {
355
- max-height: 300px;
356
- overflow: auto;
357
- font-size: 13px;
358
- .error-item {
359
- display: block;
360
- margin-bottom: 5px;
361
- }
362
- }
363
- </style>
1
+ <template>
2
+ <sh-modal v-bind="modalConfig" :loading="modalLoading" @close="onModalClose">
3
+ <div class="import-template-top">
4
+ <transition name="fade">
5
+ <sh-progress v-if="showProgress" :percent="progressPercent" :stroke-width="2">
6
+ <template v-if="progressPercent === 100"><span>上传成功</span></template>
7
+ </sh-progress>
8
+ </transition>
9
+ </div>
10
+ <div class="import-template-head">
11
+ <sh-button :size="size" @click="handleDownloadTemplateBtn">下载导入模板</sh-button>
12
+ <sh-button :size="size" status="primary" @click="handleImportFileBtn">选择导入文件</sh-button>
13
+ <div class="floatright">
14
+ <sh-poptip v-if="importErrorData.length > 0" trigger="click" title="错误信息" placement="bottom-end">
15
+ <sh-button :size="size" status="danger">
16
+ 共发现 <strong>{{ importErrorData.length }}</strong> 条不符合规范数据
17
+ </sh-button>
18
+ <template #content>
19
+ <div class="import-template-error-list">
20
+ <div v-for="(errData, errDataIndex) in importErrorData" :key="errDataIndex" class="error-item">
21
+ 第 <strong class="red">{{ errData.rowIndex }}</strong> 行,错误:<span class="red">{{ errData.errMsg }}</span>
22
+ </div>
23
+ </div>
24
+ </template>
25
+ </sh-poptip>
26
+ </div>
27
+ </div>
28
+ <div class="import-template-body">
29
+ <sh-table ref="shtable" v-bind="tableConfig"></sh-table>
30
+ </div>
31
+ <div class="import-template-foot">
32
+ <div class="foot-left"></div>
33
+ <div class="foot-right">
34
+ <sh-button :size="size" @click="onModalClose">取消</sh-button>
35
+ <sh-button :size="size" status="primary" @click="handleImportDataBtn('all')">全部导入</sh-button>
36
+ <sh-button v-if="tableGlobalConfig.selectType" :size="size" status="warning" @click="handleImportDataBtn('select')">导入选中</sh-button>
37
+ </div>
38
+ </div>
39
+ </sh-modal>
40
+ </template>
41
+
42
+ <script>
43
+ import { defineComponent, computed, getCurrentInstance, ref, onBeforeMount } from 'vue'
44
+ import * as ExcelJS from 'exceljs'
45
+ export default defineComponent({
46
+ name: 'TableImport',
47
+ props: {
48
+ modalConfig: {
49
+ type: Object,
50
+ default() {
51
+ return {}
52
+ }
53
+ },
54
+ columns: {
55
+ type: Array
56
+ },
57
+ height: {
58
+ type: [Number, String],
59
+ default: 360
60
+ },
61
+ needValidate: {
62
+ type: Boolean
63
+ },
64
+ importRules: {
65
+ type: Object,
66
+ default() {
67
+ return {}
68
+ }
69
+ },
70
+ importConfig: {
71
+ type: Object,
72
+ default() {
73
+ return {}
74
+ }
75
+ },
76
+ globalConfig: {
77
+ type: Object,
78
+ default() {
79
+ return {}
80
+ }
81
+ },
82
+ size: {
83
+ type: String,
84
+ default: 'mini' // medium / small / mini
85
+ },
86
+ importFileCallback: {
87
+ type: Function
88
+ },
89
+ importFileFinished: {
90
+ type: Function
91
+ },
92
+ downloadTemplateCallback: {
93
+ type: Function
94
+ },
95
+ downloadTemplateFinished: {
96
+ type: Function
97
+ }
98
+ },
99
+ emits: ['confirm', 'cancel'],
100
+ setup(props, context) {
101
+ const { proxy } = getCurrentInstance()
102
+ const { $vUtils, $vTable } = proxy
103
+ const { emit, slots } = context
104
+ const shtable = ref()
105
+
106
+ const importTableData = ref([])
107
+ const importErrorData = ref([])
108
+ const modalLoading = ref(false)
109
+ const showProgress = ref(false)
110
+ const progressPercent = ref(0)
111
+
112
+ const tableGlobalConfig = computed(() => Object.assign({ seq: true, title: true, tableName: '导入预览' }, props.globalConfig))
113
+ const tableColumns = computed(() => props.columns.filter(item => !(['seq', 'checkbox', 'radio'].includes(item.type) || item.renderName === '$vGlobalOption')))
114
+ const tableConfig = computed(() => {
115
+ return {
116
+ height: props.height,
117
+ size: props.size,
118
+ editRules: props.importRules,
119
+ columns: tableColumns.value,
120
+ data: importTableData.value,
121
+ globalConfig: tableGlobalConfig.value
122
+ }
123
+ })
124
+ // 初始化
125
+ const initCreated = () => {
126
+ showProgress.value = false
127
+ progressPercent.value = 0
128
+ importTableData.value = []
129
+ }
130
+ // 弹窗关闭
131
+ const onModalClose = () => {
132
+ $vUtils.set(props.modalConfig, 'modelValue', false)
133
+ emit('cancel')
134
+ }
135
+ // 确认导入数据按钮
136
+ const handleImportDataBtn = async (type = 'all') => {
137
+ let importData = type === 'all' ? importTableData.value : shtable.value.getSelectionData()
138
+ if (!importData || !Array.isArray(importData) || importData.length < 1) {
139
+ proxy.msginfo('未导入数据')
140
+ return false
141
+ }
142
+ if (props.needValidate) {
143
+ let validateErrMap = await handleImportDataValidate(importData)
144
+ if (validateErrMap) {
145
+ proxy.msgerror('导入校验失败,请检查数据')
146
+ return validateErrMap
147
+ }
148
+ }
149
+ emit('confirm', importData)
150
+ onModalClose()
151
+ }
152
+ // 导入数据验证
153
+ const handleImportDataValidate = async rows => {
154
+ return shtable.value.validate(rows)
155
+ }
156
+ // 下载导入模板按钮
157
+ const handleDownloadTemplateBtn = () => {
158
+ const parentTable = shtable.value.tableRef.value
159
+ if (typeof props.downloadTemplateCallback === 'function') {
160
+ props.downloadTemplateCallback()
161
+ }
162
+ let defaultOption = {
163
+ filename: '模板',
164
+ sheetName: props.globalConfig?.filename || '模板',
165
+ type: 'xlsx',
166
+ original: false,
167
+ download: true,
168
+ message: false,
169
+ useStyle: false,
170
+ columns: parentTable.getColumns().filter(item => item.type !== 'seq'),
171
+ data: [],
172
+ afterExportMethod: options => {
173
+ handleDownloadTemplateFinished(options)
174
+ }
175
+ }
176
+ parentTable.exportData(Object.assign(defaultOption, props.importConfig))
177
+ }
178
+ // 下载导入模板成功回调
179
+ const handleDownloadTemplateFinished = options => {
180
+ if (typeof props.downloadTemplateFinished === 'function') {
181
+ props.downloadTemplateFinished(options)
182
+ }
183
+ proxy.msgsuccess('下载模板完成!')
184
+ }
185
+ // 选择导入文件按钮
186
+ const handleImportFileBtn = async () => {
187
+ try {
188
+ const { file } = await $vTable.readFile({
189
+ types: ['xlsx', 'xls']
190
+ })
191
+ if (typeof props.importFileCallback === 'function') {
192
+ props.importFileCallback(file)
193
+ }
194
+ let fileRes = await readFile(file)
195
+ let { results, errDatas } = dealImportTableData(tableColumns.value, fileRes)
196
+ importTableData.value = results
197
+ importErrorData.value = errDatas
198
+ setTimeout(() => {
199
+ showProgress.value = false
200
+ }, 2000)
201
+ if (typeof props.importFileFinished === 'function') {
202
+ props.importFileFinished(options)
203
+ }
204
+ proxy.msgsuccess('导入完成!')
205
+ } catch (e) {
206
+ proxy.msgerror(e.message || e)
207
+ }
208
+ }
209
+ // 读取文件
210
+ const readFile = async file => {
211
+ return new Promise((resolve, reject) => {
212
+ const reader = new FileReader()
213
+ reader.readAsArrayBuffer(file)
214
+ reader.onloadstart = e => {
215
+ modalLoading.value = true
216
+ showProgress.value = true
217
+ }
218
+ reader.onprogress = e => {
219
+ progressPercent.value = Math.round((e.loaded / e.total) * 100)
220
+ }
221
+ reader.onerror = e => {
222
+ reject(new Error('文件读取出错'))
223
+ }
224
+ reader.onload = e => {
225
+ modalLoading.value = false
226
+ const workbook = new ExcelJS.Workbook()
227
+ const data = e.target.result
228
+ workbook.xlsx.load(data).then(wb => {
229
+ const firstSheet = wb.worksheets[0]
230
+ if (!firstSheet) {
231
+ reject(new Error('文件读取出错'))
232
+ }
233
+ let headerRowCount = 1
234
+ if (firstSheet.views?.length) {
235
+ const frozenViews = firstSheet.views.filter(vm => vm.state === 'frozen')
236
+ if (frozenViews.length) headerRowCount = frozenViews[0].ySplit
237
+ }
238
+ const sheetValues = firstSheet.getSheetValues()
239
+ const dataValues = sheetValues.filter(a => !!a).slice(headerRowCount)
240
+ resolve(dataValues)
241
+ })
242
+ }
243
+ })
244
+ }
245
+ // 根据表格读取数据生成表格数据
246
+ const dealImportTableData = (columns, records) => {
247
+ let fields = generateColumnsAll(columns)
248
+ let errDatas = []
249
+ let results = records.map((row, rowIndex) => {
250
+ const item = {}
251
+ row.forEach((cellValue, cIndex) => {
252
+ let column = fields[cIndex - 1]
253
+ const { field, title, renderName, renderProps } = column
254
+ let fieldValue = cellValue
255
+ let split = renderProps?.split || ','
256
+ let valueKey = renderName === '$vTree' ? renderProps?.nodeKey || 'id' : 'value'
257
+ let labelKey = renderProps?.labelField || 'label'
258
+ switch (renderName) {
259
+ case '$vSelect':
260
+ case '$vSwitch':
261
+ case '$vCheckgroup':
262
+ case '$vRadiogroup':
263
+ case '$vTree':
264
+ if (cellValue && renderProps.options && Array.isArray(renderProps.options)) {
265
+ // 如果匹配到选择项则保留,否则删除掉不规范数据
266
+ // 判断导入值为数据源的key or value 去匹配
267
+ if (renderProps.multiple || ['$vSelect', '$vCheckgroup', '$vTree'].includes(renderName)) {
268
+ let oriArray = cellValue ? cellValue.split(split).filter(_ => _ && _ !== 'undefined') : []
269
+ let values = oriArray.map(orv => {
270
+ let opt = renderProps.options.find(opt => String(orv) === String(opt[valueKey]) || String(orv) === opt[labelKey])
271
+ return opt[valueKey]
272
+ })
273
+ fieldValue = values.join(split)
274
+ } else {
275
+ let opt = renderProps.options.find(opt => String(cellValue) === String(opt[valueKey]) || String(cellValue) === opt[labelKey])
276
+ fieldValue = opt[valueKey]
277
+ }
278
+ if (!fieldValue || fieldValue.includes(undefined)) {
279
+ errDatas.push({ rowIndex: rowIndex + 1, errMsg: `字段【${title}】不符合数据源规范,已被清空` })
280
+ fieldValue = ''
281
+ }
282
+ }
283
+ break
284
+ default:
285
+ break
286
+ }
287
+ $vUtils.set(item, field, fieldValue)
288
+ })
289
+ return item
290
+ })
291
+ return { results, errDatas }
292
+ }
293
+ // 生成全表头
294
+ const generateColumnsAll = (columns, resultColumns = []) => {
295
+ columns.forEach((column, index) => {
296
+ if (column.children && Array.isArray(column.children) && column.children.length) {
297
+ generateColumnsAll(column.children, resultColumns)
298
+ } else {
299
+ if (column.field) {
300
+ resultColumns.push({
301
+ title: column.title,
302
+ field: column.field,
303
+ renderName: column.renderName,
304
+ renderProps: column.renderProps
305
+ })
306
+ }
307
+ }
308
+ })
309
+ return resultColumns
310
+ }
311
+
312
+ onBeforeMount(() => {
313
+ initCreated()
314
+ })
315
+
316
+ return {
317
+ shtable,
318
+ modalLoading,
319
+ showProgress,
320
+ progressPercent,
321
+ importErrorData,
322
+ tableConfig,
323
+ tableGlobalConfig,
324
+ onModalClose,
325
+ handleDownloadTemplateBtn,
326
+ handleImportFileBtn,
327
+ handleImportDataBtn
328
+ }
329
+ }
330
+ })
331
+ </script>
332
+
333
+ <style lang="scss" scoped>
334
+ .import-template-head {
335
+ + .import-template-body,
336
+ + .import-template-foot {
337
+ margin-top: 10px;
338
+ }
339
+ .floatright {
340
+ float: right;
341
+ }
342
+ }
343
+ .import-template-body {
344
+ + .import-template-foot,
345
+ + .import-template-error {
346
+ margin-top: 10px;
347
+ }
348
+ }
349
+ .import-template-foot {
350
+ display: flex;
351
+ align-items: center;
352
+ justify-content: space-between;
353
+ }
354
+ .import-template-error-list {
355
+ max-height: 300px;
356
+ overflow: auto;
357
+ font-size: 13px;
358
+ .error-item {
359
+ display: block;
360
+ margin-bottom: 5px;
361
+ }
362
+ }
363
+ </style>