|
|
<template> <view class="main"> <view class="table"> <view class="tableHeader"> <scroll-view class="tabs" scroll-x="true"> <view v-for="(item,index) in tabsData" :key="index" :class="['tabItem', { 'tabItem-active': item === activeTab }]" @click="handleTab(item)"> {{item}} </view> </scroll-view> </view> <view class="tableContent"> <image class="showAll" src="/static/deepExploration-images/showAll.png" mode="aspectFill"></image> <scroll-view scroll-x="true" show-scrollbar="false"> <view class="tableBox"> <view class="box_header"> <view class="name">名称</view> <view class="other" v-for="(item,index) in tableContentHeaderData" :key="index"> <text>{{item}}</text> <image v-show="ifASC" src="/static/deepExploration-images/ASC.png" mode="aspectFill"> </image> <image v-show="!ifASC" src="/static/deepExploration-images/DESC.png" mode="aspectFill"> </image> </view> </view> <view class="box_content"> <view class="row" v-for="(item,index) in strategyData" :key="index" :class="{ 'increase-positive': item.increase.startsWith('+'), 'increase-negative': item.increase.startsWith('-')}"> <view class="name_colum"> <text class="stockName">{{item.name}}</text> <text class="stockCode">{{item.stockCode}}</text> </view> <view class="other_colum">{{item.latest}}</view> <view class="other_colum">{{item.increase}}</view> <view class="other_colum">{{item.decrease}}</view> <view class="other_colum">{{item.previousClose}}</view> <view class="other_colum">{{item.volume}}</view> <view class="other_colum">{{item.turnover}}</view> <view class="other_colum">{{item.openingPrice}}</view> <view class="other_colum">{{item.highestPrice}}</view> <view class="other_colum">{{item.lowestPrice}}</view> </view> </view> </view> </scroll-view> </view> </view> </view></template>
<script setup> import { ref, onMounted } from 'vue' import { stocSelectApi, stocSelectByNameApi } from '@/api/deepExploration/deepExploration.js' import { useUserStore } from '@/stores/modules/userInfo.js' import { useDeviceStore } from '@/stores/modules/deviceInfo.js' import { LoginApi } from '@/api/start/login.js'
const tabsData = ref(['全部', '抄底卖顶', '波段行情', '价值投资', '资金及仓位管理', ]) const activeTab = ref('全部') // 点击标签:激活并按需触发名称查询
const handleTab = async (item) => { activeTab.value = item const nameMap = { '抄底卖顶': '北京', '波段行情': '安徽', '价值投资': '重庆', '资金及仓位管理': '黑龙江' } if (item === '全部') { await loadStrategy() uni.showToast({ title: `查看 ${item} 详情`, icon: 'none', duration: 1500 }) return } const apiName = nameMap[item] if (apiName) { await loadByName(apiName) uni.showToast({ title: `${item}数据已更新`, icon: 'none', duration: 1500 }) } else { uni.showToast({ title: `暂不支持:${item}`, icon: 'none' }) } } //表头是否升序
const ifASC = ref(true) //表头数据
const tableContentHeaderData = ref(['最新', '涨幅', '涨跌', '昨收', '成交量', '成交额', '开盘价', '最高价', '最低价'])
// 渲染用数据
const strategyData = ref([])
// 游客登录兜底,确保移动端有token
const ensureAuth = async () => { const userStore = useUserStore() if (userStore.userInfo?.token) return try { const deviceStore = useDeviceStore() let deviceId = deviceStore.deviceInfo?.deviceId if (!deviceId) { const cached = uni.getStorageSync('deviceId') deviceId = cached || `${Date.now()}-${Math.random().toString(36).slice(2, 10)}` uni.setStorageSync('deviceId', deviceId) deviceStore.setDeviceInfo({ ...(deviceStore.deviceInfo || {}), deviceId }) } const res = await LoginApi({ loginType: 'VISITOR', account: deviceId, useCode: false }) if (res?.code === 200 && res?.data?.token) { userStore.setUserInfo(res.data) console.log('游客登录成功,token=', res.data.token) } else { console.warn('游客登录失败', res) } } catch (err) { console.warn('游客登录异常', err) } }
const formatPctChg = (val) => { if (val === null || val === undefined || val === '') return '' const num = Number(val) if (!isFinite(num)) return String(val) const sign = num > 0 ? '+' : '' return `${sign}${num.toFixed(2)}%` } // 默认根据涨幅( pctChg )由大到小排序
const sortByPctDesc = (arr) => arr.sort((a, b) => (Number(b.pctChg) || -Infinity) - (Number(a.pctChg) || -Infinity))
// 按名称加载(抄底卖顶/波段行情/价值投资/资金及仓位管理)
const loadByName = async (apiName) => { try { const userStore = useUserStore() if (!userStore.userInfo?.token) { await ensureAuth() } const token = useUserStore().userInfo?.token const res = await stocSelectByNameApi({ name: apiName, token }) const raw = res?.data const dataObj = raw?.data || raw let list = [] if (dataObj && typeof dataObj === 'object' && !Array.isArray(dataObj)) { const target = dataObj[apiName] if (Array.isArray(target)) list = target else { const firstArr = Object.values(dataObj).find(v => Array.isArray(v)) if (Array.isArray(firstArr)) list = firstArr } } if ((!Array.isArray(list) || !list.length) && Array.isArray(raw)) { list = raw } // 排序:涨幅由大到小
if (Array.isArray(list)) list = sortByPctDesc(list) if (Array.isArray(list) && list.length) { strategyData.value = list.map(item => ({ name: item.tsCode ?? item.tscode ?? '', stockCode: item.tsCode ?? item.tscode ?? '', latest: item.close ?? '', increase: formatPctChg(item.pctChg), decrease: item.change ?? '', previousClose: item.preClose ?? item.preclose ?? '', volume: item.vol ?? '', turnover: item.amount ?? '', openingPrice: item.open ?? '', highestPrice: item.high ?? '', lowestPrice: item.low ?? '' })) console.log(`按名称(${apiName})加载成功,条数:`, strategyData.value.length, '首项:', strategyData.value[0]) } else { console.warn('getStrategyByName 返回空列表或结构不匹配', raw) } } catch (e) { console.error('getStrategyByName 接口调用失败', e) uni.showToast({ title: '按名称加载失败', icon: 'none' }) } }
const loadStrategy = async () => { try { const userStore = useUserStore() if (!userStore.userInfo?.token) { await ensureAuth() } const token = useUserStore().userInfo?.token const res = await stocSelectApi({ language: 'cn', token }) const raw = res?.data const listCandidates = [ raw?.list, raw?.data?.list, raw?.data?.rows, raw?.rows, Array.isArray(raw) ? raw : null ].filter(Array.isArray) let list = listCandidates.length ? listCandidates[0] : [] if ((!Array.isArray(list) || !list.length) && raw && typeof raw === 'object' && !Array.isArray(raw)) { const arrays = Object.values(raw).filter(Array.isArray) if (arrays.length) list = arrays.flat() } // 排序:涨幅由大到小
if (Array.isArray(list)) list = sortByPctDesc(list) if (Array.isArray(list) && list.length) { strategyData.value = list.map(item => ({ name: item.tsCode ?? item.tscode ?? '', stockCode: item.tsCode ?? item.tscode ?? '', latest: item.close ?? '', increase: formatPctChg(item.pctChg), decrease: item.change ?? '', previousClose: item.preClose ?? item.preclose ?? '', volume: item.vol ?? '', turnover: item.amount ?? '', openingPrice: item.open ?? '', highestPrice: item.high ?? '', lowestPrice: item.low ?? '' })) console.log('stockSelectDetail 加载成功(已按涨幅降序),条数:', strategyData.value.length, '首项:', strategyData.value[0]) } else { console.warn('stockSelectDetail 接口返回空列表或结构不匹配', raw) } } catch (e) { console.error('stockSelectDetail 接口调用失败', e) uni.showToast({ title: '选股策略详情加载失败', icon: 'none' }) } }
onMounted(() => { loadStrategy() })</script>
<style scoped lang="scss"> .main { width: 100%; height: 100vh; background-color: #fff;
.table { margin-top: 10rpx; box-shadow: 0 -2rpx 3rpx -1rpx rgba(0, 0, 0, 0.5);
.tableHeader { .tabs { white-space: nowrap; padding-top: 20rpx; padding-left: 40rpx;
::-webkit-scrollbar { //隐藏 滚动条
display: none; }
.tabItem { display: inline-block; color: rgb(175, 175, 175); border-radius: 10rpx; padding: 5rpx 30rpx; margin-right: 20rpx; font-size: 28rpx; background-color: rgb(243, 243, 243); }
.tabItem-active { background-color: #DB1F1D; // 红色
color: #fff; }
} }
.tableContent { width: 100%; background-color: #fff; position: relative;
.showAll { position: absolute; top: 35rpx; right: 20rpx; width: 40rpx; height: 40rpx; z-index: 100; }
scroll-view { width: 100%; white-space: nowrap;
::-webkit-scrollbar { //隐藏 滚动条
display: none; }
}
.tableBox { padding-left: 40rpx;
.box_header { margin-bottom: 19rpx; display: flex; width: max-content; margin-top: 40rpx; color: rgb(109, 109, 109); border-radius: 10rpx; margin-right: 20rpx; font-size: 23rpx;
.name { flex: 0 0 375rpx; }
.other { flex: 0 0 195rpx;
text { margin-right: 5rpx; }
image { width: 20rpx; height: 20rpx; } } }
.box_content { width: max-content;
.row { padding: 5rpx; display: flex; border-top: 1rpx dashed #eee; width: 210%;
&.increase-positive { .other_colum { color: #2DD357; font-weight: 200; } }
&.increase-negative { .other_colum { color: #FF4150; font-weight: 200; } }
.name_colum { flex: 0 0 375rpx;
display: flex; flex-direction: column; gap: 4rpx;
.stockName { color: #333333; width: 100%; max-width: 305rpx; font-size: 28rpx; font-weight: 400; line-height: 36rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.stockCode { color: #c5c5c5; ; font-size: 24rpx; font-weight: 400; line-height: 30rpx; } }
.other_colum { flex: 0 0 195rpx; display: flex; align-items: center; } } } } } } }</style>
|