候选行等待态不占位、名称省略不换行;持仓表研究面列改为综合分析三枚维度标签
建仓候选栏右侧窄,处置芯片被挤成竖排:候选观察、技术面待开口这类等待态不再显示芯片, 原因进悬停;只有拟建仓、系统已放弃这类有动作意义的才显示。行内元素不换行,名称与主题超长省略。 持仓表与管理视图持仓总览的「研究面」列改名「综合分析」,显示基本面、技术面、择时三枚标签, 与候选栏、提议卡、抽屉同一套维度与颜色;持仓接口每行挂三票 dims。 Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
7cf1d5be40
commit
8ff4ea3d8f
|
|
@ -388,6 +388,24 @@ def api_positions():
|
||||||
tech_service.attach(v["positions"])
|
tech_service.attach(v["positions"])
|
||||||
except Exception as e: # noqa: BLE001
|
except Exception as e: # noqa: BLE001
|
||||||
logger.warning("[持仓] 技术面立场挂不上: %s", e)
|
logger.warning("[持仓] 技术面立场挂不上: %s", e)
|
||||||
|
# 综合分析三票 (2026-09-15): 每行挂 dims = {fund, tech, timing, fund_fact, tech_phase, nightly},
|
||||||
|
# 供持仓表「综合分析」列三枚维度标签用。与扫描同一套装配 (consensus_service.decorate_positions:
|
||||||
|
# 基本面从逻辑状态映射的买方评析算, 技术面用早上映射, 择时用昨夜定性); 只是显示, 取不到不塌。
|
||||||
|
try:
|
||||||
|
from app.services import consensus_service
|
||||||
|
_lmap = logic_state_service.state_map() or {}
|
||||||
|
_crs = {code: (st or {}).get("company_review") for code, st in _lmap.items()}
|
||||||
|
consensus_service.decorate_positions(v["positions"], company_reviews=_crs)
|
||||||
|
for r in v["positions"]:
|
||||||
|
b = r.pop("consensus_blocks", None) or {}
|
||||||
|
fund, tech, tm = b.get("fund") or {}, b.get("tech") or {}, b.get("timing") or {}
|
||||||
|
# 技术面优先用 tech_service.attach 挂的那份 (同一映射, 已带相位与理由)
|
||||||
|
t = r.get("tech") or tech
|
||||||
|
r["dims"] = {"fund": fund.get("stance"), "fund_fact": fund.get("fact") or fund.get("no_read_why"),
|
||||||
|
"tech": t.get("stance"), "tech_phase": t.get("phase"),
|
||||||
|
"timing": tm.get("stance"), "nightly": tm.get("nightly")}
|
||||||
|
except Exception as e: # noqa: BLE001
|
||||||
|
logger.warning("[持仓] 综合分析三票挂不上: %s", e)
|
||||||
return {"ok": True, **v, "stock_params": sp}
|
return {"ok": True, **v, "stock_params": sp}
|
||||||
return ok(_view)
|
return ok(_view)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -344,8 +344,9 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
border-bottom:1px dashed rgba(128,128,128,.16);font-size:12.5px;}
|
border-bottom:1px dashed rgba(128,128,128,.16);font-size:12.5px;}
|
||||||
.sigrow2:last-child{border-bottom:none;}
|
.sigrow2:last-child{border-bottom:none;}
|
||||||
.sigrow2 .t{width:36px;flex:none;font-size:11px;color:var(--muted);}
|
.sigrow2 .t{width:36px;flex:none;font-size:11px;color:var(--muted);}
|
||||||
.sigrow2 .nm{font-weight:620;white-space:nowrap;}
|
.sigrow2 .nm{font-weight:620;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;min-width:0;}
|
||||||
.sigrow2 .cat{color:var(--muted);font-size:11.5px;white-space:nowrap;}
|
.sigrow2 .cat{color:var(--muted);font-size:11.5px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:34%;}
|
||||||
|
.sigrow2 .dim,.sigrow2 .disp{flex:none;white-space:nowrap;}
|
||||||
.sigrow2 .sp{flex:1;}
|
.sigrow2 .sp{flex:1;}
|
||||||
.sigrow2.old{opacity:.5;}
|
.sigrow2.old{opacity:.5;}
|
||||||
.sigrow2.clk{cursor:pointer;border-radius:4px;}
|
.sigrow2.clk{cursor:pointer;border-radius:4px;}
|
||||||
|
|
@ -449,6 +450,7 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
.dim.down{background:var(--down-weak);color:var(--down);}
|
.dim.down{background:var(--down-weak);color:var(--down);}
|
||||||
.dim.neutral{background:rgba(138,136,130,.14);color:var(--ink-2);}
|
.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;}
|
.dim.na{background:transparent;color:var(--muted);border:1px dashed var(--hair);padding:0 6px;}
|
||||||
|
.dims{display:flex;gap:4px;flex-wrap:wrap;align-items:center;}
|
||||||
.prop-sheet{position:fixed;left:0;right:0;bottom:0;z-index:3000;max-height:86vh;overflow:auto;
|
.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);}
|
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;}
|
.prop-metric{display:flex;flex-direction:column;gap:7px;margin:8px 0;}
|
||||||
|
|
@ -976,15 +978,13 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="muted">{{ pct(s.row.cushion_pct) }}</div>
|
<div class="muted">{{ pct(s.row.cushion_pct) }}</div>
|
||||||
<div class="muted" v-if="s.row.neg_cushion_days>0">连亏 {{ s.row.neg_cushion_days }} 日</div></template>
|
<div class="muted" v-if="s.row.neg_cushion_days>0">连亏 {{ s.row.neg_cushion_days }} 日</div></template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="研究面" width="92">
|
<el-table-column label="综合分析" width="168">
|
||||||
<template #default="s">
|
<template #default="s">
|
||||||
<template v-if="s.row.tech">
|
<div class="dims" style="cursor:pointer" title="查看个股详情" @click.stop="openCodeSignals(s.row.ts_code)">
|
||||||
<b :class="techStanceCls(s.row.tech.stance)" style="cursor:pointer;text-decoration:underline dotted"
|
<span class="dim" :class="dimCls(dimsOf(s.row).fund)" :title="dimTip('基本面', dimsOf(s.row).fund, dimsOf(s.row).fund_fact)">基本面</span>
|
||||||
@click.stop="openCodeSignals(s.row.ts_code)"
|
<span class="dim" :class="dimCls(dimsOf(s.row).tech)" :title="dimTip('技术面', dimsOf(s.row).tech, dimsOf(s.row).tech_phase)">技术面</span>
|
||||||
:title="'技术面:' + s.row.tech.stance + '(' + (s.row.tech.phase||'') + ')\n' + (s.row.tech.reason||'') + '\n点一下看研究面'">{{ s.row.tech.stance }}</b>
|
<span class="dim" :class="dimCls(dimsOf(s.row).timing)" :title="dimTip('择时', dimsOf(s.row).timing, dimsOf(s.row).nightly)">择时</span>
|
||||||
<div class="muted" style="font-size:11px" v-if="s.row.tech.phase">{{ s.row.tech.phase }}</div>
|
</div>
|
||||||
</template>
|
|
||||||
<span class="muted" v-else>无读数</span>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="当日信号" width="108">
|
<el-table-column label="当日信号" width="108">
|
||||||
|
|
@ -1287,7 +1287,8 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<div class="muted" v-if="planStatus.ok===false">上游的选股计划现在读不到。</div>
|
<div class="muted" v-if="planStatus.ok===false">上游的选股计划现在读不到。</div>
|
||||||
<div class="muted" v-else-if="!watchCandidates.length">当前没有待建仓的候选,都已处理。</div>
|
<div class="muted" v-else-if="!watchCandidates.length">当前没有待建仓的候选,都已处理。</div>
|
||||||
<div v-else v-for="r in watchCandidates" :key="r.ts_code" class="sigrow2 clk"
|
<div v-else v-for="r in watchCandidates" :key="r.ts_code" class="sigrow2 clk"
|
||||||
title="查看个股详情" @click="openCodeSignals(r.ts_code)">
|
:title="'查看个股详情' + (dispOf(r.ts_code).label ? ' · ' + dispOf(r.ts_code).label : '') + (dispOf(r.ts_code).why ? ':' + dispOf(r.ts_code).why : '')"
|
||||||
|
@click="openCodeSignals(r.ts_code)">
|
||||||
<span class="muted mono t">第{{ r.rank }}</span>
|
<span class="muted mono t">第{{ r.rank }}</span>
|
||||||
<span class="nm" style="text-decoration:underline dotted">{{ r.name || nm(r.ts_code) }}</span>
|
<span class="nm" style="text-decoration:underline dotted">{{ r.name || nm(r.ts_code) }}</span>
|
||||||
<span class="cat" v-if="r.theme">{{ r.theme }}</span>
|
<span class="cat" v-if="r.theme">{{ r.theme }}</span>
|
||||||
|
|
@ -1298,7 +1299,9 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
<span class="dim" :class="dimCls(conOf(r.ts_code).tech)" :title="'技术面 ' + (conOf(r.ts_code).tech||'无读数') + (conOf(r.ts_code).phase ? ' · ' + conOf(r.ts_code).phase : '')">技术面</span>
|
<span class="dim" :class="dimCls(conOf(r.ts_code).tech)" :title="'技术面 ' + (conOf(r.ts_code).tech||'无读数') + (conOf(r.ts_code).phase ? ' · ' + conOf(r.ts_code).phase : '')">技术面</span>
|
||||||
<span class="dim" :class="dimCls(conOf(r.ts_code).timing)" :title="'择时 ' + (conOf(r.ts_code).timing||'无读数')">择时</span>
|
<span class="dim" :class="dimCls(conOf(r.ts_code).timing)" :title="'择时 ' + (conOf(r.ts_code).timing||'无读数')">择时</span>
|
||||||
</template>
|
</template>
|
||||||
<span class="disp" :class="dispOf(r.ts_code).cls" style="padding:0 6px">{{ dispOf(r.ts_code).label }}</span>
|
<!-- 等待态 (候选观察/技术面待开口/盘中确认中/择时待转多) 不占行内位置 (2026-09-15): 这一栏本来就是
|
||||||
|
还在评估的票, 原因进悬停; 只有拟建仓、系统已放弃这类有动作意义的才显示芯片。 -->
|
||||||
|
<span class="disp" v-if="dispOf(r.ts_code).cls !== 'wait'" :class="dispOf(r.ts_code).cls" style="padding:0 6px">{{ dispOf(r.ts_code).label }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="muted" style="margin-top:6px;font-size:11.5px">
|
<div class="muted" style="margin-top:6px;font-size:11.5px">
|
||||||
受益层级<span class="help" data-tip="这只票离主题多近:直接受益还是间接受益。上游的选股计划给的。">?</span>
|
受益层级<span class="help" data-tip="这只票离主题多近:直接受益还是间接受益。上游的选股计划给的。">?</span>
|
||||||
|
|
@ -1778,16 +1781,14 @@ body.dock-r:not(.r-fold) .side-r .strip{display:none;}
|
||||||
class="muted">今天没有读数</span>
|
class="muted">今天没有读数</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<!-- 研究面列 (2026-09-14 页面收尾包): 与「持仓」那一列同款, 点技术面立场看研究面抽屉。 -->
|
<!-- 综合分析列 (2026-09-15): 与「持仓」那一列同款三枚维度标签, 点开看个股详情。 -->
|
||||||
<el-table-column label="研究面" width="92">
|
<el-table-column label="综合分析" width="168">
|
||||||
<template #default="s">
|
<template #default="s">
|
||||||
<template v-if="s.row.tech">
|
<div class="dims" style="cursor:pointer" title="查看个股详情" @click.stop="openCodeSignals(s.row.ts_code)">
|
||||||
<b :class="techStanceCls(s.row.tech.stance)" style="cursor:pointer;text-decoration:underline dotted"
|
<span class="dim" :class="dimCls(dimsOf(s.row).fund)" :title="dimTip('基本面', dimsOf(s.row).fund, dimsOf(s.row).fund_fact)">基本面</span>
|
||||||
@click.stop="openCodeSignals(s.row.ts_code)"
|
<span class="dim" :class="dimCls(dimsOf(s.row).tech)" :title="dimTip('技术面', dimsOf(s.row).tech, dimsOf(s.row).tech_phase)">技术面</span>
|
||||||
:title="'技术面:' + s.row.tech.stance + '(' + (s.row.tech.phase||'') + ')\n' + (s.row.tech.reason||'') + '\n点一下看研究面'">{{ s.row.tech.stance }}</b>
|
<span class="dim" :class="dimCls(dimsOf(s.row).timing)" :title="dimTip('择时', dimsOf(s.row).timing, dimsOf(s.row).nightly)">择时</span>
|
||||||
<div class="muted" style="font-size:11px" v-if="s.row.tech.phase">{{ s.row.tech.phase }}</div>
|
</div>
|
||||||
</template>
|
|
||||||
<span class="muted" v-else>无读数</span>
|
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="做T" width="80">
|
<el-table-column label="做T" width="80">
|
||||||
|
|
@ -3152,6 +3153,11 @@ createApp({
|
||||||
// 为什么按合议路由走)。方向按页面口径红涨绿跌; 无读数用中性灰。点开看三票与路由理由。
|
// 为什么按合议路由走)。方向按页面口径红涨绿跌; 无读数用中性灰。点开看三票与路由理由。
|
||||||
// 立场 → 颜色类 (2026-09-15, 三处共用): 看多红, 看空绿, 中性灰, 无读数虚框。
|
// 立场 → 颜色类 (2026-09-15, 三处共用): 看多红, 看空绿, 中性灰, 无读数虚框。
|
||||||
const dimCls = (st) => st === '看多' ? 'up' : (st === '看空' ? 'down' : (st === '中性' ? 'neutral' : 'na'));
|
const dimCls = (st) => st === '看多' ? 'up' : (st === '看空' ? 'down' : (st === '中性' ? 'neutral' : 'na'));
|
||||||
|
// 持仓表「综合分析」列: 后端每行挂的 dims (fund/tech/timing 三票与各自一句依据); 没挂就退回技术面那一块。
|
||||||
|
const dimsOf = (row) => (row && row.dims) || { tech: (row && row.tech && row.tech.stance) || null,
|
||||||
|
tech_phase: (row && row.tech && row.tech.phase) || null };
|
||||||
|
const NIGHT_CN2 = { BUY: '建议买入', WATCH: '观望', SELL: '建议卖出', AVOID: '回避', DROPPED: '已剔除' };
|
||||||
|
const dimTip = (name, st, extra) => name + ' ' + (st || '无读数') + (extra ? ' · ' + (NIGHT_CN2[extra] || extra) : '');
|
||||||
// 基本面行: 立场取硬数字里的 fund_stance (合议六键), 没有就按质地档推; 副文案给质地与估值。
|
// 基本面行: 立场取硬数字里的 fund_stance (合议六键), 没有就按质地档推; 副文案给质地与估值。
|
||||||
const propFundTag = (p) => {
|
const propFundTag = (p) => {
|
||||||
const fs = ((p.hard_numbers || {}).fund_stance || {}).stance, c = cr(p);
|
const fs = ((p.hard_numbers || {}).fund_stance || {}).stance, c = cr(p);
|
||||||
|
|
@ -4532,7 +4538,7 @@ createApp({
|
||||||
// 2026-09-12 提议卡重构: 来源结论标签 + 详情弹窗
|
// 2026-09-12 提议卡重构: 来源结论标签 + 详情弹窗
|
||||||
cr, propPlanTag, propPlanSub, propJudgeTag, propCompanyTag, propTechTag, propTechSub,
|
cr, propPlanTag, propPlanSub, propJudgeTag, propCompanyTag, propTechTag, propTechSub,
|
||||||
propCon, propConsensusTag, propConsensusSub,
|
propCon, propConsensusTag, propConsensusSub,
|
||||||
propFundTag, propFundSub, propTimingTag, propTimingSub, propConclusion, dimCls,
|
propFundTag, propFundSub, propTimingTag, propTimingSub, propConclusion, dimCls, dimsOf, dimTip,
|
||||||
propDetailOpen, propDetailKind, propDetailP, propTechFull, propTechErr, PROP_SRC_LABEL,
|
propDetailOpen, propDetailKind, propDetailP, propTechFull, propTechErr, PROP_SRC_LABEL,
|
||||||
openPropDetail, closePropDetail,
|
openPropDetail, closePropDetail,
|
||||||
pHaltBuy, pResumeBuy, pHaltAll, pResumeAll, pReduce, pIncrease, pLiquidate, pSectorExit,
|
pHaltBuy, pResumeBuy, pHaltAll, pResumeAll, pReduce, pIncrease, pLiquidate, pSectorExit,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue