Browse Source

Merge branch 'milestone-20251031-简版功能开发' of http://39.101.133.168:8807/qimaohong/deepChartVueApp into wangyi/feature-20251026183100-deepmate王毅

lihuilin/feature-20251024095243-我的
wangyi 4 weeks ago
parent
commit
a6f855fef6
  1. 3
      .hbuilderx/launch.json
  2. 17
      App.vue
  3. 18
      api/deepExploration/deepExploration.js
  4. 2
      api/tcpConnection.js
  5. 138
      pages/deepExploration/deepExploration.vue
  6. 326
      pages/deepExploration/stockSelectDetail.vue
  7. 9
      pages/home/home.vue
  8. 3
      utils/http.js

3
.hbuilderx/launch.json

@ -2,7 +2,8 @@
"version" : "1.0", "version" : "1.0",
"configurations" : [ "configurations" : [
{ {
"customPlaygroundType" : "local",
"customPlaygroundType" : "device",
"packageName" : "io.dcloud.HBuilder",
"playground" : "custom", "playground" : "custom",
"type" : "uni-app:app-android" "type" : "uni-app:app-android"
} }

17
App.vue

@ -1,8 +1,25 @@
<script> <script>
import { useDeviceStore } from './stores/modules/deviceInfo'
export default { export default {
onLaunch: function() { onLaunch: function() {
console.warn('当前组件仅支持 uni_modules 目录结构 ,请升级 HBuilderX 到 3.1.0 版本以上!') console.warn('当前组件仅支持 uni_modules 目录结构 ,请升级 HBuilderX 到 3.1.0 版本以上!')
console.log('App Launch') console.log('App Launch')
//
// const deviceStore = useDeviceStore()
// try {
// const sys = uni.getSystemInfoSync()
// let deviceId = ''
// // #ifdef APP-PLUS
// try { deviceId = plus?.device?.uuid || '' } catch(e) {}
// // #endif
// if (!deviceId) deviceId = uni.getStorageSync('device_id')
// if (!deviceId) {
// deviceId = `web_${Date.now()}_${Math.random().toString(36).slice(2,10)}`
// uni.setStorageSync('device_id', deviceId)
// }
// deviceStore.setDeviceInfo({ deviceId, platform: sys.platform, model: sys.model })
// console.log('Device init:', deviceStore.deviceInfo)
// } catch(e) { console.warn('Init device info failed:', e) }
}, },
onShow: function() { onShow: function() {
console.log('App Show') console.log('App Show')

18
api/deepExploration/deepExploration.js

@ -101,8 +101,22 @@ export const RecordListApi = (data) => {
data:data data:data
}) })
} }
// 选股策略
export const stocSelectApi = (data) => {
return http({
method: 'POST',
url: '/api/deep/getStrategy',
data:data
})
}
//根据名字选股策略
export const stocSelectByNameApi = (data) => {
return http({
method: 'POST',
url: '/api/deep/getStrategyByName',
data:data
})
}
//历史记录详情 //历史记录详情

2
api/tcpConnection.js

@ -6,7 +6,7 @@
*/ */
// 引用TCP插件 // 引用TCP插件
const TCPSocket = uni.requireNativePlugin('Aimer-TCPPlugin');
// const TCPSocket = uni.requireNativePlugin('Aimer-TCPPlugin');
// const TCPSocket = uni.requireNativePlugin("Aimer-TCPPlugin"); // const TCPSocket = uni.requireNativePlugin("Aimer-TCPPlugin");
// TCP连接配置 // TCP连接配置

138
pages/deepExploration/deepExploration.vue

@ -95,9 +95,9 @@
</view> </view>
<view class="contentItem"> <view class="contentItem">
<view class="row" v-for="(item, index) in stockData" :key="index"> <view class="row" v-for="(item, index) in stockData" :key="index">
<view class="nameItem">{{ item.name }}</view>
<view class="nameItem">{{ item.tscode }}</view>
<view class="closeItem">{{ item.close }}</view> <view class="closeItem">{{ item.close }}</view>
<view class="priceItem">{{ item.select }}</view>
<view class="priceItem">{{ item.preClose }}</view>
</view> </view>
</view> </view>
</view> </view>
@ -112,7 +112,7 @@
src="/static/deepExploration-images/plus.png" src="/static/deepExploration-images/plus.png"
mode="aspectFill" mode="aspectFill"
></image> ></image>
<text>抄底卖顶</text>
<text>波段行情</text>
</view> </view>
<view class="right"> <view class="right">
<image <image
@ -129,10 +129,10 @@
<view class="contentTitle_price">选股价格</view> <view class="contentTitle_price">选股价格</view>
</view> </view>
<view class="contentItem"> <view class="contentItem">
<view class="row" v-for="(item, index) in stockData" :key="index">
<view class="nameItem">{{ item.name }}</view>
<view class="row" v-for="(item, index) in stockDataByName" :key="index">
<view class="nameItem">{{ item.tscode }}</view>
<view class="closeItem">{{ item.close }}</view> <view class="closeItem">{{ item.close }}</view>
<view class="priceItem">{{ item.select }}</view>
<view class="priceItem">{{ item.preClose }}</view>
</view> </view>
</view> </view>
</view> </view>
@ -145,9 +145,13 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from "vue";
import footerBar from "@/components/footerBar.vue";
import deepExploration_header from "@/components/deepExploration_header.vue";
import {
ref,
onMounted
} from 'vue'
import footerBar from '@/components/footerBar.vue'
import deepExploration_header from '@/components/deepExploration_header.vue'
import { stocSelectApi, stocSelectByNameApi } from '@/api/deepExploration/deepExploration.js'
const type = ref("deepExploration"); const type = ref("deepExploration");
const iSMT = ref(0); const iSMT = ref(0);
@ -185,38 +189,106 @@ const searchStock = () => {
// //
const viewAll = () => { const viewAll = () => {
uni.navigateTo({ uni.navigateTo({
url: "/pages/deepExploration/stockSelectDetail",
});
};
//
const stockData = [
{
name: "(MKTW)MarketWise Inc",
close: "$14.190",
select: "$13.180",
},
{
name: "(MTCH)Match Group Inc",
close: "$32.120",
select: "$28.120",
},
{
name: "(MKTW)MarketWise Inc",
close: "$14.190",
select: "$13.180",
},
];
url: '/pages/deepExploration/stockSelectDetail'
})
}
//
const stockData = ref([]);
const stockDataByName = ref([]); //
//
const loadStockSelection = async () => {
try {
const res = await stocSelectApi({ language: 'cn', size: 3 })
// console.log(':', typeof res === 'object' ? JSON.stringify(res) : res)
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.length) {
const mapped = list.map(item => ({
tscode: item.tsCode ?? item.tscode ?? item.code ?? '',
close: item.close ?? item.lastClose ?? '',
preClose: item.preClose ?? item.preclose ?? item.prevClose ?? ''
}))
stockData.value = mapped.slice(0, 3)
console.log('选股策略列表长度:', stockData.value.length, '首项:', stockData.value[0])
} else {
console.warn('选股策略接口返回空列表或结构不匹配', raw)
}
} catch (e) {
console.error('选股策略接口调用失败', e)
uni.showToast({ title: '选股策略加载失败', icon: 'none' })
}
}
//
const loadStockSelectionByName = async () => {
try {
const res = await stocSelectByNameApi({ name: '安徽' })
const raw = res?.data
const dataObj = raw?.data || raw
let list = []
if (Array.isArray(dataObj)) {
list = dataObj
} else if (dataObj && typeof dataObj === 'object') {
const target = dataObj['安徽']
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) {
const mapped = list.map(item => ({
tscode: item.tsCode ?? item.tscode ?? item.code ?? '',
close: item.close ?? item.lastClose ?? '',
preClose: item.preClose ?? item.preclose ?? item.prevClose ?? ''
}))
stockDataByName.value = mapped.slice(0, 3)
console.log('安徽板块列表长度:', stockDataByName.value.length, '首项:', stockDataByName.value[0])
} else {
console.warn('按名称(安徽)接口返回空列表或结构不匹配', raw)
}
} catch (e) {
console.error('按名称(安徽)接口调用失败', e)
uni.showToast({ title: '安徽板块加载失败', icon: 'none' })
}
}
onMounted(() => { onMounted(() => {
// //
iSMT.value = uni.getSystemInfoSync().statusBarHeight; iSMT.value = uni.getSystemInfoSync().statusBarHeight;
});
//
loadStockSelection()
loadStockSelectionByName() //
})
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.main { .main {
width: 100%; width: 100%;
height: 100vh;
min-height: 100vh; //
height: auto; //
overflow-y: auto; //
background-color: #fff; background-color: #fff;
padding-bottom: 120rpx; //
.search { .search {
position: relative; position: relative;
@ -449,6 +521,8 @@ onMounted(() => {
.static-footer { .static-footer {
position: fixed; position: fixed;
bottom: 0; bottom: 0;
left: 0;
right: 0;
} }
} }

326
pages/deepExploration/stockSelectDetail.vue

@ -3,7 +3,7 @@
<view class="table"> <view class="table">
<view class="tableHeader"> <view class="tableHeader">
<scroll-view class="tabs" scroll-x="true"> <scroll-view class="tabs" scroll-x="true">
<view v-for="(item,index) in tabsData" :key="index" class="tabItem" @click="handleTab(item)">
<view v-for="(item,index) in tabsData" :key="index" :class="['tabItem', { 'tabItem-active': item === activeTab }]" @click="handleTab(item)">
{{item}} {{item}}
</view> </view>
</scroll-view> </scroll-view>
@ -23,7 +23,7 @@
</view> </view>
</view> </view>
<view class="box_content"> <view class="box_content">
<view class="row" v-for="(item,index) in fakeData" :key="index" :class="{ 'increase-positive': item.increase.startsWith('+'),
<view class="row" v-for="(item,index) in strategyData" :key="index" :class="{ 'increase-positive': item.increase.startsWith('+'),
'increase-negative': item.increase.startsWith('-')}"> 'increase-negative': item.increase.startsWith('-')}">
<view class="name_colum"> <view class="name_colum">
<text class="stockName">{{item.name}}</text> <text class="stockName">{{item.name}}</text>
@ -48,153 +48,182 @@
</template> </template>
<script setup> <script setup>
import {
ref
} from 'vue'
const tabsData = ref(['全部', '抄底卖顶', '波段行情', '价值投资', '资金及仓位管理', '价值投资', '价值投资', '价值投资', ])
const handleTab = (item) => {
uni.showToast({
title: `查看 ${item} 详情`,
icon: 'none',
duration: 2000
})
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 ifASC = ref(true)
// //
const tableContentHeaderData = ref(['最新', '涨幅', '跌幅', '昨收', '成交量', '成交额', '开盘价', '最高价', '最低价'])
const fakeData = [{
name: "TechCore",
stockCode: "600001",
latest: 1315.00,
increase: "+5.2%",
decrease: "+5.2%",
previousClose: 1250.00,
volume: 12000,
turnover: "15780K",
openingPrice: 1237.50,
highestPrice: 1320.00,
lowestPrice: 1230.00
},
{
name: "MediaLink",
stockCode: "600002",
latest: 1138.70,
increase: "-3.5%",
decrease: "-3.5%",
previousClose: 1180.00,
volume: 8500,
turnover: "967.9K",
openingPrice: 1191.80,
highestPrice: 1195.00,
lowestPrice: 1130.00
},
{
name: "FinServ",
stockCode: "600003",
latest: 1413.72,
increase: "+7.1%",
decrease: "+7.1%",
previousClose: 1320.00,
volume: 15000,
turnover: "2120.6K",
openingPrice: 1293.60,
highestPrice: 1420.00,
lowestPrice: 1290.00
},
{
name: "AutoDrive",
stockCode: "600004",
latest: 1080.40,
increase: "+2.8%",
decrease: "+2.8%",
previousClose: 1050.00,
volume: 9000,
turnover: "972.4K",
openingPrice: 1055.25,
highestPrice: 1085.00,
lowestPrice: 1050.00
},
{
name: "EduSmart",
stockCode: "600005",
latest: 968.24,
increase: "-1.2%",
decrease: "-1.2%",
previousClose: 980.00,
volume: 7000,
turnover: "677.8K",
openingPrice: 975.10,
highestPrice: 978.00,
lowestPrice: 965.00
},
{
name: "HealthPlusqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq",
stockCode: "600006",
latest: 1463.00,
increase: "+4.5%",
decrease: "+4.5%",
previousClose: 1400.00,
volume: 13000,
turnover: "1901.9K",
openingPrice: 1393.00,
highestPrice: 1470.00,
lowestPrice: 1385.00
},
{
name: "AgriTech",
stockCode: "600007",
latest: 1038.36,
increase: "+1.8%",
decrease: "+1.8%",
previousClose: 1020.00,
volume: 6500,
turnover: "674.9K",
openingPrice: 1028.16,
highestPrice: 1040.00,
lowestPrice: 1025.00
},
{
name: "LogiFlow",
stockCode: "600008",
latest: 1094.24,
increase: "-2.3%",
decrease: "-2.3%",
previousClose: 1120.00,
volume: 8000,
turnover: "875.4K",
openingPrice: 1122.24,
highestPrice: 1125.00,
lowestPrice: 1090.00
},
{
name: "EnergySol",
stockCode: "600009",
latest: 1435.05,
increase: "+6.3%",
decrease: "+6.3%",
previousClose: 1350.00,
volume: 14000,
turnover: "2009.1K",
openingPrice: 1339.75,
highestPrice: 1440.00,
lowestPrice: 1335.00
},
{
name: "RealEstate",
stockCode: "600010",
latest: 995.00,
increase: "-0.5%",
decrease: "-0.5%",
previousClose: 1000.00,
volume: 7500,
turnover: "746.3K",
openingPrice: 1003.00,
highestPrice: 1005.00,
lowestPrice: 990.00
}
];
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> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@ -229,12 +258,17 @@
background-color: rgb(243, 243, 243); background-color: rgb(243, 243, 243);
} }
.tabItem-active {
background-color: #DB1F1D; //
color: #fff;
}
} }
} }
.tableContent { .tableContent {
width: 100%; width: 100%;
background-color: #fff;
position: relative; position: relative;
.showAll { .showAll {

9
pages/home/home.vue

@ -47,7 +47,7 @@
<text class="section-title">深度探索</text> <text class="section-title">深度探索</text>
</view> </view>
<view class="header-right"> <view class="header-right">
<text class="more-btn">查看更多</text>
<text class="more-btn" @click="goToDeepExploration">查看更多</text>
</view> </view>
</view> </view>
</view> </view>
@ -430,6 +430,13 @@ export default {
}, },
methods: { methods: {
//
goToDeepExploration() {
uni.navigateTo({
url: '/pages/deepExploration/deepExploration'
})
},
// //
handleVisitorLoginSuccess(data) { handleVisitorLoginSuccess(data) {
console.log('收到游客登录成功事件:', data) console.log('收到游客登录成功事件:', data)

3
utils/http.js

@ -44,8 +44,7 @@ const httpInterceptor = {
//4 添加token,优先用store,没有则回退到body中的token,保持与Apifox一致 //4 添加token,优先用store,没有则回退到body中的token,保持与Apifox一致
const memberStore = useUserStore() const memberStore = useUserStore()
const token = memberStore.userInfo?.token || options.data?.token const token = memberStore.userInfo?.token || options.data?.token
// const token = '2d0b5654409646713cdd40ec0d0bb56c'
// const token = '1b3a58424c5324e40d4bf4d085e18047'
// const token = 'a72cf584af42525f214670cb47443820'
if (token) { if (token) {
options.header.token = token options.header.token = token
} }

Loading…
Cancel
Save