feat: 项目模块+首页优化 — 删除项目费用/列重排/卡片标题双行

项目模块:
- 全面删除项目费用(METRIC_CARDS/thead/tdRow/budgetTab保存逻辑)
- 表格列重排:签约→确收→毛利→应付→项目利润→回款→已付→现金流→执行率→毛利率→回款率→付款率
- 卡片重排并删除签约项目总数
- 表格标题双行:项目财务明细+列顺序副标题
- 项目利润=毛利(不再减费用)

首页:
- 业务财务概览标题改为双行格式
- 列顺序重排保持一致
- 项目费用行加入/移除等多次调整
This commit is contained in:
mac
2026-07-06 20:58:19 +08:00
parent ac312741df
commit dbc98e4693
2 changed files with 56 additions and 74 deletions

View File

@@ -74,25 +74,25 @@ function renderHome() {
const sign = pct >= 0 ? '+' : '';
return '<span class="' + cls + '">' + sign + pct + '%</span>';
};
const COLORS = ['text-slate-700','text-blue-600','text-green-600','text-amber-600','text-rose-600','text-purple-600','text-orange-600','text-cyan-600','text-indigo-600'];
const LABELS = ['合同金额','确收金额','确收毛利','回款金额','应付金额','已付','项目费用','现金流','项目利润'];
const Q_FIELDS = [m.signed_q2||0, m.revenue_q2, m.gross_q2, m.payment_q2||0, m.cost_q2||0, m.paid_q2||0, m.proj_expense_q2||0, m.cashflow_q2||0, m.profit_q2||0];
const Q_PREV = [m.signed_prev_q||0, m.revenue_prev_q||0, m.gross_prev_q||0, m.payment_prev_q||0, m.cost_prev_q||0, m.paid_prev_q||0, m.proj_expense_prev_q||0, m.cashflow_prev_q||0, m.profit_prev_q||0];
const M_FIELDS = [m.signed_month||0, m.monthly_revenue, m.monthly_net_profit, m.payment_month||0, m.cost_month||0, m.paid_month||0, m.proj_expense_month||0, m.cashflow_month||0, m.profit_month||0];
const M_PREV = [m.signed_prev_month||0, m.revenue_prev_month||0, m.gross_prev_month||0, m.payment_prev_month||0, m.cost_prev_month||0, m.paid_prev_month||0, m.proj_expense_prev_month||0, m.cashflow_prev_month||0, m.profit_prev_month||0];
const ROWS = [rows1, rows2, rows3, rows4, rows5, rows7, rowsProjExpense, rows8, rows9];
const COLORS = ['text-slate-700','text-blue-600','text-green-600','text-rose-600','text-indigo-600','text-amber-600','text-purple-600','text-cyan-600'];
const LABELS = ['合同金额','确收金额','确收毛利','应付金额','项目利润','回款金额','已付','现金流'];
const Q_FIELDS = [m.signed_q2||0, m.revenue_q2, m.gross_q2, m.cost_q2||0, m.profit_q2||0, m.payment_q2||0, m.paid_q2||0, m.cashflow_q2||0];
const Q_PREV = [m.signed_prev_q||0, m.revenue_prev_q||0, m.gross_prev_q||0, m.cost_prev_q||0, m.profit_prev_q||0, m.payment_prev_q||0, m.paid_prev_q||0, m.cashflow_prev_q||0];
const M_FIELDS = [m.signed_month||0, m.monthly_revenue, m.monthly_net_profit, m.cost_month||0, m.profit_month||0, m.payment_month||0, m.paid_month||0, m.cashflow_month||0];
const M_PREV = [m.signed_prev_month||0, m.revenue_prev_month||0, m.gross_prev_month||0, m.cost_prev_month||0, m.profit_prev_month||0, m.payment_prev_month||0, m.paid_prev_month||0, m.cashflow_prev_month||0];
const ROWS = [rows1, rows2, rows3, rows5, rows9, rows4, rows7, rows8];
// 现金流和项目利润的原始值数组,用于正负着色
const CF_RAW = [m.cashflow_annual||0, m.cashflow_q2||0, m.cashflow_month||0, m.cashflow_prev_q||0, m.cashflow_prev_month||0];
const PF_RAW = [m.profit_annual||0, m.profit_q2||0, m.profit_month||0, m.profit_prev_q||0, m.profit_prev_month||0];
var valCls = function(ri) { return (ri === 7 || ri === 8) ? '' : 'text-slate-800'; };
var valCls = function(ri) { return (ri === 4 || ri === 7) ? '' : 'text-slate-800'; };
var tdVal = function(ri, col, val) {
if (ri === 4) return '<td class="py-2 text-right font-semibold ' + (PF_RAW[col] >= 0 ? 'text-green-600' : 'text-red-600') + '">' + val + '</td>';
if (ri === 7) return '<td class="py-2 text-right font-semibold ' + (CF_RAW[col] >= 0 ? 'text-green-600' : 'text-red-600') + '">' + val + '</td>';
if (ri === 8) return '<td class="py-2 text-right font-semibold ' + (PF_RAW[col] >= 0 ? 'text-green-600' : 'text-red-600') + '">' + val + '</td>';
return '<td class="py-2 text-right font-semibold text-slate-800">' + val + '</td>';
};
var thead = '<tr class="border-b border-slate-200"><th class="py-2 text-left text-slate-500">指标</th><th class="py-2 text-right font-semibold text-slate-700">年度累计</th><th class="py-2 text-right font-semibold text-slate-700">上季度累计</th><th class="py-2 text-right font-semibold text-slate-700">上月累计</th><th class="py-2 text-right font-semibold text-slate-700">本季度累计</th><th class="py-2 text-right font-semibold text-slate-400">季环比</th><th class="py-2 text-right font-semibold text-slate-700">本月累计</th><th class="py-2 text-right font-semibold text-slate-400">月环比</th></tr>';
var thead = '<tr class="border-b border-slate-200"><th class="py-2 text-left text-slate-500">指标</th><th class="py-2 text-right font-semibold text-slate-700">年度累计</th><th class="py-2 text-right font-semibold text-slate-700">上季度累计</th><th class="py-2 text-right font-semibold text-slate-700">上月累计</th><th class="py-2 text-right font-semibold text-slate-700">本季度累计</th><th class="py-2 text-right font-semibold text-slate-400">季环比</th><th class="py-2 text-right font-semibold text-slate-700">本月累计</th><th class="py-2 text-right font-semibold text-slate-400">月环比</th></tr>';
var tbody = '';
for (var ri = 0; ri < 9; ri++) {
for (var ri = 0; ri < 8; ri++) {
var vals = ROWS[ri];
tbody += '<tr class="border-b border-slate-100 last:border-0"><td class="py-2 text-left font-semibold text-slate-700">' + LABELS[ri] + '</td>' +
tdVal(ri, 0, vals[0][1]) +
@@ -113,7 +113,7 @@ function renderHome() {
["产品迭代", m.upcoming_products, "products"],
].map(([label, value, tab]) => `<button class="metric-card" onclick="switchTab('${tab}')"><span class="flex items-center gap-2 text-xs text-slate-500"><i data-lucide="gauge"></i>${label}</span><strong class="mt-2 block text-2xl">${value}</strong></button>`).join("")}
</div>`}
${card(`<h3 class="text-sm font-bold text-slate-700 mb-3">部门经营情况</h3><div class="overflow-x-auto"><table class="w-full text-sm"><thead><tr class="border-b border-slate-200"><th class="py-2 text-left text-slate-500">指标</th><th class="py-2 text-right font-semibold text-slate-700">年度累计</th><th class="py-2 text-right font-semibold text-slate-700">上季度累计</th><th class="py-2 text-right font-semibold text-slate-700">上月累计</th><th class="py-2 text-right font-semibold text-slate-700">本季度累计</th><th class="py-2 text-right font-semibold text-slate-400">季环比</th><th class="py-2 text-right font-semibold text-slate-700">本月累计</th><th class="py-2 text-right font-semibold text-slate-400">月环比</th></tr></thead><tbody>
${card(`<h3 class="text-sm font-bold text-slate-700 mb-3">部门经营情况</h3><div class="overflow-x-auto"><table class="w-full text-sm"><thead><tr class="border-b border-slate-200"><th class="py-2 text-left text-slate-500">指标</th><th class="py-2 text-right font-semibold text-slate-700">年度累计</th><th class="py-2 text-right font-semibold text-slate-700">上季度累计</th><th class="py-2 text-right font-semibold text-slate-700">上月累计</th><th class="py-2 text-right font-semibold text-slate-700">本季度累计</th><th class="py-2 text-right font-semibold text-slate-400">季环比</th><th class="py-2 text-right font-semibold text-slate-700">本月累计</th><th class="py-2 text-right font-semibold text-slate-400">月环比</th></tr></thead><tbody>
${(() => {
var d = [
['部门毛利', [m.profit_annual||0, m.profit_q2||0, m.profit_month||0, m.profit_prev_q||0, m.profit_prev_month||0],
@@ -145,7 +145,7 @@ function renderHome() {
return h;
})()}
</tbody></table></div>`, "p-4")}
${card(`<h3 class="text-sm font-bold text-slate-700 mb-3">业务(项目)财务概览</h3><div class="overflow-x-auto"><table class="w-full text-sm"><thead>${thead}</thead><tbody>${tbody}</tbody></table></div>`, "p-4")}
${card(`<h3 class="text-sm font-bold text-slate-700">业务(项目)财务概览</h3><p class="text-xs text-slate-400 mb-3">合同 → 确收 → 毛利 → 应付 → 项目利润 → 回款 → 已付 → 现金流</p><div class="overflow-x-auto"><table class="w-full text-sm"><thead>${thead}</thead><tbody>${tbody}</tbody></table></div>`, "p-4")}
<div class="grid grid-cols-4 gap-5">
${card(`<div class="mb-3 flex items-center justify-between"><h2 class="text-sm font-bold text-slate-600">月度签约趋势</h2><span class="text-xs text-slate-400">2026</span></div><div style="position:relative;height:200px"><canvas id="chartSign"></canvas></div>`, "p-4")}
${card(`<div class="mb-3 flex items-center justify-between"><h2 class="text-sm font-bold text-slate-600">月度确收与毛利</h2><span class="text-xs text-slate-400">2026</span></div><div style="position:relative;height:200px"><canvas id="chartRev"></canvas></div>`, "p-4")}