update pay ui

This commit is contained in:
2025-12-14 16:06:06 +08:00
parent b6b9a6b5dd
commit 024a34cdd0
6 changed files with 1034 additions and 50 deletions

View File

@@ -0,0 +1,309 @@
/**
* 涨停分析静态数据服务
* 从 /data/zt/ 目录读取预生成的 JSON 文件
* 不依赖后端 API适合静态部署
*/
// 数据基础路径
const DATA_BASE_URL = '/data/zt';
// 内存缓存
const cache = {
dates: null,
daily: new Map(),
};
/**
* 获取可用日期列表
*/
export const fetchAvailableDates = async () => {
try {
// 使用缓存
if (cache.dates) {
return { success: true, events: cache.dates };
}
const response = await fetch(`${DATA_BASE_URL}/dates.json`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
// 转换为日历事件格式
const events = (data.dates || []).map(d => ({
title: `${d.count}`,
start: d.formatted_date,
end: d.formatted_date,
className: 'bg-gradient-primary',
allDay: true,
date: d.date,
count: d.count,
}));
// 缓存结果
cache.dates = events;
return { success: true, events, total: events.length };
} catch (error) {
console.error('[ztStaticService] fetchAvailableDates error:', error);
return { success: false, error: error.message, events: [] };
}
};
/**
* 获取指定日期的分析数据
*/
export const fetchDailyAnalysis = async (date) => {
try {
// 使用缓存
if (cache.daily.has(date)) {
return { success: true, data: cache.daily.get(date), from_cache: true };
}
const response = await fetch(`${DATA_BASE_URL}/daily/${date}.json`);
if (!response.ok) {
if (response.status === 404) {
return { success: false, error: `日期 ${date} 的数据不存在` };
}
throw new Error(`HTTP ${response.status}`);
}
const data = await response.json();
// 缓存结果
cache.daily.set(date, data);
return { success: true, data, from_cache: false };
} catch (error) {
console.error('[ztStaticService] fetchDailyAnalysis error:', error);
return { success: false, error: error.message };
}
};
/**
* 获取词云数据
* 从每日分析数据中提取
*/
export const fetchWordCloudData = async (date) => {
try {
const result = await fetchDailyAnalysis(date);
if (!result.success) {
return result;
}
const wordFreqData = result.data.word_freq_data || [];
return { success: true, data: wordFreqData };
} catch (error) {
console.error('[ztStaticService] fetchWordCloudData error:', error);
return { success: false, error: error.message, data: [] };
}
};
/**
* 获取高位股统计
* 从每日分析数据中的 stocks 计算
*/
export const fetchHighPositionStocks = async (date) => {
try {
const result = await fetchDailyAnalysis(date);
if (!result.success) {
return result;
}
const stocks = result.data.stocks || [];
// 筛选连板股continuous_days 包含数字 >= 2
const highPositionStocks = stocks
.filter(stock => {
const days = parseContinuousDays(stock.continuous_days);
return days >= 2;
})
.map(stock => {
const days = parseContinuousDays(stock.continuous_days);
return {
stock_code: stock.scode,
stock_name: stock.sname,
price: '-', // 静态数据中没有实时价格
increase_rate: 10.0, // 涨停固定 10%
continuous_limit_up: days,
industry: (stock.core_sectors || [])[0] || '未知',
turnover_rate: '-', // 静态数据中没有换手率
brief: stock.brief || '',
};
})
.sort((a, b) => b.continuous_limit_up - a.continuous_limit_up);
// 计算统计数据
const totalCount = highPositionStocks.length;
const maxDays = highPositionStocks.length > 0
? Math.max(...highPositionStocks.map(s => s.continuous_limit_up))
: 0;
const avgDays = highPositionStocks.length > 0
? (highPositionStocks.reduce((sum, s) => sum + s.continuous_limit_up, 0) / totalCount).toFixed(1)
: 0;
return {
success: true,
data: {
stocks: highPositionStocks,
statistics: {
total_count: totalCount,
max_continuous_days: maxDays,
avg_continuous_days: avgDays,
},
},
};
} catch (error) {
console.error('[ztStaticService] fetchHighPositionStocks error:', error);
return { success: false, error: error.message };
}
};
/**
* 解析连板天数
* 例如 "2连板" -> 2, "首板" -> 1
*/
const parseContinuousDays = (str) => {
if (!str) return 1;
const match = str.match(/(\d+)/);
if (match) {
return parseInt(match[1], 10);
}
if (str.includes('首板')) return 1;
return 1;
};
/**
* 关键词搜索股票
* 从缓存的数据中搜索
*/
export const searchStocks = async (searchParams) => {
try {
const { query, date, date_range, page = 1, page_size = 20 } = searchParams;
if (!query || query.trim() === '') {
return { success: false, error: '搜索关键词不能为空' };
}
const queryLower = query.toLowerCase().trim();
let allStocks = [];
// 确定要搜索的日期范围
let datesToSearch = [];
if (date) {
datesToSearch = [date];
} else if (date_range?.start && date_range?.end) {
// 从缓存的日期中筛选
const datesResult = await fetchAvailableDates();
if (datesResult.success) {
datesToSearch = datesResult.events
.filter(d => d.date >= date_range.start && d.date <= date_range.end)
.map(d => d.date);
}
} else {
// 默认搜索最近 30 天
const datesResult = await fetchAvailableDates();
if (datesResult.success) {
datesToSearch = datesResult.events.slice(0, 30).map(d => d.date);
}
}
// 从每个日期的数据中搜索
for (const d of datesToSearch) {
const result = await fetchDailyAnalysis(d);
if (result.success && result.data.stocks) {
const stocks = result.data.stocks.map(s => ({ ...s, date: d }));
allStocks = allStocks.concat(stocks);
}
}
// 关键词匹配
const results = allStocks
.map(stock => {
let score = 0;
// 精确匹配股票代码
if (queryLower === (stock.scode || '').toLowerCase()) {
score = 100;
}
// 精确匹配股票名称
else if (queryLower === (stock.sname || '').toLowerCase()) {
score = 90;
}
// 部分匹配股票名称
else if ((stock.sname || '').toLowerCase().includes(queryLower)) {
score = 80;
}
// 匹配板块
else if ((stock.core_sectors || []).some(s => s.toLowerCase().includes(queryLower))) {
score = 70;
}
// 匹配涨停原因
else if ((stock.brief || '').toLowerCase().includes(queryLower)) {
score = 60;
}
return { ...stock, _score: score };
})
.filter(s => s._score > 0)
.sort((a, b) => b._score - a._score || b.date.localeCompare(a.date));
// 分页
const total = results.length;
const start = (page - 1) * page_size;
const pageResults = results.slice(start, start + page_size);
return {
success: true,
data: {
stocks: pageResults,
total,
page,
page_size,
total_pages: Math.ceil(total / page_size),
search_mode: 'keyword',
},
};
} catch (error) {
console.error('[ztStaticService] searchStocks error:', error);
return { success: false, error: error.message };
}
};
/**
* 批量获取股票详情
*/
export const fetchStocksBatchDetail = async (codes, date) => {
try {
const result = await fetchDailyAnalysis(date);
if (!result.success) {
return result;
}
const stocks = (result.data.stocks || []).filter(s => codes.includes(s.scode));
return { success: true, data: stocks };
} catch (error) {
console.error('[ztStaticService] fetchStocksBatchDetail error:', error);
return { success: false, error: error.message };
}
};
/**
* 清除缓存
*/
export const clearCache = () => {
cache.dates = null;
cache.daily.clear();
};
export default {
fetchAvailableDates,
fetchDailyAnalysis,
fetchWordCloudData,
fetchHighPositionStocks,
searchStocks,
fetchStocksBatchDetail,
clearCache,
};