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.
394 lines
12 KiB
394 lines
12 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.stockName ?? item.name ?? item.tsName ?? item.tsname ?? item.secName ?? '',
|
|
stockCode: item.tsCode ?? item.tscode ?? item.code ?? item.symbol ?? '',
|
|
latest: item.close ?? item.lastClose ?? '',
|
|
increase: formatPctChg(item.pctChg),
|
|
decrease: item.change ?? item.chg ?? '',
|
|
previousClose: item.preClose ?? item.preclose ?? item.prevClose ?? '',
|
|
volume: item.vol ?? item.volume ?? '',
|
|
turnover: item.amount ?? item.turnover ?? '',
|
|
openingPrice: item.open ?? item.openPrice ?? '',
|
|
highestPrice: item.high ?? item.highPrice ?? '',
|
|
lowestPrice: item.low ?? item.lowPrice ?? ''
|
|
}))
|
|
console.log(`按名称(${apiName})加载成功,条数:`, strategyData.value.length, '首项:', strategyData.value[0])
|
|
if (!strategyData.value[0]?.name) {
|
|
console.warn('名称字段未命中,原始keys示例:', Object.keys(list[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.stockName ?? item.name ?? item.tsName ?? item.tsname ?? item.secName ?? '',
|
|
stockCode: item.tsCode ?? item.tscode ?? item.code ?? item.symbol ?? '',
|
|
latest: item.close ?? item.lastClose ?? '',
|
|
increase: formatPctChg(item.pctChg),
|
|
decrease: item.change ?? item.chg ?? '',
|
|
previousClose: item.preClose ?? item.preclose ?? item.prevClose ?? '',
|
|
volume: item.vol ?? item.volume ?? '',
|
|
turnover: item.amount ?? item.turnover ?? '',
|
|
openingPrice: item.open ?? item.openPrice ?? '',
|
|
highestPrice: item.high ?? item.highPrice ?? '',
|
|
lowestPrice: item.low ?? item.lowPrice ?? ''
|
|
}))
|
|
console.log('stockSelectDetail 加载成功(已按涨幅降序),条数:', strategyData.value.length, '首项:', strategyData.value[0])
|
|
if (!strategyData.value[0]?.name) {
|
|
console.warn('名称字段未命中,原始keys示例:', Object.keys(list[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>
|