update at 2026-02-13 15:06:04

This commit is contained in:
douboer@gmail.com
2026-02-13 15:06:04 +08:00
parent 8f58a3ced0
commit 75d403cf44
2 changed files with 49 additions and 24 deletions

View File

@@ -16,6 +16,7 @@
8. 小程序端已完成视觉骨架(非完整业务)。
9. 已实现本地持久化:用户上传文件、映射配置与预览选项会写入 localStorage刷新后自动恢复。
10. 已新增“汇聚对齐”配置Between/Middle/Top/Bottom可控制 target 侧对齐,且 gap 作为源侧基准。
11. 已优化“无配置初始化映射”:优先按表头别名自动匹配,缺失时按第二行首个数字列兜底。
二、当前状态In Progress
1. 无进行中的代码重构任务。

View File

@@ -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 = '';