feat: 新增题材页面(列表 + 详情)
- 主页增加「题材热点」入口,跳转题材列表页 - 题材列表页:展示全部题材,支持按涨幅/强度/热度/成交额排序 - 题材详情页:简介、热点事件、相关新闻、板块涨跌统计、全部相关股票(含入选理由默认展开) - 后端逆向封装东方财富题材接口 getThemeList/getDetail/getStockList,含交易时段感知缓存 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
// 题材数据获取工具:通过 Python 后端代理调用东方财富题材接口
|
||||
import { getApiBaseUrl } from "@/lib/api-client";
|
||||
|
||||
/* ── 题材列表 ── */
|
||||
|
||||
export interface ThemeItem {
|
||||
themeCode: string;
|
||||
themeName: string;
|
||||
securityName: string; // 领涨股名称
|
||||
securityCode: string; // 领涨股代码
|
||||
codeWithSuffix: string;
|
||||
hotRank: number; // 热度排名
|
||||
f3: number | null; // 领涨股涨幅
|
||||
bf3: number | null; // 题材涨幅
|
||||
hotValue: number; // 热度值
|
||||
hotValueUpLimit: number; // 热度上限
|
||||
strengthValue: number | null; // 强度值
|
||||
fex5: number | null; // 成交额
|
||||
fex3: number | null;
|
||||
label: string | null; // 标签(如"超级爆点")
|
||||
}
|
||||
|
||||
export type ThemeSortField = 1 | 3 | 4 | 5; // 1=涨幅 3=强度 4=热度排名 5=成交额
|
||||
|
||||
export interface ThemeListResponse {
|
||||
data: ThemeItem[];
|
||||
count: number;
|
||||
sort_field: number;
|
||||
asc: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取全部题材列表
|
||||
* @param sortField 1=涨幅 3=强度 4=热度排名 5=成交额
|
||||
* @param asc true=升序
|
||||
*/
|
||||
export async function fetchThemes(sortField: ThemeSortField = 1, asc: boolean = false): Promise<ThemeItem[]> {
|
||||
const baseUrl = getApiBaseUrl();
|
||||
const url = `${baseUrl}/api/themes?sort_field=${sortField}&asc=${asc}`;
|
||||
|
||||
try {
|
||||
const resp = await fetch(url, { method: "GET", cache: "no-store" });
|
||||
if (!resp.ok) return [];
|
||||
const result: ThemeListResponse = await resp.json();
|
||||
return result.data || [];
|
||||
} catch (err) {
|
||||
console.error("[theme-api] 获取题材列表失败:", err);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 题材详情 ── */
|
||||
|
||||
export interface ThemeHotEvent {
|
||||
newsTitle: string | null;
|
||||
newsSummary: string | null;
|
||||
newsMediaName: string | null;
|
||||
newsPublishTimeFormat: string | null;
|
||||
newsCode: string | null;
|
||||
}
|
||||
|
||||
export interface ThemeNews {
|
||||
newsCode: string;
|
||||
newsTitle: string;
|
||||
newsMediaName: string;
|
||||
newsPublishTime: number | null;
|
||||
}
|
||||
|
||||
export interface ThemeBaseInfo {
|
||||
themeCode: string;
|
||||
themeName: string;
|
||||
introduction: string;
|
||||
explainImgUrl: string | null;
|
||||
themeLevel: number;
|
||||
isShowRank: number;
|
||||
}
|
||||
|
||||
export interface ThemeDetail {
|
||||
baseInfo: ThemeBaseInfo;
|
||||
hotEvent: ThemeHotEvent | null;
|
||||
eventHistory: ThemeNews[];
|
||||
topicId: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取题材详情(简介 + 热点事件 + 相关新闻)
|
||||
*/
|
||||
export async function fetchThemeDetail(themeCode: string): Promise<ThemeDetail | null> {
|
||||
const baseUrl = getApiBaseUrl();
|
||||
const url = `${baseUrl}/api/themes/${themeCode}/detail`;
|
||||
|
||||
try {
|
||||
const resp = await fetch(url, { method: "GET", cache: "no-store" });
|
||||
if (!resp.ok) return null;
|
||||
const result = await resp.json();
|
||||
return result.data || null;
|
||||
} catch (err) {
|
||||
console.error("[theme-api] 获取题材详情失败:", err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── 题材相关股票 ── */
|
||||
|
||||
export interface ThemeStockKeyword {
|
||||
keywordCode: string;
|
||||
keyword: string;
|
||||
introduction: string;
|
||||
}
|
||||
|
||||
export interface ThemeStock {
|
||||
securityName: string;
|
||||
securityCode: string;
|
||||
codeSuffix: string;
|
||||
f2: number; // 现价
|
||||
f3: number; // 涨跌幅%
|
||||
f5: number; // 成交量
|
||||
f6: number; // 成交额
|
||||
f8: number; // 换手率%
|
||||
f20: number; // 总市值
|
||||
f21: number; // 流通市值
|
||||
f62: number; // 主力净流入
|
||||
f100: string; // 所属行业
|
||||
f265: string; // 板块代码
|
||||
label: string | null; // 涨停标签
|
||||
rank: number;
|
||||
dragonStockLabel: number;
|
||||
keywordList: ThemeStockKeyword[]; // 入选理由
|
||||
}
|
||||
|
||||
export interface ThemeStatistic {
|
||||
f3: number | null; // 板块涨幅
|
||||
f104: number | null; // 上涨家数
|
||||
f105: number | null; // 下跌家数
|
||||
f106: number | null; // 平盘家数
|
||||
fex5: number | null; // 板块成交额
|
||||
}
|
||||
|
||||
export interface ThemeStocksResponse {
|
||||
data: ThemeStock[];
|
||||
statistic: ThemeStatistic;
|
||||
total: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取题材下全部相关股票
|
||||
*/
|
||||
export async function fetchThemeStocks(themeCode: string): Promise<ThemeStocksResponse | null> {
|
||||
const baseUrl = getApiBaseUrl();
|
||||
const url = `${baseUrl}/api/themes/${themeCode}/stocks`;
|
||||
|
||||
try {
|
||||
const resp = await fetch(url, { method: "GET", cache: "no-store" });
|
||||
if (!resp.ok) return null;
|
||||
return await resp.json();
|
||||
} catch (err) {
|
||||
console.error("[theme-api] 获取题材股票失败:", err);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user