diff --git a/backend/migrations/columns.py b/backend/migrations/columns.py
index 72d218c..ec957b0 100644
--- a/backend/migrations/columns.py
+++ b/backend/migrations/columns.py
@@ -115,6 +115,13 @@ def migrate_add_columns():
conn.commit()
print("[migrate] 加列迁移完成")
+ # plan_finances 新增字段
+ _add_column_if_missing(conn, "plan_finances", "client_name",
+ "ALTER TABLE plan_finances ADD COLUMN client_name VARCHAR(200) NOT NULL DEFAULT ''")
+ _add_column_if_missing(conn, "plan_finances", "project_type",
+ "ALTER TABLE plan_finances ADD COLUMN project_type VARCHAR(50) NOT NULL DEFAULT '待签约'")
+ _add_column_if_missing(conn, "plan_finances", "weight",
+ "ALTER TABLE plan_finances ADD COLUMN weight VARCHAR(10) NOT NULL DEFAULT '20%'")
# 删除 plan_finances.status 列(计划模块不再需要状态字段)
cur = conn.cursor()
cur.execute("SHOW COLUMNS FROM plan_finances LIKE 'status'")
diff --git a/backend/routes.py b/backend/routes.py
index 4b360f9..c530d25 100644
--- a/backend/routes.py
+++ b/backend/routes.py
@@ -25,7 +25,7 @@ TABLES = {
"tasks": ("project_tasks", ["project_id", "phase", "milestone", "task", "owner", "due_date", "blockers", "notes", "status", "sort_order", "priority", "tenant"]),
"projectFinances": ("project_finances", ["project_id", "tenant", "business_type", "customer_name", "sign_amount", "sign_month", "sales_person", "owner", "total_rev", "total_gross", "total_payment", "total_cost", "total_paid", "budget_data", "expense_data", "start_date", "end_date", "task_type", "task_count", "service_fee_standard", "project_manager", "task_data", "project_code", "contact_name", "contact_phone", "other_info"]),
"expense": ("expense_records", ["expense_type", "expense_month", "amount", "incurred_amount", "notes", "status", "tenant"]),
- "planFinances": ("plan_finances", ["project_id", "tenant", "business_type", "customer_name", "sign_amount", "sign_month", "sales_person", "owner", "total_rev", "total_gross", "total_payment", "total_cost", "total_paid", "budget_data", "expense_data", "start_date", "end_date", "task_type", "task_count", "service_fee_standard", "project_manager", "task_data", "project_code", "contact_name", "contact_phone", "other_info"]),
+ "planFinances": ("plan_finances", ["project_id", "tenant", "business_type", "customer_name", "sign_amount", "sign_month", "sales_person", "owner", "total_rev", "total_gross", "total_payment", "total_cost", "total_paid", "budget_data", "expense_data", "start_date", "end_date", "task_type", "task_count", "service_fee_standard", "project_manager", "task_data", "project_code", "contact_name", "contact_phone", "other_info", "client_name", "project_type", "weight"]),
"planExpense": ("plan_expense_records", ["expense_type", "expense_month", "amount", "incurred_amount", "notes", "status", "tenant"]),
}
diff --git a/static/modules/plan.js b/static/modules/plan.js
index f1104f8..483b1f3 100644
--- a/static/modules/plan.js
+++ b/static/modules/plan.js
@@ -96,7 +96,7 @@ function renderPlan() {
})();
const sm = pf.sign_month || "";
const signMonthCell = `
${sm || '—'} | `;
- return `| ${esc(pf.customer_name)} | ${signMonthCell}${money(pf.sign_amount)} | ${mCols}${totalCol}
`;
+ return `| ${esc(pf.client_name || "")} | ${esc(pf.customer_name)} | ${esc(pf.weight || "20%")} | ${esc(pf.project_type || "待签约")} | ${signMonthCell}${money(pf.sign_amount)} | ${mCols}${totalCol}
`;
};
const now2 = new Date();
@@ -150,7 +150,7 @@ function renderPlan() {
-
+
@@ -285,8 +285,10 @@ function renderPlan() {
return h;
};
const SIGNED_COLS = [
+ { key: 'client_name', label: '客户' },
{ key: 'customer_name', label: '项目名称' },
- ,
+ { key: 'weight', label: '加权' },
+ { key: 'project_type', label: '项目类型' },
{ key: 'sign_amount', label: '签约金额' },
{ key: 'rev', label: '已确收' },
{ key: 'gross', label: '毛利' },
@@ -296,13 +298,13 @@ function renderPlan() {
{ key: 'paid', label: '已付' },
{ key: 'cashflow', label: '现金流' },
{ key: 'exe_rate', label: '执行率' },
- { key: 'gross_rate', label: '毛利率' },
{ key: 'pay_rate', label: '回款率' },
- { key: 'paid_rate', label: '付款率' },
];
const UNSIGNED_COLS = [
+ { key: 'client_name', label: '客户' },
{ key: 'customer_name', label: '项目名称' },
- ,
+ { key: 'weight', label: '加权' },
+ { key: 'project_type', label: '项目类型' },
{ key: 'sign_amount', label: '签约金额' },
{ key: 'sign_month', label: '签约月份' },
{ key: 'gross', label: '毛利' },
@@ -317,7 +319,7 @@ function renderPlan() {
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 theadCols = isUnsigned ? 6 : 13;
+ const theadCols = isUnsigned ? 9 : 14;
const tbody = rows.length ? `${rows.join("")}` : `| ${emptyText} |
`;
const cardGrid = isUnsigned ? 'grid-cols-4' : 'grid-cols-6';
@@ -342,14 +344,14 @@ function renderPlan() {
const profit = gross;
const pfCls = profit >= 0 ? 'text-green-600' : 'text-red-600';
const pfVal = profit ? money(profit) : '—';
- return `| ${esc(pf.customer_name)} | ${money(pf.sign_amount)} | ${fmtNoUnit(rev)} | ${fmtNoUnit(gross)} | ${fmtNoUnit(cost)} | ${pfVal} | ${fmtNoUnit(payment)} | ${fmtNoUnit(paid)} | ${cf} | ${exe} | ${grossR} | ${payR} | ${paidR} |
`;
+ 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)} | ${fmtNoUnit(cost)} | ${pfVal} | ${fmtNoUnit(payment)} | ${fmtNoUnit(paid)} | ${cf} | ${exe} | ${payR} |
`;
};
// 待签约简版行
const tdRowUnsigned = (pf, cost, gross) => {
const profit = gross;
const pfCls = profit >= 0 ? 'text-green-600' : 'text-red-600';
const pfVal = profit ? money(profit) : '—';
- return `| ${esc(pf.customer_name)} | ${money(pf.sign_amount)} | ${pf.sign_month || '—'} | ${fmtNoUnit(gross)} | ${fmtNoUnit(cost)} | ${pfVal} |
`;
+ 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)} | ${pf.sign_month || '—'} | ${fmtNoUnit(gross)} | ${fmtNoUnit(cost)} | ${pfVal} |
`;
};
if (state.planView === 'monthly') {
@@ -784,7 +786,7 @@ window.planOpenPfEditModal = (pfId) => {
document.querySelector("#plan_pf-id-input").value = pf.id;
document.querySelector("#planModalTitle").textContent = "编辑项目财务";
document.querySelector("#planDeleteBtn").classList.remove("hidden");
- const form = document.querySelector("#financeModal form");
+ const form = document.querySelector("#planModal form");
form.querySelector('[name="project_id"]').value = pf.project_id || "";
const deptDisplay = form.querySelector('.bg-slate-50 [disabled]');
if (deptDisplay) deptDisplay.value = pf.project_id || "";
@@ -820,6 +822,9 @@ window.planOpenPfEditModal = (pfId) => {
setVal("contact_name", pf.contact_name);
setVal("contact_phone", pf.contact_phone);
setVal("other_info", pf.other_info);
+ setVal("client_name", pf.client_name);
+ form.querySelector('[name="project_type"]').value = pf.project_type || "待签约";
+ form.querySelector('[name="weight"]').value = pf.weight || "20%";
let budgetData = [];
try { budgetData = JSON.parse(pf.budget_data || "[]"); } catch (e) { budgetData = []; }
planInitRevpayTable(budgetData.length ? budgetData : null);
@@ -1046,3 +1051,21 @@ function renderPlanCharts(data) {
state.planChart4 = new Chart(c4, Object.assign({}, iconf, { data: { labels: labels, datasets: [{ label: "利润", data: data.map(function(x) { return x.gross || 0; }), borderColor: "#6366f1", backgroundColor: "rgba(99,102,241,0.06)", fill: true, tension: 0.3 }] } }));
}
};
+
+function planInlineSelect(name, pfId, field, currentVal, options) {
+ var opts = '';
+ for (var i = 0; i < options.length; i++) {
+ opts += '';
+ }
+ return '';
+}
+
+window.planInlineUpdate = async (pfId, field, value) => {
+ try {
+ await api("/api/planFinances/" + pfId, { method: "PUT", body: JSON.stringify({ data: { [field]: value } }) });
+ await load();
+ toast("已更新", "success");
+ } catch (error) {
+ toast("更新失败:" + error.message, "error");
+ }
+};
diff --git a/templates/index.html b/templates/index.html
index a2090c3..3e2961e 100644
--- a/templates/index.html
+++ b/templates/index.html
@@ -80,7 +80,7 @@
-
OPC Manager v1.0.0.24
+
OPC Manager v1.0.0.25