update at 2026-02-13 22:40:22

This commit is contained in:
douboer@gmail.com
2026-02-13 22:40:22 +08:00
parent 43107afff1
commit d93e2c812f
6 changed files with 279 additions and 77 deletions

View File

@@ -129,6 +129,8 @@ const TARGET_ALIGN_OPTIONS = [
{ value: 'top', label: '顶部' },
{ value: 'bottom', label: '底部' }
];
const DEFAULT_SANKEY_FILE_NAME = 'data/sankey.xlsx';
const DEFAULT_SANKEY_FILE_PATHS = ['/data/sankey.xlsx', 'data/sankey.xlsx'];
/**
* 数值限制,避免 UI 参数导致布局异常。
@@ -141,6 +143,32 @@ function clampNumber(value, min, max, fallback) {
return Math.min(max, Math.max(min, normalized));
}
/**
* 统一错误文案:
* - xlsx 解析能力缺失时,固定提示用户去“构建 npm”
* - 其他错误返回原始 message便于定位
*/
function toFriendlyParseError(error, fallbackMessage) {
const message = error && error.message ? String(error.message) : '';
if (message.indexOf('xlsx 解析') >= 0) {
return '当前环境未启用 xlsx 解析,请先在开发者工具执行“构建 npm”';
}
return message || fallbackMessage;
}
/**
* 兼容 onWindowResize 不同回调结构,提取 windowHeight。
*/
function getWindowHeightFromResizePayload(payload) {
if (payload && payload.size && Number.isFinite(payload.size.windowHeight)) {
return payload.size.windowHeight;
}
if (payload && Number.isFinite(payload.windowHeight)) {
return payload.windowHeight;
}
return null;
}
/**
* 方向切换target->source 时对连线做镜像翻转。
*/
@@ -384,10 +412,10 @@ Page({
data: {
selectedThemeIndex: 1,
showThemeSheet: false,
uploadMessage: '点击上传或将csv/xls文件拖到这里上传',
uploadMessage: '默认加载 data/sankey.xlsx 中...',
parseError: '',
buildError: '',
columnHeaders: ['列1', '列2', '列3'],
columnHeaders: [],
tableRows: [],
sourceDataColumn: null,
sourceDescriptionColumns: [],
@@ -396,7 +424,7 @@ Page({
linksCount: 0,
droppedRows: 0,
buildWarnings: [],
infoLogs: ['解析信息: 尚未加载数据文件'],
infoLogs: ['解析信息: 正在加载默认数据文件 data/sankey.xlsx'],
sankeyLinks: [],
sankeyNodes: [],
gapOptions: GAP_OPTIONS,
@@ -413,7 +441,68 @@ Page({
targetAlignOptionLabels: TARGET_ALIGN_OPTIONS.map((item) => item.label),
targetAlignValues: TARGET_ALIGN_OPTIONS.map((item) => item.value),
targetAlignIndex: 0,
targetAlignMode: TARGET_ALIGN_OPTIONS[0].value
targetAlignMode: TARGET_ALIGN_OPTIONS[0].value,
bottomPanelsHeightPx: 300
},
/**
* 页面加载时:
* 1) 根据窗口高度计算底部双窗口的目标高度(默认 300低高度窗口自动压缩
* 2) 监听窗口变化,保持整体布局稳定
*/
onLoad() {
this.updateBottomPanelsHeight();
if (typeof wx.onWindowResize === 'function') {
this._handleWindowResize = (payload) => {
const nextWindowHeight = getWindowHeightFromResizePayload(payload);
this.updateBottomPanelsHeight(nextWindowHeight);
};
wx.onWindowResize(this._handleWindowResize);
}
},
/**
* 页面卸载时移除窗口变化监听,避免重复绑定。
*/
onUnload() {
if (this._handleWindowResize && typeof wx.offWindowResize === 'function') {
wx.offWindowResize(this._handleWindowResize);
}
},
/**
* 计算底部双窗口高度:
* - 优先保持 300px
* - 在小高度窗口中自动收缩到 180~300 区间,避免挤压主预览区
*/
updateBottomPanelsHeight(windowHeight) {
let resolvedWindowHeight = Number(windowHeight);
if (!Number.isFinite(resolvedWindowHeight)) {
try {
if (typeof wx.getWindowInfo === 'function') {
resolvedWindowHeight = wx.getWindowInfo().windowHeight;
} else {
resolvedWindowHeight = wx.getSystemInfoSync().windowHeight;
}
} catch (error) {
resolvedWindowHeight = 760;
}
}
const remainForBottomPanels = resolvedWindowHeight - 360;
const nextHeight = clampNumber(remainForBottomPanels, 180, 300, 300);
if (nextHeight === this.data.bottomPanelsHeightPx) {
return;
}
this.setData(
{
bottomPanelsHeightPx: nextHeight
},
() => {
this.drawSankey();
}
);
},
/**
@@ -522,6 +611,86 @@ Page({
*/
onReady() {
this.drawSankey();
this.loadDefaultSankeyFile();
},
/**
* 统一读取并解析文件。
* - CSV 按 utf8 文本读取
* - XLS/XLSX 按二进制读取
*/
readAndApplyFile(filePath, fileName, onReadFailPrefix) {
const that = this;
const extension = getFileExtension(fileName);
const isCsvFile = extension === 'csv';
const readOptions = {
filePath,
success(readRes) {
try {
const filePayload = isCsvFile ? String(readRes.data || '') : readRes.data;
const table = parseTableByFileName(fileName, filePayload);
that.applyParsedTable(table, fileName);
} catch (error) {
that.setData({
parseError: toFriendlyParseError(error, '文件解析失败')
});
that.refreshInfoLogs();
}
},
fail(err) {
that.setData({
parseError: `${onReadFailPrefix}: ${err && err.errMsg ? err.errMsg : '未知错误'}`
});
that.refreshInfoLogs();
}
};
if (isCsvFile) {
readOptions.encoding = 'utf8';
}
wx.getFileSystemManager().readFile(readOptions);
},
/**
* 默认加载项目内置数据data/sankey.xlsx
* 说明:
* - 多路径兜底,兼容不同开发者工具路径解析差异
* - 加载失败不展示占位列,保持“未加载文件”状态
*/
loadDefaultSankeyFile() {
const that = this;
const tryReadByIndex = (index, lastErrorMessage) => {
if (index >= DEFAULT_SANKEY_FILE_PATHS.length) {
that.setData({
uploadMessage: '点击上传或将csv/xls文件拖到这里上传',
parseError: `默认文件加载失败: ${lastErrorMessage || `未找到 ${DEFAULT_SANKEY_FILE_NAME}`}`
});
that.refreshInfoLogs();
return;
}
const candidatePath = DEFAULT_SANKEY_FILE_PATHS[index];
wx.getFileSystemManager().readFile({
filePath: candidatePath,
success(readRes) {
try {
const table = parseTableByFileName(DEFAULT_SANKEY_FILE_NAME, readRes.data);
that.applyParsedTable(table, DEFAULT_SANKEY_FILE_NAME);
} catch (error) {
that.setData({
uploadMessage: '点击上传或将csv/xls文件拖到这里上传',
parseError: toFriendlyParseError(error, '默认文件解析失败')
});
that.refreshInfoLogs();
}
},
fail(err) {
const errorMessage = err && err.errMsg ? err.errMsg : '未知错误';
tryReadByIndex(index + 1, errorMessage);
}
});
};
tryReadByIndex(0, '');
},
/**
@@ -540,33 +709,7 @@ Page({
}
const filePath = picked.path;
const fileName = picked.name || 'unknown.csv';
const extension = getFileExtension(fileName);
const isCsvFile = extension === 'csv';
const readOptions = {
filePath,
success(readRes) {
try {
const filePayload = isCsvFile ? String(readRes.data || '') : readRes.data;
const table = parseTableByFileName(fileName, filePayload);
that.applyParsedTable(table, fileName);
} catch (error) {
that.setData({
parseError: error && error.message ? error.message : '文件解析失败'
});
that.refreshInfoLogs();
}
},
fail(err) {
that.setData({
parseError: `读取文件失败: ${err && err.errMsg ? err.errMsg : '未知错误'}`
});
that.refreshInfoLogs();
}
};
if (isCsvFile) {
readOptions.encoding = 'utf8';
}
wx.getFileSystemManager().readFile(readOptions);
that.readAndApplyFile(filePath, fileName, '读取文件失败');
},
fail(err) {
if (err && String(err.errMsg || '').indexOf('cancel') >= 0) {
@@ -601,7 +744,7 @@ Page({
uploadMessage: `已加载: ${fileName}${rows.length} 行)`,
parseError: '',
buildError: '',
columnHeaders: headers.length > 0 ? headers : ['列1', '列2', '列3'],
columnHeaders: headers.length > 0 ? headers : [],
tableRows: rows,
sourceDataColumn,
sourceDescriptionColumns,