# opp_references_popup.dspy - 数据参考(references):主题列表(agent_menu_open 弹窗内嵌内容) # 点主题「查看分项」→ 对应分项列表(每条带来源,点击新窗口查证): # 招标主题 → opp_references_items.dspy;国内众包需求主题 → opp_demand_items.dspy; # 海外众包需求主题 → opp_overseas_items.dspy 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", {}) 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 i, r in enumerate(ranking): cat = str(r.get("category", "")) cnt = r.get("tender_count", 0) budget = r.get("total_budget_wan", 0) script = ("var old=bricks.getWidgetById('opp_refs_items_pw',bricks.app);" "if(old&&old.destroy){old.destroy();}" "var rp=await fetch(" + _json.dumps(items_url) + "+'?category='+encodeURIComponent(" + _json.dumps(cat) + "));" "var d=await rp.json();" "if(d){bricks.widgetBuild(d,bricks.app);}") rows.append({ "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"}, "subwidgets": [ {"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"}}, {"widgettype": "Text", "options": {"text": "预算 %s 万" % ("{:,.0f}".format(budget) if budget else "—"), "cfontsize": 0.85, "color": "#64748b"}}, {"widgettype": "Button", "options": {"label": "查看分项", "css": "small"}, "binds": [{"wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": script}]} ] }) # ── 国内众包需求板块(猪八戒/一品威客)── 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: cat = str(r.get("category", "")) cnt = r.get("tender_count", 0) budget = r.get("total_budget_wan", 0) script = ("var old=bricks.getWidgetById('opp_demand_items_pw',bricks.app);" "if(old&&old.destroy){old.destroy();}" "var rp=await fetch(" + _json.dumps(demand_items_url) + "+'?category='+encodeURIComponent(" + _json.dumps(cat) + "));" "var d=await rp.json();" "if(d){bricks.widgetBuild(d,bricks.app);}") dm_rows.append({ "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"}, "subwidgets": [ {"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"}}, {"widgettype": "Text", "options": {"text": "预算 %s 万" % ("{:,.0f}".format(budget) if budget else "—"), "cfontsize": 0.85, "color": "#64748b"}}, {"widgettype": "Button", "options": {"label": "查看分项", "css": "small"}, "binds": [{"wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": script}]} ] }) # ── 海外众包需求板块(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: cat = str(r.get("category", "")) cnt = r.get("tender_count", 0) by_src = r.get("by_source") or {} src_txt = " / ".join("%s:%d" % (k, v) for k, v in by_src.items()) script = ("var old=bricks.getWidgetById('opp_overseas_items_pw',bricks.app);" "if(old&&old.destroy){old.destroy();}" "var rp=await fetch(" + _json.dumps(overseas_items_url) + "+'?category='+encodeURIComponent(" + _json.dumps(cat) + "));" "var d=await rp.json();" "if(d){bricks.widgetBuild(d,bricks.app);}") ov_rows.append({ "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"}, "subwidgets": [ {"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"}}, {"widgettype": "Text", "options": {"text": src_txt, "cfontsize": 0.75, "color": "#94a3b8"}}, {"widgettype": "Button", "options": {"label": "查看分项", "css": "small"}, "binds": [{"wid": "self", "event": "click", "actiontype": "script", "target": "self", "script": script}]} ] }) 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 }