diff --git a/app/services/proposal_service.py b/app/services/proposal_service.py index d9976f1..a672f1e 100644 --- a/app/services/proposal_service.py +++ b/app/services/proposal_service.py @@ -740,6 +740,7 @@ def disposition_snapshot(now=None) -> dict: res["by_code"][code]["consensus"] = { "fund": rec.get("fund"), "overall": rec.get("overall"), "tech": rec.get("tech"), "phase": rec.get("phase"), + "timing": rec.get("timing"), # 择时那一票 (2026-09-15: 候选栏三枚维度标签用) "direction": rec.get("direction"), "route": rec.get("route")} return res diff --git a/app/web/static/index.html b/app/web/static/index.html index 6aa5de8..a4acf98 100644 --- a/app/web/static/index.html +++ b/app/web/static/index.html @@ -442,6 +442,13 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;} .ptag.accent{background:var(--accent-weak);color:var(--accent);} .ptag.warn{background:var(--warn-weak);color:var(--warn);} .ptag.neutral{background:var(--surface-2);color:var(--muted);} +/* 维度标签 (2026-09-15): 文字是维度名 (基本面/技术面/择时), 颜色是立场 —— 看多红、看空绿、中性灰、无读数虚框。 + 候选栏与提议卡共用一套, 与抽屉里的立场词同色, 不再各写各的。 */ +.dim{display:inline-flex;align-items:center;font-size:11px;font-weight:560;padding:1px 7px;border-radius:6px;flex:none;line-height:1.5;cursor:default;} +.dim.up{background:var(--up-weak);color:var(--up);} +.dim.down{background:var(--down-weak);color:var(--down);} +.dim.neutral{background:rgba(138,136,130,.14);color:var(--ink-2);} +.dim.na{background:transparent;color:var(--muted);border:1px dashed var(--hair);padding:0 6px;} .prop-sheet{position:fixed;left:0;right:0;bottom:0;z-index:3000;max-height:86vh;overflow:auto; background:var(--surface);border-radius:16px 16px 0 0;box-shadow:0 -12px 40px rgba(17,17,16,.22);} .prop-metric{display:flex;flex-direction:column;gap:7px;margin:8px 0;} @@ -1158,20 +1165,22 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;} {{ propJudgeTag(p).text }} 把握 {{ (p.hard_numbers||{}).judge_conf }} -
- 公司深度 - {{ propCompanyTag(p).text }} - 估值{{ cr(p).valuation }} + +
+ 基本面 + {{ propFundTag(p).text }} + {{ propFundSub(p) }}
技术面 {{ propTechTag(p).text }} {{ propTechSub(p) }}
-
- 合议 - {{ propConsensusTag(p).text }} - {{ propConsensusSub(p) }} +
+ 择时 + {{ propTimingTag(p).text }} + {{ propTimingSub(p) }}
@@ -1283,10 +1292,11 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;} {{ r.name || nm(r.ts_code) }} {{ r.theme }} - + {{ dispOf(r.ts_code).label }}
@@ -2075,19 +2085,21 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
@@ -3138,6 +3150,44 @@ createApp({ }; // 合议 (2026-09-11 工作包二): 三方多数方向与强弱, 读下单当时记进硬数字的那份 (解释这条提议 // 为什么按合议路由走)。方向按页面口径红涨绿跌; 无读数用中性灰。点开看三票与路由理由。 + // 立场 → 颜色类 (2026-09-15, 三处共用): 看多红, 看空绿, 中性灰, 无读数虚框。 + const dimCls = (st) => st === '看多' ? 'up' : (st === '看空' ? 'down' : (st === '中性' ? 'neutral' : 'na')); + // 基本面行: 立场取硬数字里的 fund_stance (合议六键), 没有就按质地档推; 副文案给质地与估值。 + const propFundTag = (p) => { + const fs = ((p.hard_numbers || {}).fund_stance || {}).stance, c = cr(p); + const st = fs || (c && c.overall ? (c.overall === '好' ? '看多' : (c.overall === '差' ? '看空' : '中性')) : null); + if (!st && !c) return null; + return { text: st || '无读数', cls: dimCls(st) }; + }; + const propFundSub = (p) => { + const c = cr(p) || {}, fs = (p.hard_numbers || {}).fund_stance || {}, bits = []; + if (c.overall) bits.push('质地' + c.overall); + if (c.valuation) bits.push('估值' + c.valuation); + if (fs.mark) bits.push(fs.mark); + return bits.join(' · '); + }; + // 择时行: 硬数字里的 timing (合议六键), 昨夜定性翻成中文。 + const NIGHT_CN = { BUY: '建议买入', WATCH: '观望', SELL: '建议卖出', AVOID: '回避', DROPPED: '已剔除' }; + const propTimingTag = (p) => { + const t = (p.hard_numbers || {}).timing; + if (!t) return null; + return { text: t.stance || '无读数', cls: dimCls(t.stance) }; + }; + const propTimingSub = (p) => { + const t = (p.hard_numbers || {}).timing || {}, bits = []; + if (t.nightly) bits.push('昨夜定性 ' + (NIGHT_CN[t.nightly] || t.nightly)); + if (t.flip_at) bits.push('盘中 ' + t.flip_at + ' 判转多'); + return bits.join(' · '); + }; + // 系统结论 (择时详情用): 把路由翻成用户看得懂的一句话, 与抽屉判断卡同一套措辞。 + const propConclusion = (p) => { + const c = propCon(p) || {}; + if (c.route === '放行') return c.strength === '强' ? '三路意见一致看多,研判通过即建仓' : '多数意见看多,研判通过即建仓'; + if (c.route === '观察') return '观察中:' + csPlainRoute(c.route_reason); + if (c.route === '交人') return '试探仓候选:' + csPlainRoute(c.route_reason) + ';研判通过后再定'; + if (c.route === '跳过') return '不建仓:' + csPlainRoute(c.route_reason); + return c.route_reason || ''; + }; const propCon = (p) => (p && p.hard_numbers && p.hard_numbers.consensus) || null; const propConsensusTag = (p) => { const c = propCon(p); @@ -3156,8 +3206,8 @@ createApp({ // 弹窗: 点某来源看它的完整依据。teleport 到 body, 层级同单票信号抽屉 (3000/2995) 盖住右栏。 const propDetailOpen = ref(false), propDetailKind = ref(''), propDetailP = ref(null); const propTechFull = ref(null), propTechErr = ref(''); - const PROP_SRC_LABEL = { plan: '选股系统', judge: '决策系统', company: '公司深度', - tech: '技术面', consensus: '三源合议' }; + const PROP_SRC_LABEL = { plan: '选股系统', judge: '决策系统', company: '基本面', + tech: '技术面', consensus: '择时' }; function openPropDetail(p, kind) { propDetailP.value = p; propDetailKind.value = kind; propDetailOpen.value = true; propTechFull.value = null; propTechErr.value = ''; @@ -4482,6 +4532,7 @@ createApp({ // 2026-09-12 提议卡重构: 来源结论标签 + 详情弹窗 cr, propPlanTag, propPlanSub, propJudgeTag, propCompanyTag, propTechTag, propTechSub, propCon, propConsensusTag, propConsensusSub, + propFundTag, propFundSub, propTimingTag, propTimingSub, propConclusion, dimCls, propDetailOpen, propDetailKind, propDetailP, propTechFull, propTechErr, PROP_SRC_LABEL, openPropDetail, closePropDetail, pHaltBuy, pResumeBuy, pHaltAll, pResumeAll, pReduce, pIncrease, pLiquidate, pSectorExit,