haiwei-ui 1.0.7 → 1.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "haiwei-ui",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.8",
|
|
4
4
|
"description": "HaiWei前端组件库",
|
|
5
5
|
"author": "Eric",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"main": "packages/index.js",
|
|
8
8
|
"scripts": {
|
|
9
|
-
"serve": "vue-cli-service serve",
|
|
10
|
-
"build": "vue-cli-service build",
|
|
9
|
+
"serve": "vue-cli-service lint && vue-cli-service serve",
|
|
10
|
+
"build": "vue-cli-service lint && vue-cli-service build",
|
|
11
11
|
"lint": "vue-cli-service lint",
|
|
12
12
|
"cm": "rimraf node_modules && rimraf dist",
|
|
13
13
|
"cc": "rimraf node_modules/.cache",
|
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<component v-bind:is="`nm-${options.showMode || 'drawer'}`" header footer draggable :padding="10" :title="title" :icon="icon" :width="width" :height="height" :visible.sync="visible_">
|
|
3
|
+
<el-form ref="form" :model="model" :rules="rules" :size="fontSize" label-width="120px">
|
|
4
|
+
<!-- 文件上传 -->
|
|
5
|
+
<el-form-item label="选择文件:" prop="file">
|
|
6
|
+
<el-upload
|
|
7
|
+
ref="upload"
|
|
8
|
+
class="nm-upload"
|
|
9
|
+
drag
|
|
10
|
+
:action="uploadUrl"
|
|
11
|
+
:headers="headers"
|
|
12
|
+
:data="uploadData"
|
|
13
|
+
:before-upload="beforeUpload"
|
|
14
|
+
:on-success="onUploadSuccess"
|
|
15
|
+
:on-error="onUploadError"
|
|
16
|
+
:on-remove="onFileRemove"
|
|
17
|
+
:file-list="fileList"
|
|
18
|
+
:limit="1"
|
|
19
|
+
accept=".xlsx,.xls"
|
|
20
|
+
>
|
|
21
|
+
<i class="el-icon-upload"></i>
|
|
22
|
+
<div class="el-upload__text">将文件拖到此处,或<em>点击上传</em></div>
|
|
23
|
+
<div class="el-upload__tip" slot="tip">只能上传Excel文件,且不超过100MB</div>
|
|
24
|
+
</el-upload>
|
|
25
|
+
</el-form-item>
|
|
26
|
+
|
|
27
|
+
<!-- 解析选项 -->
|
|
28
|
+
<el-form-item v-if="fileInfo" label="解析选项:">
|
|
29
|
+
<el-row :gutter="20">
|
|
30
|
+
<el-col :span="12">
|
|
31
|
+
<el-checkbox v-model="model.hasHeader" :disabled="parsing">包含表头</el-checkbox>
|
|
32
|
+
</el-col>
|
|
33
|
+
<el-col :span="12">
|
|
34
|
+
<el-checkbox v-model="model.skipEmptyRows" :disabled="parsing">跳过空行</el-checkbox>
|
|
35
|
+
</el-col>
|
|
36
|
+
</el-row>
|
|
37
|
+
<el-row :gutter="20" class="nm-m-t-10">
|
|
38
|
+
<el-col :span="12">
|
|
39
|
+
<el-input-number v-model="model.maxPreviewRows" :min="1" :max="1000" :disabled="parsing" controls-position="right" style="width: 100%">
|
|
40
|
+
<template slot="prepend">最大预览行数</template>
|
|
41
|
+
</el-input-number>
|
|
42
|
+
</el-col>
|
|
43
|
+
</el-row>
|
|
44
|
+
</el-form-item>
|
|
45
|
+
|
|
46
|
+
<!-- 文件信息 -->
|
|
47
|
+
<el-form-item v-if="fileInfo" label="文件信息:">
|
|
48
|
+
<el-descriptions :column="2" border size="small">
|
|
49
|
+
<el-descriptions-item label="文件名">{{ fileInfo.fileName }}</el-descriptions-item>
|
|
50
|
+
<el-descriptions-item label="文件大小">{{ formatFileSize(fileInfo.fileSize) }}</el-descriptions-item>
|
|
51
|
+
<el-descriptions-item label="工作表">{{ fileInfo.sheetCount }}个</el-descriptions-item>
|
|
52
|
+
<el-descriptions-item label="总行数">{{ fileInfo.totalRows }}行</el-descriptions-item>
|
|
53
|
+
</el-descriptions>
|
|
54
|
+
</el-form-item>
|
|
55
|
+
|
|
56
|
+
<!-- 工作表选择 -->
|
|
57
|
+
<el-form-item v-if="fileInfo && fileInfo.sheets && fileInfo.sheets.length > 1" label="选择工作表:" prop="selectedSheet">
|
|
58
|
+
<el-select v-model="model.selectedSheet" placeholder="请选择工作表" :disabled="parsing" style="width: 100%">
|
|
59
|
+
<el-option v-for="sheet in fileInfo.sheets" :key="sheet.index" :label="sheet.name" :value="sheet.index">
|
|
60
|
+
<span style="float: left">{{ sheet.name }}</span>
|
|
61
|
+
<span style="float: right; color: #8492a6; font-size: 13px">{{ sheet.rowCount }}行 × {{ sheet.columnCount }}列</span>
|
|
62
|
+
</el-option>
|
|
63
|
+
</el-select>
|
|
64
|
+
</el-form-item>
|
|
65
|
+
|
|
66
|
+
<!-- 数据预览 -->
|
|
67
|
+
<el-form-item v-if="parseResult" label="数据预览:">
|
|
68
|
+
<el-alert v-if="parseResult.basicInfo" :title="`已解析 ${parseResult.basicInfo.rowCount} 行数据,${parseResult.basicInfo.columnCount} 列`" type="info" show-icon :closable="false" class="nm-m-b-10" />
|
|
69
|
+
|
|
70
|
+
<el-table :data="parseResult.previewData" border stripe size="mini" max-height="400" v-loading="parsing">
|
|
71
|
+
<el-table-column v-for="(col, index) in parseResult.columns" :key="index" :prop="col.name" :label="col.label" :width="col.width">
|
|
72
|
+
<template v-slot:header>
|
|
73
|
+
<div class="nm-text-center">
|
|
74
|
+
<div>{{ col.label }}</div>
|
|
75
|
+
<div class="nm-text-xs nm-text-muted">({{ col.name }})</div>
|
|
76
|
+
</div>
|
|
77
|
+
</template>
|
|
78
|
+
<template v-slot="{ row }">
|
|
79
|
+
<span :title="row[col.name]">{{ row[col.name] }}</span>
|
|
80
|
+
</template>
|
|
81
|
+
</el-table-column>
|
|
82
|
+
</el-table>
|
|
83
|
+
|
|
84
|
+
<div v-if="parseResult.basicInfo && parseResult.basicInfo.totalRows > parseResult.basicInfo.rowCount" class="nm-text-center nm-m-t-10 nm-text-muted">
|
|
85
|
+
仅显示前{{ parseResult.basicInfo.rowCount }}行,共{{ parseResult.basicInfo.totalRows }}行数据
|
|
86
|
+
</div>
|
|
87
|
+
</el-form-item>
|
|
88
|
+
|
|
89
|
+
<!-- 列映射配置 -->
|
|
90
|
+
<el-form-item v-if="parseResult && parseResult.columns && parseResult.columns.length > 0" label="列映射配置:">
|
|
91
|
+
<el-table :data="columnMapping" border stripe size="mini" max-height="300">
|
|
92
|
+
<el-table-column prop="excelColumn" label="Excel列" width="150">
|
|
93
|
+
<template v-slot="{ row }">
|
|
94
|
+
<span>{{ row.excelColumn.label }} <span class="nm-text-muted">({{ row.excelColumn.name }})</span></span>
|
|
95
|
+
</template>
|
|
96
|
+
</el-table-column>
|
|
97
|
+
<el-table-column prop="targetColumn" label="目标字段" width="200">
|
|
98
|
+
<template v-slot="{ row }">
|
|
99
|
+
<el-select v-model="row.targetColumn" placeholder="请选择目标字段" style="width: 100%">
|
|
100
|
+
<el-option label="不导入此列" :value="null"></el-option>
|
|
101
|
+
<el-option v-for="col in targetColumns" :key="col.name" :label="col.label" :value="col.name">
|
|
102
|
+
<span style="float: left">{{ col.label }}</span>
|
|
103
|
+
<span style="float: right; color: #8492a6; font-size: 13px">{{ col.name }}</span>
|
|
104
|
+
</el-option>
|
|
105
|
+
</el-select>
|
|
106
|
+
</template>
|
|
107
|
+
</el-table-column>
|
|
108
|
+
<el-table-column prop="required" label="必填" width="80" align="center">
|
|
109
|
+
<template v-slot="{ row }">
|
|
110
|
+
<el-checkbox v-model="row.required" :disabled="!row.targetColumn"></el-checkbox>
|
|
111
|
+
</template>
|
|
112
|
+
</el-table-column>
|
|
113
|
+
<el-table-column prop="defaultValue" label="默认值">
|
|
114
|
+
<template v-slot="{ row }">
|
|
115
|
+
<el-input v-model="row.defaultValue" :disabled="!row.targetColumn" placeholder="当Excel为空时使用此默认值"></el-input>
|
|
116
|
+
</template>
|
|
117
|
+
</el-table-column>
|
|
118
|
+
</el-table>
|
|
119
|
+
</el-form-item>
|
|
120
|
+
|
|
121
|
+
<!-- 导入模式 -->
|
|
122
|
+
<el-form-item v-if="parseResult" label="导入模式:" prop="importMode">
|
|
123
|
+
<el-radio-group v-model="model.importMode">
|
|
124
|
+
<el-radio :label="0">新增数据</el-radio>
|
|
125
|
+
<el-radio :label="1">更新数据</el-radio>
|
|
126
|
+
<el-radio :label="2">新增或更新</el-radio>
|
|
127
|
+
</el-radio-group>
|
|
128
|
+
</el-form-item>
|
|
129
|
+
|
|
130
|
+
<!-- 错误处理 -->
|
|
131
|
+
<el-form-item v-if="parseResult" label="错误处理:" prop="errorHandling">
|
|
132
|
+
<el-radio-group v-model="model.errorHandling">
|
|
133
|
+
<el-radio :label="0">遇到错误停止导入</el-radio>
|
|
134
|
+
<el-radio :label="1">跳过错误继续导入</el-radio>
|
|
135
|
+
</el-radio-group>
|
|
136
|
+
</el-form-item>
|
|
137
|
+
</el-form>
|
|
138
|
+
|
|
139
|
+
<!--底部-->
|
|
140
|
+
<template v-slot:footer>
|
|
141
|
+
<el-button-group>
|
|
142
|
+
<nm-button v-if="fileInfo && !parseResult" type="primary" :loading="parsing" text="解析文件" @click="onParse" />
|
|
143
|
+
<nm-button v-if="parseResult" type="success" :loading="importing" text="开始导入" @click="onImport" />
|
|
144
|
+
<nm-button type="info" text="取消" @click="hide" />
|
|
145
|
+
</el-button-group>
|
|
146
|
+
</template>
|
|
147
|
+
</component>
|
|
148
|
+
</template>
|
|
149
|
+
<script>
|
|
150
|
+
import dialog from '../../../../mixins/components/dialog'
|
|
151
|
+
import { mapState } from 'vuex'
|
|
152
|
+
export default {
|
|
153
|
+
mixins: [dialog],
|
|
154
|
+
data() {
|
|
155
|
+
return {
|
|
156
|
+
title: '导入数据',
|
|
157
|
+
icon: 'import',
|
|
158
|
+
width: '900px',
|
|
159
|
+
height: '700px',
|
|
160
|
+
model: {
|
|
161
|
+
file: null,
|
|
162
|
+
hasHeader: true,
|
|
163
|
+
skipEmptyRows: false,
|
|
164
|
+
maxPreviewRows: 100,
|
|
165
|
+
selectedSheet: 0,
|
|
166
|
+
importMode: 0,
|
|
167
|
+
errorHandling: 0
|
|
168
|
+
},
|
|
169
|
+
rules: {
|
|
170
|
+
file: [{ required: true, message: '请选择要导入的文件', trigger: 'change' }],
|
|
171
|
+
selectedSheet: [{ required: true, message: '请选择工作表', trigger: 'change' }]
|
|
172
|
+
},
|
|
173
|
+
fileList: [],
|
|
174
|
+
fileInfo: null,
|
|
175
|
+
parseResult: null,
|
|
176
|
+
parsing: false,
|
|
177
|
+
importing: false,
|
|
178
|
+
columnMapping: [],
|
|
179
|
+
targetColumns: []
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
props: {
|
|
183
|
+
/** 目标列配置 */
|
|
184
|
+
cols: Array,
|
|
185
|
+
/** 导入配置 */
|
|
186
|
+
options: Object,
|
|
187
|
+
/** 导入方法 */
|
|
188
|
+
action: Function
|
|
189
|
+
},
|
|
190
|
+
computed: {
|
|
191
|
+
...mapState('app/user', { token: 'token' }),
|
|
192
|
+
uploadUrl() {
|
|
193
|
+
return `${this.$api.baseURL}/api/admin/excel/parseexcel`
|
|
194
|
+
},
|
|
195
|
+
headers() {
|
|
196
|
+
return {
|
|
197
|
+
'Authorization': `Bearer ${this.token}`
|
|
198
|
+
}
|
|
199
|
+
},
|
|
200
|
+
uploadData() {
|
|
201
|
+
return {
|
|
202
|
+
hasHeader: this.model.hasHeader,
|
|
203
|
+
maxPreviewRows: this.model.maxPreviewRows,
|
|
204
|
+
skipEmptyRows: this.model.skipEmptyRows
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
methods: {
|
|
209
|
+
/**初始化 */
|
|
210
|
+
init() {
|
|
211
|
+
// 重置状态
|
|
212
|
+
this.fileList = []
|
|
213
|
+
this.fileInfo = null
|
|
214
|
+
this.parseResult = null
|
|
215
|
+
this.parsing = false
|
|
216
|
+
this.importing = false
|
|
217
|
+
this.columnMapping = []
|
|
218
|
+
|
|
219
|
+
// 初始化目标列
|
|
220
|
+
this.initTargetColumns()
|
|
221
|
+
|
|
222
|
+
// 应用选项配置
|
|
223
|
+
const { hasHeader = true, maxPreviewRows = 100, skipEmptyRows = false } = this.options
|
|
224
|
+
this.model.hasHeader = hasHeader
|
|
225
|
+
this.model.maxPreviewRows = maxPreviewRows
|
|
226
|
+
this.model.skipEmptyRows = skipEmptyRows
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
/**初始化目标列 */
|
|
230
|
+
initTargetColumns() {
|
|
231
|
+
if (this.cols && this.cols.length > 0) {
|
|
232
|
+
this.targetColumns = this.cols
|
|
233
|
+
.filter(col => col.show && col.name)
|
|
234
|
+
.map(col => ({
|
|
235
|
+
name: col.name,
|
|
236
|
+
label: col.label || col.name,
|
|
237
|
+
type: col.type || 'string'
|
|
238
|
+
}))
|
|
239
|
+
} else {
|
|
240
|
+
this.targetColumns = []
|
|
241
|
+
}
|
|
242
|
+
},
|
|
243
|
+
|
|
244
|
+
/**上传前验证 */
|
|
245
|
+
beforeUpload(file) {
|
|
246
|
+
const isExcel = file.type === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' ||
|
|
247
|
+
file.type === 'application/vnd.ms-excel'
|
|
248
|
+
const isLt100M = file.size / 1024 / 1024 < 100
|
|
249
|
+
|
|
250
|
+
if (!isExcel) {
|
|
251
|
+
this._error('只能上传Excel文件!')
|
|
252
|
+
return false
|
|
253
|
+
}
|
|
254
|
+
if (!isLt100M) {
|
|
255
|
+
this._error('文件大小不能超过100MB!')
|
|
256
|
+
return false
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
this.model.file = file
|
|
260
|
+
return true
|
|
261
|
+
},
|
|
262
|
+
|
|
263
|
+
/**上传成功 */
|
|
264
|
+
onUploadSuccess(response, file, fileList) {
|
|
265
|
+
if (response.successful) {
|
|
266
|
+
this.fileInfo = response.data.basicInfo
|
|
267
|
+
this.parseResult = response.data
|
|
268
|
+
this.initColumnMapping()
|
|
269
|
+
this._success('文件上传成功')
|
|
270
|
+
} else {
|
|
271
|
+
this._error(response.msg || '文件上传失败')
|
|
272
|
+
this.$refs.upload.clearFiles()
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
|
|
276
|
+
/**上传失败 */
|
|
277
|
+
onUploadError(err, file, fileList) {
|
|
278
|
+
this._error('文件上传失败')
|
|
279
|
+
this.$refs.upload.clearFiles()
|
|
280
|
+
},
|
|
281
|
+
|
|
282
|
+
/**文件移除 */
|
|
283
|
+
onFileRemove(file, fileList) {
|
|
284
|
+
this.model.file = null
|
|
285
|
+
this.fileInfo = null
|
|
286
|
+
this.parseResult = null
|
|
287
|
+
this.columnMapping = []
|
|
288
|
+
},
|
|
289
|
+
|
|
290
|
+
/**初始化列映射 */
|
|
291
|
+
initColumnMapping() {
|
|
292
|
+
if (!this.parseResult || !this.parseResult.columns) return
|
|
293
|
+
|
|
294
|
+
this.columnMapping = this.parseResult.columns.map(col => ({
|
|
295
|
+
excelColumn: col,
|
|
296
|
+
targetColumn: this.findBestMatch(col),
|
|
297
|
+
required: false,
|
|
298
|
+
defaultValue: ''
|
|
299
|
+
}))
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
/**查找最佳匹配的目标列 */
|
|
303
|
+
findBestMatch(excelColumn) {
|
|
304
|
+
if (!this.targetColumns || this.targetColumns.length === 0) return null
|
|
305
|
+
|
|
306
|
+
const excelLabel = excelColumn.label.toLowerCase()
|
|
307
|
+
const excelName = excelColumn.name.toLowerCase()
|
|
308
|
+
|
|
309
|
+
// 1. 尝试完全匹配标签
|
|
310
|
+
let match = this.targetColumns.find(col => col.label.toLowerCase() === excelLabel)
|
|
311
|
+
if (match) return match.name
|
|
312
|
+
|
|
313
|
+
// 2. 尝试完全匹配名称
|
|
314
|
+
match = this.targetColumns.find(col => col.name.toLowerCase() === excelName)
|
|
315
|
+
if (match) return match.name
|
|
316
|
+
|
|
317
|
+
// 3. 尝试包含匹配
|
|
318
|
+
match = this.targetColumns.find(col =>
|
|
319
|
+
excelLabel.includes(col.label.toLowerCase()) ||
|
|
320
|
+
col.label.toLowerCase().includes(excelLabel)
|
|
321
|
+
)
|
|
322
|
+
if (match) return match.name
|
|
323
|
+
|
|
324
|
+
return null
|
|
325
|
+
},
|
|
326
|
+
|
|
327
|
+
/**解析文件 */
|
|
328
|
+
onParse() {
|
|
329
|
+
if (!this.model.file) {
|
|
330
|
+
this._error('请先选择文件')
|
|
331
|
+
return
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
this.parsing = true
|
|
335
|
+
const formData = new FormData()
|
|
336
|
+
formData.append('file', this.model.file)
|
|
337
|
+
formData.append('hasHeader', this.model.hasHeader)
|
|
338
|
+
formData.append('maxPreviewRows', this.model.maxPreviewRows)
|
|
339
|
+
formData.append('skipEmptyRows', this.model.skipEmptyRows)
|
|
340
|
+
|
|
341
|
+
this.$api.post('/api/admin/excel/parseexcel', formData, {
|
|
342
|
+
headers: { 'Content-Type': 'multipart/form-data' }
|
|
343
|
+
})
|
|
344
|
+
.then(response => {
|
|
345
|
+
if (response.successful) {
|
|
346
|
+
this.fileInfo = response.data.basicInfo
|
|
347
|
+
this.parseResult = response.data
|
|
348
|
+
this.initColumnMapping()
|
|
349
|
+
this._success('文件解析成功')
|
|
350
|
+
} else {
|
|
351
|
+
this._error(response.msg || '文件解析失败')
|
|
352
|
+
}
|
|
353
|
+
})
|
|
354
|
+
.catch(error => {
|
|
355
|
+
this._error('文件解析失败:' + (error.message || '未知错误'))
|
|
356
|
+
})
|
|
357
|
+
.finally(() => {
|
|
358
|
+
this.parsing = false
|
|
359
|
+
})
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
/**开始导入 */
|
|
363
|
+
onImport() {
|
|
364
|
+
if (!this.parseResult) {
|
|
365
|
+
this._error('请先解析文件')
|
|
366
|
+
return
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// 验证必填字段
|
|
370
|
+
const missingRequired = this.columnMapping
|
|
371
|
+
.filter(mapping => mapping.required && !mapping.targetColumn)
|
|
372
|
+
.map(mapping => mapping.excelColumn.label)
|
|
373
|
+
|
|
374
|
+
if (missingRequired.length > 0) {
|
|
375
|
+
this._error(`以下必填字段未配置映射:${missingRequired.join('、')}`)
|
|
376
|
+
return
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
this.importing = true
|
|
380
|
+
|
|
381
|
+
try {
|
|
382
|
+
// 根据列映射配置生成AddModel列表
|
|
383
|
+
const addModels = this.generateAddModels()
|
|
384
|
+
|
|
385
|
+
if (addModels.length === 0) {
|
|
386
|
+
this._error('没有可导入的数据')
|
|
387
|
+
this.importing = false
|
|
388
|
+
return
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
// 调用导入方法,传递AddModel列表
|
|
392
|
+
if (this.action && typeof this.action === 'function') {
|
|
393
|
+
this.action(addModels)
|
|
394
|
+
.then(() => {
|
|
395
|
+
this._success(`成功导入${addModels.length}条数据`)
|
|
396
|
+
this.hide()
|
|
397
|
+
this.$emit('import-success')
|
|
398
|
+
})
|
|
399
|
+
.catch(error => {
|
|
400
|
+
this._error('数据导入失败:' + (error.message || '未知错误'))
|
|
401
|
+
})
|
|
402
|
+
.finally(() => {
|
|
403
|
+
this.importing = false
|
|
404
|
+
})
|
|
405
|
+
} else {
|
|
406
|
+
this._error('未配置导入方法')
|
|
407
|
+
this.importing = false
|
|
408
|
+
}
|
|
409
|
+
} catch (error) {
|
|
410
|
+
this._error('数据转换失败:' + error.message)
|
|
411
|
+
this.importing = false
|
|
412
|
+
}
|
|
413
|
+
},
|
|
414
|
+
|
|
415
|
+
/**根据列映射配置生成AddModel列表 */
|
|
416
|
+
generateAddModels() {
|
|
417
|
+
if (!this.parseResult || !this.parseResult.previewData) {
|
|
418
|
+
return []
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// 获取有效的列映射(有目标字段的映射)
|
|
422
|
+
const validMappings = this.columnMapping.filter(mapping => mapping.targetColumn)
|
|
423
|
+
|
|
424
|
+
// 转换每一行数据为AddModel
|
|
425
|
+
return this.parseResult.previewData.map((row, rowIndex) => {
|
|
426
|
+
const addModel = {}
|
|
427
|
+
|
|
428
|
+
// 根据列映射配置设置字段值
|
|
429
|
+
validMappings.forEach(mapping => {
|
|
430
|
+
const excelColumnName = mapping.excelColumn.name
|
|
431
|
+
const targetColumn = mapping.targetColumn
|
|
432
|
+
let value = row[excelColumnName]
|
|
433
|
+
|
|
434
|
+
// 如果值为空且设置了默认值,使用默认值
|
|
435
|
+
if ((value === null || value === undefined || value === '') && mapping.defaultValue) {
|
|
436
|
+
value = mapping.defaultValue
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// 设置到AddModel中
|
|
440
|
+
addModel[targetColumn] = value
|
|
441
|
+
})
|
|
442
|
+
|
|
443
|
+
return addModel
|
|
444
|
+
})
|
|
445
|
+
},
|
|
446
|
+
|
|
447
|
+
/**格式化文件大小 */
|
|
448
|
+
formatFileSize(bytes) {
|
|
449
|
+
if (bytes === 0) return '0 B'
|
|
450
|
+
const k = 1024
|
|
451
|
+
const sizes = ['B', 'KB', 'MB', 'GB']
|
|
452
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
453
|
+
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
created() {
|
|
457
|
+
this.init()
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
</script>
|
|
461
|
+
|
|
462
|
+
<style lang="scss">
|
|
463
|
+
.nm-upload {
|
|
464
|
+
.el-upload {
|
|
465
|
+
width: 100%;
|
|
466
|
+
|
|
467
|
+
.el-upload-dragger {
|
|
468
|
+
width: 100%;
|
|
469
|
+
height: 180px;
|
|
470
|
+
display: flex;
|
|
471
|
+
flex-direction: column;
|
|
472
|
+
justify-content: center;
|
|
473
|
+
align-items: center;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
</style>
|
|
@@ -57,7 +57,34 @@ const exportOptions = {
|
|
|
57
57
|
showExportPeople: false
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
//默认导入配置
|
|
61
|
+
const importOptions = {
|
|
62
|
+
/**启用导入功能 */
|
|
63
|
+
enabled: false,
|
|
64
|
+
/**导入数据的方法 */
|
|
65
|
+
action: null,
|
|
66
|
+
/**开启高级选项 */
|
|
67
|
+
advanced: false,
|
|
68
|
+
/**高级选项显示模式,drawer/dialog */
|
|
69
|
+
showMode: 'drawer',
|
|
70
|
+
/**按钮位置,tool:右上角工具栏 querybar:查询栏里面 */
|
|
71
|
+
btnLocation: 'tool',
|
|
72
|
+
/**按钮编码,用于控制按钮权限 */
|
|
73
|
+
btnCode: '',
|
|
74
|
+
/**默认是否包含表头 */
|
|
75
|
+
hasHeader: true,
|
|
76
|
+
/**默认最大预览行数 */
|
|
77
|
+
maxPreviewRows: 100,
|
|
78
|
+
/**默认是否跳过空行 */
|
|
79
|
+
skipEmptyRows: false,
|
|
80
|
+
/**默认导入模式:0-新增,1-更新,2-新增或更新 */
|
|
81
|
+
importMode: 0,
|
|
82
|
+
/**默认错误处理:0-遇到错误停止,1-跳过错误继续 */
|
|
83
|
+
errorHandling: 0
|
|
84
|
+
}
|
|
85
|
+
|
|
60
86
|
export default {
|
|
61
87
|
columnInfo,
|
|
62
|
-
exportOptions
|
|
88
|
+
exportOptions,
|
|
89
|
+
importOptions
|
|
63
90
|
}
|
|
@@ -76,20 +76,24 @@
|
|
|
76
76
|
|
|
77
77
|
<!--导出-->
|
|
78
78
|
<query-export v-if="exportAdvancedEnabled" :options="exportOptions_" :list-title="title" :cols="columns" :visible.sync="showExport" />
|
|
79
|
+
|
|
80
|
+
<!--导入-->
|
|
81
|
+
<query-import v-if="importAdvancedEnabled" :options="importOptions_" :cols="columns" :action="importOptions_.action" :visible.sync="showImport" />
|
|
79
82
|
</section>
|
|
80
83
|
</template>
|
|
81
84
|
<script>
|
|
82
85
|
import { mapState } from 'vuex'
|
|
83
86
|
import def from './default.js'
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
import QueryHeader from './components/header'
|
|
88
|
+
import Querybar from './components/querybar'
|
|
89
|
+
import QueryTable from './components/table'
|
|
90
|
+
import QueryFooter from './components/footer'
|
|
91
|
+
import QueryExport from './components/export'
|
|
92
|
+
import QueryImport from './components/import'
|
|
89
93
|
|
|
90
94
|
export default {
|
|
91
95
|
name: 'List',
|
|
92
|
-
components: { QueryHeader, Querybar, QueryTable, QueryFooter, QueryExport },
|
|
96
|
+
components: { QueryHeader, Querybar, QueryTable, QueryFooter, QueryExport, QueryImport },
|
|
93
97
|
data() {
|
|
94
98
|
return {
|
|
95
99
|
loading_: false,
|
|
@@ -108,6 +112,7 @@
|
|
|
108
112
|
total: 0,
|
|
109
113
|
selection: [],
|
|
110
114
|
showExport: false,
|
|
115
|
+
showImport: false,
|
|
111
116
|
columns: []
|
|
112
117
|
}
|
|
113
118
|
},
|
|
@@ -180,6 +185,8 @@
|
|
|
180
185
|
},
|
|
181
186
|
/**导出配置 */
|
|
182
187
|
exportOptions: Object,
|
|
188
|
+
/**导入配置 */
|
|
189
|
+
importOptions: Object,
|
|
183
190
|
/** 页数选择项 */
|
|
184
191
|
pageSizes: {
|
|
185
192
|
type: Array,
|
|
@@ -222,6 +229,12 @@
|
|
|
222
229
|
},
|
|
223
230
|
exportAdvancedEnabled() {
|
|
224
231
|
return this.exportOptions_.enabled && this.exportOptions_.advanced
|
|
232
|
+
},
|
|
233
|
+
importOptions_() {
|
|
234
|
+
return this.$_.assignIn({ title: this.title }, def.importOptions, this.importOptions)
|
|
235
|
+
},
|
|
236
|
+
importAdvancedEnabled() {
|
|
237
|
+
return this.importOptions_.enabled && this.importOptions_.advanced
|
|
225
238
|
}
|
|
226
239
|
},
|
|
227
240
|
methods: {
|
|
@@ -360,6 +373,27 @@
|
|
|
360
373
|
this.showExport = false
|
|
361
374
|
this.$emit('export-change', this.showExport)
|
|
362
375
|
},
|
|
376
|
+
/** 切换导入对话框显示状态 */
|
|
377
|
+
triggerImport() {
|
|
378
|
+
let imp = this.importOptions_
|
|
379
|
+
//未启用高级,直接执行导入操作
|
|
380
|
+
if (!imp.advanced) {
|
|
381
|
+
// 简单导入模式:直接打开文件选择
|
|
382
|
+
this._info('请使用高级导入功能进行文件选择和配置')
|
|
383
|
+
return
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
this.showImport ? this.closeImport() : this.openImport()
|
|
387
|
+
},
|
|
388
|
+
/** 打开导入对话框 */
|
|
389
|
+
openImport() {
|
|
390
|
+
this.showImport = true
|
|
391
|
+
this.$emit('import-change', this.showImport)
|
|
392
|
+
},
|
|
393
|
+
closeImport() {
|
|
394
|
+
this.showImport = false
|
|
395
|
+
this.$emit('import-change', this.showImport)
|
|
396
|
+
},
|
|
363
397
|
/** 列表的列转导出的列 */
|
|
364
398
|
listCol2ExportCol(m) {
|
|
365
399
|
let col = {
|