Files
JiaZhiQianYan/pagesStock/stockCenterDetails/bkydmx.vue
2026-02-06 10:49:56 +08:00

447 lines
16 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="true"></navBar>
<image class="topBg absolute" src="/static/image/index/conceptTopBg.png" mode="widthFix"></image>
<view class="stockDetailsC fixed" style="background-color: white; border-radius: 10rpx; overflow: hidden;"
:style="'top:'+contentTop+'px;'">
<view style="height: 86rpx;">
<scroll-view scroll-x
style="white-space: nowrap; height: 100%; padding: 0 20rpx; box-sizing: border-box;"
scroll-with-animation :scroll-into-view="'tab-' + activeIndex">
<view style="display: flex; align-items: center; height: 100%; font-weight: 500;">
<view :id="'tab-' + index" @click="activeIndex = index" v-for="(item,index) in bkList"
:key="index"
style="display: flex; align-items: center; justify-content: center; line-height: 85rpx; margin: 0 20rpx;"
:style="{color: (activeIndex == index ? '#2B2B2B' : '#999999'), 'border-bottom': (activeIndex == index ? '1rpx solid #F2C369' : 'none'), 'font-size' : (activeIndex == index ? '28rpx' : '26rpx')}">
{{item.title}}
</view>
</view>
</scroll-view>
</view>
<view style="height: 1rpx; background-color: #E7E7E7; margin: 0 20rpx;"></view>
<view
style="height: 48rpx; display: grid; grid-template-columns: repeat(3, 1fr); gap: 10rpx; margin: 23rpx 40rpx;">
<view @click="handleFilterChange(index)"
style="height: 45rpx; display: flex; align-items: center; justify-content: center; color: #939393; font-size: 24rpx; font-weight: 500; border-radius: 5rpx;"
:style="{color: (filterIndex == index ? '#070707' : '#939393'), 'border': (filterIndex == index ? '1rpx solid #F2C369' : '1rpx solid #E5E5E5'), 'background-color' : (filterIndex == index ? '#F2C369' : '#fff')}"
v-for="(item,index) in bkFilters" :key="index">
{{item}}
</view>
</view>
<view
style="margin: 0 20rpx; background-color: #FAFAFC; display: grid; grid-template-columns: 35% 20% 20% 25%;">
<view v-for="(item,index) in ['名称', '涨幅', '连板', '板块']" :key="index"
style="font-size: 22rpx; color: #666666; padding: 0 15rpx; box-sizing: border-box; font-weight: 500; line-height: 60rpx;"
:style="{'text-align' : index == 0 ? 'left' : 'center'}">
{{item}}
</view>
</view>
<scroll-view scroll-y
style="position: absolute; top: 241rpx; left: 0; right: 0; bottom: 0; font-size: 20rpx; font-weight: 500;">
<!-- 真实股票数据渲染 -->
<view v-for="(item, index) in filteredStocks" :key="item.scode"
style="margin: 0 20rpx; display: grid; grid-template-columns: 35% 20% 20% 25%;"
:style="{'background-color': (index % 2 == 0 ? '#fff' : '#FAFAFC')}">
<!-- 股票名称 + 角色标签 -->
<view style="display: flex; align-items: center; color: #666666; height: 60rpx;">
<!-- 角色标签 -->
<view v-if="item.stockRole"
style="display: flex; align-items: center; border-radius: 5rpx; padding: 0 10rpx; margin-left: 14rpx;"
:style="getRoleTagStyle(item.stockRole)">
<image v-if="item.stockRole.icon" style="width: 15rpx; height: 17rpx; margin-right: 5rpx;"
:src="item.stockRole.icon" mode="widthFix"></image>
<!-- <image style="width: 15rpx; height: 17rpx;" src="/pagesStock/static/icon/all-icon-4.png" mode="widthFix"></image> -->
<view :style="{'color': item.stockRole.color}">{{item.stockRole.text}}</view>
</view>
<view style="margin-left: 10rpx;">{{item.sname}}</view>
</view>
<!-- 涨幅硬编码+10% -->
<view style="display: flex; align-items: center; justify-content: center;">
<view style="font-size: 24rpx; color: #EC3440; font-weight: bold;">+10.00%</view>
</view>
<!-- 连板数 -->
<view style="display: flex; align-items: center; justify-content: center;">
<view
style="padding: 0 10rpx; border-radius: 5rpx; display: flex; align-items: center; justify-content: center;"
:style="getBoardTagStyleByLevel(item.continuous_days)">
{{formatBoardText(item.continuous_days)}}
</view>
</view>
<!-- 核心板块按关键词匹配颜色 -->
<view style="display: flex; align-items: center; justify-content: center;">
<view style="background-color: #F4EFFF; border-radius: 5rpx; padding: 0 10rpx; white-space: nowrap; max-width: 120rpx; overflow: hidden;text-overflow: ellipsis;" :style="{color: getSectorTextColor(item.core_sectors[0] || '未知板块')}">
{{item.core_sectors[0] || '未知板块'}}
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import {
inject
} from 'vue'
import {
getBaseURL1
} from '@/request/http.js'
export default {
data() {
return {
navH: inject('navHeight'),
contentTop: '',
activeIndex: 0,
bkList: [],
bkFilters: [
'按连板数',
'只看龙头'
],
filterIndex: 0,
selectedFullDate: '', // 年-月-日
originData: null, // 原始接口数据
allStocks: [], // 所有股票数据(带角色标签)
// 角色配置
STOCK_ROLES: {
dragon: {
text: '龙头',
color: '#EC3440',
bgColor: '#FFE8E9',
icon: '/pagesStock/static/icon/first-icon1.png'
},
follow: {
text: '跟风',
color: '#F97316',
bgColor: '#FFF0E6',
icon: '/pagesStock/static/icon/first-icon2.png'
},
first: {
text: '首板',
color: '#01AB5D',
bgColor: '#E4F9EF',
icon: '/pagesStock/static/icon/first-icon.png'
},
normal: {
text: '',
color: '',
bgColor: '',
icon: ''
}
},
// 连板层级样式配置(新规则)
BOARD_LEVEL_STYLES: {
dragon: { // 5板及以上 龙头
color: '#ef4444',
bgColor: '#FFE8E9',
borderColor: '#ef4444'
},
high: { // 3-4板 高位
color: '#f97316',
bgColor: '#FFF0E6',
borderColor: '#f97316'
},
mid: { // 2板 中位
color: '#eab308',
bgColor: '#FFF9E6',
borderColor: '#eab308'
},
first: { // 1板 首板
color: '#22c55e',
bgColor: '#E4F9EF',
borderColor: '#22c55e'
}
},
// 板块文字颜色配置(新规则)
SECTOR_COLOR_RULES: [
{ keyword: '公告', color: '#D4AF37' }, // 金色
{ keyword: '其他', color: '#9CA3AF' }, // 灰色
{ keyword: ['AI', '人工智能', '芯片'], color: '#8B5CF6' }, // 紫色
{ keyword: ['锂电', '电池', '新能源'], color: '#10B981' }, // 翠绿
{ keyword: ['医药', '医疗'], color: '#EC4899' }, // 粉色
{ keyword: ['金融', '银行'], color: '#F59E0B' }, // 橙黄
{ keyword: ['军工', '航空'], color: '#EF4444' }, // 红色
],
DEFAULT_SECTOR_COLOR: '#06B6D4' // 默认 青色
}
},
computed: {
// 筛选后的股票列表按板块codes匹配 + 连板排序/筛选
filteredStocks() {
if (!this.originData?.stocks || !this.bkList.length) return [];
// 1. 获取当前选中板块的股票代码集合
const currentBk = this.bkList[this.activeIndex];
if (!currentBk?.codes || currentBk.codes.length === 0) return [];
const targetCodes = new Set(currentBk.codes); // 转Set提升匹配效率
// 2. 从stocks中筛选出scode在targetCodes中的股票
let stocks = this.originData.stocks.filter(stock => targetCodes.has(stock.scode));
// 3. 保留原有筛选/排序逻辑
switch (this.filterIndex) {
case 0: // 按连板数从高到低排序
stocks.sort((a, b) => {
const aDays = this.parseContinuousDays(a.continuous_days);
const bDays = this.parseContinuousDays(b.continuous_days);
return bDays - aDays;
});
break;
case 1: // 只看龙头≥2连板并按连板数从高到低排序
stocks = stocks.filter(stock => this.parseContinuousDays(stock.continuous_days) >= 2);
stocks.sort((a, b) => {
const aDays = this.parseContinuousDays(a.continuous_days);
const bDays = this.parseContinuousDays(b.continuous_days);
return bDays - aDays;
});
break;
}
return stocks;
}
},
onLoad(e) {
this.activeIndex = e.index
this.selectedFullDate = e.data
console.log("selectedFullDate", this.selectedFullDate)
this.contentTop = this.navH + 20 / 750 * inject('windowWidth')
this.fetchData()
},
methods: {
// 解析连板数
parseContinuousDays(continuousDaysStr) {
if (!continuousDaysStr) return 0;
const match = continuousDaysStr.match(/(\d+)天/);
return match ? Number(match[1]) : 0;
},
// 格式化连板文本(适配新层级)
formatBoardText(continuousDaysStr) {
const boardDays = this.parseContinuousDays(continuousDaysStr);
if (boardDays === 1) return '首板'; // 1板显示首板
if (boardDays > 1) return `${boardDays}连板`; // 2板及以上显示 X连板
return ''; // 无连板数时返回空
},
// 获取连板标签样式(按新层级规则)
getBoardTagStyleByLevel(continuousDaysStr) {
const boardDays = this.parseContinuousDays(continuousDaysStr);
let styleConfig = {};
if (boardDays >= 5) {
styleConfig = this.BOARD_LEVEL_STYLES.dragon;
} else if (boardDays >= 3 && boardDays <= 4) {
styleConfig = this.BOARD_LEVEL_STYLES.high;
} else if (boardDays === 2) {
styleConfig = this.BOARD_LEVEL_STYLES.mid;
} else if (boardDays === 1) {
styleConfig = this.BOARD_LEVEL_STYLES.first;
}
return {
'color': styleConfig.color || '#FFFFFF',
'background-color': styleConfig.bgColor || '#eab308',
'border': `1rpx solid ${styleConfig.borderColor || '#eab308'}`
};
},
// 获取板块文字颜色(按关键词匹配)
getSectorTextColor(sectorName) {
if (!sectorName) return this.DEFAULT_SECTOR_COLOR;
// 遍历匹配规则
for (const rule of this.SECTOR_COLOR_RULES) {
if (Array.isArray(rule.keyword)) {
// 多关键词匹配
const isMatch = rule.keyword.some(key => sectorName.includes(key));
if (isMatch) return rule.color;
} else {
// 单关键词完全匹配
if (sectorName === rule.keyword) return rule.color;
}
}
// 无匹配返回默认色
return this.DEFAULT_SECTOR_COLOR;
},
// 获取股票角色
getStockRole(stock, sectorStocks, sectorIndex) {
const boardDays = this.parseContinuousDays(stock.continuous_days);
// 5板以上直接是龙头
if (boardDays >= 5) {
return this.STOCK_ROLES.dragon;
}
// 首板判断连板数为1
if (boardDays === 1) {
return this.STOCK_ROLES.first;
}
// 跟风判断:热门板块(前3) + 2-4连板
if (sectorIndex < 3 && boardDays >= 2 && boardDays < 5) {
// 特殊情况:板块内涨停最早 + 3板以上 → 龙头
const sortedByTime = [...sectorStocks].sort((a, b) =>
(a.zt_time || "").localeCompare(b.zt_time || "")
);
if (sortedByTime[0]?.scode === stock.scode && boardDays >= 3) {
return this.STOCK_ROLES.dragon;
}
return this.STOCK_ROLES.follow;
}
// 其他:普通
return this.STOCK_ROLES.normal;
},
// 获取角色标签样式
getRoleTagStyle(role) {
return {
'background-color': role.bgColor
};
},
// 处理板块切换
handleTabChange(index) {
this.activeIndex = index;
// 切换板块后重新计算股票角色(可选)
this.setStockRoles();
},
// 处理筛选切换
handleFilterChange(index) {
this.filterIndex = index;
},
// 为所有股票添加角色标签
setStockRoles() {
if (!this.originData || !this.originData.stocks || !this.bkList.length) return;
this.allStocks = this.originData.stocks.map(stock => {
// 找到股票所属板块的热度排名
let sectorIndex = -1;
const stockSectors = Array.isArray(stock.sector_category) ? stock.sector_category : [stock.sector_category];
// 匹配板块列表中的位置
this.bkList.some((bk, idx) => {
const match = stockSectors.some(s => s.includes(bk.title));
if (match) {
sectorIndex = idx;
return true;
}
return false;
});
// 获取同板块的所有股票
const sectorStocks = this.originData.stocks.filter(s => {
const sSectors = Array.isArray(s.sector_category) ? s.sector_category : [s.sector_category];
return sSectors.some(ss => stockSectors.includes(ss));
});
// 获取股票角色
const stockRole = this.getStockRole(stock, sectorStocks, sectorIndex);
return {
...stock,
stockRole: stockRole.text ? stockRole : null
};
});
},
/**
* 请求接口数据(优化:动态日期+自动时间戳)
*/
// 请求接口数据
async fetchData() {
try {
const timestamp = new Date().getTime();
const formattedDate = this.selectedFullDate;
const baseURL = getBaseURL1();
const requestUrl = `${baseURL}/data/zt/daily/${formattedDate}.json?t=${timestamp}`;
console.log('请求URL', requestUrl);
const res = await uni.request({
url: requestUrl,
method: 'GET'
});
if (res.statusCode === 200 && res.data) {
this.originData = res.data;
const { sector_data } = this.originData;
// 解析sector_data生成板块列表剔除「其他」格式[{title: 板块名, codes: 股票代码数组}]
this.bkList = Object.entries(sector_data)
.filter(([sectorName]) => sectorName !== '其他') // 去掉其他板块
.map(([sectorName, sectorInfo]) => ({
title: sectorName,
codes: sectorInfo.stock_codes || [] // 取板块对应的股票代码
}));
console.log('生成板块列表:', this.bkList);
// 为股票添加角色标签
this.setStockRoles();
} else {
uni.showToast({
title: '数据请求失败',
icon: 'none'
});
}
} catch (error) {
console.error('请求异常:', error);
uni.showToast({
title: '网络异常',
icon: 'none'
});
}
},
}
}
</script>
<style lang="less">
page {
background-color: #070707;
}
.topBg {
top: 0;
left: 0;
width: 100%;
height: auto;
}
.stockDetailsC {
left: 25rpx;
right: 25rpx;
width: calc(100vw - 50rpx);
bottom: env(safe-area-inset-bottom);
}
</style>