haiwei-ui 1.0.97 → 1.0.99
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
|
@@ -2,68 +2,62 @@
|
|
|
2
2
|
<component v-bind:is="`nm-${options.showMode || 'drawer'}`" header footer draggable :padding="20" :title="title" :icon="icon" :width="width" :height="height" :visible.sync="visible_">
|
|
3
3
|
<div class="import-container">
|
|
4
4
|
<!-- 步骤指示器 -->
|
|
5
|
-
<el-steps :active="currentStep" align-center class="import-steps">
|
|
6
|
-
<el-step title="选择文件"
|
|
7
|
-
<el-step title="配置选项"
|
|
8
|
-
<el-step title="数据映射"
|
|
9
|
-
<el-step title="
|
|
5
|
+
<el-steps :active="currentStep" align-center class="import-steps" simple>
|
|
6
|
+
<el-step title="选择文件" />
|
|
7
|
+
<el-step title="配置选项" />
|
|
8
|
+
<el-step title="数据映射" />
|
|
9
|
+
<el-step title="确认导入" />
|
|
10
10
|
</el-steps>
|
|
11
11
|
|
|
12
12
|
<!-- 步骤1:选择文件 -->
|
|
13
13
|
<div v-if="currentStep === 1" class="step-content">
|
|
14
14
|
<el-card shadow="never" class="step-card">
|
|
15
15
|
<div slot="header" class="step-header">
|
|
16
|
-
<
|
|
17
|
-
|
|
16
|
+
<div class="header-left">
|
|
17
|
+
<span class="step-title">选择Excel文件</span>
|
|
18
|
+
<span class="step-subtitle">支持 .xlsx 和 .xls 格式,最大10MB</span>
|
|
19
|
+
</div>
|
|
20
|
+
<div class="header-right">
|
|
21
|
+
<el-upload
|
|
22
|
+
ref="upload"
|
|
23
|
+
:action="uploadUrl"
|
|
24
|
+
:headers="headers"
|
|
25
|
+
:before-upload="beforeUpload"
|
|
26
|
+
:on-success="onUploadSuccess"
|
|
27
|
+
:on-error="onUploadError"
|
|
28
|
+
:on-remove="onFileRemove"
|
|
29
|
+
:file-list="fileList"
|
|
30
|
+
:limit="1"
|
|
31
|
+
accept=".xlsx,.xls"
|
|
32
|
+
:show-file-list="false"
|
|
33
|
+
>
|
|
34
|
+
<el-button type="primary" size="medium" icon="el-icon-upload">
|
|
35
|
+
选择文件
|
|
36
|
+
</el-button>
|
|
37
|
+
</el-upload>
|
|
38
|
+
</div>
|
|
18
39
|
</div>
|
|
19
40
|
|
|
20
|
-
<
|
|
21
|
-
|
|
22
|
-
class="upload-area"
|
|
23
|
-
drag
|
|
24
|
-
:action="uploadUrl"
|
|
25
|
-
:headers="headers"
|
|
26
|
-
:before-upload="beforeUpload"
|
|
27
|
-
:on-success="onUploadSuccess"
|
|
28
|
-
:on-error="onUploadError"
|
|
29
|
-
:on-remove="onFileRemove"
|
|
30
|
-
:file-list="fileList"
|
|
31
|
-
:limit="1"
|
|
32
|
-
accept=".xlsx,.xls"
|
|
33
|
-
>
|
|
34
|
-
<div class="upload-content">
|
|
35
|
-
<i class="el-icon-upload upload-icon"></i>
|
|
36
|
-
<div class="upload-text">
|
|
37
|
-
<div class="upload-main-text">将文件拖到此处,或</div>
|
|
38
|
-
<div class="upload-sub-text">点击上传</div>
|
|
39
|
-
</div>
|
|
40
|
-
<div class="upload-tip">只能上传Excel文件,且不超过10MB</div>
|
|
41
|
-
</div>
|
|
42
|
-
</el-upload>
|
|
43
|
-
|
|
44
|
-
<div v-if="fileInfo" class="file-info-card">
|
|
45
|
-
<el-descriptions :column="2" border size="medium" class="file-info">
|
|
41
|
+
<div v-if="fileInfo" class="file-info">
|
|
42
|
+
<el-descriptions :column="2" border size="small">
|
|
46
43
|
<el-descriptions-item label="文件名">
|
|
47
|
-
<el-tag type="success">{{ fileInfo.fileName }}</el-tag>
|
|
44
|
+
<el-tag type="success" size="small">{{ fileInfo.fileName }}</el-tag>
|
|
48
45
|
</el-descriptions-item>
|
|
49
46
|
<el-descriptions-item label="文件大小">
|
|
50
|
-
<el-tag type="info">{{ formatFileSize(fileInfo.fileSize) }}</el-tag>
|
|
47
|
+
<el-tag type="info" size="small">{{ formatFileSize(fileInfo.fileSize) }}</el-tag>
|
|
51
48
|
</el-descriptions-item>
|
|
52
49
|
<el-descriptions-item label="工作表数量">
|
|
53
|
-
<el-tag>{{ fileInfo.sheets.length }}个</el-tag>
|
|
54
|
-
</el-descriptions-item>
|
|
55
|
-
<el-descriptions-item label="解析时间">
|
|
56
|
-
<el-tag type="warning">{{ fileInfo.parseTime }}</el-tag>
|
|
50
|
+
<el-tag size="small">{{ fileInfo.sheets.length }}个</el-tag>
|
|
57
51
|
</el-descriptions-item>
|
|
58
52
|
</el-descriptions>
|
|
59
53
|
|
|
60
54
|
<div v-if="fileInfo.sheets && fileInfo.sheets.length > 0" class="sheet-select">
|
|
61
|
-
<el-form :model="model" label-width="
|
|
62
|
-
<el-form-item label="选择工作表:"
|
|
55
|
+
<el-form :model="model" label-width="100px" size="small">
|
|
56
|
+
<el-form-item label="选择工作表:">
|
|
63
57
|
<el-select v-model="model.selectedSheet" placeholder="请选择要导入的工作表" style="width: 100%">
|
|
64
58
|
<el-option v-for="sheet in fileInfo.sheets" :key="sheet.index" :label="sheet.name" :value="sheet.index">
|
|
65
59
|
<span style="float: left">{{ sheet.name }}</span>
|
|
66
|
-
<span style="float: right; color: #8492a6; font-size:
|
|
60
|
+
<span style="float: right; color: #8492a6; font-size: 12px">
|
|
67
61
|
{{ sheet.rowCount }}行 × {{ sheet.columnCount }}列
|
|
68
62
|
</span>
|
|
69
63
|
</el-option>
|
|
@@ -72,6 +66,13 @@
|
|
|
72
66
|
</el-form>
|
|
73
67
|
</div>
|
|
74
68
|
</div>
|
|
69
|
+
|
|
70
|
+
<div v-else class="upload-placeholder">
|
|
71
|
+
<div class="placeholder-content">
|
|
72
|
+
<i class="el-icon-document placeholder-icon"></i>
|
|
73
|
+
<div class="placeholder-text">请选择要导入的Excel文件</div>
|
|
74
|
+
</div>
|
|
75
|
+
</div>
|
|
75
76
|
</el-card>
|
|
76
77
|
</div>
|
|
77
78
|
|
|
@@ -79,83 +80,52 @@
|
|
|
79
80
|
<div v-if="currentStep === 2" class="step-content">
|
|
80
81
|
<el-card shadow="never" class="step-card">
|
|
81
82
|
<div slot="header" class="step-header">
|
|
82
|
-
<
|
|
83
|
-
|
|
83
|
+
<div class="header-left">
|
|
84
|
+
<span class="step-title">配置解析选项</span>
|
|
85
|
+
</div>
|
|
84
86
|
</div>
|
|
85
87
|
|
|
86
|
-
<el-form ref="optionsForm" :model="model"
|
|
87
|
-
<el-
|
|
88
|
-
<el-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
style="width: 100%"
|
|
121
|
-
placeholder="表头所在行号(从0开始)"
|
|
122
|
-
/>
|
|
123
|
-
</el-form-item>
|
|
124
|
-
</el-col>
|
|
125
|
-
<el-col :span="12">
|
|
126
|
-
<el-form-item label="最大预览行数:" prop="maxPreviewRows">
|
|
127
|
-
<el-input-number
|
|
128
|
-
v-model="model.maxPreviewRows"
|
|
129
|
-
:min="1"
|
|
130
|
-
:max="1000"
|
|
131
|
-
controls-position="right"
|
|
132
|
-
style="width: 100%"
|
|
133
|
-
placeholder="最多预览多少行数据"
|
|
134
|
-
/>
|
|
135
|
-
</el-form-item>
|
|
136
|
-
</el-col>
|
|
137
|
-
</el-row>
|
|
138
|
-
|
|
139
|
-
<el-row :gutter="30">
|
|
140
|
-
<el-col :span="12">
|
|
141
|
-
<el-form-item label="数据去重:" prop="deduplicate">
|
|
142
|
-
<el-switch
|
|
143
|
-
v-model="model.deduplicate"
|
|
144
|
-
active-text="是"
|
|
145
|
-
inactive-text="否"
|
|
146
|
-
active-color="#13ce66"
|
|
147
|
-
inactive-color="#ff4949"
|
|
148
|
-
/>
|
|
149
|
-
</el-form-item>
|
|
150
|
-
</el-col>
|
|
151
|
-
</el-row>
|
|
88
|
+
<el-form ref="optionsForm" :model="model" label-width="120px" size="small">
|
|
89
|
+
<el-form-item label="是否包含表头:">
|
|
90
|
+
<el-switch v-model="model.hasHeader" />
|
|
91
|
+
</el-form-item>
|
|
92
|
+
|
|
93
|
+
<el-form-item label="跳过空行:">
|
|
94
|
+
<el-switch v-model="model.skipEmptyRows" />
|
|
95
|
+
</el-form-item>
|
|
96
|
+
|
|
97
|
+
<el-form-item label="表头行行号:">
|
|
98
|
+
<el-input-number
|
|
99
|
+
v-model="model.headerRowIndex"
|
|
100
|
+
:min="0"
|
|
101
|
+
:max="100"
|
|
102
|
+
controls-position="right"
|
|
103
|
+
style="width: 100%"
|
|
104
|
+
size="small"
|
|
105
|
+
/>
|
|
106
|
+
</el-form-item>
|
|
107
|
+
|
|
108
|
+
<el-form-item label="最大预览行数:">
|
|
109
|
+
<el-input-number
|
|
110
|
+
v-model="model.maxPreviewRows"
|
|
111
|
+
:min="1"
|
|
112
|
+
:max="1000"
|
|
113
|
+
controls-position="right"
|
|
114
|
+
style="width: 100%"
|
|
115
|
+
size="small"
|
|
116
|
+
/>
|
|
117
|
+
</el-form-item>
|
|
118
|
+
|
|
119
|
+
<el-form-item label="数据去重:">
|
|
120
|
+
<el-switch v-model="model.deduplicate" />
|
|
121
|
+
</el-form-item>
|
|
152
122
|
|
|
153
123
|
<div class="form-actions">
|
|
154
124
|
<el-button type="primary" :loading="parsing" @click="onParse" style="width: 200px;">
|
|
155
|
-
|
|
125
|
+
解析文件
|
|
156
126
|
</el-button>
|
|
157
127
|
<el-button @click="currentStep = 1" style="margin-left: 20px;">
|
|
158
|
-
|
|
128
|
+
返回上一步
|
|
159
129
|
</el-button>
|
|
160
130
|
</div>
|
|
161
131
|
</el-form>
|
|
@@ -166,12 +136,12 @@
|
|
|
166
136
|
<div v-if="currentStep === 3" class="step-content">
|
|
167
137
|
<el-card shadow="never" class="step-card">
|
|
168
138
|
<div slot="header" class="step-header">
|
|
169
|
-
<
|
|
170
|
-
|
|
139
|
+
<div class="header-left">
|
|
140
|
+
<span class="step-title">配置字段映射</span>
|
|
141
|
+
</div>
|
|
171
142
|
</div>
|
|
172
143
|
|
|
173
144
|
<div v-if="parseResult" class="mapping-container">
|
|
174
|
-
<!-- 数据预览 -->
|
|
175
145
|
<div class="preview-section">
|
|
176
146
|
<el-alert
|
|
177
147
|
:title="`已解析 ${parseResult.basicInfo.rowCount} 行数据,${parseResult.basicInfo.columnCount} 列`"
|
|
@@ -185,8 +155,8 @@
|
|
|
185
155
|
:data="parseResult.previewData"
|
|
186
156
|
border
|
|
187
157
|
stripe
|
|
188
|
-
size="
|
|
189
|
-
max-height="
|
|
158
|
+
size="small"
|
|
159
|
+
max-height="200"
|
|
190
160
|
v-loading="parsing"
|
|
191
161
|
class="preview-table"
|
|
192
162
|
>
|
|
@@ -197,33 +167,20 @@
|
|
|
197
167
|
:label="col.label"
|
|
198
168
|
:width="col.width"
|
|
199
169
|
show-overflow-tooltip
|
|
200
|
-
|
|
201
|
-
<template v-slot:header>
|
|
202
|
-
<div class="column-header">
|
|
203
|
-
<div class="column-label">{{ col.label }}</div>
|
|
204
|
-
<div class="column-name">({{ col.name }})</div>
|
|
205
|
-
</div>
|
|
206
|
-
</template>
|
|
207
|
-
</el-table-column>
|
|
170
|
+
/>
|
|
208
171
|
</el-table>
|
|
209
|
-
|
|
210
|
-
<div v-if="parseResult.basicInfo && parseResult.basicInfo.totalRows > parseResult.basicInfo.rowCount" class="preview-tip">
|
|
211
|
-
仅显示前{{ parseResult.basicInfo.rowCount }}行,共{{ parseResult.basicInfo.totalRows }}行数据
|
|
212
|
-
</div>
|
|
213
172
|
</div>
|
|
214
173
|
|
|
215
|
-
<!-- 列映射配置 -->
|
|
216
174
|
<div class="mapping-section">
|
|
217
|
-
<el-table :data="columnMapping" border stripe size="
|
|
218
|
-
<el-table-column prop="excelColumn" label="Excel列" width="
|
|
175
|
+
<el-table :data="columnMapping" border stripe size="small" max-height="200" class="mapping-table">
|
|
176
|
+
<el-table-column prop="excelColumn" label="Excel列" width="150">
|
|
219
177
|
<template v-slot="{ row }">
|
|
220
178
|
<div class="excel-column">
|
|
221
179
|
<div class="column-label">{{ row.excelColumn.label }}</div>
|
|
222
|
-
<div class="column-name">{{ row.excelColumn.name }}</div>
|
|
223
180
|
</div>
|
|
224
181
|
</template>
|
|
225
182
|
</el-table-column>
|
|
226
|
-
<el-table-column prop="targetColumn" label="目标字段" width="
|
|
183
|
+
<el-table-column prop="targetColumn" label="目标字段" width="180">
|
|
227
184
|
<template v-slot="{ row }">
|
|
228
185
|
<el-select
|
|
229
186
|
v-model="row.targetColumn"
|
|
@@ -231,6 +188,7 @@
|
|
|
231
188
|
style="width: 100%"
|
|
232
189
|
clearable
|
|
233
190
|
filterable
|
|
191
|
+
size="small"
|
|
234
192
|
>
|
|
235
193
|
<el-option label="不导入此列" :value="null"></el-option>
|
|
236
194
|
<el-option
|
|
@@ -238,26 +196,13 @@
|
|
|
238
196
|
:key="col.name"
|
|
239
197
|
:label="col.label"
|
|
240
198
|
:value="col.name"
|
|
241
|
-
|
|
242
|
-
<span style="float: left">{{ col.label }}</span>
|
|
243
|
-
<span style="float: right; color: #8492a6; font-size: 13px">{{ col.name }}</span>
|
|
244
|
-
</el-option>
|
|
199
|
+
/>
|
|
245
200
|
</el-select>
|
|
246
201
|
</template>
|
|
247
202
|
</el-table-column>
|
|
248
|
-
<el-table-column prop="required" label="必填" width="
|
|
249
|
-
<template v-slot="{ row }">
|
|
250
|
-
<el-checkbox v-model="row.required" :disabled="!row.targetColumn"></el-checkbox>
|
|
251
|
-
</template>
|
|
252
|
-
</el-table-column>
|
|
253
|
-
<el-table-column prop="defaultValue" label="默认值" min-width="150">
|
|
203
|
+
<el-table-column prop="required" label="必填" width="60" align="center">
|
|
254
204
|
<template v-slot="{ row }">
|
|
255
|
-
<el-
|
|
256
|
-
v-model="row.defaultValue"
|
|
257
|
-
:disabled="!row.targetColumn"
|
|
258
|
-
placeholder="当Excel为空时使用此默认值"
|
|
259
|
-
size="mini"
|
|
260
|
-
/>
|
|
205
|
+
<el-checkbox v-model="row.required" :disabled="!row.targetColumn" size="small" />
|
|
261
206
|
</template>
|
|
262
207
|
</el-table-column>
|
|
263
208
|
</el-table>
|
|
@@ -265,25 +210,26 @@
|
|
|
265
210
|
|
|
266
211
|
<div class="form-actions">
|
|
267
212
|
<el-button type="primary" @click="currentStep = 4" style="width: 200px;">
|
|
268
|
-
|
|
213
|
+
下一步:确认导入
|
|
269
214
|
</el-button>
|
|
270
215
|
<el-button @click="currentStep = 2" style="margin-left: 20px;">
|
|
271
|
-
|
|
216
|
+
返回上一步
|
|
272
217
|
</el-button>
|
|
273
218
|
<el-button type="info" @click="onParse" :loading="parsing" style="margin-left: 20px;">
|
|
274
|
-
|
|
219
|
+
重新解析
|
|
275
220
|
</el-button>
|
|
276
221
|
</div>
|
|
277
222
|
</div>
|
|
278
223
|
</el-card>
|
|
279
224
|
</div>
|
|
280
225
|
|
|
281
|
-
<!-- 步骤4
|
|
226
|
+
<!-- 步骤4:确认导入 -->
|
|
282
227
|
<div v-if="currentStep === 4" class="step-content">
|
|
283
228
|
<el-card shadow="never" class="step-card">
|
|
284
229
|
<div slot="header" class="step-header">
|
|
285
|
-
<
|
|
286
|
-
|
|
230
|
+
<div class="header-left">
|
|
231
|
+
<span class="step-title">确认导入</span>
|
|
232
|
+
</div>
|
|
287
233
|
</div>
|
|
288
234
|
|
|
289
235
|
<div class="import-summary">
|
|
@@ -295,25 +241,18 @@
|
|
|
295
241
|
/>
|
|
296
242
|
|
|
297
243
|
<div class="mapping-summary">
|
|
298
|
-
<el-descriptions :column="2" border size="
|
|
244
|
+
<el-descriptions :column="2" border size="small">
|
|
299
245
|
<el-descriptions-item label="Excel文件">
|
|
300
|
-
<el-tag type="success">{{ fileInfo.fileName }}</el-tag>
|
|
246
|
+
<el-tag type="success" size="small">{{ fileInfo.fileName }}</el-tag>
|
|
301
247
|
</el-descriptions-item>
|
|
302
248
|
<el-descriptions-item label="工作表">
|
|
303
|
-
<el-tag>{{ getSelectedSheetName() }}</el-tag>
|
|
249
|
+
<el-tag size="small">{{ getSelectedSheetName() }}</el-tag>
|
|
304
250
|
</el-descriptions-item>
|
|
305
251
|
<el-descriptions-item label="总行数">
|
|
306
|
-
<el-tag type="info">{{ parseResult.basicInfo.totalRows }}</el-tag>
|
|
307
|
-
</el-descriptions-item>
|
|
308
|
-
<el-descriptions-item label="总列数">
|
|
309
|
-
<el-tag type="info">{{ parseResult.basicInfo.totalColumns }}</el-tag>
|
|
252
|
+
<el-tag type="info" size="small">{{ parseResult.basicInfo.totalRows }}</el-tag>
|
|
310
253
|
</el-descriptions-item>
|
|
311
254
|
<el-descriptions-item label="已配置映射">
|
|
312
|
-
<el-tag type="success">{{ getMappedColumnsCount() }}列</el-tag>
|
|
313
|
-
</el-descriptions-item>
|
|
314
|
-
<el-descriptions-item label="未配置映射">
|
|
315
|
-
<el-tag v-if="getUnmappedColumnsCount() > 0" type="danger">{{ getUnmappedColumnsCount() }}列</el-tag>
|
|
316
|
-
<el-tag v-else type="success">全部已配置</el-tag>
|
|
255
|
+
<el-tag type="success" size="small">{{ getMappedColumnsCount() }}列</el-tag>
|
|
317
256
|
</el-descriptions-item>
|
|
318
257
|
</el-descriptions>
|
|
319
258
|
</div>
|
|
@@ -321,10 +260,10 @@
|
|
|
321
260
|
|
|
322
261
|
<div class="form-actions">
|
|
323
262
|
<el-button type="success" :loading="importing" @click="onImport" style="width: 200px;">
|
|
324
|
-
|
|
263
|
+
开始导入
|
|
325
264
|
</el-button>
|
|
326
265
|
<el-button @click="currentStep = 3" style="margin-left: 20px;">
|
|
327
|
-
|
|
266
|
+
返回上一步
|
|
328
267
|
</el-button>
|
|
329
268
|
</div>
|
|
330
269
|
</el-card>
|
|
@@ -335,13 +274,13 @@
|
|
|
335
274
|
<template v-slot:footer>
|
|
336
275
|
<el-button-group>
|
|
337
276
|
<el-button v-if="currentStep > 1" @click="currentStep--">
|
|
338
|
-
|
|
277
|
+
上一步
|
|
339
278
|
</el-button>
|
|
340
279
|
<el-button v-if="currentStep < 4 && fileInfo" type="primary" @click="currentStep++">
|
|
341
|
-
下一步
|
|
280
|
+
下一步
|
|
342
281
|
</el-button>
|
|
343
282
|
<el-button type="info" @click="hide">
|
|
344
|
-
|
|
283
|
+
取消
|
|
345
284
|
</el-button>
|
|
346
285
|
</el-button-group>
|
|
347
286
|
</template>
|
|
@@ -359,8 +298,8 @@ export default {
|
|
|
359
298
|
return {
|
|
360
299
|
title: '数据导入',
|
|
361
300
|
icon: 'import',
|
|
362
|
-
width: '
|
|
363
|
-
height: '
|
|
301
|
+
width: '900px',
|
|
302
|
+
height: '650px',
|
|
364
303
|
currentStep: 1,
|
|
365
304
|
model: {
|
|
366
305
|
file: null,
|
|
@@ -369,13 +308,7 @@ export default {
|
|
|
369
308
|
skipEmptyRows: false,
|
|
370
309
|
headerRowIndex: 0,
|
|
371
310
|
maxPreviewRows: 100,
|
|
372
|
-
deduplicate: true
|
|
373
|
-
deduplicateFields: []
|
|
374
|
-
},
|
|
375
|
-
rules: {
|
|
376
|
-
selectedSheet: [{ required: true, message: '请选择工作表', trigger: 'change' }],
|
|
377
|
-
headerRowIndex: [{ required: true, message: '请输入表头行行号', trigger: 'blur' }],
|
|
378
|
-
maxPreviewRows: [{ required: true, message: '请输入最大预览行数', trigger: 'blur' }]
|
|
311
|
+
deduplicate: true
|
|
379
312
|
},
|
|
380
313
|
fileList: [],
|
|
381
314
|
fileInfo: null,
|
|
@@ -496,26 +429,11 @@ export default {
|
|
|
496
429
|
const selectedSheet = this.fileInfo.sheets.find(sheet => sheet.index === this.model.selectedSheet)
|
|
497
430
|
const sheetName = selectedSheet ? selectedSheet.name : null
|
|
498
431
|
|
|
499
|
-
// 添加解析参数
|
|
500
|
-
formData.append('hasHeader', this.model.hasHeader)
|
|
501
|
-
formData.append('skipEmptyRows', this.model.skipEmptyRows)
|
|
502
|
-
formData.append('headerRowIndex', this.model.headerRowIndex)
|
|
503
|
-
formData.append('maxPreviewRows', this.model.maxPreviewRows)
|
|
504
|
-
if (sheetName) {
|
|
505
|
-
formData.append('sheetName', sheetName)
|
|
506
|
-
}
|
|
507
|
-
|
|
508
432
|
// 从token模块获取token
|
|
509
433
|
const t = token.get()
|
|
510
434
|
const accessToken = t && t.accessToken ? t.accessToken : ''
|
|
511
|
-
const config = {
|
|
512
|
-
headers: {
|
|
513
|
-
'Content-Type': 'multipart/form-data',
|
|
514
|
-
'Authorization': `Bearer ${accessToken}`
|
|
515
|
-
}
|
|
516
|
-
}
|
|
517
435
|
|
|
518
|
-
// 构建解析URL
|
|
436
|
+
// 构建解析URL - 使用与uploadUrl相同的方式
|
|
519
437
|
let baseURL = this.$http?.axios?.defaults?.baseURL || window.__HAIWEI_API_BASE_URL__ || '/api'
|
|
520
438
|
if (!baseURL.endsWith('/')) {
|
|
521
439
|
baseURL += '/'
|
|
@@ -535,38 +453,47 @@ export default {
|
|
|
535
453
|
|
|
536
454
|
const fullUrl = `${parseUrl}?${queryParams.toString()}`
|
|
537
455
|
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
456
|
+
// 使用fetch API发送请求,避免axios依赖问题
|
|
457
|
+
fetch(fullUrl, {
|
|
458
|
+
method: 'POST',
|
|
459
|
+
body: formData,
|
|
460
|
+
headers: {
|
|
461
|
+
'Authorization': `Bearer ${accessToken}`
|
|
462
|
+
// 注意:不要设置Content-Type,让浏览器自动设置multipart/form-data的boundary
|
|
463
|
+
}
|
|
464
|
+
})
|
|
465
|
+
.then(response => response.json())
|
|
466
|
+
.then(data => {
|
|
467
|
+
if (data && data.code === 1) {
|
|
468
|
+
this.fileInfo = data.data.basicInfo
|
|
469
|
+
this.parseResult = {
|
|
470
|
+
basicInfo: data.data.basicInfo,
|
|
471
|
+
columns: data.data.headers.map((header, index) => ({
|
|
472
|
+
name: `col${index}`,
|
|
473
|
+
label: header.name,
|
|
474
|
+
width: 120
|
|
475
|
+
})),
|
|
476
|
+
previewData: data.data.data.map(row => {
|
|
477
|
+
const obj = {}
|
|
478
|
+
data.data.headers.forEach((header, colIndex) => {
|
|
479
|
+
obj[`col${colIndex}`] = row[colIndex]
|
|
555
480
|
})
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
this.currentStep = 3
|
|
559
|
-
this._success('文件解析成功')
|
|
560
|
-
} else {
|
|
561
|
-
this._error(response.data.msg || '文件解析失败')
|
|
481
|
+
return obj
|
|
482
|
+
})
|
|
562
483
|
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
this.
|
|
566
|
-
}
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
484
|
+
this.initColumnMapping()
|
|
485
|
+
this.currentStep = 3
|
|
486
|
+
this._success('文件解析成功')
|
|
487
|
+
} else {
|
|
488
|
+
this._error(data.msg || '文件解析失败')
|
|
489
|
+
}
|
|
490
|
+
})
|
|
491
|
+
.catch(error => {
|
|
492
|
+
this._error('文件解析失败:' + (error.message || '未知错误'))
|
|
493
|
+
})
|
|
494
|
+
.finally(() => {
|
|
495
|
+
this.parsing = false
|
|
496
|
+
})
|
|
570
497
|
},
|
|
571
498
|
|
|
572
499
|
initColumnMapping() {
|
|
@@ -575,8 +502,7 @@ export default {
|
|
|
575
502
|
this.columnMapping = this.parseResult.columns.map(col => ({
|
|
576
503
|
excelColumn: col,
|
|
577
504
|
targetColumn: this.findBestMatch(col),
|
|
578
|
-
required: false
|
|
579
|
-
defaultValue: ''
|
|
505
|
+
required: false
|
|
580
506
|
}))
|
|
581
507
|
},
|
|
582
508
|
|
|
@@ -584,17 +510,12 @@ export default {
|
|
|
584
510
|
if (!this.targetColumns || this.targetColumns.length === 0) return null
|
|
585
511
|
|
|
586
512
|
const excelLabel = excelColumn.label.toLowerCase()
|
|
587
|
-
const excelName = excelColumn.name.toLowerCase()
|
|
588
513
|
|
|
589
|
-
//
|
|
514
|
+
// 尝试完全匹配标签
|
|
590
515
|
let match = this.targetColumns.find(col => col.label.toLowerCase() === excelLabel)
|
|
591
516
|
if (match) return match.name
|
|
592
517
|
|
|
593
|
-
//
|
|
594
|
-
match = this.targetColumns.find(col => col.name.toLowerCase() === excelName)
|
|
595
|
-
if (match) return match.name
|
|
596
|
-
|
|
597
|
-
// 3. 尝试包含匹配
|
|
518
|
+
// 尝试包含匹配
|
|
598
519
|
match = this.targetColumns.find(col =>
|
|
599
520
|
excelLabel.includes(col.label.toLowerCase()) ||
|
|
600
521
|
col.label.toLowerCase().includes(excelLabel)
|
|
@@ -674,11 +595,6 @@ export default {
|
|
|
674
595
|
const targetColumn = mapping.targetColumn
|
|
675
596
|
let value = row[excelColumnName]
|
|
676
597
|
|
|
677
|
-
// 如果值为空且设置了默认值,使用默认值
|
|
678
|
-
if ((value === null || value === undefined || value === '') && mapping.defaultValue) {
|
|
679
|
-
value = mapping.defaultValue
|
|
680
|
-
}
|
|
681
|
-
|
|
682
598
|
// 设置到AddModel中
|
|
683
599
|
addModel[targetColumn] = value
|
|
684
600
|
})
|
|
@@ -709,8 +625,7 @@ export default {
|
|
|
709
625
|
return addModels
|
|
710
626
|
}
|
|
711
627
|
|
|
712
|
-
//
|
|
713
|
-
// 在实际应用中,可能需要更复杂的去重逻辑,比如多个字段组合
|
|
628
|
+
// 使用第一个目标字段作为去重依据
|
|
714
629
|
const deduplicateField = targetFields[0]
|
|
715
630
|
|
|
716
631
|
// 使用Map进行去重,保留第一次出现的数据
|
|
@@ -730,9 +645,6 @@ export default {
|
|
|
730
645
|
if (!uniqueMap.has(key)) {
|
|
731
646
|
uniqueMap.set(key, true)
|
|
732
647
|
deduplicatedModels.push(model)
|
|
733
|
-
} else {
|
|
734
|
-
// 重复数据,记录日志(可选)
|
|
735
|
-
console.log(`发现重复数据,字段 ${deduplicateField} = ${key},已跳过`)
|
|
736
648
|
}
|
|
737
649
|
}
|
|
738
650
|
|
|
@@ -765,11 +677,6 @@ export default {
|
|
|
765
677
|
getMappedColumnsCount() {
|
|
766
678
|
if (!this.columnMapping) return 0
|
|
767
679
|
return this.columnMapping.filter(mapping => mapping.targetColumn).length
|
|
768
|
-
},
|
|
769
|
-
|
|
770
|
-
getUnmappedColumnsCount() {
|
|
771
|
-
if (!this.columnMapping) return 0
|
|
772
|
-
return this.columnMapping.filter(mapping => !mapping.targetColumn).length
|
|
773
680
|
}
|
|
774
681
|
},
|
|
775
682
|
created() {
|
|
@@ -781,126 +688,100 @@ export default {
|
|
|
781
688
|
<style lang="scss" scoped>
|
|
782
689
|
.import-container {
|
|
783
690
|
.import-steps {
|
|
784
|
-
margin-bottom:
|
|
691
|
+
margin-bottom: 20px;
|
|
785
692
|
}
|
|
786
693
|
|
|
787
694
|
.step-content {
|
|
788
695
|
.step-card {
|
|
789
|
-
border: 1px solid #
|
|
790
|
-
border-radius:
|
|
696
|
+
border: 1px solid #dcdfe6;
|
|
697
|
+
border-radius: 4px;
|
|
791
698
|
|
|
792
699
|
.step-header {
|
|
793
700
|
display: flex;
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
701
|
+
justify-content: space-between;
|
|
702
|
+
align-items: center;
|
|
703
|
+
padding: 12px 16px;
|
|
704
|
+
border-bottom: 1px solid #dcdfe6;
|
|
705
|
+
background-color: #f5f7fa;
|
|
797
706
|
|
|
798
|
-
.
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
707
|
+
.header-left {
|
|
708
|
+
display: flex;
|
|
709
|
+
flex-direction: column;
|
|
710
|
+
|
|
711
|
+
.step-title {
|
|
712
|
+
font-size: 14px;
|
|
713
|
+
font-weight: 600;
|
|
714
|
+
color: #303133;
|
|
715
|
+
margin-bottom: 4px;
|
|
716
|
+
}
|
|
717
|
+
|
|
718
|
+
.step-subtitle {
|
|
719
|
+
font-size: 12px;
|
|
720
|
+
color: #909399;
|
|
721
|
+
}
|
|
803
722
|
}
|
|
804
723
|
|
|
805
|
-
.
|
|
806
|
-
|
|
807
|
-
|
|
724
|
+
.header-right {
|
|
725
|
+
display: flex;
|
|
726
|
+
align-items: center;
|
|
808
727
|
}
|
|
809
728
|
}
|
|
810
729
|
}
|
|
811
730
|
}
|
|
812
731
|
|
|
813
|
-
.upload-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
732
|
+
.upload-placeholder {
|
|
733
|
+
display: flex;
|
|
734
|
+
flex-direction: column;
|
|
735
|
+
align-items: center;
|
|
736
|
+
justify-content: center;
|
|
737
|
+
padding: 40px 16px;
|
|
738
|
+
text-align: center;
|
|
739
|
+
|
|
740
|
+
.placeholder-content {
|
|
741
|
+
.placeholder-icon {
|
|
742
|
+
font-size: 36px;
|
|
823
743
|
color: #c0c4cc;
|
|
824
|
-
margin-bottom:
|
|
744
|
+
margin-bottom: 16px;
|
|
825
745
|
}
|
|
826
746
|
|
|
827
|
-
.
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
font-size: 14px;
|
|
833
|
-
color: #606266;
|
|
834
|
-
margin-bottom: 5px;
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
.upload-sub-text {
|
|
838
|
-
font-size: 16px;
|
|
839
|
-
color: #409eff;
|
|
840
|
-
font-weight: 500;
|
|
841
|
-
}
|
|
842
|
-
}
|
|
843
|
-
|
|
844
|
-
.upload-tip {
|
|
845
|
-
font-size: 12px;
|
|
846
|
-
color: #909399;
|
|
747
|
+
.placeholder-text {
|
|
748
|
+
font-size: 14px;
|
|
749
|
+
color: #606266;
|
|
750
|
+
margin-bottom: 8px;
|
|
751
|
+
font-weight: 500;
|
|
847
752
|
}
|
|
848
753
|
}
|
|
849
754
|
}
|
|
850
755
|
|
|
851
|
-
.file-info
|
|
852
|
-
margin-top:
|
|
853
|
-
padding:
|
|
756
|
+
.file-info {
|
|
757
|
+
margin-top: 16px;
|
|
758
|
+
padding: 16px;
|
|
854
759
|
background-color: #f8f9fa;
|
|
855
|
-
border-radius:
|
|
856
|
-
|
|
857
|
-
.file-info {
|
|
858
|
-
margin-bottom: 20px;
|
|
859
|
-
}
|
|
760
|
+
border-radius: 4px;
|
|
860
761
|
|
|
861
762
|
.sheet-select {
|
|
862
|
-
margin-top:
|
|
763
|
+
margin-top: 16px;
|
|
863
764
|
}
|
|
864
765
|
}
|
|
865
766
|
|
|
866
767
|
.form-actions {
|
|
867
768
|
display: flex;
|
|
868
769
|
justify-content: center;
|
|
869
|
-
margin-top:
|
|
870
|
-
padding-top:
|
|
871
|
-
border-top: 1px solid #
|
|
770
|
+
margin-top: 24px;
|
|
771
|
+
padding-top: 16px;
|
|
772
|
+
border-top: 1px solid #dcdfe6;
|
|
872
773
|
}
|
|
873
774
|
|
|
874
775
|
.mapping-container {
|
|
875
776
|
.preview-section {
|
|
876
|
-
margin-bottom:
|
|
777
|
+
margin-bottom: 24px;
|
|
877
778
|
|
|
878
779
|
.preview-alert {
|
|
879
|
-
margin-bottom:
|
|
780
|
+
margin-bottom: 12px;
|
|
880
781
|
}
|
|
881
782
|
|
|
882
783
|
.preview-table {
|
|
883
|
-
margin-bottom:
|
|
884
|
-
|
|
885
|
-
.column-header {
|
|
886
|
-
text-align: center;
|
|
887
|
-
|
|
888
|
-
.column-label {
|
|
889
|
-
font-weight: 500;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
.column-name {
|
|
893
|
-
font-size: 11px;
|
|
894
|
-
color: #909399;
|
|
895
|
-
}
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
|
|
899
|
-
.preview-tip {
|
|
900
|
-
text-align: center;
|
|
901
|
-
font-size: 12px;
|
|
902
|
-
color: #909399;
|
|
903
|
-
margin-top: 10px;
|
|
784
|
+
margin-bottom: 8px;
|
|
904
785
|
}
|
|
905
786
|
}
|
|
906
787
|
|
|
@@ -909,12 +790,6 @@ export default {
|
|
|
909
790
|
.excel-column {
|
|
910
791
|
.column-label {
|
|
911
792
|
font-weight: 500;
|
|
912
|
-
margin-bottom: 3px;
|
|
913
|
-
}
|
|
914
|
-
|
|
915
|
-
.column-name {
|
|
916
|
-
font-size: 11px;
|
|
917
|
-
color: #909399;
|
|
918
793
|
}
|
|
919
794
|
}
|
|
920
795
|
}
|
|
@@ -922,7 +797,7 @@ export default {
|
|
|
922
797
|
}
|
|
923
798
|
|
|
924
799
|
.import-summary {
|
|
925
|
-
margin:
|
|
800
|
+
margin: 16px 0;
|
|
926
801
|
}
|
|
927
802
|
}
|
|
928
803
|
</style>
|