From 9afcea8f8d84d150df5bcf3f424e452b689cb231 Mon Sep 17 00:00:00 2001 From: Hermes CI Fix Date: Tue, 14 Jul 2026 17:32:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=88=98=E7=95=A5=E5=9C=B0=E5=9B=BE?= =?UTF-8?q?=E6=94=B6=E5=B0=BE=20=E2=80=94=20=E8=87=AA=E5=8A=A8=E4=BF=9D?= =?UTF-8?q?=E5=AD=98+=E5=AF=BC=E5=87=BA+=E8=A1=A8=E8=BF=81=E7=A7=BB+?= =?UTF-8?q?=E5=9B=A0=E6=9E=9C=E9=93=BE=E6=8E=A8=E8=8D=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 141 bytes .../auth_middleware.cpython-312.pyc | Bin 0 -> 8577 bytes .../app/__pycache__/database.cpython-312.pyc | Bin 0 -> 5453 bytes .../api/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 145 bytes .../app/api/__pycache__/auth.cpython-312.pyc | Bin 0 -> 4096 bytes .../api/__pycache__/dashboard.cpython-312.pyc | Bin 0 -> 28080 bytes .../app/api/__pycache__/data.cpython-312.pyc | Bin 0 -> 18511 bytes .../app/api/__pycache__/kpis.cpython-312.pyc | Bin 0 -> 13471 bytes .../app/api/__pycache__/maps.cpython-312.pyc | Bin 0 -> 17244 bytes .../api/__pycache__/templates.cpython-312.pyc | Bin 0 -> 10125 bytes backend/app/api/maps.py | 62 ++++++++++++-- .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 19312 bytes .../__pycache__/budget_plan.cpython-312.pyc | Bin 0 -> 1805 bytes .../__pycache__/cost_model.cpython-312.pyc | Bin 0 -> 5347 bytes .../__pycache__/knowledge.cpython-312.pyc | Bin 0 -> 4720 bytes backend/scripts/migrate_map_objectives.py | 78 ++++++++++++++++++ frontend/components.d.ts | 51 ------------ frontend/src/views/MapCanvas.vue | 75 ++++++++++++++++- 18 files changed, 207 insertions(+), 59 deletions(-) create mode 100644 backend/app/__pycache__/__init__.cpython-312.pyc create mode 100644 backend/app/__pycache__/auth_middleware.cpython-312.pyc create mode 100644 backend/app/__pycache__/database.cpython-312.pyc create mode 100644 backend/app/api/__pycache__/__init__.cpython-312.pyc create mode 100644 backend/app/api/__pycache__/auth.cpython-312.pyc create mode 100644 backend/app/api/__pycache__/dashboard.cpython-312.pyc create mode 100644 backend/app/api/__pycache__/data.cpython-312.pyc create mode 100644 backend/app/api/__pycache__/kpis.cpython-312.pyc create mode 100644 backend/app/api/__pycache__/maps.cpython-312.pyc create mode 100644 backend/app/api/__pycache__/templates.cpython-312.pyc create mode 100644 backend/app/models/__pycache__/__init__.cpython-312.pyc create mode 100644 backend/app/models/__pycache__/budget_plan.cpython-312.pyc create mode 100644 backend/app/models/__pycache__/cost_model.cpython-312.pyc create mode 100644 backend/app/models/__pycache__/knowledge.cpython-312.pyc create mode 100644 backend/scripts/migrate_map_objectives.py diff --git a/backend/app/__pycache__/__init__.cpython-312.pyc b/backend/app/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34c847aa1c85aaa5ce0cf6504158c4cfeb993b97 GIT binary patch literal 141 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 literal 0 HcmV?d00001 diff --git a/backend/app/__pycache__/database.cpython-312.pyc b/backend/app/__pycache__/database.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3c1131fa5450e433d5b89ab790c75dcb8e1e7ad0 GIT binary patch literal 5453 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 literal 0 HcmV?d00001 diff --git a/backend/app/api/__pycache__/__init__.cpython-312.pyc b/backend/app/api/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..084293ea48687aa4b6cf53ccb557480ab16cd2d4 GIT binary patch literal 145 zcmX@j%ge<81PkwqWP#|%AOanHW&w&!XQ*V*Wb|9fP{ah}eFmxdWuRY_pI@S%oSUed zo0ylFo|>DQSE8Skn4F!Om!h9oPyoc4`tk9Zd6^~g@p=W7zc_4i^HWN5QtgUZfd()F RaWRPTk(rT^v4|PS0sz$AA;|y$ literal 0 HcmV?d00001 diff --git a/backend/app/api/__pycache__/auth.cpython-312.pyc b/backend/app/api/__pycache__/auth.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..686a1d7937da161f6838e2e1e21f02bfd86c7e3d GIT binary patch literal 4096 zcmbtXZA=`;8J_*TJ8qBT_&B}{2HO~4V;egK$Br#mIL3h_B37WOb8U4!ZWq||ec;)> z7+XgRl@&ui3ba-L#i~KEWFotTq(AJaiQNDFdG zR_aJQ@4GYayfZuVKF_@SXNRKzL3!>>SMZ`0p}&(Cv+(Q6no1!wg+$~-

