Browse Source

Merge branch 'milestone-20251031-简版功能开发' of http://39.101.133.168:8807/qimaohong/deepChartVueApp into wangyi/feature-20251026183100-deepmate王毅

lihuilin/feature-20251024095243-我的
wangyi 4 weeks ago
parent
commit
0368f2ac27
  1. 7
      .hbuilderx/launch.json
  2. 46
      api/customerServicePlatform/customerServicePlatform.js
  3. 8
      api/setting/general.js
  4. 19
      api/setting/market.js
  5. 92
      api/tcpConnection.js
  6. 31
      components/DeepMate.vue
  7. 196
      components/FeedbackModal.vue
  8. 30
      pages.json
  9. 682
      pages/customerServicePlatform/csPlatformIndex.vue
  10. 353
      pages/customerServicePlatform/historyRecord.vue
  11. 335
      pages/customerServicePlatform/questionDetail.vue
  12. 21
      pages/deepMate/deepMate.vue
  13. 1
      pages/home/member.vue
  14. 7
      pages/marketSituation/marketDetail.vue
  15. 29
      pages/setting/account.vue
  16. 11
      pages/setting/bind.vue
  17. 26
      pages/setting/createPwd.vue
  18. 48
      pages/setting/font.vue
  19. 198
      pages/setting/market.vue
  20. 10
      pages/setting/password.vue
  21. 48
      pages/setting/server.vue
  22. 45
      pages/setting/theme.vue
  23. BIN
      static/customer-service-platform/camera.png
  24. BIN
      static/customer-service-platform/cs-platform-back.png
  25. BIN
      static/customer-service-platform/ellipse-dc-img.png
  26. BIN
      static/customer-service-platform/empty-content.png
  27. BIN
      static/customer-service-platform/fail-icon.png
  28. BIN
      static/customer-service-platform/message.png
  29. BIN
      static/customer-service-platform/refresh-icon.png
  30. BIN
      static/customer-service-platform/robot-head.png
  31. BIN
      static/customer-service-platform/smile-icon.png
  32. BIN
      static/customer-service-platform/success-icon.png
  33. 4
      utils/http.js

7
.hbuilderx/launch.json

@ -3,10 +3,11 @@
"configurations" : [ "configurations" : [
{ {
"customPlaygroundType" : "local", "customPlaygroundType" : "local",
"playground" : "custom",
"playground" : "standard",
"type" : "uni-app:app-android" "type" : "uni-app:app-android"
// "playground" : "standard",
// "type" : "uni-app:app-ios"
} }
] ]
} }
// "playground" : "standard",
// "type" : "uni-app:app-ios"

46
api/customerServicePlatform/customerServicePlatform.js

@ -0,0 +1,46 @@
import { http } from '@/utils/http.js'
const baseURL = "http://39.101.133.168:8828"
//图片上传
export const uploadImageApi = (data) => {
return http({
method: 'POST',
url: baseURL +'/hljw/api/aws/upload',
data
})
}
//问题回答
export const getAnswerApi = (data) => {
return http({
method: 'POST',
url: 'http://pbb6edde.natappfree.cc' +'/api/customer/askQuestion',
data
})
}
//获取随机5条猜你想问问题
export const getQuestionApi = (data) => {
return http({
method: 'GET',
url: 'http://pbb6edde.natappfree.cc' +'/api/customer/getQuestion',
})
}
//反馈添加
export const addFeedbackRecordApi = (data) => {
return http({
method: 'POST',
url: baseURL +'/link/third/dcFeedBack/feedback/add',
data
})
}
//反馈历史记录
export const getFeedbackRecordsApi = (data) => {
return http({
method: 'POST',
url: baseURL+'/link/third/dcFeedBack/feedback/select',
data
})
}

8
api/setting/general.js

@ -9,3 +9,11 @@ export const getSetting = (data) => {
data: data, data: data,
}) })
} }
export const updateSetting = (data) => {
return http({
method: 'POST',
url: '/api/my/updateSetting',
data: data,
})
}

19
api/setting/market.js

@ -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,
})
}

