Browse Source

Merge branch 'milestone-20251031-简版功能开发' of http://39.101.133.168:8807/qimaohong/deepChartVueApp into milestone-20251031-简版功能开发

songjie/feature-20251023161635-首页
dongqian 3 weeks ago
parent
commit
c8effb1ac0
  1. 13
      api/member.js
  2. 66
      components/IndexCard.vue
  3. 54
      components/h-loading.vue
  4. 7
      package.json
  5. 164
      pages/deepExploration/MainForceActions.vue
  6. 264
      pages/home/member.vue
  7. 25
      pages/marketSituation/countryMarket.vue
  8. 8
      pages/marketSituation/globalIndex.vue
  9. 15
      pages/marketSituation/marketCondition.vue
  10. 29
      pages/marketSituation/marketDetail.vue
  11. 48
      pages/marketSituation/marketOverview.vue
  12. 9
      pages/marketSituation/marketSituation.vue
  13. 6
      pages/setting/about.vue
  14. 20
      pages/setting/account.vue
  15. 4
      pages/setting/bind.vue
  16. 10
      pages/setting/createPwd.vue
  17. 20
      pages/setting/email.vue
  18. 10
      pages/setting/general.vue
  19. 32
      pages/setting/market.vue
  20. 2
      pages/setting/password.vue
  21. BIN
      static/loading-gif.gif
  22. BIN
      static/marketSituation-image/downTimeChart.png
  23. BIN
      static/marketSituation-image/upTimeChart.png
  24. BIN
      static/my/Check-in.png
  25. BIN
      static/my/MarketSettings.png
  26. BIN
      static/my/Settings.png
  27. BIN
      static/my/aboutDC.png
  28. BIN
      static/my/bell.png
  29. BIN
      static/my/common.png
  30. BIN
      static/my/default-avatar.png
  31. BIN
      static/my/editName.png
  32. BIN
      static/my/outline.png
  33. BIN
      static/my/polylines.png
  34. BIN
      static/my/security.png
  35. BIN
      static/my/share.png
  36. 2
      stores/modules/marketSituation.js
  37. 26
      utils/http.js

13
api/member.js

@ -29,9 +29,14 @@ export const getUserInfo = (data) => {
return http({
method: 'POST',
url: '/api/my/userInfo',
data: data,
header:{
token:'014de5283d2930af6481ede591afd087'
}
data: data
})
}
export const updateUserInfo = (data) => {
return http({
method: 'POST',
url: '/api/my/updateUserInfo',
data: data
})
}

66
components/IndexCard.vue

