diff --git a/app/web/static/index.html b/app/web/static/index.html
index ff1874a..dced3d6 100644
--- a/app/web/static/index.html
+++ b/app/web/static/index.html
@@ -1075,25 +1075,48 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
{{ p.ts_code }}
{{ tx('action', p.action) }}
{{ p.expire_at }} 前有效
-
决策系统:{{ p.judge_reason }}(置信度 {{ (p.hard_numbers||{}).judge_conf }})
+
+
+
+ {{ p.qty }} 股 · 参考价 {{ (p.hard_numbers||{}).price==null ? '—' : (p.hard_numbers||{}).price }}
+ · 约需 {{ money(((p.hard_numbers||{}).price||0)*(p.qty||0)) }}
+ (占总规模 {{ propSizePct(p) }})
+
+
+ 建议档位:{{ propAdviceTier(p) }}
+ {{ propSizeMismatch(p) }}
+
选股系统:{{ propVerdictLine(p) }}
- {{ propBasis(p) }}
- 研报说:{{ propLogic(p) }}(另有 {{ propLogicMore(p) }} 条)
-
- {{ propText(p, 'valuation_text') }}
- {{ propText(p, 'events_text') }}
- {{ propText(p, 'pricing_text') }}
- {{ propText(p, 'news_text') }}
- {{ propText(p, 'industry_catalyst_text') }}
-
- 公司深度:{{ propText(p, 'company_review_text') }}
- 判决收敛:{{ propText(p, 'company_gate') }}
- {{ propText(p, 'advice_text') }}
- 择时层两个期限:{{ propHeads(p) }}
- {{ propWhy(p) }}
- 约 {{ p.qty }} 股 · 参考价 {{ (p.hard_numbers||{}).price==null ? '—' : (p.hard_numbers||{}).price }}
- · 约需 {{ money(((p.hard_numbers||{}).price||0)*(p.qty||0)) }}
+ 决策系统:{{ propJudgeShort(p) }}(把握度 {{ (p.hard_numbers||{}).judge_conf }})
+
+
+ 公司深度:{{ propText(p, 'company_review_text') }}
+
+
+ {{ x }}
+
+
+ {{ propMoreOpen.has(p.proposal_id) ? '▾ 收起证据' : '▸ 展开证据(研报、安全边际、催化、定价、快讯、行业、择时层)' }}
+
+
+ 研报说:{{ propLogic(p) }}(另有 {{ propLogicMore(p) }} 条)
+ {{ propText(p, 'valuation_text') }}
+ {{ propText(p, 'events_text') }}
+ {{ propText(p, 'pricing_text') }}
+ {{ propText(p, 'news_text') }}
+ {{ propText(p, 'industry_catalyst_text') }}
+ 判决收敛:{{ propText(p, 'company_gate') }}
+ {{ propText(p, 'advice_text') }}
+ 择时层两个期限:{{ propHeads(p) }}
+ {{ propWhy(p) }}
+ 决策系统原话:{{ p.judge_reason }}
+
采纳
驳回
@@ -2733,15 +2756,69 @@ createApp({
if (h.heat != null) bits.push('热度 ' + (typeof h.heat === 'number' ? h.heat.toFixed(2) : h.heat));
return bits.join(' · ') || '—';
}
- // 选股系统候选卡的理由 (硬数字 reasons, 后端已截到四条), 提议卡上只显示前两条
+ // 选股系统候选卡的理由 (硬数字 reasons, 后端已截到四条)。
+ // 2026-09-10: 这个函数写好之后模板里一直没引用过, 卡上从来没显示过「为什么选它」——
+ // 而这恰恰是人做决定最先要看的。现在放到卡面上, 并且不再只取前两条 (实测每票 2 到 3 条)。
function propReasons(p) {
const r = (p.hard_numbers || {}).reasons;
- return Array.isArray(r) ? r.slice(0, 2) : [];
+ return Array.isArray(r) ? r.filter(x => typeof x === 'string' && x.trim()) : [];
+ }
+ // 2026-09-10 删掉了 propBasis。它 return 的是 hard_numbers.basis, 而 propVerdictLine
+ // 在判决为「候选」或「仅展示」时 return 的也是同一个 basis —— 两个函数逐字相同,
+ // 模板里又挨着摆了两行, 于是卡上出现两行一模一样的话 (今天 77 只票里 38 只判候选,
+ // 每一只出提议都重复一次)。留 propVerdictLine 那一个, 它是 09-04 专门写的收口函数,
+ // 历史提议表格那一处 (「选股判决」列) 本来就是单独用它、没配 basis 行。
+ // 这一笔占总规模多少。hard_numbers 里有 target_amount 与 target_pct 两个数,
+ // 相除就是总规模, 不必再从参数表取一遍 (取一遍还有取不到的可能)。
+ function propScale(p) {
+ const h = p.hard_numbers || {};
+ const amt = Number(h.target_amount), pct = Number(h.target_pct);
+ return (isFinite(amt) && isFinite(pct) && pct > 0) ? amt / pct : null;
+ }
+ function propSizePct(p) {
+ const h = p.hard_numbers || {};
+ const scale = propScale(p), price = Number(h.price), qty = Number(p.qty);
+ if (!scale || !isFinite(price) || !isFinite(qty) || qty <= 0) return '';
+ return (price * qty / scale * 100).toFixed(2) + '%';
+ }
+ // 建议档位的短版。原先卡上摆的是 advice_text 那一整句 (实测 90 多个字, 把档位、
+ // 机械方案、条件、依据、研判结论全串在一行), 读的人要在里面找「到底建议买多少」。
+ // 整句仍在展开里, 这里只留结论。
+ function propAdviceTier(p) {
+ const a = (p.hard_numbers || {}).advice;
+ if (!a || typeof a !== 'object') return '';
+ const tier = String(a.tier || '').trim();
+ const pct = Number(a.target_pct);
+ if (!tier) return '';
+ return tier + (isFinite(pct) ? '(目标 ' + (pct * 100).toFixed(1) + '%)' : '');
+ }
+ // 【要紧】建议档位与真正会下的股数对不对得上。
+ //
+ // 2026-09-10 查出来的问题: 研判把握度低或不可用时, advice_service.apply_judge 会把
+ // 档位降成「试探仓」并把 advice.target_pct 改成 1%, 但它明确不改数量 (函数说明原文
+ // 「不改数量, 数量交人」), 而页面上采纳按钮没有填数量的地方, 这个「交人」无处兑现——
+ // 采纳时后端直接抄提议里的 qty 原样落单。于是卡上写着「试探仓, 目标 1.0%」,
+ // 点下去买的却是标准仓 6% 的第一批。实测恩捷股份那张卡: 卡面写 1.0%,
+ // 实下 1200 股 48.55 元共 58,260 元 = 总规模的 2.91%, 差 2.9 倍。
+ //
+ // 根子上的修法是让数量跟着档位重算, 那会改变每一笔采纳真正发出去的钱, 要单独拍板。
+ // 在那之前, 这一行先把矛盾摆到人眼前 —— 让人看错数字下单, 比让页面难看严重得多。
+ function propSizeMismatch(p) {
+ const h = p.hard_numbers || {};
+ const a = h.advice;
+ if (!a || typeof a !== 'object') return '';
+ const advPct = Number(a.target_pct), usedPct = Number(h.target_pct);
+ if (!isFinite(advPct) || !isFinite(usedPct) || usedPct <= 0) return '';
+ if (Math.abs(advPct - usedPct) < 1e-9) return '';
+ return '注意:上面那个股数是按 ' + (usedPct * 100).toFixed(1) + '% 那一档算的,'
+ + '不是按建议档位的 ' + (advPct * 100).toFixed(1) + '%。采纳后下的就是上面那个数。';
+ }
+ // 决策系统那句话最长到 500 字, 而它常常是一句技术报错 (例如研判超时那句)。
+ // 卡面只留前 60 字, 原话收进展开。
+ function propJudgeShort(p) {
+ const r = String(p.judge_reason || '').trim();
+ return r.length > 60 ? r.slice(0, 60) + '…' : r;
}
- // 研究理由 (2026-09-03): 判决依据是上游对这条判决的一句话解释; 因果论断是研报里说这家
- // 公司好在哪, 每行自带「——出处:《研报标题》 披露日」。卡片窄, 只摆第一条论断,
- // 但那一条必须原样显示到出处与日期为止 —— 一条 2024 年的论断与一条上周的, 人得一眼分得出。
- function propBasis(p) { return (p.hard_numbers || {}).basis || ''; }
// 硬数字里由上游写好的整句 (安全边际 / 催化事件 / 定价状态): 原样显示, 不是字符串就不显示。
function propText(p, key) {
const v = (p.hard_numbers || {})[key];
@@ -3820,6 +3897,14 @@ createApp({
const lFold = ref(!!_rf.l), rFold = ref(!!_rf.r);
const railOpen = ref(null); // null / 'l' / 'r'
const sectClosed = reactive({ prop:false, strat:false, pool:false, ops:false, set:true });
+ // 提议卡上「展开证据」的状态, 按提议号记。用整体替换而不是原地增删,
+ // 因为 Vue 对 Set 的方法调用不作响应, 替换引用才会重画。
+ const propMoreOpen = ref(new Set());
+ const togglePropMore = (id) => {
+ const next = new Set(propMoreOpen.value);
+ if (next.has(id)) next.delete(id); else next.add(id);
+ propMoreOpen.value = next;
+ };
function toggleSect(k) { sectClosed[k] = !sectClosed[k]; }
function _railSave() {
try { sessionStorage.setItem(RAIL_STORE_KEY, JSON.stringify({ l:lFold.value, r:rFold.value })); } catch (e) {}
@@ -3910,8 +3995,11 @@ createApp({
// 但从 2026-09-04 那批文案改造起就一直没写进这份清单, 模板拿到的是 undefined。
// 平时提议队列为空、那几行 v-if 不成立所以从不调用, 09-10 早上队列里出现
// 八条新建仓提议, 第一次真的调到它, 整页渲染当场炸掉 —— 表现是整个页面白屏。
- propVerdictLine, propBasis, propLogic, propLogicMore, propText, propHeads,
+ propVerdictLine, propLogic, propLogicMore, propText, propHeads,
hardCn, opCn, paramsCn, dropCn, plainCheck,
+ // 2026-09-10 提议卡重排新增
+ propScale, propSizePct, propAdviceTier, propSizeMismatch, propJudgeShort,
+ propMoreOpen, togglePropMore,
pHaltBuy, pResumeBuy, pHaltAll, pResumeAll, pReduce, pIncrease, pLiquidate, pSectorExit,
pctOf, tgtPos, posMoveValid, posMovePreview, doPosMove, heldSectors, secSel, doSectorExit,
pmap, pval, dcaOn, sumPosition, sumDca, sumAutonomy,