@@ -330,17 +330,17 @@ function renderPlan() {
['应付', m(payable), '成本 − 支出'],
['往来余额', m(netAsset), '应收 − 应付'],
];
- const fs = '
' + rows_data.map((r, i) =>
- '
' + r[0] + ' ' + r[1] + '
').join('') + '
';
+ const cardHtml = (r, i) => '
';
+ const fs = '
' + cardHtml(rows_data[0],0) + cardHtml(rows_data[1],1) + cardHtml(rows_data[2],2) + '' + cardHtml(rows_data[3],3) + cardHtml(rows_data[4],4) + cardHtml(rows_data[5],5) + '' + cardHtml(rows_data[6],6) + cardHtml(rows_data[7],7) + cardHtml(rows_data[8],8) + '
';
const thead = isUnsigned ? buildThead(UNSIGNED_COLS, 'uns') : buildThead(SIGNED_COLS, 'sig');
const theadCols = isUnsigned ? 9 : 15;
- const tbody = rows.length ? `
${rows.join("")}` : `
| ${emptyText} |
`;
+ const tbody = rows.length ? `
${rows.join("")}` : `
| ${emptyText} |
`;
const subtitle = isUnsigned
? '合同 → 毛利 → 成本 → 项目利润'
: '合同 → 确收 → 毛利 → 成本 → 项目利润 → 回款 → 支出 → 现金流';
return `${isUnsigned ? '' : `
${card(`
${finHeaderBase}
${finAddBtn}
`, "py-2 px-4")}
`}` +
card(fs, "px-4 py-2") +
- card(`
项目财务明细
${subtitle}
`, "p-4");
+ card(`
项目财务明细
${subtitle}
`, "p-4");
};
const fmtNoUnit = (v) => v ? `
${Number(v||0).toLocaleString('zh-CN')}` : '
—';
@@ -368,7 +368,7 @@ function renderPlan() {
const cf = cashflow ? money(cashflow) : '
—';
const cfCls = cashflow >= 0 ? 'text-green-600' : 'text-red-600';
const pending = rev - payment;
- const pendingCls = pending > 0 ? 'text-red-600' : pending < 0 ? 'text-green-600' : 'text-slate-400';
+ const pendingCls = pending > 0 ? 'text-red-600' : pending < 0 ? 'text-green-600' : 'text-slate-700';
const pendingVal = pending ? (pending > 0 ? '+' : '') + money(pending) : '
—';
var budgetArr = [];
var expenseArr = [];
@@ -390,9 +390,9 @@ function renderPlan() {
var bRev = parseFloat(b.rev || 0), bGross = parseFloat(b.gross || 0), bPay = parseFloat(b.payment || 0);
var eCost = parseFloat(exp.cost || 0), ePaid = parseFloat(exp.paid || 0);
var bCf = bPay - ePaid;
- return '
| ' + m + ' | ' + (bRev ? money(bRev) : '—') + ' | ' + (bGross ? money(bGross) : '—') + ' | | ' + (bPay ? money(bPay) : '—') + ' | | ' + (eCost ? money(eCost) : '—') + ' | ' + (ePaid ? money(ePaid) : '—') + ' | | ' + (bCf ? money(bCf) : '—') + ' | ' + (bRev - bPay ? money(bRev - bPay) : '—') + ' |
';
+ return '
| ' + m + ' | ' + (bRev ? money(bRev) : '—') + ' | ' + (bGross ? money(bGross) : '—') + ' | | ' + (bPay ? money(bPay) : '—') + ' | | ' + (eCost ? money(eCost) : '—') + ' | ' + (ePaid ? money(ePaid) : '—') + ' | | ' + (bCf ? money(bCf) : '—') + ' | ' + (bRev - bPay ? money(bRev - bPay) : '—') + ' |
';
}).join('');
- return `
| ${esc(pf.client_name || "")} | ${esc(pf.customer_name)} | ${planInlineSelect('weight', pf.id, 'weight', pf.weight || '20%', ['20%','40%','60%','80%','100%'])} | ${planInlineSelect('ptype', pf.id, 'project_type', pf.project_type || '待签约', ['待签约','已签约'])} | ${money(pf.sign_amount)} | ${fmtNoUnit(rev)} | ${fmtNoUnit(gross)} | ${grossR} | ${fmtNoUnit(payment)} | ${pendingVal} | ${payR} | ${fmtNoUnit(cost)} | ${fmtNoUnit(paid)} | ${cf} |
| 月份 | 确收 | 毛利 | | 回款 | | 当期成本 | 当期流出 | | 现金流 | 应收 | ${detailRows}
|
`;
+ return `
| ${esc(pf.client_name || "")} | ${esc(pf.customer_name)} | ${planInlineSelect('weight', pf.id, 'weight', pf.weight || '20%', ['20%','40%','60%','80%','100%'])} | ${planInlineSelect('ptype', pf.id, 'project_type', pf.project_type || '待签约', ['待签约','已签约'])} | ${money(pf.sign_amount)} | ${fmtNoUnit(rev)} | ${fmtNoUnit(gross)} | ${grossR} | ${fmtNoUnit(payment)} | ${pendingVal} | ${payR} | ${fmtNoUnit(cost)} | ${fmtNoUnit(paid)} | ${cf} |
| 月份 | 确收 | 毛利 | | 回款 | | 当期成本 | 当期流出 | | 现金流 | 应收 | ${detailRows}
|
`;
};
// 待签约简版行
const tdRowUnsigned = (pf, cost, gross) => {
@@ -659,7 +659,7 @@ window.planAddTaskRow = (taskMonth = '', taskType = '', taskCount = '', executed
const isPreset = TASK_TYPES.includes(taskType);
const typeCell = isPreset || !taskType
? `
`
- : `
`;
+ : `
`;
row.innerHTML = `
|
${typeCell} |
|
@@ -721,7 +721,7 @@ window.planOnTaskTypeChange = (sel) => {
if (sel.value !== '__custom__') return;
const td = sel.parentElement;
const oldVal = sel.value;
- td.innerHTML = `
`;
+ td.innerHTML = `
`;
if (window.lucide) window.lucide.createIcons();
td.querySelector('input').focus();
};
@@ -785,7 +785,7 @@ async function planLoadFollowups(pfId) {
const fups = await api(`/api/followups/project_finance/${pfId}`);
list.innerHTML = fups.length
? fups.map(f => `
${esc(f.follower)} · ${esc(f.follow_up_method)}${esc(f.followed_at)}
${f.next_action ? `
下一步:${text(f.next_action)}
` : ""}
`).join("")
- : '
暂无跟进记录
';
+ : '
暂无跟进记录
';
if (window.lucide) window.lucide.createIcons();
list.querySelectorAll(".rich-content").forEach(el => {
const html = el.dataset.html;
@@ -1175,9 +1175,9 @@ function renderPlanOverview() {
cfCard +
card('
项目经营详情 所有项目产生的签单,确收,毛利,回款,当期成本,当期流出,现金流
' + thead + '' + tbody + '
', 'p-4') +
'
' +
- card('
月度签约趋势
2026', 'p-4') +
- card('
月度确收与毛利
2026', 'p-4') +
- card('
月度回款与已付
2026', 'p-4') +
+ card('
月度签约趋势
2026', 'p-4') +
+ card('
月度确收与毛利
2026', 'p-4') +
+ card('
月度回款与已付
2026', 'p-4') +
'
';
if (window.lucide) window.lucide.createIcons();
@@ -1221,9 +1221,9 @@ function renderPlanQuarterlyOverview() {
}
var thead = '
'; var pct = Math.round((cur - prev) / prev * 100); var cls = pct >= 0 ? 'text-green-600' : 'text-red-600'; var sign = pct >= 0 ? '+' : ''; return '
'; };
var tbody = '';
@@ -1253,7 +1253,7 @@ function renderPlanQuarterlyOverview() {
var grossQ = [qData[2][0], qData[2][1], qData[2][2], qData[2][3]];
var grossAnnual = annualSums2.gross;
var deptThead = '
';
var deptRows = [
{ label: '项目毛利', vals: grossQ, annual: grossAnnual },
diff --git a/templates/index.html b/templates/index.html
index 675f31b..6fa3af3 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -84,7 +84,7 @@