Compare commits
286 Commits
master
...
zhaowenkan
-
14.hbuilderx/launch.json
-
41App.vue
-
46api/customerServicePlatform/customerServicePlatform.js
-
132api/deepExploration/deepExploration.js
-
68api/deepMate/deepMate.js
-
185api/home/mySelections.js
-
70api/marketSituation/marketSituation.js
-
37api/member.js
-
19api/setting/general.js
-
19api/setting/market.js
-
16api/setting/nextPwd.js
-
45api/setting/password.js
-
15api/setting/share.js
-
146api/start/login.js
-
258api/tcpConnection.js
-
392common/canvasMethod.js
-
59common/dailyData.js
-
354common/stockTimeInformation.js
-
143common/util.js
-
281components/DeepMate.vue
-
196components/FeedbackModal.vue
-
232components/IndexCard.vue
-
413components/MarketOverview.vue
-
190components/SharePopup.vue
-
516components/deepExploration_header.vue
-
49components/footerBar.vue
-
209components/login-prompt.vue
-
54main.js
-
45manifest.json
-
738package-lock.json
-
12package.json
-
330pages.json
-
55pages/analysisInstitutionalTrends/analysisInstitutionalTrends.vue
-
100pages/blank/institutionalTrendsBriefing.vue
-
100pages/blank/notice.vue
-
147pages/customStockList/customStockList.vue
-
682pages/customerServicePlatform/csPlatformIndex.vue
-
353pages/customerServicePlatform/historyRecord.vue
-
335pages/customerServicePlatform/questionDetail.vue
-
720pages/deepExploration/MainForceActions.vue
-
532pages/deepExploration/deepExploration.vue
-
388pages/deepExploration/stockSelectDetail.vue
-
1775pages/deepMate/deepMate.vue
-
28pages/home/deepExploration.vue
-
2139pages/home/home.vue
-
28pages/home/marketSituation.vue
-
300pages/home/member.vue
-
477pages/marketSituation/countryMarket.vue
-
301pages/marketSituation/forexMetals.vue
-
859pages/marketSituation/globalIndex.vue
-
2912pages/marketSituation/marketCondition.vue
-
780pages/marketSituation/marketDetail.vue
-
1041pages/marketSituation/marketOverview.vue
-
597pages/marketSituation/marketSituation.vue
-
55pages/morningMarketAnalysis/morningMarketAnalysis.vue
-
86pages/setting/about.vue
-
241pages/setting/account.vue
-
92pages/setting/bind.vue
-
257pages/setting/createPwd.vue
-
185pages/setting/email.vue
-
108pages/setting/font.vue
-
186pages/setting/general.vue
-
76pages/setting/introduce.vue
-
348pages/setting/market.vue
-
61pages/setting/message.vue
-
82pages/setting/newVersion.vue
-
175pages/setting/nextPwd.vue
-
255pages/setting/password.vue
-
185pages/setting/phone.vue
-
120pages/setting/push.vue
-
128pages/setting/server.vue
-
299pages/setting/share.vue
-
104pages/setting/theme.vue
-
1055pages/start/Registration/Registration.vue
-
1341pages/start/Registration/list.js
-
13pages/start/agreement/agreement.vue
-
1341pages/start/login/list.js
-
1190pages/start/login/login.vue
-
69pages/start/login/verification.js
-
13pages/start/privacy/privacy.vue
-
1085pages/start/recoverPassword/recoverPassword.vue
-
170pages/start/select/select.vue
-
73pages/start/startup/startup.vue
-
138server/login.json
-
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/deepExploration-images/1.png
-
BINstatic/deepExploration-images/2.png
-
BINstatic/deepExploration-images/3.png
-
BINstatic/deepExploration-images/4.png
-
BINstatic/deepExploration-images/ASC.png
-
BINstatic/deepExploration-images/Americle.png
@ -0,0 +1,14 @@ |
|||||
|
{ |
||||
|
"version" : "1.0", |
||||
|
"configurations" : [ |
||||
|
{ |
||||
|
"customPlaygroundType" : "device", |
||||
|
"packageName" : "io.dcloud.HBuilder", |
||||
|
"playground" : "custom", |
||||
|
"type" : "uni-app:app-android" |
||||
|
} |
||||
|
] |
||||
|
} |
||||
|
// "playground" : "standard", |
||||
|
// "type" : "uni-app:app-ios" |
||||
|
|
||||
@ -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,132 @@ |
|||||
|
import { http } from '@/utils/http.js' |
||||
|
|
||||
|
|
||||
|
//主力追踪意图
|
||||
|
export const getModel1First = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/trackingFirst', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
//主力追踪意图
|
||||
|
export const getModel1Second = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/trackingSecond', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//主力追踪意图
|
||||
|
export const getModel2First = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/radarFirst', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
//主力追踪意图
|
||||
|
export const getModel2Second = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/radarSecond', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
//主力追踪意图
|
||||
|
export const getModel3First = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/decodingFirst', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
//主力追踪意图
|
||||
|
export const getModel3Second = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/decodingSecond', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
//主力追踪意图
|
||||
|
export const getModel4First = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/fundsFirst', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
//主力追踪意图
|
||||
|
export const getModel4Second = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/fundsSecond', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
//不搜索时走这个
|
||||
|
export const getModeldefault = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/default', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//k线数据
|
||||
|
export const getData = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/WorkFlowData', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
//历史记录列表
|
||||
|
export const RecordListApi = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/mainForceList', |
||||
|
data:data |
||||
|
}) |
||||
|
} |
||||
|
// 选股策略
|
||||
|
export const stocSelectApi = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/deep/getStrategy', |
||||
|
data:data |
||||
|
}) |
||||
|
} |
||||
|
//根据名字选股策略
|
||||
|
export const stocSelectByNameApi = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/deep/getStrategyByName', |
||||
|
data:data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
//历史记录详情
|
||||
|
export const RecordInfoApi = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/coze/clickRecord', |
||||
|
data:data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
@ -0,0 +1,68 @@ |
|||||
|
import { http } from '../../utils/http' |
||||
|
|
||||
|
|
||||
|
|
||||
|
export const getData = () => { |
||||
|
return http({ |
||||
|
method: 'GET', |
||||
|
url: '/ka', |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 意图识别 |
||||
|
* POST /api/deepMate/dmFirst |
||||
|
* headers: token, content-type: application/json, contentType: application/json, version, client |
||||
|
* body: { content, language, marketList } |
||||
|
*/ |
||||
|
export const postIntent = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/deepMate/dmFirst', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 获取股票信息 |
||||
|
* headers: token, content-type: application/json, contentType: application/json, version, client |
||||
|
* body: { language, token, recordId, parentId, stockId } |
||||
|
*/ |
||||
|
export const postStock = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/deepMate/dmSecond', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 获取历史记录 |
||||
|
*/ |
||||
|
|
||||
|
export const postHistory = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/deepMate/dmList', |
||||
|
data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 历史记录详情 |
||||
|
*/ |
||||
|
|
||||
|
export const postHistoryDetail = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/deepMate/clickRecord', |
||||
|
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,37 @@ |
|||||
|
import util from '../common/util.js' |
||||
|
|
||||
|
/*export const getUserInfo = (data = {}) => { |
||||
|
return util.request( |
||||
|
'/api/my/userInfo', |
||||
|
(res) => { |
||||
|
console.log('用户信息请求成功:', res); |
||||
|
}, |
||||
|
{data}, |
||||
|
|
||||
|
(err) => { |
||||
|
console.log('用户信息请求失败:', err); |
||||
|
} |
||||
|
); |
||||
|
}; |
||||
|
*/ |
||||
|
|
||||
|
import { |
||||
|
http |
||||
|
} from '../utils/http' |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 用户信息获取接口 |
||||
|
* @param data |
||||
|
* @returns {Promise<unknown>} |
||||
|
*/ |
||||
|
export const getUserInfo = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/my/userInfo', |
||||
|
data: data, |
||||
|
header:{ |
||||
|
token:'014de5283d2930af6481ede591afd087' |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
import { http } from '../../utils/http' |
||||
|
|
||||
|
|
||||
|
|
||||
|
export const getSetting = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/my/getSetting', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export const updateSetting = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/my/updateSetting', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
@ -0,0 +1,19 @@ |
|||||
|
import { http } from '../../utils/http' |
||||
|
|
||||
|
|
||||
|
|
||||
|
export const getMarketSetting = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/my/getQuotationSetting', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export const updateMarketSetting = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/my/updateQuotationSetting', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
@ -0,0 +1,16 @@ |
|||||
|
import {http} from '../../utils/http' |
||||
|
|
||||
|
/** |
||||
|
* 修改密码 |
||||
|
* @param data |
||||
|
* @returns {*} |
||||
|
*/ |
||||
|
export const updatePassword = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/my/updatePassword', |
||||
|
data: |
||||
|
data |
||||
|
, |
||||
|
}) |
||||
|
} |
||||
@ -0,0 +1,45 @@ |
|||||
|
import { |
||||
|
http |
||||
|
} from '../../utils/http' |
||||
|
|
||||
|
/** |
||||
|
* 验证码发送 |
||||
|
* @param data |
||||
|
* @returns {*} |
||||
|
*/ |
||||
|
export const sendEmail = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/UserLogin/sendEmail', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 验证码验证 |
||||
|
* @param data |
||||
|
* @returns {*} |
||||
|
*/ |
||||
|
export const validateCode = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/my/validateCode', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export const sendPhone = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/UserLogin/sendPhone', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
export const changeBind = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/my/bindEmailOrPhone', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
@ -0,0 +1,15 @@ |
|||||
|
import { http } from '../../utils/http' |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 分享接口获取dccode |
||||
|
* @param data |
||||
|
* @returns {*} |
||||
|
*/ |
||||
|
export const Share = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/api/my/share', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
@ -0,0 +1,146 @@ |
|||||
|
import { http } from '../../utils/http' |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* |
||||
|
* @param data 模拟手机号码 |
||||
|
* { |
||||
|
"loginType":"EMAIL", //登录方式
|
||||
|
"account":"q614588746@163.com" , //登陆账号 手机号/邮箱/dccode
|
||||
|
"verifyCode":"837012", //验证码
|
||||
|
"password":"", //密码
|
||||
|
"useCode":"true", //是否使用验证码 true/false
|
||||
|
"idToken":"", //第三方登录idToken
|
||||
|
} |
||||
|
*/ |
||||
|
export const LoginApi = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/UserLogin/login', |
||||
|
data: |
||||
|
data |
||||
|
, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 发送邮箱验证码 |
||||
|
* @param {*} email |
||||
|
* @returns |
||||
|
*/ |
||||
|
export const SendEmailCodeApi = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/UserLogin/sendEmail', |
||||
|
data: data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 发送手机验证码 |
||||
|
* @param {*} email |
||||
|
* @returns |
||||
|
*/ |
||||
|
export const SendPhoneCodeApi = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/UserLogin/sendPhone', |
||||
|
data:data |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 注册 |
||||
|
*/ |
||||
|
|
||||
|
export const registerApi = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/UserLogin/register', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 忘记密码校验验证码 |
||||
|
*/ |
||||
|
|
||||
|
export const verifyCodeApi = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/UserLogin/verifyCode', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 忘记密码输入新的密码 |
||||
|
*/ |
||||
|
|
||||
|
export const forgetApi = (data) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/UserLogin/forget', |
||||
|
data: data, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 修改密码 |
||||
|
* |
||||
|
*/ |
||||
|
|
||||
|
export const updatePassword = (data) => { |
||||
|
return http({ |
||||
|
method: 'GET', |
||||
|
url: '/updatePassword', |
||||
|
data: { |
||||
|
data |
||||
|
}, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 通过苹果登录 |
||||
|
*/ |
||||
|
|
||||
|
export const postLoginAppleSimpleAPI = (phoneNumber) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/login', |
||||
|
data: { |
||||
|
phoneNumber, |
||||
|
}, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/** |
||||
|
* 通过谷歌登录 |
||||
|
*/ |
||||
|
|
||||
|
export const postLoginGoogleSimpleAPI = (phoneNumber) => { |
||||
|
return http({ |
||||
|
method: 'POST', |
||||
|
url: '/login/wxMin/simple', |
||||
|
data: { |
||||
|
phoneNumber, |
||||
|
}, |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
|
||||
@ -0,0 +1,258 @@ |
|||||
|
/** |
||||
|
* TCP连接工具类 |
||||
|
* 用于处理TCP连接、发送消息和断开连接 |
||||
|
* |
||||
|
* @format |
||||
|
*/ |
||||
|
|
||||
|
// 引用TCP插件
|
||||
|
// const TCPSocket = uni.requireNativePlugin('Aimer-TCPPlugin');
|
||||
|
// const TCPSocket = uni.requireNativePlugin("Aimer-TCPPlugin");
|
||||
|
|
||||
|
// TCP连接配置
|
||||
|
const TCP_CONFIG = { |
||||
|
ip: "192.168.1.9", |
||||
|
port: "8080", |
||||
|
channel: "1", // 可选 1~20
|
||||
|
charsetname: "UTF-8", // 默认UTF-8,可选GBK
|
||||
|
}; |
||||
|
|
||||
|
/** |
||||
|
* TCP连接管理类 |
||||
|
*/ |
||||
|
class TCPConnection { |
||||
|
constructor() { |
||||
|
this.channelConnections = new Map(); // 存储每个channel的连接状态
|
||||
|
this.connectionCallbacks = []; |
||||
|
this.messageCallbacks = []; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* TCP初始化连接 |
||||
|
* @param {Object} config - 连接配置 {ip, port, channel, charsetname} |
||||
|
* @param {Function} callback - 连接状态回调函数 |
||||
|
*/ |
||||
|
connect(config = {}, callback = null) { |
||||
|
const channel = config.channel || TCP_CONFIG.channel; |
||||
|
|
||||
|
// 如果该channel已经连接,先断开现有连接
|
||||
|
if (this.channelConnections.get(channel)) { |
||||
|
console.log(`检测到channel ${channel}现有TCP连接,先断开...`); |
||||
|
this.disconnect(config); |
||||
|
// 等待断开完成后再连接
|
||||
|
setTimeout(() => { |
||||
|
this._performConnect(config, callback); |
||||
|
}, 300); |
||||
|
} else { |
||||
|
// 直接连接
|
||||
|
this._performConnect(config, callback); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 执行TCP连接 |
||||
|
* @param {Object} config - 连接配置 |
||||
|
* @param {Function} callback - 连接状态回调函数 |
||||
|
*/ |
||||
|
_performConnect(config = {}, callback = null) { |
||||
|
const connectionConfig = { |
||||
|
channel: config.channel || TCP_CONFIG.channel, |
||||
|
ip: config.ip || TCP_CONFIG.ip, |
||||
|
port: config.port || TCP_CONFIG.port, |
||||
|
}; |
||||
|
|
||||
|
// 如果指定了字符集,添加到配置中
|
||||
|
if (config.charsetname || TCP_CONFIG.charsetname) { |
||||
|
connectionConfig.charsetname = config.charsetname || TCP_CONFIG.charsetname; |
||||
|
} |
||||
|
|
||||
|
console.log("开始建立TCP连接:", connectionConfig); |
||||
|
TCPSocket.connect(connectionConfig, (result) => { |
||||
|
/** |
||||
|
* status : 0 连接成功 |
||||
|
* status : 1 断开连接 |
||||
|
* receivedMsg : 服务器返回字符串(普通的字符串交互) |
||||
|
* receivedHexMsg : 服务器返回字节数组(单片机、智能家居等硬件数据交互) |
||||
|
*/ |
||||
|
if (result.status == "0") { |
||||
|
// TCP连接成功
|
||||
|
this.channelConnections.set(connectionConfig.channel, true); |
||||
|
console.log(`TCP连接成功 - Channel ${connectionConfig.channel}`); |
||||
|
this._notifyConnectionCallbacks("connected", result, connectionConfig.channel); |
||||
|
} else if (result.status == "1") { |
||||
|
// TCP断开连接
|
||||
|
this.channelConnections.set(connectionConfig.channel, false); |
||||
|
console.log(`TCP断开连接 - Channel ${connectionConfig.channel}`); |
||||
|
this._notifyConnectionCallbacks("disconnected", result, connectionConfig.channel); |
||||
|
} |
||||
|
|
||||
|
if (result.receivedMsg) { |
||||
|
// 服务器返回字符串
|
||||
|
console.log("收到字符串消息:", result.receivedMsg); |
||||
|
this._notifyMessageCallbacks("string", result.receivedMsg, null, connectionConfig.channel); |
||||
|
} |
||||
|
|
||||
|
// if (result.receivedHexMsg) {
|
||||
|
// // 硬件服务器返回16进制数据
|
||||
|
// console.log('收到16进制消息:', result.receivedHexMsg);
|
||||
|
// let msg = result.receivedHexMsg;
|
||||
|
// let sum = msg.length / 2;
|
||||
|
// let arr = [];
|
||||
|
// for (let k = 0; k < sum; k++) {
|
||||
|
// let i = msg.substring(k * 2, k * 2 + 2);
|
||||
|
// arr.push(i);
|
||||
|
// }
|
||||
|
// console.log('解析后的16进制数组:', arr);
|
||||
|
// this._notifyMessageCallbacks('hex', result.receivedHexMsg, arr);
|
||||
|
// }
|
||||
|
|
||||
|
// 执行回调函数
|
||||
|
if (callback && typeof callback === "function") { |
||||
|
callback(result); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* TCP发送消息(普通的字符串交互) |
||||
|
* @param {String|Object} message - 要发送的消息,如果是对象会自动转换为JSON字符串 |
||||
|
* @param {Object} config - 发送配置 {channel, charsetname} |
||||
|
*/ |
||||
|
send(message, config = {}) { |
||||
|
const channel = config.channel || "1"; |
||||
|
|
||||
|
if (!this.channelConnections.get(channel)) { |
||||
|
console.warn(`TCP Channel ${channel}未连接,无法发送消息`); |
||||
|
return false; |
||||
|
} |
||||
|
|
||||
|
// 如果message是对象,转换为JSON字符串
|
||||
|
let messageStr = message; |
||||
|
if (typeof message === "object") { |
||||
|
messageStr = JSON.stringify(message) + "\n"; |
||||
|
} |
||||
|
|
||||
|
const sendConfig = { |
||||
|
channel: config.channel || "1", // 注意:channel应该是字符串
|
||||
|
message: messageStr, |
||||
|
}; |
||||
|
|
||||
|
// 如果指定了字符编码,添加到配置中
|
||||
|
if (config.charsetname) { |
||||
|
sendConfig.charsetname = config.charsetname; |
||||
|
} |
||||
|
|
||||
|
TCPSocket.send(sendConfig); |
||||
|
console.log("js成功发送TCP消息:", messageStr); |
||||
|
return true; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* TCP断开连接 |
||||
|
* @param {Object} config - 断开配置 {channel} |
||||
|
*/ |
||||
|
disconnect(config = {}) { |
||||
|
const channel = config.channel || TCP_CONFIG.channel; |
||||
|
const disconnectConfig = { |
||||
|
channel: channel, |
||||
|
}; |
||||
|
|
||||
|
TCPSocket.disconnect(disconnectConfig); |
||||
|
this.channelConnections.set(channel, false); |
||||
|
console.log(`TCP连接已断开 - Channel ${channel}`, disconnectConfig); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 添加连接状态监听器 |
||||
|
* @param {Function} callback - 回调函数 (status, result) => {} |
||||
|
*/ |
||||
|
onConnectionChange(callback) { |
||||
|
if (typeof callback === "function") { |
||||
|
this.connectionCallbacks.push(callback); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 添加消息监听器 |
||||
|
* @param {Function} callback - 回调函数 (type, message, parsedArray) => {} |
||||
|
*/ |
||||
|
onMessage(callback) { |
||||
|
if (typeof callback === "function") { |
||||
|
this.messageCallbacks.push(callback); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 移除连接状态监听器 |
||||
|
* @param {Function} callback - 要移除的回调函数 |
||||
|
*/ |
||||
|
removeConnectionListener(callback) { |
||||
|
const index = this.connectionCallbacks.indexOf(callback); |
||||
|
if (index > -1) { |
||||
|
this.connectionCallbacks.splice(index, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 移除消息监听器 |
||||
|
* @param {Function} callback - 要移除的回调函数 |
||||
|
*/ |
||||
|
removeMessageListener(callback) { |
||||
|
const index = this.messageCallbacks.indexOf(callback); |
||||
|
if (index > -1) { |
||||
|
this.messageCallbacks.splice(index, 1); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 获取连接状态 |
||||
|
* @param {String} channel - 要检查的channel,如果不指定则返回所有channel的连接状态 |
||||
|
* @returns {Boolean|Object} 连接状态 |
||||
|
*/ |
||||
|
getConnectionStatus(channel = null) { |
||||
|
if (channel) { |
||||
|
return this.channelConnections.get(channel) || false; |
||||
|
} |
||||
|
// 返回所有channel的连接状态
|
||||
|
const allConnections = {}; |
||||
|
for (const [ch, status] of this.channelConnections) { |
||||
|
allConnections[ch] = status; |
||||
|
} |
||||
|
return allConnections; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通知连接状态回调 |
||||
|
* @private |
||||
|
*/ |
||||
|
_notifyConnectionCallbacks(status, result, channel) { |
||||
|
this.connectionCallbacks.forEach((callback) => { |
||||
|
try { |
||||
|
callback(status, result, channel); |
||||
|
} catch (error) { |
||||
|
console.error("连接状态回调执行错误:", error); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 通知消息回调 |
||||
|
* @private |
||||
|
*/ |
||||
|
_notifyMessageCallbacks(type, message, parsedArray = null, channel = null) { |
||||
|
this.messageCallbacks.forEach((callback) => { |
||||
|
try { |
||||
|
callback(type, message, parsedArray, channel); |
||||
|
} catch (error) { |
||||
|
console.error("消息回调执行错误:", error); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 创建TCP连接实例
|
||||
|
const tcpConnection = new TCPConnection(); |
||||
|
|
||||
|
// 导出TCP连接实例和类
|
||||
|
export default tcpConnection; |
||||
|
export { TCPConnection, TCP_CONFIG }; |
||||
@ -0,0 +1,392 @@ |
|||||
|
/** |
||||
|
* 功能:Canvas绘制方法。 |
||||
|
* 作者:洪锡林 |
||||
|
* 时间:2025年10月25日 |
||||
|
* |
||||
|
* @format |
||||
|
*/ |
||||
|
|
||||
|
import { utils } from "./util.js"; |
||||
|
export const HCharts = { |
||||
|
// 清除画布
|
||||
|
clearCanvas(ctx, width, height) { |
||||
|
ctx.clearRect(0, 0, width, height); |
||||
|
ctx.setFillStyle("#ffffff"); |
||||
|
ctx.fillRect(0, 0, width, height); |
||||
|
}, |
||||
|
// 设置画布颜色
|
||||
|
setCanvasColor(ctx, width, height, color) { |
||||
|
ctx.clearRect(0, 0, width, height); |
||||
|
ctx.setFillStyle(color); |
||||
|
ctx.fillRect(0, 0, width, height); |
||||
|
}, |
||||
|
// 绘制文本工具函数
|
||||
|
drawText(ctx, text, x, y, fontSize = 12, color = "#333", align = "left") { |
||||
|
ctx.setFontSize(fontSize); |
||||
|
ctx.setFillStyle(color); |
||||
|
ctx.setTextAlign(align); |
||||
|
ctx.fillText(text, x, y); |
||||
|
}, |
||||
|
/** |
||||
|
* 功能:绘制网格系统。 |
||||
|
* 作者:洪锡林 |
||||
|
* 时间:2025年10月25日 |
||||
|
* |
||||
|
* grid:[{ |
||||
|
* top:顶部距离 |
||||
|
* bottom:底部距离 |
||||
|
* left:左侧距离 |
||||
|
* right:右侧距离 |
||||
|
* lineColor:网格线颜色 |
||||
|
* lineWidth:网格线宽度 |
||||
|
* horizontalLineNum:水平网格线数量 |
||||
|
* verticalLineNum:垂直网格线数量 |
||||
|
* label:{ |
||||
|
* fontSize:字体大小 |
||||
|
* color:字体颜色 |
||||
|
* onlyTwo:是否只有两个标签 |
||||
|
* text:[{ |
||||
|
* value:值标签 |
||||
|
* ratio:比例标签 |
||||
|
* },{ |
||||
|
* value:值标签 |
||||
|
* ratio:比例标签 |
||||
|
* },...] |
||||
|
* },...] |
||||
|
*/ |
||||
|
// 绘制网格系统
|
||||
|
drawGrid(ctx, width, height, grid, openTime, closeTime) { |
||||
|
// 测试数据
|
||||
|
// const preClosePrice = prevClosePrice;
|
||||
|
for (let i = 0; i < grid.length; ++i) { |
||||
|
const top = grid[i].top; |
||||
|
const bottom = grid[i].bottom; |
||||
|
const left = grid[i].left; |
||||
|
const right = grid[i].right; |
||||
|
const lineColor = grid[i].lineColor; |
||||
|
const lineWidth = grid[i].lineWidth; |
||||
|
const horizontalLineNum = grid[i].horizontalLineNum - 1; |
||||
|
const verticalLineNum = grid[i].verticalLineNum - 1; |
||||
|
let label; |
||||
|
if (grid[i].label) { |
||||
|
label = grid[i].label; |
||||
|
} |
||||
|
ctx.setStrokeStyle(lineColor); |
||||
|
ctx.setLineWidth(lineWidth); |
||||
|
|
||||
|
// 画图底的开盘收盘时间
|
||||
|
if (i == 0 && openTime && closeTime) { |
||||
|
HCharts.drawText(ctx, openTime, 6, height - bottom + 12, 14, "#686868", "left"); |
||||
|
HCharts.drawText(ctx, closeTime, width - 6, height - bottom + 12, 14, "#686868", "right"); |
||||
|
} |
||||
|
// 绘制水平网格线
|
||||
|
for (let j = 0; j <= horizontalLineNum; j++) { |
||||
|
const y = top + (j * (height - bottom - top)) / horizontalLineNum; |
||||
|
ctx.beginPath(); |
||||
|
if (label.lineStyle[j] == "dash") { |
||||
|
ctx.setLineDash([5, 5]); |
||||
|
} |
||||
|
ctx.moveTo(left, y); |
||||
|
ctx.lineTo(width - right, y); |
||||
|
ctx.stroke(); |
||||
|
ctx.setLineDash([]); |
||||
|
} |
||||
|
// 绘制垂直网格线
|
||||
|
for (let i = 0; i <= verticalLineNum; i++) { |
||||
|
const x = ((width - left - right) * i) / verticalLineNum; |
||||
|
ctx.beginPath(); |
||||
|
ctx.moveTo(x + left, top); |
||||
|
ctx.lineTo(x + left, height - bottom); |
||||
|
ctx.stroke(); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
// 绘制价格标签
|
||||
|
drawAxisLabels(ctx, width, height, grid) { |
||||
|
for (let i = 0; i < grid.length; ++i) { |
||||
|
const top = grid[i].top; |
||||
|
const bottom = grid[i].bottom; |
||||
|
const left = grid[i].left; |
||||
|
const right = grid[i].right; |
||||
|
const horizontalLineNum = grid[i].horizontalLineNum - 1; |
||||
|
let label; |
||||
|
if (grid[i].label) { |
||||
|
label = grid[i].label; |
||||
|
} |
||||
|
// 绘制水平网格线
|
||||
|
for (let j = 0; j <= horizontalLineNum; j++) { |
||||
|
const y = top + (j * (height - bottom - top)) / horizontalLineNum; |
||||
|
// 价格标签
|
||||
|
if (label) { |
||||
|
let valueXText = left + 1; |
||||
|
let ratioXText = width - right - 1; |
||||
|
let yText = y + 10; |
||||
|
if (j == horizontalLineNum) { |
||||
|
yText = y - 1; |
||||
|
} |
||||
|
let valueAlign = "left"; |
||||
|
let ratioAlign = "right"; |
||||
|
let fontSize = label.fontSize; |
||||
|
let textColor = label.color[j]; |
||||
|
if (label.onlyTwo) { |
||||
|
if (j == 0) { |
||||
|
HCharts.drawText(ctx, label.text[0].value, valueXText, yText, fontSize, label.color[0], valueAlign); |
||||
|
} else if (j == horizontalLineNum) { |
||||
|
HCharts.drawText(ctx, label.text[1].value, valueXText, yText, fontSize, label.color[1], valueAlign); |
||||
|
} |
||||
|
} else { |
||||
|
HCharts.drawText(ctx, label.text[j].value, valueXText, yText, fontSize, textColor, valueAlign); |
||||
|
} |
||||
|
|
||||
|
if (typeof label.text[j]?.ratio !== "undefined") { |
||||
|
HCharts.drawText(ctx, label.text[j].ratio, ratioXText, yText, fontSize, textColor, ratioAlign); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
// 绘制价格曲线
|
||||
|
drawPriceLine(ctx, width, height, data, grid, priceRange) { |
||||
|
if (!data.length) return; |
||||
|
// 上下边距1
|
||||
|
const top = grid[0].top; |
||||
|
const bottom = grid[0].bottom; |
||||
|
const left = grid[0].left; |
||||
|
const right = grid[0].right; |
||||
|
const pointLen = 240; |
||||
|
const priceDiff = priceRange.max - priceRange.min; |
||||
|
// 绘制价格曲线
|
||||
|
ctx.setStrokeStyle("#000"); |
||||
|
ctx.setLineWidth(1); |
||||
|
ctx.beginPath(); |
||||
|
|
||||
|
data.forEach((item, index) => { |
||||
|
const x = left + (index * (width - left - right)) / pointLen; |
||||
|
const y = top + (height - top - bottom) * (1 - (item.price - priceRange.min) / priceDiff); |
||||
|
if (index === 0) { |
||||
|
ctx.moveTo(x, y); |
||||
|
} else { |
||||
|
// 使用贝塞尔曲线平滑连接
|
||||
|
const prevPoint = data[index - 1]; |
||||
|
const prevX = left + ((index - 1) * (width - left - right)) / pointLen; |
||||
|
const prevY = top + (height - top + -bottom) * (1 - (prevPoint.price - priceRange.min) / priceDiff); |
||||
|
const cp1x = (prevX + x) / 2; |
||||
|
const cp1y = prevY; |
||||
|
const cp2x = (prevX + x) / 2; |
||||
|
const cp2y = y; |
||||
|
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); |
||||
|
} |
||||
|
}); |
||||
|
ctx.stroke(); |
||||
|
// 绘制渐变背景
|
||||
|
HCharts.drawGradientBackground(ctx, width, height, data, grid, priceRange); |
||||
|
}, |
||||
|
|
||||
|
// 绘制渐变背景
|
||||
|
drawGradientBackground(ctx, width, height, data, grid, priceRange) { |
||||
|
// 上下边距1
|
||||
|
const top = grid[0].top; |
||||
|
const bottom = grid[0].bottom; |
||||
|
const left = grid[0].left; |
||||
|
const right = grid[0].right; |
||||
|
const pointLen = 240; |
||||
|
const priceDiff = priceRange.max - priceRange.min; |
||||
|
|
||||
|
const gradient = ctx.createLinearGradient(0, left, 0, height - top); |
||||
|
gradient.addColorStop(0, "rgba(0, 0, 0, 0.3)"); |
||||
|
gradient.addColorStop(1, "rgba(0, 0, 0, 0.05)"); |
||||
|
|
||||
|
ctx.beginPath(); |
||||
|
|
||||
|
// 绘制价格曲线路径
|
||||
|
data.forEach((item, index) => { |
||||
|
const x = left + (index * (width - left - right)) / pointLen; |
||||
|
const y = top + (height - top - bottom) * (1 - (item.price - priceRange.min) / priceDiff); |
||||
|
|
||||
|
if (index === 0) { |
||||
|
ctx.moveTo(x, y); |
||||
|
} else { |
||||
|
const prevPoint = data[index - 1]; |
||||
|
const prevX = left + ((index - 1) * (width - left - right)) / pointLen; |
||||
|
const prevY = top + (height - top - bottom) * (1 - (prevPoint.price - priceRange.min) / priceDiff); |
||||
|
|
||||
|
const cp1x = (prevX + x) / 2; |
||||
|
const cp1y = prevY; |
||||
|
const cp2x = (prevX + x) / 2; |
||||
|
const cp2y = y; |
||||
|
|
||||
|
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 闭合路径
|
||||
|
const lastX = left + ((data.length - 1) * (width - left - right)) / pointLen; |
||||
|
ctx.lineTo(lastX, height - bottom); |
||||
|
ctx.lineTo(left, height - bottom); |
||||
|
ctx.closePath(); |
||||
|
|
||||
|
ctx.setFillStyle(gradient); |
||||
|
ctx.fill(); |
||||
|
}, |
||||
|
// 绘制成交量
|
||||
|
drawVolume(ctx, width, height, data, index, pointLen, grid, volumeRange, offset) { |
||||
|
if (!data.length) return; |
||||
|
|
||||
|
const top = grid[index - 1].top; |
||||
|
const bottom = grid[index - 1].bottom; |
||||
|
const left = grid[index - 1].left; |
||||
|
const right = grid[index - 1].right; |
||||
|
|
||||
|
data.forEach((item, index) => { |
||||
|
const x = offset + left + (index * (width - left - right)) / pointLen; |
||||
|
const barWidth = (width - left - right) / pointLen - 0.5; |
||||
|
const barHeight = (item.volume / volumeRange.max) * (height - bottom - top); |
||||
|
// 根据涨跌设置颜色
|
||||
|
const isRise = index === 0 || item.price >= data[index - 1].price || item.close >= data[index - 1].close; |
||||
|
ctx.setFillStyle(isRise ? "green" : "red"); |
||||
|
|
||||
|
ctx.fillRect(x - barWidth / 2, height - bottom - barHeight, barWidth, barHeight); |
||||
|
}); |
||||
|
}, |
||||
|
// 字符宽度近似计算(避免使用 measureText)
|
||||
|
getApproximateTextWidth(text, fontSize = 10) { |
||||
|
// 中文字符约等于 fontSize,英文字符约等于 fontSize * 0.6
|
||||
|
let width = 0; |
||||
|
for (let char of text) { |
||||
|
// 判断是否为中文字符
|
||||
|
if (char.match(/[\u4e00-\u9fa5]/)) { |
||||
|
width += fontSize; |
||||
|
} else { |
||||
|
width += fontSize * 0.6; |
||||
|
} |
||||
|
} |
||||
|
return width; |
||||
|
}, |
||||
|
// 绘制顶部价格显示
|
||||
|
drawTopPriceDisplay(ctx, grid, text) { |
||||
|
for (let i = 0; i < text.length; i++) { |
||||
|
let x = grid[i].left; |
||||
|
let y = grid[i].top - 4; |
||||
|
for (let j = 0; j < text[i].length; j++) { |
||||
|
ctx.setFillStyle(text[i][j].color); |
||||
|
ctx.setFontSize(10); |
||||
|
ctx.setTextAlign("left"); |
||||
|
ctx.fillText(text[i][j].name + ":" + text[i][j].value, x, y); |
||||
|
x += HCharts.getApproximateTextWidth(text[i][j].name + ":" + text[i][j].value) + 5; |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
// 绘制坐标轴标签
|
||||
|
drawCrosshairAxisLabels(ctx, width, height, grid, crosshair) { |
||||
|
const { x, y } = crosshair; |
||||
|
// X轴时间标签
|
||||
|
if (crosshair.currentData && (crosshair.currentData.time || crosshair.currentData.date)) { |
||||
|
const timeText = crosshair.currentData.time || crosshair.currentData.date; |
||||
|
const xBoxWidth = crosshair.currentData.time ? 40 : 70; |
||||
|
const xBoxHeight = 15; |
||||
|
ctx.setFillStyle("#629AF5"); |
||||
|
|
||||
|
if (x - xBoxWidth / 2 <= grid[0].left) { |
||||
|
ctx.fillRect(grid[0].left, height - grid[0].bottom, xBoxWidth, xBoxHeight); |
||||
|
} else if (x + xBoxWidth / 2 < width - grid[0].right) { |
||||
|
ctx.fillRect(x - xBoxWidth / 2, height - grid[0].bottom, xBoxWidth, xBoxHeight); |
||||
|
} else { |
||||
|
ctx.fillRect(width - grid[0].right - xBoxWidth, height - grid[0].bottom, xBoxWidth, xBoxHeight); |
||||
|
} |
||||
|
|
||||
|
ctx.setFillStyle("#fff"); |
||||
|
ctx.setFontSize(12); |
||||
|
ctx.setTextAlign("center"); |
||||
|
|
||||
|
if (x - xBoxWidth / 2 <= grid[0].left) { |
||||
|
ctx.fillText(timeText, grid[0].left + xBoxWidth / 2, height - grid[0].bottom + 12); |
||||
|
} else if (x + xBoxWidth / 2 < width - grid[0].right) { |
||||
|
ctx.fillText(timeText, x, height - grid[0].bottom + 12); |
||||
|
} else { |
||||
|
ctx.fillText(timeText, width - grid[0].right - xBoxWidth / 2, height - grid[0].bottom + 12); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// Y轴价格标签
|
||||
|
if (crosshair.currentData) { |
||||
|
const priceText = utils.formatPrice(crosshair.currentData.price); |
||||
|
const yBoxWidth = 50; |
||||
|
const yBoxHeight = 14; |
||||
|
ctx.setFillStyle("#629AF5"); |
||||
|
if (x < grid[0].left + yBoxWidth + 5) { |
||||
|
ctx.fillRect(width - grid[0].right - yBoxWidth, y - yBoxHeight / 2, yBoxWidth, yBoxHeight); |
||||
|
} else { |
||||
|
ctx.fillRect(grid[0].left, y - yBoxHeight / 2, yBoxWidth, yBoxHeight); |
||||
|
} |
||||
|
|
||||
|
ctx.setFillStyle("#fff"); |
||||
|
ctx.setFontSize(11); |
||||
|
ctx.setTextAlign("center"); |
||||
|
if (x < grid[0].left + yBoxWidth + 5) { |
||||
|
ctx.fillText(priceText, width - grid[0].right - yBoxWidth / 2, y + 3); |
||||
|
} else { |
||||
|
ctx.fillText(priceText, grid[0].left + yBoxWidth / 2, y + 3); |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
// 绘制十字准线
|
||||
|
drawCrosshair(ctx, width, height, grid, crosshair, text) { |
||||
|
if (!ctx) return; |
||||
|
const { x, y } = crosshair; |
||||
|
if (crosshair.show) { |
||||
|
// 每次绘制前先清除整个画布
|
||||
|
ctx.clearRect(0, 0, width, height); |
||||
|
|
||||
|
// 绘制垂直准线1
|
||||
|
ctx.setStrokeStyle("#000"); |
||||
|
ctx.setLineWidth(1); |
||||
|
// ctx.setLineDash([5, 5]);
|
||||
|
|
||||
|
for (let i = 0; i < grid.length; i++) { |
||||
|
ctx.beginPath(); |
||||
|
ctx.moveTo(x, grid[i].top); |
||||
|
ctx.lineTo(x, height - grid[i].bottom); |
||||
|
ctx.stroke(); |
||||
|
} |
||||
|
// ctx.beginPath();
|
||||
|
// ctx.moveTo(x, grid[0].top);
|
||||
|
// ctx.lineTo(x, height - grid[0].bottom);
|
||||
|
// ctx.stroke();
|
||||
|
|
||||
|
// // 绘制垂直准线2
|
||||
|
// ctx.beginPath();
|
||||
|
// ctx.moveTo(x, grid[1].top);
|
||||
|
// ctx.lineTo(x, height - grid[1].bottom);
|
||||
|
// ctx.stroke();
|
||||
|
|
||||
|
// 绘制水平准线
|
||||
|
ctx.beginPath(); |
||||
|
ctx.moveTo(grid[0].left, y); |
||||
|
ctx.lineTo(width - grid[0].right, y); |
||||
|
ctx.stroke(); |
||||
|
|
||||
|
ctx.setLineDash([]); |
||||
|
|
||||
|
// 绘制焦点圆点 - 黑边白心(更小)
|
||||
|
// 先绘制白色填充
|
||||
|
ctx.setFillStyle("#ffffff"); |
||||
|
ctx.beginPath(); |
||||
|
ctx.arc(x, y, 2, 0, Math.PI * 2); |
||||
|
ctx.fill(); |
||||
|
|
||||
|
// 再绘制黑色边框
|
||||
|
ctx.setStrokeStyle("#000000"); |
||||
|
ctx.setLineWidth(1); |
||||
|
ctx.setLineDash([]); |
||||
|
ctx.beginPath(); |
||||
|
ctx.arc(x, y, 2, 0, Math.PI * 2); |
||||
|
ctx.stroke(); |
||||
|
|
||||
|
// 绘制坐标轴标签
|
||||
|
HCharts.drawCrosshairAxisLabels(ctx, width, height, grid, crosshair); |
||||
|
} |
||||
|
// 绘制顶部价格显示
|
||||
|
HCharts.drawTopPriceDisplay(ctx, grid, text); |
||||
|
ctx.draw(false); |
||||
|
}, |
||||
|
}; |
||||
@ -0,0 +1,59 @@ |
|||||
|
function generateDailyPacket(stock_code, startDate, days, basePrice) { |
||||
|
const data = []; |
||||
|
let prevClose = Number(basePrice); |
||||
|
const toStrDate = (dateVal) => (typeof dateVal === 'number' ? String(dateVal) : dateVal); |
||||
|
const addDays = (yyyymmdd, delta) => { |
||||
|
const y = parseInt(yyyymmdd.slice(0, 4), 10); |
||||
|
const m = parseInt(yyyymmdd.slice(4, 6), 10) - 1; |
||||
|
const d = parseInt(yyyymmdd.slice(6, 8), 10); |
||||
|
const dt = new Date(y, m, d); |
||||
|
dt.setDate(dt.getDate() + delta); |
||||
|
const yyyy = dt.getFullYear(); |
||||
|
const mm = String(dt.getMonth() + 1).padStart(2, '0'); |
||||
|
const dd = String(dt.getDate()).padStart(2, '0'); |
||||
|
return `${yyyy}${mm}${dd}`; |
||||
|
}; |
||||
|
const randDrift = () => (Math.random() - 0.5) * 0.02; // 每日 ±1% 漂移
|
||||
|
const spread = 0.00050; // 买卖点差(可按需要调整)
|
||||
|
|
||||
|
let date = toStrDate(startDate); |
||||
|
for (let i = 0; i < days; i++) { |
||||
|
const open = +(prevClose * (1 + randDrift())).toFixed(5); |
||||
|
const close = +(open * (1 + randDrift())).toFixed(5); |
||||
|
const highRaw = Math.max(open, close) * (1 + Math.random() * 0.01); |
||||
|
const lowRaw = Math.min(open, close) * (1 - Math.random() * 0.01); |
||||
|
const high = +highRaw.toFixed(5); |
||||
|
const low = +lowRaw.toFixed(5); |
||||
|
|
||||
|
const tick_qty = Math.floor(1000 + Math.random() * 5000); // 成交量
|
||||
|
const amount = +(close * tick_qty).toFixed(2); // 成交额
|
||||
|
|
||||
|
const item = { |
||||
|
ts_code: stock_code, |
||||
|
trade_date: date, |
||||
|
bid_open: open, |
||||
|
bid_high: high, |
||||
|
bid_low: low, |
||||
|
bid_close: close, |
||||
|
ask_open: +(open + spread).toFixed(5), |
||||
|
ask_high: +(high + spread).toFixed(5), |
||||
|
ask_low: +(low + spread).toFixed(5), |
||||
|
ask_close: +(close + spread).toFixed(5), |
||||
|
tick_qty, |
||||
|
amount, |
||||
|
}; |
||||
|
|
||||
|
data.push(item); |
||||
|
prevClose = close; |
||||
|
date = addDays(date, 1); |
||||
|
} |
||||
|
|
||||
|
return { stock_code, data }; |
||||
|
} |
||||
|
// ... existing code ...
|
||||
|
export const dailyDataPackets = { |
||||
|
// 如已存在同名键,可合并或覆盖
|
||||
|
'GBPAUD.FXCM': generateDailyPacket('GBPAUD.FXCM', '20240101', 3000, 2.03893), |
||||
|
'EURUSD.FXCM': generateDailyPacket('EURUSD.FXCM', '20240101', 3000, 1.08350), |
||||
|
// 可按需继续添加更多代码
|
||||
|
}; |
||||
@ -0,0 +1,354 @@ |
|||||
|
/** @format */ |
||||
|
|
||||
|
export const prevClosePrice = 14.95; // 前一日收盘价(元)
|
||||
|
export const timeData = [ |
||||
|
// 上午时段:9:30-11:30(共120个数据点)
|
||||
|
{ time: "09:30", price: 15.0, volume: 28500 }, // 开盘价15.00元,开盘放量
|
||||
|
{ time: "09:31", price: 15.08, volume: 25300 }, |
||||
|
{ time: "09:32", price: 15.12, volume: 22800 }, |
||||
|
{ time: "09:33", price: 15.09, volume: 19600 }, |
||||
|
{ time: "09:34", price: 15.15, volume: 17200 }, |
||||
|
{ time: "09:35", price: 15.18, volume: 15800 }, |
||||
|
{ time: "09:36", price: 15.16, volume: 14300 }, |
||||
|
{ time: "09:37", price: 15.2, volume: 13500 }, |
||||
|
{ time: "09:38", price: 15.17, volume: 12800 }, |
||||
|
{ time: "09:39", price: 15.22, volume: 12100 }, |
||||
|
{ time: "09:40", price: 15.25, volume: 11500 }, |
||||
|
{ time: "09:41", price: 15.23, volume: 10800 }, |
||||
|
{ time: "09:42", price: 15.26, volume: 10200 }, |
||||
|
{ time: "09:43", price: 15.24, volume: 9800 }, |
||||
|
{ time: "09:44", price: 15.28, volume: 9500 }, |
||||
|
{ time: "09:45", price: 15.3, volume: 9200 }, |
||||
|
{ time: "09:46", price: 15.27, volume: 8800 }, |
||||
|
{ time: "09:47", price: 15.29, volume: 8500 }, |
||||
|
{ time: "09:48", price: 15.32, volume: 8200 }, |
||||
|
{ time: "09:49", price: 15.3, volume: 7900 }, |
||||
|
{ time: "09:50", price: 15.33, volume: 7600 }, |
||||
|
{ time: "09:51", price: 15.31, volume: 7400 }, |
||||
|
{ time: "09:52", price: 15.34, volume: 7200 }, |
||||
|
{ time: "09:53", price: 15.32, volume: 7000 }, |
||||
|
{ time: "09:54", price: 15.35, volume: 6800 }, |
||||
|
{ time: "09:55", price: 15.33, volume: 6600 }, |
||||
|
{ time: "09:56", price: 15.36, volume: 6500 }, |
||||
|
{ time: "09:57", price: 15.34, volume: 6300 }, |
||||
|
{ time: "09:58", price: 15.37, volume: 6200 }, |
||||
|
{ time: "09:59", price: 15.35, volume: 6100 }, |
||||
|
{ time: "10:00", price: 15.38, volume: 6000 }, |
||||
|
{ time: "10:01", price: 15.36, volume: 5900 }, |
||||
|
{ time: "10:02", price: 15.39, volume: 5800 }, |
||||
|
{ time: "10:03", price: 15.37, volume: 5700 }, |
||||
|
{ time: "10:04", price: 15.4, volume: 5600 }, |
||||
|
{ time: "10:05", price: 15.38, volume: 5500 }, |
||||
|
{ time: "10:06", price: 15.41, volume: 15400 }, |
||||
|
{ time: "10:07", price: 15.39, volume: 5300 }, |
||||
|
{ time: "10:08", price: 15.42, volume: 5200 }, |
||||
|
{ time: "10:09", price: 15.4, volume: 5100 }, |
||||
|
{ time: "10:10", price: 15.43, volume: 5000 }, |
||||
|
{ time: "10:11", price: 15.41, volume: 5100 }, |
||||
|
{ time: "10:12", price: 15.44, volume: 5200 }, |
||||
|
{ time: "10:13", price: 15.42, volume: 5300 }, |
||||
|
{ time: "10:14", price: 15.45, volume: 5400 }, |
||||
|
{ time: "10:15", price: 15.43, volume: 5500 }, |
||||
|
{ time: "10:16", price: 15.46, volume: 5600 }, |
||||
|
{ time: "10:17", price: 15.44, volume: 5700 }, |
||||
|
{ time: "10:18", price: 15.47, volume: 5800 }, |
||||
|
{ time: "10:19", price: 15.45, volume: 5900 }, |
||||
|
{ time: "10:20", price: 15.48, volume: 6000 }, |
||||
|
{ time: "10:21", price: 15.46, volume: 6100 }, |
||||
|
{ time: "10:22", price: 15.49, volume: 6200 }, |
||||
|
{ time: "10:23", price: 15.47, volume: 6300 }, |
||||
|
{ time: "10:24", price: 15.5, volume: 6400 }, |
||||
|
{ time: "10:25", price: 15.48, volume: 6500 }, |
||||
|
{ time: "10:26", price: 15.51, volume: 6600 }, |
||||
|
{ time: "10:27", price: 15.49, volume: 6700 }, |
||||
|
{ time: "10:28", price: 15.52, volume: 6800 }, |
||||
|
{ time: "10:29", price: 15.5, volume: 6900 }, |
||||
|
{ time: "10:30", price: 15.53, volume: 7000 }, |
||||
|
{ time: "10:31", price: 15.51, volume: 7100 }, |
||||
|
{ time: "10:32", price: 15.54, volume: 7200 }, |
||||
|
{ time: "10:33", price: 15.52, volume: 7300 }, |
||||
|
{ time: "10:34", price: 15.55, volume: 7400 }, |
||||
|
{ time: "10:35", price: 15.53, volume: 7500 }, |
||||
|
{ time: "10:36", price: 15.56, volume: 7600 }, |
||||
|
{ time: "10:37", price: 15.54, volume: 7700 }, |
||||
|
{ time: "10:38", price: 15.57, volume: 7800 }, |
||||
|
{ time: "10:39", price: 15.55, volume: 7900 }, |
||||
|
{ time: "10:40", price: 15.58, volume: 8000 }, |
||||
|
{ time: "10:41", price: 15.56, volume: 8100 }, |
||||
|
{ time: "10:42", price: 15.59, volume: 8200 }, |
||||
|
{ time: "10:43", price: 15.57, volume: 8300 }, |
||||
|
{ time: "10:44", price: 15.6, volume: 8400 }, // 全天最高价15.60元
|
||||
|
{ time: "10:45", price: 15.58, volume: 8300 }, |
||||
|
{ time: "10:46", price: 15.56, volume: 8200 }, |
||||
|
{ time: "10:47", price: 15.54, volume: 8100 }, |
||||
|
{ time: "10:48", price: 15.52, volume: 8000 }, |
||||
|
{ time: "10:49", price: 15.5, volume: 7900 }, |
||||
|
{ time: "10:50", price: 15.48, volume: 7800 }, |
||||
|
{ time: "10:51", price: 15.46, volume: 7700 }, |
||||
|
{ time: "10:52", price: 15.44, volume: 7600 }, |
||||
|
{ time: "10:53", price: 15.42, volume: 7500 }, |
||||
|
{ time: "10:54", price: 15.4, volume: 7400 }, |
||||
|
{ time: "10:55", price: 15.38, volume: 7300 }, |
||||
|
{ time: "10:56", price: 15.36, volume: 7200 }, |
||||
|
{ time: "10:57", price: 15.34, volume: 7100 }, |
||||
|
{ time: "10:58", price: 15.32, volume: 7000 }, |
||||
|
{ time: "10:59", price: 15.3, volume: 6900 }, |
||||
|
{ time: "11:00", price: 15.28, volume: 6800 }, |
||||
|
{ time: "11:01", price: 15.26, volume: 6700 }, |
||||
|
{ time: "11:02", price: 15.24, volume: 6600 }, |
||||
|
{ time: "11:03", price: 15.22, volume: 6500 }, |
||||
|
{ time: "11:04", price: 15.2, volume: 6400 }, // 全天最低价15.20元
|
||||
|
{ time: "11:05", price: 15.22, volume: 6500 }, |
||||
|
{ time: "11:06", price: 15.24, volume: 6600 }, |
||||
|
{ time: "11:07", price: 15.26, volume: 6700 }, |
||||
|
{ time: "11:08", price: 15.28, volume: 6800 }, |
||||
|
{ time: "11:09", price: 15.3, volume: 6900 }, |
||||
|
{ time: "11:10", price: 15.32, volume: 7000 }, |
||||
|
{ time: "11:11", price: 15.34, volume: 7100 }, |
||||
|
{ time: "11:12", price: 15.36, volume: 7200 }, |
||||
|
{ time: "11:13", price: 15.38, volume: 7300 }, |
||||
|
{ time: "11:14", price: 15.4, volume: 7400 }, |
||||
|
{ time: "11:15", price: 15.42, volume: 7500 }, |
||||
|
{ time: "11:16", price: 15.44, volume: 7600 }, |
||||
|
{ time: "11:17", price: 15.46, volume: 7700 }, |
||||
|
{ time: "11:18", price: 15.48, volume: 7800 }, |
||||
|
{ time: "11:19", price: 15.5, volume: 7900 }, |
||||
|
{ time: "11:20", price: 15.45, volume: 8300 }, |
||||
|
{ time: "11:21", price: 15.47, volume: 8600 }, |
||||
|
{ time: "11:22", price: 15.43, volume: 9100 }, |
||||
|
{ time: "11:23", price: 15.46, volume: 9500 }, |
||||
|
{ time: "11:24", price: 15.49, volume: 10200 }, |
||||
|
{ time: "11:25", price: 15.5, volume: 11500 }, |
||||
|
{ time: "11:26", price: 15.48, volume: 12800 }, |
||||
|
{ time: "11:27", price: 15.52, volume: 14300 }, |
||||
|
{ time: "11:28", price: 15.5, volume: 16500 }, |
||||
|
{ time: "11:29", price: 15.53, volume: 19800 }, // 午盘收盘价15.53元
|
||||
|
|
||||
|
// 下午时段:13:00-15:00(共120个数据点)
|
||||
|
{ time: "13:00", price: 15.55, volume: 24600 }, // 午后开盘冲高
|
||||
|
{ time: "13:01", price: 15.58, volume: 21300 }, |
||||
|
{ time: "13:02", price: 15.6, volume: 18700 }, // 再次触及全天最高价
|
||||
|
{ time: "13:03", price: 15.57, volume: 16200 }, |
||||
|
{ time: "13:04", price: 15.55, volume: 14500 }, |
||||
|
{ time: "13:05", price: 15.52, volume: 12800 }, |
||||
|
{ time: "13:06", price: 15.5, volume: 11300 }, |
||||
|
{ time: "13:07", price: 15.48, volume: 10100 }, |
||||
|
{ time: "13:08", price: 15.5, volume: 9500 }, |
||||
|
{ time: "13:09", price: 15.47, volume: 8900 }, |
||||
|
{ time: "13:10", price: 15.45, volume: 8300 }, |
||||
|
{ time: "13:11", price: 15.43, volume: 7800 }, |
||||
|
{ time: "13:12", price: 15.46, volume: 7500 }, |
||||
|
{ time: "13:13", price: 15.44, volume: 7200 }, |
||||
|
{ time: "13:14", price: 15.42, volume: 6900 }, |
||||
|
{ time: "13:15", price: 15.45, volume: 6700 }, |
||||
|
{ time: "13:16", price: 15.43, volume: 6500 }, |
||||
|
{ time: "13:17", price: 15.4, volume: 6300 }, |
||||
|
{ time: "13:18", price: 15.42, volume: 6100 }, |
||||
|
{ time: "13:19", price: 15.39, volume: 5900 }, |
||||
|
{ time: "13:20", price: 15.41, volume: 5800 }, |
||||
|
{ time: "13:21", price: 15.39, volume: 5700 }, |
||||
|
{ time: "13:22", price: 15.42, volume: 5600 }, |
||||
|
{ time: "13:23", price: 15.4, volume: 5500 }, |
||||
|
{ time: "13:24", price: 15.43, volume: 5400 }, |
||||
|
{ time: "13:25", price: 15.41, volume: 5300 }, |
||||
|
{ time: "13:26", price: 15.44, volume: 5200 }, |
||||
|
{ time: "13:27", price: 15.42, volume: 5100 }, |
||||
|
{ time: "13:28", price: 15.45, volume: 5000 }, |
||||
|
{ time: "13:29", price: 15.43, volume: 5100 }, |
||||
|
{ time: "13:30", price: 15.46, volume: 5200 }, |
||||
|
{ time: "13:31", price: 15.44, volume: 5300 }, |
||||
|
{ time: "13:32", price: 15.47, volume: 5400 }, |
||||
|
{ time: "13:33", price: 15.45, volume: 5500 }, |
||||
|
{ time: "13:34", price: 15.48, volume: 5600 }, |
||||
|
{ time: "13:35", price: 15.46, volume: 5700 }, |
||||
|
{ time: "13:36", price: 15.49, volume: 5800 }, |
||||
|
{ time: "13:37", price: 15.47, volume: 5900 }, |
||||
|
{ time: "13:38", price: 15.5, volume: 6000 }, |
||||
|
{ time: "13:39", price: 15.48, volume: 6100 }, |
||||
|
{ time: "13:40", price: 15.51, volume: 6200 }, |
||||
|
{ time: "13:41", price: 15.49, volume: 6300 }, |
||||
|
{ time: "13:42", price: 15.52, volume: 6400 }, |
||||
|
{ time: "13:43", price: 15.5, volume: 6500 }, |
||||
|
{ time: "13:44", price: 15.53, volume: 6600 }, |
||||
|
{ time: "13:45", price: 15.51, volume: 6700 }, |
||||
|
{ time: "13:46", price: 15.54, volume: 6800 }, |
||||
|
{ time: "13:47", price: 15.52, volume: 6900 }, |
||||
|
{ time: "13:48", price: 15.55, volume: 7000 }, |
||||
|
{ time: "13:49", price: 15.53, volume: 7100 }, |
||||
|
{ time: "13:50", price: 15.56, volume: 7200 }, |
||||
|
{ time: "13:51", price: 15.54, volume: 7300 }, |
||||
|
{ time: "13:52", price: 15.57, volume: 7400 }, |
||||
|
{ time: "13:53", price: 15.55, volume: 7500 }, |
||||
|
{ time: "13:54", price: 15.58, volume: 7600 }, |
||||
|
{ time: "13:55", price: 15.56, volume: 7700 }, |
||||
|
{ time: "13:56", price: 15.59, volume: 7800 }, |
||||
|
{ time: "13:57", price: 15.57, volume: 7900 }, |
||||
|
{ time: "13:58", price: 15.6, volume: 8000 }, // 第三次触及全天最高价
|
||||
|
{ time: "13:59", price: 15.58, volume: 8100 }, |
||||
|
{ time: "14:00", price: 15.56, volume: 8200 }, |
||||
|
{ time: "14:01", price: 15.54, volume: 8300 }, |
||||
|
{ time: "14:02", price: 15.52, volume: 8400 }, |
||||
|
{ time: "14:03", price: 15.5, volume: 8300 }, |
||||
|
{ time: "14:04", price: 15.48, volume: 8200 }, |
||||
|
{ time: "14:05", price: 15.46, volume: 8100 }, |
||||
|
{ time: "14:06", price: 15.44, volume: 8000 }, |
||||
|
{ time: "14:07", price: 15.42, volume: 7900 }, |
||||
|
{ time: "14:08", price: 15.4, volume: 7800 }, |
||||
|
{ time: "14:09", price: 15.38, volume: 7700 }, |
||||
|
{ time: "14:10", price: 15.36, volume: 7600 }, |
||||
|
{ time: "14:11", price: 15.34, volume: 7500 }, |
||||
|
{ time: "14:12", price: 15.32, volume: 7400 }, |
||||
|
{ time: "14:13", price: 15.3, volume: 7300 }, |
||||
|
{ time: "14:14", price: 15.28, volume: 7200 }, |
||||
|
{ time: "14:15", price: 15.26, volume: 7100 }, |
||||
|
{ time: "14:16", price: 15.24, volume: 7000 }, |
||||
|
{ time: "14:17", price: 15.22, volume: 6900 }, |
||||
|
{ time: "14:18", price: 15.2, volume: 6800 }, // 再次触及全天最低价
|
||||
|
{ time: "14:19", price: 15.22, volume: 6700 }, |
||||
|
{ time: "14:20", price: 15.24, volume: 6600 }, |
||||
|
{ time: "14:21", price: 15.26, volume: 6500 }, |
||||
|
{ time: "14:22", price: 15.28, volume: 6400 }, |
||||
|
{ time: "14:23", price: 15.3, volume: 6300 }, |
||||
|
{ time: "14:24", price: 15.32, volume: 6200 }, |
||||
|
{ time: "14:25", price: 15.34, volume: 6100 }, |
||||
|
{ time: "14:26", price: 15.36, volume: 6000 }, |
||||
|
{ time: "14:27", price: 15.38, volume: 5900 }, |
||||
|
{ time: "14:28", price: 15.4, volume: 5800 }, |
||||
|
{ time: "14:29", price: 15.42, volume: 5700 }, |
||||
|
{ time: "14:30", price: 15.44, volume: 5600 }, |
||||
|
{ time: "14:31", price: 15.46, volume: 5500 }, |
||||
|
{ time: "14:32", price: 15.48, volume: 5400 }, |
||||
|
{ time: "14:33", price: 15.5, volume: 5300 }, |
||||
|
{ time: "14:34", price: 15.52, volume: 5200 }, |
||||
|
{ time: "14:35", price: 15.54, volume: 5100 }, |
||||
|
{ time: "14:36", price: 15.56, volume: 5000 }, |
||||
|
{ time: "14:37", price: 15.54, volume: 5100 }, |
||||
|
{ time: "14:38", price: 15.52, volume: 5200 }, |
||||
|
{ time: "14:39", price: 15.5, volume: 5300 }, |
||||
|
{ time: "14:40", price: 15.48, volume: 5400 }, |
||||
|
{ time: "14:41", price: 15.46, volume: 5500 }, |
||||
|
{ time: "14:42", price: 15.44, volume: 5600 }, |
||||
|
{ time: "14:43", price: 15.42, volume: 5700 }, |
||||
|
{ time: "14:44", price: 15.4, volume: 5800 }, |
||||
|
{ time: "14:45", price: 15.38, volume: 5900 }, |
||||
|
{ time: "14:46", price: 15.36, volume: 6000 }, |
||||
|
{ time: "14:47", price: 15.34, volume: 6100 }, |
||||
|
{ time: "14:48", price: 15.32, volume: 6200 }, |
||||
|
{ time: "14:49", price: 15.3, volume: 6300 }, |
||||
|
{ time: "14:50", price: 15.42, volume: 9800 }, // 尾盘开始放量
|
||||
|
{ time: "14:51", price: 15.45, volume: 11500 }, |
||||
|
{ time: "14:52", price: 15.43, volume: 13200 }, |
||||
|
{ time: "14:53", price: 15.46, volume: 15800 }, |
||||
|
{ time: "14:54", price: 15.44, volume: 18500 }, |
||||
|
{ time: "14:55", price: 15.47, volume: 21300 }, |
||||
|
{ time: "14:56", price: 15.45, volume: 24600 }, |
||||
|
{ time: "14:57", price: 15.48, volume: 27800 }, |
||||
|
{ time: "14:58", price: 15.46, volume: 31200 }, // 尾盘成交量峰值
|
||||
|
{ time: "14:59", price: 15.45, volume: 28500 }, // 当日收盘价15.45元
|
||||
|
]; |
||||
|
export const klineData = [ |
||||
|
// 第1天(起始点,位于区间中部)
|
||||
|
{ date: "2015-10-11", open: 16.5, high: 16.8, low: 16.2, close: 16.6, volume: 185000 }, |
||||
|
// 第2-90天(区间震荡:15.5-17.5元)
|
||||
|
{ date: "2015-10-12", open: 16.6, high: 16.9, low: 16.4, close: 16.7, volume: 192000 }, |
||||
|
{ date: "2015-10-13", open: 16.7, high: 17.0, low: 16.5, close: 16.6, volume: 188000 }, |
||||
|
{ date: "2015-10-14", open: 16.6, high: 16.8, low: 16.3, close: 16.4, volume: 175000 }, |
||||
|
{ date: "2015-10-15", open: 16.4, high: 16.7, low: 16.2, close: 16.5, volume: 181000 }, |
||||
|
{ date: "2015-10-16", open: 16.5, high: 16.9, low: 16.3, close: 16.8, volume: 195000 }, |
||||
|
{ date: "2015-10-17", open: 16.8, high: 17.1, low: 16.6, close: 16.7, volume: 202000 }, |
||||
|
{ date: "2015-10-18", open: 16.7, high: 16.9, low: 16.4, close: 16.5, volume: 183000 }, |
||||
|
{ date: "2015-10-19", open: 16.5, high: 16.7, low: 16.1, close: 16.3, volume: 172000 }, |
||||
|
{ date: "2015-10-20", open: 16.3, high: 16.6, low: 16.0, close: 16.4, volume: 178000 }, |
||||
|
{ date: "2015-10-21", open: 16.4, high: 16.8, low: 16.2, close: 16.6, volume: 189000 }, |
||||
|
{ date: "2015-10-22", open: 16.6, high: 17.0, low: 16.5, close: 16.9, volume: 205000 }, |
||||
|
{ date: "2015-10-23", open: 16.9, high: 17.2, low: 16.7, close: 16.8, volume: 212000 }, |
||||
|
{ date: "2015-10-24", open: 16.8, high: 17.0, low: 16.5, close: 16.6, volume: 193000 }, |
||||
|
{ date: "2015-10-25", open: 16.6, high: 16.8, low: 16.2, close: 16.3, volume: 176000 }, |
||||
|
{ date: "2015-10-26", open: 16.3, high: 16.6, low: 16.0, close: 16.5, volume: 184000 }, |
||||
|
{ date: "2015-10-27", open: 16.5, high: 16.9, low: 16.4, close: 16.7, volume: 196000 }, |
||||
|
{ date: "2015-10-28", open: 16.7, high: 17.1, low: 16.6, close: 16.9, volume: 208000 }, |
||||
|
{ date: "2015-10-29", open: 16.9, high: 17.3, low: 16.8, close: 17.0, volume: 215000 }, |
||||
|
{ date: "2015-10-30", open: 17.0, high: 17.2, low: 16.7, close: 16.8, volume: 201000 }, |
||||
|
{ date: "2015-10-31", open: 16.8, high: 17.0, low: 16.5, close: 16.6, volume: 189000 }, |
||||
|
{ date: "2015-11-01", open: 16.6, high: 16.8, low: 16.2, close: 16.4, volume: 175000 }, |
||||
|
{ date: "2015-11-02", open: 16.4, high: 16.7, low: 16.1, close: 16.3, volume: 171000 }, |
||||
|
{ date: "2015-11-03", open: 16.3, high: 16.6, low: 16.0, close: 16.5, volume: 182000 }, |
||||
|
{ date: "2015-11-04", open: 16.5, high: 16.9, low: 16.3, close: 16.7, volume: 194000 }, |
||||
|
{ date: "2015-11-05", open: 16.7, high: 17.1, low: 16.6, close: 16.8, volume: 203000 }, |
||||
|
{ date: "2015-11-06", open: 16.8, high: 17.0, low: 16.5, close: 16.6, volume: 190000 }, |
||||
|
{ date: "2015-11-07", open: 16.6, high: 16.8, low: 16.3, close: 16.4, volume: 178000 }, |
||||
|
{ date: "2015-11-08", open: 16.4, high: 16.7, low: 16.1, close: 16.3, volume: 173000 }, |
||||
|
{ date: "2015-11-09", open: 16.3, high: 16.6, low: 15.9, close: 16.2, volume: 168000 }, // 触及区间下沿
|
||||
|
{ date: "2015-11-10", open: 16.2, high: 16.5, low: 16.0, close: 16.4, volume: 176000 }, |
||||
|
{ date: "2015-11-11", open: 16.4, high: 16.8, low: 16.3, close: 16.6, volume: 187000 }, |
||||
|
{ date: "2015-11-12", open: 16.6, high: 17.0, low: 16.5, close: 16.8, volume: 198000 }, |
||||
|
{ date: "2015-11-13", open: 16.8, high: 17.2, low: 16.7, close: 16.9, volume: 206000 }, |
||||
|
{ date: "2015-11-14", open: 16.9, high: 17.3, low: 16.8, close: 17.1, volume: 218000 }, |
||||
|
{ date: "2015-11-15", open: 17.1, high: 17.4, low: 16.9, close: 17.0, volume: 212000 }, |
||||
|
{ date: "2015-11-16", open: 17.0, high: 17.2, low: 16.7, close: 16.8, volume: 197000 }, |
||||
|
{ date: "2015-11-17", open: 16.8, high: 17.0, low: 16.5, close: 16.6, volume: 185000 }, |
||||
|
{ date: "2015-11-18", open: 16.6, high: 16.8, low: 16.3, close: 16.4, volume: 177000 }, |
||||
|
{ date: "2015-11-19", open: 16.4, high: 16.7, low: 16.1, close: 16.3, volume: 172000 }, |
||||
|
{ date: "2015-11-20", open: 16.3, high: 16.6, low: 16.0, close: 16.5, volume: 183000 }, |
||||
|
{ date: "2015-11-21", open: 16.5, high: 16.9, low: 16.4, close: 16.7, volume: 195000 }, |
||||
|
{ date: "2015-11-22", open: 16.7, high: 17.1, low: 16.6, close: 16.9, volume: 204000 }, |
||||
|
{ date: "2015-11-23", open: 16.9, high: 17.2, low: 16.8, close: 17.0, volume: 213000 }, |
||||
|
{ date: "2015-11-24", open: 17.0, high: 17.3, low: 16.9, close: 17.1, volume: 221000 }, |
||||
|
{ date: "2015-11-25", open: 17.1, high: 17.4, low: 17.0, close: 17.2, volume: 228000 }, // 触及区间上沿
|
||||
|
{ date: "2015-11-26", open: 17.2, high: 17.3, low: 16.8, close: 16.9, volume: 215000 }, |
||||
|
{ date: "2015-11-27", open: 16.9, high: 17.1, low: 16.6, close: 16.7, volume: 199000 }, |
||||
|
{ date: "2015-11-28", open: 16.7, high: 16.9, low: 16.4, close: 16.5, volume: 186000 }, |
||||
|
{ date: "2015-11-29", open: 16.5, high: 16.7, low: 16.2, close: 16.3, volume: 175000 }, |
||||
|
{ date: "2015-11-30", open: 16.3, high: 16.6, low: 16.0, close: 16.4, volume: 179000 }, |
||||
|
{ date: "2015-12-01", open: 16.4, high: 16.8, low: 16.3, close: 16.6, volume: 188000 }, |
||||
|
{ date: "2015-12-02", open: 16.6, high: 17.0, low: 16.5, close: 16.8, volume: 199000 }, |
||||
|
{ date: "2015-12-03", open: 16.8, high: 17.2, low: 16.7, close: 16.9, volume: 207000 }, |
||||
|
{ date: "2015-12-04", open: 16.9, high: 17.1, low: 16.6, close: 16.7, volume: 193000 }, |
||||
|
{ date: "2015-12-05", open: 16.7, high: 16.9, low: 16.4, close: 16.5, volume: 182000 }, |
||||
|
{ date: "2015-12-06", open: 16.5, high: 16.7, low: 16.2, close: 16.3, volume: 173000 }, |
||||
|
{ date: "2015-12-07", open: 16.3, high: 16.6, low: 15.9, close: 16.1, volume: 167000 }, // 触及区间下沿
|
||||
|
{ date: "2015-12-08", open: 16.1, high: 16.4, low: 16.0, close: 16.3, volume: 174000 }, |
||||
|
{ date: "2015-12-09", open: 16.3, high: 16.7, low: 16.2, close: 16.5, volume: 185000 }, |
||||
|
{ date: "2015-12-10", open: 16.5, high: 16.9, low: 16.4, close: 16.7, volume: 196000 }, |
||||
|
{ date: "2015-12-11", open: 16.7, high: 17.1, low: 16.6, close: 16.9, volume: 205000 }, |
||||
|
{ date: "2015-12-12", open: 16.9, high: 17.3, low: 16.8, close: 17.0, volume: 214000 }, |
||||
|
{ date: "2015-12-13", open: 17.0, high: 17.2, low: 16.8, close: 16.9, volume: 203000 }, |
||||
|
{ date: "2015-12-14", open: 16.9, high: 17.1, low: 16.6, close: 16.7, volume: 191000 }, |
||||
|
{ date: "2015-12-15", open: 16.7, high: 16.9, low: 16.4, close: 16.5, volume: 180000 }, |
||||
|
{ date: "2015-12-16", open: 16.5, high: 16.7, low: 16.2, close: 16.3, volume: 172000 }, |
||||
|
{ date: "2015-12-17", open: 16.3, high: 16.6, low: 16.0, close: 16.4, volume: 178000 }, |
||||
|
{ date: "2015-12-18", open: 16.4, high: 16.8, low: 16.3, close: 16.6, volume: 189000 }, |
||||
|
{ date: "2015-12-19", open: 16.6, high: 17.0, low: 16.5, close: 16.8, volume: 200000 }, |
||||
|
{ date: "2015-12-20", open: 16.8, high: 17.2, low: 16.7, close: 16.9, volume: 208000 }, |
||||
|
{ date: "2015-12-21", open: 16.9, high: 17.3, low: 16.8, close: 17.1, volume: 219000 }, |
||||
|
{ date: "2015-12-22", open: 17.1, high: 17.4, low: 17.0, close: 17.2, volume: 226000 }, // 触及区间上沿
|
||||
|
{ date: "2015-12-23", open: 17.2, high: 17.3, low: 16.8, close: 16.9, volume: 213000 }, |
||||
|
{ date: "2015-12-24", open: 16.9, high: 17.1, low: 16.6, close: 16.7, volume: 198000 }, |
||||
|
{ date: "2015-12-25", open: 16.7, high: 16.9, low: 16.4, close: 16.5, volume: 185000 }, |
||||
|
{ date: "2015-12-26", open: 16.5, high: 16.7, low: 16.2, close: 16.3, volume: 174000 }, |
||||
|
{ date: "2015-12-27", open: 16.3, high: 16.6, low: 16.0, close: 16.5, volume: 183000 }, |
||||
|
{ date: "2015-12-28", open: 16.5, high: 16.9, low: 16.4, close: 16.7, volume: 195000 }, |
||||
|
{ date: "2015-12-29", open: 16.7, high: 17.1, low: 16.6, close: 16.9, volume: 204000 }, |
||||
|
{ date: "2015-12-30", open: 16.9, high: 17.2, low: 16.8, close: 17.0, volume: 212000 }, |
||||
|
{ date: "2015-12-31", open: 17.0, high: 17.3, low: 16.9, close: 17.1, volume: 220000 }, |
||||
|
{ date: "2016-01-01", open: 17.1, high: 17.2, low: 16.8, close: 16.9, volume: 207000 }, |
||||
|
{ date: "2016-01-02", open: 16.9, high: 17.1, low: 16.6, close: 16.7, volume: 193000 }, |
||||
|
{ date: "2016-01-03", open: 16.7, high: 16.9, low: 16.4, close: 16.5, volume: 181000 }, |
||||
|
{ date: "2016-01-04", open: 16.5, high: 16.7, low: 16.2, close: 16.3, volume: 172000 }, |
||||
|
{ date: "2016-01-05", open: 16.3, high: 16.6, low: 15.9, close: 16.2, volume: 168000 }, // 触及区间下沿
|
||||
|
{ date: "2016-01-06", open: 16.2, high: 16.5, low: 16.0, close: 16.4, volume: 175000 }, |
||||
|
{ date: "2016-01-07", open: 16.4, high: 16.8, low: 16.3, close: 16.6, volume: 186000 }, |
||||
|
{ date: "2016-01-08", open: 16.6, high: 17.0, low: 16.5, close: 16.8, volume: 197000 }, |
||||
|
{ date: "2016-01-09", open: 16.8, high: 17.2, low: 16.7, close: 16.9, volume: 206000 }, |
||||
|
{ date: "2016-01-10", open: 16.9, high: 17.3, low: 16.8, close: 17.1, volume: 217000 }, |
||||
|
{ date: "2016-01-11", open: 17.1, high: 17.4, low: 17.0, close: 17.2, volume: 225000 }, // 触及区间上沿
|
||||
|
{ date: "2016-01-12", open: 17.2, high: 17.3, low: 16.8, close: 16.9, volume: 212000 }, |
||||
|
{ date: "2016-01-13", open: 16.9, high: 17.1, low: 16.6, close: 16.7, volume: 197000 }, |
||||
|
{ date: "2016-01-14", open: 16.7, high: 16.9, low: 16.4, close: 16.5, volume: 184000 }, |
||||
|
{ date: "2016-01-15", open: 16.5, high: 16.7, low: 16.2, close: 16.4, volume: 175000 }, |
||||
|
{ date: "2016-01-16", open: 16.4, high: 16.7, low: 16.1, close: 16.3, volume: 171000 }, |
||||
|
{ date: "2016-01-17", open: 16.3, high: 16.6, low: 16.0, close: 16.5, volume: 182000 }, |
||||
|
{ date: "2016-01-18", open: 16.5, high: 16.9, low: 16.4, close: 16.7, volume: 194000 }, |
||||
|
{ date: "2016-01-19", open: 16.7, high: 17.1, low: 16.6, close: 16.9, volume: 203000 }, |
||||
|
{ date: "2016-01-20", open: 16.9, high: 17.2, low: 16.8, close: 17.0, volume: 212000 }, |
||||
|
]; |
||||
@ -0,0 +1,143 @@ |
|||||
|
var util = {} |
||||
|
util.data = {} |
||||
|
util.data.base_url = 'https://dbqb.nfdxy.net/testApi' |
||||
|
// util.data.base_url = 'https://dbqb.nfdxy.net/prodApi'
|
||||
|
|
||||
|
// AJAX 请求方法
|
||||
|
util.request = (url, callback, data = {}, failCallback) => { |
||||
|
url = util.data.base_url + url |
||||
|
console.log('请求该接口->', url, '请求参数为->', data); |
||||
|
uni.request({ |
||||
|
url: url, //仅为示例,并非真实接口地址。
|
||||
|
data, |
||||
|
method: 'post', |
||||
|
header: { |
||||
|
'content-type': 'application/json', |
||||
|
'version': uni.getSystemInfoSync().appVersion, |
||||
|
'client': uni.getSystemInfoSync().platform == 'ios' ? 'ios' : 'android', |
||||
|
'token': uni.getStorageSync('token'), |
||||
|
'deviceId': uni.getSystemInfoSync().deviceId |
||||
|
}, |
||||
|
sslVerify: false, |
||||
|
success: callback, |
||||
|
fail: failCallback |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
export default util |
||||
|
|
||||
|
// 画图需要用到的方法
|
||||
|
export const utils = { |
||||
|
// 格式化价格
|
||||
|
formatPrice(price) { |
||||
|
return price.toFixed(2); |
||||
|
}, |
||||
|
// 计算数据范围
|
||||
|
calculateDataRange(data, key) { |
||||
|
if (!data || data.length === 0) { |
||||
|
return { |
||||
|
min: 0, |
||||
|
max: 0, |
||||
|
}; |
||||
|
} |
||||
|
const values = data.map((item) => item[key]); |
||||
|
return { |
||||
|
min: Math.min(...values), |
||||
|
max: Math.max(...values), |
||||
|
}; |
||||
|
}, |
||||
|
// 计算标签
|
||||
|
calculateLabel(data, type = 2, preClosePrice = 0, key, num) { |
||||
|
let label = []; |
||||
|
if (key === "price") { |
||||
|
// 分时价格区间
|
||||
|
if (type == 1) { |
||||
|
const priceRange = utils.calculateDataRange(data, "price"); |
||||
|
const theMost = Math.max(priceRange.max - preClosePrice, preClosePrice - priceRange.min); |
||||
|
const mid = (num - 1) / 2; |
||||
|
// 计算分时价格标签
|
||||
|
label[mid] = { |
||||
|
value: utils.formatPrice(preClosePrice), |
||||
|
ratio: utils.formatPrice(0) + "%", |
||||
|
}; |
||||
|
for (let i = 0; i < mid; i++) { |
||||
|
label[i] = { |
||||
|
value: utils.formatPrice(preClosePrice + (theMost * (mid - i)) / mid), |
||||
|
ratio: utils.formatPrice((100 * (theMost * (mid - i))) / mid / preClosePrice) + "%", |
||||
|
}; |
||||
|
|
||||
|
label[num - 1 - i] = { |
||||
|
value: utils.formatPrice(preClosePrice - (theMost * (mid - i)) / mid), |
||||
|
ratio: utils.formatPrice((-1 * 100 * (theMost * (mid - i))) / mid / preClosePrice) + |
||||
|
"%", |
||||
|
}; |
||||
|
} |
||||
|
timeChartObject.value.max = preClosePrice + theMost; |
||||
|
timeChartObject.value.min = preClosePrice - theMost; |
||||
|
return label; |
||||
|
} else { |
||||
|
const highPriceRange = utils.calculateDataRange(data, "high"); |
||||
|
const lowPriceRange = utils.calculateDataRange(data, "low"); |
||||
|
const priceDiff = highPriceRange.max * 1.01 - lowPriceRange.min * 0.99; |
||||
|
for (let i = 0; i < num; ++i) { |
||||
|
label[i] = { |
||||
|
value: utils.formatPrice(highPriceRange.max - (i * priceDiff) / num), |
||||
|
}; |
||||
|
} |
||||
|
return label; |
||||
|
} |
||||
|
} else if (key === "volume") { |
||||
|
const volumeRange = utils.calculateDataRange(data, "volume"); |
||||
|
label[0] = { |
||||
|
value: utils.formatPrice(volumeRange.max), |
||||
|
}; |
||||
|
label[1] = { |
||||
|
value: utils.formatPrice(0), |
||||
|
}; |
||||
|
return label; |
||||
|
} |
||||
|
return null; |
||||
|
}, |
||||
|
// 线性插值
|
||||
|
lerp(start, end, factor) { |
||||
|
return start + (end - start) * factor; |
||||
|
}, |
||||
|
// 股市数值格式化方法
|
||||
|
formatStockNumber(value, decimalPlaces = 2) { |
||||
|
const num = Number(value); |
||||
|
if (isNaN(num)) return "0"; |
||||
|
|
||||
|
const absNum = Math.abs(num); |
||||
|
const sign = num < 0 ? "-" : ""; |
||||
|
|
||||
|
if (absNum >= 1000000000000) { |
||||
|
// 万亿级别
|
||||
|
return sign + (absNum / 1000000000000).toFixed(decimalPlaces) + "万亿"; |
||||
|
} else if (absNum >= 100000000) { |
||||
|
// 亿级别
|
||||
|
return sign + (absNum / 100000000).toFixed(decimalPlaces) + "亿"; |
||||
|
} else if (absNum >= 10000) { |
||||
|
// 万级别
|
||||
|
return sign + (absNum / 10000).toFixed(decimalPlaces) + "万"; |
||||
|
} else { |
||||
|
// 小于万的直接显示
|
||||
|
return sign + absNum.toFixed(decimalPlaces); |
||||
|
} |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
// 防抖函数
|
||||
|
export const throttle = (fn, delay = 1000) => { |
||||
|
//距离上一次的执行时间
|
||||
|
let lastTime = 0; |
||||
|
return function() { |
||||
|
let _this = this; |
||||
|
let _arguments = arguments; |
||||
|
let now = new Date().getTime(); |
||||
|
//如果距离上一次执行超过了delay才能再次执行
|
||||
|
if (now - lastTime > delay) { |
||||
|
fn.apply(_this, _arguments); |
||||
|
lastTime = now; |
||||
|
} |
||||
|
}; |
||||
|
}; |
||||
@ -0,0 +1,281 @@ |
|||||
|
<template> |
||||
|
<view class="deepmate"> |
||||
|
<view class="deepmate-container"> |
||||
|
<view class="deepmate-header"> |
||||
|
<view class="title-container"> |
||||
|
<view class="title-left"> |
||||
|
<text class="deepmate-title">DeepMate</text> |
||||
|
<text class="deepmate-subtitle">您的市场最佳顾问~</text> |
||||
|
</view> |
||||
|
<view class="title-right"> |
||||
|
<image class="deepmate-icon" src="https://d31zlh4on95l9h.cloudfront.net/images/7faa683450cc071bcc746fea8191ff6b.png" mode="aspectFit"></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="deepmate-content"> |
||||
|
<view class="deepmate-hotspots"> |
||||
|
<view class="deepmate-question"> |
||||
|
<text class="question-text">今日股票策略晨报</text> |
||||
|
</view> |
||||
|
<view class="hotspot-item"> |
||||
|
<text>热门股票分析</text> |
||||
|
</view> |
||||
|
<view class="hotspot-item"> |
||||
|
<text>行业趋势预测</text> |
||||
|
</view> |
||||
|
<view class="hotspot-item"> |
||||
|
<text>市场风险提示</text> |
||||
|
</view> |
||||
|
<view class="hotspot-item"> |
||||
|
<text>投资策略建议</text> |
||||
|
</view> |
||||
|
<view class="hotspot-item"> |
||||
|
<text>财经新闻解读</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="deepmate-action"> |
||||
|
<input |
||||
|
class="stock-input" |
||||
|
type="text" |
||||
|
placeholder="请输入股票代码/名称,获取AI洞察" |
||||
|
v-model="inputValue" |
||||
|
@confirm="handleSend" |
||||
|
/> |
||||
|
<view class="send-button-container" @click="handleSend"> |
||||
|
<image class="send-button" src="https://d31zlh4on95l9h.cloudfront.net/images/3da018821a5c82b06a1d6ddc81b960ac.png" mode="aspectFit"></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'DeepMate', |
||||
|
data() { |
||||
|
return { |
||||
|
inputValue: '' |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
handleSend() { |
||||
|
// 检查输入是否为空 |
||||
|
if (!this.inputValue.trim()) { |
||||
|
uni.showToast({ |
||||
|
title: '请输入股票代码或名称', |
||||
|
icon: 'none', |
||||
|
duration: 2000 |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
// 跳转到deepMate页面,并传递输入的内容 |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/deepMate/deepMate?query=${encodeURIComponent(this.inputValue.trim())}` |
||||
|
}) |
||||
|
|
||||
|
// 清空输入框 |
||||
|
this.inputValue = '' |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
/* DeepMate样式 */ |
||||
|
.deepmate-container { |
||||
|
background-color: #ffe6e6; |
||||
|
border-radius: 10px; |
||||
|
padding: 15px 15px 15px 15px; |
||||
|
margin: 0; |
||||
|
width: 100%; |
||||
|
box-sizing: border-box; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.deepmate-header { |
||||
|
margin-bottom: 10px; |
||||
|
} |
||||
|
|
||||
|
.title-container { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
width: 100%; |
||||
|
} |
||||
|
|
||||
|
.title-left { |
||||
|
width: 50%; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.title-right { |
||||
|
width: 50%; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.deepmate-title { |
||||
|
font-size: 18px; |
||||
|
font-weight: bold; |
||||
|
color: #ff4d4f; |
||||
|
display: block; |
||||
|
} |
||||
|
|
||||
|
.deepmate-icon { |
||||
|
width: 50px; |
||||
|
height: 50px; |
||||
|
margin-left: 0; |
||||
|
} |
||||
|
|
||||
|
.deepmate-subtitle { |
||||
|
font-size: 12px; |
||||
|
color: #666; |
||||
|
display: block; |
||||
|
margin-top: 5px; |
||||
|
} |
||||
|
|
||||
|
.deepmate-hotspots { |
||||
|
margin: 10px 0; |
||||
|
background-color: #ffffff; |
||||
|
border-radius: 10px; |
||||
|
padding: 10px; |
||||
|
} |
||||
|
|
||||
|
.deepmate-question { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-bottom: 10px; |
||||
|
padding-left: 10px; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.deepmate-question:before { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
bottom: 0; |
||||
|
width: 4px; |
||||
|
background-color: #ff4d4f; |
||||
|
border-radius: 2px; |
||||
|
} |
||||
|
|
||||
|
.question-text { |
||||
|
font-size: 16px; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.hotspot-item { |
||||
|
background-color: #f5f5f5; |
||||
|
padding: 8px 12px; |
||||
|
border-radius: 6px; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
|
||||
|
.hotspot-item text { |
||||
|
font-size: 14px; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.deepmate-action { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
background-color: #ff4d4f; |
||||
|
padding: 8px 15px; |
||||
|
border-radius: 25px; |
||||
|
margin-top: 10px; |
||||
|
border: none; |
||||
|
} |
||||
|
|
||||
|
.stock-input { |
||||
|
flex: 1; |
||||
|
height: 36px; |
||||
|
font-size: 14px; |
||||
|
color: #ffffff; |
||||
|
padding: 0 10px; |
||||
|
border: none; |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
.stock-input::placeholder { |
||||
|
color: rgba(255, 255, 255, 0.8); |
||||
|
} |
||||
|
|
||||
|
.send-button-container { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
width: 36px; |
||||
|
height: 36px; |
||||
|
background-color: #ffffff; |
||||
|
border-radius: 50%; |
||||
|
margin-left: 10px; |
||||
|
} |
||||
|
|
||||
|
.send-button { |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
} |
||||
|
|
||||
|
.icon-container { |
||||
|
position: relative; |
||||
|
width: 50px; |
||||
|
height: 50px; |
||||
|
border-radius: 50%; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
background-color: #f0f8ff; |
||||
|
} |
||||
|
|
||||
|
.deepmate-icon { |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
border-radius: 50%; |
||||
|
} |
||||
|
|
||||
|
.ripple-effect { |
||||
|
position: absolute; |
||||
|
border-radius: 50%; |
||||
|
background: radial-gradient(circle, rgba(255, 0, 0, 0.4) 0%, rgba(255, 0, 0, 0.1) 70%); |
||||
|
animation-name: ripple; |
||||
|
animation-duration: 3s; |
||||
|
animation-timing-function: ease-out; |
||||
|
animation-iteration-count: infinite; |
||||
|
} |
||||
|
|
||||
|
.ripple-1 { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
animation-delay: 0s; |
||||
|
} |
||||
|
|
||||
|
.ripple-2 { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
animation-delay: 1s; |
||||
|
} |
||||
|
|
||||
|
.ripple-3 { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
animation-delay: 2s; |
||||
|
} |
||||
|
|
||||
|
@keyframes ripple { |
||||
|
0% { |
||||
|
transform: scale(0.8); |
||||
|
opacity: 1; |
||||
|
} |
||||
|
100% { |
||||
|
transform: scale(1.5); |
||||
|
opacity: 0; |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
@ -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> |
||||
@ -0,0 +1,232 @@ |
|||||
|
<!-- @format --> |
||||
|
|
||||
|
<template> |
||||
|
<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"; |
||||
|
|
||||
|
// 定义组件属性 |
||||
|
const props = defineProps({ |
||||
|
// 国旗图标路径 |
||||
|
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 judgeSymbol = (num) => { |
||||
|
// 兼容 undefined/null/数字/字符串 |
||||
|
if (num === null || num === undefined) return ''; |
||||
|
const n = Number(num); |
||||
|
if (!isNaN(n)) { |
||||
|
// 数值:正数加'+',负数原样 |
||||
|
return (n < 0 ? '' : '+') + n; |
||||
|
} |
||||
|
// 字符串:去空格后判断前缀 |
||||
|
const s = String(num).trim(); |
||||
|
if (s.startsWith('-')) return s; |
||||
|
if (s.startsWith('+')) return s; |
||||
|
return '+' + s; |
||||
|
}; |
||||
|
|
||||
|
const getMarketFlag = (market) => { |
||||
|
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"; |
||||
|
} |
||||
|
return imagePath; |
||||
|
}; |
||||
|
|
||||
|
// 计算价格颜色 |
||||
|
const priceColor = computed(() => { |
||||
|
return props.isRising ? "#00C853" : "#FF1744"; |
||||
|
}); |
||||
|
|
||||
|
// 计算图表背景色 |
||||
|
const chartBgColor = computed(() => { |
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
.card-header { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-bottom: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.flag-container { |
||||
|
width: 48rpx; |
||||
|
height: 32rpx; |
||||
|
margin-right: 12rpx; |
||||
|
border-radius: 4rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.flag-icon { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.index-name { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: 500; |
||||
|
color: #333333; |
||||
|
flex: 1; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
} |
||||
|
|
||||
|
.price-info { |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.current-price { |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
display: block; |
||||
|
margin-bottom: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.change-info { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.change-amount { |
||||
|
font-size: 24rpx; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.change-percent { |
||||
|
font-size: 24rpx; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.chart-container { |
||||
|
height: 80rpx; |
||||
|
border-radius: 8rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.mini-chart { |
||||
|
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; |
||||
|
} |
||||
|
|
||||
|
/* 添加一些波浪效果 */ |
||||
|
.chart-line::before { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
top: -10rpx; |
||||
|
left: 20%; |
||||
|
width: 20rpx; |
||||
|
height: 20rpx; |
||||
|
border: 2rpx solid; |
||||
|
border-color: inherit; |
||||
|
border-radius: 50%; |
||||
|
background: transparent; |
||||
|
} |
||||
|
|
||||
|
.chart-line::after { |
||||
|
content: ""; |
||||
|
position: absolute; |
||||
|
top: -6rpx; |
||||
|
right: 30%; |
||||
|
width: 12rpx; |
||||
|
height: 12rpx; |
||||
|
border: 2rpx solid; |
||||
|
border-color: inherit; |
||||
|
border-radius: 50%; |
||||
|
background: transparent; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,413 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
<!-- 第一行:白色背景,标题和按钮 --> |
||||
|
<view class="market-header"> |
||||
|
<text class="section-title">今日市场概览</text> |
||||
|
<text class="more-btn" @click="showMarketSelector">{{ buttonText }}</text> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 第二行:灰色圆角背景,市场数据 --> |
||||
|
<view class="market-content"> |
||||
|
<!-- 默认显示图片 --> |
||||
|
<view class="selected-market" v-if="!showForexMarket"> |
||||
|
<image class="market-image" src="https://d31zlh4on95l9h.cloudfront.net/images/dee46373b5593d5f315d91675a38fb61.png" mode="widthFix"></image> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 外汇市场样式 --> |
||||
|
<view class="selected-market" v-if="showForexMarket"> |
||||
|
<view class="forex-market"> |
||||
|
<!-- 上部分:三个汇率容器 --> |
||||
|
<view class="forex-rates"> |
||||
|
<view class="forex-rate-item" |
||||
|
v-for="(stock, index) in stockInfoList" |
||||
|
:key="index" |
||||
|
:class="stock.change_percent >= 0 ? 'up' : 'down'"> |
||||
|
<text class="forex-pair">{{ stock.stock_name }}</text> |
||||
|
<text class="forex-value">{{ stock.current_price }}</text> |
||||
|
<text class="forex-change">{{ stock.change }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 中部分:智能解读标题 --> |
||||
|
<view class="forex-analysis-title"> |
||||
|
<text class="analysis-title">智能解读</text> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 下部分:智能解读内容 --> |
||||
|
<view class="forex-analysis-content"> |
||||
|
<view class="analysis-icon-container"> |
||||
|
<image class="analysis-brain-icon" src="https://d31zlh4on95l9h.cloudfront.net/images/8f35e54c52412467101370aa70d8fdb2.png" mode="aspectFit"></image> |
||||
|
</view> |
||||
|
<view class="analysis-items"> |
||||
|
<view class="analysis-item"> |
||||
|
<text class="analysis-dot orange"></text> |
||||
|
<text class="analysis-text">今日市场情报: 偏乐观</text> |
||||
|
</view> |
||||
|
<view class="analysis-item"> |
||||
|
<text class="analysis-dot blue"></text> |
||||
|
<text class="analysis-text">市场风险评级: 需警惕潜在风险</text> |
||||
|
</view> |
||||
|
<view class="analysis-item" @click="goToMorningAnalysis"> |
||||
|
<text class="analysis-dot green"></text> |
||||
|
<text class="analysis-text">早盘解析: 今日高开, 芯片、稀土、公共</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 市场选择对话框 --> |
||||
|
<uni-popup ref="marketPopup" type="center" :mask-click="true" @change="popupChange"> |
||||
|
<view class="market-dialog"> |
||||
|
<view class="dialog-title"> |
||||
|
<text>选择市场</text> |
||||
|
</view> |
||||
|
<view class="market-list"> |
||||
|
<view class="market-option" v-for="(market, index) in marketOptions" :key="index" @click="selectMarket(market.id)"> |
||||
|
<view class="market-flag"> |
||||
|
<image :src="market.flag" mode="aspectFit" class="flag-image"></image> |
||||
|
</view> |
||||
|
<view class="market-name-container"> |
||||
|
<text class="market-option-name">{{ market.name }}</text> |
||||
|
</view> |
||||
|
<view class="market-checkbox"> |
||||
|
<radio :checked="selectedMarket === market.id" color="#4080ff" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="dialog-buttons"> |
||||
|
<button class="confirm-btn" @click="confirmMarketSelection">确认</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</uni-popup> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
name: 'MarketOverview', |
||||
|
props: { |
||||
|
// 接收来自父组件的3个股票数据 |
||||
|
stockInfoList: { |
||||
|
type: Array, |
||||
|
default: () => ([ |
||||
|
{ |
||||
|
stock_name: '美元/日元', |
||||
|
current_price: '151.13', |
||||
|
change: '+1.62%', |
||||
|
change_value: 0, |
||||
|
change_percent: 0 |
||||
|
}, |
||||
|
{ |
||||
|
stock_name: '美元/韩元', |
||||
|
current_price: '1424.900', |
||||
|
change: '-2.92%', |
||||
|
change_value: 0, |
||||
|
change_percent: 0 |
||||
|
}, |
||||
|
{ |
||||
|
stock_name: '美元/英镑', |
||||
|
current_price: '0.730', |
||||
|
change: '+2.92%', |
||||
|
change_value: 0, |
||||
|
change_percent: 0 |
||||
|
} |
||||
|
]) |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
selectedMarket: 'forex', |
||||
|
tempSelectedMarket: 'forex', // 临时选择,确认后才会应用 |
||||
|
previousMarket: null, |
||||
|
buttonText: '切换市场', |
||||
|
showSelector: false, |
||||
|
showForexMarket: false, // 默认不显示外汇市场内容 |
||||
|
marketOptions: [ |
||||
|
{ id: 'forex', name: '外汇市场', flag: '/static/c2.png', value: '+1.62%', trend: 'up' } |
||||
|
], |
||||
|
marketData: { |
||||
|
'forex': { |
||||
|
name: '外汇市场', |
||||
|
value: '+1.62%', |
||||
|
trend: 'up', |
||||
|
indicators: [ |
||||
|
{ name: '美元/日元', value: '151.13 +1.62%', trend: 'up' }, |
||||
|
{ name: '美元/韩元', value: '1424.900 -2.92%', trend: 'down' }, |
||||
|
{ name: '美元/英镑', value: '0.79 +2.92%', trend: 'up' } |
||||
|
] |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 跳转到早盘解析页面 |
||||
|
goToMorningAnalysis() { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/morningMarketAnalysis/morningMarketAnalysis' |
||||
|
}) |
||||
|
}, |
||||
|
showMarketSelector() { |
||||
|
// 切换显示外汇市场内容 |
||||
|
this.showForexMarket = !this.showForexMarket; |
||||
|
// 更新按钮文本 |
||||
|
this.buttonText = this.showForexMarket ? '外汇市场' : '切换市场'; |
||||
|
}, |
||||
|
popupChange(e) { |
||||
|
// 对话框关闭时,如果没有确认,则恢复原来的选择 |
||||
|
if (!e.show && this.tempSelectedMarket !== this.selectedMarket) { |
||||
|
this.tempSelectedMarket = this.selectedMarket; |
||||
|
} |
||||
|
}, |
||||
|
selectMarket(marketId) { |
||||
|
// 临时选择市场,不立即应用 |
||||
|
this.tempSelectedMarket = marketId; |
||||
|
}, |
||||
|
confirmMarketSelection() { |
||||
|
// 确认选择,应用市场选择并关闭对话框 |
||||
|
this.selectedMarket = this.tempSelectedMarket; |
||||
|
this.showSelector = false; |
||||
|
this.$refs.marketPopup.close(); |
||||
|
}, |
||||
|
getSelectedMarketName() { |
||||
|
if (!this.selectedMarket) return ''; |
||||
|
return this.marketData[this.selectedMarket].name; |
||||
|
}, |
||||
|
getSelectedMarketValue() { |
||||
|
if (!this.selectedMarket) return ''; |
||||
|
return this.marketData[this.selectedMarket].value; |
||||
|
}, |
||||
|
getSelectedMarketTrend() { |
||||
|
if (!this.selectedMarket) return ''; |
||||
|
return this.marketData[this.selectedMarket].trend; |
||||
|
}, |
||||
|
getSelectedMarketIndicators() { |
||||
|
if (!this.selectedMarket) return []; |
||||
|
return this.marketData[this.selectedMarket].indicators; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
/* 市场概览样式 */ |
||||
|
.market-header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 10px 15px; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.section-title { |
||||
|
font-size: 16px; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.more-btn { |
||||
|
font-size: 14px; |
||||
|
color: #4080ff; |
||||
|
} |
||||
|
|
||||
|
.market-content { |
||||
|
margin: 0 0 15px; |
||||
|
background-color: #f5f7fa; |
||||
|
border-radius: 8px; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
/* 外汇市场样式 */ |
||||
|
.forex-market { |
||||
|
padding: 15px; |
||||
|
} |
||||
|
|
||||
|
.forex-rates { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
gap: 10px; |
||||
|
margin-bottom: 15px; |
||||
|
} |
||||
|
|
||||
|
.forex-rate-item { |
||||
|
width: 30%; |
||||
|
padding: 10px; |
||||
|
border-radius: 6px; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.forex-rate-item.up { |
||||
|
background-color: rgba(82, 196, 26, 0.1); |
||||
|
border: 1px solid #52c41a; |
||||
|
} |
||||
|
|
||||
|
.forex-rate-item.down { |
||||
|
background-color: rgba(245, 34, 45, 0.1); |
||||
|
border: 1px solid #f5222d; |
||||
|
} |
||||
|
|
||||
|
.forex-pair { |
||||
|
font-size: 12px; |
||||
|
color: #666; |
||||
|
margin-bottom: 5px; |
||||
|
} |
||||
|
|
||||
|
.forex-value { |
||||
|
font-size: 16px; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
margin-bottom: 5px; |
||||
|
} |
||||
|
|
||||
|
.forex-change { |
||||
|
font-size: 12px; |
||||
|
color: #52c41a; |
||||
|
} |
||||
|
|
||||
|
.forex-rate-item.down .forex-change { |
||||
|
color: #f5222d; |
||||
|
} |
||||
|
|
||||
|
.forex-analysis-title { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-bottom: 10px; |
||||
|
border-left: 3px solid #f5222d; |
||||
|
padding-left: 8px; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.analysis-title { |
||||
|
font-size: 14px; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.forex-analysis-content { |
||||
|
display: flex; |
||||
|
background-color: #fff; |
||||
|
border-radius: 6px; |
||||
|
padding: 12px; |
||||
|
} |
||||
|
|
||||
|
.analysis-icon-container { |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
margin-right: 10px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
align-self: center; |
||||
|
} |
||||
|
|
||||
|
.analysis-brain-icon { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
|
||||
|
.analysis-items { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.analysis-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-bottom: 8px; |
||||
|
} |
||||
|
|
||||
|
.analysis-item:last-child { |
||||
|
margin-bottom: 0; |
||||
|
} |
||||
|
|
||||
|
.analysis-dot { |
||||
|
width: 8px; |
||||
|
height: 8px; |
||||
|
border-radius: 50%; |
||||
|
margin-right: 8px; |
||||
|
} |
||||
|
|
||||
|
.analysis-dot.orange { |
||||
|
background-color: #fa8c16; |
||||
|
} |
||||
|
|
||||
|
.analysis-dot.blue { |
||||
|
background-color: #1890ff; |
||||
|
} |
||||
|
|
||||
|
.analysis-dot.green { |
||||
|
background-color: #52c41a; |
||||
|
} |
||||
|
|
||||
|
.analysis-text { |
||||
|
font-size: 12px; |
||||
|
color: #333; |
||||
|
line-height: 1.4; |
||||
|
} |
||||
|
|
||||
|
/* 市场选择对话框样式 */ |
||||
|
.market-dialog { |
||||
|
width: 300px; |
||||
|
background-color: #fff; |
||||
|
border-radius: 10px; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.dialog-title { |
||||
|
padding: 15px; |
||||
|
text-align: center; |
||||
|
border-bottom: 1px solid #eee; |
||||
|
} |
||||
|
|
||||
|
.market-list { |
||||
|
max-height: 300px; |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
|
||||
|
.market-option { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 12px 15px; |
||||
|
border-bottom: 1px solid #f5f5f5; |
||||
|
} |
||||
|
|
||||
|
.market-flag { |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
margin-right: 10px; |
||||
|
} |
||||
|
|
||||
|
.flag-image { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
border-radius: 50%; |
||||
|
} |
||||
|
|
||||
|
.market-name-container { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.market-option-name { |
||||
|
font-size: 14px; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.dialog-buttons { |
||||
|
padding: 10px 15px 15px; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.confirm-btn { |
||||
|
width: 80%; |
||||
|
height: 40px; |
||||
|
line-height: 40px; |
||||
|
text-align: center; |
||||
|
background-color: #4080ff; |
||||
|
color: #fff; |
||||
|
border-radius: 20px; |
||||
|
font-size: 14px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,190 @@ |
|||||
|
<!--自定义分享弹窗 使用uni的更改--> |
||||
|
<template> |
||||
|
<view class="uni-popup-share"> |
||||
|
<!-- <view class="uni-share-title">--> |
||||
|
<!-- <text class="uni-share-title-text">{{ shareTitleText }}</text>--> |
||||
|
<!-- </view>--> |
||||
|
<view class="uni-share-content"> |
||||
|
<view class="uni-share-content-box"> |
||||
|
<view class="uni-share-content-item" v-for="(item,index) in bottomData" :key="index" |
||||
|
@click.stop="select(item,index)"> |
||||
|
<image class="uni-share-image" :src="item.icon" mode="aspectFill"></image> |
||||
|
<text class="uni-share-text">{{ item.text }}</text> |
||||
|
</view> |
||||
|
|
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="uni-share-button-box"> |
||||
|
<button class="uni-share-button" @click="close">{{ cancelText }}</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import popup from '../uni_modules/uni-popup/components/uni-popup/popup.js' |
||||
|
// import popup from '../uni-popup/popup.js' |
||||
|
import {initVueI18n} from '@dcloudio/uni-i18n' |
||||
|
import messages from '../uni_modules/uni-popup/components/uni-popup/i18n/index.js' |
||||
|
|
||||
|
const {t} = initVueI18n(messages) |
||||
|
export default { |
||||
|
name: 'SharePopup', |
||||
|
mixins: [popup], |
||||
|
emits: ['select'], |
||||
|
props: { |
||||
|
title: { |
||||
|
type: String, |
||||
|
default: '' |
||||
|
}, |
||||
|
beforeClose: { |
||||
|
type: Boolean, |
||||
|
default: false |
||||
|
} |
||||
|
}, |
||||
|
data() { |
||||
|
return { |
||||
|
bottomData: [{ |
||||
|
text: 'WhatsApp', |
||||
|
icon: '/static/my/share/WhatsApp.png', |
||||
|
name: 'WhatsApp' |
||||
|
}, |
||||
|
{ |
||||
|
text: 'Line', |
||||
|
icon: '/static/my/share/Line.png', |
||||
|
name: 'Line' |
||||
|
}, |
||||
|
{ |
||||
|
text: 'KakaoTalk', |
||||
|
icon: '/static/my/share/KakaoTalk.png', |
||||
|
name: 'KakaoTalk' |
||||
|
}, |
||||
|
{ |
||||
|
text: 'WeChat', |
||||
|
icon: '/static/my/share/WeChat.png', |
||||
|
name: 'WeChat' |
||||
|
}, |
||||
|
{ |
||||
|
text: '复制链接', |
||||
|
icon: '/static/my/share/share.png', |
||||
|
name: '复制链接' |
||||
|
}, |
||||
|
] |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
}, |
||||
|
computed: { |
||||
|
cancelText() { |
||||
|
return t("uni-popup.cancel") |
||||
|
}, |
||||
|
shareTitleText() { |
||||
|
return this.title || t("uni-popup.shareTitle") |
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
/** |
||||
|
* 选择内容 |
||||
|
*/ |
||||
|
select(item, index) { |
||||
|
this.$emit('select', { |
||||
|
item, |
||||
|
index |
||||
|
}) |
||||
|
// this.close() |
||||
|
|
||||
|
}, |
||||
|
/** |
||||
|
* 关闭窗口 |
||||
|
*/ |
||||
|
close() { |
||||
|
if (this.beforeClose) return |
||||
|
this.popup.close() |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
<style lang="scss"> |
||||
|
.uni-popup-share { |
||||
|
background-color: #fff; |
||||
|
border-top-left-radius: 11px; |
||||
|
border-top-right-radius: 11px; |
||||
|
} |
||||
|
|
||||
|
.uni-share-title { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: row; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 40px; |
||||
|
} |
||||
|
|
||||
|
.uni-share-title-text { |
||||
|
font-size: 14px; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
.uni-share-content { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: row; |
||||
|
justify-content: center; |
||||
|
padding-top: 10px; |
||||
|
} |
||||
|
|
||||
|
.uni-share-content-box { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: row; |
||||
|
flex-wrap: wrap; |
||||
|
width: 360px; |
||||
|
} |
||||
|
|
||||
|
.uni-share-content-item { |
||||
|
width: 72px; |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
padding: 10px 0; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.uni-share-content-item:active { |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
|
||||
|
.uni-share-image { |
||||
|
width: 42px; |
||||
|
height: 42px; |
||||
|
} |
||||
|
|
||||
|
.uni-share-text { |
||||
|
margin-top: 10px; |
||||
|
font-size: 14px; |
||||
|
color: #3B4144; |
||||
|
} |
||||
|
|
||||
|
.uni-share-button-box { |
||||
|
/* #ifndef APP-NVUE */ |
||||
|
display: flex; |
||||
|
/* #endif */ |
||||
|
flex-direction: row; |
||||
|
padding: 10px 15px; |
||||
|
} |
||||
|
|
||||
|
.uni-share-button { |
||||
|
flex: 1; |
||||
|
border-radius: 50px; |
||||
|
color: #666; |
||||
|
font-size: 16px; |
||||
|
} |
||||
|
|
||||
|
.uni-share-button::after { |
||||
|
border-radius: 50px; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,516 @@ |
|||||
|
<template> |
||||
|
<view class="titleContent"> |
||||
|
<view class="left"> |
||||
|
<uni-icons |
||||
|
@click="handleBack" |
||||
|
type="back" |
||||
|
size="23" |
||||
|
color="#111" |
||||
|
></uni-icons> |
||||
|
</view> |
||||
|
<view class="title">深度探索</view> |
||||
|
<view class="right"> |
||||
|
<image |
||||
|
class="notice" |
||||
|
src="/static/deepExploration-images/notice.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
<image |
||||
|
@click="openHistoryDrawer" |
||||
|
class="history" |
||||
|
src="/static/deepExploration-images/history.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="drawer-overlay" v-show="showHistoryDrawer"></view> |
||||
|
|
||||
|
<view |
||||
|
class="drawer-panel" |
||||
|
v-show="showHistoryDrawer" |
||||
|
@click.stop |
||||
|
@touchmove.stop.prevent |
||||
|
:style="{ transform: 'translateY(' + drawerOffsetY + 'px)' }" |
||||
|
> |
||||
|
<view class="drawer-header"> |
||||
|
<text class="drawer-title">历史对话</text> |
||||
|
<view class="drawer-actions"> |
||||
|
<view class="delete-all-container"> |
||||
|
<image |
||||
|
class="delete-icon" |
||||
|
src="/static/icons/Group_48095481.svg" |
||||
|
></image> |
||||
|
<text class="delete-all" @click="clearAllHistory">删除全部</text> |
||||
|
</view> |
||||
|
<view class="drawer-close" @click="onDrawerBackClick" |
||||
|
><text class="drawer-close-icon"></text |
||||
|
></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<scroll-view scroll-y="true" class="drawer-content"> |
||||
|
<view class="drawer-inner"> |
||||
|
<view v-if="groupedHistory.length === 0" class="empty-history"> |
||||
|
<text>暂无历史记录</text> |
||||
|
</view> |
||||
|
<view |
||||
|
v-for="(section, sIdx) in groupedHistory" |
||||
|
:key="sIdx" |
||||
|
class="history-section" |
||||
|
> |
||||
|
<text class="section-title">{{ section.title }}</text> |
||||
|
<view |
||||
|
v-for="(item, idx) in section.items" |
||||
|
:key="idx" |
||||
|
class="history-item" |
||||
|
> |
||||
|
<view class="history-left"> |
||||
|
<view class="flag-circle" |
||||
|
><text class="flag-emoji">🇺🇸</text></view |
||||
|
> |
||||
|
</view> |
||||
|
<view class="history-main" @click="itemClick(item)"> |
||||
|
<text class="history-query">{{ item.stockName }}</text> |
||||
|
<text class="history-query">({{ item.stockCode }})</text> |
||||
|
<text class="history-query">({{ item.stockCode }})</text> |
||||
|
</view> |
||||
|
<text class="history-time">{{ |
||||
|
formatTimeForHistory(item.createdTime) |
||||
|
}}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
RecordListApi, |
||||
|
RecordInfoApi, |
||||
|
} from "../api/deepExploration/deepExploration"; |
||||
|
import { ref, onMounted, computed } from "vue"; |
||||
|
import { useDeepExplorationStore } from "../stores/modules/deepExploration"; |
||||
|
|
||||
|
const props = defineProps({ |
||||
|
name: { |
||||
|
type: String, |
||||
|
default: "", |
||||
|
}, |
||||
|
}); |
||||
|
const showHistoryDrawer = ref(false); |
||||
|
const modelType = ref(''); |
||||
|
const drawerOffsetY = ref(0); |
||||
|
// const handleHistory = () => { |
||||
|
// showHistoryDrawer.value = true; |
||||
|
// }; |
||||
|
|
||||
|
// 历史记录 |
||||
|
const openHistoryDrawer = async () => { |
||||
|
const res = await RecordListApi({ |
||||
|
model: 1, |
||||
|
}); |
||||
|
|
||||
|
if (res.code === 200) { |
||||
|
historyList.value = res.data; |
||||
|
} |
||||
|
|
||||
|
console.log("historyList.value", historyList.value); |
||||
|
|
||||
|
const hideDistance = uni.upx2px(900); |
||||
|
drawerOffsetY.value = hideDistance; |
||||
|
showHistoryDrawer.value = true; |
||||
|
setTimeout(() => { |
||||
|
drawerOffsetY.value = 0; |
||||
|
}, 10); |
||||
|
}; |
||||
|
|
||||
|
const clearAllHistory = () => { |
||||
|
searchHistory.value = []; |
||||
|
|
||||
|
// uni.setStorageSync("search_history", []); |
||||
|
}; |
||||
|
|
||||
|
//返回上一页 |
||||
|
const handleBack = () => { |
||||
|
uni.navigateBack(); |
||||
|
}; |
||||
|
|
||||
|
const closeHistoryDrawer = () => { |
||||
|
showHistoryDrawer.value = false; |
||||
|
}; |
||||
|
const onDrawerBackClick = () => { |
||||
|
const hideDistance = uni.upx2px(900); |
||||
|
drawerOffsetY.value = hideDistance; |
||||
|
setTimeout(() => { |
||||
|
closeHistoryDrawer(); |
||||
|
drawerOffsetY.value = 0; |
||||
|
}, 180); |
||||
|
}; |
||||
|
|
||||
|
// 历史记录详情 |
||||
|
async function itemClick(item) { |
||||
|
const res = await RecordInfoApi({ |
||||
|
recordId: item.id, |
||||
|
parentId: item.parentId, |
||||
|
model: 5, |
||||
|
}); |
||||
|
if (res.code == 200) { |
||||
|
|
||||
|
const message = res.data; |
||||
|
const deepExplorationStore = useDeepExplorationStore(); |
||||
|
deepExplorationStore.setDeepExplorationInfo(message); |
||||
|
console.log('点击了历史数据',deepExplorationStore.deepExplorationInfo); |
||||
|
onDrawerBackClick(); |
||||
|
setTimeout(() => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/deepExploration/MainForceActions' |
||||
|
}); |
||||
|
}, 200); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
const historyList = ref([]); |
||||
|
|
||||
|
// 为历史记录格式化时间:YYYY-MM-DD HH:mm |
||||
|
const formatTimeForHistory = (timeString) => { |
||||
|
// 假设 timeString 格式为 "YYYY-MM-DD HH:mm:ss" |
||||
|
const parts = timeString.split(" "); |
||||
|
if (parts.length >= 2) { |
||||
|
const datePart = parts[0]; |
||||
|
const timePart = parts[1]; |
||||
|
const timeParts = timePart.split(":"); |
||||
|
if (timeParts.length >= 2) { |
||||
|
return `${datePart} ${timeParts[0]}:${timeParts[1]}`; |
||||
|
} |
||||
|
} |
||||
|
return timeString; |
||||
|
}; |
||||
|
|
||||
|
// 历史分组(今天/昨天/近一周/按月) |
||||
|
const groupedHistory = computed(() => { |
||||
|
const sections = []; |
||||
|
|
||||
|
// 从缓存获取今天日期,如果没有则使用当前日期 |
||||
|
const now = new Date(); |
||||
|
|
||||
|
const startOfDay = (d) => |
||||
|
new Date(d.getFullYear(), d.getMonth(), d.getDate()); |
||||
|
const isSameDay = (a, b) => |
||||
|
startOfDay(a).getTime() === startOfDay(b).getTime(); |
||||
|
const isYesterday = (d) => { |
||||
|
const y = new Date(now.getFullYear(), now.getMonth(), now.getDate() - 1); |
||||
|
return isSameDay(d, y); |
||||
|
}; |
||||
|
const isToday = (d) => isSameDay(d, now); |
||||
|
const withinLast7Days = (d) => { |
||||
|
const seven = new Date( |
||||
|
now.getFullYear(), |
||||
|
now.getMonth(), |
||||
|
now.getDate() - 7 |
||||
|
); |
||||
|
return d >= seven && !isToday(d) && !isYesterday(d); |
||||
|
}; |
||||
|
const monthLabel = (d) => `${d.getMonth() + 1}月`; |
||||
|
|
||||
|
const today = []; |
||||
|
const yesterday = []; |
||||
|
const last7 = []; |
||||
|
const byMonth = new Map(); |
||||
|
|
||||
|
// 使用 historyList.value 替代 searchHistory.value |
||||
|
historyList.value.forEach((item) => { |
||||
|
// 根据你的数据结构,使用 createdTime 字段 |
||||
|
const dt = new Date(item.createdTime); |
||||
|
if (isToday(dt)) { |
||||
|
today.push(item); |
||||
|
} else if (isYesterday(dt)) { |
||||
|
yesterday.push(item); |
||||
|
} else if (withinLast7Days(dt)) { |
||||
|
last7.push(item); |
||||
|
} else { |
||||
|
const year = dt.getFullYear(); |
||||
|
const month = dt.getMonth() + 1; |
||||
|
const key = `${year}-${month}`; |
||||
|
if (!byMonth.has(key)) |
||||
|
byMonth.set(key, { |
||||
|
title: `${year}年${month}月`, |
||||
|
year, |
||||
|
month, |
||||
|
items: [], |
||||
|
}); |
||||
|
byMonth.get(key).items.push(item); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
if (today.length) sections.push({ title: "今天", items: today }); |
||||
|
if (yesterday.length) sections.push({ title: "昨天", items: yesterday }); |
||||
|
if (last7.length) sections.push({ title: "近一周", items: last7 }); |
||||
|
|
||||
|
const monthSections = Array.from(byMonth.values()).sort((a, b) => { |
||||
|
if (a.year !== b.year) return b.year - a.year; |
||||
|
return b.month - a.month; // 月份倒序,如 10月 在 9月 之前 |
||||
|
}); |
||||
|
sections.push(...monthSections); |
||||
|
|
||||
|
return sections; |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => {}); |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
* { |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
|
||||
|
.titleContent { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
padding: 30rpx 60rpx 40rpx 35rpx; |
||||
|
box-shadow: 2rpx 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
|
||||
|
.left { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
width: 36rpx; |
||||
|
height: 36rpx; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
position: absolute; |
||||
|
left: 50%; |
||||
|
transform: translateX(-50%); |
||||
|
color: #000000; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 19px; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 25px; |
||||
|
} |
||||
|
|
||||
|
.right { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 20rpx; |
||||
|
|
||||
|
.notice { |
||||
|
width: 35rpx; |
||||
|
height: 35rpx; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.history { |
||||
|
width: 32rpx; |
||||
|
height: 32rpx; |
||||
|
align-items: center; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 搜索历史侧拉框样式 */ |
||||
|
.drawer-overlay { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
top: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background-color: rgba(0, 0, 0, 0.35); |
||||
|
z-index: 900; |
||||
|
} |
||||
|
|
||||
|
.drawer-panel { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
height: 75vh; |
||||
|
max-height: 80vh; |
||||
|
width: 100%; |
||||
|
background: #ffffff; |
||||
|
box-shadow: 0 -8rpx 20rpx rgba(0, 0, 0, 0.06); |
||||
|
z-index: 1000; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
border-top-left-radius: 20rpx; |
||||
|
border-top-right-radius: 20rpx; |
||||
|
transition: transform 0.22s ease; |
||||
|
} |
||||
|
|
||||
|
.drawer-back { |
||||
|
position: absolute; |
||||
|
left: 50%; |
||||
|
top: -14px; |
||||
|
transform: translateX(-50%); |
||||
|
width: 28px; |
||||
|
height: 48px; |
||||
|
border-radius: 12px; |
||||
|
background: #fff; |
||||
|
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.08); |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.drawer-back-icon { |
||||
|
font-size: 16px; |
||||
|
color: #8a8a8a; |
||||
|
} |
||||
|
|
||||
|
.drawer-actions { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.delete-all-container { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 14rpx; |
||||
|
} |
||||
|
|
||||
|
.delete-icon { |
||||
|
width: 28rpx; |
||||
|
height: 32rpx; |
||||
|
} |
||||
|
|
||||
|
.delete-all { |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.drawer-close { |
||||
|
background: url("../static/icons/关闭2.svg"); |
||||
|
width: 48rpx; |
||||
|
height: 48rpx; |
||||
|
border-radius: 24rpx; |
||||
|
/* background: #f5f5f5; */ |
||||
|
/* box-shadow: 0 2px 8px rgba(0,0,0,0.08); */ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
|
||||
|
image { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.drawer-header { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
padding: 52rpx 28rpx 0rpx 28rpx; |
||||
|
/* border-bottom: 2rpx solid #f0f0f0; */ |
||||
|
} |
||||
|
|
||||
|
.drawer-title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: 600; |
||||
|
color: #333333; |
||||
|
} |
||||
|
|
||||
|
.drawer-content { |
||||
|
flex: 1; |
||||
|
min-height: 0; |
||||
|
/* 让 flex 子元素可在容器内收缩以启用滚动 */ |
||||
|
height: 100%; |
||||
|
overscroll-behavior-y: contain; |
||||
|
/* 防止滚动串联到页面 */ |
||||
|
-webkit-overflow-scrolling: touch; |
||||
|
/* iOS 惯性滚动 */ |
||||
|
touch-action: pan-y; |
||||
|
/* 优化触控滚动,仅垂直 */ |
||||
|
} |
||||
|
|
||||
|
.drawer-inner { |
||||
|
padding: 20rpx 24rpx 20rpx 24rpx; |
||||
|
} |
||||
|
|
||||
|
.history-section { |
||||
|
margin-bottom: 20rpx; |
||||
|
/* margin: 0 24rpx; */ |
||||
|
} |
||||
|
|
||||
|
.section-title { |
||||
|
font-size: 26rpx; |
||||
|
color: #888; |
||||
|
margin: 10rpx 6rpx 16rpx; |
||||
|
} |
||||
|
|
||||
|
.history-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 19rpx 18rpx; |
||||
|
background-color: #f6f7f9; |
||||
|
border-radius: 12rpx; |
||||
|
margin-bottom: 12rpx; |
||||
|
} |
||||
|
|
||||
|
.history-left { |
||||
|
margin-right: 20rpx; |
||||
|
width: 50rpx; |
||||
|
height: 50rpx; |
||||
|
} |
||||
|
|
||||
|
.flag-circle { |
||||
|
width: 50rpx; |
||||
|
height: 50rpx; |
||||
|
border-radius: 50%; |
||||
|
background: #fff; |
||||
|
border: 2rpx solid #eee; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
image { |
||||
|
width: 50rpx; |
||||
|
height: 50rpx; |
||||
|
border-radius: 50%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.history-main { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.history-query { |
||||
|
color: #000000; |
||||
|
text-align: center; |
||||
|
font-size: 24rpx; |
||||
|
font-weight: 400; |
||||
|
line-height: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.history-time { |
||||
|
font-size: 22rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.history-card { |
||||
|
background-color: #fff; |
||||
|
border: 2rpx solid #f2f2f2; |
||||
|
border-left: 8rpx solid rgb(220, 31, 29); |
||||
|
border-radius: 12rpx; |
||||
|
padding: 18rpx 20rpx; |
||||
|
margin-bottom: 16rpx; |
||||
|
box-shadow: 0 4rpx 10rpx rgba(0, 0, 0, 0.03); |
||||
|
} |
||||
|
|
||||
|
.history-query { |
||||
|
font-size: 28rpx; |
||||
|
color: #333333; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.history-time { |
||||
|
margin-top: 8rpx; |
||||
|
font-size: 22rpx; |
||||
|
color: #888888; |
||||
|
} |
||||
|
|
||||
|
.empty-history { |
||||
|
padding: 40rpx; |
||||
|
color: #999999; |
||||
|
text-align: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,209 @@ |
|||||
|
<template> |
||||
|
<view class="login-prompt" v-if="showPrompt"> |
||||
|
<view class="mask" :class="{ 'mask-show': showAnimation }"></view> |
||||
|
<view class="prompt-content" :class="{ 'slide-up': showAnimation }"> |
||||
|
<text class="prompt-title">登录以获得更好的体验</text> |
||||
|
<button class="login-btn" @click="goLogin">登录</button> |
||||
|
<button class="visitor-btn" @click="continueAsVisitor"> |
||||
|
以访客身份继续 |
||||
|
</button> |
||||
|
<text class="prompt-title-small" @click="goRegister" |
||||
|
>如果您还没有账号,点击注册</text |
||||
|
> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, nextTick, onMounted, watch } from "vue"; |
||||
|
import { useUserStore } from "../stores/modules/userInfo"; |
||||
|
import { useDeviceStore } from "../stores/modules/deviceInfo"; |
||||
|
import { useLoginStore } from "../stores/modules/login"; |
||||
|
import { LoginApi } from "../api/start/login"; |
||||
|
const deviceId = ref(""); |
||||
|
|
||||
|
const userStore = useUserStore(); |
||||
|
const deviceStore = useDeviceStore(); |
||||
|
const loginStore = useLoginStore(); |
||||
|
// 初始化 |
||||
|
onMounted(() => { |
||||
|
if (!userStore.userInfo) { |
||||
|
show(); |
||||
|
} |
||||
|
}), |
||||
|
// watch( |
||||
|
// () => loginStore.loginInfo, |
||||
|
// (newVal, oldVal) => { |
||||
|
// console.log("登录状态改变"); |
||||
|
// if (newVal === "false") { |
||||
|
// console.log("登录失败"); |
||||
|
// show(); |
||||
|
// loginStore.setLoginInfo("true"); |
||||
|
// } |
||||
|
// } |
||||
|
// ); |
||||
|
|
||||
|
loginStore.$subscribe(() => { |
||||
|
if (loginStore.loginInfo === "false") { |
||||
|
console.log("登录失败"); |
||||
|
show(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 定义响应式数据 |
||||
|
const showPrompt = ref(false); |
||||
|
const showAnimation = ref(false); |
||||
|
|
||||
|
// 显示弹窗 |
||||
|
const show = () => { |
||||
|
showPrompt.value = true; |
||||
|
// 在下一帧触发动画 |
||||
|
nextTick(() => { |
||||
|
setTimeout(() => { |
||||
|
showAnimation.value = true; |
||||
|
}, 10); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 隐藏弹窗 |
||||
|
const hide = () => { |
||||
|
showAnimation.value = false; |
||||
|
// 等待动画结束后再隐藏 |
||||
|
setTimeout(() => { |
||||
|
showPrompt.value = false; |
||||
|
}, 300); |
||||
|
}; |
||||
|
|
||||
|
// 跳转到登录页面 |
||||
|
const goLogin = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/start/login/login", |
||||
|
}); |
||||
|
hide(); |
||||
|
}; |
||||
|
// 跳转到登录页面 |
||||
|
const goRegister = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/start/Registration/Registration", |
||||
|
}); |
||||
|
hide(); |
||||
|
}; |
||||
|
|
||||
|
// 以访客身份继续 |
||||
|
const continueAsVisitor = async () => { |
||||
|
// 设置访客模式 |
||||
|
const res = await LoginApi({ |
||||
|
loginType: "VISITOR", //登录方式EMAIL,PHONE,DCCODE,APPLE,GOOGLE,VISITOR |
||||
|
account: "", //登陆账号 手机号/邮箱/dccode |
||||
|
verifyCode: "", //验证码 |
||||
|
password: "", //密码 |
||||
|
useCode: "", //是否使用验证码 true/false |
||||
|
idToken: "", //第三方登录idToken |
||||
|
deviceId: deviceStore.deviceInfo.deviceId, |
||||
|
}); |
||||
|
|
||||
|
if (res.code === 200) { |
||||
|
userStore.setUserInfo(res.data); |
||||
|
console.log("0loginStore.loginInfo", loginStore.loginInfo); |
||||
|
hide(); |
||||
|
|
||||
|
// 发送游客登录成功事件,通知首页重新加载 |
||||
|
uni.$emit('visitorLoginSuccess', { |
||||
|
userInfo: res.data |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 暴露方法给外部使用 |
||||
|
defineExpose({ |
||||
|
show, |
||||
|
hide, |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.login-prompt { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
z-index: 999; |
||||
|
} |
||||
|
|
||||
|
.mask { |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background-color: rgba(0, 0, 0, 0.8); |
||||
|
opacity: 0; |
||||
|
transition: opacity 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.mask.mask-show { |
||||
|
opacity: 1; |
||||
|
} |
||||
|
|
||||
|
.prompt-content { |
||||
|
position: absolute; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
height: 400rpx; |
||||
|
border-radius: 20rpx 20rpx 0 0; |
||||
|
background-color: white; |
||||
|
padding: 20rpx 70rpx; |
||||
|
transform: translateY(100%); |
||||
|
transition: transform 0.3s ease; |
||||
|
box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.3); |
||||
|
} |
||||
|
|
||||
|
.prompt-content.slide-up { |
||||
|
transform: translateY(0); |
||||
|
} |
||||
|
|
||||
|
.prompt-title { |
||||
|
display: block; |
||||
|
text-align: center; |
||||
|
font-size: 40rpx; |
||||
|
font-weight: 700; |
||||
|
color: #000000; |
||||
|
margin-top: 30rpx; |
||||
|
margin-bottom: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.login-btn { |
||||
|
width: 100%; |
||||
|
height: 80rpx; |
||||
|
background-color: rgb(0, 0, 0); |
||||
|
color: white; |
||||
|
font-size: 32rpx; |
||||
|
line-height: 80rpx; |
||||
|
border-radius: 40rpx; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.visitor-btn { |
||||
|
width: 100%; |
||||
|
height: 80rpx; |
||||
|
background-color: #f5f5f5; |
||||
|
color: #333; |
||||
|
font-size: 32rpx; |
||||
|
line-height: 80rpx; |
||||
|
border-radius: 40rpx; |
||||
|
} |
||||
|
.prompt-title-small { |
||||
|
display: block; |
||||
|
text-align: center; |
||||
|
font-size: 24rpx; |
||||
|
color: #6a6a6a; |
||||
|
margin-top: 30rpx; |
||||
|
margin-bottom: 40rpx; |
||||
|
line-height: 15.5px; |
||||
|
letter-spacing: 0.3px; |
||||
|
font-style: normal; |
||||
|
font-family: "PingFang SC"; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,738 @@ |
|||||
|
{ |
||||
|
"name": "deepChartVueApp", |
||||
|
"lockfileVersion": 3, |
||||
|
"requires": true, |
||||
|
"packages": { |
||||
|
"": { |
||||
|
"dependencies": { |
||||
|
"@dcloudio/uni-ui": "^1.5.11", |
||||
|
"@element-plus/icons-vue": "^2.3.2", |
||||
|
"element-plus": "^2.11.5", |
||||
|
"highlight.js": "^11.11.1", |
||||
|
"marked": "^2.0.1", |
||||
|
"pinia": "^3.0.3", |
||||
|
"pinia-plugin-persistedstate": "^4.5.0", |
||||
|
"vue-i18n": "^9.14.5" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@babel/helper-string-parser": { |
||||
|
"version": "7.27.1", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"engines": { |
||||
|
"node": ">=6.9.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@babel/helper-validator-identifier": { |
||||
|
"version": "7.28.5", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"engines": { |
||||
|
"node": ">=6.9.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@babel/parser": { |
||||
|
"version": "7.28.5", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@babel/types": "^7.28.5" |
||||
|
}, |
||||
|
"bin": { |
||||
|
"parser": "bin/babel-parser.js" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=6.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@babel/types": { |
||||
|
"version": "7.28.5", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@babel/helper-string-parser": "^7.27.1", |
||||
|
"@babel/helper-validator-identifier": "^7.28.5" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=6.9.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@ctrl/tinycolor": { |
||||
|
"version": "3.6.1", |
||||
|
"resolved": "https://registry.npmmirror.com/@ctrl/tinycolor/-/tinycolor-3.6.1.tgz", |
||||
|
"integrity": "sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==", |
||||
|
"license": "MIT", |
||||
|
"engines": { |
||||
|
"node": ">=10" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@dcloudio/uni-ui": { |
||||
|
"version": "1.5.11", |
||||
|
"resolved": "https://registry.npmmirror.com/@dcloudio/uni-ui/-/uni-ui-1.5.11.tgz", |
||||
|
"integrity": "sha512-DBtk046ofmeFd82zRI7d89SoEwrAxYzUN3WVPm1DIBkpLPG5F5QDNkHMnZGu2wNrMEmGBjBpUh3vqEY1L3jaMw==", |
||||
|
"license": "Apache-2.0" |
||||
|
}, |
||||
|
"node_modules/@element-plus/icons-vue": { |
||||
|
"version": "2.3.2", |
||||
|
"resolved": "https://registry.npmmirror.com/@element-plus/icons-vue/-/icons-vue-2.3.2.tgz", |
||||
|
"integrity": "sha512-OzIuTaIfC8QXEPmJvB4Y4kw34rSXdCJzxcD1kFStBvr8bK6X1zQAYDo0CNMjojnfTqRQCJ0I7prlErcoRiET2A==", |
||||
|
"license": "MIT", |
||||
|
"peerDependencies": { |
||||
|
"vue": "^3.2.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@floating-ui/core": { |
||||
|
"version": "1.7.3", |
||||
|
"resolved": "https://registry.npmmirror.com/@floating-ui/core/-/core-1.7.3.tgz", |
||||
|
"integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@floating-ui/utils": "^0.2.10" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@floating-ui/dom": { |
||||
|
"version": "1.7.4", |
||||
|
"resolved": "https://registry.npmmirror.com/@floating-ui/dom/-/dom-1.7.4.tgz", |
||||
|
"integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@floating-ui/core": "^1.7.3", |
||||
|
"@floating-ui/utils": "^0.2.10" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@floating-ui/utils": { |
||||
|
"version": "0.2.10", |
||||
|
"resolved": "https://registry.npmmirror.com/@floating-ui/utils/-/utils-0.2.10.tgz", |
||||
|
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/@intlify/core-base": { |
||||
|
"version": "9.14.5", |
||||
|
"resolved": "https://registry.npmmirror.com/@intlify/core-base/-/core-base-9.14.5.tgz", |
||||
|
"integrity": "sha512-5ah5FqZG4pOoHjkvs8mjtv+gPKYU0zCISaYNjBNNqYiaITxW8ZtVih3GS/oTOqN8d9/mDLyrjD46GBApNxmlsA==", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@intlify/message-compiler": "9.14.5", |
||||
|
"@intlify/shared": "9.14.5" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">= 16" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/kazupon" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@intlify/message-compiler": { |
||||
|
"version": "9.14.5", |
||||
|
"resolved": "https://registry.npmmirror.com/@intlify/message-compiler/-/message-compiler-9.14.5.tgz", |
||||
|
"integrity": "sha512-IHzgEu61/YIpQV5Pc3aRWScDcnFKWvQA9kigcINcCBXN8mbW+vk9SK+lDxA6STzKQsVJxUPg9ACC52pKKo3SVQ==", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@intlify/shared": "9.14.5", |
||||
|
"source-map-js": "^1.0.2" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">= 16" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/kazupon" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@intlify/shared": { |
||||
|
"version": "9.14.5", |
||||
|
"resolved": "https://registry.npmmirror.com/@intlify/shared/-/shared-9.14.5.tgz", |
||||
|
"integrity": "sha512-9gB+E53BYuAEMhbCAxVgG38EZrk59sxBtv3jSizNL2hEWlgjBjAw1AwpLHtNaeda12pe6W20OGEa0TwuMSRbyQ==", |
||||
|
"license": "MIT", |
||||
|
"engines": { |
||||
|
"node": ">= 16" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/kazupon" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@jridgewell/sourcemap-codec": { |
||||
|
"version": "1.5.5", |
||||
|
"license": "MIT", |
||||
|
"peer": true |
||||
|
}, |
||||
|
"node_modules/@popperjs/core": { |
||||
|
"name": "@sxzz/popperjs-es", |
||||
|
"version": "2.11.7", |
||||
|
"resolved": "https://registry.npmmirror.com/@sxzz/popperjs-es/-/popperjs-es-2.11.7.tgz", |
||||
|
"integrity": "sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==", |
||||
|
"license": "MIT", |
||||
|
"funding": { |
||||
|
"type": "opencollective", |
||||
|
"url": "https://opencollective.com/popperjs" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@types/lodash": { |
||||
|
"version": "4.17.20", |
||||
|
"resolved": "https://registry.npmmirror.com/@types/lodash/-/lodash-4.17.20.tgz", |
||||
|
"integrity": "sha512-H3MHACvFUEiujabxhaI/ImO6gUrd8oOurg7LQtS7mbwIXA/cUqWrvBsaeJ23aZEPk1TAYkurjfMbSELfoCXlGA==", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/@types/lodash-es": { |
||||
|
"version": "4.17.12", |
||||
|
"resolved": "https://registry.npmmirror.com/@types/lodash-es/-/lodash-es-4.17.12.tgz", |
||||
|
"integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@types/lodash": "*" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@types/web-bluetooth": { |
||||
|
"version": "0.0.16", |
||||
|
"resolved": "https://registry.npmmirror.com/@types/web-bluetooth/-/web-bluetooth-0.0.16.tgz", |
||||
|
"integrity": "sha512-oh8q2Zc32S6gd/j50GowEjKLoOVOwHP/bWVjKJInBwQqdOYMdPrf1oVlelTlyfFK3CKxL1uahMDAr+vy8T7yMQ==", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/@vue/compiler-core": { |
||||
|
"version": "3.5.22", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@babel/parser": "^7.28.4", |
||||
|
"@vue/shared": "3.5.22", |
||||
|
"entities": "^4.5.0", |
||||
|
"estree-walker": "^2.0.2", |
||||
|
"source-map-js": "^1.2.1" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/compiler-dom": { |
||||
|
"version": "3.5.22", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@vue/compiler-core": "3.5.22", |
||||
|
"@vue/shared": "3.5.22" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/compiler-sfc": { |
||||
|
"version": "3.5.22", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@babel/parser": "^7.28.4", |
||||
|
"@vue/compiler-core": "3.5.22", |
||||
|
"@vue/compiler-dom": "3.5.22", |
||||
|
"@vue/compiler-ssr": "3.5.22", |
||||
|
"@vue/shared": "3.5.22", |
||||
|
"estree-walker": "^2.0.2", |
||||
|
"magic-string": "^0.30.19", |
||||
|
"postcss": "^8.5.6", |
||||
|
"source-map-js": "^1.2.1" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/compiler-ssr": { |
||||
|
"version": "3.5.22", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@vue/compiler-dom": "3.5.22", |
||||
|
"@vue/shared": "3.5.22" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/devtools-api": { |
||||
|
"version": "7.7.7", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@vue/devtools-kit": "^7.7.7" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/devtools-kit": { |
||||
|
"version": "7.7.7", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@vue/devtools-shared": "^7.7.7", |
||||
|
"birpc": "^2.3.0", |
||||
|
"hookable": "^5.5.3", |
||||
|
"mitt": "^3.0.1", |
||||
|
"perfect-debounce": "^1.0.0", |
||||
|
"speakingurl": "^14.0.1", |
||||
|
"superjson": "^2.2.2" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/devtools-shared": { |
||||
|
"version": "7.7.7", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"rfdc": "^1.4.1" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/reactivity": { |
||||
|
"version": "3.5.22", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@vue/shared": "3.5.22" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/runtime-core": { |
||||
|
"version": "3.5.22", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@vue/reactivity": "3.5.22", |
||||
|
"@vue/shared": "3.5.22" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/runtime-dom": { |
||||
|
"version": "3.5.22", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@vue/reactivity": "3.5.22", |
||||
|
"@vue/runtime-core": "3.5.22", |
||||
|
"@vue/shared": "3.5.22", |
||||
|
"csstype": "^3.1.3" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/server-renderer": { |
||||
|
"version": "3.5.22", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@vue/compiler-ssr": "3.5.22", |
||||
|
"@vue/shared": "3.5.22" |
||||
|
}, |
||||
|
"peerDependencies": { |
||||
|
"vue": "3.5.22" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vue/shared": { |
||||
|
"version": "3.5.22", |
||||
|
"license": "MIT", |
||||
|
"peer": true |
||||
|
}, |
||||
|
"node_modules/@vueuse/core": { |
||||
|
"version": "9.13.0", |
||||
|
"resolved": "https://registry.npmmirror.com/@vueuse/core/-/core-9.13.0.tgz", |
||||
|
"integrity": "sha512-pujnclbeHWxxPRqXWmdkKV5OX4Wk4YeK7wusHqRwU0Q7EFusHoqNA/aPhB6KCh9hEqJkLAJo7bb0Lh9b+OIVzw==", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@types/web-bluetooth": "^0.0.16", |
||||
|
"@vueuse/metadata": "9.13.0", |
||||
|
"@vueuse/shared": "9.13.0", |
||||
|
"vue-demi": "*" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/antfu" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vueuse/core/node_modules/vue-demi": { |
||||
|
"version": "0.14.10", |
||||
|
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", |
||||
|
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", |
||||
|
"hasInstallScript": true, |
||||
|
"license": "MIT", |
||||
|
"bin": { |
||||
|
"vue-demi-fix": "bin/vue-demi-fix.js", |
||||
|
"vue-demi-switch": "bin/vue-demi-switch.js" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=12" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/antfu" |
||||
|
}, |
||||
|
"peerDependencies": { |
||||
|
"@vue/composition-api": "^1.0.0-rc.1", |
||||
|
"vue": "^3.0.0-0 || ^2.6.0" |
||||
|
}, |
||||
|
"peerDependenciesMeta": { |
||||
|
"@vue/composition-api": { |
||||
|
"optional": true |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vueuse/metadata": { |
||||
|
"version": "9.13.0", |
||||
|
"resolved": "https://registry.npmmirror.com/@vueuse/metadata/-/metadata-9.13.0.tgz", |
||||
|
"integrity": "sha512-gdU7TKNAUVlXXLbaF+ZCfte8BjRJQWPCa2J55+7/h+yDtzw3vOoGQDRXzI6pyKyo6bXFT5/QoPE4hAknExjRLQ==", |
||||
|
"license": "MIT", |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/antfu" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vueuse/shared": { |
||||
|
"version": "9.13.0", |
||||
|
"resolved": "https://registry.npmmirror.com/@vueuse/shared/-/shared-9.13.0.tgz", |
||||
|
"integrity": "sha512-UrnhU+Cnufu4S6JLCPZnkWh0WwZGUp72ktOF2DFptMlOs3TOdVv8xJN53zhHGARmVOsz5KqOls09+J1NR6sBKw==", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"vue-demi": "*" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/antfu" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/@vueuse/shared/node_modules/vue-demi": { |
||||
|
"version": "0.14.10", |
||||
|
"resolved": "https://registry.npmmirror.com/vue-demi/-/vue-demi-0.14.10.tgz", |
||||
|
"integrity": "sha512-nMZBOwuzabUO0nLgIcc6rycZEebF6eeUfaiQx9+WSk8e29IbLvPU9feI6tqW4kTo3hvoYAJkMh8n8D0fuISphg==", |
||||
|
"hasInstallScript": true, |
||||
|
"license": "MIT", |
||||
|
"bin": { |
||||
|
"vue-demi-fix": "bin/vue-demi-fix.js", |
||||
|
"vue-demi-switch": "bin/vue-demi-switch.js" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=12" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/antfu" |
||||
|
}, |
||||
|
"peerDependencies": { |
||||
|
"@vue/composition-api": "^1.0.0-rc.1", |
||||
|
"vue": "^3.0.0-0 || ^2.6.0" |
||||
|
}, |
||||
|
"peerDependenciesMeta": { |
||||
|
"@vue/composition-api": { |
||||
|
"optional": true |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/async-validator": { |
||||
|
"version": "4.2.5", |
||||
|
"resolved": "https://registry.npmmirror.com/async-validator/-/async-validator-4.2.5.tgz", |
||||
|
"integrity": "sha512-7HhHjtERjqlNbZtqNqy2rckN/SpOOlmDliet+lP7k+eKZEjPk3DgyeU9lIXLdeLz0uBbbVp+9Qdow9wJWgwwfg==", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/birpc": { |
||||
|
"version": "2.6.1", |
||||
|
"license": "MIT", |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/antfu" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/copy-anything": { |
||||
|
"version": "4.0.5", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"is-what": "^5.2.0" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=18" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/mesqueeb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/csstype": { |
||||
|
"version": "3.1.3", |
||||
|
"license": "MIT", |
||||
|
"peer": true |
||||
|
}, |
||||
|
"node_modules/dayjs": { |
||||
|
"version": "1.11.18", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/deep-pick-omit": { |
||||
|
"version": "1.2.1", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/defu": { |
||||
|
"version": "6.1.4", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/destr": { |
||||
|
"version": "2.0.5", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/element-plus": { |
||||
|
"version": "2.11.5", |
||||
|
"resolved": "https://registry.npmmirror.com/element-plus/-/element-plus-2.11.5.tgz", |
||||
|
"integrity": "sha512-O+bIVHQCjUDm4GiIznDXRoS8ar2TpWLwfOGnN/Aam0VXf5kbuc4SxdKKJdovWNxmxeqbcwjsSZPKgtXNcqys4A==", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@ctrl/tinycolor": "^3.4.1", |
||||
|
"@element-plus/icons-vue": "^2.3.2", |
||||
|
"@floating-ui/dom": "^1.0.1", |
||||
|
"@popperjs/core": "npm:@sxzz/popperjs-es@^2.11.7", |
||||
|
"@types/lodash": "^4.17.20", |
||||
|
"@types/lodash-es": "^4.17.12", |
||||
|
"@vueuse/core": "^9.1.0", |
||||
|
"async-validator": "^4.2.5", |
||||
|
"dayjs": "^1.11.18", |
||||
|
"lodash": "^4.17.21", |
||||
|
"lodash-es": "^4.17.21", |
||||
|
"lodash-unified": "^1.0.3", |
||||
|
"memoize-one": "^6.0.0", |
||||
|
"normalize-wheel-es": "^1.2.0" |
||||
|
}, |
||||
|
"peerDependencies": { |
||||
|
"vue": "^3.2.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/entities": { |
||||
|
"version": "4.5.0", |
||||
|
"license": "BSD-2-Clause", |
||||
|
"peer": true, |
||||
|
"engines": { |
||||
|
"node": ">=0.12" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/fb55/entities?sponsor=1" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/estree-walker": { |
||||
|
"version": "2.0.2", |
||||
|
"license": "MIT", |
||||
|
"peer": true |
||||
|
}, |
||||
|
"node_modules/highlight.js": { |
||||
|
"version": "11.11.1", |
||||
|
"resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-11.11.1.tgz", |
||||
|
"integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", |
||||
|
"license": "BSD-3-Clause", |
||||
|
"engines": { |
||||
|
"node": ">=12.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/hookable": { |
||||
|
"version": "5.5.3", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/is-what": { |
||||
|
"version": "5.5.0", |
||||
|
"license": "MIT", |
||||
|
"engines": { |
||||
|
"node": ">=18" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/mesqueeb" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/lodash": { |
||||
|
"version": "4.17.21", |
||||
|
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz", |
||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/lodash-es": { |
||||
|
"version": "4.17.21", |
||||
|
"resolved": "https://registry.npmmirror.com/lodash-es/-/lodash-es-4.17.21.tgz", |
||||
|
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/lodash-unified": { |
||||
|
"version": "1.0.3", |
||||
|
"resolved": "https://registry.npmmirror.com/lodash-unified/-/lodash-unified-1.0.3.tgz", |
||||
|
"integrity": "sha512-WK9qSozxXOD7ZJQlpSqOT+om2ZfcT4yO+03FuzAHD0wF6S0l0090LRPDx3vhTTLZ8cFKpBn+IOcVXK6qOcIlfQ==", |
||||
|
"license": "MIT", |
||||
|
"peerDependencies": { |
||||
|
"@types/lodash-es": "*", |
||||
|
"lodash": "*", |
||||
|
"lodash-es": "*" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/magic-string": { |
||||
|
"version": "0.30.21", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@jridgewell/sourcemap-codec": "^1.5.5" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/marked": { |
||||
|
"version": "2.0.1", |
||||
|
"license": "MIT", |
||||
|
"bin": { |
||||
|
"marked": "bin/marked" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">= 8.16.2" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/memoize-one": { |
||||
|
"version": "6.0.0", |
||||
|
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", |
||||
|
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/mitt": { |
||||
|
"version": "3.0.1", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/nanoid": { |
||||
|
"version": "3.3.11", |
||||
|
"funding": [ |
||||
|
{ |
||||
|
"type": "github", |
||||
|
"url": "https://github.com/sponsors/ai" |
||||
|
} |
||||
|
], |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"bin": { |
||||
|
"nanoid": "bin/nanoid.cjs" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/normalize-wheel-es": { |
||||
|
"version": "1.2.0", |
||||
|
"resolved": "https://registry.npmmirror.com/normalize-wheel-es/-/normalize-wheel-es-1.2.0.tgz", |
||||
|
"integrity": "sha512-Wj7+EJQ8mSuXr2iWfnujrimU35R2W4FAErEyTmJoJ7ucwTn2hOUSsRehMb5RSYkxXGTM7Y9QpvPmp++w5ftoJw==", |
||||
|
"license": "BSD-3-Clause" |
||||
|
}, |
||||
|
"node_modules/perfect-debounce": { |
||||
|
"version": "1.0.0", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/picocolors": { |
||||
|
"version": "1.1.1", |
||||
|
"license": "ISC", |
||||
|
"peer": true |
||||
|
}, |
||||
|
"node_modules/pinia": { |
||||
|
"version": "3.0.3", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@vue/devtools-api": "^7.7.2" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/posva" |
||||
|
}, |
||||
|
"peerDependencies": { |
||||
|
"typescript": ">=4.4.4", |
||||
|
"vue": "^2.7.0 || ^3.5.11" |
||||
|
}, |
||||
|
"peerDependenciesMeta": { |
||||
|
"typescript": { |
||||
|
"optional": true |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/pinia-plugin-persistedstate": { |
||||
|
"version": "4.5.0", |
||||
|
"resolved": "https://registry.npmjs.org/pinia-plugin-persistedstate/-/pinia-plugin-persistedstate-4.5.0.tgz", |
||||
|
"integrity": "sha512-QTkP1xJVyCdr2I2p3AKUZM84/e+IS+HktRxKGAIuDzkyaKKV48mQcYkJFVVDuvTxlI5j6X3oZObpqoVB8JnWpw==", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"deep-pick-omit": "^1.2.1", |
||||
|
"defu": "^6.1.4", |
||||
|
"destr": "^2.0.5" |
||||
|
}, |
||||
|
"peerDependencies": { |
||||
|
"@nuxt/kit": ">=3.0.0", |
||||
|
"@pinia/nuxt": ">=0.10.0", |
||||
|
"pinia": ">=3.0.0" |
||||
|
}, |
||||
|
"peerDependenciesMeta": { |
||||
|
"@nuxt/kit": { |
||||
|
"optional": true |
||||
|
}, |
||||
|
"@pinia/nuxt": { |
||||
|
"optional": true |
||||
|
}, |
||||
|
"pinia": { |
||||
|
"optional": true |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/postcss": { |
||||
|
"version": "8.5.6", |
||||
|
"funding": [ |
||||
|
{ |
||||
|
"type": "opencollective", |
||||
|
"url": "https://opencollective.com/postcss/" |
||||
|
}, |
||||
|
{ |
||||
|
"type": "tidelift", |
||||
|
"url": "https://tidelift.com/funding/github/npm/postcss" |
||||
|
}, |
||||
|
{ |
||||
|
"type": "github", |
||||
|
"url": "https://github.com/sponsors/ai" |
||||
|
} |
||||
|
], |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"nanoid": "^3.3.11", |
||||
|
"picocolors": "^1.1.1", |
||||
|
"source-map-js": "^1.2.1" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": "^10 || ^12 || >=14" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/rfdc": { |
||||
|
"version": "1.4.1", |
||||
|
"license": "MIT" |
||||
|
}, |
||||
|
"node_modules/source-map-js": { |
||||
|
"version": "1.2.1", |
||||
|
"license": "BSD-3-Clause", |
||||
|
"engines": { |
||||
|
"node": ">=0.10.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/speakingurl": { |
||||
|
"version": "14.0.1", |
||||
|
"license": "BSD-3-Clause", |
||||
|
"engines": { |
||||
|
"node": ">=0.10.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/superjson": { |
||||
|
"version": "2.2.3", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"copy-anything": "^4" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">=16" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/vue": { |
||||
|
"version": "3.5.22", |
||||
|
"license": "MIT", |
||||
|
"peer": true, |
||||
|
"dependencies": { |
||||
|
"@vue/compiler-dom": "3.5.22", |
||||
|
"@vue/compiler-sfc": "3.5.22", |
||||
|
"@vue/runtime-dom": "3.5.22", |
||||
|
"@vue/server-renderer": "3.5.22", |
||||
|
"@vue/shared": "3.5.22" |
||||
|
}, |
||||
|
"peerDependencies": { |
||||
|
"typescript": "*" |
||||
|
}, |
||||
|
"peerDependenciesMeta": { |
||||
|
"typescript": { |
||||
|
"optional": true |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/vue-i18n": { |
||||
|
"version": "9.14.5", |
||||
|
"resolved": "https://registry.npmmirror.com/vue-i18n/-/vue-i18n-9.14.5.tgz", |
||||
|
"integrity": "sha512-0jQ9Em3ymWngyiIkj0+c/k7WgaPO+TNzjKSNq9BvBQaKJECqn9cd9fL4tkDhB5G1QBskGl9YxxbDAhgbFtpe2g==", |
||||
|
"license": "MIT", |
||||
|
"dependencies": { |
||||
|
"@intlify/core-base": "9.14.5", |
||||
|
"@intlify/shared": "9.14.5", |
||||
|
"@vue/devtools-api": "^6.5.0" |
||||
|
}, |
||||
|
"engines": { |
||||
|
"node": ">= 16" |
||||
|
}, |
||||
|
"funding": { |
||||
|
"url": "https://github.com/sponsors/kazupon" |
||||
|
}, |
||||
|
"peerDependencies": { |
||||
|
"vue": "^3.0.0" |
||||
|
} |
||||
|
}, |
||||
|
"node_modules/vue-i18n/node_modules/@vue/devtools-api": { |
||||
|
"version": "6.6.4", |
||||
|
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz", |
||||
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", |
||||
|
"license": "MIT" |
||||
|
} |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,12 @@ |
|||||
|
{ |
||||
|
"dependencies": { |
||||
|
"@dcloudio/uni-ui": "^1.5.11", |
||||
|
"@element-plus/icons-vue": "^2.3.2", |
||||
|
"element-plus": "^2.11.5", |
||||
|
"highlight.js": "^11.11.1", |
||||
|
"marked": "^2.0.1", |
||||
|
"pinia": "^3.0.3", |
||||
|
"pinia-plugin-persistedstate": "^4.5.0", |
||||
|
"vue-i18n": "^9.14.5" |
||||
|
} |
||||
|
} |
||||
@ -0,0 +1,55 @@ |
|||||
|
<template> |
||||
|
<view class="container"> |
||||
|
<view class="content"> |
||||
|
<image |
||||
|
class="no-data-image" |
||||
|
src="https://d31zlh4on95l9h.cloudfront.net/images/f5a9bd32c81bc7cca47252b51357c12f.png" |
||||
|
mode="aspectFit" |
||||
|
></image> |
||||
|
<text class="no-data-text">暂无数据~</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
|
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.container { |
||||
|
width: 100%; |
||||
|
height: 100vh; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.no-data-image { |
||||
|
width: 200px; |
||||
|
height: 200px; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.no-data-text { |
||||
|
font-size: 16px; |
||||
|
color: #999999; |
||||
|
text-align: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,100 @@ |
|||||
|
<template> |
||||
|
|
||||
|
<view class="blank-page"> |
||||
|
<view class="header" :style="{ paddingTop: safeAreaInsets?.top + 'px' }"> |
||||
|
<!-- 返回按钮 --> |
||||
|
<view class="head-left"> |
||||
|
<image class="back-button" @click="goBack" src="/static/icons/Left_(左).png"> |
||||
|
|
||||
|
|
||||
|
<!-- <text class="tip">当前特斯拉该如何布局?</text> --> |
||||
|
</image> |
||||
|
</view> |
||||
|
<view class="header-center"> |
||||
|
<text class="title" :style="{ paddingTop: safeAreaInsets?.top + 'px' }" |
||||
|
>机构动向解析</text |
||||
|
> |
||||
|
</view> |
||||
|
</view> |
||||
|
<image class="picture" src="/static/images/缺省.png" /> |
||||
|
<text class="tip">暂无内容~</text> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
// 返回到 deepMate 页面 |
||||
|
const goBack = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/deepMate/deepMate' |
||||
|
}); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.blank-page { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
position: fixed; |
||||
|
/* 充满视口,彻底禁用页面滚动 */ |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
height: 100vh; |
||||
|
overflow: hidden; |
||||
|
/* 锁定页面滚动 */ |
||||
|
background-color: #ffffff; |
||||
|
padding: 20rpx 0rpx; |
||||
|
} |
||||
|
.header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 20rpx 30rpx; |
||||
|
background-color: #ffffff; |
||||
|
box-shadow: 0 2rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
.head-left { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.back-button { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
} |
||||
|
.header-center .title { |
||||
|
position: fixed; |
||||
|
top: 25rpx; |
||||
|
left: 50%; |
||||
|
transform: translateX(-50%); |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333333; |
||||
|
} |
||||
|
.back-button:hover { |
||||
|
background-color: #e0e0e0; |
||||
|
} |
||||
|
|
||||
|
.back-button:active { |
||||
|
transform: scale(0.95); |
||||
|
} |
||||
|
|
||||
|
.back-icon { |
||||
|
font-size: 32rpx; |
||||
|
color: #333333; |
||||
|
margin-right: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.picture { |
||||
|
display: block; |
||||
|
margin: 200rpx auto 0; /* 图片水平居中 */ |
||||
|
width: 60%; |
||||
|
height: 600rpx; |
||||
|
} |
||||
|
.tip { |
||||
|
color: #999999; |
||||
|
font-size: 28rpx; |
||||
|
text-align: center; |
||||
|
margin-top: 20rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,100 @@ |
|||||
|
<template> |
||||
|
|
||||
|
<view class="blank-page"> |
||||
|
<view class="header" :style="{ paddingTop: safeAreaInsets?.top + 'px' }"> |
||||
|
<!-- 返回按钮 --> |
||||
|
<view class="head-left"> |
||||
|
<image class="back-button" @click="goBack" src="/static/icons/Left_(左).png"> |
||||
|
|
||||
|
|
||||
|
<!-- <text class="tip">当前特斯拉该如何布局?</text> --> |
||||
|
</image> |
||||
|
</view> |
||||
|
<view class="header-center"> |
||||
|
<text class="title" :style="{ paddingTop: safeAreaInsets?.top + 'px' }" |
||||
|
>消息推送通知</text |
||||
|
> |
||||
|
</view> |
||||
|
</view> |
||||
|
<image class="picture" src="/static/images/缺省.png" /> |
||||
|
<text class="tip">暂无内容~</text> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
// 返回到 deepMate 页面 |
||||
|
const goBack = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/deepMate/deepMate' |
||||
|
}); |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.blank-page { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
position: fixed; |
||||
|
/* 充满视口,彻底禁用页面滚动 */ |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
height: 100vh; |
||||
|
overflow: hidden; |
||||
|
/* 锁定页面滚动 */ |
||||
|
background-color: #ffffff; |
||||
|
padding: 20rpx 0rpx; |
||||
|
} |
||||
|
.header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 20rpx 30rpx; |
||||
|
background-color: #ffffff; |
||||
|
box-shadow: 0 2rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
.head-left { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
.back-button { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
} |
||||
|
.header-center .title { |
||||
|
position: fixed; |
||||
|
top: 25rpx; |
||||
|
left: 50%; |
||||
|
transform: translateX(-50%); |
||||
|
font-size: 36rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333333; |
||||
|
} |
||||
|
.back-button:hover { |
||||
|
background-color: #e0e0e0; |
||||
|
} |
||||
|
|
||||
|
.back-button:active { |
||||
|
transform: scale(0.95); |
||||
|
} |
||||
|
|
||||
|
.back-icon { |
||||
|
font-size: 32rpx; |
||||
|
color: #333333; |
||||
|
margin-right: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.picture { |
||||
|
display: block; |
||||
|
margin: 200rpx auto 0; /* 图片水平居中 */ |
||||
|
width: 60%; |
||||
|
height: 600rpx; |
||||
|
} |
||||
|
.tip { |
||||
|
color: #999999; |
||||
|
font-size: 28rpx; |
||||
|
text-align: center; |
||||
|
margin-top: 20rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,147 @@ |
|||||
|
|
||||
|
<!-- 自选股页面 --> |
||||
|
<template> |
||||
|
<view class="container"> |
||||
|
<!-- 自定义导航栏 --> |
||||
|
<view class="custom-navbar"> |
||||
|
<view class="navbar-content"> |
||||
|
<view class="navbar-left"> |
||||
|
<view class="back-btn" @click="goBack"> |
||||
|
<text class="back-icon">‹</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="navbar-center"> |
||||
|
<text class="navbar-title">我的自选</text> |
||||
|
</view> |
||||
|
<view class="navbar-right"> |
||||
|
<image |
||||
|
class="navbar-btn" |
||||
|
src="https://d31zlh4on95l9h.cloudfront.net/images/ba5c8a2eda065274e868bcd9b2d7d914.png" |
||||
|
@click="onFirstButtonClick" |
||||
|
mode="aspectFit" |
||||
|
></image> |
||||
|
<image |
||||
|
class="navbar-btn" |
||||
|
src="https://d31zlh4on95l9h.cloudfront.net/images/a4ae8952aeae90dac6d2b4c221c65fa9.png" |
||||
|
@click="onSecondButtonClick" |
||||
|
mode="aspectFit" |
||||
|
></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 页面内容 --> |
||||
|
<view class="page-content"> |
||||
|
|
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
|
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
// 返回上一页 |
||||
|
goBack() { |
||||
|
uni.navigateBack() |
||||
|
}, |
||||
|
|
||||
|
// 第一个按钮点击事件 |
||||
|
onFirstButtonClick() { |
||||
|
console.log('第一个按钮被点击') |
||||
|
// 这里可以添加具体的功能逻辑 |
||||
|
}, |
||||
|
|
||||
|
// 第二个按钮点击事件 |
||||
|
onSecondButtonClick() { |
||||
|
console.log('第二个按钮被点击') |
||||
|
// 这里可以添加具体的功能逻辑 |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.container { |
||||
|
width: 100%; |
||||
|
height: 100vh; |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
|
||||
|
/* 自定义导航栏 */ |
||||
|
.custom-navbar { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 999; |
||||
|
background-color: #ffffff; |
||||
|
border-bottom: 1px solid #e5e5e5; |
||||
|
} |
||||
|
|
||||
|
.navbar-content { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
height: 44px; |
||||
|
padding: 0 15px; |
||||
|
/* 适配状态栏高度 */ |
||||
|
padding-top: var(--status-bar-height, 20px); |
||||
|
min-height: calc(44px + var(--status-bar-height, 20px)); |
||||
|
} |
||||
|
|
||||
|
.navbar-left { |
||||
|
flex: 0 0 auto; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.back-btn { |
||||
|
width: 40px; |
||||
|
height: 40px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.back-icon { |
||||
|
font-size: 24px; |
||||
|
color: #333333; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.navbar-center { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.navbar-title { |
||||
|
font-size: 18px; |
||||
|
font-weight: 500; |
||||
|
color: #333333; |
||||
|
} |
||||
|
|
||||
|
.navbar-right { |
||||
|
flex: 0 0 auto; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 10px; |
||||
|
} |
||||
|
|
||||
|
.navbar-btn { |
||||
|
width: 24px; |
||||
|
height: 24px; |
||||
|
} |
||||
|
|
||||
|
/* 页面内容 */ |
||||
|
.page-content { |
||||
|
padding-top: calc(44px + var(--status-bar-height, 20px) + 1px); |
||||
|
min-height: calc(100vh - 44px - var(--status-bar-height, 20px) - 1px); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,682 @@ |
|||||
|
<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 { |
||||
|
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: [], |
||||
|
} |
||||
|
}, |
||||
|
mounted() { |
||||
|
// 状态栏高度 |
||||
|
this.iSMT = uni.getSystemInfoSync().statusBarHeight; |
||||
|
this.getQuestionList() |
||||
|
}, |
||||
|
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({ |
||||
|
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,353 @@ |
|||||
|
<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 { |
||||
|
getFeedbackRecordsApi, |
||||
|
} from "../../api/customerServicePlatform/customerServicePlatform"; |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
iSMT: 0, |
||||
|
statusText: '反馈成功', |
||||
|
historyList: [], |
||||
|
}; |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.iSMT = uni.getSystemInfoSync().statusBarHeight; |
||||
|
this.loadHistoryList() |
||||
|
|
||||
|
}, |
||||
|
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() |
||||
|
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,335 @@ |
|||||
|
<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 { |
||||
|
getAnswerApi |
||||
|
} from "../../api/customerServicePlatform/customerServicePlatform"; |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
headerTitle: '智能客服中台', |
||||
|
iSMT: 0, |
||||
|
questionTitle: '', |
||||
|
answerContent: '正在思考...', |
||||
|
showLoginRegister:false, |
||||
|
}; |
||||
|
}, |
||||
|
mounted() { |
||||
|
this.iSMT = uni.getSystemInfoSync().statusBarHeight || 0; |
||||
|
this.getAnswerContent() |
||||
|
}, |
||||
|
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, |
||||
|
}) |
||||
|
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> |
||||
@ -0,0 +1,720 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<!-- 顶部状态栏占位 --> |
||||
|
<view class="top" :style="{height:iSMT+'px'}"></view> |
||||
|
<deepExploration_header></deepExploration_header> |
||||
|
|
||||
|
<view class="search"> |
||||
|
<input v-model="searchName" class="searchInput" type="text" placeholder="请输入股票名称、股票代码" |
||||
|
placeholder-style="color: #A6A6A6; font-size: 22rpx;" /> |
||||
|
<image @click="searchStock" class="seachIcon" src="/static/deepExploration-images/search.png" |
||||
|
mode="aspectFill"></image> |
||||
|
</view> |
||||
|
|
||||
|
<view class="content"> |
||||
|
<view class="select"> |
||||
|
<image class="img" :src="navItems[currentIndex].icon" mode=""></image> |
||||
|
<view v-for="(item, index) in navItems" :key="index" class="selectItem" |
||||
|
:class="{ active: currentIndex === index }" @click="handleModel(index)"> |
||||
|
<button class="btn"></button> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="graphAndTxt"> |
||||
|
<view class="graph"> |
||||
|
<view class="graph_header"> |
||||
|
<view class="left">{{stockCode}}</view> |
||||
|
<view class="center"> |
||||
|
<text>{{stockName}}</text> |
||||
|
</view> |
||||
|
<view class="right">{{stockTime}}</view> |
||||
|
</view> |
||||
|
<view class="graph_data"> |
||||
|
<text>{{stockPrice}}</text> |
||||
|
<text>{{stockChange}}</text> |
||||
|
<text>{{stockAdd}}</text> |
||||
|
</view> |
||||
|
<view class="graph_content"> |
||||
|
<view class="charts-box"> |
||||
|
<!-- uCharts 蜡烛图组件 --> |
||||
|
<qiun-data-charts type="candle" :opts="opts" :chartData="chartData" :disableScroll="true" |
||||
|
:ontouch="true" :onzoom="true" :key="chartKey" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="txt"> |
||||
|
<view class="txtHeader"> |
||||
|
<image src="/static/deepExploration-images/plus.png" mode="aspectFill"></image> |
||||
|
<text>{{navItems[currentIndex].name}}</text> |
||||
|
</view> |
||||
|
<view class="txtContent"> |
||||
|
<view v-if="loading" class="loading">加载中...</view> |
||||
|
<rich-text :nodes="htmlContent"></rich-text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部切换栏 --> |
||||
|
<footerBar class="static-footer" :type="type"></footerBar> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted, |
||||
|
watch |
||||
|
} from 'vue' |
||||
|
import deepExploration_header from '@/components/deepExploration_header.vue' |
||||
|
import footerBar from '@/components/footerBar.vue' |
||||
|
import { |
||||
|
onLoad |
||||
|
} from '@dcloudio/uni-app' |
||||
|
import { |
||||
|
getModel1First, |
||||
|
getModel1Second, |
||||
|
getModel2First, |
||||
|
getModel2Second, |
||||
|
getModel3First, |
||||
|
getModel3Second, |
||||
|
getModel4First, |
||||
|
getModel4Second, |
||||
|
getModeldefault, |
||||
|
getData |
||||
|
} from '/api/deepExploration/deepExploration.js' |
||||
|
import marked from 'marked'; // 引入 marked 库 |
||||
|
import hljs from 'highlight.js'; |
||||
|
import 'highlight.js/styles/atom-one-dark.css'; // 可替换为其他主题 |
||||
|
import { |
||||
|
useDeepExplorationStore |
||||
|
} from '@/stores/modules/deepExploration' |
||||
|
|
||||
|
const deepExplorationStore = useDeepExplorationStore() |
||||
|
|
||||
|
const historyData = ref({}) |
||||
|
|
||||
|
// 响应式变量定义 |
||||
|
const type = ref('deepExploration') |
||||
|
const iSMT = ref(0) |
||||
|
const currentIndex = ref(0) |
||||
|
const navItems = ref([{ |
||||
|
name: '主力追踪', |
||||
|
icon: '/static/deepExploration-images/1.png' |
||||
|
}, |
||||
|
{ |
||||
|
name: '主力雷达', |
||||
|
icon: '/static/deepExploration-images/2.png' |
||||
|
}, |
||||
|
{ |
||||
|
name: '主力解码', |
||||
|
icon: '/static/deepExploration-images/3.png' |
||||
|
}, |
||||
|
{ |
||||
|
name: '主力资金流', |
||||
|
icon: '/static/deepExploration-images/4.png' |
||||
|
}, |
||||
|
]) |
||||
|
|
||||
|
//搜索股票 |
||||
|
const searchStock = () => { |
||||
|
htmlContent.value = '' |
||||
|
console.log('搜索参数:', stockName.value, currentIndex.value); |
||||
|
if (currentIndex.value >= 0 && currentIndex.value <= 3) { |
||||
|
handleModels() |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: '请选择模块', |
||||
|
icon: 'none', |
||||
|
duration: 2000 |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
//点击四大模块 |
||||
|
const handleModel = async (index) => { |
||||
|
htmlContent.value = '' |
||||
|
currentIndex.value = index |
||||
|
await handleModels() |
||||
|
// await getServerData() |
||||
|
} |
||||
|
|
||||
|
const stockName = ref('Tesla Inc.') |
||||
|
const searchName = ref('') |
||||
|
const stockCode = ref('TSLA') |
||||
|
const language = ref('') |
||||
|
const recordId = ref('') |
||||
|
const parentId = ref('') |
||||
|
const stockId = ref('') |
||||
|
const market = ref('') |
||||
|
const stockTime = ref('2025/10/24') |
||||
|
|
||||
|
const loading = ref(true); |
||||
|
const error = ref(''); |
||||
|
const htmlContent = ref(''); |
||||
|
const markdownContent = ref(''); |
||||
|
|
||||
|
|
||||
|
const renderer = new marked.Renderer(); |
||||
|
renderer.heading = function(text, level) { |
||||
|
return `<p>${text}</p>`; |
||||
|
}; |
||||
|
// 初始化 marked 配置(支持代码高亮) |
||||
|
marked.setOptions({ |
||||
|
highlight: (code, lang) => { |
||||
|
if (lang && hljs.getLanguage(lang)) { |
||||
|
return hljs.highlight(code, { |
||||
|
language: lang |
||||
|
}).value; |
||||
|
} |
||||
|
return hljs.highlightAuto(code).value; |
||||
|
}, |
||||
|
renderer, |
||||
|
breaks: true, // 换行转<br> |
||||
|
gfm: true, // 支持GitHub flavored Markdown |
||||
|
sanitize: false, // 保留HTML标签(如<span style>) |
||||
|
}); |
||||
|
|
||||
|
//获取模型数据 |
||||
|
const handleModels = async () => { |
||||
|
try { |
||||
|
// markdownContent.value = '\n## 📊 主力追踪分析:\n\n### 🕵️ 主力行为\n\t1. 📊 该股庄家中长期筹码成本价格为 360.249,短期资金成本价格为 412.577。该股筹码分散,当日筹码成本价格为 444.330。\n\t2. 🔍 近日没有出现主力集中吸筹。\n\t3. 📈 近期主力持仓比例大于散户持仓比例。 当日主力持仓增加。 当日散户持仓减少。\n\n### 📊 空间维度:\n\t- 📉 预测低一值: <font color=\"#13c2c2\">443.092</font> \n - 📈 预测高一值: <font color=\"#ff4d4f\">466.458</font>\n\t- 📉 预测低二值: <font color=\"#13c2c2\">447.354</font>\n\t- 📈 预测高二值: <font color=\"#ff4d4f\">462.514</font>\n\t<font color=\"#722ed1\">AI智能均线空头排列,当前卖盘小于买盘</font>\n\n### 综合作战\n\t\t\t<font color=\"#fa8c16\">当前股票处于安全区,牵牛绳为红色,出现蓝色推进K线。</font>\n\t\t\t<font color=\"#eb2f96\">该股整体趋势相对较强,个股正处于推进上涨的关键阶段。若当前持有该股票,建议继续持有,进行持续跟踪。若当前无该股票,建议持续跟踪,等待适当时机再进行介入。</font>\n\n---\n<font color=\"#8c8c8c\">*该内容由AI生成,仅供参考,投资有风险,请注意甄别。*</font>\n ' |
||||
|
// htmlContent.value = marked.parse(markdownContent.value); |
||||
|
loading.value = true; |
||||
|
if (searchName.value == '') { |
||||
|
console.log('没有搜索', searchName.value); |
||||
|
handleDefault() |
||||
|
} else { |
||||
|
console.log('搜索', searchName.value); |
||||
|
const result = await getModel1First({ |
||||
|
content: searchName.value, |
||||
|
language: "cn", |
||||
|
marketList: "hk,cn,usa,my,sg,vi,in,gb", |
||||
|
model: currentIndex.value + 1 |
||||
|
}) |
||||
|
console.log('result', result); |
||||
|
if (result.code == 200) { |
||||
|
stockCode.value = result.data.code |
||||
|
// stockName.value = result.data.name |
||||
|
recordId.value = result.data.recordId |
||||
|
parentId.value = result.data.parentId |
||||
|
stockId.value = result.data.stockId |
||||
|
language.value = result.data.language |
||||
|
market.value = result.data.market |
||||
|
|
||||
|
|
||||
|
const res = await getModel1Second({ |
||||
|
language: language.value, |
||||
|
recordId: recordId.value, |
||||
|
parentId: parentId.value, |
||||
|
stockId: stockId.value, |
||||
|
token: 'pCtw6AYK0EHAaIexoFHsbZjtsfEAIhcmwkCFm6uKko8VPfMvyDiODL9v9c0veic9fIpQbvT8zN4sH/Si6Q' |
||||
|
}) |
||||
|
if (res.code == 200) { |
||||
|
const rawMarkdown = res.data.markdown; |
||||
|
const adaptedMarkdown = rawMarkdown.replace(/^### /gm, ''); // 全局替换行首的### |
||||
|
markdownContent.value = adaptedMarkdown; |
||||
|
// markdownContent.value = res.data.markdown |
||||
|
htmlContent.value = marked.parse(markdownContent.value); |
||||
|
} |
||||
|
console.log('res', res); |
||||
|
|
||||
|
await getServerData() |
||||
|
|
||||
|
|
||||
|
} else if (result.code == 400) { |
||||
|
markdownContent.value = result.message; |
||||
|
htmlContent.value = marked.parse(markdownContent.value); |
||||
|
} else { |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
} catch (e) { |
||||
|
error.value = e.message || '加载失败,请重试'; |
||||
|
} finally { |
||||
|
loading.value = false; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const handleDefault = async () => { |
||||
|
const result = await getModeldefault({ |
||||
|
token: "pCtw6AYK0EHAaIexoFHsbZjtsfEAIhcmwkCFm6uKko8VPfMvyDiODL9v9c0veic9fIpQbvT8zN4sH/Si6Q", |
||||
|
model: currentIndex.value + 1 |
||||
|
}) |
||||
|
if (result.code == 200) { |
||||
|
const rawMarkdown = result.data.markdown; |
||||
|
const adaptedMarkdown = rawMarkdown.replace(/^### /gm, ''); // 全局替换行首的### |
||||
|
markdownContent.value = adaptedMarkdown; |
||||
|
htmlContent.value = marked.parse(markdownContent.value); |
||||
|
} else { |
||||
|
|
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
const stockPrice = ref('435.900') |
||||
|
const stockAdd = ref('22.410') |
||||
|
const stockChange = ref('5.120%') |
||||
|
const chartKey = ref(0); |
||||
|
|
||||
|
const getServerData = async () => { |
||||
|
const result = await getData({ |
||||
|
market: market.value || '', |
||||
|
code: searchName.value || '', |
||||
|
language: "cn", |
||||
|
brainPrivilegeState: 1, |
||||
|
marketList: "usa.sg.my.hk.cn.can.vi.th.in.gb" |
||||
|
}) |
||||
|
|
||||
|
console.log('k线数据', result); |
||||
|
|
||||
|
stockName.value = result.data.StockInformation.Name || 'Tesla Inc.' |
||||
|
stockCode.value = result.data.StockInformation.Code || 'TSLA' |
||||
|
stockTime.value = result.data.StockInformation.Time || '2025/10/29' |
||||
|
stockChange.value = result.data.StockInformation.Zhang || '5.120%' |
||||
|
stockAdd.value = result.data.StockInformation.ZhangFu || '22.410' |
||||
|
stockPrice.value = result.data.StockInformation.Price || '435.900' |
||||
|
|
||||
|
if (result.data.chartData) { |
||||
|
|
||||
|
chartData.value = { |
||||
|
...JSON.parse(JSON.stringify(result.data.chartData)) |
||||
|
} |
||||
|
chartKey.value++; |
||||
|
console.log('chartData', chartData.value); |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
// 1. K线图配置 |
||||
|
const opts = ref({ |
||||
|
rotate: false, |
||||
|
rotateLock: false, |
||||
|
color: ["#1890FF", "#91CB74", "#FAC858", "#EE6666", "#73C0DE", "#3CA272", "#FC8452", "#9A60B4", "#ea7ccc"], |
||||
|
padding: [15, 15, 0, 15], |
||||
|
dataLabel: false, |
||||
|
enableScroll: true, |
||||
|
enableMarkLine: false, |
||||
|
legend: {}, |
||||
|
xAxis: { |
||||
|
labelCount: 4, |
||||
|
itemCount: 30, |
||||
|
disableGrid: true, |
||||
|
gridColor: "#CCCCCC", |
||||
|
gridType: "solid", |
||||
|
dashLength: 4, |
||||
|
scrollShow: false, |
||||
|
scrollAlign: "left", |
||||
|
scrollColor: "#A6A6A6", |
||||
|
scrollBackgroundColor: "#EFEBEF", |
||||
|
labelColor: "#8C8C8C", |
||||
|
fontSize: 9 |
||||
|
}, |
||||
|
yAxis: { |
||||
|
labelColor: "#8C8C8C", |
||||
|
fontSize: 9 |
||||
|
}, |
||||
|
extra: { |
||||
|
candle: { |
||||
|
color: { |
||||
|
upLine: "#f04864", |
||||
|
upFill: "#f04864", |
||||
|
downLine: "#2fc25b", |
||||
|
downFill: "#2fc25b" |
||||
|
}, |
||||
|
average: { |
||||
|
show: false, |
||||
|
name: ["MA5", "MA10", "MA30"], |
||||
|
day: [5, 10, 20], |
||||
|
color: ["#1890ff", "#2fc25b", "#facc14"] |
||||
|
} |
||||
|
}, |
||||
|
markLine: { |
||||
|
type: "dash", |
||||
|
dashLength: 5, |
||||
|
data: [{ |
||||
|
value: 2150, |
||||
|
lineColor: "#f04864", |
||||
|
showLabel: false |
||||
|
}, |
||||
|
{ |
||||
|
value: 2350, |
||||
|
lineColor: "#f04864", |
||||
|
showLabel: false |
||||
|
} |
||||
|
] |
||||
|
}, |
||||
|
tooltip: { |
||||
|
showCategory: true |
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 2. K线图数据(响应式定义) |
||||
|
const chartData = ref({ |
||||
|
categories: [], |
||||
|
series: [{ |
||||
|
name: '', |
||||
|
data: [] |
||||
|
}] |
||||
|
}) |
||||
|
|
||||
|
//获取K线数据函数(直接定义,无需methods) |
||||
|
// const getServerData1 = () => { |
||||
|
// // 模拟服务器请求延时 |
||||
|
// setTimeout(() => { |
||||
|
// const res = { |
||||
|
// "categories": [ |
||||
|
// "2025/10/23", |
||||
|
// "2025/10/24", |
||||
|
// "2025/10/27" |
||||
|
// ], |
||||
|
// series: [{ |
||||
|
// "name": "贵州茅台", |
||||
|
// "data": [ |
||||
|
// [ |
||||
|
// 1455.0, |
||||
|
// 1468.8, |
||||
|
// 1447.2, |
||||
|
// 1467.98 |
||||
|
// ], |
||||
|
// [ |
||||
|
// 1467.95, |
||||
|
// 1478.88, |
||||
|
// 1449.34, |
||||
|
// 1450.0 |
||||
|
// ], |
||||
|
// [ |
||||
|
// 1440.0, |
||||
|
// 1452.49, |
||||
|
// 1435.99, |
||||
|
// 1440.41 |
||||
|
// ] |
||||
|
// ], |
||||
|
// }] |
||||
|
// } |
||||
|
// // 给响应式变量赋值(需修改.value) |
||||
|
// chartData.value = JSON.parse(JSON.stringify(res)) |
||||
|
// }, 500) |
||||
|
// } |
||||
|
|
||||
|
|
||||
|
|
||||
|
let unwatch = null; |
||||
|
// 生命周期钩子:组件挂载后执行(替代onReady) |
||||
|
onMounted(async () => { |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight |
||||
|
await getServerData() // 调用数据获取函数 |
||||
|
await handleModels() |
||||
|
|
||||
|
unwatch = watch( |
||||
|
() => deepExplorationStore.deepExplorationInfo, // 监听的目标值(函数返回,避免响应式丢失) |
||||
|
(newVal, oldVal) => { |
||||
|
console.log('deepExplorationInfo 变化了:', newVal) |
||||
|
historyData.value = { |
||||
|
...newVal |
||||
|
} |
||||
|
console.log(historyData.value.wokeFlowData); |
||||
|
console.log('222', historyData.value.stockData.StockInformation); |
||||
|
|
||||
|
//工作流数据 |
||||
|
const rawMarkdown = historyData.value.wokeFlowData.One.markdown; |
||||
|
const adaptedMarkdown = rawMarkdown.replace(/^### /gm, ''); // 全局替换行首的### |
||||
|
markdownContent.value = adaptedMarkdown; |
||||
|
// markdownContent.value = res.data.markdown |
||||
|
htmlContent.value = marked.parse(markdownContent.value); |
||||
|
|
||||
|
//k线 |
||||
|
chartData.value = { |
||||
|
...JSON.parse(JSON.stringify(historyData.value.stockData.chartData)) |
||||
|
} |
||||
|
chartKey.value++; |
||||
|
console.log('chartData', chartData.value); |
||||
|
|
||||
|
stockName.value = historyData.value.stockData.StockInformation.Name || 'Tesla Inc.' |
||||
|
stockCode.value = historyData.value.stockData.StockInformation.Code || 'TSLA' |
||||
|
stockTime.value = historyData.value.stockData.StockInformation.Time || '2025/10/29' |
||||
|
stockChange.value = historyData.value.stockData.StockInformation.Zhang || '5.120%' |
||||
|
stockAdd.value = historyData.value.stockData.StockInformation.ZhangFu || '22.410' |
||||
|
stockPrice.value = historyData.value.stockData.StockInformation.Price || '435.900' |
||||
|
}, { |
||||
|
deep: true, |
||||
|
immediate: true |
||||
|
} // 开启深度监听(对象内部属性变化也能触发) |
||||
|
) |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
// 页面加载时执行 |
||||
|
onLoad((e) => { |
||||
|
if (e.index) { |
||||
|
currentIndex.value = e.index - 1 |
||||
|
console.log('模块:', currentIndex.value) |
||||
|
} |
||||
|
if (e.stockName) { |
||||
|
searchName.value = e.stockName |
||||
|
console.log('股票名称:', searchName.value) |
||||
|
} |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.main { |
||||
|
width: 100%; |
||||
|
min-height: 100vh; |
||||
|
background-color: #fff; |
||||
|
padding-bottom: 120rpx; |
||||
|
|
||||
|
.search { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
background-color: #F3F3F3; |
||||
|
width: calc(100% - 60rpx); |
||||
|
height: 80rpx; |
||||
|
border-radius: 50rpx; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
padding: 0 40rpx; |
||||
|
margin: 15rpx 30rpx 0 30rpx; |
||||
|
|
||||
|
.seachIcon { |
||||
|
position: absolute; |
||||
|
right: 50rpx; |
||||
|
width: 32rpx; |
||||
|
height: 32rpx; |
||||
|
} |
||||
|
|
||||
|
.searchInput { |
||||
|
color: #111; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
margin-top: 30rpx; |
||||
|
padding-top: 30rpx; |
||||
|
background-color: rgb(248, 248, 248); |
||||
|
|
||||
|
.select { |
||||
|
position: relative; |
||||
|
margin-bottom: -5rpx; |
||||
|
|
||||
|
.img { |
||||
|
width: 750rpx; |
||||
|
height: 198rpx; |
||||
|
} |
||||
|
|
||||
|
.selectItem { |
||||
|
.btn { |
||||
|
position: absolute; |
||||
|
width: 120rpx; |
||||
|
height: 150rpx; |
||||
|
background-color: transparent; |
||||
|
|
||||
|
&::after { |
||||
|
border: none; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&:nth-of-type(1) .btn { |
||||
|
top: 30rpx; |
||||
|
left: 60rpx; |
||||
|
} |
||||
|
|
||||
|
&:nth-of-type(2) .btn { |
||||
|
top: 30rpx; |
||||
|
left: 230rpx; |
||||
|
} |
||||
|
|
||||
|
&:nth-of-type(3) .btn { |
||||
|
top: 30rpx; |
||||
|
left: 400rpx; |
||||
|
} |
||||
|
|
||||
|
&:nth-of-type(4) .btn { |
||||
|
top: 30rpx; |
||||
|
left: 570rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.graphAndTxt { |
||||
|
background-color: #fff; |
||||
|
border-radius: 50rpx 50rpx 0 0; |
||||
|
padding: 68.6rpx 36.5rpx 0 36.5rpx; |
||||
|
|
||||
|
.graph { |
||||
|
border: 1rpx solid #e2e2e2; |
||||
|
border-radius: 30rpx 30rpx 0 0; |
||||
|
|
||||
|
.graph_header { |
||||
|
padding: 32rpx 20.5rpx 0 24rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
.left { |
||||
|
color: #333333; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 15px; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 15px; |
||||
|
} |
||||
|
|
||||
|
.center { |
||||
|
margin-left: 155rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
|
||||
|
text { |
||||
|
width: 160rpx; |
||||
|
height: 36rpx; |
||||
|
padding-left: 10rpx; |
||||
|
color: #000000; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
text-align: center; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 18px; |
||||
|
font-style: normal; |
||||
|
font-weight: 500; |
||||
|
line-height: 18px; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
} |
||||
|
|
||||
|
.right { |
||||
|
margin-left: 60rpx; |
||||
|
color: #6a6a6a; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 13px; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 15px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.graph_data { |
||||
|
display: flex; |
||||
|
padding: 48rpx 24rpx; |
||||
|
|
||||
|
text { |
||||
|
display: flex; |
||||
|
color: #25ba5d; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 17px; |
||||
|
line-height: 17px; |
||||
|
} |
||||
|
|
||||
|
text:nth-child(2) { |
||||
|
margin-left: 120rpx; |
||||
|
} |
||||
|
|
||||
|
text:nth-child(3) { |
||||
|
margin-left: 150rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.graph_content { |
||||
|
min-height: 500rpx; |
||||
|
|
||||
|
.charts-box { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.txt { |
||||
|
background-color: #F3F3F3; |
||||
|
margin-top: 48rpx; |
||||
|
border-radius: 30rpx; |
||||
|
|
||||
|
.txtHeader { |
||||
|
padding: 30rpx 24rpx; |
||||
|
|
||||
|
image { |
||||
|
width: 20rpx; |
||||
|
height: 20rpx; |
||||
|
} |
||||
|
|
||||
|
text { |
||||
|
background-color: #FFFFFF; |
||||
|
color: #000000; |
||||
|
padding: 0 22rpx; |
||||
|
border-radius: 22rpx; |
||||
|
font-size: 28rpx; |
||||
|
font-weight: 400; |
||||
|
line-height: 37rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.txtContent { |
||||
|
min-height: 200rpx; |
||||
|
padding: 20rpx 30rpx; |
||||
|
margin-bottom: 100rpx; |
||||
|
|
||||
|
::v-deep * { |
||||
|
box-sizing: border-box; |
||||
|
width: 100% !important; // 强制所有解析后的标签占满容器宽度 |
||||
|
white-space: normal !important; // 取消强制不换行 |
||||
|
word-wrap: break-word !important; // 长词/长数字自动换行 |
||||
|
} |
||||
|
|
||||
|
// 标题样式(确保换行) |
||||
|
::v-deep h2 { |
||||
|
font-size: 32rpx; |
||||
|
color: #333; |
||||
|
margin: 25rpx 0 15rpx; |
||||
|
line-height: 1.5; |
||||
|
} |
||||
|
|
||||
|
// 段落样式(核心换行控制) |
||||
|
::v-deep p { |
||||
|
font-size: 26rpx; |
||||
|
color: #666; |
||||
|
margin: 15rpx 0; |
||||
|
line-height: 1.8; // 增加行高,提升可读性 |
||||
|
text-align: justify; // 两端对齐,避免单侧参差不齐 |
||||
|
} |
||||
|
|
||||
|
// 列表样式(纵向排列) |
||||
|
::v-deep ul, |
||||
|
::v-deep ol { |
||||
|
margin: 15rpx 0 15rpx 30rpx; |
||||
|
} |
||||
|
|
||||
|
::v-deep li { |
||||
|
margin: 10rpx 0; |
||||
|
line-height: 1.6; |
||||
|
} |
||||
|
|
||||
|
// 加载状态样式 |
||||
|
.loading { |
||||
|
text-align: center; |
||||
|
padding: 50rpx 0; |
||||
|
color: #666; |
||||
|
font-size: 26rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.static-footer { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
width: 100%; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
* { |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,532 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<!-- 顶部状态栏占位 --> |
||||
|
<view class="top" :style="{ height: iSMT + 'px' }"></view> |
||||
|
<!-- 标题图标部分 --> |
||||
|
<deepExploration_header |
||||
|
></deepExploration_header> |
||||
|
<view class="search"> |
||||
|
<input |
||||
|
v-model="stockName" |
||||
|
class="searchInput" |
||||
|
type="text" |
||||
|
placeholder="请输入股票名称、股票代码" |
||||
|
placeholder-style="color: #A6A6A6; font-size: 22rpx;" |
||||
|
/> |
||||
|
<image |
||||
|
@click="searchStock" |
||||
|
class="seachIcon" |
||||
|
src="/static/deepExploration-images/search.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 四大功能模块 --> |
||||
|
<view class="select"> |
||||
|
<view class="selectItem" @click="toMain('主力追踪')"> |
||||
|
<image |
||||
|
class="img" |
||||
|
src="/static/deepExploration-images/icon3.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
<view class="txt">主力追踪</view> |
||||
|
</view> |
||||
|
<view class="selectItem" @click="toMain('主力雷达')"> |
||||
|
<image |
||||
|
class="img" |
||||
|
src="/static/deepExploration-images/icon2.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
<view class="txt">主力雷达</view> |
||||
|
</view> |
||||
|
<view class="selectItem" @click="toMain('主力解码')"> |
||||
|
<image |
||||
|
class="img" |
||||
|
src="/static/deepExploration-images/icon1.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
<view class="txt">主力解码</view> |
||||
|
</view> |
||||
|
<view class="selectItem" @click="toMain('主力资金流')"> |
||||
|
<image |
||||
|
class="img" |
||||
|
src="/static/deepExploration-images/icon4.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
<view class="txt">主力资金流</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 灰色间隔 --> |
||||
|
<view class="gap"></view> |
||||
|
<!-- 选股策略 --> |
||||
|
<view class="stockSelection"> |
||||
|
<view class="stockSelection_top"> |
||||
|
<view class="txt"> |
||||
|
<text>选股策略</text> |
||||
|
</view> |
||||
|
<view class="viewAll" @click="viewAll"> |
||||
|
<text>查看全部</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="stockSelection_content"> |
||||
|
<view class="selectionItem"> |
||||
|
<view class="header"> |
||||
|
<view class="left"> |
||||
|
<image |
||||
|
src="/static/deepExploration-images/plus.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
<text>抄底卖顶</text> |
||||
|
</view> |
||||
|
<view class="right"> |
||||
|
<image |
||||
|
src="/static/deepExploration-images/Americle.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
<text>美股</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="content"> |
||||
|
<view class="contentTitle"> |
||||
|
<view class="contentTitle_name">股票名称</view> |
||||
|
<view class="contentTitle_close">最新收盘价</view> |
||||
|
<view class="contentTitle_price">选股价格</view> |
||||
|
</view> |
||||
|
<view class="contentItem"> |
||||
|
<view class="row" v-for="(item, index) in stockData" :key="index"> |
||||
|
<view class="nameItem">{{ item.tscode }}</view> |
||||
|
<view class="closeItem">{{ item.close }}</view> |
||||
|
<view class="priceItem">{{ item.preClose }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="stockSelection_content"> |
||||
|
<view class="selectionItem"> |
||||
|
<view class="header"> |
||||
|
<view class="left"> |
||||
|
<image |
||||
|
src="/static/deepExploration-images/plus.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
<text>波段行情</text> |
||||
|
</view> |
||||
|
<view class="right"> |
||||
|
<image |
||||
|
src="/static/deepExploration-images/Americle.png" |
||||
|
mode="aspectFill" |
||||
|
></image> |
||||
|
<text>美股</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="content"> |
||||
|
<view class="contentTitle"> |
||||
|
<view class="contentTitle_name">股票名称</view> |
||||
|
<view class="contentTitle_close">最新收盘价</view> |
||||
|
<view class="contentTitle_price">选股价格</view> |
||||
|
</view> |
||||
|
<view class="contentItem"> |
||||
|
<view class="row" v-for="(item, index) in stockDataByName" :key="index"> |
||||
|
<view class="nameItem">{{ item.tscode }}</view> |
||||
|
<view class="closeItem">{{ item.close }}</view> |
||||
|
<view class="priceItem">{{ item.preClose }}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<footerBar class="static-footer" :type="type"></footerBar> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import footerBar from '@/components/footerBar.vue' |
||||
|
import deepExploration_header from '@/components/deepExploration_header.vue' |
||||
|
import { stocSelectApi, stocSelectByNameApi } from '@/api/deepExploration/deepExploration.js' |
||||
|
|
||||
|
const type = ref("deepExploration"); |
||||
|
const iSMT = ref(0); |
||||
|
|
||||
|
//查看全部选股策略 |
||||
|
const toMain = (val) => { |
||||
|
if (val == "主力追踪") { |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/deepExploration/MainForceActions?index=1", |
||||
|
}); |
||||
|
} else if (val == "主力雷达") { |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/deepExploration/MainForceActions?index=2", |
||||
|
}); |
||||
|
} else if (val == "主力解码") { |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/deepExploration/MainForceActions?index=3", |
||||
|
}); |
||||
|
} else if (val == "主力资金流") { |
||||
|
uni.navigateTo({ |
||||
|
url: "/pages/deepExploration/MainForceActions?index=4", |
||||
|
}); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const stockName = ref(""); |
||||
|
//搜索股票 |
||||
|
const searchStock = () => { |
||||
|
console.log("搜索参数:", stockName.value); |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/deepExploration/MainForceActions?stockName=${stockName.value}`, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
//查看全部选股策略 |
||||
|
const viewAll = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/deepExploration/stockSelectDetail' |
||||
|
}) |
||||
|
} |
||||
|
//选股策略数据(接口填充) |
||||
|
const stockData = ref([]); |
||||
|
const stockDataByName = ref([]); // 安徽板块数据(只显示前三条) |
||||
|
|
||||
|
// 加载选股策略(接口) |
||||
|
const loadStockSelection = async () => { |
||||
|
try { |
||||
|
const res = await stocSelectApi({ language: 'cn', size: 3 }) |
||||
|
// console.log('选股策略接口响应原始:', typeof res === 'object' ? JSON.stringify(res) : res) |
||||
|
const raw = res?.data |
||||
|
const listCandidates = [ |
||||
|
raw?.list, |
||||
|
raw?.data?.list, |
||||
|
raw?.data?.rows, |
||||
|
raw?.rows, |
||||
|
Array.isArray(raw) ? raw : null |
||||
|
].filter(Array.isArray) |
||||
|
let list = listCandidates.length ? listCandidates[0] : [] |
||||
|
|
||||
|
// 若是对象包含多个数组(如不同市场),进行扁平化 |
||||
|
if ((!Array.isArray(list) || !list.length) && raw && typeof raw === 'object' && !Array.isArray(raw)) { |
||||
|
const arrays = Object.values(raw).filter(Array.isArray) |
||||
|
if (arrays.length) { |
||||
|
list = arrays.flat() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Array.isArray(list) && list.length) { |
||||
|
const mapped = list.map(item => ({ |
||||
|
tscode: item.tsCode ?? item.tscode ?? item.code ?? '', |
||||
|
close: item.close ?? item.lastClose ?? '', |
||||
|
preClose: item.preClose ?? item.preclose ?? item.prevClose ?? '' |
||||
|
})) |
||||
|
stockData.value = mapped.slice(0, 3) |
||||
|
console.log('选股策略列表长度:', stockData.value.length, '首项:', stockData.value[0]) |
||||
|
} else { |
||||
|
console.warn('选股策略接口返回空列表或结构不匹配', raw) |
||||
|
} |
||||
|
} catch (e) { |
||||
|
console.error('选股策略接口调用失败', e) |
||||
|
uni.showToast({ title: '选股策略加载失败', icon: 'none' }) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 安徽板块:按名称查询,仅前三条,字段映射不变 |
||||
|
const loadStockSelectionByName = async () => { |
||||
|
try { |
||||
|
const res = await stocSelectByNameApi({ name: '安徽' }) |
||||
|
const raw = res?.data |
||||
|
const dataObj = raw?.data || raw |
||||
|
let list = [] |
||||
|
if (Array.isArray(dataObj)) { |
||||
|
list = dataObj |
||||
|
} else if (dataObj && typeof dataObj === 'object') { |
||||
|
const target = dataObj['安徽'] |
||||
|
if (Array.isArray(target)) { |
||||
|
list = target |
||||
|
} else { |
||||
|
const firstArr = Object.values(dataObj).find(v => Array.isArray(v)) |
||||
|
if (Array.isArray(firstArr)) list = firstArr |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (Array.isArray(list) && list.length) { |
||||
|
const mapped = list.map(item => ({ |
||||
|
tscode: item.tsCode ?? item.tscode ?? item.code ?? '', |
||||
|
close: item.close ?? item.lastClose ?? '', |
||||
|
preClose: item.preClose ?? item.preclose ?? item.prevClose ?? '' |
||||
|
})) |
||||
|
stockDataByName.value = mapped.slice(0, 3) |
||||
|
console.log('安徽板块列表长度:', stockDataByName.value.length, '首项:', stockDataByName.value[0]) |
||||
|
} else { |
||||
|
console.warn('按名称(安徽)接口返回空列表或结构不匹配', raw) |
||||
|
} |
||||
|
} catch (e) { |
||||
|
console.error('按名称(安徽)接口调用失败', e) |
||||
|
uni.showToast({ title: '安徽板块加载失败', icon: 'none' }) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
// 调用接口填充数据 |
||||
|
loadStockSelection() |
||||
|
loadStockSelectionByName() // 安徽板块填充 |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.main { |
||||
|
width: 100%; |
||||
|
min-height: 100vh; // 保持至少一屏高度 |
||||
|
height: auto; // 允许内容超出时自动增高 |
||||
|
overflow-y: auto; // 开启纵向滚动 |
||||
|
background-color: #fff; |
||||
|
padding-bottom: 120rpx; // 为固定底栏预留空间,避免遮挡 |
||||
|
|
||||
|
.search { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
background-color: #f3f3f3; |
||||
|
width: calc(100% - 60rpx); |
||||
|
height: 80rpx; |
||||
|
border-radius: 50rpx; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
padding: 0 40rpx; |
||||
|
margin: 15rpx 30rpx 0 30rpx; |
||||
|
|
||||
|
.seachIcon { |
||||
|
position: absolute; |
||||
|
right: 50rpx; |
||||
|
width: 32rpx; |
||||
|
height: 32rpx; |
||||
|
} |
||||
|
|
||||
|
.searchInput { |
||||
|
color: #111; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.select { |
||||
|
display: flex; |
||||
|
padding: 60rpx 10rpx 30rpx 30rpx; |
||||
|
gap: 70rpx; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
|
||||
|
.selectItem { |
||||
|
.img { |
||||
|
width: 80rpx; |
||||
|
height: 80rpx; |
||||
|
display: block; |
||||
|
margin: 0 auto; |
||||
|
} |
||||
|
|
||||
|
.txt { |
||||
|
color: #6a6a6a; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 11px; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 14.5px; |
||||
|
margin-top: 13rpx; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.gap { |
||||
|
width: 100%; |
||||
|
height: 15rpx; |
||||
|
background-color: #f3f3f3; |
||||
|
} |
||||
|
|
||||
|
.stockSelection { |
||||
|
width: 100%; |
||||
|
padding: 32rpx 15rpx; |
||||
|
|
||||
|
.stockSelection_top { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
|
||||
|
.txt { |
||||
|
color: #000000; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 38rpx; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 50rpx; |
||||
|
} |
||||
|
|
||||
|
.viewAll { |
||||
|
background-color: #000000; |
||||
|
border-radius: 10rpx; |
||||
|
padding: 6rpx 20rpx; |
||||
|
color: #ffffff; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 10rpx; |
||||
|
font-style: normal; |
||||
|
font-weight: 100; |
||||
|
line-height: 29rpx; |
||||
|
height: 40rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.stockSelection_content { |
||||
|
.selectionItem { |
||||
|
background-color: #f3f3f3; |
||||
|
padding: 30rpx 15rpx 17rpx 30rpx; |
||||
|
border-radius: 30rpx; |
||||
|
margin-top: 30rpx; |
||||
|
|
||||
|
.header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
|
||||
|
.left { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
|
||||
|
image { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
width: 15rpx; |
||||
|
height: 15rpx; |
||||
|
} |
||||
|
|
||||
|
text { |
||||
|
margin-left: 15rpx; |
||||
|
color: #000000; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 28rpx; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 18.5px; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.right { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
border-radius: 15rpx; |
||||
|
background-color: #ffffff; |
||||
|
padding: 6rpx 20rpx; |
||||
|
|
||||
|
image { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
width: 40rpx; |
||||
|
height: 26.5rpx; |
||||
|
} |
||||
|
|
||||
|
text { |
||||
|
margin-left: 10rpx; |
||||
|
color: #6a6a6a; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 18rpx; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 24rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
.contentTitle { |
||||
|
display: flex; |
||||
|
color: #6a6a6a; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 11px; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 14.5px; |
||||
|
margin-top: 24rpx; |
||||
|
margin-bottom: 20rpx; |
||||
|
|
||||
|
.contentTitle_name { |
||||
|
width: 100rpx; |
||||
|
} |
||||
|
|
||||
|
.contentTitle_close { |
||||
|
width: 130rpx; |
||||
|
margin-left: 260rpx; |
||||
|
} |
||||
|
|
||||
|
.contentTitle_price { |
||||
|
width: 120rpx; |
||||
|
margin-left: 60rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.contentItem { |
||||
|
.row { |
||||
|
display: flex; |
||||
|
box-shadow: 0 -2rpx 5rpx rgba(0, 0, 0, 0.05); |
||||
|
padding: 10rpx 0; |
||||
|
margin-bottom: 10rpx; |
||||
|
|
||||
|
.nameItem { |
||||
|
width: 260rpx; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
color: #000000; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 13px; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 17.5px; |
||||
|
} |
||||
|
|
||||
|
.closeItem { |
||||
|
width: 120rpx; |
||||
|
margin-left: 100rpx; |
||||
|
color: #25ba5d; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 13px; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 17.5px; |
||||
|
} |
||||
|
|
||||
|
.priceItem { |
||||
|
width: 120rpx; |
||||
|
margin-left: 73rpx; |
||||
|
color: #25ba5d; |
||||
|
font-family: "PingFang SC"; |
||||
|
font-size: 13px; |
||||
|
font-style: normal; |
||||
|
font-weight: 400; |
||||
|
line-height: 17.5px; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.static-footer { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
* { |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,388 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view class="table"> |
||||
|
<view class="tableHeader"> |
||||
|
<scroll-view class="tabs" scroll-x="true"> |
||||
|
<view v-for="(item,index) in tabsData" :key="index" :class="['tabItem', { 'tabItem-active': item === activeTab }]" @click="handleTab(item)"> |
||||
|
{{item}} |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
<view class="tableContent"> |
||||
|
<image class="showAll" src="/static/deepExploration-images/showAll.png" mode="aspectFill"></image> |
||||
|
<scroll-view scroll-x="true" show-scrollbar="false"> |
||||
|
<view class="tableBox"> |
||||
|
<view class="box_header"> |
||||
|
<view class="name">名称</view> |
||||
|
<view class="other" v-for="(item,index) in tableContentHeaderData" :key="index"> |
||||
|
<text>{{item}}</text> |
||||
|
<image v-show="ifASC" src="/static/deepExploration-images/ASC.png" mode="aspectFill"> |
||||
|
</image> |
||||
|
<image v-show="!ifASC" src="/static/deepExploration-images/DESC.png" mode="aspectFill"> |
||||
|
</image> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="box_content"> |
||||
|
<view class="row" v-for="(item,index) in strategyData" :key="index" :class="{ 'increase-positive': item.increase.startsWith('+'), |
||||
|
'increase-negative': item.increase.startsWith('-')}"> |
||||
|
<view class="name_colum"> |
||||
|
<text class="stockName">{{item.name}}</text> |
||||
|
<text class="stockCode">{{item.stockCode}}</text> |
||||
|
</view> |
||||
|
<view class="other_colum">{{item.latest}}</view> |
||||
|
<view class="other_colum">{{item.increase}}</view> |
||||
|
<view class="other_colum">{{item.decrease}}</view> |
||||
|
<view class="other_colum">{{item.previousClose}}</view> |
||||
|
<view class="other_colum">{{item.volume}}</view> |
||||
|
<view class="other_colum">{{item.turnover}}</view> |
||||
|
<view class="other_colum">{{item.openingPrice}}</view> |
||||
|
<view class="other_colum">{{item.highestPrice}}</view> |
||||
|
<view class="other_colum">{{item.lowestPrice}}</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted } from 'vue' |
||||
|
import { stocSelectApi, stocSelectByNameApi } from '@/api/deepExploration/deepExploration.js' |
||||
|
import { useUserStore } from '@/stores/modules/userInfo.js' |
||||
|
import { useDeviceStore } from '@/stores/modules/deviceInfo.js' |
||||
|
import { LoginApi } from '@/api/start/login.js' |
||||
|
|
||||
|
const tabsData = ref(['全部', '抄底卖顶', '波段行情', '价值投资', '资金及仓位管理', ]) |
||||
|
const activeTab = ref('全部') |
||||
|
// 点击标签:激活并按需触发名称查询 |
||||
|
const handleTab = async (item) => { |
||||
|
activeTab.value = item |
||||
|
const nameMap = { |
||||
|
'抄底卖顶': '北京', |
||||
|
'波段行情': '安徽', |
||||
|
'价值投资': '重庆', |
||||
|
'资金及仓位管理': '黑龙江' |
||||
|
} |
||||
|
if (item === '全部') { |
||||
|
await loadStrategy() |
||||
|
uni.showToast({ title: `查看 ${item} 详情`, icon: 'none', duration: 1500 }) |
||||
|
return |
||||
|
} |
||||
|
const apiName = nameMap[item] |
||||
|
if (apiName) { |
||||
|
await loadByName(apiName) |
||||
|
uni.showToast({ title: `${item}数据已更新`, icon: 'none', duration: 1500 }) |
||||
|
} else { |
||||
|
uni.showToast({ title: `暂不支持:${item}`, icon: 'none' }) |
||||
|
} |
||||
|
} |
||||
|
//表头是否升序 |
||||
|
const ifASC = ref(true) |
||||
|
//表头数据 |
||||
|
const tableContentHeaderData = ref(['最新', '涨幅', '涨跌', '昨收', '成交量', '成交额', '开盘价', '最高价', '最低价']) |
||||
|
|
||||
|
// 渲染用数据 |
||||
|
const strategyData = ref([]) |
||||
|
|
||||
|
// 游客登录兜底,确保移动端有token |
||||
|
const ensureAuth = async () => { |
||||
|
const userStore = useUserStore() |
||||
|
if (userStore.userInfo?.token) return |
||||
|
try { |
||||
|
const deviceStore = useDeviceStore() |
||||
|
let deviceId = deviceStore.deviceInfo?.deviceId |
||||
|
if (!deviceId) { |
||||
|
const cached = uni.getStorageSync('deviceId') |
||||
|
deviceId = cached || `${Date.now()}-${Math.random().toString(36).slice(2, 10)}` |
||||
|
uni.setStorageSync('deviceId', deviceId) |
||||
|
deviceStore.setDeviceInfo({ ...(deviceStore.deviceInfo || {}), deviceId }) |
||||
|
} |
||||
|
const res = await LoginApi({ |
||||
|
loginType: 'VISITOR', |
||||
|
account: deviceId, |
||||
|
useCode: false |
||||
|
}) |
||||
|
if (res?.code === 200 && res?.data?.token) { |
||||
|
userStore.setUserInfo(res.data) |
||||
|
console.log('游客登录成功,token=', res.data.token) |
||||
|
} else { |
||||
|
console.warn('游客登录失败', res) |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.warn('游客登录异常', err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const formatPctChg = (val) => { |
||||
|
if (val === null || val === undefined || val === '') return '' |
||||
|
const num = Number(val) |
||||
|
if (!isFinite(num)) return String(val) |
||||
|
const sign = num > 0 ? '+' : '' |
||||
|
return `${sign}${num.toFixed(2)}%` |
||||
|
} |
||||
|
// 默认根据涨幅( pctChg )由大到小排序 |
||||
|
const sortByPctDesc = (arr) => arr.sort((a, b) => (Number(b.pctChg) || -Infinity) - (Number(a.pctChg) || -Infinity)) |
||||
|
|
||||
|
// 按名称加载(抄底卖顶/波段行情/价值投资/资金及仓位管理) |
||||
|
const loadByName = async (apiName) => { |
||||
|
try { |
||||
|
const userStore = useUserStore() |
||||
|
if (!userStore.userInfo?.token) { |
||||
|
await ensureAuth() |
||||
|
} |
||||
|
const token = useUserStore().userInfo?.token |
||||
|
const res = await stocSelectByNameApi({ name: apiName, token }) |
||||
|
const raw = res?.data |
||||
|
const dataObj = raw?.data || raw |
||||
|
let list = [] |
||||
|
if (dataObj && typeof dataObj === 'object' && !Array.isArray(dataObj)) { |
||||
|
const target = dataObj[apiName] |
||||
|
if (Array.isArray(target)) list = target |
||||
|
else { |
||||
|
const firstArr = Object.values(dataObj).find(v => Array.isArray(v)) |
||||
|
if (Array.isArray(firstArr)) list = firstArr |
||||
|
} |
||||
|
} |
||||
|
if ((!Array.isArray(list) || !list.length) && Array.isArray(raw)) { |
||||
|
list = raw |
||||
|
} |
||||
|
// 排序:涨幅由大到小 |
||||
|
if (Array.isArray(list)) list = sortByPctDesc(list) |
||||
|
if (Array.isArray(list) && list.length) { |
||||
|
strategyData.value = list.map(item => ({ |
||||
|
name: item.tsCode ?? item.tscode ?? '', |
||||
|
stockCode: item.tsCode ?? item.tscode ?? '', |
||||
|
latest: item.close ?? '', |
||||
|
increase: formatPctChg(item.pctChg), |
||||
|
decrease: item.change ?? '', |
||||
|
previousClose: item.preClose ?? item.preclose ?? '', |
||||
|
volume: item.vol ?? '', |
||||
|
turnover: item.amount ?? '', |
||||
|
openingPrice: item.open ?? '', |
||||
|
highestPrice: item.high ?? '', |
||||
|
lowestPrice: item.low ?? '' |
||||
|
})) |
||||
|
console.log(`按名称(${apiName})加载成功,条数:`, strategyData.value.length, '首项:', strategyData.value[0]) |
||||
|
} else { |
||||
|
console.warn('getStrategyByName 返回空列表或结构不匹配', raw) |
||||
|
} |
||||
|
} catch (e) { |
||||
|
console.error('getStrategyByName 接口调用失败', e) |
||||
|
uni.showToast({ title: '按名称加载失败', icon: 'none' }) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const loadStrategy = async () => { |
||||
|
try { |
||||
|
const userStore = useUserStore() |
||||
|
if (!userStore.userInfo?.token) { |
||||
|
await ensureAuth() |
||||
|
} |
||||
|
const token = useUserStore().userInfo?.token |
||||
|
const res = await stocSelectApi({ language: 'cn', token }) |
||||
|
const raw = res?.data |
||||
|
const listCandidates = [ |
||||
|
raw?.list, |
||||
|
raw?.data?.list, |
||||
|
raw?.data?.rows, |
||||
|
raw?.rows, |
||||
|
Array.isArray(raw) ? raw : null |
||||
|
].filter(Array.isArray) |
||||
|
let list = listCandidates.length ? listCandidates[0] : [] |
||||
|
if ((!Array.isArray(list) || !list.length) && raw && typeof raw === 'object' && !Array.isArray(raw)) { |
||||
|
const arrays = Object.values(raw).filter(Array.isArray) |
||||
|
if (arrays.length) list = arrays.flat() |
||||
|
} |
||||
|
// 排序:涨幅由大到小 |
||||
|
if (Array.isArray(list)) list = sortByPctDesc(list) |
||||
|
if (Array.isArray(list) && list.length) { |
||||
|
strategyData.value = list.map(item => ({ |
||||
|
name: item.tsCode ?? item.tscode ?? '', |
||||
|
stockCode: item.tsCode ?? item.tscode ?? '', |
||||
|
latest: item.close ?? '', |
||||
|
increase: formatPctChg(item.pctChg), |
||||
|
decrease: item.change ?? '', |
||||
|
previousClose: item.preClose ?? item.preclose ?? '', |
||||
|
volume: item.vol ?? '', |
||||
|
turnover: item.amount ?? '', |
||||
|
openingPrice: item.open ?? '', |
||||
|
highestPrice: item.high ?? '', |
||||
|
lowestPrice: item.low ?? '' |
||||
|
})) |
||||
|
console.log('stockSelectDetail 加载成功(已按涨幅降序),条数:', strategyData.value.length, '首项:', strategyData.value[0]) |
||||
|
} else { |
||||
|
console.warn('stockSelectDetail 接口返回空列表或结构不匹配', raw) |
||||
|
} |
||||
|
} catch (e) { |
||||
|
console.error('stockSelectDetail 接口调用失败', e) |
||||
|
uni.showToast({ title: '选股策略详情加载失败', icon: 'none' }) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
loadStrategy() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped lang="scss"> |
||||
|
.main { |
||||
|
width: 100%; |
||||
|
height: 100vh; |
||||
|
background-color: #fff; |
||||
|
|
||||
|
|
||||
|
.table { |
||||
|
margin-top: 10rpx; |
||||
|
box-shadow: 0 -2rpx 3rpx -1rpx rgba(0, 0, 0, 0.5); |
||||
|
|
||||
|
.tableHeader { |
||||
|
.tabs { |
||||
|
white-space: nowrap; |
||||
|
padding-top: 20rpx; |
||||
|
padding-left: 40rpx; |
||||
|
|
||||
|
::-webkit-scrollbar { |
||||
|
//隐藏 滚动条 |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
.tabItem { |
||||
|
display: inline-block; |
||||
|
color: rgb(175, 175, 175); |
||||
|
border-radius: 10rpx; |
||||
|
padding: 5rpx 30rpx; |
||||
|
margin-right: 20rpx; |
||||
|
font-size: 28rpx; |
||||
|
background-color: rgb(243, 243, 243); |
||||
|
} |
||||
|
|
||||
|
.tabItem-active { |
||||
|
background-color: #DB1F1D; // 红色 |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.tableContent { |
||||
|
width: 100%; |
||||
|
background-color: #fff; |
||||
|
position: relative; |
||||
|
|
||||
|
.showAll { |
||||
|
position: absolute; |
||||
|
top: 35rpx; |
||||
|
right: 20rpx; |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
z-index: 100; |
||||
|
} |
||||
|
|
||||
|
scroll-view { |
||||
|
width: 100%; |
||||
|
white-space: nowrap; |
||||
|
|
||||
|
::-webkit-scrollbar { |
||||
|
//隐藏 滚动条 |
||||
|
display: none; |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
.tableBox { |
||||
|
padding-left: 40rpx; |
||||
|
|
||||
|
.box_header { |
||||
|
margin-bottom: 19rpx; |
||||
|
display: flex; |
||||
|
width: max-content; |
||||
|
margin-top: 40rpx; |
||||
|
color: rgb(109, 109, 109); |
||||
|
border-radius: 10rpx; |
||||
|
margin-right: 20rpx; |
||||
|
font-size: 23rpx; |
||||
|
|
||||
|
|
||||
|
.name { |
||||
|
flex: 0 0 375rpx; |
||||
|
} |
||||
|
|
||||
|
.other { |
||||
|
flex: 0 0 195rpx; |
||||
|
|
||||
|
text { |
||||
|
margin-right: 5rpx; |
||||
|
} |
||||
|
|
||||
|
image { |
||||
|
width: 20rpx; |
||||
|
height: 20rpx; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.box_content { |
||||
|
width: max-content; |
||||
|
|
||||
|
.row { |
||||
|
padding: 5rpx; |
||||
|
display: flex; |
||||
|
border-top: 1rpx dashed #eee; |
||||
|
width: 210%; |
||||
|
|
||||
|
&.increase-positive { |
||||
|
.other_colum { |
||||
|
color: #2DD357; |
||||
|
font-weight: 200; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
&.increase-negative { |
||||
|
.other_colum { |
||||
|
color: #FF4150; |
||||
|
font-weight: 200; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.name_colum { |
||||
|
flex: 0 0 375rpx; |
||||
|
|
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
gap: 4rpx; |
||||
|
|
||||
|
.stockName { |
||||
|
color: #333333; |
||||
|
width: 100%; |
||||
|
max-width: 305rpx; |
||||
|
font-size: 28rpx; |
||||
|
font-weight: 400; |
||||
|
line-height: 36rpx; |
||||
|
white-space: nowrap; |
||||
|
overflow: hidden; |
||||
|
text-overflow: ellipsis; |
||||
|
} |
||||
|
|
||||
|
.stockCode { |
||||
|
color: #c5c5c5; |
||||
|
; |
||||
|
font-size: 24rpx; |
||||
|
font-weight: 400; |
||||
|
line-height: 30rpx; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.other_colum { |
||||
|
flex: 0 0 195rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
1775
pages/deepMate/deepMate.vue
File diff suppressed because it is too large
View File
@ -1,28 +0,0 @@ |
|||||
<template> |
|
||||
<view class="main"> |
|
||||
<!-- 顶部状态栏占位 --> |
|
||||
<view class="top" :style="{height:iSMT+'px'}"></view> |
|
||||
<view>深度探索</view> |
|
||||
<footerBar class="static-footer" :type="type"></footerBar> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { ref,onMounted } from 'vue' |
|
||||
import footerBar from '../../components/footerBar.vue' |
|
||||
|
|
||||
const type = ref('deepExploration') |
|
||||
const iSMT = ref(0) |
|
||||
|
|
||||
onMounted(() => { |
|
||||
// 状态栏高度 |
|
||||
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
|
||||
}) |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
.static-footer { |
|
||||
position: fixed; |
|
||||
bottom: 0; |
|
||||
} |
|
||||
</style> |
|
||||
2139
pages/home/home.vue
File diff suppressed because it is too large
View File
@ -1,28 +0,0 @@ |
|||||
<template> |
|
||||
<view class="main"> |
|
||||
<!-- 顶部状态栏占位 --> |
|
||||
<view class="top" :style="{height:iSMT+'px'}"></view> |
|
||||
<view>行情</view> |
|
||||
<footerBar class="static-footer" :type="type"></footerBar> |
|
||||
</view> |
|
||||
</template> |
|
||||
|
|
||||
<script setup> |
|
||||
import { ref,onMounted } from 'vue' |
|
||||
import footerBar from '../../components/footerBar.vue' |
|
||||
|
|
||||
const type = ref('marketSituation') |
|
||||
const iSMT = ref(0) |
|
||||
|
|
||||
onMounted(() => { |
|
||||
// 状态栏高度 |
|
||||
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
|
||||
}) |
|
||||
</script> |
|
||||
|
|
||||
<style scoped> |
|
||||
.static-footer { |
|
||||
position: fixed; |
|
||||
bottom: 0; |
|
||||
} |
|
||||
</style> |
|
||||
@ -1,28 +1,306 @@ |
|||||
<template> |
<template> |
||||
<view class="main"> |
|
||||
<!-- 顶部状态栏占位 --> |
|
||||
<view class="top" :style="{height:iSMT+'px'}"></view> |
|
||||
<view>我的</view> |
|
||||
<footerBar class="static-footer" :type="type"></footerBar> |
|
||||
</view> |
|
||||
|
<LoginPrompt ref="loginPrompt"></LoginPrompt> |
||||
|
<view class="main"> |
||||
|
<view class="top"> |
||||
|
<view class="bell"> |
||||
|
<image class="image-bell" src="/static/my/bell.png"></image> |
||||
|
</view> |
||||
|
<view class="msg"> |
||||
|
<view class="msg-left"> |
||||
|
<view class="avatar"></view> |
||||
|
</view> |
||||
|
<view class="msg-center"> |
||||
|
<view style="display: flex;"> |
||||
|
<view class="userInfo">{{ username }}</view> |
||||
|
<image class="image-editName" src="/static/my/editName.png"></image> |
||||
|
</view> |
||||
|
<view class="userId">ID:{{ dccode }}</view> |
||||
|
</view> |
||||
|
<view class="msg-right"> |
||||
|
<image class="image-attendance" src="/static/my/Check-in.png"/> |
||||
|
<span style="font-size:10px;">签到</span> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="settings-buttons"> |
||||
|
<view class="setting-btn" @click="goToMarket"> |
||||
|
<image src="/static/my/MarketSettings.png" class="setting-icon"/> |
||||
|
<text>行情设置</text> |
||||
|
</view> |
||||
|
<view class="setting-btn" @click="goToGeneral"> |
||||
|
<image src="/static/my/Settings.png" class="setting-icon"/> |
||||
|
<text>通用设置</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="share" @click="goToShare"> |
||||
|
<image class="img-share" src="/static/my/share.png" mode="widthFix"/> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="bottom"> |
||||
|
<view class="list-item" @click="goToAccount"> |
||||
|
<image src="/static/my/security.png" class="list-icon"/> |
||||
|
<text>账号与安全</text> |
||||
|
<uni-icons type="arrowright" size="16" class="arrow"/> |
||||
|
</view> |
||||
|
<view class="list-item"> |
||||
|
<image src="/static/my/connection.png" class="list-icon"/> |
||||
|
<text>联系我们</text> |
||||
|
<uni-icons type="arrowright" size="16" class="arrow"/> |
||||
|
</view> |
||||
|
<view class="list-item" @click="goToNewVersion"> |
||||
|
<image src="/static/my/update.png" class="list-icon"/> |
||||
|
<text>新版本更新</text> |
||||
|
<view class="update-tip">有新版本可更新 |
||||
|
<view class="circle"></view> |
||||
|
</view> |
||||
|
<uni-icons type="arrowright" size="16" class="arrow"/> |
||||
|
</view> |
||||
|
<view class="list-item"> |
||||
|
<image src="/static/my/opinion.png" class="list-icon"/> |
||||
|
<text>意见反馈</text> |
||||
|
<uni-icons type="arrowright" size="16" class="arrow"/> |
||||
|
</view> |
||||
|
<view class="list-item" @click="goToAbout"> |
||||
|
<image src="/static/my/about.png" class="list-icon"/> |
||||
|
<text>关于DeepChart</text> |
||||
|
<uni-icons type="arrowright" size="16" class="arrow"/> |
||||
|
</view> |
||||
|
</view> |
||||
|
<footerBar class="static-footer" :type="type"></footerBar> |
||||
|
</view> |
||||
</template> |
</template> |
||||
|
|
||||
<script setup> |
<script setup> |
||||
import { ref,onMounted } from 'vue' |
|
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import { |
||||
|
ArrowRight |
||||
|
} from '@element-plus/icons-vue' |
||||
import footerBar from '../../components/footerBar.vue' |
import footerBar from '../../components/footerBar.vue' |
||||
|
import {getUserInfo} from "@/api/member" |
||||
|
|
||||
const type = ref('member') |
const type = ref('member') |
||||
const iSMT = ref(0) |
const iSMT = ref(0) |
||||
|
const username = ref('') |
||||
|
const dccode = ref('') |
||||
|
|
||||
|
const userInfoRes = ref()// 用户身份信息 |
||||
|
userInfoRes.value = getUserInfo() |
||||
|
userInfoRes.value.then(res => { |
||||
|
username.value = res.data.username |
||||
|
dccode.value = res.data.dccode |
||||
|
console.log('用户信息', userInfoRes.value) |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
const goToGeneral = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/setting/general' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToMarket = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/market' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToAccount = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/account' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToNewVersion = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/newVersion' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToAbout = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/about' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToShare = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/share' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
onMounted(() => { |
onMounted(() => { |
||||
// 状态栏高度 |
|
||||
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
|
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight |
||||
|
console.log('??????????????', iSMT.value) |
||||
}) |
}) |
||||
</script> |
</script> |
||||
|
|
||||
<style scoped> |
<style scoped> |
||||
.static-footer { |
.static-footer { |
||||
position: fixed; |
|
||||
bottom: 0; |
|
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
} |
||||
|
|
||||
|
.top { |
||||
|
height: 47vh; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.bell { |
||||
|
height: 9.6vh; |
||||
|
display: flex; |
||||
|
align-items: flex-end; |
||||
|
justify-content: flex-end; |
||||
|
padding-right: 50rpx; |
||||
|
} |
||||
|
|
||||
|
.image-bell { |
||||
|
width: 13px; |
||||
|
height: 16px; |
||||
|
} |
||||
|
|
||||
|
.msg { |
||||
|
height: 10.7vh; |
||||
|
display: flex; |
||||
|
margin-top: 3vh; |
||||
|
margin-bottom: 3vh; |
||||
|
} |
||||
|
|
||||
|
.msg-left { |
||||
|
width: 33.6vw; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.avatar { |
||||
|
width: 175rpx; |
||||
|
height: 175rpx; |
||||
|
border-radius: 50%; |
||||
|
background-color: black; |
||||
|
} |
||||
|
|
||||
|
.msg-center { |
||||
|
width: 51.7vw; |
||||
|
padding-left: 2.5vh; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.userInfo { |
||||
|
font-size: 20px; |
||||
|
} |
||||
|
|
||||
|
.userId { |
||||
|
font-size: 14px; |
||||
|
margin-top: 1vh; |
||||
|
} |
||||
|
|
||||
|
.image-editName { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
margin-left: 2vw; |
||||
|
} |
||||
|
|
||||
|
.msg-right { |
||||
|
width: 14.7vw; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.image-attendance { |
||||
|
width: 43rpx; |
||||
|
height: 43rpx; |
||||
|
} |
||||
|
|
||||
|
.settings-buttons { |
||||
|
display: flex; |
||||
|
justify-content: space-around; |
||||
|
} |
||||
|
|
||||
|
.setting-btn { |
||||
|
width: 349rpx; |
||||
|
height: 135rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
background-color: rgb(243, 243, 243); |
||||
|
border-radius: 8%; |
||||
|
} |
||||
|
|
||||
|
.setting-icon { |
||||
|
width: 64.7rpx; |
||||
|
height: 64.7rpx; |
||||
|
margin-right: 25rpx; |
||||
|
} |
||||
|
|
||||
|
.setting-btn text { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.share { |
||||
|
height: 12.6vh; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.img-share { |
||||
|
width: 720rpx; |
||||
|
height: 160rpx; |
||||
|
} |
||||
|
|
||||
|
.bottom { |
||||
|
height: 44.5vh; |
||||
|
margin-top: 1vh; |
||||
|
background-color: rgb(255, 255, 255); |
||||
|
} |
||||
|
|
||||
|
.list-item { |
||||
|
width: 670rpx; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0rpx 40rpx; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.list-item:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.list-icon { |
||||
|
width: 42rpx; |
||||
|
height: 42rpx; |
||||
|
margin-right: 18rpx; |
||||
|
} |
||||
|
|
||||
|
.arrow { |
||||
|
margin-left: auto; |
||||
|
} |
||||
|
|
||||
|
.update-tip { |
||||
|
display: flex; |
||||
|
color: #999; |
||||
|
font-size: 24rpx; |
||||
|
align-items: center; |
||||
|
margin-left: 200rpx; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.circle { |
||||
|
width: 10rpx; |
||||
|
height: 10rpx; |
||||
|
border-radius: 50%; |
||||
|
background-color: red; |
||||
|
margin-left: 10rpx; |
||||
} |
} |
||||
</style> |
</style> |
||||
@ -0,0 +1,477 @@ |
|||||
|
<!-- @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" v-if="activeTabIndex === 0"> |
||||
|
<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" :key="i" class="index_item"> |
||||
|
<IndexCard :market="index.market" :stockName="index.name" :currentPrice="index.price" :changeAmount="index.change" :changePercent="index.changePercent" :isRising="index.isRising" @click="viewIndexDetail(index,i)"/> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 今日市场情绪温度 --> |
||||
|
<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" v-if="activeTabIndex === 1"> |
||||
|
<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 countryInfo" :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" v-if="activeTabIndex === 2"> |
||||
|
<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 countryInfo" :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, watch } from "vue"; |
||||
|
import IndexCard from "../../components/IndexCard.vue"; |
||||
|
import { queryStockDataAPI } from "@/api/marketSituation/marketSituation"; |
||||
|
import { useMarketSituationStore } from "../../stores/modules/marketSituation.js"; |
||||
|
const marketSituationStore = useMarketSituationStore(); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
switchTab(0); |
||||
|
}); |
||||
|
|
||||
|
// 子Tab与操作 |
||||
|
// const marketTabs = ["全部", "美股", "纽交所", "纳斯达克"]; |
||||
|
const activeTabIndex = ref(0); |
||||
|
const switchTab = (i) => { |
||||
|
activeTabIndex.value = i; |
||||
|
queryStockDataAPI({ |
||||
|
parentId: props.countryId, |
||||
|
tradeId: activeTabIndex.value+1, |
||||
|
}).then((res) => { |
||||
|
if (res.code === 200) { |
||||
|
countryInfo.value = res.data.dataPage.records; |
||||
|
marketSituationStore.countryMarketCardData = countryInfo.value.map((item) => ({ |
||||
|
market: item.market, |
||||
|
stockCode: item.code, |
||||
|
stockName: item.name, |
||||
|
})); |
||||
|
console.log(res.data) |
||||
|
console.log(res.data.dataPage.records) |
||||
|
console.log(countryInfo.value); |
||||
|
} |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 今日情绪温度示例数据 |
||||
|
const sentimentMeters = [ |
||||
|
{ value: 90, label: "道琼斯", theme: "hot" }, |
||||
|
{ value: 60, label: "纳斯达克", theme: "warm" }, |
||||
|
{ value: 20, label: "标普500", theme: "cool" }, |
||||
|
]; |
||||
|
|
||||
|
// 图标映射 |
||||
|
const selectIcons = { |
||||
|
hot: "/static/marketSituation-image/hot.png", |
||||
|
warm: "/static/marketSituation-image/warm.png", |
||||
|
cool: "/static/marketSituation-image/cool.png", |
||||
|
}; |
||||
|
|
||||
|
// Props |
||||
|
const props = defineProps({ |
||||
|
countryId: { |
||||
|
type: Number, |
||||
|
required: true, |
||||
|
}, |
||||
|
marketTabs: { |
||||
|
type: Array, |
||||
|
}, |
||||
|
tabData: { |
||||
|
type: Object, |
||||
|
default: null |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
// 计算当前国家信息 |
||||
|
const countryInfo = ref('') |
||||
|
// 方法:查看指数详情 |
||||
|
const viewIndexDetail = (item, index) => { |
||||
|
console.log("查看指数详情:", item); |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/marketSituation/marketCondition?stockInformation=${encodeURIComponent(JSON.stringify(item))}&index=${index}&from=countryMarket`, |
||||
|
}); |
||||
|
}; |
||||
|
// 处理从父组件接收的数据 |
||||
|
const handleTabData = (tabData) => { |
||||
|
if (tabData && tabData.type === 'country' && tabData.data) { |
||||
|
if (tabData.data.dataPage && tabData.data.dataPage.records) { |
||||
|
countryInfo.value = tabData.data.dataPage.records; |
||||
|
marketSituationStore.countryMarketCardData = countryInfo.value.map((item) => ({ |
||||
|
market: item.market, |
||||
|
stockCode: item.code, |
||||
|
stockName: item.name, |
||||
|
})); |
||||
|
console.log('countryMarket接收到数据:', countryInfo.value); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 监听tabData变化 |
||||
|
watch(() => props.tabData, (newTabData) => { |
||||
|
if (newTabData) { |
||||
|
handleTabData(newTabData); |
||||
|
} |
||||
|
}, { immediate: true }); |
||||
|
|
||||
|
// 查看更多占位 |
||||
|
const viewMore = (type) => { |
||||
|
}; |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.content { |
||||
|
padding: 0 20rpx 20rpx 20rpx; |
||||
|
} |
||||
|
|
||||
|
/* 子Tab */ |
||||
|
.sub_tabs { |
||||
|
display: flex; |
||||
|
gap: 16rpx; |
||||
|
padding: 0 20rpx 20rpx 20rpx; |
||||
|
} |
||||
|
|
||||
|
.tab_item { |
||||
|
padding: 6rpx 20rpx; |
||||
|
border-radius: 5rpx; |
||||
|
background: #f5f5f5; |
||||
|
color: #666; |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.tab_item.active { |
||||
|
background: #ff4444; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.section { |
||||
|
padding: 20rpx; |
||||
|
border-radius: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.section_header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.section_title { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.section_action { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.indices_grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
} |
||||
|
|
||||
|
/* 情绪温度 */ |
||||
|
.sentiment { |
||||
|
background-color: #f6f6f6; |
||||
|
padding: 0 20rpx 20rpx 20rpx; |
||||
|
} |
||||
|
|
||||
|
.section_subtitle { |
||||
|
font-size: 24rpx; |
||||
|
color: #000000; |
||||
|
padding: 20rpx 0; |
||||
|
} |
||||
|
|
||||
|
.meters { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
gap: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.meter_item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
/* padding: 10rpx; */ |
||||
|
background: #ffffff; |
||||
|
border-radius: 12rpx; |
||||
|
} |
||||
|
|
||||
|
.meter_item image { |
||||
|
width: 100rpx; |
||||
|
height: 100rpx; |
||||
|
} |
||||
|
|
||||
|
.meter_info { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.meter_value { |
||||
|
font-size: 36rpx; |
||||
|
} |
||||
|
|
||||
|
.meter_value.hot { |
||||
|
color: #ff6b6b; |
||||
|
} |
||||
|
|
||||
|
.meter_value.warm { |
||||
|
color: #ffd166; |
||||
|
} |
||||
|
|
||||
|
.meter_value.cool { |
||||
|
color: #60a5fa; |
||||
|
} |
||||
|
|
||||
|
.meter_label { |
||||
|
font-size: 22rpx; |
||||
|
} |
||||
|
|
||||
|
.meter_label.hot { |
||||
|
color: #ff6b6b; |
||||
|
} |
||||
|
|
||||
|
.meter_label.warm { |
||||
|
color: #ffd166; |
||||
|
} |
||||
|
|
||||
|
.meter_label.cool { |
||||
|
color: #60a5fa; |
||||
|
} |
||||
|
|
||||
|
/* 板块 */ |
||||
|
.sectors_grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
gap: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.sector_item { |
||||
|
background: #fafafa; |
||||
|
border-radius: 12rpx; |
||||
|
padding: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.sector_header { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
margin-bottom: 12rpx; |
||||
|
} |
||||
|
|
||||
|
.sector_name { |
||||
|
font-size: 24rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.sector_change { |
||||
|
font-size: 22rpx; |
||||
|
} |
||||
|
|
||||
|
.sector_change.rising { |
||||
|
color: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
.sector_change.falling { |
||||
|
color: #27ae60; |
||||
|
} |
||||
|
|
||||
|
.sector_price { |
||||
|
font-size: 24rpx; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
/* 股票表 */ |
||||
|
.table { |
||||
|
background: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.table_header, |
||||
|
.table_row { |
||||
|
display: grid; |
||||
|
grid-template-columns: 2fr 1fr 1fr; |
||||
|
padding: 18rpx 20rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.table_header { |
||||
|
background: #fafafa; |
||||
|
} |
||||
|
|
||||
|
.cell.name { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
} |
||||
|
|
||||
|
.stk_name { |
||||
|
font-size: 26rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.stk_code { |
||||
|
font-size: 22rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.stk_price { |
||||
|
font-size: 26rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.stk_change { |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.stk_change.rising { |
||||
|
color: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
.stk_change.falling { |
||||
|
color: #27ae60; |
||||
|
} |
||||
|
|
||||
|
.index_item { |
||||
|
background: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.hot_stocks { |
||||
|
margin-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.stocks_list { |
||||
|
background: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.stock_item { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 24rpx 20rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.stock_item:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.stock_info { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.stock_name { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
display: block; |
||||
|
margin-bottom: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.stock_code { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.stock_price { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.price { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
display: block; |
||||
|
margin-bottom: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.change { |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.change.rising { |
||||
|
color: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
.change.falling { |
||||
|
color: #27ae60; |
||||
|
} |
||||
|
|
||||
|
.bottom_safe_area { |
||||
|
height: 120rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,301 @@ |
|||||
|
<template> |
||||
|
<view class="content"> |
||||
|
<view class="section" v-if="type === 'forex'"> |
||||
|
<view class="section_title"> |
||||
|
<text class="title_icon">💱</text> |
||||
|
<text>外汇市场</text> |
||||
|
</view> |
||||
|
<view class="forex_grid"> |
||||
|
<view v-for="(item, index) in forexData" :key="index" class="forex_item"> |
||||
|
<view class="forex_pair"> |
||||
|
<text class="base_currency">{{ item.base }}</text> |
||||
|
<text class="separator">/</text> |
||||
|
<text class="quote_currency">{{ item.quote }}</text> |
||||
|
</view> |
||||
|
<view class="forex_price"> |
||||
|
<text class="price">{{ item.price }}</text> |
||||
|
<text :class="['change', item.isRising ? 'rising' : 'falling']"> |
||||
|
{{ item.change }} |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="section" v-if="type === 'metals'"> |
||||
|
<view class="section_title"> |
||||
|
<text class="title_icon">🥇</text> |
||||
|
<text>贵金属</text> |
||||
|
</view> |
||||
|
<view class="metals_grid"> |
||||
|
<view v-for="(item, index) in metalsData" :key="index" class="metal_item"> |
||||
|
<view class="metal_info"> |
||||
|
<text class="metal_icon">{{ item.icon }}</text> |
||||
|
<text class="metal_name">{{ item.name }}</text> |
||||
|
</view> |
||||
|
<view class="metal_price"> |
||||
|
<text class="price">{{ item.price }}</text> |
||||
|
<text class="unit">{{ item.unit }}</text> |
||||
|
<text :class="['change', item.isRising ? 'rising' : 'falling']"> |
||||
|
{{ item.change }} |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 市场动态 --> |
||||
|
<view class="market_news"> |
||||
|
<view class="section_title"> |
||||
|
<text class="title_icon">📰</text> |
||||
|
<text>市场动态</text> |
||||
|
</view> |
||||
|
<view class="news_list"> |
||||
|
<view v-for="(news, index) in newsData" :key="index" class="news_item"> |
||||
|
<view class="news_content"> |
||||
|
<text class="news_title">{{ news.title }}</text> |
||||
|
<text class="news_time">{{ news.time }}</text> |
||||
|
</view> |
||||
|
<view class="news_impact" :class="news.impact"> |
||||
|
<text>{{ news.impactText }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部安全区域 --> |
||||
|
<view class="bottom_safe_area"></view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, computed } from 'vue' |
||||
|
|
||||
|
// Props |
||||
|
const props = defineProps({ |
||||
|
countryId: { |
||||
|
type: Number, |
||||
|
required: true |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
// 判断类型 |
||||
|
const type = computed(() => { |
||||
|
return props.countryId === 11 ? 'forex' : 'metals' |
||||
|
}) |
||||
|
|
||||
|
// 外汇数据 |
||||
|
const forexData = ref([ |
||||
|
{ base: 'USD', quote: 'CNY', price: '7.2456', change: '+0.0123', isRising: true }, |
||||
|
{ base: 'EUR', quote: 'USD', price: '1.0876', change: '-0.0034', isRising: false }, |
||||
|
{ base: 'GBP', quote: 'USD', price: '1.2654', change: '+0.0087', isRising: true }, |
||||
|
{ base: 'USD', quote: 'JPY', price: '149.87', change: '+0.45', isRising: true }, |
||||
|
{ base: 'AUD', quote: 'USD', price: '0.6543', change: '-0.0021', isRising: false }, |
||||
|
{ base: 'USD', quote: 'SGD', price: '1.3456', change: '+0.0012', isRising: true } |
||||
|
]) |
||||
|
|
||||
|
// 贵金属数据 |
||||
|
const metalsData = ref([ |
||||
|
{ icon: '🥇', name: '黄金', price: '2,034.56', unit: 'USD/盎司', change: '+12.34', isRising: true }, |
||||
|
{ icon: '🥈', name: '白银', price: '24.87', unit: 'USD/盎司', change: '-0.23', isRising: false }, |
||||
|
{ icon: '⚪', name: '铂金', price: '987.65', unit: 'USD/盎司', change: '+5.67', isRising: true }, |
||||
|
{ icon: '⚫', name: '钯金', price: '1,234.56', unit: 'USD/盎司', change: '-8.90', isRising: false } |
||||
|
]) |
||||
|
|
||||
|
// 新闻数据 |
||||
|
const newsData = ref([ |
||||
|
{ |
||||
|
title: '美联储暗示可能暂停加息', |
||||
|
time: '2小时前', |
||||
|
impact: 'high', |
||||
|
impactText: '高影响' |
||||
|
}, |
||||
|
{ |
||||
|
title: '欧洲央行维持利率不变', |
||||
|
time: '4小时前', |
||||
|
impact: 'medium', |
||||
|
impactText: '中影响' |
||||
|
}, |
||||
|
{ |
||||
|
title: '黄金价格创新高', |
||||
|
time: '6小时前', |
||||
|
impact: 'medium', |
||||
|
impactText: '中影响' |
||||
|
}, |
||||
|
{ |
||||
|
title: '美元指数小幅下跌', |
||||
|
time: '8小时前', |
||||
|
impact: 'low', |
||||
|
impactText: '低影响' |
||||
|
} |
||||
|
]) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.content { |
||||
|
padding: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.section { |
||||
|
margin-bottom: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.section_title { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
font-size: 28rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.title_icon { |
||||
|
font-size: 32rpx; |
||||
|
margin-right: 12rpx; |
||||
|
} |
||||
|
|
||||
|
.forex_grid, .metals_grid { |
||||
|
background: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.forex_item, .metal_item { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 24rpx 20rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.forex_item:last-child, .metal_item:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.forex_pair { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.base_currency { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.separator { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
margin: 0 8rpx; |
||||
|
} |
||||
|
|
||||
|
.quote_currency { |
||||
|
font-size: 28rpx; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
.forex_price, .metal_price { |
||||
|
text-align: right; |
||||
|
} |
||||
|
|
||||
|
.price { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
display: block; |
||||
|
margin-bottom: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.unit { |
||||
|
font-size: 20rpx; |
||||
|
color: #999; |
||||
|
margin-left: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.change { |
||||
|
font-size: 24rpx; |
||||
|
} |
||||
|
|
||||
|
.change.rising { |
||||
|
color: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
.change.falling { |
||||
|
color: #27ae60; |
||||
|
} |
||||
|
|
||||
|
.metal_info { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.metal_icon { |
||||
|
font-size: 32rpx; |
||||
|
margin-right: 16rpx; |
||||
|
} |
||||
|
|
||||
|
.metal_name { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.market_news { |
||||
|
margin-bottom: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.news_list { |
||||
|
background: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.news_item { |
||||
|
display: flex; |
||||
|
justify-content: space-between; |
||||
|
align-items: center; |
||||
|
padding: 24rpx 20rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.news_item:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.news_content { |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.news_title { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
display: block; |
||||
|
margin-bottom: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.news_time { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.news_impact { |
||||
|
padding: 6rpx 12rpx; |
||||
|
border-radius: 16rpx; |
||||
|
font-size: 20rpx; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.news_impact.high { |
||||
|
background: #e74c3c; |
||||
|
} |
||||
|
|
||||
|
.news_impact.medium { |
||||
|
background: #f39c12; |
||||
|
} |
||||
|
|
||||
|
.news_impact.low { |
||||
|
background: #95a5a6; |
||||
|
} |
||||
|
|
||||
|
.bottom_safe_area { |
||||
|
height: 120rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,859 @@ |
|||||
|
<!-- @format --> |
||||
|
|
||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<!-- 固定头部 --> |
||||
|
<view class="header_fixed" :style="{ top: iSMT + 'px' }"> |
||||
|
<view class="header_content"> |
||||
|
<view class="header_back" @click="goBack"> |
||||
|
<image src="/static/marketSituation-image/back.png" mode=""></image> |
||||
|
</view> |
||||
|
<view class="header_input_wrapper"> |
||||
|
<image class="search_icon" src="/static/marketSituation-image/search.png" mode="" @click="onSearchClick"></image> |
||||
|
<input class="header_input" type="text" placeholder="搜索" placeholder-style="color: #A6A6A6; font-size: 22rpx;" v-model="searchValue" @input="onSearchInput" @confirm="onSearchConfirm" /> |
||||
|
</view> |
||||
|
<view class="header_icons"> |
||||
|
<view class="header_icon" @click="selected"> |
||||
|
<image src="/static/marketSituation-image/mySeclected.png" mode=""></image> |
||||
|
</view> |
||||
|
<view class="header_icon" @click="history"> |
||||
|
<image src="/static/marketSituation-image/history.png" mode=""></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="warn"> |
||||
|
<image src="/static/marketSituation-image/warn.png" mode="aspectFit"></image> |
||||
|
<view class="warn_text_container"> |
||||
|
<text :class="warnTextClass">{{ $t("marketSituation.warn") }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 内容区域 --> |
||||
|
<scroll-view class="content" :style="{ top: contentTopPosition + 'px' }" scroll-y="true"> |
||||
|
<!-- 亚太-中华 --> |
||||
|
<view class="market-section" v-for="(item, parentIndex) in marketSituationStore.gloablCardData" :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, index) 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, parentIndex, index)" |
||||
|
/> |
||||
|
</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, onUnmounted, computed, nextTick, watch } from "vue"; |
||||
|
import footerBar from "../../components/footerBar.vue"; |
||||
|
import IndexCard from "../../components/IndexCard.vue"; |
||||
|
import { getRegionalGroupAPI } from "../../api/marketSituation/marketSituation.js"; |
||||
|
import { useMarketSituationStore } from "../../stores/modules/marketSituation.js"; |
||||
|
const marketSituationStore = useMarketSituationStore(); |
||||
|
// 响应式数据 |
||||
|
const iSMT = ref(0); // 状态栏高度 |
||||
|
const contentHeight = ref(0); |
||||
|
const headerHeight = ref(0); // 头部高度 |
||||
|
const searchValue = ref(""); // 搜索值 |
||||
|
const isWarnTextOverflow = ref(false); // warn文字是否溢出 |
||||
|
|
||||
|
// warn文字的class计算属性 |
||||
|
const warnTextClass = computed(() => { |
||||
|
return isWarnTextOverflow.value ? "warn_text scroll-active" : "warn_text"; |
||||
|
}); |
||||
|
|
||||
|
// 检测warn文字是否溢出 |
||||
|
const checkWarnTextOverflow = () => { |
||||
|
nextTick(() => { |
||||
|
setTimeout(() => { |
||||
|
const query = uni.createSelectorQuery(); |
||||
|
|
||||
|
// 同时查询容器和文字元素 |
||||
|
query.select(".warn_text_container").boundingClientRect(); |
||||
|
query.select(".warn_text").boundingClientRect(); |
||||
|
query.exec((res) => { |
||||
|
const containerRect = res[0]; |
||||
|
const textRect = res[1]; |
||||
|
|
||||
|
if (!containerRect || !textRect) { |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
// 判断文字是否超出容器(留一些余量) |
||||
|
const isOverflow = textRect.width > containerRect.width - 10; |
||||
|
|
||||
|
isWarnTextOverflow.value = isOverflow; |
||||
|
}); |
||||
|
}, 500); |
||||
|
}); |
||||
|
}; |
||||
|
const 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, |
||||
|
}, |
||||
|
]); |
||||
|
|
||||
|
// 亚太指数数据 |
||||
|
const asiaIndexes = ref([ |
||||
|
{ |
||||
|
flagIcon: "/static/c7.png", |
||||
|
stockName: "日经225", |
||||
|
stockCode: "noCode", |
||||
|
currentPrice: "28456.78", |
||||
|
changeAmount: "+234.56", |
||||
|
changePercent: "+0.83%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: "/static/c8.png", |
||||
|
stockName: "韩国KOSPI", |
||||
|
stockCode: "noCode", |
||||
|
currentPrice: "2567.89", |
||||
|
changeAmount: "-12.34", |
||||
|
changePercent: "-0.48%", |
||||
|
isRising: false, |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: "/static/c9.png", |
||||
|
stockName: "印度孟买", |
||||
|
stockCode: "noCode", |
||||
|
currentPrice: "65432.10", |
||||
|
changeAmount: "+456.78", |
||||
|
changePercent: "+0.70%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
]); |
||||
|
|
||||
|
// 美洲指数数据 |
||||
|
const americaIndexes = ref([ |
||||
|
{ |
||||
|
flagIcon: "/static/c7.png", |
||||
|
stockName: "道琼斯指数", |
||||
|
stockCode: "noCode", |
||||
|
currentPrice: "34567.89", |
||||
|
changeAmount: "+123.45", |
||||
|
changePercent: "+0.36%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: "/static/c8.png", |
||||
|
stockName: "纳斯达克", |
||||
|
stockCode: "noCode", |
||||
|
currentPrice: "13456.78", |
||||
|
changeAmount: "-67.89", |
||||
|
changePercent: "-0.50%", |
||||
|
isRising: false, |
||||
|
}, |
||||
|
{ |
||||
|
flagIcon: "/static/c9.png", |
||||
|
stockName: "标普500", |
||||
|
stockCode: "noCode", |
||||
|
currentPrice: "4234.56", |
||||
|
changeAmount: "+23.45", |
||||
|
changePercent: "+0.56%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
]); |
||||
|
|
||||
|
// 计算属性:内容区域顶部位置 |
||||
|
const contentTopPosition = computed(() => { |
||||
|
const statusBarHeight = iSMT.value || 0; |
||||
|
const currentHeaderHeight = headerHeight.value > 0 ? headerHeight.value : 100; |
||||
|
return statusBarHeight + currentHeaderHeight; |
||||
|
}); |
||||
|
|
||||
|
// 方法:返回上一页 |
||||
|
const goBack = () => { |
||||
|
uni.navigateBack(); |
||||
|
}; |
||||
|
|
||||
|
// 方法:搜索输入 |
||||
|
const onSearchInput = (e) => { |
||||
|
searchValue.value = e.detail.value; |
||||
|
}; |
||||
|
|
||||
|
// 方法:清除搜索 |
||||
|
const clearSearch = () => { |
||||
|
searchValue.value = ""; |
||||
|
}; |
||||
|
|
||||
|
// 方法:查看更多 |
||||
|
const viewMore = (market) => { |
||||
|
console.log("查看更多:", market); |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/marketSituation/marketDetail?market=${market}`, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
// 方法:查看指数详情 |
||||
|
const viewIndexDetail = (item, parentIndex, index) => { |
||||
|
console.log("查看指数详情:", item.stockName); |
||||
|
// uni.showToast({ |
||||
|
// title: `查看 ${item.stockName} 详情`, |
||||
|
// icon: 'none', |
||||
|
// duration: 2000 |
||||
|
// }) |
||||
|
// 这里可以跳转到具体的指数详情页面 |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/marketSituation/marketCondition?stockInformation=${encodeURIComponent(JSON.stringify(item))}&parentIndex=${parentIndex}&index=${index}&from=globalIndex`, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const getRegionalGroup = async () => { |
||||
|
try { |
||||
|
const result = await getRegionalGroupAPI(); |
||||
|
globalIndexArray.value = result.data; |
||||
|
marketSituationStore.gloablCardData = result.data; |
||||
|
} catch (e) { |
||||
|
console.log("获取区域指数失败", e); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// TCP相关响应式变量 |
||||
|
import tcpConnection, { TCPConnection, TCP_CONFIG } from "@/api/tcpConnection.js"; |
||||
|
const tcpConnected = ref(false); |
||||
|
const connectionListener = ref(null); |
||||
|
const messageListener = ref(null); |
||||
|
// 初始化TCP监听器 |
||||
|
const initTcpListeners = () => { |
||||
|
// 创建连接状态监听器并保存引用 |
||||
|
connectionListener.value = (status, result) => { |
||||
|
tcpConnected.value = status === "connected"; |
||||
|
console.log("TCP连接状态变化:", status, tcpConnected.value); |
||||
|
|
||||
|
// 显示连接状态提示 |
||||
|
|
||||
|
// 如果连接,发送获取批量数据 |
||||
|
if (status === "connected") { |
||||
|
sendTcpMessage("batch_real_time"); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 创建消息监听器并保存引用 |
||||
|
messageListener.value = (type, message, parsedArray) => { |
||||
|
const messageObj = { |
||||
|
type: type, |
||||
|
content: message, |
||||
|
parsedArray: parsedArray, |
||||
|
timestamp: new Date().toLocaleTimeString(), |
||||
|
direction: "received", |
||||
|
}; |
||||
|
|
||||
|
// 解析股票数据 |
||||
|
parseStockData(message); |
||||
|
}; |
||||
|
|
||||
|
// 注册监听器 |
||||
|
tcpConnection.onConnectionChange(connectionListener.value); |
||||
|
tcpConnection.onMessage(messageListener.value); |
||||
|
}; |
||||
|
|
||||
|
// 连接TCP服务器 |
||||
|
const connectTcp = () => { |
||||
|
console.log("开始连接TCP服务器..."); |
||||
|
tcpConnection.connect(); |
||||
|
}; |
||||
|
|
||||
|
// 断开TCP连接 |
||||
|
const disconnectTcp = () => { |
||||
|
console.log("断开TCP连接..."); |
||||
|
tcpConnection.disconnect(); |
||||
|
tcpConnected.value = false; |
||||
|
}; |
||||
|
|
||||
|
// 发送TCP消息 |
||||
|
const sendTcpMessage = (command) => { |
||||
|
let messageData; |
||||
|
let messageDataArray = []; |
||||
|
if (command == "batch_real_time") { |
||||
|
for (let i = 0; i < globalIndexArray.value.length; ++i) { |
||||
|
for (let j = 0; j < globalIndexArray.value[i].list.length; ++j) { |
||||
|
messageDataArray.push(globalIndexArray.value[i].list[j].code); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
console.log(messageDataArray); |
||||
|
|
||||
|
switch (command) { |
||||
|
// 实时行情推送 |
||||
|
case "real_time": |
||||
|
messageData = { |
||||
|
command: "real_time", |
||||
|
stock_code: "SH.000001", |
||||
|
}; |
||||
|
break; |
||||
|
// 初始化获取行情历史数据 |
||||
|
case "init_real_time": |
||||
|
messageData = { |
||||
|
command: "init_real_time", |
||||
|
stock_code: "SH.000001", |
||||
|
}; |
||||
|
break; |
||||
|
case "stop_real_time": |
||||
|
messageData = { |
||||
|
command: "stop_real_time", |
||||
|
}; |
||||
|
break; |
||||
|
// 股票列表 |
||||
|
case "stock_list": |
||||
|
messageData = { |
||||
|
command: "stock_list", |
||||
|
}; |
||||
|
break; |
||||
|
case "batch_real_time": |
||||
|
messageData = { |
||||
|
command: "batch_real_time", |
||||
|
stock_codes: messageDataArray, |
||||
|
}; |
||||
|
break; |
||||
|
case "help": |
||||
|
messageData = { |
||||
|
command: "help", |
||||
|
}; |
||||
|
break; |
||||
|
} |
||||
|
if (!messageData) { |
||||
|
return; |
||||
|
} else { |
||||
|
try { |
||||
|
// 发送消息 |
||||
|
const success = tcpConnection.send(messageData); |
||||
|
if (success) { |
||||
|
console.log("home发送TCP消息:", messageData); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error("发送TCP消息时出错:", error); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 获取TCP连接状态 |
||||
|
const getTcpStatus = () => { |
||||
|
const status = tcpConnection.getConnectionStatus(); |
||||
|
uni.showModal({ |
||||
|
title: "TCP连接状态", |
||||
|
content: `当前状态: ${status ? "已连接" : "未连接"}`, |
||||
|
showCancel: false, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
let isMorePacket = { |
||||
|
init_batch_real_time: false, |
||||
|
batch_real_time: false, |
||||
|
}; |
||||
|
let receivedMessage; |
||||
|
|
||||
|
// 解析TCP股票数据 |
||||
|
const parseStockData = (message) => { |
||||
|
try { |
||||
|
console.log("进入parseStockData, message类型:", typeof message); |
||||
|
|
||||
|
let parsedMessage; |
||||
|
// 如果isMorePacket是true,说明正在接受分包数据,无条件接收 |
||||
|
// 如果message是字符串且以{开头,说明是JSON字符串,需要解析 |
||||
|
// 如果不属于以上两种情况,说明是普通字符串,不预解析 |
||||
|
if (message.includes("欢迎连接到股票数据服务器")) { |
||||
|
console.log("服务器命令列表,不予处理"); |
||||
|
return; |
||||
|
} |
||||
|
if ((typeof message === "string" && message.includes("batch_data_start")) || isMorePacket.init_batch_real_time) { |
||||
|
if (typeof message === "string" && message.includes("batch_data_start")) { |
||||
|
console.log("开始接受分包数据"); |
||||
|
receivedMessage = ""; |
||||
|
} else { |
||||
|
console.log("接收分包数据过程中"); |
||||
|
} |
||||
|
isMorePacket.init_batch_real_time = true; |
||||
|
receivedMessage += message; |
||||
|
// 如果当前消息包含},说明收到JSON字符串结尾,结束接收,开始解析 |
||||
|
if (receivedMessage.includes("batch_data_complete")) { |
||||
|
console.log("接受分包数据结束"); |
||||
|
isMorePacket.init_batch_real_time = false; |
||||
|
|
||||
|
console.log("展示数据", receivedMessage); |
||||
|
let startIndex = 0; |
||||
|
let startCount = 0; |
||||
|
let endIndex = receivedMessage.indexOf("batch_data_complete"); |
||||
|
for (let i = 0; i < receivedMessage.length; ++i) { |
||||
|
if (receivedMessage[i] == "{") { |
||||
|
startCount++; |
||||
|
if (startCount == 2) { |
||||
|
startIndex = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
for (let i = receivedMessage.indexOf("batch_data_complete"); i >= 0; --i) { |
||||
|
if (receivedMessage[i] == "}" || startIndex == endIndex) { |
||||
|
endIndex = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (startIndex >= endIndex) { |
||||
|
throw new Error("JSON字符串格式错误"); |
||||
|
} |
||||
|
console.log("message", startIndex, endIndex, receivedMessage[endIndex], receivedMessage[startIndex]); |
||||
|
parsedMessage = JSON.parse(receivedMessage.substring(startIndex, endIndex + 1)); |
||||
|
|
||||
|
console.log("JSON解析成功,解析后类型:", typeof parsedMessage, parsedMessage); |
||||
|
|
||||
|
const stockDataArray = parsedMessage.data; |
||||
|
for (let i = 0; i < globalIndexArray.value.length; ++i) { |
||||
|
for (let j = 0; j < globalIndexArray.value[i].list.length; ++j) { |
||||
|
const stockCode = globalIndexArray.value[i].list[j].code; |
||||
|
marketSituationStore.gloablCardData[i].list[j].currentPrice = stockDataArray[stockCode][0].current_price.toFixed(2); |
||||
|
marketSituationStore.gloablCardData[i].list[j].changeAmount = (stockDataArray[stockCode][0].current_price - stockDataArray[stockCode][0].pre_close).toFixed(2); |
||||
|
marketSituationStore.gloablCardData[i].list[j].changePercent = ((100 * (stockDataArray[stockCode][0].current_price - stockDataArray[stockCode][0].pre_close)) / stockDataArray[stockCode][0].pre_close).toFixed(2) + "%"; |
||||
|
marketSituationStore.gloablCardData[i].list[j].isRising = stockDataArray[stockCode][0].current_price - stockDataArray[stockCode][0].pre_close >= 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else if ((typeof message === "string" && message.includes('{"count')) || isMorePacket.batch_real_time) { |
||||
|
if (typeof message === "string" && message.includes('{"count')) { |
||||
|
console.log("开始接受分包数据"); |
||||
|
receivedMessage = ""; |
||||
|
} else { |
||||
|
console.log("接收分包数据过程中"); |
||||
|
} |
||||
|
isMorePacket.batch_real_time = true; |
||||
|
receivedMessage += message; |
||||
|
// 如果当前消息包含},说明收到JSON字符串结尾,结束接收,开始解析 |
||||
|
if (receivedMessage.includes("batch_realtime_data")) { |
||||
|
console.log("接受分包数据结束"); |
||||
|
isMorePacket.batch_real_time = false; |
||||
|
|
||||
|
console.log("展示数据", receivedMessage); |
||||
|
let startIndex = 0; |
||||
|
let endIndex = receivedMessage.length - 1; |
||||
|
for (let i = 0; i < receivedMessage.length; ++i) { |
||||
|
if (receivedMessage[i] == "{") { |
||||
|
startIndex = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
for (let i = receivedMessage.length - 1; i >= 0; --i) { |
||||
|
if (receivedMessage[i] == "}" || startIndex == endIndex) { |
||||
|
endIndex = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (startIndex >= endIndex) { |
||||
|
throw new Error("JSON字符串格式错误"); |
||||
|
} |
||||
|
parsedMessage = JSON.parse(receivedMessage.substring(startIndex, endIndex + 1)); |
||||
|
|
||||
|
console.log("JSON解析成功,解析后类型:", typeof parsedMessage, parsedMessage); |
||||
|
const stockDataArray = parsedMessage.data; |
||||
|
for (let i = 0; i < globalIndexArray.value.length; ++i) { |
||||
|
for (let j = 0; j < globalIndexArray.value[i].list.length; ++j) { |
||||
|
const stockCode = globalIndexArray.value[i].list[j].code; |
||||
|
marketSituationStore.gloablCardData[i].list[j].currentPrice = stockDataArray[stockCode][0].current_price.toFixed(2); |
||||
|
marketSituationStore.gloablCardData[i].list[j].changeAmount = (stockDataArray[stockCode][0].current_price - stockDataArray[stockCode][0].pre_close).toFixed(2); |
||||
|
marketSituationStore.gloablCardData[i].list[j].changePercent = ((100 * (stockDataArray[stockCode][0].current_price - stockDataArray[stockCode][0].pre_close)) / stockDataArray[stockCode][0].pre_close).toFixed(2) + "%"; |
||||
|
marketSituationStore.gloablCardData[i].list[j].isRising = stockDataArray[stockCode][0].current_price - stockDataArray[stockCode][0].pre_close >= 0; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
// 没有通过JSON解析判断,说明不是需要的数据 |
||||
|
console.log("不是需要的数据,不做处理"); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error("解析TCP股票数据失败:", error.message); |
||||
|
console.error("错误详情:", error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 移除TCP监听器 |
||||
|
const removeTcpListeners = () => { |
||||
|
if (connectionListener.value) { |
||||
|
tcpConnection.removeConnectionListener(connectionListener.value); |
||||
|
connectionListener.value = null; |
||||
|
console.log("已移除TCP连接状态监听器"); |
||||
|
} |
||||
|
|
||||
|
if (messageListener.value) { |
||||
|
tcpConnection.removeMessageListener(messageListener.value); |
||||
|
messageListener.value = null; |
||||
|
console.log("已移除TCP消息监听器"); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const startTcp = () => { |
||||
|
try { |
||||
|
removeTcpListeners(); |
||||
|
disconnectTcp(); |
||||
|
initTcpListeners(); |
||||
|
connectTcp(); |
||||
|
} catch (error) { |
||||
|
console.error("建立连接并设置监听出错:", error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
onUnmounted(() => { |
||||
|
sendTcpMessage("stop_real_time"); |
||||
|
removeTcpListeners(); |
||||
|
disconnectTcp(); |
||||
|
}); |
||||
|
|
||||
|
// 生命周期:页面挂载 |
||||
|
onMounted(async () => { |
||||
|
await getRegionalGroup(); |
||||
|
initTcpListeners(); |
||||
|
await nextTick(); |
||||
|
// 开始连接 |
||||
|
startTcp(); |
||||
|
// 获取系统信息 |
||||
|
const systemInfo = uni.getSystemInfoSync(); |
||||
|
iSMT.value = systemInfo.statusBarHeight || 0; |
||||
|
|
||||
|
console.log("全球指数页面加载完成"); |
||||
|
// 动态计算header实际高度 |
||||
|
uni |
||||
|
.createSelectorQuery() |
||||
|
.select(".header_fixed") |
||||
|
.boundingClientRect((rect) => { |
||||
|
if (rect) { |
||||
|
headerHeight.value = rect.height; |
||||
|
console.log("Header实际高度:", headerHeight.value, "px"); |
||||
|
} |
||||
|
}) |
||||
|
.exec(); |
||||
|
// 检测warn文字是否溢出 |
||||
|
checkWarnTextOverflow(); |
||||
|
}); |
||||
|
|
||||
|
// 监听headerHeight变化,重新计算contentHeight |
||||
|
watch(headerHeight, (newHeight) => { |
||||
|
if (newHeight > 0) { |
||||
|
const systemInfo = uni.getSystemInfoSync(); |
||||
|
const windowHeight = systemInfo.windowHeight; |
||||
|
const statusBarHeight = systemInfo.statusBarHeight || 0; |
||||
|
const footerHeight = 100; |
||||
|
|
||||
|
contentHeight.value = windowHeight - statusBarHeight - newHeight - footerHeight; |
||||
|
console.log("重新计算contentHeight:", contentHeight.value); |
||||
|
} |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.main { |
||||
|
position: relative; |
||||
|
height: 100vh; |
||||
|
overflow: hidden; |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
|
||||
|
/* 状态栏占位 */ |
||||
|
.top { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1001; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
/* 固定头部样式 */ |
||||
|
.header_fixed { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
background-color: #ffffff; |
||||
|
padding: 20rpx 0 0 0; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.header_content { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
height: 80rpx; |
||||
|
padding: 0 20rpx; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.header_back { |
||||
|
margin-right: 20rpx; |
||||
|
width: 25rpx; |
||||
|
height: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.header_back image { |
||||
|
width: 25rpx; |
||||
|
height: 30rpx; |
||||
|
} |
||||
|
|
||||
|
.header_input_wrapper { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
width: 100%; |
||||
|
margin: 0 20rpx 0 0; |
||||
|
height: 70rpx; |
||||
|
border-radius: 35rpx; |
||||
|
background-color: #ffffff; |
||||
|
border: 1rpx solid #e9ecef; |
||||
|
padding: 0 80rpx 0 30rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #5c5c5c; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.search_icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
opacity: 0.6; |
||||
|
} |
||||
|
|
||||
|
.header_input { |
||||
|
margin-left: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.header_icons { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 15rpx; |
||||
|
} |
||||
|
|
||||
|
.header_icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.header_icon image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.warn { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: flex-start; |
||||
|
gap: 10rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #666666; |
||||
|
padding: 20rpx; |
||||
|
max-width: 100%; |
||||
|
overflow: hidden; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.warn image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
flex-shrink: 0; |
||||
|
/* 防止图片被压缩 */ |
||||
|
position: relative; |
||||
|
z-index: 2; |
||||
|
/* 确保图片在最上层 */ |
||||
|
} |
||||
|
|
||||
|
.warn_text_container { |
||||
|
flex: 1; |
||||
|
overflow: hidden; |
||||
|
position: relative; |
||||
|
min-width: 0; |
||||
|
/* 允许容器收缩 */ |
||||
|
} |
||||
|
|
||||
|
.warn_text { |
||||
|
display: block; |
||||
|
white-space: nowrap; |
||||
|
will-change: transform; |
||||
|
/* 优化动画性能 */ |
||||
|
} |
||||
|
|
||||
|
/* 文字滚动动画 */ |
||||
|
@keyframes scrollText { |
||||
|
0% { |
||||
|
transform: translateX(0); |
||||
|
} |
||||
|
|
||||
|
20% { |
||||
|
transform: translateX(0); |
||||
|
} |
||||
|
|
||||
|
80% { |
||||
|
transform: translateX(-85%); |
||||
|
} |
||||
|
|
||||
|
100% { |
||||
|
transform: translateX(-85%); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* 当文字超长时启用滚动动画 */ |
||||
|
.warn_text.scroll-active { |
||||
|
animation: scrollText 12s linear infinite; |
||||
|
animation-delay: 2s; |
||||
|
/* 延迟2秒开始滚动,让用户先看到开头 */ |
||||
|
} |
||||
|
|
||||
|
/* 内容区域 */ |
||||
|
.content { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 120rpx; |
||||
|
background-color: #f5f5f5; |
||||
|
padding: 0; |
||||
|
} |
||||
|
|
||||
|
/* 市场分组 */ |
||||
|
.market-section { |
||||
|
background-color: white; |
||||
|
border-radius: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.market-header { |
||||
|
margin: 20rpx 20rpx 0 20rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
margin-bottom: 10rpx; |
||||
|
padding-bottom: 10rpx; |
||||
|
border-bottom: 2rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.market-title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: 600; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.market-more { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 8rpx; |
||||
|
} |
||||
|
|
||||
|
.more-text { |
||||
|
font-size: 24rpx; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
.more-arrow { |
||||
|
font-size: 20rpx; |
||||
|
color: #666; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
/* 三列卡片网格 */ |
||||
|
.cards-grid-three { |
||||
|
display: grid; |
||||
|
grid-template-columns: repeat(3, 1fr); |
||||
|
} |
||||
|
|
||||
|
.card-item { |
||||
|
background-color: white; |
||||
|
border-radius: 16rpx; |
||||
|
overflow: hidden; |
||||
|
transition: transform 0.2s ease, box-shadow 0.2s ease; |
||||
|
} |
||||
|
|
||||
|
.card-item:active { |
||||
|
transform: scale(0.98); |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.12); |
||||
|
} |
||||
|
|
||||
|
/* 底部安全区域 */ |
||||
|
.bottom-safe-area { |
||||
|
height: 40rpx; |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
|
||||
|
/* 底部导航栏 */ |
||||
|
.static-footer { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
} |
||||
|
|
||||
|
/* 响应式设计 */ |
||||
|
@media (max-width: 400rpx) { |
||||
|
.cards-grid-three { |
||||
|
grid-template-columns: repeat(2, 1fr); |
||||
|
} |
||||
|
} |
||||
|
</style> |
||||
2912
pages/marketSituation/marketCondition.vue
File diff suppressed because it is too large
View File
@ -0,0 +1,780 @@ |
|||||
|
<!-- @format --> |
||||
|
|
||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<!-- 自定义导航栏 --> |
||||
|
<view class="header_fixed" :style="{ top: iSMT + 'px' }"> |
||||
|
<view class="header-content"> |
||||
|
<view class="header-left" @click="goBack"> |
||||
|
<text class="back-text">‹</text> |
||||
|
</view> |
||||
|
<view class="header-center"> |
||||
|
<text class="header-title">{{ marketTitle }}</text> |
||||
|
</view> |
||||
|
<view class="header-right"> |
||||
|
<image src="/static/marketSituation-image/search.png" class="header-icon" mode="aspectFit"></image> |
||||
|
<text class="more-text">···</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<!-- 表头 --> |
||||
|
<view class="table-header"> |
||||
|
<view class="header-item name-column"> |
||||
|
<text class="header-text">名称</text> |
||||
|
</view> |
||||
|
<view class="header-item price-column" @click="sortByPrice"> |
||||
|
<text class="header-text">最新</text> |
||||
|
<text class="sort-icon">{{ sortType === "price" ? (sortOrder === "asc" ? "↑" : "↓") : "↕" }}</text> |
||||
|
</view> |
||||
|
<view class="header-item change-column" @click="sortByChange"> |
||||
|
<text class="header-text">涨幅</text> |
||||
|
<text class="sort-icon">{{ sortType === "change" ? (sortOrder === "asc" ? "↑" : "↓") : "↕" }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 内容区域 --> |
||||
|
<scroll-view class="content" :style="{ top: contentTopPosition + 'px' }" scroll-y="true"> |
||||
|
<!-- 股票列表 --> |
||||
|
<view class="stock-list"> |
||||
|
<view class="stock-row" v-for="(item,index) in sortedStockList" :key="item" @click="viewIndexDetail(item,index)"> |
||||
|
<view class="stock-cell name-column"> |
||||
|
<view class="stock-name">{{ item.stockName }}</view> |
||||
|
<view class="stock-code">{{ item.stockCode }}</view> |
||||
|
</view> |
||||
|
<view class="stock-cell price-column"> |
||||
|
<text class="stock-price" :class="item.isRising ? 'rising' : 'falling'"> |
||||
|
{{ typeof item.currentPrice === "number" ? item.currentPrice.toFixed(2) : item.currentPrice }} |
||||
|
</text> |
||||
|
</view> |
||||
|
<view class="stock-cell change-column"> |
||||
|
<text class="stock-change" :class="item.isRising ? 'rising' : 'falling'"> |
||||
|
{{ item.changePercent }} |
||||
|
</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部安全区域 --> |
||||
|
<!-- <view class="bottom-safe-area"></view> --> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 底部导航栏 --> |
||||
|
<!-- <footerBar class="static-footer" :type="'marketSituation'"></footerBar> --> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, computed, onMounted, onUnmounted, nextTick, watch } from "vue"; |
||||
|
import { onLoad } from "@dcloudio/uni-app"; |
||||
|
import footerBar from "@/components/footerBar.vue"; |
||||
|
import { getRegionalGroupListAPI } from "../../api/marketSituation/marketSituation.js"; |
||||
|
import { useMarketSituationStore } from "../../stores/modules/marketSituation.js"; |
||||
|
const marketSituationStore = useMarketSituationStore(); |
||||
|
// 响应式数据 |
||||
|
const iSMT = ref(0); |
||||
|
const contentHeight = ref(0); |
||||
|
const headerHeight = ref(80); |
||||
|
const marketTitle = ref(); |
||||
|
const sortType = ref(""); // 排序类型:'price' 或 'change' |
||||
|
const sortOrder = ref("desc"); // 排序顺序:'asc' 或 'desc' |
||||
|
|
||||
|
const regionalGroupArray = ref([]); |
||||
|
// 股票数据 |
||||
|
const stockList = ref([ |
||||
|
{ |
||||
|
stockName: "Telecommunication", |
||||
|
stockCode: "888607", |
||||
|
price: 1349.47, |
||||
|
change: "+7.67%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Other", |
||||
|
stockCode: "888607", |
||||
|
price: 1349.47, |
||||
|
change: "+6.67%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Consumer Discretio...", |
||||
|
stockCode: "888610", |
||||
|
price: 1349.47, |
||||
|
change: "+5.67%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Telecommunication", |
||||
|
stockCode: "888607", |
||||
|
price: 1349.47, |
||||
|
change: "+4.67%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Other", |
||||
|
stockCode: "888611", |
||||
|
price: 1359.47, |
||||
|
change: "+3.67%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Consumer Discretio...", |
||||
|
stockCode: "888610", |
||||
|
price: 1349.47, |
||||
|
change: "+2.67%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Telecommunication", |
||||
|
stockCode: "888607", |
||||
|
price: 1349.47, |
||||
|
change: "+1.67%", |
||||
|
isRising: true, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Other", |
||||
|
stockCode: "888611", |
||||
|
price: 1009.98, |
||||
|
change: "-1.67%", |
||||
|
isRising: false, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Consumer Discretio...", |
||||
|
stockCode: "888610", |
||||
|
price: 1009.98, |
||||
|
change: "-0.67%", |
||||
|
isRising: false, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Telecommunication", |
||||
|
stockCode: "888607", |
||||
|
price: 1009.98, |
||||
|
change: "-0.67%", |
||||
|
isRising: false, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Other", |
||||
|
stockCode: "888611", |
||||
|
price: 1009.98, |
||||
|
change: "-1.67%", |
||||
|
isRising: false, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Consumer Discretio...", |
||||
|
stockCode: "888610", |
||||
|
price: 1009.98, |
||||
|
change: "-4.67%", |
||||
|
isRising: false, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Consumer Discretio...", |
||||
|
stockCode: "888610", |
||||
|
price: 1009.98, |
||||
|
change: "-3.67%", |
||||
|
isRising: false, |
||||
|
}, |
||||
|
{ |
||||
|
stockName: "Consumer Discretio...", |
||||
|
stockCode: "888610", |
||||
|
price: 1009.98, |
||||
|
change: "-3.67%", |
||||
|
isRising: false, |
||||
|
}, |
||||
|
]); |
||||
|
|
||||
|
// 计算属性 |
||||
|
const contentTopPosition = computed(() => { |
||||
|
return iSMT.value + headerHeight.value; |
||||
|
}); |
||||
|
|
||||
|
const sortedStockList = computed(() => { |
||||
|
console.log("计算sortedStockList,原始数据长度:", marketSituationStore.marketDetailCardData.length); |
||||
|
let list = [...marketSituationStore.marketDetailCardData]; |
||||
|
|
||||
|
if (sortType.value === "price") { |
||||
|
list.sort((a, b) => { |
||||
|
return sortOrder.value === "asc" ? a.price - b.price : b.price - a.price; |
||||
|
}); |
||||
|
} else if (sortType.value === "change") { |
||||
|
list.sort((a, b) => { |
||||
|
const aChange = parseFloat(a.changePercent.replace(/[+%-]/g, "")); |
||||
|
const bChange = parseFloat(b.changePercent.replace(/[+%-]/g, "")); |
||||
|
return sortOrder.value === "asc" ? aChange - bChange : bChange - aChange; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
console.log("排序后数据长度:", list.length); |
||||
|
return list; |
||||
|
}); |
||||
|
|
||||
|
const getRegionalGroupList = async () => { |
||||
|
try { |
||||
|
const result = await getRegionalGroupListAPI({ |
||||
|
name: marketTitle.value, |
||||
|
}); |
||||
|
regionalGroupArray.value = result.data; |
||||
|
marketSituationStore.marketDetailCardData = result.data; |
||||
|
} catch (e) { |
||||
|
console.error("获取区域分组列表失败:", e); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 方法 |
||||
|
const goBack = () => { |
||||
|
uni.navigateBack(); |
||||
|
}; |
||||
|
|
||||
|
// 方法:查看指数详情 |
||||
|
const viewIndexDetail = (item,index) => { |
||||
|
console.log("查看指数详情:", item.stockName); |
||||
|
// 这里可以跳转到具体的指数详情页面 |
||||
|
uni.navigateTo({ |
||||
|
url: `/pages/marketSituation/marketCondition?stockInformation=${encodeURIComponent(JSON.stringify(item))}&index=${index}&from=marketDetail`, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
const sortByPrice = () => { |
||||
|
if (sortType.value === "price") { |
||||
|
sortOrder.value = sortOrder.value === "asc" ? "desc" : "asc"; |
||||
|
} else { |
||||
|
sortType.value = "price"; |
||||
|
sortOrder.value = "desc"; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const sortByChange = () => { |
||||
|
if (sortType.value === "change") { |
||||
|
sortOrder.value = sortOrder.value === "asc" ? "desc" : "asc"; |
||||
|
} else { |
||||
|
sortType.value = "change"; |
||||
|
sortOrder.value = "desc"; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// TCP相关响应式变量 |
||||
|
import tcpConnection, { TCPConnection, TCP_CONFIG } from "@/api/tcpConnection.js"; |
||||
|
const tcpConnected = ref(false); |
||||
|
const connectionListener = ref(null); |
||||
|
const messageListener = ref(null); |
||||
|
// 初始化TCP监听器 |
||||
|
const initTcpListeners = () => { |
||||
|
// 创建连接状态监听器并保存引用 |
||||
|
connectionListener.value = (status, result) => { |
||||
|
tcpConnected.value = status === "connected"; |
||||
|
console.log("TCP连接状态变化:", status, tcpConnected.value); |
||||
|
|
||||
|
// 如果连接,发送获取批量数据 |
||||
|
if (status === "connected") { |
||||
|
sendTcpMessage("batch_real_time"); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 创建消息监听器并保存引用 |
||||
|
messageListener.value = (type, message, parsedArray) => { |
||||
|
const messageObj = { |
||||
|
type: type, |
||||
|
content: message, |
||||
|
parsedArray: parsedArray, |
||||
|
timestamp: new Date().toLocaleTimeString(), |
||||
|
direction: "received", |
||||
|
}; |
||||
|
|
||||
|
// 解析股票数据 |
||||
|
parseStockData(message); |
||||
|
}; |
||||
|
|
||||
|
// 注册监听器 |
||||
|
tcpConnection.onConnectionChange(connectionListener.value); |
||||
|
tcpConnection.onMessage(messageListener.value); |
||||
|
}; |
||||
|
|
||||
|
// 连接TCP服务器 |
||||
|
const connectTcp = () => { |
||||
|
console.log("开始连接TCP服务器..."); |
||||
|
tcpConnection.connect(); |
||||
|
}; |
||||
|
|
||||
|
// 断开TCP连接 |
||||
|
const disconnectTcp = () => { |
||||
|
console.log("断开TCP连接..."); |
||||
|
tcpConnection.disconnect(); |
||||
|
tcpConnected.value = false; |
||||
|
}; |
||||
|
|
||||
|
// 发送TCP消息 |
||||
|
const sendTcpMessage = (command) => { |
||||
|
let messageData; |
||||
|
let messageDataArray = []; |
||||
|
if (command == "batch_real_time") { |
||||
|
messageDataArray = regionalGroupArray.value.map((item) => item.code); |
||||
|
} |
||||
|
console.log(messageDataArray); |
||||
|
|
||||
|
switch (command) { |
||||
|
// 实时行情推送 |
||||
|
case "real_time": |
||||
|
messageData = { |
||||
|
command: "real_time", |
||||
|
stock_code: "SH.000001", |
||||
|
}; |
||||
|
break; |
||||
|
// 初始化获取行情历史数据 |
||||
|
case "init_real_time": |
||||
|
messageData = { |
||||
|
command: "init_real_time", |
||||
|
stock_code: "SH.000001", |
||||
|
}; |
||||
|
break; |
||||
|
case "stop_real_time": |
||||
|
messageData = { |
||||
|
command: "stop_real_time", |
||||
|
}; |
||||
|
break; |
||||
|
// 股票列表 |
||||
|
case "stock_list": |
||||
|
messageData = { |
||||
|
command: "stock_list", |
||||
|
}; |
||||
|
break; |
||||
|
case "batch_real_time": |
||||
|
messageData = { |
||||
|
command: "batch_real_time", |
||||
|
stock_codes: messageDataArray, |
||||
|
}; |
||||
|
break; |
||||
|
case "help": |
||||
|
messageData = { |
||||
|
command: "help", |
||||
|
}; |
||||
|
break; |
||||
|
} |
||||
|
if (!messageData) { |
||||
|
|
||||
|
return; |
||||
|
} else { |
||||
|
try { |
||||
|
// 发送消息 |
||||
|
const success = tcpConnection.send(messageData); |
||||
|
if (success) { |
||||
|
console.log("home发送TCP消息:", messageData); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error("发送TCP消息时出错:", error); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 获取TCP连接状态 |
||||
|
const getTcpStatus = () => { |
||||
|
const status = tcpConnection.getConnectionStatus(); |
||||
|
uni.showModal({ |
||||
|
title: "TCP连接状态", |
||||
|
content: `当前状态: ${status ? "已连接" : "未连接"}`, |
||||
|
showCancel: false, |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
let isMorePacket = { |
||||
|
init_batch_real_time: false, |
||||
|
batch_real_time: false, |
||||
|
}; |
||||
|
let receivedMessage; |
||||
|
|
||||
|
// 解析TCP股票数据 |
||||
|
const parseStockData = (message) => { |
||||
|
try { |
||||
|
console.log("进入parseStockData, message类型:", typeof message); |
||||
|
|
||||
|
let parsedMessage; |
||||
|
// 如果isMorePacket是true,说明正在接受分包数据,无条件接收 |
||||
|
// 如果message是字符串且以{开头,说明是JSON字符串,需要解析 |
||||
|
// 如果不属于以上两种情况,说明是普通字符串,不预解析 |
||||
|
if (message.includes("欢迎连接到股票数据服务器")) { |
||||
|
console.log("服务器命令列表,不予处理"); |
||||
|
return; |
||||
|
} |
||||
|
if ((typeof message === "string" && message.includes("batch_data_start")) || isMorePacket.init_batch_real_time) { |
||||
|
if (typeof message === "string" && message.includes("batch_data_start")) { |
||||
|
console.log("开始接受分包数据"); |
||||
|
receivedMessage = ""; |
||||
|
} else { |
||||
|
console.log("接收分包数据过程中"); |
||||
|
} |
||||
|
isMorePacket.init_batch_real_time = true; |
||||
|
receivedMessage += message; |
||||
|
// 如果当前消息包含},说明收到JSON字符串结尾,结束接收,开始解析 |
||||
|
if (receivedMessage.includes("batch_data_complete")) { |
||||
|
console.log("接受分包数据结束"); |
||||
|
isMorePacket.init_batch_real_time = false; |
||||
|
|
||||
|
console.log("展示数据", receivedMessage); |
||||
|
let startIndex = 0; |
||||
|
let startCount = 0; |
||||
|
let endIndex = receivedMessage.indexOf("batch_data_complete"); |
||||
|
for (let i = 0; i < receivedMessage.length; ++i) { |
||||
|
if (receivedMessage[i] == "{") { |
||||
|
startCount++; |
||||
|
if (startCount == 2) { |
||||
|
startIndex = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
for (let i = receivedMessage.indexOf("batch_data_complete"); i >= 0; --i) { |
||||
|
if (receivedMessage[i] == "}" || startIndex == endIndex) { |
||||
|
endIndex = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (startIndex >= endIndex) { |
||||
|
throw new Error("JSON字符串格式错误"); |
||||
|
} |
||||
|
console.log("message", startIndex, endIndex, receivedMessage[endIndex], receivedMessage[startIndex]); |
||||
|
parsedMessage = JSON.parse(receivedMessage.substring(startIndex, endIndex + 1)); |
||||
|
|
||||
|
console.log("JSON解析成功,解析后类型:", typeof parsedMessage, parsedMessage); |
||||
|
|
||||
|
const stockDataArray = parsedMessage.data; |
||||
|
marketSituationStore.marketDetailCardData = regionalGroupArray.value.map((item) => ({ |
||||
|
market: item.market, |
||||
|
stockCode: item.code, |
||||
|
stockName: item.name, |
||||
|
id: item.id, |
||||
|
currentPrice: stockDataArray[item.code][0].current_price.toFixed(2), |
||||
|
changeAmount: (stockDataArray[item.code][0].current_price - stockDataArray[item.code][0].pre_close).toFixed(2), |
||||
|
changePercent: ((100 * (stockDataArray[item.code][0].current_price - stockDataArray[item.code][0].pre_close)) / stockDataArray[item.code][0].pre_close).toFixed(2) + "%", |
||||
|
isRising: stockDataArray[item.code][0].current_price - stockDataArray[item.code][0].pre_close >= 0, |
||||
|
})); |
||||
|
} |
||||
|
} else if ((typeof message === "string" && message.includes('{"count')) || isMorePacket.batch_real_time) { |
||||
|
if (typeof message === "string" && message.includes('{"count')) { |
||||
|
console.log("开始接受分包数据"); |
||||
|
receivedMessage = ""; |
||||
|
} else { |
||||
|
console.log("接收分包数据过程中"); |
||||
|
} |
||||
|
isMorePacket.batch_real_time = true; |
||||
|
receivedMessage += message; |
||||
|
// 如果当前消息包含},说明收到JSON字符串结尾,结束接收,开始解析 |
||||
|
if (receivedMessage.includes("batch_realtime_data")) { |
||||
|
console.log("接受分包数据结束"); |
||||
|
isMorePacket.batch_real_time = false; |
||||
|
|
||||
|
console.log("展示数据", receivedMessage); |
||||
|
let startIndex = 0; |
||||
|
let endIndex = receivedMessage.length - 1; |
||||
|
for (let i = 0; i < receivedMessage.length; ++i) { |
||||
|
if (receivedMessage[i] == "{") { |
||||
|
startIndex = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
for (let i = receivedMessage.length - 1; i >= 0; --i) { |
||||
|
if (receivedMessage[i] == "}" || startIndex == endIndex) { |
||||
|
endIndex = i; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
if (startIndex >= endIndex) { |
||||
|
throw new Error("JSON字符串格式错误"); |
||||
|
} |
||||
|
parsedMessage = JSON.parse(receivedMessage.substring(startIndex, endIndex + 1)); |
||||
|
|
||||
|
console.log("JSON解析成功,解析后类型:", typeof parsedMessage, parsedMessage); |
||||
|
const stockDataArray = parsedMessage.data; |
||||
|
marketSituationStore.marketDetailCardData = regionalGroupArray.value.map((item) => ({ |
||||
|
market: item.market, |
||||
|
stockCode: item.code, |
||||
|
stockName: item.name, |
||||
|
id: item.id, |
||||
|
currentPrice: stockDataArray[item.code][0].current_price.toFixed(2), |
||||
|
changeAmount: (stockDataArray[item.code][0].current_price - stockDataArray[item.code][0].pre_close).toFixed(2), |
||||
|
changePercent: ((100 * (stockDataArray[item.code][0].current_price - stockDataArray[item.code][0].pre_close)) / stockDataArray[item.code][0].pre_close).toFixed(2) + "%", |
||||
|
isRising: stockDataArray[item.code][0].current_price - stockDataArray[item.code][0].pre_close >= 0, |
||||
|
})); |
||||
|
} |
||||
|
} else { |
||||
|
// 没有通过JSON解析判断,说明不是需要的数据 |
||||
|
console.log("不是需要的数据,不做处理"); |
||||
|
} |
||||
|
} catch (error) { |
||||
|
console.error("解析TCP股票数据失败:", error.message); |
||||
|
console.error("错误详情:", error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 移除TCP监听器 |
||||
|
const removeTcpListeners = () => { |
||||
|
if (connectionListener.value) { |
||||
|
tcpConnection.removeConnectionListener(connectionListener.value); |
||||
|
connectionListener.value = null; |
||||
|
console.log("已移除TCP连接状态监听器"); |
||||
|
} |
||||
|
|
||||
|
if (messageListener.value) { |
||||
|
tcpConnection.removeMessageListener(messageListener.value); |
||||
|
messageListener.value = null; |
||||
|
console.log("已移除TCP消息监听器"); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const startTcp = () => { |
||||
|
try { |
||||
|
removeTcpListeners(); |
||||
|
disconnectTcp(); |
||||
|
initTcpListeners(); |
||||
|
connectTcp(); |
||||
|
} catch (error) { |
||||
|
console.error("建立连接并设置监听出错:", error); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 页面加载时接收参数 |
||||
|
onLoad(async (options) => { |
||||
|
if (options && options.market) { |
||||
|
marketTitle.value = options.market; |
||||
|
await getRegionalGroupList(); |
||||
|
initTcpListeners(); |
||||
|
await nextTick(); |
||||
|
// 开始连接 |
||||
|
startTcp(); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
onUnmounted(() => { |
||||
|
sendTcpMessage("stop_real_time"); |
||||
|
removeTcpListeners(); |
||||
|
disconnectTcp(); |
||||
|
}); |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 获取状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
// 动态计算header实际高度 |
||||
|
uni |
||||
|
.createSelectorQuery() |
||||
|
.select(".header_fixed") |
||||
|
.boundingClientRect((rect) => { |
||||
|
if (rect) { |
||||
|
headerHeight.value = rect.height; |
||||
|
console.log("Header实际高度:", headerHeight.value, "px"); |
||||
|
} |
||||
|
}) |
||||
|
.exec(); |
||||
|
}); |
||||
|
|
||||
|
// 监听headerHeight变化,重新计算contentHeight |
||||
|
watch(headerHeight, (newHeight) => { |
||||
|
if (newHeight > 0) { |
||||
|
const systemInfo = uni.getSystemInfoSync(); |
||||
|
const windowHeight = systemInfo.windowHeight; |
||||
|
const statusBarHeight = systemInfo.statusBarHeight || 0; |
||||
|
const footerHeight = 100; |
||||
|
|
||||
|
contentHeight.value = windowHeight - statusBarHeight - newHeight - footerHeight; |
||||
|
console.log("重新计算contentHeight:", contentHeight.value); |
||||
|
} |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.main { |
||||
|
width: 100%; |
||||
|
height: 100vh; |
||||
|
background-color: #f5f5f5; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
/* 自定义导航栏 */ |
||||
|
.header_fixed { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
background-color: #ffffff; |
||||
|
border-bottom: 1px solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.header-content { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
height: 44px; |
||||
|
padding: 0 15px; |
||||
|
} |
||||
|
|
||||
|
.header-left, |
||||
|
.header-right { |
||||
|
width: 60px; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.header-left { |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
|
||||
|
.header-right { |
||||
|
justify-content: flex-end; |
||||
|
gap: 10px; |
||||
|
} |
||||
|
|
||||
|
.back-text { |
||||
|
font-size: 24px; |
||||
|
color: #333333; |
||||
|
font-weight: 500; |
||||
|
line-height: 1; |
||||
|
} |
||||
|
|
||||
|
.header-center { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.header-title { |
||||
|
font-size: 18px; |
||||
|
font-weight: 600; |
||||
|
color: #333333; |
||||
|
} |
||||
|
|
||||
|
.header-icon { |
||||
|
width: 20px; |
||||
|
height: 20px; |
||||
|
} |
||||
|
|
||||
|
.more-text { |
||||
|
font-size: 20px; |
||||
|
color: #666666; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
/* 内容区域 */ |
||||
|
.content { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
/* 表头样式 */ |
||||
|
.table-header { |
||||
|
display: flex; |
||||
|
padding: 12px 15px; |
||||
|
background-color: #f8f9fa; |
||||
|
border-bottom: 1px solid #e9ecef; |
||||
|
} |
||||
|
|
||||
|
.header-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
cursor: pointer; |
||||
|
} |
||||
|
|
||||
|
.header-item.name-column { |
||||
|
flex: 2; |
||||
|
justify-content: flex-start; |
||||
|
} |
||||
|
|
||||
|
.header-item.price-column, |
||||
|
.header-item.change-column { |
||||
|
flex: 1; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.header-text { |
||||
|
font-size: 14px; |
||||
|
color: #666666; |
||||
|
font-weight: 500; |
||||
|
} |
||||
|
|
||||
|
.sort-icon { |
||||
|
margin-left: 4px; |
||||
|
font-size: 12px; |
||||
|
color: #999999; |
||||
|
} |
||||
|
|
||||
|
/* 股票列表 */ |
||||
|
.stock-list { |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
.stock-row { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 12px 15px; |
||||
|
border-bottom: 1px solid #f5f5f5; |
||||
|
} |
||||
|
|
||||
|
.stock-row:active { |
||||
|
background-color: #f8f8f8; |
||||
|
} |
||||
|
|
||||
|
.stock-cell { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.stock-cell.name-column { |
||||
|
flex: 2; |
||||
|
align-items: flex-start; |
||||
|
} |
||||
|
|
||||
|
.stock-cell.price-column, |
||||
|
.stock-cell.change-column { |
||||
|
flex: 1; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.stock-name { |
||||
|
font-size: 15px; |
||||
|
color: #333333; |
||||
|
font-weight: 500; |
||||
|
line-height: 1.2; |
||||
|
margin-bottom: 2px; |
||||
|
} |
||||
|
|
||||
|
.stock-code { |
||||
|
font-size: 11px; |
||||
|
color: #999999; |
||||
|
line-height: 1.2; |
||||
|
} |
||||
|
|
||||
|
.stock-price { |
||||
|
font-size: 15px; |
||||
|
font-weight: 600; |
||||
|
line-height: 1.2; |
||||
|
} |
||||
|
|
||||
|
.stock-change { |
||||
|
font-size: 13px; |
||||
|
font-weight: 500; |
||||
|
line-height: 1.2; |
||||
|
} |
||||
|
|
||||
|
.rising { |
||||
|
color: #00c851; |
||||
|
} |
||||
|
|
||||
|
.falling { |
||||
|
color: #ff4444; |
||||
|
} |
||||
|
|
||||
|
/* 底部安全区域 */ |
||||
|
/* .bottom-safe-area { |
||||
|
height: 20px; |
||||
|
} */ |
||||
|
|
||||
|
/* 底部导航栏 */ |
||||
|
/* .static-footer { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
} */ |
||||
|
</style> |
||||
1041
pages/marketSituation/marketOverview.vue
File diff suppressed because it is too large
View File
@ -0,0 +1,597 @@ |
|||||
|
<!-- @format --> |
||||
|
|
||||
|
<template> |
||||
|
<view> |
||||
|
<view class="main"> |
||||
|
<!-- 固定头部 --> |
||||
|
<view class="header_fixed" :style="{ top: iSMT + 'px' }"> |
||||
|
<view class="header_content"> |
||||
|
<view class="header_input_wrapper"> |
||||
|
<image class="search_icon" src="/static/marketSituation-image/search.png" mode="" @click="onSearchClick"></image> |
||||
|
<input class="header_input" type="text" placeholder="搜索" placeholder-style="color: #A6A6A6; font-size: 22rpx;" v-model="searchValue" @input="onSearchInput" @confirm="onSearchConfirm" /> |
||||
|
</view> |
||||
|
<view class="header_icons"> |
||||
|
<view class="header_icon" @click="selected"> |
||||
|
<image src="/static/marketSituation-image/mySeclected.png" mode=""></image> |
||||
|
</view> |
||||
|
<view class="header_icon" @click="history"> |
||||
|
<image src="/static/marketSituation-image/history.png" mode=""></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="channel_li" v-if="channelData.length > 0"> |
||||
|
<scroll-view class="channel_wrap" scroll-x="true" :scroll-into-view="scrollToView" :scroll-with-animation="true" show-scrollbar="false"> |
||||
|
<view class="channel_innerWrap"> |
||||
|
<view v-for="(item, index) in channelData" :key="item.id" :id="'nav' + item.id" :class="['channel_item', index === pageIndex ? 'active' : '']" @click="navClick(index)"> |
||||
|
<text class="channel_text">{{ item.title }}</text> |
||||
|
<view v-if="index === pageIndex" class="active_indicator"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</scroll-view> |
||||
|
<view class="scroll_indicator" @click="channel_more"> |
||||
|
<image src="/static/marketSituation-image/menu.png" mode="aspectFit"></image> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 可滚动内容区域 --> |
||||
|
<scroll-view class="content_scroll" scroll-y="true" :style="{ top: contentTopPosition + 'px' }"> |
||||
|
<!-- 动态组件切换 --> |
||||
|
<component :is="currentComponent" :countryId="currentChannelId" :marketTabs="getChildMarketTabs(currentChannelId)" :tabData="currentTabData"/> |
||||
|
</scroll-view> |
||||
|
</view> |
||||
|
|
||||
|
<footerBar class="static-footer" :type="type"></footerBar> |
||||
|
|
||||
|
<!-- 更多tab弹窗 --> |
||||
|
<view v-if="showCountryModal" class="modal_overlay" @click="closeModal"> |
||||
|
<view class="modal_content" @click.stop> |
||||
|
<view class="modal_header"> |
||||
|
<text class="modal_title">全部栏目</text> |
||||
|
<view class="modal_close" @click="closeModal"> |
||||
|
<text>×</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="modal_body"> |
||||
|
<view class="country_grid"> |
||||
|
<view v-for="(item, index) in channelData" :key="index" :class="['country_item', selectedCountry === item.title ? 'selected' : '']" @click="selectCountry(item.title)"> |
||||
|
<text class="country_text">{{ item.title }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
<LoginPrompt ref="loginPrompt"></LoginPrompt> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted, watch, nextTick, computed } from "vue"; |
||||
|
import footerBar from "../../components/footerBar.vue"; |
||||
|
import forexMetals from "./forexMetals.vue"; |
||||
|
import marketOverview from "./marketOverview.vue"; |
||||
|
import countryMarket from "./countryMarket.vue"; |
||||
|
import { getAllTabsAPI, queryStockDataAPI } from "../../api/marketSituation/marketSituation.js"; |
||||
|
const type = ref("marketSituation"); |
||||
|
const iSMT = ref(0); |
||||
|
const searchValue = ref(""); |
||||
|
const contentHeight = ref(0); |
||||
|
const headerHeight = ref(0); // 动态计算的header高度 |
||||
|
const currentTabData = ref(null); |
||||
|
|
||||
|
// Tab 栏相关数据 |
||||
|
const channelData = ref([{ id: 1, title: "概况" }]); |
||||
|
const pageIndex = ref(0); |
||||
|
const scrollToView = ref(""); |
||||
|
|
||||
|
// 动态组件相关 |
||||
|
const currentChannelId = computed(() => { |
||||
|
return channelData.value[pageIndex.value]?.id || 1; |
||||
|
}); |
||||
|
|
||||
|
const currentComponent = computed(() => { |
||||
|
const channelId = currentChannelId.value; |
||||
|
|
||||
|
// 概况页面使用 MarketOverview 组件 |
||||
|
if (pageIndex.value === 0) { |
||||
|
return marketOverview; |
||||
|
} |
||||
|
// 外汇(id=11)和贵金属(id=12)使用 ForexMetals 组件 |
||||
|
else if (channelId === 11 || channelId === 12) { |
||||
|
return forexMetals; |
||||
|
} |
||||
|
// 其他国家/地区页面使用 CountryMarket 组件 |
||||
|
else { |
||||
|
return countryMarket; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
// 计算属性:精准计算content区域的top值 |
||||
|
const contentTopPosition = computed(() => { |
||||
|
const statusBarHeight = iSMT.value || 0; |
||||
|
const currentHeaderHeight = headerHeight.value > 0 ? headerHeight.value : 140; |
||||
|
return statusBarHeight + currentHeaderHeight; |
||||
|
}); |
||||
|
|
||||
|
// 弹窗相关数据 |
||||
|
const showCountryModal = ref(false); |
||||
|
const selectedCountry = ref("概况"); |
||||
|
|
||||
|
const getAllTabs = async () => { |
||||
|
try { |
||||
|
const result = await getAllTabsAPI(); |
||||
|
channelData.value = result.data.map((item) => ({ ...item, id: item.id, title: item.tradeName })); |
||||
|
} catch (e) { |
||||
|
console.error("获取地区分组列表失败", e); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
const getChildMarketTabs = (id) => { |
||||
|
return channelData.value.filter((item) => item.id === id)[0]?.children?.map((child) => child.tradeName); |
||||
|
}; |
||||
|
|
||||
|
// 搜索输入事件 |
||||
|
const onSearchInput = (e) => { |
||||
|
searchValue.value = e.detail.value; |
||||
|
}; |
||||
|
|
||||
|
// 搜索确认事件 |
||||
|
const onSearchConfirm = (e) => { |
||||
|
console.log("搜索内容:", e.detail.value); |
||||
|
// 这里可以添加搜索逻辑 |
||||
|
performSearch(e.detail.value); |
||||
|
}; |
||||
|
|
||||
|
// 搜索图标点击事件 |
||||
|
const onSearchClick = () => { |
||||
|
if (searchValue.value.trim()) { |
||||
|
performSearch(searchValue.value); |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
// 执行搜索 |
||||
|
const performSearch = (keyword) => { |
||||
|
if (!keyword.trim()) { |
||||
|
uni.showToast({ |
||||
|
title: "请输入搜索内容", |
||||
|
icon: "none", |
||||
|
}); |
||||
|
return; |
||||
|
} |
||||
|
|
||||
|
uni.showToast({ |
||||
|
title: `搜索: ${keyword}`, |
||||
|
icon: "none", |
||||
|
}); |
||||
|
// 这里添加实际的搜索逻辑 |
||||
|
}; |
||||
|
|
||||
|
// 我的收藏点击事件 |
||||
|
const selected = () => { |
||||
|
uni.showToast({ |
||||
|
title: "我的收藏", |
||||
|
icon: "none", |
||||
|
}); |
||||
|
// 这里可以跳转到收藏页面 |
||||
|
}; |
||||
|
|
||||
|
// 历史记录点击事件 |
||||
|
const history = () => { |
||||
|
// 跳转到自定义股票列表页面 |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/customStockList/customStockList' |
||||
|
}) |
||||
|
}; |
||||
|
|
||||
|
// Tab 栏点击事件 |
||||
|
const navClick = (index) => { |
||||
|
pageIndex.value = index; |
||||
|
const currentItem = channelData.value[index]; |
||||
|
scrollToView.value = "nav" + currentItem.id; |
||||
|
|
||||
|
// 同步更新弹窗中的选中状态 |
||||
|
selectedCountry.value = currentItem.title; |
||||
|
|
||||
|
// 切换tab后,查询当前国家的股票数据 |
||||
|
queryStockDataAPI({ |
||||
|
parentId: currentItem.id, |
||||
|
tradeId: 1, |
||||
|
}).then((res) => { |
||||
|
if (res.code == 200) { |
||||
|
currentTabData.value = { |
||||
|
type: 'country', |
||||
|
data: res.data |
||||
|
}; |
||||
|
} |
||||
|
}); |
||||
|
// 这里可以添加切换 tab 后的数据加载逻辑 |
||||
|
console.log("当前选中的 tab:", currentItem); |
||||
|
}; |
||||
|
|
||||
|
// 更多选项点击事件 |
||||
|
const channel_more = () => { |
||||
|
showCountryModal.value = true; |
||||
|
}; |
||||
|
|
||||
|
// 选择国家 |
||||
|
const selectCountry = (country) => { |
||||
|
selectedCountry.value = country; |
||||
|
|
||||
|
// 查找对应的tab索引 |
||||
|
const targetIndex = channelData.value.findIndex((item) => item.title === country); |
||||
|
|
||||
|
if (targetIndex !== -1) { |
||||
|
// 同步更新页面tab |
||||
|
pageIndex.value = targetIndex; |
||||
|
const currentItem = channelData.value[targetIndex]; |
||||
|
scrollToView.value = "nav" + currentItem.id; |
||||
|
|
||||
|
console.log("选中了:" + country + ",同步到tab索引:" + targetIndex); |
||||
|
uni.showToast({ |
||||
|
title: "已切换到:" + country, |
||||
|
icon: "none", |
||||
|
duration: 2000, |
||||
|
}); |
||||
|
} else { |
||||
|
// 如果是"概况"或其他特殊选项,默认切换到第一个tab |
||||
|
if (country === "概况" || country === "全部") { |
||||
|
pageIndex.value = 0; |
||||
|
scrollToView.value = "nav" + channelData.value[0].id; |
||||
|
} |
||||
|
|
||||
|
console.log("选中了:" + country); |
||||
|
uni.showToast({ |
||||
|
title: "已选择:" + country, |
||||
|
icon: "none", |
||||
|
duration: 2000, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// 这里可以添加切换到对应国家/地区数据的逻辑 |
||||
|
// 例如:loadMarketData(country) |
||||
|
closeModal(); |
||||
|
}; |
||||
|
|
||||
|
// 关闭弹窗 |
||||
|
const closeModal = () => { |
||||
|
showCountryModal.value = false; |
||||
|
}; |
||||
|
|
||||
|
onMounted(async () => { |
||||
|
await getAllTabs(); |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
|
||||
|
// 初始化 tab 栏 |
||||
|
if (channelData.value.length > 0) { |
||||
|
pageIndex.value = 0; |
||||
|
scrollToView.value = "nav" + channelData.value[0].id; |
||||
|
} |
||||
|
|
||||
|
// 确保DOM渲染完成后再查询高度 |
||||
|
nextTick(() => { |
||||
|
// 动态计算header实际高度 |
||||
|
uni |
||||
|
.createSelectorQuery() |
||||
|
.select(".header_fixed") |
||||
|
.boundingClientRect((rect) => { |
||||
|
if (rect) { |
||||
|
headerHeight.value = rect.height; |
||||
|
console.log("Header实际高度:", headerHeight.value, "px"); |
||||
|
} |
||||
|
}) |
||||
|
.exec(); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
// 监听headerHeight变化,重新计算contentHeight |
||||
|
watch(headerHeight, (newHeight) => { |
||||
|
if (newHeight > 0) { |
||||
|
const systemInfo = uni.getSystemInfoSync(); |
||||
|
const windowHeight = systemInfo.windowHeight; |
||||
|
const statusBarHeight = systemInfo.statusBarHeight || 0; |
||||
|
const footerHeight = 100; |
||||
|
|
||||
|
contentHeight.value = windowHeight - statusBarHeight - newHeight - footerHeight; |
||||
|
console.log("重新计算contentHeight:", contentHeight.value); |
||||
|
} |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
/* 主容器样式调整 */ |
||||
|
.main { |
||||
|
position: relative; |
||||
|
height: 100vh; |
||||
|
overflow: hidden; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
/* 状态栏占位 */ |
||||
|
.top { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1001; |
||||
|
background-color: #ffffff; |
||||
|
} |
||||
|
|
||||
|
/* 固定头部样式 */ |
||||
|
.header_fixed { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
z-index: 1000; |
||||
|
background-color: #ffffff; |
||||
|
padding: 20rpx 0 0 0; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
/* 可滚动内容区域 */ |
||||
|
.content_scroll { |
||||
|
position: fixed; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 100rpx; |
||||
|
/* 底部导航栏高度 */ |
||||
|
overflow-y: auto; |
||||
|
} |
||||
|
|
||||
|
.header_content { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: space-between; |
||||
|
height: 80rpx; |
||||
|
padding: 0 20rpx; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.header_input_wrapper { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
width: 100%; |
||||
|
margin: 0 20rpx 0 0; |
||||
|
height: 70rpx; |
||||
|
border-radius: 35rpx; |
||||
|
background-color: #ffffff; |
||||
|
border: 1rpx solid #e9ecef; |
||||
|
padding: 0 80rpx 0 30rpx; |
||||
|
font-size: 28rpx; |
||||
|
color: #5c5c5c; |
||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1); |
||||
|
} |
||||
|
|
||||
|
.search_icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
opacity: 0.6; |
||||
|
} |
||||
|
|
||||
|
.header_input { |
||||
|
margin-left: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.header_icons { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 15rpx; |
||||
|
} |
||||
|
|
||||
|
.header_icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.header_icon image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
} |
||||
|
|
||||
|
/* Tab 栏样式 */ |
||||
|
.channel_li { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 80rpx; |
||||
|
background-color: #ffffff; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.channel_wrap { |
||||
|
width: calc(100% - 60rpx); |
||||
|
height: 100%; |
||||
|
overflow: hidden; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.channel_innerWrap { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 100%; |
||||
|
padding: 0 20rpx; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.channel_item { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 60rpx; |
||||
|
padding: 0 20rpx; |
||||
|
border-radius: 30rpx; |
||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); |
||||
|
cursor: pointer; |
||||
|
white-space: nowrap; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.channel_item:active { |
||||
|
transform: scale(0.98); |
||||
|
} |
||||
|
|
||||
|
.channel_item.active { |
||||
|
color: #333; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.channel_text { |
||||
|
font-size: 28rpx; |
||||
|
font-weight: 500; |
||||
|
color: #666666; |
||||
|
transition: color 0.3s ease; |
||||
|
white-space: nowrap; |
||||
|
} |
||||
|
|
||||
|
.channel_item.active .channel_text { |
||||
|
color: #333333; |
||||
|
font-weight: 400; |
||||
|
z-index: 2; |
||||
|
} |
||||
|
|
||||
|
.active_indicator { |
||||
|
position: absolute; |
||||
|
left: 50%; |
||||
|
top: 60%; |
||||
|
transform: translateX(-45%); |
||||
|
width: calc(100% - 20rpx); |
||||
|
min-width: 40rpx; |
||||
|
max-width: 120rpx; |
||||
|
height: 8rpx; |
||||
|
background-image: url("/static/marketSituation-image/bg.png"); |
||||
|
background-size: cover; |
||||
|
background-position: center; |
||||
|
background-repeat: no-repeat; |
||||
|
animation: slideIn 0.1s ease; |
||||
|
border-radius: 8rpx; |
||||
|
z-index: 1; |
||||
|
} |
||||
|
|
||||
|
@keyframes slideIn { |
||||
|
from { |
||||
|
width: 0; |
||||
|
opacity: 0; |
||||
|
} |
||||
|
|
||||
|
to { |
||||
|
width: 40rpx; |
||||
|
opacity: 1; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.scroll_indicator { |
||||
|
border-left: 1rpx solid #b6b6b6; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
width: 60rpx; |
||||
|
height: 30rpx; |
||||
|
background-color: #ffffff; |
||||
|
flex-shrink: 0; |
||||
|
} |
||||
|
|
||||
|
.scroll_indicator image { |
||||
|
width: 20rpx; |
||||
|
height: 20rpx; |
||||
|
opacity: 0.5; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
margin-top: 20rpx; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.static-footer { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
} |
||||
|
|
||||
|
/* 弹窗样式 */ |
||||
|
.modal_overlay { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
bottom: 0; |
||||
|
background-color: rgba(0, 0, 0, 0.5); |
||||
|
display: flex; |
||||
|
align-items: flex-end; |
||||
|
z-index: 1000; |
||||
|
} |
||||
|
|
||||
|
.modal_content { |
||||
|
width: 100%; |
||||
|
background-color: #fff; |
||||
|
border-radius: 20rpx 20rpx 0 0; |
||||
|
max-height: 80vh; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.modal_header { |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
padding: 30rpx 40rpx; |
||||
|
border-bottom: 1rpx solid #f0f0f0; |
||||
|
} |
||||
|
|
||||
|
.modal_title { |
||||
|
font-size: 32rpx; |
||||
|
font-weight: bold; |
||||
|
color: #333333; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.modal_close { |
||||
|
position: absolute; |
||||
|
right: 40rpx; |
||||
|
top: 50%; |
||||
|
transform: translateY(-50%); |
||||
|
width: 60rpx; |
||||
|
height: 60rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
font-size: 40rpx; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.modal_body { |
||||
|
padding: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.country_grid { |
||||
|
display: grid; |
||||
|
grid-template-columns: 1fr 1fr; |
||||
|
gap: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.country_item { |
||||
|
padding: 24rpx 30rpx; |
||||
|
border-radius: 12rpx; |
||||
|
background-color: #f8f8f8; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
transition: all 0.3s ease; |
||||
|
} |
||||
|
|
||||
|
.country_item.selected { |
||||
|
background-color: #ff4444; |
||||
|
color: #fff; |
||||
|
} |
||||
|
|
||||
|
.country_text { |
||||
|
font-size: 28rpx; |
||||
|
color: #333; |
||||
|
} |
||||
|
|
||||
|
.country_item.selected .country_text { |
||||
|
color: #fff; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,55 @@ |
|||||
|
<template> |
||||
|
<view class="container"> |
||||
|
<view class="content"> |
||||
|
<image |
||||
|
class="no-data-image" |
||||
|
src="https://d31zlh4on95l9h.cloudfront.net/images/f5a9bd32c81bc7cca47252b51357c12f.png" |
||||
|
mode="aspectFit" |
||||
|
></image> |
||||
|
<text class="no-data-text">暂无数据~</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
export default { |
||||
|
data() { |
||||
|
return { |
||||
|
|
||||
|
} |
||||
|
}, |
||||
|
methods: { |
||||
|
|
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.container { |
||||
|
width: 100%; |
||||
|
height: 100vh; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
background-color: #f5f5f5; |
||||
|
} |
||||
|
|
||||
|
.content { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.no-data-image { |
||||
|
width: 200px; |
||||
|
height: 200px; |
||||
|
margin-bottom: 20px; |
||||
|
} |
||||
|
|
||||
|
.no-data-text { |
||||
|
font-size: 16px; |
||||
|
color: #999999; |
||||
|
text-align: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,86 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
|
||||
|
<view style="height:1.5vh" /> |
||||
|
<view class="top"> |
||||
|
<img src="/static/my/aboutDC.png"></img> |
||||
|
</view> |
||||
|
|
||||
|
<view class="bottom"> |
||||
|
<view class="bottom-list" @click="goToIntroduce"> |
||||
|
<text class="label">产品介绍</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
<view class="bottom-list"> |
||||
|
<text class="label">免责声明</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
<view class="bottom-list"> |
||||
|
<text class="label">隐私政策</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
<view class="bottom-list"> |
||||
|
<text class="label">服务协议</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
<view class="bottom-list"> |
||||
|
<text class="label">鼓励一下</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
const iSMT = ref(0) |
||||
|
const goToIntroduce = () =>{ |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/introduce' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
height: 23vh; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.bottom { |
||||
|
height: 35vh; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.bottom-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
margin: 0 40rpx; |
||||
|
padding: 0 10rpx; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.bottom-list:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.label{ |
||||
|
flex:1; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,241 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
|
||||
|
<view style="height:1.5vh;"></view> |
||||
|
|
||||
|
<view class="setting-list"> |
||||
|
<view class="setting-item"> |
||||
|
<text class="item-label">头像</text> |
||||
|
<view class="item-right"> |
||||
|
<image src="/static/avatar.png" class="avatar" mode="aspectFill"></image> |
||||
|
<uni-icons type="arrowright" size="16"></uni-icons> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="setting-item"> |
||||
|
<text class="item-label">昵称</text> |
||||
|
<view class="item-right"> |
||||
|
<text class="item-text">{{userInfoRes.dcname}}</text> |
||||
|
<uni-icons type="arrowright" size="16"></uni-icons> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="setting-item"> |
||||
|
<text class="item-label">ID</text> |
||||
|
<view class="item-right"> |
||||
|
<text class="item-text">{{ userInfoRes.dccode }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="setting-item" @click="goToPassword"> |
||||
|
<text class="item-label"> |
||||
|
{{ userInfoRes.hasPwd === 0 ? '创建密码' : '修改密码' }} |
||||
|
</text> |
||||
|
<uni-icons type="arrowright" size="16"></uni-icons> |
||||
|
</view> |
||||
|
<view class="setting-item"> |
||||
|
<text class="item-label">注销账号</text> |
||||
|
<uni-icons type="arrowright" size="16"></uni-icons> |
||||
|
</view> |
||||
|
<view class="setting-item" @click="goToBind"> |
||||
|
<text class="item-label">绑定账号</text> |
||||
|
<uni-icons type="arrowright" size="16"></uni-icons> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view style="height:1.5vh;"></view> |
||||
|
|
||||
|
<view class="logout" @click="showLogout = true"> |
||||
|
<text>退出登录</text> |
||||
|
</view> |
||||
|
|
||||
|
<view class="logout-confirm" v-if="showLogout"> |
||||
|
<view class="logoutDialog"> |
||||
|
<view class="tips">是否退出登录</view> |
||||
|
<view class="tips-button"> |
||||
|
<button class="confirm-btn" @click="handleConfirmLogout">确认</button> |
||||
|
<button class="cancel-btn" @click="showLogout = false">取消</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import { |
||||
|
getUserInfo |
||||
|
} from "@/api/member" |
||||
|
import { |
||||
|
useUserStore |
||||
|
} from "../../stores/modules/userInfo" |
||||
|
|
||||
|
const userStore = useUserStore() |
||||
|
const iSMT = ref(0) |
||||
|
// const dccode = ref('') |
||||
|
const userInfoRes = ref({}) |
||||
|
const showLogout = ref(false) |
||||
|
|
||||
|
const userInfoPromise = getUserInfo() |
||||
|
userInfoPromise.then(res => { |
||||
|
if (res.code === 200) { |
||||
|
userInfoRes.value.dccode = res.data.dccode; |
||||
|
userInfoRes.value.dcname = res.data.dcname; |
||||
|
userInfoRes.value.hasPwd = res.data.hasPassword; |
||||
|
console.log('用户信息', userInfoRes.value) |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: '用户信息请求失败', |
||||
|
icon: 'none', |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
const handleConfirmLogout = () => { |
||||
|
userStore.clearUserInfo() |
||||
|
showLogout.value = false |
||||
|
uni.showToast({ |
||||
|
title: '退出登录成功', |
||||
|
icon: 'none', |
||||
|
}) |
||||
|
|
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/start/login/login' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToBind = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/bind' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToPassword = () => { |
||||
|
if (userInfoRes.value.hasPwd === 0) { |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/createPwd' |
||||
|
}) |
||||
|
} else { |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/password' |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.setting-list { |
||||
|
height: 42vh; |
||||
|
background-color: #fff; |
||||
|
} |
||||
|
|
||||
|
.setting-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 7vh; |
||||
|
padding: 0 40rpx; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.setting-item:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.item-label { |
||||
|
font-size: 28rpx; |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.item-right { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.avatar { |
||||
|
width: 60rpx; |
||||
|
height: 60rpx; |
||||
|
border-radius: 50%; |
||||
|
margin-right: 20rpx; |
||||
|
background-color: #999; |
||||
|
} |
||||
|
|
||||
|
.item-text { |
||||
|
margin-right: 20rpx; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.arrow, |
||||
|
.eye-icon { |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.logout { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
height: 7vh; |
||||
|
font-size: 28rpx; |
||||
|
color: #f56c6c; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.logout-confirm { |
||||
|
position: fixed; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
background-color: rgba(0, 0, 0, 0.5); |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
z-index: 999; |
||||
|
} |
||||
|
|
||||
|
.logoutDialog { |
||||
|
width: 500rpx; |
||||
|
background-color: #fff; |
||||
|
border-radius: 12rpx; |
||||
|
padding: 40rpx; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.tips { |
||||
|
font-size: 32rpx; |
||||
|
margin-bottom: 40rpx; |
||||
|
} |
||||
|
|
||||
|
.tips-button { |
||||
|
display: flex; |
||||
|
width: 100%; |
||||
|
justify-content: space-between; |
||||
|
} |
||||
|
|
||||
|
.confirm-btn, |
||||
|
.cancel-btn { |
||||
|
width: 180rpx; |
||||
|
height: 60rpx; |
||||
|
line-height: 60rpx; |
||||
|
border-radius: 30rpx; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.confirm-btn { |
||||
|
background-color: #fff; |
||||
|
color: #000; |
||||
|
border: 1rpx solid #ddd; |
||||
|
} |
||||
|
|
||||
|
.cancel-btn { |
||||
|
background-color: #000; |
||||
|
color: #fff; |
||||
|
border: none; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,92 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
|
||||
|
<view style="height:1.5vh;" /> |
||||
|
|
||||
|
<view class="top"> |
||||
|
<view class="top-list" @click="goToBindPhone"> |
||||
|
<text class="label">手机号</text> |
||||
|
<view class="right"> |
||||
|
<text style="font-size: 28rpx;"> |
||||
|
{{ userStore.userInfo?.phone || '未绑定' }} |
||||
|
</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="top-list" @click="goToBindEmail"> |
||||
|
<text class="label">邮箱</text> |
||||
|
<view class="right"> |
||||
|
<text style="font-size: 28rpx;"> |
||||
|
{{ userStore.userInfo?.email || '未绑定' }} |
||||
|
</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import { useUserStore } from "../../stores/modules/userInfo" |
||||
|
const userStore = useUserStore() |
||||
|
const iSMT = ref(0) |
||||
|
const goToBindPhone = () =>{ |
||||
|
uni.navigateTo({ |
||||
|
url:'../setting/phone' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToBindEmail = () =>{ |
||||
|
uni.navigateTo({ |
||||
|
url:'../setting/email' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
console.log('看看用户信息',userStore.userInfo) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
height: 14vh; |
||||
|
background-color: white; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.top-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
margin: 0rpx 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.top-list:last-child { |
||||
|
border: none; |
||||
|
} |
||||
|
|
||||
|
.label { |
||||
|
font-size: 28rpx; |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.right{ |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,257 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
|
||||
|
<view class="tab"> |
||||
|
<view class="tab-item" :class="{active: activeTab === 'email'}" @click="activeTab = 'email'">邮箱</view> |
||||
|
<view class="tab-item" :class="{active: activeTab === 'phone'}" @click="activeTab = 'phone'">手机号</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="switch-tab"> |
||||
|
<view class="input-list" v-if="activeTab === 'email'"> |
||||
|
<image src="/static/my/changeEmail.png" mode="aspectFit"></image> |
||||
|
<input type="text" placeholder="请输入邮箱" class="input" v-model="userEmail" /> |
||||
|
<button class="code-btn" :class="{disabled: gettingCode}" @click="getCode" :disabled="gettingCode"> |
||||
|
{{ gettingCode ? `重新发送 ${time}s` : '获取验证码' }} |
||||
|
</button> |
||||
|
</view> |
||||
|
|
||||
|
<view class="input-list" v-else> |
||||
|
<image src="/static/my/changeBindPhone.png" mode="aspectFit"></image> |
||||
|
<input type="number" placeholder="请输入手机号" class="input" v-model="userPhone" /> |
||||
|
<button class="code-btn" :class="{disabled: gettingCode}" @click="getCode" :disabled="gettingCode"> |
||||
|
{{ gettingCode ? `重新发送 ${time}s` : '获取验证码' }} |
||||
|
</button> |
||||
|
</view> |
||||
|
|
||||
|
<view class="input-list"> |
||||
|
<image src="/static/my/verification.png" mode="aspectFit"></image> |
||||
|
<input type="text" placeholder="请输入验证码" class="input" v-model="verifyCode" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="btn-area"> |
||||
|
<button class="next-btn" @click="goToPwdNext">下一步</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import { |
||||
|
sendEmail, |
||||
|
validateCode, |
||||
|
sendPhone |
||||
|
} from "@/api/setting/password"; |
||||
|
|
||||
|
const iSMT = ref(0) |
||||
|
const activeTab = ref('email') |
||||
|
const gettingCode = ref(false) |
||||
|
const time = ref(60) |
||||
|
|
||||
|
const userEmail = ref('') |
||||
|
const userPhone = ref('') |
||||
|
const verifyCode = ref('') |
||||
|
|
||||
|
|
||||
|
const getCode = () => { |
||||
|
if (gettingCode.value) return |
||||
|
gettingCode.value = true |
||||
|
|
||||
|
time.value = 2 |
||||
|
|
||||
|
const timer = setInterval(() => { |
||||
|
time.value-- |
||||
|
if (time.value <= 0) { |
||||
|
clearInterval(timer) |
||||
|
gettingCode.value = false |
||||
|
time.value = 2 |
||||
|
} |
||||
|
}, 1000) |
||||
|
if (activeTab.value === 'email') { |
||||
|
sendEmail({ |
||||
|
email: userEmail.value |
||||
|
}) |
||||
|
} else { |
||||
|
sendPhone({ |
||||
|
phone: userPhone.value |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
const goToPwdNext = async () => { |
||||
|
console.log('发请求之前的activeTab', activeTab.value) |
||||
|
if (activeTab.value === 'email') { |
||||
|
if (!userEmail.value) { |
||||
|
uni.showToast({ |
||||
|
title: '请输入邮箱', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
} else { |
||||
|
if (!userPhone.value) { |
||||
|
uni.showToast({ |
||||
|
title: '请输入手机号', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
if (!verifyCode.value) { |
||||
|
uni.showToast({ |
||||
|
title: '请输入验证码', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
|
||||
|
try { |
||||
|
let param; |
||||
|
if (activeTab.value === 'email') { |
||||
|
param = { |
||||
|
loginType: 'EMAIL', |
||||
|
account: userEmail.value, |
||||
|
verifyCode: verifyCode.value |
||||
|
} |
||||
|
} else { |
||||
|
param = { |
||||
|
loginType: 'PHONE', |
||||
|
account: userPhone.value, |
||||
|
verifyCode: verifyCode.value |
||||
|
} |
||||
|
} |
||||
|
const res = await validateCode(param) |
||||
|
console.log('看看参数', param) |
||||
|
console.log('看看结果', res) |
||||
|
// 如果返回成功 |
||||
|
if (res.code === 200) { |
||||
|
uni.showToast({ |
||||
|
title: '验证成功', |
||||
|
icon: 'success' |
||||
|
}) |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/nextPwd' |
||||
|
}) |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: res.msg || '验证失败', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error(err) |
||||
|
uni.showToast({ |
||||
|
title: '请求出错', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 获取状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.tab { |
||||
|
display: flex; |
||||
|
height: 8vh; |
||||
|
background-color: #fff; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.tab-item { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
font-size: 32rpx; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.tab-item.active { |
||||
|
color: #000; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.tab-item.active::after { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
bottom: 0; |
||||
|
left: 50%; |
||||
|
transform: translateX(-50%); |
||||
|
width: 40rpx; |
||||
|
height: 6rpx; |
||||
|
background-color: #000; |
||||
|
/* ????? */ |
||||
|
} |
||||
|
|
||||
|
.switch-tab { |
||||
|
background-color: #fff; |
||||
|
padding: 0 60rpx; |
||||
|
} |
||||
|
|
||||
|
.input-list { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 7vh; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.input-list image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
margin-right: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
flex: 1; |
||||
|
height: 14vh; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.code-btn { |
||||
|
width: 200rpx; |
||||
|
height: 60rpx; |
||||
|
font-size: 24rpx; |
||||
|
border-radius: 10rpx; |
||||
|
background-color: #eee; |
||||
|
color: #666; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.code-btn.disabled { |
||||
|
background-color: #ccc; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.btn-area { |
||||
|
height: 8vh; |
||||
|
background-color: white; |
||||
|
padding-top: 120rpx; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.next-btn { |
||||
|
width: 610rpx; |
||||
|
height: 85rpx; |
||||
|
background-color: #000; |
||||
|
color: #fff; |
||||
|
font-size: 30rpx; |
||||
|
border-radius: 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,185 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
|
||||
|
<view style="height:1.5vh;" /> |
||||
|
|
||||
|
<view class="top"> |
||||
|
<view class="top-list"> |
||||
|
<view class="left"> |
||||
|
<img src="/static/my/bindedEmail.png" /> |
||||
|
<text class="label">已绑邮箱:{{ email }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="top-list"> |
||||
|
<view class="left"> |
||||
|
<img src="/static/my/changeEmail.png" /> |
||||
|
<input v-model="userEmail" placeholder="请输入您的换绑邮箱" class="input" /> |
||||
|
</view> |
||||
|
<view class="right"> |
||||
|
<button class="verification" :class="{ 'disabled': gettingCode }" @click="getCode" |
||||
|
:disabled="gettingCode"> |
||||
|
{{ gettingCode ? `重新发送 ${time}s` : '获取验证码' }} |
||||
|
</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="top-list"> |
||||
|
<view class="left"> |
||||
|
<img src="/static/my/verification.png" /> |
||||
|
<input type="text" placeholder="请输入验证码" class="input" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="bottom"> |
||||
|
<button class="change-btn" @click="changeAccount">换绑</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import { |
||||
|
getUserInfo |
||||
|
} from "@/api/member" |
||||
|
import { |
||||
|
sendEmail, |
||||
|
changeBind |
||||
|
} from "@/api/setting/password" |
||||
|
const iSMT = ref(0) |
||||
|
const email = ref('') |
||||
|
const gettingCode = ref(false) |
||||
|
const time = ref(60) |
||||
|
const userEmail = ref('') |
||||
|
|
||||
|
const userInfoPromise = getUserInfo() |
||||
|
userInfoPromise.then(res => { |
||||
|
if (res.code === 200) { |
||||
|
console.log('个人信息', res.data) |
||||
|
email.value = res.data.email |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: '用户信息请求失败', |
||||
|
icon: 'none', |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const changeAccount = () => { |
||||
|
const res = changeBind({ |
||||
|
verificateType: 0, |
||||
|
account: userEmail.value |
||||
|
}) |
||||
|
if(res.code === 200){ |
||||
|
uni.showToast({ |
||||
|
title: '绑定成功', |
||||
|
icon: 'none', |
||||
|
}) |
||||
|
}else { |
||||
|
uni.showToast({ |
||||
|
title: '用户绑定失败', |
||||
|
icon: 'none', |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const getCode = () => { |
||||
|
if (gettingCode.value) return |
||||
|
gettingCode.value = true |
||||
|
|
||||
|
time.value = 2 |
||||
|
|
||||
|
const timer = setInterval(() => { |
||||
|
time.value-- |
||||
|
if (time.value <= 0) { |
||||
|
clearInterval(timer) |
||||
|
gettingCode.value = false |
||||
|
time.value = 2 |
||||
|
} |
||||
|
}, 1000) |
||||
|
sendEmail({ |
||||
|
email: userEmail.value |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
height: auto; |
||||
|
background-color: white; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.top-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
margin: 0rpx 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.left { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.label { |
||||
|
font-size: 28rpx; |
||||
|
margin-left: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.right { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
flex: 1; |
||||
|
height: 70rpx; |
||||
|
font-size: 29rpx; |
||||
|
margin-left: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.verification { |
||||
|
font-size: 24rpx; |
||||
|
border-radius: 10rpx; |
||||
|
background-color: rgb(230, 230, 230); |
||||
|
} |
||||
|
|
||||
|
.bottom { |
||||
|
height: 22vh; |
||||
|
background-color: white; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.change-btn { |
||||
|
height: 85rpx; |
||||
|
width: 610rpx; |
||||
|
padding: 0 20rpx; |
||||
|
background-color: black; |
||||
|
color: white; |
||||
|
border-radius: 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,108 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
<view class="top"> |
||||
|
<view class="top-list"> |
||||
|
<text>标准</text> |
||||
|
<radio value="0" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 0" |
||||
|
@click="selectFont('small')" /> |
||||
|
</view> |
||||
|
<view class="top-list"> |
||||
|
<text>中号</text> |
||||
|
<radio value="1" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 1" |
||||
|
@click="selectFont('medium')" /> |
||||
|
</view> |
||||
|
<view class="top-list"> |
||||
|
<text>大号</text> |
||||
|
<radio value="2" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 2" |
||||
|
@click="selectFont('large')" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import { |
||||
|
getSetting, |
||||
|
updateSetting |
||||
|
} from "@/api/setting/general" |
||||
|
const iSMT = ref(0) |
||||
|
const selectedIndex = ref(0) |
||||
|
const fontTypeMap = { |
||||
|
'small': 0, |
||||
|
'medium': 1, |
||||
|
'large': 2 |
||||
|
} |
||||
|
|
||||
|
const getFont = async () => { |
||||
|
try { |
||||
|
const res = await getSetting() |
||||
|
if (res.code === 200) { |
||||
|
const fontSize = res.data.fontSize |
||||
|
selectedIndex.value = fontTypeMap[fontSize] ?? 0 |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error("获取字体设置失败:", err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const selectFont = async (fontType) => { |
||||
|
try { |
||||
|
selectedIndex.value = fontTypeMap[fontType] |
||||
|
console.log('字体类型:', fontType, ',looklook索引:', selectedIndex.value) |
||||
|
|
||||
|
const updateRes = await updateSetting({ |
||||
|
fontSize: fontType |
||||
|
}) |
||||
|
if (updateRes.code === 200) { |
||||
|
uni.showToast({ |
||||
|
title: '字体大小设置成功', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error("更新字体设置失败:", err) |
||||
|
uni.showToast({ |
||||
|
title: '设置失败,请重试', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
getFont() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
margin-top: 1.5vh; |
||||
|
height: 21vh; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.top-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0 40rpx; |
||||
|
padding: 0 10rpx; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.top-list:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.radio-btn { |
||||
|
margin-left: auto; |
||||
|
transform: scale(0.6); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,186 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
<view class="top"> |
||||
|
<view class="top-list"> |
||||
|
<text class="label">语言</text> |
||||
|
<text class="language">{{settingRes.language}}</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
<view class="top-list" @click="goToFont"> |
||||
|
<text class="label">字体大小</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
<view class="top-list" @click="goToTheme"> |
||||
|
<text class="label">主题切换</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
|
||||
|
<view class="center"> |
||||
|
<view class="center-list" @click="goToMessage"> |
||||
|
<text class="label">消息推送</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="bottom"> |
||||
|
<view class="bottom-list" @click="goToServer"> |
||||
|
<text class="label">切换服务器</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
<view class="bottom-list" @click="clearCache"> |
||||
|
<text class="label">清理缓存</text> |
||||
|
<text class="cache">{{ cache }}M</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import {getUserInfo} from "@/api/member"; |
||||
|
const iSMT = ref(0) |
||||
|
const cache = ref('45.5') |
||||
|
|
||||
|
const settingRes = ref({}) |
||||
|
|
||||
|
const settingPromise = getUserInfo() |
||||
|
settingPromise.then(res => { |
||||
|
if (res.code === 200){ |
||||
|
settingRes.value.language = res.data.language; |
||||
|
settingRes.value.fontSize = res.data.fontSize; |
||||
|
settingRes.value.theme = res.data.theme; |
||||
|
settingRes.value.serverSelection = res.data.serverSelection; |
||||
|
console.log('用户信息', res.data) |
||||
|
}else { |
||||
|
console.log('用户信息请求失败:', res.message); |
||||
|
} |
||||
|
|
||||
|
}) |
||||
|
|
||||
|
const goToFont = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/setting/font' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToTheme = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/setting/theme' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToMessage = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/setting/message' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const goToServer = () => { |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/setting/server' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const clearCache = () => { |
||||
|
cache.value = 0 |
||||
|
uni.showToast({ |
||||
|
title: '清理成功', |
||||
|
icon: 'success', |
||||
|
duration: 1500 |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
margin-top: 1.5vh; |
||||
|
height: 21vh; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.top-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0 40rpx; |
||||
|
padding: 0 10rpx; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.top-list:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.language { |
||||
|
margin-left: 70%; |
||||
|
font-size: 14px; |
||||
|
color: rgb(203, 203, 203); |
||||
|
} |
||||
|
|
||||
|
.arrow { |
||||
|
margin-left: auto; |
||||
|
} |
||||
|
|
||||
|
.center { |
||||
|
background-color: white; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin-top: 1vh; |
||||
|
} |
||||
|
|
||||
|
.center-list { |
||||
|
width: 630rpx; |
||||
|
margin: 0rpx 40rpx; |
||||
|
display: flex; |
||||
|
padding: 0 10rpx; |
||||
|
} |
||||
|
|
||||
|
.center-list > .arrow { |
||||
|
margin-right: 0; |
||||
|
} |
||||
|
|
||||
|
.bottom { |
||||
|
height: 13.5vh; |
||||
|
background-color: white; |
||||
|
margin-top: 1vh; |
||||
|
} |
||||
|
|
||||
|
.bottom-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0 40rpx; |
||||
|
padding: 0 10rpx; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.cache { |
||||
|
font-size: 14px; |
||||
|
color: rgb(203, 203, 203); |
||||
|
} |
||||
|
|
||||
|
.bottom-list:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.label{ |
||||
|
flex:1; |
||||
|
font-size:28rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,76 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
|
||||
|
<view style="height:1.5vh" /> |
||||
|
<view class="top"> |
||||
|
<img src="/static/my/aboutDC.png"></img> |
||||
|
</view> |
||||
|
|
||||
|
<view class="bottom"> |
||||
|
<view class="title">1.产品定位</view> |
||||
|
<view class="main-text">DeepChart:全球最懂机构行为的AI(你的AI投资伙伴)强化"深度分析" |
||||
|
的品牌标签(DeepChart=全球最懂机构行为的AI),主打"深度解读机构行为"的APP。</view> |
||||
|
|
||||
|
<view class="title">2.产品介绍</view> |
||||
|
<view class="main-text">DeepChart是一款以"Al智能体"为决策核心的智能投资分析平台, |
||||
|
专注于深度研究机构行为,专为全球散户投资者量身打造。它重新定义了人与投资工具之间的关系, |
||||
|
是一个真正懂投资、懂市场、更懂用户的AI投资伙伴。</view> |
||||
|
|
||||
|
<view class="title">3.产品理念</view> |
||||
|
<view class="main-text">从“人找信息”到“AI智能体替你思考和管理”。</view> |
||||
|
|
||||
|
<view class="title">4.功能定位——全景AI决策体系</view> |
||||
|
<view class="main-text">黄其振是大笨蛋</view> |
||||
|
<view class="main-text">李建霖是大笨蛋</view> |
||||
|
<view class="main-text">double是大笨蛋</view> |
||||
|
<view class="main-text">张鲁平是大笨蛋</view> |
||||
|
<view style="height:1.5vh;background-color: white;" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
const iSMT = ref(0) |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
height: 26vh; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.bottom { |
||||
|
height: 35vh; |
||||
|
padding: 0 60rpx; |
||||
|
background-color: white; |
||||
|
height: auto; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
font-size: 30rpx; |
||||
|
font-weight: bold; |
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.main-text { |
||||
|
font-size: 27rpx; |
||||
|
margin-bottom: 20rpx; |
||||
|
color: rgb(122, 122, 122); |
||||
|
text-align: justify; |
||||
|
text-justify: inter-character;/* 两端对齐哈哈哈哈 */ |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,348 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
|
||||
|
<view class="time-share-title"> |
||||
|
<text>分时设计</text> |
||||
|
</view> |
||||
|
<view style="height:57.5vh;background-color: white;"> |
||||
|
<view class="title">A股竞价</view> |
||||
|
<view class="top-options"> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': aStockBid === 'auto' }" |
||||
|
@click="handleAStockBidChange('auto')" |
||||
|
> |
||||
|
<text>智能开启</text> |
||||
|
<view class="active-dot" v-if="aStockBid === 'auto'"></view> |
||||
|
</view> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': aStockBid === 'open' }" |
||||
|
@click="handleAStockBidChange('open')" |
||||
|
> |
||||
|
<text>保持开启</text> |
||||
|
<view class="active-dot" v-if="aStockBid === 'open'"></view> |
||||
|
</view> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': aStockBid === 'close' }" |
||||
|
@click="handleAStockBidChange('close')" |
||||
|
> |
||||
|
<text>保持关闭</text> |
||||
|
<view class="active-dot" v-if="aStockBid === 'close'"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="title">K线样式</view> |
||||
|
<view class="top-options"> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': kStyle === 'common' }" |
||||
|
@click="handleKStyleChange('common')" |
||||
|
> |
||||
|
<img src="/static/my/common.png" class="kline-icon" /> |
||||
|
<text>普通</text> |
||||
|
<view class="active-dot" v-if="kStyle === 'common'"></view> |
||||
|
</view> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': kStyle === 'Outline' }" |
||||
|
@click="handleKStyleChange('Outline')" |
||||
|
> |
||||
|
<img src="/static/my/outline.png" class="kline-icon" /> |
||||
|
<text>轮廓图</text> |
||||
|
<view class="active-dot" v-if="kStyle === 'Outline'"></view> |
||||
|
</view> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': kStyle === 'polylines' }" |
||||
|
@click="handleKStyleChange('polylines')" |
||||
|
> |
||||
|
<img src="/static/my/polylines.png" class="kline-icon" /> |
||||
|
<text>折线图</text> |
||||
|
<view class="active-dot" v-if="kStyle === 'polylines'"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="title">除权类型</view> |
||||
|
<view class="top-options"> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': exRights === 'exRights' }" |
||||
|
@click="handleExRightsChange('exRights')" |
||||
|
> |
||||
|
<text>除权</text> |
||||
|
<view class="active-dot" v-if="exRights === 'exRights'"></view> |
||||
|
</view> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': exRights === 'normal' }" |
||||
|
@click="handleExRightsChange('normal')" |
||||
|
> |
||||
|
<text>普通</text> |
||||
|
<view class="active-dot" v-if="exRights === 'normal'"></view> |
||||
|
</view> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': exRights === 'Weighted' }" |
||||
|
@click="handleExRightsChange('Weighted')" |
||||
|
> |
||||
|
<text>加权</text> |
||||
|
<view class="active-dot" v-if="exRights === 'Weighted'"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="title">涨跌颜色</view> |
||||
|
<view class="top-options"> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': rfColor === 'green' }" |
||||
|
@click="handleRfColorChange('green')" |
||||
|
> |
||||
|
<view class="color-icon"> |
||||
|
<img src="/static/my/greenRise.png" class="kline-icon" /> |
||||
|
</view> |
||||
|
<text>绿涨红跌</text> |
||||
|
<view class="active-dot" v-if="rfColor === 'green'"></view> |
||||
|
</view> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': rfColor === 'red' }" |
||||
|
@click="handleRfColorChange('red')" |
||||
|
> |
||||
|
<view class="color-icon"> |
||||
|
<img src="/static/my/redRise.png" class="kline-icon" /> |
||||
|
</view> |
||||
|
<text>红涨绿跌</text> |
||||
|
<view class="active-dot" v-if="rfColor === 'red'"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="title">副图指标个数</view> |
||||
|
<view class="top-options"> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': indexCount === 1 }" |
||||
|
@click="handleIndexCountChange(1)" |
||||
|
> |
||||
|
<text>1</text> |
||||
|
</view> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': indexCount === 2 }" |
||||
|
@click="handleIndexCountChange(2)" |
||||
|
> |
||||
|
<text>2</text> |
||||
|
</view> |
||||
|
<view |
||||
|
class="option-btn" |
||||
|
:class="{ 'active': indexCount === 3 }" |
||||
|
@click="handleIndexCountChange(3)" |
||||
|
> |
||||
|
<text>3</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="indicator-title"> |
||||
|
<text>指标设置</text> |
||||
|
</view> |
||||
|
<view class="indicator-list"> |
||||
|
<view class="indicator-item" v-for="(item, index) in indicatorList" :key="index"> |
||||
|
<text class="indicator-text">{{ item }}</text> |
||||
|
<view class="indicator-icons"> |
||||
|
<img src="/static/my/setting.png" class="icon" /> |
||||
|
<img src="/static/my/menu.png" class="icon" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view style="height:10vh;background-color: white;"></view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { ref, onMounted } from 'vue' |
||||
|
import { getMarketSetting, updateMarketSetting } from "@/api/setting/market" |
||||
|
|
||||
|
const iSMT = ref(0) |
||||
|
const aStockBid = ref('auto') // A股竞价:auto/open/close |
||||
|
const kStyle = ref('common') // K线样式:common/Outline/polylines |
||||
|
const exRights = ref('exRights') // 除权类型:exRights/normal/Weighted |
||||
|
const rfColor = ref('green') // 涨跌颜色:green/red |
||||
|
const indexCount = ref(1) // 副图个数:1/2/3 |
||||
|
const indicatorList = ref(['K线', '均线', '成交量', 'KDJ', 'MACD', 'RSI']) |
||||
|
|
||||
|
const getMarketSettings = async () => { |
||||
|
try { |
||||
|
const res = await getMarketSetting() |
||||
|
if (res.code === 200) { |
||||
|
aStockBid.value = res.data.auctionDisplay ?? 'auto' |
||||
|
kStyle.value = res.data.klineStyle ?? 'common' |
||||
|
exRights.value = res.data.rightsIssueType ?? 'exRights' |
||||
|
rfColor.value = res.data.priceColorScheme ?? 'green' |
||||
|
indexCount.value = res.data.subChartCount ?? 1 |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error("获取市场设置失败:", err) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const updateSetting = async () => { |
||||
|
try { |
||||
|
const params = { |
||||
|
auctionDisplay: aStockBid.value, |
||||
|
klineStyle: kStyle.value, |
||||
|
rightsIssueType: exRights.value, |
||||
|
priceColorScheme: rfColor.value, |
||||
|
subChartCount: indexCount.value |
||||
|
} |
||||
|
const res = await updateMarketSetting(params) |
||||
|
if (res.code === 200) { |
||||
|
uni.showToast({ title: '设置已更新', icon: 'none' }) |
||||
|
} else { |
||||
|
uni.showToast({ title: '更新失败', icon: 'none' }) |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error("更新设置失败:", err) |
||||
|
uni.showToast({ title: '更新失败', icon: 'none' }) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const handleAStockBidChange = (newValue) => { |
||||
|
if (newValue !== aStockBid.value) { |
||||
|
aStockBid.value = newValue |
||||
|
updateSetting() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const handleKStyleChange = (newValue) => { |
||||
|
if (newValue !== kStyle.value) { |
||||
|
kStyle.value = newValue |
||||
|
updateSetting() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const handleExRightsChange = (newValue) => { |
||||
|
if (newValue !== exRights.value) { |
||||
|
exRights.value = newValue |
||||
|
updateSetting() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const handleRfColorChange = (newValue) => { |
||||
|
if (newValue !== rfColor.value) { |
||||
|
rfColor.value = newValue |
||||
|
updateSetting() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const handleIndexCountChange = (newValue) => { |
||||
|
if (newValue !== indexCount.value) { |
||||
|
indexCount.value = newValue |
||||
|
updateSetting() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
getMarketSettings() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.time-share-title { |
||||
|
height: 4.5vh; |
||||
|
padding: 0 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.title { |
||||
|
height: 5.5vh; |
||||
|
padding: 0 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
font-size: 26rpx; |
||||
|
color: #666; |
||||
|
} |
||||
|
|
||||
|
.top-options { |
||||
|
height: 5.5vh; |
||||
|
display: flex; |
||||
|
padding: 0 40rpx; |
||||
|
} |
||||
|
|
||||
|
.option-btn { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
border: 1rpx solid #ddd; |
||||
|
border-radius: 8rpx; |
||||
|
margin: 0 10rpx; |
||||
|
padding: 15rpx 0; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.option-btn.active { |
||||
|
border-color: red; |
||||
|
} |
||||
|
|
||||
|
.active-dot { |
||||
|
width: 16rpx; |
||||
|
height: 16rpx; |
||||
|
background-color: red; |
||||
|
border-radius: 50%; |
||||
|
margin-left: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.kline-icon { |
||||
|
margin-right: 10rpx; |
||||
|
font-size: 32rpx; |
||||
|
} |
||||
|
|
||||
|
.color-icon { |
||||
|
margin-right: 10rpx; |
||||
|
display: flex; |
||||
|
gap: 4rpx; |
||||
|
} |
||||
|
|
||||
|
.indicator-title { |
||||
|
height: 6vh; |
||||
|
padding: 0 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.indicator-list { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
padding: 0 40rpx; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.indicator-item { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
height: 7.5vh; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.indicator-text { |
||||
|
font-size: 28rpx; |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.indicator-icons { |
||||
|
display: flex; |
||||
|
gap: 100rpx; |
||||
|
margin-left: auto; |
||||
|
} |
||||
|
|
||||
|
.icon { |
||||
|
width: 28rpx; |
||||
|
height: 28rpx; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,61 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
<view class="top"> |
||||
|
<view class="top-list" @click="goToPush"> |
||||
|
<text class="text">消息推送</text> |
||||
|
<text class="message" v-if="isMessage">通知已开启</text> |
||||
|
<text class="message" v-if="!isMessage">通知未开启</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
const iSMT = ref(0) |
||||
|
const isMessage = ref(true) |
||||
|
|
||||
|
const goToPush = () =>{ |
||||
|
uni.navigateTo({ |
||||
|
url: '/pages/setting/push' |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
margin-top: 1.5vh; |
||||
|
height: 7vh; |
||||
|
background-color: white; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.top-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0rpx 40rpx; |
||||
|
} |
||||
|
|
||||
|
.message { |
||||
|
font-size: 14px; |
||||
|
color: rgb(203, 203, 203); |
||||
|
} |
||||
|
|
||||
|
.text{ |
||||
|
flex:1; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,82 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
<view style="height:1.5vh;" /> |
||||
|
<view class="top"> |
||||
|
<view class="top-list"> |
||||
|
<text v-if="hasNew === true" class="label">已有新版本</text> |
||||
|
<text v-if="hasNew === false" class="label">已是最新版本</text> |
||||
|
<view class="right"> |
||||
|
<text style="font-size: 28rpx;">{{ version }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view style="height:1vh;" /> |
||||
|
|
||||
|
<view class="bottom"> |
||||
|
<button v-if="hasNew === true" class="bottom-btn">立即更新</button> |
||||
|
<button v-if="hasNew === false" class="bottom-btn" disabled |
||||
|
style="background-color: rgb(204,204,204);color:white;">暂无更新</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
const iSMT = ref(0) |
||||
|
const hasNew = ref(true) |
||||
|
const version = ref('2.0') |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
height: 7vh; |
||||
|
background-color: white; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.top-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0rpx 40rpx; |
||||
|
} |
||||
|
|
||||
|
.label { |
||||
|
font-size: 28rpx; |
||||
|
flex: 1; |
||||
|
} |
||||
|
|
||||
|
.bottom { |
||||
|
height: 11vh; |
||||
|
background-color: white; |
||||
|
padding: 0 50rpx; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.bottom-btn { |
||||
|
width: 670rpx; |
||||
|
height: 84rpx; |
||||
|
border-radius: 40rpx; |
||||
|
background-color: #000; |
||||
|
color: #fff; |
||||
|
font-size: 28rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,175 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
|
||||
|
<view style="height:1.5vh;"/> |
||||
|
|
||||
|
<view class="title"> |
||||
|
<text class="label">确认新密码</text> |
||||
|
</view> |
||||
|
|
||||
|
<view class="top"> |
||||
|
|
||||
|
|
||||
|
<view class="top-list"> |
||||
|
<view class="left"> |
||||
|
<img src="/static/my/unlock.png"/> |
||||
|
<input type="password" :type="pwdType" placeholder="请输入新密码" class="input" v-model="oldPassword" |
||||
|
/> |
||||
|
<img :src="pwdType === 1 ? '/static/my/hideEye.png' : '/static/my/openEye.png'" |
||||
|
@click="changeEye(1)"/> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="top-list"> |
||||
|
<view class="left"> |
||||
|
<img src="/static/my/unlock.png"/> |
||||
|
<input type="password" :type="pwdType2" placeholder="再次确认" class="input" v-model="newPassword"/> |
||||
|
<img :src="pwdType === 1 ? '/static/my/hideEye.png' : '/static/my/openEye.png'" |
||||
|
@click="changeEye(2)"/> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<text class="tips">密码最少8位数</text> |
||||
|
</view> |
||||
|
|
||||
|
<view class="bottom"> |
||||
|
<button class="change-btn" @click="confirmChange">确认</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import {onMounted, ref} from 'vue' |
||||
|
import {updatePassword} from "@/api/setting/nextPwd"; |
||||
|
|
||||
|
const iSMT = ref(0) |
||||
|
const pwdType = ref('password') |
||||
|
const pwdType2 = ref('password') |
||||
|
|
||||
|
// 绑定的数据 旧密码 新密码 确认密码 |
||||
|
const oldPassword = ref('') |
||||
|
const newPassword = ref('') |
||||
|
|
||||
|
// 点击确认按钮 |
||||
|
const confirmChange = async () => { |
||||
|
|
||||
|
if (newPassword.value !== oldPassword.value) { |
||||
|
uni.showToast({title: '两次输入的密码不一致', icon: 'none'}) |
||||
|
return |
||||
|
} |
||||
|
const updatePasswordPromise = updatePassword({ |
||||
|
oldPassword: oldPassword.value, |
||||
|
newPassword: newPassword.value |
||||
|
}) |
||||
|
updatePasswordPromise |
||||
|
.then(res => { |
||||
|
if (res.code === 200) { |
||||
|
uni.showToast({ title: '修改成功', icon: 'success' }); |
||||
|
} else { |
||||
|
uni.showToast({ title: res.message,icon: 'none' }); |
||||
|
} |
||||
|
}) |
||||
|
.catch(err => { |
||||
|
console.log('修改密码失败:', err); |
||||
|
}); |
||||
|
|
||||
|
} |
||||
|
|
||||
|
|
||||
|
const changeEye = (type) => { |
||||
|
if (type === 1) { |
||||
|
pwdType.value = pwdType.value === 'password' ? 'text' : 'password' |
||||
|
} else { |
||||
|
pwdType2.value = pwdType2.value === 'password' ? 'text' : 'password' |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.title { |
||||
|
height: 8.5vh; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.label { |
||||
|
height: 8.5vh; |
||||
|
font-size: 40rpx; |
||||
|
font-weight: bold; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
padding: 0 60rpx; |
||||
|
} |
||||
|
|
||||
|
.top { |
||||
|
height: auto; |
||||
|
background-color: white; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.top-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
margin: 0rpx 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.left { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
flex: 1; |
||||
|
height: 70rpx; |
||||
|
font-size: 29rpx; |
||||
|
margin-left: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.bottom { |
||||
|
height: 22vh; |
||||
|
background-color: white; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.change-btn { |
||||
|
height: 85rpx; |
||||
|
width: 610rpx; |
||||
|
padding: 0 20rpx; |
||||
|
background-color: black; |
||||
|
color: white; |
||||
|
border-radius: 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.img { |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
top: 50%; |
||||
|
transform: translateY(-50%); |
||||
|
} |
||||
|
|
||||
|
.tips { |
||||
|
font-size: 24rpx; |
||||
|
color: #999; |
||||
|
margin-top: 20rpx; |
||||
|
margin-left: 60rpx; |
||||
|
align-self: flex-start; |
||||
|
/* 这是左对齐 */ |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,255 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
|
||||
|
<view class="tab"> |
||||
|
<view class="tab-item" :class="{active: activeTab === 'email'}" @click="activeTab = 'email'">邮箱</view> |
||||
|
<view class="tab-item" :class="{active: activeTab === 'phone'}" @click="activeTab = 'phone'">手机号</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="switch-tab"> |
||||
|
<view class="input-list" v-if="activeTab === 'email'"> |
||||
|
<image src="/static/my/changeEmail.png" mode="aspectFit"></image> |
||||
|
<input type="text" placeholder="请输入邮箱" class="input" v-model="userEmail" /> |
||||
|
<button class="code-btn" :class="{disabled: gettingCode}" @click="getCode" :disabled="gettingCode"> |
||||
|
{{ gettingCode ? `重新发送 ${time}s` : '获取验证码' }} |
||||
|
</button> |
||||
|
</view> |
||||
|
|
||||
|
<view class="input-list" v-else> |
||||
|
<image src="/static/my/changeBindPhone.png" mode="aspectFit"></image> |
||||
|
<input type="number" placeholder="请输入手机号" class="input" v-model="userPhone" /> |
||||
|
<button class="code-btn" :class="{disabled: gettingCode}" @click="getCode" :disabled="gettingCode"> |
||||
|
{{ gettingCode ? `重新发送 ${time}s` : '获取验证码' }} |
||||
|
</button> |
||||
|
</view> |
||||
|
|
||||
|
<view class="input-list"> |
||||
|
<image src="/static/my/verification.png" mode="aspectFit"></image> |
||||
|
<input type="text" placeholder="请输入验证码" class="input" v-model="verifyCode" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="btn-area"> |
||||
|
<button class="next-btn" @click="goToPwdNext">下一步</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import { |
||||
|
sendEmail, |
||||
|
validateCode, |
||||
|
sendPhone |
||||
|
} from "@/api/setting/password"; |
||||
|
|
||||
|
const iSMT = ref(0) |
||||
|
const activeTab = ref('email') |
||||
|
const gettingCode = ref(false) |
||||
|
const time = ref(60) |
||||
|
|
||||
|
const userEmail = ref('') |
||||
|
const userPhone = ref('') |
||||
|
const verifyCode = ref('') |
||||
|
|
||||
|
|
||||
|
const getCode = () => { |
||||
|
if (gettingCode.value) return |
||||
|
gettingCode.value = true |
||||
|
|
||||
|
time.value = 2 |
||||
|
|
||||
|
const timer = setInterval(() => { |
||||
|
time.value-- |
||||
|
if (time.value <= 0) { |
||||
|
clearInterval(timer) |
||||
|
gettingCode.value = false |
||||
|
time.value = 2 |
||||
|
} |
||||
|
}, 1000) |
||||
|
if (activeTab.value === 'email') { |
||||
|
sendEmail({ |
||||
|
email: userEmail.value |
||||
|
}) |
||||
|
} else { |
||||
|
sendPhone({ |
||||
|
phone: userPhone.value |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
} |
||||
|
|
||||
|
const goToPwdNext = async () => { |
||||
|
if (activeTab.value === 'email') { |
||||
|
if (!userEmail.value) { |
||||
|
uni.showToast({ |
||||
|
title: '请输入邮箱', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
}else{ |
||||
|
if (!userPhone.value) { |
||||
|
uni.showToast({ |
||||
|
title: '请输入手机号', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
} |
||||
|
if (!verifyCode.value) { |
||||
|
uni.showToast({ |
||||
|
title: '请输入验证码', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
return |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
let param; |
||||
|
if (activeTab.value === 'email') { |
||||
|
param = { |
||||
|
loginType: 'EMAIL', |
||||
|
account: userEmail.value, |
||||
|
verifyCode: verifyCode.value |
||||
|
} |
||||
|
} else { |
||||
|
param = { |
||||
|
loginType: 'PHONE', |
||||
|
account: userPhone.value, |
||||
|
verifyCode: verifyCode.value |
||||
|
} |
||||
|
} |
||||
|
const res = await validateCode(param) |
||||
|
console.log('看看参数', param) |
||||
|
console.log('看看结果', res) |
||||
|
// 如果返回成功 |
||||
|
if (res.code === 200) { |
||||
|
uni.showToast({ |
||||
|
title: '验证成功', |
||||
|
icon: 'success' |
||||
|
}) |
||||
|
uni.navigateTo({ |
||||
|
url: '../setting/nextPwd' |
||||
|
}) |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: res.msg || '验证失败', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error(err) |
||||
|
uni.showToast({ |
||||
|
title: '请求出错', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 获取状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.tab { |
||||
|
display: flex; |
||||
|
height: 8vh; |
||||
|
background-color: #fff; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.tab-item { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
font-size: 32rpx; |
||||
|
position: relative; |
||||
|
} |
||||
|
|
||||
|
.tab-item.active { |
||||
|
color: #000; |
||||
|
font-weight: bold; |
||||
|
} |
||||
|
|
||||
|
.tab-item.active::after { |
||||
|
content: ''; |
||||
|
position: absolute; |
||||
|
bottom: 0; |
||||
|
left: 50%; |
||||
|
transform: translateX(-50%); |
||||
|
width: 40rpx; |
||||
|
height: 6rpx; |
||||
|
background-color: #000; |
||||
|
/* ????? */ |
||||
|
} |
||||
|
|
||||
|
.switch-tab { |
||||
|
background-color: #fff; |
||||
|
padding: 0 60rpx; |
||||
|
} |
||||
|
|
||||
|
.input-list { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
height: 7vh; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.input-list image { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
margin-right: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
flex: 1; |
||||
|
height: 14vh; |
||||
|
font-size: 28rpx; |
||||
|
} |
||||
|
|
||||
|
.code-btn { |
||||
|
width: 200rpx; |
||||
|
height: 60rpx; |
||||
|
font-size: 24rpx; |
||||
|
border-radius: 10rpx; |
||||
|
background-color: #eee; |
||||
|
color: #666; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.code-btn.disabled { |
||||
|
background-color: #ccc; |
||||
|
color: #999; |
||||
|
} |
||||
|
|
||||
|
.btn-area { |
||||
|
height: 8vh; |
||||
|
background-color: white; |
||||
|
padding-top: 120rpx; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
.next-btn { |
||||
|
width: 610rpx; |
||||
|
height: 85rpx; |
||||
|
background-color: #000; |
||||
|
color: #fff; |
||||
|
font-size: 30rpx; |
||||
|
border-radius: 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,185 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
|
||||
|
<view style="height:1.5vh;" /> |
||||
|
|
||||
|
<view class="top"> |
||||
|
<view class="top-list"> |
||||
|
<view class="left"> |
||||
|
<img src="/static/my/bindedPhone.png" /> |
||||
|
<text class="label">已绑手机号:{{ phone }}</text> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="top-list"> |
||||
|
<view class="left"> |
||||
|
<img src="/static/my/changeBindPhone.png" /> |
||||
|
<text class="label">+86</text> |
||||
|
<input type="number" v-model="userPhone" placeholder="请输入您的换绑手机号" class="input" /> |
||||
|
</view> |
||||
|
<view class="right"> |
||||
|
<button class="verification" :class="{ 'disabled': gettingCode }" @click="getCode" |
||||
|
:disabled="gettingCode"> |
||||
|
{{ gettingCode ? `重新发送 ${time}s` : '获取验证码' }} |
||||
|
</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="top-list"> |
||||
|
<view class="left"> |
||||
|
<img src="/static/my/verification.png" /> |
||||
|
<input type="text" placeholder="请输入验证码" class="input" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="bottom"> |
||||
|
<button class="change-btn" @click="changeAccount">换绑</button> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import { |
||||
|
sendPhone, |
||||
|
changeBind |
||||
|
} from "@/api/setting/password" |
||||
|
import { |
||||
|
getUserInfo |
||||
|
} from "@/api/member" |
||||
|
|
||||
|
const iSMT = ref(0) |
||||
|
const phone = ref('') |
||||
|
const gettingCode = ref(false) |
||||
|
const time = ref(60) |
||||
|
const userPhone = ref('') |
||||
|
|
||||
|
const userInfoPromise = getUserInfo() |
||||
|
userInfoPromise.then(res => { |
||||
|
if (res.code === 200) { |
||||
|
console.log('个人信息', res.data) |
||||
|
phone.value = res.data.phone |
||||
|
} else { |
||||
|
uni.showToast({ |
||||
|
title: '用户信息请求失败', |
||||
|
icon: 'none', |
||||
|
}) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
const getCode = () => { |
||||
|
if (gettingCode.value) return |
||||
|
gettingCode.value = true |
||||
|
|
||||
|
time.value = 60 |
||||
|
|
||||
|
const timer = setInterval(() => { |
||||
|
time.value-- |
||||
|
if (time.value <= 0) { |
||||
|
clearInterval(timer) |
||||
|
gettingCode.value = false |
||||
|
} |
||||
|
}, 1000) |
||||
|
sendPhone({ |
||||
|
phone: userPhone.value |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const changeAccount = () => { |
||||
|
const res = changeBind({ |
||||
|
verificateType: 1, |
||||
|
account: userPhone.value |
||||
|
}) |
||||
|
if(res.code === 200){ |
||||
|
uni.showToast({ |
||||
|
title: '绑定成功', |
||||
|
icon: 'none', |
||||
|
}) |
||||
|
}else { |
||||
|
uni.showToast({ |
||||
|
title: '用户绑定失败', |
||||
|
icon: 'none', |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
height: auto; |
||||
|
background-color: white; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.top-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
margin: 0rpx 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.left { |
||||
|
flex: 1; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.label { |
||||
|
font-size: 28rpx; |
||||
|
margin-left: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.right { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.input { |
||||
|
flex: 1; |
||||
|
height: 70rpx; |
||||
|
font-size: 29rpx; |
||||
|
margin-left: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.verification { |
||||
|
font-size: 24rpx; |
||||
|
border-radius: 10rpx; |
||||
|
background-color: rgb(230, 230, 230); |
||||
|
} |
||||
|
|
||||
|
.bottom { |
||||
|
height: 22vh; |
||||
|
background-color: white; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
|
||||
|
.change-btn { |
||||
|
height: 85rpx; |
||||
|
width: 610rpx; |
||||
|
padding:0 20rpx; |
||||
|
background-color: black; |
||||
|
color: white; |
||||
|
border-radius: 40rpx; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,120 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
<view class="top"> |
||||
|
<view class="top-list"> |
||||
|
<text class="label">公共消息</text> |
||||
|
<view class="right"> |
||||
|
<text class="public">重大咨询、财经要闻等系统提醒</text> |
||||
|
<switch class="switch-btn" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="top-list"> |
||||
|
<text class="label">指标消息提醒</text> |
||||
|
<view class="right"> |
||||
|
<text class="public">所有指标消息的提醒</text> |
||||
|
<switch class="switch-btn" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
|
||||
|
<view class="bottom"> |
||||
|
<view class="bottom-list"> |
||||
|
<text class="label">盯盘预警</text> |
||||
|
<view class="right"> |
||||
|
<text class="public">自选股预警和个性化预警设置</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
<view class="bottom-list"> |
||||
|
<text class="label">订阅服务</text> |
||||
|
<view class="right"> |
||||
|
<text class="public">订阅你感兴趣的专题服务</text> |
||||
|
<uni-icons type="arrowright" size="16" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
const iSMT = ref(0) |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
margin-top: 1.5vh; |
||||
|
height: 14vh; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.top-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0 40rpx; |
||||
|
padding: 0 10rpx; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.top-list:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.right { |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
gap: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.switch-btn { |
||||
|
transform: scale(0.6); |
||||
|
transform-origin: center right; |
||||
|
} |
||||
|
|
||||
|
.public { |
||||
|
font-size: 10px; |
||||
|
color: rgb(203, 203, 203); |
||||
|
} |
||||
|
|
||||
|
.bottom { |
||||
|
height: 14vh; |
||||
|
background-color: white; |
||||
|
margin-top: 1vh; |
||||
|
} |
||||
|
|
||||
|
.bottom-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
margin: 0 40rpx; |
||||
|
padding: 0 10rpx; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.cache { |
||||
|
margin-left: 55%; |
||||
|
font-size: 14px; |
||||
|
color: rgb(203, 203, 203); |
||||
|
} |
||||
|
|
||||
|
.bottom-list:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.label { |
||||
|
flex: 1; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,128 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
<view class="top"> |
||||
|
<view class="top-list"> |
||||
|
<text>自动选择</text> |
||||
|
<radio value="0" class="radio-btn" activeBackgroundColor="red" |
||||
|
:checked="selectedIndex === 0" @click="selectFont('auto')" /> |
||||
|
</view> |
||||
|
<view class="top-list"> |
||||
|
<text>新加坡服务器</text> |
||||
|
<radio value="1" class="radio-btn" activeBackgroundColor="red" |
||||
|
:checked="selectedIndex === 1" @click="selectFont('singapore')" /> |
||||
|
</view> |
||||
|
<view class="top-list"> |
||||
|
<text>香港服务器</text> |
||||
|
<radio value="2" class="radio-btn" activeBackgroundColor="red" |
||||
|
:checked="selectedIndex === 2" @click="selectFont('hongkong')" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import { |
||||
|
getSetting, |
||||
|
updateSetting |
||||
|
} from "@/api/setting/general" |
||||
|
|
||||
|
const iSMT = ref(0) |
||||
|
const selectedIndex = ref(0) |
||||
|
const servertypeMap = { |
||||
|
'auto': 0, |
||||
|
'singapore': 1, |
||||
|
'hongkong': 2 |
||||
|
} |
||||
|
|
||||
|
const getServer = async () => { |
||||
|
try { |
||||
|
const res = await getSetting() |
||||
|
if (res.code === 200) { |
||||
|
const serverSelection = res.data.serverSelection |
||||
|
selectedIndex.value = servertypeMap[serverSelection] ?? 0; |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error("获取服务器设置失败:", err); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const selectFont = async (servertype) => { |
||||
|
try { |
||||
|
selectedIndex.value = servertypeMap[servertype] |
||||
|
console.log('服务器类型:', servertype, ',looklook索引:', selectedIndex.value) |
||||
|
|
||||
|
const updateRes = await updateSetting({ |
||||
|
serverSelection: servertype |
||||
|
}) |
||||
|
if (updateRes.code === 200) { |
||||
|
uni.showToast({ |
||||
|
title: '服务器大小设置成功', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error("更新服务器设置失败:", err); |
||||
|
uni.showToast({ |
||||
|
title: '设置失败,请重试', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
getServer() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.top { |
||||
|
margin-top: 1.5vh; |
||||
|
height: 21vh; |
||||
|
background-color: white; |
||||
|
} |
||||
|
|
||||
|
.top-list { |
||||
|
width: 630rpx; |
||||
|
height: 7vh; |
||||
|
display: flex; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
margin: 0 40rpx; |
||||
|
padding: 0 10rpx; |
||||
|
border-bottom: 1rpx solid #eee; |
||||
|
} |
||||
|
|
||||
|
.top-list:last-child { |
||||
|
border-bottom: none; |
||||
|
} |
||||
|
|
||||
|
.switch-btn { |
||||
|
width: 100rpx; |
||||
|
transform: scale(0.6); |
||||
|
transform-origin: center right; |
||||
|
} |
||||
|
|
||||
|
.public { |
||||
|
width: 450rpx; |
||||
|
margin-left: auto; |
||||
|
font-size: 10px; |
||||
|
color: rgb(203, 203, 203); |
||||
|
} |
||||
|
|
||||
|
.arrow { |
||||
|
margin-left: auto; |
||||
|
} |
||||
|
|
||||
|
.radio-btn { |
||||
|
margin-left: auto; |
||||
|
transform: scale(0.6); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,299 @@ |
|||||
|
<template> |
||||
|
<view class="all"> |
||||
|
<!-- 背景图部分 --> |
||||
|
<image class="img-share" src="/static/my/shareBackground.png"/> |
||||
|
<image class="img-greenBack" src="/static/my/greenBackground.png"/> |
||||
|
<!-- todo 这里给我个码--> |
||||
|
<image class="img-QRcode" src="/static/my/QRcode.png"/> |
||||
|
<image class="img-award" src="/static/my/award.png"/> |
||||
|
<image class="img-myFriends" src="/static/my/myFriends.png"/> |
||||
|
<image class="img-friends" src="/static/my/shareFriends.png"/> |
||||
|
|
||||
|
<!-- dccode --> |
||||
|
<text class="jwcode">{{ dccode }}</text> |
||||
|
|
||||
|
<!-- 邀请按钮 --> |
||||
|
<button class="invite" @click="openShare">立即邀请</button> |
||||
|
|
||||
|
<!-- 分享弹窗 --> |
||||
|
<uni-popup ref="shareRef" type="share" safeArea> |
||||
|
<SharePopup @select="onShareSelect" @close="closeShare" title=" "/> |
||||
|
</uni-popup> |
||||
|
|
||||
|
<!-- 二次弹窗 --> |
||||
|
<uni-popup ref="secondPopup" type="share"> |
||||
|
<view class="second-popup"> |
||||
|
<view style=" display: flex;justify-content: center;align-items: center; font-size: 17px"> |
||||
|
<image style="width: 16px; height: 16px; margin-right: 8rpx" src="/static/my/share/success.png"/> |
||||
|
<text>已复制</text> |
||||
|
</view> |
||||
|
|
||||
|
<view class="popup-msg-box"> |
||||
|
<text>{{ popupMsg }}</text> |
||||
|
</view> |
||||
|
<view style="justify-content: center; align-items: center;"> |
||||
|
<!-- 二次弹窗中的按钮图标 --> |
||||
|
<button |
||||
|
style="border-radius: 40rpx; background-color: black; color: white; display: flex; align-items: center; justify-content: center; padding: 12rpx 24rpx;" |
||||
|
@click="closeSecondPopup"> |
||||
|
<image style="width: 25px; height: 25px; margin-right: 8rpx;" |
||||
|
:src="platformIconMap[selectedPlatform]"/> |
||||
|
去粘贴给好友 |
||||
|
</button> |
||||
|
</view> |
||||
|
|
||||
|
|
||||
|
</view> |
||||
|
</uni-popup> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import {ref} from 'vue' |
||||
|
import SharePopup from '@/components/SharePopup.vue' |
||||
|
import {getUserInfo} from "@/api/member"; |
||||
|
import {Share} from "@/api/setting/share"; |
||||
|
|
||||
|
/* =============== 数据与引用 =============== */ |
||||
|
const shareRef = ref(null) |
||||
|
const secondPopup = ref(null) |
||||
|
const popupMsg = ref('') |
||||
|
// const jwcode = ref('90047681') |
||||
|
// 选中的平台 |
||||
|
const selectedPlatform = ref('') |
||||
|
|
||||
|
// dccode |
||||
|
const dccode = ref('') |
||||
|
// token |
||||
|
const token = ref('1ab8f83f391ca866191385d0e5048938') |
||||
|
// 设备码 |
||||
|
const deviceId = ref(100) |
||||
|
// 版本 |
||||
|
const version = ref(100) |
||||
|
// 获取设备类型 |
||||
|
const client = ref('android') |
||||
|
|
||||
|
// 平台图标 |
||||
|
const platformIconMap = ref({ |
||||
|
'WeChat': '/static/my/share/WeChat.png', |
||||
|
'WhatsApp': '/static/my/share/WhatsApp.png', |
||||
|
'Line': '/static/my/share/Line.png', |
||||
|
'KakaoTalk': '/static/my/share/KakaoTalk.png', |
||||
|
'复制链接': '/static/my/share/share.png' |
||||
|
}) |
||||
|
|
||||
|
// 用户信息 |
||||
|
const userInfoRes = ref() |
||||
|
|
||||
|
// 分享人的dccode |
||||
|
const shareLink = ref('') |
||||
|
|
||||
|
/* =============== 方法 =============== */ |
||||
|
|
||||
|
userInfoRes.value = getUserInfo() |
||||
|
userInfoRes.value.then(res => { |
||||
|
dccode.value = res.data.dccode |
||||
|
console.log('用户信息', res.data) |
||||
|
}) |
||||
|
|
||||
|
const ShareRes = ref() |
||||
|
ShareRes.value = Share() |
||||
|
ShareRes.value.then(res => { |
||||
|
if (res.code === 200){ |
||||
|
shareLink.value = res.message |
||||
|
console.log('分享接口返回', res.data) |
||||
|
}else { |
||||
|
console.log('分享接口返回失败', res.data) |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
|
||||
|
|
||||
|
// 打开分享弹窗 |
||||
|
function openShare() { |
||||
|
Share() |
||||
|
shareRef.value.open() |
||||
|
} |
||||
|
|
||||
|
// 关闭分享弹窗 |
||||
|
function closeShare() { |
||||
|
shareRef.value.close() |
||||
|
} |
||||
|
|
||||
|
// |
||||
|
|
||||
|
// 点击分享选项 |
||||
|
function onShareSelect({item}) { |
||||
|
console.log('选择了:', item.name) |
||||
|
selectedPlatform.value = item.name // 记录选中的平台 |
||||
|
|
||||
|
// // 生成动态助力链接 |
||||
|
// const baseUrl = 'https:' |
||||
|
// // const shareLink = `${baseUrl}?token=${encodeURIComponent(token.value)}&deviceId=${encodeURIComponent(deviceId.value)}&version=${encodeURIComponent(version.value)}&client=${encodeURIComponent(client.value)}` |
||||
|
// const shareLink = `$ ` |
||||
|
|
||||
|
// 关闭第一个弹窗 |
||||
|
shareRef.value.close() |
||||
|
popupMsg.value = `【DeepChart】邀请你加入,点击链接帮我助力: ${shareLink.value}` |
||||
|
|
||||
|
|
||||
|
uni.setClipboardData({ |
||||
|
data: popupMsg.value, |
||||
|
showToast: false |
||||
|
}); |
||||
|
/* // 根据分享选项显示不同提示 |
||||
|
if (item.name === '复制链接') { |
||||
|
popupMsg.value = '链接已复制,快去分享给好友吧~' |
||||
|
} else if (item.name === 'WeChat') { |
||||
|
popupMsg.value = '请在微信中分享~' |
||||
|
} else { |
||||
|
popupMsg.value = `你选择了 ${item.name}` |
||||
|
}*/ |
||||
|
|
||||
|
// 打开第二个弹窗 |
||||
|
secondPopup.value.open() |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// 关闭第二个弹窗 |
||||
|
function closeSecondPopup() { |
||||
|
if (selectedPlatform.value === 'WeChat') { |
||||
|
uni.share({ |
||||
|
provider: "weixin", |
||||
|
scene: "WXSceneSession", |
||||
|
type: 1, |
||||
|
summary: popupMsg.value, |
||||
|
success: function (res) { |
||||
|
console.log("success:" + JSON.stringify(res)); |
||||
|
}, |
||||
|
fail: function (err) { |
||||
|
console.log("fail:" + JSON.stringify(err)); |
||||
|
} |
||||
|
}); |
||||
|
secondPopup.value.close() |
||||
|
} |
||||
|
// 其他的几种情况 需要适配 |
||||
|
else if (selectedPlatform.value === 'WhatsApp' || selectedPlatform.value === 'Line' || selectedPlatform.value === 'KakaoTalk') { |
||||
|
secondPopup.value.close() |
||||
|
uni.showToast({title: '开发中……', icon: 'none'}) |
||||
|
} else if (selectedPlatform.value === '复制链接') { |
||||
|
uni.showToast({title: '已复制', icon: 'success'}) |
||||
|
|
||||
|
secondPopup.value.close() |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.all { |
||||
|
position: relative; |
||||
|
width: 750rpx; |
||||
|
height: auto; |
||||
|
} |
||||
|
|
||||
|
/* 背景图片部分 */ |
||||
|
.img-share { |
||||
|
width: 750rpx; |
||||
|
height: 2118rpx; |
||||
|
position: absolute; |
||||
|
top: 0; |
||||
|
left: 0; |
||||
|
z-index: 1; |
||||
|
} |
||||
|
|
||||
|
.img-greenBack { |
||||
|
width: 670rpx; |
||||
|
height: 1740rpx; |
||||
|
position: absolute; |
||||
|
top: 16vh; |
||||
|
left: 40rpx; |
||||
|
z-index: 2; |
||||
|
} |
||||
|
|
||||
|
.img-QRcode { |
||||
|
width: 320rpx; |
||||
|
height: 320rpx; |
||||
|
position: absolute; |
||||
|
top: 26vh; |
||||
|
left: 215rpx; |
||||
|
z-index: 3; |
||||
|
} |
||||
|
|
||||
|
.img-award { |
||||
|
width: 300rpx; |
||||
|
height: 120rpx; |
||||
|
position: absolute; |
||||
|
top: 61vh; |
||||
|
left: 75rpx; |
||||
|
z-index: 3; |
||||
|
} |
||||
|
|
||||
|
.img-myFriends { |
||||
|
width: 300rpx; |
||||
|
height: 88rpx; |
||||
|
position: absolute; |
||||
|
top: 61vh; |
||||
|
right: 75rpx; |
||||
|
z-index: 3; |
||||
|
} |
||||
|
|
||||
|
.img-friends { |
||||
|
width: 602rpx; |
||||
|
height: 840rpx; |
||||
|
position: absolute; |
||||
|
top: 68vh; |
||||
|
left: 74rpx; |
||||
|
z-index: 3; |
||||
|
} |
||||
|
|
||||
|
/* 邀请码与按钮 */ |
||||
|
.jwcode { |
||||
|
width: 320rpx; |
||||
|
height: 38rpx; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
position: absolute; |
||||
|
top: 19vh; |
||||
|
left: 212rpx; |
||||
|
z-index: 999; |
||||
|
} |
||||
|
|
||||
|
.invite { |
||||
|
width: 320rpx; |
||||
|
height: 80rpx; |
||||
|
border-radius: 40rpx; |
||||
|
background-color: black; |
||||
|
color: white; |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
position: absolute; |
||||
|
top: 50.7vh; |
||||
|
left: 212rpx; |
||||
|
z-index: 999; |
||||
|
} |
||||
|
|
||||
|
/* 第二个弹窗样式 */ |
||||
|
.second-popup { |
||||
|
background-color: #fff; |
||||
|
border-radius: 12px; |
||||
|
padding: 30rpx; |
||||
|
text-align: center; |
||||
|
} |
||||
|
|
||||
|
.popup-msg-box { |
||||
|
background-color: #F3F3F3; |
||||
|
border-radius: 8px; |
||||
|
padding: 12px 16px; |
||||
|
margin: 10px; |
||||
|
|
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
|
||||
|
overflow: hidden; /* 隐藏溢出内容 */ |
||||
|
text-overflow: ellipsis; /* 溢出部分显示... */ |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,104 @@ |
|||||
|
<template> |
||||
|
<view class="main"> |
||||
|
<view :style="{height:iSMT+'px'}"></view> |
||||
|
<view class="theme"> |
||||
|
<view class="left"> |
||||
|
<image class="img-theme" src="/static/my/whiteTheme.png" mode="widthFix" /> |
||||
|
<radio value="0" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 0" |
||||
|
@click="updateTheme('light')" /> |
||||
|
</view> |
||||
|
<view class="left"> |
||||
|
<image class="img-theme" src="/static/my/blackTheme.png" mode="widthFix" /> |
||||
|
<radio value="1" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 1" |
||||
|
@click="updateTheme('dark')" /> |
||||
|
</view> |
||||
|
</view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { |
||||
|
ref, |
||||
|
onMounted |
||||
|
} from 'vue' |
||||
|
import { |
||||
|
getSetting, |
||||
|
updateSetting |
||||
|
} from "@/api/setting/general" |
||||
|
const iSMT = ref(0) |
||||
|
const selectedIndex = ref(0) |
||||
|
const themeTypeMap = { |
||||
|
'light': 0, |
||||
|
'dark': 1 |
||||
|
} |
||||
|
|
||||
|
const getTheme = async () => { |
||||
|
try { |
||||
|
const res = await getSetting() |
||||
|
if (res.code === 200) { |
||||
|
const theme = res.data.theme |
||||
|
selectedIndex.value = themeTypeMap[theme] ?? 0 |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error("获取主题设置失败:", err); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const updateTheme = async (themeType) => { |
||||
|
try { |
||||
|
selectedIndex.value = themeTypeMap[themeType] |
||||
|
console.log('主题:', themeType, ',looklook索引:', selectedIndex.value) |
||||
|
|
||||
|
const updateRes = await updateSetting({ |
||||
|
theme: themeType |
||||
|
}) |
||||
|
if (updateRes.code === 200) { |
||||
|
uni.showToast({ |
||||
|
title: '主题设置成功', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
} catch (err) { |
||||
|
console.error("更新主题设置失败:", err); |
||||
|
uni.showToast({ |
||||
|
title: '设置失败,请重试', |
||||
|
icon: 'none' |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
// 状态栏高度 |
||||
|
iSMT.value = uni.getSystemInfoSync().statusBarHeight; |
||||
|
console.log('看看高度', iSMT.value) |
||||
|
getTheme() |
||||
|
}) |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.theme { |
||||
|
margin-top: 1.5vh; |
||||
|
height: 34vh; |
||||
|
background-color: white; |
||||
|
display: flex; |
||||
|
justify-content: space-around; |
||||
|
} |
||||
|
|
||||
|
.img-theme { |
||||
|
width: 316rpx; |
||||
|
height: 362rpx; |
||||
|
} |
||||
|
|
||||
|
.left { |
||||
|
width: 350rpx; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
} |
||||
|
|
||||
|
.radio-btn { |
||||
|
margin-top: 36rpx; |
||||
|
transform: scale(0.8); |
||||
|
} |
||||
|
</style> |
||||
1055
pages/start/Registration/Registration.vue
File diff suppressed because it is too large
View File
1341
pages/start/Registration/list.js
File diff suppressed because it is too large
View File
@ -0,0 +1,13 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
用户协议 |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
|
||||
|
</style> |
||||
1341
pages/start/login/list.js
File diff suppressed because it is too large
View File
1190
pages/start/login/login.vue
File diff suppressed because it is too large
View File
@ -0,0 +1,69 @@ |
|||||
|
|
||||
|
function verificationPhone(countryCode,phoneNumber) { |
||||
|
switch (countryCode) { |
||||
|
case '+86': |
||||
|
return verificationChina(phoneNumber); |
||||
|
case '+1': |
||||
|
return verificationAmerica(phoneNumber); |
||||
|
case '+65': |
||||
|
return verificationSingapore(phoneNumber); |
||||
|
case '+60': |
||||
|
return verificationMalaysia(phoneNumber); |
||||
|
case '+66': |
||||
|
return verificationThailand(phoneNumber); |
||||
|
case '+852': |
||||
|
return verificationHongKong(phoneNumber); |
||||
|
case '+84': |
||||
|
return verificationVietnam(phoneNumber); |
||||
|
default: |
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function verificationChina(phoneNumber){ |
||||
|
const phoneRegex = /^1[3-9]\d{9}$/; |
||||
|
return phoneRegex.test(phoneNumber); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function verificationAmerica(phoneNumber){ |
||||
|
const phoneRegex = /^[2-9]\d{2}[- ]?\d{4}$/; |
||||
|
return phoneRegex.test(phoneNumber); |
||||
|
} |
||||
|
function verificationSingapore(phoneNumber){ |
||||
|
const phoneRegex = /^[89]\d{7}$/; |
||||
|
return phoneRegex.test(phoneNumber); |
||||
|
} |
||||
|
function verificationMalaysia(phoneNumber){ |
||||
|
const phoneRegex = /^01\d{8}$/; |
||||
|
return phoneRegex.test(phoneNumber); |
||||
|
} |
||||
|
|
||||
|
function verificationHongKong(phoneNumber){ |
||||
|
const phoneRegex = /^0[896]\d{8}$/; |
||||
|
return phoneRegex.test(phoneNumber); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
function verificationThailand(phoneNumber){ |
||||
|
const phoneRegex = /^[5-9]\d{7}$/; |
||||
|
return phoneRegex.test(phoneNumber); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
function verificationVietnam(phoneNumber){ |
||||
|
const phoneRegex = /^(0)?[3-9]\d{8}$/; |
||||
|
return phoneRegex.test(phoneNumber); |
||||
|
} |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
function verificationEmail(email) { |
||||
|
const emailRegex = /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/; |
||||
|
return emailRegex.test(email); |
||||
|
} |
||||
|
|
||||
|
export {verificationPhone,verificationEmail} |
||||
@ -0,0 +1,13 @@ |
|||||
|
<template> |
||||
|
<view> |
||||
|
隐私政策 |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
|
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
|
||||
|
</style> |
||||
1085
pages/start/recoverPassword/recoverPassword.vue
File diff suppressed because it is too large
View File
@ -0,0 +1,170 @@ |
|||||
|
<template> |
||||
|
<view class="login-container"> |
||||
|
<!-- 顶部标题 --> |
||||
|
<view class="title-section"> |
||||
|
<text class="main-title">DeepChart</text> |
||||
|
</view> |
||||
|
<view class="subtitle-section"> |
||||
|
<text class="subtitle">您的股市随身顾问</text> |
||||
|
</view> |
||||
|
|
||||
|
<!-- 手机卡片样式 --> |
||||
|
<view class="phone-card"> </view> |
||||
|
|
||||
|
<!-- 登录注册按钮 --> |
||||
|
<view class="button-group"> |
||||
|
<button class="login-button" @click="toLogin">登录</button> |
||||
|
<button class="register-button" @click="toRegistration">注册</button> |
||||
|
</view> |
||||
|
|
||||
|
<footerBar class="static-footer" :type="type"></footerBar> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import footerBar from "../../../components/footerBar"; |
||||
|
import { ref, reactive, toRefs, watch } from "vue"; |
||||
|
|
||||
|
|
||||
|
const type = ref(""); |
||||
|
|
||||
|
function toRegistration() { |
||||
|
uni.redirectTo({ |
||||
|
url: "/pages/start/Registration/Registration", |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
function toLogin() { |
||||
|
uni.redirectTo({ |
||||
|
url: "/pages/start/login/login", |
||||
|
}); |
||||
|
} |
||||
|
</script> |
||||
|
|
||||
|
<style scoped> |
||||
|
.login-container { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
/* justify-content: space-between; */ |
||||
|
padding: 40rpx; |
||||
|
height: 90vh; |
||||
|
background-color: #ffffff; |
||||
|
overflow: hidden; |
||||
|
max-height: 100vh; |
||||
|
-webkit-overflow-scrolling: none; |
||||
|
} |
||||
|
|
||||
|
.title-section { |
||||
|
text-align: center; |
||||
|
margin-top: 120rpx; |
||||
|
|
||||
|
margin-bottom: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.main-title { |
||||
|
text-align: center; |
||||
|
font-size: 64rpx; |
||||
|
font-weight: 300; |
||||
|
color: #000000; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
.subtitle-section { |
||||
|
margin-bottom: 80rpx; |
||||
|
} |
||||
|
.subtitle { |
||||
|
/* font-weight: bold; */ |
||||
|
font-size: 32rpx; |
||||
|
color: #333333; |
||||
|
} |
||||
|
|
||||
|
.phone-card { |
||||
|
background-image: url("/static/select.png"); |
||||
|
background-repeat: no-repeat; |
||||
|
background-size: contain; |
||||
|
background-position: center; |
||||
|
/* background-position: center; */ |
||||
|
/* min-width: 300rpx; */ |
||||
|
|
||||
|
width: 420rpx; |
||||
|
height: 786rpx; |
||||
|
border-radius: 50rpx; |
||||
|
padding: 40rpx; |
||||
|
/* box-shadow: 0 20rpx 40rpx rgba(0, 0, 0, 0.3); */ |
||||
|
position: relative; |
||||
|
overflow: hidden; |
||||
|
} |
||||
|
|
||||
|
.button-group { |
||||
|
display: flex; |
||||
|
gap: 40rpx; |
||||
|
margin: 80rpx 0; |
||||
|
width: 100%; |
||||
|
max-width: 600rpx; |
||||
|
} |
||||
|
|
||||
|
.login-button { |
||||
|
flex: 1; |
||||
|
background-color: #f5f5f5; |
||||
|
color: #000000; |
||||
|
border-radius: 60rpx; |
||||
|
font-size: 32rpx; |
||||
|
padding: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.register-button { |
||||
|
flex: 1; |
||||
|
background-color: #000000; |
||||
|
color: #ffffff; |
||||
|
border-radius: 60rpx; |
||||
|
font-size: 32rpx; |
||||
|
padding: 20rpx; |
||||
|
} |
||||
|
|
||||
|
.bottom-nav { |
||||
|
display: flex; |
||||
|
justify-content: space-around; |
||||
|
align-items: center; |
||||
|
width: 100%; |
||||
|
height: 100rpx; |
||||
|
background-color: #ffffff; |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
border-top: 1rpx solid #e5e5e5; |
||||
|
} |
||||
|
|
||||
|
.nav-item { |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
align-items: center; |
||||
|
justify-content: center; |
||||
|
padding: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.nav-item.active .nav-icon { |
||||
|
transform: scale(1.2); |
||||
|
} |
||||
|
|
||||
|
.nav-icon { |
||||
|
width: 40rpx; |
||||
|
height: 40rpx; |
||||
|
margin-bottom: 10rpx; |
||||
|
} |
||||
|
|
||||
|
.nav-text { |
||||
|
font-size: 24rpx; |
||||
|
color: #666666; |
||||
|
} |
||||
|
|
||||
|
.nav-item.active .nav-text { |
||||
|
color: #000000; |
||||
|
} |
||||
|
|
||||
|
.static-footer { |
||||
|
position: fixed; |
||||
|
bottom: 0; |
||||
|
left: 0; |
||||
|
right: 0; |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,73 @@ |
|||||
|
<template> |
||||
|
<view class="background"> |
||||
|
<image |
||||
|
class="logo" |
||||
|
src="../../../static/icons/start-logo.png" |
||||
|
mode="scaleToFill" |
||||
|
/> |
||||
|
<view class="logo-text"> DeepChart </view> |
||||
|
</view> |
||||
|
</template> |
||||
|
|
||||
|
<script setup> |
||||
|
import { onShow } from "@dcloudio/uni-app"; |
||||
|
import { useUserStore } from "../../../stores/modules/userInfo"; |
||||
|
import { useDeviceStore } from "../../../stores/modules/deviceInfo"; |
||||
|
|
||||
|
onShow(() => { |
||||
|
const deviceInfo = useDeviceStore(); |
||||
|
// 获取设备ID |
||||
|
uni.getSystemInfo({ |
||||
|
success: (res) => { |
||||
|
deviceInfo.setDeviceInfo(res) |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
setTimeout(() => { |
||||
|
const userStore = useUserStore(); |
||||
|
if (!userStore.userInfo) |
||||
|
uni.redirectTo({ |
||||
|
url: "/pages/start/select/select", |
||||
|
animationType: "slide-in-right", |
||||
|
animationDuration: 1000, |
||||
|
}); |
||||
|
else { |
||||
|
uni.redirectTo({ |
||||
|
url: "/pages/home/home", |
||||
|
animationType: "slide-in-right", |
||||
|
animationDuration: 1000, |
||||
|
}); |
||||
|
} |
||||
|
}, 1500); |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style> |
||||
|
.background { |
||||
|
background: linear-gradient(180deg, #fb6967, #fb6967); |
||||
|
width: 100vw; |
||||
|
height: 100vh; |
||||
|
} |
||||
|
|
||||
|
.logo-text { |
||||
|
display: flex; |
||||
|
justify-content: center; |
||||
|
align-items: center; |
||||
|
color: white; |
||||
|
font-weight: bold; |
||||
|
font-size: 24px; |
||||
|
position: absolute; |
||||
|
bottom: 5%; |
||||
|
left: 50%; |
||||
|
transform: translateX(-50%); |
||||
|
} |
||||
|
|
||||
|
.logo { |
||||
|
width: 320rpx; |
||||
|
height: 200rpx; |
||||
|
position: absolute; |
||||
|
top: 30%; |
||||
|
left: 50%; |
||||
|
transform: translate(-50%, -50%); |
||||
|
} |
||||
|
</style> |
||||
@ -0,0 +1,138 @@ |
|||||
|
{ |
||||
|
"loginSuccessByEmail": { |
||||
|
"code": 200, |
||||
|
"message": "c3e9ed50ad72073b94dfb04860562b58", |
||||
|
"data": { |
||||
|
"loginType": null, |
||||
|
"device": "unknown", |
||||
|
"dccode": "90047686", |
||||
|
"account": null, |
||||
|
"password": null, |
||||
|
"verifyCode": null, |
||||
|
"useCode": false, |
||||
|
"idToken": null, |
||||
|
"token": "c3e9ed50ad72073b94dfb04860562b58", |
||||
|
"market": "新加坡", |
||||
|
"phone": "17861484516", |
||||
|
"email": "q614588746@163.com", |
||||
|
"language": "中文", |
||||
|
"avatar": "123" |
||||
|
} |
||||
|
}, |
||||
|
"loginFailureEmailNotFound": { |
||||
|
"code": 404, |
||||
|
"message": "账号不存在", |
||||
|
"interface": "login" |
||||
|
}, |
||||
|
"loginFailureWrongCode": { |
||||
|
"code": 400, |
||||
|
"message": "验证码错误", |
||||
|
"interface": "login" |
||||
|
}, |
||||
|
"loginSuccessByPhone": { |
||||
|
"code": 200, |
||||
|
"message": "登录成功", |
||||
|
"interface": "login", |
||||
|
"data": { |
||||
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", |
||||
|
"userInfo": { |
||||
|
"id": "987654321", |
||||
|
"username": "13800138000", |
||||
|
"phone": "13800138000" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"loginSuccessByPassword": { |
||||
|
"code": 200, |
||||
|
"message": "登录成功", |
||||
|
"interface": "login", |
||||
|
"data": { |
||||
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", |
||||
|
"userInfo": { |
||||
|
"id": "556677889", |
||||
|
"username": "testuser" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"sendCodeSuccess": { |
||||
|
"code": 200, |
||||
|
"message": "验证码发送成功", |
||||
|
"interface": "sendCode", |
||||
|
"data": { |
||||
|
"expireTime": 180 |
||||
|
} |
||||
|
}, |
||||
|
"sendCodeFailureInvalidFormat": { |
||||
|
"code": 400, |
||||
|
"message": "手机号格式错误", |
||||
|
"interface": "sendCode" |
||||
|
}, |
||||
|
"sendCodeFailureTooFrequent": { |
||||
|
"code": 429, |
||||
|
"message": "发送频率过高,请稍后再试", |
||||
|
"interface": "sendCode" |
||||
|
}, |
||||
|
"registerSuccess": { |
||||
|
"code": 200, |
||||
|
"message": "注册成功", |
||||
|
"interface": "register", |
||||
|
"data": { |
||||
|
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", |
||||
|
"userInfo": { |
||||
|
"id": "112233445", |
||||
|
"username": "newuser", |
||||
|
"email": "newuser@example.com" |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
"registerFailureEmailExist": { |
||||
|
"code": 400, |
||||
|
"message": "邮箱已被注册", |
||||
|
"interface": "register" |
||||
|
}, |
||||
|
"registerFailurePhoneExist": { |
||||
|
"code": 400, |
||||
|
"message": "手机号已被注册", |
||||
|
"interface": "register" |
||||
|
}, |
||||
|
"registerFailureWrongCode": { |
||||
|
"code": 400, |
||||
|
"message": "验证码错误", |
||||
|
"interface": "register" |
||||
|
}, |
||||
|
"updatePasswordSuccess": { |
||||
|
"code": 200, |
||||
|
"message": "密码修改成功", |
||||
|
"interface": "updatePassword" |
||||
|
}, |
||||
|
"updatePasswordFailureWrongOldPassword": { |
||||
|
"code": 400, |
||||
|
"message": "旧密码错误", |
||||
|
"interface": "updatePassword" |
||||
|
}, |
||||
|
"updatePasswordFailureWrongCode": { |
||||
|
"code": 400, |
||||
|
"message": "验证码错误", |
||||
|
"interface": "updatePassword" |
||||
|
}, |
||||
|
"forgotPasswordSuccessByEmail": { |
||||
|
"code": 200, |
||||
|
"message": "密码重置成功,新密码已发送至邮箱", |
||||
|
"interface": "forgotPassword" |
||||
|
}, |
||||
|
"forgotPasswordSuccessByPhone": { |
||||
|
"code": 200, |
||||
|
"message": "密码重置成功,新密码已发送至手机", |
||||
|
"interface": "forgotPassword" |
||||
|
}, |
||||
|
"forgotPasswordFailureAccountNotFound": { |
||||
|
"code": 404, |
||||
|
"message": "账号不存在", |
||||
|
"interface": "forgotPassword" |
||||
|
}, |
||||
|
"forgotPasswordFailureWrongCode": { |
||||
|
"code": 400, |
||||
|
"message": "验证码错误", |
||||
|
"interface": "forgotPassword" |
||||
|
} |
||||
|
} |
||||
|
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: 750 | Height: 198 | Size: 35 KiB |
|
After Width: 750 | Height: 198 | Size: 36 KiB |
|
After Width: 750 | Height: 198 | Size: 37 KiB |
|
After Width: 750 | Height: 198 | Size: 35 KiB |
|
After Width: 200 | Height: 200 | Size: 4.8 KiB |
|
After Width: 305 | Height: 200 | Size: 7.9 KiB |