# 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": {"text": "请先登录", "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": {"text": "%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": { "text": "预算 %s 万" % ("{:,.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": { "text": "近30天暂无招标数据", "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": { "text": "国内众包软件需求(猪八戒/一品威客,近30天)", "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": { "text": "海外众包软件需求(Freelancer.com / PeoplePerHour,近30天)", "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": { "text": "点击任一主题条目,弹出该主题的全部分项明细;明细中再点击任一条,弹出具体内容(数据来自数据爬取平台,含公告全文与附件下载)。", "cfontsize": 0.8, "color": "#64748b", "halign": "left", "margin": "0 0 8px 0"}} ] + rows + dm_rows + ov_rows }