From 1fde77f728c88987d01f5d2bebf0c8283dc46e55 Mon Sep 17 00:00:00 2001 From: zlt Date: Fri, 14 Aug 2026 14:00:05 +0800 Subject: [PATCH] =?UTF-8?q?PMS=E4=BF=A1=E5=8F=B7=E5=8C=BA:=20=E9=BB=98?= =?UTF-8?q?=E8=AE=A4=E6=8A=98=E5=8F=A0+=E7=BA=A7=E5=88=AB=E7=AD=9B?= =?UTF-8?q?=E9=80=89=E8=B7=A8=E5=88=B7=E6=96=B0=E4=BF=9D=E7=95=99;=20mtf?= =?UTF-8?q?=E8=B5=84=E9=87=91=E5=BC=82=E5=8A=A8=E6=8B=86data=E5=8F=96?= =?UTF-8?q?=E7=9C=9F=E5=AE=9E=E5=AD=97=E6=AE=B5(=E6=96=B9=E5=90=91/?= =?UTF-8?q?=E5=BC=82=E5=8A=A8z/=E5=87=80=E9=A2=9D),=E5=8E=BB=E7=A9=BA?= =?UTF-8?q?=E7=BA=A7=E5=88=AB=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/services/upstream_signals.py | 21 +++++++++++++++++---- app/web/static/index.html | 22 ++++++++++++++-------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/app/services/upstream_signals.py b/app/services/upstream_signals.py index ab19f82..4a68b81 100644 --- a/app/services/upstream_signals.py +++ b/app/services/upstream_signals.py @@ -182,14 +182,27 @@ def _read_alerts(c, ymd): def _read_metrics(c): - """mtf 资金异动: payload schema 未在契约里写全, 挑常见字段, 其余整体带过去供页面兜底显示。""" + """mtf 资金异动: 真实载荷打包在 stream 的 data(JSON字符串)里, 顶层只有 ts_code —— + 之前直接取顶层 z_dd/direction 全落空(页面显示为空)。这里拆 data 取真实字段, 顶层作兜底。 + mtf 侧字段: direction(inflow/outflow), z_dd(横截面异动z), window_net(净额), window_ret(区间涨跌); 无原生 level。""" key = "mtf:intraday:stream:metrics" out = [] for _id, f in c.xrevrange(key, count=_limit()): code = f.get("ts_code") or f.get("code") - out.append({"ts_code": code, "z_dd": _num(f.get("z_dd")), "direction": f.get("direction"), - "level": f.get("level"), "value": _num(f.get("value") if f.get("value") is not None else f.get("net")), - "raw": {k: v for k, v in f.items() if k not in ("ts_code", "code")}}) + payload = f.get("data") + if isinstance(payload, str): + try: + payload = json.loads(payload) + except Exception: + payload = {} + if not isinstance(payload, dict): + payload = {} + g = lambda k, d=None: payload.get(k, f.get(k, d)) + out.append({"ts_code": code, "direction": g("direction"), + "z_dd": _num(g("z_dd")), "window_net": _num(g("window_net")), + "window_ret": _num(g("window_ret")), "level": g("level"), + "value": _num(g("value") if g("value") is not None else g("net")), + "raw": payload}) return {"key": key, "items": out} diff --git a/app/web/static/index.html b/app/web/static/index.html index e33e9ad..95d4d67 100644 --- a/app/web/static/index.html +++ b/app/web/static/index.html @@ -263,11 +263,11 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
mtf 资金异动 · 读取失败
- + - - - + + +
@@ -1404,10 +1404,14 @@ createApp({ const lastRefresh = ref(''); const autoRefresh = ref(true); const upstream = ref({ sources: {} }); - const showUpstream = ref(true); + const showUpstream = ref(false); // 默认折叠, 点标题展开 const showAlerts = ref(false); const alertShowMap = reactive({ money_flow:true, capital:true, qrs:true, volume:true, multi:true, price_notice:false, buy_emitted:false, other:true }); - const levelShow = reactive({ DANGER:true, WARNING:true, IMPORTANT:true, INFO:false, NORMAL:false }); + // 级别筛选记忆: 存 sessionStorage —— 普通刷新保留, 关标签页/新开标签=回默认。默认关 普通/提示。 + const LV_STORE_KEY = 'pms_level_show'; + const _lvDefault = { DANGER:true, WARNING:true, IMPORTANT:true, INFO:false, NORMAL:false }; + const _lvLoad = () => { try { const v = JSON.parse(sessionStorage.getItem(LV_STORE_KEY)); if (v && typeof v === 'object') return { ..._lvDefault, ...v }; } catch (e) {} return { ..._lvDefault }; }; + const levelShow = reactive(_lvLoad()); const openScan = ref({ by_code: {}, notes: [] }); const insTab = ref('live'); const stratDlg = reactive({ visible:false, busy:false, reasons:[], ts_code:'', price:0, @@ -1853,7 +1857,7 @@ createApp({ // 级别筛选 (纯前端): 已知级别按开关过滤; 未知级别一律放行 (风控从宽, 不误杀)。 const KNOWN_LV = new Set(['DANGER','WARNING','IMPORTANT','INFO','NORMAL']); const levelShown = k => levelShow[k] !== false; - function toggleLevel(k){ levelShow[k] = !levelShown(k); } + function toggleLevel(k){ levelShow[k] = !levelShown(k); try { sessionStorage.setItem(LV_STORE_KEY, JSON.stringify(levelShow)); } catch (e) {} } const passLevel = lv => { const x = String(lv == null ? '' : lv); return !KNOWN_LV.has(x) || levelShow[x] !== false; }; const alertItemsF = computed(() => alertItems.value.filter(a => passLevel(a.level))); const levelCount = k => alertItems.value.reduce((n, a) => n + (String(a.level) === k ? 1 : 0), 0); @@ -1864,6 +1868,8 @@ createApp({ const LV_LABEL = { DANGER:'危险', WARNING:'警告', IMPORTANT:'重要', INFO:'提示', NORMAL:'普通' }; const lvLabel = v => LV_LABEL[v] || v || '—'; const lvClass = v => v === 'DANGER' ? 'deny' : ((v === 'WARNING' || v === 'IMPORTANT') ? 'queue' : 'wait'); + const flowText = d => d === 'inflow' ? '流入' : (d === 'outflow' ? '流出' : (d || '—')); + const flowClass = d => d === 'inflow' ? 'clr-up' : (d === 'outflow' ? 'clr-down' : 'muted'); // 告警固定分类格子: 6 类常驻 + 其他(catch-all), 永远这几个框、空的显示暂无 —— 不再随数据冒出冒消。 const ALERT_CATS_FIXED = [ { key:'money_flow', label:'资金流强度' }, { key:'capital', label:'资金分布' }, { key:'qrs', label:'日QRS对称' }, @@ -2188,7 +2194,7 @@ createApp({ upstream, showUpstream, usrc, loadUpstream, showAlerts, alertItems, alertLatest, alertLevelClass, alertGroups, alertStripItems, alertStripCount, alertTotalCount, catShown, toggleCat, ageText, alertRowClass, - alertItemsF, levelShown, toggleLevel, levelCount, ALERT_LEVELS, lvLabel, lvClass, alertGridFixed, passLevel }; + alertItemsF, levelShown, toggleLevel, levelCount, ALERT_LEVELS, lvLabel, lvClass, alertGridFixed, passLevel, flowText, flowClass }; } }).use(ElementPlus).mount('#app');