个股中心、涨停分析接口对接

This commit is contained in:
renzhijun
2026-01-31 16:59:33 +08:00
parent 44d8ecf318
commit 1c13386dfc
3 changed files with 459 additions and 611 deletions

View File

@@ -125,14 +125,45 @@
this.getYesterdayDateData()
this.generateMonthDateListData()
// 新增:初始化时触发一次事件,传递当天数据给父页面
this.emitDateChange(currentYear, currentMonth, currentDay, this.getTodayItem(currentYear, currentMonth, currentDay))
//this.emitDateChange(currentYear, currentMonth, currentDay, this.getTodayItem(currentYear, currentMonth, currentDay))
this.emitDateChange(
currentYear,
currentMonth,
currentDay,
this.getTodayItem(currentYear, currentMonth, currentDay),
this.getPrevDayItem(currentYear, currentMonth, currentDay) // 新增上一天数据
)
},
mounted() {
this.getCalendarCombinedData()
},
methods: {
methods: {
// 3. 在日历组件methods中新增getPrevDayItem方法
/**
* 获取指定日期的上一天数据
*/
getPrevDayItem(year, month, day) {
// 计算上一天日期
const currentDate = new Date(`${year}-${month}-${day}`);
const prevDate = new Date(currentDate.getTime() - 24 * 60 * 60 * 1000);
const prevYear = prevDate.getFullYear();
const prevMonth = prevDate.getMonth() + 1;
const prevDay = prevDate.getDate();
// 获取上一天的完整数据
const targetDate = `${prevYear}-${prevMonth > 9 ? prevMonth : '0' + prevMonth}-${prevDay > 9 ? prevDay : '0' + prevDay}`;
const currentMonthList = this.monthDateList[this.selectMonthIndex] || [];
const localItem = currentMonthList.find(item => item.date === targetDate) || null;
const apiData = this.getCalendarItemByDate(targetDate) || {};
return {
...localItem,
zt_count: apiData.zt_count || 0,
top_sector: apiData.top_sector || '-',
zaban_rate: apiData.zaban_rate || '0%'
};
},
/**
* 获取当天的item数据合并接口数据
*/
@@ -156,26 +187,52 @@
/**
* 触发日期变更事件传递包含接口数据的item
*/
emitDateChange(year, month, day, item) {
const yearMonth = `${year}-${month > 9 ? month : '0' + month}`;
const fullDate = `${year}-${month > 9 ? month : '0' + month}-${day > 9 ? day : '0' + day}`;
this.$emit('date-change', {
yearMonth,
fullDate,
item: item || { // 兜底无item时赋值空对象+默认值
date: fullDate,
year,
month,
day,
zt_count: 0,
top_sector: '-',
zaban_rate: '0%'
},
year,
month,
day
});
},
// emitDateChange(year, month, day, item) {
// const yearMonth = `${year}-${month > 9 ? month : '0' + month}`;
// const fullDate = `${year}-${month > 9 ? month : '0' + month}-${day > 9 ? day : '0' + day}`;
// this.$emit('date-change', {
// yearMonth,
// fullDate,
// item: item || { // 兜底无item时赋值空对象+默认值
// date: fullDate,
// year,
// month,
// day,
// zt_count: 0,
// top_sector: '-',
// zaban_rate: '0%'
// },
// year,
// month,
// day
// });
// },
emitDateChange(year, month, day, item, prevItem = { zt_count: 0 }) { // 新增prevItem参数设置默认值兜底
const yearMonth = `${year}-${month > 9 ? month : '0' + month}`;
const fullDate = `${year}-${month > 9 ? month : '0' + month}-${day > 9 ? day : '0' + day}`;
this.$emit('date-change', {
yearMonth,
fullDate,
item: item || { // 兜底无当前item时赋值空对象+默认值
date: fullDate,
year,
month,
day,
zt_count: 0,
top_sector: '-',
zaban_rate: '0%'
},
prevItem: prevItem || { // 新增:传递上一天数据,兜底默认值
zt_count: 0,
top_sector: '-',
zaban_rate: '0%'
},
year,
month,
day
});
},
/**
* 获取当前时间前一天的数据
*/
@@ -259,7 +316,14 @@
// 接口数据加载后,重新触发一次当前选中日期的事件,更新数据
if (this.selectDateStr) {
const [year, month, day] = this.selectDateStr.split('-').map(Number);
this.emitDateChange(year, month, day, this.getTodayItem(year, month, day));
//this.emitDateChange(year, month, day, this.getTodayItem(year, month, day));
this.emitDateChange(
year,
month,
day,
this.getTodayItem(year, month, day),
this.getPrevDayItem(year, month, day) // 新增上一天数据
)
}
} else {
this.calendarApiData = [];
@@ -460,28 +524,84 @@
/**
* 点击选择开始日期和结束日期
* @param {Object} item
*/
clickSelectDate(item, index) { // 新增index参数
if (!item.isCurrentMonth) return
if (this.selectDateStr != item.date) {
this.selectDateStr = item.date
// 1. 获取该日期的接口数据
const apiData = this.getCalendarItemByDate(item.date) || {};
// 2. 合并本地item和接口数据补充默认值
const mergedItem = {
...item, // 本地日期基础数据
zt_count: apiData.zt_count || 0, // 涨停家数默认0
top_sector: apiData.top_sector || '-', // 热门板块,默认'-'
zaban_rate: apiData.zaban_rate || '0%', // 炸板率,示例字段
isWeekend: index % 7 === 0 || index % 7 === 6 // 是否周末
};
this.chgStockData = mergedItem;
// 3. 解析日期触发事件传递合并后的item
const [year, month, day] = item.date.split('-').map(Number);
this.emitDateChange(year, month, day, mergedItem);
console.log('点击某天(含接口数据)', mergedItem);
}
}
*/
clickSelectDate(item, index) { // 新增index参数
if (!item.isCurrentMonth) return
if (this.selectDateStr != item.date) {
this.selectDateStr = item.date
// 1. 获取该日期的接口数据
const apiData = this.getCalendarItemByDate(item.date) || {};
// 2. 合并本地item和接口数据补充默认值
const mergedItem = {
...item, // 本地日期基础数据
zt_count: apiData.zt_count || 0, // 涨停家数默认0
top_sector: apiData.top_sector || '-', // 热门板块,默认'-'
zaban_rate: apiData.zaban_rate || '0%', // 炸板率,示例字段
isWeekend: index % 7 === 0 || index % 7 === 6 // 是否周末
};
// ===== 新增:获取上一天的日期和数据 =====
// 2.1 计算上一天的日期
const currentDate = new Date(item.date);
const prevDate = new Date(currentDate.getTime() - 24 * 60 * 60 * 1000); // 减一天
const prevYear = prevDate.getFullYear();
const prevMonth = prevDate.getMonth() + 1;
const prevDay = prevDate.getDate();
const prevDateStr = `${prevYear}-${prevMonth > 9 ? prevMonth : '0' + prevMonth}-${prevDay > 9 ? prevDay : '0' + prevDay}`;
// 2.2 获取上一天的接口数据
const prevApiData = this.getCalendarItemByDate(prevDateStr) || {};
// 2.3 查找上一天的本地item用于补全基础信息
let prevLocalItem = null;
// 先在当前月份列表找
const currentMonthList = this.monthDateList[this.selectMonthIndex] || [];
prevLocalItem = currentMonthList.find(i => i.date === prevDateStr);
// 如果当前月份没有(跨月),找对应月份的列表
if (!prevLocalItem) {
const prevMonthIndex = this.selectMonthIndex - (prevMonth < item.month ? 1 : 0);
const prevMonthList = this.monthDateList[prevMonthIndex] || [];
prevLocalItem = prevMonthList.find(i => i.date === prevDateStr);
}
// 2.4 合并上一天的完整数据
const prevMergedItem = {
...(prevLocalItem || {}),
zt_count: prevApiData.zt_count || 0,
top_sector: prevApiData.top_sector || '-',
zaban_rate: prevApiData.zaban_rate || '0%',
isWeekend: false // 兜底默认值
};
// =======================================
this.chgStockData = mergedItem;
// 3. 解析日期,触发事件传递【当前数据+上一天数据】
const [year, month, day] = item.date.split('-').map(Number);
this.emitDateChange(year, month, day, mergedItem, prevMergedItem); // 新增传递上一天数据
console.log('点击某天(含接口数据)', { current: mergedItem, prev: prevMergedItem });
}
}
// clickSelectDate(item, index) {
// if (!item.isCurrentMonth) return
// if (this.selectDateStr != item.date) {
// this.selectDateStr = item.date
// const apiData = this.getCalendarItemByDate(item.date) || {};
// const mergedItem = {
// ...item,
// zt_count: apiData.zt_count || 0,
// top_sector: apiData.top_sector || '-',
// zaban_rate: apiData.zaban_rate || '0%',
// isWeekend: index % 7 === 0 || index % 7 === 6
// };
// this.chgStockData = mergedItem;
// const [year, month, day] = item.date.split('-').map(Number);
// this.emitDateChange(year, month, day, mergedItem);
// console.log('点击某天(含接口数据)', mergedItem);
// }
// }
}
}
</script>