@ -18,9 +18,8 @@
</view>
<view class="chart-container">
<view class="mini-chart" :style="{ backgroundColor: chartBgColor }">
<!-- 这里可以放置实际的图表组件目前用简单的波浪线表示 -->
<view class="chart-line" :style="{ borderColor: priceColor }"></view>
<view class="mini-chart" >
<image class="time-chart" :src="timeChart" mode="aspectFit"></image>
</view>
</view>
</view>
@ -64,7 +63,18 @@ const props = defineProps({
});
const judgeSymbol = (num) => {
return num[0] === "-" ? num : "+" + num;
// undefined/null//
if (num === null || num === undefined) return '';
const n = Number(num);
if (!isNaN(n)) {
// '+'
return (n < 0 ? '' : '+') + n;
}
//
const s = String(num).trim();
if (s.startsWith('-')) return s;
if (s.startsWith('+')) return s;
return '+' + s;
};
const getMarketFlag = (market) => {
@ -97,10 +107,14 @@ const priceColor = computed(() => {
return props.isRising ? "#00C853" : "#FF1744";
});
//
const chartBgColor = computed(() => {
return props.isRising ? "#E8F5E8" : "#FFEBEE";
const timeChart = computed(() => {
return props.isRising ? "/static/marketSituation-image/upTimeChart.png" : "/static/marketSituation-image/downTimeChart.png";
});
//
// const chartBgColor = computed(() => {
// return props.isRising ? "#E8F5E8" : "#FFEBEE";
// });
</script>
<style scoped>
@ -182,40 +196,8 @@ const chartBgColor = computed(() => {
border-radius: 8rpx;
}
.chart-line {
position: absolute;
bottom: 20rpx;
left: 10rpx;
right: 10rpx;
height: 2rpx;
border-top: 2rpx solid;
border-style: solid;
}
/* 添加一些波浪效果 */
.chart-line::before {
content: "";
position: absolute;
top: -10rpx;
left: 20%;
width: 20rpx;
height: 20rpx;
border: 2rpx solid;
border-color: inherit;
border-radius: 50%;
background: transparent;
}
.chart-line::after {
content: "";
position: absolute;
top: -6rpx;
right: 30%;
width: 12rpx;
height: 12rpx;
border: 2rpx solid;
border-color: inherit;
border-radius: 50%;
background: transparent;
.time-chart {
width: 100%;
height: 100%;
}
</style>

54
components/h-loading.vue

@ -0,0 +1,54 @@
<!-- @format -->
<template>
<view v-if="loading" class="loading">
<image class="loadingGif" src="/static/loading-gif.gif" mode=""></image>
</view>
</template>
<script setup>
import { ref, computed, onMounted,watch } from "vue";
// props
const props = defineProps({
loading: {
type: Boolean,
default: false,
},
});
const loading = ref(false);
// props.loading
watch(() => props.loading, (newVal) => {
loading.value = newVal;
});
//
onMounted(() => {
loading.value = props.loading;
});
</script>
<style scoped>
.loading {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
}
.loadingGif {
width: 100rpx;
height: 100rpx;
}
</style>

7
package.json

@ -1,4 +1,8 @@
{
"scripts": {
"dev:h5": "cross-env NODE_OPTIONS=\"--max-old-space-size=4096\" uni build --watch",
"build:h5": "cross-env NODE_OPTIONS=\"--max-old-space-size=4096\" uni build"
},
"dependencies": {
"@dcloudio/uni-ui": "^1.5.11",
"@element-plus/icons-vue": "^2.3.2",
@ -8,5 +12,8 @@
"pinia": "^3.0.3",
"pinia-plugin-persistedstate": "^4.5.0",
"vue-i18n": "^9.14.5"
},
"devDependencies": {
"cross-env": "^7.0.3"
}
}

164
pages/deepExploration/MainForceActions.vue

@ -41,7 +41,30 @@
<qiun-data-charts type="candle" :opts="opts" :chartData="chartData" :disableScroll="true"
:ontouch="true" :onzoom="true" :key="chartKey" />
</view>
<image src="/static/deepExploration-images/kLineAll.png" mode="aspectFill"></image>
<image @click.stop="showFullscreenKline" src="/static/deepExploration-images/kLineAll.png"
mode="aspectFill"></image>
</view>
</view>
<!-- 全屏横屏弹窗 -->
<view class="fullscreen-mask" v-if="isFullscreen" @click="closeFullscreen">
<view class="fullscreen-container" :style="{ transform: isLandscape ? 'rotate(90deg)' : '' }">
<!-- 关闭按钮 -->
<view class="fullscreen-close" @click.stop="closeFullscreen">
<image src="/static/icons/close.png" mode="widthFix"></image>
</view>
<!-- 横屏切换按钮 -->
<view class="fullscreen-rotate" @click.stop="toggleOrientation">
<image src="/static/icons/rotate.png" mode="widthFix"></image>
</view>
<!-- 全屏K线图 -->
<view class="fullscreen-chart">
<qiun-data-charts type="candle" :opts="fullscreenOpts" :chartData="chartData"
:key="fullscreenChartKey" />
</view>
</view>
</view>
<view class="txt">
@ -107,6 +130,37 @@
//ref
const loginPrompt = ref(null)
//
const isFullscreen = ref(false); //
const isLandscape = ref(true); //
const fullscreenChartKey = ref(0); //
// K线
const showFullscreenKline = () => {
isFullscreen.value = true;
isLandscape.value = true; //
//
setTimeout(() => {
fullscreenChartKey.value++;
}, 100);
};
//
const closeFullscreen = () => {
isFullscreen.value = false;
};
//
const toggleOrientation = () => {
isLandscape.value = !isLandscape.value;
//
setTimeout(() => {
fullscreenChartKey.value++;
}, 300);
};
//
const type = ref('deepExploration')
const iSMT = ref(0)
@ -159,7 +213,7 @@
const recordId = ref('')
const parentId = ref('')
const stockId = ref('')
const market = ref('')
const market = ref('usa')
const stockTime = ref('2025/10/24')
const loading = ref(true);
@ -204,6 +258,7 @@
if (searchName.value == '') {
console.log('没有搜索', searchName.value);
handleDefault()
getServerData()
} else {
if (currentIndex.value == 0) {
console.log('搜索', searchName.value);
@ -495,6 +550,28 @@
}
})
//
const fullscreenOpts = ref({
...opts.value, //
padding: [30, 30, 30, 30],
xAxis: {
...opts.value.xAxis,
labelCount: 8, //
fontSize: 12
},
yAxis: {
...opts.value.yAxis,
fontSize: 12
},
extra: {
...opts.value.extra,
candle: {
...opts.value.extra.candle,
width: 12 // K线
}
}
});
// 2. K线
const chartData = ref({
categories: [],
@ -548,6 +625,7 @@
let unwatch = null;
// onReady
onMounted(async () => {
iSMT.value = uni.getSystemInfoSync().statusBarHeight
getUserInfo()
await handleModels()
@ -571,7 +649,8 @@
//k线
if (historyData.value.stockData.chartData.categories.length > 1) { //
historyData.value.stockData.chartData.categories[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))
@ -585,7 +664,7 @@
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
currentIndex.value = historyData.value.model - 1
}, {
deep: true,
immediate: true
@ -597,7 +676,7 @@
//
onLoad((e) => {
if (e.index) {
// currentIndex.value = e.index - 1
currentIndex.value = e.index - 1
console.log('模块:', currentIndex.value)
}
if (e.stockName) {
@ -771,7 +850,7 @@
position: relative;
min-height: 500rpx;
image{
image {
position: absolute;
bottom: 20rpx;
right: 30rpx;
@ -787,6 +866,79 @@
}
}
/* 横屏按钮样式 */
.rotate-btn {
background: transparent;
padding: 0 10rpx;
margin-left: 15rpx;
.btn-icon {
width: 36rpx;
height: 36rpx;
vertical-align: middle;
}
}
/* 全屏遮罩 */
.fullscreen-mask {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background-color: #000;
z-index: 9999;
display: flex;
justify-content: center;
align-items: center;
}
/* 全屏容器 */
.fullscreen-container {
width: 100vh; /* 横屏时宽度等于屏幕高度 */
height: 100vw; /* 横屏时高度等于屏幕宽度 */
transition: transform 0.3s ease;
position: relative;
}
/* 关闭按钮 */
.fullscreen-close {
position: absolute;
top: 20rpx;
right: 20rpx;
z-index: 10;
image {
width: 48rpx;
height: 48rpx;
}
}
/* 旋转按钮 */
.fullscreen-rotate {
position: absolute;
top: 20rpx;
left: 20rpx;
z-index: 10;
image {
width: 48rpx;
height: 48rpx;
}
}
/* 全屏图表容器 */
.fullscreen-chart {
width: 100%;
height: 100%;
}
/* 竖屏模式适配 */
:deep(.fullscreen-container:not([style*="rotate(90deg)"])) {
width: 100vw;
height: 100vh;
}
.txt {
background-color: #F3F3F3;
margin-top: 48rpx;

264
pages/home/member.vue

@ -1,22 +1,30 @@
<template>
<LoginPrompt ref="loginwPrompt"></LoginPrompt>
<LoginPrompt ref="loginPrompt"></LoginPrompt>
<view class="main">
<view class="top">
<view class="bell">
<image class="image-bell" src="/static/my/bell.png"></image>
<!-- <image class="image-bell" src="/static/my/bell.png" /> -->
</view>
<view class="msg">
<view class="msg-left">
<view class="avatar"></view>
<image class="avatar"
:src="avatarUrl || 'https://d31zlh4on95l9h.cloudfront.net/default/1961d9d2e31e78e47e885231e7514a35.png'"
mode="aspectFill" />
</view>
<view class="msg-center">
<view class=" msg-center">
<view style="display: flex;">
<template v-if="!isEditingName">
<view class="userInfo">{{ username }}</view>
<image class="image-editName" src="/static/my/editName.png"></image>
<image class="image-editName" src="/static/my/editName.png" @click="startEditName" />
</template>
<template v-else>
<input v-model="editingName" class="edit-input" @blur="finishEditName"
@keyup.enter="finishEditName" autofocus placeholder="请输入用户名" />
</template>
</view>
<view class="userId">ID:{{ dccode }}</view>
</view>
<!-- <view class="msg-right">
<!-- <view class="msg-right">
<image class="image-attendance" src="/static/my/Check-in.png"/>
<span style="font-size:10px;">签到</span>
</view> -->
@ -24,47 +32,47 @@
<view class="settings-buttons">
<view class="setting-btn" @click="goToMarket">
<image src="/static/my/MarketSettings.png" class="setting-icon"/>
<image src="/static/my/MarketSettings.png" class="setting-icon" />
<text>行情设置</text>
</view>
<view class="setting-btn" @click="goToGeneral">
<image src="/static/my/Settings.png" class="setting-icon"/>
<image src="/static/my/Settings.png" class="setting-icon" />
<text>通用设置</text>
</view>
</view>
<view class="share" @click="goToShare">
<image class="img-share" src="/static/my/share.png" mode="widthFix"/>
<image class="img-share" src="/static/my/share.png" mode="widthFix" />
</view>
</view>
<view class="bottom">
<view class="list-item" @click="goToAccount">
<image src="/static/my/security.png" class="list-icon"/>
<image src="/static/my/security.png" class="list-icon" />
<text>账号与安全</text>
<uni-icons type="arrowright" size="16" class="arrow"/>
<uni-icons type="arrowright" size="16" class="arrow" />
</view>
<view class="list-item">
<image src="/static/my/connection.png" class="list-icon"/>
<!-- <view class="list-item">
<image src="/static/my/connection.png" class="list-icon" />
<text>联系我们</text>
<uni-icons type="arrowright" size="16" class="arrow"/>
<uni-icons type="arrowright" size="16" class="arrow" />
</view>
<view class="list-item" @click="goToNewVersion">
<image src="/static/my/update.png" class="list-icon"/>
<image src="/static/my/update.png" class="list-icon" />
<text>新版本更新</text>
<view class="update-tip">有新版本可更新
<view class="circle"></view>
</view>
<uni-icons type="arrowright" size="16" class="arrow"/>
<uni-icons type="arrowright" size="16" class="arrow" />
</view>
<view class="list-item">
<image src="/static/my/opinion.png" class="list-icon"/>
<image src="/static/my/opinion.png" class="list-icon" />
<text>意见反馈</text>
<uni-icons type="arrowright" size="16" class="arrow"/>
</view>
<uni-icons type="arrowright" size="16" class="arrow" />
</view> -->
<view class="list-item" @click="goToAbout">
<image src="/static/my/about.png" class="list-icon"/>
<image src="/static/my/about.png" class="list-icon" />
<text>关于DeepChart</text>
<uni-icons type="arrowright" size="16" class="arrow"/>
<uni-icons type="arrowright" size="16" class="arrow" />
</view>
</view>
<footerBar class="static-footer" :type="type"></footerBar>
@ -72,63 +80,100 @@
</template>
<script setup>
import {
import {
ref,
onMounted
} from 'vue'
import {
} from 'vue'
import {
ArrowRight
} from '@element-plus/icons-vue'
import footerBar from '../../components/footerBar.vue'
import {getUserInfo} from "@/api/member"
import { useUserStore } from "../../stores/modules/userInfo"
const userStore = useUserStore()
const type = ref('member')
const iSMT = ref(0)
const username = ref('')
const dccode = ref('')
const userInfoRes = ref()//
userInfoRes.value = getUserInfo()
userInfoRes.value.then(res => {
} from '@element-plus/icons-vue'
import footerBar from '../../components/footerBar.vue'
import {
getUserInfo,
updateUserInfo
} from "@/api/member"
import {
useUserStore
} from "../../stores/modules/userInfo"
const userStore = useUserStore()
const type = ref('member')
const iSMT = ref(0)
const username = ref('')
const dccode = ref('')
const avatarUrl = ref('')
const isEditingName = ref(false)
const editingName = ref('')
const userInfoRes = ref() //
userInfoRes.value = getUserInfo()
userInfoRes.value.then(res => {
username.value = res.data.dcname
dccode.value = res.data.dccode
avatarUrl.value = res.data.avatar
console.log('用户信息', userInfoRes.value)
})
})
const startEditName = () => {
isEditingName.value = true
editingName.value = username.value
}
const goToGeneral = () => {
const finishEditName = async () => {
if (editingName.value.trim() === '' || editingName.value === username.value) {
isEditingName.value = false
return
}
try {
await updateUserInfo({
dcname: editingName.value
})
uni.showToast({
title: '昵称修改成功',
icon: 'none'
})
} catch (err) {
uni.showToast({
title: '修改失败,请重试',
icon: 'none'
})
} finally {
isEditingName.value = false
}
}
const goToGeneral = () => {
uni.navigateTo({
url: '/pages/setting/general'
})
}
}
const goToMarket = () => {
const goToMarket = () => {
uni.navigateTo({
url: '../setting/market'
})
}
}
const goToAccount = () => {
const goToAccount = () => {
uni.navigateTo({
url: '../setting/account'
})
}
}
const goToNewVersion = () => {
const goToNewVersion = () => {
uni.navigateTo({
url: '../setting/newVersion'
})
}
}
const goToAbout = () => {
const goToAbout = () => {
uni.navigateTo({
url: '../setting/about'
})
}
}
const goToShare = () => {
const goToShare = () => {
console.log('用户信息==========',userStore.userInfo.isVisitor)
if (userStore.userInfo.isVisitor) {
uni.showToast({
@ -140,101 +185,102 @@ const goToShare = () => {
uni.navigateTo({
url: '../setting/share'
})
}
}
onMounted(() => {
onMounted(() => {
//
iSMT.value = uni.getSystemInfoSync().statusBarHeight
console.log('??????????????', iSMT.value)
console.log('通信来的用户身份',userStore.userInfo)
})
console.log('通信来的用户身份', userStore.userInfo)
})
</script>
<style scoped>
.static-footer {
.static-footer {
position: fixed;
bottom: 0;
}
}
.top {
.top {
height: 50vh;
background-color: white;
}
}
.bell {
.bell {
height: 9.6vh;
display: flex;
align-items: flex-end;
justify-content: flex-end;
padding-right: 50rpx;
}
}
.image-bell {
.image-bell {
width: 26rpx;
height: 32rpx;
}
}
.msg {
.msg {
height: 10.7vh;
display: flex;
margin-top: 3vh;
margin-bottom: 3vh;
}
}
.msg-left {
.msg-left {
width: 252rpx;
display: flex;
justify-content: center;
align-items: center;
}
}
.avatar {
.avatar {
width: 175rpx;
height: 175rpx;
border-radius: 50%;
background-color: black;
}
}
.msg-center {
.msg-center {
width: 388rpx;
padding-left: 2.5vh;
display: flex;
flex-direction: column;
justify-content: center;
}
}
.userInfo {
font-size: 20px;
}
.userInfo {
font-size: 30rpx;
font-weight: bold;
}
.userId {
.userId {
font-size: 14px;
margin-top: 1vh;
}
}
.image-editName {
.image-editName {
width: 40rpx;
height: 40rpx;
margin-left: 15rpx;
}
}
.msg-right {
.msg-right {
display: flex;
flex-direction: column;
justify-content: center;
}
}
.image-attendance {
.image-attendance {
width: 43rpx;
height: 43rpx;
}
}
.settings-buttons {
.settings-buttons {
display: flex;
justify-content: space-around;
}
}
.setting-btn {
.setting-btn {
width: 349rpx;
height: 135rpx;
display: flex;
@ -243,75 +289,79 @@ onMounted(() => {
background-color: rgb(243, 243, 243);
border-radius: 8%;
margin-bottom: 1vh;
}
}
.setting-icon {
.setting-icon {
width: 64.7rpx;
height: 64.7rpx;
margin-right: 25rpx;
}
}
.setting-btn text {
.setting-btn text {
font-size: 28rpx;
font-weight: bold;
color: #333;
}
}
.share {
.share {
height: 12.6vh;
display: flex;
justify-content: center;
align-items: center;
}
}
.img-share {
.img-share {
width: 720rpx;
height: 160rpx;
}
}
.bottom {
.bottom {
height: 44.5vh;
margin-top: 1vh;
background-color: rgb(255, 255, 255);
}
}
.list-item {
.list-item {
width: 670rpx;
height: 7vh;
display: flex;
align-items: center;
margin: 0rpx 40rpx;
border-bottom: 1rpx solid #eee;
}
}
.list-item:last-child {
.list-item:last-child {
border-bottom: none;
}
}
.list-icon {
.list-icon {
width: 42rpx;
height: 42rpx;
margin-right: 18rpx;
}
}
.arrow {
.arrow {
margin-left: auto;
}
}
.update-tip {
.update-tip {
display: flex;
color: #999;
font-size: 24rpx;
align-items: center;
margin-left: 200rpx;
justify-content: center;
}
}
.circle {
.circle {
width: 10rpx;
height: 10rpx;
border-radius: 50%;
background-color: red;
margin-left: 10rpx;
}
}
.edit-input {
background-color: #999;
}
</style>

25
pages/marketSituation/countryMarket.vue

@ -17,7 +17,7 @@
</view>
<view class="indices_grid">
<view v-for="(index, i) in countryInfo" :key="i" class="index_item">
<IndexCard :market="index.market" :stockName="index.name" :currentPrice="index.price" :changeAmount="index.change" :changePercent="index.changePercent" :isRising="index.isRising" />
<IndexCard :market="index.market" :stockName="index.name" :currentPrice="index.price" :changeAmount="index.change" :changePercent="index.changePercent" :isRising="index.isRising" @click="viewIndexDetail(index,i)"/>
</view>
</view>
@ -95,6 +95,8 @@
import { ref, computed, onMounted, watch } from "vue";
import IndexCard from "../../components/IndexCard.vue";
import { queryStockDataAPI } from "@/api/marketSituation/marketSituation";
import { useMarketSituationStore } from "../../stores/modules/marketSituation.js";
const marketSituationStore = useMarketSituationStore();
onMounted(() => {
switchTab(0);
@ -111,6 +113,11 @@ const switchTab = (i) => {
}).then((res) => {
if (res.code === 200) {
countryInfo.value = res.data.dataPage.records;
marketSituationStore.countryMarketCardData = countryInfo.value.map((item) => ({
market: item.market,
stockCode: item.code,
stockName: item.name,
}));
console.log(res.data)
console.log(res.data.dataPage.records)
console.log(countryInfo.value);
@ -149,12 +156,23 @@ const props = defineProps({
//
const countryInfo = ref('')
//
const viewIndexDetail = (item, index) => {
console.log("查看指数详情:", item);
uni.navigateTo({
url: `/pages/marketSituation/marketCondition?stockInformation=${encodeURIComponent(JSON.stringify(item))}&index=${index}&from=countryMarket`,
});
};
//
const handleTabData = (tabData) => {
if (tabData && tabData.type === 'country' && tabData.data) {
if (tabData.data.dataPage && tabData.data.dataPage.records) {
countryInfo.value = tabData.data.dataPage.records;
marketSituationStore.countryMarketCardData = countryInfo.value.map((item) => ({
market: item.market,
stockCode: item.code,
stockName: item.name,
}));
console.log('countryMarket接收到数据:', countryInfo.value);
}
}
@ -169,9 +187,6 @@ watch(() => props.tabData, (newTabData) => {
//
const viewMore = (type) => {
// type
// indices/sectors/stocks
// uni.navigateTo({ url: `/pages/marketSituation/${type}List` })
};
</script>

8
pages/marketSituation/globalIndex.vue

@ -2,6 +2,8 @@
<template>
<view class="main">
<!-- 加载中 -->
<h-loading :loading="loading"></h-loading>
<!-- 固定头部 -->
<view class="header_fixed" :style="{ top: iSMT + 'px' }">
<view class="header_content">
@ -69,6 +71,7 @@ import { ref, onMounted, onUnmounted, computed, nextTick, watch } from "vue";
import { onShow, onHide } from "@dcloudio/uni-app";
import footerBar from "../../components/footerBar.vue";
import IndexCard from "../../components/IndexCard.vue";
import hLoading from "@/components/h-loading.vue";
import { getRegionalGroupAPI } from "../../api/marketSituation/marketSituation.js";
import { useMarketSituationStore } from "../../stores/modules/marketSituation.js";
const marketSituationStore = useMarketSituationStore();
@ -78,7 +81,7 @@ const contentHeight = ref(0);
const headerHeight = ref(0); //
const searchValue = ref(""); //
const isWarnTextOverflow = ref(false); // warn
const loading = ref(false);
//
const isDataLoaded = ref(false);
@ -160,11 +163,14 @@ const viewIndexDetail = (item, parentIndex, index) => {
const getRegionalGroup = async () => {
try {
loading.value = true;
const result = await getRegionalGroupAPI();
globalIndexArray.value = result.data;
marketSituationStore.gloablCardData = result.data;
loading.value = false;
} catch (e) {
console.log("获取区域指数失败", e);
loading.value = false;
}
};

15
pages/marketSituation/marketCondition.vue

@ -660,6 +660,8 @@ const toLeftPage = () => {
nextStockInformation = marketSituationStore.marketDetailCardData[currentStockIndex.value];
} else if (currentStockFrom.value == "globalIndex") {
nextStockInformation = marketSituationStore.gloablCardData[currentStockParentIndex.value].list[currentStockIndex.value];
} else if (currentStockFrom.value == "countryMarket") {
nextStockInformation = marketSituationStore.countryMarketCardData[currentStockIndex.value];
} else {
uni.showToast({
title: "没有更多股票了",
@ -713,6 +715,19 @@ const toRightPage = () => {
nextStockInformation = marketSituationStore.gloablCardData[currentStockParentIndex.value].list[currentStockIndex.value];
updateStockInformation(nextStockInformation);
}
} else if (currentStockFrom.value == "countryMarket") {
if (currentStockIndex.value == marketSituationStore.countryMarketCardData.length) {
uni.showToast({
title: "没有更多股票了",
icon: "none",
duration: 1000,
});
return;
} else {
currentStockIndex.value++;
nextStockInformation = marketSituationStore.countryMarketCardData[currentStockIndex.value];
updateStockInformation(nextStockInformation);
}
} else {
uni.showToast({
title: "没有更多股票了",

29
pages/marketSituation/marketDetail.vue

@ -2,6 +2,8 @@
<template>
<view class="main">
<!-- 加载中 -->
<h-loading :loading="loading"></h-loading>
<!-- 自定义导航栏 -->
<view class="header_fixed" :style="{ top: iSMT + 'px' }">
<view class="header-content">
@ -65,8 +67,9 @@
<script setup>
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from "vue";
import { onLoad } from "@dcloudio/uni-app";
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
import footerBar from "@/components/footerBar.vue";
import hLoading from "@/components/h-loading.vue";
import { getRegionalGroupListAPI } from "../../api/marketSituation/marketSituation.js";
import { useMarketSituationStore } from "../../stores/modules/marketSituation.js";
const marketSituationStore = useMarketSituationStore();
@ -77,7 +80,7 @@ const headerHeight = ref(80);
const marketTitle = ref();
const sortType = ref(""); // 'price' 'change'
const sortOrder = ref("desc"); // 'asc' 'desc'
const loading = ref(false);
const regionalGroupArray = ref([]);
//
@ -111,13 +114,16 @@ const judgeSymbol = (num) => {
const getRegionalGroupList = async () => {
try {
loading.value = true;
const result = await getRegionalGroupListAPI({
name: marketTitle.value,
});
regionalGroupArray.value = result.data;
marketSituationStore.marketDetailCardData = result.data;
loading.value = false;
} catch (e) {
console.error("获取区域分组列表失败:", e);
loading.value = false;
}
};
@ -430,15 +436,26 @@ const startTcp = () => {
}
};
//
onLoad(async (options) => {
if (options && options.market) {
marketTitle.value = options.market;
onShow(async () => {
console.log("显示页面");
await getRegionalGroupList();
initTcpListeners();
await nextTick();
//
startTcp();
});
onHide(() => {
console.log("隐藏页面");
sendTcpMessage("stop_real_time");
removeTcpListeners();
disconnectTcp();
});
//
onLoad(async (options) => {
if (options && options.market) {
marketTitle.value = options.market;
}
});

48
pages/marketSituation/marketOverview.vue

@ -2,14 +2,23 @@
<template>
<view class="main">
<h-loading :loading="loading"></h-loading>
<!-- 可滚动内容区域 -->
<scroll-view class="content_scroll" scroll-y="true" :style="{ top: contentTopPosition + 'px' }">
<view class="content">
<view class="map">
<view class="INDU">道琼斯<view :class="getSignClass(INDU.value)">{{ judgeSymbol(INDU.value) }}</view></view>
<view class="NDX">纳斯达克<view :class="getSignClass(NDX.value)">{{ judgeSymbol(NDX.value) }}</view></view>
<view class="HSI">恒生指数<view :class="getSignClass(HSI.value)">{{ judgeSymbol(HSI.value) }}</view></view>
<view class="CN">上证指数<view :class="getSignClass(CN.value)">{{ judgeSymbol(CN.value) }}</view></view>
<view class="INDU"
>道琼斯<view :class="getSignClass(INDU.value)">{{ judgeSymbol(INDU.value) }}</view></view
>
<view class="NDX"
>纳斯达克<view :class="getSignClass(NDX.value)">{{ judgeSymbol(NDX.value) }}</view></view
>
<view class="HSI"
>恒生指数<view :class="getSignClass(HSI.value)">{{ judgeSymbol(HSI.value) }}</view></view
>
<view class="CN"
>上证指数<view :class="getSignClass(CN.value)">{{ judgeSymbol(CN.value) }}</view></view
>
<image src="/static/marketSituation-image/map.png" mode="widthFix"></image>
</view>
<view class="global_index">
@ -54,6 +63,7 @@ import { ref, onMounted, onUnmounted, watch, nextTick, computed } from "vue";
import { onShow, onHide } from "@dcloudio/uni-app";
import util from "../../common/util.js";
import IndexCard from "../../components/IndexCard.vue";
import hLoading from "@/components/h-loading.vue";
import { useMarketSituationStore } from "../../stores/modules/marketSituation.js";
const marketSituationStore = useMarketSituationStore();
import { getGlobalIndexAPI } from "../../api/marketSituation/marketSituation.js";
@ -63,6 +73,7 @@ const searchValue = ref("");
const contentHeight = ref(0);
const headerHeight = ref(0); // header
const isWarnTextOverflow = ref(false); // warn
const loading = ref(false);
const INDU = ref({ stockName: "道琼斯", stockCode: "INDU", value: "" });
const NDX = ref({ stockName: "纳斯达克", stockCode: "513300", value: "" });
@ -91,13 +102,13 @@ const judgeSymbol = (num) => {
};
function getSignClass(value) {
const s = typeof value === 'string' ? value : String(value ?? '');
const s = typeof value === "string" ? value : String(value ?? "");
const trimmed = s.trim();
if (trimmed.startsWith('-')) return 'index-down';
if (trimmed.startsWith('+')) return 'index-up';
if (trimmed.startsWith("-")) return "index-down";
if (trimmed.startsWith("+")) return "index-up";
const n = parseFloat(trimmed);
if (!isNaN(n)) return n >= 0 ? 'index-up' : 'index-down';
return '';
if (!isNaN(n)) return n >= 0 ? "index-up" : "index-down";
return "";
}
//
@ -185,10 +196,13 @@ const goToGlobalIndex = () => {
const getGlobalIndex = async () => {
try {
loading.value = true;
const result = await getGlobalIndexAPI();
globalIndexArray.value = result.data;
loading.value = false;
} catch (e) {
console.log("获取全球指数失败", e);
loading.value = false;
}
};
@ -786,8 +800,8 @@ watch(headerHeight, (newHeight) => {
.NDX {
position: absolute;
top: 30%;
left: 17%;
top: 23vh;
left: 17vw;
transform: translate(-50%, -50%);
font-size: 11rpx;
color: #000000;
@ -801,8 +815,8 @@ watch(headerHeight, (newHeight) => {
}
.INDU {
position: absolute;
top: 22%;
left: 35%;
top: 15vh;
left: 35vw;
transform: translate(-50%, -50%);
font-size: 11rpx;
color: #000000;
@ -816,8 +830,8 @@ watch(headerHeight, (newHeight) => {
}
.HSI {
position: absolute;
top: 30%;
right: 4%;
top: 23vh;
right: 4vw;
transform: translate(-50%, -50%);
font-size: 11rpx;
color: #000000;
@ -831,8 +845,8 @@ watch(headerHeight, (newHeight) => {
}
.CN {
position: absolute;
top: 23%;
right: 8%;
top: 16vh;
right: 8vw;
transform: translate(-50%, -50%);
font-size: 11rpx;
color: #000000;

9
pages/marketSituation/marketSituation.vue

@ -177,11 +177,10 @@ const selected = () => {
//
const history = () => {
uni.showToast({
title: "历史记录",
icon: "none",
});
//
//
uni.navigateTo({
url: '/pages/customStockList/customStockList'
})
};
// Tab

6
pages/setting/about.vue

@ -12,7 +12,7 @@
<text class="label">产品介绍</text>
<uni-icons type="arrowright" size="16" />
</view>
<view class="bottom-list">
<!-- <view class="bottom-list">
<text class="label">免责声明</text>
<uni-icons type="arrowright" size="16" />
</view>
@ -27,7 +27,7 @@
<view class="bottom-list">
<text class="label">鼓励一下</text>
<uni-icons type="arrowright" size="16" />
</view>
</view> -->
</view>
</view>
</template>
@ -61,7 +61,7 @@
}
.bottom {
height: 35vh;
height: 7vh;
background-color: white;
}

20
pages/setting/account.vue

@ -1,6 +1,6 @@
<template>
<view class="main">
<view :style="{height:iSMT+'px'}"></view>
<view :style="{ height: iSMT + 'px' }"></view>
<view style="height:1.5vh;"></view>
@ -8,15 +8,15 @@
<view class="setting-item">
<text class="item-label">头像</text>
<view class="item-right">
<image src="/static/avatar.png" class="avatar" mode="aspectFill"></image>
<uni-icons type="arrowright" size="16"></uni-icons>
<image class="avatar" :src="avatarUrl || 'https://d31zlh4on95l9h.cloudfront.net/default/1961d9d2e31e78e47e885231e7514a35.png'" mode="aspectFill" />
<!-- <uni-icons type="arrowright" size="16"></uni-icons> -->
</view>
</view>
<view class="setting-item">
<text class="item-label">昵称</text>
<view class="item-right">
<text class="item-text">{{userInfoRes.dcname}}</text>
<uni-icons type="arrowright" size="16"></uni-icons>
<text class="item-text">{{ userInfoRes.dcname }}</text>
<!-- <uni-icons type="arrowright" size="16"></uni-icons> -->
</view>
</view>
<view class="setting-item">
@ -33,7 +33,7 @@
</view>
<view class="setting-item">
<text class="item-label">注销账号</text>
<uni-icons type="arrowright" size="16"></uni-icons>
<!-- <uni-icons type="arrowright" size="16"></uni-icons> -->
</view>
<view class="setting-item" @click="goToBind">
<text class="item-label">绑定账号</text>
@ -77,13 +77,15 @@
// const dccode = ref('')
const userInfoRes = ref({})
const showLogout = ref(false)
const avatarUrl = ref('')
const userInfoPromise = getUserInfo()
userInfoPromise.then(res => {
if (res.code === 200) {
userInfoRes.value.dccode = res.data.dccode;
userInfoRes.value.dcname = res.data.dcname;
userInfoRes.value.hasPwd = res.data.hasPassword;
userInfoRes.value.dccode = res.data.dccode
userInfoRes.value.dcname = res.data.dcname
userInfoRes.value.hasPwd = res.data.hasPassword
avatarUrl.value = res.data.avatar
console.log('用户信息', userInfoRes.value)
} else {
uni.showToast({

4
pages/setting/bind.vue

@ -5,7 +5,7 @@
<view style="height:1.5vh;" />
<view class="top">
<view class="top-list" @click="goToBindPhone">
<!-- <view class="top-list" @click="goToBindPhone">
<text class="label">手机号</text>
<view class="right">
<text style="font-size: 28rpx;">
@ -13,7 +13,7 @@
</text>
<uni-icons type="arrowright" size="16" />
</view>
</view>
</view> -->
<view class="top-list" @click="goToBindEmail">
<text class="label">邮箱</text>

10
pages/setting/createPwd.vue

@ -4,7 +4,7 @@
<view class="tab">
<view class="tab-item" :class="{active: activeTab === 'email'}" @click="activeTab = 'email'">邮箱</view>
<view class="tab-item" :class="{active: activeTab === 'phone'}" @click="activeTab = 'phone'">手机号</view>
<!-- <view class="tab-item" :class="{active: activeTab === 'phone'}" @click="activeTab = 'phone'">手机号</view> -->
</view>
<view class="switch-tab">
@ -83,8 +83,7 @@
}
const goToPwdNext = async () => {
console.log('发请求之前的activeTab', activeTab.value)
const goToPwdNext = async () => {
if (activeTab.value === 'email') {
if (!userEmail.value) {
uni.showToast({
@ -93,7 +92,7 @@
})
return
}
} else {
}else{
if (!userPhone.value) {
uni.showToast({
title: '请输入手机号',
@ -110,7 +109,6 @@
return
}
try {
let param;
if (activeTab.value === 'email') {
@ -140,7 +138,7 @@
})
} else {
uni.showToast({
title: res.msg || '验证失败',
title: res.message || '验证失败',
icon: 'none'
})
}

20
pages/setting/email.vue

@ -49,11 +49,9 @@
} from "@/api/member"
import {
sendEmail,
changeBind
changeBind,
validateCode
} from "@/api/setting/password"
import {
verifyCodeApi
} from "@/api/start/login"
const iSMT = ref(0)
const email = ref('')
@ -75,7 +73,7 @@
}
})
const changeAccount = () => {
const changeAccount = async () => {
if (!userEmail) {
uni.showToast({
title: '请输入邮箱',
@ -92,17 +90,18 @@
return
}
const res1 = verifyCodeApi({
const res1 = await validateCode({
loginType: 'EMAIL',
account: userEmail.value,
account: email.value,
verifyCode: verifyCode.value
})
console.log('校验码的结果',res1.code)
if (res1.code === 200) {
const res2 = changeBind({
const res2 = await changeBind({
verificateType: 0,
account: userEmail.value
})
console.log('换绑的结果',res2.code)
if (res2.code === 200) {
uni.showToast({
title: '绑定成功',
@ -111,6 +110,7 @@
uni.navigateTo({
url: '/pages/home/member'
})
} else {
uni.showToast({
title: '用户绑定失败',
@ -142,7 +142,7 @@
}
}, 1000)
sendEmail({
email: userEmail.value
email: email.value
})
}

10
pages/setting/general.vue

@ -2,11 +2,11 @@
<view class="main">
<view :style="{height:iSMT+'px'}"></view>
<view class="top">
<view class="top-list">
<!-- <view class="top-list">
<text class="label">语言</text>
<text class="language">{{settingRes.language}}</text>
<uni-icons type="arrowright" size="16" />
</view>
</view> -->
<view class="top-list" @click="goToFont">
<text class="label">字体大小</text>
<uni-icons type="arrowright" size="16" />
@ -18,12 +18,12 @@
</view>
<view class="center">
<!-- <view class="center">
<view class="center-list" @click="goToMessage">
<text class="label">消息推送</text>
<uni-icons type="arrowright" size="16" />
</view>
</view>
</view> -->
<view class="bottom">
<view class="bottom-list" @click="goToServer">
@ -107,7 +107,7 @@ onMounted(() => {
<style>
.top {
margin-top: 1.5vh;
height: 21vh;
height: 14vh;
background-color: white;
}

32
pages/setting/market.vue

@ -47,12 +47,12 @@
</view>
<view
class="option-btn"
:class="{ 'active': kStyle === 'Outline' }"
@click="handleKStyleChange('Outline')"
:class="{ 'active': kStyle === 'outline' }"
@click="handleKStyleChange('outline')"
>
<image src="../../static/my/outline.png" class="kline-icon" />
<text>轮廓图</text>
<view class="active-dot" v-if="kStyle === 'Outline'"></view>
<view class="active-dot" v-if="kStyle === 'outline'"></view>
</view>
<view
class="option-btn"
@ -85,11 +85,11 @@
</view>
<view
class="option-btn"
:class="{ 'active': exRights === 'Weighted' }"
@click="handleExRightsChange('Weighted')"
:class="{ 'active': exRights === 'weighted' }"
@click="handleExRightsChange('weighted')"
>
<text>加权</text>
<view class="active-dot" v-if="exRights === 'Weighted'"></view>
<view class="active-dot" v-if="exRights === 'weighted'"></view>
</view>
</view>
@ -97,14 +97,14 @@
<view class="top-options">
<view
class="option-btn"
:class="{ 'active': rfColor === 'green' }"
@click="handleRfColorChange('green')"
:class="{ 'active': rfColor === 'greenUp' }"
@click="handleRfColorChange('greenUp')"
>
<view class="color-icon">
<image src="../../static/my/greenRise.png" class="kline-icon" />
</view>
<text>绿涨红跌</text>
<view class="active-dot" v-if="rfColor === 'green'"></view>
<view class="active-dot" v-if="rfColor === 'greenUp'"></view>
</view>
<view
class="option-btn"
@ -115,7 +115,7 @@
<image src="../../static/my/redRise.png" class="kline-icon" />
</view>
<text>红涨绿跌</text>
<view class="active-dot" v-if="rfColor === 'red'"></view>
<view class="active-dot" v-if="rfColor === 'redUp'"></view>
</view>
</view>
@ -145,7 +145,7 @@
</view>
</view>
<view class="indicator-title">
<!-- <view class="indicator-title">
<text>指标设置</text>
</view>
<view class="indicator-list">
@ -157,7 +157,7 @@
</view>
</view>
<view style="height:10vh;background-color: white;"></view>
</view>
</view> -->
</view>
</template>
@ -167,9 +167,9 @@
const iSMT = ref(0)
const aStockBid = ref('auto') // Aauto/open/close
const kStyle = ref('common') // K线common/Outline/polylines
const exRights = ref('exRights') // exRights/normal/Weighted
const rfColor = ref('green') // green/red
const kStyle = ref('common') // K线common/outline/polylines
const exRights = ref('exRights') // exRights/normal/weighted
const rfColor = ref('greenUp') // greenUp/redUp
const indexCount = ref(1) // 1/2/3
const indicatorList = ref(['K线', '均线', '成交量', 'KDJ', 'MACD', 'RSI'])
@ -180,7 +180,7 @@
aStockBid.value = res.data.auctionDisplay ?? 'auto'
kStyle.value = res.data.klineStyle ?? 'common'
exRights.value = res.data.rightsIssueType ?? 'exRights'
rfColor.value = res.data.priceColorScheme ?? 'green'
rfColor.value = res.data.priceColorScheme ?? 'greenUp'
indexCount.value = res.data.subChartCount ?? 1
}
} catch (err) {

2
pages/setting/password.vue

@ -4,7 +4,7 @@
<view class="tab">
<view class="tab-item" :class="{active: activeTab === 'email'}" @click="activeTab = 'email'">邮箱</view>
<view class="tab-item" :class="{active: activeTab === 'phone'}" @click="activeTab = 'phone'">手机号</view>
<!-- <view class="tab-item" :class="{active: activeTab === 'phone'}" @click="activeTab = 'phone'">手机号</view> -->
</view>
<view class="switch-tab">

BIN
static/loading-gif.gif

After

Width: 120  |  Height: 120  |  Size: 134 KiB

BIN
static/marketSituation-image/downTimeChart.png

After

Width: 160  |  Height: 62  |  Size: 8.4 KiB

BIN
static/marketSituation-image/upTimeChart.png

After

Width: 160  |  Height: 62  |  Size: 9.1 KiB

BIN
static/my/Check-in.png

Before

Width: 22  |  Height: 22  |  Size: 614 B

After

Width: 44  |  Height: 44  |  Size: 1.5 KiB

BIN
static/my/MarketSettings.png

Before

Width: 33  |  Height: 33  |  Size: 687 B

After

Width: 66  |  Height: 66  |  Size: 1.3 KiB

BIN
static/my/Settings.png

Before

Width: 27  |  Height: 27  |  Size: 1.2 KiB

After

Width: 54  |  Height: 54  |  Size: 3.3 KiB

BIN
static/my/aboutDC.png

Before

Width: 167  |  Height: 155  |  Size: 8.7 KiB

After

Width: 334  |  Height: 310  |  Size: 21 KiB

BIN
static/my/bell.png

Before

Width: 13  |  Height: 16  |  Size: 358 B

After

Width: 26  |  Height: 32  |  Size: 902 B

BIN
static/my/common.png

Before

Width: 31  |  Height: 23  |  Size: 237 B

After

Width: 62  |  Height: 46  |  Size: 355 B

BIN
static/my/default-avatar.png

After

Width: 216  |  Height: 216  |  Size: 58 KiB

BIN
static/my/editName.png

Before

Width: 15  |  Height: 15  |  Size: 424 B

After

Width: 30  |  Height: 30  |  Size: 1.1 KiB

BIN
static/my/outline.png

Before

Width: 31  |  Height: 25  |  Size: 1.3 KiB

After

Width: 62  |  Height: 49  |  Size: 4.3 KiB

BIN
static/my/polylines.png

Before

Width: 31  |  Height: 23  |  Size: 1.3 KiB

After

Width: 62  |  Height: 46  |  Size: 3.6 KiB

BIN
static/my/security.png

Before

Width: 23  |  Height: 23  |  Size: 669 B

After

Width: 46  |  Height: 46  |  Size: 1.6 KiB

BIN
static/my/share.png

Before

Width: 360  |  Height: 80  |  Size: 28 KiB

After

Width: 720  |  Height: 160  |  Size: 93 KiB

2
stores/modules/marketSituation.js

@ -9,11 +9,13 @@ export const useMarketSituationStore = defineStore(
const cardData = ref([]);
const gloablCardData = ref([]);
const marketDetailCardData = ref([]);
const countryMarketCardData = ref([]);
// 记得 return
return {
cardData,
gloablCardData,
marketDetailCardData,
countryMarketCardData,
};
},
// TODO: 持久化

26
utils/http.js

@ -49,7 +49,7 @@ const httpInterceptor = {
if (token) {
options.header.token = token
}
console.log("最终请求参数:",options)
console.log("最终请求参数:", options)
// 避免误用 Authorization 头,后端要求的是 token 头
// if (options.header.Authorization) delete options.header.Authorization
return options
@ -75,10 +75,24 @@ export const http = (options) => {
loginStore.setLoginInfo("false")
console.log("2loginStore.loginInfo", loginStore.loginInfo);
// uni.showToast({
// title: '请先登录',
// icon: 'none'
// })
}
if (result.data.code === 400) {
const userStore = useUserStore()
uni.showModal({
title: "登录失效",
content: "点击确定跳转登录页面",
confirmText: "确定",
cancelText: "取消",
success: (res) => {
if (res.confirm) {
userStore.clearUserInfo()
uni.navigateTo({
url: '/pages/start/login/login'
})
}
},
});
}
resolve(result.data)
} else if (result.statusCode === 401) {
@ -87,7 +101,7 @@ export const http = (options) => {
memberStore.clearUserInfo()
// 提示用户重新登录
uni.navigateTo({
url: '/pages/login/login'
url: '/pages/start/login/login'
})
reject(result)
} else {

Loading…
Cancel
Save