feat: 优化数据中枢 Excel 连接创建交互

- 在新增连接中完成 Excel 上传并自动填写连接名称

- 限制上传格式并提供空表和无表头错误反馈

- 移除旧导入入口和无效测试连接操作
This commit is contained in:
2026-08-03 11:39:40 +08:00
parent 19dac5146c
commit 219e4f7eff
11 changed files with 301 additions and 233 deletions

View File

@@ -1,5 +1,5 @@
<script setup lang="ts">
import type { FormInstance } from 'element-plus';
import type { FormInstance, UploadFile, UploadInstance } from 'element-plus';
import { computed, ref, watch } from 'vue';
@@ -7,20 +7,31 @@ import { EasyFlowFormModal } from '@easyflow/common-ui';
import { EasyFlowButton } from '@easyflow-core/shadcn-ui';
import { CircleCheckFilled, CircleCloseFilled } from '@element-plus/icons-vue';
import {
CircleCheckFilled,
CircleCloseFilled,
UploadFilled,
} from '@element-plus/icons-vue';
import {
ElForm,
ElFormItem,
ElIcon,
ElInput,
ElMessage,
ElOption,
ElSelect,
ElUpload,
} from 'element-plus';
import {
sourceTypeLabels,
sourceTypeOptions,
} from '../composables/datacenter-constants';
import {
EXCEL_FILE_ACCEPT,
isSupportedExcelFileName,
sourceNameFromExcelFileName,
} from '../composables/datacenter-excel-upload';
import { useSourceForm } from '../composables/use-source-form';
import SourceBrandIcon from './SourceBrandIcon.vue';
@@ -31,13 +42,17 @@ const props = defineProps<{
}>();
const emit = defineEmits<{
save: [form: Record<string, any>];
save: [form: Record<string, any>, excelFile?: File];
test: [form: Record<string, any>];
'update:visible': [value: boolean];
}>();
const formRef = ref<FormInstance>();
const excelUploadRef = ref<UploadInstance>();
const testResult = ref<any>(null);
const showAdvanced = ref(false);
const pendingExcelFile = ref<File | null>(null);
const excelFileError = ref('');
const lastGeneratedSourceName = ref('');
const {
form,
@@ -60,10 +75,24 @@ const testButtonClass = computed(() => ({
const selectedSourceTypeLabel = computed(
() => sourceTypeLabels[form.sourceType] || form.sourceType,
);
const isExcelCreate = computed(() => form.sourceType === 'EXCEL' && !form.id);
const confirmDisabled = computed(
() =>
isExcelCreate.value &&
(!pendingExcelFile.value || !String(form.sourceName || '').trim()),
);
function resetExcelFile() {
excelUploadRef.value?.clearFiles();
pendingExcelFile.value = null;
excelFileError.value = '';
lastGeneratedSourceName.value = '';
}
function resetDialogState() {
testResult.value = null;
showAdvanced.value = false;
resetExcelFile();
}
function open(row?: any) {
@@ -78,8 +107,16 @@ function close() {
}
async function handleSave() {
if (isExcelCreate.value && !pendingExcelFile.value) {
excelFileError.value = '请选择需要导入的 Excel 文件';
return;
}
await formRef.value?.validate();
emit('save', { ...form });
emit(
'save',
{ ...form },
isExcelCreate.value ? pendingExcelFile.value || undefined : undefined,
);
}
async function handleTest() {
@@ -96,6 +133,33 @@ function setTestResult(result: any) {
testResult.value = result;
}
function handleUploadChange(file: UploadFile) {
if (!isSupportedExcelFileName(file.name)) {
excelUploadRef.value?.clearFiles();
pendingExcelFile.value = null;
excelFileError.value = '仅支持 .xls 和 .xlsx 格式的 Excel 文件';
ElMessage.warning(excelFileError.value);
return;
}
const generatedSourceName = sourceNameFromExcelFileName(file.name);
if (
!String(form.sourceName || '').trim() ||
form.sourceName === lastGeneratedSourceName.value
) {
form.sourceName = generatedSourceName;
formRef.value?.clearValidate('sourceName');
}
lastGeneratedSourceName.value = generatedSourceName;
pendingExcelFile.value = file.raw || null;
excelFileError.value = '';
}
function handleUploadRemove() {
pendingExcelFile.value = null;
excelFileError.value = '';
}
watch(
() => [
form.sourceName,
@@ -117,6 +181,15 @@ watch(
},
);
watch(
() => form.sourceType,
(_sourceType, previousSourceType) => {
if (previousSourceType && previousSourceType !== form.sourceType) {
resetExcelFile();
}
},
);
defineExpose({ close, open, setTestResult });
</script>
@@ -125,6 +198,7 @@ defineExpose({ close, open, setTestResult });
:open="visible"
:title="modalTitle"
:before-close="handleBeforeClose"
:confirm-disabled="confirmDisabled"
:confirm-loading="saving"
confirm-text="保存"
:submitting="saving"
@@ -141,13 +215,19 @@ defineExpose({ close, open, setTestResult });
>
<div class="form-grid">
<ElFormItem label="连接名称" prop="sourceName">
<ElInput v-model="form.sourceName" placeholder="例如:华东 MySQL" />
<ElInput
v-model="form.sourceName"
maxlength="100"
:placeholder="
isExcelCreate ? '选择文件后自动填写' : '例如:华东 MySQL'
"
/>
</ElFormItem>
<ElFormItem label="连接类型" prop="sourceType">
<ElSelect
v-model="form.sourceType"
class="w-full"
:disabled="Boolean(form.builtinFlag)"
:disabled="Boolean(form.builtinFlag || form.id)"
>
<template #label>
<span class="source-type-select-label">
@@ -174,6 +254,35 @@ defineExpose({ close, open, setTestResult });
</ElFormItem>
</div>
<ElFormItem
v-if="isExcelCreate"
label="表格文件"
required
:error="excelFileError"
class="excel-file-field"
>
<ElUpload
ref="excelUploadRef"
class="excel-upload"
drag
:accept="EXCEL_FILE_ACCEPT"
:auto-upload="false"
:show-file-list="true"
:limit="1"
:disabled="saving"
:on-change="handleUploadChange"
:on-remove="handleUploadRemove"
>
<ElIcon class="excel-upload__icon">
<UploadFilled />
</ElIcon>
<div class="excel-upload__title">拖拽或点击选择 Excel 文件</div>
<div class="excel-upload__description">
仅支持 .xls.xlsx工作表首行需包含表头
</div>
</ElUpload>
</ElFormItem>
<template v-if="supportsExternalConnection">
<div class="form-grid">
<ElFormItem label="主机">
@@ -242,6 +351,7 @@ defineExpose({ close, open, setTestResult });
<template #footer-extra>
<EasyFlowButton
v-if="supportsExternalConnection"
variant="outline"
:loading="testing"
:disabled="saving"
@@ -308,6 +418,62 @@ defineExpose({ close, open, setTestResult });
grid-column: 1 / -1;
}
.excel-file-field {
margin-bottom: 0;
}
.excel-upload {
width: 100%;
}
.excel-upload :deep(.el-upload) {
width: 100%;
}
.excel-upload :deep(.el-upload-dragger) {
width: 100%;
padding: 24px;
background: hsl(var(--surface-subtle) / 72%);
border-color: hsl(var(--border) / 76%);
border-radius: var(--radius-panel);
transition:
background-color 0.16s,
border-color 0.16s;
}
.excel-upload :deep(.el-upload-dragger:hover),
.excel-upload :deep(.el-upload-dragger:focus-visible) {
background: hsl(var(--primary) / 5%);
border-color: hsl(var(--primary) / 56%);
}
.excel-upload__icon {
margin-bottom: 8px;
font-size: 28px;
color: hsl(var(--primary));
}
.excel-upload__title {
font-size: 14px;
font-weight: 600;
color: hsl(var(--foreground));
}
.excel-upload__description {
margin-top: 6px;
font-size: 12px;
line-height: 1.5;
color: hsl(var(--muted-foreground));
}
.excel-upload :deep(.el-upload-list) {
margin-top: 8px;
}
.excel-upload :deep(.el-upload-list__item) {
border-radius: var(--radius-control);
}
.toggle-advanced {
display: inline-flex;
align-items: center;