From cff3b0752782efe34788ceb83b5ddd54640f6263 Mon Sep 17 00:00:00 2001 From: shy3130 <415333856@qq.com> Date: Sun, 30 Aug 2026 19:05:30 +0800 Subject: [PATCH] =?UTF-8?q?feat(recap):=20=E9=BE=99=E8=99=8E=E6=A6=9C?= =?UTF-8?q?=E5=8D=A1=E7=89=87=E4=BF=A1=E6=81=AF=E5=AF=86=E5=BA=A6=E4=B8=8E?= =?UTF-8?q?=E5=8F=AF=E8=AF=BB=E6=80=A7=E5=A2=9E=E5=BC=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 摘要行扩为净买/净卖/机构净买/机构净卖各 Top5, 四行药丸左缘固定槽对齐 - fmtVolume 负数按绝对值分级补万/亿单位 (净卖席位原显示裸数字) - 个股名称后复用 boardTag 加创/科/北板块徽章 (表格/药丸/游资chips) - 三榜表格数值列头可点击排序 (升降切换, 空值恒垫底, 切tab重置) - 游资席位金额去掉「净买」前缀 - AI 复盘提示词龙虎榜摘要四段统一 Top5 --- backend/app/services/dragon_tiger.py | 6 +- frontend/src/lib/format.ts | 6 +- frontend/src/pages/Review.tsx | 108 ++++++++++++++++++++++----- 3 files changed, 95 insertions(+), 25 deletions(-) diff --git a/backend/app/services/dragon_tiger.py b/backend/app/services/dragon_tiger.py index 0cfba58..1d919f8 100644 --- a/backend/app/services/dragon_tiger.py +++ b/backend/app/services/dragon_tiger.py @@ -208,7 +208,7 @@ def build_recap_context(data_dir: Path) -> str: top_sell = sorted( [i for i in items if (i.get("net_value") or 0) < 0], key=lambda x: x.get("net_value") or 0, - )[:3] + )[:5] if top_sell: lines.append("净卖出居前: " + "; ".join( f"{i.get('name')} 净卖{abs(float(i.get('net_value') or 0))/1e8:.2f}亿" @@ -216,11 +216,11 @@ def build_recap_context(data_dir: Path) -> str: if org_items: lines.append("机构净买居前: " + "; ".join( f"{i.get('name')} 机构净买{float(i.get('org_net_value') or 0)/1e8:.2f}亿" - for i in sorted(org_items, key=lambda x: x.get("org_net_value") or 0, reverse=True)[:3])) + for i in sorted(org_items, key=lambda x: x.get("org_net_value") or 0, reverse=True)[:5])) if hm_items: lines.append("活跃游资: " + "; ".join( f"{h.get('name')} 净买{float(h.get('buying') or 0)/1e8:.2f}亿" - for h in sorted(hm_items, key=lambda x: x.get('buying') or 0, reverse=True)[:3])) + for h in sorted(hm_items, key=lambda x: x.get('buying') or 0, reverse=True)[:5])) return "\n".join(lines) except Exception as e: # noqa: BLE001 — 摘要失败不影响复盘主流程 logger.debug("龙虎榜复盘摘要构建失败: %s", e) diff --git a/frontend/src/lib/format.ts b/frontend/src/lib/format.ts index eb18066..67350bd 100644 --- a/frontend/src/lib/format.ts +++ b/frontend/src/lib/format.ts @@ -13,8 +13,10 @@ export function fmtPct(v: number | null | undefined, digits = 2): string { export function fmtVolume(v: number | null | undefined): string { if (v == null || Number.isNaN(v)) return '—' - if (v >= 1e8) return `${(v / 1e8).toFixed(2)}亿` - if (v >= 1e4) return `${(v / 1e4).toFixed(2)}万` + const sign = v < 0 ? '-' : '' + const a = Math.abs(v) + if (a >= 1e8) return `${sign}${(a / 1e8).toFixed(2)}亿` + if (a >= 1e4) return `${sign}${(a / 1e4).toFixed(2)}万` return v.toFixed(0) } diff --git a/frontend/src/pages/Review.tsx b/frontend/src/pages/Review.tsx index 3619ce8..53e894d 100644 --- a/frontend/src/pages/Review.tsx +++ b/frontend/src/pages/Review.tsx @@ -12,7 +12,7 @@ import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query' import { motion, AnimatePresence } from 'framer-motion' import { BookOpenCheck, RefreshCw, Sparkles, Trash2, History, ChevronRight, AlertTriangle, - Database, Wand2, Copy, Download, Clock, X, Check, Trophy, ChevronDown, + Database, Wand2, Copy, Download, Clock, X, Check, Trophy, ChevronDown, ChevronUp, } from 'lucide-react' import { api, type OverviewMarket, type AiReviewReport, type DragonTigerStockItem } from '@/lib/api' @@ -20,6 +20,7 @@ import { QK } from '@/lib/queryKeys' import { cn } from '@/lib/cn' import { fmtPct, fmtVolume, priceColorClass } from '@/lib/format' import { StockPreviewDialog } from '@/components/StockPreviewDialog' +import { boardTag } from '@/components/stock-table/primitives' import { fmtBigNum } from '@/lib/format' import { PageHeader } from '@/components/PageHeader' import { MarkdownRenderer } from '@/components/financials/MarkdownRenderer' @@ -870,8 +871,9 @@ const _DT_TABS: { key: _DtTabKey; label: string }[] = [ { key: 'hot_money', label: '游资' }, ] -function _dtSorted(items: DragonTigerStockItem[], key: 'net_value' | 'org_net_value'): DragonTigerStockItem[] { - return [...items].sort((a, b) => (b[key] ?? -Infinity) - (a[key] ?? -Infinity)) +function _dtSorted(items: DragonTigerStockItem[], key: 'net_value' | 'org_net_value', asc = false): DragonTigerStockItem[] { + const s = [...items].sort((a, b) => (b[key] ?? -Infinity) - (a[key] ?? -Infinity)) + return asc ? s.reverse() : s } /** 排名序号色: 前三用暖色系奖牌感, 其余弱化 */ @@ -901,6 +903,11 @@ function _DtPill({ item, idx, value, onOpenStock }: { {item.name ?? item.thscode} + {(() => { const b = boardTag(item.thscode); return b && ( + + {b.label} + + ) })()} {fmtVolume(value ?? null)} @@ -909,7 +916,7 @@ function _DtPill({ item, idx, value, onOpenStock }: { } function _DtSummaryRow({ label, items, pick, onOpenStock }: { - label: string + label?: string items: DragonTigerStockItem[] pick: (i: DragonTigerStockItem) => number | null | undefined onOpenStock: (s: string) => void @@ -917,40 +924,86 @@ function _DtSummaryRow({ label, items, pick, onOpenStock }: { if (!items.length) return null return (