diff --git a/src/App.vue b/src/App.vue index d2acd5f..855804f 100644 --- a/src/App.vue +++ b/src/App.vue @@ -41,13 +41,14 @@ + + diff --git a/src/views/stock/LimitBoard.vue b/src/views/stock/LimitBoard.vue index bd25d2b..04ac5f7 100644 --- a/src/views/stock/LimitBoard.vue +++ b/src/views/stock/LimitBoard.vue @@ -90,6 +90,7 @@ +
@@ -147,6 +148,14 @@ 开盘 {{ formatPrice(stock.open) }} 前收 {{ formatPrice(stock.previous_close) }}
+
+ 开盘 {{ formatPrice(stock.open) }} + 前收 {{ formatPrice(stock.previous_close) }} + 阈值 {{ formatPrice(stock.open_threshold_price) }} +
@@ -180,6 +189,7 @@ import { fetchTradeCalendar } from "@/composables/useTradeCalendar"; import { getKlineData, getLimitBoardOpenLowerGroups, + getLimitBoardOpenThresholdGroups, getLimitBoardOverview, } from "@/api/stock"; @@ -220,6 +230,17 @@ const openLowerDetail = ref({ }, groups: [], }); +const openThresholdDetail = ref({ + trade_date: "", + previous_trade_date: "", + summary: { + limit_count: 0, + open_within_threshold_count: 0, + open_at_or_above_threshold_count: 0, + threshold_ratio: 1.01, + }, + groups: [], +}); const dialogVisible = ref(false); const dialogTitle = ref(""); @@ -292,6 +313,8 @@ const displayDetailGroups = computed(() => { if (activeDetailTab.value === "sector") return sectorDetailGroups.value; if (activeDetailTab.value === "openLower") return openLowerDisplayGroups.value; + if (activeDetailTab.value === "openThreshold") + return openThresholdDetail.value.groups; return detailGroups.value.map((group) => ({ ...group, key: `board-${group.board_count ?? group.label}`, @@ -313,6 +336,7 @@ const detailModeDescription = computed( board: "按连板高度从高到低展示当日涨停股票。", sector: "按所属行业或板块汇总展示当日涨停股票。", openLower: `仅统计当日涨停且前一交易日也涨停的股票,首二一一红色优先,首板进二板黄色随后,前一交易日 ${openLowerDetail.value.previous_trade_date || "--"}。`, + openThreshold: `仅统计当日涨停且前一交易日也涨停的股票,并按开盘价是否低于前收 101% 分组,前一交易日 ${openThresholdDetail.value.previous_trade_date || "--"}。`, })[activeDetailTab.value], ); const openLowerTabLabel = computed(() => { @@ -379,9 +403,14 @@ const disableDates = (time) => { }; const fetchOverviewPage = async (tradeDate) => { - const [currentOverview, currentOpenLowerGroups] = await Promise.all([ + const [ + currentOverview, + currentOpenLowerGroups, + currentOpenThresholdGroups, + ] = await Promise.all([ getLimitBoardOverview({ trade_date: tradeDate }), getLimitBoardOpenLowerGroups({ trade_date: tradeDate }), + getLimitBoardOpenThresholdGroups({ trade_date: tradeDate }), ]); overview.value = normalizeOverview(currentOverview, tradeDate); @@ -389,6 +418,10 @@ const fetchOverviewPage = async (tradeDate) => { currentOpenLowerGroups, tradeDate, ); + openThresholdDetail.value = normalizeOpenThresholdGroups( + currentOpenThresholdGroups, + tradeDate, + ); strengthSeries.value = overview.value.recent_max_board_stats; detailTradeDate.value = overview.value.trade_date; detailGroups.value = overview.value.groups; @@ -402,10 +435,12 @@ const fetchDetailGroups = async (tradeDate) => { detailLoading.value = true; try { - const [detailOverview, detailOpenLowerGroups] = await Promise.all([ - getLimitBoardOverview({ trade_date: tradeDate }), - getLimitBoardOpenLowerGroups({ trade_date: tradeDate }), - ]); + const [detailOverview, detailOpenLowerGroups, detailOpenThresholdGroups] = + await Promise.all([ + getLimitBoardOverview({ trade_date: tradeDate }), + getLimitBoardOpenLowerGroups({ trade_date: tradeDate }), + getLimitBoardOpenThresholdGroups({ trade_date: tradeDate }), + ]); const normalized = normalizeOverview(detailOverview, tradeDate); detailTradeDate.value = normalized.trade_date; detailGroups.value = normalized.groups; @@ -413,6 +448,10 @@ const fetchDetailGroups = async (tradeDate) => { detailOpenLowerGroups, tradeDate, ); + openThresholdDetail.value = normalizeOpenThresholdGroups( + detailOpenThresholdGroups, + tradeDate, + ); } catch (err) { console.error("刷新涨停分组明细失败", err); } finally { @@ -461,6 +500,28 @@ const normalizeOpenLowerGroups = (data, fallbackTradeDate = "") => ({ : [], }); +const normalizeOpenThresholdGroups = (data, fallbackTradeDate = "") => ({ + trade_date: data?.trade_date || fallbackTradeDate, + previous_trade_date: data?.previous_trade_date || "", + summary: { + limit_count: data?.summary?.limit_count || 0, + open_within_threshold_count: + data?.summary?.open_within_threshold_count || 0, + open_at_or_above_threshold_count: + data?.summary?.open_at_or_above_threshold_count || 0, + threshold_ratio: Number(data?.summary?.threshold_ratio || 1.01), + }, + groups: Array.isArray(data?.groups) + ? data.groups.map((group) => ({ + key: `open-threshold-${group?.key || group?.label || "group"}`, + rawKey: group?.key || "", + label: group?.label || "--", + count: Number(group?.count || 0), + stocks: Array.isArray(group?.stocks) ? group.stocks : [], + })) + : [], +}); + const handleQuery = async () => { if (!selectedDate.value) return; @@ -815,7 +876,7 @@ const openKlineDialog = async (stock) => { }, ], dataZoom: [ - { type: "inside", xAxisIndex: [0, 1], start: 80, end: 100 }, + { type: "inside", xAxisIndex: [0, 1], start: 95, end: 100 }, { show: true, type: "slider", xAxisIndex: [0, 1], top: "90%" }, ], series: [ @@ -1152,7 +1213,8 @@ const openKlineDialog = async (stock) => { .stock-meta, .stock-metrics, .stock-limit-time, -.stock-open-compare { +.stock-open-compare, +.stock-open-threshold { color: #607289; font-size: 13px; } @@ -1166,7 +1228,8 @@ const openKlineDialog = async (stock) => { } .stock-limit-time, -.stock-open-compare { +.stock-open-compare, +.stock-open-threshold { display: flex; align-items: center; justify-content: space-between; @@ -1181,7 +1244,8 @@ const openKlineDialog = async (stock) => { color: #7a8aa1; } -.stock-open-compare span { +.stock-open-compare span, +.stock-open-threshold span { color: #526982; font-weight: 600; }