refactor(selection): 统一信号与股票处理逻辑,重构相关数据结构与接口,更新前端展示以支持股票信息

This commit is contained in:
yuxuanhui
2026-08-28 11:39:05 +08:00
parent 3da992700b
commit ad9545ef55
12 changed files with 408 additions and 189 deletions
@@ -45,22 +45,31 @@ const selectedResult: SelectionResults = {
run_id: "run-1",
selected_stock_count: 1,
signal_count: 2,
signals_total: 2,
signals: [
stocks_total: 1,
stocks: [
{
category: "zhixing_b1_original_b1",
close: 10.5,
details: { j: 12, rsi: 20 },
name: "平安银行",
strategy: "zhixing_b1",
target_trade_date: "2026-08-08",
ts_code: "000001.SZ",
},
{
category: "zhixing_b1_pullback_white",
close: 10.5,
details: { j: 12 },
name: "平安银行",
signals: [
{
category: "zhixing_b1_original_b1",
close: 10.5,
details: { j: 12, rsi: 20 },
name: "平安银行",
strategy: "zhixing_b1",
target_trade_date: "2026-08-08",
ts_code: "000001.SZ",
},
{
category: "zhixing_b1_pullback_white",
close: 10.5,
details: { j: 13 },
name: "平安银行",
strategy: "zhixing_b1",
target_trade_date: "2026-08-08",
ts_code: "000001.SZ",
},
],
strategy: "zhixing_b1",
target_trade_date: "2026-08-08",
ts_code: "000001.SZ",
@@ -113,14 +122,15 @@ describe("SelectionResultsPage", () => {
).not.toBeInTheDocument()
expect(within(table).getByText("原始 B1")).toBeInTheDocument()
expect(within(table).getByText("回踩白线 B")).toBeInTheDocument()
expect(within(table).getAllByText("12")).toHaveLength(2)
expect(within(table).getAllByText("平安银行")).toHaveLength(2)
expect(within(table).getByText("12")).toBeInTheDocument()
expect(within(table).getByText("13")).toBeInTheDocument()
expect(within(table).getByText("平安银行")).toBeInTheDocument()
const detailPanel = screen.getByRole("complementary", {
name: "当前股票详情",
})
expect(within(detailPanel).getByText("关键指标")).toBeInTheDocument()
expect(within(detailPanel).getByText("J 值")).toBeInTheDocument()
expect(within(detailPanel).getAllByText("J 值")).toHaveLength(2)
expect(within(detailPanel).getByText("RSI")).toBeInTheDocument()
})
@@ -220,8 +230,8 @@ describe("SelectionResultsPage", () => {
data: {
...selectedResult,
signal_count: 0,
signals: [],
signals_total: 0,
stocks: [],
stocks_total: 0,
},
isError: false,
isPending: false,
@@ -260,7 +270,7 @@ describe("SelectionResultsPage", () => {
},
],
signal_count: 0,
signals: [],
stocks: [],
status: "failed",
},
isError: false,
@@ -293,6 +303,9 @@ describe("SelectionResultsPage", () => {
fireEvent.click(categoryTrigger)
const oversoldOption = await screen.findByRole("option", { name: "超卖类" })
const content = oversoldOption.closest('[data-slot="select-content"]')
expect(content).toHaveAttribute("data-align-trigger", "false")
expect(content?.parentElement).toHaveAttribute("data-align", "start")
fireEvent.pointerDown(oversoldOption, { pointerType: "mouse" })
fireEvent.click(oversoldOption)
@@ -316,18 +329,26 @@ describe("SelectionResultsPage", () => {
})
it("filters signals, updates the detail panel, and expands mobile details", () => {
const extraSignal = {
...selectedResult.signals[0],
category: "zhixing_b1_oversold_volume",
details: { macd: 3.2 },
const extraStock = {
...selectedResult.stocks[0],
name: "招商银行",
signals: [
{
...selectedResult.stocks[0].signals[0],
category: "zhixing_b1_oversold_volume",
details: { macd: 3.2 },
name: "招商银行",
ts_code: "600036.SH",
},
],
ts_code: "600036.SH",
}
useSelectionResults.mockReturnValue({
data: {
...selectedResult,
signal_count: 3,
signals: [...selectedResult.signals, extraSignal],
stocks: [...selectedResult.stocks, extraStock],
stocks_total: 2,
},
isError: false,
isPending: false,
@@ -335,7 +356,7 @@ describe("SelectionResultsPage", () => {
render(<SelectionResultsPage />)
fireEvent.change(screen.getByLabelText("搜索命中信号"), {
fireEvent.change(screen.getByLabelText("搜索命中股票"), {
target: { value: "招商" },
})
const table = screen.getByRole("table")
@@ -347,7 +368,7 @@ describe("SelectionResultsPage", () => {
).getByText("招商银行"),
).toBeInTheDocument()
const expandButton = screen.getAllByRole("button", { name: "查看详情" })[2]
const expandButton = screen.getAllByRole("button", { name: "查看详情" })[1]
fireEvent.click(expandButton)
expect(expandButton).toHaveAttribute("aria-expanded", "true")
expect(screen.getAllByText("MACD").length).toBeGreaterThan(1)
@@ -355,17 +376,25 @@ describe("SelectionResultsPage", () => {
})
it("navigates to another page when the result list is paginated", () => {
const signals = Array.from({ length: 6 }, (_, index) => ({
...selectedResult.signals[0],
const stocks = Array.from({ length: 6 }, (_, index) => ({
...selectedResult.stocks[0],
name: `股票${index + 1}`,
signals: [
{
...selectedResult.stocks[0].signals[0],
name: `股票${index + 1}`,
ts_code: `00000${index + 1}.SZ`,
},
],
ts_code: `00000${index + 1}.SZ`,
}))
useSelectionResults.mockReturnValue({
data: {
...selectedResult,
signal_count: signals.length,
signals,
signals_total: signals.length,
selected_stock_count: stocks.length,
signal_count: stocks.length,
stocks,
stocks_total: stocks.length,
},
isError: false,
isPending: false,
@@ -422,7 +451,7 @@ describe("SelectionResultsPage", () => {
],
selected_stock_count: 0,
signal_count: 0,
signals: [],
stocks: [],
status: "failed",
},
isError: false,
@@ -442,7 +471,7 @@ describe("SelectionResultsPage", () => {
data: {
...selectedResult,
run_id: null,
signals: [],
stocks: [],
status: "no_data",
target_trade_date: "2026-08-08",
},
@@ -490,12 +519,12 @@ describe("SelectionResultsPage", () => {
it("continues polling a persisted running run after page reload", () => {
useSelectionResults.mockReturnValue({
data: { ...selectedResult, signals: [], status: "running" },
data: { ...selectedResult, stocks: [], status: "running" },
isError: false,
isPending: false,
})
useSelectionRun.mockReturnValue({
data: { ...selectedResult, signals: [], status: "running" },
data: { ...selectedResult, stocks: [], status: "running" },
isError: false,
isPending: false,
})