rW?*ygnvIv*+s+r4%}w}gbS!|MnadJDr&uaiPF zk{Hn{+WM$l1vlx;oZcd-D7e#HNKT6Oo3IvAU=%Ca%(q@j6r^I&Az6A^vG5XiGh>9j zC0qD$nj`;%pP~(ZO2N-F>rOWa(e)iah2|Tqr&zN0(qhR5AMP!Dl$xW&vP&qt^70F` z#~l-3vL{#OACKN%`Tg~kg^{nxYnrR3r=@gaDTOs^n9nXQhZPVCh^;#*)t;Q~P!9wO3zh z>$>P@@(;*Ofv~@cw8lt4WR~Hfb$$R#z4D;Ws{TIZBHt8>_Q?^Q75%DTr^TMY`bm=Y zA{#smJZt^XI)&mWxPFSH(<~&m%<~_jx3FP0*f4P_NW5+;Dr+{F`x>;EEjgL93PRj( zE^+$o9)z;v?VJ0dZ@p=VjkEZ8oDGtb+Ei4h*$^x;wVaGI!IIpYshNJWbC;PtIceIQ zeGVZksO4sl$cmh~gTJKIO0ykSS7qusY0faj#ku?z(3)>=*xWEvaq1DWQ>Q?X<5WBFvO)$=`4II&M4pR zZNky0N+7*2?2q{Sq_7lGn|k~K-~bWOK42i8pnd~+^mZ`91yEbd9vKd;%3Che^Wd#h%D zVff^--8s>mENMuTG~6m*sQsw+j`gndQ>S*gL-V|p^mHdY-J0k1zm&Xwzp#4xg?sf! zwZdcftj8V+71O2H%4fUaOp` zoNc~wWbVj(k5+jwS=o}PY+3BRBi@xim9@(DRXgu0SmkU2pGG#0UxTki7i~>g>XQ{X zy!=$ALl6O#QTc*Y&61x9vLHFZO(ka)G_TqX7C?aHz&90H=FQkP;mRSA2)V0Oeapo$_6>ee2ix$)3mi6X3;SZ4uAxnnl_aYQ$ z%sr#Xd1YxaXJHa5Bq%nqK&F5^?41UbVoxLmxoSxYQs2VLtWNyN^dFQg2`LIB+#FC5(Xyw`PVD&c-#Q zGT^_RXmz9_q>6Vyr89m})cHU(9F|p>HA@3JCk;aE8XTaI?IGyLB%#u+W?19B@Jd`M zpF;((77EAP$J(!Sztfo%>Jvi!^<6ji&Fx!kzJ2)P!*^cNzW-uk$BUZKo)kJ0LZ>F2 z9&TH<2MrB{|% z{&L4i$EBB-gtCdB-?Q(&$L_`r@C6r{Cg6D?5W^Ta%|{qH0Ed??WOt1uWCau>3CVWl z6o|JhcE8iQ9%H#Sh%1y2w`vx(K_j)^IAW_Y8?HP;=$nd2<)6t!0QBZbK1BK|4{^*G z7v0LQZl}iIPksKI)YyBOKz?+0?(xWi5uI><7`ICHqRU(@1W7tK&>xLR9!p*evQ}(F zX%lE33hx7tD1Ua@{Q?ua{GbA_5+JMw6d0D*^xxqkvqHpv^RO8D{-PrE9+iz)g)az6Rw@B z4hx}g3&&@IfrpDOBq;%yFk7-|s0vN+Lqm}tn@q`f>Lx3kMB`|j3NU>rK>rjr=_e2& zLud$2B@QVop%ah{ks^DQnWWz>VNrk?qJ|J*k^oJuznw=OF18EIR_9fKkT0U+@F;Ci8RuYgKB}m>W}mTt;P@xUxC*{1 zDxIJv4yGC8E}x}lchA`t&)uo|ct{i4))?qdGl0l6;yC_aYc#wYAW9HnVg8=EVBzVmeAHKSZTogaSriqwtd`iR` zgXr=!4O)7SIpW6}Rk_@_fBwx6f3b=Ls&S$@O~mw%o)SSuRHbRq(zRqn)kxK7n?@Bc p*EP-yT3yR3vVr%~r$i9jk~9rky27+=*Ql~p+LGyX8=CR){|2IzM!^68 literal 0 HcmV?d00001 diff --git a/backend/app/api/__pycache__/dashboard.cpython-312.pyc b/backend/app/api/__pycache__/dashboard.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ed734add2187f476bd97ea5b49e560144ae6a545 GIT binary patch literal 28080 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 literal 0 HcmV?d00001 diff --git a/backend/app/api/__pycache__/templates.cpython-312.pyc b/backend/app/api/__pycache__/templates.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..492fb29de52f6367d3d20a886b4e101acadfc30f GIT binary patch literal 10125 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 literal 0 HcmV?d00001 diff --git a/backend/app/api/maps.py b/backend/app/api/maps.py index fbdabc9f..32fbdc49 100644 --- a/backend/app/api/maps.py +++ b/backend/app/api/maps.py @@ -4,7 +4,7 @@ from sqlalchemy.orm import Session from typing import Optional from app.database import get_db from app.auth_middleware import require_auth, require_role -from app.models import StrategicMap, OperationLog +from app.models import StrategicMap, OperationLog, MapObjective import json router = APIRouter(prefix="/api/cma/maps", tags=["战略地图"], @@ -62,7 +62,7 @@ STRATEGIC_MAP_TEMPLATE = [ @router.get("") def list_maps(db: Session = Depends(get_db)): maps = db.query(StrategicMap).order_by(StrategicMap.updated_at.desc()).all() - return {"data": [m_to_dict(m) for m in maps]} + return {"data": [m_to_dict(m, db) for m in maps]} @router.post("") def create_map(data: dict, db: Session = Depends(get_db)): @@ -70,7 +70,8 @@ def create_map(data: dict, db: Session = Depends(get_db)): db.add(m) db.commit() db.refresh(m) - return m_to_dict(m) + _sync_map_objectives(m, db) + return m_to_dict(m, db) @router.post("/create-with-template") @@ -86,7 +87,8 @@ def create_map_with_template(data: dict, db: Session = Depends(get_db)): db.add(m) db.commit() db.refresh(m) - return m_to_dict(m) + _sync_map_objectives(m, db) + return m_to_dict(m, db) @router.put("/{map_id}") @@ -101,12 +103,14 @@ def update_map(map_id: int, data: dict, db: Session = Depends(get_db)): setattr(m, k, v) db.commit() + # 同步目标到map_objectives表 + _sync_map_objectives(m, db) # ├─ 版本管理: draft → published 时自动创建快照 if old_status == "draft" and m.status == "published": _auto_snapshot(m, db) - return m_to_dict(m) + return m_to_dict(m, db) # ── 删除地图 ───────────────────────────────── @@ -259,8 +263,52 @@ def _auto_snapshot(m: StrategicMap, db: Session): # ── 工具函数 ───────────────────────────────── -def m_to_dict(m): - return {c.name: getattr(m, c.name) for c in m.__table__.columns} +def m_to_dict(m, db: Session = None): + d = {c.name: getattr(m, c.name) for c in m.__table__.columns} + if db: + _merge_map_objectives(m, db) + d["dimensions"] = m.dimensions + return d + + +def _sync_map_objectives(m, db): + """保存时:将dimensions JSON中的目标同步到map_objectives表""" + db.query(MapObjective).filter(MapObjective.map_id == m.id).delete() + dims = m.dimensions + if isinstance(dims, str): + dims = json.loads(dims) + for dim in dims: + for i, obj in enumerate(dim.get("objectives", [])): + mo = MapObjective( + map_id=m.id, + dimension_key=dim.get("key", ""), + name=obj.get("name", ""), + description=obj.get("description", ""), + icon=obj.get("icon", "target"), + sort_order=i, + ) + db.add(mo) + db.commit() + + +def _merge_map_objectives(m, db): + """读取时:将map_objectives表的数据合并进dimensions JSON""" + objs = db.query(MapObjective).filter(MapObjective.map_id == m.id).order_by(MapObjective.sort_order).all() + if not objs: + return + dims = m.dimensions + if isinstance(dims, str): + dims = json.loads(dims) + # 按dimension_key分组 + from collections import defaultdict + grouped = defaultdict(list) + for o in objs: + grouped[o.dimension_key].append(o) + for dim in dims: + key = dim.get("key", "") + if key in grouped: + dim["objectives"] = [{"name": o.name, "description": o.description or "", "icon": o.icon or "target"} for o in grouped[key]] + m.dimensions = dims # ── 战略回顾会 聚合接口 ────────────────────── diff --git a/backend/app/models/__pycache__/__init__.cpython-312.pyc b/backend/app/models/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c80085e00064bb898c864aea6201a71c26eb5f88 GIT binary patch literal 19312 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 IXAcVfKOi*K761SM literal 0 HcmV?d00001 diff --git a/backend/app/models/__pycache__/budget_plan.cpython-312.pyc b/backend/app/models/__pycache__/budget_plan.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6ffb2f5933946261bcd1f3db575ba91b9e8036f5 GIT binary patch literal 1805 zcma)6U1%It6rS0i+1bCOo82^NwGfIymzr#n(rUE|Mw^nPCbbDh=4F`dy-g;WpY7e5 z*zMC6Vw<91Y70%+*QMZrvk!bg$&q z1xe6-l1~>UQTI!Jp6R)@fF6{BJm=z0a>z5uAukTjyB(iWC?mvub~A9BpG=k(Z)_~i z6{bEa=H?1_-f#Lnd%dZ&I9Hm!^l0@KNcsmhnOR(jw388x|hHgbD)|QH>Gm z?b1xeqT(Bhg$Gp~(?FL=usUM&;tcf+;`0{uoVAT)oTr{nC5@9#9u5_@nXOfx6^c2D zhhklbm)yuDd0ZU!Vgb34d%;sFz~`VqMdY0aJGPL3d<)_{93)$kpXtCCK>jL?0BGzS zL$K^61a`qHG+agnKtok%O$7~Cp|uQ!uR2Bzs$JMqiLvKjG$N?3N+VLyh*t7+qgYjx zx{5~quExUNdDnKOQ6|SRp!H+VJ6JWH-)0sXDi#g@&*DElHdg9v%FCm&#H!Msiu)`RZ*AihiHZOmNzb52mfFw8n&h>!y2Z( zq^axJur_0$Vl(1oRh<2J^TxfVj<$}YEp1224jcg_Vuvbfg|F^C`uZC?QUM@y0Girc z+B+_ICfZxu>?p`eSEh=y3xyBwH!&zN(b3ulS@4f^D29EGAr;nlvU8n6Q$WueggeFmIx1*eL`zt}niiin44d zI+kS`kY(LOw#N1$Sw3eg+P1_e%g9Uu9F}E^9gTw%b{z=2HPoddBC?IZju;sB2vH%0 ziE1Lk60CG+LeXqYgWLVeV1-mfx@lOW)CVc2p*>VcTZ(0;sX#Dva-0T|1Vh$H9?lRS zxFqZiq;A7}cUJ_DNx&%%$%^#_w*@NPlcNb@npPsID=oTWC?lA)H8HFt$FYGDN-C9r z%CVMCR83K9Dnr673L~t7bP32Lx9&OibL?RD&ENdB)4scdOR>EFXtr}b9GyOSQ(AlO z_`?IQ{NP<18qSB4*)D*ucFlBL>&exxz1Wuz_X8B&H`APJUU@1XJ&`@NUe`F&kUO%{ zny>55_A=SCxf5$I_vNGgAgqa9eS4rh;KsW%@!wG-p;wcgw_ zD`GzU8X%E|o5D=`b})zXk;B;@;Ah08#--T(#xEO}_va(W|A9RieSp3n{dV-bv3%sM zi#>$B2Ib28;$iYMFf@=przu);6ziGY#+UnC?yu7kHUos+D5p0eINDa2G-VjxZW7$_ zNe;1HIYbNWo`}bt!F!do0m~lc^g$qhx_O>o=MMeK4gJIoJ>-Ud;SM|&YWXw#bnGvV I{mVZ71`N;(djJ3c literal 0 HcmV?d00001 diff --git a/backend/app/models/__pycache__/cost_model.cpython-312.pyc b/backend/app/models/__pycache__/cost_model.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6906bf67f5923035475988b58415439cfbbeb90a GIT binary patch literal 5347 zcmd5=TW}NS6<*zCU2IF1WMgu12v9@{q7*1Gm&Eu&z}N(^+f*6Q$l48-(Z&4Qbz}M> zrUXn<2UCY2Fs1eb6EV21l5~m_!h}v9N~bTH>2`{DrVnW>3BR?)OlR`cbN-cA>q`b` z`p}*6(eC%3|NOgW_k90%{3pBJ!oachqw1a)D;ef1yqG%-#ghjqIJwBMjGtjOtTv*F zYW>=%&aaE={rafEZ-^TG#;D0}ikkiAXqmq(YVlh%=&OrZqc*=y!)Te^468rIum;X{ zLnp`e+bL=U)TBboDQX6^OodiZ)B>ngg;w_4ymo^${FU+C!0G(ep?rEQclDjR zzbD_TD@=?PhE7j^cKQCq*#1M7{OFn7nbSqz!Lh>GGhZc7<|eMrTzabr-u}hR$Y}tl zZ(o_7yexzHk1wog*wyH@%;1fC%OqoCJR(G6l4(zj=ZsYN{uVpv`XJmD(Zd|V<(u64dnOVaPn1u&xWKH8{#iLB|C{uY@R2~+^ z!>aPIsXVNThdPE`57_FS4Kk*-H;NbIHOh=-k2m9y5tk`FO{1Mlar4CdY*D_lOMcF$iu z1s}0go+!o)gdk|5_+%#^g(TFCA^Bvy4K_sLMz$O{=m_N5N7suQ3}&-uPo*YJsSb9P&oSQ$$0 zk)hD=Pt%>*?XQZJ2LP&hCMBeW$(UHvmesqbtd8@mQ_kVFqkQHy(YiC)H055NYECy# z?iAg7lkHQk+SKOs=4}0sMVCL>HdXFA|HIU-VSm~)xm+x7PVSzvSDkN3Z5-a4HfQTw zMf<*F3qV6nqnkwg8aQ`Ur_7m}jO%92hczG9ijM8cy$i5A-FMhObpO8liyqN&;N)IX zKbMzzTZgh=_x%5E9qJ8g(bfS}X=Y2C#Qbdp)BOd-Z~InymcVD^wv?@z?RrL zINTmwJ!>Jy3l3!ya4VaDr{CkPmMje+9!k0q3HwEuv{+J5!k)hMZu7xcefhKRl)Ct+F7yz;XtG!O%sr045o#mAZX89nCP=iT z2F)@w%h5aqhVrAlK#@C;bS%+pl8cJYD!k80< zzUGo7)0S10GT^GXKtf!Jp2!xA^8+;aX5`Y@hG#qG3??e^M=(e(jrUwD7Lkim!0znU zS48_BDBAxe-*}!%b*4Kf+eJ@naz8{)txB(&d`7H#1sKa&lX9nF*&m6{*OIOCsH8)* zcP>RAa|D48IFBGOn|M6<=-Uy54hjp^C2mh}_`gYAS`}qO)g@;I-D(>zp;Rx@qPMFw zXSvFQ7CrSS5eI6=QVgmtxeB%tO5LffCu>JUt5hh}j?~wsLQ8tlt=xr&tyXyy^&;Gd zy9Yne1&2ZlC2d#@z6Q?qs2PtMA1`Uc8mbMg4P7DNns6WN;sTaG^d)kuGCle0+~+^b zO)XOm8d*rAx8N| zh33zlyZ?D+hJ~;cl?7Ms?R0Krlmhaptkeh!ZJ`28v9(IxlKm-jsND0(Td47nGm7&E zPS3nGSa|mgFcS>QvwZTH>`WPwJPR`v!4{6u$LA;|k>_Ek-Mj3`c*$N&V85)lN|z|O zWHD62SS}HgtW>?Q1e79?tnzOFrIyTN7Cz1wsa>)cNBgjo

Lu%6Jpp?UDqx6$ciu z*lxtoAZaGP1cNO0>{1fRReNd6=!%j0G&|XOw_S9-4q}C3&yQ@&xbKvUuKg5AZA!14 zgsa_f<*d2n8P$y}r*g>I1`I?I?MN*|X&0TXfPj86j2_Q!Y!n?$AYG^&dMVaOm1IZmh(zL{AP=fuz+Mm7qgb@G#_msKK$Xb7 z<MhTW{z8^MjQVu zeINrMSHM4Xu=b&+rT^b43JmuX6*6hSg|Xk0P%2RF48Z-A^oFc5iWM1(#}ZJIERd-D zl+e^*T`yUY3yQG=S3-k{NL&Pi#kl>RYt`dO3fSta$JS=oz5M4D4R@PG=Rv5K$CDYd zfWZF6a{v^C+@55&?GxQ?(4QQe;XOi5zSwj!+&88|BZQi*67e2)C_H!W$<@)O}S>rkZYEKpV<`^II?n< S!Qa`HRv2k|$iNR3d;bAY`U^V% literal 0 HcmV?d00001 diff --git a/backend/app/models/__pycache__/knowledge.cpython-312.pyc b/backend/app/models/__pycache__/knowledge.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9b9809edc7ee5af808025f3fa731b294b1007d10 GIT binary patch literal 4720 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 Ihqi int: + """将单个地图的 dimensions 同步到 map_objectives 表,返回同步的目标数""" + # 删除该地图的旧目标 + conn.execute( + text("DELETE FROM map_objectives WHERE map_id = :mid"), + {"mid": map_id}, + ) + + count = 0 + for dim in dims: + dim_key = dim.get("key", "") + for idx, obj in enumerate(dim.get("objectives", [])): + name = obj.get("name", "").strip() + if not name: + continue + conn.execute( + text("""INSERT INTO map_objectives + (map_id, dimension_key, name, description, icon, sort_order) + VALUES (:mid, :dk, :name, :desc, :icon, :sort)"""), + { + "mid": map_id, + "dk": dim_key, + "name": name, + "desc": obj.get("description", ""), + "icon": obj.get("icon", "target"), + "sort": idx, + }, + ) + count += 1 + + return count + + +def migrate(): + engine = get_engine() + with engine.begin() as conn: + # 查询所有战略地图 + maps = conn.execute( + text("SELECT id, title, dimensions FROM strategic_maps ORDER BY id") + ).fetchall() + + total_maps = 0 + total_objectives = 0 + + for mid, title, dims_json in maps: + if not dims_json: + continue + + dims = json.loads(dims_json) if isinstance(dims_json, str) else dims_json + if not isinstance(dims, list): + continue + + count = sync_map_to_objectives(conn, mid, dims) + if count > 0: + total_maps += 1 + total_objectives += count + logger.info(f" 地图[{mid}] {title}: 同步 {count} 个目标") + + logger.info(f"\n✅ 迁移完成: {total_maps} 个地图, {total_objectives} 个目标已同步") + + +if __name__ == "__main__": + logger.info("=== 开始迁移 map_objectives ===") + migrate() + logger.info("=== 完成 ===") diff --git a/frontend/components.d.ts b/frontend/components.d.ts index 7300ae99..1752aa0b 100644 --- a/frontend/components.d.ts +++ b/frontend/components.d.ts @@ -13,54 +13,6 @@ declare module 'vue' { export interface GlobalComponents { BulletChart: typeof import('./src/components/charts/BulletChart.vue')['default'] ConnectionLines: typeof import('./src/components/strategy/ConnectionLines.vue')['default'] - ElAlert: typeof import('element-plus/es')['ElAlert'] - ElAside: typeof import('element-plus/es')['ElAside'] - ElButton: typeof import('element-plus/es')['ElButton'] - ElCard: typeof import('element-plus/es')['ElCard'] - ElCheckbox: typeof import('element-plus/es')['ElCheckbox'] - ElCheckboxGroup: typeof import('element-plus/es')['ElCheckboxGroup'] - ElCol: typeof import('element-plus/es')['ElCol'] - ElContainer: typeof import('element-plus/es')['ElContainer'] - ElDatePicker: typeof import('element-plus/es')['ElDatePicker'] - ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] - ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] - ElDialog: typeof import('element-plus/es')['ElDialog'] - ElDivider: typeof import('element-plus/es')['ElDivider'] - ElDropdown: typeof import('element-plus/es')['ElDropdown'] - ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] - ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] - ElEmpty: typeof import('element-plus/es')['ElEmpty'] - ElForm: typeof import('element-plus/es')['ElForm'] - ElFormItem: typeof import('element-plus/es')['ElFormItem'] - ElHeader: typeof import('element-plus/es')['ElHeader'] - ElIcon: typeof import('element-plus/es')['ElIcon'] - ElInput: typeof import('element-plus/es')['ElInput'] - ElInputNumber: typeof import('element-plus/es')['ElInputNumber'] - ElMain: typeof import('element-plus/es')['ElMain'] - ElMenu: typeof import('element-plus/es')['ElMenu'] - ElMenuItem: typeof import('element-plus/es')['ElMenuItem'] - ElOption: typeof import('element-plus/es')['ElOption'] - ElPagination: typeof import('element-plus/es')['ElPagination'] - ElProgress: typeof import('element-plus/es')['ElProgress'] - ElRadio: typeof import('element-plus/es')['ElRadio'] - ElRadioButton: typeof import('element-plus/es')['ElRadioButton'] - ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup'] - ElRow: typeof import('element-plus/es')['ElRow'] - ElSelect: typeof import('element-plus/es')['ElSelect'] - ElSkeleton: typeof import('element-plus/es')['ElSkeleton'] - ElSlider: typeof import('element-plus/es')['ElSlider'] - ElSubMenu: typeof import('element-plus/es')['ElSubMenu'] - ElSwitch: typeof import('element-plus/es')['ElSwitch'] - ElTable: typeof import('element-plus/es')['ElTable'] - ElTableColumn: typeof import('element-plus/es')['ElTableColumn'] - ElTabPane: typeof import('element-plus/es')['ElTabPane'] - ElTabs: typeof import('element-plus/es')['ElTabs'] - ElTag: typeof import('element-plus/es')['ElTag'] - ElTimeline: typeof import('element-plus/es')['ElTimeline'] - ElTimelineItem: typeof import('element-plus/es')['ElTimelineItem'] - ElTooltip: typeof import('element-plus/es')['ElTooltip'] - ElTree: typeof import('element-plus/es')['ElTree'] - ElUpload: typeof import('element-plus/es')['ElUpload'] GaugeChart: typeof import('./src/components/charts/GaugeChart.vue')['default'] KnowledgePanel: typeof import('./src/components/KnowledgePanel.vue')['default'] KPIListView: typeof import('./src/components/KPIListView.vue')['default'] @@ -74,7 +26,4 @@ declare module 'vue' { WaterfallChart: typeof import('./src/components/charts/WaterfallChart.vue')['default'] WelcomeGuide: typeof import('./src/components/WelcomeGuide.vue')['default'] } - export interface GlobalDirectives { - vLoading: typeof import('element-plus/es')['ElLoadingDirective'] - } } diff --git a/frontend/src/views/MapCanvas.vue b/frontend/src/views/MapCanvas.vue index a09846c0..c5d5a611 100644 --- a/frontend/src/views/MapCanvas.vue +++ b/frontend/src/views/MapCanvas.vue @@ -24,7 +24,9 @@ 🔗 因果 - 保存 + 📷 导出图片 + 🔗 因果链推荐 + 保存 发布 版本历史 @@ -167,6 +169,35 @@ + + +

+
+
+ 🔗 KPI因果链推荐 + +
+
+
加载中...
+
暂无因果链推荐
+
+
+
+ {{ rec.source_kpi_code || rec.source_code }} + + {{ rec.target_kpi_code || rec.target_code }} + 强度: {{ rec.strength || '-' }} +
+ +
+
+
+ +
+
@@ -935,6 +966,48 @@ async function exportImage() { ElMessage.error('导出失败') } } +// ── KPI因果链推荐 ── +const showCausalityDialog = ref(false) +const causalityRecommendations = ref([]) +const causalityLoading = ref(false) + +async function showCausalityRecommendations() { + if (!currentMap.value) { ElMessage.warning('请先选择或创建战略地图'); return } + causalityLoading.value = true + showCausalityDialog.value = true + try { + const r: any = await api.get('/kpi-causality') + const items = r.data || r.items || r || [] + causalityRecommendations.value = Array.isArray(items) ? items : [] + } catch (e) { + ElMessage.error('加载因果链失败') + causalityRecommendations.value = [] + } + causalityLoading.value = false +} + +async function applyCausality(rec: any) { + try { + await api.post(\\`/maps/\\${currentMap.value?.id}/connections\\`, { from: rec.source_kpi_code, to: rec.target_kpi_code }) + ElMessage.success('连线已添加') + showCausalityDialog.value = false + loadMap() + } catch { ElMessage.error('添加失败') } +} + +async function applyAllCausality() { + let added = 0 + for (const rec of causalityRecommendations.value) { + try { + await api.post(\\`/maps/\\${currentMap.value?.id}/connections\\`, { from: rec.source_kpi_code, to: rec.target_kpi_code }) + added++ + } catch {} + } + ElMessage.success(\\`已添加 \\${added}/\\${causalityRecommendations.value.length} 条因果链\\`) + showCausalityDialog.value = false + loadMap() +} + onMounted(async () => { const r: any = await mapApi.list()