You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

388 lines
11 KiB

<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>