🐛 fix(export-workbench): 修正未知总数进度展示并优化 XLSX 收尾阶段

- 修正总行数为 0 时仍被当作已知总数的问题,避免导出进度百分比失真
- 调整导出进度条判定逻辑,未知总数时改为展示实时写入进度
- 统一 Sidebar、TableOverview 和导出工作台的预计行数口径,仅在总数大于 0 时视为已知
- 优化 XLSX 收尾阶段的 ZIP 压缩策略和拷贝缓冲,降低 Windows 大文件导出封装耗时
- 细化 finalizing 阶段文案,明确显示 XLSX 正在封装压缩
- 补充导出进度状态与零总数场景的回归测试
This commit is contained in:
Syngnat
2026-06-18 10:15:00 +08:00
parent 9613f6b624
commit 2a8ae05363
10 changed files with 116 additions and 22 deletions

View File

@@ -1,5 +1,10 @@
export type ExportProgressStatus = 'idle' | 'start' | 'running' | 'finalizing' | 'done' | 'error';
const hasUsableExportTotal = (total: number, totalRowsKnown: boolean): boolean => {
const normalizedTotal = Number.isFinite(total) ? Math.max(0, Math.trunc(total)) : 0;
return totalRowsKnown && normalizedTotal > 0;
};
const clampPercent = (value: number): number => {
if (!Number.isFinite(value)) return 0;
if (value <= 0) return 0;
@@ -12,11 +17,10 @@ export const shouldUseExactExportProgress = (
total: number,
totalRowsKnown: boolean,
): boolean => {
const normalizedTotal = Number.isFinite(total) ? Math.max(0, Math.trunc(total)) : 0;
if (totalRowsKnown && normalizedTotal > 0) {
if (hasUsableExportTotal(total, totalRowsKnown)) {
return true;
}
if ((status === 'done' || status === 'error') && totalRowsKnown) {
if ((status === 'done' || status === 'error') && hasUsableExportTotal(total, totalRowsKnown)) {
return true;
}
return false;
@@ -24,8 +28,9 @@ export const shouldUseExactExportProgress = (
export const shouldUseIndeterminateExportProgress = (
status: ExportProgressStatus,
total: number,
totalRowsKnown: boolean,
): boolean => !totalRowsKnown && status !== 'idle' && status !== 'done' && status !== 'error';
): boolean => !hasUsableExportTotal(total, totalRowsKnown) && status !== 'idle' && status !== 'done' && status !== 'error';
export const resolveExportProgressPercent = (
status: ExportProgressStatus,
@@ -35,7 +40,7 @@ export const resolveExportProgressPercent = (
): number => {
const normalizedCurrent = Number.isFinite(current) ? Math.max(0, current) : 0;
const normalizedTotal = Number.isFinite(total) ? Math.max(0, total) : 0;
if (totalRowsKnown && normalizedTotal > 0) {
if (hasUsableExportTotal(total, totalRowsKnown)) {
return clampPercent((normalizedCurrent / normalizedTotal) * 100);
}
if ((status === 'done' || status === 'error') && (totalRowsKnown || normalizedCurrent >= 0)) {
@@ -51,7 +56,7 @@ export const formatExportProgressRows = (
): string => {
const formatter = new Intl.NumberFormat('zh-CN');
const safeCurrent = formatter.format(Math.max(0, Math.trunc(Number(current) || 0)));
if (!totalRowsKnown) {
if (!hasUsableExportTotal(total, totalRowsKnown)) {
return `已写入 ${safeCurrent}`;
}
const safeTotal = formatter.format(Math.max(0, Math.trunc(Number(total) || 0)));