电脑端聊天客服系统完成

This commit is contained in:
2025-06-13 14:58:47 +08:00
parent ac85206085
commit e74f5b8d75
41 changed files with 4348 additions and 957 deletions

View File

@@ -1,7 +1,7 @@
<template>
<div id="app">
<router-view class="page" />
<!-- <ChatWidget v-if="!$route.path.includes('/customerService') && !$isMobile" /> -->
<ChatWidget v-if="!$route.path.includes('/customerService') && !$isMobile" />
</div>
</template>
<script >

File diff suppressed because it is too large Load Diff

View File

@@ -89,6 +89,8 @@ export const ChatWidget_zh = {
serverLimitError:"服务器连接数已达上限,请稍后刷新重试",
identityError:"用户身份设置失败,请刷新页面重试",
emailError:"用户信息获取失败,请刷新页面重试",
refreshPage:"刷新页面",
reconnectSuccess:"重新连接成功",
},
@@ -186,6 +188,8 @@ export const ChatWidget_en = {
serverLimitError:"Connection limit reached, please try again later",
identityError:"Failed to set user identity, please refresh page",
emailError:"Failed to get user information, please refresh page",
refreshPage:"Refresh page",
reconnectSuccess:"Reconnect successfully",
}

File diff suppressed because one or more lines are too long

View File

