交易页面:研究依据复用详情入口,收起技术追溯字段
一、修复侧栏信息堆叠 右侧提议新增字段展开后出现长版本号溢出,裁决按钮被挤到下方。删除该清单,沿用基本面入口,按判断与风险组织研究详情。 二、保留追溯并统一阅读方式 版本、指纹与消费顺序收进默认折叠的数据来源。候选、持仓和运维提议共用详情,不改变研究原文、交易数量、参数或采纳核验。 三、验证 本地三道页面守卫通过。JavaScript 语法、空记录、零值、研究变化、数字格式与原始数据保持验证通过。155 挂载已核对,纯静态页无需重建交易进程,实机验收另记。
This commit is contained in:
parent
5378f70e8c
commit
c2d0dc921f
13
DEVLOG.md
13
DEVLOG.md
|
|
@ -1919,3 +1919,16 @@ PMS 页面需要重新登录;基座页面版本与研究字段已实机核对
|
|||
09:25:03 正常扫描 consumed 64 消费区制版 6abfc0e8,24 只顺序与指纹一致,过滤原因完整。
|
||||
登录后页面误报消失,旧提议与持仓明示追溯缺失;没有采纳或人为制造交易。
|
||||
真实盘中评析传播、提议重核与新成交防双记继续等自然事件。备份、镜像及回退见四仓交接最新节。
|
||||
|
||||
|
||||
## 2026-10-16 · 研究展示按交易员使用顺序收口
|
||||
|
||||
用户指出右栏展开的版本与研究字段破坏页面风格,长版本号横向溢出并把裁决按钮挤出视线。
|
||||
删除侧栏新增的字段清单,复用原“基本面”入口。研究详情按判断、风险、预期、后续验证及变化分段。
|
||||
报告时间与实际材料期保持区分。缺失项不连续铺“未知”;无摘要用一句明确说明。
|
||||
计划标识、指纹和消费顺序放在详情底部“数据来源”,默认折叠且长字符串可换行。
|
||||
候选与持仓、运维提议的同类字段清单一起复用详情,避免表格行无限增高。
|
||||
只改 app/web/static/index.html,未改评析原文、建议数量、采纳核验或交易接口。
|
||||
本地三页面守卫通过,页面脚本语法、空记录、零值、变化与原始值保持检查通过。
|
||||
155 已核查 /app 源码绑定挂载,本次纯静态页可经 git pull 生效,不重建交易进程。
|
||||
上线前先备份旧页面并在禁网临时容器运行守卫;浏览器结果另追加。
|
||||
|
|
|
|||
|
|
@ -517,6 +517,17 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
|||
.prop-metric{display:flex;flex-direction:column;gap:7px;margin:8px 0;}
|
||||
.prop-metric .m{background:var(--surface-2);border-radius:9px;padding:9px 11px;display:flex;align-items:center;gap:10px;}
|
||||
.prop-metric .m .nm{font-size:12.5px;flex:1;} .prop-metric .m .dt{font-size:11px;color:var(--muted);}
|
||||
.research-link{display:inline-block;border:0;padding:3px 0;background:none;color:var(--accent);font:inherit;font-size:12px;cursor:pointer;text-align:left;}
|
||||
.research-link:hover{text-decoration:underline;}
|
||||
.prop-sheet.research-sheet{left:50%;right:auto;bottom:24px;transform:translateX(-50%);width:min(720px,calc(100vw - 32px));max-height:calc(100vh - 48px);border-radius:16px;}
|
||||
.research-summary{display:flex;gap:8px;margin-bottom:14px;}
|
||||
.research-section .st{white-space:pre-wrap;overflow-wrap:anywhere;line-height:1.75;}
|
||||
.research-section .st + .st{margin-top:8px;}
|
||||
.research-materials{display:flex;flex-wrap:wrap;gap:6px 16px;margin:18px 0 10px;color:var(--muted);font-size:12px;line-height:1.65;}
|
||||
.research-source{border-top:1px solid var(--hair);margin-top:16px;padding-top:12px;font-size:12px;color:var(--muted);}
|
||||
.research-source summary{cursor:pointer;}
|
||||
.research-source dl{display:grid;grid-template-columns:90px minmax(0,1fr);gap:8px 12px;margin:14px 0 0;}
|
||||
.research-source dt,.research-source dd{margin:0;overflow-wrap:anywhere;line-height:1.65;}
|
||||
.prop-badge{font-size:11px;font-family:ui-monospace,Menlo,monospace;background:var(--surface-2);padding:2px 7px;border-radius:5px;}
|
||||
</style>
|
||||
</head>
|
||||
|
|
@ -1262,7 +1273,6 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
|||
</div>
|
||||
</div>
|
||||
<div style="margin-top:6px">
|
||||
<details><summary>计划版本与研究变化</summary><div v-for="line in researchInfo(p.hard_numbers)" :key="line">{{ line }}</div></details>
|
||||
<el-button type="success" size="small" @click="decide(p,'ACCEPTED')">采纳</el-button>
|
||||
<el-button type="danger" size="small" @click="decide(p,'DECLINED')">驳回</el-button>
|
||||
</div>
|
||||
|
|
@ -1839,7 +1849,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
|||
查回的逻辑状态、第一条出处与证据截止日。状态值是上游给的中文原文, 不是枚举码。 -->
|
||||
<el-table-column label="当初为什么买" min-width="220">
|
||||
<template #default="s">
|
||||
<details><summary>入场计划版本与研究依据</summary><div v-for="line in researchInfo(s.row.entry)" :key="line">{{ line }}</div></details>
|
||||
<button class="research-link" @click="openResearch(s.row.entry, s.row.ts_code)">查看入场依据</button>
|
||||
<template v-if="s.row.entry && s.row.entry.basis">
|
||||
<div :title="(s.row.entry.logic||[]).join('\n')">{{ s.row.entry.basis }}</div>
|
||||
<div class="muted" v-if="(s.row.entry.logic||[]).length">{{ s.row.entry.logic[0] }}</div>
|
||||
|
|
@ -1852,7 +1862,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
|||
</el-table-column>
|
||||
<el-table-column label="证据还在不在" min-width="200">
|
||||
<template #default="s">
|
||||
<details v-if="s.row.company"><summary>当前研究版本与观察时间</summary><div v-for="line in researchInfo(s.row.company)" :key="line">{{ line }}</div></details>
|
||||
<button class="research-link" v-if="s.row.company" @click="openResearch(s.row.company, s.row.ts_code)">查看研究详情</button>
|
||||
<template v-if="s.row.logic_now && s.row.logic_now.state">
|
||||
<el-tag size="small" effect="dark" :type="logicTag(s.row.logic_now.state)">{{ s.row.logic_now.state }}</el-tag>
|
||||
<div class="muted" :title="s.row.logic_now.text">{{ s.row.logic_now.text }}</div>
|
||||
|
|
@ -1992,7 +2002,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
|||
<el-table-column prop="qty" label="数量" width="90"></el-table-column>
|
||||
<el-table-column label="当时的依据" min-width="320">
|
||||
<template #default="s"><span class="muted" :title="JSON.stringify(s.row.hard_numbers)">{{ hardCn(s.row.hard_numbers) }}</span>
|
||||
<details><summary>计划版本与研究变化</summary><div v-for="line in researchInfo(s.row.hard_numbers)" :key="line">{{ line }}</div></details>
|
||||
<button class="research-link" @click="openResearch(s.row.hard_numbers, s.row.ts_code)">查看研究依据</button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="研判" width="100">
|
||||
|
|
@ -2113,7 +2123,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
|||
<teleport to="body">
|
||||
<div v-if="propDetailOpen">
|
||||
<div class="cs-scrim" @click="closePropDetail"></div>
|
||||
<aside class="prop-sheet">
|
||||
<aside class="prop-sheet" :class="{'research-sheet':propDetailKind==='company'}" role="dialog" aria-label="提议依据">
|
||||
<div class="cs-h">
|
||||
<span class="ttl">{{ nm((propDetailP||{}).ts_code) }}</span>
|
||||
<span class="muted mono" style="font-size:11px">{{ (propDetailP||{}).ts_code }}</span>
|
||||
|
|
@ -2141,19 +2151,28 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
|||
</template>
|
||||
|
||||
<template v-else-if="propDetailKind==='company'">
|
||||
<div style="display:flex;gap:6px;flex-wrap:wrap;margin-bottom:10px" v-if="cr(propDetailP)">
|
||||
<span class="ptag" :class="propCompanyTag(propDetailP).cls">质地{{ cr(propDetailP).overall }}</span>
|
||||
<span class="ptag neutral" v-if="cr(propDetailP).valuation">估值{{ cr(propDetailP).valuation }}</span>
|
||||
<span class="prop-badge" v-if="cr(propDetailP).grade_counts">十视角 好 {{ cr(propDetailP).grade_counts['好'] }} / 中 {{ cr(propDetailP).grade_counts['中'] }} / 差 {{ cr(propDetailP).grade_counts['差'] }}</span>
|
||||
<span class="prop-badge" v-if="cr(propDetailP).confidence">置信 {{ cr(propDetailP).confidence }}</span>
|
||||
<div class="research-summary" v-if="researchData(propDetailP.hard_numbers).overall || researchData(propDetailP.hard_numbers).valuation">
|
||||
<span class="ptag neutral" v-if="researchData(propDetailP.hard_numbers).overall">质地{{ researchData(propDetailP.hard_numbers).overall }}</span>
|
||||
<span class="ptag neutral" v-if="researchData(propDetailP.hard_numbers).valuation">估值{{ researchData(propDetailP.hard_numbers).valuation }}</span>
|
||||
</div>
|
||||
<div class="cs-sect" v-if="cr(propDetailP) && cr(propDetailP).thesis"><div class="h" style="color:var(--down)">买方论点</div><div class="st">{{ cr(propDetailP).thesis }}</div></div>
|
||||
<div class="cs-sect" v-if="cr(propDetailP) && cr(propDetailP).invalidation"><div class="h" style="color:var(--warn)">失效条件</div><div class="st">{{ cr(propDetailP).invalidation }}</div></div>
|
||||
<div class="cs-sect" v-if="cr(propDetailP) && cr(propDetailP).antithesis"><div class="h" style="color:var(--up)">反方</div><div class="st">{{ cr(propDetailP).antithesis }}</div></div>
|
||||
<div class="cs-sect" v-if="cr(propDetailP) && cr(propDetailP).catalyst"><div class="h">催化</div><div class="st muted">{{ cr(propDetailP).catalyst }}</div></div>
|
||||
<div class="st" v-if="cr(propDetailP) && cr(propDetailP).report_version_url" style="margin-top:8px"><span class="lnk" @click="openCompanyReport(propDetailP.ts_code, cr(propDetailP).review_ref)">查看本版完整评析报告</span></div>
|
||||
<div class="muted" v-else>未保留历史报告版本;摘要不代表完整原文。</div>
|
||||
<div class="st muted" v-if="!cr(propDetailP)">这只票没有公司深度评析。</div>
|
||||
<div class="st muted" v-if="!researchSections(propDetailP.hard_numbers).length">这条记录未保存研究摘要。</div>
|
||||
<section class="cs-sect research-section" v-for="section in researchSections(propDetailP.hard_numbers)" :key="section.title">
|
||||
<div class="h">{{ section.title }}</div>
|
||||
<div class="st" v-for="line in section.lines" :key="line">{{ line }}</div>
|
||||
</section>
|
||||
<div class="research-materials" v-if="researchMaterials(propDetailP.hard_numbers).length">
|
||||
<span v-for="item in researchMaterials(propDetailP.hard_numbers)" :key="item">{{ item }}</span>
|
||||
</div>
|
||||
<div class="st" v-if="researchData(propDetailP.hard_numbers).report_version_url">
|
||||
<button class="research-link" @click="openCompanyReport(propDetailP.ts_code, researchData(propDetailP.hard_numbers).review_ref)">阅读完整评析</button>
|
||||
</div>
|
||||
<div class="st muted" v-else-if="researchSections(propDetailP.hard_numbers).length">仅保留评析摘要,完整原文暂不可查。</div>
|
||||
<details class="research-source" :key="(propDetailP.ts_code||'') + propDetailKind">
|
||||
<summary>数据来源</summary>
|
||||
<dl><template v-for="item in researchSource(propDetailP.hard_numbers)" :key="item.label">
|
||||
<dt>{{ item.label }}</dt><dd>{{ item.value }}</dd>
|
||||
</template></dl>
|
||||
</details>
|
||||
</template>
|
||||
|
||||
<template v-else-if="propDetailKind==='tech'">
|
||||
|
|
@ -2578,8 +2597,8 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
|||
</el-tabs>
|
||||
<el-divider content-position="left">主榜明细</el-divider>
|
||||
<el-table :data="planRows" size="small" border height="420">
|
||||
<el-table-column label="版本与研究" min-width="190"><template #default="s">
|
||||
<details><summary>研究依据与变化</summary><div v-for="line in researchInfo(s.row)" :key="line">{{ line }}</div></details>
|
||||
<el-table-column label="研究依据" width="110"><template #default="s">
|
||||
<button class="research-link" @click="openResearch(s.row, s.row.ts_code)">查看详情</button>
|
||||
</template></el-table-column>
|
||||
<el-table-column prop="rank" label="rank" width="62"></el-table-column>
|
||||
<el-table-column prop="ts_code" label="代码" width="104"></el-table-column>
|
||||
|
|
@ -3245,31 +3264,70 @@ createApp({
|
|||
// 标签返回 {text, cls}; cls 走 .ptag 取色 —— 技术面看多看空按页面口径红涨绿跌 (up/down),
|
||||
// 其余评价类用中性色 (accent 蓝 / warn 黄 / neutral 灰), 不掺进涨跌红绿。
|
||||
const cr = (p) => (p && p.hard_numbers && p.hard_numbers.company_review) || null;
|
||||
function researchInfo(value) {
|
||||
const h = value || {}, d = h.decision || {}, r = h.research || h.company_review || {};
|
||||
const fields = {overall:'质地', valuation:'估值', doubt_hard:'财务风险', expectation:'预期差',
|
||||
cycle:'周期', period_fi:'财务材料期', period_rf:'研报材料期', n_materials:'材料数量',
|
||||
unknown_n:'未知问题数', numbers_unmatched:'数字核验缺项', thesis:'投资论点', antithesis:'反方论点',
|
||||
text:'说明', gap_pts:'预期增速差(百分点)', implied_pe_vs_cons:'隐含市盈率与一致预期比',
|
||||
grade:'评价', stance:'立场'};
|
||||
const show = x => x == null ? '未知' : Array.isArray(x) ? x.map(show).join(';') :
|
||||
typeof x === 'object' ? Object.entries(x).map(([k,v]) => (fields[k] || k) + ':' + show(v)).join(';') : String(x);
|
||||
const when = x => {
|
||||
if (!x) return '未知';
|
||||
if (!/(?:Z|[+-]\d{2}:?\d{2})$/i.test(String(x))) return x + '(时区未记录)';
|
||||
const date = new Date(x);
|
||||
return Number.isNaN(date.getTime()) ? '未知' : date.toLocaleString('zh-CN', { timeZone: 'Asia/Shanghai' }) + '(北京时间)';
|
||||
// 研究内容用于判断,技术来源仅供追溯。保持冻结值,不回源或重算建议。
|
||||
function researchData(value) {
|
||||
const h = value || {};
|
||||
return { ...(h.company_review || h), ...(h.research || {}) };
|
||||
}
|
||||
function researchText(value) {
|
||||
if (value == null || value === '') return '';
|
||||
if (typeof value === 'number') return Number.isFinite(value) ? String(Number(value.toFixed(2))) : '';
|
||||
if (Array.isArray(value)) return value.map(researchText).filter(Boolean).join(';');
|
||||
if (typeof value === 'object') {
|
||||
if (value.text) return researchText(value.text);
|
||||
const labels = {grade:'评价', stance:'立场', gap_pts:'预期增速差(百分点)',
|
||||
implied_pe_vs_cons:'隐含市盈率与一致预期比'};
|
||||
return Object.entries(labels).filter(([k]) => value[k] != null)
|
||||
.map(([k,label]) => label + ':' + researchText(value[k])).join(';');
|
||||
}
|
||||
return String(value).trim();
|
||||
}
|
||||
function researchWhen(value) {
|
||||
if (!value) return '';
|
||||
if (!/(?:Z|[+-]\d{2}:?\d{2})$/i.test(String(value))) return value + '(时区未记录)';
|
||||
const d = new Date(value);
|
||||
return Number.isNaN(d.getTime()) ? '' : d.toLocaleString('zh-CN', {timeZone:'Asia/Shanghai'}) + '(北京时间)';
|
||||
}
|
||||
function researchSections(value) {
|
||||
const r = researchData(value), sections = [];
|
||||
const add = (title, values) => {
|
||||
const lines = values.map(researchText).filter(Boolean);
|
||||
if (lines.length) sections.push({title, lines});
|
||||
};
|
||||
return ['计划版本:' + (d.id || '旧数据,追溯不完整'), '变更原因:' + (d.reason || '未记录'),
|
||||
'决策截止:' + when(d.cutoff_at), '独立研究观察:' + when(h.observation_at), '报告生成:' + when(r.report_ran_at),
|
||||
...[['投资论点','thesis'], ['反方论点','antithesis'], ['预期差','expectation'], ['周期位置','cycle'],
|
||||
['财务材料期','period_fi'], ['研报材料期','period_rf'], ['材料数量','n_materials'],
|
||||
['未知问题数','unknown_n'], ['数字核验缺项','numbers_unmatched']].map(([label,key]) => label + ':' + show(r[key])),
|
||||
'催化与验证事项:' + show((h.company_review || {}).catalyst),
|
||||
'失效条件:' + show((h.company_review || {}).invalidation),
|
||||
'研究变化:' + ((r.changes || []).length ? r.changes.map(x =>
|
||||
(fields[x.field] || x.field) + ':' + show(x.before) + ' → ' + show(x.after)).join(';') : '无已记录变化'),
|
||||
'执行次序:' + show(h.execution_rank) + ';' + (h.priority_reason || '未记录')];
|
||||
add('核心判断', [r.thesis]);
|
||||
add('主要风险', [r.antithesis]);
|
||||
add('预期与经营周期', [r.expectation, r.cycle]);
|
||||
add('后续关注', [r.catalyst]);
|
||||
add('判断失效的条件', [r.invalidation]);
|
||||
const labels = {overall:'质地',valuation:'估值',doubt_hard:'财务风险',expectation:'预期',
|
||||
cycle:'周期',period_fi:'财务材料期',period_rf:'研报材料期',n_materials:'材料数量',
|
||||
unknown_n:'待核实问题',numbers_unmatched:'数字核验缺项',thesis:'核心判断',antithesis:'主要风险'};
|
||||
add('研究变化', (Array.isArray(r.changes) ? r.changes : []).map(x =>
|
||||
(labels[x.field] || '研究材料') + ':' + (researchText(x.before) || '未提供') + ' → ' + (researchText(x.after) || '未提供')));
|
||||
return sections;
|
||||
}
|
||||
function researchMaterials(value) {
|
||||
const r = researchData(value), items = [];
|
||||
if (r.report_ran_at) items.push('评析时间 ' + researchWhen(r.report_ran_at));
|
||||
[['period_fi','财务材料'],['period_rf','研报材料'],['n_materials','材料数量'],
|
||||
['unknown_n','待核实问题'],['numbers_unmatched','数字核验缺项']].forEach(([key,label]) => {
|
||||
if (r[key] != null && r[key] !== '') items.push(label + ' ' + researchText(r[key]));
|
||||
});
|
||||
return items;
|
||||
}
|
||||
function researchSource(value) {
|
||||
const h = value || {}, d = h.decision || {}, r = researchData(value), out = [];
|
||||
const add = (label, v) => { if (v != null && v !== '') out.push({label,value:String(v)}); };
|
||||
add('记录完整度', d.id ? '已保存当时依据' : '旧记录未保存完整来源');
|
||||
add('计划标识', d.id); add('更新原因', d.reason);
|
||||
add('信息截止', researchWhen(d.cutoff_at)); add('持仓研究查询时间', researchWhen(h.observation_at));
|
||||
add('评析引用', typeof r.review_ref === 'string' ? r.review_ref : (r.review_ref || {}).fingerprint);
|
||||
add('执行顺序', h.execution_rank); add('顺序依据', h.priority_reason);
|
||||
add('输入指纹', d.input_hash); add('配置指纹', d.params_hash); add('消费记录', h.consumption_id);
|
||||
return out;
|
||||
}
|
||||
function openResearch(value, code) {
|
||||
openPropDetail({ts_code:code, hard_numbers:value || {}}, 'company');
|
||||
}
|
||||
const propPlanTag = (p) => {
|
||||
const v = String((p.hard_numbers || {}).verdict || '').trim();
|
||||
|
|
@ -4779,7 +4837,7 @@ createApp({
|
|||
scaleGap,
|
||||
wsRaw, wsAuto, wsHidePong, wsc, wst, wsMode, wsOrders, wsInbox, wsWarnings, loadWs,
|
||||
loadAll, loadParams, saveParams, loadPlans, loadLots, onCmdChange, issue,
|
||||
cancelCmd, replan, decide, decideDlg, decideCodes, submitDecide, ops, loadSchema, openReport, cancelIns, researchInfo,
|
||||
cancelCmd, replan, decide, decideDlg, decideCodes, submitDecide, ops, loadSchema, openReport, cancelIns, researchData, researchSections, researchMaterials, researchSource, openResearch,
|
||||
planDrawer, planLoading, planStatus, planFetchedText, planRows, planCand, planResult,
|
||||
loadPlan, openPlan, refreshPlan,
|
||||
techStatus, techChipText, techChipCls,
|
||||
|
|
|
|||
Loading…
Reference in New Issue