feat: 优化数据中枢 Excel 连接创建交互
- 在新增连接中完成 Excel 上传并自动填写连接名称 - 限制上传格式并提供空表和无表头错误反馈 - 移除旧导入入口和无效测试连接操作
This commit is contained in:
@@ -1,37 +1,26 @@
|
||||
<script setup lang="ts">
|
||||
import type { TreeNode } from './composables/use-connection-tree';
|
||||
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue';
|
||||
|
||||
import {
|
||||
EasyFlowButton,
|
||||
ResizableHandle,
|
||||
ResizablePanel,
|
||||
ResizablePanelGroup,
|
||||
} from '@easyflow-core/shadcn-ui';
|
||||
|
||||
import {
|
||||
ElDropdown,
|
||||
ElDropdownItem,
|
||||
ElDropdownMenu,
|
||||
ElEmpty,
|
||||
ElMessageBox,
|
||||
} from 'element-plus';
|
||||
import { ElEmpty, ElMessage, ElMessageBox } from 'element-plus';
|
||||
|
||||
import ConnectionTree from './components/ConnectionTree.vue';
|
||||
import ExcelActionDrawer from './components/ExcelActionDrawer.vue';
|
||||
import SourceFormDrawer from './components/SourceFormDrawer.vue';
|
||||
import TableDetailView from './components/TableDetailView.vue';
|
||||
import TableListView from './components/TableListView.vue';
|
||||
import { useConnectionTree } from './composables/use-connection-tree';
|
||||
import { useDatacenterExcel } from './composables/use-datacenter-excel';
|
||||
import { useDatacenterSources } from './composables/use-datacenter-sources';
|
||||
import { useDatacenterTables } from './composables/use-datacenter-tables';
|
||||
|
||||
const sourceFormRef = ref<InstanceType<typeof SourceFormDrawer>>();
|
||||
const excelActionRef = ref<InstanceType<typeof ExcelActionDrawer>>();
|
||||
const sourceFormVisible = ref(false);
|
||||
const excelActionVisible = ref(false);
|
||||
const workspaceRef = ref<HTMLElement>();
|
||||
const workspaceHeight = ref('100%');
|
||||
|
||||
@@ -42,6 +31,7 @@ const {
|
||||
loading,
|
||||
saving,
|
||||
testing,
|
||||
importExcelSource,
|
||||
loadSources,
|
||||
removeSource,
|
||||
saveSource,
|
||||
@@ -76,7 +66,7 @@ async function reloadAll(options?: {
|
||||
resetTable?: boolean;
|
||||
}) {
|
||||
await loadSources();
|
||||
if (!selectedSourceId.value) {
|
||||
if (selectedSourceId.value === null) {
|
||||
selectedCatalogId.value = null;
|
||||
selectedTableId.value = null;
|
||||
selectedNodeKey.value = '';
|
||||
@@ -97,61 +87,11 @@ async function reloadAll(options?: {
|
||||
viewMode.value = 'list';
|
||||
}
|
||||
|
||||
const {
|
||||
actionLoading,
|
||||
pendingUploadFile,
|
||||
splitForm,
|
||||
mergeForm,
|
||||
deriveForm,
|
||||
exportForm,
|
||||
resetSplitForm,
|
||||
resetMergeForm,
|
||||
resetDeriveForm,
|
||||
resetExportForm,
|
||||
handleImport,
|
||||
handleSplit,
|
||||
handleMerge,
|
||||
handleDerive,
|
||||
handleExport,
|
||||
} = useDatacenterExcel(
|
||||
selectedSourceId,
|
||||
selectedCatalogId,
|
||||
selectedTableId,
|
||||
reloadAll,
|
||||
);
|
||||
|
||||
// 视图状态:empty | list | detail
|
||||
const selectedNodeKey = ref('');
|
||||
const viewMode = ref<'detail' | 'empty' | 'list'>('empty');
|
||||
|
||||
const isExcelContext = computed(() => {
|
||||
const st =
|
||||
selectedSource.value?.sourceType || schema.value?.source?.sourceType;
|
||||
return st === 'EXCEL' || st === 'EXCEL_MATERIALIZED';
|
||||
});
|
||||
const canImportExcel = computed(
|
||||
() => selectedSource.value?.sourceType === 'EXCEL',
|
||||
);
|
||||
const canExport = computed(() =>
|
||||
Boolean(selectedTable.value || selectedSource.value),
|
||||
);
|
||||
const canShowMoreActions = computed(
|
||||
() =>
|
||||
isExcelContext.value &&
|
||||
(Boolean(selectedTable.value) ||
|
||||
managedTables.value.length > 1 ||
|
||||
canExport.value),
|
||||
);
|
||||
const showToolbar = computed(
|
||||
() => canImportExcel.value || canShowMoreActions.value,
|
||||
);
|
||||
const { treeData, parseNodeKey } = useConnectionTree(sources);
|
||||
const selectedFieldOptions = computed(() =>
|
||||
(schema.value?.fields || []).map((f: any) => ({
|
||||
label: f.fieldName,
|
||||
value: f.fieldName,
|
||||
})),
|
||||
);
|
||||
|
||||
async function handleNodeSelect(node: TreeNode) {
|
||||
selectedNodeKey.value = node.id;
|
||||
@@ -188,7 +128,10 @@ async function handleBatchRegister(rows: any[]) {
|
||||
async function handleBatchRemove(rows: any[]) {
|
||||
const removedIds = rows.map((row) => row?.id).filter(Boolean);
|
||||
await batchRemoveTables(rows);
|
||||
if (selectedTableId.value && removedIds.includes(selectedTableId.value)) {
|
||||
if (
|
||||
selectedTableId.value !== null &&
|
||||
removedIds.includes(selectedTableId.value)
|
||||
) {
|
||||
viewMode.value = 'list';
|
||||
}
|
||||
}
|
||||
@@ -245,8 +188,18 @@ async function handleRemoveSource(node: TreeNode) {
|
||||
await reloadAll({ focus: 'source', resetTable: true });
|
||||
}
|
||||
|
||||
async function handleSaveSource(formData: Record<string, any>) {
|
||||
await saveSource(formData);
|
||||
async function handleSaveSource(
|
||||
formData: Record<string, any>,
|
||||
excelFile?: File,
|
||||
) {
|
||||
if (formData.sourceType === 'EXCEL' && !formData.id) {
|
||||
if (!excelFile) return;
|
||||
await importExcelSource(excelFile, formData.sourceName);
|
||||
ElMessage.success('Excel 已导入');
|
||||
} else {
|
||||
await saveSource(formData);
|
||||
ElMessage.success(formData.id ? '连接已更新' : '连接已创建');
|
||||
}
|
||||
sourceFormRef.value?.close();
|
||||
sourceFormVisible.value = false;
|
||||
await nextTick();
|
||||
@@ -259,58 +212,6 @@ async function handleTestConnection(formData: Record<string, any>) {
|
||||
if (formData.id) await loadSources();
|
||||
}
|
||||
|
||||
function openExcelAction(
|
||||
action: 'derive' | 'export' | 'import' | 'merge' | 'split',
|
||||
) {
|
||||
switch (action) {
|
||||
case 'derive': {
|
||||
resetDeriveForm();
|
||||
break;
|
||||
}
|
||||
case 'export': {
|
||||
resetExportForm();
|
||||
break;
|
||||
}
|
||||
case 'merge': {
|
||||
resetMergeForm();
|
||||
break;
|
||||
}
|
||||
case 'split': {
|
||||
resetSplitForm();
|
||||
break;
|
||||
}
|
||||
}
|
||||
excelActionRef.value?.open(action);
|
||||
excelActionVisible.value = true;
|
||||
}
|
||||
|
||||
async function handleExcelAction(action: string) {
|
||||
let success = false;
|
||||
switch (action) {
|
||||
case 'derive': {
|
||||
success = await handleDerive();
|
||||
break;
|
||||
}
|
||||
case 'export': {
|
||||
success = await handleExport(loadTableRuntime);
|
||||
break;
|
||||
}
|
||||
case 'import': {
|
||||
success = await handleImport();
|
||||
break;
|
||||
}
|
||||
case 'merge': {
|
||||
success = await handleMerge();
|
||||
break;
|
||||
}
|
||||
case 'split': {
|
||||
success = await handleSplit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (success) excelActionVisible.value = false;
|
||||
}
|
||||
|
||||
async function loadAll() {
|
||||
loading.value = true;
|
||||
try {
|
||||
@@ -346,53 +247,6 @@ onBeforeUnmount(() => {
|
||||
:style="{ height: workspaceHeight }"
|
||||
v-loading="loading"
|
||||
>
|
||||
<!-- 顶部工具栏 -->
|
||||
<div v-if="showToolbar" class="workspace-toolbar">
|
||||
<div class="toolbar-left">
|
||||
<EasyFlowButton
|
||||
v-if="canImportExcel"
|
||||
class="toolbar-button"
|
||||
variant="outline"
|
||||
@click="openExcelAction('import')"
|
||||
>
|
||||
导入 Excel
|
||||
</EasyFlowButton>
|
||||
<ElDropdown v-if="canShowMoreActions">
|
||||
<EasyFlowButton class="toolbar-button" variant="outline">
|
||||
更多操作
|
||||
</EasyFlowButton>
|
||||
<template #dropdown>
|
||||
<ElDropdownMenu>
|
||||
<ElDropdownItem
|
||||
v-if="canExport"
|
||||
@click="openExcelAction('export')"
|
||||
>
|
||||
导出
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem
|
||||
v-if="selectedTable"
|
||||
@click="openExcelAction('split')"
|
||||
>
|
||||
拆分
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem
|
||||
v-if="managedTables.length > 1"
|
||||
@click="openExcelAction('merge')"
|
||||
>
|
||||
合并
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem
|
||||
v-if="selectedTable"
|
||||
@click="openExcelAction('derive')"
|
||||
>
|
||||
生成新表
|
||||
</ElDropdownItem>
|
||||
</ElDropdownMenu>
|
||||
</template>
|
||||
</ElDropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 双栏主体 -->
|
||||
<ResizablePanelGroup direction="horizontal" class="workspace-body">
|
||||
<ResizablePanel :default-size="20" :min-size="16" :max-size="30">
|
||||
@@ -447,25 +301,6 @@ onBeforeUnmount(() => {
|
||||
@save="handleSaveSource"
|
||||
@test="handleTestConnection"
|
||||
/>
|
||||
|
||||
<ExcelActionDrawer
|
||||
ref="excelActionRef"
|
||||
v-model:visible="excelActionVisible"
|
||||
v-model:split-form="splitForm"
|
||||
v-model:merge-form="mergeForm"
|
||||
v-model:derive-form="deriveForm"
|
||||
v-model:export-form="exportForm"
|
||||
:action-loading="actionLoading"
|
||||
:managed-tables="managedTables"
|
||||
:field-options="selectedFieldOptions"
|
||||
:pending-upload-file="pendingUploadFile"
|
||||
@update:pending-upload-file="(f) => (pendingUploadFile = f)"
|
||||
@import="handleExcelAction('import')"
|
||||
@split="handleExcelAction('split')"
|
||||
@merge="handleExcelAction('merge')"
|
||||
@derive="handleExcelAction('derive')"
|
||||
@export="handleExcelAction('export')"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -481,25 +316,6 @@ onBeforeUnmount(() => {
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.workspace-toolbar {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
padding: 0 0 12px;
|
||||
}
|
||||
|
||||
.toolbar-left {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.toolbar-button {
|
||||
min-width: 84px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.workspace-body {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
|
||||
Reference in New Issue
Block a user