@@ -4,6 +4,13 @@ import { Notification, MessageBox, Message } from 'element-ui'
import loadingManager from './loadingManager';
import errorNotificationManager from './errorNotificationManager';
const pendingRequestMap = new Map(); //处理Request aborted 错误
function getRequestKey(config) { //处理Request aborted 错误 生成唯一 key 的函数
const { url, method, params, data } = config;
return [url, method, JSON.stringify(params), JSON.stringify(data)].join('&');
}
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分
@@ -143,9 +150,6 @@ window.addEventListener('online', () => {
});
});
// 使用错误提示管理器控制网络断开提示
window.addEventListener('offline', () => {
if (window.vm && window.vm.$message && errorNotificationManager.canShowError('networkOffline')) {
@@ -215,6 +219,22 @@ service.interceptors.request.use(config => {
config.params = {};
config.url = url;
}
// 生成请求唯一key 处理Request aborted 错误
const requestKey = getRequestKey(config);
// 如果有相同请求,先取消 处理Request aborted 错误
if (pendingRequestMap.has(requestKey)) {
const cancel = pendingRequestMap.get(requestKey);
cancel(); // 取消上一次请求
pendingRequestMap.delete(requestKey);
}
// 创建新的CancelToken 处理Request aborted 错误
config.cancelToken = new axios.CancelToken(cancel => {
pendingRequestMap.set(requestKey, cancel);
});
return config
}, error => {
Promise.reject(error)
@@ -222,6 +242,10 @@ service.interceptors.request.use(config => {
// 响应拦截器
service.interceptors.response.use(res => {
// 请求完成后移除
const requestKey = getRequestKey(res.config);
pendingRequestMap.delete(requestKey);
// 未设置状态码则默认成功状态
const code = res.data.code || 200;
// 获取错误信息
@@ -284,8 +308,15 @@ service.interceptors.response.use(res => {
},
error => {
if (error.message && error.message.includes('canceled') || error.message.includes('Request aborted')) {
// 主动取消的请求,直接忽略,不提示
return Promise.reject(error);
}
// 请求异常也要移除 处理Request aborted 错误
if (error.config) {
const requestKey = getRequestKey(error.config);
pendingRequestMap.delete(requestKey);
}
let { message } = error;

View File

@@ -89,13 +89,13 @@
<p>{{ $t(`ServiceTerms.clauseTermination2`) }}</p>
</div>
</section>
<section class="clauseBox">
<!-- <section class="clauseBox">
<h5>{{ $t(`ServiceTerms.title11`) }}</h5>
<div class="textBox">
<p>{{ $t(`ServiceTerms.clauseLaw1`) }}</p>
<p>{{ $t(`ServiceTerms.clauseLaw2`) }}</p>
</div>
</section>
</section> -->
</section>
@@ -189,13 +189,13 @@
<p>{{ $t(`ServiceTerms.clauseTermination2`) }}</p>
</div>
</section>
<section class="clauseBox">
<!-- <section class="clauseBox">
<h3>{{ $t(`ServiceTerms.title11`) }}</h3>
<div class="textBox">
<p>{{ $t(`ServiceTerms.clauseLaw1`) }}</p>
<p>{{ $t(`ServiceTerms.clauseLaw2`) }}</p>
</div>
</section>
</section> -->
</section>

File diff suppressed because it is too large Load Diff

View File

@@ -276,20 +276,20 @@ export default {
name: "GH/s",
nameTextStyle: {
padding: [0, 0, 0, -40],
},
axisLabel: {
formatter: function (value) {
// let data
// if (value > 10000000) {
// data = `${(value / 10000000)} KW`
// } else if (value > 1000000) {
// data = `${(value / 1000000)} M`
// } else if (value / 10000) {
// data = `${(value / 10000)} W`
// }
return value
}
}
// axisLabel: {
// formatter: function (value) {
// let data
// if (value > 10000000) {
// data = `${(value / 10000000)} KW`
// } else if (value > 1000000) {
// data = `${(value / 1000000)} M`
// } else if (value / 10000) {
// data = `${(value / 10000)} W`
// }
// return data
// }
// }
},
{
position: "right",
@@ -833,6 +833,7 @@ export default {
const maxValue = Math.max(...this.option.series[0].data); // 获取数据最大值
const formatter = yAxis.axisLabel.formatter;
const formattedValue = formatter(maxValue); // 格式化最大值
// 创建一个临时 DOM 元素计算宽度
const tempDiv = document.createElement('div');
@@ -847,11 +848,30 @@ export default {
// 动态设置 grid.left加上安全边距
const safeMargin = 20;
this.option.grid.left = labelWidth + safeMargin + 'px';
// this.$nextTick(
// // 更新图表
// this.inCharts()
// )
// ------------全网算力图---------------
//
const yAxis2 = this.minerOption.yAxis[0]; // 第一个 Y 轴
const maxValue2 = Math.max(...this.minerOption.series[0].data); // 获取数据最大值
const formatter2 = yAxis2.axisLabel.formatter;
const formattedValue2 = formatter2(maxValue2); // 格式化最大值
// 创建一个临时 DOM 元素计算宽度
const tempDiv2 = document.createElement('div');
tempDiv2.style.position = 'absolute';
tempDiv2.style.visibility = 'hidden';
tempDiv2.style.fontSize = '12px'; // 与 axisLabel.fontSize 一致
tempDiv2.innerText = formattedValue2;
document.body.appendChild(tempDiv2);
const labelWidth2 = tempDiv2.offsetWidth;
document.body.removeChild(tempDiv2);
// 动态设置 grid.left加上安全边距
const safeMargin2 = 20;
this.minerOption.grid.left = labelWidth2 + safeMargin2 + 'px';
}
@@ -888,6 +908,30 @@ export default {
methods: {
handelOptionYAxis(option){
const yAxis = option.yAxis[0]; // 第一个 Y 轴
const maxValue = Math.max(...option.series[0].data); // 获取数据最大值
const formatter = yAxis.axisLabel.formatter;
const formattedValue = formatter(maxValue); // 格式化最大值
// 创建一个临时 DOM 元素计算宽度
const tempDiv = document.createElement('div');
tempDiv.style.position = 'absolute';
tempDiv.style.visibility = 'hidden';
tempDiv.style.fontSize = '12px'; // 与 axisLabel.fontSize 一致
tempDiv.innerText = formattedValue;
document.body.appendChild(tempDiv);
const labelWidth = tempDiv.offsetWidth;
document.body.removeChild(tempDiv);
// 动态设置 grid.left加上安全边距
const safeMargin = 20;
option.grid.left = labelWidth + safeMargin + 'px';
return option
},
slideLeft() {
const allLength = this.currencyList.length * 120
const boxLength = document.getElementById('list-box').clientWidth
@@ -940,7 +984,7 @@ export default {
this.MinerChart = echarts.init(document.getElementById("minerChart"));
}
this.minerOption= this.handelOptionYAxis(this.minerOption) // Y轴文字显示动态调整grid.left
this.minerOption.series[0].name = this.$t(`home.networkPower`)
this.minerOption.series[1].name = this.$t(`home.currencyPrice`)
this.MinerChart.setOption(this.minerOption);

View File

@@ -214,6 +214,7 @@ import {
import { encryption } from "../../utils/fun";
import { getAccountList } from "../../api/personalCenter";
export default {
data() {
return {
@@ -372,6 +373,11 @@ export default {
JSON.stringify(data.data.access_token)
);
// 等待一小段时间确保写入完成
await new Promise(resolve => setTimeout(resolve, 50));
// 登录成功后
this.$bus.$emit('user-logged-in'); // 触发登录成功全局事件
this.fetchAccountList();
this.fetchAccountGradeList();
this.fetchJurisdiction();

View File

@@ -1,6 +1,7 @@
import { getCheck,getAddBalace, getAddMinerAccount, getAccountList, getDelMinerAccount, getMinerAccountBalance, getCheckAccount,getCheckBalance,getIfBind } from "../../../api/personalCenter"
import {getAccountGradeList } from "../../../api/login"
import { Debounce,throttle }from "../../../utils/publicMethods";
export default {
data() {
@@ -542,13 +543,7 @@ export default {
}
},
confirmAdd() {
// this.accountList.push({
// account: this.params.account,
// miningPool: this.params.miningPool,
// currency: this.params.miningPool,
// remarks: this.params.remarks,
// })
confirmAdd:Debounce(function(){
if (!this.AccountParams.ma) {
this.$message({
message: this.$t(`personal.accountNumber`),
@@ -600,12 +595,66 @@ export default {
}
this.fetchCheck({ coin: this.AccountParams.coin, ma: this.AccountParams.ma,balance: this.AccountParams.balance})
// this.fetchCheckAccount({ coin: this.AccountParams.coin, ma: this.AccountParams.ma })
},200),
// confirmAdd() {
// if (!this.AccountParams.ma) {
// this.$message({
// message: this.$t(`personal.accountNumber`),
// type: "error",
// showClose: true
// });
// return
// }
// if (!this.AccountParams.balance) {
// this.$message({
// message: this.$t(`personal.inputWalletAddress`),
// type: "error",
// showClose: true
// });
// return
// }
// if (!this.AccountParams.coin) {
// this.$message({
// message:this.$t(`personal.selectCurrency`),
// type: "error",
// showClose: true
// });
// return
// }
// if (!this.AccountParams.code && this.isItBound) {
// this.$message({
// showClose: true,
// message: this.$t(`personal.gCode`),
// type: 'error'
// });
// return
// }
// // 账户只能输入字母、数字、下划线且不能以数字开头长度不小于4位不大于24位
// const regexAccount=/^[a-zA-Z_][a-zA-Z0-9_]{3,23}$/
// const PasswordIsValid = regexAccount.test(this.AccountParams.ma);
// if (!PasswordIsValid) {
// // 如果输入不符合要求,可以根据具体需求给出错误提示或进行其他处理
// this.$message({
// message: this.$t(`personal.accountFormat`),
// type: "error",
// showClose: true
// });
// return;
// }
// this.fetchCheck({ coin: this.AccountParams.coin, ma: this.AccountParams.ma,balance: this.AccountParams.balance})
// // this.fetchCheckAccount({ coin: this.AccountParams.coin, ma: this.AccountParams.ma })
},
// },
handelAddClose(){
for (let key in this.AccountParams) {
this.AccountParams[key] = "";

View File

@@ -247,7 +247,7 @@
import { getResetPwd, getResetPwdCode } from "../../api/login";
import { encryption } from "../../utils/fun";
import { getEmailIfBind } from "../../api/personalCenter";
import { Debounce,throttle }from "../../utils/publicMethods";
export default {
data() {
return {
@@ -419,6 +419,10 @@ export default {
type: "success",
showClose: true,
});
for (const key in this.loginForm) {//清空表单
this.loginForm[key] = "";
}
this.$router.push(`/${this.lang}/login`);
}
},
@@ -521,7 +525,8 @@ export default {
}
});
},
submitForm() {
submitForm:Debounce(function(){
this.$refs.ruleForm.validate((valid) => {
if (valid) {
//去空格
@@ -596,7 +601,83 @@ export default {
this.fetchResetPwd(form);
}
});
},
},200),
// submitForm() {
// this.$refs.ruleForm.validate((valid) => {
// if (valid) {
// //去空格
// this.loginForm.userName = this.loginForm.email.trim();
// this.loginForm.password = this.loginForm.password.trim();
// this.loginForm.newPassword = this.loginForm.newPassword.trim();
// if (this.loginForm.password !== this.loginForm.newPassword) {
// this.$message({
// message: this.$t(`user.confirmPassword2`),
// type: "error",
// customClass: "messageClass",
// showClose: true,
// });
// return;
// }
// //邮箱格式验证
// const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+.[a-zA-Z]{2,}$/;
// let isMailbox = emailRegex.test(this.loginForm.email);
// if (!isMailbox) {
// this.$message({
// message: this.$t(`user.emailVerification`),
// type: "error",
// customClass: "messageClass",
// showClose: true,
// });
// return;
// //用户名规则1.长度限制3<=用户名<=16; 字符限制:仅允许使用字母、数字、下划线 用户名必须以字母开头
// // const regex = /^[a-zA-Z][a-zA-Z0-9_]{2,15}$/; // 正则表达式
// // const isValid = regex.test(this.loginForm.email);
// // if (!isValid) {
// // // 如果输入不符合要求,可以根据具体需求给出错误提示或进行其他处理
// // this.$message({
// // message: this.$t(`user.accountReminder`),
// // type: "error",
// // customClass: "messageClass",
// // showClose: true
// // });
// // return;
// // }
// }
// // 密码验证 8<=密码<=32 包含大小写字母、数字和特殊字符(!@#¥%……&*
// const regexPassword =
// /^(?!.*[\u4e00-\u9fa5])(?![a-zA-Z]+$)(?![A-Z0-9]+$)(?![A-Z\W_]+$)(?![a-z0-9]+$)(?![a-z\W_]+$)(?![0-9\W_]+$)[a-zA-Z0-9\W_]{8,32}$/; // 正则表达式
// const PasswordIsValid = regexPassword.test(this.loginForm.password);
// if (!PasswordIsValid) {
// // 如果输入不符合要求,可以根据具体需求给出错误提示或进行其他处理
// this.$message({
// message: this.$t(`user.PasswordReminder`),
// type: "error",
// showClose: true,
// });
// return;
// }
// // ,gCode: this.loginForm.gCode,
// let obj = {
// email: this.loginForm.email,
// password: this.loginForm.password,
// resetPwdCode: this.loginForm.resetPwdCode,
// };
// //加密
// // const form = { ...this.loginForm };
// // form.password = encryption(this.loginForm.password);
// const form = { ...obj };
// form.password = encryption(obj.password);
// this.fetchResetPwd(form);
// }
// });
// },
handleClick() {
this.$router.push(`/${this.lang}`);
},

View File

@@ -103,11 +103,21 @@ export default {
data: this.FormDatas,
}).then(res => {
console.log(res,"文件返回");
if (res.status == 200 && res.data.code != 200) {
this.$message.error(res.data.msg);
return
}
this.ruleForm.files = res.data.data.id
// if (this.ruleForm.files) {//成功拿到返回ID
// this.fetchSubmitWork(this.ruleForm)
// }
if (this.ruleForm.files) {//成功拿到返回ID
this.fetchSubmitWork(this.ruleForm)
}
})
} else {