Files
JiaZhiQianYan/pages/ztfx/ztfx.vue
2026-02-05 15:01:16 +08:00

1035 lines
32 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<navBar leftText="涨停分析" hideNavBg hideBack></navBar>
<image class="topBg absolute" src="/static/image/index/conceptTopBg.png" mode="widthFix"></image>
<scroll-view scroll-y class="stockDetailsC fixed" :style="'top:'+contentTop+'px;'">
<view style="color: white; font-weight: 500; display: flex; align-items: center; margin: 35rpx 25rpx;">
<image style="width: 40rpx; height: 40rpx;" src="/pagesStock/static/icon/ai-icon.png" mode="widthFix">
</image>
<text style="font-size: 36rpx; margin-left: 10rpx; margin-right: 20rpx;">AI总结</text>
<text style="font-size: 28rpx;"></text>
</view>
<view style="background-color: white; border-radius: 10rpx; overflow: hidden; margin: 25rpx;">
<view
style="color: #2B2B2B; font-weight: 500; display: flex; align-items: center; margin: 25rpx 20rpx;">
<image style="width: 40rpx; height: 40rpx;" src="/pagesStock/static/icon/ai-icon-1.png"
mode="widthFix"></image>
<text
style="font-size: 30rpx; font-weight: bold; margin-left: 10rpx; margin-right: 20rpx;">核心指标</text>
</view>
<view style="display: grid; gap: 15rpx; grid-template-columns: repeat(3, 1fr); margin: 20rpx;">
<view v-for="(item,index) in tabTypes" :key="index"
style="display: flex; align-items: center; justify-content: center; flex-direction: column; background-color: #FFF8F0; border: 1rpx solid #F59B38; border-radius: 5rpx; padding: 20rpx; box-sizing: border-box;">
<view style="display: flex;align-items: center;justify-content: center;">
<view style="color: #F59B38; font-size: 30rpx;">{{item.data}}</view>
<!-- 优化兼容正数负数0的显示 -->
<view v-if="item.change !== 0" :style="[
{
marginLeft: '10rpx',
borderRadius: '5rpx',
color: 'white',
padding: '0 5rpx',
fontSize: '24rpx',
fontWeight: 'bold'
},
item.change > 0 ? { backgroundColor: '#F59B38' } : { backgroundColor: '#EF4444' }
]">
{{item.change > 0 ? '+' + item.change : item.change}}
</view>
</view>
<view style="color: #555555; font-size: 20rpx; margin-top: 5rpx;">{{item.title}}</view>
</view>
</view>
<view style="margin: 25rpx;">
<LCCalendar @date-change="handleDateChange"></LCCalendar>
</view>
<view style="color: #2B2B2B; font-weight: 500; display: flex; margin: 25rpx 20rpx;">
<image style="width: 40rpx; height: 42rpx;" src="/pagesStock/static/icon/all-icon-3.png"
mode="widthFix"></image>
<view style="margin-left: 10rpx;">
<view style="font-size: 30rpx; font-weight: bold;">市场全景</view>
<view style="font-size: 24rpx; font-weight: 500; margin-top: 10rpx;">
<text
style="color: #F3C368; border: 1rpx solid #F3C368; border-radius: 5rpx; text-align: center; padding: 0 10rpx;">{{bkList.length}}个板块</text>
<text
style="color: #EC3440; border: 1rpx solid #EC3440; border-radius: 5rpx; text-align: center; padding: 0 10rpx; margin: 0 10rpx;">{{number_limit_stocks}}只涨停</text>
<text
style="color: #F59B38; border: 1rpx solid #F59B38; border-radius: 5rpx; text-align: center; padding: 0 10rpx;">高位股风险:
</text>
</view>
</view>
</view>
<view
style="background-color: #FAFAFC; border-radius: 10rpx; padding: 30rpx 20rpx; box-sizing: border-box; margin: 25rpx;">
<view style="display: flex; align-items: center; font-size: 22rpx; font-weight: 500;">
<view style="flex: 1; color: #2B2B2B; font-weight: bold; font-size: 28rpx;">板块热力图</view>
<view style="color: #EF4444; display: flex;align-items: center;">
<view
style="margin: 0 10rpx; box-sizing: border-box; border-radius: 15rpx; background-color: #EF4444; overflow: hidden; width: 15rpx; height: 15rpx;">
</view>高热度
</view>
<view style="color: #F97316; display: flex;align-items: center;">
<view
style="margin: 0 10rpx; box-sizing: border-box; border-radius: 15rpx; background-color: #F97316; overflow: hidden; width: 15rpx; height: 15rpx;">
</view>中热度
</view>
<view style="color: #F3B800; display: flex;align-items: center;">
<view
style="margin: 0 10rpx; box-sizing: border-box; border-radius: 15rpx; background-color: #F3B800; overflow: hidden; width: 15rpx; height: 15rpx;">
</view>低热度
</view>
<view style="color: #01AB5D; display: flex;align-items: center;">
<view
style="margin: 0 10rpx; box-sizing: border-box; border-radius: 15rpx; background-color: #01AB5D; overflow: hidden; width: 15rpx; height: 15rpx;">
</view>冷门
</view>
</view>
<view style="display: grid; grid-template-columns: repeat(4, 1fr); gap: 7rpx; margin-top: 25rpx;">
<view v-for="(item, index) in bkList" :key="index" :style="{
backgroundColor: item.bgColor,
borderRadius: '5rpx',
padding: '15rpx',
color: 'white',
fontSize: '24rpx',
fontWeight: '500'
}" >
<view class="single-line-ellipsis">{{item.title}}</view>
<view class="count-text">{{item.count}}</view>
</view>
</view>
</view>
<view
style="margin: 25rpx; display: grid; grid-template-columns: repeat(3, 1fr); gap: 20rpx; color: #999999; font-size: 22rpx; font-weight: 500;">
<view v-for="(item, index) in bkTypes" @click="switchTab(index)"
style="display: flex; align-items: center; justify-content: center; padding: 10rpx 20rpx; border-radius: 8rpx;"
:style="{
color: (activeType == index ? '#BB8520' : '#999999'),
border: `1rpx solid ${activeType == index ? '#F2C369' : '#D2D2D2'}`,
'background-color' : (activeType == index ? '#FFFAF1' : '#FFF'),
'cursor': 'pointer'
}">
{{item}}
</view>
</view>
<view style=" display: flex; align-items: center; justify-content: center;padding: 0 25rpx; ">
<!-- <view v-if="activeType === 0"
style="width: 100%; height: 500rpx; display: flex; align-items: center; justify-content: center; color: #999;">
板块关联图内容区域
</view> -->
<!-- <view v-show="activeType === 0" style="width: 100%; height: 500rpx;">
<l-echart ref="graphChartRef"></l-echart>
</view> -->
<view v-show="activeType === 0" style="width: 100%; height: 500rpx;">
<l-echart ref="chartRef"></l-echart>
</view>
<WordCloud v-show="activeType === 1" :wordData="wordData" :width="330" :height="330" />
</view>
<view style="color: #2B2B2B; font-weight: 500; display: flex; margin: 25rpx 20rpx;">
<image style="width: 40rpx; height: 42rpx;" src="/pagesStock/static/icon/all-icon-3.png"
mode="widthFix"></image>
<view style="margin-left: 10rpx;">
<view style="font-size: 30rpx; font-weight: bold;">高位股统计</view>
<view style="font-size: 24rpx; font-weight: 500; margin-top: 10rpx;">
<text
style="color: #F3C368; border: 1rpx solid #F3C368; border-radius: 5rpx; text-align: center; padding: 0 10rpx;">
高位股{{highPositionStats.total_count}}
</text>
<text
style="color: #EC3440; border: 1rpx solid #EC3440; border-radius: 5rpx; text-align: center; padding: 0 10rpx; margin: 0 10rpx;">
平均{{highPositionStats.avg_continuous_days}}
</text>
<text
style="color: #F59B38; border: 1rpx solid #F59B38; border-radius: 5rpx; text-align: center; padding: 0 10rpx;">
最高{{highPositionStats.max_continuous_days}}
</text>
<text :style="{
color: '#FFF',
backgroundColor: riskAssessment.color,
borderRadius: '5rpx',
textAlign: 'center',
padding: '0 10rpx',
marginLeft: '10rpx'
}">
{{riskAssessment.level}}
</text>
</view>
</view>
</view>
<view v-for="(stock,index) in highPositionStockList" :key="index"
style="background-color: #FAFAFC; border-radius: 10rpx; padding: 15rpx 20rpx; display: flex; align-items: center; margin: 10rpx 25rpx;">
<view style="flex: 1;">
<view style="color: #2B2B2B; font-weight: bold; font-size: 26rpx;">{{stock.sname}}</view>
<view :style="{
color: '#999999',
fontWeight: 500,
fontSize: '24rpx',
marginTop: '5rpx'
}">
({{stock.risk_info.status}})
</view>
</view>
<view :style="{
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
padding: '4rpx 10rpx',
backgroundColor: stock.risk_info.bg,
borderRadius: '5rpx',
border: `1rpx solid ${stock.risk_info.border}`,
color: stock.risk_info.color,
fontSize: '20rpx',
fontWeight: 400,
margin: '0 10rpx'
}">
<image v-if="['high', 'medium'].includes(getStockHeatType(stock))" :style="{
width: '15rpx',
height: '17rpx',
marginRight: '10rpx',
tintColor: heatIconMap[getStockHeatType(stock)].icon4Color
}" :src="heatIconMap[getStockHeatType(stock)].icon4" mode="widthFix"></image>
<view>{{stock.continuous_days_num}}连板</view>
</view>
<image :style="{width: '27rpx', height: '25rpx'}" :src="heatIconMap[getStockHeatType(stock)].icon5"
mode="widthFix"></image>
</view>
<view
style="margin: 20rpx 30rpx; color: #EF4444; font-size: 24rpx; font-weight: 500; display: flex; align-items: center;">
<image style="width: 27rpx; height: 25rpx; margin-right: 10rpx;"
src="/pagesStock/static/icon/all-icon-5.png" mode="widthFix"></image>
<text>高位股风险较高追涨需谨慎</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import {
getBaseURL1
} from '@/request/http.js'
import {
inject
} from 'vue'
import {
calendarCombinedData,
analyseHighStocks
} from '@/request/api'
const echarts = require('../../uni_modules/lime-echart/static/echarts.min.js');
import WordCloud from '@/components/WordCloud/WordCloud.vue';
// 模拟关系图数据替代接口请求的les-miserables.json
const mockGraphData = {
categories: [{
name: '板块'
},
{
name: '概念'
},
{
name: '个股'
}
],
nodes: [{
name: '科技板块',
symbolSize: 50,
category: 0
},
{
name: '人工智能',
symbolSize: 30,
category: 1
},
{
name: '大数据',
symbolSize: 25,
category: 1
},
{
name: '科大讯飞',
symbolSize: 20,
category: 2
},
{
name: '百度',
symbolSize: 20,
category: 2
},
{
name: '金融板块',
symbolSize: 50,
category: 0
},
{
name: '数字货币',
symbolSize: 30,
category: 1
},
{
name: '招商银行',
symbolSize: 20,
category: 2
},
{
name: '平安银行',
symbolSize: 20,
category: 2
}
],
links: [{
source: '科技板块',
target: '人工智能',
value: 10
},
{
source: '科技板块',
target: '大数据',
value: 8
},
{
source: '人工智能',
target: '科大讯飞',
value: 6
},
{
source: '人工智能',
target: '百度',
value: 6
},
{
source: '金融板块',
target: '数字货币',
value: 9
},
{
source: '数字货币',
target: '招商银行',
value: 5
},
{
source: '数字货币',
target: '平安银行',
value: 5
}
]
};
export default {
components: {
WordCloud
},
data() {
return {
navH: inject('navHeight'),
contentTop: '',
selectedYearMonth: '', // 年-月(去掉日)
selectedFullDate: '', // 年-月-日
selectedItem: null, // 选中的item完整数据
tabTypes: [{
data: '',
change: 0,
title: '当前日期'
},
{
data: '',
change: 3,
title: '涨停家数'
},
{
data: '22%',
change: 0,
title: '炸板率'
}
],
wordData: [],
bkList: [],
number_limit_stocks: '',
HEAT_LEVELS: [{
threshold: 0.7,
color: '#EF4444',
level: '高热度'
}, // >70%
{
threshold: 0.4,
color: '#F97316',
level: '中热度'
}, // 40%~70%
{
threshold: 0.2,
color: '#F3B800',
level: '低热度'
}, // 20%~40%
{
threshold: 0,
color: '#01AB5D',
level: '无热度'
} // ≤20%
],
// bkTypes: [
// '板块关联图',
// '板块分布',
// '热门概念词云'
// ],
highPositionStats: {
total_count: 0, // 高位股数量
avg_continuous_days: 0, // 平均连板数
max_continuous_days: 0 // 最高连板数
},
riskAssessment: { // 风险评估结果
level: "正常",
color: "#22c55e"
},
highPositionStockList: [], // 新增:存储筛选后的高位股列表
// 风险阈值常量(对应参考代码)
RISK_THRESHOLDS: {
CRITICAL: 7,
HIGH: 5,
MEDIUM: 3,
LOW: 2,
},
// 风险颜色常量
RISK_COLORS: {
critical: {
color: "#ef4444",
bg: "rgba(239, 68, 68, 0.2)",
border: "rgba(239, 68, 68, 0.4)",
},
high: {
color: "#f97316",
bg: "rgba(249, 115, 22, 0.2)",
border: "rgba(249, 115, 22, 0.4)",
},
medium: {
color: "#eab308",
bg: "rgba(234, 179, 8, 0.2)",
border: "rgba(234, 179, 8, 0.4)",
},
low: {
color: "#22c55e",
bg: "rgba(34, 197, 94, 0.2)",
border: "rgba(34, 197, 94, 0.4)",
},
},
heatIconMap: {
high: {
icon4: '/pagesStock/static/icon/all-icon-4.png',
icon4Color: '#EF4444', // 高热度图标颜色
icon5: '/pagesStock/static/icon/all-icon-5.png' // 高热度右侧图标
},
medium: {
icon4: '/pagesStock/static/icon/all-icon-9.png',
icon4Color: '#F97316', // 中热度图标颜色
icon5: '/pagesStock/static/icon/all-icon-6.png' // 中热度右侧图标
},
low: {
icon4: '', // 低热度不显示icon4
icon4Color: '#F3B800',
icon5: '/pagesStock/static/icon/all-icon-7.png' // 低热度右侧图标
},
none: {
icon4: '', // 冷门
icon4Color: '#01AB5D',
icon5: '/pagesStock/static/icon/all-icon-8.png' // 无热度右侧图标
}
},
originData: null, // 原始接口数据
bkTypes: [
'板块分布',
'热门概念词云'
],
activeType: 0, // 默认选中第一个标签
// 饼图配置项
pieOption: {
tooltip: {
trigger: 'item'
},
animation: false,
legend: {
top: '5%',
left: 'center',
show: false
},
series: [{
name: 'Access From',
type: 'pie',
radius: ['40%', '70%'],
avoidLabelOverlap: false,
padAngle: 2,
itemStyle: {
borderRadius: 8
},
emphasis: {
label: {
show: true,
fontSize: 10,
}
},
labelLine: {
length: 1,
length2: 5,
},
data: []
}]
},
// 关系图配置项
graphOption: {
tooltip: {},
legend: [],
series: [{
name: '板块关联',
type: 'graph',
layout: 'none',
data: [],
links: [],
categories: [],
roam: true, // 允许拖拽和缩放
label: {
show: true,
position: 'right',
formatter: '{b}'
},
labelLayout: {
hideOverlap: true
},
scaleLimit: {
min: 0.4,
max: 2
},
lineStyle: {
color: 'source',
curveness: 0.3
}
}]
},
}
},
onLoad(e) {
this.activeIndex = e.index
this.contentTop = this.navH + 20 / 750 * inject('windowWidth')
},
onReady() {
// 页面就绪后,若默认选中的是板块分布,初始化饼图
//if (this.activeType === 0) {
//this.initPieChart(); // 初始化关系图
//} else if (this.activeType === 1) {
// 初始化饼图
//}
},
methods: {
getStockHeatType(stock) {
// 假设通过连板数计算热度(可根据实际业务逻辑调整)
const days = stock.continuous_days_num || 0;
if (days >= this.RISK_THRESHOLDS.CRITICAL) { // ≥5连板 → 高热度
return 'high';
} else if (days >= this.RISK_THRESHOLDS.HIGH) { // 3-4连板 → 中热度
return 'medium';
} else if (days >= this.RISK_THRESHOLDS.MEDIUM) { // 2连板 → 低热度
return 'low';
} else { // <2连板 → 冷门(无热度)
return 'none';
}
},
//解析连板数(从"2天2板"格式中提取数字)
parseContinuousDays(continuousDaysStr) {
if (!continuousDaysStr) return 0;
const match = continuousDaysStr.match(/(\d+)天/);
return match ? Number(match[1]) : 0;
},
// 风险等级判断函数
getRiskLevel(days) {
const {
RISK_THRESHOLDS,
RISK_COLORS
} = this;
if (days >= RISK_THRESHOLDS.CRITICAL) {
return {
level: "极高",
color: RISK_COLORS.critical.color,
bg: RISK_COLORS.critical.bg,
border: RISK_COLORS.critical.border,
status: "缩量一字,高风险",
};
}
if (days >= RISK_THRESHOLDS.HIGH) {
return {
level: "高",
color: RISK_COLORS.high.color,
bg: RISK_COLORS.high.bg,
border: RISK_COLORS.high.border,
status: "放量分歧,需观察",
};
}
if (days >= RISK_THRESHOLDS.MEDIUM) {
return {
level: "中",
color: RISK_COLORS.medium.color,
bg: RISK_COLORS.medium.bg,
border: RISK_COLORS.medium.border,
status: "正常波动",
};
}
return {
level: "低",
color: RISK_COLORS.low.color,
bg: RISK_COLORS.low.bg,
border: RISK_COLORS.low.border,
status: "健康",
};
},
// 计算高位股统计数据
calculateHighPositionStats() {
if (!this.originData || !this.originData.stocks) return;
// 1. 筛选高位股:连板数 >= 2 的股票 + 补充连板数和风险信息
const highPositionStocks = this.originData.stocks
.filter(stock => {
const days = this.parseContinuousDays(stock.continuous_days);
return days >= 2;
})
.map(stock => {
const days = this.parseContinuousDays(stock.continuous_days);
const riskInfo = this.getRiskLevel(days);
return {
...stock,
continuous_days_num: days, // 提取纯数字的连板数
risk_info: riskInfo // 风险等级信息
};
})
// 2. 按连板天数降序排列
.sort((a, b) => b.continuous_days_num - a.continuous_days_num);
// 3. 赋值给页面渲染用的列表(
this.highPositionStockList = highPositionStocks
// 4. 原有统计数据计算逻辑(保持不变)
const totalCount = highPositionStocks.length;
const maxDays = highPositionStocks.length ?
Math.max(...highPositionStocks.map(s => this.parseContinuousDays(s.continuous_days))) :
0;
const totalDays = highPositionStocks.reduce((sum, stock) => {
return sum + this.parseContinuousDays(stock.continuous_days);
}, 0);
const avgDays = totalCount > 0 ? (totalDays / totalCount).toFixed(1) : 0;
// 5. 更新统计数据
this.highPositionStats = {
total_count: totalCount,
avg_continuous_days: avgDays,
max_continuous_days: maxDays
};
// 6. 计算风险等级
this.calculateRiskAssessment();
},
// 计算风险评估
calculateRiskAssessment() {
const {
avg_continuous_days,
max_continuous_days,
total_count
} = this.highPositionStats;
const avgDays = Number(avg_continuous_days) || 0;
const maxDays = Number(max_continuous_days) || 0;
const totalCount = Number(total_count) || 0;
// 计算风险评分(和参考代码一致)
const score = avgDays * 2 + maxDays * 0.5 + totalCount * 0.3;
// 根据评分确定风险等级和颜色
if (score >= 20) {
this.riskAssessment = {
level: "高风险",
color: "#ef4444"
};
} else if (score >= 12) {
this.riskAssessment = {
level: "中风险",
color: "#f97316"
};
} else if (score >= 6) {
this.riskAssessment = {
level: "偏高",
color: "#eab308"
};
} else {
this.riskAssessment = {
level: "正常",
color: "#22c55e"
};
}
},
getHeatColor(value, max) {
// 处理边界最大值为0时直接返回绿色
if (max === 0) return '#01AB5D';
const ratio = value / max;
// 找到第一个满足「占比 > 阈值」的等级(因数组从高到低排序,匹配第一个即最高等级)
const matchedLevel = this.HEAT_LEVELS.find(level => ratio > level.threshold);
return matchedLevel ? matchedLevel.color : '#01AB5D';
},
// 切换标签
async switchTab(index) {
this.activeType = index;
switch (index) {
case 0:
//this.$refs.graphChartRef && this.initGraphChart(); // 增加存在性判断
this.$refs.chartRef && this.initPieChart(); // 增加存在性判断
break;
case 1:
//this.$refs.chartRef && this.initPieChart(); // 增加存在性判断
this.initWordCloud();
break;
case 2:
this.initWordCloud();
break;
}
},
getPreviousDayDate() {
const now = new Date();
// 步骤2将系统时间格式化为 YYYY-MM-DD 格式(适配后续校验逻辑)
const currentYear = now.getFullYear();
const currentMonth = String(now.getMonth() + 1).padStart(2, '0');
const currentDay = String(now.getDate()).padStart(2, '0');
const dateStr = `${currentYear}-${currentMonth}-${currentDay}`; // 示例2026-02-04
// 校验输入日期格式是否正确
if (!/^\d{4}-\d{2}-\d{2}$/.test(dateStr)) {
console.error('日期格式错误,请传入 YYYY-MM-DD 格式的日期');
return '';
}
// 创建日期对象(注意:月份是 0 开始的,所以需要处理)
const [year, month, day] = dateStr.split('-').map(Number);
const date = new Date(year, month - 1, day);
// 将日期减一天
date.setDate(date.getDate() - 1);
// 格式化前一天的日期为 YYYYMMDD 格式(补零处理)
const prevYear = date.getFullYear();
const prevMonth = String(date.getMonth() + 1).padStart(2, '0');
const prevDay = String(date.getDate()).padStart(2, '0');
return `${prevYear}${prevMonth}${prevDay}`;
},
/**
* 请求接口数据(优化:动态日期+自动时间戳)
*/
async fetchData() {
try {
// 1. 自动生成当前时间戳(替代固定值)
const timestamp = new Date().getTime();
// 调用上面的函数获取前一天的格式化日期YYYYMMDD
// const formattedDate = this.getPreviousDayDate(this.selectedFullDate);
const formattedDate = this.selectedFullDate;
const baseURL = getBaseURL1();
const requestUrl = `${baseURL}/data/zt/daily/${formattedDate}.json?t=${timestamp}`;
console.log('请求URL', requestUrl); // 打印URL便于调试
const res = await uni.request({
url: requestUrl,
method: 'GET'
});
if (res.statusCode === 200 && res.data) {
this.originData = res.data;
const chartData = this.originData.chart_data || {};
const labels = chartData.labels || [];
const counts = chartData.counts || [];
// 1. 找到counts中的最大值用于计算热度颜色
const maxCount = counts.length > 0 ? Math.max(...counts) : 0;
// 2. 遍历组装bkList包含标题、数量、背景色、占比先保证labels和counts长度一致
let bkList = [];
const maxLen = Math.min(labels.length, counts.length); // 取两者较短的长度,避免越界
for (let i = 0; i < maxLen; i++) {
const title = labels[i];
const count = counts[i] || 0;
// 计算背景色当前count / 最大count → 匹配颜色)
const bgColor = this.getHeatColor(count, maxCount);
// 计算占比保留2位小数可选
const ratio = maxCount === 0 ? 0 : ((count / maxCount) * 100).toFixed(2);
bkList.push({
title, // 板块名称
count, // 数量
bgColor, // 背景色
ratio // 占比(百分比,可选)
});
}
// 3. 核心限制最多显示16条切片操作放在最后
this.bkList = bkList.slice(0, 16);
this.initPieChart();
this.calculateHighPositionStats();
} else {
uni.showToast({
title: '数据请求失败',
icon: 'none'
});
}
} catch (error) {
console.error('请求异常:', error);
uni.showToast({
title: '网络异常',
icon: 'none'
});
}
},
// 初始化关系图(增加容错)
async initGraphChart() {
const chart = await this.$refs.graphChartRef.init(echarts);
this.graphOption.legend = [{
data: mockGraphData.categories.map(a => a.name)
}];
this.graphOption.series[0].data = mockGraphData.nodes;
this.graphOption.series[0].links = mockGraphData.links;
this.graphOption.series[0].categories = mockGraphData.categories;
chart.setOption(this.graphOption);
},
// 初始化饼图(核心修复)
async initPieChart() {
// const Piechart = await this.$refs.chartRef.init(echarts);
// console.log("Piechart", Piechart);
// Piechart.setOption(this.pieOption);
try {
// 处理饼图数据将labels和counts组合成name/value格式
let pieData = [];
const chartData = this.originData.chart_data || {};
const labels = chartData.labels || [];
const counts = chartData.counts || [];
// 遍历组合数据,确保数组长度一致
const maxLen = Math.min(labels.length, counts.length);
for (let i = 0; i < maxLen; i++) {
pieData.push({
name: labels[i], // 板块名称
value: counts[i] // 对应数量
});
}
// 更新饼图配置的data
this.pieOption.series[0].data = pieData.length > 0 ? pieData : [{
value: 10,
name: '科技板块'
},
{
value: 8,
name: '人脑工程'
},
{
value: 9,
name: '商业航天'
}
];
// 初始化ECharts并设置配置
if (this.$refs.chartRef) {
const Piechart = await this.$refs.chartRef.init(echarts);
console.log("Piechart实例创建成功", Piechart);
Piechart.setOption(this.pieOption);
}
} catch (error) {
console.error('饼图初始化失败:', error);
}
},
// 初始化词云
initWordCloud() {
if (this.originData.word_freq_data && Array.isArray(this.originData.word_freq_data)) {
// 直接赋值接口返回的词频数据
this.wordData = this.originData.word_freq_data;
console.log('词云数据赋值完成', this.wordData);
} else {
// 兜底默认数据
this.wordData = [{
name: "脑机",
value: 10000
}, {
name: "航天",
value: 3428
}];
}
//console.log('父页面设置词云数据:', JSON.stringify(this.wordData));
},
handleDateChange(data) {
console.log('从日历组件接收的参数:', {
currentZtCount: data.item?.zt_count,
prevZtCount: data.prevItem?.zt_count
});
// 赋值到父页面的变量中
this.selectedYearMonth = data.yearMonth;
this.selectedFullDate = data.fullDate ? data.fullDate.replace(/-/g, '') : '';
this.selectedItem = data.item;
// 2. 格式化日期:年-月-日 → 月日(如 2026-01-14 → 1月14日
if (data.fullDate) {
const [year, month, day] = data.fullDate.split('-').map(Number);
this.tabTypes[0].data = `${month}${day}`;
}
// 3. 赋值涨停家数优先取item中的zt_count无数据则显示0
const ztCount = data.item?.zt_count ?? 0;
this.tabTypes[1].data = ztCount.toString(); // 转为字符串保证格式统一
this.number_limit_stocks = ztCount.toString();
// ===== 核心修改新增0值判断逻辑 =====
const prevZtCount = data.prevItem?.zt_count ?? 0; // 上一天的涨停家数
// 条件当天或上一天的zt_count为0 → 差值直接赋值0否则计算真实差值
const changeValue = (ztCount === 0 || prevZtCount === 0) ?
0 :
ztCount - prevZtCount;
this.tabTypes[1].change = changeValue;
// =======================================
// ===== 新增:判断选中日期是否为系统当天,若是则日期减一天 =====
if (this.selectedFullDate) {
// 获取系统当前日期并格式化为 YYYYMMDD
const today = new Date();
const todayYear = today.getFullYear();
const todayMonth = String(today.getMonth() + 1).padStart(2, '0');
const todayDay = String(today.getDate()).padStart(2, '0');
const todayFormatted = `${todayYear}${todayMonth}${todayDay}`;
// 判断选中日期是否等于系统当天
if (this.selectedFullDate === todayFormatted) {
// 创建选中日期的Date对象
const selectedDate = new Date(
parseInt(this.selectedFullDate.substring(0, 4)), // 年
parseInt(this.selectedFullDate.substring(4, 6)) - 1, // 月月份从0开始
parseInt(this.selectedFullDate.substring(6, 8)) // 日
);
// 将日期减一天
selectedDate.setDate(selectedDate.getDate() - 1);
// 格式化前一天的日期为 YYYYMMDD 格式(补零处理)
const prevYear = selectedDate.getFullYear();
const prevMonth = String(selectedDate.getMonth() + 1).padStart(2, '0');
const prevDay = String(selectedDate.getDate()).padStart(2, '0');
const prevDateFormatted = `${prevYear}${prevMonth}${prevDay}`;
// 更新选中的日期为前一天
this.selectedFullDate = prevDateFormatted;
console.log(`选中日期为当天(${todayFormatted}),已自动调整为前一天:`, prevDateFormatted);
}
}
this.fetchData()
},
bkydAction(index) {
uni.navigateTo({
url: `/pagesStock/stockCenterDetails/bkydmx?index=${index}&data=${this.selectedFullDate}`
})
}
}
}
</script>
<style lang="less">
page {
background-color: #070707;
}
.topBg {
top: 0;
left: 0;
width: 100%;
height: auto;
}
.stockDetailsC {
left: 0;
right: 0;
bottom: calc(55px + env(safe-area-inset-bottom));
}
/* 单行省略样式类 */
.single-line-ellipsis {
max-width: 100%;
width: 120rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
}
/* 数量行样式(可选抽离) */
.count-text {
font-size: 22rpx;
margin-top: 4rpx;
text-align: center;
}
</style>