refactor(selection): 统一信号与股票处理逻辑,重构相关数据结构与接口,更新前端展示以支持股票信息
This commit is contained in:
@@ -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,
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user