修复导出进度:导出方法名与进度字段重名导致点击无反应,改用 exportedRows;去掉原生确认框

This commit is contained in:
yueyang 2026-10-08 14:38:55 +08:00
parent 29defd2025
commit 18b4b9f0fb
2 changed files with 10 additions and 15 deletions

View File

@ -94,11 +94,11 @@ const dateConfig = {dateFormat:'Y-m-d',locale:Mandarin,disableMobile:true,allowI
const emptyFilters = billType => ({store_name:'',ledger_remark:'',bill_item:'',start:'',end:'',keyword:'',keyword_match:'contains'});
export default {
name:'AccountBills', components:{ TableColumnSettings, flatPicker, [Select.name]:Select, [Option.name]:Option }, props:{ apiBase:{type:String,required:true}, billType:{type:String,required:true}, progressBase:{type:String,default:''}, userKey:{type:[String,Number],required:true} },
data(){return{loading:false,loadError:false,sync:null,rows:[],sourceColumns:[],configuredColumns:[],columnsTitle:'',sourceGroups:[],summary:{income:0,expense:0,balance:0,count:0},total:0,currentPage:1,lastPage:1,perPage:20,filters:emptyFilters(this.billType),queryFilters:emptyFilters(this.billType),resultFilters:null,filterOptions:{shops:[],bill_items:[],ledger_remarks:[]},filterOptionsLoading:false,filterOptionsError:false,requestSerial:0,sort:{key:'occurred_at',dir:'desc'},exportMode:'filtered-visible',exporting:false,exportRows:0,exportTotal:0,exportTimer:null};},
data(){return{loading:false,loadError:false,sync:null,rows:[],sourceColumns:[],configuredColumns:[],columnsTitle:'',sourceGroups:[],summary:{income:0,expense:0,balance:0,count:0},total:0,currentPage:1,lastPage:1,perPage:20,filters:emptyFilters(this.billType),queryFilters:emptyFilters(this.billType),resultFilters:null,filterOptions:{shops:[],bill_items:[],ledger_remarks:[]},filterOptionsLoading:false,filterOptionsError:false,requestSerial:0,sort:{key:'occurred_at',dir:'desc'},exportMode:'filtered-visible',exporting:false,exportedRows:0,exportTotal:0,exportTimer:null};},
computed:{
isDouyin(){return this.billType==='douyin';},
exportPercent(){return this.exportTotal>0?Math.min(100,Math.round(this.exportRows*100/this.exportTotal)):0;},
exportText(){return '导出已开始,浏览器会保存到下载目录,你可以继续查看其它页面 · 已导出 '+Number(this.exportRows||0).toLocaleString()+' / '+Number(this.exportTotal||0).toLocaleString()+' 行('+this.exportPercent+'%)';},
exportPercent(){return this.exportTotal>0?Math.min(100,Math.round(this.exportedRows*100/this.exportTotal)):0;},
exportText(){const total=Number(this.exportTotal||0);const minutes=Math.max(1,Math.ceil(total/1800/60));return '共 '+total.toLocaleString()+' 行,预计约 '+minutes+' 分钟 · 已导出 '+Number(this.exportedRows||0).toLocaleString()+' 行('+this.exportPercent+'%)· 导出在后台进行,可以继续查看其它页面';},
// 费用结算时间标签、来源说明与收支净额统计口径按京东系列页签(京东账单/京喜账单)统一。
isJdBillTab(){return JD_BILL_TABS.indexOf(this.billType)!==-1;},
dateFilterLabel(){return DATE_FILTER_LABELS[this.billType]||'发生时间';},
@ -135,7 +135,6 @@ export default {
if(this.exportMode==='filtered-visible')this.visibleColumns.forEach(column=>params.append('columns[]',column.field));
// 服务端逐行生成 CSV:行数大时要几分钟,先告知用户,避免误判成「导不出来」。
const rows=Number(this.total||0);
if(rows>20000){const minutes=Math.max(1,Math.ceil(rows/1800/60));if(!window.confirm(`即将导出 ${rows.toLocaleString('zh-CN')} 行,服务端逐行生成,预计需要 ${minutes} 分钟。导出会在后台进行,你可以继续看其它页面,页面上会显示导出进度。是否继续?`)){return;}}
const token=`ab${Date.now().toString(36)}${Math.random().toString(36).slice(2,10)}`;
params.set('token',token);
params.set('total',String(rows));
@ -146,7 +145,7 @@ export default {
startExportWatch(token,total){
this.stopExportWatch();
this.exporting=true;
this.exportRows=0;
this.exportedRows=0;
this.exportTotal=Number(total||0);
const progressUrl=this.progressBase||`${this.apiBase}/export-progress`;
this.exportTimer=window.setInterval(()=>{
@ -154,7 +153,7 @@ export default {
.then(response=>response.ok?response.json():null)
.then(state=>{
if(!state||state.status==='unknown'){return;}
this.exportRows=Number(state.rows||0);
this.exportedRows=Number(state.rows||0);
if(state.total){this.exportTotal=Number(state.total);}
if(state.status==='done'||state.status==='failed'){this.stopExportWatch();}
})

View File

@ -124,14 +124,14 @@ const SORTABLE = ['occurred_at', 'income', 'expense', 'balance', 'record_code'];
export default {
name: 'TaobaoAlipayBills', components: { TableColumnSettings, flatPicker, [Select.name]: Select, [Option.name]: Option },
props: { apiBase: { type: String, required: true }, exportBase: { type: String, required: true }, progressBase: { type: String, default: '' }, userKey: { type: [String, Number], required: true } },
data() { return { loading: false, error: '', rows: [], configuredColumns: [], summary: { income: 0, expense: 0, balance: 0, count: 0 }, total: 0, currentPage: 1, lastPage: 1, perPage: 20, filters: { store_name: '', ledger_remark: '', source_channel: '', bill_item: '', business_type: '', start: '', end: '', keyword: '' }, filterOptions: { shops: [], bill_items: [], ledger_remarks: [] }, filterOptionsLoading: false, filterOptionsError: false, sort: { key: 'occurred_at', dir: 'desc' }, exportMode: 'filtered-visible', exporting: false, exportRows: 0, exportTotal: 0, exportTimer: null }; },
data() { return { loading: false, error: '', rows: [], configuredColumns: [], summary: { income: 0, expense: 0, balance: 0, count: 0 }, total: 0, currentPage: 1, lastPage: 1, perPage: 20, filters: { store_name: '', ledger_remark: '', source_channel: '', bill_item: '', business_type: '', start: '', end: '', keyword: '' }, filterOptions: { shops: [], bill_items: [], ledger_remarks: [] }, filterOptionsLoading: false, filterOptionsError: false, sort: { key: 'occurred_at', dir: 'desc' }, exportMode: 'filtered-visible', exporting: false, exportedRows: 0, exportTotal: 0, exportTimer: null }; },
computed: {
defaultColumns() { return COLUMNS; }, visibleColumns() { return this.configuredColumns.filter((column) => column.visible); },
visibleGroups() { return this.visibleColumns.reduce((groups, column) => { const last = groups[groups.length - 1]; if (last && last.label === column.group) last.span += 1; else groups.push({ label: column.group, span: 1 }); return groups; }, []); },
stats() { return [{ label: '收入合计', value: this.moneyWan(this.summary.income), note: `${this.summary.count} 笔交易`, className: 'is-income' }, { label: '支出合计', value: this.moneyWan(this.summary.expense), note: '按当前筛选', className: 'is-expense' }, { label: '收支差额', value: this.moneyWan(this.summary.balance), note: '收入合计 - 支出合计', className: '' }, { label: '交易笔数', value: this.summary.count, note: '当前筛选结果', className: '' }]; },
rangeStart() { return this.total === 0 ? 0 : (this.currentPage - 1) * this.perPage + 1; }, rangeEnd() { return Math.min(this.currentPage * this.perPage, this.total); },
exportPercent() { return this.exportTotal > 0 ? Math.min(100, Math.round(this.exportRows * 100 / this.exportTotal)) : 0; },
exportText() { return '导出已开始,浏览器会保存到下载目录,你可以继续查看其它页面 · 已导出 ' + Number(this.exportRows || 0).toLocaleString() + ' / ' + Number(this.exportTotal || 0).toLocaleString() + ' 行(' + this.exportPercent + '%)'; },
exportPercent() { return this.exportTotal > 0 ? Math.min(100, Math.round(this.exportedRows * 100 / this.exportTotal)) : 0; },
exportText() { const total = Number(this.exportTotal || 0); const minutes = Math.max(1, Math.ceil(total / 1800 / 60)); return '共 ' + total.toLocaleString() + ' 行,预计约 ' + minutes + ' 分钟 · 已导出 ' + Number(this.exportedRows || 0).toLocaleString() + ' 行(' + this.exportPercent + '%)· 导出在后台进行,可以继续查看其它页面'; },
invalidDateRange() { return Boolean(this.filters.start && this.filters.end && this.filters.start > this.filters.end); },
startDateConfig() { return { ...dateConfig, maxDate: this.filters.end || null }; },
endDateConfig() { return { ...dateConfig, minDate: this.filters.start || null }; },
@ -156,10 +156,6 @@ export default {
if (this.exportMode === 'filtered-visible') this.visibleColumns.forEach((column) => params.append('columns[]', column.field));
// 服务端逐行生成 CSV:行数大时要几分钟,先告知用户,避免误判成「导不出来」。
const rows = Number(this.total || 0);
if (rows > 20000) {
const minutes = Math.max(1, Math.ceil(rows / 1800 / 60));
if (!window.confirm(`即将导出 ${rows.toLocaleString('zh-CN')} 行,服务端逐行生成,预计需要 ${minutes} 分钟。导出会在后台进行,你可以继续看其它页面,页面上会显示导出进度。是否继续?`)) { return; }
}
const token = `ab${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
params.set('token', token);
params.set('total', String(rows));
@ -170,7 +166,7 @@ export default {
startExportWatch(token, total) {
this.stopExportWatch();
this.exporting = true;
this.exportRows = 0;
this.exportedRows = 0;
this.exportTotal = Number(total || 0);
const progressUrl = this.progressBase || `${this.apiBase}/export-progress`;
this.exportTimer = window.setInterval(() => {
@ -178,7 +174,7 @@ export default {
.then((response) => (response.ok ? response.json() : null))
.then((state) => {
if (!state || state.status === 'unknown') { return; }
this.exportRows = Number(state.rows || 0);
this.exportedRows = Number(state.rows || 0);
if (state.total) { this.exportTotal = Number(state.total); }
if (state.status === 'done' || state.status === 'failed') { this.stopExportWatch(); }
})