feat(web): 选股列表改为滚动加载并精简操作区
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
import {
|
||||
useInfiniteQuery,
|
||||
useMutation,
|
||||
useQuery,
|
||||
useQueryClient,
|
||||
type InfiniteData,
|
||||
type QueryClient,
|
||||
} from "@tanstack/react-query"
|
||||
import { useEffect } from "react"
|
||||
@@ -12,12 +14,20 @@ import {
|
||||
getSelectionRun,
|
||||
triggerSelectionRun,
|
||||
} from "./selection.api"
|
||||
import type {
|
||||
SelectionResultsQuery,
|
||||
SelectionRunRequest,
|
||||
SelectionStrategy,
|
||||
import {
|
||||
selectionResultPageSize,
|
||||
type SelectionResults,
|
||||
type SelectionResultsQuery,
|
||||
type SelectionRunRequest,
|
||||
type SelectionStrategy,
|
||||
} from "./selection.types"
|
||||
|
||||
export type SelectionResultsListQuery = Omit<SelectionResultsQuery, "page">
|
||||
|
||||
const defaultListQuery: SelectionResultsListQuery = {
|
||||
pageSize: selectionResultPageSize,
|
||||
}
|
||||
|
||||
export const selectionChartQueryKey = (
|
||||
tsCode?: string,
|
||||
targetTradeDate?: string,
|
||||
@@ -27,14 +37,13 @@ export const selectionChartQueryKey = (
|
||||
export const selectionResultsQueryKey = (
|
||||
strategy: SelectionStrategy,
|
||||
targetTradeDate?: string,
|
||||
query: SelectionResultsQuery = { page: 1, pageSize: 10 },
|
||||
query: SelectionResultsListQuery = defaultListQuery,
|
||||
) =>
|
||||
[
|
||||
"selection",
|
||||
"results",
|
||||
strategy,
|
||||
targetTradeDate,
|
||||
query.page,
|
||||
query.pageSize,
|
||||
query.search ?? "",
|
||||
query.category ?? "all",
|
||||
@@ -43,27 +52,50 @@ export const selectionResultsQueryKey = (
|
||||
|
||||
export const selectionRunQueryKey = (
|
||||
runId: string,
|
||||
query: SelectionResultsQuery = { page: 1, pageSize: 10 },
|
||||
query: SelectionResultsListQuery = defaultListQuery,
|
||||
) =>
|
||||
[
|
||||
"selection",
|
||||
"runs",
|
||||
runId,
|
||||
query.page,
|
||||
query.pageSize,
|
||||
query.search ?? "",
|
||||
query.category ?? "all",
|
||||
query.sort ?? "code",
|
||||
] as const
|
||||
|
||||
export function flattenSelectionResults(
|
||||
data: InfiniteData<SelectionResults> | undefined,
|
||||
): SelectionResults | undefined {
|
||||
const firstPage = data?.pages[0]
|
||||
if (!firstPage) return undefined
|
||||
return {
|
||||
...firstPage,
|
||||
stocks: data.pages.flatMap((page) => page.stocks),
|
||||
}
|
||||
}
|
||||
|
||||
export function useSelectionResults(
|
||||
strategy: SelectionStrategy,
|
||||
targetTradeDate?: string,
|
||||
query: SelectionResultsQuery = { page: 1, pageSize: 10 },
|
||||
query: SelectionResultsListQuery = defaultListQuery,
|
||||
) {
|
||||
return useQuery({
|
||||
queryFn: ({ signal }) =>
|
||||
getSelectionResults(strategy, targetTradeDate, query, signal),
|
||||
return useInfiniteQuery<
|
||||
SelectionResults,
|
||||
Error,
|
||||
InfiniteData<SelectionResults>,
|
||||
ReturnType<typeof selectionResultsQueryKey>,
|
||||
number
|
||||
>({
|
||||
getNextPageParam: nextSelectionPageParam,
|
||||
initialPageParam: 1,
|
||||
queryFn: ({ pageParam, signal }) =>
|
||||
getSelectionResults(
|
||||
strategy,
|
||||
targetTradeDate,
|
||||
{ ...query, page: pageParam },
|
||||
signal,
|
||||
),
|
||||
queryKey: selectionResultsQueryKey(strategy, targetTradeDate, query),
|
||||
})
|
||||
}
|
||||
@@ -79,19 +111,28 @@ export function useSelectionChart(tsCode?: string, targetTradeDate?: string) {
|
||||
|
||||
export function useSelectionRun(
|
||||
runId: string | null,
|
||||
resultQuery: SelectionResultsQuery = { page: 1, pageSize: 10 },
|
||||
resultQuery: SelectionResultsListQuery = defaultListQuery,
|
||||
) {
|
||||
const queryClient = useQueryClient()
|
||||
const query = useQuery({
|
||||
const query = useInfiniteQuery<
|
||||
SelectionResults,
|
||||
Error,
|
||||
InfiniteData<SelectionResults>,
|
||||
ReturnType<typeof selectionRunQueryKey>,
|
||||
number
|
||||
>({
|
||||
enabled: Boolean(runId),
|
||||
queryFn: ({ signal }) => getSelectionRun(runId ?? "", resultQuery, signal),
|
||||
getNextPageParam: nextSelectionPageParam,
|
||||
initialPageParam: 1,
|
||||
queryFn: ({ pageParam, signal }) =>
|
||||
getSelectionRun(runId ?? "", { ...resultQuery, page: pageParam }, signal),
|
||||
queryKey: selectionRunQueryKey(runId ?? "none", resultQuery),
|
||||
refetchInterval: (query) =>
|
||||
query.state.data?.status === "running" ? 1500 : false,
|
||||
refetchInterval: (currentQuery) =>
|
||||
currentQuery.state.data?.pages[0]?.status === "running" ? 1500 : false,
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
const result = query.data
|
||||
const result = query.data?.pages[0]
|
||||
if (!runId || !result || result.status === "running") return
|
||||
|
||||
void invalidateSelectionResults(queryClient, result.strategy)
|
||||
@@ -125,3 +166,26 @@ export function invalidateSelectionResults(
|
||||
queryKey: ["selection", "results", strategy],
|
||||
})
|
||||
}
|
||||
|
||||
function nextSelectionPageParam(
|
||||
lastPage: SelectionResults,
|
||||
pages: SelectionResults[],
|
||||
lastPageParam: number,
|
||||
) {
|
||||
const loadedStockCount = pages.reduce(
|
||||
(count, page) => count + page.stocks.length,
|
||||
0,
|
||||
)
|
||||
const total = lastPage.stocks_total ?? lastPage.selected_stock_count
|
||||
if (
|
||||
(lastPage.status !== "success" && lastPage.status !== "partial_success") ||
|
||||
lastPage.stocks.length === 0 ||
|
||||
lastPage.page !== lastPageParam ||
|
||||
loadedStockCount >= total
|
||||
) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
const nextPageParam = lastPageParam + 1
|
||||
return Number.isSafeInteger(nextPageParam) ? nextPageParam : undefined
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user