92
api/tcpConnection.js

@ -6,7 +6,7 @@
*/ */
// 引用TCP插件 // 引用TCP插件
const TCPSocket = uni.requireNativePlugin('Aimer-TCPPlugin');
//const TCPSocket = uni.requireNativePlugin('Aimer-TCPPlugin');
// const TCPSocket = uni.requireNativePlugin("Aimer-TCPPlugin"); // const TCPSocket = uni.requireNativePlugin("Aimer-TCPPlugin");
// TCP连接配置 // TCP连接配置
@ -67,52 +67,52 @@ class TCPConnection {
} }
console.log('开始建立TCP连接:', connectionConfig); 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);
}
// 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.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 (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);
}
});
// // 执行回调函数
// if (callback && typeof callback === "function") {
// callback(result);
// }
// });
} }
/** /**
@ -144,7 +144,7 @@ class TCPConnection {
sendConfig.charsetname = config.charsetname; sendConfig.charsetname = config.charsetname;
} }
TCPSocket.send(sendConfig);
//TCPSocket.send(sendConfig);
console.log("js成功发送TCP消息:", messageStr); console.log("js成功发送TCP消息:", messageStr);
return true; return true;
} }
@ -159,7 +159,7 @@ class TCPConnection {
channel: channel channel: channel
}; };
TCPSocket.disconnect(disconnectConfig);
//TCPSocket.disconnect(disconnectConfig);
this.channelConnections.set(channel, false); this.channelConnections.set(channel, false);
console.log(`TCP连接已断开 - Channel ${channel}`, disconnectConfig); console.log(`TCP连接已断开 - Channel ${channel}`, disconnectConfig);
} }

31
components/DeepMate.vue

@ -34,8 +34,14 @@
</view> </view>
</view> </view>
<view class="deepmate-action"> <view class="deepmate-action">
<input class="stock-input" type="text" placeholder="请输入股票代码/名称,获取AI洞察" />
<view class="send-button-container">
<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> <image class="send-button" src="https://d31zlh4on95l9h.cloudfront.net/images/3da018821a5c82b06a1d6ddc81b960ac.png" mode="aspectFit"></image>
</view> </view>
</view> </view>
@ -49,7 +55,28 @@
name: 'DeepMate', name: 'DeepMate',
data() { data() {
return { 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 = ''
} }
} }
} }

196
components/FeedbackModal.vue

@ -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>

30
pages.json

@ -246,14 +246,12 @@
"path": "pages/setting/phone", "path": "pages/setting/phone",
"style": { "style": {
"navigationBarTitleText": "绑定账号" "navigationBarTitleText": "绑定账号"
//
} }
}, },
{ {
"path": "pages/setting/email", "path": "pages/setting/email",
"style": { "style": {
"navigationBarTitleText": "绑定账号" "navigationBarTitleText": "绑定账号"
//
} }
}, },
{ {
@ -306,7 +304,35 @@
{ {
"navigationBarTitleText" : "创建密码" "navigationBarTitleText" : "创建密码"
} }
},
{
"path": "pages/customerServicePlatform/csPlatformIndex",
"style": {
"navigationStyle": "custom",
"disableSwipeBack": true,
"titleNView": false,
"bounce": false
}
},
{
"path": "pages/customerServicePlatform/historyRecord",
"style": {
"navigationStyle": "custom",
"disableSwipeBack": true,
"titleNView": false,
"bounce": false
}
},
{
"path": "pages/customerServicePlatform/questionDetail",
"style": {
"navigationStyle": "custom",
"disableSwipeBack": true,
"titleNView": false,
"bounce": false
}
} }
], ],
"globalStyle": { "globalStyle": {
"navigationBarTextStyle": "black", "navigationBarTextStyle": "black",

682
pages/customerServicePlatform/csPlatformIndex.vue

@ -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>

353
pages/customerServicePlatform/historyRecord.vue

@ -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>

335
pages/customerServicePlatform/questionDetail.vue

@ -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>

21
pages/deepMate/deepMate.vue

@ -291,7 +291,7 @@ const { safeAreaInsets } = uni.getSystemInfoSync();
import { ref, computed, onMounted, onUnmounted, watch, nextTick } from "vue"; import { ref, computed, onMounted, onUnmounted, watch, nextTick } from "vue";
import footerBar from "../../components/footerBar"; import footerBar from "../../components/footerBar";
import marked from "marked"; // marked import marked from "marked"; // marked
import { onPageScroll } from "@dcloudio/uni-app";
import { onPageScroll, onLoad } from "@dcloudio/uni-app";
import { import {
postStock, postStock,
postIntent, postIntent,
@ -358,6 +358,25 @@ const hotTopics = ref([
}, },
]); ]);
//
onLoad((options) => {
console.log('deepMate页面接收到参数:', options);
// query
if (options.query) {
const decodedQuery = decodeURIComponent(options.query);
console.log('解码后的查询内容:', decodedQuery);
//
inputMessage.value = decodedQuery;
//
setTimeout(() => {
sendMessage();
}, 500);
}
});
// //
onMounted(() => { onMounted(() => {
const sys = uni.getSystemInfoSync(); const sys = uni.getSystemInfoSync();

1
pages/home/member.vue

@ -1,4 +1,5 @@
<template> <template>
<LoginPrompt ref="loginPrompt"></LoginPrompt>
<view class="main"> <view class="main">
<view class="top"> <view class="top">
<view class="bell"> <view class="bell">

7
pages/marketSituation/marketDetail.vue

@ -76,6 +76,7 @@ const marketTitle = ref();
const sortType = ref(""); // 'price' 'change' const sortType = ref(""); // 'price' 'change'
const sortOrder = ref("desc"); // 'asc' 'desc' const sortOrder = ref("desc"); // 'asc' 'desc'
const regionalGroupArray = ref([]);
// //
const stockList = ref([ const stockList = ref([
{ {
@ -206,17 +207,19 @@ const sortedStockList = computed(() => {
const getRegionalGroupList = async () => { const getRegionalGroupList = async () => {
try { try {
const result = await getRegionalGroupListAPI({ const result = await getRegionalGroupListAPI({
market: marketTitle.value,
name: marketTitle.value,
}); });
regionalGroupArray.value = result.data;
} catch (e) { } catch (e) {
console.error("获取区域分组列表失败:", e); console.error("获取区域分组列表失败:", e);
} }
}; };
// //
onLoad((options) => {
onLoad(async (options) => {
if (options && options.market) { if (options && options.market) {
marketTitle.value = options.market; marketTitle.value = options.market;
await getRegionalGroupList();
} }
}); });

29
pages/setting/account.vue

@ -27,9 +27,7 @@
</view> </view>
<view class="setting-item" @click="goToPassword"> <view class="setting-item" @click="goToPassword">
<text class="item-label"> <text class="item-label">
<template #default>
{{ userInfoRes.hasPwd === 0 ? '创建密码' : '修改密码' }} {{ userInfoRes.hasPwd === 0 ? '创建密码' : '修改密码' }}
</template>
</text> </text>
<uni-icons type="arrowright" size="16"></uni-icons> <uni-icons type="arrowright" size="16"></uni-icons>
</view> </view>
@ -67,19 +65,34 @@
ref, ref,
onMounted onMounted
} from 'vue' } from 'vue'
import {useUserStore} from "../../stores/modules/userInfo"
import { import {
getUserInfo getUserInfo
} from "@/api/member";
} from "@/api/member"
import {
useUserStore
} from "../../stores/modules/userInfo"
const userStore = useUserStore()
const iSMT = ref(0) const iSMT = ref(0)
// const dccode = ref('') // const dccode = ref('')
const userInfoRes = ref({}) const userInfoRes = ref({})
const showLogout = ref(false) const showLogout = ref(false)
const userStore = useUserStore()
const handleConfirmLogout = () => {
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() userStore.clearUserInfo()
showLogout.value = false showLogout.value = false
uni.showToast({ uni.showToast({

11
pages/setting/bind.vue

@ -8,7 +8,9 @@
<view class="top-list" @click="goToBindPhone"> <view class="top-list" @click="goToBindPhone">
<text class="label">手机号</text> <text class="label">手机号</text>
<view class="right"> <view class="right">
<text style="font-size: 28rpx;">未绑定</text>
<text style="font-size: 28rpx;">
{{ userStore.userInfo?.phone || '未绑定' }}
</text>
<uni-icons type="arrowright" size="16" /> <uni-icons type="arrowright" size="16" />
</view> </view>
</view> </view>
@ -16,7 +18,9 @@
<view class="top-list" @click="goToBindEmail"> <view class="top-list" @click="goToBindEmail">
<text class="label">邮箱</text> <text class="label">邮箱</text>
<view class="right"> <view class="right">
<text style="font-size: 28rpx;">analsak@163.com</text>
<text style="font-size: 28rpx;">
{{ userStore.userInfo?.email || '未绑定' }}
</text>
<uni-icons type="arrowright" size="16" /> <uni-icons type="arrowright" size="16" />
</view> </view>
</view> </view>
@ -29,6 +33,8 @@
ref, ref,
onMounted onMounted
} from 'vue' } from 'vue'
import { useUserStore } from "../../stores/modules/userInfo"
const userStore = useUserStore()
const iSMT = ref(0) const iSMT = ref(0)
const goToBindPhone = () =>{ const goToBindPhone = () =>{
uni.navigateTo({ uni.navigateTo({
@ -46,6 +52,7 @@
// //
iSMT.value = uni.getSystemInfoSync().statusBarHeight; iSMT.value = uni.getSystemInfoSync().statusBarHeight;
console.log('看看高度', iSMT.value) console.log('看看高度', iSMT.value)
console.log('看看用户信息',userStore.userInfo)
}) })
</script> </script>

26
pages/setting/createPwd.vue

@ -83,13 +83,24 @@
} }
const goToPwdNext = async () => {
if (!userEmail.value) {
uni.showToast({
title: '请输入邮箱',
icon: 'none'
})
return
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) { if (!verifyCode.value) {
uni.showToast({ uni.showToast({
@ -99,6 +110,7 @@ const goToPwdNext = async () => {
return return
} }
try { try {
let param; let param;
if (activeTab.value === 'email') { if (activeTab.value === 'email') {

48
pages/setting/font.vue

@ -5,17 +5,17 @@
<view class="top-list"> <view class="top-list">
<text>标准</text> <text>标准</text>
<radio value="0" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 0" <radio value="0" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 0"
@click="selectFont(0)" />
@click="selectFont('small')" />
</view> </view>
<view class="top-list"> <view class="top-list">
<text>中号</text> <text>中号</text>
<radio value="1" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 1" <radio value="1" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 1"
@click="selectFont(1)" />
@click="selectFont('medium')" />
</view> </view>
<view class="top-list"> <view class="top-list">
<text>大号</text> <text>大号</text>
<radio value="2" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 2" <radio value="2" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 2"
@click="selectFont(2)" />
@click="selectFont('large')" />
</view> </view>
</view> </view>
</view> </view>
@ -27,32 +27,50 @@
onMounted onMounted
} from 'vue' } from 'vue'
import { import {
getSetting
getSetting,
updateSetting
} from "@/api/setting/general" } from "@/api/setting/general"
const iSMT = ref(0) const iSMT = ref(0)
const selectedIndex = ref(0) const selectedIndex = ref(0)
const fontTypeMap = {
'small': 0,
'medium': 1,
'large': 2
}
const getFont = async () => { const getFont = async () => {
try { try {
const res = await getSetting() const res = await getSetting()
if (res.code === 200) { if (res.code === 200) {
const fontSize = res.data.fontSize const fontSize = res.data.fontSize
const sizeMap = {
small: 0,
medium: 1,
large: 2
}
console.log('看看字体', res.data.fontSize)
selectedIndex.value = sizeMap[fontSize] ?? 0;
selectedIndex.value = fontTypeMap[fontSize] ?? 0
} }
} catch (err) { } catch (err) {
console.error("获取字体设置失败:", err);
console.error("获取字体设置失败:", err)
} }
} }
const selectFont = (index) => {
selectedIndex.value = index
console.log('看看选中状态', selectedIndex.value)
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(() => { onMounted(() => {
// //

198
pages/setting/market.vue

@ -8,83 +8,138 @@
<view style="height:57.5vh;background-color: white;"> <view style="height:57.5vh;background-color: white;">
<view class="title">A股竞价</view> <view class="title">A股竞价</view>
<view class="top-options"> <view class="top-options">
<view class="option-btn" :class="{ 'active': aStockBid === 0 }" @click="aStockBid = 0">
<view
class="option-btn"
:class="{ 'active': aStockBid === 'auto' }"
@click="handleAStockBidChange('auto')"
>
<text>智能开启</text> <text>智能开启</text>
<view class="active-dot" v-if="aStockBid === 0"></view>
<view class="active-dot" v-if="aStockBid === 'auto'"></view>
</view> </view>
<view class="option-btn" :class="{ 'active': aStockBid === 1 }" @click="aStockBid = 1">
<view
class="option-btn"
:class="{ 'active': aStockBid === 'open' }"
@click="handleAStockBidChange('open')"
>
<text>保持开启</text> <text>保持开启</text>
<view class="active-dot" v-if="aStockBid === 1"></view>
<view class="active-dot" v-if="aStockBid === 'open'"></view>
</view> </view>
<view class="option-btn" :class="{ 'active': aStockBid === 2 }" @click="aStockBid = 2">
<view
class="option-btn"
:class="{ 'active': aStockBid === 'close' }"
@click="handleAStockBidChange('close')"
>
<text>保持关闭</text> <text>保持关闭</text>
<view class="active-dot" v-if="aStockBid === 2"></view>
<view class="active-dot" v-if="aStockBid === 'close'"></view>
</view> </view>
</view> </view>
<view class="title">K线样式</view> <view class="title">K线样式</view>
<view class="top-options"> <view class="top-options">
<view class="option-btn" :class="{ 'active': kStyle === 0 }" @click="kStyle = 0">
<view
class="option-btn"
:class="{ 'active': kStyle === 'common' }"
@click="handleKStyleChange('common')"
>
<img src="/static/my/common.png" class="kline-icon" /> <img src="/static/my/common.png" class="kline-icon" />
<text>普通</text> <text>普通</text>
<view class="active-dot" v-if="kStyle === 0"></view>
<view class="active-dot" v-if="kStyle === 'common'"></view>
</view> </view>
<view class="option-btn" :class="{ 'active': kStyle === 1 }" @click="kStyle = 1">
<view
class="option-btn"
:class="{ 'active': kStyle === 'Outline' }"
@click="handleKStyleChange('Outline')"
>
<img src="/static/my/outline.png" class="kline-icon" /> <img src="/static/my/outline.png" class="kline-icon" />
<text>轮廓图</text> <text>轮廓图</text>
<view class="active-dot" v-if="kStyle === 1"></view>
<view class="active-dot" v-if="kStyle === 'Outline'"></view>
</view> </view>
<view class="option-btn" :class="{ 'active': kStyle === 2 }" @click="kStyle = 2">
<view
class="option-btn"
:class="{ 'active': kStyle === 'polylines' }"
@click="handleKStyleChange('polylines')"
>
<img src="/static/my/polylines.png" class="kline-icon" /> <img src="/static/my/polylines.png" class="kline-icon" />
<text>折线图</text> <text>折线图</text>
<view class="active-dot" v-if="kStyle === 2"></view>
<view class="active-dot" v-if="kStyle === 'polylines'"></view>
</view> </view>
</view> </view>
<view class="title">除权类型</view> <view class="title">除权类型</view>
<view class="top-options"> <view class="top-options">
<view class="option-btn" :class="{ 'active': exRights === 0 }" @click="exRights = 0">
<view
class="option-btn"
:class="{ 'active': exRights === 'exRights' }"
@click="handleExRightsChange('exRights')"
>
<text>除权</text> <text>除权</text>
<view class="active-dot" v-if="exRights === 0"></view>
<view class="active-dot" v-if="exRights === 'exRights'"></view>
</view> </view>
<view class="option-btn" :class="{ 'active': exRights === 1 }" @click="exRights = 1">
<view
class="option-btn"
:class="{ 'active': exRights === 'normal' }"
@click="handleExRightsChange('normal')"
>
<text>普通</text> <text>普通</text>
<view class="active-dot" v-if="exRights === 1"></view>
<view class="active-dot" v-if="exRights === 'normal'"></view>
</view> </view>
<view class="option-btn" :class="{ 'active': exRights === 2 }" @click="exRights = 2">
<view
class="option-btn"
:class="{ 'active': exRights === 'Weighted' }"
@click="handleExRightsChange('Weighted')"
>
<text>加权</text> <text>加权</text>
<view class="active-dot" v-if="exRights === 2"></view>
<view class="active-dot" v-if="exRights === 'Weighted'"></view>
</view> </view>
</view> </view>
<view class="title">涨跌颜色</view> <view class="title">涨跌颜色</view>
<view class="top-options"> <view class="top-options">
<view class="option-btn" :class="{ 'active': rfColor === 0 }" @click="rfColor = 0">
<view
class="option-btn"
:class="{ 'active': rfColor === 'green' }"
@click="handleRfColorChange('green')"
>
<view class="color-icon"> <view class="color-icon">
<img src="/static/my/greenRise.png" class="kline-icon" /> <img src="/static/my/greenRise.png" class="kline-icon" />
</view> </view>
<text>绿涨红跌</text> <text>绿涨红跌</text>
<view class="active-dot" v-if="rfColor === 0"></view>
<view class="active-dot" v-if="rfColor === 'green'"></view>
</view> </view>
<view class="option-btn" :class="{ 'active': rfColor === 1 }" @click="rfColor = 1">
<view
class="option-btn"
:class="{ 'active': rfColor === 'red' }"
@click="handleRfColorChange('red')"
>
<view class="color-icon"> <view class="color-icon">
<img src="/static/my/redRise.png" class="kline-icon" /> <img src="/static/my/redRise.png" class="kline-icon" />
</view> </view>
<text>红涨绿跌</text> <text>红涨绿跌</text>
<view class="active-dot" v-if="rfColor === 1"></view>
<view class="active-dot" v-if="rfColor === 'red'"></view>
</view> </view>
</view> </view>
<view class="title">副图指标个数</view> <view class="title">副图指标个数</view>
<view class="top-options"> <view class="top-options">
<view class="option-btn" :class="{ 'active': indexCount === 0 }" @click="indexCount = 0">
<view
class="option-btn"
:class="{ 'active': indexCount === 1 }"
@click="handleIndexCountChange(1)"
>
<text>1</text> <text>1</text>
</view> </view>
<view class="option-btn" :class="{ 'active': indexCount === 1 }" @click="indexCount = 1">
<view
class="option-btn"
:class="{ 'active': indexCount === 2 }"
@click="handleIndexCountChange(2)"
>
<text>2</text> <text>2</text>
</view> </view>
<view class="option-btn" :class="{ 'active': indexCount === 2 }" @click="indexCount = 2">
<view
class="option-btn"
:class="{ 'active': indexCount === 3 }"
@click="handleIndexCountChange(3)"
>
<text>3</text> <text>3</text>
</view> </view>
</view> </view>
@ -107,20 +162,91 @@
</template> </template>
<script setup> <script setup>
import {
ref,
onMounted
} from 'vue'
import { ref, onMounted } from 'vue'
import { getMarketSetting, updateMarketSetting } from "@/api/setting/market"
const iSMT = ref(0) const iSMT = ref(0)
const aStockBid = ref(0) // Stock bidding
const kStyle = ref(0) // k线
const exRights = ref(0) // Ex-rights
const rfColor = ref(0) // rise-fall
const indexCount = ref(0) //
const aStockBid = ref('auto') // Aauto/open/close
const kStyle = ref('common') // K线common/Outline/polylines
const exRights = ref('exRights') // exRights/normal/Weighted
const rfColor = ref('green') // green/red
const indexCount = ref(1) // 1/2/3
const indicatorList = ref(['K线', '均线', '成交量', 'KDJ', 'MACD', 'RSI']) 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(() => { onMounted(() => {
iSMT.value = uni.getSystemInfoSync().statusBarHeight; iSMT.value = uni.getSystemInfoSync().statusBarHeight;
getMarketSettings()
}) })
</script> </script>
@ -206,7 +332,7 @@
.indicator-text { .indicator-text {
font-size: 28rpx; font-size: 28rpx;
flex:1;
flex: 1;
} }
.indicator-icons { .indicator-icons {

10
pages/setting/password.vue

@ -84,6 +84,7 @@
} }
const goToPwdNext = async () => { const goToPwdNext = async () => {
if (activeTab.value === 'email') {
if (!userEmail.value) { if (!userEmail.value) {
uni.showToast({ uni.showToast({
title: '请输入邮箱', title: '请输入邮箱',
@ -91,6 +92,15 @@ const goToPwdNext = async () => {
}) })
return return
} }
}else{
if (!userPhone.value) {
uni.showToast({
title: '请输入手机号',
icon: 'none'
})
return
}
}
if (!verifyCode.value) { if (!verifyCode.value) {
uni.showToast({ uni.showToast({
title: '请输入验证码', title: '请输入验证码',

48
pages/setting/server.vue

@ -5,17 +5,17 @@
<view class="top-list"> <view class="top-list">
<text>自动选择</text> <text>自动选择</text>
<radio value="0" class="radio-btn" activeBackgroundColor="red" <radio value="0" class="radio-btn" activeBackgroundColor="red"
:checked="selectedIndex === 0" @click="selectFont(0)" />
:checked="selectedIndex === 0" @click="selectFont('auto')" />
</view> </view>
<view class="top-list"> <view class="top-list">
<text>新加坡服务器</text> <text>新加坡服务器</text>
<radio value="1" class="radio-btn" activeBackgroundColor="red" <radio value="1" class="radio-btn" activeBackgroundColor="red"
:checked="selectedIndex === 1" @click="selectFont(1)" />
:checked="selectedIndex === 1" @click="selectFont('singapore')" />
</view> </view>
<view class="top-list"> <view class="top-list">
<text>香港服务器</text> <text>香港服务器</text>
<radio value="2" class="radio-btn" activeBackgroundColor="red" <radio value="2" class="radio-btn" activeBackgroundColor="red"
:checked="selectedIndex === 2" @click="selectFont(2)" />
:checked="selectedIndex === 2" @click="selectFont('hongkong')" />
</view> </view>
</view> </view>
</view> </view>
@ -27,33 +27,51 @@
onMounted onMounted
} from 'vue' } from 'vue'
import { import {
getSetting
getSetting,
updateSetting
} from "@/api/setting/general" } from "@/api/setting/general"
const iSMT = ref(0) const iSMT = ref(0)
const selectedIndex = ref(0) const selectedIndex = ref(0)
const servertypeMap = {
'auto': 0,
'singapore': 1,
'hongkong': 2
}
const getServer = async () => { const getServer = async () => {
try { try {
const res = await getSetting() const res = await getSetting()
if (res.code === 200) { if (res.code === 200) {
const fontSize = res.data.fontSize
const sizeMap = {
'auto': 0,
'singapore': 1,
'hongkong': 2
}
console.log('看看服务器', res.data.fontSize)
selectedIndex.value = sizeMap[fontSize] ?? 0;
const serverSelection = res.data.serverSelection
selectedIndex.value = servertypeMap[serverSelection] ?? 0;
} }
} catch (err) { } catch (err) {
console.error("获取服务器设置失败:", err); console.error("获取服务器设置失败:", err);
} }
} }
const selectFont = (index) => {
selectedIndex.value = index
console.log('看看选中状态',selectedIndex.value)
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(() => { onMounted(() => {

45
pages/setting/theme.vue

@ -5,12 +5,12 @@
<view class="left"> <view class="left">
<image class="img-theme" src="/static/my/whiteTheme.png" mode="widthFix" /> <image class="img-theme" src="/static/my/whiteTheme.png" mode="widthFix" />
<radio value="0" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 0" <radio value="0" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 0"
@click="selectFont(0)" />
@click="updateTheme('light')" />
</view> </view>
<view class="left"> <view class="left">
<image class="img-theme" src="/static/my/blackTheme.png" mode="widthFix" /> <image class="img-theme" src="/static/my/blackTheme.png" mode="widthFix" />
<radio value="1" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 1" <radio value="1" class="radio-btn" activeBackgroundColor="red" :checked="selectedIndex === 1"
@click="selectFont(1)" />
@click="updateTheme('dark')" />
</view> </view>
</view> </view>
</view> </view>
@ -22,32 +22,51 @@
onMounted onMounted
} from 'vue' } from 'vue'
import { import {
getSetting
getSetting,
updateSetting
} from "@/api/setting/general" } from "@/api/setting/general"
const iSMT = ref(0) const iSMT = ref(0)
const selectedIndex = ref(0) const selectedIndex = ref(0)
const themeTypeMap = {
'light': 0,
'dark': 1
}
const getTheme = async () => { const getTheme = async () => {
try { try {
const res = await getSetting() const res = await getSetting()
if (res.code === 200) { if (res.code === 200) {
const theme = res.data.theme const theme = res.data.theme
const sizeMap = {
'light': 0,
'dark': 1
}
console.log('看看主题', res.data.theme)
selectedIndex.value = sizeMap[theme] ?? 0;
selectedIndex.value = themeTypeMap[theme] ?? 0
} }
} catch (err) { } catch (err) {
console.error("获取主题设置失败:", err); console.error("获取主题设置失败:", err);
} }
} }
const selectFont = (index) => {
selectedIndex.value = index
console.log('看看选中状态', selectedIndex.value)
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(() => { onMounted(() => {
// //
iSMT.value = uni.getSystemInfoSync().statusBarHeight; iSMT.value = uni.getSystemInfoSync().statusBarHeight;

BIN
static/customer-service-platform/camera.png

After

Width: 18  |  Height: 16  |  Size: 428 B

BIN
static/customer-service-platform/cs-platform-back.png

After

Width: 20  |  Height: 20  |  Size: 247 B

BIN
static/customer-service-platform/ellipse-dc-img.png

After

Width: 60  |  Height: 60  |  Size: 2.1 KiB

BIN
static/customer-service-platform/empty-content.png

After

Width: 213  |  Height: 228  |  Size: 18 KiB

BIN
static/customer-service-platform/fail-icon.png

After

Width: 100  |  Height: 100  |  Size: 4.2 KiB

BIN
static/customer-service-platform/message.png

After

Width: 18  |  Height: 18  |  Size: 523 B

BIN
static/customer-service-platform/refresh-icon.png

After

Width: 15  |  Height: 15  |  Size: 339 B

BIN
static/customer-service-platform/robot-head.png

After

Width: 21  |  Height: 19  |  Size: 1.2 KiB

BIN
static/customer-service-platform/smile-icon.png

After

Width: 16  |  Height: 16  |  Size: 440 B

BIN
static/customer-service-platform/success-icon.png

After

Width: 100  |  Height: 100  |  Size: 4.2 KiB

4
utils/http.js

@ -2,8 +2,8 @@ import { useUserStore } from "../stores/modules/userInfo"
import { useDeviceStore } from "../stores/modules/deviceInfo" import { useDeviceStore } from "../stores/modules/deviceInfo"
import { useLoginStore } from "../stores/modules/login" import { useLoginStore } from "../stores/modules/login"
const baseURL = "https://dbqb.nfdxy.net/testApi"
// const baseURL = "https://dbqb.nfdxy.net/testApi"
const baseURL = "http://192.168.40.8:9000"
const httpInterceptor = { const httpInterceptor = {
// 拦截前触发 // 拦截前触发

Loading…
Cancel
Save