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() {
- +
业务类型
${fmTypes.map(t => ``).join("")}
@@ -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

科普 OPC 工作台