fix: 计划总览独立数据源+实际模块表头修复
- 计划月度/季度总览: 改用planFinances计算,不再引用实际模块数据 - 新增computePlanMonthly函数从planFinances计算月度数据 - 实际模块SIGNED_COLS/UNSIGNED_COLS补全client_name列 - theadCols计数修正(14/7) - 版本号 v1.0.0.28
This commit is contained in:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user