Compare commits

...

21 Commits

Author SHA1 Message Date
lzx a8e3066e02 Merge branch 'dev' of http://47.129.22.53:22345/lizixuan/m2pool_web_frontend 2025-06-17 13:40:37 +08:00
yaoqin 0dbb43233a 聊天系统断网重连及客服离线识别 完成 2025-06-17 11:27:44 +08:00
yaoqin e74f5b8d75 电脑端聊天客服系统完成 2025-06-13 14:58:47 +08:00
yaoqin ac85206085 更新服务条款页面内容 2025-06-06 15:31:04 +08:00
yaoqin e0a7fb8ee2 1.游客功能添加、删除列表离线游客 目前游客断开没有返回关闭信息
2.游客收不到客服消息 已处理
3.中英文翻译 已完成
4.将本地时间修改为UTC时间 完成
5.发送图片有问题 又重新改回url上传
6.游客用户增加提示和跳转登录的功能
7.客服消息换行及显示已处理
8.客服端限制输入400个字符 用户端限制输入300个字符 完成
2025-05-30 16:39:09 +08:00
yaoqin 99b471bb86 更新聊天客服系统软件测试 2025-05-28 15:01:22 +08:00
yaoqin 38fbb4e625 客服系统:1.滚动到列表下方分页请求历史用户列表拼接 完成 2.游客功能添加、删除列表离线游客 目前游客断开没有返回关闭信息 3.中英文翻译 处理中 4.客服页面添加回到底部功能 增加用户体验 完成 2025-05-23 14:46:29 +08:00
lzx 75b9fb289c Merge branch 'dev' of http://47.129.22.53:22345/lizixuan/m2pool_web_frontend into dev 2025-05-16 17:22:20 +08:00
lizixuan f9869da1ae revert 65e02d1d26
revert revert a3c6bf8e07

revert Merge branch 'dev' of http://47.129.22.53:22345/lizixuan/m2pool_web_frontend into dev
 :wq
2025-05-16 09:21:17 +00:00
lizixuan 65e02d1d26 revert a3c6bf8e07
revert Merge branch 'dev' of http://47.129.22.53:22345/lizixuan/m2pool_web_frontend into dev
 :wq
2025-05-16 09:17:18 +00:00
yaoqin d3ac95af75 修复主页图片显示bug 2025-05-16 16:53:11 +08:00
yaoqin a326f62f81 .m2pool 客服系统对接 1.订阅接口区分用户和客服 避免消息混乱 已处理 2.历史消息渲染isSelf返回值有问题 已处理 3.图片发送方式修改为用base64Image发送后soket自动断开 已处理 4.优化心跳机制 断线重连 最多自动重连5次 已处理 5.游客功能添加、删除列表离线游客 处理中
6.客服给游客发送消息 游客收不到推送消息 已处理
2025-05-16 14:01:38 +08:00
yaoqin 2e56d71b0c 客服聊天系统对接及功能添加 2025-04-30 15:22:35 +08:00
yaoqin 13add51a20 1.聊天系统页面完成
2.功能及接口联调中
2025-04-25 14:09:32 +08:00
yaoqin a3c6bf8e07 Merge branch 'dev' of http://47.129.22.53:22345/lizixuan/m2pool_web_frontend into dev
:wq
2025-04-22 14:37:23 +08:00
yaoqin 21a331680e 取消enx活动图片 费率改为1%. 2025-04-22 14:34:00 +08:00
yaoqin 3ee77547ff 取消enx活动图片 费率改为1% 2025-04-22 14:31:04 +08:00
ylx 4332df54e8 更新 README.md 2025-04-22 06:27:50 +00:00
yaoqin 368d0a8a10 取消enx活动图片 费率改为1% 2025-04-22 14:26:41 +08:00
yaoqin 4d436c725e 1.m2pool断网重连 60秒内重连机制 所有页面添加
2.优化响应错误提示 3秒内同一种错误只提示一次
2025-04-18 14:45:39 +08:00
ylx 66a2381e41 更新 README.md
修改readme的位置
2025-04-18 06:40:47 +00:00
211 changed files with 10252 additions and 944 deletions

278
README.md
View File

