pipeline-opportunity/wwwroot/api/opp_reports_list.dspy
yumoqing 4092304726 feat(feasibility): P3可行性研究——opp_feasibility引擎(fp_estimate规则引擎FP/draft_feasibility八节报告/score_candidate评分/promote_to_project立项门禁)+report_type列(models/DDL/CRUD重生成)+详情页与PPT按类型选四节或八节模板(sections_for_type单一规则源)+列表类型徽标+prompt链路C+params/demand_mining字典种子+i18n词条
- fp_calc.calc()返回键实锤核对: total_fp/counts/errors(原total_ufp/by_type兜底猜错,已修)
- promote门禁修正: resolve_approval会把status置approved,confirmed只是中间态→门禁接受confirmed/approval_initiated/approved(原只认confirmed会导致审批通过后反而不能立项)
- _cluster_ctx补项目隔离(对齐_check_batch_visible口径)+全personal类别如实拒绝可行性样本不足
- 八节模板兜底节=需求规格;四节存量解析口径零变化
- opp_approvals CRUD手改(项目过滤)被xls2ui重生成吃掉→已checkout还原,只保留opp_reports新列部分
2026-09-14 09:03:58 +08:00

142 lines
6.4 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# opp_reports_list.dspy - 研发报告列表「研发报告」菜单入口2026-09-09 用户要求改造)
# 替换原 CRUD 裸表页:按项目 owner 过滤list_visible_reports 单一事实源),
# 整行点击 → opp_report_detail.dspy 弹出详情(四节+成果文件下载)。
# 交互范式对齐 opp_references_items.dspy整行下钻fetch+widgetBuild
# 2026-09-11 会话当前项目过滤:只显示当前项目的报告 + 平台级调研(未挂项目,
# 不属于任何项目、不构成跨项目泄漏);没当前项目 → 提示选择或新建项目。
import json as _json
uid = await get_user()
if not uid:
return {"widgettype": "Message", "options": {"title": "未登录", "message": "请先登录"}}
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": {"otext": "当前会话还没有项目,请先选择或新建项目", "text": "当前会话还没有项目,请先选择或新建项目", "i18n": True,
"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 = {
"draft": ("草稿", "#94a3b8"),
"confirmed": ("已确认", "#2563eb"),
"approval_initiated": ("审批中", "#d97706"),
"approved": ("审批通过", "#16a34a"),
"rejected": ("已驳回", "#dc2626"),
}
TYPE_TXT = {
"research": ("调研", "#0369a1"),
"feasibility": ("可行性研究", "#7c3aed"),
}
rows = []
if not reports:
rows.append({"widgettype": "Text", "options": {
"otext": "暂无可见的研发报告(列表按项目 owner 过滤:仅显示你 own 的项目及平台级调研报告)", "text": "暂无可见的研发报告(列表按项目 owner 过滤:仅显示你 own 的项目及平台级调研报告)", "i18n": True,
"cfontsize": 0.85, "color": "#64748b", "padding": "12px", "halign": "left"}})
for r in reports:
rid = str(r.get("id") or "")
st = str(r.get("status") or "")
st_txt, st_color = STATUS_TXT.get(st, (st or "—", "#64748b"))
rt = str(r.get("report_type") or "research")
rt_txt, rt_color = TYPE_TXT.get(rt, ("调研", "#0369a1"))
proj = str(r.get("project_name") or "")
meta = "%s %s %s" % (
str(r.get("created_at") or "")[:16],
("项目:" + proj) if proj else "平台级调研(未挂项目)",
st_txt)
row_subs = [
{"widgettype": "HBox", "options": {"width": "100%", "gap": "8px",
"alignItems": "center"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": rt_txt,
"cfontsize": 0.7, "color": "#ffffff", "bgcolor": rt_color,
"padding": "1px 8px", "borderRadius": "10px"}},
{"widgettype": "Text", "options": {
"text": str(r.get("title") or r.get("software") or "(无标题)"),
"cfontsize": 0.95, "color": "#0f172a", "halign": "left", "wrap": True}}]},
{"widgettype": "HBox", "options": {"width": "100%", "gap": "10px",
"alignItems": "center"},
"subwidgets": [
{"widgettype": "Text", "options": {"text": meta,
"cfontsize": 0.75, "color": st_color, "halign": "left", "wrap": True}},
{"widgettype": "Filler"},
{"widgettype": "Text", "options": {"text": "",
"cfontsize": 1.0, "color": "#94a3b8"}}]
}]
row_opts = {"width": "100%", "padding": "8px 12px", "gap": "4px",
"border": "1px solid #e2e8f0", "borderRadius": "8px",
"margin": "0 0 6px 0", "bgcolor": "#ffffff"}
if rid:
row_opts["cursor"] = "pointer"
script = ("var old=bricks.getWidgetById('opp_report_detail_pw',bricks.app);"
"if(old&&old.destroy){old.destroy();}"
"var rp=await fetch(" + _json.dumps(detail_url) + "+'?report_id='+encodeURIComponent(" + _json.dumps(rid) + "));"
"var d=await rp.json();"
"if(d){bricks.widgetBuild(d,bricks.app);}")
rows.append({"widgettype": "VBox", "options": row_opts,
"binds": [{"wid": "self", "event": "click", "actiontype": "script",
"target": "self", "script": script}],
"subwidgets": row_subs})
else:
rows.append({"widgettype": "VBox", "options": row_opts, "subwidgets": row_subs})
return {
"widgettype": "VBox",
"options": {"css": "filler", "width": "100%", "height": "100%",
"padding": "12px 16px", "gap": "0px", "overflow": "auto"},
"subwidgets": [
{"widgettype": "Text", "options": {
"otext": "研发报告(${p0} 份)|按项目 owner 过滤;点击任一条查看研发场景/输入/输出/成果与成果文件下载", "text": "研发报告(${p0} 份)|按项目 owner 过滤;点击任一条查看研发场景/输入/输出/成果与成果文件下载", "i18n": True, "i18n_params": {"p0": str('%d' % (len(reports),))},
"cfontsize": 0.78, "color": "#64748b", "halign": "left", "margin": "0 0 8px 0"}}
] + rows
}