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;
}