修改前端页面

This commit is contained in:
zlt 2026-08-26 11:50:16 +08:00
parent 8372c388a3
commit a941032c24
2 changed files with 138 additions and 63 deletions

View File

@ -50,6 +50,9 @@ app/services/strategy_advisor.py新增app/services/strategy_service.py
**真机判收** **真机判收**
部分判收2026-08-25 盘中)。桥机 make deploy + make test 见 ALL SUITES PASSdry-run 试算真机四票读数全部合理688802.SH 想挂网格但「按投入比例折出 47,250 元买不起一手」被挡科创板高价票判定正确002179.SZ 有在途指令主动让路其余两票无信号静默跳过unknown_states 为空(子串归类修对了)。**尚未判收的**:真挂一条(等在途清了重扫或次日 09:40、边三/边四真机走一遍、strategy_runner 对自动网格的逐档发单。 部分判收2026-08-25 盘中)。桥机 make deploy + make test 见 ALL SUITES PASSdry-run 试算真机四票读数全部合理688802.SH 想挂网格但「按投入比例折出 47,250 元买不起一手」被挡科创板高价票判定正确002179.SZ 有在途指令主动让路其余两票无信号静默跳过unknown_states 为空(子串归类修对了)。**尚未判收的**:真挂一条(等在途清了重扫或次日 09:40、边三/边四真机走一遍、strategy_runner 对自动网格的逐档发单。
**次日午间第七批2026-08-26布局重设计——撤掉双栏改「持仓全宽 + 信号速览带 + 信号中心收起」**
第六批的左右双栏上真机即被否:持仓表被压窄到操作列整个被切掉;信号卡片本是全宽设计,塞进右栏后股票名逐字竖排;右栏比左栏长数倍,「同屏盯两块」根本没实现。重新分析:交易员要随时看的不是整个信号中心(几百条,翻查用),而是与决策相关的信号——尤其持仓票的信号。重做为融合式三层:一,持仓表恢复全宽(操作列回来),并新增「今日信号」列——每只持仓票显示今天与它相关的最新信号(类别、方向、时刻,多条显示 +N悬停看前三条信号出现在交易员本来就在看的那一行上持仓表下方新增全宽「信号速览」带——持仓票的信号置顶标「持仓」其余只收重要与警告级决策系统买卖、风控卖出、择时入场三路独立信号一并汇入一行一条按时间倒序高度受控内部滚动右上角一键展开完整信号中心完整信号中心整块移到「今日在办」之后、默认收起卡片恢复全宽可读。区块顺序持仓 → 信号速览 → 策略 → 今日在办 → 信号中心。全量 560 例通过。
**次日上午第六批2026-08-26前端文字整改与双栏布局用户三条标准** **次日上午第六批2026-08-26前端文字整改与双栏布局用户三条标准**
用户定下三条标准并全页执行:交易员可读且含义明确;开发人员口吻的文字彻底清除;系统文字最大限度精简,提示能删则删、必须留的进悬浮问号不占版面。整改清单:删掉「随便点」「只是看一眼」这类不严肃表述,预演说明并入按钮旁的悬浮提示;预演结果头行压成「预演结果(未执行)· 查 N 只持仓」;执行状态行改为「今日自动挂载已于 09:41 执行:查 4 · 挂载 0 · 被挡 1 · 跳过 1」句式颜色约定整条彩字图例压成一个悬浮问号告警级别筛选的「只前端过滤、不查后端」删除改悬浮上游信号来源行去掉内部代号mtf操作日志说明收紧为一句空策略引导语改严肃措辞后端返回里会显示到页面的英文类型码全部换成中文名网格、跟踪止盈、做T。布局交易员视图改双栏栅格左七右五——左栏持仓加策略右栏上游信号常驻展开两边可同屏实时盯窗口窄于 1500 像素自动叠回单栏,其余区块次序不变。全量 560 例通过。 用户定下三条标准并全页执行:交易员可读且含义明确;开发人员口吻的文字彻底清除;系统文字最大限度精简,提示能删则删、必须留的进悬浮问号不占版面。整改清单:删掉「随便点」「只是看一眼」这类不严肃表述,预演说明并入按钮旁的悬浮提示;预演结果头行压成「预演结果(未执行)· 查 N 只持仓」;执行状态行改为「今日自动挂载已于 09:41 执行:查 4 · 挂载 0 · 被挡 1 · 跳过 1」句式颜色约定整条彩字图例压成一个悬浮问号告警级别筛选的「只前端过滤、不查后端」删除改悬浮上游信号来源行去掉内部代号mtf操作日志说明收紧为一句空策略引导语改严肃措辞后端返回里会显示到页面的英文类型码全部换成中文名网格、跟踪止盈、做T。布局交易员视图改双栏栅格左七右五——左栏持仓加策略右栏上游信号常驻展开两边可同屏实时盯窗口窄于 1500 像素自动叠回单栏,其余区块次序不变。全量 560 例通过。

View File

@ -57,10 +57,11 @@ body{margin:0;background:var(--plane);color:var(--ink);
.m .v{color:var(--ink);font-size:21px;font-weight:680;margin-top:5px;letter-spacing:-.2px;font-variant-numeric:tabular-nums;} .m .v{color:var(--ink);font-size:21px;font-weight:680;margin-top:5px;letter-spacing:-.2px;font-variant-numeric:tabular-nums;}
.m .v .muted,.m .v span{font-weight:500;} .m .v .muted,.m .v span{font-weight:500;}
.m .v.up{color:var(--up);} .m .v.down{color:var(--down);} .m .v.up{color:var(--up);} .m .v.down{color:var(--down);}
/* 双栏布局: 持仓与策略在左、上游信号在右 (2026-08-26) */ /* 信号速览带 (2026-08-26): 全宽紧凑行, 高度受控内部滚动 */
.tcols{display:grid;grid-template-columns:minmax(0,7fr) minmax(0,5fr);gap:12px;align-items:start} .sigbrief{max-height:216px;overflow:auto}
@media (max-width:1500px){.tcols{grid-template-columns:minmax(0,1fr)}} .sb-row{display:flex;gap:8px;align-items:center;padding:2px 0;border-bottom:1px dashed rgba(128,128,128,.18)}
.tcol{min-width:0} .sb-row:last-child{border-bottom:none}
.sb-t{width:46px;flex:none;font-size:11.5px}
/* 颜色约定图例 */ /* 颜色约定图例 */
.legend{display:flex;gap:15px;flex-wrap:wrap;align-items:center;color:var(--muted);font-size:12px;margin:0 2px 16px;} .legend{display:flex;gap:15px;flex-wrap:wrap;align-items:center;color:var(--muted);font-size:12px;margin:0 2px 16px;}
.legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;margin-right:4px;vertical-align:middle;} .legend .sw{display:inline-block;width:9px;height:9px;border-radius:2px;margin-right:4px;vertical-align:middle;}
@ -298,7 +299,7 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
<span class="muted" v-else-if="usrc('alerts') && usrc('alerts').ok===false">· 读取失败:{{ usrc('alerts').error }}</span> <span class="muted" v-else-if="usrc('alerts') && usrc('alerts').ok===false">· 读取失败:{{ usrc('alerts').error }}</span>
<span class="muted" v-else>· 暂无告警</span> <span class="muted" v-else>· 暂无告警</span>
<div class="spacer"></div> <div class="spacer"></div>
<span class="muted" style="font-size:11.5px;cursor:pointer" @click="showUpstream=true">下方查看全部</span> <span class="muted" style="font-size:11.5px;cursor:pointer" @click="openSigCenter">完整信号中心</span>
</div> </div>
@ -348,8 +349,6 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
</div> </div>
<!-- 3) 我的持仓 --> <!-- 3) 我的持仓 -->
<!-- 双栏 (2026-08-26 用户定): 左=持仓+策略, 右=上游信号, 两边都要实时盯; 窄屏自动叠回单栏 -->
<div class="tcols"><div class="tcol">
<div class="panel tblk" id="t-pos"> <div class="panel tblk" id="t-pos">
<h3>我的持仓 · {{ heldPositions.length }} 只</h3> <h3>我的持仓 · {{ heldPositions.length }} 只</h3>
<el-table :data="heldPositions" size="small" border max-height="460" @expand-change="onPosExpand"> <el-table :data="heldPositions" size="small" border max-height="460" @expand-change="onPosExpand">
@ -398,6 +397,21 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
<template #default="s"><el-tag size="small" effect="dark" :type="cuTag(s.row.cushion_state)">{{ tx('cushion', s.row.cushion_state) }}</el-tag> <template #default="s"><el-tag size="small" effect="dark" :type="cuTag(s.row.cushion_state)">{{ tx('cushion', s.row.cushion_state) }}</el-tag>
<div class="muted">{{ pct(s.row.cushion_pct) }}<span v-if="s.row.neg_cushion_days>0"> · 连负{{ s.row.neg_cushion_days }}日</span></div></template> <div class="muted">{{ pct(s.row.cushion_pct) }}<span v-if="s.row.neg_cushion_days>0"> · 连负{{ s.row.neg_cushion_days }}日</span></div></template>
</el-table-column> </el-table-column>
<el-table-column label="今日信号" width="136">
<template #default="s">
<template v-if="heldSigOf(s.row.ts_code).length">
<span style="cursor:default"
:title="heldSigOf(s.row.ts_code).slice(0,3).map(g=>(g.time||'')+' '+g.cat+' '+(g.direction||'')).join('\n')">
<span :class="dirUp(heldSigOf(s.row.ts_code)[0].direction)?'clr-up':'clr-down'">
{{ heldSigOf(s.row.ts_code)[0].cat }} {{ heldSigOf(s.row.ts_code)[0].direction || '' }}</span>
<span class="muted" style="font-size:11px"> {{ heldSigOf(s.row.ts_code)[0].time }}</span>
<span v-if="heldSigOf(s.row.ts_code).length>1" class="muted" style="font-size:11px">
+{{ heldSigOf(s.row.ts_code).length-1 }}</span>
</span>
</template>
<span v-else class="muted"></span>
</template>
</el-table-column>
<el-table-column label="状态" width="120"> <el-table-column label="状态" width="120">
<template #default="s">{{ tx('posStatus', s.row.status) }} <template #default="s">{{ tx('posStatus', s.row.status) }}
<div v-if="s.row.frozen_reason && s.row.frozen_reason!=='NONE'" style="color:var(--warn)">{{ tx('frozen', s.row.frozen_reason) }}</div></template> <div v-if="s.row.frozen_reason && s.row.frozen_reason!=='NONE'" style="color:var(--warn)">{{ tx('frozen', s.row.frozen_reason) }}</div></template>
@ -423,6 +437,24 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
</el-table-column> </el-table-column>
</el-table> </el-table>
</div> </div>
<!-- 信号速览 (2026-08-26 重设计): 全宽一行一条 —— 持仓票的信号置顶标「持仓」,
其余只收重要/警告级; 完整信号中心默认收起在下方, 点右上角展开跳转 -->
<div class="panel tblk">
<h3>信号速览 <span class="muted" style="font-weight:400">· {{ sigBrief.length }} 条,持仓相关 {{ sigBriefHeldCount }} 条</span>
<span class="muted" style="font-size:12px;font-weight:normal;cursor:pointer;float:right" @click="openSigCenter">完整信号中心 ▾</span></h3>
<div class="muted" v-if="!sigBrief.length">持仓票今日暂无信号,全场暂无重要或警告级信号。</div>
<div v-else class="sigbrief">
<div v-for="(g,i) in sigBrief.slice(0,80)" :key="i" class="sb-row">
<span class="muted mono sb-t">{{ g.time || '—' }}</span>
<el-tag v-if="g.held" size="small" type="warning" effect="dark">持仓</el-tag>
<b>{{ nm(g.ts_code) }}</b><span class="muted mono" style="font-size:11px">{{ g.ts_code }}</span>
<span class="muted">{{ g.cat }}</span>
<span :class="dirUp(g.direction)?'clr-up':(g.direction?'clr-down':'muted')">{{ g.direction || '' }}</span>
<span class="disp" :class="lvClass(g.level)" style="padding:0 6px">{{ lvLabel(g.level) }}</span>
<span v-if="g.stale" class="ab-stale">陈旧</span>
</div>
</div>
</div>
<!-- 4.5) 我的策略 + 操作日志 --> <!-- 4.5) 我的策略 + 操作日志 -->
<div class="panel tblk"> <div class="panel tblk">
<h3>我的策略 · {{ strategies.length }} 个 <h3>我的策略 · {{ strategies.length }} 个
@ -525,9 +557,61 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
</div> </div>
</template> </template>
</div> </div>
</div><div class="tcol">
<!-- 7) 上游信号 (只读参考) --> <!-- 4) 今日在办与动向 -->
<div class="panel tblk"> <div class="panel tblk">
<h3>今日在办</h3>
<div class="sub2">今天下出去的买卖单,按状态分三档:还在跑的、已成交的、已经结束的。</div>
<el-tabs v-model="insTab">
<el-tab-pane name="live">
<template #label>在办中 <span class="tabn">{{ insLive.length }}</span></template>
<el-table :data="insLive" size="small" max-height="260" empty-text="没有在办的买卖">
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
<el-table-column label="动作" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
<el-table-column label="数量 / 已成" width="120" align="right"><template #default="s"><span class="mono">{{ s.row.qty }} / {{ s.row.exec_qty||0 }}</span></template></el-table-column>
<el-table-column label="限价" width="82" align="right"><template #default="s"><span class="mono">{{ s.row.limit_price||'—' }}</span></template></el-table-column>
<el-table-column label="状态" width="118"><template #default="s"><span class="chip idle"><span class="dot"></span>{{ tx('insStatus', s.row.status) }}</span></template></el-table-column>
<el-table-column label="操作" width="76"><template #default="s"><button class="tbtn sm" @click="cancelIns(s.row.instruction_id)">撤单</button></template></el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane name="done">
<template #label>今日成交 <span class="tabn">{{ insDone.length }}</span></template>
<el-table :data="insDone" size="small" max-height="260" empty-text="今天还没有成交">
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
<el-table-column label="动作" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
<el-table-column label="数量 / 已成" width="120" align="right"><template #default="s"><span class="mono">{{ s.row.qty }} / {{ s.row.exec_qty||0 }}</span></template></el-table-column>
<el-table-column label="状态" width="120"><template #default="s"><span class="chip ok"><span class="dot"></span>{{ tx('insStatus', s.row.status) }}</span></template></el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane name="end">
<template #label>已了结 <span class="tabn">{{ insEnd.length }}</span></template>
<el-table :data="insEnd" size="small" max-height="260" empty-text="今天没有了结的单">
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
<el-table-column label="动作" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
<el-table-column label="结束方式" min-width="150"><template #default="s"><span class="chip warn"><span class="dot"></span>{{ tx('insStatus', s.row.status) }}</span></template></el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane name="deny">
<template #label>今日否决 <span class="tabn">{{ denyToday.length }}</span></template>
<div class="sub2" style="margin:8px 2px 4px">今天考察过、但没建仓的票,连原因一起摆出来。</div>
<el-table :data="denyToday" size="small" max-height="320" empty-text="今天没有被否决的候选">
<el-table-column label="股票" width="180"><template #default="s"><b>{{ s.row.name }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
<el-table-column label="为什么没建仓" min-width="440"><template #default="s"><div class="why" style="margin:0">{{ s.row.why || '—' }}</div></template></el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<h3 style="margin-top:14px;cursor:pointer" @click="showLedger=!showLedger">今天发生了什么 · {{ ledger.length }} 条 <span class="muted" style="font-size:12px;font-weight:normal">{{ showLedger?'▲ 收起':'▼ 展开' }}</span></h3>
<template v-if="showLedger">
<div class="muted" v-if="!ledger.length">今天还没有记录。</div>
<div v-for="(l,i) in ledger" :key="i" class="feed-row">
<span class="muted mono">{{ (l.decided_at||'').slice(11,16) }}</span>
<b> {{ nm(l.ts_code) }}</b>{{ tx('arbiter', l.arbiter) }}{{ tx('verdict', l.verdict) }} {{ tx('action', l.action) }}<span v-if="l.reason" class="muted"> —— {{ l.reason }}</span>
</div>
</template>
</div>
<!-- 7) 上游信号 (只读参考) -->
<div class="panel tblk" id="t-sig">
<h3 style="cursor:pointer" @click="showUpstream=!showUpstream">上游信号 <span class="muted" style="font-weight:400">· 只读参考,不进下单逻辑</span> <span class="muted" style="font-size:12px;font-weight:normal">{{ showUpstream?'▲ 收起':'▼ 展开' }}</span></h3> <h3 style="cursor:pointer" @click="showUpstream=!showUpstream">上游信号 <span class="muted" style="font-weight:400">· 只读参考,不进下单逻辑</span> <span class="muted" style="font-size:12px;font-weight:normal">{{ showUpstream?'▲ 收起':'▼ 展开' }}</span></h3>
<template v-if="showUpstream"> <template v-if="showUpstream">
<div class="muted" style="margin:-4px 0 12px" v-if="upstream.at">最近取数 {{ (upstream.at||'').slice(11,19) }} · 来源:决策系统 / 盘中择时</div> <div class="muted" style="margin:-4px 0 12px" v-if="upstream.at">最近取数 {{ (upstream.at||'').slice(11,19) }} · 来源:决策系统 / 盘中择时</div>
@ -696,59 +780,6 @@ pre.json{background:var(--surface-2);border:1px solid var(--hair);border-radius:
</div> </div>
</template> </template>
</div> </div>
</div></div>
<!-- 4) 今日在办与动向 -->
<div class="panel tblk">
<h3>今日在办</h3>
<div class="sub2">今天下出去的买卖单,按状态分三档:还在跑的、已成交的、已经结束的。</div>
<el-tabs v-model="insTab">
<el-tab-pane name="live">
<template #label>在办中 <span class="tabn">{{ insLive.length }}</span></template>
<el-table :data="insLive" size="small" max-height="260" empty-text="没有在办的买卖">
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
<el-table-column label="动作" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
<el-table-column label="数量 / 已成" width="120" align="right"><template #default="s"><span class="mono">{{ s.row.qty }} / {{ s.row.exec_qty||0 }}</span></template></el-table-column>
<el-table-column label="限价" width="82" align="right"><template #default="s"><span class="mono">{{ s.row.limit_price||'—' }}</span></template></el-table-column>
<el-table-column label="状态" width="118"><template #default="s"><span class="chip idle"><span class="dot"></span>{{ tx('insStatus', s.row.status) }}</span></template></el-table-column>
<el-table-column label="操作" width="76"><template #default="s"><button class="tbtn sm" @click="cancelIns(s.row.instruction_id)">撤单</button></template></el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane name="done">
<template #label>今日成交 <span class="tabn">{{ insDone.length }}</span></template>
<el-table :data="insDone" size="small" max-height="260" empty-text="今天还没有成交">
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
<el-table-column label="动作" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
<el-table-column label="数量 / 已成" width="120" align="right"><template #default="s"><span class="mono">{{ s.row.qty }} / {{ s.row.exec_qty||0 }}</span></template></el-table-column>
<el-table-column label="状态" width="120"><template #default="s"><span class="chip ok"><span class="dot"></span>{{ tx('insStatus', s.row.status) }}</span></template></el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane name="end">
<template #label>已了结 <span class="tabn">{{ insEnd.length }}</span></template>
<el-table :data="insEnd" size="small" max-height="260" empty-text="今天没有了结的单">
<el-table-column label="股票" min-width="150"><template #default="s"><b>{{ nm(s.row.ts_code) }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
<el-table-column label="动作" width="150"><template #default="s">{{ tx('action', s.row.action) }} · {{ tx('side', s.row.side) }}</template></el-table-column>
<el-table-column label="结束方式" min-width="150"><template #default="s"><span class="chip warn"><span class="dot"></span>{{ tx('insStatus', s.row.status) }}</span></template></el-table-column>
</el-table>
</el-tab-pane>
<el-tab-pane name="deny">
<template #label>今日否决 <span class="tabn">{{ denyToday.length }}</span></template>
<div class="sub2" style="margin:8px 2px 4px">今天考察过、但没建仓的票,连原因一起摆出来。</div>
<el-table :data="denyToday" size="small" max-height="320" empty-text="今天没有被否决的候选">
<el-table-column label="股票" width="180"><template #default="s"><b>{{ s.row.name }}</b> <span class="muted mono">{{ s.row.ts_code }}</span></template></el-table-column>
<el-table-column label="为什么没建仓" min-width="440"><template #default="s"><div class="why" style="margin:0">{{ s.row.why || '—' }}</div></template></el-table-column>
</el-table>
</el-tab-pane>
</el-tabs>
<h3 style="margin-top:14px;cursor:pointer" @click="showLedger=!showLedger">今天发生了什么 · {{ ledger.length }} 条 <span class="muted" style="font-size:12px;font-weight:normal">{{ showLedger?'▲ 收起':'▼ 展开' }}</span></h3>
<template v-if="showLedger">
<div class="muted" v-if="!ledger.length">今天还没有记录。</div>
<div v-for="(l,i) in ledger" :key="i" class="feed-row">
<span class="muted mono">{{ (l.decided_at||'').slice(11,16) }}</span>
<b> {{ nm(l.ts_code) }}</b>{{ tx('arbiter', l.arbiter) }}{{ tx('verdict', l.verdict) }} {{ tx('action', l.action) }}<span v-if="l.reason" class="muted"> —— {{ l.reason }}</span>
</div>
</template>
</div>
<!-- 挂交易方案 对话框 (非盲填: 现价/持仓/可卖/浮盈都摆出来) --> <!-- 挂交易方案 对话框 (非盲填: 现价/持仓/可卖/浮盈都摆出来) -->
@ -1648,7 +1679,7 @@ createApp({
const lastRefresh = ref(''); const lastRefresh = ref('');
const autoRefresh = ref(true); const autoRefresh = ref(true);
const upstream = ref({ sources: {} }); const upstream = ref({ sources: {} });
const showUpstream = ref(true); // 双栏后信号在右栏常驻显示, 点标题可折叠 (2026-08-26) const showUpstream = ref(false); // 信号中心默认收起; 常驻速览带在持仓下方 (2026-08-26)
const showAlerts = 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 alertShowMap = reactive({ money_flow:true, capital:true, qrs:true, volume:true, multi:true, price_notice:false, buy_emitted:false, other:true });
// 级别筛选记忆: 存 sessionStorage —— 普通刷新保留, 关标签页/新开标签=回默认。默认关 普通/提示。 // 级别筛选记忆: 存 sessionStorage —— 普通刷新保留, 关标签页/新开标签=回默认。默认关 普通/提示。
@ -2207,6 +2238,46 @@ createApp({
const alertLatest = computed(() => alertStripItems.value[0] || {}); const alertLatest = computed(() => alertStripItems.value[0] || {});
const alertStripCount = computed(() => alertStripItems.value.length); const alertStripCount = computed(() => alertStripItems.value.length);
const alertTotalCount = computed(() => alertItems.value.length); const alertTotalCount = computed(() => alertItems.value.length);
// ── 信号速览 (2026-08-26 重设计): 交易员要随时看到的不是整个信号中心, 而是
// 「持仓票的信号 + 全场重要/警告级」。持仓票置顶; 决策系统买卖/风控卖出/择时
// 入场三路独立信号也并进来, 免得只看告警漏了真正会动手的那几条。 ──
const code6 = s => (String(s || '').match(/\d{6}/) || [''])[0];
const heldCodeSet = computed(() => new Set(heldPositions.value.map(p => code6(p.ts_code))));
const sigBrief = computed(() => {
const held = heldCodeSet.value;
const out = [];
for (const a of alertItems.value) {
const isHeld = held.has(code6(a.ts_code));
if (!isHeld && !['DANGER', 'WARNING'].includes(normLv(a.level))) continue;
out.push({ ts_code: a.ts_code, time: a.time || '', cat: a.cat_label || a.cat,
direction: a.direction, level: a.level, held: isHeld, stale: a.stale });
}
const s = SIG.value;
for (const r of (s.sell || [])) out.push({
ts_code: r.ts_code, time: r.time || '', cat: '风控卖出',
direction: '卖出', level: 'DANGER', held: held.has(code6(r.ts_code)) });
for (const r of (s.decision || [])) out.push({
ts_code: r.ts_code, time: r.time || '', cat: '决策系统',
direction: r.action === 'SELL' ? '卖出' : '买入', level: 'WARNING',
held: held.has(code6(r.ts_code)) });
for (const r of (s.timing || [])) out.push({
ts_code: r.ts_code, time: r.time || '', cat: '择时入场',
direction: '买入', level: 'INFO', held: held.has(code6(r.ts_code)) });
out.sort((a, b) => (b.held - a.held) || String(b.time).localeCompare(String(a.time)));
return out;
});
const sigBriefHeldCount = computed(() => sigBrief.value.filter(x => x.held).length);
const heldSigMap = computed(() => {
const m = {};
for (const x of sigBrief.value) {
if (!x.held) continue;
const k = code6(x.ts_code);
(m[k] || (m[k] = [])).push(x);
}
return m;
});
function heldSigOf(code) { return heldSigMap.value[code6(code)] || []; }
function openSigCenter() { showUpstream.value = true; scrollTo('t-sig'); }
const alertLevelClass = computed(() => { const alertLevelClass = computed(() => {
const it = alertStripItems.value; const it = alertStripItems.value;
if (it.some(x => normLv(x.level) === 'DANGER')) return 'strong'; if (it.some(x => normLv(x.level) === 'DANGER')) return 'strong';
@ -2590,6 +2661,7 @@ createApp({
showAlerts, alertItems, alertLatest, alertLevelClass, alertGroups, alertStripItems, showAlerts, alertItems, alertLatest, alertLevelClass, alertGroups, alertStripItems,
alertStripCount, alertTotalCount, catShown, toggleCat, ageText, alertRowClass, alertStripCount, alertTotalCount, catShown, toggleCat, ageText, alertRowClass,
alertItemsF, levelShown, toggleLevel, sellOpen, toggleSell, levelCount, ALERT_LEVELS, lvLabel, lvClass, alertGridFixed, passLevel, flowText, flowClass, alertItemsF, levelShown, toggleLevel, sellOpen, toggleSell, levelCount, ALERT_LEVELS, lvLabel, lvClass, alertGridFixed, passLevel, flowText, flowClass,
sigBrief, sigBriefHeldCount, heldSigOf, openSigCenter,
SIG, moneyCn, zBand, zBars, dirUp, eventsQuiet }; SIG, moneyCn, zBand, zBars, dirUp, eventsQuiet };
} }
}).use(ElementPlus).mount('#app'); }).use(ElementPlus).mount('#app');