View File

@@ -35,6 +35,15 @@ export default {
colorList: {
type: Array,
default: () => ['#60A5FA', '#FEC200', '#EF4444'] // 外圈、中间、中心
},
// 新增:字号配置,让组件更灵活
fontSizeConfig: {
type: Object,
default: () => ({
minSize: 12, // 最小字号
maxSize: 40, // 最大字号
scaleFactor: 0.1 // 缩放因子,越大字号差异越明显
})
}
},
data() {
@@ -113,6 +122,11 @@ export default {
// 按权重排序(权重越大,文字越大)
const sortedWords = [...this.wordData].sort((a, b) => b.value - a.value);
// 计算value的最大值和最小值用于归一化
const values = sortedWords.map(item => item.value);
this.valueMax = Math.max(...values);
this.valueMin = Math.min(...values);
// 逐个绘制文字
sortedWords.forEach((word, index) => {
this.placeWord(word, index);
@@ -124,24 +138,22 @@ export default {
const ctx = this.ctx;
// 优化1提高最大尝试次数从50→150让更多文字能找到位置
const maxAttempts = 150;
const baseFontSize = 12; // 基础字体大小
const { minSize, maxSize, scaleFactor } = this.fontSizeConfig;
// 分段计算分母适配不同value区间
const value = word.value;
const baseDenominator = 1000; // 基础分母
const interval = 500; // 区间步长
const step = 500; // 分母每次增加的步长
// ===== 核心优化:重新设计字号计算逻辑 =====
// 1. 归一化value0-1区间
let normalizedValue = 1;
if (this.valueMax !== this.valueMin) {
normalizedValue = (word.value - this.valueMin) / (this.valueMax - this.valueMin);
}
// 计算当前value所属区间动态确定分母
const intervalNum = Math.floor(value / interval);
let denominator = baseDenominator + (intervalNum * step);
// 兜底避免分母过小比如value为0时
denominator = Math.max(denominator, baseDenominator);
// 根据分段分母计算字体大小,限制最大值
const maxFontSize = 28; // 优化2适当减小最大字体从32→28节省空间
const fontSize = Math.min(baseFontSize + (value / denominator) * 16, maxFontSize);
// 2. 计算字号:基于归一化值,确保最小到最大的平滑过渡
// 公式:最小字号 + (最大字号 - 最小字号) * 归一化值 * 缩放因子
const fontSize = Math.min(
minSize + (maxSize - minSize) * normalizedValue * scaleFactor,
maxSize
);
// ==========================================
// 旋转角度:-60° 到 60°
const rotateAngle = (Math.random() - 0.5) * 120 * Math.PI / 180;
@@ -150,7 +162,7 @@ export default {
ctx.font = `${fontSize}px sans-serif`;
// 获取文字宽度和高度(用于碰撞检测)
const textWidth = ctx.measureText(word.name).width;
const textWidth = ctx.measureText(word.text || word.name).width; // 兼容text/name字段
// 优化3更精准的文字高度估算从1.2→1.05),减少无效空间
const textHeight = fontSize * 1.05;
@@ -193,7 +205,7 @@ export default {
ctx.fillStyle = color;
// 无重叠,绘制文字
this.drawTextAtPosition(word.name, x, y, rotateAngle, fontSize);
this.drawTextAtPosition(word.text || word.name, x, y, rotateAngle, fontSize);
// 记录已放置的文字信息(用于后续碰撞检测)
this.placedWords.push({
@@ -238,9 +250,9 @@ export default {
// 四个顶点坐标(相对于中心)
const points = [
{ x: -halfW, y: -halfH },
{ x: halfW, y: -halfH },
{ x: -halfW, y: halfH },
{ x: halfW, y: halfH },
{ x: -halfW, y: halfH }
{ x: halfW, y: -halfH }
];
// 旋转并平移到实际位置