diff --git a/mining-pool/.env.development b/mining-pool/.env.development index d6273a0..bb41560 100644 --- a/mining-pool/.env.development +++ b/mining-pool/.env.development @@ -5,8 +5,8 @@ VUE_APP_TITLE = m2pool ENV = 'development' #开发环境 -VUE_APP_BASE_API = 'http://test.m2pool.com/api/' +VUE_APP_BASE_API = 'https://test.m2pool.com/api/' # VUE_APP_BASE_API = 'http://18.183.240.108:8080/api/' -VUE_APP_BASE_URL = 'http://test.m2pool.com/' +VUE_APP_BASE_URL = 'https://test.m2pool.com/' # 路由懒加载 VUE_CLI_BABEL_TRANSPILE_MODULES = true diff --git a/mining-pool/.env.staging b/mining-pool/.env.staging index b8c0c6f..d7db6d9 100644 --- a/mining-pool/.env.staging +++ b/mining-pool/.env.staging @@ -8,8 +8,8 @@ ENV = 'staging' # 测试环境 # VUE_APP_BASE_API = 'http://18.183.240.108:8080/api/' -VUE_APP_BASE_API = 'http://test.m2pool.com/api/' -VUE_APP_BASE_URL = 'http://test.m2pool.com/' +VUE_APP_BASE_API = 'https://test.m2pool.com/api/' +VUE_APP_BASE_URL = 'https://test.m2pool.com/' # 路由懒加载 VUE_CLI_BABEL_TRANSPILE_MODULES = true \ No newline at end of file diff --git a/mining-pool/dist.zip b/mining-pool/dist.zip index ff6691d..14db860 100644 Binary files a/mining-pool/dist.zip and b/mining-pool/dist.zip differ diff --git a/mining-pool/src/components/BackAdminLayout.vue b/mining-pool/src/components/BackAdminLayout.vue index b3f7ff9..105bb09 100644 --- a/mining-pool/src/components/BackAdminLayout.vue +++ b/mining-pool/src/components/BackAdminLayout.vue @@ -53,7 +53,7 @@
- 选择左侧导航栏查看页面 + {{ $t("backendSystem.leftNavigation") }}
@@ -118,6 +118,9 @@ export default { } this.userEmail = userEmail; }); + + //网络变化 + window.addEventListener("online", this.handleNetworkChange); }, methods: { async fetchSignOut() { @@ -178,7 +181,19 @@ export default { this.$message.error(this.$t("common.langChangeFailed")); } }, + // 处理网络状态变化 + handleNetworkChange() { + + if (navigator.onLine) { + // === 强制重置状态,兜底 === + location.reload(); // 重新加载当前页面 + + } }, + }, + beforeDestroy() { + window.removeEventListener("online", this.handleNetworkChange); + }, }; diff --git a/mining-pool/src/components/ChatWidget.vue b/mining-pool/src/components/ChatWidget.vue index f977fae..f56544b 100644 --- a/mining-pool/src/components/ChatWidget.vue +++ b/mining-pool/src/components/ChatWidget.vue @@ -1429,7 +1429,6 @@ export default { // 加载历史消息 async loadHistoryMessages() { if (this.isLoadingHistory || !this.roomId) return; - this.isLoadingHistory = true; try { const response = await getHistory7({ @@ -1437,41 +1436,18 @@ export default { userType: this.userType, email: this.userEmail, }); - - // console.log("📋 初始历史消息加载响应:", { - // code: response?.code, - // dataExists: !!response?.data, - // dataLength: response?.data?.length || 0, - // isArray: Array.isArray(response?.data), - // }); - if (response?.code === 200 && Array.isArray(response.data)) { - // 使用统一的格式化方法 const historyMessages = this.formatHistoryMessages(response.data); - if (historyMessages.length > 0) { - // 有历史消息,按时间顺序排序 this.messages = historyMessages.sort( (a, b) => new Date(a.time) - new Date(b.time) ); - // console.log( - // "✅ 成功加载", - // historyMessages.length, - // "条初始历史消息" - // ); - - // 保持对话框打开状态 - this.isChatOpen = true; - this.isMinimized = false; - - // 只有在初始加载历史消息时才滚动到底部(当消息列表为空时) + // 不自动弹窗,只滚动 await this.$nextTick(); - // 添加一个小延时确保所有内容都渲染完成 setTimeout(() => { - this.scrollToBottom(true); // 传入 true 表示强制滚动 + this.scrollToBottom(true); }, 100); } else { - // 格式化后没有有效消息 this.messages = [ { type: "system", @@ -1480,10 +1456,8 @@ export default { time: new Date().toISOString(), }, ]; - // console.log("📋 初始历史消息为空(格式化后无有效消息)"); } } else { - // 响应无效或无数据 this.messages = [ { type: "system", @@ -1492,11 +1466,9 @@ export default { time: new Date().toISOString(), }, ]; - // console.log("📋 初始历史消息为空(响应无效)"); } } catch (error) { console.error("加载历史消息失败:", error); - // === 简化历史消息加载失败提示 === this.$message.error( this.$t("chat.loadHistoryFailed") || "加载历史消息失败" ); diff --git a/mining-pool/src/components/comAside.vue b/mining-pool/src/components/comAside.vue index c26d154..015ff2d 100644 --- a/mining-pool/src/components/comAside.vue +++ b/mining-pool/src/components/comAside.vue @@ -63,32 +63,28 @@ export default { id:"3", }, - // {//用户算力 - // path:"userComputingPower", - // label:`backendSystem.userComputingPower`, - // icon:"el-icon-finished", - // id:"4", - - // }, + ], activeIndex: "0", } }, mounted(){ - const savedIndex = localStorage.getItem("activeIndex"); - if(savedIndex){ - this.activeIndex = savedIndex; - - - }else{ - this.$addStorageEvent(1, "activeIndex", this.activeIndex); - + const lang = this.$i18n.locale; + const currentPath = this.$route.path.replace(`/${lang}/`, ''); + // 优先根据当前路由匹配菜单项 + const matchedMenu = this.menuList.find(item => item.path === currentPath); + if (matchedMenu) { + this.activeIndex = matchedMenu.id; + this.$addStorageEvent(1, "activeIndex", matchedMenu.id); + } else { + // 如果localStorage有值,优先用localStorage + const savedIndex = localStorage.getItem("activeIndex"); + if(savedIndex){ + this.activeIndex = savedIndex; + } else { + this.$addStorageEvent(1, "activeIndex", this.activeIndex); + } } - - - - - }, methods:{ handleClick(item){ diff --git a/mining-pool/src/i18n/ChatWidget.js b/mining-pool/src/i18n/ChatWidget.js index 81d2e2e..2ebb503 100644 --- a/mining-pool/src/i18n/ChatWidget.js +++ b/mining-pool/src/i18n/ChatWidget.js @@ -45,7 +45,7 @@ export const ChatWidget_zh = { readImage:"读取图片失败,请重试", processingFailed:"图片处理失败,请重试", Disconnected:"连接已断开", - reconnecting:"正在重连...", + reconnecting:"正在连接...", contactList:"联系列表", search:"搜索最近联系人", tourist:"游客", @@ -107,6 +107,7 @@ export const ChatWidget_zh = { serviceAddressUnavailable:"服务地址不可用,请稍后重试", connectionFailedService:"无法连接到服务器,请稍后重试", connectionFailedCustomer:"连接客服系统失败,请检查网络或稍后重试", + logoutSyncNotice:"检测到您已在其他窗口退出登录,当前窗口将自动跳转", }, @@ -140,7 +141,7 @@ export const ChatWidget_en = { subscriptionFailed: "Message subscription failed", break: "Connection lost", retry: "Retry in seconds", - disconnectWaiting: "Reconnecting...", + disconnectWaiting: "connecting...", sendFailed: "Send failed, please retry", noHistory: "No history", historicalFailure: "Failed to load history", diff --git a/mining-pool/src/i18n/backendSystem.js b/mining-pool/src/i18n/backendSystem.js index c2f38e1..e442f0d 100644 --- a/mining-pool/src/i18n/backendSystem.js +++ b/mining-pool/src/i18n/backendSystem.js @@ -20,8 +20,7 @@ export const backendSystem_zh = { deleteRemind:"确定删除该广播吗?", deleteSuccess:"删除成功", editSuccess:"修改成功", - addSuccess:"发布成功", - + addSuccess:"发布成功", cancel:"取 消", publish:"发 布", logout:"退出", @@ -29,6 +28,61 @@ export const backendSystem_zh = { dialogTitle:"新增广播内容", pleaseInputContent:"请输入广播内容", newlineInvalid:"广播内容输入换行符无效", + onlineUserNum:"在线数量", + offlineUserNum:"离线数量", + userPower:"用户算力", + chartTitle:"用户算力及在离线状态图", + noData:"无数据", + startDate:"开始日期", + endDate:"结束日期", + to:"至", + userDetails:"用户详情", + return:"返回", + coin:"币种:", + user:"挖矿账户:", + amount:"交易金额:", + createDate:"收益分配日期", + maxHeight:"最大高度", + shouldOutDate:"实际转账日期", + address:"转账地址", + historyAddress:"历史支付地址:", + + userManagementTitle:"注册用户管理", + pleaseInput:"请输入", + email:"邮箱:", + email2:"邮箱", + query:"查询", + coin2:"币种", + amount2:"最小起付金额", + status2:"用户状态", + minerUser2:"挖矿账号", + balance2:"支付地址", + active2:"是否自动提现", + normal:"正常", + delete:"删除", + yes:"是", + no:"否", + Details:"详情", + sendEmail:"发送邮件", + sendSuccess:"发送成功", + sendFail:"发送失败", + sendRemind:"确定发送邮件吗?", + recipient:"收件人", + subject:"邮件主题", + text:"邮件内容", + send:"发送", + emailRemind:"可输入多个邮箱,用逗号隔开", + + pleaseInputCorrectEmail:"请输入正确的邮箱地址", + pleaseInputSubject:"请输入邮件主题", + pleaseInputText:"请输入邮件内容", + pleaseInputCorrectEmail2:"请输入正确的邮箱地址,多个邮箱用逗号分隔", + existDuplicateEmail:"存在重复邮箱,请检查", + pleaseInputQueryConditions:"请输入查询条件(挖矿账号、邮箱)", + emailContent:"邮件内容", + leftNavigation:"选择左侧导航栏查看页面", + + } } @@ -63,7 +117,59 @@ export const backendSystem_en = { dialogTitle:"Add Broadcast Content", pleaseInputContent:"Please input broadcast content", newlineInvalid:"Invalid line break for broadcasting content input", - + onlineUserNum:"Online Number", + offlineUserNum:"Offline Number", + userPower:"User Power", + chartTitle:"User Power and Online/Offline Status Chart", + noData:"No Data", + startDate:"Start Date", + endDate:"End Date", + to:"To", + userDetails:"User Details", + return:"Return", + coin:"Coin:", + user:"Mining account:", + amount:"Amount:", + createDate:"Create Date", + maxHeight:"Max Height", + shouldOutDate:"Should Out Date", + address:"Address", + historyAddress:"History Payment Address:", + userManagementTitle:"User Management", + pleaseInput:"Please Input", + email:"Email:", + email2:"Email", + query:"Query", + coin2:"Coin", + amount2:"Minimum Payout Amount", + status2:"User Status", + minerUser2:"Miner User", + balance2:"Payment Address", + active2:"Automatic Withdrawal", + normal:"Normal", + delete:"Delete", + yes:"Yes", + no:"No", + Details:"Details", + sendEmail:"Send Email", + sendSuccess:"Send Success", + sendFail:"Send Fail", + sendRemind:"Are you sure you want to send an email?", + recipient:"Recipient", + subject:"Email Subject", + text:"Email Content", + send:"Send", + emailRemind:"Multiple emails can be entered, separated by commas", + pleaseInputCorrectEmail:"Please enter a valid email address", + pleaseInputSubject:"Please enter an email subject", + pleaseInputText:"Please enter an email content", + pleaseInputCorrectEmail2:"Please enter a valid email address, multiple emails separated by commas", + existDuplicateEmail:"Duplicate email exists, please check", + pleaseInputQueryConditions:"Please enter the query conditions (miner account, email)", + + emailContent:"Email Content", + leftNavigation:"Select the left navigation bar to view the page", + } } diff --git a/mining-pool/src/main.js b/mining-pool/src/main.js index 8d877f8..dcbde5d 100644 --- a/mining-pool/src/main.js +++ b/mining-pool/src/main.js @@ -26,7 +26,7 @@ Vue.use(ElementUI, { }); Vue.prototype.$axios = axios -// console.log = ()=>{} //全局关闭打印 +console.log = ()=>{} //全局关闭打印 // 全局注册混入 Vue.mixin(loadingStateMixin);//loading状态管理 Vue.mixin(networkRecoveryMixin);//网络恢复后数据刷新 diff --git a/mining-pool/src/router/index.js b/mining-pool/src/router/index.js index 3269a17..9ed878c 100644 --- a/mining-pool/src/router/index.js +++ b/mining-pool/src/router/index.js @@ -6,7 +6,7 @@ import i18n from '../i18n/index' Vue.use(VueRouter) - // 提取子路由配置 +// 提取子路由配置 const childrenRoutes = [ { path: '', @@ -17,536 +17,571 @@ const childrenRoutes = [ description: i18n.t(`seo.Home`), allAuthority: [`all`], // keywords: 'M2Pool, cryptocurrency mining pool, bitcoin mining, DGB mining, mining pool service, 加密货币矿池, 比特币挖矿, DGB挖矿, 矿池服务' - keywords:{ + keywords: { en: 'M2Pool, cryptocurrency mining pool, bitcoin mining, DGB mining, mining pool service, 加密货币矿池, 比特币挖矿, DGB挖矿, 矿池服务', zh: 'M2Pool, 加密货币矿池, 比特币挖矿, DGB挖矿, 矿池服务' } - - - + + + } }, - {//只读页面挖矿账户页 + {//只读页面挖矿账户页 path: 'miningAccount', name: 'MiningAccount', component: () => import('../views/miningAccount/index.vue'), - meta: {title: '挖矿账户页面', - description:i18n.t(`seo.miningAccount`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], + meta: { + title: '挖矿账户页面', + description: i18n.t(`seo.miningAccount`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], // keywords: 'M2Pool mining account, crypto mining stats, mining rewards, hashrate monitor, 矿池账户, 挖矿收益, 算力监控' - keywords:{ + keywords: { en: 'M2Pool mining account, crypto mining stats, mining rewards, hashrate monitor, 矿池账户, 挖矿收益, 算力监控', zh: 'M2Pool 挖矿账户, 加密挖矿统计, 挖矿奖励, 算力监控, 矿池账户, 挖矿收益, 算力监控' } } }, - {//只读页面展示页 + {//只读页面展示页 path: 'readOnlyDisplay', name: 'ReadOnlyDisplay', component: () => import('../views/readOnlyDisplay/index.vue'), - meta: {title: '只读页面展示页', - description:i18n.t(`seo.readOnlyDisplay`), - allAuthority:[`all`], + meta: { + title: '只读页面展示页', + description: i18n.t(`seo.readOnlyDisplay`), + allAuthority: [`all`], // keywords: 'M2Pool 矿池,只读页面,收益状况,矿工信息,Read only page,Revenue situation,Mining Pool,Miner information' - keywords:{ + keywords: { en: 'Read only page,Revenue situation,Mining Pool,Miner information', zh: 'M2Pool 矿池,只读页面,收益状况,矿工信息' } - - - + + + } }, {//报块页面 path: 'reportBlock', name: 'ReportBlock', component: () => import('../views/reportBlock/index.vue'), - meta: {title: '报块页面', - description:i18n.t(`seo.reportBlock`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], + meta: { + title: '报块页面', + description: i18n.t(`seo.reportBlock`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], // keywords: 'M2Pool 矿池,报块页面,幸运值,区块高度,Block page,Lucky Value,block height,Mining Pool' - keywords:{ + keywords: { en: 'Block page,Lucky Value,block height,Mining Pool', zh: 'M2Pool 矿池,报块页面,幸运值,区块高度' } - - + + } }, - {//报块页面 + {//报块页面 path: 'broadcast', name: 'Broadcast', component: () => import('../views/broadcast/index.vue'), - meta: {title: '广播页面', - description:i18n.t(`seo.broadcast`), - allAuthority:[`admin`,`back_admin`], - keywords:{ + meta: { + title: '广播页面', + description: i18n.t(`seo.broadcast`), + allAuthority: [`back_admin`], + keywords: { en: 'broadcast', zh: '广播' - } + } } }, - {//用户管理 + {//用户管理 path: 'userManagement', name: 'UserManagement', component: () => import('../views/userManagement/index.vue'), - meta: {title: '用户管理', - description:i18n.t(`seo.userManagement`), - allAuthority:[`admin`,`back_admin`], - keywords:{ + meta: { + title: '用户管理', + description: i18n.t(`seo.userManagement`), + allAuthority: [`back_admin`], + keywords: { en: 'userManagement', zh: '用户管理' - } + } }, - + }, {//用户详情 path: 'userDetails', name: 'UserDetails', component: () => import('../views/userDetails/index.vue'), - meta: {title: '用户详情', - description:i18n.t(`seo.userManagement`), - allAuthority:[`admin`,`back_admin`], - keywords:{ + meta: { + title: '用户详情', + description: i18n.t(`seo.userManagement`), + allAuthority: [`back_admin`], + keywords: { en: 'userDetails', zh: '用户详情' - } + } } }, - - {//费率 - path: 'rate', - name: 'Rate', - component: () => import('../views/rate/index.vue'), - meta: {title: '费率页面', - description:i18n.t(`seo.rate`), - allAuthority:[`all`], - // keywords: 'M2Pool 矿池,费率页面,挖矿费率,收益计算,Mining Pool,Rate,Mining fee rate,Profit calculation' - keywords:{ - en: 'Mining Pool,Rate,Mining fee rate,Profit calculation', - zh: 'M2Pool 矿池,费率页面,挖矿费率,收益计算' - } - + {//费率 + path: 'rate', + name: 'Rate', + component: () => import('../views/rate/index.vue'), + meta: { + title: '费率页面', + description: i18n.t(`seo.rate`), + allAuthority: [`all`], + // keywords: 'M2Pool 矿池,费率页面,挖矿费率,收益计算,Mining Pool,Rate,Mining fee rate,Profit calculation' + keywords: { + en: 'Mining Pool,Rate,Mining fee rate,Profit calculation', + zh: 'M2Pool 矿池,费率页面,挖矿费率,收益计算' } - }, - {//分配说明 - path: 'allocationExplanation', - name: 'AllocationExplanation', - component: () => import('../views/allocationExplanation/index.vue'), - meta: {title: '分配说明页面', - description:i18n.t(`seo.rate`), - allAuthority:[`all`], - // keywords: '分配、转账说明,矿池分配,转账说明,Allocation,Transfer,Mining Pool,Pool allocation,Transfer instructions' - keywords:{ - en: 'Allocation,Transfer,Mining Pool,Pool allocation,Transfer instructions', - zh: '分配、转账说明,矿池分配,转账说明' - } + + + } + }, + {//分配说明 + path: 'allocationExplanation', + name: 'AllocationExplanation', + component: () => import('../views/allocationExplanation/index.vue'), + meta: { + title: '分配说明页面', + description: i18n.t(`seo.rate`), + allAuthority: [`all`], + // keywords: '分配、转账说明,矿池分配,转账说明,Allocation,Transfer,Mining Pool,Pool allocation,Transfer instructions' + keywords: { + en: 'Allocation,Transfer,Mining Pool,Pool allocation,Transfer instructions', + zh: '分配、转账说明,矿池分配,转账说明' } + } + }, + {//API文档 + path: 'apiFile', + name: 'ApiFile', + component: () => import('../views/apiFile/index.vue'), + meta: { + title: 'API文档页面', + description: i18n.t(`seo.apiFile`), + allAuthority: [`all`], + // keywords: 'M2Pool 矿池,API 文档,认证 token,接口调用,API file,authentication token,Interface call' + keywords: { + en: 'API file,authentication token,Interface call', + zh: 'M2Pool 矿池,API 文档,认证 token,接口调用' + } + + } + }, + {//在线客服 + path: 'customerService', + name: 'CustomerService', + component: () => import('../views/customerService/index.vue'), + meta: { + title: '在线客服', + description: i18n.t(`seo.apiFile`), + allAuthority: [`customer_service`, `admin`,],//客服权限 + // keywords: 'M2Pool 矿池,API 文档,认证 token,接口调用,API file,authentication token,Interface call' + keywords: { + en: 'API file,authentication token,Interface call', + zh: 'M2Pool 矿池,API 文档,认证 token,接口调用' + } + + } + }, + + {//接入矿池页面 + path: '/:lang/AccessMiningPool', + name: 'AccessMiningPool', + component: () => import('../views/AccessMiningPool/index.vue'), + meta: { + title: '接入矿池页面', + description: i18n.t(`seo.allocationExplanation`), + allAuthority: [`all`], + // keywords: 'M2Pool 矿池,接入矿池,币种接入,挖矿指南,Access to Mining Pools,Coin Access,Mining Guide' + keywords: { + en: 'Access to Mining Pools,Coin Access,Mining Guide', + zh: 'M2Pool 矿池,接入矿池,币种接入,挖矿指南' + } + }, - {//API文档 - path: 'apiFile', - name: 'ApiFile', - component: () => import('../views/apiFile/index.vue'), - meta: {title: 'API文档页面', - description:i18n.t(`seo.apiFile`), - allAuthority:[`all`], - // keywords: 'M2Pool 矿池,API 文档,认证 token,接口调用,API file,authentication token,Interface call' - keywords:{ - en: 'API file,authentication token,Interface call', - zh: 'M2Pool 矿池,API 文档,认证 token,接口调用' + children: [ + {//nexa 挖矿页面 + path: 'nexaAccess', + name: 'NexaAccess', + component: () => import('../views/AccessMiningPool/nexaAccess/index.vue'), + meta: { + title: 'nexa 挖矿页面', + description: i18n.t(`seo.nexaAccess`), + allAuthority: [`all`], + keepAlive: true, + requiresAuth: false, + // keywords: 'nexa,挖矿教程,Nexa接入,Nexa Access,Mining Tutorial' + keywords: { + en: 'Nexa Access,Mining Tutorial', + zh: 'nexa,挖矿教程,Nexa接入,Nexa Access,Mining Tutorial' } - } }, - {//在线客服 - path: 'customerService', - name: 'CustomerService', - component: () => import('../views/customerService/index.vue'), - meta: {title: '在线客服', - description:i18n.t(`seo.apiFile`), - allAuthority:[`customer_service`,`admin`,],//客服权限 - // keywords: 'M2Pool 矿池,API 文档,认证 token,接口调用,API file,authentication token,Interface call' - keywords:{ - en: 'API file,authentication token,Interface call', - zh: 'M2Pool 矿池,API 文档,认证 token,接口调用' + {//rxd 挖矿页面 + path: 'rxdAccess', + name: 'RxdAccess', + component: () => import('../views/AccessMiningPool/rxdAccess/index.vue'), + meta: { + title: 'rxd 挖矿页面', + description: i18n.t(`seo.rxdAccess`), + allAuthority: [`all`], + // keywords: 'rxd,挖矿教程,Radiant接入,rxd Access,Radiant Access,Mining Tutorial,radiant' + keywords: { + en: 'rxd Access,Radiant Access,Mining Tutorial,radiant', + zh: 'rxd,矿池挖矿教程,Radiant接入,' } - } }, - - {//接入矿池页面 - path: '/:lang/AccessMiningPool', - name: 'AccessMiningPool', - component: () => import('../views/AccessMiningPool/index.vue'), - meta: {title: '接入矿池页面', - description:i18n.t(`seo.allocationExplanation`), - allAuthority:[`all`], - // keywords: 'M2Pool 矿池,接入矿池,币种接入,挖矿指南,Access to Mining Pools,Coin Access,Mining Guide' - keywords:{ - en: 'Access to Mining Pools,Coin Access,Mining Guide', - zh: 'M2Pool 矿池,接入矿池,币种接入,挖矿指南' - } + {//mona 挖矿页面 + path: 'monaAccess', + name: 'MonaAccess', + component: () => import('../views/AccessMiningPool/monaAccess/index.vue'), + meta: { + title: 'mona 挖矿页面', + description: i18n.t(`seo.monaAccess`), + allAuthority: [`all`], + // keywords: 'mona,挖矿教程,mona接入,Mona Access,MONA Access,Mining Tutorial' + keywords: { + en: 'Mona Access,MONA Access,Mining Tutorial', + zh: 'mona,挖矿教程,mona接入,' + } + }, }, - children: [ - {//nexa 挖矿页面 - path: 'nexaAccess', - name: 'NexaAccess', - component: () => import('../views/AccessMiningPool/nexaAccess/index.vue'), - meta: {title: 'nexa 挖矿页面', - description:i18n.t(`seo.nexaAccess`), - allAuthority:[`all`], - keepAlive: true, - requiresAuth: false, - // keywords: 'nexa,挖矿教程,Nexa接入,Nexa Access,Mining Tutorial' - keywords:{ - en: 'Nexa Access,Mining Tutorial', - zh: 'nexa,挖矿教程,Nexa接入,Nexa Access,Mining Tutorial' - } + {//grs 挖矿页面 + path: 'grsAccess', + name: 'GrsAccess', + component: () => import('../views/AccessMiningPool/grsAccess/index.vue'), + meta: { + title: 'grs 挖矿页面', + description: i18n.t(`seo.grsAccess`), + allAuthority: [`all`], + // keywords: 'GRS,Grs接入,GRS Access,grs Access,Mining Tutorial' + keywords: { + en: 'GRS Access,grs Access,Mining Tutorial', + zh: 'GRS,Grs接入,GRS挖矿教程' } }, - {//rxd 挖矿页面 - path: 'rxdAccess', - name: 'RxdAccess', - component: () => import('../views/AccessMiningPool/rxdAccess/index.vue'), - meta: {title: 'rxd 挖矿页面', - description:i18n.t(`seo.rxdAccess`), - allAuthority:[`all`], - // keywords: 'rxd,挖矿教程,Radiant接入,rxd Access,Radiant Access,Mining Tutorial,radiant' - keywords:{ - en: 'rxd Access,Radiant Access,Mining Tutorial,radiant', - zh: 'rxd,矿池挖矿教程,Radiant接入,' - } - } - }, - {//mona 挖矿页面 - path: 'monaAccess', - name: 'MonaAccess', - component: () => import('../views/AccessMiningPool/monaAccess/index.vue'), - meta: {title: 'mona 挖矿页面', - description:i18n.t(`seo.monaAccess`), - allAuthority:[`all`], - // keywords: 'mona,挖矿教程,mona接入,Mona Access,MONA Access,Mining Tutorial' - keywords:{ - en: 'Mona Access,MONA Access,Mining Tutorial', - zh: 'mona,挖矿教程,mona接入,' - } - }, - - }, - {//grs 挖矿页面 - path: 'grsAccess', - name: 'GrsAccess', - component: () => import('../views/AccessMiningPool/grsAccess/index.vue'), - meta: {title: 'grs 挖矿页面', - description:i18n.t(`seo.grsAccess`), - allAuthority:[`all`], - // keywords: 'GRS,Grs接入,GRS Access,grs Access,Mining Tutorial' - keywords:{ - en: 'GRS Access,grs Access,Mining Tutorial', - zh: 'GRS,Grs接入,GRS挖矿教程' - } - }, - - }, - {//dgbqA挖矿页面 dgboAccess - path: 'dgbqAccess', - name: 'DgbqAccess', - component: () => import('../views/AccessMiningPool/dgbqAccess/index.vue'), - meta: {title: 'Dgbq 挖矿页面', - description:i18n.t(`seo.dgbAccess`), - allAuthority:[`all`], - // keywords: 'Dgbq,dgb(qubit)接入,Dgb(qubit) Access,DGB(qubit) Access,Mining Tutorial,DGB' - keywords:{ - en: 'Dgb(qubit) Access,DGB(qubit) Access,Mining Tutorial,DGB', - zh: 'Dgbq,dgb(qubit)接入,dgb(qubit)挖矿教程' - } - }, - - }, - {//dgboAccess 挖矿页面 - path: 'dgboAccess', - name: 'DgboAccess', - component: () => import('../views/AccessMiningPool/dgboAccess/index.vue'), - meta: {title: 'Dgbo 挖矿页面', - description:i18n.t(`seo.dgbAccess`), - allAuthority:[`all`], - // keywords: 'dgbo,dgb(odocrypt)接入,Dgb(odocrypt) Access,DGB(odocrypt) Access,Mining Tutorial,DGB' - keywords:{ - en: 'Dgb(odocrypt) Access,DGB(odocrypt) Access,Mining Tutorial,DGB', - zh: 'dgbo,dgb(odocrypt)接入,dgb(odocrypt)挖矿教程' - } - }, - - }, - {//dgbsAccess 挖矿页面 - path: 'dgbsAccess', - name: 'DgbsAccess', - component: () => import('../views/AccessMiningPool/dgbsAccess/index.vue'), - meta: {title: 'Dgbs 挖矿页面', - description:i18n.t(`seo.dgbAccess`), - allAuthority:[`all`], - // keywords: 'dgbs,dgb(skein)接入,Dgb(skein) Access,DGB(skein) Access,Mining Tutorial,DGB' - keywords:{ - en: 'Dgb(skein) Access,DGB(skein) Access,Mining Tutorial,DGB', - zh: 'dgbs,dgb(skein)接入,dgb(skein)挖矿教程' - } - }, - - }, - {//enxAccess 挖矿页面 - path: 'enxAccess', - name: 'EnxAccess', - component: () => import('../views/AccessMiningPool/enxAccess/index.vue'), - meta: {title: ' Entropyx(enx) 挖矿页面', - description:i18n.t(`seo.enxAccess`), - allAuthority:[`all`], - // keywords: 'Entropyx(Enx), Entropyx(enx)接入,enx,ENX,Mining Tutorial' - keywords:{ - en: 'Entropyx(Enx), enx,ENX,Mining Tutorial', - zh: 'Entropyx(enx)接入,Entropyx挖矿教程' - } - }, - - }, - {//alphminingPool 挖矿页面 - path: 'alphminingPool', - name: 'AlphminingPool', - component: () => import('../views/AccessMiningPool/alphminingPool/index.vue'), - meta: {title: ' alephium 挖矿页面', - description:i18n.t(`seo.alphAccess`), - allAuthority:[`all`], - // keywords: 'Entropyx(Enx), Entropyx(enx)接入,enx,ENX,Mining Tutorial' - keywords:{ - en: 'alephium(alph), Alephium,Mining Tutorial', - zh: 'alephium(alph)接入,alephium挖矿教程' - } - }, - - }, - - - ] - }, - {//服务条款 - path: 'ServiceTerms', - name: 'ServiceTerms', - component: () => import('../views/ServiceTerms/index.vue'), - meta: {title: '服务条款页面', - description:i18n.t(`seo.ServiceTerms`), - allAuthority:[`all`], - // keywords: 'M2Pool 矿池,服务条款,用户权益,权利义务,Terms of Service, User Rights, Rights and Obligations' - keywords:{ - en: 'Terms of Service, User Rights, Rights and Obligations', - zh: 'M2Pool 矿池,服务条款,用户权益,权利义务' - } - } - }, - {//提交工单 - path: 'submitWorkOrder', - name: 'SubmitWorkOrder', - component: () => import('../views/submitWorkOrder/index.vue'), - meta: {title: '提交工单页面', - description:i18n.t(`seo.submitWorkOrder`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], - // keywords: 'M2Pool 矿池,提交工单,技术支持,问题处理,Mining Pool,Work Order Submission, Technical Support, Troubleshooting' - keywords:{ - en: 'Mining Pool,Work Order Submission, Technical Support, Troubleshooting', - zh: 'M2Pool 矿池,提交工单,技术支持,问题处理' - } - } - }, - {//用户查看工单记录 - path: 'workOrderRecords', - name: 'WorkOrderRecords', - component: () => import('../views/workOrderRecords/index.vue'), - meta: {title: '工单记录页面(用户)', - description:i18n.t(`seo.workOrderRecords`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], - // keywords: 'M2Pool 矿池,用户工单记录,处理状态,问题进度,User Work Order Records, Processing Status, Issue Progress' - keywords:{ - en: 'User Work Order Records, Processing Status, Issue Progress', - zh: 'M2Pool 矿池,用户工单记录,处理状态,问题进度' - } - } - }, - {//用户查看工单详情 - path: 'userWorkDetails', - name: 'UserWorkDetails', - component: () => import('../views/userWorkDetails/index.vue'), - meta: {title: '工单详情页面(用户)', - description:i18n.t(`seo.userWorkDetails`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], - // keywords: 'M2Pool 矿池,用户工单详情,问题描述,补充提交,User Work Order Details, Problem Description, Additional Submissions' - keywords:{ - en: 'User Work Order Details, Problem Description, Additional Submissions', - zh: 'M2Pool 矿池,用户工单详情,问题描述,补充提交' - } - } - }, - {//后台工单管理 - path: 'workOrderBackend', - name: 'WorkOrderBackend', - component: () => import('../views/workOrderBackend/index.vue'), - meta: {title: '工单管理页面(后台)', - description:"M2Pool 矿池后台工单管理页面,供 M2Pool 管理员查看和管理用户提交的工单记录,确保问题及时处理,提升用户体验。", - allAuthority:[`admin`,`back_admin`], - // keywords: 'M2Pool 矿池,后台工单管理,用户工单,及时处理,Back-office work order management, user work orders, timely processing' - keywords:{ - en: 'Back-office work order management, user work orders, timely processing', - zh: 'M2Pool 矿池,后台工单管理,用户工单,及时处理' - } - } - }, - {//后台工单详情页面 - path: 'BKWorkDetails', - name: 'BKWorkDetails', - component: () => import('../views/BKWorkDetails/index.vue'), - meta: {title: '工单详情页面(后台)', - description:"M2Pool 矿池后台工单详情页面,管理员可在此查看提交工单的详细情况,包括提交时间、详细问题描述以及处理过程,并通过本页面对该工单进行回复处理。", - allAuthority:[`admin`,`back_admin`], - // keywords: 'M2Pool 矿池,后台工单详情,问题处理,回复工单,Backend Work Order Details, Problem Handling, Responding to Work Orders' - keywords:{ - en: 'Backend Work Order Details, Problem Handling, Responding to Work Orders', - zh: 'M2Pool 矿池,后台工单详情,问题处理,回复工单' - } - } - }, - {//数据展示 - path: 'dataDisplay', - name: 'DataDisplay', - component: () => import('../views/dataDisplay/index.vue'), - meta: {title: '数据展示页面', - description:"M2Pool 矿池数据展示页面", - allAuthority:[`all`], - // keywords: 'M2Pool 矿池,数据展示,Mining Pool,Data Display' - keywords:{ - en: 'Mining Pool,Data Display', - zh: 'M2Pool 矿池,数据展示' - } - } - }, - {//警报通知 - path: 'alerts', - name: 'Alerts', - component: () => import('../views/alerts/index.vue'), - meta: {title: '警报通知', - description:i18n.t(`seo.alerts`), - allAuthority:[`admin`,`registered`,`back_admin`], - // keywords: 'M2Pool, 矿池,离线告警设置,矿机离线,Mining Pool,Offline Alarm Setting,Mining Machine Offline' - keywords:{ - en: 'Mining Pool,Offline Alarm Setting,Mining Machine Offline', - zh: 'M2Pool 矿池,离线告警设置,矿机离线' - } - } - }, - - {//个人中心 - path: 'personalCenter', - name: 'PersonalCenter', - // redirect: "/personalCenter/personalMining", - component: () => import('../views/personalCenter/index.vue'), - meta: {title: '个人中心页面', - description:i18n.t(`seo.personalCenter`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], - // keywords: 'M2Pool 矿池,个人中心,挖矿账户,只读页面设置,安全设置,API密钥生成,Personal Center,Mining Account,Read-Only Page Setup,Security Settings,API Key Generation' - keywords:{ - en: 'Personal Center,Mining Account,Read-Only Page Setup,Security Settings,API Key Generation', - zh: 'M2Pool 矿池,个人中心,挖矿账户,只读页面设置,安全设置,API密钥生成' - } - }, - children: [ - {//个人中心挖矿账户 - path: 'personalMining', - name: 'PersonalMining', - component: () => import('../views/personalCenter/personalMining/index.vue'), - meta: {title: '挖矿账户设置页面', - description:i18n.t(`seo.personalMining`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], - // keywords: 'M2Pool 矿池,个人中心,挖矿账户设置,币种账户,Personal Center,Mining Account Settings,Coin Accounts' - keywords:{ - en: 'Personal Center,Mining Account Settings,Coin Accounts', - zh: 'M2Pool 矿池,个人中心,挖矿账户设置,币种账户' - } - } - }, - {//个人中心只读页面 securitySetting - path: 'readOnly', - name: 'ReadOnly', - component: () => import('../views/personalCenter/readOnly/index.vue'), - meta: {title: '只读页面设置', - description:i18n.t(`seo.readOnly`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], - // keywords: 'M2Pool 矿池,个人中心,只读页面设置,矿池分享,Personal Center,Read-Only Page Setting,Mining Pool Sharing' - keywords:{ - en: 'Personal Center,Read-Only Page Setting,Mining Pool Sharing', - zh: 'M2Pool 矿池,个人中心,只读页面设置,矿池分享' - } - } - }, - {//个人中心安全设置personal - path: 'securitySetting', - name: 'SecuritySetting', - component: () => import('../views/personalCenter/securitySetting/index.vue'), - meta: {title: '安全设置页面', - description:i18n.t(`seo.securitySetting`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], - // keywords: 'M2Pool 矿池,安全设置,密码修改,Security settings, password change' - keywords:{ - en: 'Security settings, password change', - zh: 'M2Pool 矿池,安全设置,密码修改' - } - } - }, - {//个人中心 - path: 'personal', - name: 'personal', - component: () => import('../views/personalCenter/personal/index.vue'), - meta: {title: '个人信息页面', - description:i18n.t(`seo.personal`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], - // keywords: 'M2Pool 矿池,个人信息,登录历史,Personal Information, Login History' - keywords:{ - en: 'Personal Information, Login History', - zh: 'M2Pool 矿池,个人信息,登录历史' - } - } - }, - {//个人中心 挖矿报告 (隐藏) - path: 'miningReport', - name: 'MiningReport', - component: () => import('../views/personalCenter/miningReport/index.vue'), - meta: {title: '挖矿报告页面', - description:i18n.t(`seo.miningReport`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], - // keywords: 'M2Pool 矿池,个人中心,挖矿报告,订阅服务,Mining Report, Subscription Service' - keywords:{ - en: 'Mining Report, Subscription Service', - zh: 'M2Pool 矿池,个人中心,挖矿报告,订阅服务' - } - } - }, - {//个人中心 API - path: 'personalAPI', - name: 'PersonalAPI', - component: () => import('../views/personalCenter/personalAPI/index.vue'), - meta: {title: 'API页面', - description:i18n.t(`seo.personalAPI`), - allAuthority:[`admin`,`registered`,`customer_service`,`back_admin`], - // keywords: 'M2Pool 矿池,个人中心,API 页面,API密钥生成,API Page,API Key Generation' - keywords:{ - en: 'API Page,API Key Generation', - zh: 'M2Pool 矿池,个人中心,API 页面,API密钥生成' - } - } - }, - - ] }, + {//dgbqA挖矿页面 dgboAccess + path: 'dgbqAccess', + name: 'DgbqAccess', + component: () => import('../views/AccessMiningPool/dgbqAccess/index.vue'), + meta: { + title: 'Dgbq 挖矿页面', + description: i18n.t(`seo.dgbAccess`), + allAuthority: [`all`], + // keywords: 'Dgbq,dgb(qubit)接入,Dgb(qubit) Access,DGB(qubit) Access,Mining Tutorial,DGB' + keywords: { + en: 'Dgb(qubit) Access,DGB(qubit) Access,Mining Tutorial,DGB', + zh: 'Dgbq,dgb(qubit)接入,dgb(qubit)挖矿教程' + } + }, + + }, + {//dgboAccess 挖矿页面 + path: 'dgboAccess', + name: 'DgboAccess', + component: () => import('../views/AccessMiningPool/dgboAccess/index.vue'), + meta: { + title: 'Dgbo 挖矿页面', + description: i18n.t(`seo.dgbAccess`), + allAuthority: [`all`], + // keywords: 'dgbo,dgb(odocrypt)接入,Dgb(odocrypt) Access,DGB(odocrypt) Access,Mining Tutorial,DGB' + keywords: { + en: 'Dgb(odocrypt) Access,DGB(odocrypt) Access,Mining Tutorial,DGB', + zh: 'dgbo,dgb(odocrypt)接入,dgb(odocrypt)挖矿教程' + } + }, + + }, + {//dgbsAccess 挖矿页面 + path: 'dgbsAccess', + name: 'DgbsAccess', + component: () => import('../views/AccessMiningPool/dgbsAccess/index.vue'), + meta: { + title: 'Dgbs 挖矿页面', + description: i18n.t(`seo.dgbAccess`), + allAuthority: [`all`], + // keywords: 'dgbs,dgb(skein)接入,Dgb(skein) Access,DGB(skein) Access,Mining Tutorial,DGB' + keywords: { + en: 'Dgb(skein) Access,DGB(skein) Access,Mining Tutorial,DGB', + zh: 'dgbs,dgb(skein)接入,dgb(skein)挖矿教程' + } + }, + + }, + {//enxAccess 挖矿页面 + path: 'enxAccess', + name: 'EnxAccess', + component: () => import('../views/AccessMiningPool/enxAccess/index.vue'), + meta: { + title: ' Entropyx(enx) 挖矿页面', + description: i18n.t(`seo.enxAccess`), + allAuthority: [`all`], + // keywords: 'Entropyx(Enx), Entropyx(enx)接入,enx,ENX,Mining Tutorial' + keywords: { + en: 'Entropyx(Enx), enx,ENX,Mining Tutorial', + zh: 'Entropyx(enx)接入,Entropyx挖矿教程' + } + }, + + }, + {//alphminingPool 挖矿页面 + path: 'alphminingPool', + name: 'AlphminingPool', + component: () => import('../views/AccessMiningPool/alphminingPool/index.vue'), + meta: { + title: ' alephium 挖矿页面', + description: i18n.t(`seo.alphAccess`), + allAuthority: [`all`], + // keywords: 'Entropyx(Enx), Entropyx(enx)接入,enx,ENX,Mining Tutorial' + keywords: { + en: 'alephium(alph), Alephium,Mining Tutorial', + zh: 'alephium(alph)接入,alephium挖矿教程' + } + }, + + }, + + + ] + }, + {//服务条款 + path: 'ServiceTerms', + name: 'ServiceTerms', + component: () => import('../views/ServiceTerms/index.vue'), + meta: { + title: '服务条款页面', + description: i18n.t(`seo.ServiceTerms`), + allAuthority: [`all`], + // keywords: 'M2Pool 矿池,服务条款,用户权益,权利义务,Terms of Service, User Rights, Rights and Obligations' + keywords: { + en: 'Terms of Service, User Rights, Rights and Obligations', + zh: 'M2Pool 矿池,服务条款,用户权益,权利义务' + } + } + }, + {//提交工单 + path: 'submitWorkOrder', + name: 'SubmitWorkOrder', + component: () => import('../views/submitWorkOrder/index.vue'), + meta: { + title: '提交工单页面', + description: i18n.t(`seo.submitWorkOrder`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], + // keywords: 'M2Pool 矿池,提交工单,技术支持,问题处理,Mining Pool,Work Order Submission, Technical Support, Troubleshooting' + keywords: { + en: 'Mining Pool,Work Order Submission, Technical Support, Troubleshooting', + zh: 'M2Pool 矿池,提交工单,技术支持,问题处理' + } + } + }, + {//用户查看工单记录 + path: 'workOrderRecords', + name: 'WorkOrderRecords', + component: () => import('../views/workOrderRecords/index.vue'), + meta: { + title: '工单记录页面(用户)', + description: i18n.t(`seo.workOrderRecords`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], + // keywords: 'M2Pool 矿池,用户工单记录,处理状态,问题进度,User Work Order Records, Processing Status, Issue Progress' + keywords: { + en: 'User Work Order Records, Processing Status, Issue Progress', + zh: 'M2Pool 矿池,用户工单记录,处理状态,问题进度' + } + } + }, + {//用户查看工单详情 + path: 'userWorkDetails', + name: 'UserWorkDetails', + component: () => import('../views/userWorkDetails/index.vue'), + meta: { + title: '工单详情页面(用户)', + description: i18n.t(`seo.userWorkDetails`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], + // keywords: 'M2Pool 矿池,用户工单详情,问题描述,补充提交,User Work Order Details, Problem Description, Additional Submissions' + keywords: { + en: 'User Work Order Details, Problem Description, Additional Submissions', + zh: 'M2Pool 矿池,用户工单详情,问题描述,补充提交' + } + } + }, + {//后台工单管理 + path: 'workOrderBackend', + name: 'WorkOrderBackend', + component: () => import('../views/workOrderBackend/index.vue'), + meta: { + title: '工单管理页面(后台)', + description: "M2Pool 矿池后台工单管理页面,供 M2Pool 管理员查看和管理用户提交的工单记录,确保问题及时处理,提升用户体验。", + allAuthority: [`back_admin`], + // keywords: 'M2Pool 矿池,后台工单管理,用户工单,及时处理,Back-office work order management, user work orders, timely processing' + keywords: { + en: 'Back-office work order management, user work orders, timely processing', + zh: 'M2Pool 矿池,后台工单管理,用户工单,及时处理' + } + } + }, + {//后台工单详情页面 + path: 'BKWorkDetails', + name: 'BKWorkDetails', + component: () => import('../views/BKWorkDetails/index.vue'), + meta: { + title: '工单详情页面(后台)', + description: "M2Pool 矿池后台工单详情页面,管理员可在此查看提交工单的详细情况,包括提交时间、详细问题描述以及处理过程,并通过本页面对该工单进行回复处理。", + allAuthority: [`back_admin`], + // keywords: 'M2Pool 矿池,后台工单详情,问题处理,回复工单,Backend Work Order Details, Problem Handling, Responding to Work Orders' + keywords: { + en: 'Backend Work Order Details, Problem Handling, Responding to Work Orders', + zh: 'M2Pool 矿池,后台工单详情,问题处理,回复工单' + } + } + }, + {//数据展示 + path: 'dataDisplay', + name: 'DataDisplay', + component: () => import('../views/dataDisplay/index.vue'), + meta: { + title: '数据展示页面', + description: "M2Pool 矿池数据展示页面", + allAuthority: [`all`], + // keywords: 'M2Pool 矿池,数据展示,Mining Pool,Data Display' + keywords: { + en: 'Mining Pool,Data Display', + zh: 'M2Pool 矿池,数据展示' + } + } + }, + {//警报通知 + path: 'alerts', + name: 'Alerts', + component: () => import('../views/alerts/index.vue'), + meta: { + title: '警报通知', + description: i18n.t(`seo.alerts`), + allAuthority: [`admin`, `registered`, `back_admin`], + // keywords: 'M2Pool, 矿池,离线告警设置,矿机离线,Mining Pool,Offline Alarm Setting,Mining Machine Offline' + keywords: { + en: 'Mining Pool,Offline Alarm Setting,Mining Machine Offline', + zh: 'M2Pool 矿池,离线告警设置,矿机离线' + } + } + }, + + {//个人中心 + path: 'personalCenter', + name: 'PersonalCenter', + // redirect: "/personalCenter/personalMining", + component: () => import('../views/personalCenter/index.vue'), + meta: { + title: '个人中心页面', + description: i18n.t(`seo.personalCenter`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], + // keywords: 'M2Pool 矿池,个人中心,挖矿账户,只读页面设置,安全设置,API密钥生成,Personal Center,Mining Account,Read-Only Page Setup,Security Settings,API Key Generation' + keywords: { + en: 'Personal Center,Mining Account,Read-Only Page Setup,Security Settings,API Key Generation', + zh: 'M2Pool 矿池,个人中心,挖矿账户,只读页面设置,安全设置,API密钥生成' + } + }, + children: [ + {//个人中心挖矿账户 + path: 'personalMining', + name: 'PersonalMining', + component: () => import('../views/personalCenter/personalMining/index.vue'), + meta: { + title: '挖矿账户设置页面', + description: i18n.t(`seo.personalMining`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], + // keywords: 'M2Pool 矿池,个人中心,挖矿账户设置,币种账户,Personal Center,Mining Account Settings,Coin Accounts' + keywords: { + en: 'Personal Center,Mining Account Settings,Coin Accounts', + zh: 'M2Pool 矿池,个人中心,挖矿账户设置,币种账户' + } + } + }, + {//个人中心只读页面 securitySetting + path: 'readOnly', + name: 'ReadOnly', + component: () => import('../views/personalCenter/readOnly/index.vue'), + meta: { + title: '只读页面设置', + description: i18n.t(`seo.readOnly`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], + // keywords: 'M2Pool 矿池,个人中心,只读页面设置,矿池分享,Personal Center,Read-Only Page Setting,Mining Pool Sharing' + keywords: { + en: 'Personal Center,Read-Only Page Setting,Mining Pool Sharing', + zh: 'M2Pool 矿池,个人中心,只读页面设置,矿池分享' + } + } + }, + {//个人中心安全设置personal + path: 'securitySetting', + name: 'SecuritySetting', + component: () => import('../views/personalCenter/securitySetting/index.vue'), + meta: { + title: '安全设置页面', + description: i18n.t(`seo.securitySetting`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], + // keywords: 'M2Pool 矿池,安全设置,密码修改,Security settings, password change' + keywords: { + en: 'Security settings, password change', + zh: 'M2Pool 矿池,安全设置,密码修改' + } + } + }, + {//个人中心 + path: 'personal', + name: 'personal', + component: () => import('../views/personalCenter/personal/index.vue'), + meta: { + title: '个人信息页面', + description: i18n.t(`seo.personal`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], + // keywords: 'M2Pool 矿池,个人信息,登录历史,Personal Information, Login History' + keywords: { + en: 'Personal Information, Login History', + zh: 'M2Pool 矿池,个人信息,登录历史' + } + } + }, + {//个人中心 挖矿报告 (隐藏) + path: 'miningReport', + name: 'MiningReport', + component: () => import('../views/personalCenter/miningReport/index.vue'), + meta: { + title: '挖矿报告页面', + description: i18n.t(`seo.miningReport`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], + // keywords: 'M2Pool 矿池,个人中心,挖矿报告,订阅服务,Mining Report, Subscription Service' + keywords: { + en: 'Mining Report, Subscription Service', + zh: 'M2Pool 矿池,个人中心,挖矿报告,订阅服务' + } + } + }, + + {//个人中心 API + path: 'personalAPI', + name: 'PersonalAPI', + component: () => import('../views/personalCenter/personalAPI/index.vue'), + meta: { + title: 'API页面', + description: i18n.t(`seo.personalAPI`), + allAuthority: [`admin`, `registered`, `customer_service`, `back_admin`], + // keywords: 'M2Pool 矿池,个人中心,API 页面,API密钥生成,API Page,API Key Generation' + keywords: { + en: 'API Page,API Key Generation', + zh: 'M2Pool 矿池,个人中心,API 页面,API密钥生成' + } + } + }, + + ] + }, ] @@ -562,7 +597,7 @@ const staticRoutes = [ description: "M2Pool 矿池登录页面", allAuthority: [`all`], // keywords: 'M2Pool 矿池,登录页面,账号密码,安全登录' - keywords:{ + keywords: { en: 'M2Pool Mining Pool,login page,account password', zh: 'M2Pool 矿池,登录页面,账号密码,安全登录' } @@ -572,36 +607,39 @@ const staticRoutes = [ path: '/:lang/register', name: 'Register', component: () => import('../views/register/register.vue'), - meta: {title: '注册页面',description:"M2Pool 矿池注册页面,新用户可在此便捷注册账号,加入 M2Pool 矿池大家庭。",allAuthority:[`all`], - // keywords: 'M2Pool 矿池,注册页面,新用户注册,账号创建' - keywords:{ + meta: { + title: '注册页面', description: "M2Pool 矿池注册页面,新用户可在此便捷注册账号,加入 M2Pool 矿池大家庭。", allAuthority: [`all`], + // keywords: 'M2Pool 矿池,注册页面,新用户注册,账号创建' + keywords: { en: 'M2Pool Mining Pool,register page,new user registration,account creation', zh: 'M2Pool 矿池,注册页面,新用户注册,账号创建' - } + } } }, { path: '/:lang/simulation', name: 'simulation', component: () => import('../views/simulation.vue'), - meta: {title: '测试页面',description:"M2Pool 矿池测试页面,用于进行系统功能的模拟和测试,确保矿池稳定运行",allAuthority:[`all`], - // keywords: 'M2Pool 矿池,测试页面,系统测试,稳定运行' - keywords:{ - en: 'M2Pool Mining Pool,test page,system test,stable operation', - zh: 'M2Pool 矿池,测试页面,系统测试,稳定运行' - } + meta: { + title: '测试页面', description: "M2Pool 矿池测试页面,用于进行系统功能的模拟和测试,确保矿池稳定运行", allAuthority: [`all`], + // keywords: 'M2Pool 矿池,测试页面,系统测试,稳定运行' + keywords: { + en: 'M2Pool Mining Pool,test page,system test,stable operation', + zh: 'M2Pool 矿池,测试页面,系统测试,稳定运行' + } } }, - - - + + + { path: '/:lang/resetPassword', name: 'ResetPassword', component: () => import('../views/resetPassword/index.vue'), - meta: {title: '重置密码页面',description:"M2Pool 矿池重置密码页面,用户可在此修改矿池网站账号密码,保障账户安全。",allAuthority:[`all`], + meta: { + title: '重置密码页面', description: "M2Pool 矿池重置密码页面,用户可在此修改矿池网站账号密码,保障账户安全。", allAuthority: [`all`], // keywords: 'M2Pool 矿池,重置密码,修改密码,账户安全' - keywords:{ + keywords: { en: 'M2Pool Mining Pool,reset password,modify password,account security', zh: 'M2Pool 矿池,重置密码,修改密码,账户安全' } @@ -610,9 +648,10 @@ const staticRoutes = [ {//404 path: '/:lang/404', component: () => import('../views/page404.vue'), - meta: {title: '404页面',description:"M2Pool 矿池 404 页面,当 URL 错误时将跳转至此页面,提示用户页面不存在。",allAuthority:[`all`], + meta: { + title: '404页面', description: "M2Pool 矿池 404 页面,当 URL 错误时将跳转至此页面,提示用户页面不存在。", allAuthority: [`all`], // keywords: 'M2Pool 矿池,404 页面,页面不存在,错误跳转' - keywords:{ + keywords: { en: 'M2Pool Mining Pool,404 page,page not found,error redirect', zh: 'M2Pool 矿池,404 页面,页面不存在,错误跳转' } @@ -629,12 +668,12 @@ const routes = [ beforeEnter: (to, from, next) => { const lang = to.params.lang const supportedLanguages = ['zh', 'en'] - + // 检查语言参数 if (!supportedLanguages.includes(lang)) { return next(`/en${to.path}`) } - + // 设置语言 if (i18n.locale !== lang) { i18n.locale = lang @@ -671,106 +710,14 @@ const router = new VueRouter({ base: process.env.BASE_URL, routes, strict: true, // 启用严格模式,不允许尾部斜杠 - - + + }) -// router.beforeEach((to, from, next) => { - - - - -// // 检查语言参数 -// const lang = to.params.lang; -// const supportedLanguages = ['zh', 'en']; - -// // 如果路径以斜杠结尾且不是根路径,则重定向 -// if (to.path.endsWith('/') && to.path.length > 1) { -// const path = to.path.slice(0, -1); -// return next({ -// path, -// query: to.query, -// hash: to.hash, -// params: to.params -// }); -// } - -// if (!lang && to.path !== '/') { -// const defaultLang = localStorage.getItem('lang') || 'en'; -// return next(`/${defaultLang}${to.path}`); -// } - -// let data = localStorage.getItem("jurisdiction"); -// let jurisdiction =JSON.parse(data); -// console.log(jurisdiction,"权限"); - -// localStorage.setItem('superReportError',"") -// let element = document.getElementsByClassName('el-main')[0]; -// if(element){ -// element.scrollTop = 0 -// } - -// let token -// try{ -// token =JSON.parse(localStorage.getItem('token')) -// }catch(e){ -// console.log(e); -// } - -// console.log('beforeEach:', to.path, 'token:', token); - - - -// if (token) { - - -// if (to.path === `/${lang}/login`|| to.path === `/${lang}/register`) { -// next({ path: `/${lang}` }) -// }else if(to.meta.allAuthority && to.meta.allAuthority[0] ==`all`){ -// next() -// }else if(jurisdiction.roleKey && to.meta.allAuthority&&to.meta.allAuthority.some(item=>item == jurisdiction.roleKey )){ -// next() -// }else{ -// console.log(to.meta.allAuthority,to.path,"权限"); - -// Message({//权限不足 -// showClose: true, -// message:i18n.t(`mining.jurisdiction`), -// type: 'error' -// }); - -// next({ path: `/${lang}` }) // 添加这行,重定向到首页 -// } - -// }else{ - -// console.log(to.path,"to.path 权限不足"); -// let paths = [`/${lang}/miningAccount`,`/${lang}/workOrderRecords`,`/${lang}/userWorkDetails`,`/${lang}/submitWorkOrder`,`/${lang}/workOrderBackend`,`/${lang}/BKWorkDetails`] -// if (paths.includes(to.path) || to.path.includes(`personalCenter`) ) { -// console.log(to.path,"to.path 权限不足"); - -// Message({//权限不足 -// showClose: true, -// message:i18n.t(`mining.logInFirst`), -// type: 'error' -// }); - -// next({ path: `/${lang}/login` }) -// } else { - -// next() -// } -// return; // 防止多次 next -// } - - - -// }) - router.beforeEach((to, from, next) => { const lang = to.params.lang || (localStorage.getItem('lang') || 'en'); let token = null; @@ -779,40 +726,6 @@ router.beforeEach((to, from, next) => { } catch (e) { token = null; } - - // 需要登录的页面 - const needLoginPaths = [ - `/${lang}/miningAccount`, - `/${lang}/workOrderRecords`, - `/${lang}/userWorkDetails`, - `/${lang}/submitWorkOrder`, - `/${lang}/workOrderBackend`, - `/${lang}/BKWorkDetails` - ]; - - // 未登录 - if (!token) { - // 只在不是登录/注册页时才跳转 - if ( - (needLoginPaths.includes(to.path) || to.path.includes('personalCenter')) && - to.name !== 'Login' && - to.name !== 'Register' - ) { - Message({ - showClose: true, - message: i18n.t(`mining.logInFirst`), - type: 'error' - }); - return next({ path: `/${lang}/login` }); - } - return next(); - } - - // 已登录 - if (to.name === 'Login' || to.name === 'Register') { - return next({ path: `/${lang}` }); - } - // 权限判断 let jurisdiction = null; try { @@ -820,6 +733,74 @@ router.beforeEach((to, from, next) => { } catch (e) { jurisdiction = null; } + + + + // if (jurisdiction && jurisdiction.roleKey == `back_admin`) { + // if (to.name == 'Login' || to.name == 'Register' || jurisdiction && jurisdiction.roleKey && to.meta.allAuthority.includes(jurisdiction.roleKey)) { + // return next(); + // } else { + // // 无权限 + // Message({ + // showClose: true, + // message: `当前身份只能访问后台管理系统`, + // type: 'error' + // }); + // localStorage.setItem("activeIndex", `1`); + // return next({ path: `/${lang}/broadcast` }); + + + // } + // } else { + // // 未登录 + // if (!token) { + // console.log(`未登录`); + // if ( + // to.meta.allAuthority && + // ( + // to.meta.allAuthority[0] === 'all' || + // (jurisdiction && jurisdiction.roleKey && to.meta.allAuthority.includes(jurisdiction.roleKey)) + // ) + // ) { + // return next(); + // } else { + + + // // 无权限 + // Message({ + // showClose: true, + // message: i18n.t(`mining.jurisdiction`), + // type: 'error' + // }); + // return next({ path: `/${lang}/login` }); + // } + + // } else { + // if (to.name === 'Login' || to.name === 'Register') { + // return next({ path: `/${lang}` }); + // } else if (to.meta.allAuthority && + // ( + // to.meta.allAuthority[0] === 'all' || + // (jurisdiction && jurisdiction.roleKey && to.meta.allAuthority.includes(jurisdiction.roleKey)) + // ) + // ) { + // return next(); + // } else { + // // 无权限 + // Message({ + // showClose: true, + // message: i18n.t(`mining.jurisdiction`), + // type: 'error' + // }); + // return next({ path: `/${lang}/login` }); + // } + // } + // } + +// -------------------------- +// 未登录 +if (!token) { + console.log(`未登录`); if ( to.meta.allAuthority && ( @@ -828,15 +809,71 @@ router.beforeEach((to, from, next) => { ) ) { return next(); + } else { + + + // 无权限 + Message({ + showClose: true, + message: i18n.t(`mining.jurisdiction`), + type: 'error' + }); + return next({ path: `/${lang}/login` }); } - // 无权限 - Message({ - showClose: true, - message: i18n.t(`mining.jurisdiction`), - type: 'error' - }); - return next({ path: `/${lang}/login` }); // 建议无权限时跳转登录页而不是首页 +} else { + + + if (jurisdiction && jurisdiction.roleKey == `back_admin`) { + if (to.name == 'Login' || to.name == 'Register' || to.path == `/${lang}` || jurisdiction && jurisdiction.roleKey && to.meta.allAuthority.includes(jurisdiction.roleKey)) { + return next(); + } else { + // 无权限 + Message({ + showClose: true, + message: `当前身份只能访问后台管理系统`, + type: 'error' + }); + localStorage.setItem("activeIndex", `1`); + return next({ path: `/${lang}/broadcast` }); + + + } + } else { + + if (to.name === 'Login' || to.name === 'Register') { + return next({ path: `/${lang}` }); + } else if (to.meta.allAuthority && + ( + to.meta.allAuthority[0] === 'all' || + (jurisdiction && jurisdiction.roleKey && to.meta.allAuthority.includes(jurisdiction.roleKey)) + ) + ) { + return next(); + } else { + // 无权限 + Message({ + showClose: true, + message: i18n.t(`mining.jurisdiction`), + type: 'error' + }); + return next({ path: `/${lang}/login` }); + } + + + } + + + + + + +} + + + + + }); diff --git a/mining-pool/src/utils/errorCode.js b/mining-pool/src/utils/errorCode.js index 7dcc089..919859f 100644 --- a/mining-pool/src/utils/errorCode.js +++ b/mining-pool/src/utils/errorCode.js @@ -1,5 +1,5 @@ export default { - '401': '认证失败,无法访问系统资源,请重新登录', + // '401': '认证失败,无法访问系统资源,请重新登录', '403': '当前操作没有权限', '404': '访问资源不存在', 'default': '系统未知错误,请反馈给管理员' diff --git a/mining-pool/src/utils/request.js b/mining-pool/src/utils/request.js index 4568da6..7b0fce7 100644 --- a/mining-pool/src/utils/request.js +++ b/mining-pool/src/utils/request.js @@ -4,7 +4,7 @@ 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 的函数 @@ -130,7 +130,7 @@ window.addEventListener('online', () => { // 常见的加载状态 const commonLoadingProps = [ 'minerChartLoading', 'reportBlockLoading', 'apiPageLoading', - 'MiningLoading', 'miniLoading' + 'MiningLoading', 'miniLoading', 'bthLoading', 'editLoading' ]; commonLoadingProps.forEach(prop => { @@ -138,6 +138,15 @@ window.addEventListener('online', () => { window.vm[prop] = false; } }); + + // 重置所有以Loading结尾的状态 + Object.keys(window.vm).forEach(key => { + if (key.endsWith('Loading')) { + window.vm[key] = false; + } + }); + + } // 触发网络重试完成事件 @@ -267,10 +276,10 @@ service.interceptors.response.use(res => { type: 'warning' } ).then(() => { - window.vm.$router.push("/login") + window.vm.$router.push(`/${window.vm.$i18n.locale}/login`) localStorage.removeItem('token') }).catch(() => { - window.vm.$router.push("/") + window.vm.$router.push(`/${window.vm.$i18n.locale}/`) localStorage.removeItem('token') }); @@ -308,10 +317,20 @@ service.interceptors.response.use(res => { }, error => { - if (error.message && error.message.includes('canceled') || error.message.includes('Request aborted')) { - // 主动取消的请求,直接忽略,不提示 - return Promise.reject(error); + + // 主动取消的请求,直接忽略,不提示 + if ( + error.code === 'ERR_CANCELED' || + (error.message && error.message.includes('canceled')) || + error.message?.includes('Request aborted') + ) { + // 静默处理,不提示,不冒泡 + return new Promise(() => {}); // 返回pending Promise,阻止控制台报错 } + + + + // 请求异常也要移除 处理Request aborted 错误 if (error.config) { const requestKey = getRequestKey(error.config); diff --git a/mining-pool/src/views/broadcast/index.js b/mining-pool/src/views/broadcast/index.js index 747d5eb..a0ff4a4 100644 --- a/mining-pool/src/views/broadcast/index.js +++ b/mining-pool/src/views/broadcast/index.js @@ -39,6 +39,9 @@ export default { editLoading: false, byteCount: "", isOverLimit: false, + total: 0, + pageSizes: [50, 100, 300], + currentPage: 1, } }, @@ -54,8 +57,7 @@ export default { if (token) { this.fetchList(this.listParams); } - - + }, methods: { async fetchList(params) { @@ -64,6 +66,7 @@ export default { const res = await listBroadcast(params) if (res.code === 200) { this.tableData = res.rows + this.total = res.total } this.setLoading('broadcastLoading', false); }, @@ -109,6 +112,7 @@ export default { handleClose() { this.dialogVisible = false; this.addParams.content = "" + this.setLoading('bthLoading', false); }, sureAddBroadcast() { @@ -136,6 +140,7 @@ export default { handleEditClose() { this.editDialogVisible = false; this.editParams.content = "" + this.setLoading('editLoading', false); }, handelDelete(row) { this.deleteBroadcast({ id: row.id }); @@ -184,9 +189,28 @@ export default { handelTime(time) { return `${time.split("T")[0]} ${time.split("T")[1]}` - } + }, + handleSizeChange(val) { + console.log(`每页 ${val} 条`); + this.listParams.pageSize = val + this.listParams.pageNum = 1 + this.currentPage = 1 + this.fetchList(this.listParams); + + }, + handleCurrentChange(val) { + console.log(`当前页: ${val}`); + this.listParams.pageNum = val + this.fetchList(this.listParams); - } + }, + + + + + + }, + } \ No newline at end of file diff --git a/mining-pool/src/views/broadcast/index.vue b/mining-pool/src/views/broadcast/index.vue index 22c0e4a..e390f4e 100644 --- a/mining-pool/src/views/broadcast/index.vue +++ b/mining-pool/src/views/broadcast/index.vue @@ -1,153 +1,234 @@ \ No newline at end of file diff --git a/mining-pool/src/views/customerService/index.vue b/mining-pool/src/views/customerService/index.vue index 1627b82..a17e132 100644 --- a/mining-pool/src/views/customerService/index.vue +++ b/mining-pool/src/views/customerService/index.vue @@ -433,7 +433,7 @@ export default { }, }, - async created() { + created() { try { let userEmail = localStorage.getItem("userEmail"); this.userEmail = JSON.parse(userEmail); @@ -443,11 +443,11 @@ export default { }); // 获取聊天室列表 - await this.fetchRoomList(); + this.fetchRoomList(); // 在组件创建时加载手动创建的聊天室 this.loadManualCreatedRooms(); - console.log(this.userEmail, "初始化的时候"); + console.log("mounted userEmail=", userEmail, "parsed=", this.userEmail); // 初始化 WebSocket 连接 this.initWebSocket(); } catch (error) { @@ -455,6 +455,8 @@ export default { } }, async mounted() { + + // 获取聊天室列表 await this.fetchRoomList(); @@ -577,6 +579,9 @@ export default { return; } + console.log("走这里了嘛 家电节爱哦都觉得久啊是哦大点击"); + + // 防止重复初始化 if (this.stompClient && this.stompClient.state !== "DISCONNECTED") { console.log("WebSocket正在连接中,跳过初始化"); @@ -588,15 +593,9 @@ export default { if (this.stompClient) { this.forceDisconnectAll(); } - let apiUrl = process.env.VUE_APP_BASE_API; - let baseUrl="" - // 将 https 替换为 wss - if (apiUrl.startsWith("https://")) { - baseUrl= apiUrl.replace("https://", "wss://"); - } - if (apiUrl.startsWith("http://")) { - baseUrl=apiUrl.replace("http://", "ws://"); - } + + console.log("开始初始化WebSocket连接..."); + const baseUrl = process.env.VUE_APP_BASE_API.replace("https", "wss"); const wsUrl = `${baseUrl}chat/ws`; this.stompClient = Stomp.client(wsUrl); @@ -650,11 +649,12 @@ export default { this.subscribeToMessages(); this.updateLastActivityTime(); - // === 启动心跳检测 === - this.startHeartbeat(); - - // === 启动连接状态检查 === - this.startConnectionCheck(); + // === 暂时禁用心跳和连接检查,避免误判 === + // this.startHeartbeat(); + // this.startConnectionCheck(); + + // 只依赖 STOMP 内置心跳和订阅成功状态 + console.log("✅ 连接成功,只使用 STOMP 内置心跳机制"); // === 注意:不在这里启动验证,而是在订阅成功后 === console.log("⚡ 客服连接成功,等待订阅完成后验证"); @@ -715,11 +715,10 @@ export default { console.log("📢 客服订阅成功,立即标记连接已验证"); this.markConnectionVerified(); - // 确保连接状态正确 - if (this.connectionStatus !== "connected") { - console.log("📡 修正客服连接状态为connected"); - this.connectionStatus = "connected"; - } + // 强制确保连接状态正确 + this.isWebSocketConnected = true; + this.connectionStatus = "connected"; + console.log("✅ 强制设置连接状态为已连接"); } else { console.error("❌ 客服订阅失败,返回空subscription"); // 如果订阅失败,启动验证机制等待超时重连 @@ -1734,6 +1733,10 @@ export default { this.markConnectionVerified(); this.updateLastActivityTime(); // 收到消息也是一种活动 this.lastHeartbeatTime = Date.now(); // 更新心跳时间 + + // 强制确保连接状态正确(收到消息说明连接肯定是好的) + this.isWebSocketConnected = true; + this.connectionStatus = "connected"; const msg = JSON.parse(message.body); console.log("客服收到的消息", msg); @@ -3665,14 +3668,45 @@ export default { localStorage.setItem(key, String(count)); }, /** - * 监听storage事件,实现多窗口未读同步 + * 监听storage事件,实现多窗口未读同步和登录状态同步 */ handleStorageChange(e) { + // 监听未读消息同步 if (e.key && e.key.startsWith("cs_unread_")) { const roomId = e.key.replace("cs_unread_", ""); const count = parseInt(e.newValue, 10) || 0; const contact = this.contacts.find((c) => c.roomId == roomId); if (contact) contact.unread = count; + return; + } + + // 监听登录状态同步 - 当userEmail被清除时表示用户已退出登录 + if (e.key === "userEmail" && e.oldValue && (!e.newValue || e.newValue === "null")) { + this.handleLogoutSync(); + } + }, + + /** + * 处理多窗口登录状态同步 + */ + handleLogoutSync() { + try { + // 断开WebSocket连接 + this.forceDisconnectAll(); + + // 清除本地数据 + this.userEmail = ""; + this.currentContactId = null; + this.contacts = []; + this.messages = {}; + this.inputMessage = ""; + this.isWebSocketConnected = false; + this.connectionStatus = "disconnected"; + + // 跳转到首页 + this.$router.replace("/"); + } catch (error) { + this.$router.replace("/"); } }, diff --git a/mining-pool/src/views/home/index.js b/mining-pool/src/views/home/index.js index 27c5830..84551d4 100644 --- a/mining-pool/src/views/home/index.js +++ b/mining-pool/src/views/home/index.js @@ -918,7 +918,7 @@ export default { }); // this.getBroadcastList({pageNum:1,pageSize:100}) - this.getBroadcastList() + this.getBroadcastList({lang:this.$i18n.locale}) this.startScroll(); this.$nextTick(() => { @@ -1732,6 +1732,10 @@ scrollRight() { if (this.broadcastList.length <= 1) return; this.scrollTimer = setInterval(this.nextScroll, 3000); }, + stopScroll() { + if (this.scrollTimer) clearInterval(this.scrollTimer); + this.scrollTimer = null; + }, nextScroll() { if (this.broadcastList.length <= 1) return; this.isTransition = true; diff --git a/mining-pool/src/views/home/index.vue b/mining-pool/src/views/home/index.vue index a910326..eefe8be 100644 --- a/mining-pool/src/views/home/index.vue +++ b/mining-pool/src/views/home/index.vue @@ -421,7 +421,8 @@
-
+
{{ $t(`home.describeTitle`) }} - {{ item.content }} + {{ item.content }} ${params[i].marker} ${params[i].seriesName}      ${params[i].value} USD` + // } else { + // res += `
${params[i].marker} ${params[i].seriesName}      ${params[i].value}` + // } + + // } + + + + // return res; + // }, + + }, + legend: { + right: "8%", + + }, + // grid: {//解决Y轴显示不全 + // left: "10%",//10% + // containLabel: true + // }, + grid: { + left: "8%", + right: "8%", + top: "10%", + bottom: "15%", // 增加底部空间,为旋转的X轴标签和滑动条留出空间 + }, + + + xAxis: { + // type: "time", + boundaryGap: true, // 让柱子不超出X轴 + axisLabel: { + interval: 'auto', // 自动间隔显示标签,避免重叠 + rotate: 45, // 旋转45度,避免标签重叠 + formatter: function(value) { + // 格式化显示,只显示月-日 时:分 + if (value.includes(' ')) { + const [date, time] = value.split(' '); + const [year, month, day] = date.split('-'); + const [hour, minute] = time.split(':'); + return `${month}-${day} ${hour}:${minute}`; + } + return value; + } + }, + // axisTick: { + // //去除刻度 + // show: false, + // }, + // axisLine: { + // //去除轴线 + // show: false, + // }, + data: [] + }, + yAxis: [ + { + // position: "left", + type: "value", + name: "GH/s", + nameTextStyle: { + + padding: [0, 0, 0, -40], + }, + // min: `dataMin`, + // max: `dataMax`, + 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 + } + } + }, + { + + position: "right", + // type: "log", + // splitNumber: "5", + show: true, + // min: 0, + // max: this.maxValue, + splitLine: {//不显示右侧Y轴横线 + show: false + }, + // name: "", + nameTextStyle: { + + padding: [0, 0, 0, 40], + } + }, + + ], + dataZoom: [ + { + type: "inside", + start: 50, // 默认显示后30%的数据,避免图表过于拥挤 + end: 100, + maxSpan: 100, + minSpan: 2, // 最小显示5%的数据 + animation: false, + }, + { + type: "inside", + start: 50, + end: 100, + height: 20, // 滑动条高度 + bottom: 0, // 滑动条位置 + showDetail: false, // 不显示详细数值 + }, + ], + series: [ + + {//在线数量 + name: "Number of users online", + type: "bar", + data: [], + yAxisIndex: 1, + itemStyle: { + color: '#239342', // 这里设置柱子的主色为绿色 + + }, + barWidth: '40%', // 使用百分比宽度,保持适当的柱子宽度 + barGap: '10%', // 同类型柱子间的间隔 + barCategoryGap: '20%', // 类目间的间隔 + + }, + { + name: "Number of offline users", + type: "bar", + // smooth: false, //线条是否圆滑 + // symbol: "circle", + // symbolSize: 5, + // showSymbol: false, + itemStyle: { + color: "#FE2E74", + borderColor: "rgba(221,220,107,0.1)", + + }, + barWidth: '40%', // 使用百分比宽度,保持适当的柱子宽度 + barGap: '10%', // 同类型柱子间的间隔 + barCategoryGap: '20%', // 类目间的间隔 + + data: [], + yAxisIndex: 1, + + }, + { + name: "User computing power", + type: "line", + smooth: false, //线条是否圆滑 + symbol: "circle", + symbolSize: 5, + showSymbol: false, + itemStyle: { + color: "#5721E4", + borderColor: "rgba(221,220,107,0.1)", + borderWidth: 12, + }, + lineStyle: { + //线条样式 + color: "#5721E4", + width: "2", + }, + // areaStyle: { + // color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + // { + // offset: 0, + // color: 'rgb(210,195,234)' + // }, + // { + // offset: 1, + // color: 'rgb(255, 255, 255)' + // } + // ]) + // }, + zlevel: 1, z: 1, + data: [], + yAxisIndex: 0, + }, + + + + ], + }, + lineChartLoading:false, + lineChartData:[ + + { + "minerUser": "miner", + "pv": "8.00080", + "date": "2024-11-28T11:00:00", + "unit":"GH/s" + }, + { + "minerUser": "miner", + "pv": "6.00060", + "date": "2024-11-28T11:30:00", + "unit":"GH/s" + }, + { + "minerUser": "miner", + "pv": "0.05000", + "date": "2024-11-28T12:00:00", + "unit":"GH/s" + }, + { + "minerUser": "miner", + "pv": "2.0000", + "date": "2024-11-28T12:30:00", + "unit":"GH/s" + }, + { + "minerUser": "miner", + "pv": "0.3000", + "date": "2024-11-28T13:00:00", + "unit":"GH/s" + }, + { + "minerUser": "miner", + "pv": "0.9000", + "date": "2024-11-28T13:30:00", + "unit":"GH/s" + }, + { + "minerUser": "miner", + "pv": "15.0000", + "date": "2024-11-28T14:00:00", + "unit":"GH/s" + }, + + + + ], + chartShow:true, + tableData:[], + historyBalance:[], + } }, + mounted() { - console.log('userDetails mounted', this.$route.path, this.$route.query) + let token + try{ + token =JSON.parse(localStorage.getItem('token')) + }catch(e){ + console.log(e); + } + if (!token) { + this.$router.push({ path: `/${lang}/login` }); + + } const params= this.$route.query || JSON.parse(localStorage.getItem("userDetailsParams")); - this.lineChartParams.user=params.user + this.lineChartParams.minerUser=params.minerUser this.lineChartParams.coin=params.coin - this.onlineStatusParams.user=params.user + this.onlineStatusParams.minerUser=params.minerUser this.onlineStatusParams.coin=params.coin this.userDetailsParams.coin=params.coin this.userDetailsParams.minerUser=params.minerUser @@ -66,42 +378,132 @@ export default { localStorage.setItem("userDetailsParams", JSON.stringify(params)); this.fetchUserDetails(this.userDetailsParams); - this.fetchUserLineChart(this.lineChartParams); - this.fetchUserOnlineStatus(this.onlineStatusParams); + // 等待两个图表相关接口都完成后再调用inCharts + this.fetchChartData(); } + + + }, methods: { + //初始化图表 + inCharts() { + if (this.myChart == null) { + this.myChart = echarts.init(document.getElementById("lineChart")); + } + + + this.option.series[0].name = this.$t(`backendSystem.onlineUserNum`) + this.option.series[1].name = this.$t(`backendSystem.offlineUserNum`) + this.option.series[2].name = this.$t(`backendSystem.userPower`) + + this.myChart.setOption(this.option); + // 回调函数,在渲染完成后执行 + this.myChart.on('finished', () => { + // 图表渲染完成 + console.log('图表渲染完成'); + }); + + window.addEventListener('resize', throttle(() => { + if (this.myChart) this.myChart.resize(); + }, 200)); + }, async fetchUserDetails(params) { - this.userDetailsLoading = true - // 这里写你的获取详情逻辑 + this.setLoading('userDetailsLoading', true); + // 获取用户详情逻辑 const res = await getUserDetails(params) console.log(res) if(res && res.code == 200){ if (!res.data) { - // this.$message.error('未获取到用户信息'); this.noDataTip = true - this.userData.coin = this.userDetailsParams.coin - this.userData.user = this.userDetailsParams.minerUser + // this.userData.coin = this.userDetailsParams.coin + // this.userData.user = this.userDetailsParams.minerUser }else{ this.userData = res.data - this.userData.shouldOutDate=`${this.userData.shouldOutDate.split("T")[0]} ${this.userData.shouldOutDate.split("T")[1]}` - this.userData.createDate=`${this.userData.createDate.split("T")[0]} ${this.userData.createDate.split("T")[1]}` - + // this.userData.shouldOutDate=`${this.userData.shouldOutDate.split("T")[0]} ${this.userData.shouldOutDate.split("T")[1]}` + // this.userData.createDate=`${this.userData.createDate.split("T")[0]} ${this.userData.createDate.split("T")[1]}` + this.historyBalance=res.data.historyBalance + this.tableData = res.data.walletInInfo.sort((a, b) => new Date(b.createDate) - new Date(a.createDate)); this.noDataTip = false } } - this.userDetailsLoading = false + this.setLoading('userDetailsLoading', false); }, async fetchUserLineChart(params) { const res = await getUserLineChart(params) console.log(res) + let xData = [] + let pvData = [] + if(res && res.code == 200){ + + if (!Array.isArray(res.data) || res.data.length === 0 || !res.data[0].unit) { + this.chartShow = false + return; + } + this.chartShow = true + this.lineChartData = res.data + this.lineChartData.forEach(item => { + item.date = item.date.split("T")[0] + " " + item.date.split("T")[1].split(".")[0] + xData.push(item.date) + pvData.push(Number(item.pv).toFixed(6)) + }) + + this.option.xAxis.data = xData + this.option.series[2].data = pvData + this.option.yAxis[0].name = this.lineChartData[0].unit + // this.inCharts() + } }, async fetchUserOnlineStatus(params) { const res = await getUserOnlineStatus(params) console.log(res) + // let xData = [] + let offlineNum = [] + let onlineNum = [] + if(res && res.code == 200){ + if (!Array.isArray(res.data) || res.data.length === 0 ) { + this.chartShow = false + return; + } + this.chartShow = true + this.onlineStatusData = res.data + + this.onlineStatusData.forEach(item => { + offlineNum.push(item.offlineNum) + onlineNum.push(item.onlineNum) + }) + this.option.series[0].data = onlineNum + this.option.series[1].data = offlineNum + + + } + }, + /** + * 获取图表数据 - 等待两个接口都完成后再渲染图表 + */ + async fetchChartData() { + try { + this.setLoading('lineChartLoading', true); + + // 并行调用两个接口,等待都完成 + await Promise.all([ + this.fetchUserLineChart(this.lineChartParams), + this.fetchUserOnlineStatus(this.onlineStatusParams) + ]); + + // 两个接口都完成后,检查是否有数据再渲染图表 + if (this.chartShow) { + this.inCharts(); + } + + } catch (error) { + console.error('获取图表数据失败:', error); + } finally { + this.setLoading('lineChartLoading', false); + } }, goBack(){ const lang = this.$i18n.locale; @@ -109,5 +511,31 @@ export default { path: `/${lang}/userManagement`, }) }, + + handleLineChatTimesChange(val){ + console.log(val,'val'); + if(val){ + this.lineChartParams.startDate=val[0] + this.lineChartParams.endDate=val[1] + this.onlineStatusParams.startDate=val[0] + this.onlineStatusParams.endDate=val[1] + this.userDetailsParams.startDate=val[0] + this.userDetailsParams.endDate=val[1] + + }else{ + this.lineChartParams.startDate="" + this.lineChartParams.endDate="" + this.onlineStatusParams.startDate="" + this.onlineStatusParams.endDate="" + this.userDetailsParams.startDate="" + this.userDetailsParams.endDate="" + } + this.fetchUserDetails(this.userDetailsParams); + // 等待两个图表相关接口都完成后再调用inCharts + this.fetchChartData(); + }, + handelTime(time){ + return time.split("T")[0] + " " + time.split("T")[1] + } } } \ No newline at end of file diff --git a/mining-pool/src/views/userDetails/index.vue b/mining-pool/src/views/userDetails/index.vue index 698f19d..094becd 100644 --- a/mining-pool/src/views/userDetails/index.vue +++ b/mining-pool/src/views/userDetails/index.vue @@ -1,87 +1,216 @@ @@ -100,7 +229,7 @@ export default { color: #333; margin-bottom: 18px; } -.user-details-box{ +.user-details-box { width: 100%; margin: 0 auto; box-sizing: border-box; @@ -124,55 +253,76 @@ export default { // background: palegoldenrod; } -.no-data-tip{ +.no-data-tip { width: 80%; margin: 0 auto; font-size: 16px; color: #999; text-align: center; margin-top: 20px; - } -.history-balance-item{ - - margin: 0; - padding: 0; - background:#E7DFF3 ; - padding: 0px 20px; +.history-balance-item { + display: inline-block; /* 让宽度随内容自适应 */ + background: #e7dff3; + padding: 8px 20px; border-radius: 20px; margin-bottom: 10px; - color: rgba(0,0,0,0.6); - + color: rgba(0, 0, 0, 0.6); + /* width: auto; 其实可以省略,inline-block默认就是auto */ + /* 防止flex布局下被拉伸 */ + flex: none; + align-self: flex-start; + margin-left: 10px; } ::v-deep .el-form-item__label { // color: #409EFF; /* 例如设置为 Element UI 主色 */ - font-weight: bold; /* 加粗 */ - font-size: 16px; /* 字号 */ - letter-spacing: 1px; /* 字间距 */ + font-weight: bold; /* 加粗 */ + font-size: 16px; /* 字号 */ + letter-spacing: 1px; /* 字间距 */ /* 你可以根据需要添加更多样式 */ } -.chartBox{ +.chartBox { width: 80vw; box-sizing: border-box; // background: palegoldenrod; - height: 800px; + + + padding: 0; + margin: 30px 0; +} + +.lineChartBox { + width: 97%; + height: 80%; + + box-sizing: border-box; + box-shadow: 0px 0px 1px 1px #ccc; + padding: 20px; + border-radius: 10px; +} +.lineChartBox-title { + font-size: 16px; + font-weight: bold; + color: rgba(0, 0, 0, 0.6); + margin-bottom: 20px; +} +.lineChartBox-header{ display: flex; - align-content: center; - justify-content: space-around; - + justify-content: space-between; + align-items: center; + margin-bottom: 18px; + padding:1% 5%; } -.lineChartBox{ - width: 49%; - height: 100%; - background: palegoldenrod; + + + \ No newline at end of file + diff --git a/mining-pool/src/views/userManagement/index.js b/mining-pool/src/views/userManagement/index.js index 7b36db6..faf9bf8 100644 --- a/mining-pool/src/views/userManagement/index.js +++ b/mining-pool/src/views/userManagement/index.js @@ -24,17 +24,17 @@ export default { user: [ { type: 'email', - message: '请输入正确的邮箱地址', + message: this.$t('backendSystem.pleaseInputCorrectEmail'), trigger: ['blur', 'change'] } ] }, emailRules: { subject: [ - { required: true, message: '请输入邮件主题', trigger: 'blur' } + { required: true, message: this.$t('backendSystem.pleaseInputSubject'), trigger: 'blur' } ], text: [ - { required: true, message: '请输入邮件内容', trigger: 'blur' } + { required: true, message: this.$t('backendSystem.pleaseInputText'), trigger: 'blur' } ], to: [ { @@ -56,7 +56,7 @@ export default { // 检查格式 for (let email of emails) { if (!emailReg.test(email)) { - callback(new Error('请输入正确的邮箱地址,多个邮箱用逗号分隔')); + callback(new Error(this.$t('backendSystem.pleaseInputCorrectEmail2'))); return; } } @@ -65,7 +65,7 @@ export default { for (let email of emails) { const lower = email.toLowerCase(); if (lowerSet.has(lower)) { - callback(new Error('存在重复邮箱,请检查')); + callback(new Error(this.$t('backendSystem.existDuplicateEmail'))); return; } lowerSet.add(lower); @@ -83,6 +83,9 @@ export default { to:"", }, sendEmailLoading: false, + total: 0, + pageSizes: [50, 100, 300], + currentPage: 1, } @@ -94,11 +97,11 @@ export default { }catch(e){ console.log(e); } - if (token) { + if (!token) { + this.$router.push({ path: `/${lang}/login` }); - this.fetchUserList(this.userListParams); } - + this.fetchUserList(this.userListParams); this.currencyList = JSON.parse(localStorage.getItem("currencyList")) window.addEventListener("setItem", () => { this.currencyList = JSON.parse(localStorage.getItem("currencyList")) @@ -110,9 +113,12 @@ export default { this.setLoading('userManagementLoading', true); const data = await getUserList(params); + console.log(data,'data'); if (data && data.code == 200) { this.tableData = data.rows; + this.total = data.total; + } this.setLoading('userManagementLoading', false); @@ -124,7 +130,7 @@ export default { if (data && data.code == 200) { - this.$message.success('发送成功'); + this.$message.success(this.$t('backendSystem.sendSuccess')); this.dialogVisible = false; for (const key in this.senParams) { this.senParams[key] = ""; @@ -146,7 +152,11 @@ export default { }, handelImg(coin) { - return this.currencyList.find(item => item.value === coin)?.imgUrl || ''; + if(this.currencyList &&this.currencyList.length > 0 && coin){ + return this.currencyList.find(item => item.value === coin)?.imgUrl || ''; + }else{ + return ''; + } }, handelQuery() { this.$refs.formRef.validate((valid) => { @@ -158,7 +168,7 @@ export default { } if (!this.userListParams.minerUser && !this.userListParams.user) { - this.$message.error('请输入查询条件(挖矿账号、邮箱)'); + this.$message.error(this.$t('backendSystem.pleaseInputQueryConditions')); return; } this.fetchUserList(this.userListParams); @@ -218,6 +228,23 @@ export default { // 保存ID到localStorage localStorage.setItem("userDetailsParams",JSON.stringify(obj)); - } + }, + + handleSizeChange(val) { + console.log(`每页 ${val} 条`); + this.userListParams.pageSize = val + this.userListParams.pageNum = 1 + this.currentPage = 1 + this.fetchUserList(this.userListParams); + + }, + handleCurrentChange(val) { + console.log(`当前页: ${val}`); + this.userListParams.pageNum = val + this.fetchUserList(this.userListParams); + + + + }, } } \ No newline at end of file diff --git a/mining-pool/src/views/userManagement/index.vue b/mining-pool/src/views/userManagement/index.vue index ed6245d..cbd727e 100644 --- a/mining-pool/src/views/userManagement/index.vue +++ b/mining-pool/src/views/userManagement/index.vue @@ -1,9 +1,9 @@