Browse Source
Merge branch 'milestone-20251031-简版功能开发' of http://39.101.133.168:8807/qimaohong/deepChartVueApp into wangyi/feature-20251026183100-deepmate王毅
lihuilin/feature-20251024095243-我的
Merge branch 'milestone-20251031-简版功能开发' of http://39.101.133.168:8807/qimaohong/deepChartVueApp into wangyi/feature-20251026183100-deepmate王毅
lihuilin/feature-20251024095243-我的
28 changed files with 4883 additions and 2042 deletions
-
4.hbuilderx/launch.json
-
21api/tcpConnection.js
-
5common/util.js
-
50components/MarketOverview.vue
-
2components/footerBar.vue
-
25manifest.json
-
42pages.json
-
23pages/blank/blank.vue
-
100pages/blank/institutionalTrendsBriefing.vue
-
100pages/blank/notice.vue
-
27pages/deepMate/deepMate.vue
-
579pages/home/globalIndex.vue
-
705pages/home/home.vue
-
488pages/home/marketDetail.vue
-
920pages/home/marketSituation.vue
-
655pages/marketSituation/chartExample.vue
-
493pages/marketSituation/countryMarket.vue
-
301pages/marketSituation/forexMetals.vue
-
574pages/marketSituation/globalIndex.vue
-
0pages/marketSituation/marketCondition.vue
-
485pages/marketSituation/marketDetail.vue
-
733pages/marketSituation/marketOverview.vue
-
593pages/marketSituation/marketSituation.vue
-
BINstatic/icons/Left_(左).png
-
BINstatic/images/缺省.png
-
BINstatic/marketSituation-image/cool.png
-
BINstatic/marketSituation-image/hot.png
-
BINstatic/marketSituation-image/warm.png
@ -1,23 +0,0 @@ |
|||||
<template> |
|
||||
<view class="blank-page"> |
|
||||
<text class="tip">当前特斯拉该如何布局?</text> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
// 空白页,无逻辑 |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
.blank-page { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
height: 100vh; |
|
||||
background-color: #ffffff; |
|
||||
} |
|
||||
.tip { |
|
||||
color: #999999; |
|
||||
font-size: 28rpx; |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,100 @@ |
|||||
|
<template> |
||||
|
|
||||
|
<view class="blank-page"> |
||||
|
<view class="header" :style="{ paddingTop: safeAreaInsets?.top + 'px' }"> |
||||
|
<!-- 返回按钮 --> |
||||
|
<view class="head-left"> |
||||
|
<image class="back-button" @click="goBack" src="/static/icons/Left_(左).png"> |
||||
|
|
||||
|
|
||||
|
<!-- <text class="tip">当前特斯拉该如何布局?</text> --> |
||||
|
</image> |
||||
|
</view> |
||||
|
<view class="header-center"> |
||||
|
<text class="title" :style="{ paddingTop: safeAreaInsets?.top + 'px' }" |
||||
|
>机构动向解析</text |
||||
|
> |
||||
|
</view> |
||||
|
</view> |
||||
|
<image class="picture" src="/static/images/缺省.png" /> |
||||
|
<text class="tip">暂无内容~</text> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
// 返回到 deepMate 页面 |
||||
|
const goBack = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/deepMate/deepMate' |
||||
|
}); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.blank-page { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
position: fixed; |
||||
|
/* 充满视口,彻底禁用页面滚动 */ |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
height: 100vh; |
||||
|
overflow: hidden; |
||||
|
/* 锁定页面滚动 */ |
||||
|
background-color: #ffffff; |
||||
|
padding: 20rpx 0rpx; |
||||
|
} |
||||
|
.header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 20rpx 30rpx; |
||||
|
background-color: #ffffff; |
||||
|
box-shadow: 0 2rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
.head-left { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.back-button { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
} |
||||
|
.header-center .title { |
||||
|
position: fixed; |
||||
|
top: 25rpx; |
||||
|
left: 50%; |
||||
|
transform: translateX(-50%); |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333333; |
||||
|
} |
||||
|
.back-button:hover { |
||||
|
background-color: #e0e0e0; |
||||
|
} |
||||
|
|
||||
|
.back-button:active { |
||||
|
transform: scale(0.95); |
||||
|
} |
||||
|
|
||||
|
.back-icon { |
||||
|
font-size: 32rpx; |
||||
|
color: #333333; |
||||
|
margin-right: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.picture { |
||||
|
display: block; |
||||
|
margin: 200rpx auto 0; /* 图片水平居中 */ |
||||
|
width: 60%; |
||||
|
height: 600rpx; |
||||
|
} |
||||
|
.tip { |
||||
|
color: #999999; |
||||
|
font-size: 28rpx; |
||||
|
text-align: center; |
||||
|
margin-top: 20rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,100 @@ |
|||||
|
<template> |
||||
|
|
||||
|
<view class="blank-page"> |
||||
|
<view class="header" :style="{ paddingTop: safeAreaInsets?.top + 'px' }"> |
||||
|
<!-- 返回按钮 --> |
||||
|
<view class="head-left"> |
||||
|
<image class="back-button" @click="goBack" src="/static/icons/Left_(左).png"> |
||||
|
|
||||
|
|
||||
|
<!-- <text class="tip">当前特斯拉该如何布局?</text> --> |
||||
|
</image> |
||||
|
</view> |
||||
|
<view class="header-center"> |
||||
|
<text class="title" :style="{ paddingTop: safeAreaInsets?.top + 'px' }" |
||||
|
>消息推送通知</text |
||||
|
> |
||||
|
</view> |
||||
|
</view> |
||||
|
<image class="picture" src="/static/images/缺省.png" /> |
||||
|
<text class="tip">暂无内容~</text> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
// 返回到 deepMate 页面 |
||||
|
const goBack = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/deepMate/deepMate' |
||||
|
}); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.blank-page { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
position: fixed; |
||||
|
/* 充满视口,彻底禁用页面滚动 */ |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
height: 100vh; |
||||
|
overflow: hidden; |
||||
|
/* 锁定页面滚动 */ |
||||
|
background-color: #ffffff; |
||||
|
padding: 20rpx 0rpx; |
||||
|
} |
||||
|
.header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 20rpx 30rpx; |
||||
|
background-color: #ffffff; |
||||
|
box-shadow: 0 2rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
.head-left { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.back-button { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
} |
||||
|
.header-center .title { |
||||
|
position: fixed; |
||||
|
top: 25rpx; |
||||
|
left: 50%; |
||||
|
transform: translateX(-50%); |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333333; |
||||
|
} |
||||
|
.back-button:hover { |
||||
|
background-color: #e0e0e0; |
||||
|
} |
||||
|
|
||||
|
.back-button:active { |
||||
|
transform: scale(0.95); |
||||
|
} |
||||
|
|
||||
|
.back-icon { |
||||
|
font-size: 32rpx; |
||||
|
color: #333333; |
||||
|
margin-right: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.picture { |
||||
|
display: block; |
||||
|
margin: 200rpx auto 0; /* 图片水平居中 */ |
||||
|
width: 60%; |
||||
|
height: 600rpx; |
||||
|
} |
||||
|
.tip { |
||||
|
color: #999999; |
||||
|
font-size: 28rpx; |
||||
|
text-align: center; |
||||
|
margin-top: 20rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -1,579 +0,0 @@ |
|||||
<!-- @format --> |
|
||||
|
|
||||
<template> |
|
||||
<view class="main"> |
|
||||
<!-- 固定头部 --> |
|
||||
<view class="header_fixed" :style="{ top: iSMT + 'px' }"> |
|
||||
<view class="header_content"> |
|
||||
<view class="header_back" @click="goBack"> |
|
||||
<image src="/static/marketSituation-image/back.png" mode=""></image> |
|
||||
</view> |
|
||||
<view class="header_input_wrapper"> |
|
||||
<image class="search_icon" src="/static/marketSituation-image/search.png" mode="" @click="onSearchClick"></image> |
|
||||
<input class="header_input" type="text" placeholder="搜索" placeholder-style="color: #A6A6A6; font-size: 22rpx;" v-model="searchValue" @input="onSearchInput" @confirm="onSearchConfirm" /> |
|
||||
</view> |
|
||||
<view class="header_icons"> |
|
||||
<view class="header_icon" @click="selected"> |
|
||||
<image src="/static/marketSituation-image/mySeclected.png" mode=""></image> |
|
||||
</view> |
|
||||
<view class="header_icon" @click="history"> |
|
||||
<image src="/static/marketSituation-image/history.png" mode=""></image> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="warn"> |
|
||||
<image src="/static/marketSituation-image/warn.png" mode="aspectFit"></image> |
|
||||
<view class="warn_text_container"> |
|
||||
<text :class="warnTextClass">{{ $t("marketSituation.warn") }}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 内容区域 --> |
|
||||
<scroll-view class="content" :style="{ top: contentTopPosition + 'px' }" scroll-y="true"> |
|
||||
<!-- 亚太-中华 --> |
|
||||
<view class="market-section"> |
|
||||
<view class="market-header"> |
|
||||
<text class="market-title">亚太-中华</text> |
|
||||
<view class="market-more" @click="viewMore('asia-china')"> |
|
||||
<text class="more-text">查看更多</text> |
|
||||
<text class="more-arrow">></text> |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="cards-grid-three"> |
|
||||
<view v-for="(item, index) in asiachinaIndexes" :key="index" class="card-item"> |
|
||||
<IndexCard :flagIcon="item.flagIcon" :stockName="item.stockName" :currentPrice="item.currentPrice" :changeAmount="item.changeAmount" :changePercent="item.changePercent" :isRising="item.isRising" @click="viewIndexDetail(item)" /> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 亚太 --> |
|
||||
<view class="market-section"> |
|
||||
<view class="market-header"> |
|
||||
<text class="market-title">亚太</text> |
|
||||
<view class="market-more" @click="viewMore('asia')"> |
|
||||
<text class="more-text">查看更多</text> |
|
||||
<text class="more-arrow">></text> |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="cards-grid-three"> |
|
||||
<view v-for="(item, index) in asiaIndexes" :key="index" class="card-item"> |
|
||||
<IndexCard :flagIcon="item.flagIcon" :stockName="item.stockName" :currentPrice="item.currentPrice" :changeAmount="item.changeAmount" :changePercent="item.changePercent" :isRising="item.isRising" @click="viewIndexDetail(item)" /> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 美洲 --> |
|
||||
<view class="market-section"> |
|
||||
<view class="market-header"> |
|
||||
<text class="market-title">美洲</text> |
|
||||
<view class="market-more" @click="viewMore('america')"> |
|
||||
<text class="more-text">查看更多</text> |
|
||||
<text class="more-arrow">></text> |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="cards-grid-three"> |
|
||||
<view v-for="(item, index) in americaIndexes" :key="index" class="card-item"> |
|
||||
<IndexCard :flagIcon="item.flagIcon" :stockName="item.stockName" :currentPrice="item.currentPrice" :changeAmount="item.changeAmount" :changePercent="item.changePercent" :isRising="item.isRising" @click="viewIndexDetail(item)" /> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 底部安全区域 --> |
|
||||
<view class="bottom-safe-area"></view> |
|
||||
</scroll-view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 底部导航栏 --> |
|
||||
<footerBar class="static-footer" :type="'marketSituation'"></footerBar> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { ref, onMounted, computed, nextTick, watch } from "vue"; |
|
||||
import footerBar from "../../components/footerBar.vue"; |
|
||||
import IndexCard from "../../components/IndexCard.vue"; |
|
||||
|
|
||||
// 响应式数据 |
|
||||
const iSMT = ref(0); // 状态栏高度 |
|
||||
const contentHeight = ref(0); |
|
||||
const headerHeight = ref(0); // 头部高度 |
|
||||
const searchValue = ref(""); // 搜索值 |
|
||||
const isWarnTextOverflow = ref(false); // warn文字是否溢出 |
|
||||
|
|
||||
// warn文字的class计算属性 |
|
||||
const warnTextClass = computed(() => { |
|
||||
return isWarnTextOverflow.value ? "warn_text scroll-active" : "warn_text"; |
|
||||
}); |
|
||||
|
|
||||
// 检测warn文字是否溢出 |
|
||||
const checkWarnTextOverflow = () => { |
|
||||
nextTick(() => { |
|
||||
setTimeout(() => { |
|
||||
const query = uni.createSelectorQuery(); |
|
||||
|
|
||||
// 同时查询容器和文字元素 |
|
||||
query.select(".warn_text_container").boundingClientRect(); |
|
||||
query.select(".warn_text").boundingClientRect(); |
|
||||
query.exec((res) => { |
|
||||
const containerRect = res[0]; |
|
||||
const textRect = res[1]; |
|
||||
|
|
||||
if (!containerRect || !textRect) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 判断文字是否超出容器(留一些余量) |
|
||||
const isOverflow = textRect.width > containerRect.width - 10; |
|
||||
|
|
||||
isWarnTextOverflow.value = isOverflow; |
|
||||
}); |
|
||||
}, 500); |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
// 亚太-中华指数数据 |
|
||||
const asiachinaIndexes = ref([ |
|
||||
{ |
|
||||
flagIcon: "/static/c1.png", |
|
||||
stockName: "上证指数", |
|
||||
stockCode: "1A0001", |
|
||||
currentPrice: "3933.96", |
|
||||
changeAmount: "+24.32", |
|
||||
changePercent: "+0.62%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "/static/c2.png", |
|
||||
stockName: "深证成指", |
|
||||
stockCode: "2A01", |
|
||||
currentPrice: "45757.90", |
|
||||
changeAmount: "-123.45", |
|
||||
changePercent: "-0.27%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "/static/c3.png", |
|
||||
stockName: "创业板指", |
|
||||
stockCode: "399006", |
|
||||
currentPrice: "6606.08", |
|
||||
changeAmount: "+89.76", |
|
||||
changePercent: "+1.38%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "/static/c4.png", |
|
||||
stockName: "沪深300", |
|
||||
stockCode: "1B0300", |
|
||||
currentPrice: "45757.90", |
|
||||
changeAmount: "-89.12", |
|
||||
changePercent: "-0.19%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "/static/c5.png", |
|
||||
stockName: "上证50", |
|
||||
stockCode: "1B0011", |
|
||||
currentPrice: "45757.90", |
|
||||
changeAmount: "+234.56", |
|
||||
changePercent: "+0.52%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "/static/c6.png", |
|
||||
stockName: "科创50", |
|
||||
stockCode: "1B0688", |
|
||||
currentPrice: "22333.96", |
|
||||
changeAmount: "+156.78", |
|
||||
changePercent: "+0.71%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
]); |
|
||||
|
|
||||
// 亚太指数数据 |
|
||||
const asiaIndexes = ref([ |
|
||||
{ |
|
||||
flagIcon: "/static/c7.png", |
|
||||
stockName: "日经225", |
|
||||
stockCode: "noCode", |
|
||||
currentPrice: "28456.78", |
|
||||
changeAmount: "+234.56", |
|
||||
changePercent: "+0.83%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "/static/c8.png", |
|
||||
stockName: "韩国KOSPI", |
|
||||
stockCode: "noCode", |
|
||||
currentPrice: "2567.89", |
|
||||
changeAmount: "-12.34", |
|
||||
changePercent: "-0.48%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "/static/c9.png", |
|
||||
stockName: "印度孟买", |
|
||||
stockCode: "noCode", |
|
||||
currentPrice: "65432.10", |
|
||||
changeAmount: "+456.78", |
|
||||
changePercent: "+0.70%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
]); |
|
||||
|
|
||||
// 美洲指数数据 |
|
||||
const americaIndexes = ref([ |
|
||||
{ |
|
||||
flagIcon: "/static/c7.png", |
|
||||
stockName: "道琼斯指数", |
|
||||
stockCode: "noCode", |
|
||||
currentPrice: "34567.89", |
|
||||
changeAmount: "+123.45", |
|
||||
changePercent: "+0.36%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "/static/c8.png", |
|
||||
stockName: "纳斯达克", |
|
||||
stockCode: "noCode", |
|
||||
currentPrice: "13456.78", |
|
||||
changeAmount: "-67.89", |
|
||||
changePercent: "-0.50%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "/static/c9.png", |
|
||||
stockName: "标普500", |
|
||||
stockCode: "noCode", |
|
||||
currentPrice: "4234.56", |
|
||||
changeAmount: "+23.45", |
|
||||
changePercent: "+0.56%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
]); |
|
||||
|
|
||||
// 计算属性:内容区域顶部位置 |
|
||||
const contentTopPosition = computed(() => { |
|
||||
const statusBarHeight = iSMT.value || 0; |
|
||||
const currentHeaderHeight = headerHeight.value > 0 ? headerHeight.value : 100; |
|
||||
return statusBarHeight + currentHeaderHeight; |
|
||||
}); |
|
||||
|
|
||||
// 方法:返回上一页 |
|
||||
const goBack = () => { |
|
||||
uni.navigateBack(); |
|
||||
}; |
|
||||
|
|
||||
// 方法:搜索输入 |
|
||||
const onSearchInput = (e) => { |
|
||||
searchValue.value = e.detail.value; |
|
||||
}; |
|
||||
|
|
||||
// 方法:清除搜索 |
|
||||
const clearSearch = () => { |
|
||||
searchValue.value = ""; |
|
||||
}; |
|
||||
|
|
||||
// 方法:查看更多 |
|
||||
const viewMore = (market) => { |
|
||||
console.log("查看更多:", market); |
|
||||
uni.navigateTo({ |
|
||||
url: `/pages/home/marketDetail?market=${market}`, |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
// 方法:查看指数详情 |
|
||||
const viewIndexDetail = (item) => { |
|
||||
console.log("查看指数详情:", item.stockName); |
|
||||
// uni.showToast({ |
|
||||
// title: `查看 ${item.stockName} 详情`, |
|
||||
// icon: 'none', |
|
||||
// duration: 2000 |
|
||||
// }) |
|
||||
// 这里可以跳转到具体的指数详情页面 |
|
||||
uni.navigateTo({ |
|
||||
url: `/pages/home/marketCondition?stockInformation=${encodeURIComponent(JSON.stringify(item))}`, |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
// 生命周期:页面挂载 |
|
||||
onMounted(() => { |
|
||||
// 获取系统信息 |
|
||||
const systemInfo = uni.getSystemInfoSync(); |
|
||||
iSMT.value = systemInfo.statusBarHeight || 0; |
|
||||
|
|
||||
console.log("全球指数页面加载完成"); |
|
||||
// 动态计算header实际高度 |
|
||||
uni |
|
||||
.createSelectorQuery() |
|
||||
.select(".header_fixed") |
|
||||
.boundingClientRect((rect) => { |
|
||||
if (rect) { |
|
||||
headerHeight.value = rect.height; |
|
||||
console.log("Header实际高度:", headerHeight.value, "px"); |
|
||||
} |
|
||||
}) |
|
||||
.exec(); |
|
||||
// 检测warn文字是否溢出 |
|
||||
checkWarnTextOverflow(); |
|
||||
}); |
|
||||
|
|
||||
// 监听headerHeight变化,重新计算contentHeight |
|
||||
watch(headerHeight, (newHeight) => { |
|
||||
if (newHeight > 0) { |
|
||||
const systemInfo = uni.getSystemInfoSync(); |
|
||||
const windowHeight = systemInfo.windowHeight; |
|
||||
const statusBarHeight = systemInfo.statusBarHeight || 0; |
|
||||
const footerHeight = 100; |
|
||||
|
|
||||
contentHeight.value = windowHeight - statusBarHeight - newHeight - footerHeight; |
|
||||
console.log("重新计算contentHeight:", contentHeight.value); |
|
||||
} |
|
||||
}); |
|
||||
</script> |
|
||||
|
|
||||
<style lang="scss" scoped> |
|
||||
.main { |
|
||||
position: relative; |
|
||||
height: 100vh; |
|
||||
overflow: hidden; |
|
||||
background-color: #f5f5f5; |
|
||||
} |
|
||||
|
|
||||
/* 状态栏占位 */ |
|
||||
.top { |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
z-index: 1001; |
|
||||
background-color: #ffffff; |
|
||||
} |
|
||||
|
|
||||
/* 固定头部样式 */ |
|
||||
.header_fixed { |
|
||||
position: fixed; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
z-index: 1000; |
|
||||
background-color: #ffffff; |
|
||||
padding: 20rpx 0 0 0; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
|
||||
} |
|
||||
|
|
||||
.header_content { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
height: 80rpx; |
|
||||
padding: 0 20rpx; |
|
||||
margin-bottom: 10rpx; |
|
||||
} |
|
||||
|
|
||||
.header_back { |
|
||||
margin-right: 20rpx; |
|
||||
width: 25rpx; |
|
||||
height: 30rpx; |
|
||||
} |
|
||||
|
|
||||
.header_back image { |
|
||||
width: 25rpx; |
|
||||
height: 30rpx; |
|
||||
} |
|
||||
|
|
||||
.header_input_wrapper { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
width: 100%; |
|
||||
margin: 0 20rpx 0 0; |
|
||||
height: 70rpx; |
|
||||
border-radius: 35rpx; |
|
||||
background-color: #ffffff; |
|
||||
border: 1rpx solid #e9ecef; |
|
||||
padding: 0 80rpx 0 30rpx; |
|
||||
font-size: 28rpx; |
|
||||
color: #5c5c5c; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
|
||||
} |
|
||||
|
|
||||
.search_icon { |
|
||||
width: 40rpx; |
|
||||
height: 40rpx; |
|
||||
opacity: 0.6; |
|
||||
} |
|
||||
|
|
||||
.header_input { |
|
||||
margin-left: 10rpx; |
|
||||
} |
|
||||
|
|
||||
.header_icons { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 15rpx; |
|
||||
} |
|
||||
|
|
||||
.header_icon { |
|
||||
width: 40rpx; |
|
||||
height: 40rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.header_icon image { |
|
||||
width: 40rpx; |
|
||||
height: 40rpx; |
|
||||
} |
|
||||
|
|
||||
.warn { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: flex-start; |
|
||||
gap: 10rpx; |
|
||||
font-size: 28rpx; |
|
||||
color: #666666; |
|
||||
padding: 20rpx; |
|
||||
max-width: 100%; |
|
||||
overflow: hidden; |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
.warn image { |
|
||||
width: 40rpx; |
|
||||
height: 40rpx; |
|
||||
flex-shrink: 0; |
|
||||
/* 防止图片被压缩 */ |
|
||||
position: relative; |
|
||||
z-index: 2; |
|
||||
/* 确保图片在最上层 */ |
|
||||
} |
|
||||
|
|
||||
.warn_text_container { |
|
||||
flex: 1; |
|
||||
overflow: hidden; |
|
||||
position: relative; |
|
||||
min-width: 0; |
|
||||
/* 允许容器收缩 */ |
|
||||
} |
|
||||
|
|
||||
.warn_text { |
|
||||
display: block; |
|
||||
white-space: nowrap; |
|
||||
will-change: transform; |
|
||||
/* 优化动画性能 */ |
|
||||
} |
|
||||
|
|
||||
/* 文字滚动动画 */ |
|
||||
@keyframes scrollText { |
|
||||
0% { |
|
||||
transform: translateX(0); |
|
||||
} |
|
||||
|
|
||||
20% { |
|
||||
transform: translateX(0); |
|
||||
} |
|
||||
|
|
||||
80% { |
|
||||
transform: translateX(-85%); |
|
||||
} |
|
||||
|
|
||||
100% { |
|
||||
transform: translateX(-85%); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 当文字超长时启用滚动动画 */ |
|
||||
.warn_text.scroll-active { |
|
||||
animation: scrollText 12s linear infinite; |
|
||||
animation-delay: 2s; |
|
||||
/* 延迟2秒开始滚动,让用户先看到开头 */ |
|
||||
} |
|
||||
|
|
||||
/* 内容区域 */ |
|
||||
.content { |
|
||||
position: fixed; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
bottom: 120rpx; |
|
||||
background-color: #f5f5f5; |
|
||||
padding: 0; |
|
||||
} |
|
||||
|
|
||||
/* 市场分组 */ |
|
||||
.market-section { |
|
||||
background-color: white; |
|
||||
border-radius: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.market-header { |
|
||||
margin: 20rpx 20rpx 0 20rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
margin-bottom: 10rpx; |
|
||||
padding-bottom: 10rpx; |
|
||||
border-bottom: 2rpx solid #f0f0f0; |
|
||||
} |
|
||||
|
|
||||
.market-title { |
|
||||
font-size: 32rpx; |
|
||||
font-weight: 600; |
|
||||
color: #333; |
|
||||
} |
|
||||
|
|
||||
.market-more { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 8rpx; |
|
||||
} |
|
||||
|
|
||||
.more-text { |
|
||||
font-size: 24rpx; |
|
||||
color: #666; |
|
||||
} |
|
||||
|
|
||||
.more-arrow { |
|
||||
font-size: 20rpx; |
|
||||
color: #666; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
|
|
||||
/* 三列卡片网格 */ |
|
||||
.cards-grid-three { |
|
||||
display: grid; |
|
||||
grid-template-columns: repeat(3, 1fr); |
|
||||
} |
|
||||
|
|
||||
.card-item { |
|
||||
background-color: white; |
|
||||
border-radius: 16rpx; |
|
||||
overflow: hidden; |
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease; |
|
||||
} |
|
||||
|
|
||||
.card-item:active { |
|
||||
transform: scale(0.98); |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12); |
|
||||
} |
|
||||
|
|
||||
/* 底部安全区域 */ |
|
||||
.bottom-safe-area { |
|
||||
height: 40rpx; |
|
||||
background-color: transparent; |
|
||||
} |
|
||||
|
|
||||
/* 底部导航栏 */ |
|
||||
.static-footer { |
|
||||
position: fixed; |
|
||||
bottom: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
z-index: 1000; |
|
||||
} |
|
||||
|
|
||||
/* 响应式设计 */ |
|
||||
@media (max-width: 400rpx) { |
|
||||
.cards-grid-three { |
|
||||
grid-template-columns: repeat(2, 1fr); |
|
||||
} |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,488 +0,0 @@ |
|||||
<!-- @format --> |
|
||||
|
|
||||
<template> |
|
||||
<view class="main"> |
|
||||
<!-- 自定义导航栏 --> |
|
||||
<view class="header_fixed" :style="{ top: iSMT + 'px' }"> |
|
||||
<view class="header-content"> |
|
||||
<view class="header-left" @click="goBack"> |
|
||||
<text class="back-text">‹</text> |
|
||||
</view> |
|
||||
<view class="header-center"> |
|
||||
<text class="header-title">{{ marketTitle }}</text> |
|
||||
</view> |
|
||||
<view class="header-right"> |
|
||||
<image src="/static/marketSituation-image/search.png" class="header-icon" mode="aspectFit"></image> |
|
||||
<text class="more-text">···</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
<!-- 表头 --> |
|
||||
<view class="table-header"> |
|
||||
<view class="header-item name-column"> |
|
||||
<text class="header-text">名称</text> |
|
||||
</view> |
|
||||
<view class="header-item price-column" @click="sortByPrice"> |
|
||||
<text class="header-text">最新</text> |
|
||||
<text class="sort-icon">{{ sortType === "price" ? (sortOrder === "asc" ? "↑" : "↓") : "↕" }}</text> |
|
||||
</view> |
|
||||
<view class="header-item change-column" @click="sortByChange"> |
|
||||
<text class="header-text">涨幅</text> |
|
||||
<text class="sort-icon">{{ sortType === "change" ? (sortOrder === "asc" ? "↑" : "↓") : "↕" }}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 内容区域 --> |
|
||||
<scroll-view class="content" :style="{ top: contentTopPosition + 'px' }" scroll-y="true"> |
|
||||
<!-- 股票列表 --> |
|
||||
<view class="stock-list"> |
|
||||
<view class="stock-row" v-for="(stock, index) in sortedStockList" :key="index" @click="viewStockDetail(stock)"> |
|
||||
<view class="stock-cell name-column"> |
|
||||
<view class="stock-name">{{ stock.stockName }}</view> |
|
||||
<view class="stock-code">{{ stock.stockCode }}</view> |
|
||||
</view> |
|
||||
<view class="stock-cell price-column"> |
|
||||
<text class="stock-price" :class="stock.isRising ? 'rising' : 'falling'"> |
|
||||
{{ typeof stock.price === "number" ? stock.price.toFixed(2) : stock.price }} |
|
||||
</text> |
|
||||
</view> |
|
||||
<view class="stock-cell change-column"> |
|
||||
<text class="stock-change" :class="stock.isRising ? 'rising' : 'falling'"> |
|
||||
{{ stock.change || stock.changePercent }} |
|
||||
</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 底部安全区域 --> |
|
||||
<!-- <view class="bottom-safe-area"></view> --> |
|
||||
</scroll-view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 底部导航栏 --> |
|
||||
<!-- <footerBar class="static-footer" :type="'marketSituation'"></footerBar> --> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { ref, computed, onMounted, watch } from "vue"; |
|
||||
import { onLoad } from "@dcloudio/uni-app"; |
|
||||
import footerBar from "@/components/footerBar.vue"; |
|
||||
|
|
||||
// 响应式数据 |
|
||||
const iSMT = ref(0); |
|
||||
const headerHeight = ref(80); |
|
||||
const marketType = ref("america"); |
|
||||
const marketTitle = ref("美洲"); |
|
||||
const sortType = ref(""); // 排序类型:'price' 或 'change' |
|
||||
const sortOrder = ref("desc"); // 排序顺序:'asc' 或 'desc' |
|
||||
|
|
||||
// 股票数据 |
|
||||
const stockList = ref([ |
|
||||
{ |
|
||||
stockName: "Telecommunication", |
|
||||
stockCode: "888607", |
|
||||
price: 1349.47, |
|
||||
change: "+7.67%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Other", |
|
||||
stockCode: "888607", |
|
||||
price: 1349.47, |
|
||||
change: "+6.67%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Consumer Discretio...", |
|
||||
stockCode: "888610", |
|
||||
price: 1349.47, |
|
||||
change: "+5.67%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Telecommunication", |
|
||||
stockCode: "888607", |
|
||||
price: 1349.47, |
|
||||
change: "+4.67%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Other", |
|
||||
stockCode: "888611", |
|
||||
price: 1359.47, |
|
||||
change: "+3.67%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Consumer Discretio...", |
|
||||
stockCode: "888610", |
|
||||
price: 1349.47, |
|
||||
change: "+2.67%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Telecommunication", |
|
||||
stockCode: "888607", |
|
||||
price: 1349.47, |
|
||||
change: "+1.67%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Other", |
|
||||
stockCode: "888611", |
|
||||
price: 1009.98, |
|
||||
change: "-1.67%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Consumer Discretio...", |
|
||||
stockCode: "888610", |
|
||||
price: 1009.98, |
|
||||
change: "-0.67%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Telecommunication", |
|
||||
stockCode: "888607", |
|
||||
price: 1009.98, |
|
||||
change: "-0.67%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Other", |
|
||||
stockCode: "888611", |
|
||||
price: 1009.98, |
|
||||
change: "-1.67%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Consumer Discretio...", |
|
||||
stockCode: "888610", |
|
||||
price: 1009.98, |
|
||||
change: "-4.67%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Consumer Discretio...", |
|
||||
stockCode: "888610", |
|
||||
price: 1009.98, |
|
||||
change: "-3.67%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
stockName: "Consumer Discretio...", |
|
||||
stockCode: "888610", |
|
||||
price: 1009.98, |
|
||||
change: "-3.67%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
]); |
|
||||
|
|
||||
// 计算属性 |
|
||||
const contentTopPosition = computed(() => { |
|
||||
return iSMT.value + headerHeight.value; |
|
||||
}); |
|
||||
|
|
||||
const sortedStockList = computed(() => { |
|
||||
console.log("计算sortedStockList,原始数据长度:", stockList.value.length); |
|
||||
let list = [...stockList.value]; |
|
||||
|
|
||||
if (sortType.value === "price") { |
|
||||
list.sort((a, b) => { |
|
||||
return sortOrder.value === "asc" ? a.price - b.price : b.price - a.price; |
|
||||
}); |
|
||||
} else if (sortType.value === "change") { |
|
||||
list.sort((a, b) => { |
|
||||
const aChange = parseFloat(a.change.replace(/[+%-]/g, "")); |
|
||||
const bChange = parseFloat(b.change.replace(/[+%-]/g, "")); |
|
||||
return sortOrder.value === "asc" ? aChange - bChange : bChange - aChange; |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
console.log("排序后数据长度:", list.length); |
|
||||
return list; |
|
||||
}); |
|
||||
|
|
||||
// 页面加载时接收参数 |
|
||||
onLoad((options) => { |
|
||||
if (options && options.market) { |
|
||||
marketType.value = options.market; |
|
||||
switch (options.market) { |
|
||||
case "america": |
|
||||
marketTitle.value = "美洲"; |
|
||||
break; |
|
||||
case "asia": |
|
||||
marketTitle.value = "亚太"; |
|
||||
break; |
|
||||
case "asia-china": |
|
||||
marketTitle.value = "亚太-中华"; |
|
||||
break; |
|
||||
default: |
|
||||
marketTitle.value = "全球指数"; |
|
||||
} |
|
||||
} |
|
||||
}); |
|
||||
|
|
||||
// 方法 |
|
||||
const goBack = () => { |
|
||||
uni.navigateBack(); |
|
||||
}; |
|
||||
|
|
||||
const sortByPrice = () => { |
|
||||
if (sortType.value === "price") { |
|
||||
sortOrder.value = sortOrder.value === "asc" ? "desc" : "asc"; |
|
||||
} else { |
|
||||
sortType.value = "price"; |
|
||||
sortOrder.value = "desc"; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const sortByChange = () => { |
|
||||
if (sortType.value === "change") { |
|
||||
sortOrder.value = sortOrder.value === "asc" ? "desc" : "asc"; |
|
||||
} else { |
|
||||
sortType.value = "change"; |
|
||||
sortOrder.value = "desc"; |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
const viewStockDetail = (stock) => { |
|
||||
console.log("查看股票详情:", stock); |
|
||||
// 这里可以跳转到股票详情页面 |
|
||||
uni.navigateTo({ |
|
||||
url: `/pages/home/marketCondition?stockInformation=${encodeURIComponent(JSON.stringify(stock))}`, |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
onMounted(() => { |
|
||||
// 获取状态栏高度 |
|
||||
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
|
||||
// 动态计算header实际高度 |
|
||||
uni |
|
||||
.createSelectorQuery() |
|
||||
.select(".header_fixed") |
|
||||
.boundingClientRect((rect) => { |
|
||||
if (rect) { |
|
||||
headerHeight.value = rect.height; |
|
||||
console.log("Header实际高度:", headerHeight.value, "px"); |
|
||||
} |
|
||||
}) |
|
||||
.exec(); |
|
||||
}); |
|
||||
|
|
||||
// 监听headerHeight变化,重新计算contentHeight |
|
||||
watch(headerHeight, (newHeight) => { |
|
||||
if (newHeight > 0) { |
|
||||
const systemInfo = uni.getSystemInfoSync(); |
|
||||
const windowHeight = systemInfo.windowHeight; |
|
||||
const statusBarHeight = systemInfo.statusBarHeight || 0; |
|
||||
const footerHeight = 100; |
|
||||
|
|
||||
contentHeight.value = windowHeight - statusBarHeight - newHeight - footerHeight; |
|
||||
console.log("重新计算contentHeight:", contentHeight.value); |
|
||||
} |
|
||||
}); |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
.main { |
|
||||
width: 100%; |
|
||||
height: 100vh; |
|
||||
background-color: #f5f5f5; |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
/* 自定义导航栏 */ |
|
||||
.header_fixed { |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
z-index: 1000; |
|
||||
background-color: #ffffff; |
|
||||
border-bottom: 1px solid #f0f0f0; |
|
||||
} |
|
||||
|
|
||||
.header-content { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
height: 44px; |
|
||||
padding: 0 15px; |
|
||||
} |
|
||||
|
|
||||
.header-left, |
|
||||
.header-right { |
|
||||
width: 60px; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.header-left { |
|
||||
justify-content: flex-start; |
|
||||
} |
|
||||
|
|
||||
.header-right { |
|
||||
justify-content: flex-end; |
|
||||
gap: 10px; |
|
||||
} |
|
||||
|
|
||||
.back-text { |
|
||||
font-size: 24px; |
|
||||
color: #333333; |
|
||||
font-weight: 500; |
|
||||
line-height: 1; |
|
||||
} |
|
||||
|
|
||||
.header-center { |
|
||||
flex: 1; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.header-title { |
|
||||
font-size: 18px; |
|
||||
font-weight: 600; |
|
||||
color: #333333; |
|
||||
} |
|
||||
|
|
||||
.header-icon { |
|
||||
width: 20px; |
|
||||
height: 20px; |
|
||||
} |
|
||||
|
|
||||
.more-text { |
|
||||
font-size: 20px; |
|
||||
color: #666666; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
|
|
||||
/* 内容区域 */ |
|
||||
.content { |
|
||||
position: fixed; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
bottom: 0; |
|
||||
background-color: #ffffff; |
|
||||
} |
|
||||
|
|
||||
/* 表头样式 */ |
|
||||
.table-header { |
|
||||
display: flex; |
|
||||
padding: 12px 15px; |
|
||||
background-color: #f8f9fa; |
|
||||
border-bottom: 1px solid #e9ecef; |
|
||||
} |
|
||||
|
|
||||
.header-item { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
cursor: pointer; |
|
||||
} |
|
||||
|
|
||||
.header-item.name-column { |
|
||||
flex: 2; |
|
||||
justify-content: flex-start; |
|
||||
} |
|
||||
|
|
||||
.header-item.price-column, |
|
||||
.header-item.change-column { |
|
||||
flex: 1; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.header-text { |
|
||||
font-size: 14px; |
|
||||
color: #666666; |
|
||||
font-weight: 500; |
|
||||
} |
|
||||
|
|
||||
.sort-icon { |
|
||||
margin-left: 4px; |
|
||||
font-size: 12px; |
|
||||
color: #999999; |
|
||||
} |
|
||||
|
|
||||
/* 股票列表 */ |
|
||||
.stock-list { |
|
||||
background-color: #ffffff; |
|
||||
} |
|
||||
|
|
||||
.stock-row { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
padding: 12px 15px; |
|
||||
border-bottom: 1px solid #f5f5f5; |
|
||||
} |
|
||||
|
|
||||
.stock-row:active { |
|
||||
background-color: #f8f8f8; |
|
||||
} |
|
||||
|
|
||||
.stock-cell { |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.stock-cell.name-column { |
|
||||
flex: 2; |
|
||||
align-items: flex-start; |
|
||||
} |
|
||||
|
|
||||
.stock-cell.price-column, |
|
||||
.stock-cell.change-column { |
|
||||
flex: 1; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.stock-name { |
|
||||
font-size: 15px; |
|
||||
color: #333333; |
|
||||
font-weight: 500; |
|
||||
line-height: 1.2; |
|
||||
margin-bottom: 2px; |
|
||||
} |
|
||||
|
|
||||
.stock-code { |
|
||||
font-size: 11px; |
|
||||
color: #999999; |
|
||||
line-height: 1.2; |
|
||||
} |
|
||||
|
|
||||
.stock-price { |
|
||||
font-size: 15px; |
|
||||
font-weight: 600; |
|
||||
line-height: 1.2; |
|
||||
} |
|
||||
|
|
||||
.stock-change { |
|
||||
font-size: 13px; |
|
||||
font-weight: 500; |
|
||||
line-height: 1.2; |
|
||||
} |
|
||||
|
|
||||
.rising { |
|
||||
color: #00c851; |
|
||||
} |
|
||||
|
|
||||
.falling { |
|
||||
color: #ff4444; |
|
||||
} |
|
||||
|
|
||||
/* 底部安全区域 */ |
|
||||
/* .bottom-safe-area { |
|
||||
height: 20px; |
|
||||
} */ |
|
||||
|
|
||||
/* 底部导航栏 */ |
|
||||
/* .static-footer { |
|
||||
position: fixed; |
|
||||
bottom: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
z-index: 1000; |
|
||||
} */ |
|
||||
</style> |
|
||||
@ -1,920 +0,0 @@ |
|||||
<!-- @format --> |
|
||||
|
|
||||
<template> |
|
||||
<view class="main"> |
|
||||
<!-- 固定头部 --> |
|
||||
<view class="header_fixed" :style="{ top: iSMT + 'px' }"> |
|
||||
<view class="header_content"> |
|
||||
<view class="header_input_wrapper"> |
|
||||
<image class="search_icon" src="/static/marketSituation-image/search.png" mode="" @click="onSearchClick"></image> |
|
||||
<input class="header_input" type="text" placeholder="搜索" placeholder-style="color: #A6A6A6; font-size: 22rpx;" v-model="searchValue" @input="onSearchInput" @confirm="onSearchConfirm" /> |
|
||||
</view> |
|
||||
<view class="header_icons"> |
|
||||
<view class="header_icon" @click="selected"> |
|
||||
<image src="/static/marketSituation-image/mySeclected.png" mode=""></image> |
|
||||
</view> |
|
||||
<view class="header_icon" @click="history"> |
|
||||
<image src="/static/marketSituation-image/history.png" mode=""></image> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="channel_li" v-if="channelData.length > 0"> |
|
||||
<scroll-view class="channel_wrap" scroll-x="true" :scroll-into-view="scrollToView" :scroll-with-animation="true" show-scrollbar="false"> |
|
||||
<view class="channel_innerWrap"> |
|
||||
<view v-for="(item, index) in channelData" :key="item.id" :id="'nav' + item.id" :class="['channel_item', index === pageIndex ? 'active' : '']" @click="navClick(index)"> |
|
||||
<text class="channel_text">{{ item.title }}</text> |
|
||||
<view v-if="index === pageIndex" class="active_indicator"></view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</scroll-view> |
|
||||
<view class="scroll_indicator" @click="channel_more"> |
|
||||
<image src="/static/marketSituation-image/menu.png" mode="aspectFit"></image> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 可滚动内容区域 --> |
|
||||
<scroll-view class="content_scroll" scroll-y="true" :style="{ top: contentTopPosition + 'px' }"> |
|
||||
<view class="content"> |
|
||||
<view class="map"> |
|
||||
<image src="/static/marketSituation-image/map.png" mode="widthFix"></image> |
|
||||
</view> |
|
||||
<view class="global_index"> |
|
||||
<view class="global_index_title"> |
|
||||
{{ $t("marketSituation.globalIndex") }} |
|
||||
</view> |
|
||||
<view class="global_index_more" @click="goToGlobalIndex"> |
|
||||
<text>{{ $t("marketSituation.globalIndexMore") }}</text> |
|
||||
<image src="/static/marketSituation-image/more.png" mode="aspectFit"></image> |
|
||||
</view> |
|
||||
</view> |
|
||||
|
|
||||
<!-- 卡片网格 --> |
|
||||
<view class="cards_grid"> |
|
||||
<view v-for="(card, index) in cardData" :key="index" class="card_item"> |
|
||||
<IndexCard :flagIcon="card.flagIcon" :stockName="card.stockName" :currentPrice="card.currentPrice" :changeAmount="card.changeAmount" :changePercent="card.changePercent" :isRising="card.isRising" @click="viewIndexDetail(card)" /> |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="warn"> |
|
||||
<image src="/static/marketSituation-image/warn.png" mode="aspectFit"></image> |
|
||||
<view class="warn_text_container"> |
|
||||
<text :class="warnTextClass">{{ $t("marketSituation.warn") }}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
<!-- 底部安全区域,防止被导航栏遮挡 --> |
|
||||
<view class="bottom_safe_area"></view> |
|
||||
</view> |
|
||||
</scroll-view> |
|
||||
</view> |
|
||||
|
|
||||
<footerBar class="static-footer" :type="type"></footerBar> |
|
||||
|
|
||||
<!-- 更多tab弹窗 --> |
|
||||
<view v-if="showCountryModal" class="modal_overlay" @click="closeModal"> |
|
||||
<view class="modal_content" @click.stop> |
|
||||
<view class="modal_header"> |
|
||||
<text class="modal_title">全部栏目</text> |
|
||||
<view class="modal_close" @click="closeModal"> |
|
||||
<text>×</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
<view class="modal_body"> |
|
||||
<view class="country_grid"> |
|
||||
<view v-for="(country, index) in countryList" :key="index" :class="['country_item', selectedCountry === country ? 'selected' : '']" @click="selectCountry(country)"> |
|
||||
<text class="country_text">{{ country }}</text> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { ref, onMounted, watch, nextTick, computed } from "vue"; |
|
||||
import util from "../../common/util.js"; |
|
||||
import footerBar from "../../components/footerBar.vue"; |
|
||||
import IndexCard from "../../components/IndexCard.vue"; |
|
||||
|
|
||||
const type = ref("marketSituation"); |
|
||||
const iSMT = ref(0); |
|
||||
const searchValue = ref(""); |
|
||||
const contentHeight = ref(0); |
|
||||
const headerHeight = ref(0); // 动态计算的header高度 |
|
||||
const isWarnTextOverflow = ref(false); // warn文字是否溢出 |
|
||||
|
|
||||
// Tab 栏相关数据 |
|
||||
const channelData = ref([ |
|
||||
{ id: 1, title: "概况" }, |
|
||||
{ id: 2, title: "新加坡" }, |
|
||||
{ id: 3, title: "马来西亚" }, |
|
||||
{ id: 4, title: "印度尼西亚" }, |
|
||||
{ id: 5, title: "美国" }, |
|
||||
{ id: 6, title: "中国香港" }, |
|
||||
{ id: 7, title: "泰国" }, |
|
||||
{ id: 8, title: "中国" }, |
|
||||
{ id: 9, title: "加拿大" }, |
|
||||
{ id: 10, title: "越南" }, |
|
||||
{ id: 11, title: "外汇" }, |
|
||||
{ id: 12, title: "贵金属" }, |
|
||||
]); |
|
||||
const pageIndex = ref(0); |
|
||||
const scrollToView = ref(""); |
|
||||
|
|
||||
// 计算属性:精准计算content区域的top值 |
|
||||
const contentTopPosition = computed(() => { |
|
||||
const statusBarHeight = iSMT.value || 0; |
|
||||
const currentHeaderHeight = headerHeight.value > 0 ? headerHeight.value : 140; |
|
||||
return statusBarHeight + currentHeaderHeight; |
|
||||
}); |
|
||||
|
|
||||
// warn文字的class计算属性 |
|
||||
const warnTextClass = computed(() => { |
|
||||
return isWarnTextOverflow.value ? "warn_text scroll-active" : "warn_text"; |
|
||||
}); |
|
||||
|
|
||||
// 弹窗相关数据 |
|
||||
const showCountryModal = ref(false); |
|
||||
const selectedCountry = ref("概况"); |
|
||||
const countryList = ref(["概况", "新加坡", "马来西亚", "印度尼西亚", "美国", "中国香港", "泰国", "中国", "加拿大", "越南", "外汇", "贵金属"]); |
|
||||
|
|
||||
// 卡片数据 |
|
||||
const cardData = ref([ |
|
||||
{ |
|
||||
flagIcon: "🇺🇸", |
|
||||
stockName: "道琼斯", |
|
||||
stockCode: "DJIA", |
|
||||
currentPrice: "45757.90", |
|
||||
changeAmount: "-125.22", |
|
||||
changePercent: "-0.27%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "🇺🇸", |
|
||||
stockName: "纳斯达克", |
|
||||
stockCode: "NDX", |
|
||||
currentPrice: "22333.96", |
|
||||
changeAmount: "+125.22", |
|
||||
changePercent: "+0.47%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "🇺🇸", |
|
||||
stockName: "标普500", |
|
||||
stockCode: "SPX", |
|
||||
currentPrice: "6606.08", |
|
||||
changeAmount: "+125.22", |
|
||||
changePercent: "+0.27%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "🇨🇳", |
|
||||
stockName: "上证指数", |
|
||||
stockCode: "1A0001", |
|
||||
currentPrice: "3333.96", |
|
||||
changeAmount: "+125.22", |
|
||||
changePercent: "+0.27%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "🇨🇳", |
|
||||
stockName: "科创50", |
|
||||
stockCode: "1B0688", |
|
||||
currentPrice: "757.90", |
|
||||
changeAmount: "-25.22", |
|
||||
changePercent: "-0.27%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "🇭🇰", |
|
||||
stockName: "恒生指数", |
|
||||
stockCode: "HSI", |
|
||||
currentPrice: "19757.90", |
|
||||
changeAmount: "-125.22", |
|
||||
changePercent: "-0.63%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "🇸🇬", |
|
||||
stockName: "道琼斯", |
|
||||
stockCode: "DJIA", |
|
||||
currentPrice: "3757.90", |
|
||||
changeAmount: "+85.22", |
|
||||
changePercent: "+2.31%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "🇲🇾", |
|
||||
stockName: "纳斯达克", |
|
||||
stockCode: "NDX", |
|
||||
currentPrice: "1657.90", |
|
||||
changeAmount: "-15.22", |
|
||||
changePercent: "-0.91%", |
|
||||
isRising: false, |
|
||||
}, |
|
||||
{ |
|
||||
flagIcon: "🇹🇭", |
|
||||
stockName: "标普500", |
|
||||
stockCode: "SPX", |
|
||||
currentPrice: "1457.90", |
|
||||
changeAmount: "+35.22", |
|
||||
changePercent: "+2.48%", |
|
||||
isRising: true, |
|
||||
}, |
|
||||
]); |
|
||||
|
|
||||
// 搜索输入事件 |
|
||||
const onSearchInput = (e) => { |
|
||||
searchValue.value = e.detail.value; |
|
||||
}; |
|
||||
|
|
||||
// 搜索确认事件 |
|
||||
const onSearchConfirm = (e) => { |
|
||||
console.log("搜索内容:", e.detail.value); |
|
||||
// 这里可以添加搜索逻辑 |
|
||||
performSearch(e.detail.value); |
|
||||
}; |
|
||||
|
|
||||
// 搜索图标点击事件 |
|
||||
const onSearchClick = () => { |
|
||||
if (searchValue.value.trim()) { |
|
||||
performSearch(searchValue.value); |
|
||||
} |
|
||||
}; |
|
||||
|
|
||||
// 执行搜索 |
|
||||
const performSearch = (keyword) => { |
|
||||
if (!keyword.trim()) { |
|
||||
uni.showToast({ |
|
||||
title: "请输入搜索内容", |
|
||||
icon: "none", |
|
||||
}); |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: `搜索: ${keyword}`, |
|
||||
icon: "none", |
|
||||
}); |
|
||||
// 这里添加实际的搜索逻辑 |
|
||||
}; |
|
||||
|
|
||||
// 我的收藏点击事件 |
|
||||
const selected = () => { |
|
||||
uni.showToast({ |
|
||||
title: "我的收藏", |
|
||||
icon: "none", |
|
||||
}); |
|
||||
// 这里可以跳转到收藏页面 |
|
||||
}; |
|
||||
|
|
||||
// 历史记录点击事件 |
|
||||
const history = () => { |
|
||||
uni.showToast({ |
|
||||
title: "历史记录", |
|
||||
icon: "none", |
|
||||
}); |
|
||||
// 这里可以跳转到历史页面 |
|
||||
}; |
|
||||
|
|
||||
// Tab 栏点击事件 |
|
||||
const navClick = (index) => { |
|
||||
pageIndex.value = index; |
|
||||
const currentItem = channelData.value[index]; |
|
||||
scrollToView.value = "nav" + currentItem.id; |
|
||||
|
|
||||
// 同步更新弹窗中的选中状态 |
|
||||
selectedCountry.value = currentItem.title; |
|
||||
|
|
||||
uni.showToast({ |
|
||||
title: `切换到: ${currentItem.title}`, |
|
||||
icon: "none", |
|
||||
}); |
|
||||
|
|
||||
// 这里可以添加切换 tab 后的数据加载逻辑 |
|
||||
console.log("当前选中的 tab:", currentItem); |
|
||||
}; |
|
||||
|
|
||||
// 更多选项点击事件 |
|
||||
const channel_more = () => { |
|
||||
showCountryModal.value = true; |
|
||||
}; |
|
||||
|
|
||||
// 选择国家 |
|
||||
const selectCountry = (country) => { |
|
||||
selectedCountry.value = country; |
|
||||
|
|
||||
// 查找对应的tab索引 |
|
||||
const targetIndex = channelData.value.findIndex((item) => item.title === country); |
|
||||
|
|
||||
if (targetIndex !== -1) { |
|
||||
// 同步更新页面tab |
|
||||
pageIndex.value = targetIndex; |
|
||||
const currentItem = channelData.value[targetIndex]; |
|
||||
scrollToView.value = "nav" + currentItem.id; |
|
||||
|
|
||||
console.log("选中了:" + country + ",同步到tab索引:" + targetIndex); |
|
||||
uni.showToast({ |
|
||||
title: "已切换到:" + country, |
|
||||
icon: "none", |
|
||||
duration: 2000, |
|
||||
}); |
|
||||
} else { |
|
||||
// 如果是"概况"或其他特殊选项,默认切换到第一个tab |
|
||||
if (country === "概况" || country === "全部") { |
|
||||
pageIndex.value = 0; |
|
||||
scrollToView.value = "nav" + channelData.value[0].id; |
|
||||
} |
|
||||
|
|
||||
console.log("选中了:" + country); |
|
||||
uni.showToast({ |
|
||||
title: "已选择:" + country, |
|
||||
icon: "none", |
|
||||
duration: 2000, |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
// 这里可以添加切换到对应国家/地区数据的逻辑 |
|
||||
// 例如:loadMarketData(country) |
|
||||
closeModal(); |
|
||||
}; |
|
||||
|
|
||||
// 关闭弹窗 |
|
||||
const closeModal = () => { |
|
||||
showCountryModal.value = false; |
|
||||
}; |
|
||||
|
|
||||
// 检测warn文字是否溢出 |
|
||||
const checkWarnTextOverflow = () => { |
|
||||
nextTick(() => { |
|
||||
setTimeout(() => { |
|
||||
const query = uni.createSelectorQuery(); |
|
||||
|
|
||||
// 同时查询容器和文字元素 |
|
||||
query.select(".warn_text_container").boundingClientRect(); |
|
||||
query.select(".warn_text").boundingClientRect(); |
|
||||
query.exec((res) => { |
|
||||
const containerRect = res[0]; |
|
||||
const textRect = res[1]; |
|
||||
|
|
||||
if (!containerRect || !textRect) { |
|
||||
return; |
|
||||
} |
|
||||
|
|
||||
// 判断文字是否超出容器(留一些余量) |
|
||||
const isOverflow = textRect.width > containerRect.width - 10; |
|
||||
|
|
||||
isWarnTextOverflow.value = isOverflow; |
|
||||
}); |
|
||||
}, 500); |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
// 跳转到全球指数页面 |
|
||||
const goToGlobalIndex = () => { |
|
||||
uni.navigateTo({ |
|
||||
url: "/pages/home/globalIndex", |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
// 方法:查看指数详情 |
|
||||
const viewIndexDetail = (item) => { |
|
||||
console.log("查看指数详情:", item.stockName); |
|
||||
// uni.showToast({ |
|
||||
// title: `查看 ${item.stockName} 详情`, |
|
||||
// icon: 'none', |
|
||||
// duration: 2000 |
|
||||
// }) |
|
||||
// 这里可以跳转到具体的指数详情页面 |
|
||||
uni.navigateTo({ |
|
||||
url: `/pages/home/marketCondition?stockInformation=${encodeURIComponent(JSON.stringify(item))}`, |
|
||||
}); |
|
||||
}; |
|
||||
|
|
||||
onMounted(() => { |
|
||||
// 状态栏高度 |
|
||||
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
|
||||
|
|
||||
// 初始化 tab 栏 |
|
||||
if (channelData.value.length > 0) { |
|
||||
pageIndex.value = 0; |
|
||||
scrollToView.value = "nav" + channelData.value[0].id; |
|
||||
} |
|
||||
|
|
||||
util.request( |
|
||||
"link/api/brain/privilege", |
|
||||
(res) => { |
|
||||
console.log(res); |
|
||||
}, |
|
||||
{ |
|
||||
token: "9ior41AF0xTIbIG2pRnnbZi0+fEeMx8pywnIlrmTwo5FbqJ9lWrSWOxp9MkpKiNtedtUafqvzIwpFKrwuMs", |
|
||||
}, |
|
||||
(err) => { |
|
||||
console.log(err); |
|
||||
} |
|
||||
); |
|
||||
|
|
||||
// 确保DOM渲染完成后再查询高度 |
|
||||
nextTick(() => { |
|
||||
// 动态计算header实际高度 |
|
||||
uni |
|
||||
.createSelectorQuery() |
|
||||
.select(".header_fixed") |
|
||||
.boundingClientRect((rect) => { |
|
||||
if (rect) { |
|
||||
headerHeight.value = rect.height; |
|
||||
console.log("Header实际高度:", headerHeight.value, "px"); |
|
||||
} |
|
||||
}) |
|
||||
.exec(); |
|
||||
|
|
||||
// 检测warn文字是否溢出 |
|
||||
checkWarnTextOverflow(); |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
// 监听headerHeight变化,重新计算contentHeight |
|
||||
watch(headerHeight, (newHeight) => { |
|
||||
if (newHeight > 0) { |
|
||||
const systemInfo = uni.getSystemInfoSync(); |
|
||||
const windowHeight = systemInfo.windowHeight; |
|
||||
const statusBarHeight = systemInfo.statusBarHeight || 0; |
|
||||
const footerHeight = 100; |
|
||||
|
|
||||
contentHeight.value = windowHeight - statusBarHeight - newHeight - footerHeight; |
|
||||
console.log("重新计算contentHeight:", contentHeight.value); |
|
||||
} |
|
||||
}); |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
/* 状态栏占位 */ |
|
||||
.top { |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
z-index: 1001; |
|
||||
background-color: #ffffff; |
|
||||
} |
|
||||
|
|
||||
/* 固定头部样式 */ |
|
||||
.header_fixed { |
|
||||
position: fixed; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
z-index: 1000; |
|
||||
background-color: #ffffff; |
|
||||
padding: 20rpx 0 0 0; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
|
||||
} |
|
||||
|
|
||||
/* 可滚动内容区域 */ |
|
||||
.content_scroll { |
|
||||
position: fixed; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
bottom: 100rpx; |
|
||||
/* 底部导航栏高度 */ |
|
||||
overflow-y: auto; |
|
||||
} |
|
||||
|
|
||||
.header_content { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: space-between; |
|
||||
height: 80rpx; |
|
||||
padding: 0 20rpx; |
|
||||
margin-bottom: 10rpx; |
|
||||
} |
|
||||
|
|
||||
.header_input_wrapper { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
width: 100%; |
|
||||
margin: 0 20rpx 0 0; |
|
||||
height: 70rpx; |
|
||||
border-radius: 35rpx; |
|
||||
background-color: #ffffff; |
|
||||
border: 1rpx solid #e9ecef; |
|
||||
padding: 0 80rpx 0 30rpx; |
|
||||
font-size: 28rpx; |
|
||||
color: #5c5c5c; |
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
|
||||
} |
|
||||
|
|
||||
.search_icon { |
|
||||
width: 40rpx; |
|
||||
height: 40rpx; |
|
||||
opacity: 0.6; |
|
||||
} |
|
||||
|
|
||||
.header_input { |
|
||||
margin-left: 10rpx; |
|
||||
} |
|
||||
|
|
||||
.header_icons { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
gap: 15rpx; |
|
||||
} |
|
||||
|
|
||||
.header_icon { |
|
||||
width: 40rpx; |
|
||||
height: 40rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
} |
|
||||
|
|
||||
.header_icon image { |
|
||||
width: 40rpx; |
|
||||
height: 40rpx; |
|
||||
} |
|
||||
|
|
||||
/* Tab 栏样式 */ |
|
||||
.channel_li { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
height: 80rpx; |
|
||||
background-color: #ffffff; |
|
||||
border-bottom: 1rpx solid #f0f0f0; |
|
||||
} |
|
||||
|
|
||||
.channel_wrap { |
|
||||
width: calc(100% - 60rpx); |
|
||||
height: 100%; |
|
||||
overflow: hidden; |
|
||||
flex-shrink: 0; |
|
||||
} |
|
||||
|
|
||||
.channel_innerWrap { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
height: 100%; |
|
||||
padding: 0 20rpx; |
|
||||
white-space: nowrap; |
|
||||
} |
|
||||
|
|
||||
.channel_item { |
|
||||
position: relative; |
|
||||
display: flex; |
|
||||
flex-direction: column; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
height: 60rpx; |
|
||||
padding: 0 20rpx; |
|
||||
border-radius: 30rpx; |
|
||||
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
|
||||
cursor: pointer; |
|
||||
white-space: nowrap; |
|
||||
flex-shrink: 0; |
|
||||
} |
|
||||
|
|
||||
.channel_item:active { |
|
||||
transform: scale(0.98); |
|
||||
} |
|
||||
|
|
||||
.channel_item.active { |
|
||||
color: #333; |
|
||||
font-weight: bold; |
|
||||
} |
|
||||
|
|
||||
.channel_text { |
|
||||
font-size: 28rpx; |
|
||||
font-weight: 500; |
|
||||
color: #666666; |
|
||||
transition: color 0.3s ease; |
|
||||
white-space: nowrap; |
|
||||
} |
|
||||
|
|
||||
.channel_item.active .channel_text { |
|
||||
color: #333333; |
|
||||
font-weight: 400; |
|
||||
z-index: 2; |
|
||||
} |
|
||||
|
|
||||
.active_indicator { |
|
||||
position: absolute; |
|
||||
left: 50%; |
|
||||
top: 60%; |
|
||||
transform: translateX(-45%); |
|
||||
width: calc(100% - 20rpx); |
|
||||
min-width: 40rpx; |
|
||||
max-width: 120rpx; |
|
||||
height: 8rpx; |
|
||||
background-image: url("/static/marketSituation-image/bg.png"); |
|
||||
background-size: cover; |
|
||||
background-position: center; |
|
||||
background-repeat: no-repeat; |
|
||||
animation: slideIn 0.1s ease; |
|
||||
border-radius: 8rpx; |
|
||||
z-index: 1; |
|
||||
} |
|
||||
|
|
||||
@keyframes slideIn { |
|
||||
from { |
|
||||
width: 0; |
|
||||
opacity: 0; |
|
||||
} |
|
||||
|
|
||||
to { |
|
||||
width: 40rpx; |
|
||||
opacity: 1; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.scroll_indicator { |
|
||||
border-left: 1rpx solid #b6b6b6; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
width: 60rpx; |
|
||||
height: 30rpx; |
|
||||
background-color: #ffffff; |
|
||||
flex-shrink: 0; |
|
||||
} |
|
||||
|
|
||||
.scroll_indicator image { |
|
||||
width: 20rpx; |
|
||||
height: 20rpx; |
|
||||
opacity: 0.5; |
|
||||
} |
|
||||
|
|
||||
.content { |
|
||||
margin-top: 20rpx; |
|
||||
background-color: white; |
|
||||
} |
|
||||
|
|
||||
.map { |
|
||||
width: calc(100% - 60rpx); |
|
||||
margin: 0 30rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
background-color: #f3f3f3; |
|
||||
border-radius: 30rpx; |
|
||||
border: 1rpx solid #e0e0e0; |
|
||||
padding: 30rpx 20rpx; |
|
||||
box-sizing: border-box; |
|
||||
/* 设置最小高度保护,但允许内容撑开 */ |
|
||||
min-height: 200rpx; |
|
||||
} |
|
||||
|
|
||||
.map image { |
|
||||
width: 100%; |
|
||||
height: auto; |
|
||||
max-width: 100%; |
|
||||
display: block; |
|
||||
/* widthFix模式下,高度会自动按比例调整 */ |
|
||||
/* 设置最大高度避免图片过大 */ |
|
||||
max-height: 60vh; |
|
||||
/* 添加平滑过渡效果 */ |
|
||||
transition: all 0.3s ease; |
|
||||
max-height: 60vh; |
|
||||
} |
|
||||
|
|
||||
/* 响应式优化 */ |
|
||||
@media screen and (max-width: 750rpx) { |
|
||||
.map { |
|
||||
margin: 0 20rpx; |
|
||||
width: calc(100% - 40rpx); |
|
||||
padding: 20rpx 15rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
@media screen and (max-width: 480rpx) { |
|
||||
.map { |
|
||||
margin: 0 15rpx; |
|
||||
width: calc(100% - 30rpx); |
|
||||
padding: 15rpx 10rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.static-footer { |
|
||||
position: fixed; |
|
||||
bottom: 0; |
|
||||
} |
|
||||
|
|
||||
/* 弹窗样式 */ |
|
||||
.modal_overlay { |
|
||||
position: fixed; |
|
||||
top: 0; |
|
||||
left: 0; |
|
||||
right: 0; |
|
||||
bottom: 0; |
|
||||
background-color: rgba(0, 0, 0, 0.5); |
|
||||
display: flex; |
|
||||
align-items: flex-end; |
|
||||
z-index: 1000; |
|
||||
} |
|
||||
|
|
||||
.modal_content { |
|
||||
width: 100%; |
|
||||
background-color: #fff; |
|
||||
border-radius: 20rpx 20rpx 0 0; |
|
||||
max-height: 80vh; |
|
||||
overflow: hidden; |
|
||||
} |
|
||||
|
|
||||
.modal_header { |
|
||||
position: relative; |
|
||||
display: flex; |
|
||||
justify-content: center; |
|
||||
align-items: center; |
|
||||
padding: 30rpx 40rpx; |
|
||||
border-bottom: 1rpx solid #f0f0f0; |
|
||||
} |
|
||||
|
|
||||
.modal_title { |
|
||||
font-size: 32rpx; |
|
||||
font-weight: bold; |
|
||||
color: #333333; |
|
||||
text-align: center; |
|
||||
} |
|
||||
|
|
||||
.modal_close { |
|
||||
position: absolute; |
|
||||
right: 40rpx; |
|
||||
top: 50%; |
|
||||
transform: translateY(-50%); |
|
||||
width: 60rpx; |
|
||||
height: 60rpx; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
font-size: 40rpx; |
|
||||
color: #999; |
|
||||
} |
|
||||
|
|
||||
.modal_body { |
|
||||
padding: 40rpx; |
|
||||
} |
|
||||
|
|
||||
.country_grid { |
|
||||
display: grid; |
|
||||
grid-template-columns: 1fr 1fr; |
|
||||
gap: 20rpx; |
|
||||
} |
|
||||
|
|
||||
.country_item { |
|
||||
padding: 24rpx 30rpx; |
|
||||
border-radius: 12rpx; |
|
||||
background-color: #f8f8f8; |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: center; |
|
||||
transition: all 0.3s ease; |
|
||||
} |
|
||||
|
|
||||
.country_item.selected { |
|
||||
background-color: #ff4444; |
|
||||
color: #fff; |
|
||||
} |
|
||||
|
|
||||
.country_text { |
|
||||
font-size: 28rpx; |
|
||||
color: #333; |
|
||||
} |
|
||||
|
|
||||
.country_item.selected .country_text { |
|
||||
color: #fff; |
|
||||
} |
|
||||
|
|
||||
.global_index { |
|
||||
margin: 30rpx 20rpx 0 20rpx; |
|
||||
display: flex; |
|
||||
justify-content: space-between; |
|
||||
} |
|
||||
|
|
||||
.global_index_title { |
|
||||
margin-left: 20rpx; |
|
||||
font-size: 40rpx; |
|
||||
font-weight: 100; |
|
||||
color: #333333; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.global_index_more { |
|
||||
display: flex; |
|
||||
gap: 10rpx; |
|
||||
font-size: 28rpx; |
|
||||
color: #333333; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
.global_index_more image { |
|
||||
width: 40rpx; |
|
||||
height: 40rpx; |
|
||||
align-items: center; |
|
||||
} |
|
||||
|
|
||||
/* 卡片网格样式 */ |
|
||||
.cards_grid { |
|
||||
display: grid; |
|
||||
grid-template-columns: repeat(3, 1fr); |
|
||||
margin: 0; |
|
||||
box-sizing: border-box; |
|
||||
width: 100%; |
|
||||
} |
|
||||
|
|
||||
.card_item { |
|
||||
width: 100%; |
|
||||
box-sizing: border-box; |
|
||||
min-width: 0; |
|
||||
/* 防止内容溢出 */ |
|
||||
} |
|
||||
|
|
||||
/* 响应式布局 - 小屏幕时改为两列 */ |
|
||||
@media (max-width: 600rpx) { |
|
||||
.cards_grid { |
|
||||
grid-template-columns: repeat(2, 1fr); |
|
||||
padding: 30rpx 20rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 超小屏幕时改为单列 */ |
|
||||
@media (max-width: 400rpx) { |
|
||||
.cards_grid { |
|
||||
grid-template-columns: 1fr; |
|
||||
padding: 30rpx 20rpx; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
.warn { |
|
||||
display: flex; |
|
||||
align-items: center; |
|
||||
justify-content: flex-start; |
|
||||
gap: 10rpx; |
|
||||
font-size: 28rpx; |
|
||||
color: #666666; |
|
||||
padding: 20rpx; |
|
||||
max-width: 100%; |
|
||||
overflow: hidden; |
|
||||
position: relative; |
|
||||
} |
|
||||
|
|
||||
.warn image { |
|
||||
width: 40rpx; |
|
||||
height: 40rpx; |
|
||||
flex-shrink: 0; |
|
||||
/* 防止图片被压缩 */ |
|
||||
position: relative; |
|
||||
z-index: 2; |
|
||||
/* 确保图片在最上层 */ |
|
||||
} |
|
||||
|
|
||||
.warn_text_container { |
|
||||
flex: 1; |
|
||||
overflow: hidden; |
|
||||
position: relative; |
|
||||
min-width: 0; |
|
||||
/* 允许容器收缩 */ |
|
||||
} |
|
||||
|
|
||||
.warn_text { |
|
||||
display: block; |
|
||||
white-space: nowrap; |
|
||||
will-change: transform; |
|
||||
/* 优化动画性能 */ |
|
||||
} |
|
||||
|
|
||||
/* 文字滚动动画 */ |
|
||||
@keyframes scrollText { |
|
||||
0% { |
|
||||
transform: translateX(0); |
|
||||
} |
|
||||
|
|
||||
20% { |
|
||||
transform: translateX(0); |
|
||||
} |
|
||||
|
|
||||
80% { |
|
||||
transform: translateX(-85%); |
|
||||
} |
|
||||
|
|
||||
100% { |
|
||||
transform: translateX(-85%); |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/* 当文字超长时启用滚动动画 */ |
|
||||
.warn_text.scroll-active { |
|
||||
animation: scrollText 12s linear infinite; |
|
||||
animation-delay: 2s; |
|
||||
/* 延迟2秒开始滚动,让用户先看到开头 */ |
|
||||
} |
|
||||
|
|
||||
/* 底部安全区域 */ |
|
||||
.bottom_safe_area { |
|
||||
height: 40rpx; |
|
||||
background-color: transparent; |
|
||||
} |
|
||||
|
|
||||
/* 主容器样式调整 */ |
|
||||
.main { |
|
||||
position: relative; |
|
||||
height: 100vh; |
|
||||
overflow: hidden; |
|
||||
background-color: white; |
|
||||
} |
|
||||
</style> |
|
||||
@ -0,0 +1,655 @@ |
|||||
|
<template> |
||||
|
<view style="width: 750rpx; height: 750rpx;"> |
||||
|
<l-echart ref="chartRef" @finished="initChart"></l-echart> |
||||
|
</view> |
||||
|
</template> |
||||
|
<script setup> |
||||
|
import { ref, computed, onMounted } from 'vue'; |
||||
|
|
||||
|
const chartRef = ref(null) |
||||
|
// 获取系统信息,替代 window.innerWidth |
||||
|
const systemInfo = uni.getSystemInfoSync() |
||||
|
const screenWidth = ref(systemInfo.screenWidth || 375) // 默认值 375px |
||||
|
|
||||
|
// 生成30天的交易信号数据 |
||||
|
const generateAIGoldBullData = () => { |
||||
|
const data = [] |
||||
|
|
||||
|
for (let i = 0; i < 30; i++) { |
||||
|
// 模拟交易信号 [索引, 买入信号, 卖出信号, 持有信号, 强度, 成交量] |
||||
|
const buySignal = Math.random() > 0.7 ? 1 : 0 // 30%概率买入信号 |
||||
|
const sellSignal = Math.random() > 0.8 ? 1 : 0 // 20%概率卖出信号 |
||||
|
const holdSignal = Math.random() > 0.5 ? 1 : 0 // 50%概率持有信号 |
||||
|
const strength = Math.floor(Math.random() * 3) + 1 // 信号强度1-3 |
||||
|
const volume = Math.floor(Math.random() * 2000) + 500 // 成交量500-2500 |
||||
|
|
||||
|
data.push([i, buySignal, sellSignal, holdSignal, strength, volume]) |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
// 添加缺失的变量定义 |
||||
|
var option |
||||
|
const AIGoldBull = ref({ |
||||
|
JN: generateAIGoldBullData() |
||||
|
}) |
||||
|
|
||||
|
// 模拟多语言数据 |
||||
|
const t = ref({ |
||||
|
suoxie: 'zh', |
||||
|
tianxian: '天线', |
||||
|
feixian: '飞线', |
||||
|
zhoongxian: '中线', |
||||
|
liuxian: '流线', |
||||
|
Klinetext_5: 'K线5', |
||||
|
Klinetext_6: 'K线6', |
||||
|
maipan: '买盘', |
||||
|
maipan1: '卖盘' |
||||
|
}) |
||||
|
|
||||
|
// 生成30天的模拟K线数据 [日期, 开盘, 最高, 最低, 收盘] |
||||
|
const generateKLineData = () => { |
||||
|
const data = [] |
||||
|
let basePrice = 2450 // 黄金基础价格 |
||||
|
|
||||
|
for (let i = 0; i < 30; i++) { |
||||
|
const date = new Date(2024, 0, i + 1).toISOString().split('T')[0] |
||||
|
|
||||
|
// 模拟价格波动 |
||||
|
const volatility = (Math.random() - 0.5) * 50 // ±25的波动 |
||||
|
const open = basePrice + volatility |
||||
|
|
||||
|
const highVolatility = Math.random() * 30 + 10 // 10-40的向上波动 |
||||
|
const lowVolatility = Math.random() * 30 + 10 // 10-40的向下波动 |
||||
|
|
||||
|
const high = Math.max(open, open + highVolatility) |
||||
|
const low = Math.min(open, open - lowVolatility) |
||||
|
|
||||
|
const closeVolatility = (Math.random() - 0.5) * 20 |
||||
|
const close = Math.max(low, Math.min(high, open + closeVolatility)) |
||||
|
|
||||
|
data.push([date, |
||||
|
Math.round(open * 100) / 100, |
||||
|
Math.round(high * 100) / 100, |
||||
|
Math.round(low * 100) / 100, |
||||
|
Math.round(close * 100) / 100 |
||||
|
]) |
||||
|
|
||||
|
basePrice = close // 下一天的基础价格 |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
// 生成30天的成交量和技术指标数据 [日期, 成交量1, 成交量2, 指标1, 指标2, 指标3] |
||||
|
const generateWaveVolData = () => { |
||||
|
const data = [] |
||||
|
|
||||
|
for (let i = 0; i < 30; i++) { |
||||
|
const date = new Date(2024, 0, i + 1).toISOString().split('T')[0] |
||||
|
|
||||
|
// 模拟成交量数据 |
||||
|
const vol1 = Math.floor(Math.random() * 2000) + 800 // 800-2800 |
||||
|
const vol2 = Math.floor(Math.random() * 1500) + 600 // 600-2100 |
||||
|
|
||||
|
// 模拟技术指标 |
||||
|
const indicator1 = Math.floor(Math.random() * 30) + 40 // 40-70 |
||||
|
const indicator2 = Math.floor(Math.random() * 40) + 50 // 50-90 |
||||
|
const indicator3 = Math.floor(Math.random() * 35) + 60 // 60-95 |
||||
|
|
||||
|
data.push([date, vol1, vol2, indicator1, indicator2, indicator3]) |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
// 生成30天的移动平均线数据 [MA5, MA10, MA20, MA30] |
||||
|
const generateFTLineData = () => { |
||||
|
const data = [] |
||||
|
let ma5Base = 2450 |
||||
|
let ma10Base = 2445 |
||||
|
let ma20Base = 2440 |
||||
|
let ma30Base = 2435 |
||||
|
|
||||
|
for (let i = 0; i < 30; i++) { |
||||
|
// 模拟移动平均线的平滑变化 |
||||
|
ma5Base += (Math.random() - 0.5) * 10 |
||||
|
ma10Base += (Math.random() - 0.5) * 8 |
||||
|
ma20Base += (Math.random() - 0.5) * 6 |
||||
|
ma30Base += (Math.random() - 0.5) * 4 |
||||
|
|
||||
|
data.push([ |
||||
|
Math.round(ma5Base * 100) / 100, |
||||
|
Math.round(ma10Base * 100) / 100, |
||||
|
Math.round(ma20Base * 100) / 100, |
||||
|
Math.round(ma30Base * 100) / 100 |
||||
|
]) |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
// 生成模拟数据 |
||||
|
const mockKLineData = generateKLineData() |
||||
|
const mockWaveVolData = generateWaveVolData() |
||||
|
const mockFTLineData = generateFTLineData() |
||||
|
|
||||
|
// 生成RSI指标数据 (相对强弱指数) |
||||
|
const generateRSIData = () => { |
||||
|
const data = [] |
||||
|
for (let i = 0; i < 30; i++) { |
||||
|
const rsi = Math.random() * 60 + 20 // RSI值在20-80之间 |
||||
|
data.push(Math.round(rsi * 100) / 100) |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
// 生成MACD指标数据 |
||||
|
const generateMACDData = () => { |
||||
|
const data = [] |
||||
|
for (let i = 0; i < 30; i++) { |
||||
|
const macd = (Math.random() - 0.5) * 20 // MACD值在-10到10之间 |
||||
|
const signal = (Math.random() - 0.5) * 15 // 信号线 |
||||
|
const histogram = macd - signal // 柱状图 |
||||
|
|
||||
|
data.push([ |
||||
|
Math.round(macd * 100) / 100, |
||||
|
Math.round(signal * 100) / 100, |
||||
|
Math.round(histogram * 100) / 100 |
||||
|
]) |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
// 生成布林带数据 |
||||
|
const generateBollingerData = () => { |
||||
|
const data = [] |
||||
|
let middleLine = 2450 |
||||
|
|
||||
|
for (let i = 0; i < 30; i++) { |
||||
|
middleLine += (Math.random() - 0.5) * 10 |
||||
|
const upperBand = middleLine + Math.random() * 30 + 20 // 上轨 |
||||
|
const lowerBand = middleLine - Math.random() * 30 - 20 // 下轨 |
||||
|
|
||||
|
data.push([ |
||||
|
Math.round(upperBand * 100) / 100, |
||||
|
Math.round(middleLine * 100) / 100, |
||||
|
Math.round(lowerBand * 100) / 100 |
||||
|
]) |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
// 生成成交量分析数据 |
||||
|
const generateVolumeAnalysisData = () => { |
||||
|
const data = [] |
||||
|
for (let i = 0; i < 30; i++) { |
||||
|
const buyVolume = Math.floor(Math.random() * 1500) + 500 // 买入量 |
||||
|
const sellVolume = Math.floor(Math.random() * 1500) + 500 // 卖出量 |
||||
|
const netVolume = buyVolume - sellVolume // 净买入量 |
||||
|
|
||||
|
data.push([buyVolume, sellVolume, netVolume]) |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
// 生成市场情绪数据 |
||||
|
const generateMarketSentimentData = () => { |
||||
|
const sentiments = ['极度恐慌', '恐慌', '中性', '贪婪', '极度贪婪'] |
||||
|
const data = [] |
||||
|
|
||||
|
for (let i = 0; i < 30; i++) { |
||||
|
const sentimentIndex = Math.floor(Math.random() * 100) // 0-100的情绪指数 |
||||
|
const sentimentLabel = sentiments[Math.floor(sentimentIndex / 20)] |
||||
|
|
||||
|
data.push({ |
||||
|
date: new Date(2024, 0, i + 1).toISOString().split('T')[0], |
||||
|
index: sentimentIndex, |
||||
|
label: sentimentLabel, |
||||
|
fearGreedRatio: Math.random() * 100 |
||||
|
}) |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
// 生成重要新闻事件数据 |
||||
|
const generateNewsEventsData = () => { |
||||
|
const events = [ |
||||
|
'美联储利率决议', |
||||
|
'非农就业数据发布', |
||||
|
'通胀数据公布', |
||||
|
'地缘政治紧张', |
||||
|
'央行政策变化', |
||||
|
'经济数据超预期', |
||||
|
'市场技术突破', |
||||
|
'大宗商品价格波动' |
||||
|
] |
||||
|
|
||||
|
const data = [] |
||||
|
for (let i = 0; i < 10; i++) { // 生成10个随机事件 |
||||
|
const randomDay = Math.floor(Math.random() * 30) + 1 |
||||
|
const event = events[Math.floor(Math.random() * events.length)] |
||||
|
const impact = Math.floor(Math.random() * 5) + 1 // 影响力1-5 |
||||
|
|
||||
|
data.push({ |
||||
|
date: new Date(2024, 0, randomDay).toISOString().split('T')[0], |
||||
|
event: event, |
||||
|
impact: impact, |
||||
|
type: Math.random() > 0.5 ? 'positive' : 'negative' |
||||
|
}) |
||||
|
} |
||||
|
return data.sort((a, b) => new Date(a.date) - new Date(b.date)) |
||||
|
} |
||||
|
|
||||
|
// 生成价格预测数据 |
||||
|
const generatePricePredictionData = () => { |
||||
|
const data = [] |
||||
|
let currentPrice = 2450 |
||||
|
|
||||
|
for (let i = 0; i < 7; i++) { // 未来7天预测 |
||||
|
const date = new Date(2024, 1, i + 1).toISOString().split('T')[0] // 2月份 |
||||
|
|
||||
|
// 模拟AI预测的价格区间 |
||||
|
const prediction = currentPrice + (Math.random() - 0.5) * 100 |
||||
|
const confidence = Math.random() * 40 + 60 // 60-100%的置信度 |
||||
|
const upperBound = prediction + Math.random() * 50 |
||||
|
const lowerBound = prediction - Math.random() * 50 |
||||
|
|
||||
|
data.push({ |
||||
|
date: date, |
||||
|
predicted_price: Math.round(prediction * 100) / 100, |
||||
|
confidence: Math.round(confidence), |
||||
|
upper_bound: Math.round(upperBound * 100) / 100, |
||||
|
lower_bound: Math.round(lowerBound * 100) / 100 |
||||
|
}) |
||||
|
|
||||
|
currentPrice = prediction |
||||
|
} |
||||
|
return data |
||||
|
} |
||||
|
|
||||
|
// 模拟提取的绘图数据 |
||||
|
const extractedDrawData = { |
||||
|
KLine20: mockKLineData, |
||||
|
WAVEVOL: mockWaveVolData, |
||||
|
FTLINE: mockFTLineData, |
||||
|
RSI: generateRSIData(), |
||||
|
MACD: generateMACDData(), |
||||
|
BOLLINGER: generateBollingerData(), |
||||
|
VOLUME_ANALYSIS: generateVolumeAnalysisData(), |
||||
|
MARKET_SENTIMENT: generateMarketSentimentData(), |
||||
|
NEWS_EVENTS: generateNewsEventsData(), |
||||
|
PRICE_PREDICTION: generatePricePredictionData() |
||||
|
} |
||||
|
|
||||
|
const fnShowEcharts4 = (extractedDrawData) => { |
||||
|
const splitData = (b) => { |
||||
|
const a = JSON.parse(JSON.stringify(b)) |
||||
|
let categoryData = [] |
||||
|
let values = [] |
||||
|
for (let i = 0; i < a.length; i++) { |
||||
|
categoryData.push(a[i].splice(0, 1)[0]) |
||||
|
values.push(a[i]) |
||||
|
} |
||||
|
return { |
||||
|
categoryData, |
||||
|
values |
||||
|
} |
||||
|
} |
||||
|
var bodongliang = splitData(extractedDrawData.WAVEVOL) |
||||
|
function bodongliangData(values, i) { |
||||
|
return values.map((subArray) => subArray[i]) |
||||
|
} |
||||
|
function calculateMA(index, data) { |
||||
|
let result = [] |
||||
|
if (data.FTLINE) { |
||||
|
data.FTLINE.forEach((item) => { |
||||
|
result.push(item[index]) |
||||
|
}) |
||||
|
} |
||||
|
return result |
||||
|
} |
||||
|
function vwToPx(vw) { |
||||
|
return (screenWidth.value * vw) / 100 |
||||
|
} |
||||
|
var dealData = splitData(extractedDrawData.KLine20) |
||||
|
var dealGnBullData = AIGoldBull.value.JN |
||||
|
const textEcharts = t.value |
||||
|
const firstLegend = computed(() => { |
||||
|
if (screenWidth.value < 768) { |
||||
|
if (textEcharts.suoxie === 'en' || textEcharts.suoxie === 'th') { |
||||
|
return '2%' |
||||
|
} else if (textEcharts.suoxie === 'kr') { |
||||
|
return '2%' |
||||
|
} else { |
||||
|
return '2%' |
||||
|
} |
||||
|
} else { |
||||
|
return textEcharts.suoxie === 'en' || |
||||
|
textEcharts.suoxie === 'th' || |
||||
|
textEcharts.suoxie === 'kr' |
||||
|
? '9%' |
||||
|
: '9%' |
||||
|
} |
||||
|
}) |
||||
|
const processBarData = (data) => { |
||||
|
const barData = [] |
||||
|
data.forEach((item) => { |
||||
|
let color |
||||
|
switch (item[4]) { |
||||
|
case 1: |
||||
|
color = '#13E113' |
||||
|
break |
||||
|
case 2: |
||||
|
color = '#FF0E00' |
||||
|
break |
||||
|
case 3: |
||||
|
color = '#0000FE' |
||||
|
break |
||||
|
case 4: |
||||
|
color = '#1397FF' |
||||
|
break |
||||
|
} |
||||
|
barData.push({ |
||||
|
value: item[5], |
||||
|
itemStyle: { |
||||
|
normal: { |
||||
|
color: color |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
}) |
||||
|
return { barData } |
||||
|
} |
||||
|
const { barData } = processBarData(dealGnBullData) |
||||
|
option = { |
||||
|
tooltip: { |
||||
|
trigger: 'axis', |
||||
|
axisPointer: { |
||||
|
type: 'cross' |
||||
|
}, |
||||
|
backgroundColor: 'rgba(119, 120, 125, 0.6)', |
||||
|
borderWidth: 1, |
||||
|
borderColor: '#77787D', |
||||
|
padding: 10, |
||||
|
textStyle: { |
||||
|
color: '#fff' |
||||
|
} |
||||
|
}, |
||||
|
axisPointer: { |
||||
|
link: [ |
||||
|
{ |
||||
|
xAxisIndex: 'all' |
||||
|
} |
||||
|
], |
||||
|
label: { |
||||
|
backgroundColor: '#77787D' |
||||
|
} |
||||
|
}, |
||||
|
toolbox: { |
||||
|
show: false |
||||
|
}, |
||||
|
grid: [ |
||||
|
{ |
||||
|
left: screenWidth.value > 768 ? '10%' : '12%', |
||||
|
right: screenWidth.value > 768 ? '4%' : '6%', |
||||
|
top: screenWidth.value > 768 ? '10%' : '12%', |
||||
|
height: screenWidth.value > 768 ? '35%' : '34%', |
||||
|
containLabel: false |
||||
|
}, |
||||
|
{ |
||||
|
left: screenWidth.value > 768 ? '10%' : '12%', |
||||
|
right: screenWidth.value > 768 ? '4%' : '6%', |
||||
|
top: screenWidth.value > 768 ? '48%' : '48%', |
||||
|
height: screenWidth.value > 768 ? '19%' : '21%', |
||||
|
containLabel: false |
||||
|
}, |
||||
|
{ |
||||
|
left: screenWidth.value > 768 ? '10%' : '12%', |
||||
|
right: screenWidth.value > 768 ? '4%' : '6%', |
||||
|
top: screenWidth.value > 768 ? '70%' : '71%', |
||||
|
height: screenWidth.value > 768 ? '19%' : '21%', |
||||
|
containLabel: false |
||||
|
} |
||||
|
], |
||||
|
xAxis: [ |
||||
|
{ |
||||
|
type: 'category', |
||||
|
data: dealData.categoryData, |
||||
|
boundaryGap: true, |
||||
|
axisLine: { onZero: false }, |
||||
|
splitLine: { show: false }, |
||||
|
min: 'dataMin', |
||||
|
max: 'dataMax', |
||||
|
axisPointer: { |
||||
|
z: 100, |
||||
|
label: { |
||||
|
show: false // 不显示标签 |
||||
|
} |
||||
|
}, |
||||
|
axisLine: { |
||||
|
lineStyle: { |
||||
|
color: 'black' |
||||
|
} |
||||
|
}, // |
||||
|
axisLabel: { show: false }, |
||||
|
axisTick: { show: false } |
||||
|
}, |
||||
|
{ |
||||
|
type: 'category', |
||||
|
gridIndex: 1, |
||||
|
data: dealData.categoryData, |
||||
|
boundaryGap: true, |
||||
|
axisPointer: { |
||||
|
z: 100, |
||||
|
label: { |
||||
|
show: false // 不显示标签 |
||||
|
} |
||||
|
}, |
||||
|
axisLine: { lineStyle: { color: 'black' } }, |
||||
|
axisLabel: { |
||||
|
show: false, |
||||
|
interval: 'auto' |
||||
|
}, |
||||
|
axisTick: { show: false } |
||||
|
}, |
||||
|
{ |
||||
|
type: 'category', |
||||
|
gridIndex: 2, |
||||
|
data: dealData.categoryData, |
||||
|
boundaryGap: true, |
||||
|
axisLine: { lineStyle: { color: 'black' } }, |
||||
|
axisLabel: { |
||||
|
show: true, |
||||
|
interval: 'auto', |
||||
|
fontSize: screenWidth.value > 768 ? 15 : 9 |
||||
|
}, |
||||
|
axisTick: { show: false } |
||||
|
} |
||||
|
], |
||||
|
yAxis: [ |
||||
|
{ |
||||
|
scale: true, |
||||
|
gridIndex: 0, |
||||
|
position: 'left', |
||||
|
axisLabel: { |
||||
|
inside: false, |
||||
|
align: 'right', |
||||
|
fontSize: screenWidth.value > 768 ? 15 : 9 |
||||
|
}, |
||||
|
axisLine: { |
||||
|
show: true, |
||||
|
lineStyle: { |
||||
|
fontSize: '', |
||||
|
color: 'black' |
||||
|
} |
||||
|
}, |
||||
|
axisTick: { show: false }, |
||||
|
splitLine: { show: false } |
||||
|
}, |
||||
|
{ |
||||
|
scale: true, |
||||
|
gridIndex: 1, |
||||
|
splitNumber: 4, |
||||
|
min: 0, |
||||
|
minInterval: 1, |
||||
|
axisLabel: { |
||||
|
show: true, |
||||
|
fontSize: screenWidth.value > 768 ? 15 : 9, |
||||
|
margin: 8, |
||||
|
}, |
||||
|
axisLine: { show: true, lineStyle: { color: 'black' } }, |
||||
|
axisTick: { show: false }, |
||||
|
splitLine: { show: true, lineStyle: { type: 'dashed' } }, |
||||
|
boundaryGap: ['20%', '20%'] |
||||
|
}, |
||||
|
{ |
||||
|
scale: true, |
||||
|
gridIndex: 2, |
||||
|
splitNumber: 2, |
||||
|
axisLabel: { |
||||
|
show: true, |
||||
|
fontSize: screenWidth.value > 768 ? 15 : 9 |
||||
|
}, |
||||
|
axisLine: { show: true, lineStyle: { color: 'black' } }, |
||||
|
axisTick: { show: false }, |
||||
|
splitLine: { show: false } |
||||
|
} |
||||
|
], |
||||
|
dataZoom: [ |
||||
|
{ |
||||
|
type: 'inside', |
||||
|
xAxisIndex: [0, 1, 2], |
||||
|
start: 50, |
||||
|
end: 100 |
||||
|
}, |
||||
|
{ |
||||
|
show: true, |
||||
|
xAxisIndex: [0, 1, 2], |
||||
|
type: 'slider', |
||||
|
start: 50, |
||||
|
end: 100 |
||||
|
} |
||||
|
], |
||||
|
series: [ |
||||
|
{ |
||||
|
type: 'candlestick', |
||||
|
name: '日K', |
||||
|
xAxisIndex: 0, |
||||
|
yAxisIndex: 0, |
||||
|
data: dealData.values, |
||||
|
itemStyle: { |
||||
|
normal: { |
||||
|
color0: 'red', |
||||
|
color: 'green', |
||||
|
borderColor0: 'red', |
||||
|
borderColor: 'green' |
||||
|
} |
||||
|
}, |
||||
|
gridIndex: 1 |
||||
|
}, |
||||
|
{ |
||||
|
name: '成交量', |
||||
|
type: 'bar', |
||||
|
barWidth: '70%', |
||||
|
xAxisIndex: 1, |
||||
|
yAxisIndex: 1, |
||||
|
data: barData, |
||||
|
}, |
||||
|
// { |
||||
|
// name: textEcharts.feixian, |
||||
|
// type: 'line', |
||||
|
// data: calculateMA(1, extractedDrawData), |
||||
|
// smooth: true, |
||||
|
// symbol: 'none', |
||||
|
// xAxisIndex: 2, |
||||
|
// yAxisIndex: 2, |
||||
|
// itemStyle: { |
||||
|
// normal: { |
||||
|
// color: '#00a32e', |
||||
|
// lineStyle: { |
||||
|
// color: '#00a32e', |
||||
|
// width: 2, |
||||
|
// type: 'solid' |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// }, |
||||
|
// { |
||||
|
// name: textEcharts.zhoongxian, |
||||
|
// type: 'line', |
||||
|
// data: calculateMA(2, extractedDrawData), |
||||
|
// smooth: true, |
||||
|
// symbol: 'none', |
||||
|
// xAxisIndex: 2, |
||||
|
// yAxisIndex: 2, |
||||
|
// itemStyle: { |
||||
|
// normal: { |
||||
|
// color: '#de0000', |
||||
|
// lineStyle: { |
||||
|
// color: '#de0000', |
||||
|
// width: 2, |
||||
|
// type: 'solid' |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// }, |
||||
|
// { |
||||
|
// name: textEcharts.tianxian, |
||||
|
// type: 'line', |
||||
|
// data: calculateMA(3, extractedDrawData), |
||||
|
// smooth: true, |
||||
|
// symbol: 'none', |
||||
|
// xAxisIndex: 2, |
||||
|
// yAxisIndex: 2, |
||||
|
// itemStyle: { |
||||
|
// normal: { |
||||
|
// color: '#ffb300', |
||||
|
// lineStyle: { |
||||
|
// color: '#ffb300', |
||||
|
// width: 2, |
||||
|
// type: 'solid' |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// }, |
||||
|
// { |
||||
|
// name: textEcharts.liuxian, |
||||
|
// type: 'line', |
||||
|
// data: calculateMA(4, extractedDrawData), |
||||
|
// smooth: true, |
||||
|
// symbol: 'none', |
||||
|
// xAxisIndex: 2, |
||||
|
// yAxisIndex: 2, |
||||
|
// itemStyle: { |
||||
|
// normal: { |
||||
|
// color: '#00c8ff', |
||||
|
// lineStyle: { |
||||
|
// color: '#00c8ff', |
||||
|
// width: 2, |
||||
|
// type: 'solid' |
||||
|
// } |
||||
|
// } |
||||
|
// } |
||||
|
// }, |
||||
|
] |
||||
|
} |
||||
|
initChart() |
||||
|
} |
||||
|
|
||||
|
// 组件挂载时初始化图表 |
||||
|
onMounted(() => { |
||||
|
// 调用图表初始化函数 |
||||
|
fnShowEcharts4(extractedDrawData) |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
// 初始化图表 |
||||
|
const initChart = async () => { |
||||
|
if (!chartRef.value) return |
||||
|
|
||||
|
try { |
||||
|
const chart = await chartRef.value.init(echarts) |
||||
|
chart.setOption(option) |
||||
|
} catch (error) { |
||||
|
console.error('图表初始化失败:', error) |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
@ -0,0 +1,493 @@ |
|||||
|
<template> |
||||
|
<view class="content"> |
||||
|
<!-- 市场子Tab --> |
||||
|
<view class="sub_tabs"> |
||||
|
<view v-for="(tab, i) in marketTabs" :key="tab" :class="['tab_item', i === activeTabIndex ? 'active' : '']" |
||||
|
@click="switchTab(i)"> |
||||
|
<text>{{ tab }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 大盘指数 --> |
||||
|
<view class="section"> |
||||
|
<view class="section_header"> |
||||
|
<text class="section_title">大盘指数</text> |
||||
|
<text class="section_action" @click="viewMore('indices')">查看更多 ></text> |
||||
|
</view> |
||||
|
<view class="indices_grid"> |
||||
|
<view v-for="(index, i) in countryInfo.mainIndices" :key="i" class="index_item"> |
||||
|
<IndexCard :flagIcon="countryInfo.flag" :indexName="index.name" :currentPrice="index.price" |
||||
|
:changeAmount="index.change" :changePercent="index.changePercent" :isRising="index.isRising" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 今日市场情绪温度 --> |
||||
|
<view class="sentiment"> |
||||
|
<view class="section_subtitle"> |
||||
|
<text>今日市场情绪温度</text> |
||||
|
</view> |
||||
|
<view class="meters"> |
||||
|
<view class="meter_item" v-for="(m, i) in sentimentMeters" :key="i"> |
||||
|
<image class="meter_icon" :class="m.theme" :src="selectIcons[m.theme]" mode="aspectFit"></image> |
||||
|
<view class="meter_info"> |
||||
|
<text class="meter_value" :class="m.theme">{{ m.value }}°C</text> |
||||
|
<text class="meter_label" :class="m.theme">{{ m.label }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 板块 --> |
||||
|
<view class="section"> |
||||
|
<view class="section_header"> |
||||
|
<text class="section_title">板块</text> |
||||
|
<text class="section_action" @click="viewMore('sectors')">查看更多 ></text> |
||||
|
</view> |
||||
|
<view class="sectors_grid"> |
||||
|
<view v-for="(sec, i) in sectors" :key="i" class="sector_item"> |
||||
|
<view class="sector_header"> |
||||
|
<text class="sector_name">{{ sec.name }}</text> |
||||
|
<text :class="['sector_change', sec.isRising ? 'rising' : 'falling']"> |
||||
|
{{ sec.change }} |
||||
|
</text> |
||||
|
</view> |
||||
|
<view class="sector_price">{{ sec.price }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 股票 --> |
||||
|
<view class="section"> |
||||
|
<view class="section_header"> |
||||
|
<text class="section_title">股票</text> |
||||
|
<text class="section_action" @click="viewMore('stocks')">查看更多 ></text> |
||||
|
</view> |
||||
|
<view class="table"> |
||||
|
<view class="table_header"> |
||||
|
<text class="cell name">名称</text> |
||||
|
<text class="cell price">最新</text> |
||||
|
<text class="cell change">涨幅</text> |
||||
|
</view> |
||||
|
<view class="table_row" v-for="(stk, i) in stocks" :key="i"> |
||||
|
<view class="cell name"> |
||||
|
<text class="stk_name">{{ stk.name }}</text> |
||||
|
<text class="stk_code">{{ stk.code }}</text> |
||||
|
</view> |
||||
|
<view class="cell price"> |
||||
|
<text class="stk_price">{{ stk.price }}</text> |
||||
|
</view> |
||||
|
<view class="cell change"> |
||||
|
<text :class="['stk_change', stk.isRising ? 'rising' : 'falling']"> |
||||
|
{{ stk.change }} |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部安全区域 --> |
||||
|
<view class="bottom_safe_area"></view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, computed, onMounted } from 'vue' |
||||
|
import IndexCard from '../../components/IndexCard.vue' |
||||
|
|
||||
|
// 子Tab与操作 |
||||
|
const marketTabs = ['全部', '美股', '纽交所', '纳斯达克'] |
||||
|
const activeTabIndex = ref(0) |
||||
|
const switchTab = (i) => { |
||||
|
activeTabIndex.value = i |
||||
|
} |
||||
|
|
||||
|
// 今日情绪温度示例数据 |
||||
|
const sentimentMeters = [ |
||||
|
{ value: 90, label: '道琼斯', theme: 'hot' }, |
||||
|
{ value: 60, label: '纳斯达克', theme: 'warm' }, |
||||
|
{ value: 20, label: '标普500', theme: 'cool' } |
||||
|
] |
||||
|
|
||||
|
// 图标映射 |
||||
|
const selectIcons = { |
||||
|
hot: '/static/marketSituation-image/hot.png', |
||||
|
warm: '/static/marketSituation-image/warm.png', |
||||
|
cool: '/static/marketSituation-image/cool.png' |
||||
|
} |
||||
|
|
||||
|
// Props |
||||
|
const props = defineProps({ |
||||
|
countryId: { |
||||
|
type: Number, |
||||
|
required: true |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 国家/地区信息映射 |
||||
|
const countryInfoMap = { |
||||
|
2: { // 新加坡 |
||||
|
name: '新加坡', |
||||
|
flag: '🇸🇬', |
||||
|
isMarketOpen: true, |
||||
|
mainIndices: [ |
||||
|
{ name: '海峡时报指数', price: '3,234.56', change: '+12.34', changePercent: '+0.38%', isRising: true }, |
||||
|
{ name: 'FTSE ST Mid Cap', price: '1,234.56', change: '-5.67', changePercent: '-0.46%', isRising: false } |
||||
|
], |
||||
|
hotStocks: [ |
||||
|
{ name: '星展银行', code: 'D05.SI', price: '35.20', change: '+0.15', isRising: true }, |
||||
|
{ name: '华侨银行', code: 'O39.SI', price: '13.45', change: '-0.05', isRising: false } |
||||
|
] |
||||
|
}, |
||||
|
3: { // 马来西亚 |
||||
|
name: '马来西亚', |
||||
|
flag: '🇲🇾', |
||||
|
isMarketOpen: false, |
||||
|
mainIndices: [ |
||||
|
{ name: '富时大马KLCI指数', price: '1,567.89', change: '+8.90', changePercent: '+0.57%', isRising: true } |
||||
|
], |
||||
|
hotStocks: [ |
||||
|
{ name: '马来亚银行', code: '1155.KL', price: '9.85', change: '+0.10', isRising: true }, |
||||
|
{ name: '大众银行', code: '1295.KL', price: '4.32', change: '-0.02', isRising: false } |
||||
|
] |
||||
|
}, |
||||
|
4: { // 印度尼西亚 |
||||
|
name: '印度尼西亚', |
||||
|
flag: '🇮🇩', |
||||
|
isMarketOpen: true, |
||||
|
mainIndices: [ |
||||
|
{ name: '雅加达综合指数', price: '7,234.56', change: '+45.67', changePercent: '+0.63%', isRising: true } |
||||
|
], |
||||
|
hotStocks: [] |
||||
|
}, |
||||
|
5: { // 美国 |
||||
|
name: '美国', |
||||
|
flag: '🇺🇸', |
||||
|
isMarketOpen: false, |
||||
|
mainIndices: [ |
||||
|
{ name: '道琼斯', price: '45,757.90', change: '-125.22', changePercent: '-0.27%', isRising: false }, |
||||
|
{ name: '纳斯达克', price: '22,333.96', change: '+125.22', changePercent: '+0.47%', isRising: true }, |
||||
|
{ name: '标普500', price: '6,606.08', change: '+125.22', changePercent: '+0.27%', isRising: true } |
||||
|
], |
||||
|
hotStocks: [ |
||||
|
{ name: '苹果', code: 'AAPL', price: '195.89', change: '+2.34', isRising: true }, |
||||
|
{ name: '微软', code: 'MSFT', price: '378.85', change: '-1.23', isRising: false } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 计算当前国家信息 |
||||
|
const countryInfo = computed(() => { |
||||
|
return countryInfoMap[props.countryId] || { |
||||
|
name: '未知地区', |
||||
|
flag: '🌍', |
||||
|
isMarketOpen: false, |
||||
|
mainIndices: [], |
||||
|
hotStocks: [] |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 计算当前国家的板块与股票 |
||||
|
const sectors = computed(() => { |
||||
|
return countryInfoMap[props.countryId]?.sectors || [] |
||||
|
}) |
||||
|
const stocks = computed(() => { |
||||
|
return countryInfoMap[props.countryId]?.stocks || [] |
||||
|
}) |
||||
|
|
||||
|
// 查看更多占位 |
||||
|
const viewMore = (type) => { |
||||
|
// 可根据 type 跳转到相应页面或加载更多 |
||||
|
// 例如:indices/sectors/stocks |
||||
|
// uni.navigateTo({ url: `/pages/marketSituation/${type}List` }) |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.content { |
||||
|
padding: 0 20rpx 20rpx 20rpx; |
||||
|
} |
||||
|
|
||||
|
/* 子Tab */ |
||||
|
.sub_tabs { |
||||
|
display: flex; |
||||
|
gap: 16rpx; |
||||
|
padding: 0 20rpx 20rpx 20rpx; |
||||
|
} |
||||
|
|
||||
|
.tab_item { |
||||
|
padding: 6rpx 20rpx; |
||||
|
border-radius: 5rpx; |
||||
|
background: #f5f5f5; |
||||
|
color: #666; |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.tab_item.active { |
||||
|
background: #ff4444; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.section { |
||||
|
padding: 20rpx; |
||||
|
border-radius: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.section_header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.section_title { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.section_action { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.indices_grid { |
||||
|
padding: 20rpx; |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
gap: 20rpx; |
||||
|
background-color: #F6F6F6; |
||||
|
} |
||||
|
|
||||
|
/* 情绪温度 */ |
||||
|
.sentiment { |
||||
|
background-color: #F6F6F6; |
||||
|
padding: 0 20rpx 20rpx 20rpx; |
||||
|
} |
||||
|
|
||||
|
.section_subtitle { |
||||
|
font-size: 24rpx; |
||||
|
color: #000000; |
||||
|
padding: 20rpx 0; |
||||
|
} |
||||
|
|
||||
|
.meters { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
gap: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.meter_item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
/* padding: 10rpx; */ |
||||
|
background: #ffffff; |
||||
|
border-radius: 12rpx; |
||||
|
} |
||||
|
|
||||
|
.meter_item image { |
||||
|
width: 100rpx; |
||||
|
height: 100rpx; |
||||
|
} |
||||
|
|
||||
|
.meter_info { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.meter_value { |
||||
|
font-size: 36rpx; |
||||
|
} |
||||
|
|
||||
|
.meter_value.hot { |
||||
|
color: #ff6b6b; |
||||
|
} |
||||
|
|
||||
|
.meter_value.warm { |
||||
|
color: #ffd166; |
||||
|
} |
||||
|
|
||||
|
.meter_value.cool { |
||||
|
color: #60a5fa; |
||||
|
} |
||||
|
|
||||
|
.meter_label { |
||||
|
font-size: 22rpx; |
||||
|
} |
||||
|
|
||||
|
.meter_label.hot { |
||||
|
color: #ff6b6b; |
||||
|
} |
||||
|
|
||||
|
.meter_label.warm { |
||||
|
color: #ffd166; |
||||
|
} |
||||
|
|
||||
|
.meter_label.cool { |
||||
|
color: #60a5fa; |
||||
|
} |
||||
|
|
||||
|
/* 板块 */ |
||||
|
.sectors_grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
gap: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.sector_item { |
||||
|
background: #fafafa; |
||||
|
border-radius: 12rpx; |
||||
|
padding: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.sector_header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 12rpx; |
||||
|
} |
||||
|
|
||||
|
.sector_name { |
||||
|
font-size: 24rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.sector_change { |
||||
|
font-size: 22rpx; |
||||
|
} |
||||
|
|
||||
|
.sector_change.rising { |
||||
|
color: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
.sector_change.falling { |
||||
|
color: #27ae60; |
||||
|
} |
||||
|
|
||||
|
.sector_price { |
||||
|
font-size: 24rpx; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
/* 股票表 */ |
||||
|
.table { |
||||
|
background: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.table_header, |
||||
|
.table_row { |
||||
|
display: grid; |
||||
|
grid-template-columns: 2fr 1fr 1fr; |
||||
|
padding: 18rpx 20rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.table_header { |
||||
|
background: #fafafa; |
||||
|
} |
||||
|
|
||||
|
.cell.name { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.stk_name { |
||||
|
font-size: 26rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.stk_code { |
||||
|
font-size: 22rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.stk_price { |
||||
|
font-size: 26rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.stk_change { |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.stk_change.rising { |
||||
|
color: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
.stk_change.falling { |
||||
|
color: #27ae60; |
||||
|
} |
||||
|
|
||||
|
.index_item { |
||||
|
background: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.hot_stocks { |
||||
|
margin-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.stocks_list { |
||||
|
background: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.stock_item { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 24rpx 20rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.stock_item:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.stock_info { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.stock_name { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
display: block; |
||||
|
margin-bottom: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.stock_code { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.stock_price { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.price { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
display: block; |
||||
|
margin-bottom: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.change { |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.change.rising { |
||||
|
color: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
.change.falling { |
||||
|
color: #27ae60; |
||||
|
} |
||||
|
|
||||
|
.bottom_safe_area { |
||||
|
height: 120rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,301 @@ |
|||||
|
<template> |
||||
|
<view class="content"> |
||||
|
<view class="section" v-if="type === 'forex'"> |
||||
|
<view class="section_title"> |
||||
|
<text class="title_icon">💱</text> |
||||
|
<text>外汇市场</text> |
||||
|
</view> |
||||
|
<view class="forex_grid"> |
||||
|
<view v-for="(item, index) in forexData" :key="index" class="forex_item"> |
||||
|
<view class="forex_pair"> |
||||
|
<text class="base_currency">{{ item.base }}</text> |
||||
|
<text class="separator">/</text> |
||||
|
<text class="quote_currency">{{ item.quote }}</text> |
||||
|
</view> |
||||
|
<view class="forex_price"> |
||||
|
<text class="price">{{ item.price }}</text> |
||||
|
<text :class="['change', item.isRising ? 'rising' : 'falling']"> |
||||
|
{{ item.change }} |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="section" v-if="type === 'metals'"> |
||||
|
<view class="section_title"> |
||||
|
<text class="title_icon">🥇</text> |
||||
|
<text>贵金属</text> |
||||
|
</view> |
||||
|
<view class="metals_grid"> |
||||
|
<view v-for="(item, index) in metalsData" :key="index" class="metal_item"> |
||||
|
<view class="metal_info"> |
||||
|
<text class="metal_icon">{{ item.icon }}</text> |
||||
|
<text class="metal_name">{{ item.name }}</text> |
||||
|
</view> |
||||
|
<view class="metal_price"> |
||||
|
<text class="price">{{ item.price }}</text> |
||||
|
<text class="unit">{{ item.unit }}</text> |
||||
|
<text :class="['change', item.isRising ? 'rising' : 'falling']"> |
||||
|
{{ item.change }} |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 市场动态 --> |
||||
|
<view class="market_news"> |
||||
|
<view class="section_title"> |
||||
|
<text class="title_icon">📰</text> |
||||
|
<text>市场动态</text> |
||||
|
</view> |
||||
|
<view class="news_list"> |
||||
|
<view v-for="(news, index) in newsData" :key="index" class="news_item"> |
||||
|
<view class="news_content"> |
||||
|
<text class="news_title">{{ news.title }}</text> |
||||
|
<text class="news_time">{{ news.time }}</text> |
||||
|
</view> |
||||
|
<view class="news_impact" :class="news.impact"> |
||||
|
<text>{{ news.impactText }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部安全区域 --> |
||||
|
<view class="bottom_safe_area"></view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, computed } from 'vue' |
||||
|
|
||||
|
// Props |
||||
|
const props = defineProps({ |
||||
|
countryId: { |
||||
|
type: Number, |
||||
|
required: true |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 判断类型 |
||||
|
const type = computed(() => { |
||||
|
return props.countryId === 11 ? 'forex' : 'metals' |
||||
|
}) |
||||
|
|
||||
|
// 外汇数据 |
||||
|
const forexData = ref([ |
||||
|
{ base: 'USD', quote: 'CNY', price: '7.2456', change: '+0.0123', isRising: true }, |
||||
|
{ base: 'EUR', quote: 'USD', price: '1.0876', change: '-0.0034', isRising: false }, |
||||
|
{ base: 'GBP', quote: 'USD', price: '1.2654', change: '+0.0087', isRising: true }, |
||||
|
{ base: 'USD', quote: 'JPY', price: '149.87', change: '+0.45', isRising: true }, |
||||
|
{ base: 'AUD', quote: 'USD', price: '0.6543', change: '-0.0021', isRising: false }, |
||||
|
{ base: 'USD', quote: 'SGD', price: '1.3456', change: '+0.0012', isRising: true } |
||||
|
]) |
||||
|
|
||||
|
// 贵金属数据 |
||||
|
const metalsData = ref([ |
||||
|
{ icon: '🥇', name: '黄金', price: '2,034.56', unit: 'USD/盎司', change: '+12.34', isRising: true }, |
||||
|
{ icon: '🥈', name: '白银', price: '24.87', unit: 'USD/盎司', change: '-0.23', isRising: false }, |
||||
|
{ icon: '⚪', name: '铂金', price: '987.65', unit: 'USD/盎司', change: '+5.67', isRising: true }, |
||||
|
{ icon: '⚫', name: '钯金', price: '1,234.56', unit: 'USD/盎司', change: '-8.90', isRising: false } |
||||
|
]) |
||||
|
|
||||
|
// 新闻数据 |
||||
|
const newsData = ref([ |
||||
|
{ |
||||
|
title: '美联储暗示可能暂停加息', |
||||
|
time: '2小时前', |
||||
|
impact: 'high', |
||||
|
impactText: '高影响' |
||||
|
}, |
||||
|
{ |
||||
|
title: '欧洲央行维持利率不变', |
||||
|
time: '4小时前', |
||||
|
impact: 'medium', |
||||
|
impactText: '中影响' |
||||
|
}, |
||||
|
{ |
||||
|
title: '黄金价格创新高', |
||||
|
time: '6小时前', |
||||
|
impact: 'medium', |
||||
|
impactText: '中影响' |
||||
|
}, |
||||
|
{ |
||||
|
title: '美元指数小幅下跌', |
||||
|
time: '8小时前', |
||||
|
impact: 'low', |
||||
|
impactText: '低影响' |
||||
|
} |
||||
|
]) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.content { |
||||
|
padding: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.section { |
||||
|
margin-bottom: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.section_title { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
font-size: 28rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.title_icon { |
||||
|
font-size: 32rpx; |
||||
|
margin-right: 12rpx; |
||||
|
} |
||||
|
|
||||
|
.forex_grid, .metals_grid { |
||||
|
background: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.forex_item, .metal_item { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 24rpx 20rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.forex_item:last-child, .metal_item:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.forex_pair { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.base_currency { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.separator { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
margin: 0 8rpx; |
||||
|
} |
||||
|
|
||||
|
.quote_currency { |
||||
|
font-size: 28rpx; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
.forex_price, .metal_price { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.price { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
display: block; |
||||
|
margin-bottom: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.unit { |
||||
|
font-size: 20rpx; |
||||
|
color: #999; |
||||
|
margin-left: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.change { |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.change.rising { |
||||
|
color: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
.change.falling { |
||||
|
color: #27ae60; |
||||
|
} |
||||
|
|
||||
|
.metal_info { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.metal_icon { |
||||
|
font-size: 32rpx; |
||||
|
margin-right: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.metal_name { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.market_news { |
||||
|
margin-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.news_list { |
||||
|
background: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.news_item { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 24rpx 20rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.news_item:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.news_content { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.news_title { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
display: block; |
||||
|
margin-bottom: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.news_time { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.news_impact { |
||||
|
padding: 6rpx 12rpx; |
||||
|
border-radius: 16rpx; |
||||
|
font-size: 20rpx; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.news_impact.high { |
||||
|
background: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
.news_impact.medium { |
||||
|
background: #f39c12; |
||||
|
} |
||||
|
|
||||
|
.news_impact.low { |
||||
|
background: #95a5a6; |
||||
|
} |
||||
|
|
||||
|
.bottom_safe_area { |
||||
|
height: 120rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,574 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<!-- 固定头部 --> |
||||
|
<view class="header_fixed" :style="{ top: iSMT + 'px' }"> |
||||
|
<view class="header_content"> |
||||
|
<view class="header_back" @click="goBack"> |
||||
|
<image src="/static/marketSituation-image/back.png" mode=""></image> |
||||
|
</view> |
||||
|
<view class="header_input_wrapper"> |
||||
|
<image class="search_icon" src="/static/marketSituation-image/search.png" mode="" |
||||
|
@click="onSearchClick"></image> |
||||
|
<input class="header_input" type="text" placeholder="搜索" |
||||
|
placeholder-style="color: #A6A6A6; font-size: 22rpx;" v-model="searchValue" |
||||
|
@input="onSearchInput" @confirm="onSearchConfirm" /> |
||||
|
</view> |
||||
|
<view class="header_icons"> |
||||
|
<view class="header_icon" @click="selected"> |
||||
|
<image src="/static/marketSituation-image/mySeclected.png" mode=""></image> |
||||
|
</view> |
||||
|
<view class="header_icon" @click="history"> |
||||
|
<image src="/static/marketSituation-image/history.png" mode=""></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="warn"> |
||||
|
<image src="/static/marketSituation-image/warn.png" mode="aspectFit"></image> |
||||
|
<view class="warn_text_container"> |
||||
|
<text :class="warnTextClass">{{ $t('marketSituation.warn') }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 内容区域 --> |
||||
|
<scroll-view class="content" :style="{ top: contentTopPosition + 'px' }" scroll-y="true"> |
||||
|
<!-- 亚太-中华 --> |
||||
|
<view class="market-section"> |
||||
|
<view class="market-header"> |
||||
|
<text class="market-title">亚太-中华</text> |
||||
|
<view class="market-more" @click="viewMore('asia-china')"> |
||||
|
<text class="more-text">查看更多</text> |
||||
|
<text class="more-arrow">></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="cards-grid-three"> |
||||
|
<view v-for="(item, index) in asiachinaIndexes" :key="index" class="card-item"> |
||||
|
<IndexCard :flagIcon="item.flagIcon" :indexName="item.indexName" |
||||
|
:currentPrice="item.currentPrice" :changeAmount="item.changeAmount" |
||||
|
:changePercent="item.changePercent" :isRising="item.isRising" |
||||
|
@click="viewIndexDetail(item)" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 亚太 --> |
||||
|
<view class="market-section"> |
||||
|
<view class="market-header"> |
||||
|
<text class="market-title">亚太</text> |
||||
|
<view class="market-more" @click="viewMore('asia')"> |
||||
|
<text class="more-text">查看更多</text> |
||||
|
<text class="more-arrow">></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="cards-grid-three"> |
||||
|
<view v-for="(item, index) in asiaIndexes" :key="index" class="card-item"> |
||||
|
<IndexCard :flagIcon="item.flagIcon" :indexName="item.indexName" |
||||
|
:currentPrice="item.currentPrice" :changeAmount="item.changeAmount" |
||||
|
:changePercent="item.changePercent" :isRising="item.isRising" |
||||
|
@click="viewIndexDetail(item)" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 美洲 --> |
||||
|
<view class="market-section"> |
||||
|
<view class="market-header"> |
||||
|
<text class="market-title">美洲</text> |
||||
|
<view class="market-more" @click="viewMore('america')"> |
||||
|
<text class="more-text">查看更多</text> |
||||
|
<text class="more-arrow">></text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="cards-grid-three"> |
||||
|
<view v-for="(item, index) in americaIndexes" :key="index" class="card-item"> |
||||
|
<IndexCard :flagIcon="item.flagIcon" :indexName="item.indexName" |
||||
|
:currentPrice="item.currentPrice" :changeAmount="item.changeAmount" |
||||
|
:changePercent="item.changePercent" :isRising="item.isRising" |
||||
|
@click="viewIndexDetail(item)" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部安全区域 --> |
||||
|
<view class="bottom-safe-area"></view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部导航栏 --> |
||||
|
<footerBar class="static-footer" :type="'marketSituation'"></footerBar> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted, computed, nextTick, watch } from 'vue' |
||||
|
import footerBar from '../../components/footerBar.vue' |
||||
|
import IndexCard from '../../components/IndexCard.vue' |
||||
|
|
||||
|
// 响应式数据 |
||||
|
const iSMT = ref(0) // 状态栏高度 |
||||
|
const contentHeight = ref(0) |
||||
|
const headerHeight = ref(0) // 头部高度 |
||||
|
const searchValue = ref('') // 搜索值 |
||||
|
const isWarnTextOverflow = ref(false) // warn文字是否溢出 |
||||
|
|
||||
|
// warn文字的class计算属性 |
||||
|
const warnTextClass = computed(() => { |
||||
|
return isWarnTextOverflow.value ? 'warn_text scroll-active' : 'warn_text' |
||||
|
}) |
||||
|
|
||||
|
// 检测warn文字是否溢出 |
||||
|
const checkWarnTextOverflow = () => { |
||||
|
nextTick(() => { |
||||
|
setTimeout(() => { |
||||
|
const query = uni.createSelectorQuery() |
||||
|
|
||||
|
// 同时查询容器和文字元素 |
||||
|
query.select('.warn_text_container').boundingClientRect() |
||||
|
query.select('.warn_text').boundingClientRect() |
||||
|
query.exec((res) => { |
||||
|
const containerRect = res[0] |
||||
|
const textRect = res[1] |
||||
|
|
||||
|
if (!containerRect || !textRect) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 判断文字是否超出容器(留一些余量) |
||||
|
const isOverflow = textRect.width > (containerRect.width - 10) |
||||
|
|
||||
|
isWarnTextOverflow.value = isOverflow |
||||
|
}) |
||||
|
}, 500) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 亚太-中华指数数据 |
||||
|
const asiachinaIndexes = ref([ |
||||
|
{ |
||||
|
flagIcon: '/static/c1.png', |
||||
|
indexName: '上证指数', |
||||
|
currentPrice: '3933.96', |
||||
|
changeAmount: '+24.32', |
||||
|
changePercent: '+0.62%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '/static/c2.png', |
||||
|
indexName: '深证成指', |
||||
|
currentPrice: '45757.90', |
||||
|
changeAmount: '-123.45', |
||||
|
changePercent: '-0.27%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '/static/c3.png', |
||||
|
indexName: '创业板指', |
||||
|
currentPrice: '6606.08', |
||||
|
changeAmount: '+89.76', |
||||
|
changePercent: '+1.38%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '/static/c4.png', |
||||
|
indexName: 'HSI50', |
||||
|
currentPrice: '22333.96', |
||||
|
changeAmount: '+156.78', |
||||
|
changePercent: '+0.71%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '/static/c5.png', |
||||
|
indexName: '沪深300', |
||||
|
currentPrice: '45757.90', |
||||
|
changeAmount: '-89.12', |
||||
|
changePercent: '-0.19%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '/static/c6.png', |
||||
|
indexName: '上证50', |
||||
|
currentPrice: '45757.90', |
||||
|
changeAmount: '+234.56', |
||||
|
changePercent: '+0.52%', |
||||
|
isRising: true |
||||
|
} |
||||
|
]) |
||||
|
|
||||
|
// 亚太指数数据 |
||||
|
const asiaIndexes = ref([ |
||||
|
{ |
||||
|
flagIcon: '/static/c7.png', |
||||
|
indexName: '日经225', |
||||
|
currentPrice: '28456.78', |
||||
|
changeAmount: '+234.56', |
||||
|
changePercent: '+0.83%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '/static/c8.png', |
||||
|
indexName: '韩国KOSPI', |
||||
|
currentPrice: '2567.89', |
||||
|
changeAmount: '-12.34', |
||||
|
changePercent: '-0.48%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '/static/c9.png', |
||||
|
indexName: '印度孟买', |
||||
|
currentPrice: '65432.10', |
||||
|
changeAmount: '+456.78', |
||||
|
changePercent: '+0.70%', |
||||
|
isRising: true |
||||
|
} |
||||
|
]) |
||||
|
|
||||
|
// 美洲指数数据 |
||||
|
const americaIndexes = ref([ |
||||
|
{ |
||||
|
flagIcon: '/static/c7.png', |
||||
|
indexName: '道琼斯指数', |
||||
|
currentPrice: '34567.89', |
||||
|
changeAmount: '+123.45', |
||||
|
changePercent: '+0.36%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '/static/c8.png', |
||||
|
indexName: '纳斯达克', |
||||
|
currentPrice: '13456.78', |
||||
|
changeAmount: '-67.89', |
||||
|
changePercent: '-0.50%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '/static/c9.png', |
||||
|
indexName: '标普500', |
||||
|
currentPrice: '4234.56', |
||||
|
changeAmount: '+23.45', |
||||
|
changePercent: '+0.56%', |
||||
|
isRising: true |
||||
|
} |
||||
|
]) |
||||
|
|
||||
|
// 计算属性:内容区域顶部位置 |
||||
|
const contentTopPosition = computed(() => { |
||||
|
const statusBarHeight = iSMT.value || 0 |
||||
|
const currentHeaderHeight = headerHeight.value > 0 ? headerHeight.value : 100 |
||||
|
return statusBarHeight + currentHeaderHeight |
||||
|
}) |
||||
|
|
||||
|
// 方法:返回上一页 |
||||
|
const goBack = () => { |
||||
|
uni.navigateBack() |
||||
|
} |
||||
|
|
||||
|
// 方法:搜索输入 |
||||
|
const onSearchInput = (e) => { |
||||
|
searchValue.value = e.detail.value |
||||
|
} |
||||
|
|
||||
|
// 方法:清除搜索 |
||||
|
const clearSearch = () => { |
||||
|
searchValue.value = '' |
||||
|
} |
||||
|
|
||||
|
// 方法:查看更多 |
||||
|
const viewMore = (market) => { |
||||
|
console.log('查看更多:', market) |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/marketSituation/marketDetail?market=${market}` |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 方法:查看指数详情 |
||||
|
const viewIndexDetail = (item) => { |
||||
|
console.log('查看指数详情:', item.indexName) |
||||
|
uni.showToast({ |
||||
|
title: `查看 ${item.indexName} 详情`, |
||||
|
icon: 'none', |
||||
|
duration: 2000 |
||||
|
}) |
||||
|
// 这里可以跳转到具体的指数详情页面 |
||||
|
// uni.navigateTo({ |
||||
|
// url: `/pages/detail/indexDetail?id=${item.id}` |
||||
|
// }) |
||||
|
} |
||||
|
|
||||
|
// 生命周期:页面挂载 |
||||
|
onMounted(() => { |
||||
|
// 获取系统信息 |
||||
|
const systemInfo = uni.getSystemInfoSync() |
||||
|
iSMT.value = systemInfo.statusBarHeight || 0 |
||||
|
|
||||
|
console.log('全球指数页面加载完成') |
||||
|
// 动态计算header实际高度 |
||||
|
uni.createSelectorQuery().select('.header_fixed').boundingClientRect((rect) => { |
||||
|
if (rect) { |
||||
|
headerHeight.value = rect.height |
||||
|
console.log('Header实际高度:', headerHeight.value, 'px') |
||||
|
} |
||||
|
}).exec() |
||||
|
// 检测warn文字是否溢出 |
||||
|
checkWarnTextOverflow() |
||||
|
}) |
||||
|
|
||||
|
// 监听headerHeight变化,重新计算contentHeight |
||||
|
watch(headerHeight, (newHeight) => { |
||||
|
if (newHeight > 0) { |
||||
|
const systemInfo = uni.getSystemInfoSync() |
||||
|
const windowHeight = systemInfo.windowHeight |
||||
|
const statusBarHeight = systemInfo.statusBarHeight || 0 |
||||
|
const footerHeight = 100 |
||||
|
|
||||
|
contentHeight.value = windowHeight - statusBarHeight - newHeight - footerHeight |
||||
|
console.log('重新计算contentHeight:', contentHeight.value) |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.main { |
||||
|
position: relative; |
||||
|
height: 100vh; |
||||
|
overflow: hidden; |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
|
||||
|
/* 状态栏占位 */ |
||||
|
.top { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1001; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
/* 固定头部样式 */ |
||||
|
.header_fixed { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
background-color: #ffffff; |
||||
|
padding: 20rpx 0 0 0; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.header_content { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
height: 80rpx; |
||||
|
padding: 0 20rpx; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.header_back { |
||||
|
margin-right: 20rpx; |
||||
|
width: 25rpx; |
||||
|
height: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.header_back image { |
||||
|
width: 25rpx; |
||||
|
height: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.header_input_wrapper { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
width: 100%; |
||||
|
margin: 0 20rpx 0 0; |
||||
|
height: 70rpx; |
||||
|
border-radius: 35rpx; |
||||
|
background-color: #ffffff; |
||||
|
border: 1rpx solid #e9ecef; |
||||
|
padding: 0 80rpx 0 30rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #5c5c5c; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.search_icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
opacity: 0.6; |
||||
|
} |
||||
|
|
||||
|
.header_input { |
||||
|
margin-left: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.header_icons { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 15rpx; |
||||
|
} |
||||
|
|
||||
|
.header_icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.header_icon image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.warn { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: flex-start; |
||||
|
gap: 10rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #666666; |
||||
|
padding: 20rpx; |
||||
|
max-width: 100%; |
||||
|
overflow: hidden; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.warn image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
flex-shrink: 0; |
||||
|
/* 防止图片被压缩 */ |
||||
|
position: relative; |
||||
|
z-index: 2; |
||||
|
/* 确保图片在最上层 */ |
||||
|
} |
||||
|
|
||||
|
.warn_text_container { |
||||
|
flex: 1; |
||||
|
overflow: hidden; |
||||
|
position: relative; |
||||
|
min-width: 0; |
||||
|
/* 允许容器收缩 */ |
||||
|
} |
||||
|
|
||||
|
.warn_text { |
||||
|
display: block; |
||||
|
white-space: nowrap; |
||||
|
will-change: transform; |
||||
|
/* 优化动画性能 */ |
||||
|
} |
||||
|
|
||||
|
/* 文字滚动动画 */ |
||||
|
@keyframes scrollText { |
||||
|
0% { |
||||
|
transform: translateX(0); |
||||
|
} |
||||
|
|
||||
|
20% { |
||||
|
transform: translateX(0); |
||||
|
} |
||||
|
|
||||
|
80% { |
||||
|
transform: translateX(-85%); |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
transform: translateX(-85%); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 当文字超长时启用滚动动画 */ |
||||
|
.warn_text.scroll-active { |
||||
|
animation: scrollText 12s linear infinite; |
||||
|
animation-delay: 2s; |
||||
|
/* 延迟2秒开始滚动,让用户先看到开头 */ |
||||
|
} |
||||
|
|
||||
|
/* 内容区域 */ |
||||
|
.content { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 120rpx; |
||||
|
background-color: #f5f5f5; |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
/* 市场分组 */ |
||||
|
.market-section { |
||||
|
background-color: white; |
||||
|
border-radius: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.market-header { |
||||
|
margin: 20rpx 20rpx 0 20rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
margin-bottom: 10rpx; |
||||
|
padding-bottom: 10rpx; |
||||
|
border-bottom: 2rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.market-title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: 600; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.market-more { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.more-text { |
||||
|
font-size: 24rpx; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
.more-arrow { |
||||
|
font-size: 20rpx; |
||||
|
color: #666; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
/* 三列卡片网格 */ |
||||
|
.cards-grid-three { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
} |
||||
|
|
||||
|
.card-item { |
||||
|
background-color: white; |
||||
|
border-radius: 16rpx; |
||||
|
overflow: hidden; |
||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease; |
||||
|
} |
||||
|
|
||||
|
.card-item:active { |
||||
|
transform: scale(0.98); |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12); |
||||
|
} |
||||
|
|
||||
|
/* 底部安全区域 */ |
||||
|
.bottom-safe-area { |
||||
|
height: 40rpx; |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
/* 底部导航栏 */ |
||||
|
.static-footer { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
} |
||||
|
|
||||
|
/* 响应式设计 */ |
||||
|
@media (max-width: 400rpx) { |
||||
|
.cards-grid-three { |
||||
|
grid-template-columns: repeat(2, 1fr); |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,485 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<!-- 自定义导航栏 --> |
||||
|
<view class="header_fixed" :style="{ top: iSMT + 'px' }"> |
||||
|
<view class="header-content"> |
||||
|
<view class="header-left" @click="goBack"> |
||||
|
<text class="back-text">‹</text> |
||||
|
</view> |
||||
|
<view class="header-center"> |
||||
|
<text class="header-title">{{ marketTitle }}</text> |
||||
|
</view> |
||||
|
<view class="header-right"> |
||||
|
<image src="/static/marketSituation-image/search.png" class="header-icon" mode="aspectFit"></image> |
||||
|
<text class="more-text">···</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 表头 --> |
||||
|
<view class="table-header"> |
||||
|
<view class="header-item name-column"> |
||||
|
<text class="header-text">名称</text> |
||||
|
</view> |
||||
|
<view class="header-item price-column" @click="sortByPrice"> |
||||
|
<text class="header-text">最新</text> |
||||
|
<text class="sort-icon">{{ sortType === 'price' ? (sortOrder === 'asc' ? '↑' : '↓') : '↕' }}</text> |
||||
|
</view> |
||||
|
<view class="header-item change-column" @click="sortByChange"> |
||||
|
<text class="header-text">涨幅</text> |
||||
|
<text class="sort-icon">{{ sortType === 'change' ? (sortOrder === 'asc' ? '↑' : '↓') : '↕' }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 内容区域 --> |
||||
|
<scroll-view class="content" :style="{ top: contentTopPosition + 'px' }" scroll-y="true"> |
||||
|
|
||||
|
|
||||
|
<!-- 股票列表 --> |
||||
|
<view class="stock-list"> |
||||
|
<view class="stock-row" v-for="(stock, index) in sortedStockList" :key="index" |
||||
|
@click="viewStockDetail(stock)"> |
||||
|
<view class="stock-cell name-column"> |
||||
|
<view class="stock-name">{{ stock.name }}</view> |
||||
|
<view class="stock-code">{{ stock.code }}</view> |
||||
|
</view> |
||||
|
<view class="stock-cell price-column"> |
||||
|
<text class="stock-price" |
||||
|
:class="stock.isRising ? 'rising' : 'falling'"> |
||||
|
{{ typeof stock.price === 'number' ? stock.price.toFixed(2) : stock.price }} |
||||
|
</text> |
||||
|
</view> |
||||
|
<view class="stock-cell change-column"> |
||||
|
<text class="stock-change" |
||||
|
:class="stock.isRising ? 'rising' : 'falling'"> |
||||
|
{{ stock.change || stock.changePercent }} |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部安全区域 --> |
||||
|
<!-- <view class="bottom-safe-area"></view> --> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部导航栏 --> |
||||
|
<!-- <footerBar class="static-footer" :type="'marketSituation'"></footerBar> --> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, computed, onMounted, watch } from 'vue' |
||||
|
import { onLoad } from '@dcloudio/uni-app' |
||||
|
import footerBar from '@/components/footerBar.vue' |
||||
|
|
||||
|
// 响应式数据 |
||||
|
const iSMT = ref(0) |
||||
|
const contentHeight = ref(0) |
||||
|
const headerHeight = ref(80) |
||||
|
const marketType = ref('america') |
||||
|
const marketTitle = ref('美洲') |
||||
|
const sortType = ref('') // 排序类型:'price' 或 'change' |
||||
|
const sortOrder = ref('desc') // 排序顺序:'asc' 或 'desc' |
||||
|
|
||||
|
// 股票数据 |
||||
|
const stockList = ref([ |
||||
|
{ |
||||
|
name: 'Telecommunication', |
||||
|
code: '888607', |
||||
|
price: 1349.47, |
||||
|
change: '+7.67%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Other', |
||||
|
code: '888607', |
||||
|
price: 1349.47, |
||||
|
change: '+6.67%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Consumer Discretio...', |
||||
|
code: '888610', |
||||
|
price: 1349.47, |
||||
|
change: '+5.67%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Telecommunication', |
||||
|
code: '888607', |
||||
|
price: 1349.47, |
||||
|
change: '+4.67%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Other', |
||||
|
code: '888611', |
||||
|
price: 1359.47, |
||||
|
change: '+3.67%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Consumer Discretio...', |
||||
|
code: '888610', |
||||
|
price: 1349.47, |
||||
|
change: '+2.67%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Telecommunication', |
||||
|
code: '888607', |
||||
|
price: 1349.47, |
||||
|
change: '+1.67%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Other', |
||||
|
code: '888611', |
||||
|
price: 1009.98, |
||||
|
change: '-1.67%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Consumer Discretio...', |
||||
|
code: '888610', |
||||
|
price: 1009.98, |
||||
|
change: '-0.67%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Telecommunication', |
||||
|
code: '888607', |
||||
|
price: 1009.98, |
||||
|
change: '-0.67%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Other', |
||||
|
code: '888611', |
||||
|
price: 1009.98, |
||||
|
change: '-1.67%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Consumer Discretio...', |
||||
|
code: '888610', |
||||
|
price: 1009.98, |
||||
|
change: '-4.67%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Consumer Discretio...', |
||||
|
code: '888610', |
||||
|
price: 1009.98, |
||||
|
change: '-3.67%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
name: 'Consumer Discretio...', |
||||
|
code: '888610', |
||||
|
price: 1009.98, |
||||
|
change: '-3.67%', |
||||
|
isRising: false |
||||
|
} |
||||
|
]) |
||||
|
|
||||
|
// 计算属性 |
||||
|
const contentTopPosition = computed(() => { |
||||
|
return iSMT.value + headerHeight.value |
||||
|
}) |
||||
|
|
||||
|
const sortedStockList = computed(() => { |
||||
|
console.log('计算sortedStockList,原始数据长度:', stockList.value.length); |
||||
|
let list = [...stockList.value] |
||||
|
|
||||
|
if (sortType.value === 'price') { |
||||
|
list.sort((a, b) => { |
||||
|
return sortOrder.value === 'asc' ? a.price - b.price : b.price - a.price |
||||
|
}) |
||||
|
} else if (sortType.value === 'change') { |
||||
|
list.sort((a, b) => { |
||||
|
const aChange = parseFloat(a.change.replace(/[+%-]/g, '')) |
||||
|
const bChange = parseFloat(b.change.replace(/[+%-]/g, '')) |
||||
|
return sortOrder.value === 'asc' ? aChange - bChange : bChange - aChange |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
console.log('排序后数据长度:', list.length); |
||||
|
return list |
||||
|
}) |
||||
|
|
||||
|
// 页面加载时接收参数 |
||||
|
onLoad((options) => { |
||||
|
if (options && options.market) { |
||||
|
marketType.value = options.market |
||||
|
switch (options.market) { |
||||
|
case 'america': |
||||
|
marketTitle.value = '美洲' |
||||
|
break |
||||
|
case 'asia': |
||||
|
marketTitle.value = '亚太' |
||||
|
break |
||||
|
case 'asia-china': |
||||
|
marketTitle.value = '亚太-中华' |
||||
|
break |
||||
|
default: |
||||
|
marketTitle.value = '全球指数' |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 方法 |
||||
|
const goBack = () => { |
||||
|
uni.navigateBack() |
||||
|
} |
||||
|
|
||||
|
const sortByPrice = () => { |
||||
|
if (sortType.value === 'price') { |
||||
|
sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc' |
||||
|
} else { |
||||
|
sortType.value = 'price' |
||||
|
sortOrder.value = 'desc' |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const sortByChange = () => { |
||||
|
if (sortType.value === 'change') { |
||||
|
sortOrder.value = sortOrder.value === 'asc' ? 'desc' : 'asc' |
||||
|
} else { |
||||
|
sortType.value = 'change' |
||||
|
sortOrder.value = 'desc' |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const viewStockDetail = (stock) => { |
||||
|
console.log('查看股票详情:', stock) |
||||
|
// 这里可以跳转到股票详情页面 |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 获取状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
// 动态计算header实际高度 |
||||
|
uni.createSelectorQuery().select('.header_fixed').boundingClientRect((rect) => { |
||||
|
if (rect) { |
||||
|
headerHeight.value = rect.height |
||||
|
console.log('Header实际高度:', headerHeight.value, 'px') |
||||
|
} |
||||
|
}).exec() |
||||
|
}) |
||||
|
|
||||
|
// 监听headerHeight变化,重新计算contentHeight |
||||
|
watch(headerHeight, (newHeight) => { |
||||
|
if (newHeight > 0) { |
||||
|
const systemInfo = uni.getSystemInfoSync() |
||||
|
const windowHeight = systemInfo.windowHeight |
||||
|
const statusBarHeight = systemInfo.statusBarHeight || 0 |
||||
|
const footerHeight = 100 |
||||
|
|
||||
|
contentHeight.value = windowHeight - statusBarHeight - newHeight - footerHeight |
||||
|
console.log('重新计算contentHeight:', contentHeight.value) |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.main { |
||||
|
width: 100%; |
||||
|
height: 100vh; |
||||
|
background-color: #f5f5f5; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
/* 自定义导航栏 */ |
||||
|
.header_fixed { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
background-color: #ffffff; |
||||
|
border-bottom: 1px solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.header-content { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
height: 44px; |
||||
|
padding: 0 15px; |
||||
|
} |
||||
|
|
||||
|
.header-left, |
||||
|
.header-right { |
||||
|
width: 60px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.header-left { |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
|
||||
|
.header-right { |
||||
|
justify-content: flex-end; |
||||
|
gap: 10px; |
||||
|
} |
||||
|
|
||||
|
.back-text { |
||||
|
font-size: 24px; |
||||
|
color: #333333; |
||||
|
font-weight: 500; |
||||
|
line-height: 1; |
||||
|
} |
||||
|
|
||||
|
.header-center { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.header-title { |
||||
|
font-size: 18px; |
||||
|
font-weight: 600; |
||||
|
color: #333333; |
||||
|
} |
||||
|
|
||||
|
.header-icon { |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
} |
||||
|
|
||||
|
.more-text { |
||||
|
font-size: 20px; |
||||
|
color: #666666; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
/* 内容区域 */ |
||||
|
.content { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
/* 表头样式 */ |
||||
|
.table-header { |
||||
|
display: flex; |
||||
|
padding: 12px 15px; |
||||
|
background-color: #f8f9fa; |
||||
|
border-bottom: 1px solid #e9ecef; |
||||
|
} |
||||
|
|
||||
|
.header-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.header-item.name-column { |
||||
|
flex: 2; |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
|
||||
|
.header-item.price-column, |
||||
|
.header-item.change-column { |
||||
|
flex: 1; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.header-text { |
||||
|
font-size: 14px; |
||||
|
color: #666666; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.sort-icon { |
||||
|
margin-left: 4px; |
||||
|
font-size: 12px; |
||||
|
color: #999999; |
||||
|
} |
||||
|
|
||||
|
/* 股票列表 */ |
||||
|
.stock-list { |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.stock-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 12px 15px; |
||||
|
border-bottom: 1px solid #f5f5f5; |
||||
|
} |
||||
|
|
||||
|
.stock-row:active { |
||||
|
background-color: #f8f8f8; |
||||
|
} |
||||
|
|
||||
|
.stock-cell { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.stock-cell.name-column { |
||||
|
flex: 2; |
||||
|
align-items: flex-start; |
||||
|
} |
||||
|
|
||||
|
.stock-cell.price-column, |
||||
|
.stock-cell.change-column { |
||||
|
flex: 1; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.stock-name { |
||||
|
font-size: 15px; |
||||
|
color: #333333; |
||||
|
font-weight: 500; |
||||
|
line-height: 1.2; |
||||
|
margin-bottom: 2px; |
||||
|
} |
||||
|
|
||||
|
.stock-code { |
||||
|
font-size: 11px; |
||||
|
color: #999999; |
||||
|
line-height: 1.2; |
||||
|
} |
||||
|
|
||||
|
.stock-price { |
||||
|
font-size: 15px; |
||||
|
font-weight: 600; |
||||
|
line-height: 1.2; |
||||
|
} |
||||
|
|
||||
|
.stock-change { |
||||
|
font-size: 13px; |
||||
|
font-weight: 500; |
||||
|
line-height: 1.2; |
||||
|
} |
||||
|
|
||||
|
.rising { |
||||
|
color: #00C851; |
||||
|
} |
||||
|
|
||||
|
.falling { |
||||
|
color: #FF4444; |
||||
|
} |
||||
|
|
||||
|
/* 底部安全区域 */ |
||||
|
/* .bottom-safe-area { |
||||
|
height: 20px; |
||||
|
} */ |
||||
|
|
||||
|
/* 底部导航栏 */ |
||||
|
/* .static-footer { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
} */ |
||||
|
</style> |
||||
@ -0,0 +1,733 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<!-- 可滚动内容区域 --> |
||||
|
<scroll-view class="content_scroll" scroll-y="true" :style="{ top: contentTopPosition + 'px' }"> |
||||
|
<view class="content"> |
||||
|
<button @click="goToChartExample">图表</button> |
||||
|
<view class="map"> |
||||
|
<image src="/static/marketSituation-image/map.png" mode="widthFix"></image> |
||||
|
</view> |
||||
|
<view class="global_index"> |
||||
|
<view class="global_index_title"> |
||||
|
{{ $t('marketSituation.globalIndex') }} |
||||
|
</view> |
||||
|
<view class="global_index_more" @click="goToGlobalIndex"> |
||||
|
<text>{{ $t('marketSituation.globalIndexMore') }}</text> |
||||
|
<image src="/static/marketSituation-image/more.png" mode="aspectFit"></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 卡片网格 --> |
||||
|
<view class="cards_grid"> |
||||
|
<view v-for="(card, index) in cardData" :key="index" class="card_item"> |
||||
|
<IndexCard :flagIcon="card.flagIcon" :indexName="card.indexName" |
||||
|
:currentPrice="card.currentPrice" :changeAmount="card.changeAmount" |
||||
|
:changePercent="card.changePercent" :isRising="card.isRising" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="warn"> |
||||
|
<image src="/static/marketSituation-image/warn.png" mode="aspectFit"></image> |
||||
|
<view class="warn_text_container"> |
||||
|
<text :class="warnTextClass">{{ $t('marketSituation.warn') }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 底部安全区域,防止被导航栏遮挡 --> |
||||
|
<view class="bottom_safe_area"></view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted, watch, nextTick, computed } from 'vue' |
||||
|
import util from '../../common/util.js' |
||||
|
import IndexCard from '../../components/IndexCard.vue' |
||||
|
|
||||
|
const iSMT = ref(0) |
||||
|
const searchValue = ref('') |
||||
|
const contentHeight = ref(0) |
||||
|
const headerHeight = ref(0) // 动态计算的header高度 |
||||
|
const isWarnTextOverflow = ref(false) // warn文字是否溢出 |
||||
|
|
||||
|
const pageIndex = ref(0) |
||||
|
const scrollToView = ref('') |
||||
|
|
||||
|
// 跳转图表示例页面 |
||||
|
const goToChartExample = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/marketSituation/chartExample' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 计算属性:精准计算content区域的top值 |
||||
|
const contentTopPosition = computed(() => { |
||||
|
const statusBarHeight = iSMT.value || 0 |
||||
|
const currentHeaderHeight = headerHeight.value > 0 ? headerHeight.value : 140 |
||||
|
return statusBarHeight + currentHeaderHeight |
||||
|
}) |
||||
|
|
||||
|
// warn文字的class计算属性 |
||||
|
const warnTextClass = computed(() => { |
||||
|
return isWarnTextOverflow.value ? 'warn_text scroll-active' : 'warn_text' |
||||
|
}) |
||||
|
|
||||
|
// 弹窗相关数据 |
||||
|
const showCountryModal = ref(false) |
||||
|
const selectedCountry = ref('概况') |
||||
|
const countryList = ref([ |
||||
|
'概况', '新加坡', '马来西亚', '印度尼西亚', '美国', '中国香港', |
||||
|
'泰国', '中国', '加拿大', '越南', '外汇', '贵金属' |
||||
|
]) |
||||
|
|
||||
|
// 卡片数据 |
||||
|
const cardData = ref([ |
||||
|
{ |
||||
|
flagIcon: '🇺🇸', |
||||
|
indexName: '道琼斯', |
||||
|
currentPrice: '45757.90', |
||||
|
changeAmount: '-125.22', |
||||
|
changePercent: '-0.27%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '🇺🇸', |
||||
|
indexName: '纳斯达克', |
||||
|
currentPrice: '22333.96', |
||||
|
changeAmount: '+125.22', |
||||
|
changePercent: '+0.47%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '🇺🇸', |
||||
|
indexName: '标普500', |
||||
|
currentPrice: '6606.08', |
||||
|
changeAmount: '+125.22', |
||||
|
changePercent: '+0.27%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '🇨🇳', |
||||
|
indexName: '上证指数', |
||||
|
currentPrice: '3333.96', |
||||
|
changeAmount: '+125.22', |
||||
|
changePercent: '+0.27%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '🇨🇳', |
||||
|
indexName: '科创50', |
||||
|
currentPrice: '757.90', |
||||
|
changeAmount: '-25.22', |
||||
|
changePercent: '-0.27%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '🇭🇰', |
||||
|
indexName: '恒生指数', |
||||
|
currentPrice: '19757.90', |
||||
|
changeAmount: '-125.22', |
||||
|
changePercent: '-0.63%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '🇸🇬', |
||||
|
indexName: '道琼斯', |
||||
|
currentPrice: '3757.90', |
||||
|
changeAmount: '+85.22', |
||||
|
changePercent: '+2.31%', |
||||
|
isRising: true |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '🇲🇾', |
||||
|
indexName: '纳斯达克', |
||||
|
currentPrice: '1657.90', |
||||
|
changeAmount: '-15.22', |
||||
|
changePercent: '-0.91%', |
||||
|
isRising: false |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: '🇹🇭', |
||||
|
indexName: '标普500', |
||||
|
currentPrice: '1457.90', |
||||
|
changeAmount: '+35.22', |
||||
|
changePercent: '+2.48%', |
||||
|
isRising: true |
||||
|
} |
||||
|
]) |
||||
|
|
||||
|
// 搜索输入事件 |
||||
|
const onSearchInput = (e) => { |
||||
|
searchValue.value = e.detail.value |
||||
|
} |
||||
|
|
||||
|
// 搜索确认事件 |
||||
|
const onSearchConfirm = (e) => { |
||||
|
console.log('搜索内容:', e.detail.value) |
||||
|
// 这里可以添加搜索逻辑 |
||||
|
performSearch(e.detail.value) |
||||
|
} |
||||
|
|
||||
|
// 搜索图标点击事件 |
||||
|
const onSearchClick = () => { |
||||
|
if (searchValue.value.trim()) { |
||||
|
performSearch(searchValue.value) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 执行搜索 |
||||
|
const performSearch = (keyword) => { |
||||
|
if (!keyword.trim()) { |
||||
|
uni.showToast({ |
||||
|
title: '请输入搜索内容', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
uni.showToast({ |
||||
|
title: `搜索: ${keyword}`, |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
// 这里添加实际的搜索逻辑 |
||||
|
} |
||||
|
|
||||
|
// 检测warn文字是否溢出 |
||||
|
const checkWarnTextOverflow = () => { |
||||
|
nextTick(() => { |
||||
|
setTimeout(() => { |
||||
|
const query = uni.createSelectorQuery() |
||||
|
|
||||
|
// 同时查询容器和文字元素 |
||||
|
query.select('.warn_text_container').boundingClientRect() |
||||
|
query.select('.warn_text').boundingClientRect() |
||||
|
query.exec((res) => { |
||||
|
const containerRect = res[0] |
||||
|
const textRect = res[1] |
||||
|
|
||||
|
if (!containerRect || !textRect) { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 判断文字是否超出容器(留一些余量) |
||||
|
const isOverflow = textRect.width > (containerRect.width - 10) |
||||
|
|
||||
|
isWarnTextOverflow.value = isOverflow |
||||
|
}) |
||||
|
}, 500) |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 跳转到全球指数页面 |
||||
|
const goToGlobalIndex = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/marketSituation/globalIndex' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
|
||||
|
// 确保DOM渲染完成后再查询高度 |
||||
|
nextTick(() => { |
||||
|
// 动态计算header实际高度 |
||||
|
uni.createSelectorQuery().select('.header_fixed').boundingClientRect((rect) => { |
||||
|
if (rect) { |
||||
|
headerHeight.value = rect.height |
||||
|
console.log('Header实际高度:', headerHeight.value, 'px') |
||||
|
} |
||||
|
}).exec() |
||||
|
|
||||
|
// 检测warn文字是否溢出 |
||||
|
checkWarnTextOverflow() |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
// 监听headerHeight变化,重新计算contentHeight |
||||
|
watch(headerHeight, (newHeight) => { |
||||
|
if (newHeight > 0) { |
||||
|
const systemInfo = uni.getSystemInfoSync() |
||||
|
const windowHeight = systemInfo.windowHeight |
||||
|
const statusBarHeight = systemInfo.statusBarHeight || 0 |
||||
|
const footerHeight = 100 |
||||
|
|
||||
|
contentHeight.value = windowHeight - statusBarHeight - newHeight - footerHeight |
||||
|
console.log('重新计算contentHeight:', contentHeight.value) |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 状态栏占位 */ |
||||
|
.top { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1001; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
/* 固定头部样式 */ |
||||
|
.header_fixed { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
background-color: #ffffff; |
||||
|
padding: 20rpx 0 0 0; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
/* 可滚动内容区域 */ |
||||
|
.content_scroll { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 100rpx; |
||||
|
/* 底部导航栏高度 */ |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
|
||||
|
.header_content { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
height: 80rpx; |
||||
|
padding: 0 20rpx; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.header_input_wrapper { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
width: 100%; |
||||
|
margin: 0 20rpx 0 0; |
||||
|
height: 70rpx; |
||||
|
border-radius: 35rpx; |
||||
|
background-color: #ffffff; |
||||
|
border: 1rpx solid #e9ecef; |
||||
|
padding: 0 80rpx 0 30rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #5c5c5c; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.search_icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
opacity: 0.6; |
||||
|
} |
||||
|
|
||||
|
.header_input { |
||||
|
margin-left: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.header_icons { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 15rpx; |
||||
|
} |
||||
|
|
||||
|
.header_icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.header_icon image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
} |
||||
|
|
||||
|
/* Tab 栏样式 */ |
||||
|
.channel_li { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 80rpx; |
||||
|
background-color: #ffffff; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.channel_wrap { |
||||
|
width: calc(100% - 60rpx); |
||||
|
height: 100%; |
||||
|
overflow: hidden; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.channel_innerWrap { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 100%; |
||||
|
padding: 0 20rpx; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.channel_item { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 60rpx; |
||||
|
padding: 0 20rpx; |
||||
|
border-radius: 30rpx; |
||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
||||
|
cursor: pointer; |
||||
|
white-space: nowrap; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.channel_item:active { |
||||
|
transform: scale(0.98); |
||||
|
} |
||||
|
|
||||
|
.channel_item.active { |
||||
|
color: #333; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.channel_text { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: 500; |
||||
|
color: #666666; |
||||
|
transition: color 0.3s ease; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.channel_item.active .channel_text { |
||||
|
color: #333333; |
||||
|
font-weight: 400; |
||||
|
z-index: 2; |
||||
|
} |
||||
|
|
||||
|
.active_indicator { |
||||
|
position: absolute; |
||||
|
left: 50%; |
||||
|
top: 60%; |
||||
|
transform: translateX(-45%); |
||||
|
width: calc(100% - 20rpx); |
||||
|
min-width: 40rpx; |
||||
|
max-width: 120rpx; |
||||
|
height: 8rpx; |
||||
|
background-image: url('/static/marketSituation-image/bg.png'); |
||||
|
background-size: cover; |
||||
|
background-position: center; |
||||
|
background-repeat: no-repeat; |
||||
|
animation: slideIn 0.1s ease; |
||||
|
border-radius: 8rpx; |
||||
|
z-index: 1; |
||||
|
} |
||||
|
|
||||
|
@keyframes slideIn { |
||||
|
from { |
||||
|
width: 0; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
|
||||
|
to { |
||||
|
width: 40rpx; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.scroll_indicator { |
||||
|
border-left: 1rpx solid #b6b6b6; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
width: 60rpx; |
||||
|
height: 30rpx; |
||||
|
background-color: #ffffff; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.scroll_indicator image { |
||||
|
width: 20rpx; |
||||
|
height: 20rpx; |
||||
|
opacity: 0.5; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
margin-top: 20rpx; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.map { |
||||
|
width: calc(100% - 60rpx); |
||||
|
margin: 0 30rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
background-color: #F3F3F3; |
||||
|
border-radius: 30rpx; |
||||
|
border: 1rpx solid #E0E0E0; |
||||
|
padding: 30rpx 20rpx; |
||||
|
box-sizing: border-box; |
||||
|
/* 设置最小高度保护,但允许内容撑开 */ |
||||
|
min-height: 200rpx; |
||||
|
} |
||||
|
|
||||
|
.map image { |
||||
|
width: 100%; |
||||
|
height: auto; |
||||
|
max-width: 100%; |
||||
|
display: block; |
||||
|
/* widthFix模式下,高度会自动按比例调整 */ |
||||
|
/* 设置最大高度避免图片过大 */ |
||||
|
max-height: 60vh; |
||||
|
/* 添加平滑过渡效果 */ |
||||
|
transition: all 0.3s ease; |
||||
|
max-height: 60vh; |
||||
|
} |
||||
|
|
||||
|
/* 响应式优化 */ |
||||
|
@media screen and (max-width: 750rpx) { |
||||
|
.map { |
||||
|
margin: 0 20rpx; |
||||
|
width: calc(100% - 40rpx); |
||||
|
padding: 20rpx 15rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
@media screen and (max-width: 480rpx) { |
||||
|
.map { |
||||
|
margin: 0 15rpx; |
||||
|
width: calc(100% - 30rpx); |
||||
|
padding: 15rpx 10rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.static-footer { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
} |
||||
|
|
||||
|
/* 弹窗样式 */ |
||||
|
.modal_overlay { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background-color: rgba(0, 0, 0, 0.5); |
||||
|
display: flex; |
||||
|
align-items: flex-end; |
||||
|
z-index: 1000; |
||||
|
} |
||||
|
|
||||
|
.modal_content { |
||||
|
width: 100%; |
||||
|
background-color: #fff; |
||||
|
border-radius: 20rpx 20rpx 0 0; |
||||
|
max-height: 80vh; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.modal_header { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
padding: 30rpx 40rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.modal_title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333333; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.modal_close { |
||||
|
position: absolute; |
||||
|
right: 40rpx; |
||||
|
top: 50%; |
||||
|
transform: translateY(-50%); |
||||
|
width: 60rpx; |
||||
|
height: 60rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
font-size: 40rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.modal_body { |
||||
|
padding: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.country_grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.country_item { |
||||
|
padding: 24rpx 30rpx; |
||||
|
border-radius: 12rpx; |
||||
|
background-color: #f8f8f8; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
transition: all 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.country_item.selected { |
||||
|
background-color: #ff4444; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.country_text { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.country_item.selected .country_text { |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.global_index { |
||||
|
margin: 30rpx 20rpx 0 20rpx; |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
|
||||
|
.global_index_title { |
||||
|
margin-left: 20rpx; |
||||
|
font-size: 40rpx; |
||||
|
font-weight: 100; |
||||
|
color: #333333; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.global_index_more { |
||||
|
display: flex; |
||||
|
gap: 10rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #333333; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.global_index_more image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
/* 卡片网格样式 */ |
||||
|
.cards_grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
margin: 0; |
||||
|
box-sizing: border-box; |
||||
|
width: 100%; |
||||
|
padding: 30rpx 20rpx; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.card_item { |
||||
|
width: 100%; |
||||
|
box-sizing: border-box; |
||||
|
min-width: 0; |
||||
|
/* 防止内容溢出 */ |
||||
|
} |
||||
|
|
||||
|
/* 响应式布局 - 小屏幕时改为两列 */ |
||||
|
@media (max-width: 600rpx) { |
||||
|
.cards_grid { |
||||
|
grid-template-columns: repeat(2, 1fr); |
||||
|
padding: 30rpx 20rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 超小屏幕时改为单列 */ |
||||
|
@media (max-width: 400rpx) { |
||||
|
.cards_grid { |
||||
|
grid-template-columns: 1fr; |
||||
|
padding: 30rpx 20rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.warn { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: flex-start; |
||||
|
gap: 10rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #666666; |
||||
|
padding: 20rpx; |
||||
|
max-width: 100%; |
||||
|
overflow: hidden; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.warn image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
flex-shrink: 0; |
||||
|
/* 防止图片被压缩 */ |
||||
|
position: relative; |
||||
|
z-index: 2; |
||||
|
/* 确保图片在最上层 */ |
||||
|
} |
||||
|
|
||||
|
.warn_text_container { |
||||
|
flex: 1; |
||||
|
overflow: hidden; |
||||
|
position: relative; |
||||
|
min-width: 0; |
||||
|
/* 允许容器收缩 */ |
||||
|
} |
||||
|
|
||||
|
.warn_text { |
||||
|
display: block; |
||||
|
white-space: nowrap; |
||||
|
will-change: transform; |
||||
|
/* 优化动画性能 */ |
||||
|
} |
||||
|
|
||||
|
/* 文字滚动动画 */ |
||||
|
@keyframes scrollText { |
||||
|
0% { |
||||
|
transform: translateX(0); |
||||
|
} |
||||
|
|
||||
|
20% { |
||||
|
transform: translateX(0); |
||||
|
} |
||||
|
|
||||
|
80% { |
||||
|
transform: translateX(-85%); |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
transform: translateX(-85%); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 当文字超长时启用滚动动画 */ |
||||
|
.warn_text.scroll-active { |
||||
|
animation: scrollText 12s linear infinite; |
||||
|
animation-delay: 2s; |
||||
|
/* 延迟2秒开始滚动,让用户先看到开头 */ |
||||
|
} |
||||
|
|
||||
|
/* 底部安全区域 */ |
||||
|
.bottom_safe_area { |
||||
|
height: 40rpx; |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
/* 主容器样式调整 */ |
||||
|
.main { |
||||
|
position: relative; |
||||
|
height: 100vh; |
||||
|
overflow: hidden; |
||||
|
background-color: white; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,593 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<view class="main"> |
||||
|
<!-- 固定头部 --> |
||||
|
<view class="header_fixed" :style="{ top: iSMT + 'px' }"> |
||||
|
<view class="header_content"> |
||||
|
<view class="header_input_wrapper"> |
||||
|
<image class="search_icon" src="/static/marketSituation-image/search.png" mode="" |
||||
|
@click="onSearchClick"></image> |
||||
|
<input class="header_input" type="text" placeholder="搜索" |
||||
|
placeholder-style="color: #A6A6A6; font-size: 22rpx;" v-model="searchValue" |
||||
|
@input="onSearchInput" @confirm="onSearchConfirm" /> |
||||
|
</view> |
||||
|
<view class="header_icons"> |
||||
|
<view class="header_icon" @click="selected"> |
||||
|
<image src="/static/marketSituation-image/mySeclected.png" mode=""></image> |
||||
|
</view> |
||||
|
<view class="header_icon" @click="history"> |
||||
|
<image src="/static/marketSituation-image/history.png" mode=""></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="channel_li" v-if="channelData.length > 0"> |
||||
|
<scroll-view class="channel_wrap" scroll-x="true" :scroll-into-view="scrollToView" |
||||
|
:scroll-with-animation="true" show-scrollbar="false"> |
||||
|
<view class="channel_innerWrap"> |
||||
|
<view v-for="(item, index) in channelData" :key="item.id" :id="'nav' + item.id" |
||||
|
:class="['channel_item', index === pageIndex ? 'active' : '']" @click="navClick(index)"> |
||||
|
<text class="channel_text">{{ item.title }}</text> |
||||
|
<view v-if="index === pageIndex" class="active_indicator"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
<view class="scroll_indicator" @click="channel_more"> |
||||
|
<image src="/static/marketSituation-image/menu.png" mode="aspectFit"></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 可滚动内容区域 --> |
||||
|
<scroll-view class="content_scroll" scroll-y="true" :style="{ top: contentTopPosition + 'px' }"> |
||||
|
<!-- 动态组件切换 --> |
||||
|
<component :is="currentComponent" :countryId="currentChannelId" /> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
|
||||
|
<footerBar class="static-footer" :type="type"></footerBar> |
||||
|
|
||||
|
<!-- 更多tab弹窗 --> |
||||
|
<view v-if="showCountryModal" class="modal_overlay" @click="closeModal"> |
||||
|
<view class="modal_content" @click.stop> |
||||
|
<view class="modal_header"> |
||||
|
<text class="modal_title">全部栏目</text> |
||||
|
<view class="modal_close" @click="closeModal"> |
||||
|
<text>×</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="modal_body"> |
||||
|
<view class="country_grid"> |
||||
|
<view v-for="(country, index) in countryList" :key="index" |
||||
|
:class="['country_item', selectedCountry === country ? 'selected' : '']" |
||||
|
@click="selectCountry(country)"> |
||||
|
<text class="country_text">{{ country }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted, watch, nextTick, computed } from 'vue' |
||||
|
import footerBar from '../../components/footerBar.vue' |
||||
|
import forexMetals from './forexMetals.vue' |
||||
|
import marketOverview from './marketOverview.vue' |
||||
|
import countryMarket from './countryMarket.vue' |
||||
|
|
||||
|
const type = ref('marketSituation') |
||||
|
const iSMT = ref(0) |
||||
|
const searchValue = ref('') |
||||
|
const contentHeight = ref(0) |
||||
|
const headerHeight = ref(0) // 动态计算的header高度 |
||||
|
|
||||
|
// Tab 栏相关数据 |
||||
|
const channelData = ref([ |
||||
|
{ id: 1, title: '概况' }, |
||||
|
{ id: 2, title: '新加坡' }, |
||||
|
{ id: 3, title: '马来西亚' }, |
||||
|
{ id: 4, title: '印度尼西亚' }, |
||||
|
{ id: 5, title: '美国' }, |
||||
|
{ id: 6, title: '中国香港' }, |
||||
|
{ id: 7, title: '泰国' }, |
||||
|
{ id: 8, title: '中国' }, |
||||
|
{ id: 9, title: '加拿大' }, |
||||
|
{ id: 10, title: '越南' }, |
||||
|
{ id: 11, title: '外汇' }, |
||||
|
{ id: 12, title: '贵金属' }, |
||||
|
]) |
||||
|
const pageIndex = ref(0) |
||||
|
const scrollToView = ref('') |
||||
|
|
||||
|
// 动态组件相关 |
||||
|
const currentChannelId = computed(() => { |
||||
|
return channelData.value[pageIndex.value]?.id || 1 |
||||
|
}) |
||||
|
|
||||
|
const currentComponent = computed(() => { |
||||
|
const channelId = currentChannelId.value |
||||
|
|
||||
|
// 概况页面使用 MarketOverview 组件 |
||||
|
if (pageIndex.value === 0) { |
||||
|
return marketOverview |
||||
|
} |
||||
|
// 外汇(id=11)和贵金属(id=12)使用 ForexMetals 组件 |
||||
|
else if (channelId === 11 || channelId === 12) { |
||||
|
return forexMetals |
||||
|
} |
||||
|
// 其他国家/地区页面使用 CountryMarket 组件 |
||||
|
else { |
||||
|
return countryMarket |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 计算属性:精准计算content区域的top值 |
||||
|
const contentTopPosition = computed(() => { |
||||
|
const statusBarHeight = iSMT.value || 0 |
||||
|
const currentHeaderHeight = headerHeight.value > 0 ? headerHeight.value : 140 |
||||
|
return statusBarHeight + currentHeaderHeight |
||||
|
}) |
||||
|
|
||||
|
// 弹窗相关数据 |
||||
|
const showCountryModal = ref(false) |
||||
|
const selectedCountry = ref('概况') |
||||
|
const countryList = ref([ |
||||
|
'概况', '新加坡', '马来西亚', '印度尼西亚', '美国', '中国香港', |
||||
|
'泰国', '中国', '加拿大', '越南', '外汇', '贵金属' |
||||
|
]) |
||||
|
|
||||
|
// 搜索输入事件 |
||||
|
const onSearchInput = (e) => { |
||||
|
searchValue.value = e.detail.value |
||||
|
} |
||||
|
|
||||
|
// 搜索确认事件 |
||||
|
const onSearchConfirm = (e) => { |
||||
|
console.log('搜索内容:', e.detail.value) |
||||
|
// 这里可以添加搜索逻辑 |
||||
|
performSearch(e.detail.value) |
||||
|
} |
||||
|
|
||||
|
// 搜索图标点击事件 |
||||
|
const onSearchClick = () => { |
||||
|
if (searchValue.value.trim()) { |
||||
|
performSearch(searchValue.value) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 执行搜索 |
||||
|
const performSearch = (keyword) => { |
||||
|
if (!keyword.trim()) { |
||||
|
uni.showToast({ |
||||
|
title: '请输入搜索内容', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
uni.showToast({ |
||||
|
title: `搜索: ${keyword}`, |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
// 这里添加实际的搜索逻辑 |
||||
|
} |
||||
|
|
||||
|
// 我的收藏点击事件 |
||||
|
const selected = () => { |
||||
|
uni.showToast({ |
||||
|
title: '我的收藏', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
// 这里可以跳转到收藏页面 |
||||
|
} |
||||
|
|
||||
|
// 历史记录点击事件 |
||||
|
const history = () => { |
||||
|
uni.showToast({ |
||||
|
title: '历史记录', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
// 这里可以跳转到历史页面 |
||||
|
} |
||||
|
|
||||
|
// Tab 栏点击事件 |
||||
|
const navClick = (index) => { |
||||
|
pageIndex.value = index |
||||
|
const currentItem = channelData.value[index] |
||||
|
scrollToView.value = 'nav' + currentItem.id |
||||
|
|
||||
|
// 同步更新弹窗中的选中状态 |
||||
|
selectedCountry.value = currentItem.title |
||||
|
|
||||
|
uni.showToast({ |
||||
|
title: `切换到: ${currentItem.title}`, |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
|
||||
|
// 这里可以添加切换 tab 后的数据加载逻辑 |
||||
|
console.log('当前选中的 tab:', currentItem) |
||||
|
} |
||||
|
|
||||
|
// 更多选项点击事件 |
||||
|
const channel_more = () => { |
||||
|
showCountryModal.value = true |
||||
|
} |
||||
|
|
||||
|
// 选择国家 |
||||
|
const selectCountry = (country) => { |
||||
|
selectedCountry.value = country |
||||
|
|
||||
|
// 查找对应的tab索引 |
||||
|
const targetIndex = channelData.value.findIndex(item => item.title === country) |
||||
|
|
||||
|
if (targetIndex !== -1) { |
||||
|
// 同步更新页面tab |
||||
|
pageIndex.value = targetIndex |
||||
|
const currentItem = channelData.value[targetIndex] |
||||
|
scrollToView.value = 'nav' + currentItem.id |
||||
|
|
||||
|
console.log('选中了:' + country + ',同步到tab索引:' + targetIndex) |
||||
|
uni.showToast({ |
||||
|
title: '已切换到:' + country, |
||||
|
icon: 'none', |
||||
|
duration: 2000 |
||||
|
}) |
||||
|
} else { |
||||
|
// 如果是"概况"或其他特殊选项,默认切换到第一个tab |
||||
|
if (country === '概况' || country === '全部') { |
||||
|
pageIndex.value = 0 |
||||
|
scrollToView.value = 'nav' + channelData.value[0].id |
||||
|
} |
||||
|
|
||||
|
console.log('选中了:' + country) |
||||
|
uni.showToast({ |
||||
|
title: '已选择:' + country, |
||||
|
icon: 'none', |
||||
|
duration: 2000 |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
// 这里可以添加切换到对应国家/地区数据的逻辑 |
||||
|
// 例如:loadMarketData(country) |
||||
|
closeModal() |
||||
|
} |
||||
|
|
||||
|
// 关闭弹窗 |
||||
|
const closeModal = () => { |
||||
|
showCountryModal.value = false |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
|
||||
|
// 初始化 tab 栏 |
||||
|
if (channelData.value.length > 0) { |
||||
|
pageIndex.value = 0 |
||||
|
scrollToView.value = 'nav' + channelData.value[0].id |
||||
|
} |
||||
|
|
||||
|
// 确保DOM渲染完成后再查询高度 |
||||
|
nextTick(() => { |
||||
|
// 动态计算header实际高度 |
||||
|
uni.createSelectorQuery().select('.header_fixed').boundingClientRect((rect) => { |
||||
|
if (rect) { |
||||
|
headerHeight.value = rect.height |
||||
|
console.log('Header实际高度:', headerHeight.value, 'px') |
||||
|
} |
||||
|
}).exec() |
||||
|
}) |
||||
|
}) |
||||
|
|
||||
|
// 监听headerHeight变化,重新计算contentHeight |
||||
|
watch(headerHeight, (newHeight) => { |
||||
|
if (newHeight > 0) { |
||||
|
const systemInfo = uni.getSystemInfoSync() |
||||
|
const windowHeight = systemInfo.windowHeight |
||||
|
const statusBarHeight = systemInfo.statusBarHeight || 0 |
||||
|
const footerHeight = 100 |
||||
|
|
||||
|
contentHeight.value = windowHeight - statusBarHeight - newHeight - footerHeight |
||||
|
console.log('重新计算contentHeight:', contentHeight.value) |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 主容器样式调整 */ |
||||
|
.main { |
||||
|
position: relative; |
||||
|
height: 100vh; |
||||
|
overflow: hidden; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
/* 状态栏占位 */ |
||||
|
.top { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1001; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
/* 固定头部样式 */ |
||||
|
.header_fixed { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
background-color: #ffffff; |
||||
|
padding: 20rpx 0 0 0; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
/* 可滚动内容区域 */ |
||||
|
.content_scroll { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 100rpx; |
||||
|
/* 底部导航栏高度 */ |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
|
||||
|
.header_content { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
height: 80rpx; |
||||
|
padding: 0 20rpx; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.header_input_wrapper { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
width: 100%; |
||||
|
margin: 0 20rpx 0 0; |
||||
|
height: 70rpx; |
||||
|
border-radius: 35rpx; |
||||
|
background-color: #ffffff; |
||||
|
border: 1rpx solid #e9ecef; |
||||
|
padding: 0 80rpx 0 30rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #5c5c5c; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.search_icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
opacity: 0.6; |
||||
|
} |
||||
|
|
||||
|
.header_input { |
||||
|
margin-left: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.header_icons { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 15rpx; |
||||
|
} |
||||
|
|
||||
|
.header_icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.header_icon image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
} |
||||
|
|
||||
|
/* Tab 栏样式 */ |
||||
|
.channel_li { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 80rpx; |
||||
|
background-color: #ffffff; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.channel_wrap { |
||||
|
width: calc(100% - 60rpx); |
||||
|
height: 100%; |
||||
|
overflow: hidden; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.channel_innerWrap { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 100%; |
||||
|
padding: 0 20rpx; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.channel_item { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 60rpx; |
||||
|
padding: 0 20rpx; |
||||
|
border-radius: 30rpx; |
||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
||||
|
cursor: pointer; |
||||
|
white-space: nowrap; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.channel_item:active { |
||||
|
transform: scale(0.98); |
||||
|
} |
||||
|
|
||||
|
.channel_item.active { |
||||
|
color: #333; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.channel_text { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: 500; |
||||
|
color: #666666; |
||||
|
transition: color 0.3s ease; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.channel_item.active .channel_text { |
||||
|
color: #333333; |
||||
|
font-weight: 400; |
||||
|
z-index: 2; |
||||
|
} |
||||
|
|
||||
|
.active_indicator { |
||||
|
position: absolute; |
||||
|
left: 50%; |
||||
|
top: 60%; |
||||
|
transform: translateX(-45%); |
||||
|
width: calc(100% - 20rpx); |
||||
|
min-width: 40rpx; |
||||
|
max-width: 120rpx; |
||||
|
height: 8rpx; |
||||
|
background-image: url('/static/marketSituation-image/bg.png'); |
||||
|
background-size: cover; |
||||
|
background-position: center; |
||||
|
background-repeat: no-repeat; |
||||
|
animation: slideIn 0.1s ease; |
||||
|
border-radius: 8rpx; |
||||
|
z-index: 1; |
||||
|
} |
||||
|
|
||||
|
@keyframes slideIn { |
||||
|
from { |
||||
|
width: 0; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
|
||||
|
to { |
||||
|
width: 40rpx; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.scroll_indicator { |
||||
|
border-left: 1rpx solid #b6b6b6; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
width: 60rpx; |
||||
|
height: 30rpx; |
||||
|
background-color: #ffffff; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.scroll_indicator image { |
||||
|
width: 20rpx; |
||||
|
height: 20rpx; |
||||
|
opacity: 0.5; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
margin-top: 20rpx; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.static-footer { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
} |
||||
|
|
||||
|
/* 弹窗样式 */ |
||||
|
.modal_overlay { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background-color: rgba(0, 0, 0, 0.5); |
||||
|
display: flex; |
||||
|
align-items: flex-end; |
||||
|
z-index: 1000; |
||||
|
} |
||||
|
|
||||
|
.modal_content { |
||||
|
width: 100%; |
||||
|
background-color: #fff; |
||||
|
border-radius: 20rpx 20rpx 0 0; |
||||
|
max-height: 80vh; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.modal_header { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
padding: 30rpx 40rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.modal_title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333333; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.modal_close { |
||||
|
position: absolute; |
||||
|
right: 40rpx; |
||||
|
top: 50%; |
||||
|
transform: translateY(-50%); |
||||
|
width: 60rpx; |
||||
|
height: 60rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
font-size: 40rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.modal_body { |
||||
|
padding: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.country_grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.country_item { |
||||
|
padding: 24rpx 30rpx; |
||||
|
border-radius: 12rpx; |
||||
|
background-color: #f8f8f8; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
transition: all 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.country_item.selected { |
||||
|
background-color: #ff4444; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.country_text { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.country_item.selected .country_text { |
||||
|
color: #fff; |
||||
|
} |
||||
|
</style> |
||||
|
After Width: 20 | Height: 20 | Size: 247 B |
|
After Width: 213 | Height: 228 | Size: 16 KiB |
|
After Width: 50 | Height: 110 | Size: 11 KiB |
|
After Width: 50 | Height: 109 | Size: 11 KiB |
|
After Width: 50 | Height: 110 | Size: 12 KiB |
Write
Preview
Loading…
Cancel
Save
Reference in new issue