diff --git a/resources/assets/js/components/business/AccountBills.vue b/resources/assets/js/components/business/AccountBills.vue index f1c33876..e215c05a 100644 --- a/resources/assets/js/components/business/AccountBills.vue +++ b/resources/assets/js/components/business/AccountBills.vue @@ -112,7 +112,9 @@ const JD_BILL_TABS = ['jd-transactions','jingxi']; // 时间筛选标签与该页签的时间列名保持一致(其余页签沿用「发生时间」)。 const DATE_FILTER_LABELS = {'jd-transactions':'费用结算时间','jingxi':'费用结算时间','jd-wallet':'费用结算时间','douyin':'结算时间','vipshop':'结算时间','vipshop-jx':'结算时间'}; const dateConfig = {dateFormat:'Y-m-d',locale:Mandarin,disableMobile:true,allowInput:false,onReady:(_,value,picker)=>picker.calendarContainer.classList.add('account-bill-calendar')}; -const emptyFilters = billType => ({store_name:'',ledger_remark:'',bill_item:'',start:'',end:'',keyword:'',keyword_match:'contains'}); +// 默认账期=本月:不填日期时查全表(几百万行)会很慢,默认落到当月既快又符合日常用法。 +const currentMonthRange = () => { const now = new Date(); const year = now.getFullYear(); const month = now.getMonth() + 1; const pad = value => String(value).padStart(2, '0'); const lastDay = new Date(year, month, 0).getDate(); return { start: `${year}-${pad(month)}-01`, end: `${year}-${pad(month)}-${pad(lastDay)}` }; }; +const emptyFilters = billType => ({store_name:'',ledger_remark:'',bill_item:'',...currentMonthRange(),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,exportPanelOpen:false,exportTasks:[],exportHint:'导出在后台生成,成功后点「下载」;期间可以继续浏览其它页面。',exportTimer:null};}, @@ -144,7 +146,8 @@ export default { attachGroups(columns,groups){let index=0;return columns.map((column,columnIndex)=>{while(groups[index]&&columnIndex>=groups.slice(0,index+1).reduce((sum,group)=>sum+Number(group.span||0),0))index+=1;return{...column,key:column.field||`column-${columnIndex}`,group:groups[index]?groups[index].label:''};});}, applyFilters(){if(this.invalidDateRange)return;this.queryFilters={...this.filters};this.currentPage=1;return this.fetch();},resetFilters(){this.filters=emptyFilters(this.billType);return this.applyFilters();},changePage(page){this.currentPage=page;this.fetch();this.$nextTick(()=>{if(this.$refs.scroll)this.$refs.scroll.scrollTop=0;});}, openDatePicker(refName){const picker=this.$refs[refName];if(picker&&picker.fp)picker.fp.open();}, - changeDate(field,value){const date=value||'';if(this.filters[field]===date)return;this.filters[field]=date;return this.applyFilters();}, + // 选日期不自动查询:日期是一次选两个(开始/结束),中途触发会先按半截条件查全表,很慢。改完点「查询」再生效。 + changeDate(field,value){const date=value||'';if(this.filters[field]===date)return;this.filters[field]=date;}, maybeSort(column){if(!this.sortableField(column.field))return;this.sort.dir=this.sort.key===column.field&&this.sort.dir==='desc'?'asc':'desc';this.sort.key=column.field;this.applyFilters();},sortableField(field){return SORTABLE.indexOf(field)!==-1;},sortMark(field){return this.sort.key===field?(this.sort.dir==='asc'?'↑':'↓'):'↕';}, getValue(row,field){if(!field)return'';if(field.indexOf('payload.')===0){const payload=row.payload||{};const value=payload[field.slice(8)];return value===null||value===undefined?'':value;}const value=row[field];return value===null||value===undefined?'':value;},rawValue(row,column){return this.getValue(row,column.field);}, displayValue(row,column){const value=this.rawValue(row,column);if(value==='')return'—';if(column.kind==='money_in')return Number(value)>0?`+${this.money(value)}`:'—';if(column.kind==='money_out')return Number(value)>0?`-${this.money(value)}`:'—';if(['balance','money'].indexOf(column.kind)!==-1)return this.money(value);return value;},cellClass(column){return{'is-number':this.isNumber(column.kind),'is-id':column.kind==='num','is-income':column.kind==='money_in','is-expense':column.kind==='money_out'};},isNumber(kind){return['money','money_in','money_out','balance','num'].indexOf(kind)!==-1;},money(value){return`¥${Number(value||0).toLocaleString('zh-CN',{minimumFractionDigits:2,maximumFractionDigits:2})}`;},moneyWan(value){return`¥${(Number(value||0)/10000).toLocaleString('zh-CN',{minimumFractionDigits:2,maximumFractionDigits:2})}万`;}, diff --git a/resources/assets/js/components/business/TaobaoAlipayBills.vue b/resources/assets/js/components/business/TaobaoAlipayBills.vue index c4d2f937..d15014c7 100644 --- a/resources/assets/js/components/business/TaobaoAlipayBills.vue +++ b/resources/assets/js/components/business/TaobaoAlipayBills.vue @@ -55,11 +55,11 @@
账单明细{{ total }} 条
- +
@@ -133,6 +133,9 @@ import 'flatpickr/dist/flatpickr.css'; const dateConfig = { dateFormat: 'Y-m-d', locale: Mandarin, disableMobile: true, allowInput: false, onReady: (_, value, picker) => picker.calendarContainer.classList.add('alipay-bill-calendar') }; +// 默认账期=本月:不填日期会查全表(几百万行)非常慢,默认落到当月。 +const currentMonthRange = () => { const now = new Date(); const year = now.getFullYear(); const month = now.getMonth() + 1; const pad = value => String(value).padStart(2, '0'); const lastDay = new Date(year, month, 0).getDate(); return { start: `${year}-${pad(month)}-01`, end: `${year}-${pad(month)}-${pad(lastDay)}` }; }; + const COLUMNS = [ ['记录编码', 'record_code', 'num', '基础信息'], ['原始店铺', 'store_name', 'text', '基础信息'], ['总账备注', 'ledger_remark', 'text', '基础信息'], ['自定义备注', 'custom_remark', 'text', '基础信息'], ['账单项目', 'bill_item', 'tag', '基础信息'], ['支付宝账号', 'alipay_account', 'num', '账户与交易'], ['账务订单号', 'account_order_no', 'num', '账户与交易'], ['业务流水号', 'business_serial_no', 'num', '账户与交易'], ['商户订单号', 'merchant_order_no', 'num', '账户与交易'], ['业务基础订单号', 'business_base_order_no', 'num', '账户与交易'], ['商品名称', 'goods_name', 'text', '账户与交易'], ['对方账号', 'counterparty_account', 'text', '账户与交易'], ['发生时间', 'occurred_at', 'date', '账户与交易'], @@ -145,7 +148,7 @@ 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, exportPanelOpen: false, exportTasks: [], exportHint: '导出在后台生成,成功后点「下载」;期间可以继续浏览其它页面。', 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: '', ...currentMonthRange(), keyword: '' }, filterOptions: { shops: [], bill_items: [], ledger_remarks: [] }, filterOptionsLoading: false, filterOptionsError: false, sort: { key: 'occurred_at', dir: 'desc' }, exportMode: 'filtered-visible', exporting: false, exportPanelOpen: false, exportTasks: [], exportHint: '导出在后台生成,成功后点「下载」;期间可以继续浏览其它页面。', 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; }, []); }, @@ -161,9 +164,10 @@ export default { methods: { async fetchFilterOptions() { this.filterOptionsLoading = true; this.filterOptionsError = false; try { const response = await window.axios.get(`${this.apiBase}/filter-options`); const body = response.data || {}; this.filterOptions = { shops: Array.isArray(body.shops) ? body.shops : [], bill_items: Array.isArray(body.bill_items) ? body.bill_items : [], ledger_remarks: Array.isArray(body.ledger_remarks) ? body.ledger_remarks : [] }; } catch (error) { this.filterOptionsError = true; } finally { this.filterOptionsLoading = false; } }, async fetch() { this.loading = true; this.error = ''; try { const response = await window.axios.get(this.apiBase, { params: { ...this.filters, sort: this.sort.key, dir: this.sort.dir, limit: this.perPage, page: this.currentPage } }); const body = response.data || {}; this.rows = body.data || []; this.total = body.total || 0; this.currentPage = body.current_page || 1; this.lastPage = body.last_page || 1; this.perPage = body.per_page || this.perPage; this.summary = body.summary || { income: 0, expense: 0, balance: 0, count: 0 }; } catch (error) { this.rows = []; this.total = 0; this.summary = { income: 0, expense: 0, balance: 0, count: 0 }; this.error = (error.response && error.response.data && error.response.data.message) || '淘系支付宝账单暂时无法读取,请稍后重试。'; } finally { this.loading = false; } }, - applyFilters() { if (this.invalidDateRange) return; this.currentPage = 1; this.fetch(); }, resetFilters() { this.filters = { store_name: '', ledger_remark: '', source_channel: '', bill_item: '', business_type: '', start: '', end: '', keyword: '' }; this.applyFilters(); }, + applyFilters() { if (this.invalidDateRange) return; this.currentPage = 1; this.fetch(); }, resetFilters() { this.filters = { store_name: '', ledger_remark: '', source_channel: '', bill_item: '', business_type: '', ...currentMonthRange(), keyword: '' }; this.applyFilters(); }, openDatePicker(refName) { const picker = this.$refs[refName]; if (picker && picker.fp) picker.fp.open(); }, - changeDate(field, value) { const date = value || ''; if (this.filters[field] === date) return; this.filters[field] = date; this.applyFilters(); }, + // 选日期不自动查询:日期是一对(开始/结束),中途触发会先按半截条件查全表,很慢。改完点「查询」再生效。 + changeDate(field, value) { const date = value || ''; if (this.filters[field] === date) return; this.filters[field] = date; }, changePage(page) { this.currentPage = page; this.fetch(); this.$nextTick(() => { if (this.$refs.scroll) this.$refs.scroll.scrollTop = 0; }); }, maybeSort(column) { if (!this.sortableField(column.field)) return; this.sort.dir = this.sort.key === column.field && this.sort.dir === 'desc' ? 'asc' : 'desc'; this.sort.key = column.field; this.applyFilters(); }, sortableField(field) { return SORTABLE.indexOf(field) !== -1; }, sortMark(field) { return this.sort.key === field ? (this.sort.dir === 'asc' ? '↑' : '↓') : '↕'; }, rawValue(row, column) { const value = row[column.field]; return value === null || value === undefined ? '' : value; },