This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
# 依赖:db.py;被 routes.py 和 migrations/seed_data.py 调用
|
||||
|
||||
import json
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
from db import _exec, rows, one
|
||||
|
||||
@@ -61,7 +62,8 @@ def attach_common(conn, resource, items):
|
||||
|
||||
|
||||
def monthly_finance(conn, tenant="科普·无界"):
|
||||
months = [f"2026-{m:02d}" for m in range(1, 13)]
|
||||
_year = date.today().year
|
||||
months = [f"{_year}-{m:02d}" for m in range(1, 13)]
|
||||
pfs = rows(conn,
|
||||
"SELECT sign_amount, sign_month, status, budget_data, expense_data FROM project_finances WHERE tenant=? AND status='已签约'",
|
||||
[tenant])
|
||||
|
||||
@@ -228,6 +228,7 @@ def index():
|
||||
def bootstrap():
|
||||
if "user_id" not in session:
|
||||
return jsonify({"error": "未登录"}), 401
|
||||
_year = date.today().year
|
||||
tenant = request.args.get("tenant", session.get("tenants", ["科普·无界"])[0])
|
||||
allowed = session.get("tenants", [])
|
||||
if "总工作台" not in allowed:
|
||||
@@ -262,7 +263,7 @@ def bootstrap():
|
||||
for pf in t_pfs:
|
||||
bm = t_bm.get(pf["id"], {})
|
||||
for m in months_range:
|
||||
b = bm.get(f"2026_{m:02d}")
|
||||
b = bm.get(f"{_year}_{m:02d}")
|
||||
if b:
|
||||
total += float(b.get(field) or 0)
|
||||
return total
|
||||
@@ -281,7 +282,7 @@ def bootstrap():
|
||||
for pf in t_pfs:
|
||||
em = t_em.get(pf["id"], {})
|
||||
for m in months_range:
|
||||
e = em.get(f"2026_{m:02d}")
|
||||
e = em.get(f"{_year}_{m:02d}")
|
||||
if e:
|
||||
total += float(e.get(field) or 0)
|
||||
return total
|
||||
@@ -289,10 +290,10 @@ def bootstrap():
|
||||
_now_month = date.today().month
|
||||
_q_start = ((_now_month - 1) // 3) * 3 + 1
|
||||
_q_range = range(_q_start, _q_start + 3)
|
||||
_q_months = [f"2026-{m:02d}" for m in _q_range]
|
||||
_q_months = [f"{_year}-{m:02d}" for m in _q_range]
|
||||
_prev_q_start = ((_now_month - 4) // 3) * 3 + 1
|
||||
_prev_q_range = range(max(_prev_q_start, 1), _prev_q_start + 3)
|
||||
_prev_q_months = [f"2026-{m:02d}" for m in _prev_q_range]
|
||||
_prev_q_months = [f"{_year}-{m:02d}" for m in _prev_q_range]
|
||||
_prev_month = _now_month - 1 if _now_month > 1 else None
|
||||
all_metrics.append({
|
||||
"total_projects": len(t_signed_pfs),
|
||||
@@ -302,9 +303,9 @@ def bootstrap():
|
||||
"signed_amount": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约"),
|
||||
"signed_annual": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约"),
|
||||
"signed_q2": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] in _q_months),
|
||||
"signed_month": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"2026-{_now_month:02d}"),
|
||||
"signed_month": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"{_year}-{_now_month:02d}"),
|
||||
"signed_prev_q": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] in _prev_q_months),
|
||||
"signed_prev_month": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"2026-{_prev_month:02d}") if _prev_month else 0,
|
||||
"signed_prev_month": sum(x["sign_amount"] or 0 for x in t_pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"{_year}-{_prev_month:02d}") if _prev_month else 0,
|
||||
"revenue_annual": t_sum_budget("rev", range(1, 13)),
|
||||
"revenue_q2": t_sum_budget("rev", _q_range),
|
||||
"monthly_revenue": t_sum_budget("rev", [_now_month]),
|
||||
@@ -369,7 +370,7 @@ def bootstrap():
|
||||
agg[key] = sum(m.get(key, 0) for m in all_metrics)
|
||||
merged_monthly = []
|
||||
for i in range(12):
|
||||
m = {"month": all_monthly[0][i]["month"] if all_monthly and len(all_monthly[0]) > i else f"2026-{i+1:02d}"}
|
||||
m = {"month": all_monthly[0][i]["month"] if all_monthly and len(all_monthly[0]) > i else f"{_year}-{i+1:02d}"}
|
||||
for field in ["revenue","gross","payment","cost","paid","sign"]:
|
||||
m[field] = sum(tl[i][field] if i < len(tl) else 0 for tl in all_monthly)
|
||||
merged_monthly.append(m)
|
||||
@@ -415,7 +416,7 @@ def bootstrap():
|
||||
total = 0
|
||||
for pf, bm in budget_maps:
|
||||
for m in months_range:
|
||||
b = bm.get(f"2026_{m:02d}")
|
||||
b = bm.get(f"{_year}_{m:02d}")
|
||||
if b:
|
||||
total += float(b.get(field) or 0)
|
||||
return total
|
||||
@@ -424,7 +425,7 @@ def bootstrap():
|
||||
total = 0
|
||||
for pf, em in expense_maps:
|
||||
for m in months_range:
|
||||
e = em.get(f"2026_{m:02d}")
|
||||
e = em.get(f"{_year}_{m:02d}")
|
||||
if e:
|
||||
total += float(e.get(field) or 0)
|
||||
return total
|
||||
@@ -494,12 +495,12 @@ def bootstrap():
|
||||
return sum(x["sign_amount"] or 0 for x in pfs if x["status"] == status)
|
||||
signed_amount = pf_status_sum("已签约")
|
||||
signed_annual = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约")
|
||||
_q_months = [f"2026-{m:02d}" for m in _q_range]
|
||||
_q_months = [f"{_year}-{m:02d}" for m in _q_range]
|
||||
signed_q2 = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] in _q_months)
|
||||
signed_month = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"2026-{_now_month:02d}")
|
||||
_prev_q_months = [f"2026-{m:02d}" for m in _prev_q_range]
|
||||
signed_month = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"{_year}-{_now_month:02d}")
|
||||
_prev_q_months = [f"{_year}-{m:02d}" for m in _prev_q_range]
|
||||
signed_prev_q = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] in _prev_q_months)
|
||||
signed_prev_month = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"2026-{_prev_month:02d}") if _prev_month else 0
|
||||
signed_prev_month = sum(x["sign_amount"] or 0 for x in pfs if x["status"] == "已签约" and (x.get("sign_month") or "")[:7] == f"{_year}-{_prev_month:02d}") if _prev_month else 0
|
||||
pipeline_amount = sum(x["expected_contract_amount"] or 0 for x in operations if x["project_status"] not in ["已签约","已丢单","已归档","已完成"])
|
||||
signed_not_executed = sum(x["expected_contract_amount"] or 0 for x in operations if x["project_type"] == "execution" and x["execution_progress"] < 100)
|
||||
summary = {
|
||||
|
||||
@@ -105,14 +105,15 @@ function renderHome() {
|
||||
}
|
||||
document.querySelector("#home").innerHTML = `
|
||||
<div class="grid gap-5">
|
||||
${state.tenant === "总工作台" ? "" : `<div class="grid grid-cols-4 gap-3">
|
||||
${[
|
||||
["项目管理", m.total_projects, "finance"],
|
||||
["重点工作与台账", m.total_proposals, "projects"],
|
||||
["业务方案", m.total_products, "proposals"],
|
||||
["产品迭代", m.upcoming_products, "products"],
|
||||
].map(([label, value, tab]) => `<button class="metric-card" onclick="switchTab('${tab}')"><span class="flex items-center gap-2 text-xs text-slate-500"><i data-lucide="gauge"></i>${label}</span><strong class="mt-2 block text-2xl">${value}</strong></button>`).join("")}
|
||||
</div>`}
|
||||
<div class="flex items-center gap-2">
|
||||
<span class="text-xs text-slate-500">筛选:</span>
|
||||
<select id="homeFilter" class="text-xs font-medium py-1.5 px-3 rounded border border-slate-200 bg-white cursor-pointer" onchange="renderHomeCards()" style="appearance:none;-webkit-appearance:none;padding-right:28px;background:url('data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 width=%2212%22 height=%2212%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%236b7280%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22%3E%3Cpolyline points=%226 9 12 15 18 9%22%3E%3C/polyline%3E%3C/svg%3E') no-repeat right 8px center">
|
||||
<option value="all">全部时间</option>
|
||||
<option value="year">本年度</option>
|
||||
<option value="quarter">本季度</option>
|
||||
<option value="month">本月</option>
|
||||
</select>
|
||||
</div>
|
||||
${card(`<h3 class="text-sm font-bold text-slate-700 mb-3">部门经营情况</h3><div class="overflow-x-auto"><table class="w-full text-sm"><thead><tr class="border-b border-slate-200"><th class="py-2 text-left text-slate-500">指标</th><th class="py-2 text-right font-semibold text-slate-700">年度累计</th><th class="py-2 text-right font-semibold text-slate-700">上季度累计</th><th class="py-2 text-right font-semibold text-slate-700">上月累计</th><th class="py-2 text-right font-semibold text-slate-700">本季度累计</th><th class="py-2 text-right font-semibold text-slate-400">季环比</th><th class="py-2 text-right font-semibold text-slate-700">本月累计</th><th class="py-2 text-right font-semibold text-slate-400">月环比</th></tr></thead><tbody>
|
||||
${(() => {
|
||||
var d = [
|
||||
|
||||
@@ -76,7 +76,7 @@
|
||||
<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-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.2</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.3</span></p>
|
||||
<div class="flex items-center gap-4 mt-1">
|
||||
<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">
|
||||
|
||||
Reference in New Issue
Block a user