看股功能更新
This commit is contained in:
@@ -18,6 +18,9 @@ import type {
|
||||
SyncRequest,
|
||||
SyncResponse,
|
||||
Timeframe,
|
||||
TradesClearResponse,
|
||||
TradesImportResponse,
|
||||
UserTrade,
|
||||
} from './types';
|
||||
|
||||
// dev 用 Vite 代理(/api -> :8000);生产构建设 VITE_API_BASE 指向后端地址。
|
||||
@@ -37,8 +40,11 @@ async function readError(res: Response, fallback: string): Promise<string> {
|
||||
const body = await res.text();
|
||||
if (!body) return fallback;
|
||||
try {
|
||||
const data = JSON.parse(body) as { detail?: string };
|
||||
return data.detail || fallback;
|
||||
const data = JSON.parse(body) as { detail?: unknown };
|
||||
// FastAPI 校验类 422 的 detail 是对象数组,直接当字符串用会显示成 [object Object]
|
||||
if (typeof data.detail === 'string') return data.detail;
|
||||
if (data.detail != null) return JSON.stringify(data.detail);
|
||||
return fallback;
|
||||
} catch {
|
||||
return body;
|
||||
}
|
||||
@@ -49,7 +55,9 @@ async function apiFetch(path: string, init: RequestInit = {}): Promise<Response>
|
||||
...init,
|
||||
credentials: 'include',
|
||||
headers: {
|
||||
...(init.body ? { 'Content-Type': 'application/json' } : {}),
|
||||
// 仅 JSON(字符串 body)手工设 Content-Type;FormData 必须留给浏览器生成
|
||||
// multipart 边界,手工设置会导致后端解析失败 422
|
||||
...(typeof init.body === 'string' ? { 'Content-Type': 'application/json' } : {}),
|
||||
...init.headers,
|
||||
},
|
||||
});
|
||||
@@ -158,6 +166,8 @@ export async function getStocks(params: {
|
||||
industry?: string;
|
||||
area?: string;
|
||||
watched_only?: boolean;
|
||||
sort?: string;
|
||||
order?: 'asc' | 'desc';
|
||||
limit?: number;
|
||||
offset?: number;
|
||||
}): Promise<StockListResponse> {
|
||||
@@ -167,6 +177,8 @@ export async function getStocks(params: {
|
||||
if (params.industry) q.set('industry', params.industry);
|
||||
if (params.area) q.set('area', params.area);
|
||||
if (params.watched_only) q.set('watched_only', 'true');
|
||||
if (params.sort) q.set('sort', params.sort);
|
||||
if (params.order) q.set('order', params.order);
|
||||
q.set('limit', String(params.limit ?? 100));
|
||||
q.set('offset', String(params.offset ?? 0));
|
||||
const res = await apiFetch(`/api/stocks?${q.toString()}`);
|
||||
@@ -224,3 +236,27 @@ export async function deleteScreenerQuery(id: number): Promise<void> {
|
||||
const res = await apiFetch(`/api/screener/queries/${id}`, { method: 'DELETE' });
|
||||
if (!res.ok && res.status !== 401) throw new ApiError(`删除失败 (HTTP ${res.status})`, res.status);
|
||||
}
|
||||
|
||||
// ---------- 交割单(个人实盘买卖点) ----------
|
||||
export async function getTrades(tsCode?: string): Promise<UserTrade[]> {
|
||||
const q = tsCode ? `?ts_code=${encodeURIComponent(tsCode)}` : '';
|
||||
const res = await apiFetch(`/api/trades${q}`);
|
||||
if (!res.ok) throw new ApiError(await readError(res, `获取成交记录失败 (HTTP ${res.status})`), res.status);
|
||||
return (await res.json()) as UserTrade[];
|
||||
}
|
||||
|
||||
/** 上传交割单文件(CSV/Excel/HTML 均可,后端自动识别列名与编码)。 */
|
||||
export async function importTrades(file: File): Promise<TradesImportResponse> {
|
||||
const form = new FormData();
|
||||
form.append('file', file);
|
||||
// 注意:不能手工设 Content-Type,FormData 需自带 multipart 边界
|
||||
const res = await apiFetch('/api/trades/import', { method: 'POST', body: form });
|
||||
if (!res.ok) throw new ApiError(await readError(res, `导入失败 (HTTP ${res.status})`), res.status);
|
||||
return (await res.json()) as TradesImportResponse;
|
||||
}
|
||||
|
||||
export async function clearTrades(): Promise<TradesClearResponse> {
|
||||
const res = await apiFetch('/api/trades', { method: 'DELETE' });
|
||||
if (!res.ok) throw new ApiError(await readError(res, `清空成交失败 (HTTP ${res.status})`), res.status);
|
||||
return (await res.json()) as TradesClearResponse;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user