sohelp-eleplus 1.1.19 → 1.1.21
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/components.js +2 -0
- package/http/SohelpHttp.js +14 -5
- package/package.json +1 -1
- package/sohelp-card/index.vue +13 -0
- package/sohelp-dict/index.vue +25 -1
- package/sohelp-dyn-select/props.js +4 -4
- package/sohelp-entity-form/index.vue +31 -6
- package/sohelp-grid/components/filter-condition-item.vue +9 -3
- package/sohelp-grid/index.vue +348 -156
- package/sohelp-grid/js/DefaultGridOptions.js +25 -16
- package/sohelp-grid/js/useSohelpGridConfig.js +180 -91
- package/sohelp-grid-view/filter/config/grid-filter-condition.vue +8 -37
- package/sohelp-grid-view/filter/config/index.vue +6 -9
- package/sohelp-grid-view/filter/filter-form.vue +21 -17
- package/sohelp-grid-view/filter/index.vue +5 -2
- package/sohelp-grid-view/index.vue +22 -9
- package/sohelp-import/index.vue +470 -0
- package/sohelp-modal/index.vue +7 -2
- package/sohelp-page/index.vue +13 -0
- package/sohelp-pro-form/components/pro-form-item.vue +1 -0
- package/sohelp-rich-text/index.vue +3 -21
- package/sohelp-vxe-grid/DefaultGridOptions.js +51 -26
- package/sohelp-vxe-grid/index.vue +383 -375
- package/sohelp-workflow/nodes/approver.vue +0 -1
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<ele-modal form :model-value="modelValue" title="数据导入" :width="860" @update:modelValue="updateModelValue">
|
|
3
|
+
<div class="import-wrap" v-loading="loading">
|
|
4
|
+
<div class="import-header">
|
|
5
|
+
<div class="tips">
|
|
6
|
+
<ele-text>支持文件格式:xls、xlsx;大小 ≤ 50MB;最多 10,000 行</ele-text>
|
|
7
|
+
</div>
|
|
8
|
+
<div class="actions">
|
|
9
|
+
<el-button plain :loading="downloadingTemplate" @click="downloadTemplate">下载导入模板</el-button>
|
|
10
|
+
<el-button plain @click="openConfig" v-if="false">配置导入列表</el-button>
|
|
11
|
+
<el-link :underline="false" @click="viewHistory">查看导入历史</el-link>
|
|
12
|
+
</div>
|
|
13
|
+
</div>
|
|
14
|
+
<div class="upload-area" @drop.prevent="onDrop" @dragover.prevent>
|
|
15
|
+
<el-upload
|
|
16
|
+
drag
|
|
17
|
+
:auto-upload="false"
|
|
18
|
+
:show-file-list="false"
|
|
19
|
+
:on-change="onFileChange"
|
|
20
|
+
:on-remove="onRemove"
|
|
21
|
+
:limit="1"
|
|
22
|
+
accept=".xls,.xlsx"
|
|
23
|
+
class="uploader"
|
|
24
|
+
>
|
|
25
|
+
<i class="el-icon"><UploadFilled /></i>
|
|
26
|
+
<div class="el-upload__text">拖拽文件到此处或点击上传</div>
|
|
27
|
+
<div class="el-upload__tip">仅限 .xls/.xlsx,大小不超过 50MB</div>
|
|
28
|
+
</el-upload>
|
|
29
|
+
<div class="status" v-if="fileName">
|
|
30
|
+
<ele-text>已选择:{{ fileName }}({{ formatSize(fileSize) }})</ele-text>
|
|
31
|
+
<el-progress :percentage="progress" :status="progressStatus" />
|
|
32
|
+
<ele-text v-if="rowCount">总共发现 {{ rowCount }} 条数据</ele-text>
|
|
33
|
+
</div>
|
|
34
|
+
</div>
|
|
35
|
+
<div class="result-actions" v-if="rowCount">
|
|
36
|
+
<el-button type="primary" plain @click="togglePreview">{{ previewVisible ? '隐藏预览数据' : '查看预览数据' }}</el-button>
|
|
37
|
+
<el-button plain @click="downloadProcessed">下载导入文件</el-button>
|
|
38
|
+
</div>
|
|
39
|
+
<ele-card v-if="previewVisible" style="margin-top: 12px" header="预览前 50 条">
|
|
40
|
+
<vxe-grid :columns="previewColumns" :data="previewData" size="mini" border />
|
|
41
|
+
</ele-card>
|
|
42
|
+
</div>
|
|
43
|
+
<template #footer>
|
|
44
|
+
<el-button @click="onClose">取消</el-button>
|
|
45
|
+
<el-button type="primary" :disabled="!canImport" @click="commitImport">确认导入</el-button>
|
|
46
|
+
</template>
|
|
47
|
+
</ele-modal>
|
|
48
|
+
<ele-modal :model-value="configVisible" title="配置导入列表" :width="640" @update:modelValue="updateConfigVisible">
|
|
49
|
+
<div class="config-wrap">
|
|
50
|
+
<ele-card header="可导入字段白名单">
|
|
51
|
+
<el-checkbox-group v-model="whitelist">
|
|
52
|
+
<el-checkbox v-for="f in fields" :key="f.name" :label="f.name">{{ f.label || f.name }}</el-checkbox>
|
|
53
|
+
</el-checkbox-group>
|
|
54
|
+
</ele-card>
|
|
55
|
+
<ele-card header="字段映射关系" style="margin-top: 12px">
|
|
56
|
+
<div class="mapping">
|
|
57
|
+
<div class="row" v-for="f in whitelist" :key="f">
|
|
58
|
+
<span class="label">{{ getFieldLabel(f) }}</span>
|
|
59
|
+
<el-input v-model="mapping[f]" placeholder="Excel列名" style="width: 240px" />
|
|
60
|
+
</div>
|
|
61
|
+
</div>
|
|
62
|
+
</ele-card>
|
|
63
|
+
</div>
|
|
64
|
+
<template #footer>
|
|
65
|
+
<el-button @click="updateConfigVisible(false)">取消</el-button>
|
|
66
|
+
<el-button type="primary" @click="saveConfig">保存</el-button>
|
|
67
|
+
</template>
|
|
68
|
+
</ele-modal>
|
|
69
|
+
<ele-modal
|
|
70
|
+
:model-value="historyVisible"
|
|
71
|
+
title="导入历史"
|
|
72
|
+
:width="800"
|
|
73
|
+
@update:modelValue="(v) => (historyVisible = v)"
|
|
74
|
+
>
|
|
75
|
+
<div class="history-wrap">
|
|
76
|
+
<vxe-grid :columns="historyColumns" :data="historyList" size="mini" border>
|
|
77
|
+
<template #op="{ row }">
|
|
78
|
+
<el-space :size="6">
|
|
79
|
+
<el-button size="small" plain @click="downloadHistoryFile(row)">下载文件</el-button>
|
|
80
|
+
<el-button size="small" plain @click="restoreHistoryFile(row)">恢复导入</el-button>
|
|
81
|
+
<el-button size="small" plain type="danger" @click="deleteHistoryFile(row)">删除文件</el-button>
|
|
82
|
+
</el-space>
|
|
83
|
+
</template>
|
|
84
|
+
</vxe-grid>
|
|
85
|
+
</div>
|
|
86
|
+
<template #footer>
|
|
87
|
+
<el-button @click="historyVisible = false">关闭</el-button>
|
|
88
|
+
</template>
|
|
89
|
+
</ele-modal>
|
|
90
|
+
</template>
|
|
91
|
+
<script setup>
|
|
92
|
+
import { ref, computed } from 'vue';
|
|
93
|
+
import { ElMessage } from 'element-plus/es';
|
|
94
|
+
import { EleMessage } from '@/components/ele-admin-plus/components';
|
|
95
|
+
import { UploadFilled } from '@element-plus/icons-vue';
|
|
96
|
+
import SohelpHttp from '../http/SohelpHttp.js';
|
|
97
|
+
|
|
98
|
+
const MAX_ROWS = 10000;
|
|
99
|
+
|
|
100
|
+
const props = defineProps({
|
|
101
|
+
modelValue: Boolean,
|
|
102
|
+
refid: {
|
|
103
|
+
type: String,
|
|
104
|
+
required: true
|
|
105
|
+
},
|
|
106
|
+
fields: {
|
|
107
|
+
type: Array,
|
|
108
|
+
default: () => []
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
const emit = defineEmits(['update:modelValue', 'close']);
|
|
112
|
+
|
|
113
|
+
const loading = ref(false);
|
|
114
|
+
const fileName = ref('');
|
|
115
|
+
const fileSize = ref(0);
|
|
116
|
+
const progress = ref(0);
|
|
117
|
+
const progressStatus = ref('success');
|
|
118
|
+
const rowCount = ref(0);
|
|
119
|
+
const fileId = ref('');
|
|
120
|
+
const previewVisible = ref(false);
|
|
121
|
+
const previewData = ref([]);
|
|
122
|
+
const previewColumns = ref([]);
|
|
123
|
+
|
|
124
|
+
const configVisible = ref(false);
|
|
125
|
+
const whitelist = ref([]);
|
|
126
|
+
const mapping = ref({});
|
|
127
|
+
const historyVisible = ref(false);
|
|
128
|
+
const historyList = ref([]);
|
|
129
|
+
const downloadingTemplate = ref(false);
|
|
130
|
+
const historyColumns = ref([
|
|
131
|
+
{ field: 'file', title: '文件名', align: 'center', minWidth: 160 },
|
|
132
|
+
{ field: 'size', title: '大小', align: 'center', minWidth: 100 },
|
|
133
|
+
{ field: 'rows', title: '记录数', align: 'center', minWidth: 90 },
|
|
134
|
+
{ field: 'upload_user', title: '导入人员', align: 'center', minWidth: 120 },
|
|
135
|
+
{ field: 'update_time', title: '导入时间', align: 'center', minWidth: 180 },
|
|
136
|
+
{ field: 'op', title: '操作', align: 'center', minWidth: 220, slots: { default: 'op' } }
|
|
137
|
+
]);
|
|
138
|
+
|
|
139
|
+
const canImport = computed(() => !!fileId.value && rowCount.value > 0 && rowCount.value <= MAX_ROWS);
|
|
140
|
+
|
|
141
|
+
const updateModelValue = (val) => {
|
|
142
|
+
emit('update:modelValue', val);
|
|
143
|
+
if (!val) {
|
|
144
|
+
onRemove();
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
const onClose = () => {
|
|
149
|
+
emit('update:modelValue', false);
|
|
150
|
+
emit('close');
|
|
151
|
+
onRemove();
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const formatSize = (n) => {
|
|
155
|
+
if (n < 1024) return n + 'B';
|
|
156
|
+
if (n < 1024 * 1024) return (n / 1024).toFixed(1) + 'KB';
|
|
157
|
+
return (n / 1024 / 1024).toFixed(1) + 'MB';
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
const openConfig = () => {
|
|
161
|
+
if (!whitelist.value.length) {
|
|
162
|
+
whitelist.value = props.fields.map((f) => f.name);
|
|
163
|
+
}
|
|
164
|
+
if (Object.keys(mapping.value).length === 0) {
|
|
165
|
+
props.fields.forEach((f) => (mapping.value[f.name] = f.label || f.name));
|
|
166
|
+
}
|
|
167
|
+
configVisible.value = true;
|
|
168
|
+
};
|
|
169
|
+
const updateConfigVisible = (v) => (configVisible.value = v);
|
|
170
|
+
const saveConfig = () => {
|
|
171
|
+
configVisible.value = false;
|
|
172
|
+
};
|
|
173
|
+
const getFieldLabel = (name) => props.fields.find((f) => f.name === name)?.label || name;
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* 下载导入模板
|
|
177
|
+
* - 绑定click事件监听器到 button 元素
|
|
178
|
+
* - 在事件处理函数中动态获取当前列表的refid值
|
|
179
|
+
* - 构造完整下载URL:/engine/web/import/downloadTemplate?refid=${动态获取的refid}
|
|
180
|
+
* - 使用SohelpHttp.download方法发起请求,参数格式为:{refid: '动态refid值'}
|
|
181
|
+
* - 错误处理:捕获refid无效、网络错误、服务器错误,并实现重试
|
|
182
|
+
* - 用户体验:显示loading,下载进度
|
|
183
|
+
*/
|
|
184
|
+
const downloadTemplate = async () => {
|
|
185
|
+
// 验证 refid 参数
|
|
186
|
+
if (!props.refid) {
|
|
187
|
+
EleMessage.error('请选择有效的模板');
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
downloadingTemplate.value = true;
|
|
192
|
+
const maxRetries = 3;
|
|
193
|
+
let retryCount = 0;
|
|
194
|
+
let lastError = null;
|
|
195
|
+
|
|
196
|
+
// 错误重试机制(最多3次)
|
|
197
|
+
while (retryCount <= maxRetries) {
|
|
198
|
+
try {
|
|
199
|
+
await SohelpHttp.download(
|
|
200
|
+
'/engine/web/import/downloadTemplate',
|
|
201
|
+
{ refid: props.refid },
|
|
202
|
+
'导入模板.xlsx',
|
|
203
|
+
(loaded, total) => {
|
|
204
|
+
// 大文件下载时显示进度条
|
|
205
|
+
// 这里可以对接UI的进度条,目前简单处理,如果需要更复杂的UI交互可以扩展
|
|
206
|
+
if (total > 0) {
|
|
207
|
+
const percent = Math.floor((loaded / total) * 100);
|
|
208
|
+
// console.log(`下载进度: ${percent}%`);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
{ timeout: 30000 } // 实现请求超时机制,默认设置为30秒
|
|
212
|
+
);
|
|
213
|
+
EleMessage.success('模板下载成功');
|
|
214
|
+
downloadingTemplate.value = false;
|
|
215
|
+
return;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
lastError = error;
|
|
218
|
+
retryCount++;
|
|
219
|
+
if (retryCount <= maxRetries) {
|
|
220
|
+
// 简单的指数退避或固定等待
|
|
221
|
+
await new Promise((resolve) => setTimeout(resolve, 1000));
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
downloadingTemplate.value = false;
|
|
227
|
+
// 错误处理机制
|
|
228
|
+
const errorMsg = String(lastError);
|
|
229
|
+
if (errorMsg.includes('timeout') || errorMsg.includes('Network Error')) {
|
|
230
|
+
EleMessage.error('网络连接异常,请稍后重试');
|
|
231
|
+
} else {
|
|
232
|
+
EleMessage.error(errorMsg || '下载失败');
|
|
233
|
+
}
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
const onDrop = async (e) => {
|
|
237
|
+
const file = e.dataTransfer.files?.[0];
|
|
238
|
+
if (file) await handleFile(file);
|
|
239
|
+
};
|
|
240
|
+
const onFileChange = async (file) => {
|
|
241
|
+
const f = file?.raw || file;
|
|
242
|
+
if (f) await handleFile(f);
|
|
243
|
+
};
|
|
244
|
+
const onRemove = () => {
|
|
245
|
+
fileName.value = '';
|
|
246
|
+
fileSize.value = 0;
|
|
247
|
+
progress.value = 0;
|
|
248
|
+
rowCount.value = 0;
|
|
249
|
+
previewVisible.value = false;
|
|
250
|
+
fileId.value = '';
|
|
251
|
+
previewData.value = [];
|
|
252
|
+
previewColumns.value = [];
|
|
253
|
+
progressStatus.value = 'success';
|
|
254
|
+
loading.value = false;
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const handleFile = async (file) => {
|
|
258
|
+
const ext = file.name.toLowerCase().split('.').pop();
|
|
259
|
+
if (!['xls', 'xlsx'].includes(ext)) {
|
|
260
|
+
ElMessage.error('仅支持 .xls 或 .xlsx 文件');
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
if (file.size > 50 * 1024 * 1024) {
|
|
264
|
+
ElMessage.error('文件大小超过 50MB 限制');
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
fileName.value = file.name;
|
|
268
|
+
fileSize.value = file.size;
|
|
269
|
+
progress.value = 10;
|
|
270
|
+
const form = new FormData();
|
|
271
|
+
form.append('refid', props.refid);
|
|
272
|
+
form.append('file', file);
|
|
273
|
+
try {
|
|
274
|
+
const res = await SohelpHttp.post('/engine/web/import/upload', form);
|
|
275
|
+
if (!res.meta.success) {
|
|
276
|
+
progressStatus.value = 'exception';
|
|
277
|
+
ElMessage.error(res.meta.message || '上传失败');
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
fileId.value = res.data?.fileId || '';
|
|
281
|
+
rowCount.value = res.data?.rows || 0;
|
|
282
|
+
if (rowCount.value > MAX_ROWS) {
|
|
283
|
+
progressStatus.value = 'exception';
|
|
284
|
+
ElMessage.error('超过最大行数限制(10,000)');
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
progress.value = 60;
|
|
288
|
+
await loadPreview();
|
|
289
|
+
progress.value = 100;
|
|
290
|
+
progressStatus.value = 'success';
|
|
291
|
+
// 默认不显示预览数据
|
|
292
|
+
previewVisible.value = false;
|
|
293
|
+
} catch (e) {
|
|
294
|
+
progressStatus.value = 'exception';
|
|
295
|
+
ElMessage.error(String(e));
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const togglePreview = () => {
|
|
300
|
+
previewVisible.value = !previewVisible.value;
|
|
301
|
+
};
|
|
302
|
+
const downloadProcessed = () => {
|
|
303
|
+
if (!fileId.value) {
|
|
304
|
+
ElMessage.error('请先上传导入文件');
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
SohelpHttp.download(
|
|
308
|
+
'/engine/web/import/download',
|
|
309
|
+
{ fileId: fileId.value },
|
|
310
|
+
fileName.value || '导入数据.xlsx'
|
|
311
|
+
).catch((e) => ElMessage.error(String(e)));
|
|
312
|
+
};
|
|
313
|
+
const viewHistory = async () => {
|
|
314
|
+
try {
|
|
315
|
+
const res = await SohelpHttp.get('/engine/web/import/history', { refid: props.refid });
|
|
316
|
+
if (res.meta.success) {
|
|
317
|
+
const list = res.data?.results || [];
|
|
318
|
+
historyList.value = list;
|
|
319
|
+
historyVisible.value = true;
|
|
320
|
+
} else {
|
|
321
|
+
ElMessage.error(res.meta.message || '获取历史失败');
|
|
322
|
+
}
|
|
323
|
+
} catch (e) {
|
|
324
|
+
ElMessage.error(String(e));
|
|
325
|
+
}
|
|
326
|
+
};
|
|
327
|
+
const commitImport = async () => {
|
|
328
|
+
if (!canImport.value) {
|
|
329
|
+
ElMessage.error('请上传有效的导入文件');
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const loadingTip = EleMessage.loading('导入中..');
|
|
333
|
+
try {
|
|
334
|
+
loading.value = true;
|
|
335
|
+
const res = await SohelpHttp.post('/engine/web/import/confirm', { fileId: fileId.value, refid: props.refid });
|
|
336
|
+
if (res.meta.success) {
|
|
337
|
+
ElMessage.success(res.meta.message || '导入完成');
|
|
338
|
+
emit('update:modelValue', false);
|
|
339
|
+
} else {
|
|
340
|
+
ElMessage.error(res.meta.message || '导入失败');
|
|
341
|
+
}
|
|
342
|
+
} catch (e) {
|
|
343
|
+
ElMessage.error(String(e));
|
|
344
|
+
} finally {
|
|
345
|
+
loadingTip.close();
|
|
346
|
+
loading.value = false;
|
|
347
|
+
}
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
const loadPreview = async () => {
|
|
351
|
+
try {
|
|
352
|
+
const res = await SohelpHttp.get('/engine/web/import/preview', { refid: props.refid, fileId: fileId.value });
|
|
353
|
+
if (res.meta.success) {
|
|
354
|
+
const rows = Array.isArray(res.data.results) ? res.data.results : [];
|
|
355
|
+
previewData.value = rows.slice(0, 50);
|
|
356
|
+
const keys = rows.length
|
|
357
|
+
? Object.keys(rows[0])
|
|
358
|
+
: whitelist.value.length
|
|
359
|
+
? whitelist.value
|
|
360
|
+
: props.fields.map((f) => f.name);
|
|
361
|
+
previewColumns.value = keys.map((k) => ({
|
|
362
|
+
field: k,
|
|
363
|
+
title: getFieldLabel(k),
|
|
364
|
+
align: 'center',
|
|
365
|
+
minWidth: 120
|
|
366
|
+
}));
|
|
367
|
+
// previewVisible.value = true; // 加载数据后不自动显示
|
|
368
|
+
} else {
|
|
369
|
+
ElMessage.error(res.meta.message || '预览失败');
|
|
370
|
+
}
|
|
371
|
+
} catch (e) {
|
|
372
|
+
ElMessage.error(String(e));
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
const downloadHistoryFile = (row) => {
|
|
377
|
+
SohelpHttp.download('/engine/web/import/download', { fileId: row.file_uuid }, row.file).catch((e) =>
|
|
378
|
+
ElMessage.error(String(e))
|
|
379
|
+
);
|
|
380
|
+
};
|
|
381
|
+
const restoreHistoryFile = async (row) => {
|
|
382
|
+
try {
|
|
383
|
+
const res = await SohelpHttp.post('/engine/web/import/restore', { fileId: row.file_uuid, refid: props.refid });
|
|
384
|
+
if (res.meta.success) {
|
|
385
|
+
ElMessage.success(res.meta.message || '恢复成功');
|
|
386
|
+
} else {
|
|
387
|
+
ElMessage.error(res.meta.message || '恢复失败');
|
|
388
|
+
}
|
|
389
|
+
} catch (e) {
|
|
390
|
+
ElMessage.error(String(e));
|
|
391
|
+
}
|
|
392
|
+
};
|
|
393
|
+
const deleteHistoryFile = async (row) => {
|
|
394
|
+
try {
|
|
395
|
+
const res = await SohelpHttp.post('/engine/web/import/delete', { fileId: row.file_uuid, refid: props.refid });
|
|
396
|
+
if (res.meta.success) {
|
|
397
|
+
ElMessage.success(res.meta.message || '删除成功');
|
|
398
|
+
await viewHistory();
|
|
399
|
+
} else {
|
|
400
|
+
ElMessage.error(res.meta.message || '删除失败');
|
|
401
|
+
}
|
|
402
|
+
} catch (e) {
|
|
403
|
+
ElMessage.error(String(e));
|
|
404
|
+
}
|
|
405
|
+
};
|
|
406
|
+
</script>
|
|
407
|
+
<style scoped lang="scss">
|
|
408
|
+
.import-wrap {
|
|
409
|
+
display: flex;
|
|
410
|
+
flex-direction: column;
|
|
411
|
+
gap: 12px;
|
|
412
|
+
}
|
|
413
|
+
.import-header {
|
|
414
|
+
display: flex;
|
|
415
|
+
align-items: center;
|
|
416
|
+
justify-content: space-between;
|
|
417
|
+
}
|
|
418
|
+
.actions {
|
|
419
|
+
display: flex;
|
|
420
|
+
gap: 10px;
|
|
421
|
+
}
|
|
422
|
+
.actions :deep(.el-button:hover) {
|
|
423
|
+
opacity: 0.9;
|
|
424
|
+
}
|
|
425
|
+
.upload-area {
|
|
426
|
+
min-width: 300px;
|
|
427
|
+
min-height: 200px;
|
|
428
|
+
border: 1px dashed #bbb;
|
|
429
|
+
display: flex;
|
|
430
|
+
flex-direction: column;
|
|
431
|
+
align-items: center;
|
|
432
|
+
justify-content: center;
|
|
433
|
+
padding: 20px;
|
|
434
|
+
border-radius: 6px;
|
|
435
|
+
}
|
|
436
|
+
.uploader {
|
|
437
|
+
width: 100%;
|
|
438
|
+
}
|
|
439
|
+
.status {
|
|
440
|
+
width: 100%;
|
|
441
|
+
margin-top: 10px;
|
|
442
|
+
}
|
|
443
|
+
.result-actions {
|
|
444
|
+
display: flex;
|
|
445
|
+
gap: 10px;
|
|
446
|
+
}
|
|
447
|
+
.config-wrap {
|
|
448
|
+
display: flex;
|
|
449
|
+
flex-direction: column;
|
|
450
|
+
gap: 12px;
|
|
451
|
+
}
|
|
452
|
+
.mapping {
|
|
453
|
+
display: flex;
|
|
454
|
+
flex-direction: column;
|
|
455
|
+
gap: 8px;
|
|
456
|
+
}
|
|
457
|
+
.mapping .row {
|
|
458
|
+
display: flex;
|
|
459
|
+
align-items: center;
|
|
460
|
+
gap: 10px;
|
|
461
|
+
}
|
|
462
|
+
.mapping .label {
|
|
463
|
+
width: 160px;
|
|
464
|
+
}
|
|
465
|
+
.history-wrap {
|
|
466
|
+
display: flex;
|
|
467
|
+
flex-direction: column;
|
|
468
|
+
gap: 10px;
|
|
469
|
+
}
|
|
470
|
+
</style>
|
package/sohelp-modal/index.vue
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<template>
|
|
2
|
-
<ele-modal v-bind="$attrs" v-model="modelValue">
|
|
2
|
+
<ele-modal v-bind="$attrs" v-model="modelValue" :close-on-click-modal="closeOnClickModal">
|
|
3
3
|
<template #header>
|
|
4
4
|
<div class="modal-header">
|
|
5
5
|
<slot name="header">
|
|
@@ -17,8 +17,8 @@
|
|
|
17
17
|
|
|
18
18
|
<template #footer>
|
|
19
19
|
<slot name="footer" v-if="showFooter">
|
|
20
|
-
<el-button type="primary" @click="confirm">确认</el-button>
|
|
21
20
|
<el-button @click="cancel">关闭</el-button>
|
|
21
|
+
<el-button type="primary" @click="confirm">确认</el-button>
|
|
22
22
|
</slot>
|
|
23
23
|
</template>
|
|
24
24
|
</ele-modal>
|
|
@@ -30,10 +30,15 @@
|
|
|
30
30
|
showFooter: {
|
|
31
31
|
type: Boolean,
|
|
32
32
|
default: true
|
|
33
|
+
},
|
|
34
|
+
closeOnClickModal: {
|
|
35
|
+
type: Boolean,
|
|
36
|
+
default: false
|
|
33
37
|
}
|
|
34
38
|
});
|
|
35
39
|
|
|
36
40
|
const confirm = () => {
|
|
41
|
+
close();
|
|
37
42
|
emit('confirm');
|
|
38
43
|
};
|
|
39
44
|
const cancel = () => {
|
|
@@ -5,15 +5,7 @@
|
|
|
5
5
|
</template>
|
|
6
6
|
|
|
7
7
|
<script setup>
|
|
8
|
-
import {
|
|
9
|
-
watch,
|
|
10
|
-
onMounted,
|
|
11
|
-
onBeforeUnmount,
|
|
12
|
-
onActivated,
|
|
13
|
-
onDeactivated,
|
|
14
|
-
nextTick,
|
|
15
|
-
useAttrs
|
|
16
|
-
} from 'vue';
|
|
8
|
+
import { watch, onMounted, onBeforeUnmount, onActivated, onDeactivated, nextTick, useAttrs } from 'vue';
|
|
17
9
|
import tinymce from 'tinymce/tinymce';
|
|
18
10
|
import 'tinymce/themes/silver';
|
|
19
11
|
import 'tinymce/icons/default';
|
|
@@ -46,13 +38,7 @@
|
|
|
46
38
|
import 'tinymce/plugins/emoticons/js/emojis';
|
|
47
39
|
import { storeToRefs } from 'pinia';
|
|
48
40
|
import { useThemeStore } from '@/store/modules/theme';
|
|
49
|
-
import {
|
|
50
|
-
DEFAULT_CONFIG,
|
|
51
|
-
DARK_CONFIG,
|
|
52
|
-
uuid,
|
|
53
|
-
bindHandlers,
|
|
54
|
-
openAlert
|
|
55
|
-
} from './util';
|
|
41
|
+
import { DEFAULT_CONFIG, DARK_CONFIG, uuid, bindHandlers, openAlert } from './util';
|
|
56
42
|
|
|
57
43
|
const props = defineProps({
|
|
58
44
|
/** 编辑器唯一id */
|
|
@@ -106,11 +92,7 @@
|
|
|
106
92
|
|
|
107
93
|
/** 修改内容 */
|
|
108
94
|
const setContent = (value) => {
|
|
109
|
-
if (
|
|
110
|
-
editorIns &&
|
|
111
|
-
typeof value === 'string' &&
|
|
112
|
-
value !== editorIns.getContent()
|
|
113
|
-
) {
|
|
95
|
+
if (editorIns && typeof value === 'string' && value !== editorIns.getContent()) {
|
|
114
96
|
editorIns.setContent(value);
|
|
115
97
|
}
|
|
116
98
|
};
|
|
@@ -3,55 +3,73 @@
|
|
|
3
3
|
*/
|
|
4
4
|
export default {
|
|
5
5
|
loading: false,
|
|
6
|
+
showFooter: false,
|
|
7
|
+
round: true,
|
|
8
|
+
border: true,
|
|
9
|
+
stripe: true,
|
|
10
|
+
maxHeight: '100%',
|
|
11
|
+
size: 'mini',
|
|
12
|
+
minHeight: 200,
|
|
13
|
+
params: {},
|
|
14
|
+
columnConfig: {
|
|
15
|
+
resizable: true
|
|
16
|
+
},
|
|
6
17
|
customConfig: {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
18
|
+
immediate: false,
|
|
19
|
+
isCurrent: false,
|
|
20
|
+
resizable: true,
|
|
21
|
+
storage: {
|
|
22
|
+
visible: true, // 保存列显示/隐藏状态
|
|
23
|
+
resizable: true, // 保存列宽调整
|
|
24
|
+
sort: true, // 保存列排序
|
|
25
|
+
fixed: true
|
|
26
|
+
},
|
|
27
|
+
slots: {
|
|
28
|
+
header: 'setting'
|
|
29
|
+
}
|
|
10
30
|
},
|
|
11
|
-
params: {},
|
|
12
31
|
rowConfig: {
|
|
13
|
-
useKey: true,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
drag: true
|
|
18
|
-
keyField: "id"
|
|
32
|
+
useKey: true, //sortable 排序
|
|
33
|
+
isHover: true,
|
|
34
|
+
isCurrent: true,
|
|
35
|
+
resizable: false,
|
|
36
|
+
drag: true
|
|
19
37
|
},
|
|
20
38
|
rowDragConfig: {
|
|
21
|
-
trigger: "cell",
|
|
22
39
|
showGuidesStatus: true
|
|
23
40
|
},
|
|
41
|
+
editConfig: {
|
|
42
|
+
trigger: 'click',
|
|
43
|
+
mode: 'cell',
|
|
44
|
+
autoClear: false,
|
|
45
|
+
autoFocus: true,
|
|
46
|
+
showInsertStatus: true,
|
|
47
|
+
showUpdateStatus: true
|
|
48
|
+
},
|
|
24
49
|
keyboardConfig: {
|
|
25
50
|
isEdit: true,
|
|
26
51
|
isArrow: true,
|
|
27
52
|
isEnter: true,
|
|
28
53
|
isTab: true,
|
|
29
54
|
isDel: true,
|
|
30
|
-
isBack: true
|
|
55
|
+
isBack: true,
|
|
56
|
+
editMode: 'insert'
|
|
31
57
|
},
|
|
32
58
|
mouseConfig: {
|
|
33
59
|
selected: true
|
|
34
60
|
},
|
|
35
61
|
checkboxConfig: {
|
|
36
|
-
checkRowKey: "id",
|
|
37
62
|
highlight: true,
|
|
38
63
|
range: true,
|
|
39
|
-
|
|
40
|
-
reserve: true // 跨页保留选中状态
|
|
64
|
+
reserve: true
|
|
41
65
|
},
|
|
42
66
|
radioConfig: {
|
|
43
67
|
strict: false,
|
|
44
68
|
checkRowKey: "id",
|
|
45
69
|
highlight: true,
|
|
46
70
|
reserve: true,
|
|
47
|
-
trigger: "
|
|
71
|
+
trigger: "cell" // 关键配置:点击行触发选中
|
|
48
72
|
},
|
|
49
|
-
border: true,
|
|
50
|
-
stripe: true,
|
|
51
|
-
loading: false,
|
|
52
|
-
minHeight: 200,
|
|
53
|
-
maxHeight: "100%",
|
|
54
|
-
size: "mini",
|
|
55
73
|
pagerConfig: {
|
|
56
74
|
enabled: true,
|
|
57
75
|
total: 0,
|
|
@@ -59,10 +77,7 @@ export default {
|
|
|
59
77
|
pageSize: 50,
|
|
60
78
|
align: "right"
|
|
61
79
|
},
|
|
62
|
-
|
|
63
|
-
resizable: true,
|
|
64
|
-
isCurrent: false
|
|
65
|
-
},
|
|
80
|
+
|
|
66
81
|
formConfig: {
|
|
67
82
|
data: {},
|
|
68
83
|
items: []
|
|
@@ -86,5 +101,15 @@ export default {
|
|
|
86
101
|
options: []
|
|
87
102
|
}
|
|
88
103
|
},
|
|
104
|
+
scrollX: {
|
|
105
|
+
gt: 0,
|
|
106
|
+
//自动启用纵向虚拟滚动
|
|
107
|
+
enabled: true
|
|
108
|
+
},
|
|
109
|
+
scrollY: {
|
|
110
|
+
gt: 0,
|
|
111
|
+
//自动启用纵向虚拟滚动
|
|
112
|
+
enabled: true
|
|
113
|
+
},
|
|
89
114
|
columns: []
|
|
90
115
|
};
|