12 changed files with 3401 additions and 1527 deletions
-
5common/util.js
-
28pages.json
-
655pages/home/chartExample.vue
-
493pages/home/countryMarket.vue
-
301pages/home/forexMetals.vue
-
839pages/home/globalIndex.vue
-
683pages/home/marketDetail.vue
-
733pages/home/marketOverview.vue
-
1185pages/home/marketSituation.vue
-
BINstatic/marketSituation-image/cool.png
-
BINstatic/marketSituation-image/hot.png
-
BINstatic/marketSituation-image/warm.png
@ -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> |
|||
@ -1,579 +1,574 @@ |
|||
<!-- @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 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> |
|||
<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> |
|||
<!-- 内容区域 --> |
|||
<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> |
|||
<!-- 底部导航栏 --> |
|||
<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"; |
|||
import { ref, onMounted, computed, nextTick, watch } from 'vue' |
|||
import footerBar from './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文字是否溢出 |
|||
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"; |
|||
}); |
|||
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); |
|||
}); |
|||
}; |
|||
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, |
|||
}, |
|||
]); |
|||
{ |
|||
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", |
|||
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, |
|||
}, |
|||
]); |
|||
{ |
|||
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", |
|||
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, |
|||
}, |
|||
]); |
|||
{ |
|||
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 statusBarHeight = iSMT.value || 0 |
|||
const currentHeaderHeight = headerHeight.value > 0 ? headerHeight.value : 100 |
|||
return statusBarHeight + currentHeaderHeight |
|||
}) |
|||
|
|||
// 方法:返回上一页 |
|||
const goBack = () => { |
|||
uni.navigateBack(); |
|||
}; |
|||
uni.navigateBack() |
|||
} |
|||
|
|||
// 方法:搜索输入 |
|||
const onSearchInput = (e) => { |
|||
searchValue.value = e.detail.value; |
|||
}; |
|||
searchValue.value = e.detail.value |
|||
} |
|||
|
|||
// 方法:清除搜索 |
|||
const clearSearch = () => { |
|||
searchValue.value = ""; |
|||
}; |
|||
searchValue.value = '' |
|||
} |
|||
|
|||
// 方法:查看更多 |
|||
const viewMore = (market) => { |
|||
console.log("查看更多:", market); |
|||
uni.navigateTo({ |
|||
url: `/pages/home/marketDetail?market=${market}`, |
|||
}); |
|||
}; |
|||
console.log('查看更多:', market) |
|||
uni.navigateTo({ |
|||
url: `/pages/marketSituation/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))}`, |
|||
}); |
|||
}; |
|||
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(); |
|||
}); |
|||
// 获取系统信息 |
|||
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); |
|||
} |
|||
}); |
|||
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; |
|||
position: relative; |
|||
height: 100vh; |
|||
overflow: hidden; |
|||
background-color: #f5f5f5; |
|||
} |
|||
|
|||
/* 状态栏占位 */ |
|||
.top { |
|||
position: fixed; |
|||
top: 0; |
|||
left: 0; |
|||
right: 0; |
|||
z-index: 1001; |
|||
background-color: #ffffff; |
|||
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); |
|||
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; |
|||
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; |
|||
margin-right: 20rpx; |
|||
width: 25rpx; |
|||
height: 30rpx; |
|||
} |
|||
|
|||
.header_back image { |
|||
width: 25rpx; |
|||
height: 30rpx; |
|||
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); |
|||
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; |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
opacity: 0.6; |
|||
} |
|||
|
|||
.header_input { |
|||
margin-left: 10rpx; |
|||
margin-left: 10rpx; |
|||
} |
|||
|
|||
.header_icons { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 15rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 15rpx; |
|||
} |
|||
|
|||
.header_icon { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.header_icon image { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
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; |
|||
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; |
|||
/* 确保图片在最上层 */ |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
flex-shrink: 0; |
|||
/* 防止图片被压缩 */ |
|||
position: relative; |
|||
z-index: 2; |
|||
/* 确保图片在最上层 */ |
|||
} |
|||
|
|||
.warn_text_container { |
|||
flex: 1; |
|||
overflow: hidden; |
|||
position: relative; |
|||
min-width: 0; |
|||
/* 允许容器收缩 */ |
|||
flex: 1; |
|||
overflow: hidden; |
|||
position: relative; |
|||
min-width: 0; |
|||
/* 允许容器收缩 */ |
|||
} |
|||
|
|||
.warn_text { |
|||
display: block; |
|||
white-space: nowrap; |
|||
will-change: transform; |
|||
/* 优化动画性能 */ |
|||
display: block; |
|||
white-space: nowrap; |
|||
will-change: transform; |
|||
/* 优化动画性能 */ |
|||
} |
|||
|
|||
/* 文字滚动动画 */ |
|||
@keyframes scrollText { |
|||
0% { |
|||
transform: translateX(0); |
|||
} |
|||
0% { |
|||
transform: translateX(0); |
|||
} |
|||
|
|||
20% { |
|||
transform: translateX(0); |
|||
} |
|||
20% { |
|||
transform: translateX(0); |
|||
} |
|||
|
|||
80% { |
|||
transform: translateX(-85%); |
|||
} |
|||
80% { |
|||
transform: translateX(-85%); |
|||
} |
|||
|
|||
100% { |
|||
transform: translateX(-85%); |
|||
} |
|||
100% { |
|||
transform: translateX(-85%); |
|||
} |
|||
} |
|||
|
|||
/* 当文字超长时启用滚动动画 */ |
|||
.warn_text.scroll-active { |
|||
animation: scrollText 12s linear infinite; |
|||
animation-delay: 2s; |
|||
/* 延迟2秒开始滚动,让用户先看到开头 */ |
|||
animation: scrollText 12s linear infinite; |
|||
animation-delay: 2s; |
|||
/* 延迟2秒开始滚动,让用户先看到开头 */ |
|||
} |
|||
|
|||
/* 内容区域 */ |
|||
.content { |
|||
position: fixed; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 120rpx; |
|||
background-color: #f5f5f5; |
|||
padding: 0; |
|||
position: fixed; |
|||
left: 0; |
|||
right: 0; |
|||
bottom: 120rpx; |
|||
background-color: #f5f5f5; |
|||
padding: 0; |
|||
} |
|||
|
|||
/* 市场分组 */ |
|||
.market-section { |
|||
background-color: white; |
|||
border-radius: 20rpx; |
|||
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; |
|||
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; |
|||
font-size: 32rpx; |
|||
font-weight: 600; |
|||
color: #333; |
|||
} |
|||
|
|||
.market-more { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 8rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 8rpx; |
|||
} |
|||
|
|||
.more-text { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
.more-arrow { |
|||
font-size: 20rpx; |
|||
color: #666; |
|||
font-weight: bold; |
|||
font-size: 20rpx; |
|||
color: #666; |
|||
font-weight: bold; |
|||
} |
|||
|
|||
/* 三列卡片网格 */ |
|||
.cards-grid-three { |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); |
|||
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; |
|||
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); |
|||
transform: scale(0.98); |
|||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12); |
|||
} |
|||
|
|||
/* 底部安全区域 */ |
|||
.bottom-safe-area { |
|||
height: 40rpx; |
|||
background-color: transparent; |
|||
height: 40rpx; |
|||
background-color: transparent; |
|||
} |
|||
|
|||
/* 底部导航栏 */ |
|||
.static-footer { |
|||
position: fixed; |
|||
bottom: 0; |
|||
left: 0; |
|||
right: 0; |
|||
z-index: 1000; |
|||
position: fixed; |
|||
bottom: 0; |
|||
left: 0; |
|||
right: 0; |
|||
z-index: 1000; |
|||
} |
|||
|
|||
/* 响应式设计 */ |
|||
@media (max-width: 400rpx) { |
|||
.cards-grid-three { |
|||
grid-template-columns: repeat(2, 1fr); |
|||
} |
|||
.cards-grid-three { |
|||
grid-template-columns: repeat(2, 1fr); |
|||
} |
|||
} |
|||
</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> |
|||
1185
pages/home/marketSituation.vue
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
|
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