diff --git a/悦/PDA拣货/pda-picking.html b/悦/PDA拣货/pda-picking.html index 845fb5f..4af3dd8 100644 --- a/悦/PDA拣货/pda-picking.html +++ b/悦/PDA拣货/pda-picking.html @@ -2,8 +2,8 @@ - -PDA集装箱拣货 + +柏悦物流 PDA · 仓储作业平台 @@ -1029,51 +1255,128 @@
+ + + + + + + + + @@ -1081,10 +1384,10 @@
@@ -7110,7 +7352,8 @@ GARMENTS( MEN'S COTTON PADDED CLOTHES)) 成本(¥) 利润(¥) 利润率 -关联单据数 +关联单据 +操作 @@ -7634,10 +7877,11 @@ GARMENTS( MEN'S COTTON PADDED CLOTHES))
- +
+ @@ -7656,11 +7900,11 @@ GARMENTS( MEN'S COTTON PADDED CLOTHES))
-
+
+
-
@@ -7678,7 +7922,7 @@ GARMENTS( MEN'S COTTON PADDED CLOTHES))
-
预装清单
+
预装清单
@@ -8508,6 +8752,23 @@ GARMENTS( MEN'S COTTON PADDED CLOTHES))
+ + + + + + - - - + + + `).join(''):''; } /* 装柜清单明细 - 行内编辑备注(仅自己/客户/仓库) */ +function saveMfInlineNote(input,field){ + var manifestNo=input.dataset.mf; + var d=ctManifestData.find(function(x){return x.no===manifestNo;}); + if(!d)return; + var value=input.value.trim(); + input.value=value; + d[field]=value; + input.style.boxShadow='none'; + input.style.borderColor='#52c41a'; + var cellClass=field==='noteSelf'?'mf-note-self':(field==='noteCustomer'?'mf-note-cust':'mf-note-wh'); + document.querySelectorAll('#tbody-ct-manifest-detail .'+cellClass+' input[data-mf="'+CSS.escape(manifestNo)+'"]').forEach(function(el){ + if(el!==input)el.value=value; + }); + setTimeout(function(){if(document.activeElement!==input)input.style.borderColor='#d9d9d9';},600); +} function mfDetailToggleEdit(manifestNo, linkEl){ var tr=linkEl.closest('tr'); if(!tr)return; + var directInput=tr.querySelector('.mf-note-self input'); + if(directInput){directInput.focus();directInput.select();return;} /* 已在编辑模式 → 保存 */ if(tr.dataset.editing==='1'){ mfDetailSaveNotes(manifestNo,tr); return; } tr.dataset.editing='1'; @@ -14415,6 +14989,96 @@ function stkReset(){ showToast('已重置','info'); } +/* 暂扣库存 - 出库 */ +function openStockOutboundModal(){ + const type=document.getElementById('stock-out-type'); + const dest=document.getElementById('stock-out-dest'); + const remark=document.getElementById('stock-out-remark'); + if(type)type.value=''; + if(dest)dest.value=''; + if(remark)remark.value=''; + updateStockOutboundType(); + renderStockOutboundProducts(); + showModal('modal-stock-outbound'); +} +function updateStockOutboundType(){ + const type=(document.getElementById('stock-out-type')||{}).value||''; + const label=document.getElementById('stock-out-dest-label'); + const input=document.getElementById('stock-out-dest'); + const note=document.getElementById('stock-out-type-note'); + const cfg={ + '一件代发':{label:'收件信息 / 订单号',placeholder:'请输入收件人、订单号或收货地址',note:'一件代发:选择商品数量后,将按客户订单进行拣货、打包和派送。'}, + 'FBA仓':{label:'FBA仓 / 货件编号',placeholder:'请输入FBA仓编码或货件编号',note:'FBA仓:商品将出库并发往指定FBA仓,请填写仓库编码或货件编号。'}, + '换标':{label:'换标要求 / 新SKU',placeholder:'请输入新SKU或换标要求',note:'换标:商品出库进入换标作业,完成后可重新入库或转发。'} + }; + const item=cfg[type]||{label:'目的信息',placeholder:'请先选择出库类型',note:'请选择出库类型,再选择需要出库的商品及数量。'}; + if(label)label.textContent=item.label; + if(input){input.placeholder=item.placeholder;input.disabled=!type;} + if(note)note.textContent=item.note; +} +function renderStockOutboundProducts(){ + const tb=document.getElementById('stock-out-product-body'); + if(!tb)return; + tb.innerHTML=overseasStockData.map(function(d,index){ + const available=Math.max(0,Number(d.stock)||0); + const disabled=available<=0; + return ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''+ + ''; + }).join(''); + updateStockOutboundSummary(); +} +function toggleStockOutboundProduct(index,checked){ + const input=document.querySelector('.stock-out-qty[data-stock-index="'+index+'"]'); + if(!input)return; + input.disabled=!checked; + if(checked){if(!input.value)input.value='1';input.focus();} + else input.value=''; + updateStockOutboundSummary(); +} +function updateStockOutboundSummary(){ + const checked=[].slice.call(document.querySelectorAll('.stock-out-check:checked')); + let total=0; + checked.forEach(function(cb){ + const qty=document.querySelector('.stock-out-qty[data-stock-index="'+cb.dataset.stockIndex+'"]'); + total+=Math.max(0,Number((qty||{}).value)||0); + }); + const countEl=document.getElementById('stock-out-selected-count'); + const totalEl=document.getElementById('stock-out-total-qty'); + if(countEl)countEl.textContent=checked.length; + if(totalEl)totalEl.textContent=total; +} +function submitStockOutbound(){ + const type=(document.getElementById('stock-out-type')||{}).value||''; + if(!type){showToast('请选择出库类型','warning');return;} + const checked=[].slice.call(document.querySelectorAll('.stock-out-check:checked')); + if(!checked.length){showToast('请至少选择一个出库商品','warning');return;} + const rows=[]; + for(let i=0;iavailable){showToast(d.sku+' 出库数量不能超过可用库存 '+available,'warning');return;} + rows.push({item:d,qty:qty}); + } + rows.forEach(function(row){row.item.stock=Math.max(0,row.item.stock-row.qty);}); + hideModal('modal-stock-outbound'); + renderOverseasStock(); + const orderNo='CK'+new Date().getTime().toString().slice(-10); + const total=rows.reduce(function(sum,row){return sum+row.qty;},0); + showToast(type+'出库单 '+orderNo+' 已创建,共 '+total+' 件','success'); +} + /* 海外仓入库单 */ const ibStatusColor={'待到仓':'tag-orange','收货中':'tag-blue','已上架':'tag-green','已取消':'tag-gray'}; function renderOverseasInbound(list){ @@ -15259,14 +15923,14 @@ function delLocation(idx){ /* 托盘管理 */ const overseasPalletData=[ - {code:'TP-001',type:'木托盘',vol:1.20,status:'闲置',remark:'标准川字托盘'}, - {code:'TP-002',type:'木托盘',vol:1.20,status:'占用',remark:''}, - {code:'TP-003',type:'塑料托盘',vol:1.35,status:'闲置',remark:'防潮耐腐蚀'}, - {code:'TP-004',type:'金属托盘',vol:1.50,status:'闲置',remark:'重载钢托盘'}, - {code:'TP-005',type:'免熏蒸托盘',vol:1.10,status:'占用',remark:'出口免检'}, - {code:'TP-006',type:'纸托盘',vol:0.80,status:'闲置',remark:'一次性轻货'}, - {code:'TP-007',type:'塑料托盘',vol:1.35,status:'闲置',remark:''}, - {code:'TP-008',type:'木托盘',vol:1.20,status:'占用',remark:'仓库周转'} + {code:'TP-001',type:'木托盘',vol:1.20,weight:22.50,status:'闲置',remark:'标准川字托盘'}, + {code:'TP-002',type:'木托盘',vol:1.20,weight:23.00,status:'占用',remark:''}, + {code:'TP-003',type:'塑料托盘',vol:1.35,weight:15.20,status:'闲置',remark:'防潮耐腐蚀'}, + {code:'TP-004',type:'金属托盘',vol:1.50,weight:38.50,status:'闲置',remark:'重载钢托盘'}, + {code:'TP-005',type:'免熏蒸托盘',vol:1.10,weight:19.80,status:'占用',remark:'出口免检'}, + {code:'TP-006',type:'纸托盘',vol:0.80,weight:8.50,status:'闲置',remark:'一次性轻货'}, + {code:'TP-007',type:'塑料托盘',vol:1.35,weight:15.00,status:'闲置',remark:''}, + {code:'TP-008',type:'木托盘',vol:1.20,weight:22.80,status:'占用',remark:'仓库周转'} ]; let editingPalletIdx=-1; function renderOverseasPallet(list){ @@ -15277,6 +15941,7 @@ function renderOverseasPallet(list){ + @@ -15291,10 +15956,10 @@ function openPalletModal(idx){ if(idx>=0){ const d=overseasPalletData[idx]; title.textContent='编辑托盘'; - set('pallet-code-in',d.code);set('pallet-type-in',d.type);set('pallet-vol-in',d.vol);set('pallet-status-in',d.status);set('pallet-remark-in',d.remark); + set('pallet-code-in',d.code);set('pallet-type-in',d.type);set('pallet-vol-in',d.vol);set('pallet-weight-in',d.weight);set('pallet-status-in',d.status);set('pallet-remark-in',d.remark); }else{ title.textContent='新增托盘'; - ['pallet-code-in','pallet-vol-in','pallet-remark-in'].forEach(function(id){set(id,'');}); + ['pallet-code-in','pallet-vol-in','pallet-weight-in','pallet-remark-in'].forEach(function(id){set(id,'');}); set('pallet-type-in','木托盘');set('pallet-status-in','闲置'); } showModal('modal-pallet'); @@ -15305,7 +15970,9 @@ function savePallet(){ if(!code){showToast('请填写托盘编码','warning');return;} const vol=get('pallet-vol-in'); if(!vol||isNaN(Number(vol))||Number(vol)<=0){showToast('请填写有效的托盘容积(m³)','warning');return;} - const rec={code:code,type:get('pallet-type-in'),vol:Number(vol),status:get('pallet-status-in')||'闲置',remark:get('pallet-remark-in')}; + const weight=get('pallet-weight-in'); + if(!weight||isNaN(Number(weight))||Number(weight)<=0){showToast('请填写有效的托盘重量(kg)','warning');return;} + const rec={code:code,type:get('pallet-type-in'),vol:Number(vol),weight:Number(weight),status:get('pallet-status-in')||'闲置',remark:get('pallet-remark-in')}; if(editingPalletIdx>=0){overseasPalletData[editingPalletIdx]=rec;} else{overseasPalletData.unshift(rec);} editingPalletIdx=-1; @@ -15509,95 +16176,153 @@ const profitMonthData=[ {month:'2026-06',income:1980000,cost:1660000,profit:320000,docs:59} ]; let profitTab='customer'; -function switchProfitTab(el,key){ +let profitVisibleTickets=[]; +let profitCurrentData={customer:[],container:[],ticket:[],month:[]}; +function activateProfitTab(key){ profitTab=key; - document.querySelectorAll('#page-profit .profit-stab').forEach(t=>{t.style.color='#374151';t.style.borderBottomColor='transparent';t.style.fontWeight='';}); - el.style.color='#1677ff';el.style.borderBottomColor='#1677ff';el.style.fontWeight='600'; + document.querySelectorAll('#page-profit .profit-stab').forEach(t=>{ + const active=t.dataset.profitTab===key; + t.classList.toggle('active',active); + t.style.color=active?'#1677ff':'#374151'; + t.style.borderBottomColor=active?'#1677ff':'transparent'; + t.style.fontWeight=active?'600':''; + }); document.querySelectorAll('#page-profit .profit-panel').forEach(p=>p.style.display='none'); - document.getElementById('panel-profit-'+key).style.display='block'; + const panel=document.getElementById('panel-profit-'+key); + if(panel)panel.style.display='block'; + const total=document.getElementById('profit-total'); + const labels={customer:'客户',container:'货柜',ticket:'单票',month:'单月'}; + if(total&&profitCurrentData[key])total.textContent='共 '+profitCurrentData[key].length+' 条'+labels[key]+'维度数据'; +} +function switchProfitTab(el,key){ + const scope=document.getElementById('profit-scope'); + if(scope)scope.value=key; + profitSetPlaceholder(key); + activateProfitTab(key); +} +function profitSetPlaceholder(scope){ + const placeholders={customer:'请输入客户名称',container:'请输入货柜号或客户',ticket:'请输入单票号、客户或渠道',month:'请输入月份,如 2026-06'}; + const kw=document.getElementById('profit-kw'); + if(kw)kw.placeholder=placeholders[scope]||'请输入查询内容'; } function profitScopeChange(){ - const s=(document.getElementById('profit-scope')||{}).value; - const kw=document.getElementById('profit-kw'); - if(kw)kw.placeholder = s==='container'?'请输入货柜号':'货柜号 / 客户名 / 单票号'; + const scope=(document.getElementById('profit-scope')||{}).value||'customer'; + profitSetPlaceholder(scope); + activateProfitTab(scope); +} +function profitAggregate(tickets,dim){ + const keyField={customer:'cust',container:'ctNo',month:'month'}[dim]; + const groups=new Map(); + tickets.forEach(t=>{ + const key=t[keyField]; + if(!groups.has(key))groups.set(key,{income:0,cost:0,profit:0,docs:0,tickets:[],customers:new Set()}); + const row=groups.get(key); + row.income+=t.income;row.cost+=t.cost;row.profit+=t.profit;row.docs++;row.tickets.push(t);row.customers.add(t.cust); + }); + return Array.from(groups.entries()).map(([key,row])=>{ + if(dim==='customer')row.cust=key; + if(dim==='container'){row.ctNo=key;row.cust=Array.from(row.customers).join('、');} + if(dim==='month')row.month=key; + return row; + }).sort((a,b)=>String(a[keyField]).localeCompare(String(b[keyField]),'zh-CN')); +} +function profitRelatedDocs(d){ + const docs=(d.tickets||[]).map(t=>t.ticket); + if(!docs.length)return '-'; + const shown=docs.slice(0,2).join('、'); + return ''+shown+(docs.length>2?' 等'+docs.length+'单':'')+''; +} +function profitViewButton(dim,val){ + return ''; } function profitRows(arr,dim){ - if(!arr.length) return ''; + const colspans={customer:7,container:8,ticket:10,month:7}; + if(!arr.length)return ''; const fmt=n=>'¥'+n.toLocaleString('zh-CN'); const sum=f=>arr.reduce((s,d)=>s+(d[f]||0),0); let body=''; arr.forEach(d=>{ - const rate=d.income>0?(d.profit/d.income*100):0; - const rateColor=rate>=0?'#52c41a':'#f5222d'; - const rateTxt=rate.toFixed(1)+'%'; + const rate=profitRate(d.profit,d.income),rateColor=rate>=0?'#52c41a':'#f5222d',rateTxt=rate.toFixed(1)+'%'; + const common='style="border-bottom:1px solid #f0f0f0;cursor:pointer" title="点击查看明细"'; if(dim==='customer'){ - body+=''; - } else if(dim==='container'){ - body+=''; - } else if(dim==='ticket'){ - body+=''; - } else if(dim==='month'){ - body+=''; + body+=''; + }else if(dim==='container'){ + body+=''; + }else if(dim==='ticket'){ + body+=''; + }else if(dim==='month'){ + body+=''; } }); - const tIncome=sum('income'),tCost=sum('cost'),tProfit=sum('profit'); - const tRate=tIncome>0?(tProfit/tIncome*100):0; - const tColor=tProfit>=0?'#52c41a':'#f5222d'; - let totalRow=''; - if(dim==='customer'){ - totalRow+=''; - } else if(dim==='container'){ - totalRow+=''; - } else if(dim==='ticket'){ - totalRow+=''; - } else if(dim==='month'){ - totalRow+=''; - } - totalRow+=''; - return body+totalRow; + const tIncome=sum('income'),tCost=sum('cost'),tProfit=sum('profit'),tRate=profitRate(tProfit,tIncome),tColor=tProfit>=0?'#52c41a':'#f5222d'; + const base=''; + let total=''; + if(dim==='customer')total+=''+base+''; + if(dim==='container')total+=''+base+''; + if(dim==='ticket')total+=''+base+''; + if(dim==='month')total+=''+base+''; + return body+total+''; } -function profitRenderAll(cust,ct,ticket,month){ - const tbC=document.getElementById('tbody-profit-customer'); if(tbC)tbC.innerHTML=profitRows(cust,'customer'); - const tbT=document.getElementById('tbody-profit-container'); if(tbT)tbT.innerHTML=profitRows(ct,'container'); - const tbK=document.getElementById('tbody-profit-ticket'); if(tbK)tbK.innerHTML=profitRows(ticket,'ticket'); - const tbM=document.getElementById('tbody-profit-month'); if(tbM)tbM.innerHTML=profitRows(month,'month'); - const pg=document.getElementById('profit-total'); - if(pg)pg.textContent='共 '+(cust.length+ct.length+ticket.length+month.length)+' 条(客户'+cust.length+' · 货柜'+ct.length+' · 单票'+ticket.length+' · 单月'+month.length+')'; +function profitUpdateSummary(tickets){ + const sum=f=>tickets.reduce((s,d)=>s+(d[f]||0),0); + const income=sum('income'),cost=sum('cost'),profit=sum('profit'); + const fmt=n=>'¥'+n.toLocaleString('zh-CN'); + document.getElementById('profit-sum-income').textContent=fmt(income); + document.getElementById('profit-sum-cost').textContent=fmt(cost); + document.getElementById('profit-sum-profit').textContent=fmt(profit); + document.getElementById('profit-sum-rate').textContent=profitRate(profit,income).toFixed(1)+'%'; } -function profitInRange(m){ - const s=(document.getElementById('profit-start')||{}).value; - const e=(document.getElementById('profit-end')||{}).value; - if(s && me) return false; - return true; +function profitRenderAll(cust,ct,ticket,month,summaryTickets){ + profitCurrentData={customer:cust,container:ct,ticket:ticket,month:month}; + const tbC=document.getElementById('tbody-profit-customer');if(tbC)tbC.innerHTML=profitRows(cust,'customer'); + const tbT=document.getElementById('tbody-profit-container');if(tbT)tbT.innerHTML=profitRows(ct,'container'); + const tbK=document.getElementById('tbody-profit-ticket');if(tbK)tbK.innerHTML=profitRows(ticket,'ticket'); + const tbM=document.getElementById('tbody-profit-month');if(tbM)tbM.innerHTML=profitRows(month,'month'); + profitUpdateSummary(summaryTickets||profitVisibleTickets); + const current=profitCurrentData[profitTab]||[]; + const labels={customer:'客户',container:'货柜',ticket:'单票',month:'单月'}; + const pg=document.getElementById('profit-total');if(pg)pg.textContent='共 '+current.length+' 条'+labels[profitTab]+'维度数据'; +} +function profitInRange(month){ + const start=(document.getElementById('profit-start')||{}).value; + const end=(document.getElementById('profit-end')||{}).value; + return (!start||month>=start)&&(!end||month<=end); } function profitSearch(){ - const scope=(document.getElementById('profit-scope')||{}).value||'all'; + const scope=(document.getElementById('profit-scope')||{}).value||'customer'; + const start=(document.getElementById('profit-start')||{}).value; + const end=(document.getElementById('profit-end')||{}).value; + if(start&&end&&start>end){showToast('开始月份不能晚于结束月份','error');return;} const kw=((document.getElementById('profit-kw')||{}).value||'').trim().toLowerCase(); - const match=(d,fields)=>{ if(!kw) return true; return fields.some(f=>(''+(d[f]||'')).toLowerCase().includes(kw)); }; - let cust=profitCustomerData, ct=profitContainerData, ticket=profitTicketData, month=profitMonthData; + profitVisibleTickets=profitTicketData.filter(d=>profitInRange(d.month)); + let data={ + customer:profitAggregate(profitVisibleTickets,'customer'), + container:profitAggregate(profitVisibleTickets,'container'), + ticket:profitVisibleTickets.slice(), + month:profitAggregate(profitVisibleTickets,'month') + }; if(kw){ - cust=profitCustomerData.filter(d=>match(d,['cust'])); - ct=profitContainerData.filter(d=>match(d,['ctNo','cust'])); - ticket=profitTicketData.filter(d=>match(d,['ticket','cust','channel','ctNo'])); - if(scope!=='container'){ month=profitMonthData.filter(d=>match(d,['month','cust'])); } + const match=(values)=>values.some(v=>String(v||'').toLowerCase().includes(kw)); + data[scope]=data[scope].filter(d=>{ + if(scope==='customer')return match([d.cust].concat((d.tickets||[]).map(t=>t.ticket))); + if(scope==='container')return match([d.ctNo,d.cust].concat((d.tickets||[]).map(t=>t.ticket))); + if(scope==='ticket')return match([d.ticket,d.cust,d.channel,d.ctNo,d.month]); + return match([d.month].concat((d.tickets||[]).map(t=>t.ticket))); + }); } - // time range - month=month.filter(d=>profitInRange(d.month)); - ticket=ticket.filter(d=>profitInRange(d.month)); - const activeCust=new Set(profitTicketData.filter(d=>profitInRange(d.month)).map(d=>d.cust)); - const activeCt=new Set(profitTicketData.filter(d=>profitInRange(d.month)).map(d=>d.ctNo)); - cust=cust.filter(d=>activeCust.has(d.cust)); - ct=ct.filter(d=>activeCt.has(d.ctNo)); - profitRenderAll(cust,ct,ticket,month); - showToast('查询完成','success'); + const summaryTickets=scope==='ticket'?data.ticket:Array.from(new Map(data[scope].flatMap(d=>d.tickets||[]).map(t=>[t.ticket,t])).values()); + activateProfitTab(scope); + profitRenderAll(data.customer,data.container,data.ticket,data.month,summaryTickets); + showToast('查询完成,共 '+data[scope].length+' 条结果','success'); } function profitReset(){ - ['profit-scope','profit-kw','profit-start','profit-end'].forEach(id=>{const el=document.getElementById(id);if(el)el.value='';}); - profitScopeChange(); - profitRenderAll(profitCustomerData.slice(),profitContainerData.slice(),profitTicketData.slice(),profitMonthData.slice()); + const scope=document.getElementById('profit-scope');if(scope)scope.value='customer'; + ['profit-kw','profit-start','profit-end'].forEach(id=>{const el=document.getElementById(id);if(el)el.value='';}); + profitVisibleTickets=profitTicketData.slice(); + activateProfitTab('customer');profitSetPlaceholder('customer'); + profitRenderAll(profitAggregate(profitVisibleTickets,'customer'),profitAggregate(profitVisibleTickets,'container'),profitVisibleTickets.slice(),profitAggregate(profitVisibleTickets,'month'),profitVisibleTickets); } -function profitRate(p,i){return i>0?(p/i*100):0;} +function profitRate(profit,income){return income>0?(profit/income*100):0;} function profitTicketFeeDetail(t){ const I=t.income,C=t.cost; const i1=Math.round(I*0.68),i2=Math.round(I*0.22),i3=I-i1-i2; @@ -15611,12 +16336,10 @@ function openProfitDetail(dim,val){ const title=document.getElementById('profit-detail-title'); const body=document.getElementById('profit-detail-body'); const fmt=n=>'¥'+n.toLocaleString('zh-CN'); - let agg=null; - if(dim==='customer')agg=profitCustomerData.find(d=>d.cust===val); - else if(dim==='container')agg=profitContainerData.find(d=>d.ctNo===val); - else if(dim==='month')agg=profitMonthData.find(d=>d.month===val); - else if(dim==='ticket')agg=profitTicketData.find(d=>d.ticket===val); + const keyField={customer:'cust',container:'ctNo',month:'month',ticket:'ticket'}[dim]; + const agg=(profitCurrentData[dim]||[]).find(d=>d[keyField]===val); const label={customer:'客户',container:'货柜号',month:'月份',ticket:'单票'}[dim]; + if(!agg){showToast('未找到对应利润数据','error');return;} let head=''; if(agg){ const rate=profitRate(agg.profit,agg.income); @@ -15625,8 +16348,8 @@ function openProfitDetail(dim,val){ '
收入
'+fmt(agg.income)+'
'+ '
成本
'+fmt(agg.cost)+'
'+ '
利润
'+fmt(agg.profit)+'
'+ - '
利润率
'+(rate*100).toFixed(1)+'%
'+ - (agg.docs!==undefined?'
关联单据
'+agg.docs+' 笔
':'')+ + '
利润率
'+rate.toFixed(1)+'%
'+ + '
关联单据
'+(agg.docs||1)+' 笔
'+ ''; } title.textContent=label+':'+val; @@ -15636,12 +16359,12 @@ function openProfitDetail(dim,val){ const incRows=f.inc.map(r=>'
').join(''); const cosRows=f.cos.map(r=>'').join(''); const iSum=f.inc.reduce((s,r)=>s+r.a,0),cSum=f.cos.reduce((s,r)=>s+r.a,0); - html+='
'+ + html+='
关联货柜:'+agg.ctNo+'客户:'+agg.cust+'渠道:'+agg.channel+'业务月份:'+agg.month+'
'+ '
收入明细
托盘编码 托盘类型 托盘容积(m³)托盘重量(kg) 状态 备注 操作 ${d.fbaTrack} ${d.pkgNo} ${d.skuNk}${nSelf||'-'}${nCust||'-'}${nWh||'-'} ${cDoc?'📎 '+(cDoc.length>12?cDoc.substring(0,12)+'...':cDoc)+'':''} ${tDoc?'📎 '+(tDoc.length>12?tDoc.substring(0,12)+'...':tDoc)+'':''}
请选择上方清单查看明细
'+escapeMfAttr(d.sku)+''+escapeMfAttr(d.cnName)+''+escapeMfAttr(d.batchNo)+''+escapeMfAttr(d.palletNo)+''+escapeMfAttr(d.wh)+''+available+'
${d.code} ${d.type} ${Number(d.vol).toFixed(2)}${Number(d.weight||0).toFixed(2)} ${d.status} ${d.remark||'-'} 编辑   删除
暂无数据
暂无符合条件的数据
'+d.cust+''+fmt(d.income)+''+fmt(d.cost)+''+fmt(d.profit)+''+rateTxt+''+d.docs+'
'+d.ctNo+''+d.cust+''+fmt(d.income)+''+fmt(d.cost)+''+fmt(d.profit)+''+rateTxt+'
'+d.ticket+''+d.cust+''+d.channel+''+fmt(d.income)+''+fmt(d.cost)+''+fmt(d.profit)+''+rateTxt+'
'+d.month+''+fmt(d.income)+''+fmt(d.cost)+''+fmt(d.profit)+''+rateTxt+''+d.docs+'
'+d.cust+''+fmt(d.income)+''+fmt(d.cost)+''+fmt(d.profit)+''+rateTxt+''+profitRelatedDocs(d)+''+profitViewButton('customer',d.cust)+'
'+d.ctNo+''+d.cust+''+fmt(d.income)+''+fmt(d.cost)+''+fmt(d.profit)+''+rateTxt+''+profitRelatedDocs(d)+''+profitViewButton('container',d.ctNo)+'
'+d.ticket+''+d.cust+''+d.channel+''+d.month+''+fmt(d.income)+''+fmt(d.cost)+''+fmt(d.profit)+''+rateTxt+''+d.ctNo+''+profitViewButton('ticket',d.ticket)+'
'+d.month+''+fmt(d.income)+''+fmt(d.cost)+''+fmt(d.profit)+''+rateTxt+''+profitRelatedDocs(d)+''+profitViewButton('month',d.month)+'
合计'+fmt(tIncome)+''+fmt(tCost)+''+fmt(tProfit)+''+tRate.toFixed(1)+'%'+sum('docs')+'合计'+fmt(tIncome)+''+fmt(tCost)+''+fmt(tProfit)+''+tRate.toFixed(1)+'%合计'+fmt(tIncome)+''+fmt(tCost)+''+fmt(tProfit)+''+tRate.toFixed(1)+'%合计'+fmt(tIncome)+''+fmt(tCost)+''+fmt(tProfit)+''+tRate.toFixed(1)+'%'+sum('docs')+'
'+fmt(tIncome)+''+fmt(tCost)+''+fmt(tProfit)+''+tRate.toFixed(1)+'%
合计'+sum('docs')+' 笔单据合计'+sum('docs')+' 笔单据合计'+arr.length+' 个货柜关联合计'+sum('docs')+' 笔单据
'+r.n+''+fmt(r.a)+'
'+r.n+''+fmt(r.a)+'
'+incRows+'
费用项金额
小计'+fmt(iSum)+'
'+ '
成本明细
'+cosRows+'
费用项金额
小计'+fmt(cSum)+'
'+ ''; } else { - const rows=profitTicketData.filter(x=>({customer:x.cust,container:x.ctNo,month:x.month}[dim]===val)); + const rows=agg.tickets||profitVisibleTickets.filter(x=>({customer:x.cust,container:x.ctNo,month:x.month}[dim]===val)); if(!rows.length){ html+='
该'+label+'下暂无明细单据
'; } else { @@ -15650,10 +16373,10 @@ function openProfitDetail(dim,val){ const rate=profitRate(t.profit,t.income); const rc=rate>=0?'#52c41a':'#f5222d'; sI+=t.income;sC+=t.cost;sP+=t.profit; - rb+=''+t.ticket+''+t.cust+''+t.channel+''+t.ctNo+''+fmt(t.income)+''+fmt(t.cost)+''+fmt(t.profit)+''+(rate*100).toFixed(1)+'%'; + rb+=''+t.cust+''+t.channel+''+t.ctNo+''+fmt(t.income)+''+fmt(t.cost)+''+fmt(t.profit)+''+rate.toFixed(1)+'%'; }); const tRate=profitRate(sP,sI); - rb+='本页合计('+rows.length+' 单)'+fmt(sI)+''+fmt(sC)+''+fmt(sP)+''+(tRate*100).toFixed(1)+'%'; + rb+='本页合计('+rows.length+' 单)'+fmt(sI)+''+fmt(sC)+''+fmt(sP)+''+tRate.toFixed(1)+'%'; html+='
关联单票明细('+rows.length+' 单)
'+rb+'
单票号客户渠道货柜号收入成本利润利润率
'; } } @@ -15840,6 +16563,21 @@ function deleteVehicle(key,id){ } // ==================== NAVIGATION ==================== +function toggleTopMessageMenu(event){ + if(event)event.stopPropagation(); + const menu=document.getElementById('topMessageMenu'); + if(menu)menu.classList.toggle('open'); +} +function openTopMessagePage(page,event){ + if(event)event.stopPropagation(); + const menu=document.getElementById('topMessageMenu'); + if(menu)menu.classList.remove('open'); + switchPage(page); +} +document.addEventListener('click',function(event){ + const menu=document.getElementById('topMessageMenu'); + if(menu&&!menu.contains(event.target))menu.classList.remove('open'); +}); let currentPage='dashboard'; function switchPage(page,el){ document.querySelectorAll('.page').forEach(p=>p.classList.remove('active')); @@ -16380,6 +17118,76 @@ hideModal('modal-package-edit'); renderPackage(); } +// ==================== 全局表格与查询区规范化 ==================== +let _unifiedTableFrame=0; + +function tableDirectCells(row){ + return Array.from(row.children).filter(function(cell){return cell.tagName==='TH'||cell.tagName==='TD';}); +} + +function normalizeTableOperationColumn(table){ + if(!table||!table.tHead)return; + const headerRows=Array.from(table.tHead.rows); + const headerRow=headerRows.find(function(row){ + return tableDirectCells(row).some(function(cell){return cell.textContent.replace(/[\s↑↓↕]/g,'')==='操作';}); + }); + if(!headerRow)return; + const headerCells=tableDirectCells(headerRow); + const operationHeader=headerCells.find(function(cell){return cell.textContent.replace(/[\s↑↓↕]/g,'')==='操作';}); + if(!operationHeader)return; + let sourceIndex=Number(table.dataset.operationSourceIndex); + if(!Number.isInteger(sourceIndex)||sourceIndex<0){ + sourceIndex=headerCells.indexOf(operationHeader); + table.dataset.operationSourceIndex=String(sourceIndex); + } + const firstHeader=headerCells[0]; + const hasSelectionFirst=!!(firstHeader&&(firstHeader.querySelector('input[type="checkbox"],input[type="radio"]')||firstHeader.textContent.trim()==='')); + const keepOperationAtEnd=!!table.closest('#page-tenant-list,#page-warehouse-tier-fee'); + const targetIndex=keepOperationAtEnd?headerCells.length-1:(hasSelectionFirst?1:0); + operationHeader.classList.add('unified-operation-col'); + operationHeader.dataset.operationColumn='1'; + let currentHeaderCells=tableDirectCells(headerRow); + if(currentHeaderCells.indexOf(operationHeader)!==targetIndex){ + headerRow.insertBefore(operationHeader,currentHeaderCells[targetIndex]||null); + } + const expectedCount=headerCells.length; + Array.from(table.tBodies).forEach(function(tbody){ + Array.from(tbody.rows).forEach(function(row){ + let cells=tableDirectCells(row); + if(cells.length!==expectedCount)return; + let operationCell=cells.find(function(cell){return cell.dataset.operationColumn==='1';}); + if(!operationCell)operationCell=cells[sourceIndex]; + if(!operationCell)return; + operationCell.dataset.operationColumn='1'; + operationCell.classList.add('unified-operation-col'); + cells=tableDirectCells(row); + if(cells.indexOf(operationCell)!==targetIndex)row.insertBefore(operationCell,cells[targetIndex]||null); + }); + }); + table.classList.add('unified-data-table'); +} + +function normalizeAllListPresentation(){ + document.querySelectorAll('.page table').forEach(normalizeTableOperationColumn); + document.querySelectorAll('.page .search-row,.page .inbound-search-section,.page #rsq-wrap,.page .ds-search').forEach(function(panel){panel.classList.add('unified-search-panel');}); +} + +function scheduleListPresentationNormalize(){ + if(_unifiedTableFrame)return; + _unifiedTableFrame=requestAnimationFrame(function(){ + _unifiedTableFrame=0; + normalizeAllListPresentation(); + }); +} + +function initUnifiedListPresentation(){ + normalizeAllListPresentation(); + const observer=new MutationObserver(function(mutations){ + if(mutations.some(function(m){return m.type==='childList'&&m.addedNodes.length;}))scheduleListPresentationNormalize(); + }); + observer.observe(document.body,{childList:true,subtree:true}); +} + // ==================== INIT ==================== document.addEventListener('DOMContentLoaded',()=>{ initCharts(); @@ -16448,6 +17256,7 @@ renderCost(); renderUsers(); renderRoles(); renderSysTenant(); +initUnifiedListPresentation(); }); // ==================== BRAND SETTINGS ==================== @@ -16528,6 +17337,7 @@ function toggleContainerFields(){ // ==================== DASHBOARD FULLSCREEN ==================== let _fsChartTimer=null; +let _fsResizeTimer=null; function toggleDashboardFullscreen(){ const isFs=document.body.classList.contains('dashboard-fullscreen'); if(!isFs){ @@ -16541,6 +17351,7 @@ if(!isFs){ // Start clock updateFsClock(); _fsChartTimer=setInterval(updateFsClock,1000); + refreshDashboardData(false); // Update btn text const btn=document.getElementById('dashboardFullscreenBtn'); if(btn){btn.textContent='⛶ 退出全屏';btn.title='退出全屏模式';} @@ -16555,6 +17366,7 @@ document.body.classList.remove('dashboard-fullscreen'); if(document.exitFullscreen&&document.fullscreenElement)document.exitFullscreen().catch(()=>{}); else if(document.webkitExitFullscreen&&document.webkitFullscreenElement)document.webkitExitFullscreen(); if(_fsChartTimer){clearInterval(_fsChartTimer);_fsChartTimer=null;} +scheduleDashboardResize(); const btn=document.getElementById('dashboardFullscreenBtn'); if(btn){btn.textContent='⛶ 数据看板全屏';btn.title='首页看板全屏';} } @@ -16562,9 +17374,33 @@ if(btn){btn.textContent='⛶ 数据看板全屏';btn.title='首页看板全屏'; function updateFsClock(){ const d=new Date(); const pad=n=>String(n).padStart(2,'0'); -const timeStr=`${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; -const el=document.getElementById('fsClock'); -if(el)el.textContent=timeStr; +const dateEl=document.getElementById('fsDate'); +const timeEl=document.getElementById('fsClock'); +if(dateEl)dateEl.textContent=`${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())}`; +if(timeEl)timeEl.textContent=`${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; +} + +function scheduleDashboardResize(){ +if(_fsResizeTimer)clearTimeout(_fsResizeTimer); +_fsResizeTimer=setTimeout(()=>{ + window.dispatchEvent(new Event('resize')); + if(window.Chart&&Chart.instances){ + Object.keys(Chart.instances).forEach(key=>{const chart=Chart.instances[key];if(chart&&chart.resize)chart.resize();}); + } + _fsResizeTimer=null; +},220); +} + +function refreshDashboardData(showFeedback=true){ +const d=new Date(); +const pad=n=>String(n).padStart(2,'0'); +const stamp=`${d.getFullYear()}-${pad(d.getMonth()+1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}`; +const updated=document.getElementById('dashboardUpdatedAt'); +const fsUpdated=document.getElementById('fsUpdatedAt'); +if(updated)updated.textContent=stamp; +if(fsUpdated)fsUpdated.textContent=stamp; +scheduleDashboardResize(); +if(showFeedback)showToast('数据看板已刷新','success'); } // ==================== 入库单列表 - 合关报关 ==================== @@ -17126,7 +17962,7 @@ var tb=document.getElementById('tbody-arap'); if(!tb)return; var auditCls={'财务通过':'color:#52c41a;background:#f6ffed;border:1px solid #b7eb8f;padding:2px 10px;border-radius:10px;font-size:12px','客服提交':'color:#1890ff;background:#e6f7ff;border:1px solid #91d5ff;padding:2px 10px;border-radius:10px;font-size:12px','新增':'color:#fa8c16;background:#fff7e6;border:1px solid #ffd591;padding:2px 10px;border-radius:10px;font-size:12px'}; tb.innerHTML=arapData.map(function(d,i){ -var ops='详情' +var ops='详情' +'审核' +'编辑' +'禁止结算' @@ -17149,6 +17985,63 @@ return ''+item[0]+''+item[1]+'';}).join(''); + + var steps=[{name:'费用已生成',done:true,desc:'业务单据触发费用生成'},{name:'客服已提交',done:d.auditStatus!=='新增',desc:d.auditStatus==='新增'?'等待客服确认':'费用信息已提交财务'},{name:'财务已审核',done:d.auditStatus==='财务通过',desc:d.auditStatus==='财务通过'?'审核通过,可参与对账':'等待财务审核'}]; + document.getElementById('arap-detail-timeline').innerHTML=steps.map(function(step,i){return '
'+(step.done?'✓':'·')+''+(i':'')+'
'+step.name+'
'+step.desc+'
';}).join(''); + + document.getElementById('tbody-arap-detail-business').innerHTML=''+(d.whCode||'—')+''+(d.orderStatus||'—')+''+(d.dispatchNo||'—')+''+(d.containerNo||'—')+''+(d.cabinetNo||'—')+''+(d.billNo||'暂未生成')+''; + var baseDay=String(10+currentArapDetailIndex).padStart(2,'0'); + var logs=[{time:'2026-07-'+baseDay+' 09:20',node:'生成费用',operator:'系统',result:'成功',remark:'由业务单据自动生成'}]; + if(d.auditStatus!=='新增')logs.push({time:'2026-07-'+baseDay+' 10:05',node:'客服提交',operator:'客服专员',result:'已提交',remark:'费用信息核对完成'}); + if(d.auditStatus==='财务通过')logs.push({time:'2026-07-'+baseDay+' 14:30',node:'财务审核',operator:'财务专员',result:'审核通过',remark:'允许参与结算'}); + document.getElementById('tbody-arap-detail-log').innerHTML=logs.map(function(log){return ''+log.time+''+log.node+''+log.operator+''+log.result+''+log.remark+'';}).join(''); +} + // ============ 客户对账单 ============ var customerReconStmtData=[ {id:'Y2607095',status:'待审核',customer:'杭州恺予科技有限公司',startDate:'2026-07-21',endDate:'2026-08-20',period:7,currency:'CNY',rate:6.8,totalAr:86809.8,paidAr:0,lastPayTime:'',settled:'否',invoiceDate:'',remark:'',creator:'叶珊珊',createDate:'2026-07-21 14:14:22',updater:'叶珊珊',updateDate:'2026-07-21 14:14:22'}, @@ -17386,6 +18279,8 @@ function renderSettlementAr(){ totalAr+=d.arAmount;totalRec+=d.received;totalBal+=d.balance; return ''+ ''+ + '详情 '+ + (d.balance>0&&d.status==='生效'?'':'')+''+ ''+d.id+''+ ''+d.customer+''+ ''+d.feeType+''+ @@ -17402,7 +18297,6 @@ function renderSettlementAr(){ ''+d.creator+''+ ''+(d.reviewer||'-')+''+ ''+d.status+''+ - '详情 编辑'+ ''; }).filter(Boolean); tb.innerHTML=rows.length?rows.join(''):'暂无匹配数据'; @@ -17418,10 +18312,208 @@ function resetSettlementAr(){ renderSettlementAr(); } // ============ 结算单应付 ============ +var settlementApData=[ + {id:'SAP-202606-001',supplier:'中远海运集装箱运输有限公司',feeType:'海运费',currency:'CNY',apAmount:168000,paid:100000,balance:68000,billNo:'SUP-RECON-202606-001',period:'2026-06',genDate:'2026-06-25',dueDate:'2026-07-25',payStatus:'部分付款',approveStatus:'已审批',hasProfit:'是',profitAmount:12600,creator:'苏天雅',reviewer:'王建国',status:'生效'}, + {id:'SAP-202606-002',supplier:'马士基(中国)航运有限公司',feeType:'订舱费',currency:'USD',apAmount:28600,paid:0,balance:28600,billNo:'SUP-RECON-202606-002',period:'2026-06',genDate:'2026-06-28',dueDate:'2026-07-28',payStatus:'待付款',approveStatus:'已审批',hasProfit:'否',profitAmount:0,creator:'苏天雅',reviewer:'王建国',status:'生效'}, + {id:'SAP-202607-001',supplier:'宁波中基惠通报关有限公司',feeType:'报关费',currency:'CNY',apAmount:42600,paid:42600,balance:0,billNo:'SUP-RECON-202607-001',period:'2026-07',genDate:'2026-07-05',dueDate:'2026-08-05',payStatus:'已结清',approveStatus:'已审批',hasProfit:'否',profitAmount:0,creator:'李明',reviewer:'王建国',status:'生效'}, + {id:'SAP-202607-002',supplier:'宁波北仑顺达集装箱运输',feeType:'拖车费',currency:'CNY',apAmount:93500,paid:35000,balance:58500,billNo:'SUP-RECON-202607-002',period:'2026-07',genDate:'2026-07-12',dueDate:'2026-08-12',payStatus:'部分付款',approveStatus:'已审批',hasProfit:'是',profitAmount:6800,creator:'李芳',reviewer:'王建国',status:'生效'}, + {id:'SAP-202607-003',supplier:'华测检测认证集团',feeType:'认证费',currency:'CNY',apAmount:31800,paid:0,balance:31800,billNo:'SUP-RECON-202607-003',period:'2026-07',genDate:'2026-07-18',dueDate:'2026-08-18',payStatus:'待付款',approveStatus:'审批中',hasProfit:'否',profitAmount:0,creator:'苏天雅',reviewer:'',status:'待审批'} +]; + function renderSettleAp(){ -var tb=document.getElementById('tbody-settle-ap'); -if(!tb)return; -tb.innerHTML='暂无数据'; + var tb=document.getElementById('tbody-settle-ap'); + if(!tb)return; + var status=document.getElementById('sap-status')?document.getElementById('sap-status').value:'全部状态'; + var supplier=document.getElementById('sap-supplier')?document.getElementById('sap-supplier').value:'全部供应商'; + var currency=document.getElementById('sap-currency')?document.getElementById('sap-currency').value:'全部币种'; + var no=(document.getElementById('sap-no')?document.getElementById('sap-no').value:'').trim().toLowerCase(); + var payCls={'已结清':'tag-green','部分付款':'tag-orange','待付款':'tag-red'}; + var approveCls={'已审批':'tag-green','审批中':'tag-orange','草稿':'tag-gray'}; + var statusCls={'生效':'tag-green','待审批':'tag-orange','草稿':'tag-gray'}; + var total=0,paid=0,balance=0,profit=0; + var rows=settlementApData.map(function(d){ + if(status!=='全部状态'&&d.payStatus!==status)return null; + if(supplier!=='全部供应商'&&d.supplier!==supplier)return null; + if(currency!=='全部币种'&&d.currency!==currency)return null; + if(no&&!d.id.toLowerCase().includes(no)&&!d.billNo.toLowerCase().includes(no))return null; + total+=d.apAmount;paid+=d.paid;balance+=d.balance;profit+=d.profitAmount||0; + var symbol=d.currency==='USD'?'$':'¥'; + return ''+ + '详情 '+(d.balance>0&&d.status==='生效'?'':'')+''+d.id+''+d.supplier+''+d.feeType+''+d.currency+''+ + ''+symbol+d.apAmount.toLocaleString()+''+symbol+d.paid.toLocaleString()+''+symbol+d.balance.toLocaleString()+''+ + ''+d.billNo+''+d.period+''+d.genDate+''+d.dueDate+''+ + ''+d.payStatus+''+d.approveStatus+''+ + ''+d.hasProfit+''+symbol+(d.profitAmount||0).toLocaleString()+''+d.creator+''+(d.reviewer||'-')+''+d.status+''; + }).filter(Boolean); + tb.innerHTML=rows.length?rows.join(''):'暂无匹配数据'; + document.getElementById('sap-total').textContent='¥'+total.toLocaleString(); + document.getElementById('sap-paid').textContent='¥'+paid.toLocaleString(); + document.getElementById('sap-pending').textContent='¥'+balance.toLocaleString(); + document.getElementById('sap-profit').textContent='¥'+profit.toLocaleString(); +} + +function resetSettlementAp(){ + document.getElementById('sap-status').selectedIndex=0; + document.getElementById('sap-supplier').selectedIndex=0; + document.getElementById('sap-currency').selectedIndex=0; + document.getElementById('sap-no').value=''; + renderSettleAp(); +} + +var currentSettlementDetail=null; + +function settlementDetailRecord(){ + if(!currentSettlementDetail)return null; + var list=currentSettlementDetail.type==='ar'?settlementArData:settlementApData; + return list.find(function(d){return d.id===currentSettlementDetail.id;})||null; +} + +function openSettlementDetail(type,id){ + currentSettlementDetail={type:type,id:id}; + switchPage('settlement-detail'); + var main=document.getElementById('mainContent'); + if(main)main.scrollTop=0; + window.scrollTo(0,0); + renderSettlementDetail(); +} + +function backFromSettlementDetail(){ + var page=currentSettlementDetail&¤tSettlementDetail.type==='ap'?'settlement-ap':'settlement-ar'; + switchPage(page); +} + +function partialFromSettlementDetail(){ + if(currentSettlementDetail)showPartialSettlement(currentSettlementDetail.type,currentSettlementDetail.id); +} + +function renderSettlementDetail(){ + var record=settlementDetailRecord(); + if(!record)return; + var isAr=currentSettlementDetail.type==='ar'; + var party=isAr?record.customer:record.supplier; + var total=isAr?record.arAmount:record.apAmount; + var done=isAr?record.received:record.paid; + var kind=isAr?'应收':'应付'; + var action=isAr?'收款':'付款'; + var symbol=record.currency==='USD'?'$':'¥'; + var money=function(v){return symbol+Number(v||0).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2});}; + document.getElementById('settlement-detail-breadcrumb').textContent='结算单'+kind; + document.getElementById('settlement-detail-breadcrumb-no').textContent=record.id; + document.getElementById('settlement-detail-title').textContent=kind+'结算单 · '+record.id; + var statusEl=document.getElementById('settlement-detail-status'); + statusEl.textContent=record.payStatus; + statusEl.className='tag '+(record.payStatus==='已结清'?'tag-green':record.payStatus.indexOf('部分')===0?'tag-orange':'tag-red'); + document.getElementById('settlement-detail-total-label').textContent=kind+'金额'; + document.getElementById('settlement-detail-done-label').textContent='已'+action+'金额'; + document.getElementById('settlement-detail-total').textContent=money(total); + document.getElementById('settlement-detail-done').textContent=money(done); + document.getElementById('settlement-detail-balance').textContent=money(record.balance); + document.getElementById('settlement-detail-due').textContent=record.dueDate; + var days=Math.ceil((new Date(record.dueDate+'T23:59:59')-new Date())/86400000); + var dueHint=document.getElementById('settlement-detail-due-hint'); + dueHint.textContent=record.balance===0?'已结清':days<0?'已逾期 '+Math.abs(days)+' 天':days===0?'今日到期':'距到期 '+days+' 天'; + dueHint.style.color=record.balance===0?'#16a34a':days<=7?'#ef4444':'#64748b'; + var partialBtn=document.getElementById('settlement-detail-partial-btn'); + partialBtn.textContent='部分'+action; + partialBtn.style.display=record.balance>0&&record.status==='生效'?'inline-flex':'none'; + + var info=[ + ['结算单号',record.id],[(isAr?'客户公司':'供应商'),party],['费用类型',record.feeType],['币种',record.currency], + ['关联账单',record.billNo],['业务期间',record.period],['生成日期',record.genDate],['到期日期',record.dueDate], + ['制单人',record.creator],['当前状态',record.status] + ]; + document.getElementById('settlement-detail-info').innerHTML=info.map(function(item){return '
'+item[0]+''+item[1]+'
';}).join(''); + var approved=record.approveStatus==='已审批'; + document.getElementById('settlement-detail-approval').innerHTML='
✓
结算单已创建
'+record.creator+' · '+record.genDate+'
'+(approved?'✓':'…')+'
'+record.approveStatus+'
'+(record.reviewer||'等待审核人处理')+'
'; + + document.getElementById('settlement-detail-fee-done-label').textContent='已'+action+'金额'; + document.getElementById('settlement-detail-fee-summary').textContent='共 1 项费用'; + document.getElementById('tbody-settlement-detail-fee').innerHTML=''+record.billNo+''+record.feeType+''+record.period+''+record.currency+''+money(total)+''+money(done)+''+money(record.balance)+''+record.payStatus+''; + + var records=(record.paymentRecords||[]).slice(); + var recorded=records.reduce(function(sum,r){return sum+Number(r.amount||0);},0); + if(done-recorded>0.001)records.push({amount:done-recorded,date:record.lastPaymentDate||record.genDate,method:'历史累计',reference:'—',remark:'系统期初数据',operator:record.creator}); + document.getElementById('settlement-detail-record-title').textContent=action+'记录'; + document.getElementById('settlement-detail-record-date-label').textContent=action+'日期'; + document.getElementById('settlement-detail-record-count').textContent='共 '+records.length+' 条'; + document.getElementById('tbody-settlement-detail-record').innerHTML=records.length?records.map(function(r,i){return ''+(i+1)+''+r.date+''+money(r.amount)+''+(r.method||'银行转账')+''+(r.reference||'—')+''+(r.remark||'—')+''+(r.operator||'张经理')+'';}).join(''):'暂无'+action+'记录'; +} + +var currentPartialSettlement=null; + +function partialSettlementRecord(){ + if(!currentPartialSettlement)return null; + var list=currentPartialSettlement.type==='ar'?settlementArData:settlementApData; + return list.find(function(d){return d.id===currentPartialSettlement.id;})||null; +} + +function partialSettlementMoney(record,value){ + var symbol=record.currency==='USD'?'$':'¥'; + return symbol+Number(value||0).toLocaleString(undefined,{minimumFractionDigits:2,maximumFractionDigits:2}); +} + +function showPartialSettlement(type,id){ + currentPartialSettlement={type:type,id:id}; + var record=partialSettlementRecord(); + if(!record)return; + var isAr=type==='ar'; + var total=isAr?record.arAmount:record.apAmount; + var done=isAr?record.received:record.paid; + document.getElementById('partial-settlement-title').textContent=isAr?'部分收款登记':'部分付款登记'; + document.getElementById('partial-settlement-no').textContent=record.id; + document.getElementById('partial-settlement-party-label').textContent=isAr?'客户公司':'供应商'; + document.getElementById('partial-settlement-party').textContent=isAr?record.customer:record.supplier; + document.getElementById('partial-total-label').textContent=isAr?'应收金额':'应付金额'; + document.getElementById('partial-done-label').textContent=isAr?'已收金额':'已付金额'; + document.getElementById('partial-amount-label').textContent=isAr?'本次收款金额 *':'本次付款金额 *'; + document.getElementById('partial-date-label').textContent=isAr?'收款日期 *':'付款日期 *'; + document.getElementById('partial-method-label').textContent=isAr?'收款方式 *':'付款方式 *'; + document.getElementById('partial-submit-btn').textContent=isAr?'确认收款':'确认付款'; + document.getElementById('partial-total').textContent=partialSettlementMoney(record,total); + document.getElementById('partial-done').textContent=partialSettlementMoney(record,done); + document.getElementById('partial-balance').textContent=partialSettlementMoney(record,record.balance); + var amount=document.getElementById('partial-amount'); + amount.value='';amount.max=record.balance;amount.placeholder='最多 '+partialSettlementMoney(record,record.balance); + var now=new Date(),pad=function(n){return String(n).padStart(2,'0');}; + document.getElementById('partial-date').value=now.getFullYear()+'-'+pad(now.getMonth()+1)+'-'+pad(now.getDate()); + document.getElementById('partial-method').selectedIndex=0; + document.getElementById('partial-reference').value=''; + document.getElementById('partial-remark').value=''; + document.getElementById('partial-after-balance').textContent=partialSettlementMoney(record,record.balance); + showModal('modal-partial-settlement'); + setTimeout(function(){amount.focus();},80); +} + +function updatePartialSettlementPreview(){ + var record=partialSettlementRecord(); + if(!record)return; + var amount=Number(document.getElementById('partial-amount').value)||0; + document.getElementById('partial-after-balance').textContent=partialSettlementMoney(record,Math.max(0,record.balance-amount)); +} + +function confirmPartialSettlement(){ + var record=partialSettlementRecord(); + if(!record)return; + var isAr=currentPartialSettlement.type==='ar'; + var amount=Number(document.getElementById('partial-amount').value); + var date=document.getElementById('partial-date').value; + if(!amount||amount<=0){showToast('请输入有效的'+(isAr?'收款':'付款')+'金额','warning');return;} + if(amount>record.balance){showToast('本次金额不能超过当前余额','warning');return;} + if(!date){showToast('请选择'+(isAr?'收款':'付款')+'日期','warning');return;} + if(isAr)record.received=Number((record.received+amount).toFixed(2)); + else record.paid=Number((record.paid+amount).toFixed(2)); + var total=isAr?record.arAmount:record.apAmount; + var done=isAr?record.received:record.paid; + record.balance=Number(Math.max(0,total-done).toFixed(2)); + record.payStatus=record.balance===0?'已结清':(isAr?'部分收款':'部分付款'); + record.lastPaymentDate=date; + if(!record.paymentRecords)record.paymentRecords=[]; + record.paymentRecords.unshift({amount:amount,date:date,method:document.getElementById('partial-method').value,reference:document.getElementById('partial-reference').value,remark:document.getElementById('partial-remark').value}); + hideModal('modal-partial-settlement'); + if(isAr)renderSettlementAr();else renderSettleAp(); + if(document.getElementById('page-settlement-detail').classList.contains('active'))renderSettlementDetail(); + showToast((isAr?'收款':'付款')+'登记成功,剩余余额 '+partialSettlementMoney(record,record.balance),'success'); + currentPartialSettlement=null; } var supplierData=[ @@ -18328,5 +19420,139 @@ function cancelRelease(){ +