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.
 
 
 
 
 

867 lines
23 KiB

<template>
<LoginPrompt ref="loginPrompt"></LoginPrompt>
<view class="main">
<!-- 顶部状态栏占位 -->
<view class="top" :style="{height:iSMT+'px'}"></view>
<deepExploration_header></deepExploration_header>
<view class="search">
<input v-model="searchName" class="searchInput" type="text" placeholder="请输入股票名称、股票代码"
placeholder-style="color: #A6A6A6; font-size: 28rpx;" />
<image @click="searchStock" class="seachIcon" src="/static/deepExploration-images/search.png"
mode="aspectFill"></image>
</view>
<view class="content">
<view class="select">
<image class="img" :src="navItems[currentIndex].icon" mode=""></image>
<view v-for="(item, index) in navItems" :key="index" class="selectItem"
:class="{ active: currentIndex === index }" @click="handleModel(index)">
<button class="btn"></button>
</view>
</view>
<view class="graphAndTxt">
<view class="graph">
<view class="graph_header">
<view class="left">{{stockCode}}</view>
<view class="center">
<text>{{stockName}}</text>
</view>
<view class="right">{{stockTime}}</view>
</view>
<view class="graph_data">
<text>{{stockPrice}}</text>
<text>{{stockChange}}</text>
<text>{{stockAdd}}</text>
</view>
<view class="graph_content">
<view class="charts-box">
<!-- uCharts 蜡烛图组件 -->
<qiun-data-charts type="candle" :opts="opts" :chartData="chartData" :disableScroll="true"
:ontouch="true" :onzoom="true" :key="chartKey" />
</view>
</view>
</view>
<view class="txt">
<view class="txtHeader">
<image src="/static/deepExploration-images/plus.png" mode="aspectFill"></image>
<text>{{navItems[currentIndex].name}}</text>
</view>
<view class="txtContent">
<view v-if="loading" class="loading">加载中...</view>
<rich-text :nodes="htmlContent"></rich-text>
</view>
</view>
</view>
</view>
<!-- 底部切换栏 -->
<footerBar class="static-footer" :type="type"></footerBar>
</view>
</template>
<script setup>
import {
ref,
onMounted,
watch
} from 'vue'
import deepExploration_header from '@/components/deepExploration_header.vue'
import footerBar from '@/components/footerBar.vue'
import {
onLoad
} from '@dcloudio/uni-app'
import {
getModel1First,
getModel1Second,
getModel2First,
getModel2Second,
getModel3First,
getModel3Second,
getModel4First,
getModel4Second,
getModeldefault,
getData
} from '/api/deepExploration/deepExploration.js'
import marked from 'marked'; // 引入 marked 库
import hljs from 'highlight.js';
import 'highlight.js/styles/atom-one-dark.css'; // 可替换为其他主题
import {
useDeepExplorationStore
} from '@/stores/modules/deepExploration'
import {
getUserInfo
} from "@/api/member"
import {
useUserStore
} from '@/stores/modules/userInfo.js'
const deepExplorationStore = useDeepExplorationStore()
const userInfo = getUserInfo()
//历史数据
const historyData = ref({})
//登录弹窗提示ref
const loginPrompt = ref(null)
// 响应式变量定义
const type = ref('deepExploration')
const iSMT = ref(0)
const currentIndex = ref(0)
const navItems = ref([{
name: '主力追踪',
icon: '/static/deepExploration-images/1.png'
},
{
name: '主力雷达',
icon: '/static/deepExploration-images/2.png'
},
{
name: '主力解码',
icon: '/static/deepExploration-images/3.png'
},
{
name: '主力资金流',
icon: '/static/deepExploration-images/4.png'
},
])
//搜索股票
const searchStock = () => {
htmlContent.value = ''
console.log('搜索参数:', stockName.value, currentIndex.value);
if (currentIndex.value >= 0 && currentIndex.value <= 3) {
handleModels()
} else {
uni.showToast({
title: '请选择模块',
icon: 'none',
duration: 2000
})
}
}
//点击四大模块
const handleModel = async (index) => {
htmlContent.value = ''
currentIndex.value = index
await handleModels()
// await getServerData()
}
const stockName = ref('Tesla Inc.')
const searchName = ref('')
const stockCode = ref('TSLA')
const language = ref('')
const recordId = ref('')
const parentId = ref('')
const stockId = ref('')
const market = ref('')
const stockTime = ref('2025/10/24')
const loading = ref(true);
const error = ref('');
const htmlContent = ref('');
const markdownContent = ref('');
const renderer = new marked.Renderer();
renderer.heading = function(text, level) {
return `<p>${text}</p>`;
};
// 初始化 marked 配置(支持代码高亮)
marked.setOptions({
highlight: (code, lang) => {
if (lang && hljs.getLanguage(lang)) {
return hljs.highlight(code, {
language: lang
}).value;
}
return hljs.highlightAuto(code).value;
},
renderer,
breaks: true, // 换行转<br>
gfm: true, // 支持GitHub flavored Markdown
sanitize: false, // 保留HTML标签(如<span style>)
});
//获取模型数据
const handleModels = async () => {
try {
if (userInfo.isVisitor) {
console.log('是游客');
loginPrompt.value.show()
return
}
console.log('搜了吗');
// markdownContent.value = '\n## 📊 主力追踪分析:\n\n### 🕵️ 主力行为\n\t1. 📊 该股庄家中长期筹码成本价格为 360.249,短期资金成本价格为 412.577。该股筹码分散,当日筹码成本价格为 444.330。\n\t2. 🔍 近日没有出现主力集中吸筹。\n\t3. 📈 近期主力持仓比例大于散户持仓比例。 当日主力持仓增加。 当日散户持仓减少。\n\n### 📊 空间维度:\n\t- 📉 预测低一值: <font color=\"#13c2c2\">443.092</font> \n - 📈 预测高一值: <font color=\"#ff4d4f\">466.458</font>\n\t- 📉 预测低二值: <font color=\"#13c2c2\">447.354</font>\n\t- 📈 预测高二值: <font color=\"#ff4d4f\">462.514</font>\n\t<font color=\"#722ed1\">AI智能均线空头排列,当前卖盘小于买盘</font>\n\n### 综合作战\n\t\t\t<font color=\"#fa8c16\">当前股票处于安全区,牵牛绳为红色,出现蓝色推进K线。</font>\n\t\t\t<font color=\"#eb2f96\">该股整体趋势相对较强,个股正处于推进上涨的关键阶段。若当前持有该股票,建议继续持有,进行持续跟踪。若当前无该股票,建议持续跟踪,等待适当时机再进行介入。</font>\n\n---\n<font color=\"#8c8c8c\">*该内容由AI生成,仅供参考,投资有风险,请注意甄别。*</font>\n '
// htmlContent.value = marked.parse(markdownContent.value);
loading.value = true;
if (searchName.value == '') {
console.log('没有搜索', searchName.value);
handleDefault()
} else {
if (currentIndex.value == 0) {
console.log('搜索', searchName.value);
const result = await getModel1First({
content: searchName.value,
language: "cn",
marketList: "hk,cn,usa,my,sg,vi,in,gb",
model: currentIndex.value + 1
})
console.log('result', result);
if (result.code == 200) {
stockCode.value = result.data.code
// stockName.value = result.data.name
recordId.value = result.data.recordId
parentId.value = result.data.parentId
stockId.value = result.data.stockId
language.value = result.data.language
market.value = result.data.market
const res = await getModel1Second({
language: language.value,
recordId: recordId.value,
parentId: parentId.value,
stockId: stockId.value,
token: 'pCtw6AYK0EHAaIexoFHsbZjtsfEAIhcmwkCFm6uKko8VPfMvyDiODL9v9c0veic9fIpQbvT8zN4sH/Si6Q'
})
if (res.code == 200) {
const rawMarkdown = res.data.markdown;
const adaptedMarkdown = rawMarkdown.replace(/^### /gm, ''); // 全局替换行首的###
markdownContent.value = adaptedMarkdown;
// markdownContent.value = res.data.markdown
htmlContent.value = marked.parse(markdownContent.value);
await getServerData()
}
console.log('res', res);
} else if (result.code == 400) {
markdownContent.value = result.message;
htmlContent.value = marked.parse(markdownContent.value);
} else {
return
}
} else if (currentIndex.value == 1) {
console.log('搜索', searchName.value);
const result = await getModel2First({
content: searchName.value,
language: "cn",
marketList: "hk,cn,usa,my,sg,vi,in,gb",
model: currentIndex.value + 1
})
console.log('result', result);
if (result.code == 200) {
stockCode.value = result.data.code
recordId.value = result.data.recordId
parentId.value = result.data.parentId
stockId.value = result.data.stockId
language.value = result.data.language
market.value = result.data.market
const res = await getModel2Second({
language: language.value,
recordId: recordId.value,
parentId: parentId.value,
stockId: stockId.value,
token: 'pCtw6AYK0EHAaIexoFHsbZjtsfEAIhcmwkCFm6uKko8VPfMvyDiODL9v9c0veic9fIpQbvT8zN4sH/Si6Q'
})
if (res.code == 200) {
const rawMarkdown = res.data.markdown;
const adaptedMarkdown = rawMarkdown.replace(/^### /gm, ''); // 全局替换行首的###
markdownContent.value = adaptedMarkdown;
// markdownContent.value = res.data.markdown
htmlContent.value = marked.parse(markdownContent.value);
await getServerData()
}
console.log('res', res);
} else if (result.code == 400) {
markdownContent.value = result.message;
htmlContent.value = marked.parse(markdownContent.value);
} else {
return
}
} else if (currentIndex.value == 2) {
console.log('搜索', searchName.value);
const result = await getModel3First({
content: searchName.value,
language: "cn",
marketList: "hk,cn,usa,my,sg,vi,in,gb",
model: currentIndex.value + 1
})
console.log('result', result);
if (result.code == 200) {
stockCode.value = result.data.code
// stockName.value = result.data.name
recordId.value = result.data.recordId
parentId.value = result.data.parentId
stockId.value = result.data.stockId
language.value = result.data.language
market.value = result.data.market
const res = await getModel3Second({
language: language.value,
recordId: recordId.value,
parentId: parentId.value,
stockId: stockId.value,
token: 'pCtw6AYK0EHAaIexoFHsbZjtsfEAIhcmwkCFm6uKko8VPfMvyDiODL9v9c0veic9fIpQbvT8zN4sH/Si6Q'
})
if (res.code == 200) {
const rawMarkdown = res.data.markdown;
const adaptedMarkdown = rawMarkdown.replace(/^### /gm, ''); // 全局替换行首的###
markdownContent.value = adaptedMarkdown;
// markdownContent.value = res.data.markdown
htmlContent.value = marked.parse(markdownContent.value);
await getServerData()
}
console.log('res', res);
} else if (result.code == 400) {
markdownContent.value = result.message;
htmlContent.value = marked.parse(markdownContent.value);
} else {
return
}
} else if (currentIndex.value == 3) {
console.log('搜索', searchName.value);
const result = await getModel4First({
content: searchName.value,
language: "cn",
marketList: "hk,cn,usa,my,sg,vi,in,gb",
model: currentIndex.value + 1
})
console.log('result', result);
if (result.code == 200) {
stockCode.value = result.data.code
// stockName.value = result.data.name
recordId.value = result.data.recordId
parentId.value = result.data.parentId
stockId.value = result.data.stockId
language.value = result.data.language
market.value = result.data.market
const res = await getModel4Second({
language: language.value,
recordId: recordId.value,
parentId: parentId.value,
stockId: stockId.value,
token: 'pCtw6AYK0EHAaIexoFHsbZjtsfEAIhcmwkCFm6uKko8VPfMvyDiODL9v9c0veic9fIpQbvT8zN4sH/Si6Q'
})
if (res.code == 200) {
const rawMarkdown = res.data.markdown;
const adaptedMarkdown = rawMarkdown.replace(/^### /gm, ''); // 全局替换行首的###
markdownContent.value = adaptedMarkdown;
// markdownContent.value = res.data.markdown
htmlContent.value = marked.parse(markdownContent.value);
await getServerData()
}
console.log('res', res);
} else if (result.code == 400) {
markdownContent.value = result.message;
htmlContent.value = marked.parse(markdownContent.value);
} else {
return
}
} else {
return
}
}
} catch (e) {
error.value = e.message || '加载失败,请重试';
} finally {
loading.value = false;
}
}
const handleDefault = async () => {
const result = await getModeldefault({
token: "pCtw6AYK0EHAaIexoFHsbZjtsfEAIhcmwkCFm6uKko8VPfMvyDiODL9v9c0veic9fIpQbvT8zN4sH/Si6Q",
model: currentIndex.value + 1
})
if (result.code == 200) {
const rawMarkdown = result.data.markdown;
const adaptedMarkdown = rawMarkdown.replace(/^### /gm, ''); // 全局替换行首的###
markdownContent.value = adaptedMarkdown;
htmlContent.value = marked.parse(markdownContent.value);
} else {
}
}
const stockPrice = ref('435.900')
const stockAdd = ref('22.410')
const stockChange = ref('5.120%')
const chartKey = ref(0);
const getServerData = async () => {
const result = await getData({
market: market.value || '',
code: stockCode.value || '',
language: "cn",
brainPrivilegeState: 1,
marketList: "usa.sg.my.hk.cn.can.vi.th.in.gb"
})
console.log('k线数据', result);
stockName.value = result.data.StockInformation.Name || 'Tesla Inc.'
stockCode.value = result.data.StockInformation.Code || 'TSLA'
stockTime.value = result.data.StockInformation.Time || '2025/10/29'
stockChange.value = result.data.StockInformation.Zhang || '5.120%'
stockAdd.value = result.data.StockInformation.ZhangFu || '22.410'
stockPrice.value = result.data.StockInformation.Price || '435.900'
if (result.data.chartData) {
const rawData = JSON.parse(JSON.stringify(result.data.chartData));
if (rawData.categories.length > 1) { // 确保至少保留一个日期
rawData.categories[rawData.categories.length - 1] = ''; // 删除最后一个日期
console.log('删了;');
}
chartData.value = {
...rawData
}
chartKey.value++;
console.log('chartData', chartData.value);
}
}
// 1. K线图配置
const opts = ref({
rotate: false,
rotateLock: false,
color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4", "#ea7ccc"],
padding: [15, 30, 0, 15],
dataLabel: false,
enableScroll: true,
enableMarkLine: false,
legend: {},
xAxis: {
labelCount: 4,
itemCount: 20,
disableGrid: true,
gridColor: "#CCCCCC",
gridType: "solid",
dashLength: 4,
scrollShow: false,
rotate: 45,
scrollAlign: "left",
scrollColor: "#A6A6A6",
scrollBackgroundColor: "#EFEBEF",
labelColor: "#8C8C8C",
fontSize: 9,
},
yAxis: {
labelColor: "#8C8C8C",
fontSize: 9
},
extra: {
candle: {
color: {
upLine: "#f04864",
upFill: "#f04864",
downLine: "#2fc25b",
downFill: "#2fc25b"
},
average: {
show: false,
name: ["MA5", "MA10", "MA30"],
day: [5, 10, 20],
color: ["#1890ff", "#2fc25b", "#facc14"]
}
},
markLine: {
type: "dash",
dashLength: 5,
data: [{
value: 2150,
lineColor: "#f04864",
showLabel: false
},
{
value: 2350,
lineColor: "#f04864",
showLabel: false
}
]
},
tooltip: {
showCategory: true
}
}
})
// 2. K线图数据(响应式定义)
const chartData = ref({
categories: [],
series: [{
name: '',
data: []
}]
})
//获取K线数据函数(直接定义,无需methods)
// const getServerData1 = () => {
// // 模拟服务器请求延时
// setTimeout(() => {
// const res = {
// "categories": [
// "2025/10/23",
// "2025/10/24",
// "2025/10/27"
// ],
// series: [{
// "name": "贵州茅台",
// "data": [
// [
// 1455.0,
// 1468.8,
// 1447.2,
// 1467.98
// ],
// [
// 1467.95,
// 1478.88,
// 1449.34,
// 1450.0
// ],
// [
// 1440.0,
// 1452.49,
// 1435.99,
// 1440.41
// ]
// ],
// }]
// }
// // 给响应式变量赋值(需修改.value)
// chartData.value = JSON.parse(JSON.stringify(res))
// }, 500)
// }
let unwatch = null;
// 生命周期钩子:组件挂载后执行(替代onReady)
onMounted(async () => {
iSMT.value = uni.getSystemInfoSync().statusBarHeight
getUserInfo()
await handleModels()
unwatch = watch(
() => deepExplorationStore.deepExplorationInfo, // 监听的目标值(函数返回,避免响应式丢失)
(newVal, oldVal) => {
console.log('deepExplorationInfo 变化了:', newVal)
historyData.value = {
...newVal
}
console.log(historyData.value.wokeFlowData);
console.log('222', historyData.value.stockData.StockInformation);
//工作流数据
const rawMarkdown = historyData.value.wokeFlowData.One.markdown;
const adaptedMarkdown = rawMarkdown.replace(/^### /gm, ''); // 全局替换行首的###
markdownContent.value = adaptedMarkdown;
// markdownContent.value = res.data.markdown
htmlContent.value = marked.parse(markdownContent.value);
//k线
if (historyData.value.stockData.chartData.categories.length > 1) { // 确保至少保留一个日期
historyData.value.stockData.chartData.categories[historyData.value.stockData.chartData.categories.length - 1] = ''; // 删除最后一个日期
}
chartData.value = {
...JSON.parse(JSON.stringify(historyData.value.stockData.chartData))
}
chartKey.value++;
console.log('chartData', chartData.value);
stockName.value = historyData.value.stockData.StockInformation.Name || 'Tesla Inc.'
stockCode.value = historyData.value.stockData.StockInformation.Code || 'TSLA'
stockTime.value = historyData.value.stockData.StockInformation.Time || '2025/10/29'
stockChange.value = historyData.value.stockData.StockInformation.Zhang || '5.120%'
stockAdd.value = historyData.value.stockData.StockInformation.ZhangFu || '22.410'
stockPrice.value = historyData.value.stockData.StockInformation.Price || '435.900'
currentIndex.value = historyData.value.model-1
}, {
deep: true,
immediate: true
} // 开启深度监听(对象内部属性变化也能触发)
)
})
// 页面加载时执行
onLoad((e) => {
if (e.index) {
// currentIndex.value = e.index - 1
console.log('模块:', currentIndex.value)
}
if (e.stockName) {
searchName.value = e.stockName
console.log('股票名称:', searchName.value)
}
})
</script>
<style scoped lang="scss">
.main {
width: 100%;
min-height: 100vh;
background-color: #fff;
padding-bottom: 120rpx;
.search {
position: relative;
display: flex;
align-items: center;
background-color: #F3F3F3;
width: calc(100% - 60rpx);
height: 80rpx;
border-radius: 50rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
padding: 0 40rpx;
margin: 15rpx 30rpx 0 30rpx;
.seachIcon {
position: absolute;
right: 50rpx;
width: 32rpx;
height: 32rpx;
}
.searchInput {
color: #111;
width: 100%;
}
}
.content {
margin-top: 30rpx;
padding-top: 30rpx;
background-color: rgb(248, 248, 248);
.select {
position: relative;
margin-bottom: -5rpx;
.img {
width: 750rpx;
height: 198rpx;
}
.selectItem {
.btn {
position: absolute;
width: 120rpx;
height: 150rpx;
background-color: transparent;
&::after {
border: none;
}
}
&:nth-of-type(1) .btn {
top: 30rpx;
left: 60rpx;
}
&:nth-of-type(2) .btn {
top: 30rpx;
left: 230rpx;
}
&:nth-of-type(3) .btn {
top: 30rpx;
left: 400rpx;
}
&:nth-of-type(4) .btn {
top: 30rpx;
left: 570rpx;
}
}
}
.graphAndTxt {
background-color: #fff;
border-radius: 50rpx 50rpx 0 0;
padding: 68.6rpx 36.5rpx 0 36.5rpx;
.graph {
border: 1rpx solid #e2e2e2;
border-radius: 30rpx 30rpx 0 0;
.graph_header {
padding: 32rpx 20.5rpx 0 24rpx;
display: flex;
align-items: center;
.left {
color: #333333;
font-family: "PingFang SC";
font-size: 15px;
font-style: normal;
font-weight: 400;
line-height: 15px;
}
.center {
margin-left: 155rpx;
display: flex;
align-items: center;
text {
width: 160rpx;
height: 36rpx;
padding-left: 10rpx;
color: #000000;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
text-align: center;
font-family: "PingFang SC";
font-size: 18px;
font-style: normal;
font-weight: 500;
line-height: 18px;
}
}
.right {
margin-left: 50rpx;
color: #6a6a6a;
font-family: "PingFang SC";
font-size: 13px;
font-style: normal;
font-weight: 400;
line-height: 15px;
white-space: nowrap;
}
}
.graph_data {
display: flex;
padding: 48rpx 24rpx;
text {
display: flex;
color: #25ba5d;
font-family: "PingFang SC";
font-size: 17px;
line-height: 17px;
}
text:nth-child(2) {
margin-left: 120rpx;
}
text:nth-child(3) {
margin-left: 150rpx;
}
}
.graph_content {
min-height: 500rpx;
.charts-box {
width: 100%;
height: 100%;
overflow: visible;
}
}
}
.txt {
background-color: #F3F3F3;
margin-top: 48rpx;
border-radius: 30rpx;
.txtHeader {
padding: 30rpx 24rpx;
image {
width: 20rpx;
height: 20rpx;
}
text {
margin-left: 5rpx;
background-color: #FFFFFF;
color: #000000;
padding: 5rpx 22rpx;
border-radius: 22rpx;
font-size: 28rpx;
font-weight: 400;
line-height: 37rpx;
}
}
.txtContent {
min-height: 300rpx;
padding: 20rpx 30rpx;
margin-bottom: 100rpx;
::v-deep * {
box-sizing: border-box;
width: 100% !important; // 强制所有解析后的标签占满容器宽度
white-space: normal !important; // 取消强制不换行
word-wrap: break-word !important; // 长词/长数字自动换行
}
// 标题样式(确保换行)
::v-deep h2 {
font-size: 32rpx;
color: #333;
margin: 25rpx 0 15rpx;
line-height: 1.5;
}
// 段落样式(核心换行控制)
::v-deep p {
font-size: 26rpx;
color: #666;
margin: 15rpx 0;
line-height: 1.8; // 增加行高,提升可读性
text-align: justify; // 两端对齐,避免单侧参差不齐
}
// 列表样式(纵向排列)
::v-deep ul,
::v-deep ol {
margin: 15rpx 0 15rpx 30rpx;
}
::v-deep li {
margin: 10rpx 0;
line-height: 1.6;
}
// 加载状态样式
.loading {
text-align: center;
padding: 50rpx 0;
color: #666;
font-size: 26rpx;
}
}
}
}
}
.static-footer {
position: fixed;
bottom: 0;
width: 100%;
}
}
* {
box-sizing: border-box;
}
</style>