个股中心

This commit is contained in:
renzhijun
2026-01-31 09:22:58 +08:00
parent 5ffaac8fb2
commit 441f4c7360
6 changed files with 1292 additions and 562 deletions

View File

@@ -11,7 +11,7 @@
<scroll-view scroll-y class="stockDetailsC fixed" :style="'top:'+contentTop+'px;'">
<view>
<view style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 16rpx; margin: 0 20rpx;">
<view @click="list2Index = index" v-for="(item,index) in topLists" :key="index"
<view @click="handleTypeClick(index)" v-for="(item,index) in topLists" :key="index"
style="padding: 12rpx;"
:style="{'border-bottom': (list2Index == index ? '1rpx solid #F2C369' : 'none')}">
<view style="font-size: 24rpx; color: #070707; font-weight: bold; text-align: center;"
@@ -21,8 +21,8 @@
<view style="font-size: 20rpx; font-weight: 400; text-align: center;"
:style="{color: (list2Index == index ? '#BB8520' : '#070707')}">{{item.value}}</view>
</view>
</view>
</view>
<view style="height: 1rpx; margin: 0 20rpx; background-color: #E7E7E7;"></view>
<!-- '股票名称', '涨跌幅', '市值', '成交额', '行业' -->
<view
@@ -33,14 +33,20 @@
</view>
</view>
<!-- '股票名称', '涨跌幅', '市值', '成交额', '行业' 内容 -->
<view v-for="(obj, j) in 10"
style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 10rpx; min-height: 60rpx; margin: 0 20rpx;"
:style="{'background-color': (j % 2 == 0 ? '#fff' : '#FAFAFC')}">
<view v-for="(item,index) in ['云南白药', '+0.04%', '996.85 亿元', '4.44 亿元', '医药生物']" :key="index"
style="padding: 10rpx 0; color: #666666; font-size: 20rpx; font-weight: 500; text-align: center; display: flex; justify-content: center; align-items: center; flex-direction: column;" :style="{color: (index == 0 ? '#222222' : index == 1 ? '#EC3440' : '#666666')}">
<view>{{item}}</view>
<view v-if="index == 0" style="color: #666666; font-size: 20rpx; font-weight: 500;">000768</view>
</view>
<view v-for="(obj, j) in filteredData" @click="itemDetails(obj)"
style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 10rpx; min-height: 60rpx; margin: 0 20rpx;"
:style="{'background-color': (j % 2 == 0 ? '#fff' : '#FAFAFC')}">
<!-- 外层循环每一行数据 -->
<view v-for="(item,index) in getTableItem(obj)" :key="index"
style="padding: 10rpx 0; color: #666666; font-size: 20rpx; font-weight: 500; text-align: center; display: flex; justify-content: center; align-items: center; flex-direction: column;"
:style="{
color: (index == 0 ? '#222222' :
index == 1 ? (item[2] === 'positive' ? '#EC3440' : '#01AB5D') : '#666666')
}">
<view>{{item[0]}}</view>
<view v-if="index == 0" style="color: #666666; font-size: 20rpx; font-weight: 500;">{{item[1]}}</view>
</view>
</view>
<view style="height: 25rpx;"></view>
</view>
@@ -53,12 +59,18 @@
import {
inject
} from 'vue'
import {
marketHeatmap
} from '@/request/api'
export default {
data() {
return {
navH: inject('navHeight'),
contentTop: '',
allStockData: [],
filteredData: [],
currentDate: '', // 最终要赋值的日期
topLists: [{
title: '超大盘股',
value: '>1000亿',
@@ -76,11 +88,90 @@
}
},
onLoad(e) {
this.activeIndex = e.index
this.currentDate = e.currentDate
this.contentTop = this.navH + (20 + 70 + 25) / 750 * inject('windowWidth')
this.marketHeatmap()
},
methods: {
handleTypeClick(index) {
this.list2Index = index;
// 先请求数据,再筛选
this.marketHeatmap();
},
getTableItem(obj) {
// 1. 处理空值,避免 toFixed 调用时报错
const marketCap = obj.market_cap ? obj.market_cap.toFixed(2) : '0.00';
const amount = obj.amount ? obj.amount.toFixed(2) : '0.00';
// 统一处理涨跌幅空值默认0转数字避免字符串干扰判断
const changePercent = obj.change_percent ? Number(obj.change_percent) : 0;
// 2. 处理涨跌幅的符号和类型标记
let changePercentStr = '';
let changeType = ''; // 标记正负positive/negative/zero
if (changePercent > 0) {
changePercentStr = `+${changePercent}%`; // 正数拼接+号
changeType = 'positive';
} else if (changePercent < 0) {
changePercentStr = `${changePercent}%`; // 负数直接显示
changeType = 'negative';
} else {
changePercentStr = '0%'; // 0值统一显示
changeType = 'zero';
}
// 3. 返回数组:涨跌幅位置新增 changeType 用于模板判断颜色
return [
[obj.stock_name, obj.stock_code],
[changePercentStr, '', changeType], // 第三个元素存类型标记
[`${marketCap}亿元`],
[`${amount}亿元`],
[obj.industry || '暂无'] // 处理行业为空的情况
];
},
marketHeatmap() {
let param = {
limit: 500,
date: this.currentDate
}
marketHeatmap(param).then(res => {
// 存储原始数据
this.allStockData = res.data || [];
// 调用筛选方法
this.filterStockByMarketCap();
}).catch(error => {
})
},
// 根据市值区间筛选数据
filterStockByMarketCap() {
const {
list2Index,
allStockData
} = this;
let filtered = [];
switch (list2Index) {
case 0: // 超大盘股(>1000亿
filtered = allStockData.filter(item => item.market_cap > 1000);
break;
case 1: // 大盘股500-1000亿
filtered = allStockData.filter(item => item.market_cap >= 500 && item.market_cap <= 1000);
break;
case 2: // 中盘股100-500亿
filtered = allStockData.filter(item => item.market_cap >= 100 && item.market_cap <= 500);
break;
default:
filtered = allStockData;
}
this.filteredData = filtered;
},
itemDetails(item) {
uni.navigateTo({
url: '/pagesStock/stockCenterDetails/stockCenterDetails?code=' + item.stock_code
})
},
}
}
</script>
@@ -123,8 +214,8 @@
.stockDetailsC {
left: 25rpx;
width: calc(100vw - 50rpx);
bottom: env(safe-area-inset-bottom);
background-color: white;
bottom: env(safe-area-inset-bottom);
background-color: white;
border-radius: 10rpx;
}
</style>

View File

@@ -25,7 +25,7 @@
</view>
<view style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 16rpx; margin: 0 20rpx;">
<view @click="handleTypeClick(index)" v-for="(item,index) in topLists2" :key="index"
<view @click="handleTypeClick(index)" v-for="(item,index) in topLists2" :key="index"
style="border: 1rpx solid #D2D2D2; padding: 12rpx;"
:style="{border: `1rpx solid ${list2Index == index ? '#F2C369' : '#D2D2D2'}`}">
<view style="font-size: 24rpx; color: #070707; font-weight: bold; text-align: center;"
@@ -39,18 +39,20 @@
<!-- '股票名称', '涨跌幅', '市值', '成交额', '行业' -->
<view
style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 10rpx; background-color: #FAFAFC; line-height: 60rpx; margin: 0 20rpx; margin-top: 20rpx;">
<view v-for="(item,index) in ['股票名称', '涨跌幅', '市值', '成交额', '行业']" :key="index"
<view v-for="(item,index) in ['股票名称', '涨跌幅', '市值', '成交额', '行业']" :key="index"
style="color: #666666; font-size: 20rpx; font-weight: 500; text-align: center;">
{{item}}
</view>
</view>
<!-- '股票名称', '涨跌幅', '市值', '成交额', '行业' 内容 -->
<view v-for="(obj, j) in filteredData" @click="itemDetails(obj)"
style="display: grid; grid-template-columns: repeat(5, 1fr); gap: 10rpx; min-height: 60rpx; margin: 0 20rpx;"
:style="{'background-color': (j % 2 == 0 ? '#fff' : '#FAFAFC')}">
<!-- 外层循环每一行数据 -->
<view v-for="(item,index) in getTableItem(obj)" :key="index"
style="padding: 10rpx 0; color: #666666; font-size: 20rpx; font-weight: 500; text-align: center; display: flex; justify-content: center; align-items: center; flex-direction: column;"
:style="{color: (index == 0 ? '#222222' : index == 1 ? '#EC3440' : '#666666')}">
:style="{ color: (index == 0 ? '#222222' : index == 1 ? (item[2] === 'positive' ? '#EC3440' : '#01AB5D') : '#666666') }">
<view>{{item[0]}}</view>
<view v-if="index == 0" style="color: #666666; font-size: 20rpx; font-weight: 500;">{{item[1]}}
</view>
@@ -80,7 +82,7 @@
</view>
<view @click="allAction(2)"
style="border: 1rpx solid #DCDCDC; border-radius: 5rpx; padding: 2rpx 10rpx; display: flex; align-items: center; justify-content: center;">
<view style="color: #888888; font-size: 22rpx; font-weight: 500;">2026/01/20</view>
<view style="color: #888888; font-size: 22rpx; font-weight: 500;">{{currentDate}}</view>
<image style="width: 11rpx; height: 6rpx; margin-left: 20rpx;"
src="/static/icon/invest/downArrow.png" mode="widthFix"></image>
</view>
@@ -99,12 +101,13 @@
</view>
</view>
<view v-for="(item,index) in 10" :key="index" @click="bkydAction(item)"
<view v-for="(item,index) in marketAlertsList" :key="index" @click="bkydAction(item)"
style="margin: 20rpx; margin-top: 0; background-color: #FAFAFC; border-radius: 10rpx; overflow: hidden; padding: 20rpx 30rpx; font-weight: 500;">
<view style="color: #888888; font-size: 20rpx;">09:54</view>
<view style="color: #888888; font-size: 20rpx;">{{item.time}}</view>
<view style="display: flex; align-items: center; margin-top: 10rpx;">
<view style="color: #2B2B2B; font-weight: bold; font-size: 26rpx; margin-right: 10rpx;">数据交易所
<view style="color: #2B2B2B; font-weight: bold; font-size: 26rpx; margin-right: 10rpx;">
{{item.concept_name}}
</view>
<view
style="color: #EC3440; font-size: 20rpx; border: 1rpx solid #EC3440; border-radius: 15rpx; height: 30rpx; display: flex; align-items: center; justify-content: center; padding: 0 10rpx; box-sizing: border-box;">
@@ -114,11 +117,17 @@
</view>
<view style="flex: 1; font-size: 22rpx; text-align: right;">
<text style="color: #71675D;">板块均涨</text>
<text
style="color: #EC3440; font-weight: bold; margin-left: 10rpx; margin-right: 20rpx;">+6.64%</text>
<text style="color: #EC3440; font-weight: bold;">14</text>
<text :style="{
color: Number(item.alpha) > 0 ? '#EC3440' : '#01AB5D',
fontWeight: 'bold',
marginLeft: '5rpx',
marginRight: '15rpx'
}">
{{ Number(item.alpha) > 0 ? '+' + formatAlpha(item.alpha) : formatAlpha(item.alpha) }}%
</text>
<text style="color: #EC3440; font-weight: bold;">0</text>
<text style="color: #888888; margin: 0 5rpx;">/</text>
<text style="color: #01AB5D; font-weight: bold;">5</text>
<text style="color: #01AB5D; font-weight: bold;">0</text>
</view>
</view>
@@ -126,15 +135,20 @@
style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 20rpx; font-size: 22rpx; color: #71675D; font-weight: 500; margin-top: 15rpx;">
<view style="text-align: left;">
<text>评分</text>
<text style="color: #EC3440; font-weight: bold; margin-left: 10rpx;">56</text>
<text
style="color: #EC3440; font-weight: bold; margin-left: 10rpx;">{{ Math.round(item.final_score) }}</text>
</view>
<view style="text-align: center;">
<text>超额收益</text>
<text style="color: #EC3440; font-weight: bold; margin-left: 10rpx;">+9.28%</text>
<text style="color: #EC3440; font-weight: bold; margin-left: 10rpx;">+ 0%</text>
</view>
<view style="text-align: right;">
<view style="text-align: right;"
v-if="item && Number(item.limit_up_ratio) > 0 && !isNaN(Number(item.limit_up_ratio))">
<text>涨停比</text>
<text style="color: #EC3440; font-weight: bold; margin-left: 10rpx;">19%</text>
<text style="color: #EC3440; font-weight: bold; margin-left: 10rpx;">
{{ formatLimitUpRatio(item.limit_up_ratio, 0) }}
</text>
</view>
</view>
</view>
@@ -154,72 +168,86 @@
<view style="color: #D79412; padding: 0 25rpx;" @click="confirmAction(1)">确定</view>
</view>
<view v-for="(item,index) in typeList" :key="index">
<view style="height: 1rpx; background-color: #EAEAEA; margin: 0 20rpx;"></view>
<view style="display: flex; align-items: center; justify-content: center; height: 80rpx;">
<image style="width: 26rpx; height: 26rpx; margin-right: 18rpx;" :src="item.backIcon" mode="aspectFit"></image>
<view style="min-width: 100rpx; text-align: center; font-size: 24rpx; font-weight: 500; color: #070707;">{{item.title}}</view>
</view>
<view v-for="(item,index) in typeList" :key="index">
<view style="height: 1rpx; background-color: #EAEAEA; margin: 0 20rpx;"></view>
<view style="display: flex; align-items: center; justify-content: center; height: 80rpx;">
<image style="width: 26rpx; height: 26rpx; margin-right: 18rpx;" :src="item.backIcon"
mode="aspectFit"></image>
<view
style="min-width: 100rpx; text-align: center; font-size: 24rpx; font-weight: 500; color: #070707;">
{{item.title}}
</view>
</view>
</view>
</view>
</uni-popup>
<uni-popup ref="datePopup" type="bottom" :safeArea="false">
<view class="detailPopup">
<view
style="height: 120rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 500;">
<view style="color: #727A8E; padding: 0 25rpx;" @click="closeAction(2)">取消</view>
<view style="flex: 1; text-align: center; color: #333333; font-size: 36rpx; font-weight: bold;">选择日期
</view>
<view style="color: #D79412; padding: 0 25rpx;" @click="confirmAction(2)">确定</view>
</view>
<view style="margin: 0 38rpx; padding-bottom: 38rpx;">
<LCCalendar2></LCCalendar2>
</view>
</view>
</uni-popup>
<uni-popup ref="detailPopup" type="bottom" :safeArea="false">
<view class="detailPopup">
<view
style="height: 120rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 500;">
<view style="color: #727A8E; width: 60rpx;"></view>
<view style="flex: 1; text-align: center; color: #333333; font-size: 36rpx; font-weight: bold;">详情
</view>
<view style="color: #D79412; width: 60rpx; display: flex; align-items: center; justify-content: center;" @click="closeAction(3)">
<image style="width: 20rpx; height: 20rpx;" src="/static/icon/home/close.png" mode="widthFix"></image>
</view>
</view>
<view style="height: 1rpx; margin: 0 20rpx; background-color: #E7E7E7;"></view>
<view style="padding: 0 25rpx; box-sizing: border-box; height: 45rpx; margin: 0 45rpx; margin-top: 15rpx; background-color: #FAFAFC;">
<view style="display: flex; align-items: center; height: 100%;">
<view style="color: #666666; font-weight: 500; font-size: 24rpx; margin-right: 10rpx;">相关股票
</view>
<view style="flex: 1; font-size: 22rpx; text-align: right;">
<text style="color: #71675D;">板块均涨</text>
<text
style="color: #EC3440; font-weight: bold; margin-left: 10rpx; margin-right: 20rpx;">+6.64%</text>
<text style="color: #EC3440; font-weight: bold;">14</text>
<text style="color: #888888; margin: 0 5rpx;">/</text>
<text style="color: #01AB5D; font-weight: bold;">5</text>
<text style="color: #71675D; margin-left: 20rpx;">涨停比</text>
<text style="color: #EC3440; font-weight: bold; margin-left: 10rpx;">19%</text>
</view>
</view>
</view>
<view v-for="(item, index) in 10" style="padding: 0 25rpx; box-sizing: border-box; height: 45rpx; margin: 0 45rpx; display: flex; align-items: center; font-weight: 500;"
:style="{ 'background-color': (index % 2 == 0 ? '#fff' : '#FAFAFC')}">
<view style="color: #222222; font-size: 24rpx; font-weight: bold;">科泰电源</view>
<view style="flex: 1; color: #888888; font-size: 20rpx; margin: 0 20rpx;">000880</view>
<view style="color: #EC3440; font-size: 22rpx; font-weight: bold;">+11.02%</view>
</view>
</view>
</uni-popup>
<uni-popup ref="datePopup" type="bottom" :safeArea="false">
<view class="detailPopup">
<view
style="height: 120rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 500;">
<view style="color: #727A8E; padding: 0 25rpx;" @click="closeAction(2)">取消</view>
<view style="flex: 1; text-align: center; color: #333333; font-size: 36rpx; font-weight: bold;">选择日期
</view>
<view style="color: #D79412; padding: 0 25rpx;" @click="confirmAction(2)">确定</view>
</view>
<view style="margin: 0 38rpx; padding-bottom: 38rpx;">
<LCCalendar2 @date-change="handleDateChange"></LCCalendar2>
</view>
</view>
</uni-popup>
<uni-popup ref="detailPopup" type="bottom" :safeArea="false">
<view class="detailPopup">
<view
style="height: 120rpx; display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 500;">
<view style="color: #727A8E; width: 60rpx;"></view>
<view style="flex: 1; text-align: center; color: #333333; font-size: 36rpx; font-weight: bold;">详情
</view>
<view
style="color: #D79412; width: 60rpx; display: flex; align-items: center; justify-content: center;"
@click="closeAction(3)">
<image style="width: 20rpx; height: 20rpx;" src="/static/icon/home/close.png" mode="widthFix">
</image>
</view>
</view>
<view style="height: 1rpx; margin: 0 20rpx; background-color: #E7E7E7;"></view>
<view
style="padding: 0 25rpx; box-sizing: border-box; height: 45rpx; margin: 0 45rpx; margin-top: 15rpx; background-color: #FAFAFC;">
<view style="display: flex; align-items: center; height: 100%;">
<view style="color: #666666; font-weight: 500; font-size: 24rpx; margin-right: 10rpx;">相关股票
</view>
<view style="flex: 1; font-size: 22rpx; text-align: right;">
<text style="color: #71675D;">板块均涨</text>
<text
style="color: #EC3440; font-weight: bold; margin-left: 10rpx; margin-right: 20rpx;">+0.00%</text>
<text style="color: #EC3440; font-weight: bold;">0</text>
<text style="color: #888888; margin: 0 5rpx;">/</text>
<text style="color: #01AB5D; font-weight: bold;">0</text>
<text style="color: #71675D; margin-left: 20rpx;">涨停比</text>
<text style="color: #EC3440; font-weight: bold; margin-left: 10rpx;">0%</text>
</view>
</view>
</view>
<view v-for="(item, index) in conceptStocksList" :key="index"
style="padding: 0 25rpx; box-sizing: border-box; height: 45rpx; margin: 0 45rpx; display: flex; align-items: center; font-weight: 500;"
:style="{ 'background-color': (index % 2 == 0 ? '#fff' : '#FAFAFC')}">
<!-- 股票名称 -->
<view style="color: #222222; font-size: 24rpx; font-weight: bold;">{{ item.name }}</view>
<!-- 股票代码 -->
<view style="flex: 1; color: #888888; font-size: 20rpx; margin: 0 20rpx;">{{ item.code }}</view>
<!-- 涨跌幅动态绑定颜色和格式化显示 -->
<view :style="{ color: getChangeColor(item.change_pct), fontSize: '22rpx', fontWeight: 'bold' }">
{{ formatChangePct(item.change_pct) }}
</view>
</view>
</view>
</uni-popup>
</view>
</template>
@@ -228,47 +256,56 @@
import {
inject
} from 'vue'
import { conceptsDailyTop,marketHeatmap,marketStatistics,marketHotspotOverview } from '@/request/api'
import {
conceptsDailyTop,
marketHeatmap,
marketStatistics,
marketHotspotOverview,
conceptStocks
} from '@/request/api'
export default {
data() {
return {
navH: inject('navHeight'),
contentTop: '',
allStockData:[],
filteredData:[],
currentDate: '', // 最终要赋值的日期
selectedDate: '', // 临时存储选中的日期
allStockData: [],
filteredData: [],
conceptStocksList: [],
topLists: [{
title: '大盘涨跌幅',
value: '+0.31%',
value: '+0.00%',
color: '#EC3440',
backIcon: '/static/icon/gegu/gg-top-0.png'
},
{
title: '涨停/跌停',
value: '+0.31%',
value: '+0.00%',
color: '#070707',
backIcon: '/static/icon/gegu/gg-top-1.png'
},
{
title: '多空对比',
value: '3572/1855',
value: '0/0',
color: '#070707',
backIcon: '/static/icon/gegu/gg-top-2.png'
},
{
title: '今日成交额',
value: '1.5万亿',
value: '0万亿',
color: '#070707',
backIcon: '/static/icon/gegu/gg-top-3.png'
},
{
title: 'A股总市值',
value: '113.8万亿',
value: '0万亿',
color: '#070707',
backIcon: '/static/icon/gegu/gg-top-4.png'
},
{
title: '连板龙头',
value: '10只',
value: '0只',
color: '#F59B38',
backIcon: '/static/icon/gegu/gg-top-5.png'
}
@@ -286,148 +323,341 @@ import { conceptsDailyTop,marketHeatmap,marketStatistics,marketHotspotOverview }
value: '100-500亿',
}
],
list2Index: 0,
list2Index: 0,
typeList: [{
title: '缩量急涨',
backIcon: '/static/icon/gegu/cate-0.png'
},
{
title: '异动',
backIcon: '/static/icon/gegu/cate-1.png'
},
{
title: '急跌',
backIcon: '/static/icon/gegu/cate-2.png'
},
{
title: '急涨',
backIcon: '/static/icon/gegu/cate-3.png'
},
{
title: '放量震荡',
backIcon: '/static/icon/gegu/cate-4.png'
}]
title: '缩量急涨',
backIcon: '/static/icon/gegu/cate-0.png'
},
{
title: '异动',
backIcon: '/static/icon/gegu/cate-1.png'
},
{
title: '急跌',
backIcon: '/static/icon/gegu/cate-2.png'
},
{
title: '急涨',
backIcon: '/static/icon/gegu/cate-3.png'
},
{
title: '放量震荡',
backIcon: '/static/icon/gegu/cate-4.png'
}
],
marketAlertsList: []
}
},
onLoad(e) {
this.activeIndex = e.index
this.contentTop = this.navH + (20 + 70 + 25) / 750 * inject('windowWidth')
//this.conceptsDailyTop()
this.marketHeatmap()
//this.marketStatistics()
//this.marketHotspotOverview()
const now = new Date()
const year = now.getFullYear()
const month = (now.getMonth() + 1).toString().padStart(2, '0')
const day = now.getDate().toString().padStart(2, '0')
this.currentDate = `${year}-${month}-${day}`
},
onShow() {
this.marketHeatmap();
this.marketStatistics()
this.marketHotspotListOverview()
},
methods: {
handleTypeClick(index) {
this.list2Index = index;
// 先请求数据,再筛选
this.marketHeatmap();
},
getTableItem(obj) {
// 先处理空值,避免 toFixed 调用时报错
const marketCap = obj.market_cap ? obj.market_cap.toFixed(2) : '0.00';
const amount = obj.amount ? obj.amount.toFixed(2) : '0.00';
const changePercent = obj.change_percent ? obj.change_percent : 0;
return [
[obj.stock_name, obj.stock_code],
[`${changePercent}%`], // 使用模板字符串更规范
[`${marketCap}亿元`],
[`${amount}亿元`],
[obj.industry || '暂无'] // 处理行业为空的情况
];
},
conceptsDailyTop(){
conceptsDailyTop().then(res=>{
}).catch(error=>{
})
formatAlpha(value) {
// 1. 空值/非数字处理
if (value === null || value === undefined || isNaN(Number(value))) {
return '0.0';
}
// 2. 转数字后保留1位小数
return Number(value).toFixed(1);
},
marketHeatmap(){
handleTypeClick(index) {
this.list2Index = index;
// 先请求数据,再筛选
this.marketHeatmap(this.currentDate);
},
getTableItem(obj) {
// 1. 处理空值,避免 toFixed 调用时报错
const marketCap = obj.market_cap ? obj.market_cap.toFixed(2) : '0.00';
const amount = obj.amount ? obj.amount.toFixed(2) : '0.00';
// 统一处理涨跌幅空值默认0转数字避免字符串干扰判断
const changePercent = obj.change_percent ? Number(obj.change_percent) : 0;
// 2. 处理涨跌幅的符号和类型标记
let changePercentStr = '';
let changeType = ''; // 标记正负positive/negative/zero
if (changePercent > 0) {
changePercentStr = `+${changePercent}%`; // 正数拼接+号
changeType = 'positive';
} else if (changePercent < 0) {
changePercentStr = `${changePercent}%`; // 负数直接显示
changeType = 'negative';
} else {
changePercentStr = '0%'; // 0值统一显示
changeType = 'zero';
}
// 3. 返回数组:涨跌幅位置新增 changeType 用于模板判断颜色
return [
[obj.stock_name, obj.stock_code],
[changePercentStr, '', changeType], // 第三个元素存类型标记
[`${marketCap}亿元`],
[`${amount}亿元`],
[obj.industry || '暂无'] // 处理行业为空的情况
];
},
// 处理涨停比:转百分比 + 四舍五入(可指定保留小数位数)
formatLimitUpRatio(value, decimalPlaces = 0) {
// 1. 先判断值是否有效无效直接返回空或0%
if (!value || isNaN(Number(value))) {
return '0%';
}
// 2. 正常计算逻辑
const percentValue = Number(value) * 100;
const result = decimalPlaces === 0 ? Math.round(percentValue) : percentValue.toFixed(decimalPlaces);
return `${result}%`;
},
conceptsDailyTop() {
conceptsDailyTop().then(res => {
}).catch(error => {
})
},
marketHeatmap(currentDate) {
let param = {
limit: 500
}
marketHeatmap(param).then(res=>{
// 存储原始数据
this.allStockData = res.data || [];
// 调用筛选方法
this.filterStockByMarketCap();
}).catch(error=>{
})
if (currentDate && currentDate !== 'undefined' && currentDate.trim() !== '') {
param.date = currentDate;
}
marketHeatmap(param).then(res => {
this.topLists[2].value = res.statistics.rising_count + "/" + res.statistics.falling_count;
// 存储原始数据
this.allStockData = res.data || [];
// 2. 计算涨停数和跌停数(核心新增逻辑)
// 涨停数:涨幅 >= 9.9% 的股票数量
const limitUpCount = this.allStockData.filter((s) => {
// 做空值/非数字保护避免change_percent异常导致判断错误
const changePercent = Number(s.change_percent);
return !isNaN(changePercent) && changePercent >= 9.9;
}).length;
// 跌停数:跌幅 <= -9.9% 的股票数量
const limitDownCount = this.allStockData.filter((s) => {
const changePercent = Number(s.change_percent);
return !isNaN(changePercent) && changePercent <= -9.9;
}).length;
this.topLists[1].value = limitUpCount + "/" + limitDownCount;
this.topLists[5].value = limitUpCount + "只";
// 调用筛选方法
this.filterStockByMarketCap();
}).catch(error => {
})
},
// 根据市值区间筛选数据
filterStockByMarketCap() {
const { list2Index, allStockData } = this;
let filtered = [];
switch (list2Index) {
case 0: // 超大盘股(>1000亿
filtered = allStockData.filter(item => item.market_cap > 1000);
break;
case 1: // 大盘股(500-1000亿
filtered = allStockData.filter(item => item.market_cap >= 500 && item.market_cap <= 1000);
break;
case 2: // 盘股(100-500亿
filtered = allStockData.filter(item => item.market_cap >= 100 && item.market_cap <= 500);
break;
default:
filtered = allStockData;
}
this.filteredData = filtered;
},
marketStatistics(){
marketStatistics().then(res=>{
}).catch(error=>{
})
// 根据市值区间筛选数据
filterStockByMarketCap() {
const {
list2Index,
allStockData
} = this;
let filtered = [];
switch (list2Index) {
case 0: // 大盘股(>1000亿
filtered = allStockData.filter(item => item.market_cap > 1000);
break;
case 1: // 盘股(500-1000亿
filtered = allStockData.filter(item => item.market_cap >= 500 && item.market_cap <= 1000);
break;
case 2: // 中盘股100-500亿
filtered = allStockData.filter(item => item.market_cap >= 100 && item.market_cap <= 500);
break;
default:
filtered = allStockData;
}
this.filteredData = filtered.slice(0, 10);
},
marketHotspotOverview(){
marketHotspotOverview().then(res=>{
}).catch(error=>{
})
marketStatistics() {
marketStatistics().then(res => {
this.topLists[3].value = this.formatToTrillion(res.summary.total_amount);
// 格式化 total_market_cap 为 114.7 万亿
this.topLists[4].value = this.formatToTrillion(res.summary.total_market_cap);
}).catch(error => {
this.topLists[3].value = '0.0 万亿';
this.topLists[4].value = '0.0 万亿';
})
},
itemDetails(item){
formatToTrillion(num) {
if (typeof num !== 'number' || isNaN(num)) {
return '0.0 万亿'; // 处理非数字的异常情况
}
// 转换为万亿单位(除以 10000并保留 1 位小数
const trillionValue = (num / 10000).toFixed(1);
return `${trillionValue} 万亿`;
},
marketHotspotListOverview() {
let param = {
date: this.currentDate
}
marketHotspotOverview(param).then(res => {
const alerts = res?.data?.alerts || [];
const changePct = res.data.index.change_pct;
let numPct = 0;
// 校验数值有效性,转成数字类型
if (changePct && !isNaN(Number(changePct))) {
numPct = Number(changePct);
}
const roundedPct = Math.round(numPct * 100) / 100;
const fixedPct = roundedPct.toFixed(2);
// 3. 处理正负符号和百分号
let formattedPct = '';
if (roundedPct > 0) {
formattedPct = `+${fixedPct}%`; // 正数拼接+号
} else if (roundedPct < 0) {
formattedPct = `${fixedPct}%`; // 负数直接显示
} else {
formattedPct = '0.00%'; // 0值统一显示
}
// 4. 根据正负值设置颜色
const color = roundedPct > 0 ? '#EC3440' : (roundedPct < 0 ? '#01AB5D' : '#666666');
// 5. 赋值给topLists
this.topLists[0].value = formattedPct;
this.topLists[0].color = color;
// 2. 定义时间排序函数:将 time 字符串(如 "09:42")转换为分钟数进行比较
const sortByTimeDesc = (a, b) => {
// 把 "HH:MM" 格式的时间转成分钟数(比如 09:42 → 9*60+42=582 分钟)
const timeToMinutes = (timeStr) => {
const [hours, minutes] = timeStr.split(':').map(Number);
return hours * 60 + minutes;
};
// 计算两个条目的分钟数倒序排列b - a 实现时间大的在前)
const minutesA = timeToMinutes(a.time);
const minutesB = timeToMinutes(b.time);
return minutesB - minutesA;
};
// 3. 对 alerts 数组进行排序
const sortedAlerts = alerts.sort(sortByTimeDesc);
// 4. 将排序后的数组赋值给页面变量(假设你用的是 Vue可根据实际框架调整
this.marketAlertsList = sortedAlerts;
}).catch(error => {
})
},
itemDetails(item) {
uni.navigateTo({
url: '/pagesStock/stockCenterDetails/stockCenterDetails?code='+item.stock_code
url: '/pagesStock/stockCenterDetails/stockCenterDetails?code=' + item.stock_code
})
},
moreAction() {
uni.navigateTo({
url: '/pages/geGuCenter/detail'
url: '/pages/geGuCenter/detail?currentDate=' + this.currentDate
})
},
allAction(index) {
if (index == 1) {
this.$refs["typePopup"].open()
}else if (index == 2) {
this.$refs["datePopup"].open()
allAction(index) {
if (index == 1) {
this.$refs["typePopup"].open()
} else if (index == 2) {
this.$refs["datePopup"].open()
}
},
closeAction(index) {
if (index == 1) {
this.$refs["typePopup"].close()
}else if (index == 2) {
this.$refs["datePopup"].close()
}else if (index == 3) {
this.$refs["detailPopup"].close()
closeAction(index) {
if (index == 1) {
this.$refs["typePopup"].close()
} else if (index == 2) {
this.$refs["datePopup"].close()
} else if (index == 3) {
this.$refs["detailPopup"].close()
}
},
confirmAction(index) {
if (index == 1) {
this.$refs["typePopup"].close()
}else if (index == 2) {
this.$refs["datePopup"].close()
}
},
bkydAction(item) {
this.$refs["detailPopup"].open()
},
handleDateChange(date) {
this.selectedDate = date
console.log('选中的日期:', date)
},
confirmAction(index) {
if (index == 1) {
this.$refs["typePopup"].close()
} else if (index == 2) {
if (this.selectedDate) {
this.currentDate = this.selectedDate
console.log('最终确认的日期:', this.currentDate)
} else {
// 如果没有选择日期,使用当前日期
const now = new Date()
const year = now.getFullYear()
const month = (now.getMonth() + 1).toString().padStart(2, '0')
const day = now.getDate().toString().padStart(2, '0')
this.currentDate = `${year}-${month}-${day}`
}
this.marketHeatmap(this.currentDate)
this.marketStatistics()
this.marketHotspotListOverview()
this.$refs["datePopup"].close()
}
},
bkydAction(item) {
this.$refs["detailPopup"].open()
this.conceptStocksDetails(item.concept_id)
},
conceptStocksDetails(concept_id) {
console.log("concept_id", concept_id)
conceptStocks(concept_id,{}).then(res => {
if (res.data && res.data.stocks) {
// 将接口数据赋值给列表数组
this.conceptStocksList = res.data.stocks;
} else {
console.warn('接口返回数据格式异常', res);
}
}).catch(error => {
})
},
// 格式化涨跌幅显示(处理正负号、保留两位小数)
formatChangePct(change_pct) {
if (typeof change_pct !== 'number') return '0.00%';
// 正数加+号,负数保留-号,保留两位小数
const symbol = change_pct >= 0 ? '+' : '';
return `${symbol}${change_pct.toFixed(2)}%`;
},
// 获取涨跌幅文字颜色(涨红跌绿,平盘灰色)
getChangeColor(change_pct) {
if (typeof change_pct !== 'number') return '#888888';
if (change_pct > 0) return '#EC3440'; // 上涨:红色
if (change_pct < 0) return '#00B42A'; // 下跌:绿色
return '#888888'; // 平盘:灰色
}
}
}

View File

@@ -404,10 +404,7 @@
itemStyle: {
borderRadius: 8
},
// label: {
// show: false,
// position: 'center'
// },
emphasis: {
label: {
show: true,
@@ -516,6 +513,7 @@
},
onReady() {
this.fetchData()
// 页面就绪后,若默认选中的是板块分布,初始化饼图
//if (this.activeType === 0) {
this.initGraphChart(); // 初始化关系图
@@ -541,6 +539,64 @@
break;
}
},
getPreviousDayDate(dateStr) {
// 校验输入日期格式是否正确
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 requestUrl = `https://valuefrontier.cn/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;
console.log('接口数据请求成功', this.originData.chart_data );
} else {
uni.showToast({
title: '数据请求失败',
icon: 'none'
});
}
} catch (error) {
console.error('请求异常:', error);
uni.showToast({
title: '网络异常',
icon: 'none'
});
}
},
// 初始化关系图(增加容错)
async initGraphChart() {
@@ -557,350 +613,399 @@
// 初始化饼图(核心修复)
async initPieChart() {
const Piechart = await this.$refs.chartRef.init(echarts);
console.log("Piechart", Piechart);
Piechart.setOption(this.pieOption);
// 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() {
this.wordData = [{
name: "脑机",
value: 10000
},
{
name: "航天",
value: 3428
},
{
name: "商业",
value: 1747
},
{
name: "智能",
value: 1692
},
{
name: "量产",
value: 1589
},
{
name: "落地",
value: 1555
},
{
name: "存储芯片",
value: 1487
},
{
name: "医疗",
value: 1348
},
{
name: "马斯克",
value: 1346
},
{
name: "业绩",
value: 1234
},
{
name: "康复",
value: 1143
},
{
name: "机器人",
value: 1127
},
{
name: "洁净室",
value: 1078
},
{
name: "标的",
value: 1072
},
{
name: "设备",
value: 1071
},
{
name: "算力",
value: 1015
},
{
name: "材料",
value: 983
},
{
name: "卫星",
value: 970
},
{
name: "科技",
value: 947
},
{
name: "资产",
value: 828
},
{
name: "半导体",
value: 774
},
{
name: "重估",
value: 750
},
{
name: "人脑",
value: 747
},
{
name: "平台",
value: 737
},
{
name: "产业链",
value: 726
},
{
name: "赛道",
value: 715
},
{
name: "电池",
value: 694
},
{
name: "估值",
value: 689
},
{
name: "景气",
value: 682
},
{
name: "A股",
value: 662
},
{
name: "商业化",
value: 643
},
{
name: "固态",
value: 642
},
{
name: "工程",
value: 642
},
{
name: "军工",
value: 642
},
{
name: "芯片",
value: 615
},
{
name: "医疗器械",
value: 606
},
{
name: "供应链",
value: 585
},
{
name: "弹性",
value: 573
},
{
name: "蓝箭",
value: 551
},
{
name: "市值",
value: 541
},
{
name: "高端",
value: 527
},
{
name: "植入",
value: 523
},
{
name: "耗材",
value: 523
},
{
name: "逻辑",
value: 519
},
{
name: "数据",
value: 512
},
{
name: "服务器",
value: 504
},
{
name: "供应商",
value: 503
},
{
name: "电子",
value: 483
},
{
name: "芳纶",
value: 458
},
{
name: "传闻",
value: 454
},
{
name: "国产化",
value: 453
},
{
name: "营销",
value: 452
},
{
name: "涨价",
value: 450
},
{
name: "临床",
value: 449
},
{
name: "转型",
value: 444
},
{
name: "强脑",
value: 441
},
{
name: "储能",
value: 441
},
{
name: "智能家居",
value: 438
},
{
name: "场景",
value: 435
},
{
name: "港股",
value: 423
},
{
name: "柔性",
value: 422
},
{
name: "人形",
value: 414
},
{
name: "国产",
value: 411
},
{
name: "接口技术",
value: 401
},
{
name: "消费",
value: 399
},
{
name: "创板",
value: 397
},
{
name: "全球",
value: 389
},
{
name: "替代",
value: 389
},
{
name: "融资",
value: 388
},
{
name: "补贴",
value: 369
},
{
name: "管线",
value: 368
},
{
name: "电极",
value: 367
},
{
name: "模态",
value: 364
},
{
name: "国家",
value: 361
},
{
name: "盈利",
value: 359
},
{
name: "测试",
value: 356
},
{
name: "子公司",
value: 354
},
{
name: "实控",
value: 353
},
{
name: "八院",
value: 353
},
{
name: "价格",
value: 352
},
{
name: "旗下",
value: 351
},
{
name: "组件",
value: 346
},
{
name: "电解液",
value: 342
},
{
name: "中标",
value: 340
}
];
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
}];
}
// this.wordData = [{
// name: "脑机",
// value: 10000
// },
// {
// name: "航天",
// value: 3428
// },
// {
// name: "商业",
// value: 1747
// },
// {
// name: "智能",
// value: 1692
// },
// {
// name: "量产",
// value: 1589
// },
// {
// name: "落地",
// value: 1555
// },
// {
// name: "存储芯片",
// value: 1487
// },
// {
// name: "医疗",
// value: 1348
// },
// {
// name: "马斯克",
// value: 1346
// },
// {
// name: "业绩",
// value: 1234
// },
// {
// name: "康复",
// value: 1143
// },
// {
// name: "机器人",
// value: 1127
// },
// {
// name: "洁净室",
// value: 1078
// },
// {
// name: "标的",
// value: 1072
// },
// {
// name: "设备",
// value: 1071
// },
// {
// name: "算力",
// value: 1015
// },
// {
// name: "材料",
// value: 983
// },
// {
// name: "卫星",
// value: 970
// },
// {
// name: "科技",
// value: 947
// },
// {
// name: "资产",
// value: 828
// },
// {
// name: "半导体",
// value: 774
// },
// {
// name: "重估",
// value: 750
// },
// {
// name: "人脑",
// value: 747
// },
// {
// name: "平台",
// value: 737
// },
// {
// name: "产业链",
// value: 726
// },
// {
// name: "赛道",
// value: 715
// },
// {
// name: "电池",
// value: 694
// },
// {
// name: "估值",
// value: 689
// },
// {
// name: "景气",
// value: 682
// },
// {
// name: "A股",
// value: 662
// },
// {
// name: "商业化",
// value: 643
// },
// {
// name: "固态",
// value: 642
// },
// {
// name: "工程",
// value: 642
// },
// {
// name: "军工",
// value: 642
// },
// {
// name: "芯片",
// value: 615
// },
// {
// name: "医疗器械",
// value: 606
// },
// {
// name: "供应链",
// value: 585
// },
// {
// name: "弹性",
// value: 573
// },
// {
// name: "蓝箭",
// value: 551
// },
// {
// name: "市值",
// value: 541
// },
// {
// name: "高端",
// value: 527
// },
// {
// name: "植入",
// value: 523
// },
// {
// name: "耗材",
// value: 523
// },
// {
// name: "逻辑",
// value: 519
// },
// {
// name: "数据",
// value: 512
// },
// {
// name: "服务器",
// value: 504
// },
// {
// name: "供应商",
// value: 503
// },
// {
// name: "电子",
// value: 483
// },
// {
// name: "芳纶",
// value: 458
// },
// {
// name: "传闻",
// value: 454
// },
// {
// name: "国产化",
// value: 453
// },
// {
// name: "营销",
// value: 452
// },
// {
// name: "涨价",
// value: 450
// },
// {
// name: "临床",
// value: 449
// },
// {
// name: "转型",
// value: 444
// },
// {
// name: "强脑",
// value: 441
// },
// {
// name: "储能",
// value: 441
// },
// {
// name: "智能家居",
// value: 438
// },
// {
// name: "场景",
// value: 435
// },
// {
// name: "港股",
// value: 423
// },
// {
// name: "柔性",
// value: 422
// },
// {
// name: "人形",
// value: 414
// },
// {
// name: "国产",
// value: 411
// },
// {
// name: "接口技术",
// value: 401
// },
// {
// name: "消费",
// value: 399
// },
// {
// name: "创板",
// value: 397
// },
// {
// name: "全球",
// value: 389
// },
// {
// name: "替代",
// value: 389
// },
// {
// name: "融资",
// value: 388
// },
// {
// name: "补贴",
// value: 369
// },
// {
// name: "管线",
// value: 368
// },
// {
// name: "电极",
// value: 367
// },
// {
// name: "模态",
// value: 364
// },
// {
// name: "国家",
// value: 361
// },
// {
// name: "盈利",
// value: 359
// },
// {
// name: "测试",
// value: 356
// },
// {
// name: "子公司",
// value: 354
// },
// {
// name: "实控",
// value: 353
// },
// {
// name: "八院",
// value: 353
// },
// {
// name: "价格",
// value: 352
// },
// {
// name: "旗下",
// value: 351
// },
// {
// name: "组件",
// value: 346
// },
// {
// name: "电解液",
// value: 342
// },
// {
// name: "中标",
// value: 340
// }
// ];
//console.log('父页面设置词云数据:', JSON.stringify(this.wordData));
},