update at 2026-02-13 15:06:04
This commit is contained in:
72
src/App.vue
72
src/App.vue
@@ -393,9 +393,9 @@ const uploadedFileSnapshot = ref<PersistedUploadedFile | null>(null);
|
||||
const isRestoringWorkspace = ref(false);
|
||||
|
||||
const mapping = reactive<MappingConfig>({
|
||||
sourceDataColumn: 2,
|
||||
sourceDescriptionColumns: [0],
|
||||
targetDescriptionColumns: [2],
|
||||
sourceDataColumn: null,
|
||||
sourceDescriptionColumns: [],
|
||||
targetDescriptionColumns: [],
|
||||
delimiter: '-'
|
||||
});
|
||||
|
||||
@@ -935,26 +935,46 @@ function findHeaderIndex(headers: string[], aliases: string[]): number {
|
||||
}
|
||||
|
||||
/**
|
||||
* 上传文件后按列名设置默认映射:
|
||||
* - 源数据-数据列:value
|
||||
* - 源数据-描述列:source
|
||||
* - 目标数据-描述列:target
|
||||
* 若未找到对应列名,回退到安全列索引。
|
||||
* 判断单元格文本是否可作为数值列候选(支持千分位)。
|
||||
*/
|
||||
function setDefaultMappingByHeaders(headers: string[]): void {
|
||||
const safeSize = Math.max(headers.length, 1);
|
||||
const safeColumn = (index: number): number => Math.min(index, safeSize - 1);
|
||||
const sourceDataByName = findHeaderIndex(headers, ['value']);
|
||||
const sourceDescByName = findHeaderIndex(headers, ['source']);
|
||||
const targetDescByName = findHeaderIndex(headers, ['target']);
|
||||
function isNumericCell(text: string): boolean {
|
||||
const normalized = text.replace(/,/g, '').trim();
|
||||
if (!normalized) {
|
||||
return false;
|
||||
}
|
||||
return !Number.isNaN(Number(normalized));
|
||||
}
|
||||
|
||||
mapping.sourceDataColumn = sourceDataByName >= 0 ? sourceDataByName : safeColumn(2);
|
||||
mapping.sourceDescriptionColumns = [sourceDescByName >= 0 ? sourceDescByName : safeColumn(0)].filter(
|
||||
(item, index, list) => list.indexOf(item) === index
|
||||
);
|
||||
mapping.targetDescriptionColumns = [targetDescByName >= 0 ? targetDescByName : safeColumn(2)].filter(
|
||||
(item, index, list) => list.indexOf(item) === index
|
||||
);
|
||||
/**
|
||||
* 从“第二行内容”(RawTable 第 1 条数据行)中找第一个数字列。
|
||||
* 找不到时返回 -1。
|
||||
*/
|
||||
function findNumericColumnFromSecondRow(table: RawTable): number {
|
||||
const secondRow = table.rows[0] ?? [];
|
||||
return secondRow.findIndex((cell) => isNumericCell(cell ?? ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* 在“用户没有配置”的情况下,根据产品规则设置默认映射:
|
||||
* 1) 源数据-数据列:
|
||||
* - 优先表头 data/value/数据/值
|
||||
* - 否则取第二行内容中的第一个数字列
|
||||
* 2) 源数据-描述列:
|
||||
* - 取表头 source/源,找不到则不选
|
||||
* 3) 目标数据-描述列:
|
||||
* - 取表头 target/目标,找不到则不选
|
||||
*/
|
||||
function setDefaultMappingByTable(table: RawTable): void {
|
||||
const headers = table.headers;
|
||||
const sourceDataByName = findHeaderIndex(headers, ['data', 'value', '数据', '值']);
|
||||
const sourceDataBySecondRow = findNumericColumnFromSecondRow(table);
|
||||
const sourceDescByName = findHeaderIndex(headers, ['source', '源']);
|
||||
const targetDescByName = findHeaderIndex(headers, ['target', '目标']);
|
||||
|
||||
mapping.sourceDataColumn =
|
||||
sourceDataByName >= 0 ? sourceDataByName : sourceDataBySecondRow >= 0 ? sourceDataBySecondRow : null;
|
||||
mapping.sourceDescriptionColumns = sourceDescByName >= 0 ? [sourceDescByName] : [];
|
||||
mapping.targetDescriptionColumns = targetDescByName >= 0 ? [targetDescByName] : [];
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1200,7 +1220,11 @@ async function restoreWorkspaceFromStorage(): Promise<{
|
||||
const parsed = parseTableByFileName(workspace.uploadedFile.name, fileBuffer);
|
||||
rawTable.value = parsed;
|
||||
uploadedFileSnapshot.value = workspace.uploadedFile;
|
||||
applyMappingConfig(sanitizePersistedMapping(workspace.mapping, parsed.headers.length));
|
||||
if (workspace.mapping) {
|
||||
applyMappingConfig(sanitizePersistedMapping(workspace.mapping, parsed.headers.length));
|
||||
} else {
|
||||
setDefaultMappingByTable(parsed);
|
||||
}
|
||||
uploadMessage.value = `已恢复: ${workspace.uploadedFile.name}(${parsed.rows.length} 行)`;
|
||||
parseError.value = '';
|
||||
return { restoredUploadedFile: true };
|
||||
@@ -1223,7 +1247,7 @@ async function loadDataFile(file: File): Promise<void> {
|
||||
const fileBuffer = await file.arrayBuffer();
|
||||
const parsed = parseTableByFileName(file.name, fileBuffer);
|
||||
rawTable.value = parsed;
|
||||
setDefaultMappingByHeaders(parsed.headers);
|
||||
setDefaultMappingByTable(parsed);
|
||||
uploadedFileSnapshot.value = createUploadedFileSnapshot(file.name, file.type, fileBuffer);
|
||||
uploadMessage.value = `已加载: ${file.name}(${parsed.rows.length} 行)`;
|
||||
persistWorkspace();
|
||||
@@ -1269,7 +1293,7 @@ async function loadDefaultExampleFile(): Promise<void> {
|
||||
const buffer = await response.arrayBuffer();
|
||||
const parsed = parseXlsxBuffer(buffer);
|
||||
rawTable.value = parsed;
|
||||
setDefaultMappingByHeaders(parsed.headers);
|
||||
setDefaultMappingByTable(parsed);
|
||||
uploadedFileSnapshot.value = null;
|
||||
uploadMessage.value = `已加载: example0.xlsx(${parsed.rows.length} 行)`;
|
||||
parseError.value = '';
|
||||
|
||||
Reference in New Issue
Block a user