Merge branch 'refs/heads/milestone-20251031-简版功能开发' into lihuilin/feature-20251024095243-我的
# Conflicts: # utils/http.jszhaowenkang/feature-20251028181547-行情页面
-
46api/customerServicePlatform/customerServicePlatform.js
-
185api/home/mySelections.js
-
70api/marketSituation/marketSituation.js
-
30api/start/login.js
-
196components/FeedbackModal.vue
-
288components/IndexCard.vue
-
2components/deepExploration_header.vue
-
30components/login-prompt.vue
-
28pages.json
-
687pages/customerServicePlatform/csPlatformIndex.vue
-
358pages/customerServicePlatform/historyRecord.vue
-
340pages/customerServicePlatform/questionDetail.vue
-
69pages/deepMate/deepMate.vue
-
258pages/home/home.vue
-
589pages/marketSituation/countryMarket.vue
-
842pages/marketSituation/globalIndex.vue
-
74pages/marketSituation/marketCondition.vue
-
704pages/marketSituation/marketDetail.vue
-
121pages/marketSituation/marketOverview.vue
-
844pages/marketSituation/marketSituation.vue
-
1pages/start/Registration/Registration.vue
-
95pages/start/recoverPassword/recoverPassword.vue
-
BINstatic/customer-service-platform/camera.png
-
BINstatic/customer-service-platform/cs-platform-back.png
-
BINstatic/customer-service-platform/ellipse-dc-img.png
-
BINstatic/customer-service-platform/empty-content.png
-
BINstatic/customer-service-platform/fail-icon.png
-
BINstatic/customer-service-platform/message.png
-
BINstatic/customer-service-platform/refresh-icon.png
-
BINstatic/customer-service-platform/robot-head.png
-
BINstatic/customer-service-platform/smile-icon.png
-
BINstatic/customer-service-platform/success-icon.png
-
BINstatic/marketSituation-image/country-flag/can.png
-
BINstatic/marketSituation-image/country-flag/cn.png
-
BINstatic/marketSituation-image/country-flag/global.png
-
BINstatic/marketSituation-image/country-flag/hk.png
-
BINstatic/marketSituation-image/country-flag/my.png
-
BINstatic/marketSituation-image/country-flag/sg.png
-
BINstatic/marketSituation-image/country-flag/th.png
-
BINstatic/marketSituation-image/country-flag/us.png
-
BINstatic/marketSituation-image/country-flag/vi.png
-
1stores/index.js
-
44stores/modules/login.js
-
78stores/modules/marketSituation.js
-
22utils/http.js
@ -0,0 +1,46 @@ |
|||
import { http } from '@/utils/http.js' |
|||
|
|||
const baseURL = "http://39.101.133.168:8828" |
|||
//图片上传
|
|||
export const uploadImageApi = (data) => { |
|||
return http({ |
|||
method: 'POST', |
|||
url: baseURL +'/hljw/api/aws/upload', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
//问题回答
|
|||
export const getAnswerApi = (data) => { |
|||
return http({ |
|||
method: 'POST', |
|||
url: 'http://pbb6edde.natappfree.cc' +'/api/customer/askQuestion', |
|||
data |
|||
}) |
|||
} |
|||
|
|||
//获取随机5条猜你想问问题
|
|||
export const getQuestionApi = (data) => { |
|||
return http({ |
|||
method: 'GET', |
|||
url: 'http://pbb6edde.natappfree.cc' +'/api/customer/getQuestion', |
|||
}) |
|||
} |
|||
|
|||
|
|||
//反馈添加
|
|||
export const addFeedbackRecordApi = (data) => { |
|||
return http({ |
|||
method: 'POST', |
|||
url: baseURL +'/link/third/dcFeedBack/feedback/add', |
|||
data |
|||
}) |
|||
} |
|||
//反馈历史记录
|
|||
export const getFeedbackRecordsApi = (data) => { |
|||
return http({ |
|||
method: 'POST', |
|||
url: baseURL+'/link/third/dcFeedBack/feedback/select', |
|||
data |
|||
}) |
|||
} |
|||
@ -0,0 +1,185 @@ |
|||
/** |
|||
* 我的自选股相关API接口封装 |
|||
* 使用utils/http.js中的拦截器封装请求方法 |
|||
*/ |
|||
|
|||
import { http } from '../../utils/http.js' |
|||
|
|||
/** |
|||
* 我的自选股API接口类 |
|||
*/ |
|||
class MySelectionsAPI { |
|||
|
|||
/** |
|||
* 判断用户是否存在自选股分组 |
|||
* @param {Function} successCallback - 成功回调函数 |
|||
* @param {Function} failCallback - 失败回调函数 |
|||
* @param {Object} data - 请求参数 |
|||
* @returns {Promise} |
|||
*/ |
|||
static async checkExist(successCallback, failCallback = null, data = {}) { |
|||
const url = '/api/homePage/userStock/checkExist' |
|||
|
|||
try { |
|||
const response = await http({ |
|||
url: url, |
|||
method: 'POST', |
|||
data: data |
|||
}) |
|||
|
|||
console.log('检查用户自选股分组存在性 - 响应:', response) |
|||
if (successCallback && typeof successCallback === 'function') { |
|||
successCallback(response) |
|||
} |
|||
return response |
|||
} catch (error) { |
|||
console.error('检查用户自选股分组存在性 - 失败:', error) |
|||
if (failCallback && typeof failCallback === 'function') { |
|||
failCallback(error) |
|||
} |
|||
throw error |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 查询用户所有自选股分组 |
|||
* @param {Function} successCallback - 成功回调函数 |
|||
* @param {Function} failCallback - 失败回调函数 |
|||
* @param {Object} data - 请求参数 |
|||
* @returns {Promise} |
|||
*/ |
|||
static async getUserStockGroupList(successCallback, failCallback = null, data = {}) { |
|||
const url = '/api/homePage/userStockGroup/list' |
|||
|
|||
try { |
|||
const response = await http({ |
|||
url: url, |
|||
method: 'POST', |
|||
data: data |
|||
}) |
|||
|
|||
console.log('查询用户自选股分组列表 - 响应:', response) |
|||
if (successCallback && typeof successCallback === 'function') { |
|||
successCallback(response) |
|||
} |
|||
return response |
|||
} catch (error) { |
|||
console.error('查询用户自选股分组列表 - 失败:', error) |
|||
if (failCallback && typeof failCallback === 'function') { |
|||
failCallback(error) |
|||
} |
|||
throw error |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 分页查询某一个分组下的所有自选股 |
|||
* @param {Function} successCallback - 成功回调函数 |
|||
* @param {Function} failCallback - 失败回调函数 |
|||
* @param {Object} data - 请求参数 {groupId, pageNum, pageSize, ...} |
|||
* @returns {Promise} |
|||
*/ |
|||
static async getUserStockList(successCallback, failCallback = null, data = {}) { |
|||
const url = '/api/homePage/userStock/list' |
|||
|
|||
// 设置默认分页参数
|
|||
const requestData = { |
|||
pageNum: 1, |
|||
pageSize: 20, |
|||
...data |
|||
} |
|||
|
|||
try { |
|||
const response = await http({ |
|||
url: url, |
|||
method: 'POST', |
|||
data: requestData |
|||
}) |
|||
|
|||
console.log('分页查询分组自选股 - 响应:', response) |
|||
if (successCallback && typeof successCallback === 'function') { |
|||
successCallback(response) |
|||
} |
|||
return response |
|||
} catch (error) { |
|||
console.error('分页查询分组自选股 - 失败:', error) |
|||
if (failCallback && typeof failCallback === 'function') { |
|||
failCallback(error) |
|||
} |
|||
throw error |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 查询默认自选股 |
|||
* @param {Function} successCallback - 成功回调函数 |
|||
* @param {Function} failCallback - 失败回调函数 |
|||
* @param {Object} data - 请求参数 |
|||
* @returns {Promise} |
|||
*/ |
|||
static async getUserOrDefault(successCallback, failCallback = null, data = {}) { |
|||
const url = '/api/homePage/userStock/getUserOrDefault' |
|||
|
|||
try { |
|||
const response = await http({ |
|||
url: url, |
|||
method: 'POST', |
|||
data: data |
|||
}) |
|||
|
|||
console.log('查询默认自选股 - 响应:', response) |
|||
if (successCallback && typeof successCallback === 'function') { |
|||
successCallback(response) |
|||
} |
|||
return response |
|||
} catch (error) { |
|||
console.error('查询默认自选股 - 失败:', error) |
|||
if (failCallback && typeof failCallback === 'function') { |
|||
failCallback(error) |
|||
} |
|||
throw error |
|||
} |
|||
} |
|||
|
|||
/** |
|||
* 游客查询默认自选股 |
|||
* @param {Function} successCallback - 成功回调函数 |
|||
* @param {Function} failCallback - 失败回调函数 |
|||
* @returns {Promise} |
|||
*/ |
|||
static async getDefaultStocks(successCallback, failCallback = null) { |
|||
const url = '/api/homePage/userStock/getDefaultStocks' |
|||
|
|||
try { |
|||
const response = await http({ |
|||
url: url, |
|||
method: 'POST', |
|||
data: {} |
|||
}) |
|||
|
|||
console.log('游客查询默认自选股 - 响应:', response) |
|||
if (successCallback && typeof successCallback === 'function') { |
|||
successCallback(response) |
|||
} |
|||
return response |
|||
} catch (error) { |
|||
console.error('游客查询默认自选股 - 失败:', error) |
|||
if (failCallback && typeof failCallback === 'function') { |
|||
failCallback(error) |
|||
} |
|||
throw error |
|||
} |
|||
} |
|||
} |
|||
|
|||
// 导出API类
|
|||
export default MySelectionsAPI |
|||
|
|||
// 也可以导出单个方法供直接使用
|
|||
export const { |
|||
checkExist, |
|||
getUserStockGroupList, |
|||
getUserStockList, |
|||
getUserOrDefault, |
|||
getDefaultStocks |
|||
} = MySelectionsAPI |
|||
@ -0,0 +1,70 @@ |
|||
/** @format */ |
|||
|
|||
import { http } from "../../utils/http"; |
|||
|
|||
export const getData = () => { |
|||
return http({ |
|||
method: "GET", |
|||
url: "/ka", |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 获取全球指数 |
|||
* POST /api/global/getGlobalIndex |
|||
*/ |
|||
export const getGlobalIndexAPI = (data) => { |
|||
return http({ |
|||
method: "POST", |
|||
url: "/api/global/getGlobalIndex", |
|||
data, |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 获取地区分组列表 |
|||
* POST /api/global/regionalGroup |
|||
*/ |
|||
export const getRegionalGroupAPI = (data) => { |
|||
return http({ |
|||
method: "POST", |
|||
url: "/api/global/regionalGroup", |
|||
data, |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 获取地区详情 |
|||
* POST /api/global/regionalGroupList |
|||
*/ |
|||
export const getRegionalGroupListAPI = (data) => { |
|||
return http({ |
|||
method: "POST", |
|||
url: "/api/global/regionalGroupList", |
|||
data, |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 一次性查询所有Tab栏父子结构 |
|||
* POST /api/homework/tab/getAll |
|||
*/ |
|||
export const getAllTabsAPI = (data) => { |
|||
return http({ |
|||
method: "POST", |
|||
url: "/api/homework/tab/getAll", |
|||
data, |
|||
}); |
|||
}; |
|||
|
|||
/** |
|||
* 通用市场数据分页查询接口(默认1页20条) |
|||
* POST /api/market/data/page/query |
|||
*/ |
|||
export const queryStockDataAPI = (data) => { |
|||
return http({ |
|||
method: "POST", |
|||
url: "/api/market/data/page/query", |
|||
data, |
|||
}); |
|||
}; |
|||
@ -0,0 +1,196 @@ |
|||
<template> |
|||
<view> |
|||
<view v-if="internalVisible" class="fm-overlay"></view> |
|||
|
|||
<view v-if="internalVisible" class="fm-wrap"> |
|||
<view class="fm-card" :style="{ width: _width }"> |
|||
<text class="fm-title">{{ _title }}</text> |
|||
|
|||
<view class="fm-icon-wrap"> |
|||
<image :src="imgSrcComputed" mode="aspectFit" class="fm-icon-img" /> |
|||
</view> |
|||
|
|||
<text class="fm-sub">{{ _subtitle }}</text> |
|||
|
|||
<view class="fm-btn-wrap"> |
|||
<button class="fm-btn" @click="onConfirm">{{ _buttonText }}</button> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
export default { |
|||
name: 'FeedbackModal', |
|||
props: { |
|||
title: { |
|||
type: String, |
|||
default: '提交成功' |
|||
}, |
|||
subtitle: { |
|||
type: String, |
|||
default: '— 感谢您的反馈 —' |
|||
}, |
|||
buttonText: { |
|||
type: String, |
|||
default: '确定' |
|||
}, |
|||
status: { |
|||
type: String, |
|||
default: 'success' |
|||
}, |
|||
lockScroll: { |
|||
type: Boolean, |
|||
default: true |
|||
}, |
|||
width: { |
|||
type: String, |
|||
default: '600rpx' |
|||
} |
|||
}, |
|||
data() { |
|||
return { |
|||
internalVisible: false, |
|||
_title: this.title, |
|||
_subtitle: this.subtitle, |
|||
_buttonText: this.buttonText, |
|||
_status: this.status, |
|||
_width: this.width, |
|||
}; |
|||
}, |
|||
computed: { |
|||
imgSrcComputed() { |
|||
return this._status === 'fail' ? |
|||
'/static/customer-service-platform/fail-icon.png' : |
|||
'/static/customer-service-platform/success-icon.png'; |
|||
} |
|||
}, |
|||
methods: { |
|||
show(options = {}) { |
|||
if (options.title) this._title = options.title; |
|||
if (options.subtitle) this._subtitle = options.subtitle; |
|||
if (options.buttonText) this._buttonText = options.buttonText; |
|||
if (options.status) this._status = options.status; |
|||
if (options.width) this._width = options.width; |
|||
if (this.lockScroll) this._lockScroll(); |
|||
|
|||
this.internalVisible = true; |
|||
}, |
|||
|
|||
hide() { |
|||
this.internalVisible = false; |
|||
if (this.lockScroll) this._unlockScroll(); |
|||
}, |
|||
|
|||
onConfirm() { |
|||
this.hide(); |
|||
this.$nextTick(() => { |
|||
this.$emit('confirm', { |
|||
status: this._status |
|||
}); |
|||
}); |
|||
}, |
|||
//整个页面的滚动条被禁用,防止用户无法上下滚动页面 |
|||
_lockScroll() { |
|||
try { |
|||
document.body.style.overflow = 'hidden'; |
|||
} catch (e) {} |
|||
}, |
|||
_unlockScroll() { |
|||
try { |
|||
document.body.style.overflow = ''; |
|||
} catch (e) {} |
|||
} |
|||
}, |
|||
beforeDestroy() { |
|||
if (this.lockScroll) this._unlockScroll(); |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.fm-overlay { |
|||
position: fixed; |
|||
left: 0; |
|||
right: 0; |
|||
top: 0; |
|||
bottom: 0; |
|||
background: rgba(0, 0, 0, 0.35); |
|||
z-index: 999; |
|||
} |
|||
|
|||
.fm-wrap { |
|||
position: fixed; |
|||
left: 0; |
|||
right: 0; |
|||
top: 0; |
|||
bottom: 0; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
z-index: 1000; |
|||
padding: 40rpx; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.fm-card { |
|||
background: #fff; |
|||
border-radius: 24rpx; |
|||
padding: 40rpx; |
|||
box-sizing: border-box; |
|||
text-align: center; |
|||
box-shadow: 0 6rpx 30rpx rgba(0, 0, 0, 0.12); |
|||
} |
|||
|
|||
.fm-title { |
|||
display: block; |
|||
font-size: 48rpx; |
|||
font-weight: 700; |
|||
color: #333333; |
|||
margin-bottom: 40rpx; |
|||
} |
|||
|
|||
.fm-icon-wrap { |
|||
width: 200rpx; |
|||
height: 200rpx; |
|||
margin: 0 auto 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.fm-icon-img { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
|
|||
.fm-sub { |
|||
display: block; |
|||
color: #666666; |
|||
font-size: 32rpx; |
|||
margin-bottom: 40rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.fm-btn-wrap { |
|||
display: flex; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.fm-btn { |
|||
width: 220rpx; |
|||
height: 60rpx; |
|||
border-radius: 32rpx; |
|||
background: #000; |
|||
color: #ffffff; |
|||
font-weight: 700; |
|||
font-size: 32rpx; |
|||
font-style: normal; |
|||
border: none; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
</style> |
|||
@ -1,190 +1,220 @@ |
|||
<!-- @format --> |
|||
|
|||
<template> |
|||
<view class="index-card"> |
|||
<view class="card-header"> |
|||
<view class="flag-container"> |
|||
<image :src="flagIcon" class="flag-icon" mode="aspectFit"></image> |
|||
</view> |
|||
<text class="index-name">{{ stockName }}</text> |
|||
</view> |
|||
|
|||
<view class="price-info"> |
|||
<text class="current-price" :style="{ color: priceColor }">{{ currentPrice }}</text> |
|||
<view class="change-info"> |
|||
<text class="change-amount" :style="{ color: priceColor }">{{ changeAmount }}</text> |
|||
<text class="change-percent" :style="{ color: priceColor }">{{ changePercent }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="chart-container"> |
|||
<view class="mini-chart" :style="{ backgroundColor: chartBgColor }"> |
|||
<!-- 这里可以放置实际的图表组件,目前用简单的波浪线表示 --> |
|||
<view class="chart-line" :style="{ borderColor: priceColor }"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<view class="index-card"> |
|||
<view class="card-header"> |
|||
<view class="flag-container"> |
|||
<image :src="getMarketFlag(market)" class="flag-icon" mode="aspectFit"></image> |
|||
</view> |
|||
<text class="index-name">{{ stockName }}</text> |
|||
</view> |
|||
|
|||
<view class="price-info"> |
|||
<text class="current-price" :style="{ color: priceColor }">{{ currentPrice }}</text> |
|||
<view class="change-info"> |
|||
<text class="change-amount" :style="{ color: priceColor }">{{ changeAmount }}</text> |
|||
<text class="change-percent" :style="{ color: priceColor }">{{ changePercent }}</text> |
|||
</view> |
|||
</view> |
|||
|
|||
<view class="chart-container"> |
|||
<view class="mini-chart" :style="{ backgroundColor: chartBgColor }"> |
|||
<!-- 这里可以放置实际的图表组件,目前用简单的波浪线表示 --> |
|||
<view class="chart-line" :style="{ borderColor: priceColor }"></view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script setup> |
|||
import { computed } from 'vue' |
|||
import { computed } from "vue"; |
|||
|
|||
// 定义组件属性 |
|||
const props = defineProps({ |
|||
// 国旗图标路径 |
|||
flagIcon: { |
|||
type: String, |
|||
required: true |
|||
}, |
|||
// 指数名称 |
|||
stockName: { |
|||
type: String, |
|||
required: true |
|||
}, |
|||
// 当前价格 |
|||
currentPrice: { |
|||
type: [String, Number], |
|||
required: true |
|||
}, |
|||
// 涨跌金额 |
|||
changeAmount: { |
|||
type: [String, Number], |
|||
required: true |
|||
}, |
|||
// 涨跌幅 |
|||
changePercent: { |
|||
type: [String, Number], |
|||
required: true |
|||
}, |
|||
// 是否上涨 |
|||
isRising: { |
|||
type: Boolean, |
|||
default: true |
|||
} |
|||
}) |
|||
// 国旗图标路径 |
|||
market: { |
|||
type: String, |
|||
required: true, |
|||
}, |
|||
// 指数名称 |
|||
stockName: { |
|||
type: String, |
|||
required: true, |
|||
}, |
|||
// 当前价格 |
|||
currentPrice: { |
|||
type: [String, Number], |
|||
required: true, |
|||
}, |
|||
// 涨跌金额 |
|||
changeAmount: { |
|||
type: [String, Number], |
|||
required: true, |
|||
}, |
|||
// 涨跌幅 |
|||
changePercent: { |
|||
type: [String, Number], |
|||
required: true, |
|||
}, |
|||
// 是否上涨 |
|||
isRising: { |
|||
type: Boolean, |
|||
default: true, |
|||
}, |
|||
}); |
|||
|
|||
const getMarketFlag = (market) => { |
|||
console.log("market", market); |
|||
let imagePath; |
|||
|
|||
if (market === "cn") { |
|||
imagePath = "/static/marketSituation-image/country-flag/cn.png"; |
|||
} else if (market === "hk") { |
|||
imagePath = "/static/marketSituation-image/country-flag/hk.png"; |
|||
} else if (market === "can") { |
|||
imagePath = "/static/marketSituation-image/country-flag/can.png"; |
|||
} else if (market === "my") { |
|||
imagePath = "/static/marketSituation-image/country-flag/my.png"; |
|||
} else if (market === "sg") { |
|||
imagePath = "/static/marketSituation-image/country-flag/sg.png"; |
|||
} else if (market === "th") { |
|||
imagePath = "/static/marketSituation-image/country-flag/th.png"; |
|||
} else if (market === "vi") { |
|||
imagePath = "/static/marketSituation-image/country-flag/vi.png"; |
|||
} else if (market === "us" || market === "usa") { |
|||
imagePath = "/static/marketSituation-image/country-flag/us.png"; |
|||
} else { |
|||
imagePath = "/static/marketSituation-image/country-flag/global.png"; |
|||
} |
|||
|
|||
console.log("返回的图片路径:", imagePath); |
|||
return imagePath; |
|||
}; |
|||
|
|||
// 计算价格颜色 |
|||
const priceColor = computed(() => { |
|||
return props.isRising ? '#00C853' : '#FF1744' |
|||
}) |
|||
return props.isRising ? "#00C853" : "#FF1744"; |
|||
}); |
|||
|
|||
// 计算图表背景色 |
|||
const chartBgColor = computed(() => { |
|||
return props.isRising ? '#E8F5E8' : '#FFEBEE' |
|||
}) |
|||
return props.isRising ? "#E8F5E8" : "#FFEBEE"; |
|||
}); |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.index-card { |
|||
background-color: #ffffff; |
|||
border-radius: 12rpx; |
|||
padding: 20rpx; |
|||
margin: 16rpx; |
|||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
|||
border: 1rpx solid #f0f0f0; |
|||
background-color: #ffffff; |
|||
border-radius: 12rpx; |
|||
padding: 20rpx; |
|||
margin: 16rpx; |
|||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
|||
border: 1rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.card-header { |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 16rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.flag-container { |
|||
width: 48rpx; |
|||
height: 32rpx; |
|||
margin-right: 12rpx; |
|||
border-radius: 4rpx; |
|||
overflow: hidden; |
|||
width: 48rpx; |
|||
height: 32rpx; |
|||
margin-right: 12rpx; |
|||
border-radius: 4rpx; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.flag-icon { |
|||
width: 100%; |
|||
height: 100%; |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
|
|||
.index-name { |
|||
font-size: 28rpx; |
|||
font-weight: 500; |
|||
color: #333333; |
|||
flex: 1; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
font-size: 28rpx; |
|||
font-weight: 500; |
|||
color: #333333; |
|||
flex: 1; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
.price-info { |
|||
margin-bottom: 20rpx; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.current-price { |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
font-size: 36rpx; |
|||
font-weight: bold; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.change-info { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 16rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 16rpx; |
|||
} |
|||
|
|||
.change-amount { |
|||
font-size: 24rpx; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.change-percent { |
|||
font-size: 24rpx; |
|||
font-weight: 500; |
|||
font-size: 24rpx; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.chart-container { |
|||
height: 80rpx; |
|||
border-radius: 8rpx; |
|||
overflow: hidden; |
|||
height: 80rpx; |
|||
border-radius: 8rpx; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.mini-chart { |
|||
width: 100%; |
|||
height: 100%; |
|||
position: relative; |
|||
border-radius: 8rpx; |
|||
width: 100%; |
|||
height: 100%; |
|||
position: relative; |
|||
border-radius: 8rpx; |
|||
} |
|||
|
|||
.chart-line { |
|||
position: absolute; |
|||
bottom: 20rpx; |
|||
left: 10rpx; |
|||
right: 10rpx; |
|||
height: 2rpx; |
|||
border-top: 2rpx solid; |
|||
border-style: solid; |
|||
position: absolute; |
|||
bottom: 20rpx; |
|||
left: 10rpx; |
|||
right: 10rpx; |
|||
height: 2rpx; |
|||
border-top: 2rpx solid; |
|||
border-style: solid; |
|||
} |
|||
|
|||
/* 添加一些波浪效果 */ |
|||
.chart-line::before { |
|||
content: ''; |
|||
position: absolute; |
|||
top: -10rpx; |
|||
left: 20%; |
|||
width: 20rpx; |
|||
height: 20rpx; |
|||
border: 2rpx solid; |
|||
border-color: inherit; |
|||
border-radius: 50%; |
|||
background: transparent; |
|||
content: ""; |
|||
position: absolute; |
|||
top: -10rpx; |
|||
left: 20%; |
|||
width: 20rpx; |
|||
height: 20rpx; |
|||
border: 2rpx solid; |
|||
border-color: inherit; |
|||
border-radius: 50%; |
|||
background: transparent; |
|||
} |
|||
|
|||
.chart-line::after { |
|||
content: ''; |
|||
position: absolute; |
|||
top: -6rpx; |
|||
right: 30%; |
|||
width: 12rpx; |
|||
height: 12rpx; |
|||
border: 2rpx solid; |
|||
border-color: inherit; |
|||
border-radius: 50%; |
|||
background: transparent; |
|||
content: ""; |
|||
position: absolute; |
|||
top: -6rpx; |
|||
right: 30%; |
|||
width: 12rpx; |
|||
height: 12rpx; |
|||
border: 2rpx solid; |
|||
border-color: inherit; |
|||
border-radius: 50%; |
|||
background: transparent; |
|||
} |
|||
</style> |
|||
</style> |
|||
@ -0,0 +1,687 @@ |
|||
<template> |
|||
<view class="main"> |
|||
<view class="top" :style="{height:iSMT+'px'}"></view> |
|||
|
|||
<!-- 头部导航 --> |
|||
<view class="header"> |
|||
<view class="back-icon"> |
|||
<image @click="onBack" src="/static/customer-service-platform/cs-platform-back.png" |
|||
class="header-icon-image"></image> |
|||
</view> |
|||
<view class="title">{{headerTitle}}</view> |
|||
<view class="notification-icon"> |
|||
<image src="/static/customer-service-platform/message.png" class="header-icon-image"></image> |
|||
</view> |
|||
</view> |
|||
<!-- 内容区域 - 使用滚动视图 --> |
|||
<scroll-view scroll-y class="content-container"> |
|||
<view class="content-header"> |
|||
<view class="content-header-area"> |
|||
<view class="logo"> |
|||
<image mode="aspectFit" src="/static/customer-service-platform/ellipse-dc-img.png"></image> |
|||
</view> |
|||
<view class="greeting"> |
|||
<text class="greet-title">嗨,我能为你做点什么?</text> |
|||
<text class="greet-sub">DeepChart随时为您提供服务</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<!--猜你想问卡片部分--> |
|||
<view class="card"> |
|||
<view class="suggest-header"> |
|||
<text class="suggest-title">猜你想问</text> |
|||
<view class="swap" @click="getQuestionList()"> |
|||
<image class="swap-icon" src="/static/customer-service-platform/refresh-icon.png"></image> |
|||
<text class="swap-title">换一换</text> |
|||
</view> |
|||
</view> |
|||
<view class="card-line"></view> |
|||
<view class="suggest-list"> |
|||
<view class="suggest-item" v-for="(q, idx) in showQuestions" :key="idx" @click="onQuestionClick(q)"> |
|||
<view class="left"> |
|||
<view :class="['num', 'num-' + ((idx % 5) + 1)]">{{ idx + 1 }}</view> |
|||
<text class="q-text">{{ q }}</text> |
|||
</view> |
|||
<view class="right"> |
|||
<text class="arrow">›</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
<!--反馈卡片部分--> |
|||
<text class="feedback-card-title">反馈中心</text> |
|||
<view class="card"> |
|||
<view class="suggest-header"> |
|||
<text class="feedback-title">填写反馈内容</text> |
|||
</view> |
|||
<view class="card-line"></view> |
|||
<textarea class="feedback-input" placeholder="请描述您想反馈的内容 最多可输入200字" maxlength="200" |
|||
v-model="feedbackText" /> |
|||
<view class="meta-row"> |
|||
<text class="char-count">{{ feedbackText.length }}/200</text> |
|||
</view> |
|||
<view class="suggest-header"> |
|||
<text class="upload-img-tip">上传图片</text> |
|||
</view> |
|||
<view class="upload-row"> |
|||
<view class="img-slot" v-for="(img, index) in images" :key="index"> |
|||
<image :src="img" mode="scaleToFill" class="slot-img" /> |
|||
<button v-if="img" class="remove" @click="removeImage(index)">×</button> |
|||
</view> |
|||
<view class="img-slot" v-if="images.length < 3"> |
|||
<view class="slot-empty" @click="chooseImage()"> |
|||
<image src="/static/customer-service-platform/camera.png" class="camera-icon" /> |
|||
</view> |
|||
</view> |
|||
<text class="tip-text" v-if="images.length === 0">最多添加3张图片</text> |
|||
</view> |
|||
|
|||
<button class="feedback-btn" @click="onSumbitFeedback()">提交</button> |
|||
<feedback-modal ref="feedback" @confirm="onConfirm" /> |
|||
</view> |
|||
<!--历史反馈卡片部分--> |
|||
<view class="card"> |
|||
<text class="feedback-title">历史反馈内容</text> |
|||
<view class="card-line"></view> |
|||
<button class="feedback-btn" @click="viewHistory">查看</button> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { useUserStore } from "../../stores/modules/userInfo.js" |
|||
import { |
|||
getQuestionApi, |
|||
addFeedbackRecordApi, |
|||
uploadImageApi |
|||
} from "../../api/customerServicePlatform/customerServicePlatform"; |
|||
|
|||
import FeedbackModal from '@/components/FeedbackModal.vue' |
|||
export default { |
|||
components: { |
|||
FeedbackModal |
|||
}, |
|||
data() { |
|||
return { |
|||
headerTitle: '智能客服中台', |
|||
iSMT: 0, |
|||
questions: [ |
|||
"DeepChart 有免费功能和付费功能的区分吗?具体有哪些?", |
|||
"如何参与平台的用户反馈活动?反馈的问题会被采纳吗?", |
|||
"我的自选股最多能添加多少只?能否按市场分类管理?", |
|||
"注册时必须提供手机号 / 邮箱吗?能否匿名使用?", |
|||
"忘记登录密码了,如何找回?", |
|||
'如何注册账号?', |
|||
'为什么无法注册账户?' |
|||
], |
|||
showQuestions: [], |
|||
feedbackText: '', |
|||
images: [], |
|||
token:'' |
|||
} |
|||
}, |
|||
mounted() { |
|||
// 状态栏高度 |
|||
this.iSMT = uni.getSystemInfoSync().statusBarHeight; |
|||
this.getQuestionList() |
|||
const memberStore = useUserStore() |
|||
this.token = memberStore.userInfo?.token |
|||
}, |
|||
methods: { |
|||
onSuccess() { |
|||
this.$refs.feedback.show({ |
|||
status: 'success', |
|||
title: '提交成功', |
|||
subtitle: '— 感谢您的反馈 —', |
|||
buttonText: '确定', |
|||
width: '80%', |
|||
}); |
|||
}, |
|||
onFail() { |
|||
this.$refs.feedback.show({ |
|||
status: 'fail', |
|||
title: '提交失败', |
|||
subtitle: '— 请重新提交 —', |
|||
buttonText: '确定', |
|||
width: '80%', |
|||
}); |
|||
}, |
|||
onBack() { |
|||
if (typeof uni !== 'undefined') uni.navigateBack(); |
|||
}, |
|||
async getQuestionList() { |
|||
const res = await getQuestionApi() |
|||
console.log(res) |
|||
if (res.code == 200) { |
|||
this.showQuestions = res.data |
|||
} |
|||
|
|||
}, |
|||
onQuestionClick(q) { |
|||
if (typeof uni !== 'undefined') uni.navigateTo({ |
|||
url: `/pages/customerServicePlatform/questionDetail?question=${encodeURIComponent(q)}` |
|||
}); |
|||
}, |
|||
chooseImage() { |
|||
const that = this; |
|||
if (typeof uni === 'undefined' || !uni.chooseImage) return; |
|||
|
|||
const remain = 3 - (that.images ? that.images.length : 0); |
|||
if (remain <= 0) { |
|||
if (typeof uni !== 'undefined') uni.showToast({ |
|||
title: '最多只能上传3张', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
|
|||
uni.chooseImage({ |
|||
count: remain, |
|||
sizeType: ['original', 'compressed'], |
|||
sourceType: ['album', 'camera'], |
|||
success(res) { |
|||
const paths = res.tempFilePaths || (res.tempFiles && res.tempFiles.map(f => f.path)) || []; |
|||
for (let p of paths) { |
|||
if (that.images.length < 3) { |
|||
that.images.push(p); |
|||
} |
|||
} |
|||
}, |
|||
fail(err) { |
|||
uni.showToast({ |
|||
title: `选择图片失败`, |
|||
icon: 'none' |
|||
}); |
|||
} |
|||
}); |
|||
}, |
|||
|
|||
removeImage(index) { |
|||
// 删除并保持响应 |
|||
this.images.splice(index, 1); |
|||
}, |
|||
async onSumbitFeedback() { |
|||
if (!this.feedbackText.trim()) { |
|||
if (typeof uni !== 'undefined') uni.showToast({ |
|||
title: '请填写反馈内容', |
|||
icon: 'none' |
|||
}); |
|||
return; |
|||
} |
|||
if (typeof uni !== 'undefined') uni.showLoading({ |
|||
title: '提交中...' |
|||
}); |
|||
try { |
|||
let uploadedImages = []; |
|||
let imgFlag = true |
|||
for (let i = 0; i < this.images.length; i++) { |
|||
const f = this.images[i]; |
|||
await new Promise((resolve, reject) => { |
|||
uni.getImageInfo({ |
|||
src: f, |
|||
success: () => resolve(), |
|||
fail: reject |
|||
}); |
|||
}); |
|||
const uploadRes = await new Promise((resolve, reject) => { |
|||
uni.uploadFile({ |
|||
url: 'http://39.101.133.168:8828/hljw/api/aws/upload', |
|||
filePath: f, |
|||
name: 'file', |
|||
formData: { |
|||
dir: 'deepchart' |
|||
}, |
|||
success: (res) => { |
|||
try { |
|||
const data = JSON.parse(res.data); |
|||
if (data.code === 200) { |
|||
uploadedImages.push(data.data.url); |
|||
resolve(data); |
|||
} else { |
|||
uni.showToast({ |
|||
title: `第${i + 1}张图片上传失败`, |
|||
icon: 'none' |
|||
}); |
|||
imgFlag = false; |
|||
reject(data); |
|||
} |
|||
} catch (err) { |
|||
imgFlag = false; |
|||
reject(err); |
|||
} |
|||
}, |
|||
fail: (err) => { |
|||
imgFlag = false; |
|||
uni.showToast({ |
|||
title: `第${i + 1}张图片上传失败`, |
|||
icon: 'none' |
|||
}); |
|||
reject(err); |
|||
} |
|||
}); |
|||
}); |
|||
} |
|||
if (!imgFlag) { |
|||
return |
|||
} |
|||
const [image1 = '', image2 = '', image3 = ''] = uploadedImages; |
|||
const res = await addFeedbackRecordApi({ |
|||
token: this.token, |
|||
content: this.feedbackText, |
|||
image1, |
|||
image2, |
|||
image3 |
|||
}) |
|||
if (res.code == 200) { |
|||
this.onSuccess() |
|||
} else { |
|||
this.onFail() |
|||
} |
|||
} catch { |
|||
this.onFail() |
|||
} finally { |
|||
uni.hideLoading(); |
|||
this.feedbackText = ''; |
|||
this.images = []; |
|||
} |
|||
}, |
|||
viewHistory() { |
|||
// 跳转到历史页 |
|||
if (typeof uni !== 'undefined') uni.navigateTo({ |
|||
url: '/pages/customerServicePlatform/historyRecord' |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.main { |
|||
display: flex; |
|||
flex-direction: column; |
|||
height: 100vh; |
|||
background-color: #ffffff; |
|||
} |
|||
|
|||
.header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 20rpx 30rpx; |
|||
background-color: #ffffff; |
|||
} |
|||
|
|||
.title { |
|||
color: #000000; |
|||
text-align: center; |
|||
font-size: 32rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
.back-icon, |
|||
.notification-icon { |
|||
width: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.header-icon-image { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
object-fit: contain; |
|||
} |
|||
|
|||
.content-container { |
|||
padding: 20rpx; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
overflow-x: hidden; |
|||
} |
|||
|
|||
|
|||
.content-header { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
gap: 24rpx; |
|||
padding: 0 60rpx; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
height: 188rpx; |
|||
} |
|||
|
|||
.content-header-area { |
|||
display: flex; |
|||
gap: 20rpx; |
|||
} |
|||
|
|||
.logo { |
|||
width: 120rpx; |
|||
height: 120rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
flex: 0 0 112rpx; |
|||
} |
|||
|
|||
.greeting { |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
.greet-title { |
|||
color: #000; |
|||
font-size: 40rpx; |
|||
font-style: normal; |
|||
font-weight: 500; |
|||
line-height: normal; |
|||
margin: 0; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.greet-sub { |
|||
color: #838383; |
|||
font-size: 28rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: normal; |
|||
margin-top: 12rpx; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.card { |
|||
width: 90%; |
|||
margin: 0 auto; |
|||
border-radius: 16rpx; |
|||
padding: 20rpx 40rpx; |
|||
box-sizing: border-box; |
|||
border-radius: 12rpx; |
|||
border: 4rpx solid #FCC8D4; |
|||
background: linear-gradient(180deg, #FCC8D3 0%, #FEF0F3 30%, #FFF 100%); |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.suggest-header { |
|||
width: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
.suggest-title { |
|||
color: #000000; |
|||
font-size: 32rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: normal; |
|||
} |
|||
|
|||
.swap { |
|||
display: flex; |
|||
align-items: center; |
|||
transition: transform 0.1s ease, background-color 0.1s ease; |
|||
} |
|||
|
|||
.swap:active { |
|||
transform: scale(0.95); |
|||
} |
|||
|
|||
.swap-icon { |
|||
width: 30rpx; |
|||
height: 30rpx; |
|||
} |
|||
|
|||
.swap-title { |
|||
padding-left: 8rpx; |
|||
color: #000000; |
|||
font-size: 24rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: normal; |
|||
} |
|||
|
|||
.suggest-list { |
|||
margin-top: 20rpx; |
|||
} |
|||
|
|||
.card-line { |
|||
margin-top: 20rpx; |
|||
width: 100%; |
|||
height: 2rpx; |
|||
border-radius: 2rpx; |
|||
background: #FFF; |
|||
} |
|||
|
|||
.suggest-item { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding-top: 10rpx; |
|||
border-radius: 12rpx; |
|||
margin-bottom: 20rpx; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
} |
|||
|
|||
.left { |
|||
width: 90%; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.num { |
|||
font-size: 40rpx; |
|||
font-style: normal; |
|||
font-weight: 700; |
|||
line-height: normal; |
|||
} |
|||
|
|||
.num-1 { |
|||
color: #df5662; |
|||
} |
|||
|
|||
.num-2 { |
|||
color: #ec6d4f; |
|||
} |
|||
|
|||
.num-3 { |
|||
color: #f3ba40; |
|||
} |
|||
|
|||
.num-4 { |
|||
color: #9296a0; |
|||
} |
|||
|
|||
.num-5 { |
|||
color: #9296a0; |
|||
} |
|||
|
|||
.q-text { |
|||
padding-left: 14rpx; |
|||
display: block; |
|||
color: #333; |
|||
font-size: 28rpx; |
|||
white-space: nowrap; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
} |
|||
|
|||
.right { |
|||
width: 48rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: flex-end; |
|||
} |
|||
|
|||
.arrow { |
|||
color: #cfcfcf; |
|||
font-size: 36rpx; |
|||
} |
|||
|
|||
.suggest-item:active { |
|||
background: rgba(255, 77, 128, 0.06); |
|||
} |
|||
|
|||
.feedback-card-title { |
|||
display: flex; |
|||
justify-content: center; |
|||
color: #000000; |
|||
font-size: 32rpx; |
|||
font-weight: 700; |
|||
line-height: 40rpx; |
|||
width: 100%; |
|||
margin-bottom: 20rpx; |
|||
} |
|||
|
|||
.feedback-title { |
|||
color: #000000; |
|||
font-size: 32rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: normal; |
|||
} |
|||
|
|||
.feedback-input { |
|||
width: 100%; |
|||
display: flex; |
|||
padding: 20rpx; |
|||
flex-direction: column; |
|||
box-sizing: border-box; |
|||
align-items: flex-start; |
|||
gap: 12rpx; |
|||
align-self: stretch; |
|||
border-radius: 12rpx; |
|||
border: 2rpx solid #F0F1F1; |
|||
margin-top: 20rpx; |
|||
display: flex; |
|||
background: #FFF; |
|||
color: #8a8a8a; |
|||
font-size: 24rpx; |
|||
font-weight: 700; |
|||
line-height: normal; |
|||
} |
|||
|
|||
.meta-row { |
|||
display: flex; |
|||
justify-content: flex-end; |
|||
margin-top: 12rpx; |
|||
} |
|||
|
|||
.char-count { |
|||
color: #999 |
|||
} |
|||
|
|||
.upload-img-tip { |
|||
color: #000000; |
|||
font-size: 24rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: normal; |
|||
} |
|||
|
|||
.upload-row { |
|||
display: flex; |
|||
justify-content: flex-start; |
|||
align-items: flex-start; |
|||
align-content: flex-start; |
|||
flex-wrap: wrap; |
|||
gap: 20rpx; |
|||
margin-top: 20rpx; |
|||
width: 100%; |
|||
} |
|||
|
|||
.img-slot { |
|||
width: calc((100% - 2 * 30rpx) / 3); |
|||
aspect-ratio: 1 / 1; |
|||
border-radius: 6px; |
|||
border: 1px solid #F0F1F1; |
|||
background: #FFF; |
|||
position: relative; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
overflow: hidden; |
|||
transition: transform 0.2s ease; |
|||
} |
|||
|
|||
.slot-empty { |
|||
width: 100%; |
|||
height: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.camera-icon { |
|||
width: 34rpx; |
|||
height: 34rpx; |
|||
} |
|||
|
|||
.slot-img { |
|||
width: 100%; |
|||
height: 100%; |
|||
border-radius: 16rpx; |
|||
} |
|||
|
|||
.remove { |
|||
position: absolute; |
|||
right: 6rpx; |
|||
top: 6rpx; |
|||
border-radius: 50%; |
|||
background: #fd5c58; |
|||
padding: 0; |
|||
color: #fff; |
|||
width: 36rpx; |
|||
height: 36rpx; |
|||
font-size: 28rpx; |
|||
line-height: 36rpx; |
|||
text-align: center; |
|||
border: none; |
|||
outline: none; |
|||
cursor: pointer; |
|||
} |
|||
|
|||
.remove:active { |
|||
background: rgba(0, 0, 0, 0.75); |
|||
} |
|||
|
|||
.image-upload-tip { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.tip-text { |
|||
color: #999999; |
|||
font-size: 24rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: 40rpx; |
|||
padding-top: 64rpx; |
|||
} |
|||
|
|||
.feedback-btn { |
|||
margin-top: 24rpx; |
|||
width: 180rpx; |
|||
height: 60rpx; |
|||
aspect-ratio: 89/30; |
|||
border-radius: 30rpx; |
|||
background: #090A08; |
|||
color: #ffffff; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
font-size: 32rpx; |
|||
font-style: normal; |
|||
font-weight: 700; |
|||
line-height: normal; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,358 @@ |
|||
<template> |
|||
<view class="main"> |
|||
<view class="top" :style="{height:iSMT+'px'}"></view> |
|||
<!-- 头部导航 --> |
|||
<view class="header"> |
|||
<view class="back-icon"> |
|||
<image @click="goBack()" src="/static/customer-service-platform/cs-platform-back.png" |
|||
class="header-icon-image"></image> |
|||
</view> |
|||
<view class="title">智能客服中台</view> |
|||
<view class="notification-icon"> |
|||
<image src="/static/customer-service-platform/message.png" class="header-icon-image"></image> |
|||
</view> |
|||
</view> |
|||
<!-- 内容区域 - 使用滚动视图 --> |
|||
<scroll-view scroll-y class="content-container"> |
|||
<view class="list-wrapper" v-if="historyList.length > 0"> |
|||
<view class="content-header"> |
|||
<text class="content-title">历史反馈内容</text> |
|||
</view> |
|||
<view class="card-line"></view> |
|||
<view v-for="(item, idx) in historyList" :key="item.id" class="history-item card"> |
|||
<view class="item-line" v-if="idx != 0"></view> |
|||
<view class="item-head"> |
|||
<view class="dot-outer"> |
|||
<view class="dot-inner"></view> |
|||
</view> |
|||
|
|||
<text class="feedback-time">{{ formatTime(item.createdAt) }}</text> |
|||
<text class="feedback-status"> |
|||
{{ statusText }} |
|||
<image class="smile-icon" src="/static/customer-service-platform/smile-icon.png"></image> |
|||
</text> |
|||
</view> |
|||
|
|||
<view class="content-box"> |
|||
<text class="content-text">{{ item.content }}</text> |
|||
<text class="count">{{ item.content.length }}/200</text> |
|||
</view> |
|||
<view v-if="item.images && item.images.length" class="thumb-row"> |
|||
<view v-for="(img, i) in item.images" :key="i" class="thumb-slot" |
|||
@click="previewImage(item.images, i)"> |
|||
<image :src="img" mode="scaleToFill" class="thumb-img" /> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 如果没有历史,显示空态 --> |
|||
<view v-if="historyList.length === 0" class="empty"> |
|||
<image mode="aspectFit" class="empty-img" src="/static/customer-service-platform/empty-content.png"> |
|||
</image> |
|||
<text class="empty-tip">暂无内容~</text> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { useUserStore } from "../../stores/modules/userInfo.js" |
|||
import { |
|||
getFeedbackRecordsApi, |
|||
} from "../../api/customerServicePlatform/customerServicePlatform"; |
|||
export default { |
|||
data() { |
|||
return { |
|||
iSMT: 0, |
|||
statusText: '反馈成功', |
|||
historyList: [], |
|||
token:'' |
|||
}; |
|||
}, |
|||
mounted() { |
|||
this.iSMT = uni.getSystemInfoSync().statusBarHeight; |
|||
this.loadHistoryList() |
|||
const memberStore = useUserStore() |
|||
this.token = memberStore.userInfo?.token |
|||
}, |
|||
methods: { |
|||
formatTime(str) { |
|||
if (!str) return ''; |
|||
const d = new Date(str); |
|||
const yyyy = d.getFullYear(); |
|||
const mm = String(d.getMonth() + 1).padStart(2, '0'); |
|||
const dd = String(d.getDate()).padStart(2, '0'); |
|||
const hh = String(d.getHours()).padStart(2, '0'); |
|||
const mi = String(d.getMinutes()).padStart(2, '0'); |
|||
return `${yyyy}-${mm}-${dd} ${hh}:${mi}`; |
|||
}, |
|||
goBack() { |
|||
if (typeof uni !== 'undefined' && uni.navigateBack) { |
|||
uni.navigateBack(); |
|||
} else { |
|||
window.history.back(); |
|||
} |
|||
}, |
|||
async loadHistoryList() { |
|||
const res = await getFeedbackRecordsApi({ |
|||
token: this.token |
|||
}) |
|||
console.log(res) |
|||
if (res.code == 200) { |
|||
this.historyList = res.data.map(item => { |
|||
const images = [item.image1, item.image2, item.image3].filter(img => !!img) |
|||
return { |
|||
id: item.id, |
|||
createdAt: item.createdAt, |
|||
content: item.content, |
|||
images, |
|||
dccode: item.dccode |
|||
} |
|||
}) |
|||
} |
|||
}, |
|||
previewImage(list, index) { |
|||
if (typeof uni !== 'undefined' && uni.previewImage) { |
|||
uni.previewImage({ |
|||
current: list[index], |
|||
urls: list |
|||
}); |
|||
} else { |
|||
window.open(list[index], '_blank'); |
|||
} |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.main { |
|||
display: flex; |
|||
flex-direction: column; |
|||
height: 100vh; |
|||
background-color: #ffffff; |
|||
} |
|||
|
|||
.header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 20rpx 30rpx; |
|||
background-color: #ffffff; |
|||
} |
|||
|
|||
.title { |
|||
color: #000000; |
|||
text-align: center; |
|||
font-size: 32rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
.back-icon, |
|||
.notification-icon { |
|||
width: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.header-icon-image { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
object-fit: contain; |
|||
} |
|||
|
|||
.content-container { |
|||
padding: 20rpx; |
|||
padding-top: 0; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
overflow-x: hidden; |
|||
} |
|||
|
|||
/* 列表包装器,居中卡片 */ |
|||
.list-wrapper { |
|||
width: 90%; |
|||
margin: 0 auto; |
|||
padding: 20rpx 40rpx; |
|||
flex-direction: column; |
|||
align-items: center; |
|||
gap: 20rpx; |
|||
box-sizing: border-box; |
|||
border-radius: 12rpx; |
|||
border: 4rpx solid #FCC8D4; |
|||
background: linear-gradient(180deg, #FCC8D3 0%, #FEF0F3 30%, #FFF 100%); |
|||
} |
|||
|
|||
.content-header { |
|||
width: 100%; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: space-between; |
|||
} |
|||
|
|||
.content-title { |
|||
color: #000000; |
|||
font-size: 32rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: normal; |
|||
} |
|||
|
|||
.card-line { |
|||
margin-top: 20rpx; |
|||
width: 100%; |
|||
height: 2rpx; |
|||
border-radius: 2rpx; |
|||
background: #FFF; |
|||
} |
|||
|
|||
/* 每一条历史卡片 */ |
|||
.history-item { |
|||
border-radius: 10rpx; |
|||
padding: 20rpx; |
|||
margin-bottom: 20rpx; |
|||
box-sizing: border-box; |
|||
box-shadow: 0 4rpx 12rpx rgba(255, 77, 128, 0.06); |
|||
} |
|||
.item-line{ |
|||
margin-bottom: 20rpx; |
|||
width: 100%; |
|||
height: 2rpx; |
|||
border-radius: 2rpx; |
|||
background: #D9D9D9; |
|||
} |
|||
.item-head { |
|||
display: flex; |
|||
align-items: center; |
|||
gap: 12rpx; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.dot-outer { |
|||
width: 24rpx; |
|||
height: 24rpx; |
|||
border-radius: 50%; |
|||
background: rgba(255, 214, 230, 0.5); |
|||
/*粉色外圈*/ |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
box-shadow: 0 0 0 4rpx #ffffff; |
|||
/* 最外层白色 */ |
|||
} |
|||
|
|||
.dot-inner { |
|||
width: 14rpx; |
|||
height: 14rpx; |
|||
border-radius: 50%; |
|||
background: #ff4150; |
|||
/* 中心红色 */ |
|||
} |
|||
|
|||
|
|||
.feedback-time { |
|||
color: #000000; |
|||
flex: 1; |
|||
font-size: 22rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: normal; |
|||
padding-left: 26rpx; |
|||
} |
|||
|
|||
.feedback-status { |
|||
color: #ff4150; |
|||
font-size: 12rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: normal; |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.smile-icon { |
|||
width: 32rpx; |
|||
height: 32rpx; |
|||
} |
|||
|
|||
/* 内容框 */ |
|||
.content-box { |
|||
border: 2rpx solid #f0e6ea; |
|||
background: #fff; |
|||
border-radius: 8rpx; |
|||
padding: 18rpx; |
|||
position: relative; |
|||
box-sizing: border-box; |
|||
min-height: 160rpx; |
|||
} |
|||
|
|||
.content-text { |
|||
display: block; |
|||
white-space: pre-wrap; |
|||
word-break: break-word; |
|||
color: #8a8a8a; |
|||
font-size: 24rpx; |
|||
font-style: normal; |
|||
font-weight: 700; |
|||
line-height: normal; |
|||
padding-bottom: 26rpx; |
|||
} |
|||
|
|||
.count { |
|||
position: absolute; |
|||
right: 14rpx; |
|||
bottom: 10rpx; |
|||
color: #000000; |
|||
font-size: 24rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: normal; |
|||
} |
|||
|
|||
.thumb-row { |
|||
display: flex; |
|||
flex-wrap: wrap; |
|||
justify-content: flex-start; |
|||
align-items: flex-start; |
|||
align-content: flex-start; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
gap: 20rpx; |
|||
margin-top: 14rpx; |
|||
background: #F9FAFE; |
|||
padding: 20rpx; |
|||
} |
|||
|
|||
.thumb-slot { |
|||
width: calc((100% - 2 * 25rpx) / 3); |
|||
aspect-ratio: 1 / 1; |
|||
border-radius: 7rpx; |
|||
border: 1.2rpx solid #F0F1F1; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.thumb-img { |
|||
width: 100%; |
|||
height: 100%; |
|||
} |
|||
|
|||
.empty { |
|||
padding: 50rpx 0; |
|||
text-align: center; |
|||
color: #afafaf; |
|||
font-size: 32rpx; |
|||
font-style: normal; |
|||
font-weight: 500; |
|||
line-height: 48rpx; |
|||
} |
|||
|
|||
.empty-img { |
|||
width: 100%; |
|||
} |
|||
</style> |
|||
@ -0,0 +1,340 @@ |
|||
<template> |
|||
<view class="main"> |
|||
<view class="top" :style="{ height: iSMT + 'px' }"></view> |
|||
|
|||
<!-- 头部导航 --> |
|||
<view class="header"> |
|||
<view class="back-icon"> |
|||
<image @click="onBack" src="/static/customer-service-platform/cs-platform-back.png" |
|||
class="header-icon-image"></image> |
|||
</view> |
|||
<view class="title">{{ headerTitle }}</view> |
|||
<view class="notification-icon"> |
|||
<image src="/static/customer-service-platform/message.png" class="header-icon-image"></image> |
|||
</view> |
|||
</view> |
|||
|
|||
<scroll-view scroll-y class="content-container"> |
|||
|
|||
<view class="content-header"> |
|||
<view class="content-header-area"> |
|||
<view class="logo"> |
|||
<image mode="aspectFit" src="/static/customer-service-platform/ellipse-dc-img.png"></image> |
|||
</view> |
|||
<view class="greeting"> |
|||
<text class="greet-title">嗨,我能为你做点什么?</text> |
|||
<text class="greet-sub">DeepChart随时为您提供服务</text> |
|||
</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 卡片部分 --> |
|||
<view class="card"> |
|||
<!-- 问题头部--> |
|||
<view class="question-header"> |
|||
<view class="question-row"> |
|||
<image class="question-avatar" src="/static/customer-service-platform/robot-head.png" |
|||
mode="aspectFill"></image> |
|||
<view class="question-title">{{ questionTitle }}</view> |
|||
</view> |
|||
</view> |
|||
|
|||
<!-- 卡片内容区--> |
|||
<view class="card-body"> |
|||
<image class="card-logo" src="/static/customer-service-platform/ellipse-dc-img.png" |
|||
mode="aspectFit"></image> |
|||
|
|||
<view class="card-text"> |
|||
<text class="card-paragraph"> |
|||
{{answerContent}} |
|||
</text> |
|||
</view> |
|||
</view> |
|||
|
|||
</view> |
|||
|
|||
<view class="login-row" v-if="showLoginRegister"> |
|||
<button class="login-btn" @click="toLogin">登录</button> |
|||
<button class="register-btn" @click="toRegistration">注册</button> |
|||
</view> |
|||
</scroll-view> |
|||
</view> |
|||
</template> |
|||
|
|||
<script> |
|||
import { useUserStore } from "../../stores/modules/userInfo.js" |
|||
import { |
|||
getAnswerApi |
|||
} from "../../api/customerServicePlatform/customerServicePlatform"; |
|||
export default { |
|||
data() { |
|||
return { |
|||
headerTitle: '智能客服中台', |
|||
iSMT: 0, |
|||
questionTitle: '', |
|||
answerContent: '正在思考...', |
|||
showLoginRegister:false, |
|||
token:'' |
|||
}; |
|||
}, |
|||
mounted() { |
|||
this.iSMT = uni.getSystemInfoSync().statusBarHeight || 0; |
|||
this.getAnswerContent() |
|||
const memberStore = useUserStore() |
|||
this.token = memberStore.userInfo?.token |
|||
}, |
|||
onLoad(options) { |
|||
if (options.question) { |
|||
this.questionTitle = decodeURIComponent(options.question); |
|||
if (this.questionTitle.includes("如何注册")) { |
|||
this.showLoginRegister = true |
|||
} else { |
|||
this.showLoginRegister = false |
|||
} |
|||
} |
|||
}, |
|||
methods: { |
|||
async getAnswerContent() { |
|||
let conversationId = ''; |
|||
try { |
|||
const cache = uni.getStorageSync('conversationId'); |
|||
if (cache) conversationId = cache; |
|||
} catch (e) { |
|||
conversationId = ''; |
|||
} |
|||
|
|||
const res = await getAnswerApi({ |
|||
question: this.questionTitle, |
|||
conversationId: conversationId, |
|||
token:this.token |
|||
}) |
|||
console.log(res) |
|||
|
|||
if (res.code == 200) { |
|||
uni.setStorageSync('conversationId', res.data.conversationId); |
|||
const answer = res.data.answer |
|||
this.answerContent = ''; |
|||
for (let i = 0; i < answer.length; i++) { |
|||
this.answerContent += answer[i]; |
|||
await this.sleepTime(150); |
|||
} |
|||
} else { |
|||
this.answerContent = '获取回答失败,请重试'; |
|||
} |
|||
}, |
|||
async sleepTime() { |
|||
const ms = Math.floor(Math.random() * (300 - 30 + 1)) + 30; |
|||
return new Promise(resolve => setTimeout(resolve, ms)); |
|||
}, |
|||
|
|||
toRegistration() { |
|||
uni.redirectTo({ |
|||
url: "/pages/start/Registration/Registration", |
|||
}); |
|||
}, |
|||
toLogin() { |
|||
uni.redirectTo({ |
|||
url: "/pages/start/login/login", |
|||
}); |
|||
}, |
|||
onBack() { |
|||
if (typeof uni !== 'undefined') uni.navigateBack(); |
|||
} |
|||
} |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.main { |
|||
display: flex; |
|||
flex-direction: column; |
|||
height: 100vh; |
|||
background-color: #ffffff; |
|||
} |
|||
|
|||
.header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 20rpx 30rpx; |
|||
background-color: #ffffff; |
|||
} |
|||
|
|||
.title { |
|||
color: #000000; |
|||
text-align: center; |
|||
font-size: 32rpx; |
|||
font-weight: 400; |
|||
} |
|||
|
|||
.back-icon, |
|||
.notification-icon { |
|||
width: 40rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
} |
|||
|
|||
.header-icon-image { |
|||
width: 40rpx; |
|||
height: 40rpx; |
|||
object-fit: contain; |
|||
} |
|||
|
|||
.content-container { |
|||
padding: 20rpx; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
overflow-x: hidden; |
|||
} |
|||
|
|||
.content-header { |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
gap: 24rpx; |
|||
padding: 0 60rpx; |
|||
width: 100%; |
|||
box-sizing: border-box; |
|||
height: 188rpx; |
|||
} |
|||
|
|||
.content-header-area { |
|||
display: flex; |
|||
gap: 20rpx; |
|||
} |
|||
|
|||
.logo { |
|||
width: 120rpx; |
|||
height: 120rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
justify-content: center; |
|||
flex: 0 0 112rpx; |
|||
} |
|||
|
|||
.greeting { |
|||
display: flex; |
|||
flex-direction: column; |
|||
justify-content: center; |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
.greet-title { |
|||
color: #000; |
|||
font-size: 40rpx; |
|||
font-style: normal; |
|||
font-weight: 500; |
|||
line-height: normal; |
|||
margin: 0; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.greet-sub { |
|||
color: #838383; |
|||
font-size: 28rpx; |
|||
font-style: normal; |
|||
font-weight: 400; |
|||
line-height: normal; |
|||
margin-top: 12rpx; |
|||
overflow: hidden; |
|||
text-overflow: ellipsis; |
|||
white-space: nowrap; |
|||
} |
|||
|
|||
.card { |
|||
width: 90%; |
|||
margin: 0 auto 20rpx; |
|||
padding: 28rpx; |
|||
box-sizing: border-box; |
|||
border-radius: 16rpx; |
|||
border: 4rpx solid #FF7C99; |
|||
background: #fff; |
|||
} |
|||
|
|||
/* 问题头部 */ |
|||
.question-header { |
|||
width: 100%; |
|||
margin-bottom: 48rpx; |
|||
} |
|||
|
|||
.question-row { |
|||
display: flex; |
|||
align-items: center; |
|||
} |
|||
|
|||
.question-avatar { |
|||
width: 52rpx; |
|||
height: 52rpx; |
|||
border-radius: 999rpx; |
|||
margin-right: 20rpx; |
|||
flex-shrink: 0; |
|||
} |
|||
|
|||
.question-title { |
|||
color: #000000; |
|||
font-size: 34rpx; |
|||
} |
|||
|
|||
/* 卡片内部布局 */ |
|||
.card-body { |
|||
display: flex; |
|||
gap: 20rpx; |
|||
align-items: flex-start; |
|||
} |
|||
|
|||
.card-logo { |
|||
width: 52rpx; |
|||
height: 52rpx; |
|||
flex: 0 0 52rpx; |
|||
border-radius: 8rpx; |
|||
} |
|||
|
|||
.card-text { |
|||
flex: 1 1 auto; |
|||
} |
|||
|
|||
.card-paragraph { |
|||
display: block; |
|||
color: #000000; |
|||
font-size: 28rpx; |
|||
margin-bottom: 14rpx; |
|||
font-style: normal; |
|||
font-weight: 500; |
|||
} |
|||
|
|||
.login-row { |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
width: 100%; |
|||
margin-top: 100rpx; |
|||
} |
|||
|
|||
.login-btn { |
|||
width: 260rpx; |
|||
height: 100rpx; |
|||
border-radius: 50rpx; |
|||
background: #F3F3F3; |
|||
color: #000000; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
font-size: 28rpx; |
|||
margin-right: 20rpx; |
|||
} |
|||
|
|||
.register-btn { |
|||
width: 260rpx; |
|||
height: 100rpx; |
|||
border-radius: 60rpx; |
|||
background: #000; |
|||
color: #ffffff; |
|||
display: flex; |
|||
justify-content: center; |
|||
align-items: center; |
|||
font-size: 28rpx; |
|||
} |
|||
</style> |
|||
@ -1,493 +1,498 @@ |
|||
<!-- @format --> |
|||
|
|||
<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> |
|||
<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 :market="countryInfo.market" :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' |
|||
import { ref, computed, onMounted } from "vue"; |
|||
import IndexCard from "../../components/IndexCard.vue"; |
|||
|
|||
// 子Tab与操作 |
|||
const marketTabs = ['全部', '美股', '纽交所', '纳斯达克'] |
|||
const activeTabIndex = ref(0) |
|||
// const marketTabs = ["全部", "美股", "纽交所", "纳斯达克"]; |
|||
const activeTabIndex = ref(0); |
|||
const switchTab = (i) => { |
|||
activeTabIndex.value = i |
|||
} |
|||
activeTabIndex.value = i; |
|||
}; |
|||
|
|||
// 今日情绪温度示例数据 |
|||
const sentimentMeters = [ |
|||
{ value: 90, label: '道琼斯', theme: 'hot' }, |
|||
{ value: 60, label: '纳斯达克', theme: 'warm' }, |
|||
{ value: 20, label: '标普500', theme: 'cool' } |
|||
] |
|||
{ 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' |
|||
} |
|||
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 |
|||
} |
|||
}) |
|||
countryId: { |
|||
type: Number, |
|||
required: true, |
|||
}, |
|||
marketTabs: { |
|||
type: Array, |
|||
}, |
|||
}); |
|||
|
|||
// 国家/地区信息映射 |
|||
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 } |
|||
] |
|||
} |
|||
} |
|||
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: [] |
|||
} |
|||
}) |
|||
return ( |
|||
countryInfoMap[props.countryId] || { |
|||
name: "未知地区", |
|||
flag: "🌍", |
|||
isMarketOpen: false, |
|||
mainIndices: [], |
|||
hotStocks: [], |
|||
} |
|||
); |
|||
}); |
|||
|
|||
// 计算当前国家的板块与股票 |
|||
const sectors = computed(() => { |
|||
return countryInfoMap[props.countryId]?.sectors || [] |
|||
}) |
|||
return countryInfoMap[props.countryId]?.sectors || []; |
|||
}); |
|||
const stocks = computed(() => { |
|||
return countryInfoMap[props.countryId]?.stocks || [] |
|||
}) |
|||
return countryInfoMap[props.countryId]?.stocks || []; |
|||
}); |
|||
|
|||
// 查看更多占位 |
|||
const viewMore = (type) => { |
|||
// 可根据 type 跳转到相应页面或加载更多 |
|||
// 例如:indices/sectors/stocks |
|||
// uni.navigateTo({ url: `/pages/marketSituation/${type}List` }) |
|||
} |
|||
// 可根据 type 跳转到相应页面或加载更多 |
|||
// 例如:indices/sectors/stocks |
|||
// uni.navigateTo({ url: `/pages/marketSituation/${type}List` }) |
|||
}; |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.content { |
|||
padding: 0 20rpx 20rpx 20rpx; |
|||
padding: 0 20rpx 20rpx 20rpx; |
|||
} |
|||
|
|||
/* 子Tab */ |
|||
.sub_tabs { |
|||
display: flex; |
|||
gap: 16rpx; |
|||
padding: 0 20rpx 20rpx 20rpx; |
|||
display: flex; |
|||
gap: 16rpx; |
|||
padding: 0 20rpx 20rpx 20rpx; |
|||
} |
|||
|
|||
.tab_item { |
|||
padding: 6rpx 20rpx; |
|||
border-radius: 5rpx; |
|||
background: #f5f5f5; |
|||
color: #666; |
|||
font-size: 24rpx; |
|||
padding: 6rpx 20rpx; |
|||
border-radius: 5rpx; |
|||
background: #f5f5f5; |
|||
color: #666; |
|||
font-size: 24rpx; |
|||
} |
|||
|
|||
.tab_item.active { |
|||
background: #ff4444; |
|||
color: #fff; |
|||
background: #ff4444; |
|||
color: #fff; |
|||
} |
|||
|
|||
.section { |
|||
padding: 20rpx; |
|||
border-radius: 16rpx; |
|||
padding: 20rpx; |
|||
border-radius: 16rpx; |
|||
} |
|||
|
|||
.section_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 16rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 16rpx; |
|||
} |
|||
|
|||
.section_title { |
|||
font-size: 28rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
font-size: 28rpx; |
|||
font-weight: bold; |
|||
color: #333; |
|||
} |
|||
|
|||
.section_action { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.indices_grid { |
|||
padding: 20rpx; |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); |
|||
gap: 20rpx; |
|||
background-color: #F6F6F6; |
|||
padding: 20rpx; |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); |
|||
gap: 20rpx; |
|||
background-color: #f6f6f6; |
|||
} |
|||
|
|||
/* 情绪温度 */ |
|||
.sentiment { |
|||
background-color: #F6F6F6; |
|||
padding: 0 20rpx 20rpx 20rpx; |
|||
background-color: #f6f6f6; |
|||
padding: 0 20rpx 20rpx 20rpx; |
|||
} |
|||
|
|||
.section_subtitle { |
|||
font-size: 24rpx; |
|||
color: #000000; |
|||
padding: 20rpx 0; |
|||
font-size: 24rpx; |
|||
color: #000000; |
|||
padding: 20rpx 0; |
|||
} |
|||
|
|||
.meters { |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); |
|||
gap: 16rpx; |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); |
|||
gap: 16rpx; |
|||
} |
|||
|
|||
.meter_item { |
|||
display: flex; |
|||
align-items: center; |
|||
/* padding: 10rpx; */ |
|||
background: #ffffff; |
|||
border-radius: 12rpx; |
|||
display: flex; |
|||
align-items: center; |
|||
/* padding: 10rpx; */ |
|||
background: #ffffff; |
|||
border-radius: 12rpx; |
|||
} |
|||
|
|||
.meter_item image { |
|||
width: 100rpx; |
|||
height: 100rpx; |
|||
width: 100rpx; |
|||
height: 100rpx; |
|||
} |
|||
|
|||
.meter_info { |
|||
display: flex; |
|||
flex-direction: column; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.meter_value { |
|||
font-size: 36rpx; |
|||
font-size: 36rpx; |
|||
} |
|||
|
|||
.meter_value.hot { |
|||
color: #ff6b6b; |
|||
color: #ff6b6b; |
|||
} |
|||
|
|||
.meter_value.warm { |
|||
color: #ffd166; |
|||
color: #ffd166; |
|||
} |
|||
|
|||
.meter_value.cool { |
|||
color: #60a5fa; |
|||
color: #60a5fa; |
|||
} |
|||
|
|||
.meter_label { |
|||
font-size: 22rpx; |
|||
font-size: 22rpx; |
|||
} |
|||
|
|||
.meter_label.hot { |
|||
color: #ff6b6b; |
|||
color: #ff6b6b; |
|||
} |
|||
|
|||
.meter_label.warm { |
|||
color: #ffd166; |
|||
color: #ffd166; |
|||
} |
|||
|
|||
.meter_label.cool { |
|||
color: #60a5fa; |
|||
color: #60a5fa; |
|||
} |
|||
|
|||
/* 板块 */ |
|||
.sectors_grid { |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); |
|||
gap: 16rpx; |
|||
display: grid; |
|||
grid-template-columns: repeat(3, 1fr); |
|||
gap: 16rpx; |
|||
} |
|||
|
|||
.sector_item { |
|||
background: #fafafa; |
|||
border-radius: 12rpx; |
|||
padding: 16rpx; |
|||
background: #fafafa; |
|||
border-radius: 12rpx; |
|||
padding: 16rpx; |
|||
} |
|||
|
|||
.sector_header { |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 12rpx; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
margin-bottom: 12rpx; |
|||
} |
|||
|
|||
.sector_name { |
|||
font-size: 24rpx; |
|||
color: #333; |
|||
font-size: 24rpx; |
|||
color: #333; |
|||
} |
|||
|
|||
.sector_change { |
|||
font-size: 22rpx; |
|||
font-size: 22rpx; |
|||
} |
|||
|
|||
.sector_change.rising { |
|||
color: #e74c3c; |
|||
color: #e74c3c; |
|||
} |
|||
|
|||
.sector_change.falling { |
|||
color: #27ae60; |
|||
color: #27ae60; |
|||
} |
|||
|
|||
.sector_price { |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
font-size: 24rpx; |
|||
color: #666; |
|||
} |
|||
|
|||
/* 股票表 */ |
|||
.table { |
|||
background: #fff; |
|||
border-radius: 12rpx; |
|||
overflow: hidden; |
|||
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; |
|||
display: grid; |
|||
grid-template-columns: 2fr 1fr 1fr; |
|||
padding: 18rpx 20rpx; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.table_header { |
|||
background: #fafafa; |
|||
background: #fafafa; |
|||
} |
|||
|
|||
.cell.name { |
|||
display: flex; |
|||
flex-direction: column; |
|||
display: flex; |
|||
flex-direction: column; |
|||
} |
|||
|
|||
.stk_name { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
} |
|||
|
|||
.stk_code { |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
font-size: 22rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.stk_price { |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
font-size: 26rpx; |
|||
color: #333; |
|||
} |
|||
|
|||
.stk_change { |
|||
font-size: 24rpx; |
|||
font-size: 24rpx; |
|||
} |
|||
|
|||
.stk_change.rising { |
|||
color: #e74c3c; |
|||
color: #e74c3c; |
|||
} |
|||
|
|||
.stk_change.falling { |
|||
color: #27ae60; |
|||
color: #27ae60; |
|||
} |
|||
|
|||
.index_item { |
|||
background: #fff; |
|||
border-radius: 12rpx; |
|||
overflow: hidden; |
|||
background: #fff; |
|||
border-radius: 12rpx; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.hot_stocks { |
|||
margin-bottom: 30rpx; |
|||
margin-bottom: 30rpx; |
|||
} |
|||
|
|||
.stocks_list { |
|||
background: #fff; |
|||
border-radius: 12rpx; |
|||
overflow: hidden; |
|||
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; |
|||
display: flex; |
|||
justify-content: space-between; |
|||
align-items: center; |
|||
padding: 24rpx 20rpx; |
|||
border-bottom: 1rpx solid #f0f0f0; |
|||
} |
|||
|
|||
.stock_item:last-child { |
|||
border-bottom: none; |
|||
border-bottom: none; |
|||
} |
|||
|
|||
.stock_info { |
|||
flex: 1; |
|||
flex: 1; |
|||
} |
|||
|
|||
.stock_name { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.stock_code { |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
font-size: 24rpx; |
|||
color: #999; |
|||
} |
|||
|
|||
.stock_price { |
|||
text-align: right; |
|||
text-align: right; |
|||
} |
|||
|
|||
.price { |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
font-size: 28rpx; |
|||
color: #333; |
|||
display: block; |
|||
margin-bottom: 8rpx; |
|||
} |
|||
|
|||
.change { |
|||
font-size: 24rpx; |
|||
font-size: 24rpx; |
|||
} |
|||
|
|||
.change.rising { |
|||
color: #e74c3c; |
|||
color: #e74c3c; |
|||
} |
|||
|
|||
.change.falling { |
|||
color: #27ae60; |
|||
color: #27ae60; |
|||
} |
|||
|
|||
.bottom_safe_area { |
|||
height: 120rpx; |
|||
height: 120rpx; |
|||
} |
|||
</style> |
|||
</style> |
|||
@ -1,586 +1,566 @@ |
|||
<!-- @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 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> |
|||
|
|||
<!-- 内容区域 --> |
|||
<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 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> |
|||
|
|||
<!-- 底部导航栏 --> |
|||
<footerBar class="static-footer" :type="'marketSituation'"></footerBar> |
|||
<!-- 内容区域 --> |
|||
<scroll-view class="content" :style="{ top: contentTopPosition + 'px' }" scroll-y="true"> |
|||
<!-- 亚太-中华 --> |
|||
<view class="market-section" v-for="item in globalIndexArray" :key="item"> |
|||
<view class="market-header"> |
|||
<text class="market-title">{{ item.ac }}</text> |
|||
<view class="market-more" @click="viewMore(item.ac)"> |
|||
<text class="more-text">查看更多</text> |
|||
<text class="more-arrow">></text> |
|||
</view> |
|||
</view> |
|||
<view class="cards-grid-three"> |
|||
<view v-for="iitem in item.list" :key="iitem" class="card-item"> |
|||
<IndexCard |
|||
:market="iitem.market" |
|||
:stockName="iitem.name" |
|||
:currentPrice="iitem.currentPrice" |
|||
:changeAmount="iitem.changeAmount" |
|||
:changePercent="iitem.changePercent" |
|||
:isRising="iitem.isRising" |
|||
@click="viewIndexDetail(iitem)" |
|||
/> |
|||
</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' |
|||
import { ref, onMounted, computed, nextTick, watch } from "vue"; |
|||
import footerBar from "../../components/footerBar.vue"; |
|||
import IndexCard from "../../components/IndexCard.vue"; |
|||
import { getRegionalGroupAPI } from "../../api/marketSituation/marketSituation.js"; |
|||
|
|||
// 响应式数据 |
|||
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 globalIndexArray = ref([]); |
|||
// 亚太-中华指数数据 |
|||
const asiachinaIndexes = ref([ |
|||
{ |
|||
flagIcon: '/static/c1.png', |
|||
stockName: '上证指数', |
|||
stockCode:'noCode', |
|||
currentPrice: '3933.96', |
|||
changeAmount: '+24.32', |
|||
changePercent: '+0.62%', |
|||
isRising: true |
|||
}, |
|||
{ |
|||
flagIcon: '/static/c2.png', |
|||
stockName: '深证成指', |
|||
stockCode:'noCode', |
|||
currentPrice: '45757.90', |
|||
changeAmount: '-123.45', |
|||
changePercent: '-0.27%', |
|||
isRising: false |
|||
}, |
|||
{ |
|||
flagIcon: '/static/c3.png', |
|||
stockName: '创业板指', |
|||
stockCode:'noCode', |
|||
currentPrice: '6606.08', |
|||
changeAmount: '+89.76', |
|||
changePercent: '+1.38%', |
|||
isRising: true |
|||
}, |
|||
{ |
|||
flagIcon: '/static/c4.png', |
|||
stockName: 'HSI50', |
|||
stockCode:'noCode', |
|||
currentPrice: '22333.96', |
|||
changeAmount: '+156.78', |
|||
changePercent: '+0.71%', |
|||
isRising: true |
|||
}, |
|||
{ |
|||
flagIcon: '/static/c5.png', |
|||
stockName: '沪深300', |
|||
stockCode:'noCode', |
|||
currentPrice: '45757.90', |
|||
changeAmount: '-89.12', |
|||
changePercent: '-0.19%', |
|||
isRising: false |
|||
}, |
|||
{ |
|||
flagIcon: '/static/c6.png', |
|||
stockName: '上证50', |
|||
stockCode:'noCode', |
|||
currentPrice: '45757.90', |
|||
changeAmount: '+234.56', |
|||
changePercent: '+0.52%', |
|||
isRising: true |
|||
} |
|||
]) |
|||
{ |
|||
flagIcon: "/static/c1.png", |
|||
stockName: "上证指数", |
|||
stockCode: "noCode", |
|||
currentPrice: "3933.96", |
|||
changeAmount: "+24.32", |
|||
changePercent: "+0.62%", |
|||
isRising: true, |
|||
}, |
|||
{ |
|||
flagIcon: "/static/c2.png", |
|||
stockName: "深证成指", |
|||
stockCode: "noCode", |
|||
currentPrice: "45757.90", |
|||
changeAmount: "-123.45", |
|||
changePercent: "-0.27%", |
|||
isRising: false, |
|||
}, |
|||
{ |
|||
flagIcon: "/static/c3.png", |
|||
stockName: "创业板指", |
|||
stockCode: "noCode", |
|||
currentPrice: "6606.08", |
|||
changeAmount: "+89.76", |
|||
changePercent: "+1.38%", |
|||
isRising: true, |
|||
}, |
|||
{ |
|||
flagIcon: "/static/c4.png", |
|||
stockName: "HSI50", |
|||
stockCode: "noCode", |
|||
currentPrice: "22333.96", |
|||
changeAmount: "+156.78", |
|||
changePercent: "+0.71%", |
|||
isRising: true, |
|||
}, |
|||
{ |
|||
flagIcon: "/static/c5.png", |
|||
stockName: "沪深300", |
|||
stockCode: "noCode", |
|||
currentPrice: "45757.90", |
|||
changeAmount: "-89.12", |
|||
changePercent: "-0.19%", |
|||
isRising: false, |
|||
}, |
|||
{ |
|||
flagIcon: "/static/c6.png", |
|||
stockName: "上证50", |
|||
stockCode: "noCode", |
|||
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", |
|||
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 |
|||
} |
|||
]) |
|||
{ |
|||
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 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/marketSituation/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/marketSituation/marketCondition?stockInformation=${encodeURIComponent(JSON.stringify(item))}` |
|||
}) |
|||
} |
|||
console.log("查看指数详情:", item.stockName); |
|||
// uni.showToast({ |
|||
// title: `查看 ${item.stockName} 详情`, |
|||
// icon: 'none', |
|||
// duration: 2000 |
|||
// }) |
|||
// 这里可以跳转到具体的指数详情页面 |
|||
uni.navigateTo({ |
|||
url: `/pages/marketSituation/marketCondition?stockInformation=${encodeURIComponent(JSON.stringify(item))}`, |
|||
}); |
|||
}; |
|||
|
|||
const getRegionalGroup = async () => { |
|||
try { |
|||
const result = await getRegionalGroupAPI(); |
|||
globalIndexArray.value = result.data; |
|||
} catch (e) { |
|||
console.log("获取区域指数失败", e); |
|||
} |
|||
}; |
|||
|
|||
// 生命周期:页面挂载 |
|||
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() |
|||
}) |
|||
onMounted(async () => { |
|||
await getRegionalGroup(); |
|||
// 获取系统信息 |
|||
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> |
|||
</style> |
|||
844
pages/marketSituation/marketSituation.vue
File diff suppressed because it is too large
View File
|
After Width: 18 | Height: 16 | Size: 428 B |
|
After Width: 20 | Height: 20 | Size: 247 B |
|
After Width: 60 | Height: 60 | Size: 2.1 KiB |
|
After Width: 213 | Height: 228 | Size: 18 KiB |
|
After Width: 100 | Height: 100 | Size: 4.2 KiB |
|
After Width: 18 | Height: 18 | Size: 523 B |
|
After Width: 15 | Height: 15 | Size: 339 B |
|
After Width: 21 | Height: 19 | Size: 1.2 KiB |
|
After Width: 16 | Height: 16 | Size: 440 B |
|
After Width: 100 | Height: 100 | Size: 4.2 KiB |
|
After Width: 32 | Height: 32 | Size: 1.1 KiB |
|
After Width: 32 | Height: 32 | Size: 1.1 KiB |
|
After Width: 32 | Height: 32 | Size: 2.5 KiB |
|
After Width: 32 | Height: 32 | Size: 1.7 KiB |
|
After Width: 32 | Height: 32 | Size: 1.7 KiB |
|
After Width: 32 | Height: 32 | Size: 1.1 KiB |
|
After Width: 32 | Height: 32 | Size: 703 B |
|
After Width: 32 | Height: 32 | Size: 1.4 KiB |
|
After Width: 32 | Height: 32 | Size: 1.1 KiB |
@ -0,0 +1,44 @@ |
|||
import { defineStore } from 'pinia' |
|||
import { ref } from 'vue' |
|||
|
|||
// 定义 Store
|
|||
export const useLoginStore = defineStore( |
|||
'login', |
|||
() => { |
|||
// 会员信息
|
|||
const loginInfo = ref("true") |
|||
|
|||
// 保存会员信息,登录时使用
|
|||
const setLoginInfo = (val) => { |
|||
loginInfo.value = val |
|||
} |
|||
|
|||
// 清理会员信息,退出时使用
|
|||
const clearLoginInfo = () => { |
|||
loginInfo.value = undefined |
|||
} |
|||
|
|||
// 记得 return
|
|||
return { |
|||
loginInfo, |
|||
setLoginInfo, |
|||
clearLoginInfo, |
|||
} |
|||
}, |
|||
// TODO: 持久化
|
|||
{ |
|||
// 网页端持久化
|
|||
// persist: true,
|
|||
// 小程序端持久化
|
|||
persist: { |
|||
storage: { |
|||
getItem(key) { |
|||
return uni.getStorageSync(key) |
|||
}, |
|||
setItem(key, value) { |
|||
uni.setStorageSync(key, value) |
|||
}, |
|||
}, |
|||
}, |
|||
}, |
|||
) |
|||
@ -0,0 +1,78 @@ |
|||
/** @format */ |
|||
|
|||
import { defineStore } from "pinia"; |
|||
import { ref } from "vue"; |
|||
// 定义 Store
|
|||
export const useMarketSituationStore = defineStore( |
|||
"marketSituation", |
|||
() => { |
|||
const cardData = ref([ |
|||
{ |
|||
market: "usa", |
|||
stockName: "道琼斯", |
|||
stockCode: "noCode", |
|||
currentPrice: "45757.90", |
|||
changeAmount: "-125.22", |
|||
changePercent: "-0.27%", |
|||
isRising: false, |
|||
}, |
|||
{ |
|||
market: "usa", |
|||
stockName: "纳斯达克", |
|||
stockCode: "noCode", |
|||
currentPrice: "22333.96", |
|||
changeAmount: "+125.22", |
|||
changePercent: "+0.47%", |
|||
isRising: true, |
|||
}, |
|||
{ |
|||
market: "usa", |
|||
stockName: "标普500", |
|||
stockCode: "noCode", |
|||
currentPrice: "6606.08", |
|||
changeAmount: "+125.22", |
|||
changePercent: "+0.27%", |
|||
isRising: true, |
|||
}, |
|||
{ |
|||
market: "cn", |
|||
stockName: "上证指数", |
|||
stockCode: "noCode", |
|||
currentPrice: "3333.96", |
|||
changeAmount: "+125.22", |
|||
changePercent: "+0.27%", |
|||
isRising: true, |
|||
}, |
|||
{ |
|||
market: "cn", |
|||
stockName: "科创50", |
|||
stockCode: "noCode", |
|||
currentPrice: "757.90", |
|||
changeAmount: "-25.22", |
|||
changePercent: "-0.27%", |
|||
isRising: false, |
|||
}, |
|||
]); |
|||
|
|||
// 记得 return
|
|||
return { |
|||
cardData |
|||
}; |
|||
}, |
|||
// TODO: 持久化
|
|||
{ |
|||
// 网页端持久化
|
|||
// persist: true,
|
|||
// 小程序端持久化
|
|||
persist: { |
|||
storage: { |
|||
getItem(key) { |
|||
return uni.getStorageSync(key); |
|||
}, |
|||
setItem(key, value) { |
|||
uni.setStorageSync(key, value); |
|||
}, |
|||
}, |
|||
}, |
|||
} |
|||
); |
|||