diff --git a/backend/flask_app.py b/backend/flask_app.py index dec7d02..bd5976d 100644 --- a/backend/flask_app.py +++ b/backend/flask_app.py @@ -316,12 +316,12 @@ def attach_common(conn, resource, items): return items -def monthly_finance(conn): +def monthly_finance(conn, tenant="科普·无界"): data = [] - for item in rows(conn, "SELECT DISTINCT month FROM finance_records ORDER BY month"): + for item in rows(conn, "SELECT DISTINCT month FROM finance_records WHERE tenant=? ORDER BY month", (tenant,)): month = item["month"] - revenue = one(conn, "SELECT COALESCE(SUM(amount),0) AS v FROM finance_records WHERE month=? AND record_type='revenue'", (month,))["v"] - cost = one(conn, "SELECT COALESCE(SUM(amount),0) AS v FROM finance_records WHERE month=? AND record_type='cost_expense'", (month,))["v"] + revenue = one(conn, "SELECT COALESCE(SUM(amount),0) AS v FROM finance_records WHERE month=? AND record_type='revenue' AND tenant=?", (month, tenant))["v"] + cost = one(conn, "SELECT COALESCE(SUM(amount),0) AS v FROM finance_records WHERE month=? AND record_type='cost_expense' AND tenant=?", (month, tenant))["v"] data.append({"month": month, "revenue": revenue, "gross_profit": revenue - cost, "cost_expense": cost, "net_profit": revenue - cost}) return data @@ -333,13 +333,17 @@ def index(): @app.route("/api/bootstrap") def bootstrap(): + tenant = request.args.get("tenant", "科普·无界") conn = db() try: - sales = attach_common(conn, "sales", rows(conn, "SELECT * FROM sales_leads ORDER BY id DESC")) - proposals = attach_common(conn, "proposals", rows(conn, "SELECT * FROM business_proposals ORDER BY id DESC")) - operations = attach_common(conn, "operations", rows(conn, "SELECT * FROM operation_projects ORDER BY id DESC")) - products = attach_common(conn, "products", rows(conn, "SELECT * FROM product_versions ORDER BY id DESC")) - finance = rows(conn, "SELECT * FROM finance_records ORDER BY month DESC, id DESC") + def q(sql, *args): + return rows(conn, sql, args) + sales = attach_common(conn, "sales", q("SELECT * FROM sales_leads WHERE tenant=? ORDER BY id DESC", tenant)) + proposals = attach_common(conn, "proposals", q("SELECT * FROM business_proposals WHERE tenant=? ORDER BY id DESC", tenant)) + operations = attach_common(conn, "operations", q("SELECT * FROM operation_projects WHERE tenant=? ORDER BY id DESC", tenant)) + products = attach_common(conn, "products", q("SELECT * FROM product_versions WHERE tenant=? ORDER BY id DESC", tenant)) + finance = q("SELECT * FROM finance_records WHERE tenant=? ORDER BY month DESC, id DESC", tenant) + tasks = q("SELECT * FROM project_tasks WHERE tenant=? ORDER BY phase, sort_order, id", tenant) current_month = "2026-06" # Finance aggregates def sum_finance(months, rtype): @@ -392,21 +396,21 @@ def bootstrap(): "gross_q2": revenue_q2 - cost_q2, "signed_not_executed": signed_not_executed, }, - "recent": rows(conn, "SELECT * FROM follow_up_records ORDER BY id DESC LIMIT 8"), + "recent": q("SELECT * FROM follow_up_records WHERE tenant=? ORDER BY id DESC LIMIT 8", tenant), "risks": [{"title": "执行提醒", "content": x["next_action"]} for x in operations if x["next_action"]][:5], } - return jsonify({"summary": summary, "sales": sales, "proposals": proposals, "operations": operations, "products": products, "finance": finance, "financeMonthly": monthly_finance(conn), "tasks": rows(conn, "SELECT * FROM project_tasks ORDER BY phase, sort_order, id")}) + return jsonify({"summary": summary, "sales": sales, "proposals": proposals, "operations": operations, "products": products, "finance": finance, "financeMonthly": monthly_finance(conn, tenant), "tasks": tasks, "tenant": tenant, "tenants": ["科普·无界","科研·无界","医患·无界"]}) finally: conn.close() TABLES = { - "sales": ("sales_leads", ["target_customer", "priority", "status"]), - "proposals": ("business_proposals", ["customer_or_project_name", "version", "description", "status", "created_date"]), - "operations": ("operation_projects", ["project_name", "project_version", "project_type", "project_status", "current_stage", "owner", "target_customer", "customer_need", "expected_contract_amount", "expected_sign_date", "sign_probability", "next_action", "sop_stage", "execution_progress", "current_deliverable", "risks", "notes"]), - "products": ("product_versions", ["product_name", "version", "version_goal", "feature_list", "launch_date", "status", "platform", "notes"]), - "finance": ("finance_records", ["month", "project_name", "record_type", "category", "amount", "occurred_date", "notes"]), - "tasks": ("project_tasks", ["project_id", "phase", "milestone", "task", "owner", "due_date", "blockers", "notes", "status", "sort_order"]), + "sales": ("sales_leads", ["target_customer", "priority", "status", "tenant"]), + "proposals": ("business_proposals", ["customer_or_project_name", "version", "description", "status", "created_date", "tenant"]), + "operations": ("operation_projects", ["project_name", "project_version", "project_type", "project_status", "current_stage", "owner", "target_customer", "customer_need", "expected_contract_amount", "expected_sign_date", "sign_probability", "next_action", "sop_stage", "execution_progress", "current_deliverable", "risks", "notes", "tenant"]), + "products": ("product_versions", ["product_name", "version", "version_goal", "feature_list", "launch_date", "status", "platform", "notes", "tenant"]), + "finance": ("finance_records", ["month", "project_name", "record_type", "category", "amount", "occurred_date", "notes", "tenant"]), + "tasks": ("project_tasks", ["project_id", "phase", "milestone", "task", "owner", "due_date", "blockers", "notes", "status", "sort_order", "tenant"]), } diff --git a/static/app.js b/static/app.js index 236dc21..b40ca27 100644 --- a/static/app.js +++ b/static/app.js @@ -1,6 +1,7 @@ const state = { active: "home", data: null, + tenant: "科普·无界", opFilter: "all", projectView: null, chart: null, @@ -48,7 +49,7 @@ function renderTable(headers, rows, rowClicks) { } async function load() { - state.data = await api("/api/bootstrap"); + state.data = await api(`/api/bootstrap?tenant=${encodeURIComponent(state.tenant)}`); render(); } @@ -231,6 +232,11 @@ window.submitTaskForm = async (event, projectId) => { }; window.createFinance = (event) => createResource(event, "finance"); window.switchTab = switchTab; +window.switchTenant = (tenant) => { + state.tenant = tenant; + state.projectView = null; + load(); +}; function renderProjects() { // 二级页面:项目任务详情 diff --git a/templates/index.html b/templates/index.html index c4c6c58..d98a084 100644 --- a/templates/index.html +++ b/templates/index.html @@ -27,9 +27,18 @@
OPC Manager · 单用户 · 单项目
-OPC Manager
+