@@ -965,11 +965,35 @@ window.deletePlanItem = async () => {
}
}
// 从 planFinances 计算月度数据
function computePlanMonthly ( planFinances ) {
var pfs = planFinances || [ ] ;
var months = [ ] ;
var year = new Date ( ) . getFullYear ( ) ;
for ( var mi = 1 ; mi <= 12 ; mi ++ ) { months . push ( year + '-' + String ( mi ) . padStart ( 2 , '0' ) ) ; }
var data = [ ] ;
for ( var i = 0 ; i < 12 ; i ++ ) {
var month = months [ i ] ;
var sign = 0 , revenue = 0 , gross = 0 , payment = 0 , cost = 0 , paid = 0 ;
for ( var j = 0 ; j < pfs . length ; j ++ ) {
var pf = pfs [ j ] ;
if ( ( pf . sign _month || '' ) === month ) sign += parseFloat ( pf . sign _amount || 0 ) ;
var bd = [ ] ; try { bd = JSON . parse ( pf . budget _data || '[]' ) ; } catch ( e ) { }
var b = bd . find ( function ( x ) { return ( x . month || '' ) === month ; } ) ;
if ( b ) { revenue += parseFloat ( b . rev || 0 ) ; gross += parseFloat ( b . gross || 0 ) ; payment += parseFloat ( b . payment || 0 ) ; }
var ed = [ ] ; try { ed = JSON . parse ( pf . expense _data || '[]' ) ; } catch ( e ) { }
var e = ed . find ( function ( x ) { return ( x . month || '' ) === month ; } ) ;
if ( e ) { cost += parseFloat ( e . cost || 0 ) ; paid += parseFloat ( e . paid || 0 ) ; }
}
data . push ( { month : month , sign : Math . round ( sign ) , revenue : Math . round ( revenue ) , gross : Math . round ( gross ) , payment : Math . round ( payment ) , cost : Math . round ( cost ) , paid : Math . round ( paid ) } ) ;
}
return data ;
}
// 总览渲染
function renderPlanOverview ( ) {
const { summary , financeMonthly } = state . data ;
if ( ! summary ) return ;
const m = summary . metrics ;
const planFinances = state . data . planFinances || [ ] ;
const fm = computePlanMonthly ( planFinances ) ;
const moneyIntL = ( v ) => Math . round ( Number ( v || 0 ) ) . toLocaleString ( "zh-CN" ) ;
const moneyWan = ( v ) => {
const n = Number ( v || 0 ) ;
@@ -977,20 +1001,20 @@ function renderPlanOverview() {
} ;
const card = ( body , cls ) => '<div class="card ' + ( cls || 'p-4' ) + '">' + body + '</div>' ;
// 月度数据 (financeMonthly 有12个月)
const fm = financeMonthly || [ ] ;
// 从 planFinances 计算年度累计
var annualSums = { sign : 0 , revenue : 0 , gross : 0 , cost : 0 , payment : 0 , paid : 0 } ;
for ( var i = 0 ; i < fm . length ; i ++ ) {
annualSums . sign += fm [ i ] . sign ; annualSums . revenue += fm [ i ] . revenue ;
annualSums . gross += fm [ i ] . gross ; annualSums . cost += fm [ i ] . cost ;
annualSums . payment += fm [ i ] . payment ; annualSums . paid += fm [ i ] . paid ;
}
const mLbl = [ '1月' , '2月' , '3月' , '4月' , '5月' , '6月' , '7月' , '8月' , '9月' , '10月' , '11月' , '12月' ] ;
const LABELS = [ '合同金额' , '确收金额' , '确收毛利' , '成本' , '项目利润' , '回款金额' , '已付' , '现金流' ] ;
// 每行的年度累计 + 12个月值
const annualVals = [
m . signed _annual || m . signed _amount || 0 ,
m . revenue _annual || 0 ,
m . gross _annual || 0 ,
m . cost _annual || 0 ,
m . profit _annual || 0 ,
m . payment _annual || 0 ,
m . paid _annual || 0 ,
m . cashflow _annual || 0 ,
annualSums . sign , annualSums . revenue , annualSums . gross , annualSums . cost ,
annualSums . gross - annualSums . cost , annualSums . payment , annualSums . paid ,
annualSums . payment - annualSums . paid ,
] ;
// 字段映射: sign, revenue, gross, cost, profit(=gross-cost), payment, paid, cashflow(=payment-paid)
const fmFields = [ 'sign' , 'revenue' , 'gross' , 'cost' , null , 'payment' , 'paid' , null ] ;
@@ -1027,23 +1051,27 @@ function renderPlanOverview() {
if ( window . lucide ) window . lucide . createIcons ( ) ;
// 渲染图表(复用 home.js 的 moneyTick / chartOptions / monthLabels)
if ( financeMonthly && window . Chart ) {
renderPlanCharts ( financeMonthly ) ;
if ( fm && window . Chart ) {
renderPlanCharts ( fm ) ;
}
}
function renderPlanQuarterlyOverview ( ) {
const { summary , financeMonthly } = state . data ;
if ( ! summary ) return ;
const m = summary . metrics ;
const planFinances = state . data . planFinances || [ ] ;
const fm = computePlanMonthly ( planFinances ) ;
const moneyIntL = ( v ) => Math . round ( Number ( v || 0 ) ) . toLocaleString ( "zh-CN" ) ;
const card = ( body , cls ) => '<div class="card ' + ( cls || 'p-4' ) + '">' + body + '</div>' ;
const fm = financeMonthly || [ ] ;
const LABELS = [ '合同金额' , '确收金额' , '确收毛利' , '成本' , '项目利润' , '回款金额' , '已付' , '现金流' ] ;
const qLbl = [ 'Q1' , 'Q2' , 'Q3' , 'Q4' ] ;
const qRanges = [ [ 0 , 1 , 2 ] , [ 3 , 4 , 5 ] , [ 6 , 7 , 8 ] , [ 9 , 10 , 11 ] ] ;
const annualVals = [ m . signed _annual || m . signed _amount || 0 , m . revenue_annual || 0 , m . gross _annual || 0 , m . cost _annual || 0 , m . profit _annual || 0 , m . payment_annual || 0 , m . paid _annual || 0 , m . cashflow _annual || 0 ] ;
var annualSums2 = { sign : 0 , revenue : 0 , gross : 0 , cost : 0 , payment : 0 , paid : 0 } ;
for ( var i2 = 0 ; i2 < fm . length ; i2 ++ ) {
annualSums2 . sign += fm [ i2 ] . sign ; annualSums2 . revenue += fm [ i2 ] . revenue ;
annualSums2 . gross += fm [ i2 ] . gross ; annualSums2 . cost += fm [ i2 ] . cost ;
annualSums2 . payment += fm [ i2 ] . payment ; annualSums2 . paid += fm [ i2 ] . paid ;
}
const annualVals = [ annualSums2 . sign , annualSums2 . revenue , annualSums2 . gross , annualSums2 . cost , annualSums2 . gross - annualSums2 . cost , annualSums2 . payment , annualSums2 . paid , annualSums2 . payment - annualSums2 . paid ] ;
const fmFields = [ 'sign' , 'revenue' , 'gross' , 'cost' , null , 'payment' , 'paid' , null ] ;
// Calculate quarterly sums