diff --git a/pipeline_opportunity/opp_ability.py b/pipeline_opportunity/opp_ability.py index 9cedfeb..233ae35 100644 --- a/pipeline_opportunity/opp_ability.py +++ b/pipeline_opportunity/opp_ability.py @@ -464,11 +464,11 @@ def register_opp_ability(): roles=OPP_ROLES, menus=[ {"label": "📊 研发报告", "icon": "", "url": "/pipeline-opportunity/api/opp_reports_list.dspy", - "type": "popup", "width": "88%", "height": "82%"}, + "type": "popup", "width": "88%", "height": "82%", "require_project": True}, {"label": "✅ 研发审批", "icon": "", "url": "/pipeline-opportunity/opp_approvals/index.ui", - "type": "popup", "width": "88%", "height": "82%"}, + "type": "popup", "width": "88%", "height": "82%", "require_project": True}, {"label": "📋 数据参考", "icon": "", "url": "/pipeline-opportunity/api/opp_references_popup.dspy", - "type": "popup", "width": "70%", "height": "76%"}, + "type": "popup", "width": "70%", "height": "76%", "require_project": True}, ], ) register_ability(ability) diff --git a/wwwroot/api/opp_reports_list.dspy b/wwwroot/api/opp_reports_list.dspy index c3d21c4..813666b 100644 --- a/wwwroot/api/opp_reports_list.dspy +++ b/wwwroot/api/opp_reports_list.dspy @@ -2,6 +2,8 @@ # 替换原 CRUD 裸表页:按项目 owner 过滤(list_visible_reports 单一事实源), # 整行点击 → opp_report_detail.dspy 弹出详情(四节+成果文件下载)。 # 交互范式对齐 opp_references_items.dspy(整行下钻,fetch+widgetBuild)。 +# 2026-09-11 会话当前项目过滤:只显示当前项目的报告 + 平台级调研(未挂项目, +# 不属于任何项目、不构成跨项目泄漏);没当前项目 → 提示选择或新建项目。 import json as _json @@ -11,11 +13,55 @@ if not uid: dbname = get_module_dbname('pipeline-opportunity') +session_id = (params_kw or {}).get('session_id', '') or '' +pipeline_id = (params_kw or {}).get('pipeline_id', '') or 'opportunity_general' + +# 会话当前项目(统一解析:session→全局兜底+产线隔离+悬空自愈) +_cur_pid = '' +try: + from pipeline_service.workspace import get_session_project_id + async with DBPools().sqlorContext(dbname) as sor: + _cur_pid = await get_session_project_id(sor, uid, session_id, pipeline_id) or '' +except Exception: + _cur_pid = '' + +if not _cur_pid: + _proj_base = entire_url("/pipeline_core/api/agent_project_popup.dspy") + _proj_q = [] + if session_id: + _proj_q.append("session_id=" + session_id) + if pipeline_id: + _proj_q.append("pipeline_id=" + pipeline_id) + + def _pbtn(label, css, op): + _js = ("var r=await fetch(" + _json.dumps(_proj_base + "?op=" + op + + (("&" + "&".join(_proj_q)) if _proj_q else "")) + ");" + "var d=await r.json();if(d){bricks.widgetBuild(d,bricks.app);}") + return {"widgettype": "Button", "options": {"label": label, "css": css}, + "binds": [{"wid": "self", "event": "click", "actiontype": "script", + "target": "self", "script": _js}]} + + return {"widgettype": "VBox", + "options": {"width": "100%", "gap": "12px", "padding": "24px"}, + "subwidgets": [ + {"widgettype": "Text", + "options": {"text": "当前会话还没有项目,请先选择或新建项目", + "cfontsize": 1, "color": "#64748b"}}, + {"widgettype": "HBox", + "options": {"width": "100%", "gap": "10px"}, + "subwidgets": [_pbtn("✨ 新建项目", "primary", "new"), + _pbtn("🔀 切换项目", "small", "switch")]} + ]} + async with DBPools().sqlorContext(dbname) as sor: from pipeline_opportunity.opp_report_capability import list_visible_reports reports = await list_visible_reports(sor, uid) await sor.sqlExe("COMMIT", {}) +# 当前项目过滤:本项目报告 + 平台级调研(project_id 空,未挂任何项目) +reports = [r for r in reports + if (not str(r.get("project_id") or "")) or str(r.get("project_id")) == _cur_pid] + detail_url = entire_url("/pipeline-opportunity/api/opp_report_detail.dspy") STATUS_TXT = { diff --git a/wwwroot/opp_approvals/get_opp_approvals.dspy b/wwwroot/opp_approvals/get_opp_approvals.dspy index 22ad591..b57189e 100644 --- a/wwwroot/opp_approvals/get_opp_approvals.dspy +++ b/wwwroot/opp_approvals/get_opp_approvals.dspy @@ -1,6 +1,26 @@ ns = params_kw.copy() +# 会话当前项目强制过滤(2026-09-11 用户要求):会话级解析(session→全局兜底+产线隔离+悬空自愈), +# 无项目报错提示;强制覆盖前端 project_id 传值(含搜索框),列表只显示当前项目数据。 +_uid = await get_user() +if not _uid: + _uid = 'user-01' +_sid = (params_kw or {}).get('session_id', '') or '' +_pl = (params_kw or {}).get('pipeline_id', '') or 'opportunity_general' +_cur_pid = '' +try: + from pipeline_service.workspace import get_session_project_id + async with DBPools().sqlorContext(get_module_dbname('pipeline-opportunity')) as _sor: + _cur_pid = await get_session_project_id(_sor, _uid, _sid, _pl) or '' +except Exception: + _cur_pid = '' +if not _cur_pid: + return {"widgettype":"Error","options":{"title":"错误","timeout":3,"cwidth":22,"cheight":9,"message":"当前会话没有项目,请先选择或新建项目"}} +ns['_cur_pid'] = _cur_pid +ns['project_id'] = _cur_pid + + debug(f'get_opp_approvals.dspy:{ns=}') if not ns.get('page'): @@ -12,7 +32,7 @@ if not ns.get('sort'): -sql = '''select * from opp_approvals where 1=1 [[filterstr]]''' +sql = '''select * from opp_approvals where project_id = ${_cur_pid}$ [[filterstr]]''' filterjson = params_kw.get('data_filter') if filterjson and isinstance(filterjson, str):