From 9970446eceda866c80dd6495ece4293834ada06c Mon Sep 17 00:00:00 2001 From: seaislee1209 Date: Thu, 12 Feb 2026 15:29:20 +0800 Subject: [PATCH] =?UTF-8?q?feat(=E6=B5=A0=EE=81=87=E3=80=83=E9=90=A9=3F:?= =?UTF-8?q?=20=E9=8F=82=E6=9D=BF=EE=96=834=E6=B6=93=E7=8B=A4Charts?= =?UTF-8?q?=E9=8D=99=EE=88=9D=EE=9D=8B=E9=8D=96=E6=A0=A7=E6=B5=98=E7=90=9B?= =?UTF-8?q?=3F=20-=20=E6=9D=A9=3F0=E6=BE=B6=E2=95=80=E9=AA=87=E9=8D=91?= =?UTF-8?q?=E9=B8=BF=E7=A7=BC=E9=8D=94=E6=8C=8E=E5=A7=8C=E7=BB=BE=E5=9E=AE?= =?UTF-8?q?=E6=B5=98=20-=20=E9=8E=B4=E6=84=AD=E6=B9=B0=E9=8F=8B=E5=8B=AC?= =?UTF-8?q?=E5=9E=9A=E9=90=9C=EE=88=9A=E8=88=B0=E6=A5=97=E7=85=8E=E6=B5=98?= =?UTF-8?q?=E9=94=9B=E5=A0=9C=E6=B1=89=E9=8D=94=3FAI=E5=AE=B8=E3=83=A5?= =?UTF-8?q?=E5=8F=BF/=E6=BE=B6=E6=A0=A7=E5=AF=98=E9=94=9B=3F-=20=E6=A4=A4?= =?UTF-8?q?=E5=9C=AD=E6=B4=B0=E6=B5=9C=D1=83=E5=9A=AD=E7=80=B5=E8=A7=84?= =?UTF-8?q?=E7=98=AE=E5=A6=AF=EE=81=84=E6=82=9C=E9=8F=8C=E8=BE=A9=E5=A7=B8?= =?UTF-8?q?=E9=8D=A5=3F-=20=E9=8E=B9=E7=86=BB=E2=82=AC=E6=A5=81=E5=B8=93?= =?UTF-8?q?=E7=90=9B=E5=B1=BE=E7=85=B4=E9=90=98=E8=B7=BA=E6=B5=98=E9=94=9B?= =?UTF-8?q?=E5=A0=9F=E5=AF=9C=E9=8E=B9=E7=86=BB=E2=82=AC=E6=A5=83=E5=B7=BC?= =?UTF-8?q?=E9=90=AB=E2=82=AC=E9=91=B9=E8=AF=A7=E7=B4=9A=20-=20=E9=8D=9A?= =?UTF-8?q?=E5=BA=A3=EE=81=AC=E9=8F=82=E6=9D=BF=EE=96=83=20daily=5Ftrend?= =?UTF-8?q?=20/=20cost=5Fbreakdown=20/=20project=5Fcomparison=20=E9=8F=81?= =?UTF-8?q?=E7=89=88=E5=B5=81=E9=8E=BA=E3=83=A5=E5=BD=9B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cursor --- backend/routers/dashboard.py | 45 +++++ frontend/src/views/Dashboard.vue | 286 +++++++++++++++++++++---------- 2 files changed, 237 insertions(+), 94 deletions(-) diff --git a/backend/routers/dashboard.py b/backend/routers/dashboard.py index 7e2595d..ad0a6ff 100644 --- a/backend/routers/dashboard.py +++ b/backend/routers/dashboard.py @@ -116,6 +116,47 @@ def get_dashboard( "profit_loss": settlement.get("profit_loss"), }) + # ── 图表数据:近30天每日产出趋势 ── + daily_trend = [] + for i in range(29, -1, -1): + d = today - timedelta(days=i) + day_secs = db.query(sa_func.sum(Submission.total_seconds)).filter( + Submission.submit_date == d, + Submission.total_seconds > 0, + ).scalar() or 0 + daily_trend.append({ + "date": str(d), + "seconds": round(day_secs, 1), + }) + + # ── 图表数据:成本构成 ── + total_labor_all = 0.0 + total_ai_all = 0.0 + total_outsource_all = 0.0 + for p in active + completed: + total_labor_all += calc_labor_cost_for_project(p.id, db) + total_ai_all += calc_ai_tool_cost_for_project(p.id, db) + total_outsource_all += calc_outsource_cost_for_project(p.id, db) + + cost_breakdown = [ + {"name": "人力成本", "value": round(total_labor_all, 0)}, + {"name": "AI工具", "value": round(total_ai_all, 0)}, + {"name": "外包", "value": round(total_outsource_all, 0)}, + ] + + # ── 图表数据:各项目产出对比(进行中项目) ── + project_comparison = [] + for p in active: + total_secs_p = db.query(sa_func.sum(Submission.total_seconds)).filter( + Submission.project_id == p.id, + Submission.total_seconds > 0, + ).scalar() or 0 + project_comparison.append({ + "name": p.name, + "submitted": round(total_secs_p, 0), + "target": p.target_total_seconds, + }) + return { "active_projects": len(active), "completed_projects": len(completed), @@ -126,6 +167,10 @@ def get_dashboard( "projects": project_summaries, "waste_ranking": waste_ranking, "settled_projects": settled, + # 图表数据 + "daily_trend": daily_trend, + "cost_breakdown": cost_breakdown, + "project_comparison": project_comparison, } diff --git a/frontend/src/views/Dashboard.vue b/frontend/src/views/Dashboard.vue index 4178d8b..0bf6cd5 100644 --- a/frontend/src/views/Dashboard.vue +++ b/frontend/src/views/Dashboard.vue @@ -32,6 +32,18 @@ + +
+
+
近 30 天产出趋势
+
+
+
+
成本构成
+
+
+
+
@@ -48,12 +60,7 @@
{{ p.progress_percent }}%
- +
{{ formatSecs(p.submitted_seconds) }} / {{ formatSecs(p.target_seconds) }} 损耗 {{ p.waste_rate }}% @@ -63,59 +70,57 @@
-
- -
-
损耗排行
-
- - - - - - - - - - -
+ +
+
+
项目产出对比
+
+
+
损耗排行
+
+
+
- -
-
已结算项目
-
- - - - - - - - - - -
+ +
+
已结算项目
+
+ + + + + + + + +