@ -1,278 +0,0 @@
const { defineConfig } = require('@vue/cli-service');
const path = require('path');
const PrerenderSPAPlugin = require('@dreysolano/prerender-spa-plugin');
const Renderer = PrerenderSPAPlugin.PuppeteerRenderer;
const SitemapWebpackPlugin = require('sitemap-webpack-plugin').default;
const CompressionPlugin = require('compression-webpack-plugin');
module.exports = defineConfig({
transpileDependencies: true,
devServer: {
hot: true, // 启用HMR
liveReload: true, // 启用实时重载
client: {
overlay: false // 禁用错误遮罩层
}
},
configureWebpack: config => {
const baseConfig = {
cache: {
type: 'filesystem', // 使用文件系统缓存 加快二次构建速度 减少重复编译
buildDependencies: {
config: [__filename]
},
// 添加缓存配置
cacheDirectory: path.resolve(__dirname, 'node_modules/.cache/webpack'),
name: process.env.NODE_ENV // 区分环境
},
optimization: {
moduleIds: 'deterministic',
splitChunks: { // 代码分割配置文件系统缓存比内存缓存更持久 可以在多次构建之间复用编译结果
chunks: 'all',
minSize: 30000, // 最小分块大小
maxSize: 300000, // 最大分块大小 避免单个文件过大
cacheGroups: { // 缓存组配置
vendors: { // 第三方库分组
name: 'chunk-vendors',
test: /[\\/]node_modules[\\/]/,
priority: -10,
chunks: 'initial'
},
common: { // 公共模块分组
name: 'chunk-common',
minChunks: 2,
priority: -20,
chunks: 'initial',
reuseExistingChunk: true
}
},
}
},
plugins: [ //压缩静态资源 减少传输体积 提升加载速度
new CompressionPlugin({
test: /\.(js|css|html|svg)$/,
threshold: 10240,
minRatio: 0.8
})
]
};
if (process.env.NODE_ENV === 'production') {
// 优化预渲染插件配置
const prerenderPlugin = new PrerenderSPAPlugin({
staticDir: path.join(__dirname, 'dist'),
// 只预渲染最重要的路由
routes: ['/zh', '/en','/en/dataDisplay', '/zh/dataDisplay','/zh/rate', '/en/rate','/zh/ServiceTerms', '/en/ServiceTerms','/zh/apiFile', '/en/apiFile'],
renderer: new Renderer({
renderAfterTime: 1000, // 增加等待时间,确保内容加载完成
maxConcurrentRoutes:2, // 降低并发数,避免资源竞争
timeout: 10000, // 增加超时时间
headless: true,
}),
});
const zhSitemapPlugin = new SitemapWebpackPlugin({
base: 'https://m2pool.com/zh',
paths: [
{
path: '/zh',
changefreq: 'daily',
lastmod: new Date().toISOString()
},
{
path: '/zh/dataDisplay',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/zh/ServiceTerms',
changefreq: 'monthly',
lastmod: new Date().toISOString()
},
{
path: '/zh/apiFile',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/zh/rate',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/zh/AccessMiningPool/nexaAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/zh/AccessMiningPool/grsAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/zh/AccessMiningPool/monaAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/zh/AccessMiningPool/dgbsAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/zh/AccessMiningPool/dgbqAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/zh/AccessMiningPool/dgboAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/zh/AccessMiningPool/rxdAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
],
options: {
filename: 'sitemap-zh.xml',
lastmod: true,
changefreq: 'weekly',
priority: 0.7
}
});
const enSitemapPlugin = new SitemapWebpackPlugin({
base: 'https://m2pool.com/en',
paths: [
{
path: '/en',
priority: 1.0,
changefreq: 'daily',
lastmod: new Date().toISOString()
},
{
path: '/en/dataDisplay',
priority: 0.8,
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/en/ServiceTerms',
priority: 0.6,
changefreq: 'monthly',
lastmod: new Date().toISOString()
},
{
path: '/en/apiFile',
priority: 0.7,
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/en/rate',
priority: 0.8,
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/en/AccessMiningPool/nexaAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/en/AccessMiningPool/grsAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/en/AccessMiningPool/monaAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/en/AccessMiningPool/dgbsAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/en/AccessMiningPool/dgbqAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/en/AccessMiningPool/dgboAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
{
path: '/en/AccessMiningPool/rxdAccess',
changefreq: 'weekly',
lastmod: new Date().toISOString()
},
],
options: {
filename: 'sitemap-en.xml',
lastmod: true,
changefreq: 'weekly',
priority: 0.7
}
});
baseConfig.plugins.push(zhSitemapPlugin);
baseConfig.plugins.push(enSitemapPlugin);
baseConfig.plugins.push(prerenderPlugin);
}
return baseConfig;
},
chainWebpack: config => {
// HTML压缩配置
config.plugin('html').tap(args => {
args[0].minify = {
removeComments: true, // 删除 HTML 注释减小文件体积
collapseWhitespace: true, // 压缩空白字符 删除多余的空格和换行
removeAttributeQuotes: true, // 删除属性的引号
collapseBooleanAttributes: true, // 简化布尔属性
removeScriptTypeAttributes: true // 删除默认的 script type 属性type="text/javascript"
};
// META 标签配置
args[0].meta = {
// 移动端视口配置
viewport: 'width=device-width, initial-scale=1, shrink-to-fit=no',
'format-detection': 'telephone=no',// 禁用电话号码自动识别
'apple-mobile-web-app-capable': 'yes' // 支持添加到 iOS 主屏幕
};
return args;
});
// 移除 prefetch 插件
//禁用 Vue CLI 默认的预取功能 prefetch 会在主资源加载完后,
// 自动下载所有其他路由的资源对于大型应用,可能会预加载过多不必要的资源
config.plugins.delete('prefetch')
},
//生产环境不生成 source map 文件源码映射信息 减小打包体积 提高网站安全性
productionSourceMap: false,
css: {
extract: true, //组件中的 CSS 提取至独立的 CSS 文件中 支持并行加载,减少页面阻塞
sourceMap: false,// CSS 不启用 source maps 减少构建文件大小
loaderOptions: {
css: {}
}
}
});

View File

@ -6,6 +6,7 @@ ENV = 'development'
#开发环境 #开发环境
VUE_APP_BASE_API = 'https://test.m2pool.com/api/' VUE_APP_BASE_API = 'https://test.m2pool.com/api/'
# VUE_APP_BASE_API = 'http://10.168.2.150:8101/'
VUE_APP_BASE_URL = 'https://test.m2pool.com/' VUE_APP_BASE_URL = 'https://test.m2pool.com/'
# 路由懒加载 # 路由懒加载
VUE_CLI_BABEL_TRANSPILE_MODULES = true VUE_CLI_BABEL_TRANSPILE_MODULES = true

Binary file not shown.

View File

@ -290,13 +290,45 @@
} }
}, },
"@babel/helpers": { "@babel/helpers": {
"version": "7.26.9", "version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.9.tgz", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
"integrity": "sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==", "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/template": "^7.26.9", "@babel/template": "^7.27.0",
"@babel/types": "^7.26.9" "@babel/types": "^7.27.0"
},
"dependencies": {
"@babel/parser": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
"integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
"dev": true,
"requires": {
"@babel/types": "^7.27.0"
}
},
"@babel/template": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
"integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.26.2",
"@babel/parser": "^7.27.0",
"@babel/types": "^7.27.0"
}
},
"@babel/types": {
"version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
"dev": true,
"requires": {
"@babel/helper-string-parser": "^7.25.9",
"@babel/helper-validator-identifier": "^7.25.9"
}
}
} }
}, },
"@babel/highlight": { "@babel/highlight": {
@ -1104,9 +1136,9 @@
} }
}, },
"@babel/runtime": { "@babel/runtime": {
"version": "7.26.9", "version": "7.27.0",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.9.tgz", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
"integrity": "sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==", "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
"dev": true, "dev": true,
"requires": { "requires": {
"regenerator-runtime": "^0.14.0" "regenerator-runtime": "^0.14.0"
@ -1718,6 +1750,11 @@
"integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==", "integrity": "sha512-T7VNNlYVM1SgQ+VsMYhnDkcGmWhQdL0bDyGm5TlQ3GBXnJscEClUUOKduWTmm2zCnvNLC1hc3JpuXjs/nFOc5w==",
"dev": true "dev": true
}, },
"@stomp/stompjs": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.1.1.tgz",
"integrity": "sha512-chcDs6YkAnKp1FqzwhGvh3i7v0+/ytzqWdKYw6XzINEKAzke/iD00dNgFPWSZEqktHOK+C1gSzXhLkLbARIaZw=="
},
"@trysound/sax": { "@trysound/sax": {
"version": "0.2.0", "version": "0.2.0",
"resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz", "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
@ -3032,9 +3069,9 @@
} }
}, },
"axios": { "axios": {
"version": "1.7.9", "version": "1.8.4",
"resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz",
"integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==",
"requires": { "requires": {
"follow-redirects": "^1.15.6", "follow-redirects": "^1.15.6",
"form-data": "^4.0.0", "form-data": "^4.0.0",

View File

@ -10,8 +10,9 @@
}, },
"dependencies": { "dependencies": {
"@dreysolano/prerender-spa-plugin": "^1.0.3", "@dreysolano/prerender-spa-plugin": "^1.0.3",
"@stomp/stompjs": "^7.1.1",
"amfe-flexible": "^2.2.1", "amfe-flexible": "^2.2.1",
"axios": "^1.5.0", "axios": "^1.8.4",
"core-js": "^3.8.3", "core-js": "^3.8.3",
"echarts": "^5.5.1", "echarts": "^5.5.1",
"element-ui": "^2.15.14", "element-ui": "^2.15.14",

View File

@ -45,7 +45,7 @@
<link rel="icon" href="<%= BASE_URL %>favicon.ico"> <link rel="icon" href="<%= BASE_URL %>favicon.ico">
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4582735_irzdjxdsrq8.css"> <link rel="stylesheet" href="//at.alicdn.com/t/c/font_4582735_7i8wfzc0art.css">
<title>M2pool - Stable leading high-yield mining pool</title> <title>M2pool - Stable leading high-yield mining pool</title>
<meta name="keywords" content="M2Pool, cryptocurrency mining pool,Entropyx(enx),entropyx, bitcoin mining, DGB mining, mining pool service, 加密货币矿池, 比特币挖矿, DGB挖矿"> <meta name="keywords" content="M2Pool, cryptocurrency mining pool,Entropyx(enx),entropyx, bitcoin mining, DGB mining, mining pool service, 加密货币矿池, 比特币挖矿, DGB挖矿">
<meta name="description" content="M2Pool provides professional mining services, supporting multiple cryptocurrency mining, including nexa, grs, mona, dgb, rxd, enx"> <meta name="description" content="M2Pool provides professional mining services, supporting multiple cryptocurrency mining, including nexa, grs, mona, dgb, rxd, enx">

View File

@ -1,16 +1,19 @@
<template> <template>
<div id="app"> <div id="app">
<router-view class="page" /> <router-view class="page" />
<ChatWidget v-if="!$route.path.includes('/customerService') && !$isMobile" />
</div> </div>
</template> </template>
<script > <script >
import ChatWidget from '../src/components/ChatWidget.vue';
import { Debounce, throttle } from '@/utils/publicMethods'; import { Debounce, throttle } from '@/utils/publicMethods';
import Vue from 'vue' import Vue from 'vue'
export default { export default {
name: 'App', name: 'App',
components: {
ChatWidget
},
data() { data() {
return { return {

View File

@ -0,0 +1,78 @@
import request from '../utils/request'
//历史聊天记录查询 用户查询七天前的聊天信息
export function getHistory(data) {
return request({
url: `chat/message/find/history/message`,
method: 'post',
data
})
}
//历史聊天记录查询 查询七天内记录
export function getHistory7(data) {
return request({
url: `chat/message/find/recently/message`,
method: 'post',
data
})
}
//用户点击对话框默认已读
export function getReadMessage(data) {
return request({
url: `chat/message/read/message`,
method: 'post',
data
})
}
//聊天列表
export function getRoomList(data) {
return request({
url: `/chat/rooms/find/room/list`,
method: 'post',
data
})
}
//重要聊天标记
export function getUpdateRoom(data) {
return request({
url: `/chat/rooms/update/room`,
method: 'post',
data
})
}
//图片上传接口
// export function getFileUpdate(data) {
// return request({
// url: `file/update`,
// method: 'post',
// data
// })
// }
//图根据当前用户邮箱查询聊天室id
export function getUserid(data) {
return request({
url: `chat/rooms/find/room/by/userid`,
method: 'post',
data
})
}
//上传图片
export function getFileUpdate(data) {
return request({
url: `pool/ticket/uploadFile`,
method: 'post',
data
})
}

File diff suppressed because it is too large Load Diff

View File

@ -382,10 +382,15 @@ export default {
document.addEventListener("click", function () { document.addEventListener("click", function () {
const dropdown = document.querySelector(".dropdown"); const dropdown = document.querySelector(".dropdown");
const arrow = document.querySelector(".arrow"); const arrow = document.querySelector(".arrow");
if (dropdown.classList.contains("show")) { try {
if (dropdown.classList.contains("show")) {
dropdown.classList.remove("show"); dropdown.classList.remove("show");
arrow.classList.remove("up"); arrow.classList.remove("up");
} }
} catch (error) {
console.log(error)
}
}); });
@ -445,6 +450,8 @@ export default {
async fetchSignOut() { async fetchSignOut() {
const data = await getLogout(); const data = await getLogout();
if (data && data.code == 200) { if (data && data.code == 200) {
// Vuex logout action
await this.$store.dispatch('logout')
const lang = this.$i18n.locale; const lang = this.$i18n.locale;
this.$router.push(`/${lang}`); this.$router.push(`/${lang}`);
} }
@ -695,8 +702,11 @@ export default {
justify-content: center; justify-content: center;
} }
.miningAccountTitle:hover { .miningAccountTitle:hover {
color: #6e3edb !important; color: #6e3edb !important;
} }
.hidden { .hidden {
display: block; display: block;

View File

@ -0,0 +1,235 @@
export const ChatWidget_zh = {
chat:{
title: '在线客服',
welcome: '欢迎使用在线客服,请问有什么可以帮您?',
placeholder: '请输入您的消息...',
send: '发送',
close: '关闭',
inputPlaceholder:"请输入您的问题...",
onlyImages:"只能上传图片文件!",
imageTooLarge:"图片大小不能超过5MB!",
imageReceived:"已收到您的图片,我们会尽快处理您的问题。",
networkError:"网络连接已断开,请刷新页面重试",
openCustomerService:"打开客服聊天",
connectToCustomerService:"正在连接客服系统...",
connectionFailed:"连接失败,请刷新页面重试",
tryConnectingAgain:"重试连接",
loading:"加载中...",
loadMore:"加载更多历史消息",
welcomeToUse:"欢迎使用在线客服,请问有什么可以帮您?",
picture:"聊天图片",
read:"已读",
unread:"未读",
subscriptionFailed:"消息订阅失败,可能无法接收新消息",
break:"连接断开",
retry:"秒后重试",
disconnectWaiting:"断线重连中...",
sendFailed:"发送消息失败,请重试",
noHistory:"暂无历史消息",
historicalFailure:"加载历史消息失败,请重试",
loadingHistory:"正在加载更多历史消息...",
noMoreHistory:"没有更多历史消息了",
Loaded:"已加载历史消息",
newMessage:"新消息",
pictureMessage:"图片消息",
initializationFailed:"初始化失败,请刷新页面重试",
beSorry:"抱歉,我暂时无法回答这个问题。请排队等待人工客服或提交工单。",
today:"今天",
yesterday:"昨天",
canOnlyUploadImages:"只能上传图片文件!",
imageSizeExceeded:"图片大小不能超过5MB!",
uploading:"正在上传图片...",
pictureFailed:"发送图片失败,请重试",
readImage:"读取图片失败,请重试",
processingFailed:"图片处理失败,请重试",
Disconnected:"连接已断开",
reconnecting:"正在重连...",
contactList:"联系列表",
search:"搜索最近联系人",
tourist:"游客",
important:"重要",
markAsImportant:"标记为重要",
cancelImportant:"已取消重要标记",
markingFailed:"标记操作失败,请重试",
Marked:"已标记为重要",
select:"请选择联系人",
notSelected:"您尚未选择联系人",
None:"暂无消息记录",
sendPicture:"发送图片",
inputMessage:"请输入消息按Enter键发送按Ctrl+Enter键换行",
bottom:"回到底部",
Preview:"预览图片",
chatRoom:"聊天室",
CLOSED:"已关闭",
picture2:"图片",
Unnamed:"未命名聊天室",
noNewsAtTheMoment:"暂无未读消息",
contactFailed:"加载更多联系人失败",
listException:"获取聊天室列表异常",
my:"我",
unknownSender:"未知发送者",
recordFailed:"加载聊天记录失败",
messageException:"加载消息异常",
chooseFirst:"请先选择联系人",
chatDisconnected:"聊天连接已断开,请刷新页面重试",
pictureSuccessful:"图片已发送",
history:"历史记录",
loadFailed:"加载失败",
guestNotice:"您当前以游客身份聊天,聊天记录将不会保存。",
guestNotice2:"后即可保存聊天记录",
loginToSave:"登录",
server500:"服务器暂时不可用,请稍后重试",
CheckNetwork:"连接失败,请检查网络后重试",
attemptToReconnect:"连接已断开,正在尝试重连...",
retryFailed:"重试连接失败,请刷新页面",
maxConnectionsError:"连接数已达上限,请刷新页面重试",
ipLimitError:"本机连接数已达上限,请刷新页面重试",
serverLimitError:"服务器连接数已达上限,请稍后刷新重试",
identityError:"用户身份设置失败,请刷新页面重试",
emailError:"用户信息获取失败,请刷新页面重试",
refreshPage:"刷新页面",
reconnectSuccess:"重新连接成功",
sendMessageEmpty:"发送消息不能为空",
unableToSubscribe:"连接状态异常,刷新页面重试",
conflict:"连接异常,可能是多窗口冲突,请关闭其他窗口重试",
abnormal:"连接异常",
networkAnomaly:"网络连接异常",
customerServiceOffline:"客服离线,请登录账号发送留言消息",
contentMax:"超出发送内容大小限制,请删除部分内容(300字以内)",
failInSend:"发送失败,请重试",
connectionLimitError:"连接数已达上限,请关闭一些窗口后刷新重试",
serverBusy:"服务器繁忙,请稍后刷新重试",
connectionTimedOut:"连接超时,稍后重试...",
reconnectFailed:"重连失败,请稍后重试",
serviceConfigurationError:"服务配置异常,请稍后重试",
serviceAddressUnavailable:"服务地址不可用,请稍后重试",
connectionFailedService:"无法连接到服务器,请稍后重试",
connectionFailedCustomer:"连接客服系统失败,请检查网络或稍后重试",
},
}
export const ChatWidget_en = {
chat:{
title: 'Online Customer Service',
welcome: 'Welcome to the online customer service, what can I help you with?',
placeholder: 'Please enter your message...',
send: 'Send',
close: 'Close',
inputPlaceholder:"Please enter your question...",
onlyImages:"Only image files can be uploaded!",
imageTooLarge:"The image size cannot exceed 5MB!",
imageReceived:"We have received your image, and we will handle your question as soon as possible.",
networkError: "Network disconnected, please refresh page",
openCustomerService: "Open customer service chat",
connectToCustomerService: "Connecting to customer service...",
connectionFailed: "Connection failed, please refresh page",
tryConnectingAgain: "Retry connection",
loading: "Loading...",
loadMore: "Load more history",
welcomeToUse: "Welcome to online service, how can I help you?",
picture: "Chat image",
read: "Read",
unread: "Unread",
subscriptionFailed: "Message subscription failed",
break: "Connection lost",
retry: "Retry in seconds",
disconnectWaiting: "Reconnecting...",
sendFailed: "Send failed, please retry",
noHistory: "No history",
historicalFailure: "Failed to load history",
loadingHistory: "Loading more history...",
noMoreHistory: "No more history",
Loaded: "History loaded",
newMessage: "New message",
pictureMessage: "Image message",
initializationFailed: "Initialization failed, please refresh",
beSorry: "Sorry, I cannot answer this. Please wait for agent or submit ticket",
today: "Today",
yesterday: "Yesterday",
canOnlyUploadImages: "Only image files allowed",
imageSizeExceeded: "Image size exceeds 5MB",
uploading: "Uploading image...",
pictureFailed: "Failed to send image",
readImage: "Failed to read image",
processingFailed: "Image processing failed",
Disconnected: "Disconnected",
reconnecting: "Reconnecting...",
contactList: "Contact list",
search: "Search contacts",
tourist: "Guest",
important: "Important",
markAsImportant: "Mark as important",
cancelImportant: "Unmarked as important",
markingFailed: "Marking failed",
select: "Select contact",
notSelected: "No contact selected",
None: "No messages",
sendPicture: "Send image",
inputMessage: "Type message, Enter to send, Ctrl+Enter for new line",
bottom: "to bottom",
Preview: "Preview image",
chatRoom: "Chat room",
CLOSED: "Closed",
picture2: "Image",
Unnamed: "Unnamed chat",
noNewsAtTheMoment: "No unread messages",
contactFailed: "Failed to load contacts",
listException: "Failed to get chat list",
my: "Me",
unknownSender: "Unknown sender",
recordFailed: "Failed to load chat records",
messageException: "Failed to load messages",
chooseFirst: "Please select contact first",
chatDisconnected: "Chat disconnected, please refresh",
pictureSuccessful: "Image sent",
history:"History",
loadFailed:"Load Fail",
Marked:"Marked as important",
guestNotice:"You are currently chatting as a guest, and your chat history will not be saved.",
guestNotice2:" to save chat history",
loginToSave:"Login",
server500:"The server is temporarily unavailable, please try again later",
CheckNetwork:"Connection failed, please check the network and try again",
attemptToReconnect:"Connection lost, attempting to reconnect...",
retryFailed:"Retry connection failed, please refresh page",
maxConnectionsError:"Connection limit reached, please refresh page",
ipLimitError:"Connection limit reached, please refresh page",
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",
sendMessageEmpty:"Message cannot be empty",
unableToSubscribe:"Connection status abnormal, please refresh the page",
conflict:"Connection exception, possibly due to multiple window conflicts, please close other windows and try again",
abnormal:"Connection exception",
networkAnomaly:"Network connection exception",
customerServiceOffline:"Customer service offline, please login to send message",
contentMax:"Content exceeds the size limit, please delete some content(300 characters or less)",
failInSend:"Failed to send, please try again",
connectionLimitError:"Connection limit reached, please close some windows and refresh to try again",
serverBusy:"Server busy, please refresh later",
connectionTimedOut:"Connection timed out, please try again later",
reconnectFailed:"Reconnect failed, please try again later",
serviceConfigurationError:"Service configuration exception, please try again later",
serviceAddressUnavailable:"Service address unavailable, please try again later",
connectionFailedService:"Failed to connect to the server, please try again later",
connectionFailedCustomer:"Failed to connect to the customer service system, please check the network or try again later",
}
}

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,7 @@ import {workOrder_zh,workOrder_en} from'./submitWorkOrder'
import {alerts_zh,alerts_en} from'./alerts' import {alerts_zh,alerts_en} from'./alerts'
import {seo_zh,seo_en} from'./seo' import {seo_zh,seo_en} from'./seo'
import {chooseUs_zh,chooseUs_en} from'./dataDisplay' import {chooseUs_zh,chooseUs_en} from'./dataDisplay'
import {ChatWidget_zh,ChatWidget_en} from'./ChatWidget'
@ -30,7 +31,7 @@ export default {
...alerts_zh, ...alerts_zh,
...seo_zh, ...seo_zh,
...chooseUs_zh, ...chooseUs_zh,
...ChatWidget_zh,
}, },
@ -50,7 +51,7 @@ export default {
...alerts_en, ...alerts_en,
...seo_en, ...seo_en,
...chooseUs_en, ...chooseUs_en,
...ChatWidget_en,

View File

@ -11,6 +11,12 @@ import './assets/icons/iconfont/iconfont.css'
import {$addStorageEvent} from '../src/utils/publicMethods' import {$addStorageEvent} from '../src/utils/publicMethods'
import MetaInfo from 'vue-meta-info' import MetaInfo from 'vue-meta-info'
import loadingStateMixin from './utils/loadingStateMixin'; import loadingStateMixin from './utils/loadingStateMixin';
import networkRecoveryMixin from './mixins/networkRecoveryMixin';
import './utils/loadingRecovery';
import errorNotificationManager from '../src/utils/errorNotificationManager';
// 创建事件总线 用于组件通信
Vue.prototype.$bus = new Vue();
Vue.use(MetaInfo) Vue.use(MetaInfo)
Vue.prototype.$addStorageEvent = $addStorageEvent // 添加storage事件 Vue.prototype.$addStorageEvent = $addStorageEvent // 添加storage事件
@ -21,7 +27,9 @@ Vue.use(ElementUI, {
Vue.prototype.$axios = axios Vue.prototype.$axios = axios
console.log = ()=>{} //全局关闭打印 console.log = ()=>{} //全局关闭打印
// 全局注册混入
Vue.mixin(loadingStateMixin); Vue.mixin(loadingStateMixin);
Vue.mixin(networkRecoveryMixin);
Vue.prototype.$baseApi = process.env.VUE_APP_BASE_URL //图片base路径 Vue.prototype.$baseApi = process.env.VUE_APP_BASE_URL //图片base路径
const screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth; const screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
@ -45,6 +53,11 @@ router.beforeEach((to, from, next) => {
next(); next();
}); });
// 定期清理过期的错误记录
setInterval(() => {
errorNotificationManager.cleanup();
}, 60000); // 每分钟清理一次
window.vm = new Vue({ window.vm = new Vue({
router, router,
store, store,

View File

@ -0,0 +1,61 @@
//处理断网重连后数据刷新的混入
export default {
data() {
return {
// 注册需要在网络恢复时调用的方法名
recoveryMethods: [],
// 上次调用这些方法时的参数
methodParams: {}
};
},
methods: {
/**
* 注册需要在网络恢复时自动重新调用的方法
* @param {String} methodName - 方法名
* @param {*} params - 调用方法时需要的参数
*/
registerRecoveryMethod(methodName, params) {
if (typeof this[methodName] === 'function' && !this.recoveryMethods.includes(methodName)) {
this.recoveryMethods.push(methodName);
this.methodParams[methodName] = params;
console.log(`[NetworkRecovery] 注册方法: ${methodName}`);
}
},
/**
* 更新方法的参数
* @param {String} methodName - 方法名
* @param {*} params - 新的参数
*/
updateMethodParams(methodName, params) {
if (this.recoveryMethods.includes(methodName)) {
this.methodParams[methodName] = params;
}
},
/**
* 网络恢复后调用所有注册的方法
*/
handleNetworkRecovery() {
console.log('[NetworkRecovery] 网络已恢复,正在刷新数据...');
this.recoveryMethods.forEach(methodName => {
if (typeof this[methodName] === 'function') {
const params = this.methodParams[methodName];
console.log(`[NetworkRecovery] 重新调用方法: ${methodName}`);
this[methodName](params);
}
});
}
},
mounted() {
// 监听网络恢复事件
window.addEventListener('network-retry-complete', this.handleNetworkRecovery);
},
beforeDestroy() {
// 清理事件监听
window.removeEventListener('network-retry-complete', this.handleNetworkRecovery);
}
};

View File

@ -32,7 +32,7 @@ const childrenRoutes = [
component: () => import('../views/miningAccount/index.vue'), component: () => import('../views/miningAccount/index.vue'),
meta: {title: '挖矿账户页面', meta: {title: '挖矿账户页面',
description:i18n.t(`seo.miningAccount`), description:i18n.t(`seo.miningAccount`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool mining account, crypto mining stats, mining rewards, hashrate monitor, 矿池账户, 挖矿收益, 算力监控' // keywords: 'M2Pool mining account, crypto mining stats, mining rewards, hashrate monitor, 矿池账户, 挖矿收益, 算力监控'
keywords:{ keywords:{
en: 'M2Pool mining account, crypto mining stats, mining rewards, hashrate monitor, 矿池账户, 挖矿收益, 算力监控', en: 'M2Pool mining account, crypto mining stats, mining rewards, hashrate monitor, 矿池账户, 挖矿收益, 算力监控',
@ -63,7 +63,7 @@ const childrenRoutes = [
component: () => import('../views/reportBlock/index.vue'), component: () => import('../views/reportBlock/index.vue'),
meta: {title: '报块页面', meta: {title: '报块页面',
description:i18n.t(`seo.reportBlock`), description:i18n.t(`seo.reportBlock`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,报块页面,幸运值,区块高度,Block page,Lucky Value,block height,Mining Pool' // keywords: 'M2Pool 矿池,报块页面,幸运值,区块高度,Block page,Lucky Value,block height,Mining Pool'
keywords:{ keywords:{
en: 'Block page,Lucky Value,block height,Mining Pool', en: 'Block page,Lucky Value,block height,Mining Pool',
@ -118,6 +118,22 @@ const childrenRoutes = [
} }
}, },
{//在线客服
path: 'customerService',
name: 'CustomerService',
component: () => import('../views/customerService/index.vue'),
meta: {title: '在线客服',
description:i18n.t(`seo.apiFile`),
allAuthority:[`customer_service`],//客服权限
// 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', path: '/:lang/AccessMiningPool',
name: 'AccessMiningPool', name: 'AccessMiningPool',
@ -292,7 +308,7 @@ const childrenRoutes = [
component: () => import('../views/submitWorkOrder/index.vue'), component: () => import('../views/submitWorkOrder/index.vue'),
meta: {title: '提交工单页面', meta: {title: '提交工单页面',
description:i18n.t(`seo.submitWorkOrder`), description:i18n.t(`seo.submitWorkOrder`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,提交工单,技术支持,问题处理,Mining Pool,Work Order Submission, Technical Support, Troubleshooting' // keywords: 'M2Pool 矿池,提交工单,技术支持,问题处理,Mining Pool,Work Order Submission, Technical Support, Troubleshooting'
keywords:{ keywords:{
en: 'Mining Pool,Work Order Submission, Technical Support, Troubleshooting', en: 'Mining Pool,Work Order Submission, Technical Support, Troubleshooting',
@ -306,7 +322,7 @@ const childrenRoutes = [
component: () => import('../views/workOrderRecords/index.vue'), component: () => import('../views/workOrderRecords/index.vue'),
meta: {title: '工单记录页面(用户)', meta: {title: '工单记录页面(用户)',
description:i18n.t(`seo.workOrderRecords`), description:i18n.t(`seo.workOrderRecords`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,用户工单记录,处理状态,问题进度,User Work Order Records, Processing Status, Issue Progress' // keywords: 'M2Pool 矿池,用户工单记录,处理状态,问题进度,User Work Order Records, Processing Status, Issue Progress'
keywords:{ keywords:{
en: 'User Work Order Records, Processing Status, Issue Progress', en: 'User Work Order Records, Processing Status, Issue Progress',
@ -320,7 +336,7 @@ const childrenRoutes = [
component: () => import('../views/userWorkDetails/index.vue'), component: () => import('../views/userWorkDetails/index.vue'),
meta: {title: '工单详情页面(用户)', meta: {title: '工单详情页面(用户)',
description:i18n.t(`seo.userWorkDetails`), description:i18n.t(`seo.userWorkDetails`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,用户工单详情,问题描述,补充提交,User Work Order Details, Problem Description, Additional Submissions' // keywords: 'M2Pool 矿池,用户工单详情,问题描述,补充提交,User Work Order Details, Problem Description, Additional Submissions'
keywords:{ keywords:{
en: 'User Work Order Details, Problem Description, Additional Submissions', en: 'User Work Order Details, Problem Description, Additional Submissions',
@ -392,7 +408,7 @@ const childrenRoutes = [
component: () => import('../views/personalCenter/index.vue'), component: () => import('../views/personalCenter/index.vue'),
meta: {title: '个人中心页面', meta: {title: '个人中心页面',
description:i18n.t(`seo.personalCenter`), description:i18n.t(`seo.personalCenter`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,个人中心,挖矿账户,只读页面设置安全设置API密钥生成,Personal Center,Mining Account,Read-Only Page Setup,Security Settings,API Key Generation' // keywords: 'M2Pool 矿池,个人中心,挖矿账户,只读页面设置安全设置API密钥生成,Personal Center,Mining Account,Read-Only Page Setup,Security Settings,API Key Generation'
keywords:{ keywords:{
en: 'Personal Center,Mining Account,Read-Only Page Setup,Security Settings,API Key Generation', en: 'Personal Center,Mining Account,Read-Only Page Setup,Security Settings,API Key Generation',
@ -406,7 +422,7 @@ const childrenRoutes = [
component: () => import('../views/personalCenter/personalMining/index.vue'), component: () => import('../views/personalCenter/personalMining/index.vue'),
meta: {title: '挖矿账户设置页面', meta: {title: '挖矿账户设置页面',
description:i18n.t(`seo.personalMining`), description:i18n.t(`seo.personalMining`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,个人中心,挖矿账户设置,币种账户,Personal Center,Mining Account Settings,Coin Accounts' // keywords: 'M2Pool 矿池,个人中心,挖矿账户设置,币种账户,Personal Center,Mining Account Settings,Coin Accounts'
keywords:{ keywords:{
en: 'Personal Center,Mining Account Settings,Coin Accounts', en: 'Personal Center,Mining Account Settings,Coin Accounts',
@ -420,7 +436,7 @@ const childrenRoutes = [
component: () => import('../views/personalCenter/readOnly/index.vue'), component: () => import('../views/personalCenter/readOnly/index.vue'),
meta: {title: '只读页面设置', meta: {title: '只读页面设置',
description:i18n.t(`seo.readOnly`), description:i18n.t(`seo.readOnly`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,个人中心,只读页面设置,矿池分享,Personal Center,Read-Only Page Setting,Mining Pool Sharing' // keywords: 'M2Pool 矿池,个人中心,只读页面设置,矿池分享,Personal Center,Read-Only Page Setting,Mining Pool Sharing'
keywords:{ keywords:{
en: 'Personal Center,Read-Only Page Setting,Mining Pool Sharing', en: 'Personal Center,Read-Only Page Setting,Mining Pool Sharing',
@ -434,7 +450,7 @@ const childrenRoutes = [
component: () => import('../views/personalCenter/securitySetting/index.vue'), component: () => import('../views/personalCenter/securitySetting/index.vue'),
meta: {title: '安全设置页面', meta: {title: '安全设置页面',
description:i18n.t(`seo.securitySetting`), description:i18n.t(`seo.securitySetting`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,安全设置,密码修改,Security settings, password change' // keywords: 'M2Pool 矿池,安全设置,密码修改,Security settings, password change'
keywords:{ keywords:{
en: 'Security settings, password change', en: 'Security settings, password change',
@ -448,7 +464,7 @@ const childrenRoutes = [
component: () => import('../views/personalCenter/personal/index.vue'), component: () => import('../views/personalCenter/personal/index.vue'),
meta: {title: '个人信息页面', meta: {title: '个人信息页面',
description:i18n.t(`seo.personal`), description:i18n.t(`seo.personal`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,个人信息,登录历史,Personal Information, Login History' // keywords: 'M2Pool 矿池,个人信息,登录历史,Personal Information, Login History'
keywords:{ keywords:{
en: 'Personal Information, Login History', en: 'Personal Information, Login History',
@ -462,7 +478,7 @@ const childrenRoutes = [
component: () => import('../views/personalCenter/miningReport/index.vue'), component: () => import('../views/personalCenter/miningReport/index.vue'),
meta: {title: '挖矿报告页面', meta: {title: '挖矿报告页面',
description:i18n.t(`seo.miningReport`), description:i18n.t(`seo.miningReport`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,个人中心,挖矿报告,订阅服务,Mining Report, Subscription Service' // keywords: 'M2Pool 矿池,个人中心,挖矿报告,订阅服务,Mining Report, Subscription Service'
keywords:{ keywords:{
en: 'Mining Report, Subscription Service', en: 'Mining Report, Subscription Service',
@ -477,7 +493,7 @@ const childrenRoutes = [
component: () => import('../views/personalCenter/personalAPI/index.vue'), component: () => import('../views/personalCenter/personalAPI/index.vue'),
meta: {title: 'API页面', meta: {title: 'API页面',
description:i18n.t(`seo.personalAPI`), description:i18n.t(`seo.personalAPI`),
allAuthority:[`admin`,`registered`], allAuthority:[`admin`,`registered`,`customer_service`],
// keywords: 'M2Pool 矿池,个人中心,API 页面,API密钥生成,API Page,API Key Generation' // keywords: 'M2Pool 矿池,个人中心,API 页面,API密钥生成,API Page,API Key Generation'
keywords:{ keywords:{
en: 'API Page,API Key Generation', en: 'API Page,API Key Generation',
@ -641,6 +657,7 @@ router.beforeEach((to, from, next) => {
let data = localStorage.getItem("jurisdiction"); let data = localStorage.getItem("jurisdiction");
let jurisdiction =JSON.parse(data); let jurisdiction =JSON.parse(data);
console.log(jurisdiction,"权限");
localStorage.setItem('superReportError',"") localStorage.setItem('superReportError',"")
let element = document.getElementsByClassName('el-main')[0]; let element = document.getElementsByClassName('el-main')[0];
@ -673,7 +690,10 @@ router.beforeEach((to, from, next) => {
message:i18n.t(`mining.jurisdiction`), message:i18n.t(`mining.jurisdiction`),
type: 'error' type: 'error'
}); });
next({ path: `/${lang}` }) // 添加这行,重定向到首页
} }
}else{ }else{

View File

@ -5,12 +5,48 @@ Vue.use(Vuex)
export default new Vuex.Store({ export default new Vuex.Store({
state: { state: {
isLoggedIn: false,
userInfo: null
}, },
getters: { getters: {
isLoggedIn: state => state.isLoggedIn,
userInfo: state => state.userInfo
}, },
mutations: { mutations: {
SET_LOGIN_STATE(state, isLoggedIn) {
state.isLoggedIn = isLoggedIn
},
SET_USER_INFO(state, userInfo) {
state.userInfo = userInfo
},
CLEAR_USER_DATA(state) {
state.isLoggedIn = false
state.userInfo = null
}
}, },
actions: { actions: {
// 退出登录
async logout({ commit }) {
try {
// 清除本地存储
localStorage.removeItem('token')
localStorage.removeItem('userEmail')
localStorage.removeItem('jurisdiction')
// 清除 Vuex 状态
commit('CLEAR_USER_DATA')
// 触发全局事件,通知其他组件用户已退出
if (Vue.prototype.$bus) {
Vue.prototype.$bus.$emit('user-logged-out')
}
return true
} catch (error) {
console.error('退出登录失败:', error)
return false
}
}
}, },
modules: { modules: {
} }

View File

@ -0,0 +1,74 @@
/**
* 错误提示管理器
* 用于控制错误提示的频率避免短时间内重复显示相同类型的错误
*/
class ErrorNotificationManager {
constructor() {
// 记录最近显示的错误信息
this.recentErrors = new Map();
// 默认节流时间 (30秒)
this.throttleTime = 3000;
// 错误类型映射
this.errorTypes = {
'Network Error': 'network',
'timeout': 'timeout',
'Request failed with status code': 'statusCode',
// 添加网络状态类型
'networkReconnected': 'networkStatus',
'NetworkError': 'network'
};
}
/**
* 获取错误类型
* @param {String} message 错误信息
* @returns {String} 错误类型
*/
getErrorType(message) {
for (const [key, type] of Object.entries(this.errorTypes)) {
if (message.includes(key)) {
return type;
}
}
return 'unknown';
}
/**
* 检查是否可以显示错误
* @param {String} message 错误信息
* @returns {Boolean} 是否可以显示
*/
canShowError(message) {
const errorType = this.getErrorType(message);
const now = Date.now();
// 检查同类型的错误是否最近已经显示过
if (this.recentErrors.has(errorType)) {
const lastTime = this.recentErrors.get(errorType);
if (now - lastTime < this.throttleTime) {
console.log(`[错误提示] 已抑制重复错误: ${errorType}`);
return false;
}
}
// 更新最后显示时间
this.recentErrors.set(errorType, now);
return true;
}
/**
* 清理过期的错误记录
*/
cleanup() {
const now = Date.now();
this.recentErrors.forEach((time, type) => {
if (now - time > this.throttleTime) {
this.recentErrors.delete(type);
}
});
}
}
// 创建单例实例
const errorNotificationManager = new ErrorNotificationManager();
export default errorNotificationManager;

View File

@ -0,0 +1,41 @@
import Vue from 'vue';
/**
* 自动恢复加载状态的指令
* 用法: v-loading-recovery="{ loading: 'loadingProp', recovery: ['method1', 'method2'] }"
*/
export const loadingRecoveryDirective = {
// 当绑定元素插入到 DOM 中时
inserted(el, binding, vnode) {
const component = vnode.context;
const { loading, recovery } = binding.value || {};
if (!loading || !recovery || !Array.isArray(recovery)) return;
// 为组件添加加载状态恢复的逻辑
const handleNetworkRecovery = () => {
// 当网络恢复时自动设置loading为false
if (component[loading]) {
component[loading] = false;
}
};
// 保存到元素上,以便后续清理
el._loadingRecovery = handleNetworkRecovery;
window.addEventListener('network-retry-complete', handleNetworkRecovery);
console.log(`[LoadingRecovery] 添加加载状态恢复: ${loading}`);
},
// 当指令与元素解绑时
unbind(el) {
// 清理事件监听
if (el._loadingRecovery) {
window.removeEventListener('network-retry-complete', el._loadingRecovery);
delete el._loadingRecovery;
}
}
};
// 注册全局指令
Vue.directive('loading-recovery', loadingRecoveryDirective);

View File

@ -2,6 +2,15 @@ import axios from 'axios'
import errorCode from './errorCode' import errorCode from './errorCode'
import { Notification, MessageBox, Message } from 'element-ui' import { Notification, MessageBox, Message } from 'element-ui'
import loadingManager from './loadingManager'; 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实例 // 创建axios实例
const service = axios.create({ const service = axios.create({
// axios中请求配置有baseURL选项表示请求URL公共部分 // axios中请求配置有baseURL选项表示请求URL公共部分
@ -19,10 +28,51 @@ let lastNetworkErrorTime = 0; // 上次网络错误提示时间
let pendingRequests = new Map(); let pendingRequests = new Map();
// 网络状态监听器
// 网络状态最后提示时间
let lastNetworkStatusTime = {
online: 0,
offline: 0
};
// 创建一个全局标志,确保每次网络恢复只显示一次提示
let networkRecoveryInProgress = false;
// 网络状态监听器 // 网络状态监听器
window.addEventListener('online', () => { window.addEventListener('online', () => {
// 网络恢复时,重试所有待处理的请求
const now = Date.now(); const now = Date.now();
// 避免短时间内多次触发
if (networkRecoveryInProgress) {
console.log('[网络] 网络恢复处理已在进行中,忽略重复事件');
return;
}
networkRecoveryInProgress = true;
// 严格检查是否应该显示提示
if (now - lastNetworkStatusTime.online > 30000) { // 30秒内不重复提示
lastNetworkStatusTime.online = now;
try {
if (window.vm && window.vm.$message) {
// 确保消息只显示一次
window.vm.$message({
message: window.vm.$i18n.t('home.networkReconnected') || '网络已重新连接,正在恢复数据...',
type: 'success',
duration: 5000,
showClose: true,
});
console.log('[网络] 显示网络恢复提示, 时间:', new Date().toLocaleTimeString());
}
} catch (e) {
console.error('[网络] 显示网络恢复提示失败:', e);
}
} else {
console.log('[网络] 抑制重复的网络恢复提示, 间隔过短:', now - lastNetworkStatusTime.online + 'ms');
}
// 网络恢复时,重试所有待处理的请求
const pendingPromises = []; const pendingPromises = [];
pendingRequests.forEach(async (request, key) => { pendingRequests.forEach(async (request, key) => {
@ -71,34 +121,53 @@ window.addEventListener('online', () => {
// 等待所有请求完成 // 等待所有请求完成
Promise.allSettled(pendingPromises).then(() => { Promise.allSettled(pendingPromises).then(() => {
// 重置所有 loading 状态 // 重置所有 loading 状态
loadingManager.resetAllLoadingStates(); if (loadingManager) {
loadingManager.resetAllLoadingStates();
}
// 手动重置一些关键的 loading 状态
if (window.vm) {
// 常见的加载状态
const commonLoadingProps = [
'minerChartLoading', 'reportBlockLoading', 'apiPageLoading',
'MiningLoading', 'miniLoading'
];
commonLoadingProps.forEach(prop => {
if (typeof window.vm[prop] !== 'undefined') {
window.vm[prop] = false;
}
});
}
// 触发网络重试完成事件 // 触发网络重试完成事件
window.dispatchEvent(new CustomEvent('network-retry-complete')); window.dispatchEvent(new CustomEvent('network-retry-complete'));
// 重置网络恢复标志
setTimeout(() => {
networkRecoveryInProgress = false;
}, 5000); // 5秒后允许再次处理网络恢复
}); });
// 显示网络恢复提示
if (window.vm && window.vm.$message) {
window.vm.$message({
message: window.vm.$i18n.t('home.networkReconnected') || '网络已重新连接,正在恢复数据...',
type: 'success',
duration: 3000
});
}
}); });
// 使用错误提示管理器控制网络断开提示
window.addEventListener('offline', () => { window.addEventListener('offline', () => {
if (window.vm && window.vm.$message) { if (window.vm && window.vm.$message && errorNotificationManager.canShowError('networkOffline')) {
window.vm.$message({ window.vm.$message({
message: window.vm.$i18n.t('home.networkOffline') || '网络连接已断开,系统将在恢复连接后自动重试', message: window.vm.$i18n.t('home.networkOffline') || '网络连接已断开,系统将在恢复连接后自动重试',
type: 'warning', type: 'error',
duration: 3000 duration: 5000,
showClose: true,
}); });
} }
}); });
service.defaults.retry = 2;// 重试次数 service.defaults.retry = 2;// 重试次数
service.defaults.retryDelay = 2000; service.defaults.retryDelay = 2000;
service.defaults.shouldRetry = (error) => true service.defaults.shouldRetry = (error) => {
// 只有网络错误或超时错误才进行重试
return error.message === "Network Error" || error.message.includes("timeout");
};
localStorage.setItem('superReportError', "") localStorage.setItem('superReportError', "")
let superReportError = localStorage.getItem('superReportError') let superReportError = localStorage.getItem('superReportError')
@ -150,6 +219,22 @@ service.interceptors.request.use(config => {
config.params = {}; config.params = {};
config.url = url; 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 return config
}, error => { }, error => {
Promise.reject(error) Promise.reject(error)
@ -157,6 +242,10 @@ service.interceptors.request.use(config => {
// 响应拦截器 // 响应拦截器
service.interceptors.response.use(res => { service.interceptors.response.use(res => {
// 请求完成后移除
const requestKey = getRequestKey(res.config);
pendingRequestMap.delete(requestKey);
// 未设置状态码则默认成功状态 // 未设置状态码则默认成功状态
const code = res.data.code || 200; const code = res.data.code || 200;
// 获取错误信息 // 获取错误信息
@ -219,12 +308,18 @@ service.interceptors.response.use(res => {
}, },
error => { 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; let { message } = error;
if (message == "Network Error" || message.includes("timeout")) { if (message == "Network Error" || message.includes("timeout")) {
if (!navigator.onLine) { if (!navigator.onLine) {
// 断网状态,添加到重试队列 // 断网状态,添加到重试队列
@ -234,7 +329,7 @@ service.interceptors.response.use(res => {
params: error.config.params, params: error.config.params,
data: error.config.data data: error.config.data
}); });
// 根据URL确定请求类型并记录回调 // 根据URL确定请求类型并记录回调
let callback = null; let callback = null;
if (error.config.url.includes('getPoolPower')) { if (error.config.url.includes('getPoolPower')) {
@ -251,7 +346,7 @@ service.interceptors.response.use(res => {
} }
}; };
} }
if (!pendingRequests.has(requestKey)) { if (!pendingRequests.has(requestKey)) {
pendingRequests.set(requestKey, { pendingRequests.set(requestKey, {
config: error.config, config: error.config,
@ -259,33 +354,40 @@ service.interceptors.response.use(res => {
retryCount: 0, retryCount: 0,
callback: callback callback: callback
}); });
console.log('请求已加入断网重连队列:', error.config.url); console.log('请求已加入断网重连队列:', error.config.url);
} }
} else if ((error.config.retry > 0 && error.config)) { } else {
// 保留现有的重试逻辑 // 网络已连接,但请求失败,尝试重试
error.config.retry--; // 确保 config 中有 __retryCount 字段
return new Promise(resolve => { error.config.__retryCount = error.config.__retryCount || 0;
setTimeout(() => {
resolve(service(error.config)); // 判断是否可以重试
}, 2000); if (error.config.__retryCount < service.defaults.retry && service.defaults.shouldRetry(error)) {
}); // 增加重试计数
error.config.__retryCount += 1;
console.log(`[请求重试] ${error.config.url} - 第 ${error.config.__retryCount} 次重试`);
// 创建新的Promise等待一段时间后重试
return new Promise(resolve => {
setTimeout(() => {
resolve(service(error.config));
}, service.defaults.retryDelay);
});
}
// 达到最大重试次数,不再重试
console.log(`[请求失败] ${error.config.url} - 已达到最大重试次数`);
} }
} }
if (!superReportError) { if (!superReportError) {
superReportError = "error" superReportError = "error"
localStorage.setItem('superReportError', superReportError) localStorage.setItem('superReportError', superReportError)
let { message } = error; //使用错误提示管理器errorNotificationManager
if (message == "Network Error") { if (errorNotificationManager.canShowError(message)) {
// message = "后端接口网络连接异常,请刷新重试"; if (message == "Network Error") {
const now = Date.now();
if (now - lastNetworkErrorTime > NETWORK_ERROR_THROTTLE_TIME) {
lastNetworkErrorTime = now; // 更新最后提示时间
Message({ Message({
message: window.vm.$i18n.t(`home.NetworkError`), message: window.vm.$i18n.t(`home.NetworkError`),
type: 'error', type: 'error',
@ -293,42 +395,38 @@ service.interceptors.response.use(res => {
showClose: true showClose: true
}); });
} }
else if (message.includes("timeout")) {
} Message({
else if (message.includes("timeout")) { message: window.vm.$i18n.t(`home.requestTimeout`),
// message = "系统接口请求超时,请刷新重试"; type: 'error',
Message({ duration: 5 * 1000,
message: window.vm.$i18n.t(`home.requestTimeout`), showClose: true
type: 'error', });
duration: 5 * 1000, }
showClose: true else if (message.includes("Request failed with status code")) {
}) Message({
message: "系统接口" + message.substr(message.length - 3) + "异常",
} type: 'error',
else if (message.includes("Request failed with status code")) { duration: 5 * 1000,
// message = "系统接口" + message.substr(message.length - 3) + "异常"; showClose: true
Message({ });
message: "系统接口" + message.substr(message.length - 3) + "异常", } else {
type: 'error', Message({
duration: 5 * 1000, message: message,
showClose: true type: 'error',
}) duration: 5 * 1000,
showClose: true
});
}
} else { } else {
// 避免完全不提示,可以在控制台记录被抑制的错误
Message({ console.log('[错误提示] 已抑制重复错误:', message);
message: message,
type: 'error',
duration: 5 * 1000,
showClose: true
})
} }
} }
return Promise.reject(error) return Promise.reject(error)
} }

View File

@ -0,0 +1,34 @@
//自动记录请求参数,并支持断网重连,创建一个函数装饰器
/**
* 包装请求方法自动注册断网重连恢复
* @param {Object} component - 组件实例
* @param {String} methodName - 方法名
* @param {Function} originalMethod - 原始方法
* @returns {Function} 包装后的方法
*/
export function wrapRequestMethod(component, methodName, originalMethod) {
return async function(...args) {
// 注册为恢复方法(仅当组件支持断网重连恢复)
if (component.registerRecoveryMethod) {
component.registerRecoveryMethod(methodName, args[0]);
}
// 调用原始方法
return await originalMethod.apply(component, args);
};
}
/**
* 自动包装组件中的所有请求方法
* @param {Object} component - 组件实例
* @param {Array<String>} methodNames - 方法名列表
*/
export function wrapComponentMethods(component, methodNames) {
methodNames.forEach(methodName => {
if (typeof component[methodName] === 'function') {
const originalMethod = component[methodName];
component[methodName] = wrapRequestMethod(component, methodName, originalMethod);
console.log(`[RequestWrapper] 包装方法: ${methodName}`);
}
});
}

View File

@ -171,10 +171,13 @@ export default {
// this.stateList = JSON.parse(localStorage.getItem('stateList') ) // this.stateList = JSON.parse(localStorage.getItem('stateList') )
// this.typeList = JSON.parse(localStorage.getItem('typeList') ) // this.typeList = JSON.parse(localStorage.getItem('typeList') )
this.registerRecoveryMethod('fetchTicketDetails', { id: this.workOrderId });
}, },
methods: { methods: {
async fetchBKendTicket(params){ async fetchBKendTicket(params){
this.totalDetailsLoading =true // this.totalDetailsLoading =true
this.setLoading('totalDetailsLoading', true);
const data = await getBKendTicket(params) const data = await getBKendTicket(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.$message({ this.$message({
@ -185,11 +188,12 @@ export default {
} }
this.totalDetailsLoading =false this.setLoading('totalDetailsLoading', false);
}, },
async fetchReply(params){ async fetchReply(params){
this.totalDetailsLoading = true // this.totalDetailsLoading = true
this.setLoading('totalDetailsLoading', true);
const data = await getReply(params) const data = await getReply(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.$message({ this.$message({
@ -205,7 +209,7 @@ export default {
} }
this.totalDetailsLoading = false this.setLoading('totalDetailsLoading', false);
}, },
handelType2(label){ handelType2(label){
if (label) { if (label) {
@ -231,14 +235,15 @@ export default {
}, },
//请求工单详情 //请求工单详情
async fetchTicketDetails(param) { async fetchTicketDetails(param) {
this.totalDetailsLoading = true // this.totalDetailsLoading = true
this.setLoading('totalDetailsLoading', true);
const { data } = await getDetails(param) const { data } = await getDetails(param)
this.recordList = data.list this.recordList = data.list
this.ticketDetails = data this.ticketDetails = data
this.totalDetailsLoading = false this.setLoading('totalDetailsLoading', false);
}, },

View File

@ -18,7 +18,8 @@
<div class="textBox"> <div class="textBox">
<p>{{ $t(`ServiceTerms.clauseService1`) }}</p> <p>{{ $t(`ServiceTerms.clauseService1`) }}</p>
<p>{{ $t(`ServiceTerms.clauseService2`) }}</p> <p>{{ $t(`ServiceTerms.clauseService2`) }}</p>
<p><span style="font-weight: 600;">{{ $t(`ServiceTerms.clauseService3`) }} </span>{{ $t(`ServiceTerms.clauseService4`) }}</p>
<p style="text-align: justify;"><span style="font-weight: 600 ;text-align: justify;">{{ $t(`ServiceTerms.clauseService3`) }} </span>{{ $t(`ServiceTerms.clauseService4`) }}</p>
</div> </div>
</section> </section>
@ -88,13 +89,13 @@
<p>{{ $t(`ServiceTerms.clauseTermination2`) }}</p> <p>{{ $t(`ServiceTerms.clauseTermination2`) }}</p>
</div> </div>
</section> </section>
<section class="clauseBox"> <!-- <section class="clauseBox">
<h5>{{ $t(`ServiceTerms.title11`) }}</h5> <h5>{{ $t(`ServiceTerms.title11`) }}</h5>
<div class="textBox"> <div class="textBox">
<p>{{ $t(`ServiceTerms.clauseLaw1`) }}</p> <p>{{ $t(`ServiceTerms.clauseLaw1`) }}</p>
<p>{{ $t(`ServiceTerms.clauseLaw2`) }}</p> <p>{{ $t(`ServiceTerms.clauseLaw2`) }}</p>
</div> </div>
</section> </section> -->
</section> </section>
@ -117,7 +118,8 @@
<div class="textBox"> <div class="textBox">
<p>{{ $t(`ServiceTerms.clauseService1`) }}</p> <p>{{ $t(`ServiceTerms.clauseService1`) }}</p>
<p>{{ $t(`ServiceTerms.clauseService2`) }}</p> <p>{{ $t(`ServiceTerms.clauseService2`) }}</p>
<p><span style="font-weight: 600;">{{ $t(`ServiceTerms.clauseService3`) }} </span>{{ $t(`ServiceTerms.clauseService4`) }}</p>
<p style="text-align: justify;"><span style="font-weight: 600;text-align: justify;">{{ $t(`ServiceTerms.clauseService3`) }} </span>{{ $t(`ServiceTerms.clauseService4`) }}</p>
</div> </div>
</section> </section>
@ -187,13 +189,13 @@
<p>{{ $t(`ServiceTerms.clauseTermination2`) }}</p> <p>{{ $t(`ServiceTerms.clauseTermination2`) }}</p>
</div> </div>
</section> </section>
<section class="clauseBox"> <!-- <section class="clauseBox">
<h3>{{ $t(`ServiceTerms.title11`) }}</h3> <h3>{{ $t(`ServiceTerms.title11`) }}</h3>
<div class="textBox"> <div class="textBox">
<p>{{ $t(`ServiceTerms.clauseLaw1`) }}</p> <p>{{ $t(`ServiceTerms.clauseLaw1`) }}</p>
<p>{{ $t(`ServiceTerms.clauseLaw2`) }}</p> <p>{{ $t(`ServiceTerms.clauseLaw2`) }}</p>
</div> </div>
</section> </section> -->
</section> </section>

View File

@ -92,7 +92,7 @@ export default {
this.params.maId = this.receiveData.id this.params.maId = this.receiveData.id
} }
this.fetchList(this.listParams) this.fetchList(this.listParams)
this.registerRecoveryMethod('fetchList', this.listParams);
}, },
methods:{ methods:{
@ -100,7 +100,8 @@ export default {
return getImageUrl(path); return getImageUrl(path);
}, },
async fetchAddNoticeEmail(params){ async fetchAddNoticeEmail(params){
this.addMinerLoading = true // this.addMinerLoading = true
this.setLoading('addMinerLoading', true);
const data = await getAddNoticeEmail(params) const data = await getAddNoticeEmail(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.$message({ this.$message({
@ -121,17 +122,18 @@ export default {
} }
} }
this.addMinerLoading = false this.setLoading('addMinerLoading', false);
}, },
async fetchList(params){ async fetchList(params){
this.alertsLoading=true // this.alertsLoading=true
this.setLoading('alertsLoading', true);
const data = await getList(params) const data = await getList(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.tableData = data.rows this.tableData = data.rows
} }
this.alertsLoading=false this.setLoading('alertsLoading', false);
}, },
async fetchCode(params){ async fetchCode(params){
@ -147,7 +149,8 @@ export default {
}, },
async fetchUpdateInfo(params){ async fetchUpdateInfo(params){
this.addMinerLoading = true // this.addMinerLoading = true
this.setLoading('addMinerLoading', true);
const data = await getUpdateInfo(params) const data = await getUpdateInfo(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.$message({ this.$message({
@ -159,11 +162,12 @@ export default {
this.fetchList(this.listParams) this.fetchList(this.listParams)
} }
this.addMinerLoading = false this.setLoading('addMinerLoading', false);
}, },
async fetchDeleteEmail(params){ async fetchDeleteEmail(params){
this.deleteLoading = true // this.deleteLoading = true
this.setLoading('deleteLoading', true);
const data = await deleteEmail(params) const data = await deleteEmail(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.$message({ this.$message({
@ -175,8 +179,8 @@ export default {
this.fetchList(this.listParams) this.fetchList(this.listParams)
} }
this.deleteLoading = false this.setLoading('deleteLoading', false);
}, },
add(){ add(){

View File

@ -442,7 +442,7 @@
<!-- 挖矿账号下矿工总览 --> <!-- 挖矿账号下矿工总览 -->
<div class="Pool"> <div class="Pool">
<p class="hash">{{ $t(`apiFile.overviewOfMiners`) }}</p> <p class="hash">{{ $t(`apiFile.overviewOfMiners`) }}</p>
<p class="Interface">Post /oapi/v1/account/miners_list</p> <p class="Interface">Post /oapi/v1/account/watch</p>
<p class="hash">{{ $t(`apiFile.jurisdiction`) }} account</p> <p class="hash">{{ $t(`apiFile.jurisdiction`) }} account</p>
<p class="hash">{{ $t(`apiFile.parameter`) }}</p> <p class="hash">{{ $t(`apiFile.parameter`) }}</p>
<table border="1"> <table border="1">
@ -797,7 +797,7 @@
<!-- 指定矿机历史24h平均算力 --> <!-- 指定矿机历史24h平均算力 -->
<div class="Pool"> <div class="Pool">
<p class="hash">{{ $t(`apiFile.miningMachineHistory24h`) }}</p> <p class="hash">{{ $t(`apiFile.miningMachineHistory24h`) }}</p>
<p class="Interface">Post /oapi/v1/miner/hashrate_history</p> <p class="Interface">Post /oapi/v1/miner/hashrate_history</p>
<p class="hash">{{ $t(`apiFile.jurisdiction`) }} miner</p> <p class="hash">{{ $t(`apiFile.jurisdiction`) }} miner</p>
<p class="hash">{{ $t(`apiFile.parameter`) }}</p> <p class="hash">{{ $t(`apiFile.parameter`) }}</p>
<table border="1"> <table border="1">
@ -1371,7 +1371,7 @@
<!-- 挖矿账号下矿工总览 --> <!-- 挖矿账号下矿工总览 -->
<div class="Pool"> <div class="Pool">
<p class="hash">{{ $t(`apiFile.overviewOfMiners`) }}</p> <p class="hash">{{ $t(`apiFile.overviewOfMiners`) }}</p>
<p class="Interface">Post /oapi/v1/account/miners_list</p> <p class="Interface">Post /oapi/v1/account/watch </p>
<p class="hash">{{ $t(`apiFile.jurisdiction`) }} account</p> <p class="hash">{{ $t(`apiFile.jurisdiction`) }} account</p>
<p class="hash">{{ $t(`apiFile.parameter`) }}</p> <p class="hash">{{ $t(`apiFile.parameter`) }}</p>
<table border="1"> <table border="1">
@ -1726,7 +1726,7 @@
<!-- 指定矿机历史24h平均算力 --> <!-- 指定矿机历史24h平均算力 -->
<div class="Pool"> <div class="Pool">
<p class="hash">{{ $t(`apiFile.miningMachineHistory24h`) }}</p> <p class="hash">{{ $t(`apiFile.miningMachineHistory24h`) }}</p>
<p class="Interface">Get /oapi/v1/miner/hashrate_history</p> <p class="Interface">Post /oapi/v1/miner/hashrate_history</p>
<p class="hash">{{ $t(`apiFile.jurisdiction`) }} miner</p> <p class="hash">{{ $t(`apiFile.jurisdiction`) }} miner</p>
<p class="hash">{{ $t(`apiFile.parameter`) }}</p> <p class="hash">{{ $t(`apiFile.parameter`) }}</p>
<table border="1"> <table border="1">

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@ import * as echarts from "echarts";
import { Debounce, throttle } from "../../utils/publicMethods"; import { Debounce, throttle } from "../../utils/publicMethods";
export default { export default {
data() { data() {
return { return {
activeName: "second", activeName: "second",
@ -76,22 +76,22 @@ export default {
nameTextStyle: { nameTextStyle: {
padding: [0, 0, 0, -40], padding: [0, 0, 0, -40],
} },
// min: `dataMin`, // min: `dataMin`,
// max: `dataMax`, // max: `dataMax`,
// axisLabel: { axisLabel: {
// formatter: function (value) { formatter: function (value) {
// let data // let data
// if (value > 10000000) { // if (value > 10000000) {
// data = `${(value / 10000000)} KW` // data = `${(value / 10000000)} KW`
// } else if (value > 1000000) { // } else if (value > 1000000) {
// data = `${(value / 1000000)} M` // data = `${(value / 1000000)} M`
// } else if (value / 10000) { // } else if (value / 10000) {
// data = `${(value / 10000)} W` // data = `${(value / 10000)} W`
// } // }
// return data return value
// } }
// } }
}, },
{ {
@ -276,20 +276,20 @@ export default {
name: "GH/s", name: "GH/s",
nameTextStyle: { nameTextStyle: {
padding: [0, 0, 0, -40], 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", position: "right",
@ -506,7 +506,8 @@ export default {
show: true, show: true,
amount: 1, amount: 1,
} },
// { //告知已删除此币种 Radiant // { //告知已删除此币种 Radiant
// value: "dgb2_odo", // value: "dgb2_odo",
// label: "dgb-odocrypt-pool2", // label: "dgb-odocrypt-pool2",
@ -759,7 +760,7 @@ export default {
], ],
FeeShow: true, FeeShow: true,
lang: 'en', lang: 'en',
activeItemCoin:{ activeItemCoin: {
value: "nexa", value: "nexa",
label: "nexa", label: "nexa",
img: require("../../assets/img/currency-nexa.png"), img: require("../../assets/img/currency-nexa.png"),
@ -781,19 +782,19 @@ export default {
}, },
activeItemCoin: { activeItemCoin: {
handler(newVal) { handler(newVal) {
// 防止无限循环 // 防止无限循环
if (this.isInternalChange) { if (this.isInternalChange) {
return; return;
} }
this.handleActiveItemChange(newVal); this.handleActiveItemChange(newVal);
}, },
deep: true deep: true
} }
}, },
mounted() { mounted() {
this.lang = this.$i18n.locale; // 初始化语言值 this.lang = this.$i18n.locale; // 初始化语言值
this.$addStorageEvent(1, `activeItemCoin`, JSON.stringify(this.currencyList[0])) this.$addStorageEvent(1, `activeItemCoin`, JSON.stringify(this.currencyList[0]))
this.minerChartLoading = true this.minerChartLoading = true
@ -825,13 +826,67 @@ export default {
} }
}else{ //动态计算图表的grid.left 让左侧的Y轴标签显示完全
const yAxis = this.option.yAxis[0]; // 第一个 Y 轴
const maxValue = Math.max(...this.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;
this.option.grid.left = labelWidth + safeMargin + 'px';
// ------------全网算力图---------------
//
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';
} }
this.getBlockInfoData(this.BlockInfoParams) this.getBlockInfoData(this.BlockInfoParams)
this.getCoinInfoData(this.params) this.getCoinInfoData(this.params)
this.getPoolPowerData(this.PowerParams) this.getPoolPowerData(this.PowerParams)
// 注册需要在网络恢复后自动重新调用的方法
this.registerRecoveryMethod('getCoinInfoData', this.params);
this.registerRecoveryMethod('getPoolPowerData', this.PowerParams);
this.registerRecoveryMethod('getBlockInfoData', this.BlockInfoParams);
this.$addStorageEvent(1, `currencyList`, JSON.stringify(this.currencyList)) this.$addStorageEvent(1, `currencyList`, JSON.stringify(this.currencyList))
if (this.$refs.select) { if (this.$refs.select) {
this.$refs.select.$el.children[0].children[0].setAttribute('style', "background:url(https://test.m2pool.com/img/nexa.png) no-repeat 10PX;background-size: 20PX 20PX;color:#333;padding-left: 30PX;"); this.$refs.select.$el.children[0].children[0].setAttribute('style', "background:url(https://test.m2pool.com/img/nexa.png) no-repeat 10PX;background-size: 20PX 20PX;color:#333;padding-left: 30PX;");
@ -844,15 +899,39 @@ export default {
// let activeItemCoin =localStorage.getItem(`activeItemCoin`) // let activeItemCoin =localStorage.getItem(`activeItemCoin`)
// this.activeItemCoin=JSON.parse(activeItemCoin) // this.activeItemCoin=JSON.parse(activeItemCoin)
window.addEventListener("setItem", () => { window.addEventListener("setItem", () => {
let value =localStorage.getItem(`activeItemCoin`) let value = localStorage.getItem(`activeItemCoin`)
this.activeItemCoin=JSON.parse(value) this.activeItemCoin = JSON.parse(value)
}); });
}, },
methods: { 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() { slideLeft() {
const allLength = this.currencyList.length * 120 const allLength = this.currencyList.length * 120
const boxLength = document.getElementById('list-box').clientWidth const boxLength = document.getElementById('list-box').clientWidth
@ -866,7 +945,7 @@ export default {
listEl.style.left = '-' + (leftMove - 360) + 'PX' listEl.style.left = '-' + (leftMove - 360) + 'PX'
} }
}, },
//初始化图表 //初始化图表
inCharts() { inCharts() {
@ -905,7 +984,7 @@ export default {
this.MinerChart = echarts.init(document.getElementById("minerChart")); 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[0].name = this.$t(`home.networkPower`)
this.minerOption.series[1].name = this.$t(`home.currencyPrice`) this.minerOption.series[1].name = this.$t(`home.currencyPrice`)
this.MinerChart.setOption(this.minerOption); this.MinerChart.setOption(this.minerOption);
@ -973,61 +1052,7 @@ export default {
}, 200), }, 200),
// async getPoolPowerData(params) {
// this.minerChartLoading = true
// const data = await getPoolPower(params)
// if (!data) {
// this.minerChartLoading = false
// if (this.myChart) {
// this.myChart.dispose()//销毁图表实列
// }
// return
// }
// let chartData = data.data
// let xData = []
// let pvData = []
// let rejectRate = []
// let price = []
// chartData.forEach(item => {
// if (item.date.includes(`T`) && params.interval == `rt`) {
// item.date = `${item.date.split("T")[0]} ${item.date.split("T")[1].split(`.`)[0]}`
// } else if (item.date.includes(`T`) && params.interval == `1d`) {
// item.date = item.date.split("T")[0]
// }
// xData.push(item.date)
// pvData.push(Number(item.pv).toFixed(2))
// // rejectRate.push((item.rejectRate * 100).toFixed(2))
// if (item.price == 0) {
// price.push(item.price)
// } else if (item.price < 1) {
// price.push(Number(item.price).toFixed(8))
// } else {
// price.push(Number(item.price).toFixed(2))
// }
// });
// // this.maxValue = Math.max(...rejectRate);
// // let leftYMaxData = Math.max(...pvData);
// // this.option.yAxis[0].max =leftYMaxData*2
// this.option.xAxis.data = xData
// this.option.series[0].data = pvData
// // this.option.series[1].data = rejectRate
// this.option.series[1].data = price
// this.$nextTick(() => {
// this.inCharts()
// })
// },
getPoolPowerData: Debounce(async function (params) { getPoolPowerData: Debounce(async function (params) {
// this.minerChartLoading = true // this.minerChartLoading = true
this.setLoading('minerChartLoading', true); this.setLoading('minerChartLoading', true);
@ -1047,15 +1072,15 @@ export default {
let rejectRate = [] let rejectRate = []
let price = [] let price = []
chartData.forEach(item => { chartData.forEach(item => {
if (item.date.includes(`T`) && params.interval == `rt`) { if (item.date.includes(`T`) && params.interval == `rt`) {
item.date = `${item.date.split("T")[0]} ${item.date.split("T")[1].split(`.`)[0]}` item.date = `${item.date.split("T")[0]} ${item.date.split("T")[1].split(`.`)[0]}`
} else if (item.date.includes(`T`) && params.interval == `1d`) { } else if (item.date.includes(`T`) && params.interval == `1d`) {
item.date = item.date.split("T")[0] item.date = item.date.split("T")[0]
} }
xData.push(item.date) xData.push(item.date)
pvData.push(Number(item.pv).toFixed(2)) pvData.push(Number(item.pv).toFixed(2))
// rejectRate.push((item.rejectRate * 100).toFixed(2)) // rejectRate.push((item.rejectRate * 100).toFixed(2))
@ -1063,13 +1088,13 @@ export default {
price.push(item.price) price.push(item.price)
} else if (item.price < 1) { } else if (item.price < 1) {
price.push(Number(item.price).toFixed(8)) price.push(Number(item.price).toFixed(8))
} else { } else {
price.push(Number(item.price).toFixed(2)) price.push(Number(item.price).toFixed(2))
} }
}); });
// this.maxValue = Math.max(...rejectRate); // this.maxValue = Math.max(...rejectRate);
// let leftYMaxData = Math.max(...pvData); // let leftYMaxData = Math.max(...pvData);
@ -1082,13 +1107,13 @@ export default {
this.inCharts() this.inCharts()
}) })
}catch{ } catch {
console.error('获取数据失败:', error); console.error('获取数据失败:', error);
}finally { } finally {
// 确保无论成功失败都设置loading为false // 确保无论成功失败都设置loading为false
this.setLoading('minerChartLoading', false); this.setLoading('minerChartLoading', false);
} }
@ -1221,37 +1246,11 @@ export default {
}, 200), }, 200),
// async getBlockInfoData(params) {
// this.reportBlockLoading = true
// const data = await getBlockInfo(params)
// if (data && data.code == 200) {
// this.BlockShow = true
// this.newBlockInfoData = []
// this.BlockInfoData = data.rows
// if (!this.BlockInfoData[0]) {
// this.reportBlockLoading = false
// return
// }
// this.BlockInfoData.forEach((item, index) => {
// item.date = `${item.date.split("T")[0]} ${item.date.split("T")[1].split(`.`)[0]}`
// if (index < 8) {
// this.newBlockInfoData.push(item)
// }
// })
// } else {
// this.BlockShow = false
// }
// this.reportBlockLoading = false
// },
getBlockInfoData: Debounce(async function (params) { getBlockInfoData: Debounce(async function (params) {
// this.reportBlockLoading = true try {
// this.reportBlockLoading = true
this.setLoading('reportBlockLoading', true); this.setLoading('reportBlockLoading', true);
const data = await getBlockInfo(params) const data = await getBlockInfo(params)
if (data && data.code == 200) { if (data && data.code == 200) {
@ -1259,7 +1258,8 @@ export default {
this.newBlockInfoData = [] this.newBlockInfoData = []
this.BlockInfoData = data.rows this.BlockInfoData = data.rows
if (!this.BlockInfoData[0]) { if (!this.BlockInfoData[0]) {
this.reportBlockLoading = false // this.reportBlockLoading = false
this.setLoading('reportBlockLoading', false);
return return
} }
@ -1271,13 +1271,21 @@ export default {
} }
}) })
} else { } else {
this.BlockShow = false this.BlockShow = false
} }
this.setLoading('reportBlockLoading', false); this.setLoading('reportBlockLoading', false);
// this.reportBlockLoading = false // this.reportBlockLoading = false
} catch (error) {
console.error('获取区块信息失败:', error);
this.BlockShow = false;
}finally {
// 确保在任何情况下都会重置 loading 状态
this.setLoading('reportBlockLoading', false);
}
@ -1321,38 +1329,54 @@ export default {
switch (this.time) { switch (this.time) {
case "day": case "day":
// this.profit = result.toFixed(10) this.profit = result.toFixed(8)
// this.profit = Math.floor(result * 1e10) / 1e10; // this.profit = Math.floor(result * 1e10) / 1e10;
this.profit = this.toFixedNoRound(result, 10); // this.profit = this.toFixedNoRound(result, 10);
break; break;
case "week": case "week":
// this.profit = (result * 7).toFixed(10) this.profit = (result * 7).toFixed(8)
// this.profit = Math.floor(result * 7 * 1e10) / 1e10; // this.profit = Math.floor(result * 7 * 1e10) / 1e10;
this.profit = this.toFixedNoRound(result * 7, 10); // this.profit = this.toFixedNoRound(result * 7, 10);
break; break;
case "month": case "month":
// this.profit = (result * 30).toFixed(10) this.profit = (result * 30).toFixed(8)
// this.profit = Math.floor(result * 30 * 1e10) / 1e10; // this.profit = Math.floor(result * 30 * 1e10) / 1e10;
this.profit = this.toFixedNoRound(result * 30, 10); // this.profit = this.toFixedNoRound(result * 30, 10);
break; break;
case "year": case "year":
// this.profit = (result * 365).toFixed(10) this.profit = (result * 365).toFixed(8)
// this.profit = Math.floor(result * 365 * 1e10) / 1e10; // this.profit = Math.floor(result * 365 * 1e10) / 1e10;
this.profit = this.toFixedNoRound(result * 365, 10); // this.profit = this.toFixedNoRound(result * 365, 10);
break; break;
default: default:
break; break;
} }
const nexaFormatter = new Intl.NumberFormat("en-US", {
// 10000000000000000 minimumFractionDigits: 2, // 强制显示 2 位小数(不足补零)
maximumFractionDigits: 2, // 可选:限制最多 2 位小数(避免多余位数)
const formatter = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 10, // 强制显示足够多的小数位
useGrouping: true, // 不使用千位分隔符(如 1,000 useGrouping: true, // 不使用千位分隔符(如 1,000
}); });
this.profit = formatter.format(this.profit); if (this.value == "nexa") {
this.profit = nexaFormatter.format(this.profit);
} else {
const formatter = new Intl.NumberFormat("en-US", {
minimumFractionDigits: 8, // 强制显示足够多的小数位
useGrouping: true, // 不使用千位分隔符(如 1,000
});
this.profit = formatter.format(this.profit);
}
// const formatter = new Intl.NumberFormat("en-US", {
// minimumFractionDigits: 8, // 强制显示足够多的小数位
// useGrouping: false, // 不使用千位分隔符(如 1,000
// });
// this.profit = formatter.format(this.profit);
}, },
@ -1383,13 +1407,13 @@ export default {
// 其他CSS样式可以根据需要添加 // 其他CSS样式可以根据需要添加
}, },
handelProfitCalculation() { handelProfitCalculation() {
for (const key in this.CalculatorData) { for (const key in this.CalculatorData) {
if (!this.CalculatorData[key]) { if (this.CalculatorData[key]!==0 && !this.CalculatorData[key]) {
// this.$message({ this.$message({
// message: this.$t(`home.acquisitionFailed`), message: this.$t(`home.acquisitionFailed`),
// type: "error", type: "error",
// }); });
this.profit = 0 this.profit = 0
// var myDiv = document.getElementById('myDiv'); // var myDiv = document.getElementById('myDiv');
// this.disableElement(myDiv); // this.disableElement(myDiv);
@ -1417,7 +1441,7 @@ export default {
this.fetchParam({ coin: this.value }) this.fetchParam({ coin: this.value })
}, },
handelJump(url) { handelJump(url) {
if (url === '/AccessMiningPool') { if (url === '/AccessMiningPool') {
const coin = this.currencyList.find(item => item.value === this.params.coin); const coin = this.currencyList.find(item => item.value === this.params.coin);
@ -1425,16 +1449,16 @@ export default {
let jumpName = coin.path.charAt(0).toUpperCase() + coin.path.slice(1) //name跳转 首字母大写 let jumpName = coin.path.charAt(0).toUpperCase() + coin.path.slice(1) //name跳转 首字母大写
// 使用 name 进行导航,避免重复的路由参数 // 使用 name 进行导航,避免重复的路由参数
this.$router.push({ this.$router.push({
name: jumpName, name: jumpName,
params: { params: {
lang: this.lang, lang: this.lang,
coin: this.params.coin, coin: this.params.coin,
imgUrl: this.currencyPath imgUrl: this.currencyPath
}, },
replace: false // 保留历史记录,允许回退 replace: false // 保留历史记录,允许回退
}); });
} else { } else {
// 移除开头的斜杠,统一处理路径格式 // 移除开头的斜杠,统一处理路径格式
const cleanPath = url.startsWith('/') ? url.slice(1) : url; const cleanPath = url.startsWith('/') ? url.slice(1) : url;
@ -1445,77 +1469,106 @@ export default {
handelCalculation() { handelCalculation() {
this.calculateIncome() this.calculateIncome()
}, },
// 获取单个币种项目的完整宽度包含margin
getItemFullWidth() {
const listEl = this.$refs.currencyList;
if (!listEl) return 120;
const firstItem = listEl.querySelector('.list-item');
if (!firstItem) return 120;
const style = window.getComputedStyle(firstItem);
const width = firstItem.offsetWidth;
const marginLeft = parseInt(style.marginLeft) || 0;
const marginRight = parseInt(style.marginRight) || 0;
return width + marginLeft + marginRight;
},
// 左滑动逻辑 // 左滑动逻辑
scrollLeft() { scrollLeft() {
const allLength = this.currencyList.length * 120 const allLength = this.currencyList.length * 120
const boxLength = document.getElementById('list-box').clientWidth const boxLength = document.getElementById('list-box').clientWidth
if (allLength < boxLength) return if (allLength < boxLength) return
const listEl = document.getElementById('list') const listEl = document.getElementById('list')
const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left)) const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
if (leftMove + boxLength - 360 < boxLength) { if (leftMove + boxLength - 360 < boxLength) {
// 到最开始的时候 // 到最开始的时候
listEl.style.left = '0PX' listEl.style.left = '0PX'
} else { } else {
listEl.style.left = '-' + (leftMove - 360) + 'PX' listEl.style.left = '-' + (leftMove - 360) + 'PX'
} }
}, },
// 右滑动逻辑 // 右滑动逻辑
scrollRight() { scrollRight() {
const allLength = this.currencyList.length * 120 const allLength = this.currencyList.length * 120
const boxLength = document.getElementById('list-box').clientWidth const boxLength = document.getElementById('list-box').clientWidth
if (allLength < boxLength) return if (allLength < boxLength) return
const listEl = document.getElementById('list') const listEl = document.getElementById('list')
const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left)) const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
if (leftMove + boxLength + 360 > allLength) { if (leftMove + boxLength + 360 > allLength) {
listEl.style.left = '-' + (allLength - boxLength) + 'PX' listEl.style.left = '-' + (allLength - boxLength) + 'PX'
} else { } else {
listEl.style.left = '-' + (leftMove + 360) + 'PX' listEl.style.left = '-' + (leftMove + 360) + 'PX'
} }
}, },
// clickCurrency: throttle(function(item) {
// this.currency = item.label
// this.currencyPath = item.imgUrl // // 左滑动逻辑
// this.params.coin = item.value // scrollLeft() {
// this.BlockInfoParams.coin = item.value // const allLength = this.currencyList.length * 120
// this.itemActive = item.value // const boxLength = document.getElementById('list-box').clientWidth
// this.PowerParams.coin = item.value // if (allLength < boxLength) return
// this.getCoinInfoData(this.params) // const listEl = document.getElementById('list')
// this.getBlockInfoData(this.BlockInfoParams) // const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
// // this.getPoolPowerData(this.PowerParams) // if (leftMove + boxLength - 360 < boxLength) {
// // this.getMinerCountData(this.params) // // 到最开始的时候
// if (this.powerActive) { // listEl.style.left = '0PX'
// this.handelPower() // } else {
// } else if (!this.powerActive) { // listEl.style.left = '-' + (leftMove - 360) + 'PX'
// this.handelMiner()
// } // }
// }, 1000), // },
// // 右滑动逻辑
// scrollRight() {
// const allLength = this.currencyList.length * 120
// const boxLength = document.getElementById('list-box').clientWidth
// if (allLength < boxLength) return
// const listEl = document.getElementById('list')
// const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
// if (leftMove + boxLength + 360 > allLength) {
// listEl.style.left = '-' + (allLength - boxLength) + 'PX'
// } else {
// listEl.style.left = '-' + (leftMove + 360) + 'PX'
// }
// },
handleActiveItemChange(item) { handleActiveItemChange(item) {
if (!item) return; if (!item) return;
this.currency = item.label; this.currency = item.label;
this.currencyPath = item.imgUrl; this.currencyPath = item.imgUrl;
this.params.coin = item.value; this.params.coin = item.value;
console.log( this.params.coin , "item"); console.log(this.params.coin, "item");
this.BlockInfoParams.coin = item.value; this.BlockInfoParams.coin = item.value;
this.itemActive = item.value; this.itemActive = item.value;
this.PowerParams.coin = item.value; this.PowerParams.coin = item.value;
// 调用相关数据更新方法 // 调用相关数据更新方法
this.getCoinInfoData(this.params); this.getCoinInfoData(this.params);
this.getBlockInfoData(this.BlockInfoParams); this.getBlockInfoData(this.BlockInfoParams);
if (this.powerActive) { if (this.powerActive) {
this.handelPower(); this.handelPower();
} else { } else {
this.handelMiner(); this.handelMiner();
} }
this.handelCoinLabel(item.value); this.handelCoinLabel(item.value);
}, },
clickCurrency(item) { clickCurrency(item) {
if (!item) return; if (!item) return;
@ -1531,16 +1584,16 @@ export default {
// this.PowerParams.coin = item.value // this.PowerParams.coin = item.value
// this.getCoinInfoData(this.params) // this.getCoinInfoData(this.params)
// this.getBlockInfoData(this.BlockInfoParams) // this.getBlockInfoData(this.BlockInfoParams)
// 在下一个事件循环中重置标记 // 在下一个事件循环中重置标记
this.$nextTick(() => { this.$nextTick(() => {
this.isInternalChange = false; this.isInternalChange = false;
}); });
// 直接调用处理方法
this.handleActiveItemChange(item); // 直接调用处理方法
this.handleActiveItemChange(item);
}, },
clickReportBlock() { clickReportBlock() {
this.$router.push({ this.$router.push({

View File

@ -3,10 +3,10 @@
<section v-if="$isMobile"> <section v-if="$isMobile">
<div class="imgTop"> <div class="imgTop">
<!-- <img src="../../assets/mobile/home/home.png" alt="mining" loading="lazy" /> --> <img src="../../assets/mobile/home/home.png" alt="mining" loading="lazy" />
<!--
<img v-if="lang == 'zh'" src="../../assets/img/enx推广.png" alt="mining" loading="lazy"/> <img v-if="lang == 'zh'" src="../../assets/img/enx推广.png" alt="mining" loading="lazy"/>
<img v-else src="../../assets/img/enx英文推广.png" alt="mining" loading="lazy"/> <img v-else src="../../assets/img/enx英文推广.png" alt="mining" loading="lazy"/> -->
</div> </div>
@ -21,12 +21,12 @@
</span> </span>
</template> </template>
<ul class="moveCurrencyBox" > <ul class="moveCurrencyBox" >
<li @click="clickCurrency(item)" v-for="item in currencyList" :key="item.value"> <li @click="clickCurrency(item)" v-for="item in currencyList" :key="item.value">
<img :src="item.img" alt="coin" loading="lazy"/> <img :src="item.img" alt="coin" loading="lazy"/>
<p>{{ item.label }}</p> <p>{{ item.label }}</p>
</li> </li>
</ul> </ul>
</el-submenu> </el-submenu>
@ -329,13 +329,12 @@
</section> </section>
<div class="content" v-else v-loading="minerChartLoading"> <div class="content" v-else v-loading="minerChartLoading">
<div class="bgBox"> <div class="bgBox">
<!--
<img v-if="lang == 'zh'" class="bgBoxImg2Img" src="../../assets/img/enx推广.png" alt="mining" loading="lazy"/> <img v-if="lang == 'zh'" class="bgBoxImg2Img" src="../../assets/img/enx推广.png" alt="mining" loading="lazy"/>
<img v-else class="bgBoxImg2Img" src="../../assets/img/enx英文推广.png" alt="mining" loading="lazy"/> <img v-else class="bgBoxImg2Img" src="../../assets/img/enx英文推广.png" alt="mining" loading="lazy"/> -->
<img class="bgImg" src="../../assets/img/home.png" alt="mining" loading="lazy"/>
<!-- <img class="bgBoxImg" src="../../assets/img/enx推广.png" style="width: 100%;height: 100%;" alt="mining" loading="lazy"/> -->
</div> </div>
<el-row> <el-row>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24"> <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
@ -366,6 +365,7 @@
</el-card> </el-card>
</el-col> </el-col>
</el-row> </el-row>
<section class="describeBox"> <section class="describeBox">
<p> <i class="iconfont icon-tishishuoming "></i><span class="describeTitle">{{ $t(`home.describeTitle`) }}</span>{{ $t(`home.describe`) }} <span class="view" @click="handelJump(`/allocationExplanation`)"> {{ $t(`home.view`) }} </span> </p> <p> <i class="iconfont icon-tishishuoming "></i><span class="describeTitle">{{ $t(`home.describeTitle`) }}</span>{{ $t(`home.describe`) }} <span class="view" @click="handelJump(`/allocationExplanation`)"> {{ $t(`home.view`) }} </span> </p>
</section> </section>
@ -375,7 +375,7 @@
<div class="currencyDescription2"> <div class="currencyDescription2">
<section class="miningPoolBox"> <section class="miningPoolBox">
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12"> <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
<div class="miningPoolLeft" v-loading="minerChartLoading"> <div class="miningPoolLeft" v-loading="minerChartLoading" v-loading-recovery="{ loading: 'minerChartLoading', recovery: ['getPoolPowerData', 'fetchNetPower'] }">
<div class="interval"> <div class="interval">
<div class="chartBth"> <div class="chartBth">
@ -406,7 +406,6 @@
>{{ $t(item.label) }}</span >{{ $t(item.label) }}</span
> >
</div> </div>
<div <div
id="chart" id="chart"
@ -603,7 +602,7 @@
<el-row> <el-row>
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24"> <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<div class="reportBlock"> <div class="reportBlock">
<div class="reportBlockBox" v-loading="reportBlockLoading"> <div class="reportBlockBox" v-loading="reportBlockLoading" v-loading-recovery="{ loading: 'reportBlockLoading', recovery: ['getBlockInfoData'] }">
<div class="belowTable"> <div class="belowTable">
<ul> <ul>
<li class="table-title"> <li class="table-title">
@ -681,6 +680,7 @@
:key="item.value" :key="item.value"
:label="item.label" :label="item.label"
:value="item.value" :value="item.value"
v-show="item.value !== 'enx'"
> >
<div style="display: flex; align-items: center"> <div style="display: flex; align-items: center">
<img :src="item.imgUrl" style="float: left; width: 20px" /> <img :src="item.imgUrl" style="float: left; width: 20px" />
@ -762,7 +762,7 @@ export default {
}; };
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
// //
@media screen and (min-width: 220px) and (max-width: 800px) { @media screen and (min-width: 220px) and (max-width: 800px) {
.imgTop { .imgTop {
@ -785,6 +785,7 @@ export default {
p{ p{
width: 100% ; width: 100% ;
background: transparent ; background: transparent ;
padding-left: 8px;
} }
i{ i{
@ -800,6 +801,7 @@ export default {
.view{ .view{
color: #5721e4; color: #5721e4;
margin-left: 5px; margin-left: 5px;
cursor: pointer;
} }
} }
@ -826,6 +828,7 @@ export default {
// overflow: hidden; // overflow: hidden;
padding: 5px 5px; padding: 5px 5px;
box-sizing: border-box; box-sizing: border-box;
cursor: pointer;
// background: palegoldenrod; // background: palegoldenrod;
img { img {
@ -841,7 +844,13 @@ export default {
text-transform: capitalize; text-transform: capitalize;
} }
} }
} }
.moveCurrencyBox li:hover {
transform: scale(1.05);
}
.currencySelect{ .currencySelect{
display: flex; display: flex;
align-items: center; align-items: center;
@ -1295,16 +1304,45 @@ export default {
@media screen and (min-width:800px) and (max-width: 1279px) { @media screen and (min-width:800px) and (max-width: 1279px) {
.imgTop { .imgTop {
width: 100%; width: 100%;
// padding-left: 10%; padding-left: 20%;
text-align: center; text-align: left;
img { img {
width: 100%; width: auto ;
height:300px;
} }
} }
#chart { #chart {
height: 400px !important; height: 400px !important;
} }
.describeBox2{
width: 100%;
font-size: 0.9rem;
padding: 8px;
margin: 0 auto;
p{
width: 100% ;
background: transparent ;
padding-left: 8px;
}
i{
color: #5721e4;
margin-right: 5px;
}
.describeTitle{
color: #5721e4;
font-weight: 600;
font-size: 0.95rem;
}
.view{
color: #5721e4;
margin-left: 5px;
cursor: pointer;
}
}
.moveCurrencyBox { .moveCurrencyBox {
@ -1331,6 +1369,7 @@ export default {
padding: 5px 5px; padding: 5px 5px;
box-sizing: border-box; box-sizing: border-box;
// background: palegoldenrod; // background: palegoldenrod;
cursor: pointer;
img { img {
width: 25px; width: 25px;
@ -1345,7 +1384,13 @@ export default {
text-transform: capitalize; text-transform: capitalize;
} }
} }
} }
.moveCurrencyBox li:hover {
transform: scale(1.05);
}
.currencySelect{ .currencySelect{
display: flex; display: flex;
align-items: center; align-items: center;
@ -2046,7 +2091,7 @@ export default {
.bgBox { .bgBox {
// background: gold; // background: gold;
width: 100%; width: 100%;
// height: 380px; height: 300px;
box-sizing: border-box; box-sizing: border-box;
// background-position: 50% 28%; // background-position: 50% 28%;
// background-size: cover; // background-size: cover;
@ -2059,12 +2104,20 @@ export default {
// background-position: 13.2vw 0 ; // background-position: 13.2vw 0 ;
// background-repeat: no-repeat; // background-repeat: no-repeat;
// margin-top: 50px; // margin-top: 50px;
margin: 30px 0px; // margin: 30px 0px;
text-align: center; text-align: left;
.bgImg{
height: 100%;
width: auto ;
position: absolute;
left: 25vw;
top: 0;
}
.bgBoxImg { .bgBoxImg {
height: 100%; height: 100%;
width: auto;
position: absolute; position: absolute;
left: 23%; left: 23%;
transition: all 0.3s linear; transition: all 0.3s linear;
@ -3233,6 +3286,7 @@ export default {
cursor: pointer; cursor: pointer;
margin-left: 8px; margin-left: 8px;
color: #6E3EDB; color: #6E3EDB;
// background: palegoldenrod;
} }
.view:hover{ .view:hover{

View File

@ -214,6 +214,7 @@ import {
import { encryption } from "../../utils/fun"; import { encryption } from "../../utils/fun";
import { getAccountList } from "../../api/personalCenter"; import { getAccountList } from "../../api/personalCenter";
export default { export default {
data() { data() {
return { return {
@ -372,6 +373,11 @@ export default {
JSON.stringify(data.data.access_token) JSON.stringify(data.data.access_token)
); );
//
await new Promise(resolve => setTimeout(resolve, 50));
//
this.$bus.$emit('user-logged-in'); //
this.fetchAccountList(); this.fetchAccountList();
this.fetchAccountGradeList(); this.fetchAccountGradeList();
this.fetchJurisdiction(); this.fetchJurisdiction();

View File

@ -1345,7 +1345,14 @@ export default {
this.getMinerListData(this.MinerListParams) this.getMinerListData(this.MinerListParams)
this.getHistoryIncomeData(this.IncomeParams) this.getHistoryIncomeData(this.IncomeParams)
this.getHistoryOutcomeData(this.OutcomeParams) this.getHistoryOutcomeData(this.OutcomeParams)
this.registerRecoveryMethod('getMinerAccountInfoData', this.params);
this.registerRecoveryMethod('getMinerAccountPowerData', this.PowerParams);
this.registerRecoveryMethod('getAccountPowerDistributionData', this.PowerDistribution);
this.registerRecoveryMethod('getMinerListData', this.MinerListParams);
this.registerRecoveryMethod('getHistoryIncomeData', this.IncomeParams);
this.registerRecoveryMethod('getHistoryOutcomeData', this.OutcomeParams);
}, },
methods: { methods: {
@ -1400,11 +1407,12 @@ export default {
// console.log(data,"获取币种信息"); // console.log(data,"获取币种信息");
}, },
async getMinerAccountPowerData(params) { async getMinerAccountPowerData(params) {
this.powerChartLoading = true // this.powerChartLoading = true
this.setLoading('powerChartLoading', true);
const data = await getMinerAccountPower(params) const data = await getMinerAccountPower(params)
if (!data) { if (!data) {
this.powerChartLoading = false this.setLoading('powerChartLoading', false);
if (this.myChart) { if (this.myChart) {
this.myChart.dispose()//销毁图表实列 this.myChart.dispose()//销毁图表实列
} }
@ -1437,13 +1445,13 @@ export default {
this.option.series[1].data = rejectRate this.option.series[1].data = rejectRate
this.inCharts() this.inCharts()
this.powerChartLoading = false this.setLoading('powerChartLoading', false);
}, },
async getAccountPowerDistributionData(params) { async getAccountPowerDistributionData(params) {
this.barChartLoading = true this.setLoading('barChartLoading', true);
const data = await getAccountPowerDistribution(params) const data = await getAccountPowerDistribution(params)
let barData = data.data let barData = data.data
let xData = [] let xData = []
@ -1455,7 +1463,7 @@ export default {
this.barOption.xAxis[0].data = xData this.barOption.xAxis[0].data = xData
this.barOption.series[0].data = barValueList this.barOption.series[0].data = barValueList
this.barInCharts() this.barInCharts()
this.barChartLoading = false this.setLoading('barChartLoading', false);
}, },
formatNumber(num) {//保留两位小数并补0 formatNumber(num) {//保留两位小数并补0
const intPart = Math.floor(num); const intPart = Math.floor(num);
@ -1463,7 +1471,7 @@ export default {
return `${intPart}.${String(decimalPart).padStart(2, '0')}`; return `${intPart}.${String(decimalPart).padStart(2, '0')}`;
}, },
async getMinerListData(params) { async getMinerListData(params) {
this.MinerListLoading = true this.setLoading('MinerListLoading', true);
const data = await getMinerList(params) const data = await getMinerList(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.MinerListData = data.data this.MinerListData = data.data
@ -1503,7 +1511,7 @@ export default {
} }
this.MinerListLoading = false this.setLoading('MinerListLoading', false);
}, },
@ -1562,11 +1570,11 @@ export default {
// this.miniLoading=false // this.miniLoading=false
// }, // },
getMinerPowerData:Debounce(async function(params){ getMinerPowerData:Debounce(async function(params){
this.miniLoading=true this.setLoading('miniLoading', true);
const data = await getMinerPower(params) const data = await getMinerPower(params)
if (!data) { if (!data) {
this.miniLoading=false this.setLoading('miniLoading', false);
return return
} }
let miniData = data.data let miniData = data.data
@ -1618,62 +1626,18 @@ export default {
this.miniLoading=false this.setLoading('miniLoading', false);
},200), },200),
//小图
// async getMinerPowerOnLine(params) {
// this.miniLoading=true
// const data = await getMinerPower(params)
// if (!data) {
// this.miniLoading=false
// return
// }
// let miniData = data.data
// let xData = []
// let pv = []
// let rejectRate = []
// miniData.forEach(item => {
// if (item.date.includes(`T`)) {
// xData.push(`${item.date.split("T")[0]} ${item.date.split("T")[1].split(".")[0]}` )
// } else {
// xData.push(item.date)
// }
// pv.push(item.pv.toFixed(2))
// rejectRate.push((item.rejectRate * 100).toFixed(4))
// })
// this.onLineOption.xAxis.data = xData
// this.onLineOption.series[0].data = pv
// this.onLineOption.series[1].data = rejectRate
// this.onLineOption.series[0].name= this.$t(`home.finallyPower`)
// this.onLineOption.series[1].name= this.$t(`home.rejectionRate`)
// this.ids = `Small${this.miniId}`
// this.miniChartOnLine = echarts.init(document.getElementById(this.ids))
// this.$nextTick(() => {
// this.miniChartOnLine.setOption(this.onLineOption,true);
// window.addEventListener("resize", () => {
// if (this.miniChartOnLine) this.miniChartOnLine.resize();
// });
// });
// this.miniLoading=false // this.miniLoading=false
// }, // },
getMinerPowerOnLine:Debounce(async function(params){ getMinerPowerOnLine:Debounce(async function(params){
this.miniLoading=true this.setLoading('miniLoading', true);
const data = await getMinerPower(params) const data = await getMinerPower(params)
if (!data) { if (!data) {
this.miniLoading=false this.setLoading('miniLoading', false);
return return
} }
let miniData = data.data let miniData = data.data
@ -1716,7 +1680,7 @@ export default {
this.miniLoading=false this.setLoading('miniLoading', false);
},200), },200),
@ -1768,10 +1732,10 @@ export default {
// this.miniLoading=false // this.miniLoading=false
// }, // },
getMinerPowerOffLine:Debounce(async function(params){ getMinerPowerOffLine:Debounce(async function(params){
this.miniLoading=true this.setLoading('miniLoading', true);
const data = await getMinerPower(params) const data = await getMinerPower(params)
if (!data) { if (!data) {
this.miniLoading=false this.setLoading('miniLoading', false);
return return
} }
let miniData = data.data let miniData = data.data
@ -1815,7 +1779,7 @@ export default {
this.miniLoading=false this.setLoading('miniLoading', false);
},200), },200),

View File

@ -96,17 +96,20 @@ export default {
}, },
mounted() { mounted() {
this.fetchApiList(this.listParams) this.fetchApiList(this.listParams)
this.registerRecoveryMethod('fetchApiList', this.listParams);
}, },
methods: { methods: {
async fetchApiKey(params) { async fetchApiKey(params) {
this.ApiKeyLoading = true this.ApiKeyLoading = true
this.setLoading('ApiKeyLoading', true);
const data = await getApiKey(params) const data = await getApiKey(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.fetchApiList(this.listParams) this.fetchApiList(this.listParams)
this.dialogVisible = false this.dialogVisible = false
} }
this.ApiKeyLoading = false this.setLoading('ApiKeyLoading', false);
}, },
async fetchApiList(params) { async fetchApiList(params) {
this.apiPageLoading = true this.apiPageLoading = true
@ -115,7 +118,7 @@ export default {
this.apiList = data.rows this.apiList = data.rows
} }
this.apiPageLoading = false this.setLoading('apiPageLoading', false);
}, },
async fetchApiInfo(params) { async fetchApiInfo(params) {
this.apiPageLoading = true this.apiPageLoading = true
@ -127,7 +130,7 @@ export default {
} }
} }
this.apiPageLoading = false this.setLoading('apiPageLoading', false);
}, },
async fetchUpdateAPI(params) { async fetchUpdateAPI(params) {
this.apiPageLoading = true this.apiPageLoading = true
@ -137,7 +140,7 @@ export default {
this.modifyDialogVisible =false this.modifyDialogVisible =false
} }
this.apiPageLoading = false this.setLoading('apiPageLoading', false);
}, },
async fetchDelApi(params) { async fetchDelApi(params) {
this.apiPageLoading = true this.apiPageLoading = true
@ -149,7 +152,7 @@ export default {
this.fetchApiList(this.listParams) this.fetchApiList(this.listParams)
} }
this.apiPageLoading = false this.setLoading('apiPageLoading', false);
}, },
RequestApiKey() { RequestApiKey() {
this.dialogVisible = true this.dialogVisible = true

View File

@ -1,6 +1,7 @@
import { getCheck,getAddBalace, getAddMinerAccount, getAccountList, getDelMinerAccount, getMinerAccountBalance, getCheckAccount,getCheckBalance,getIfBind } from "../../../api/personalCenter" import { getCheck,getAddBalace, getAddMinerAccount, getAccountList, getDelMinerAccount, getMinerAccountBalance, getCheckAccount,getCheckBalance,getIfBind } from "../../../api/personalCenter"
import {getAccountGradeList } from "../../../api/login" import {getAccountGradeList } from "../../../api/login"
import { Debounce,throttle }from "../../../utils/publicMethods";
export default { export default {
data() { data() {
@ -270,6 +271,9 @@ export default {
this.fetchAccountList() this.fetchAccountList()
this.registerRecoveryMethod('fetchIfBind', "");
this.registerRecoveryMethod('fetchAccountList', "");
this.currencyList = JSON.parse(localStorage.getItem("currencyList")) this.currencyList = JSON.parse(localStorage.getItem("currencyList"))
window.addEventListener("setItem", () => { window.addEventListener("setItem", () => {
this.currencyList = JSON.parse(localStorage.getItem("currencyList")) this.currencyList = JSON.parse(localStorage.getItem("currencyList"))
@ -284,7 +288,8 @@ export default {
}, },
methods: { methods: {
async fetchIfBind(params){ async fetchIfBind(params){
this.securityLoading = true // this.securityLoading = true
this.setLoading('securityLoading', true);
const data = await getIfBind(params) const data = await getIfBind(params)
if (data && data.code === 200) { if (data && data.code === 200) {
if (data.data) { if (data.data) {
@ -302,13 +307,14 @@ export default {
this.dialogVerification=true this.dialogVerification=true
} }
} }
this.securityLoading = false this.setLoading('securityLoading', false);
}, },
async fetchCheck(params){ async fetchCheck(params){
this.addMinerLoading =true // this.addMinerLoading =true
this.setLoading('addMinerLoading', true);
const data = await getCheck(params) const data = await getCheck(params)
if (!data) { if (!data) {
this.addMinerLoading =false this.setLoading('addMinerLoading', false);
} }
if (data && data.code === 200) { if (data && data.code === 200) {
@ -320,20 +326,21 @@ export default {
type: "error", type: "error",
showClose: true showClose: true
}); });
this.addMinerLoading =false this.setLoading('addMinerLoading', false);
}else if(data.code === 802){//钱包不可用 }else if(data.code === 802){//钱包不可用
this.$message({ this.$message({
message: this.$t(`personal.invalidAddress`), message: this.$t(`personal.invalidAddress`),
type: "error", type: "error",
showClose: true showClose: true
}); });
this.addMinerLoading =false this.setLoading('addMinerLoading', false);
} }
}, },
async fetchCheckBalance(params){ async fetchCheckBalance(params){
this.confirmBindingLoading =true // this.confirmBindingLoading =true
this.setLoading('confirmBindingLoading', true);
const data = await getCheckBalance(params) const data = await getCheckBalance(params)
if (data && data.data) { if (data && data.data) {
this.fetchWalletAddress(this.WalletAddressParams) this.fetchWalletAddress(this.WalletAddressParams)
@ -344,7 +351,7 @@ export default {
type: 'error' type: 'error'
}); });
} }
this.confirmBindingLoading =false this.setLoading('confirmBindingLoading', false);
}, },
async fetchAccountGradeList(){ async fetchAccountGradeList(){
const data = await getAccountGradeList() const data = await getAccountGradeList()
@ -369,7 +376,8 @@ export default {
} }
}, },
async fetchMinerAccountBalance(params) { async fetchMinerAccountBalance(params) {
this.MiningLoading = true // this.MiningLoading = true
this.setLoading('MiningLoading', true);
const data = await getMinerAccountBalance(params) const data = await getMinerAccountBalance(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.walletDialogVisible = true this.walletDialogVisible = true
@ -386,10 +394,11 @@ export default {
this.paymentSettingsData.active = this.paymentSettingsData.active==`1` ? this.$t(`personal.no`) :this.$t(`personal.yes`) this.paymentSettingsData.active = this.paymentSettingsData.active==`1` ? this.$t(`personal.no`) :this.$t(`personal.yes`)
this.paymentSettingsData.amount = this.paymentSettingsData.amount ? this.paymentSettingsData.amount : `0` this.paymentSettingsData.amount = this.paymentSettingsData.amount ? this.paymentSettingsData.amount : `0`
this.MiningLoading = false this.setLoading('MiningLoading', false);
}, },
async fetchDelMinerAccount(params) { async fetchDelMinerAccount(params) {
this.MiningLoading = true // this.MiningLoading = true
this.setLoading('MiningLoading', true);
const data = await getDelMinerAccount(params) const data = await getDelMinerAccount(params)
if (data && data.code == 200) { if (data && data.code == 200) {
@ -400,17 +409,18 @@ export default {
} }
this.MiningLoading = false this.setLoading('MiningLoading', false);
}, },
async fetchAccountList(params) { async fetchAccountList(params) {
this.MiningLoading = true // this.MiningLoading = true
this.setLoading('MiningLoading', true);
const data = await getAccountList(params) const data = await getAccountList(params)
this.accountList = data.data this.accountList = data.data
console.log("请求成功,",data); console.log("请求成功,",data);
this.newAccountList = this.accountList this.newAccountList = this.accountList
this.$addStorageEvent(1, `accountList`, JSON.stringify(this.accountList)) this.$addStorageEvent(1, `accountList`, JSON.stringify(this.accountList))
this.MiningLoading = false this.setLoading('MiningLoading', false);
}, },
@ -431,7 +441,8 @@ export default {
}, },
//添加挖矿账户 //添加挖矿账户
async fetchAddMinerAccount(params) { async fetchAddMinerAccount(params) {
this.addMinerLoading =true // this.addMinerLoading =true
this.setLoading('addMinerLoading', true);
const data = await getAddMinerAccount(params) const data = await getAddMinerAccount(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.$message({ this.$message({
@ -451,7 +462,7 @@ export default {
this.fetchAccountList() this.fetchAccountList()
this.fetchAccountGradeList() this.fetchAccountGradeList()
this.addMinerLoading =false this.setLoading('addMinerLoading', false);
}, },
handleCheckAllChange(val) { handleCheckAllChange(val) {
@ -532,13 +543,7 @@ export default {
} }
}, },
confirmAdd() { confirmAdd:Debounce(function(){
// this.accountList.push({
// account: this.params.account,
// miningPool: this.params.miningPool,
// currency: this.params.miningPool,
// remarks: this.params.remarks,
// })
if (!this.AccountParams.ma) { if (!this.AccountParams.ma) {
this.$message({ this.$message({
message: this.$t(`personal.accountNumber`), message: this.$t(`personal.accountNumber`),
@ -590,12 +595,66 @@ export default {
} }
this.fetchCheck({ coin: this.AccountParams.coin, ma: this.AccountParams.ma,balance: this.AccountParams.balance}) 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(){ handelAddClose(){
for (let key in this.AccountParams) { for (let key in this.AccountParams) {
this.AccountParams[key] = ""; this.AccountParams[key] = "";

View File

@ -197,14 +197,16 @@ export default {
})) }))
})); }));
console.log( this.newMiningAccountList,"isrjiojfeo");
this.fetchUrlList(this.UrlListParams) this.fetchUrlList(this.UrlListParams)
this.registerRecoveryMethod('fetchUrlList', this.UrlListParams);
}, },
methods: { methods: {
async fetchHtmlUrl(params){ async fetchHtmlUrl(params){
this.establishLoading = true // this.establishLoading = true
this.setLoading('establishLoading', true);
const data = await getHtmlUrl(params) const data = await getHtmlUrl(params)
if (data && data.code == 200) { if (data && data.code == 200) {
@ -212,10 +214,11 @@ export default {
this.dialogVisible=false this.dialogVisible=false
} }
this.establishLoading = false this.setLoading('establishLoading', false);
}, },
async fetchUrlList(params){ async fetchUrlList(params){
this.UrlListLoading = true // this.UrlListLoading = true
this.setLoading('UrlListLoading', true);
const data = await getUrlList(params) const data = await getUrlList(params)
console.log(data,666 ); console.log(data,666 );
@ -223,7 +226,7 @@ export default {
this.TotalSize = data.total this.TotalSize = data.total
this.UrlListLoading = false this.setLoading('UrlListLoading', false);
}, },
async fetchUrlInfo(params){ async fetchUrlInfo(params){
const data = await getUrlInfo(params) const data = await getUrlInfo(params)
@ -239,7 +242,8 @@ export default {
}, },
async fetchChangeUrlInfo(params){ async fetchChangeUrlInfo(params){
this.modifyLoading=true // this.modifyLoading=true
this.setLoading('modifyLoading', true);
const data = await getChangeUrlInfo(params) const data = await getChangeUrlInfo(params)
console.log(data); console.log(data);
if (data && data.code == 200) { if (data && data.code == 200) {
@ -248,7 +252,7 @@ export default {
console.log("修改成功"); console.log("修改成功");
} }
this.modifyLoading=false this.setLoading('modifyLoading', false);
}, },
async fetchDelete(params){ async fetchDelete(params){

View File

@ -155,13 +155,14 @@ export default {
this.lang = this.$i18n.locale; // 初始化语言值 this.lang = this.$i18n.locale; // 初始化语言值
this.fetchIfBind() this.fetchIfBind()
this.registerRecoveryMethod('fetchIfBind', "");
if (this.$route.params.active) { if (this.$route.params.active) {
this.handelVerification() this.handelVerification()
} }
}, },
methods: { methods: {
async fetchIfBind(params) { async fetchIfBind(params) {
this.securityLoading = true this.setLoading('securityLoading', true);
const data = await getIfBind(params) const data = await getIfBind(params)
if (data && data.code === 200) { if (data && data.code === 200) {
if (data.data) { if (data.data) {
@ -170,10 +171,10 @@ export default {
this.isItBound = false this.isItBound = false
} }
} }
this.securityLoading = false this.setLoading('securityLoading', false);
}, },
async fetchBindInfo(params) { async fetchBindInfo(params) {
this.BindInfoLoading = true this.setLoading('BindInfoLoading', true);
const data = await getBindInfo(params) const data = await getBindInfo(params)
console.log(data, "绑定信息"); console.log(data, "绑定信息");
if (data && data.code === 200) { if (data && data.code === 200) {
@ -182,10 +183,10 @@ export default {
this.dialogVisible = false this.dialogVisible = false
} }
this.BindInfoLoading = false this.setLoading('BindInfoLoading', false);
}, },
async fetchBindGoogle(params) { async fetchBindGoogle(params) {
this.BindInfoLoading = true this.setLoading('BindInfoLoading', true);
const data = await getBindGoogle(params) const data = await getBindGoogle(params)
console.log(data, "绑定"); console.log(data, "绑定");
if (data && data.code === 200) { if (data && data.code === 200) {
@ -203,7 +204,7 @@ export default {
} }
this.BindInfoLoading = false this.setLoading('BindInfoLoading', false);
}, },
async fetchBindCode(params) { async fetchBindCode(params) {
const data = await getBindCode(params) const data = await getBindCode(params)
@ -216,7 +217,7 @@ export default {
} }
}, },
async fetchResetPwd(params) { async fetchResetPwd(params) {
this.ResetPwdLoading = true this.setLoading('ResetPwdLoading', true);
const data = await getUpdatePwd(params) const data = await getUpdatePwd(params)
if (data && data.code === 200) { if (data && data.code === 200) {
this.$message({ this.$message({
@ -230,7 +231,7 @@ export default {
localStorage.removeItem("token") localStorage.removeItem("token")
this.$router.push(`/${lang}/login`); this.$router.push(`/${lang}/login`);
} }
this.ResetPwdLoading = false this.setLoading('ResetPwdLoading', false);
}, },
async fetchResetPwdCode(params) { async fetchResetPwdCode(params) {
const data = await getUpdatePwdCode(params) const data = await getUpdatePwdCode(params)
@ -244,7 +245,7 @@ export default {
}, },
async fetchCloseStepTwo(params) { async fetchCloseStepTwo(params) {
this.closeLoading = true this.setLoading('closeLoading', true);
const data = await getCloseStepTwo(params) const data = await getCloseStepTwo(params)
if (data && data.code === 200) { if (data && data.code === 200) {
this.$message({ this.$message({
@ -260,7 +261,7 @@ export default {
this.closeParams[key] = "" this.closeParams[key] = ""
} }
} }
this.closeLoading = false this.setLoading('closeLoading', false);
}, },
async fetchCloseCode(params) { async fetchCloseCode(params) {
const data = await getCloseCode(params) const data = await getCloseCode(params)

View File

@ -269,7 +269,7 @@
<span class="title">{{ $t(`personal.verificationCode`) }}</span> <span class="title">{{ $t(`personal.verificationCode`) }}</span>
<div class="verificationCode"> <div class="verificationCode">
<el-input <el-input
type="email" type="text"
v-model="closeParams.eCode" v-model="closeParams.eCode"
autocomplete="off" autocomplete="off"
:placeholder="$t(`user.verificationCode`)" :placeholder="$t(`user.verificationCode`)"

View File

@ -69,7 +69,7 @@ export default {
value:"enx", value:"enx",
label:"Entropyx(Enx)", label:"Entropyx(Enx)",
img:`${this.$baseApi}img/enx.svg`, img:`${this.$baseApi}img/enx.svg`,
rate:"0", rate:"1%",
address:"", address:"",
mode:"PPLNS+PROPDIF", mode:"PPLNS+PROPDIF",
quota:"5000", quota:"5000",

View File

@ -12,8 +12,8 @@
<template slot="title"> <template slot="title">
<div class="collapseTitle"> <div class="collapseTitle">
<span ><img :src="item.img" alt="coin" loading="lazy"> {{item.label}}</span> <span ><img :src="item.img" alt="coin" loading="lazy"> {{item.label}}</span>
<span v-if="item.value === 'enx'"> {{ $t(`course.timeLimited`) }} 0%</span> <!-- <span v-if="item.value === 'enx'"> {{ $t(`course.timeLimited`) }} 0%</span> -->
<span v-else>{{item.rate}}</span> <span >{{item.rate}}</span>
</div> </div>
</template> </template>
<section class="contentBox2"> <section class="contentBox2">
@ -76,8 +76,8 @@
<li v-for="item in rateList" :key="item.value"> <li v-for="item in rateList" :key="item.value">
<span class="coin"><img :src="item.img" alt="coin" loading="lazy"> {{item.label}}</span> <span class="coin"><img :src="item.img" alt="coin" loading="lazy"> {{item.label}}</span>
<span>{{item.address}}</span> <span>{{item.address}}</span>
<span v-if="item.value === 'enx'"> {{ $t(`course.timeLimited`) }} 0%</span> <!-- <span v-if="item.value === 'enx'"> {{ $t(`course.timeLimited`) }} 0%</span> -->
<span v-else>{{item.rate}}</span> <span >{{item.rate}}</span>
<span>{{item.mode}}</span> <span>{{item.mode}}</span>
<span>{{item.quota}}</span> <span>{{item.quota}}</span>
</li> </li>

View File

@ -1228,6 +1228,8 @@ export default {
this.getMinerAccountPowerData(this.PowerParams) this.getMinerAccountPowerData(this.PowerParams)
this.getAccountPowerDistributionData(this.PowerDistribution) this.getAccountPowerDistributionData(this.PowerDistribution)
}, },
"$i18n.locale":(val)=>{ "$i18n.locale":(val)=>{
location.reload();//刷新页面 刷新echarts location.reload();//刷新页面 刷新echarts
@ -1269,6 +1271,12 @@ export default {
this.getMinerListData(this.MinerListParams) this.getMinerListData(this.MinerListParams)
this.getMinerAccountPowerData(this.PowerParams) this.getMinerAccountPowerData(this.PowerParams)
this.getAccountPowerDistributionData(this.PowerDistribution) this.getAccountPowerDistributionData(this.PowerDistribution)
this.registerRecoveryMethod('getMinerListData', this.MinerListParams);
this.registerRecoveryMethod('getMinerAccountPowerData', this.PowerParams);
this.registerRecoveryMethod('getAccountPowerDistributionData', this.PowerDistribution);
this.registerRecoveryMethod('fetchPageInfo', {key:this.params.key});
}, },
@ -1320,7 +1328,7 @@ export default {
}, },
//返回权限 1矿工 2收益 3支付 //返回权限 1矿工 2收益 3支付
async fetchPageInfo(params){ async fetchPageInfo(params){
this.jurisdictionLoading = true this.setLoading('jurisdictionLoading', true);
const data = await getPageInfo(params) const data = await getPageInfo(params)
console.log(data); console.log(data);
if (data && data.code == 200) { if (data && data.code == 200) {
@ -1354,7 +1362,7 @@ export default {
} }
this.jurisdictionLoading = false this.setLoading('jurisdictionLoading', false);
}, },
//获取当前挖矿账号信息(包含收益、余额) //获取当前挖矿账号信息(包含收益、余额)
async getMinerAccountInfoData(params) { async getMinerAccountInfoData(params) {
@ -1362,11 +1370,11 @@ export default {
this.MinerAccountData = data.data this.MinerAccountData = data.data
}, },
async getMinerAccountPowerData(params) { async getMinerAccountPowerData(params) {
this.powerChartLoading = true this.setLoading('powerChartLoading', true);
const data = await getMinerAccountPower(params) const data = await getMinerAccountPower(params)
if (!data) { if (!data) {
this.powerChartLoading = false this.setLoading('powerChartLoading', false);
if (this.myChart) { if (this.myChart) {
this.myChart.dispose()//销毁图表实列 this.myChart.dispose()//销毁图表实列
} }
@ -1399,13 +1407,13 @@ export default {
this.option.series[1].data = rejectRate this.option.series[1].data = rejectRate
this.inCharts() this.inCharts()
this.powerChartLoading = false this.setLoading('powerChartLoading', false);
}, },
async getAccountPowerDistributionData(params) { async getAccountPowerDistributionData(params) {
this.barChartLoading = true this.setLoading('barChartLoading', true);
const data = await getAccountPowerDistribution(params) const data = await getAccountPowerDistribution(params)
let barData = data.data let barData = data.data
let xData = [] let xData = []
@ -1417,7 +1425,7 @@ export default {
this.barOption.xAxis[0].data = xData this.barOption.xAxis[0].data = xData
this.barOption.series[0].data = barValueList this.barOption.series[0].data = barValueList
this.barInCharts() this.barInCharts()
this.barChartLoading = false this.setLoading('barChartLoading', false);
}, },
formatNumber(num) {//保留两位小数并补0 formatNumber(num) {//保留两位小数并补0
const intPart = Math.floor(num); const intPart = Math.floor(num);
@ -1425,7 +1433,7 @@ export default {
return `${intPart}.${String(decimalPart).padStart(2, '0')}`; return `${intPart}.${String(decimalPart).padStart(2, '0')}`;
}, },
async getMinerListData(params) { async getMinerListData(params) {
this.MinerListLoading = true this.setLoading('MinerListLoading', true);
const data = await getMinerList(params) const data = await getMinerList(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.MinerListData = data.data this.MinerListData = data.data
@ -1451,17 +1459,17 @@ export default {
} }
this.MinerListLoading = false this.setLoading('MinerListLoading', false);
}, },
//小图 //小图
async getMinerPowerData(params) { async getMinerPowerData(params) {
this.miniLoading=true this.setLoading('miniLoading', true);
const data = await getMinerPower(params) const data = await getMinerPower(params)
if (!data) { if (!data) {
this.miniLoading=false this.setLoading('miniLoading', false);
return return
} }
let miniData = data.data let miniData = data.data
@ -1507,15 +1515,15 @@ export default {
this.miniLoading=false this.setLoading('miniLoading', false);
}, },
//小图 //小图
async getMinerPowerOnLine(params) { async getMinerPowerOnLine(params) {
this.miniLoading=true this.setLoading('miniLoading', true);
const data = await getMinerPower(params) const data = await getMinerPower(params)
if (!data) { if (!data) {
this.miniLoading=false this.setLoading('miniLoading', false);
return return
} }
let miniData = data.data let miniData = data.data
@ -1556,14 +1564,14 @@ export default {
this.miniLoading=false this.setLoading('miniLoading', false);
}, },
//小图 //小图
async getMinerPowerOffLine(params) { async getMinerPowerOffLine(params) {
this.miniLoading=true this.setLoading('miniLoading', true);
const data = await getMinerPower(params) const data = await getMinerPower(params)
if (!data) { if (!data) {
this.miniLoading=false this.setLoading('miniLoading', false);
return return
} }
let miniData = data.data let miniData = data.data
@ -1605,7 +1613,7 @@ export default {
this.miniLoading=false this.setLoading('miniLoading', false);
}, },
async getHistoryIncomeData(params) { async getHistoryIncomeData(params) {
const data = await getHistoryIncome(params) const data = await getHistoryIncome(params)

View File

@ -235,6 +235,10 @@ export default {
this.getLuckData(this.params) this.getLuckData(this.params)
this.getBlockInfoData(this.BlockInfoParams) this.getBlockInfoData(this.BlockInfoParams)
this.registerRecoveryMethod('getLuckData', this.params);
this.registerRecoveryMethod('getBlockInfoData', this.BlockInfoParams);
let value = localStorage.getItem("activeItemCoin") let value = localStorage.getItem("activeItemCoin")
this.activeItemCoin = JSON.parse(value) this.activeItemCoin = JSON.parse(value)
this.currencyList = JSON.parse(localStorage.getItem("currencyList")) this.currencyList = JSON.parse(localStorage.getItem("currencyList"))
@ -260,14 +264,14 @@ export default {
// }, // },
getLuckData: Debounce(async function (params) { getLuckData: Debounce(async function (params) {
this.LuckDataLoading = true this.setLoading('LuckDataLoading', true);
const data = await getLuck(params) const data = await getLuck(params)
if (data && data.code == 200) { if (data && data.code == 200) {
this.luckData = data.data this.luckData = data.data
} }
this.LuckDataLoading = false this.setLoading('LuckDataLoading', false);
}, 200), }, 200),
// async getBlockInfoData(params) { // async getBlockInfoData(params) {
// this.reportBlockLoading=true // this.reportBlockLoading=true
@ -286,10 +290,10 @@ export default {
// }, // },
getBlockInfoData: Debounce(async function (params) { getBlockInfoData: Debounce(async function (params) {
this.reportBlockLoading = true this.setLoading('reportBlockLoading', true);
const data = await getBlockInfo(params) const data = await getBlockInfo(params)
if (!data) { if (!data) {
this.reportBlockLoading = false this.setLoading('reportBlockLoading', false);
} }
this.totalSize = data.total this.totalSize = data.total
this.BlockInfoData = data.rows this.BlockInfoData = data.rows
@ -298,7 +302,7 @@ export default {
}) })
// this.currentPage = 1 // this.currentPage = 1
// console.log(data,"获取币种信息"); // console.log(data,"获取币种信息");
this.reportBlockLoading = false this.setLoading('reportBlockLoading', false);
}, 200), }, 200),
handleActiveItemChange(item) { handleActiveItemChange(item) {
@ -314,6 +318,9 @@ export default {
clickCurrency(item) { clickCurrency(item) {
if (!item) return; if (!item) return;
// 设置标记,防止触发 watch // 设置标记,防止触发 watch
this.luckData={}
this.isInternalChange = true; this.isInternalChange = true;
this.activeItemCoin = item; this.activeItemCoin = item;
this.$addStorageEvent(1, `activeItemCoin`, JSON.stringify(item)) this.$addStorageEvent(1, `activeItemCoin`, JSON.stringify(item))

View File

@ -27,7 +27,7 @@
</el-menu> </el-menu>
</div> </div>
<div class="luckyBox" > <div class="luckyBox" v-show="this.activeItemCoin.value != 'enx' && this.activeItemCoin.value != 'alph'">
<div class="luckyItem"> <div class="luckyItem">
<span class="title">{{$t(`home.lucky3`)}}</span> <span class="title">{{$t(`home.lucky3`)}}</span>
@ -125,7 +125,8 @@
</el-col> </el-col>
<!-- v-loading = "LuckDataLoading" --> <!-- v-loading = "LuckDataLoading" -->
<el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12"> <el-col :xs="24" :sm="24" :md="24" :lg="12" :xl="12">
<div class="luckyBox" >
<div class="luckyBox" v-show="this.activeItemCoin.value != 'enx' && this.activeItemCoin.value != 'alph'" >
<div class="luckyItem"> <div class="luckyItem">
<span class="title">{{$t(`home.lucky3`)}}</span> <span class="title">{{$t(`home.lucky3`)}}</span>

View File

@ -247,7 +247,7 @@
import { getResetPwd, getResetPwdCode } from "../../api/login"; import { getResetPwd, getResetPwdCode } from "../../api/login";
import { encryption } from "../../utils/fun"; import { encryption } from "../../utils/fun";
import { getEmailIfBind } from "../../api/personalCenter"; import { getEmailIfBind } from "../../api/personalCenter";
import { Debounce,throttle }from "../../utils/publicMethods";
export default { export default {
data() { data() {
return { return {
@ -419,6 +419,10 @@ export default {
type: "success", type: "success",
showClose: true, showClose: true,
}); });
for (const key in this.loginForm) {//
this.loginForm[key] = "";
}
this.$router.push(`/${this.lang}/login`); this.$router.push(`/${this.lang}/login`);
} }
}, },
@ -521,7 +525,8 @@ export default {
} }
}); });
}, },
submitForm() {
submitForm:Debounce(function(){
this.$refs.ruleForm.validate((valid) => { this.$refs.ruleForm.validate((valid) => {
if (valid) { if (valid) {
// //
@ -596,7 +601,83 @@ export default {
this.fetchResetPwd(form); 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() { handleClick() {
this.$router.push(`/${this.lang}`); this.$router.push(`/${this.lang}`);
}, },

View File

@ -1,153 +1,397 @@
<template> <template>
<div style="width: 1300px; height: 600px"> <div>
<div id="chart" style="width: 100%; height: 100%; min-width: 500px"></div> <h1>{{ msg }}</h1>
<!-- 用户ID输入部分 -->
<div class="user-input-container">
<div class="input-group" :class="{ 'disabled': isConnected }">
<input
v-model="email"
placeholder="请输入您的用户邮箱"
:disabled="isConnected"
/>
<input
v-model="targetEmail"
placeholder="请输入目标用户邮箱"
:disabled="isConnected"
@keyup.enter="!isConnected && !isConnecting && connectWebSocket()"
/>
</div>
<div class="button-group">
<button
@click="connectWebSocket"
:disabled="isConnected || isConnecting || !email || !targetEmail"
:class="{ 'disabled': isConnected || isConnecting || !email || !targetEmail }"
>
{{ isConnecting ? '连接中...' : '连接' }}
</button>
<button
v-if="isConnected"
@click="disconnectWebSocket"
class="disconnect-btn"
>
断开连接
</button>
</div>
</div>
<div v-if="connectionError" class="error-message">
{{ connectionError }}
</div>
<div v-if="isConnected">
<!-- WebSocket 聊天部分 -->
<div class="chat-container">
<div class="message-list" ref="messageList">
<div v-for="(msg, index) in receivedMessages" :key="index" class="message"
:class="{ 'error-message': msg.error }">
<div v-if="typeof msg === 'string'">{{ msg }}</div>
<div v-else>
<div class="message-header">
<span class="message-sender">{{ msg.sender || 'Unknown' }}</span>
<span class="message-time">{{ formatTime(msg.timestamp) }}</span>
</div>
<div class="message-content">{{ msg.content }}</div>
</div>
</div>
</div>
<div class="input-container">
<textarea
v-model="message"
@keyup.enter="sendMessage"
placeholder="输入消息..."
rows="3"
></textarea>
<button @click="sendMessage" :disabled="!message.trim()">
发送
</button>
</div>
</div>
</div>
</div> </div>
</template> </template>
<script> <script setup>
import * as echarts from "echarts"; import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue'
export default { import { Stomp } from '@stomp/stompjs'
data() {
return {
countDownTime: 60,
timer: null,
};
},
mounted() { const props = defineProps({
let base = +new Date(1968, 9, 3); msg: {
let oneDay = 24 * 3600 * 1000; type: String,
let date = []; required: true
let data = [Math.random() * 300]; }
for (let i = 1; i < 20000; i++) { })
var now = new Date((base += oneDay));
date.push([now.getFullYear(), now.getMonth() + 1, now.getDate()].join("/")); const message = ref('')
data.push(Math.round((Math.random() - 0.5) * 20 + data[i - 1])); const receivedMessages = ref([])
const email = ref('')
const targetEmail = ref('')
const isConnected = ref(false)
const isConnecting = ref(false)
const connectionError = ref('')
const messageList = ref(null)
// stompClient
const stompClient = ref(null)
//
const connectTime = ref(null)
//
const scrollToBottom = async () => {
await nextTick()
if (messageList.value) {
messageList.value.scrollTop = messageList.value.scrollHeight
}
}
//
watch(receivedMessages, () => {
scrollToBottom()
})
//
const formatTime = (timestamp) => {
if (!timestamp) return ''
try {
const date = new Date(timestamp)
return date.toLocaleTimeString()
} catch (e) {
return ''
}
}
// WebSocket
const connectWebSocket = () => {
if (!email.value || !targetEmail.value) {
connectionError.value = '请输入用户邮箱和目标用户邮箱'
return
}
connectionError.value = ''
isConnecting.value = true
connectTime.value = new Date() //
try {
stompClient.value = Stomp.client('ws://localhost:8101/chat/ws')
stompClient.value.heartbeat.outgoing = 20000
stompClient.value.heartbeat.incoming = 0
const connectHeaders = {
'email': email.value,
'type': 2 //0 1 2
} }
// 10 stompClient.value.connect(
for (let i = 0; i < 100; i++) { connectHeaders,
date.unshift(null); function(frame) {
data.unshift(null); console.log('连接成功: ' + frame)
} console.log('连接时间:', connectTime.value?.toLocaleString())
for (let i = 0; i < 100; i++) { isConnected.value = true
date.push(null); isConnecting.value = false
data.push(null);
} //
receivedMessages.value.push({
var option = { sender: 'System',
tooltip: { content: '已连接到聊天服务器',
trigger: "axis", timestamp: new Date().toISOString(),
position: function (pt) { system: true
return [pt[0], "10%"]; })
},
//
stompClient.value.subscribe(`/user/queue/${email.value}`, function(message) {
console.log('收到消息:', message.body)
try {
const parsedMessage = JSON.parse(message.body)
receivedMessages.value.push(parsedMessage)
} catch (error) {
console.error('消息解析失败:', error)
receivedMessages.value.push({
sender: 'System',
content: `消息格式错误: ${message.body}`,
timestamp: new Date().toISOString(),
error: true
})
}
})
}, },
title: { function(error) {
left: "center", console.error('连接失败:', error)
text: "Large Area Chart", isConnected.value = false
}, isConnecting.value = false
toolbox: { connectionError.value = `连接失败: ${error.headers?.message || error.message || '未知错误'}`
feature: {
dataZoom: {
yAxisIndex: "none",
},
restore: {},
saveAsImage: {},
},
},
xAxis: {
type: "category",
// boundaryGap: [0.5, 0.5],
data: date,
},
yAxis: [
{
type: "value",
boundaryGap: [0, "100%"],
axisLine: {
show: true, // Y 线
},
},
{
position: "right",
type: "value",
boundaryGap: [0, "100%"],
},
],
dataZoom: [
{
type: "inside",
start: 0,
end: 10,
},
{
start: 0,
end: 10,
},
],
series: [
{
name: "Fake Data",
type: "line",
symbol: "none",
sampling: "lttb",
itemStyle: {
color: "rgb(255, 70, 131)",
},
data: data,
},
],
};
this.myChart = echarts.init(document.getElementById("chart"));
this.myChart.setOption(option);
window.addEventListener(
"resize", () => {
if (this.myChart) this.myChart.resize();
} }
); )
}, } catch (error) {
methods: { console.error('初始化WebSocket客户端失败:', error)
// isConnected.value = false
inCharts() { isConnecting.value = false
if (this.myChart == null) { connectionError.value = `初始化失败: ${error.message || '未知错误'}`
this.myChart = echarts.init(document.getElementById("chart")); }
}
//
const disconnectWebSocket = () => {
if (stompClient.value?.connected) {
stompClient.value.disconnect(() => {
const disconnectTime = new Date()
const duration = connectTime.value ?
Math.floor((disconnectTime.getTime() - connectTime.value.getTime()) / 1000) : 0
console.log('断开连接时间:', disconnectTime.toLocaleString())
console.log(`连接持续时间: ${Math.floor(duration / 60)}${duration % 60}`)
isConnected.value = false
receivedMessages.value = []
connectTime.value = null
//
connectionError.value = '已断开连接'
setTimeout(() => {
connectionError.value = ''
}, 3000)
})
}
}
//
const sendMessage = () => {
if (!message.value.trim()) return
if (stompClient.value?.connected) {
try {
const messageObj = {
email: targetEmail.value,
content: message.value.trim()
} }
stompClient.value.send(
`/point/send/message`,
{},
JSON.stringify(messageObj)
)
//
receivedMessages.value.push({
sender: email.value,
content: message.value.trim(),
timestamp: new Date().toISOString(),
isSelf: true
})
message.value = ''
} catch (error) {
console.error('发送消息失败:', error)
receivedMessages.value.push({
sender: 'System',
content: `发送失败: ${error.message || '未知错误'}`,
timestamp: new Date().toISOString(),
error: true
})
}
} else {
connectionError.value = '连接已断开,请重新连接'
isConnected.value = false
}
}
this.option.series[0].name = this.$t(`home.computingPower`); //
this.option.series[1].name = this.$t(`home.currencyPrice`); onUnmounted(() => {
if (stompClient.value?.connected) {
stompClient.value.disconnect()
}
})
</script>
this.myChart.setOption(this.option); <style scoped>
// .read-the-docs {
this.myChart.on("finished", () => { color: #888;
// }
// console.log('');
this.minerChartLoading = false;
});
// window.addEventListener("resize", () => {
// if (this.myChart) this.myChart.resize();
// });
window.addEventListener( .user-input-container {
"resize", max-width: 400px;
throttle(() => { margin: 20px auto;
if (this.myChart) this.myChart.resize(); }
}, 200)
); .input-group {
}, display: flex;
startCountDown() { gap: 10px;
this.timer = setInterval(() => { margin-bottom: 10px;
if (this.countDownTime <= 0) { }
//countDownTime0,sessionStorage,
clearInterval(this.timer); .chat-container {
sessionStorage.removeItem("exam_time"); max-width: 600px;
alert("提交试卷"); margin: 20px auto;
} else if (this.countDownTime > 0) { padding: 20px;
//countDownTime -1,sessionStorage border: 1px solid #ddd;
this.countDownTime--; border-radius: 8px;
window.sessionStorage.setItem("exam_time", this.countDownTime); box-shadow: 0 2px 6px rgba(0,0,0,0.1);
} }
}, 1000);
}, .message-list {
}, height: 300px;
}; overflow-y: auto;
</script> border: 1px solid #eee;
padding: 10px;
margin-bottom: 10px;
border-radius: 4px;
}
.message {
margin: 8px 0;
padding: 10px;
background-color: #f5f5f5;
border-radius: 8px;
max-width: 80%;
}
.message-header {
display: flex;
justify-content: space-between;
font-size: 0.8em;
margin-bottom: 4px;
}
.message-sender {
font-weight: bold;
color: #333;
}
.message-time {
color: #999;
}
.message-content {
word-break: break-word;
}
.message[class*="isSelf"] {
background-color: #dcf8c6;
margin-left: auto;
}
.error-message {
background-color: #ffebee;
color: #d32f2f;
padding: 8px;
border-radius: 4px;
margin: 10px 0;
text-align: center;
}
.input-container {
display: flex;
gap: 10px;
}
input, textarea {
flex: 1;
padding: 10px;
border: 1px solid #ddd;
border-radius: 4px;
font-family: inherit;
resize: none;
}
button {
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover:not(:disabled) {
background-color: #45a049;
}
button:disabled {
opacity: 0.6;
cursor: not-allowed;
}
.button-group {
display: flex;
gap: 10px;
}
.disabled {
opacity: 0.6;
cursor: not-allowed;
}
.disconnect-btn {
background-color: #dc3545;
}
.disconnect-btn:hover {
background-color: #c82333;
}
input:disabled, textarea:disabled {
background-color: #f5f5f5;
cursor: not-allowed;
}
</style>

View File

@ -103,11 +103,21 @@ export default {
data: this.FormDatas, data: this.FormDatas,
}).then(res => { }).then(res => {
console.log(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 this.ruleForm.files = res.data.data.id
if (this.ruleForm.files) {//成功拿到返回ID if (this.ruleForm.files) {//成功拿到返回ID
this.fetchSubmitWork(this.ruleForm) this.fetchSubmitWork(this.ruleForm)
} }
}) })
} else { } else {

View File

@ -24,8 +24,8 @@
show-word-limit show-word-limit
v-model="ruleForm.desc"> v-model="ruleForm.desc">
</el-input> </el-input>
</el-form-item> </el-form-item>
<el-form-item style="width: 100%;"> <el-form-item style="width: 100%;">
<div style="width: 100%;font-weight: 600;color: rgba(0,0,0,0.7);">{{ $t(`work.enclosure`) }}</div> <div style="width: 100%;font-weight: 600;color: rgba(0,0,0,0.7);">{{ $t(`work.enclosure`) }}</div>

View File

@ -167,6 +167,7 @@ export default {
this.workOrderId = localStorage.getItem("workOrderId") this.workOrderId = localStorage.getItem("workOrderId")
if (this.workOrderId) { if (this.workOrderId) {
this.fetchTicketDetails({ id: this.workOrderId }) this.fetchTicketDetails({ id: this.workOrderId })
this.registerRecoveryMethod('fetchTicketDetails', { id: this.workOrderId });
} }
// this.faultList = JSON.parse(localStorage.getItem('faultList') ) // this.faultList = JSON.parse(localStorage.getItem('faultList') )

View File

@ -198,18 +198,22 @@ export default {
case `all`: case `all`:
this.params.status = 0 this.params.status = 0
this.fetchRechargeRecord0(this.params) this.fetchRechargeRecord0(this.params)
this.registerRecoveryMethod('fetchRechargeRecord0', this.params);
break; break;
case `pending`: case `pending`:
this.params.status = 2 this.params.status = 2
this.fetchRechargeRecord2(this.params) this.fetchRechargeRecord2(this.params)
this.registerRecoveryMethod('fetchRechargeRecord2', this.params);
break; break;
case `Finished`: case `Finished`:
this.params.status = 10 this.params.status = 10
this.fetchRechargeRecord10(this.params) this.fetchRechargeRecord10(this.params)
this.registerRecoveryMethod('fetchRechargeRecord10', this.params);
break; break;
case `pendingProcessing`: case `pendingProcessing`:
this.params.status = 1 this.params.status = 1
this.fetchRechargeRecord1(this.params) this.fetchRechargeRecord1(this.params)
this.registerRecoveryMethod('fetchRechargeRecord1', this.params);
break; break;
default: default:

View File

@ -95,14 +95,17 @@ export default {
case "pending": case "pending":
this.params.status = 1 this.params.status = 1
this.fetchPrivateConsume1(this.params) this.fetchPrivateConsume1(this.params)
this.registerRecoveryMethod('fetchPrivateConsume1', this.params);
break; break;
case "success"://已完成工单 case "success"://已完成工单
this.params.status = 2 this.params.status = 2
this.fetchPrivateConsume2(this.params) this.fetchPrivateConsume2(this.params)
this.registerRecoveryMethod('fetchPrivateConsume2', this.params);
break; break;
case "reply"://全部工单 case "reply"://全部工单
this.params.status = 0 this.params.status = 0
this.fetchPrivateConsume0(this.params) this.fetchPrivateConsume0(this.params)
this.registerRecoveryMethod('fetchPrivateConsume0', this.params);
break; break;
default: default:

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Binary file not shown.

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More