152 lines
7.0 KiB
Plaintext
152 lines
7.0 KiB
Plaintext
# 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
|
||
}
|