pipeline-opportunity/wwwroot/api/opp_references_popup.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

119 lines
5.9 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_references_popup.dspy - 数据参考(references):主题列表(agent_menu_open 弹窗内嵌内容)
# 交互(2026-09-08 用户定夺):每个汇总主题条目整行点击 → 弹窗显示分项/明细项:
# 招标主题 → opp_references_items.dspy;国内众包需求主题 → opp_demand_items.dspy;
# 海外众包需求主题 → opp_overseas_items.dspy
# (原「查看分项」按钮已撤,整行可点,cursor:pointer 提示)
import json as _json
uid = await get_user()
if not uid:
return {"widgettype": "Text", "options": {"otext": "请先登录", "text": "请先登录", "i18n": True, "color": "#dc2626", "padding": "12px"}}
dbname = get_module_dbname('pipeline-opportunity')
items_url = entire_url("/pipeline-opportunity/api/opp_references_items.dspy")
demand_items_url = entire_url("/pipeline-opportunity/api/opp_demand_items.dspy")
overseas_items_url = entire_url("/pipeline-opportunity/api/opp_overseas_items.dspy")
async with DBPools().sqlorContext(dbname) as sor:
from pipeline_opportunity.opp_data_capability import hot_software, hot_demands, hot_overseas
ok, res = await hot_software(sor, days=30, top=10)
await sor.sqlExe("COMMIT", {})
ok_dm, res_dm = await hot_demands(sor, days=30, top=10)
await sor.sqlExe("COMMIT", {})
ok_ov, res_ov = await hot_overseas(sor, days=30, top=10)
await sor.sqlExe("COMMIT", {})
def _topic_row(cat, cnt, budget, target_url, popup_id, extra_txt=""):
"""汇总主题行:整行点击 → 弹出该主题的分项明细列表。"""
script = ("var old=bricks.getWidgetById(" + _json.dumps(popup_id) + ",bricks.app);"
"if(old&&old.destroy){old.destroy();}"
"var rp=await fetch(" + _json.dumps(target_url) + "+'?category='+encodeURIComponent(" + _json.dumps(cat) + "));"
"var d=await rp.json();"
"if(d){bricks.widgetBuild(d,bricks.app);}")
subs = [
{"widgettype": "Text", "options": {"text": cat, "cfontsize": 1.0,
"color": "#0f172a", "halign": "left"}},
{"widgettype": "Filler"},
{"widgettype": "Text", "options": {"otext": "${p0} 条", "text": "${p0} 条", "i18n": True, "i18n_params": {"p0": str('%d' % (cnt,))},
"cfontsize": 0.85, "color": "#64748b"}},
]
if extra_txt:
subs.append({"widgettype": "Text", "options": {"text": extra_txt,
"cfontsize": 0.75, "color": "#94a3b8"}})
else:
subs.append({"widgettype": "Text", "options": {
"otext": "预算 ${p0} 万", "text": "预算 ${p0} 万", "i18n": True, "i18n_params": {"p0": str("{:,.0f}".format(budget) if budget else "—")},
"cfontsize": 0.85, "color": "#64748b"}})
subs.append({"widgettype": "Text", "options": {"text": "›",
"cfontsize": 1.1, "color": "#94a3b8"}})
return {
"widgettype": "HBox",
"options": {"width": "100%", "gap": "10px", "alignItems": "center",
"padding": "8px 14px", "border": "1px solid #e2e8f0",
"borderRadius": "8px", "margin": "0 0 8px 0", "bgcolor": "#ffffff",
"cursor": "pointer"},
"binds": [{"wid": "self", "event": "click", "actiontype": "script",
"target": "self", "script": script}],
"subwidgets": subs
}
rows = []
if not ok:
rows.append({"widgettype": "Text", "options": {
"text": "数据爬取平台暂不可用:" + str(res)[:200],
"color": "#dc2626", "padding": "12px", "halign": "left"}})
else:
ranking = res.get("ranking") or []
if not ranking:
rows.append({"widgettype": "Text", "options": {
"otext": "近30天暂无招标数据", "text": "近30天暂无招标数据", "i18n": True, "color": "#64748b", "padding": "12px", "halign": "left"}})
for r in ranking:
rows.append(_topic_row(str(r.get("category", "")), r.get("tender_count", 0),
r.get("total_budget_wan", 0), items_url, "opp_refs_items_pw"))
# ── 国内众包需求板块(猪八戒/一品威客)──
dm_rows = []
if ok_dm:
dm_ranking = res_dm.get("ranking") or []
if dm_ranking:
dm_rows.append({"widgettype": "Text", "options": {
"otext": "国内众包软件需求(猪八戒/一品威客,近30天)", "text": "国内众包软件需求(猪八戒/一品威客,近30天)", "i18n": True,
"cfontsize": 0.95, "color": "#0f172a", "halign": "left",
"margin": "14px 0 6px 0", "padding": "6px 0 0 0",
"border": "1px solid #f1f5f9", "borderRadius": "6px", "bgcolor": "#f8fafc"}})
for r in dm_ranking:
dm_rows.append(_topic_row(str(r.get("category", "")), r.get("tender_count", 0),
r.get("total_budget_wan", 0), demand_items_url, "opp_demand_items_pw"))
# ── 海外众包需求板块(Freelancer.com / PeoplePerHour)──
ov_rows = []
if ok_ov:
ov_ranking = res_ov.get("ranking") or []
if ov_ranking:
ov_rows.append({"widgettype": "Text", "options": {
"otext": "海外众包软件需求(Freelancer.com / PeoplePerHour,近30天)", "text": "海外众包软件需求(Freelancer.com / PeoplePerHour,近30天)", "i18n": True,
"cfontsize": 0.95, "color": "#0f172a", "halign": "left",
"margin": "14px 0 6px 0", "padding": "6px 0 0 0",
"border": "1px solid #f1f5f9", "borderRadius": "6px", "bgcolor": "#f8fafc"}})
for r in ov_ranking:
by_src = r.get("by_source") or {}
src_txt = " / ".join("%s:%d" % (k, v) for k, v in by_src.items())
ov_rows.append(_topic_row(str(r.get("category", "")), r.get("tender_count", 0),
r.get("total_budget_wan", 0), overseas_items_url, "opp_overseas_items_pw",
extra_txt=src_txt))
return {
"widgettype": "VBox",
"options": {"css": "filler", "width": "100%", "height": "100%", "padding": "12px 16px",
"gap": "6px", "overflow": "auto"},
"subwidgets": [
{"widgettype": "Text", "options": {
"otext": "点击任一主题条目,弹出该主题的全部分项明细;明细中再点击任一条,弹出具体内容(数据来自数据爬取平台,含公告全文与附件下载)。", "text": "点击任一主题条目,弹出该主题的全部分项明细;明细中再点击任一条,弹出具体内容(数据来自数据爬取平台,含公告全文与附件下载)。", "i18n": True,
"cfontsize": 0.8, "color": "#64748b", "halign": "left", "margin": "0 0 8px 0"}}
] + rows + dm_rows + ov_rows
}