This commit is contained in:
@@ -288,14 +288,14 @@ function renderFinance() {
|
|||||||
const SIGNED_COLS = [
|
const SIGNED_COLS = [
|
||||||
{ key: 'client_name', label: '客户' }, { key: 'customer_name', label: '项目名称' }, { key: 'sign_amount', label: '签约金额' },
|
{ key: 'client_name', label: '客户' }, { key: 'customer_name', label: '项目名称' }, { key: 'sign_amount', label: '签约金额' },
|
||||||
{ key: 'rev', label: '已确收' }, { key: 'gross', label: '毛利' }, { key: 'cost', label: '成本' },
|
{ key: 'rev', label: '已确收' }, { key: 'gross', label: '毛利' }, { key: 'cost', label: '成本' },
|
||||||
{ key: 'profit', label: '项目利润' }, { key: 'payment', label: '已回款' }, { key: 'paid', label: '已付' },
|
{ key: 'payment', label: '已回款' }, { key: 'paid', label: '已付' },
|
||||||
{ key: 'cashflow', label: '现金流' }, { key: 'exe_rate', label: '执行率' }, { key: 'gross_rate', label: '毛利率' },
|
{ key: 'cashflow', label: '现金流' }, { key: 'exe_rate', label: '执行率' }, { key: 'gross_rate', label: '毛利率' },
|
||||||
{ key: 'pay_rate', label: '回款率' }, { key: 'paid_rate', label: '付款率' },
|
{ key: 'pay_rate', label: '回款率' }, { key: 'paid_rate', label: '付款率' },
|
||||||
];
|
];
|
||||||
const UNSIGNED_COLS = [
|
const UNSIGNED_COLS = [
|
||||||
{ key: 'client_name', label: '客户' }, { key: 'customer_name', label: '项目名称' }, { key: 'sign_amount', label: '签约金额' },
|
{ key: 'client_name', label: '客户' }, { key: 'customer_name', label: '项目名称' }, { key: 'sign_amount', label: '签约金额' },
|
||||||
{ key: 'sign_month', label: '签约月份' }, { key: 'gross', label: '毛利' }, { key: 'cost', label: '成本' },
|
{ key: 'sign_month', label: '签约月份' }, { key: 'gross', label: '毛利' }, { key: 'cost', label: '成本' },
|
||||||
{ key: 'profit', label: '项目利润' },
|
,
|
||||||
];
|
];
|
||||||
|
|
||||||
const renderView = (rows, sumRev, sumPay, sumCost, sumPaid, sumGross, signTotal, signCnt, emptyText) => {
|
const renderView = (rows, sumRev, sumPay, sumCost, sumPaid, sumGross, signTotal, signCnt, emptyText) => {
|
||||||
@@ -305,7 +305,7 @@ function renderFinance() {
|
|||||||
return typeof v === 'object' ? [c.label, v.val, v.cls] : [c.label, v, c.color];
|
return typeof v === 'object' ? [c.label, v.val, v.cls] : [c.label, v, c.color];
|
||||||
});
|
});
|
||||||
const thead = isUnsigned ? buildThead(UNSIGNED_COLS, 'uns') : buildThead(SIGNED_COLS, 'sig');
|
const thead = isUnsigned ? buildThead(UNSIGNED_COLS, 'uns') : buildThead(SIGNED_COLS, 'sig');
|
||||||
const theadCols = isUnsigned ? 7 : 14;
|
const theadCols = isUnsigned ? 6 : 13;
|
||||||
const tbody = rows.length ? `<tbody>${rows.join("")}</tbody>` : `<tr><td colspan="${theadCols}" class="p-6 text-center text-slate-400">${emptyText}</td></tr>`;
|
const tbody = rows.length ? `<tbody>${rows.join("")}</tbody>` : `<tr><td colspan="${theadCols}" class="p-6 text-center text-slate-400">${emptyText}</td></tr>`;
|
||||||
|
|
||||||
const cardGrid = isUnsigned ? 'grid-cols-4' : 'grid-cols-6';
|
const cardGrid = isUnsigned ? 'grid-cols-4' : 'grid-cols-6';
|
||||||
@@ -327,17 +327,11 @@ function renderFinance() {
|
|||||||
const grossR = rev && gross ? Math.round(gross / rev * 100) + '%' : '<span class="text-slate-300">—</span>';
|
const grossR = rev && gross ? Math.round(gross / rev * 100) + '%' : '<span class="text-slate-300">—</span>';
|
||||||
const cf = cashflow ? money(cashflow) : '<span class="text-slate-300">—</span>';
|
const cf = cashflow ? money(cashflow) : '<span class="text-slate-300">—</span>';
|
||||||
const cfCls = cashflow >= 0 ? 'text-green-600' : 'text-red-600';
|
const cfCls = cashflow >= 0 ? 'text-green-600' : 'text-red-600';
|
||||||
const profit = gross;
|
return `<tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="openPfEditModal(${pf.id})"><td class="p-2 text-center align-middle text-sm">${esc(pf.client_name || "")}</td><td class="p-2 text-sm font-medium text-center align-middle truncate" style="min-width:300px;max-width:300px;width:300px" title="${esc(pf.customer_name)}">${esc(pf.customer_name)}</td><td class="p-2 text-center align-middle text-sm font-medium">${money(pf.sign_amount)}</td><td class="p-2 text-center text-blue-700 align-middle">${fmtNoUnit(rev)}</td><td class="p-2 text-center align-middle text-sm font-medium">${fmtNoUnit(gross)}</td><td class="p-2 text-center text-rose-700 align-middle">${fmtNoUnit(cost)}</td><td class="p-2 text-center text-amber-700 align-middle">${fmtNoUnit(payment)}</td><td class="p-2 text-center text-purple-700 align-middle">${fmtNoUnit(paid)}</td><td class="p-2 text-center font-semibold align-middle ${cfCls}">${cf}</td><td class="p-2 text-center align-middle text-sm">${exe}</td><td class="p-2 text-center align-middle text-sm">${grossR}</td><td class="p-2 text-center align-middle text-sm">${payR}</td><td class="p-2 text-center align-middle text-sm">${paidR}</td></tr>`;
|
||||||
const pfCls = profit >= 0 ? 'text-green-600' : 'text-red-600';
|
|
||||||
const pfVal = profit ? money(profit) : '<span class="text-slate-300">—</span>';
|
|
||||||
return `<tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="openPfEditModal(${pf.id})"><td class="p-2 text-center align-middle text-sm">${esc(pf.client_name || "")}</td><td class="p-2 text-sm font-medium text-center align-middle truncate" style="min-width:300px;max-width:300px;width:300px" title="${esc(pf.customer_name)}">${esc(pf.customer_name)}</td><td class="p-2 text-center align-middle text-sm font-medium">${money(pf.sign_amount)}</td><td class="p-2 text-center text-blue-700 align-middle">${fmtNoUnit(rev)}</td><td class="p-2 text-center align-middle text-sm font-medium">${fmtNoUnit(gross)}</td><td class="p-2 text-center text-rose-700 align-middle">${fmtNoUnit(cost)}</td><td class="p-2 text-center font-semibold align-middle ${pfCls}">${pfVal}</td><td class="p-2 text-center text-amber-700 align-middle">${fmtNoUnit(payment)}</td><td class="p-2 text-center text-purple-700 align-middle">${fmtNoUnit(paid)}</td><td class="p-2 text-center font-semibold align-middle ${cfCls}">${cf}</td><td class="p-2 text-center align-middle text-sm">${exe}</td><td class="p-2 text-center align-middle text-sm">${grossR}</td><td class="p-2 text-center align-middle text-sm">${payR}</td><td class="p-2 text-center align-middle text-sm">${paidR}</td></tr>`;
|
|
||||||
};
|
};
|
||||||
// 待签约简版行
|
// 待签约简版行
|
||||||
const tdRowUnsigned = (pf, cost, gross) => {
|
const tdRowUnsigned = (pf, cost, gross) => {
|
||||||
const profit = gross;
|
return `<tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="openPfEditModal(${pf.id})"><td class="p-2 text-sm font-medium text-center align-middle truncate" style="min-width:300px;max-width:300px;width:300px" title="${esc(pf.customer_name)}">${esc(pf.customer_name)}</td><td class="p-2 text-center align-middle text-sm font-medium">${money(pf.sign_amount)}</td><td class="p-2 text-center align-middle text-sm">${pf.sign_month || '—'}</td><td class="p-2 text-center align-middle text-sm font-medium">${fmtNoUnit(gross)}</td><td class="p-2 text-center text-rose-700 align-middle">${fmtNoUnit(cost)}</td></tr>`;
|
||||||
const pfCls = profit >= 0 ? 'text-green-600' : 'text-red-600';
|
|
||||||
const pfVal = profit ? money(profit) : '<span class="text-slate-300">—</span>';
|
|
||||||
return `<tr class="border-b border-slate-100 hover:bg-slate-50 cursor-pointer" onclick="openPfEditModal(${pf.id})"><td class="p-2 text-sm font-medium text-center align-middle truncate" style="min-width:300px;max-width:300px;width:300px" title="${esc(pf.customer_name)}">${esc(pf.customer_name)}</td><td class="p-2 text-center align-middle text-sm font-medium">${money(pf.sign_amount)}</td><td class="p-2 text-center align-middle text-sm">${pf.sign_month || '—'}</td><td class="p-2 text-center align-middle text-sm font-medium">${fmtNoUnit(gross)}</td><td class="p-2 text-center text-rose-700 align-middle">${fmtNoUnit(cost)}</td><td class="p-2 text-center font-semibold align-middle ${pfCls}">${pfVal}</td></tr>`;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if (state.finView === 'monthly') {
|
if (state.finView === 'monthly') {
|
||||||
@@ -360,7 +354,7 @@ function renderFinance() {
|
|||||||
if (!rev && !payment && !cost && !paid && !gross) return;
|
if (!rev && !payment && !cost && !paid && !gross) return;
|
||||||
dataItems.push({
|
dataItems.push({
|
||||||
pf: pf, customer_name: pf.customer_name || '', sign_amount: pf.sign_amount || 0, sign_month: pf.sign_month || '',
|
pf: pf, customer_name: pf.customer_name || '', sign_amount: pf.sign_amount || 0, sign_month: pf.sign_month || '',
|
||||||
rev: rev, gross: gross, cost: cost, profit: gross, payment: payment, paid: paid,
|
rev: rev, gross: gross, cost: cost, payment: payment, paid: paid,
|
||||||
cashflow: payment - paid,
|
cashflow: payment - paid,
|
||||||
exe_rate: rev && pf.sign_amount ? Math.round(rev / pf.sign_amount * 100) : 0,
|
exe_rate: rev && pf.sign_amount ? Math.round(rev / pf.sign_amount * 100) : 0,
|
||||||
gross_rate: rev && gross ? Math.round(gross / rev * 100) : 0,
|
gross_rate: rev && gross ? Math.round(gross / rev * 100) : 0,
|
||||||
@@ -411,7 +405,7 @@ function renderFinance() {
|
|||||||
if (!rev && !payment && !cost && !paid && !gross) return;
|
if (!rev && !payment && !cost && !paid && !gross) return;
|
||||||
dataItems.push({
|
dataItems.push({
|
||||||
pf: pf, customer_name: pf.customer_name || '', sign_amount: pf.sign_amount || 0, sign_month: pf.sign_month || '',
|
pf: pf, customer_name: pf.customer_name || '', sign_amount: pf.sign_amount || 0, sign_month: pf.sign_month || '',
|
||||||
rev: rev, gross: gross, cost: cost, profit: gross, payment: payment, paid: paid,
|
rev: rev, gross: gross, cost: cost, payment: payment, paid: paid,
|
||||||
cashflow: payment - paid,
|
cashflow: payment - paid,
|
||||||
exe_rate: rev && pf.sign_amount ? Math.round(rev / pf.sign_amount * 100) : 0,
|
exe_rate: rev && pf.sign_amount ? Math.round(rev / pf.sign_amount * 100) : 0,
|
||||||
gross_rate: rev && gross ? Math.round(gross / rev * 100) : 0,
|
gross_rate: rev && gross ? Math.round(gross / rev * 100) : 0,
|
||||||
@@ -447,7 +441,7 @@ function renderFinance() {
|
|||||||
var t = calcTotals(pf);
|
var t = calcTotals(pf);
|
||||||
return {
|
return {
|
||||||
pf: pf, customer_name: pf.customer_name || '', sign_amount: pf.sign_amount || 0, sign_month: pf.sign_month || '',
|
pf: pf, customer_name: pf.customer_name || '', sign_amount: pf.sign_amount || 0, sign_month: pf.sign_month || '',
|
||||||
rev: t.rev, gross: t.gross, cost: t.cost, profit: t.gross,
|
rev: t.rev, gross: t.gross, cost: t.cost,
|
||||||
payment: t.payment, paid: t.paid, cashflow: t.payment - t.paid,
|
payment: t.payment, paid: t.paid, cashflow: t.payment - t.paid,
|
||||||
exe_rate: t.rev && pf.sign_amount ? Math.round(t.rev / pf.sign_amount * 100) : 0,
|
exe_rate: t.rev && pf.sign_amount ? Math.round(t.rev / pf.sign_amount * 100) : 0,
|
||||||
gross_rate: t.rev && t.gross ? Math.round(t.gross / t.rev * 100) : 0,
|
gross_rate: t.rev && t.gross ? Math.round(t.gross / t.rev * 100) : 0,
|
||||||
|
|||||||
@@ -80,7 +80,7 @@
|
|||||||
<header class="topbar border-b border-slate-200 bg-white px-8 py-5">
|
<header class="topbar border-b border-slate-200 bg-white px-8 py-5">
|
||||||
<div class="flex items-center gap-3 w-full">
|
<div class="flex items-center gap-3 w-full">
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<p class="eyebrow text-xs font-semibold uppercase tracking-[0.18em] text-blue-700">OPC Manager <span class="ml-2 text-xs font-normal text-slate-400 tracking-normal">v1.0.0.28</span></p>
|
<p class="eyebrow text-xs font-semibold uppercase tracking-[0.18em] text-blue-700">OPC Manager <span class="ml-2 text-xs font-normal text-slate-400 tracking-normal">v1.0.0.29</span></p>
|
||||||
<div class="flex items-center gap-4 mt-1">
|
<div class="flex items-center gap-4 mt-1">
|
||||||
<h1 class="text-2xl font-semibold" id="workspaceTitle">科普 OPC 工作台</h1>
|
<h1 class="text-2xl font-semibold" id="workspaceTitle">科普 OPC 工作台</h1>
|
||||||
<div class="hidden sm:flex items-center gap-1.5 bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-100 rounded-full px-4 py-1.5">
|
<div class="hidden sm:flex items-center gap-1.5 bg-gradient-to-r from-blue-50 to-indigo-50 border border-blue-100 rounded-full px-4 py-1.5">
|
||||||
|
|||||||
Reference in New Issue
Block a user