1277 lines
46 KiB
Vue
1277 lines
46 KiB
Vue
<template>
|
||
<view>
|
||
<navBar leftText="个股中心" hideNavBg hideBack></navBar>
|
||
<image class="topBg absolute" src="/static/image/index/conceptTopBg.png" mode="widthFix"></image>
|
||
<view class="searchC fixed flex" :style="'top:'+navH+'px;'">
|
||
<image class="icon" src="/static/icon/home/conceptCenter/search.png" mode="widthFix"></image>
|
||
<input class="flex1" type="text" v-model="keywords" placeholder="输入股票代码或名称"
|
||
placeholder-style="color:#eeeeee" confirm-type="search" @input="clickSearch()" />
|
||
</view>
|
||
<view v-if="searchShow" class="searchResultList fixed" :style="'top:'+searchResultTop+'px;'" @click="clickSearchResultBg()">
|
||
<view class="list">
|
||
<view class="item" v-for="(item,index) in searchResultList" :key="index" @click.stop="clickSearchResultListItem(item)">
|
||
{{item.stock_code}} {{item.stock_name}}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<scroll-view scroll-y class="stockDetailsC fixed" :style="'top:'+contentTop+'px;'">
|
||
<view>
|
||
<view style="display: grid; grid-template-columns: repeat(3, 1fr); gap: 16rpx; padding: 20rpx;">
|
||
<view v-for="(item,index) in topLists" :key="index"
|
||
style="padding: 20rpx 26rpx 26rpx 35rpx; border: 1rpx dashed #777777; overflow: hidden; position: relative;">
|
||
<image
|
||
style="position: absolute; top: 0; left: 0; bottom: 0; right: 0; width: 100%; height: 100%;"
|
||
:src="item.backIcon" mode="aspectFill"></image>
|
||
<view style="position: relative; z-index: 1;">
|
||
<view style="font-size: 24rpx; color: #777777; font-weight: 500;">{{item.title}}</view>
|
||
<view style="font-size: 30rpx; margin-top: 10rpx; font-weight: bold;"
|
||
:style="{color: item.color}">{{item.value}}</view>
|
||
</view>
|
||
</view>
|
||
</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"
|
||
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;"
|
||
:style="{color: (list2Index == index ? '#BB8520' : '#070707'), 'background-color': (list2Index == index ? '#FFFAF1' : '#FFFFFF')}">
|
||
{{item.title}}
|
||
</view>
|
||
<view style="font-size: 20rpx; font-weight: 400; text-align: center;"
|
||
:style="{color: (list2Index == index ? '#BB8520' : '#070707')}">{{item.value}}</view>
|
||
</view>
|
||
</view>
|
||
<!-- '股票名称', '涨跌幅', '市值', '成交额', '行业' -->
|
||
<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"
|
||
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 ? (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 @click="moreAction"
|
||
style="display: flex;align-items: center;justify-content: center; height: 80rpx;">
|
||
<view style="font-size: 24rpx; color: #3D455C; font-weight: 500;">查看更多</view>
|
||
<image style="width: 10rpx; height: 19rpx; margin-left: 20rpx;"
|
||
src="/static/icon/home/conceptCenter/next.png" mode="widthFix"></image>
|
||
</view>
|
||
|
||
<view style="height: 1rpx; margin: 0 20rpx; background-color: #E7E7E7;"></view>
|
||
|
||
|
||
<view style="height: 78rpx; display: flex; align-items: center; margin: 0 20rpx;">
|
||
<image style="width: 40rpx; height: 40rpx;" src="/pages/geGuCenter/icon/ydjk-icon.png"
|
||
mode="widthFix"></image>
|
||
<view style="font-size: 28rpx; color: #2B2B2B; font-weight: bold; flex: 1; margin-left: 10rpx;">异动监控
|
||
</view>
|
||
<view @click="allAction(1)"
|
||
style="border: 1rpx solid #DCDCDC; border-radius: 5rpx; padding: 2rpx 10rpx; display: flex; align-items: center; justify-content: center; margin: 0 10rpx;">
|
||
<view style="color: #888888; font-size: 22rpx; font-weight: 500;">全部</view>
|
||
<image style="width: 11rpx; height: 6rpx; margin-left: 40rpx;"
|
||
src="/static/icon/invest/downArrow.png" mode="widthFix"></image>
|
||
</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;">{{currentDate}}</view>
|
||
<image style="width: 11rpx; height: 6rpx; margin-left: 20rpx;"
|
||
src="/static/icon/invest/downArrow.png" mode="widthFix"></image>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
<!-- 图表容器:相对定位(作为右侧最值的定位父级),保留原有宽高 -->
|
||
<view style="width: 100%; height: 600rpx; position: relative; box-sizing: border-box;">
|
||
<l-echart ref="chartRef"></l-echart>
|
||
|
||
<!-- 右侧最上方:最大值(红色#EC3440,带负号、2位小数+%) -->
|
||
<view
|
||
style="position: absolute; top: 0; right: 10rpx; font-size: 24rpx; color: #EC3440; z-index: 99; line-height: 1.2;margin-top: 60rpx;margin-right: 30rpx;"
|
||
>
|
||
{{y2MaxText}}
|
||
</view>
|
||
|
||
<!-- 右侧最下方:最小值(绿色#01AB5D,带负号、2位小数+%) -->
|
||
<view
|
||
style="position: absolute; bottom: 0; right: 10rpx; font-size: 24rpx; color: #01AB5D; z-index: 99; line-height: 1.2;margin-bottom: 110rpx;margin-right: 30rpx;"
|
||
>
|
||
{{y2MinText}}
|
||
</view>
|
||
</view>
|
||
|
||
<view style="height: 1rpx; margin: 0 20rpx; background-color: #E7E7E7;"></view>
|
||
<view style="height: 88rpx; display: flex; align-items: center; margin: 0 20rpx;">
|
||
<image style="width: 40rpx; height: 40rpx;" src="/pages/geGuCenter/icon/ydjk-icon.png"
|
||
mode="widthFix"></image>
|
||
<view style="font-size: 28rpx; color: #2B2B2B; font-weight: bold; flex: 1; margin-left: 10rpx;">
|
||
板块异动明细
|
||
</view>
|
||
</view>
|
||
|
||
<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;">{{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;">
|
||
{{truncateText(item.concept_name, 5)}}
|
||
</view>
|
||
|
||
<view :style="{
|
||
color: alertTypeConfig[item.alert_type]?.color || '#FF7A45',
|
||
fontSize: '20rpx',
|
||
border: '1rpx solid ' + (alertTypeConfig[item.alert_type]?.color || '#FF7A45'),
|
||
borderRadius: '15rpx',
|
||
height: '30rpx',
|
||
display: 'flex',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
padding: '0 10rpx',
|
||
boxSizing: 'border-box'
|
||
}">
|
||
|
||
<image style="width: 18rpx; height: auto;"
|
||
:style="{ filter: alertTypeConfig[item.alert_type]?.filter || '' }"
|
||
src="/pages/geGuCenter/icon/ydjk-zs.png" mode="widthFix">
|
||
</image>
|
||
<view style="margin-left: 10rpx;">
|
||
{{ alertTypeConfig[item.alert_type]?.text || '异动' }}
|
||
</view>
|
||
</view>
|
||
<view style="flex: 1; font-size: 22rpx; text-align: right;">
|
||
<text style="color: #71675D;">板块均涨</text>
|
||
<text :style="{
|
||
color: Number(item.alpha) > 0 ? '#EC3440' : '#01AB5D',
|
||
fontWeight: 'bold',
|
||
marginLeft: '5rpx',
|
||
marginRight: '25rpx'
|
||
}">
|
||
{{ item.formattedAvg }}%
|
||
</text>
|
||
<text :style="{
|
||
color: item.upCount > 0 ? '#EC3440' : '#888888',
|
||
fontWeight: 'bold'
|
||
}">
|
||
{{item.upCount}}涨
|
||
</text>
|
||
<text style="color: #888888; margin: 0 5rpx;">/</text>
|
||
<text :style="{
|
||
color: item.downCount > 0 ? '#01AB5D' : '#888888',
|
||
fontWeight: 'bold'
|
||
}">
|
||
{{item.downCount}}跌
|
||
</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view
|
||
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;">{{ Math.round(item.final_score) }}</text>
|
||
</view>
|
||
<view style="text-align: center;">
|
||
<text>超额收益</text>
|
||
<text style="color: #EC3440; font-weight: bold; margin-left: 10rpx;">+ 0%</text>
|
||
</view>
|
||
|
||
<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;">
|
||
{{ formatLimitUpRatio(item.limit_up_ratio, 0) }}
|
||
</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
|
||
<view style="height: 25rpx;"></view>
|
||
</view>
|
||
</scroll-view>
|
||
|
||
<uni-popup ref="typePopup" 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(1)">取消</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(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>
|
||
</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" style="height: 550rpx;">
|
||
<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: Number(formattedAvg) > 0 ? '#EC3440' : '#01AB5D',
|
||
fontWeight: 'bold',
|
||
marginLeft: '10rpx',
|
||
marginRight: '20rpx'
|
||
}">{{ formattedAvg }}%</text>
|
||
<text :style="{
|
||
color: upCount > 0 ? '#EC3440' : '#888888',
|
||
fontWeight: 'bold'
|
||
}">
|
||
{{upCount}}涨
|
||
</text>
|
||
<text style="color: #888888; margin: 0 5rpx;">/</text>
|
||
<text :style="{
|
||
color: downCount > 0 ? '#01AB5D' : '#888888',
|
||
fontWeight: 'bold'
|
||
}">
|
||
{{downCount}}跌
|
||
</text>
|
||
|
||
<text style="color: #71675D; margin-left: 20rpx;">涨停比</text>
|
||
<text
|
||
style="color: #EC3440; font-weight: bold; margin-left: 10rpx;">{{ formatLimitUpRatio(limit_up_ratio, 0) }}</text>
|
||
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<scroll-view scroll-y="true" show-scrollbar="false" style="height: 360rpx; ">
|
||
<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>
|
||
</scroll-view>
|
||
|
||
</view>
|
||
</uni-popup>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import {
|
||
inject
|
||
} from 'vue'
|
||
import {
|
||
conceptsDailyTop,
|
||
marketHeatmap,
|
||
marketStatistics,
|
||
marketHotspotOverview,
|
||
conceptStocks,
|
||
searchStockInfo,
|
||
stockBasicInfo
|
||
} from '@/request/api'
|
||
const echarts = require('../../uni_modules/lime-echart/static/echarts.min.js');
|
||
|
||
export default {
|
||
data() {
|
||
return {
|
||
navH: inject('navHeight'),
|
||
contentTop: '',
|
||
currentDate: '', // 最终要赋值的日期
|
||
selectedDate: '', // 临时存储选中的日期
|
||
allStockData: [],
|
||
filteredData: [],
|
||
conceptStocksList: [],
|
||
alertTypeConfig: {
|
||
'surge': {
|
||
text: '异动',
|
||
color: '#FF7A45', // rgb(255, 122, 69)
|
||
filter: 'brightness(0) saturate(100%) invert(54%) sepia(60%) saturate(467%) hue-rotate(344deg) brightness(102%) contrast(101%)'
|
||
},
|
||
'shrink_surge_up': {
|
||
text: '缩量急涨',
|
||
color: '#722ED1', // rgb(114, 46, 209)
|
||
filter: 'brightness(0) saturate(100%) invert(24%) sepia(90%) saturate(2865%) hue-rotate(266deg) brightness(87%) contrast(98%)'
|
||
},
|
||
'volume_surge_up': {
|
||
text: '放量急涨',
|
||
color: '#EB2F96', // rgb(235, 47, 150)
|
||
filter: 'brightness(0) saturate(100%) invert(34%) sepia(82%) saturate(1970%) hue-rotate(313deg) brightness(91%) contrast(94%)'
|
||
},
|
||
'volume_oscillation': {
|
||
text: '放量震荡',
|
||
color: '#13C2C2', // rgb(19, 194, 194)
|
||
filter: 'brightness(0) saturate(100%) invert(71%) sepia(62%) saturate(487%) hue-rotate(142deg) brightness(91%) contrast(93%)'
|
||
},
|
||
'surge_up': {
|
||
text: '急涨',
|
||
color: '#FF4D4F', // rgb(255, 77, 79)
|
||
filter: 'brightness(0) saturate(100%) invert(42%) sepia(93%) saturate(727%) hue-rotate(346deg) brightness(102%) contrast(104%)'
|
||
},
|
||
'surge_down': {
|
||
text: '急跌',
|
||
color: '#52C41A', // rgb(82, 196, 26)
|
||
filter: 'brightness(0) saturate(100%) invert(68%) sepia(65%) saturate(456%) hue-rotate(71deg) brightness(91%) contrast(86%)'
|
||
},
|
||
'shrink_surge_down': {
|
||
text: '缩量急跌',
|
||
color: '#FF7A45', // rgb(255, 122, 69)
|
||
filter: 'brightness(0) saturate(100%) invert(54%) sepia(60%) saturate(467%) hue-rotate(344deg) brightness(102%) contrast(101%)'
|
||
}
|
||
},
|
||
topLists: [{
|
||
title: '大盘涨跌幅',
|
||
value: '+0.00%',
|
||
color: '#EC3440',
|
||
backIcon: '/static/icon/gegu/gg-top-0.png'
|
||
},
|
||
{
|
||
title: '涨停/跌停',
|
||
value: '+0.00%',
|
||
color: '#070707',
|
||
backIcon: '/static/icon/gegu/gg-top-1.png'
|
||
},
|
||
{
|
||
title: '多空对比',
|
||
value: '0/0',
|
||
color: '#070707',
|
||
backIcon: '/static/icon/gegu/gg-top-2.png'
|
||
},
|
||
{
|
||
title: '今日成交额',
|
||
value: '0万亿',
|
||
color: '#070707',
|
||
backIcon: '/static/icon/gegu/gg-top-3.png'
|
||
},
|
||
{
|
||
title: 'A股总市值',
|
||
value: '0万亿',
|
||
color: '#070707',
|
||
backIcon: '/static/icon/gegu/gg-top-4.png'
|
||
},
|
||
{
|
||
title: '连板龙头',
|
||
value: '0只',
|
||
color: '#F59B38',
|
||
backIcon: '/static/icon/gegu/gg-top-5.png'
|
||
}
|
||
],
|
||
topLists2: [{
|
||
title: '超大盘股',
|
||
value: '(>1000亿)',
|
||
},
|
||
{
|
||
title: '大盘股',
|
||
value: '(500-1000亿)',
|
||
},
|
||
{
|
||
title: '中盘股',
|
||
value: '(100-500亿)',
|
||
}
|
||
],
|
||
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'
|
||
}
|
||
],
|
||
marketAlertsList: [],
|
||
formattedAvg: 0,
|
||
upCount: 0,
|
||
downCount: 0,
|
||
limit_up_ratio: 0,
|
||
searchResultTop:'', //搜索结果
|
||
contentTop: '',
|
||
keywords: '', //搜索关键字
|
||
searchShow:false, //是否展示搜索结果
|
||
searchResultList:[], //搜索结果
|
||
selectSearchStockInfo:null, //选中的搜索股票信息
|
||
isShowTime:false,
|
||
ec: { lazyLoad: true }, // 延迟加载 ECharts
|
||
chart: null,
|
||
y2MaxText: '', // 右侧顶部最大值文本(例:2.36% / -0.89%)
|
||
y2MinText: '' // 右侧底部最小值文本(例:-3.12% / 0.56%)
|
||
|
||
|
||
}
|
||
},
|
||
onLoad(e) {
|
||
this.activeIndex = e.index
|
||
this.searchResultTop = this.navH + (20 + 70) / 750 * inject('windowWidth')
|
||
this.contentTop = this.navH + (20 + 70 + 25) / 750 * inject('windowWidth')
|
||
this.conceptsDailyTop()
|
||
|
||
// 获取当前日期,并减去一天
|
||
const now = new Date()
|
||
// 核心修改:将日期减去 1 天(1 天 = 24 * 60 * 60 * 1000 毫秒)
|
||
// now.setTime(now.getTime() - 24 * 60 * 60 * 1000)
|
||
|
||
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.isShowTime=false;
|
||
this.marketHeatmap();
|
||
this.marketStatistics()
|
||
|
||
this.marketHotspotListOverview()
|
||
},
|
||
methods: {
|
||
truncateText(text, length) {
|
||
if (!text) return ''; // 处理空值,避免报错
|
||
return text.length > length
|
||
? text.substring(0, length) + '...'
|
||
: text;
|
||
},
|
||
/**
|
||
* 点击搜索
|
||
*/
|
||
clickSearch() {
|
||
if(this.keywords) {
|
||
this.getSearchStockInfoListData()
|
||
}else
|
||
this.selectSearchStockInfo = null
|
||
},
|
||
/**
|
||
* 点击搜索结果背景
|
||
*/
|
||
clickSearchResultBg()
|
||
{
|
||
this.searchShow = false
|
||
},
|
||
/**
|
||
* 点击搜索结果列表项
|
||
*/
|
||
clickSearchResultListItem(item) {
|
||
this.selectSearchStockInfo = item
|
||
this.searchShow = false
|
||
this.getStockBasicInfoData()
|
||
//this.getQuoteDetailsData()
|
||
},
|
||
/**
|
||
* 根据输入内容获取搜索列表项
|
||
*/
|
||
getSearchStockInfoListData() {
|
||
let param = {q:this.keywords,limit:10}
|
||
searchStockInfo(param).then(res=>{
|
||
this.searchResultList = res.data
|
||
this.searchShow = this.searchResultList.length>0
|
||
}).catch(error=>{
|
||
|
||
})
|
||
},
|
||
/**
|
||
* 获取股票基本信息
|
||
*/
|
||
getStockBasicInfoData() {
|
||
let code = this.stockCode
|
||
if (this.selectSearchStockInfo) {
|
||
code = this.selectSearchStockInfo.stock_code
|
||
}
|
||
// stockBasicInfo(code).then(res=>{
|
||
// this.stockBasicInfo = res.data
|
||
// this.navTitle = res.data.SECNAME+'('+res.data.SECCODE+')'
|
||
// }).catch(error=>{
|
||
|
||
// })
|
||
uni.navigateTo({
|
||
url: '/pagesStock/stockCenterDetails/stockCenterDetails?code=' + code
|
||
})
|
||
},
|
||
/**
|
||
* 获取股票当前市场价格信息
|
||
*/
|
||
getQuoteDetailsData() {
|
||
let code = this.stockCode
|
||
if (this.selectSearchStockInfo) {
|
||
code = this.selectSearchStockInfo.stock_code
|
||
}
|
||
quoteDetailsInfo(code).then(res=>{
|
||
this.quoteDetailsInfo = res.data
|
||
}).catch(error=>{
|
||
|
||
})
|
||
},
|
||
formatAlpha(value) {
|
||
// 1. 空值/非数字处理
|
||
if (value === null || value === undefined || isNaN(Number(value))) {
|
||
return '0.0';
|
||
}
|
||
// 2. 转数字后保留1位小数
|
||
return Number(value).toFixed(1);
|
||
},
|
||
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
|
||
}
|
||
if(this.isShowTime){
|
||
|
||
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.slice(0, 10);
|
||
},
|
||
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 万亿';
|
||
})
|
||
},
|
||
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 data = res?.data;
|
||
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;
|
||
|
||
// ========== 新增:处理每个异动条目的均涨/涨数/跌数 ==========
|
||
const processedAlerts = alerts.map(alertItem => {
|
||
// 1. 获取当前异动条目下的股票列表(假设字段是 stocks,需根据实际接口调整)
|
||
const stocks = alertItem.stocks || [];
|
||
|
||
// 2. 过滤有效股票(change_pct 非空且是数字)
|
||
const validStocks = stocks.filter(s => s.change_pct != null && !isNaN(Number(s
|
||
.change_pct)));
|
||
|
||
// 3. 计算板块均涨
|
||
const avgChange = validStocks.length > 0 ?
|
||
validStocks.reduce((sum, s) => sum + Number(s.change_pct), 0) / validStocks
|
||
.length :
|
||
0;
|
||
|
||
// 4. 计算上涨/下跌股票数量
|
||
const upCount = validStocks.filter(s => Number(s.change_pct) > 0).length;
|
||
const downCount = validStocks.filter(s => Number(s.change_pct) < 0).length;
|
||
|
||
// 5. 格式化均涨值(保留2位小数,处理正负号)
|
||
const roundedAvg = Math.round(avgChange * 100) / 100; // 四舍五入保留2位
|
||
const formattedAvg = roundedAvg > 0 ? `+${roundedAvg.toFixed(2)}` : roundedAvg
|
||
.toFixed(2);
|
||
|
||
// 6. 返回原数据 + 新增计算字段
|
||
return {
|
||
...alertItem,
|
||
alpha: avgChange, // 供模板中判断颜色和显示数值
|
||
upCount: upCount, // 上涨股票数
|
||
downCount: downCount, // 下跌股票数
|
||
formattedAvg: formattedAvg // 格式化后的均涨值(带正负号)
|
||
};
|
||
});
|
||
|
||
|
||
|
||
|
||
// 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 = processedAlerts.sort(sortByTimeDesc);
|
||
// 赋值给页面变量的是处理+排序后的数组
|
||
this.marketAlertsList = sortedAlerts;
|
||
// ========== 初始化折线图 ==========
|
||
this.initChart(data.index.timeline, processedAlerts);
|
||
}).catch(error => {
|
||
|
||
})
|
||
},
|
||
|
||
async initChart(timeline, alerts) {
|
||
// 无数据直接return
|
||
if (!timeline || timeline.length === 0) return;
|
||
|
||
const chart = await this.$refs.chartRef.init(echarts);
|
||
this.chartInstance = chart;
|
||
|
||
// 1. 提取基础数据:X轴时间、Y轴价格、change_pct(用于右侧最值)
|
||
const xAxisTime = timeline.map(item => item.time?.trim() || ''); // 时间去空格,统一格式
|
||
const yAxisPrice = timeline.map(item => Number(item.price) || 0); // 价格兜底0,避免NaN
|
||
// 提取timeline中的change_pct并转数字,过滤无效值
|
||
const changePctList = timeline
|
||
.map(item => Number(item.change_pct))
|
||
.filter(val => !isNaN(val) && val !== null && val !== undefined);
|
||
|
||
// 2. 处理第一个Y轴(上证指数价格):动态计算最值+上下缓冲(增加空值判断,避免报错)
|
||
const validPrices = yAxisPrice.filter(val => val !== 0 && !isNaN(val));
|
||
const priceMin = validPrices.length > 0 ? Math.min(...validPrices) : 0;
|
||
const priceMax = validPrices.length > 0 ? Math.max(...validPrices) : 0;
|
||
const priceRange = priceMax - priceMin;
|
||
const yAxisMin = priceRange > 0 ? (priceMin - priceRange * 0.1) : priceMin;
|
||
const yAxisMax = priceRange > 0 ? (priceMax + priceRange * 0.25) : priceMax;
|
||
|
||
// 3. 处理change_pct最值:格式化(保留2位+带%+保留负号)赋值给页面变量
|
||
let y2Min = 0, y2Max = 0;
|
||
if (changePctList.length > 0) {
|
||
y2Min = Math.min(...changePctList);
|
||
y2Max = Math.max(...changePctList);
|
||
this.y2MaxText = Number(y2Max).toFixed(2) + '%';
|
||
this.y2MinText = Number(y2Min).toFixed(2) + '%';
|
||
} else {
|
||
this.y2MaxText = '0.00%';
|
||
this.y2MinText = '0.00%';
|
||
}
|
||
|
||
// 4. 告警点基础处理:同时间保留最高评分(小程序全兼容,带统计)
|
||
const alertObj = {};
|
||
let totalAlert = 0; // 过滤后有效告警总数量
|
||
let sameTimeCount = 0; // 相同时间的告警去重数量
|
||
let sameScoreCount = 0; // 相同时间且相同评分数量
|
||
|
||
alerts.forEach(alert => {
|
||
if (!alert) return; // 跳过空对象
|
||
const alertTime = alert.time?.trim() || ''; // 时间去空格,兜底空字符串
|
||
const alertScore = Number(alert.importance_score); // 转数字
|
||
if (alertTime === '' || isNaN(alertScore)) return; // 过滤空时间/非数字评分
|
||
|
||
// 时间匹配:xAxisTime也做去空格匹配
|
||
const idx = xAxisTime.findIndex(t => t?.trim() === alertTime);
|
||
if (idx === -1) return; // 时间不匹配直接跳过
|
||
|
||
totalAlert++;
|
||
// 同时间保留最高评分核心逻辑
|
||
if (!alertObj[alertTime]) {
|
||
alertObj[alertTime] = { ...alert, idx, importance_score: alertScore };
|
||
} else {
|
||
sameTimeCount++;
|
||
const existAlert = alertObj[alertTime];
|
||
if (alertScore > existAlert.importance_score) {
|
||
alertObj[alertTime] = { ...alert, idx, importance_score: alertScore };
|
||
} else if (alertScore === existAlert.importance_score) {
|
||
sameScoreCount++;
|
||
}
|
||
}
|
||
});
|
||
|
||
// ===== 核心修改:10分钟时间段分组(如09:40-09:50),组内按评分降序取第一条 =====
|
||
// 辅助函数1:将时间字符串(09:42)转为分钟数(便于计算分组)
|
||
const timeToMinutes = (timeStr) => {
|
||
const [hour, minute] = timeStr.split(':').map(Number);
|
||
return hour * 60 + minute;
|
||
};
|
||
// 辅助函数2:根据分钟数,生成所属的10分钟时间段(如09:42→09:40-09:50)
|
||
const get10MinGroup = (minutes) => {
|
||
// 取整10分钟作为分组起点(如42分→40分,35分→30分,5分→0分)
|
||
const startMin = Math.floor(minutes / 10) * 10;
|
||
const endMin = startMin + 9; // 分组终点(起点+9分钟)
|
||
// 转成时间字符串并补0,返回「HH:MM-HH:MM」格式
|
||
const formatTime = (m) => {
|
||
const h = Math.floor(m / 60).toString().padStart(2, '0');
|
||
const mi = (m % 60).toString().padStart(2, '0');
|
||
return `${h}:${mi}`;
|
||
};
|
||
return `${formatTime(startMin)}-${formatTime(endMin)}`;
|
||
};
|
||
// 辅助函数3:10分钟分组核心逻辑→分组归类→组内降序→取第一条
|
||
const filterBy10MinGroup = (alertObj) => {
|
||
// 步骤1:提取alertObj中所有有效时间,转为[{group: '09:40-09:50', score: 90, data: 告警数据}, ...]
|
||
const alertGroupList = Object.keys(alertObj)
|
||
.filter(time => time && time.includes(':')) // 过滤无效时间
|
||
.map(time => {
|
||
const minutes = timeToMinutes(time);
|
||
return {
|
||
group: get10MinGroup(minutes), // 所属10分钟分组
|
||
score: alertObj[time].importance_score, // 告警评分
|
||
data: alertObj[time] // 原始告警数据
|
||
};
|
||
});
|
||
|
||
if (alertGroupList.length === 0) return {}; // 无有效告警,直接返回空
|
||
|
||
// 步骤2:按分组聚合,key=分组名,value=该组下所有告警
|
||
const groupMap = {};
|
||
alertGroupList.forEach(item => {
|
||
if (!groupMap[item.group]) {
|
||
groupMap[item.group] = [];
|
||
}
|
||
groupMap[item.group].push(item);
|
||
});
|
||
|
||
// 步骤3:遍历每个分组,按评分**降序**排序,取第一条(评分最高的)
|
||
const finalAlertObj = {};
|
||
Object.keys(groupMap).forEach(groupName => {
|
||
const groupItems = groupMap[groupName];
|
||
// 降序排序(评分高的在前,相同评分保留原顺序)
|
||
const sortedItems = groupItems.sort((a, b) => b.score - a.score);
|
||
// 取分组内第一条(评分最高的),按原始时间作为key
|
||
const topItem = sortedItems[0];
|
||
finalAlertObj[topItem.data.time] = topItem.data;
|
||
});
|
||
|
||
return finalAlertObj;
|
||
};
|
||
|
||
// 先执行基础去重,再执行10分钟分组筛选
|
||
const filteredAlertObj = filterBy10MinGroup(alertObj);
|
||
|
||
// ===== 多维度日志打印(更新分组统计,更直观)=====
|
||
const originalKeyLen = Object.keys(alertObj).length; // 基础去重后数量
|
||
const filteredKeyLen = Object.keys(filteredAlertObj).length; // 10分钟分组后数量
|
||
// 新增:打印分组详情(便于排查分组是否正确)
|
||
const groupDetail = Object.keys(filteredAlertObj).map(time => {
|
||
const minutes = timeToMinutes(time);
|
||
return { time, group: get10MinGroup(minutes), score: filteredAlertObj[time].importance_score };
|
||
});
|
||
console.log('===== 告警点处理全统计(10分钟分组版)=====');
|
||
console.log('1. 过滤后有效告警总数量:', totalAlert);
|
||
console.log('2. 相同时间的告警去重数量:', sameTimeCount);
|
||
console.log('3. 相同时间且相同评分数量:', sameScoreCount);
|
||
console.log('4. 基础去重后(同时间最高评分)数量:', originalKeyLen);
|
||
console.log('5. 10分钟分组后(每组取最高评分)数量:', filteredKeyLen);
|
||
console.log('6. 分组详情(时间→所属分组→评分):', groupDetail);
|
||
console.log('7. 分组后最终告警详情:', filteredAlertObj);
|
||
|
||
// 5. 格式化ECharts所需的markPoint数据(保留原逻辑,兼容已修复的显示配置)
|
||
const alertPoints = Object.values(filteredAlertObj).map(alert => {
|
||
// 新增:校验索引有效性,避免x/y轴匹配失败(核心修复显示问题)
|
||
const validIdx = !isNaN(alert.idx) && alert.idx >= 0 && alert.idx < xAxisTime.length ? alert.idx : 0;
|
||
const xVal = xAxisTime[validIdx] || '';
|
||
const yVal = !isNaN(yAxisPrice[validIdx]) ? yAxisPrice[validIdx] : 0;
|
||
return {
|
||
name: alert.concept_name || '未知概念', // 概念名兜底
|
||
coord: [xVal, yVal], // 确保x轴值严格匹配xAxis.data,y轴值有效
|
||
value:yVal,
|
||
itemStyle: { color: '#FF4444' }, // 告警点红色
|
||
label: {
|
||
formatter(){
|
||
return alert.concept_name
|
||
},
|
||
show: true,
|
||
position: 'top',
|
||
fontSize: 10,
|
||
color: '#FF4444',
|
||
fontWeight: '500',
|
||
distance: 5
|
||
}
|
||
|
||
};
|
||
}).filter(Boolean); // 最终兜底过滤无效项
|
||
console.log('8. 最终ECharts告警点数据(10分钟分组):', alertPoints);
|
||
|
||
// 6. ECharts核心配置项(保留所有显示修复:show/z/描边等)
|
||
const option = {
|
||
grid: { left: '4%', right: '8%', bottom: '8%', top: '10%', containLabel: true },
|
||
xAxis: {
|
||
type: 'category',
|
||
boundaryGap: false,
|
||
data: xAxisTime,
|
||
axisLabel: {
|
||
fontSize: 12,
|
||
rotate: 30,
|
||
interval: Math.floor(xAxisTime.length / 6)
|
||
},
|
||
axisTick: {
|
||
alignWithLabel: true,
|
||
interval: Math.floor(xAxisTime.length / 6)
|
||
}
|
||
},
|
||
yAxis: [
|
||
{
|
||
type: 'value',
|
||
min: yAxisMin,
|
||
max: yAxisMax,
|
||
nameTextStyle: { fontSize: 12 },
|
||
axisLabel: {
|
||
formatter: (val) => val.toFixed(0),
|
||
fontSize: 12
|
||
},
|
||
splitLine: { lineStyle: { type: 'dashed', color: '#EEEEEE' } },
|
||
boundaryGap: [0.05, 0.05] // 上下留5%缓冲,避免顶点告警点被裁剪
|
||
}
|
||
],
|
||
dataZoom: [],
|
||
series: [
|
||
{
|
||
name: '上证指数',
|
||
type: 'line',
|
||
smooth: true,
|
||
symbol: 'circle',
|
||
symbolSize: 5,
|
||
itemStyle: { color: '#0092FF' },
|
||
lineStyle: {
|
||
width: 2,
|
||
color: '#0092FF',
|
||
shadowColor: 'rgba(0,146,255,0.5)',
|
||
shadowBlur: 8,
|
||
shadowOffsetY: 3,
|
||
shadowOffsetX: 0
|
||
},
|
||
areaStyle: {
|
||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||
{ offset: 0, color: 'rgba(0,146,255,0.25)' },
|
||
{ offset: 1, color: 'rgba(0,146,255,0)' }
|
||
])
|
||
},
|
||
data: yAxisPrice,
|
||
// 保留所有markPoint显示修复配置(强制显示/层级/样式)
|
||
markPoint: {
|
||
show: true, // 强制开启显示(关键!)
|
||
symbol: 'circle',
|
||
symbolSize:5, // 比折线大,避免被遮挡
|
||
z: 10, // 层级置顶,不被任何元素遮挡
|
||
|
||
data: alertPoints,
|
||
itemStyle: {
|
||
color: '#FF4444',
|
||
borderColor: '#fff', // 白色描边,更醒目
|
||
borderWidth: 1,
|
||
|
||
},
|
||
label: {
|
||
show: true,
|
||
position: 'top',
|
||
fontSize: 10,
|
||
color: '#FF4444',
|
||
fontWeight: '500',
|
||
distance: 6,
|
||
backgroundColor: 'rgba(255,255,255,0.8)', // 标签白色背景,防融合
|
||
padding: [2, 4],
|
||
borderRadius: 2,
|
||
borderColor: '#FF4444', // 白色描边,更醒目
|
||
borderWidth: 1,
|
||
}
|
||
},
|
||
yAxisIndex: 0
|
||
}
|
||
],
|
||
|
||
|
||
};
|
||
console.log('7. 分组后最终告警详情:', JSON.stringify(option.series));
|
||
chart.setOption(option, true);
|
||
// 窗口自适应(优化:避免重复监听,页面销毁时可移除)
|
||
uni.onWindowResize(() => {
|
||
this.chartInstance && this.chartInstance.resize();
|
||
});
|
||
},
|
||
|
||
|
||
itemDetails(item) {
|
||
uni.navigateTo({
|
||
url: '/pagesStock/stockCenterDetails/stockCenterDetails?code=' + item.stock_code
|
||
})
|
||
},
|
||
moreAction() {
|
||
if(this.isShowTime){
|
||
uni.navigateTo({
|
||
url: '/pages/geGuCenter/detail?currentDate=' + this.currentDate
|
||
})}else{
|
||
uni.navigateTo({
|
||
url: '/pages/geGuCenter/detail'
|
||
})
|
||
}
|
||
},
|
||
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()
|
||
}
|
||
},
|
||
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.isShowTime=true;
|
||
this.marketHeatmap(this.currentDate)
|
||
this.marketStatistics()
|
||
|
||
this.marketHotspotListOverview()
|
||
|
||
this.$refs["datePopup"].close()
|
||
}
|
||
},
|
||
bkydAction(item) {
|
||
this.$refs["detailPopup"].open()
|
||
this.formattedAvg = item.formattedAvg,
|
||
this.upCount = item.upCount,
|
||
this.downCount = item.downCount,
|
||
this.limit_up_ratio = item.limit_up_ratio,
|
||
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) {
|
||
// 将接口数据赋值给列表数组
|
||
let rawData = res.data.stocks;
|
||
|
||
// 2. 对数据进行排序处理
|
||
this.conceptStocksList = rawData.sort((a, b) => {
|
||
// 将 None 值转换为 -999
|
||
const aValue = a.change_pct === null || a.change_pct === undefined ? -999 :
|
||
Number(a.change_pct);
|
||
const bValue = b.change_pct === null || b.change_pct === undefined ? -999 :
|
||
Number(b.change_pct);
|
||
|
||
// 降序排列(涨幅高的在前)
|
||
return bValue - aValue;
|
||
});
|
||
} 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'; // 平盘:灰色
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="less">
|
||
page {
|
||
background-color: #070707;
|
||
}
|
||
|
||
.topBg {
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: auto;
|
||
}
|
||
|
||
.searchC {
|
||
background-color: #292929B3;
|
||
left: 0;
|
||
right: 0;
|
||
margin: 20rpx 25rpx 0;
|
||
padding: 0 25rpx;
|
||
height: 70rpx;
|
||
border-radius: 35rpx;
|
||
font-size: 22rpx;
|
||
font-weight: 500;
|
||
|
||
.icon {
|
||
margin-right: 12rpx;
|
||
width: 25rpx;
|
||
height: auto;
|
||
}
|
||
|
||
input {
|
||
height: 100%;
|
||
color: white;
|
||
}
|
||
}
|
||
|
||
.stockDetailsC {
|
||
left: 25rpx;
|
||
width: calc(100vw - 50rpx);
|
||
bottom: calc(55px + env(safe-area-inset-bottom));
|
||
background-color: white;
|
||
border-radius: 10rpx;
|
||
}
|
||
|
||
.detailPopup {
|
||
max-height: 70%;
|
||
background-color: white;
|
||
color: red;
|
||
border-radius: 20rpx 20rpx 0 0;
|
||
padding-bottom: env(safe-area-inset-bottom);
|
||
}
|
||
.searchResultList {
|
||
background-color: #00000080;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
padding: 0 25rpx;
|
||
.list
|
||
{
|
||
background-color: white;
|
||
border-radius: 10rpx;
|
||
.item
|
||
{
|
||
padding: 0 42rpx;
|
||
line-height: 60rpx;
|
||
font-size: 22rpx;
|
||
font-weight: 500;
|
||
color: #333;
|
||
}
|
||
}
|
||
z-index: 20;
|
||
}
|
||
|
||
</style> |