160 lines
4.2 KiB
JavaScript
160 lines
4.2 KiB
JavaScript
/**
|
|
* 图表辅助函数
|
|
* 用于处理异动标注等图表相关逻辑
|
|
*/
|
|
|
|
/**
|
|
* 获取异动标注的配色和符号
|
|
* @param {string} alertType - 异动类型
|
|
* @param {number} importanceScore - 重要性得分
|
|
* @returns {Object} { color, symbol, symbolSize }
|
|
*/
|
|
export const getAlertStyle = (alertType, importanceScore = 0.5) => {
|
|
let color = '#ff6b6b';
|
|
let symbol = 'pin';
|
|
let symbolSize = 35;
|
|
|
|
switch (alertType) {
|
|
case 'surge_up':
|
|
case 'surge':
|
|
color = '#ff4757';
|
|
symbol = 'triangle';
|
|
symbolSize = 30 + Math.min(importanceScore * 20, 15);
|
|
break;
|
|
case 'surge_down':
|
|
color = '#2ed573';
|
|
symbol = 'path://M0,0 L10,0 L5,10 Z'; // 向下三角形
|
|
symbolSize = 30 + Math.min(importanceScore * 20, 15);
|
|
break;
|
|
case 'limit_up':
|
|
color = '#ff6348';
|
|
symbol = 'diamond';
|
|
symbolSize = 28;
|
|
break;
|
|
case 'rank_jump':
|
|
color = '#3742fa';
|
|
symbol = 'circle';
|
|
symbolSize = 25;
|
|
break;
|
|
case 'volume_spike':
|
|
color = '#ffa502';
|
|
symbol = 'rect';
|
|
symbolSize = 25;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return { color, symbol, symbolSize };
|
|
};
|
|
|
|
/**
|
|
* 获取异动类型的显示标签
|
|
* @param {string} alertType - 异动类型
|
|
* @returns {string} 显示标签
|
|
*/
|
|
export const getAlertTypeLabel = (alertType) => {
|
|
const labels = {
|
|
surge: '急涨',
|
|
surge_up: '暴涨',
|
|
surge_down: '暴跌',
|
|
limit_up: '涨停增加',
|
|
rank_jump: '排名跃升',
|
|
volume_spike: '放量',
|
|
unknown: '异动',
|
|
};
|
|
return labels[alertType] || alertType;
|
|
};
|
|
|
|
/**
|
|
* 生成图表标注点数据
|
|
* @param {Array} alerts - 异动数据数组
|
|
* @param {Array} times - 时间数组
|
|
* @param {Array} prices - 价格数组
|
|
* @param {number} priceMax - 最高价格(用于无法匹配时间时的默认位置)
|
|
* @param {number} maxCount - 最大显示数量
|
|
* @returns {Array} ECharts markPoint data
|
|
*/
|
|
export const getAlertMarkPoints = (alerts, times, prices, priceMax, maxCount = 15) => {
|
|
if (!alerts || alerts.length === 0) return [];
|
|
|
|
// 按重要性排序,限制显示数量
|
|
const sortedAlerts = [...alerts]
|
|
.sort((a, b) => (b.final_score || b.importance_score || 0) - (a.final_score || a.importance_score || 0))
|
|
.slice(0, maxCount);
|
|
|
|
return sortedAlerts.map((alert) => {
|
|
// 找到对应时间的价格
|
|
const timeIndex = times.indexOf(alert.time);
|
|
const price = timeIndex >= 0 ? prices[timeIndex] : (alert.index_price || priceMax);
|
|
|
|
const { color, symbol, symbolSize } = getAlertStyle(
|
|
alert.alert_type,
|
|
alert.final_score / 100 || alert.importance_score || 0.5
|
|
);
|
|
|
|
// 格式化标签
|
|
let label = alert.concept_name || '';
|
|
if (label.length > 6) {
|
|
label = label.substring(0, 5) + '...';
|
|
}
|
|
|
|
// 添加涨停数量(如果有)
|
|
if (alert.limit_up_count > 0) {
|
|
label += `\n涨停: ${alert.limit_up_count}`;
|
|
}
|
|
|
|
const isDown = alert.alert_type === 'surge_down';
|
|
|
|
return {
|
|
name: alert.concept_name,
|
|
coord: [alert.time, price],
|
|
value: label,
|
|
symbol,
|
|
symbolSize,
|
|
itemStyle: {
|
|
color,
|
|
borderColor: '#fff',
|
|
borderWidth: 1,
|
|
shadowBlur: 3,
|
|
shadowColor: 'rgba(0,0,0,0.2)',
|
|
},
|
|
label: {
|
|
show: true,
|
|
position: isDown ? 'bottom' : 'top',
|
|
formatter: '{b}',
|
|
fontSize: 9,
|
|
color: '#333',
|
|
backgroundColor: isDown ? 'rgba(46, 213, 115, 0.9)' : 'rgba(255,255,255,0.9)',
|
|
padding: [2, 4],
|
|
borderRadius: 2,
|
|
borderColor: color,
|
|
borderWidth: 1,
|
|
},
|
|
alertData: alert, // 存储原始数据
|
|
};
|
|
});
|
|
};
|
|
|
|
/**
|
|
* 格式化分数显示
|
|
* @param {number} score - 分数
|
|
* @returns {string} 格式化后的分数
|
|
*/
|
|
export const formatScore = (score) => {
|
|
if (score === null || score === undefined) return '-';
|
|
return Math.round(score).toString();
|
|
};
|
|
|
|
/**
|
|
* 获取分数对应的颜色
|
|
* @param {number} score - 分数 (0-100)
|
|
* @returns {string} 颜色代码
|
|
*/
|
|
export const getScoreColor = (score) => {
|
|
if (score >= 80) return '#ff4757';
|
|
if (score >= 60) return '#ff6348';
|
|
if (score >= 40) return '#ffa502';
|
|
return '#747d8c';
|
|
};
|