后台系统: 用户详情页面添加钱包余额显示、用户管理优化记录用户选择币种、 新增广播增加查看按钮内容、及对应跳转对应页面路径 增加帮助中心页面、公告中心页面 完成
This commit is contained in:
parent
c6f765f858
commit
a0ebd8254a
Binary file not shown.
|
@ -0,0 +1,33 @@
|
||||||
|
import request from '../utils/request'
|
||||||
|
|
||||||
|
|
||||||
|
//新增文档页面
|
||||||
|
export function addDocument(data) {
|
||||||
|
return request({
|
||||||
|
url: `manage/documents/addDocument`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//业务系统获取对应文档列表
|
||||||
|
export function documentsList(data) {
|
||||||
|
return request({
|
||||||
|
url: `manage/documents/findCatalogueDocumentList`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//从列表获取文档详情
|
||||||
|
export function findDataInfo(data) {
|
||||||
|
return request({
|
||||||
|
url: `manage/documents/findDataInfo`,
|
||||||
|
method: 'post',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -203,6 +203,7 @@
|
||||||
:disabled="connectionStatus !== 'connected'"
|
:disabled="connectionStatus !== 'connected'"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- :disabled="connectionStatus !== 'connected'" -->
|
||||||
<div
|
<div
|
||||||
class="chat-input-wrapper"
|
class="chat-input-wrapper"
|
||||||
style="display: flex; align-items: center"
|
style="display: flex; align-items: center"
|
||||||
|
@ -215,7 +216,7 @@
|
||||||
@input="handleInputMessage"
|
@input="handleInputMessage"
|
||||||
@keydown.enter="handleEnterKey"
|
@keydown.enter="handleEnterKey"
|
||||||
:placeholder="$t('chat.inputPlaceholder') || '请输入您的问题...'"
|
:placeholder="$t('chat.inputPlaceholder') || '请输入您的问题...'"
|
||||||
:disabled="connectionStatus !== 'connected'"
|
|
||||||
/>
|
/>
|
||||||
<!-- <span class="input-counter">{{ maxMessageLength - inputMessage.length }}</span> -->
|
<!-- <span class="input-counter">{{ maxMessageLength - inputMessage.length }}</span> -->
|
||||||
</div>
|
</div>
|
||||||
|
@ -309,11 +310,12 @@ export default {
|
||||||
lastErrorTime: 0, // 最后一次错误时间
|
lastErrorTime: 0, // 最后一次错误时间
|
||||||
lastConnectedEmail: null, // 最后连接的用户email,用于防止重复连接
|
lastConnectedEmail: null, // 最后连接的用户email,用于防止重复连接
|
||||||
userViewHistory: false, // 是否在查看历史消息
|
userViewHistory: false, // 是否在查看历史消息
|
||||||
customerIsOnline: true, // 保存客服在线状态
|
customerIsOnline: false, // 保存客服在线状态
|
||||||
|
|
||||||
jurisdiction: {
|
jurisdiction: {
|
||||||
roleKey: "",
|
roleKey: "",
|
||||||
},
|
},
|
||||||
|
token: "",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -375,7 +377,18 @@ export default {
|
||||||
// === 移除 markMessagesAsRead 调用,防止切换窗口时误清未读 ===
|
// === 移除 markMessagesAsRead 调用,防止切换窗口时误清未读 ===
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
token(val) {
|
||||||
|
console.log("token变化打印",val);
|
||||||
|
|
||||||
|
if (!val) {
|
||||||
|
this.forceDisconnectAll()
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.determineUserType()
|
||||||
|
})
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
let jurisdiction = localStorage.getItem("jurisdiction");
|
let jurisdiction = localStorage.getItem("jurisdiction");
|
||||||
|
@ -410,6 +423,11 @@ export default {
|
||||||
}
|
}
|
||||||
this.jurisdiction = jurisdiction;
|
this.jurisdiction = jurisdiction;
|
||||||
|
|
||||||
|
let customerIsOnline = localStorage.getItem("customerIsOnline");
|
||||||
|
this.customerIsOnline = JSON.parse(customerIsOnline);
|
||||||
|
let token = localStorage.getItem("token");
|
||||||
|
this.token = JSON.parse(token);
|
||||||
|
|
||||||
window.addEventListener("setItem", () => {
|
window.addEventListener("setItem", () => {
|
||||||
let jurisdiction = localStorage.getItem("jurisdiction");
|
let jurisdiction = localStorage.getItem("jurisdiction");
|
||||||
try {
|
try {
|
||||||
|
@ -420,6 +438,12 @@ export default {
|
||||||
jurisdiction = { roleKey: "" };
|
jurisdiction = { roleKey: "" };
|
||||||
}
|
}
|
||||||
this.jurisdiction = jurisdiction;
|
this.jurisdiction = jurisdiction;
|
||||||
|
|
||||||
|
|
||||||
|
let customerIsOnline = localStorage.getItem("customerIsOnline");
|
||||||
|
this.customerIsOnline = JSON.parse(customerIsOnline);
|
||||||
|
let token = localStorage.getItem("token");
|
||||||
|
this.token = JSON.parse(token);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加页面卸载事件监听
|
// 添加页面卸载事件监听
|
||||||
|
@ -542,6 +566,7 @@ export default {
|
||||||
this.jurisdiction.roleKey =="customer_service" ||
|
this.jurisdiction.roleKey =="customer_service" ||
|
||||||
this.jurisdiction.roleKey == "admin" ||
|
this.jurisdiction.roleKey == "admin" ||
|
||||||
this.jurisdiction.roleKey == "back_admin"
|
this.jurisdiction.roleKey == "back_admin"
|
||||||
|
|
||||||
) return;
|
) return;
|
||||||
// 获取用户ID和未读消息数
|
// 获取用户ID和未读消息数
|
||||||
const userData = await this.fetchUserid({ email: this.userEmail });
|
const userData = await this.fetchUserid({ email: this.userEmail });
|
||||||
|
@ -586,6 +611,9 @@ export default {
|
||||||
|
|
||||||
// 确定用户类型和邮箱
|
// 确定用户类型和邮箱
|
||||||
async determineUserType() {
|
async determineUserType() {
|
||||||
|
|
||||||
|
console.log("token",this.token);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
// console.log("token", token);
|
// console.log("token", token);
|
||||||
|
@ -638,7 +666,7 @@ export default {
|
||||||
|
|
||||||
this.userEmail = email;
|
this.userEmail = email;
|
||||||
|
|
||||||
if (userInfo.roleKey === "customer_service") {
|
if (userInfo.roleKey === "customer_service" || userInfo.roleKey === "admin") {
|
||||||
// 客服用户
|
// 客服用户
|
||||||
this.userType = 2;
|
this.userType = 2;
|
||||||
this.userEmail = "";
|
this.userEmail = "";
|
||||||
|
@ -1061,7 +1089,7 @@ export default {
|
||||||
|
|
||||||
// console.log("🔌 处理连接断开...");
|
// console.log("🔌 处理连接断开...");
|
||||||
|
|
||||||
// === 如果用户已经打开聊天窗口,确保保持打开状态 ===
|
// === 移除自动保持对话框打开的逻辑,只有用户主动打开才保持打开 ===
|
||||||
// if (this.isChatOpen) {
|
// if (this.isChatOpen) {
|
||||||
// console.log("📱 聊天窗口已打开,保持打开状态");
|
// console.log("📱 聊天窗口已打开,保持打开状态");
|
||||||
// // 不改变 isChatOpen 和 isMinimized 状态
|
// // 不改变 isChatOpen 和 isMinimized 状态
|
||||||
|
@ -1098,7 +1126,7 @@ export default {
|
||||||
// `🔄 尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`
|
// `🔄 尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`
|
||||||
// );
|
// );
|
||||||
|
|
||||||
// === 在自动重连期间,如果聊天窗口打开,显示连接中状态 ===
|
// === 在自动重连期间,如果聊天窗口已经打开,才显示连接中状态 ===
|
||||||
if (this.isChatOpen) {
|
if (this.isChatOpen) {
|
||||||
this.connectionStatus = "connecting";
|
this.connectionStatus = "connecting";
|
||||||
}
|
}
|
||||||
|
@ -1208,6 +1236,11 @@ export default {
|
||||||
},
|
},
|
||||||
// 发送消息
|
// 发送消息
|
||||||
sendMessage() {
|
sendMessage() {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 网络断开时阻止发送消息并提示
|
// 网络断开时阻止发送消息并提示
|
||||||
if (this.networkStatus !== "online") {
|
if (this.networkStatus !== "online") {
|
||||||
this.$message({
|
this.$message({
|
||||||
|
@ -1631,21 +1664,16 @@ export default {
|
||||||
// 修改 fetchUserid 方法,添加 token 检查
|
// 修改 fetchUserid 方法,添加 token 检查
|
||||||
async fetchUserid(params) {
|
async fetchUserid(params) {
|
||||||
try {
|
try {
|
||||||
// 先检查是否有 token
|
|
||||||
// const token = localStorage.getItem("token");
|
|
||||||
// if (!token) {
|
|
||||||
// console.log("用户未登录,不发起 getUserid 请求");
|
|
||||||
// // 对于未登录用户,可以生成一个临时 ID
|
|
||||||
// this.roomId = `guest_${Date.now()}`;
|
|
||||||
// this.receivingEmail = "customer_service@example.com"; // 或默认客服邮箱
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const res = await getUserid(params);
|
const res = await getUserid(params);
|
||||||
if (res && res.code == 200) {
|
if (res && res.code == 200) {
|
||||||
// console.log("获取用户ID成功:", res);
|
// console.log("获取用户ID成功:", res);
|
||||||
this.receivingEmail = res.data.userEmail;
|
this.receivingEmail = res.data.userEmail;
|
||||||
this.roomId = res.data.id;
|
this.roomId = res.data.id;
|
||||||
|
this.customerIsOnline = res.data.customerIsOnline;
|
||||||
|
localStorage.setItem(`customerIsOnline`,JSON.stringify(this.customerIsOnline))
|
||||||
|
|
||||||
return res.data;
|
return res.data;
|
||||||
} else {
|
} else {
|
||||||
console.warn("获取用户ID未返回有效数据");
|
console.warn("获取用户ID未返回有效数据");
|
||||||
|
@ -1991,7 +2019,7 @@ export default {
|
||||||
// connectionStatus: this.connectionStatus,
|
// connectionStatus: this.connectionStatus,
|
||||||
// isWebSocketConnected: this.isWebSocketConnected,
|
// isWebSocketConnected: this.isWebSocketConnected,
|
||||||
// });
|
// });
|
||||||
|
this.fetchUserid({ email: this.userEmail })
|
||||||
const wasOpen = this.isChatOpen;
|
const wasOpen = this.isChatOpen;
|
||||||
this.isChatOpen = !this.isChatOpen;
|
this.isChatOpen = !this.isChatOpen;
|
||||||
|
|
||||||
|
@ -2455,14 +2483,14 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 优化:重试连接按钮处理 - 保持对话框打开,直接重连
|
* 优化:重试连接按钮处理 - 直接重连,不自动打开对话框
|
||||||
*/
|
*/
|
||||||
async handleRetryConnect() {
|
async handleRetryConnect() {
|
||||||
try {
|
try {
|
||||||
// console.log("🔄 用户点击重试连接...");
|
// console.log("🔄 用户点击重试连接...");
|
||||||
|
|
||||||
// === 多窗口切换:抢占活跃权 ===
|
// === 多窗口切换:抢占活跃权 ===
|
||||||
this.setWindowActive();
|
// this.setWindowActive(); // 移除此方法调用,因为setWindowActive方法在代码中没有定义
|
||||||
|
|
||||||
// === 重置连接状态,立即显示连接中 ===
|
// === 重置连接状态,立即显示连接中 ===
|
||||||
this.connectionStatus = "connecting";
|
this.connectionStatus = "connecting";
|
||||||
|
@ -2730,9 +2758,9 @@ export default {
|
||||||
this.isReconnecting = false;
|
this.isReconnecting = false;
|
||||||
this.showRefreshButton = false; // 不显示刷新按钮,用户只需关闭多余窗口
|
this.showRefreshButton = false; // 不显示刷新按钮,用户只需关闭多余窗口
|
||||||
|
|
||||||
// === 确保聊天对话框保持打开状态 ===
|
// === 移除自动打开对话框的逻辑,只有用户主动点击才打开 ===
|
||||||
this.isChatOpen = true;
|
// this.isChatOpen = true;
|
||||||
this.isMinimized = false;
|
// this.isMinimized = false;
|
||||||
|
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
// console.log("🔥 连接数上限错误处理完成,提示用户关闭多余窗口");
|
// console.log("🔥 连接数上限错误处理完成,提示用户关闭多余窗口");
|
||||||
|
@ -3042,9 +3070,9 @@ export default {
|
||||||
this.isHandlingError = true;
|
this.isHandlingError = true;
|
||||||
this.lastErrorTime = now;
|
this.lastErrorTime = now;
|
||||||
|
|
||||||
// === 确保聊天对话框保持打开状态 ===
|
// === 移除自动打开对话框的逻辑,只有用户主动点击才打开 ===
|
||||||
this.isChatOpen = true;
|
// this.isChatOpen = true;
|
||||||
this.isMinimized = false;
|
// this.isMinimized = false;
|
||||||
|
|
||||||
// 清除验证定时器
|
// 清除验证定时器
|
||||||
this.clearConnectionVerification();
|
this.clearConnectionVerification();
|
||||||
|
@ -3073,9 +3101,9 @@ export default {
|
||||||
// console.error("❌ 重新连接失败:", error);
|
// console.error("❌ 重新连接失败:", error);
|
||||||
this.isHandlingError = false;
|
this.isHandlingError = false;
|
||||||
|
|
||||||
// === 连接失败时确保对话框仍然打开 ===
|
// === 移除自动打开对话框的逻辑,只有用户主动点击才打开 ===
|
||||||
this.isChatOpen = true;
|
// this.isChatOpen = true;
|
||||||
this.isMinimized = false;
|
// this.isMinimized = false;
|
||||||
this.connectionStatus = "error";
|
this.connectionStatus = "error";
|
||||||
this.showRefreshButton = true;
|
this.showRefreshButton = true;
|
||||||
});
|
});
|
||||||
|
@ -3098,9 +3126,9 @@ export default {
|
||||||
this.isHandlingError = true;
|
this.isHandlingError = true;
|
||||||
this.lastErrorTime = now;
|
this.lastErrorTime = now;
|
||||||
|
|
||||||
// === 确保聊天对话框保持打开状态 ===
|
// === 移除自动打开对话框的逻辑,只有用户主动点击才打开 ===
|
||||||
this.isChatOpen = true;
|
// this.isChatOpen = true;
|
||||||
this.isMinimized = false;
|
// this.isMinimized = false;
|
||||||
|
|
||||||
// === 增加重连次数并检查限制 ===
|
// === 增加重连次数并检查限制 ===
|
||||||
this.reconnectAttempts++;
|
this.reconnectAttempts++;
|
||||||
|
@ -3138,9 +3166,9 @@ export default {
|
||||||
console.error("❌ 超时重连失败:", error);
|
console.error("❌ 超时重连失败:", error);
|
||||||
this.isHandlingError = false;
|
this.isHandlingError = false;
|
||||||
|
|
||||||
// === 重连失败时确保对话框仍然打开 ===
|
// === 移除自动打开对话框的逻辑,只有用户主动点击才打开 ===
|
||||||
this.isChatOpen = true;
|
// this.isChatOpen = true;
|
||||||
this.isMinimized = false;
|
// this.isMinimized = false;
|
||||||
this.connectionStatus = "error";
|
this.connectionStatus = "error";
|
||||||
this.connectionError =
|
this.connectionError =
|
||||||
this.$t("chat.reconnectFailed") || "重连失败,请稍后重试";
|
this.$t("chat.reconnectFailed") || "重连失败,请稍后重试";
|
||||||
|
@ -3159,9 +3187,9 @@ export default {
|
||||||
console.error("❌ 强制断开连接失败:", error);
|
console.error("❌ 强制断开连接失败:", error);
|
||||||
this.isHandlingError = false;
|
this.isHandlingError = false;
|
||||||
|
|
||||||
// === 处理失败时确保对话框仍然打开 ===
|
// === 移除自动打开对话框的逻辑,只有用户主动点击才打开 ===
|
||||||
this.isChatOpen = true;
|
// this.isChatOpen = true;
|
||||||
this.isMinimized = false;
|
// this.isMinimized = false;
|
||||||
this.connectionStatus = "error";
|
this.connectionStatus = "error";
|
||||||
this.connectionError =
|
this.connectionError =
|
||||||
this.$t("chat.connectionFailed") || "连接处理失败,请稍后重试";
|
this.$t("chat.connectionFailed") || "连接处理失败,请稍后重试";
|
||||||
|
|
|
@ -0,0 +1,724 @@
|
||||||
|
<template>
|
||||||
|
<div class="horizontal-broadcast-container" v-if="shouldShowBroadcast">
|
||||||
|
<!-- 广播标题区域 -->
|
||||||
|
<div class="broadcast-header" v-if="showTitle">
|
||||||
|
<i class="iconfont icon-tishishuoming" v-if="showIcon"></i>
|
||||||
|
<span class="broadcast-title">{{ $t('home.describeTitle') }}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 横向滚动区域 -->
|
||||||
|
<div
|
||||||
|
class="horizontal-scroll-wrapper"
|
||||||
|
:class="{ 'is-hovering': isHovering, 'full-width': !showTitle }"
|
||||||
|
@mouseenter="stopHorizontalScroll"
|
||||||
|
@mouseleave="resumeHorizontalScroll"
|
||||||
|
@touchstart="stopHorizontalScroll"
|
||||||
|
@touchend="startHorizontalScrollDelayed"
|
||||||
|
:title="isHovering ? $t(`backendSystem.broadcastPause`) || '广播已暂停,移开鼠标继续滚动' : $t(`backendSystem.broadcastResume`) || '鼠标悬停可暂停滚动'"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="horizontal-scroll-content"
|
||||||
|
:style="horizontalScrollStyle"
|
||||||
|
ref="horizontalScrollContent"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in broadcastListForHorizontal"
|
||||||
|
:key="item.id + '-horizontal-' + index"
|
||||||
|
class="horizontal-broadcast-item"
|
||||||
|
@click="handleItemClick(item)"
|
||||||
|
>
|
||||||
|
<span class="horizontal-item-content">{{ item.content }}</span>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 动态渲染多个按钮 -->
|
||||||
|
<div class="button-group" v-if="item.buttonContent && item.buttonContent.length > 0">
|
||||||
|
<span
|
||||||
|
v-for="(buttonText, buttonIndex) in item.buttonContent"
|
||||||
|
:key="`button-${item.id}-${buttonIndex}`"
|
||||||
|
class="view"
|
||||||
|
@click.stop="handelJump(item.buttonPath[buttonIndex])"
|
||||||
|
>
|
||||||
|
{{ buttonText || $t(`home.view`) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span class="horizontal-item-separator" v-if="index < broadcastListForHorizontal.length - 1">•</span>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getBroadcast } from '../api/broadcast'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'HorizontalBroadcast',
|
||||||
|
props: {
|
||||||
|
// 广播数据,如果不传则自动获取
|
||||||
|
broadcastData: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
// 是否显示标题
|
||||||
|
showTitle: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 是否显示图标
|
||||||
|
showIcon: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 自定义标题
|
||||||
|
title: {
|
||||||
|
type: String,
|
||||||
|
default: ''
|
||||||
|
},
|
||||||
|
// 滚动速度 (px/step)
|
||||||
|
scrollSpeed: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
// 滚动间隔 (ms)
|
||||||
|
scrollInterval: {
|
||||||
|
type: Number,
|
||||||
|
default: 50
|
||||||
|
},
|
||||||
|
// 是否自动获取数据
|
||||||
|
autoFetch: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
// 获取数据的语言参数
|
||||||
|
lang: {
|
||||||
|
type: String,
|
||||||
|
default: 'zh'
|
||||||
|
},
|
||||||
|
// 最小显示条数(少于此数不显示组件)
|
||||||
|
minItems: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
// 组件高度
|
||||||
|
height: {
|
||||||
|
type: String,
|
||||||
|
default: '40px'
|
||||||
|
},
|
||||||
|
// 是否启用点击事件
|
||||||
|
clickable: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 内部广播数据
|
||||||
|
internalBroadcastList: [],
|
||||||
|
// 横向滚动相关
|
||||||
|
horizontalScrollTimer: null,
|
||||||
|
horizontalDelayTimer: null,
|
||||||
|
horizontalScrollOffset: 0,
|
||||||
|
isHorizontalScrolling: true,
|
||||||
|
// hover状态控制
|
||||||
|
isHovering: false,
|
||||||
|
wasScrollingBeforeHover: true,
|
||||||
|
// 组件状态
|
||||||
|
isLoading: false,
|
||||||
|
hasError: false,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
// 最终使用的广播数据
|
||||||
|
finalBroadcastList() {
|
||||||
|
let sourceData = this.broadcastData.length > 0
|
||||||
|
? this.broadcastData
|
||||||
|
: this.internalBroadcastList;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 统一处理数据格式转换
|
||||||
|
const processedData = sourceData.map(item => {
|
||||||
|
const processedItem = { ...item };
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 处理 buttonContent:如果是字符串则分割为数组
|
||||||
|
if (typeof processedItem.buttonContent === 'string' && processedItem.buttonContent.trim()) {
|
||||||
|
processedItem.buttonContent = processedItem.buttonContent
|
||||||
|
.split(/[,,]\s*/) // 支持中英文逗号,后面可能跟空格
|
||||||
|
.map(btn => btn.trim())
|
||||||
|
.filter(btn => btn); // 过滤空字符串
|
||||||
|
} else if (!Array.isArray(processedItem.buttonContent)) {
|
||||||
|
processedItem.buttonContent = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 buttonPath:如果是字符串则分割为数组
|
||||||
|
if (typeof processedItem.buttonPath === 'string' && processedItem.buttonPath.trim()) {
|
||||||
|
processedItem.buttonPath = processedItem.buttonPath
|
||||||
|
.split(/[,,]\s*/) // 支持中英文逗号,后面可能跟空格
|
||||||
|
.map(path => path.trim())
|
||||||
|
.filter(path => path); // 过滤空字符串
|
||||||
|
} else if (!Array.isArray(processedItem.buttonPath)) {
|
||||||
|
processedItem.buttonPath = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只有当两个数组都有内容时才进行长度校验
|
||||||
|
if (processedItem.buttonContent.length > 0 && processedItem.buttonPath.length > 0) {
|
||||||
|
// 确保两个数组长度一致,以较短的为准
|
||||||
|
const minLength = Math.min(
|
||||||
|
processedItem.buttonContent.length,
|
||||||
|
processedItem.buttonPath.length
|
||||||
|
);
|
||||||
|
|
||||||
|
processedItem.buttonContent = processedItem.buttonContent.slice(0, minLength);
|
||||||
|
processedItem.buttonPath = processedItem.buttonPath.slice(0, minLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return processedItem;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
return processedData;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 是否应该显示广播组件
|
||||||
|
shouldShowBroadcast() {
|
||||||
|
return this.finalBroadcastList.length >= this.minItems && !this.hasError;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 横向滚动用的数据(复制数据实现无缝循环)
|
||||||
|
broadcastListForHorizontal() {
|
||||||
|
const list = this.finalBroadcastList;
|
||||||
|
|
||||||
|
|
||||||
|
if (list.length === 0) return [];
|
||||||
|
|
||||||
|
let result;
|
||||||
|
if (list.length === 1) {
|
||||||
|
// 单条数据时复制多次以实现连续滚动
|
||||||
|
result = Array(3).fill().map((_, idx) => ({
|
||||||
|
...list[0],
|
||||||
|
id: list[0].id + '-copy-' + idx
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
// 多条数据时在末尾添加第一条实现无缝循环
|
||||||
|
result = [...list, list[0]];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 横向滚动样式
|
||||||
|
horizontalScrollStyle() {
|
||||||
|
return {
|
||||||
|
transform: `translateX(-${this.horizontalScrollOffset}px)`,
|
||||||
|
transition: this.isHorizontalScrolling ? 'none' : 'transform 0.3s ease',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
// 监听外部数据变化
|
||||||
|
broadcastData: {
|
||||||
|
handler(newData) {
|
||||||
|
if (newData && newData.length > 0) {
|
||||||
|
this.resetScroll();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true
|
||||||
|
},
|
||||||
|
// 监听滚动参数变化
|
||||||
|
scrollSpeed(newSpeed) {
|
||||||
|
this.horizontalScrollSpeed = newSpeed;
|
||||||
|
},
|
||||||
|
scrollInterval(newInterval) {
|
||||||
|
this.horizontalScrollInterval = newInterval;
|
||||||
|
if (this.isHorizontalScrolling) {
|
||||||
|
this.restartScroll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
|
||||||
|
|
||||||
|
// 如果没有外部数据且允许自动获取,则获取数据
|
||||||
|
if (this.autoFetch && this.broadcastData.length === 0) {
|
||||||
|
this.fetchBroadcastData();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动滚动
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.startHorizontalScroll();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 获取广播数据
|
||||||
|
*/
|
||||||
|
async fetchBroadcastData() {
|
||||||
|
if (this.isLoading) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
this.isLoading = true;
|
||||||
|
this.hasError = false;
|
||||||
|
|
||||||
|
const data = await getBroadcast({ lang: this.lang || (this.$i18n ? this.$i18n.locale : 'zh') });
|
||||||
|
|
||||||
|
if (data && data.code === 200 && Array.isArray(data.data)) {
|
||||||
|
this.internalBroadcastList = data.data;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
this.resetScroll();
|
||||||
|
this.$emit('data-loaded', data.data);
|
||||||
|
} else {
|
||||||
|
this.hasError = true;
|
||||||
|
this.$emit('error', '获取广播数据失败');
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取广播数据失败:', error);
|
||||||
|
this.hasError = true;
|
||||||
|
this.$emit('error', error);
|
||||||
|
} finally {
|
||||||
|
this.isLoading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开始横向滚动
|
||||||
|
*/
|
||||||
|
startHorizontalScroll() {
|
||||||
|
// 如果正在hover或数据不足,不启动滚动
|
||||||
|
if (this.isHovering || this.finalBroadcastList.length <= 1) return;
|
||||||
|
|
||||||
|
if (this.horizontalScrollTimer) {
|
||||||
|
clearInterval(this.horizontalScrollTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isHorizontalScrolling = true;
|
||||||
|
this.horizontalScrollTimer = setInterval(() => {
|
||||||
|
this.horizontalScrollStep();
|
||||||
|
}, this.scrollInterval);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 停止横向滚动
|
||||||
|
*/
|
||||||
|
stopHorizontalScroll() {
|
||||||
|
this.wasScrollingBeforeHover = this.isHorizontalScrolling;
|
||||||
|
this.isHovering = true;
|
||||||
|
|
||||||
|
if (this.horizontalScrollTimer) {
|
||||||
|
clearInterval(this.horizontalScrollTimer);
|
||||||
|
this.horizontalScrollTimer = null;
|
||||||
|
}
|
||||||
|
this.isHorizontalScrolling = false;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 鼠标移开时恢复滚动
|
||||||
|
*/
|
||||||
|
resumeHorizontalScroll() {
|
||||||
|
this.isHovering = false;
|
||||||
|
|
||||||
|
if (this.wasScrollingBeforeHover && this.finalBroadcastList.length > 1) {
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.isHovering) {
|
||||||
|
this.startHorizontalScroll();
|
||||||
|
}
|
||||||
|
}, 100);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
handelJump(url) {
|
||||||
|
const lang = this.$i18n.locale;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理单个路径跳转
|
||||||
|
* @param {string} path - 路径
|
||||||
|
*/
|
||||||
|
const handleSinglePath = (path) => {
|
||||||
|
if (!path || typeof path !== 'string') {
|
||||||
|
console.warn('无效的路径:', path);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清理路径
|
||||||
|
const cleanPath = path.trim();
|
||||||
|
if (!cleanPath) {
|
||||||
|
console.warn('路径为空');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let targetPath;
|
||||||
|
|
||||||
|
// 如果是主页路径
|
||||||
|
if (cleanPath === '/' || cleanPath === '') {
|
||||||
|
targetPath = `/${lang}/`;
|
||||||
|
} else {
|
||||||
|
// 其他路径:去掉开头的斜杠(如果有的话)
|
||||||
|
const pathWithoutSlash = cleanPath.startsWith('/') ? cleanPath.substring(1) : cleanPath;
|
||||||
|
targetPath = `/${lang}/${pathWithoutSlash}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('跳转路径:', targetPath);
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 在当前页面跳转
|
||||||
|
this.$router.push(targetPath);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('路由跳转失败:', error);
|
||||||
|
// 如果路由跳转失败,尝试直接跳转
|
||||||
|
window.location.href = targetPath;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理传入的路径
|
||||||
|
if (url) {
|
||||||
|
handleSinglePath(url);
|
||||||
|
} else {
|
||||||
|
console.warn('未提供跳转路径');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 延迟启动横向滚动(移动端触摸后)
|
||||||
|
*/
|
||||||
|
startHorizontalScrollDelayed() {
|
||||||
|
if (this.horizontalDelayTimer) {
|
||||||
|
clearTimeout(this.horizontalDelayTimer);
|
||||||
|
}
|
||||||
|
this.horizontalDelayTimer = setTimeout(() => {
|
||||||
|
this.resumeHorizontalScroll();
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 横向滚动步进
|
||||||
|
*/
|
||||||
|
horizontalScrollStep() {
|
||||||
|
if (!this.$refs.horizontalScrollContent) return;
|
||||||
|
|
||||||
|
const contentElement = this.$refs.horizontalScrollContent;
|
||||||
|
const contentWidth = contentElement.scrollWidth;
|
||||||
|
|
||||||
|
// 计算单个循环的宽度
|
||||||
|
const singleCycleWidth = contentWidth / this.broadcastListForHorizontal.length * this.finalBroadcastList.length;
|
||||||
|
|
||||||
|
this.horizontalScrollOffset += this.scrollSpeed;
|
||||||
|
|
||||||
|
// 当滚动超过单个循环宽度时,重置到开始位置实现无缝循环
|
||||||
|
if (this.horizontalScrollOffset >= singleCycleWidth) {
|
||||||
|
this.horizontalScrollOffset = 0;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重置滚动
|
||||||
|
*/
|
||||||
|
resetScroll() {
|
||||||
|
this.horizontalScrollOffset = 0;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.finalBroadcastList.length > 1) {
|
||||||
|
this.startHorizontalScroll();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重启滚动
|
||||||
|
*/
|
||||||
|
restartScroll() {
|
||||||
|
this.stopHorizontalScroll();
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.startHorizontalScroll();
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 广播项点击事件
|
||||||
|
*/
|
||||||
|
handleItemClick(item) {
|
||||||
|
if (this.clickable) {
|
||||||
|
this.$emit('item-click', item);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 手动刷新数据
|
||||||
|
*/
|
||||||
|
refresh() {
|
||||||
|
if (this.autoFetch) {
|
||||||
|
this.fetchBroadcastData();
|
||||||
|
} else {
|
||||||
|
this.resetScroll();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 暂停/继续滚动
|
||||||
|
*/
|
||||||
|
togglePause() {
|
||||||
|
if (this.isHorizontalScrolling) {
|
||||||
|
this.stopHorizontalScroll();
|
||||||
|
} else {
|
||||||
|
this.resumeHorizontalScroll();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取组件状态
|
||||||
|
*/
|
||||||
|
getStatus() {
|
||||||
|
return {
|
||||||
|
isScrolling: this.isHorizontalScrolling,
|
||||||
|
isHovering: this.isHovering,
|
||||||
|
currentOffset: this.horizontalScrollOffset,
|
||||||
|
dataCount: this.finalBroadcastList.length,
|
||||||
|
isLoading: this.isLoading,
|
||||||
|
hasError: this.hasError
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
beforeDestroy() {
|
||||||
|
// 清理定时器
|
||||||
|
if (this.horizontalScrollTimer) {
|
||||||
|
clearInterval(this.horizontalScrollTimer);
|
||||||
|
}
|
||||||
|
if (this.horizontalDelayTimer) {
|
||||||
|
clearTimeout(this.horizontalDelayTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置状态
|
||||||
|
this.isHovering = false;
|
||||||
|
this.isHorizontalScrolling = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
/* 横向滚动广播容器 */
|
||||||
|
.horizontal-broadcast-container {
|
||||||
|
width: 100%;
|
||||||
|
background: #fff;
|
||||||
|
|
||||||
|
// box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0px 20px !important;
|
||||||
|
position: relative;
|
||||||
|
z-index: 100;
|
||||||
|
|
||||||
|
background: #E7DFF3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 广播标题区域 */
|
||||||
|
.broadcast-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 15px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
|
||||||
|
i {
|
||||||
|
color: #5721e4;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-right: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.broadcast-title {
|
||||||
|
color: #5721e4;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 14px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 横向滚动包装器 */
|
||||||
|
.horizontal-scroll-wrapper {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
height: v-bind(height);
|
||||||
|
// background: #f8f9fa;
|
||||||
|
border-radius: 8px;
|
||||||
|
// border: 1px solid #e9ecef;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
&.full-width {
|
||||||
|
margin-right: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 横向滚动内容 */
|
||||||
|
.horizontal-scroll-content {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
white-space: nowrap;
|
||||||
|
will-change: transform;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 横向广播项 */
|
||||||
|
.horizontal-broadcast-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
margin-right: 30px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
|
||||||
|
.horizontal-item-content {
|
||||||
|
color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.4;
|
||||||
|
padding: 0 15px;
|
||||||
|
white-space: nowrap;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// color: #5721e4;
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮组样式 */
|
||||||
|
.button-group {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
// margin-left: 10px;
|
||||||
|
gap: 0px; /* 按钮之间的间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-item-separator {
|
||||||
|
color: #ccc;
|
||||||
|
margin: 0 15px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 滚动暂停时的视觉反馈 */
|
||||||
|
.horizontal-scroll-wrapper:hover,
|
||||||
|
.horizontal-scroll-wrapper.is-hovering {
|
||||||
|
// background: linear-gradient(135deg, #f0f3ff, #e8f2ff);
|
||||||
|
border-color: #5721e4;
|
||||||
|
// box-shadow: 0 2px 8px rgba(87, 33, 228, 0.15);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* hover状态下的内容样式 */
|
||||||
|
.horizontal-scroll-wrapper.is-hovering .horizontal-item-content {
|
||||||
|
// color: #5721e4;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 暂停指示器圆点 */
|
||||||
|
.horizontal-scroll-wrapper.is-hovering::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
right: 8px;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
background: #5721e4;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: pausePulse 1.5s ease-in-out infinite;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
.view{
|
||||||
|
color: #5721e4;
|
||||||
|
// font-size: 0.85rem;
|
||||||
|
// margin-left: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #5721e4;
|
||||||
|
color: #fff;
|
||||||
|
border-color: #5721e4;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes pausePulse {
|
||||||
|
0%, 100% {
|
||||||
|
opacity: 0.6;
|
||||||
|
transform: translateY(-50%) scale(1);
|
||||||
|
}
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(-50%) scale(1.2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.horizontal-broadcast-container {
|
||||||
|
padding: 8px 15px;
|
||||||
|
|
||||||
|
.broadcast-header {
|
||||||
|
margin-right: 10px;
|
||||||
|
|
||||||
|
i {
|
||||||
|
font-size: 16px;
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.broadcast-title {
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-broadcast-item {
|
||||||
|
margin-right: 20px;
|
||||||
|
|
||||||
|
.horizontal-item-content {
|
||||||
|
font-size: 13px;
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端按钮组样式 */
|
||||||
|
.button-group {
|
||||||
|
margin-left: 5px;
|
||||||
|
gap: 5px;
|
||||||
|
|
||||||
|
.view {
|
||||||
|
font-size: 12px;
|
||||||
|
padding: 1px 6px;
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-item-separator {
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-scroll-wrapper.is-hovering::before {
|
||||||
|
right: 5px;
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 美化滚动条 */
|
||||||
|
.horizontal-scroll-wrapper::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,293 @@
|
||||||
|
# HorizontalBroadcast 横向滚动广播组件
|
||||||
|
|
||||||
|
一个功能完整的横向滚动广播通知栏组件,支持自动获取数据、鼠标交互、触摸交互等特性。
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 基础用法
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<!-- 最简单的用法,自动获取数据 -->
|
||||||
|
<HorizontalBroadcast />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import HorizontalBroadcast from '@/components/HorizontalBroadcast.vue'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: {
|
||||||
|
HorizontalBroadcast
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📋 Props 属性
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
|--------|------|--------|------|
|
||||||
|
| `broadcastData` | Array | `[]` | 广播数据,如果不传则自动获取 |
|
||||||
|
| `showTitle` | Boolean | `true` | 是否显示标题 |
|
||||||
|
| `showIcon` | Boolean | `true` | 是否显示图标 |
|
||||||
|
| `title` | String | `''` | 自定义标题,不传则使用国际化标题 |
|
||||||
|
| `scrollSpeed` | Number | `1` | 滚动速度(像素/步) |
|
||||||
|
| `scrollInterval` | Number | `50` | 滚动间隔(毫秒) |
|
||||||
|
| `autoFetch` | Boolean | `true` | 是否自动获取数据 |
|
||||||
|
| `lang` | String | `'zh'` | 获取数据的语言参数 |
|
||||||
|
| `minItems` | Number | `1` | 最小显示条数,少于此数不显示组件 |
|
||||||
|
| `height` | String | `'40px'` | 组件高度 |
|
||||||
|
| `clickable` | Boolean | `false` | 是否启用点击事件 |
|
||||||
|
|
||||||
|
## 📡 Events 事件
|
||||||
|
|
||||||
|
| 事件名 | 参数 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| `data-loaded` | `data: Array` | 数据加载完成时触发 |
|
||||||
|
| `error` | `error: Error` | 发生错误时触发 |
|
||||||
|
| `item-click` | `item: Object` | 广播项被点击时触发(需要 `clickable=true`) |
|
||||||
|
|
||||||
|
## 🎯 使用示例
|
||||||
|
|
||||||
|
### 1. 自定义样式和行为
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<HorizontalBroadcast
|
||||||
|
:show-title="true"
|
||||||
|
:show-icon="true"
|
||||||
|
title="最新公告"
|
||||||
|
height="50px"
|
||||||
|
:scroll-speed="2"
|
||||||
|
:scroll-interval="30"
|
||||||
|
:clickable="true"
|
||||||
|
@item-click="handleClick"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
handleClick(item) {
|
||||||
|
console.log('点击了:', item.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 使用外部数据
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<HorizontalBroadcast
|
||||||
|
:broadcast-data="customData"
|
||||||
|
:auto-fetch="false"
|
||||||
|
title="自定义消息"
|
||||||
|
:clickable="true"
|
||||||
|
@item-click="handleCustomClick"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
customData: [
|
||||||
|
{ id: '1', content: '第一条消息' },
|
||||||
|
{ id: '2', content: '第二条消息' },
|
||||||
|
{ id: '3', content: '第三条消息' }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleCustomClick(item) {
|
||||||
|
this.$message.info(item.content);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. 极简模式(无标题无图标)
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<HorizontalBroadcast
|
||||||
|
:show-title="false"
|
||||||
|
:show-icon="false"
|
||||||
|
height="35px"
|
||||||
|
:scroll-speed="1.5"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. 错误处理
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<HorizontalBroadcast
|
||||||
|
@data-loaded="onDataLoaded"
|
||||||
|
@error="onError"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
onDataLoaded(data) {
|
||||||
|
console.log('广播数据加载成功:', data);
|
||||||
|
},
|
||||||
|
onError(error) {
|
||||||
|
console.error('广播组件出错:', error);
|
||||||
|
this.$message.error('加载广播数据失败');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🎮 组件方法
|
||||||
|
|
||||||
|
通过 `ref` 访问组件实例,可以调用以下方法:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<HorizontalBroadcast ref="broadcast" />
|
||||||
|
<button @click="refreshData">刷新数据</button>
|
||||||
|
<button @click="togglePause">暂停/继续</button>
|
||||||
|
<button @click="getStatus">获取状态</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
methods: {
|
||||||
|
// 刷新数据
|
||||||
|
refreshData() {
|
||||||
|
this.$refs.broadcast.refresh();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 切换暂停状态
|
||||||
|
togglePause() {
|
||||||
|
this.$refs.broadcast.togglePause();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取组件状态
|
||||||
|
getStatus() {
|
||||||
|
const status = this.$refs.broadcast.getStatus();
|
||||||
|
console.log('组件状态:', status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 可用方法:
|
||||||
|
|
||||||
|
- `refresh()` - 刷新数据
|
||||||
|
- `togglePause()` - 切换暂停/继续状态
|
||||||
|
- `getStatus()` - 获取当前状态信息
|
||||||
|
|
||||||
|
## 🎨 样式定制
|
||||||
|
|
||||||
|
组件使用 scoped 样式,如需定制外观,可以通过以下方式:
|
||||||
|
|
||||||
|
### 1. 通过 Props 定制
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<HorizontalBroadcast
|
||||||
|
height="60px" <!-- 调整高度 -->
|
||||||
|
title="自定义标题" <!-- 自定义标题 -->
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. 全局样式覆盖
|
||||||
|
|
||||||
|
```scss
|
||||||
|
// 在全局样式中
|
||||||
|
.horizontal-broadcast-container {
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
|
||||||
|
.broadcast-title {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-scroll-wrapper {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 📱 移动端适配
|
||||||
|
|
||||||
|
组件已内置移动端适配:
|
||||||
|
|
||||||
|
- 自动调整字体大小和间距
|
||||||
|
- 支持触摸交互(触摸暂停,松开恢复)
|
||||||
|
- 响应式布局自适应
|
||||||
|
|
||||||
|
## 🔧 数据格式
|
||||||
|
|
||||||
|
广播数据应遵循以下格式:
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
[
|
||||||
|
{
|
||||||
|
id: 'unique-id-1', // 必需:唯一标识
|
||||||
|
content: '广播内容...' // 必需:显示内容
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'unique-id-2',
|
||||||
|
content: '另一条广播内容...'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🌐 国际化支持
|
||||||
|
|
||||||
|
组件支持国际化,默认标题会使用 `this.$t('home.describeTitle')`。
|
||||||
|
|
||||||
|
如需自定义,可以:
|
||||||
|
|
||||||
|
```vue
|
||||||
|
<HorizontalBroadcast
|
||||||
|
:title="$t('custom.broadcast.title')"
|
||||||
|
/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚡ 性能优化
|
||||||
|
|
||||||
|
组件已内置以下性能优化:
|
||||||
|
|
||||||
|
- 使用 `will-change: transform` 启用硬件加速
|
||||||
|
- 智能的数据复制策略减少 DOM 操作
|
||||||
|
- 自动清理定时器防止内存泄漏
|
||||||
|
- 合理的滚动间隔和步进设置
|
||||||
|
|
||||||
|
## 🐛 常见问题
|
||||||
|
|
||||||
|
### Q: 为什么组件没有显示?
|
||||||
|
|
||||||
|
A: 检查以下几点:
|
||||||
|
1. 数据是否正确加载(通过 `@data-loaded` 事件检查)
|
||||||
|
2. `minItems` 设置是否过高
|
||||||
|
3. 是否有 CSS 冲突
|
||||||
|
|
||||||
|
### Q: 如何调整滚动速度?
|
||||||
|
|
||||||
|
A: 通过 `scrollSpeed` 和 `scrollInterval` 两个属性:
|
||||||
|
- `scrollSpeed`: 每次移动的像素数(越大越快)
|
||||||
|
- `scrollInterval`: 滚动间隔毫秒数(越小越快)
|
||||||
|
|
||||||
|
### Q: 点击事件不生效?
|
||||||
|
|
||||||
|
A: 确保设置了 `clickable="true"` 并监听 `@item-click` 事件。
|
||||||
|
|
||||||
|
## <20><> 许可证
|
||||||
|
|
||||||
|
MIT License
|
|
@ -175,6 +175,24 @@
|
||||||
<span class="line"></span>
|
<span class="line"></span>
|
||||||
</div>
|
</div>
|
||||||
</li>
|
</li>
|
||||||
|
<!-- 帮助中心 -->
|
||||||
|
<li
|
||||||
|
class="personalCenter"
|
||||||
|
:class="{
|
||||||
|
active: $route.path.includes(`/${$i18n.locale}/helpCenter`),
|
||||||
|
}"
|
||||||
|
@click="handelJump('helpCenter')"
|
||||||
|
>
|
||||||
|
{{ $t(`home.helpCenter`) }}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="horizontalLine"
|
||||||
|
:class="{ hidden: $route.path.includes(`helpCenter`) }"
|
||||||
|
>
|
||||||
|
<span class="circular"></span>
|
||||||
|
<span class="line"></span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
<!-- 工单管理 -->
|
<!-- 工单管理 -->
|
||||||
<!-- <li
|
<!-- <li
|
||||||
v-show="ManagementShow"
|
v-show="ManagementShow"
|
||||||
|
@ -230,6 +248,25 @@
|
||||||
<li class="register" @click="handelRegister">
|
<li class="register" @click="handelRegister">
|
||||||
{{ $t(`user.register`) }}
|
{{ $t(`user.register`) }}
|
||||||
</li>
|
</li>
|
||||||
|
<li
|
||||||
|
class="personalCenter"
|
||||||
|
style="margin-left: 20px;"
|
||||||
|
:class="{
|
||||||
|
active: $route.path.includes(`/${$i18n.locale}/helpCenter`),
|
||||||
|
}"
|
||||||
|
@click="handelJump('helpCenter')"
|
||||||
|
>
|
||||||
|
{{ $t(`home.helpCenter`) }}
|
||||||
|
|
||||||
|
<div
|
||||||
|
class="horizontalLine"
|
||||||
|
:class="{ hidden: $route.path.includes(`helpCenter`) }"
|
||||||
|
>
|
||||||
|
<span class="circular"></span>
|
||||||
|
<span class="line"></span>
|
||||||
|
</div>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li class="langBox">
|
<li class="langBox">
|
||||||
<div class="LangLine"></div>
|
<div class="LangLine"></div>
|
||||||
<el-dropdown>
|
<el-dropdown>
|
||||||
|
@ -296,6 +333,8 @@ export default {
|
||||||
imgUrl:getImageUrl(`/img/nexa.png`) ,
|
imgUrl:getImageUrl(`/img/nexa.png`) ,
|
||||||
|
|
||||||
},
|
},
|
||||||
|
customerIsOnline: false,
|
||||||
|
userEmail:"",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
|
@ -352,6 +391,8 @@ export default {
|
||||||
this.jurisdiction = JSON.parse(jurisdiction);
|
this.jurisdiction = JSON.parse(jurisdiction);
|
||||||
let currencyList = localStorage.getItem("currencyList");
|
let currencyList = localStorage.getItem("currencyList");
|
||||||
this.currencyList = JSON.parse(currencyList);
|
this.currencyList = JSON.parse(currencyList);
|
||||||
|
let userEmail = localStorage.getItem("userEmail");
|
||||||
|
this.userEmail = JSON.parse(userEmail);
|
||||||
window.addEventListener("setItem", () => {
|
window.addEventListener("setItem", () => {
|
||||||
let valueTaking = localStorage.getItem("token");
|
let valueTaking = localStorage.getItem("token");
|
||||||
this.token = JSON.parse(valueTaking);
|
this.token = JSON.parse(valueTaking);
|
||||||
|
@ -365,6 +406,8 @@ export default {
|
||||||
this.currencyList = JSON.parse(currencyList);
|
this.currencyList = JSON.parse(currencyList);
|
||||||
let active = localStorage.getItem(`activeItemCoin`)
|
let active = localStorage.getItem(`activeItemCoin`)
|
||||||
this.activeItem = JSON.parse(active)
|
this.activeItem = JSON.parse(active)
|
||||||
|
let userEmail = localStorage.getItem("userEmail");
|
||||||
|
this.userEmail = JSON.parse(userEmail);
|
||||||
|
|
||||||
if (this.jurisdiction && this.jurisdiction.roleKey == `admin`) {
|
if (this.jurisdiction && this.jurisdiction.roleKey == `admin`) {
|
||||||
this.ManagementShow = true;
|
this.ManagementShow = true;
|
||||||
|
@ -447,13 +490,35 @@ export default {
|
||||||
// localStorage.setItem(`accountList`,JSON.stringify(this.accountList))
|
// localStorage.setItem(`accountList`,JSON.stringify(this.accountList))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// 修改 fetchUserid 方法,添加 token 检查
|
||||||
|
async fetchUserid(params) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const res = await getUserid(params);
|
||||||
|
if (res && res.code == 200) {
|
||||||
|
|
||||||
|
this.customerIsOnline = res.data.customerIsOnline;
|
||||||
|
localStorage.setItem(`customerIsOnline`,JSON.stringify(this.customerIsOnline))
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取用户ID失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
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 清除前端状态
|
// 调用 Vuex 的 logout action 清除前端状态
|
||||||
await this.$store.dispatch('logout')
|
await this.$store.dispatch('logout')
|
||||||
const lang = this.$i18n.locale;
|
const lang = this.$i18n.locale;
|
||||||
|
|
||||||
|
this.fetchUserid({email:this.userEmail})
|
||||||
this.$router.push(`/${lang}`);
|
this.$router.push(`/${lang}`);
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleDropdownClick() {
|
handleDropdownClick() {
|
||||||
|
@ -564,9 +629,12 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handelSignOut() {
|
handelSignOut() {
|
||||||
|
localStorage.setItem('cs_disconnect_all', Date.now().toString()); //告知客服页面断开连接
|
||||||
this.fetchSignOut();
|
this.fetchSignOut();
|
||||||
localStorage.removeItem(`token`);
|
localStorage.removeItem(`token`);
|
||||||
localStorage.removeItem("username");
|
localStorage.removeItem("username");
|
||||||
|
localStorage.removeItem("userName");
|
||||||
|
localStorage.removeItem("userEmail");
|
||||||
localStorage.removeItem("jurisdiction");
|
localStorage.removeItem("jurisdiction");
|
||||||
this.$addStorageEvent(1, `miningAccountList`, JSON.stringify(""));
|
this.$addStorageEvent(1, `miningAccountList`, JSON.stringify(""));
|
||||||
this.isLogin = false;
|
this.isLogin = false;
|
||||||
|
|
|
@ -0,0 +1,190 @@
|
||||||
|
/**
|
||||||
|
* 公告中心页面国际化文件
|
||||||
|
* 包含公告中心页面相关的中英文翻译
|
||||||
|
*/
|
||||||
|
|
||||||
|
// 中文翻译
|
||||||
|
export const announcements_zh = {
|
||||||
|
// 页面标题和导航
|
||||||
|
title: '公告中心',
|
||||||
|
latestTitle: '最新公告',
|
||||||
|
searchPlaceholder: '搜索',
|
||||||
|
|
||||||
|
// 面包屑导航
|
||||||
|
homeCenter: '首页中心',
|
||||||
|
|
||||||
|
// 公告相关
|
||||||
|
viewAll: '查看所有',
|
||||||
|
articles: '篇文章',
|
||||||
|
noData: '暂无公告',
|
||||||
|
loadError: '加载公告失败',
|
||||||
|
|
||||||
|
// 公告类型
|
||||||
|
importantNotice: '重要通知',
|
||||||
|
systemAnnouncement: '系统公告',
|
||||||
|
marketDynamic: '市场动态',
|
||||||
|
serviceNotice: '服务通知',
|
||||||
|
faultDescription: '故障说明',
|
||||||
|
offlineNotice: '下线通知',
|
||||||
|
|
||||||
|
// 时间相关
|
||||||
|
today: '今天',
|
||||||
|
yesterday: '昨天',
|
||||||
|
daysAgo: '天前',
|
||||||
|
|
||||||
|
// 操作
|
||||||
|
back: '返回',
|
||||||
|
refresh: '刷新',
|
||||||
|
search: '搜索',
|
||||||
|
reset: '重置',
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
total: '共',
|
||||||
|
page: '页',
|
||||||
|
items: '条',
|
||||||
|
goToPage: '跳至',
|
||||||
|
|
||||||
|
// 错误信息
|
||||||
|
networkError: '网络连接失败,请检查网络设置',
|
||||||
|
serverError: '服务器异常,请稍后重试',
|
||||||
|
dataError: '数据加载失败',
|
||||||
|
|
||||||
|
// 提示信息
|
||||||
|
searchTip: '请输入关键词进行搜索',
|
||||||
|
noSearchResult: '未找到相关公告',
|
||||||
|
loadingTip: '正在加载...',
|
||||||
|
|
||||||
|
// 公告状态
|
||||||
|
published: '已发布',
|
||||||
|
draft: '草稿',
|
||||||
|
archived: '已归档',
|
||||||
|
|
||||||
|
// 公告详情
|
||||||
|
detail: '详情',
|
||||||
|
content: '内容',
|
||||||
|
publishTime: '发布时间',
|
||||||
|
updateTime: '更新时间',
|
||||||
|
author: '作者',
|
||||||
|
category: '分类',
|
||||||
|
tags: '标签',
|
||||||
|
readCount: '阅读量',
|
||||||
|
|
||||||
|
// 操作按钮
|
||||||
|
viewDetail: '查看详情',
|
||||||
|
share: '分享',
|
||||||
|
favorite: '收藏',
|
||||||
|
print: '打印',
|
||||||
|
download: '下载',
|
||||||
|
|
||||||
|
// 筛选和排序
|
||||||
|
filter: '筛选',
|
||||||
|
sort: '排序',
|
||||||
|
sortByTime: '按时间排序',
|
||||||
|
sortByPopularity: '按热度排序',
|
||||||
|
filterByType: '按类型筛选',
|
||||||
|
filterByDate: '按日期筛选',
|
||||||
|
allTypes: '全部类型',
|
||||||
|
allDates: '全部日期',
|
||||||
|
|
||||||
|
// 高级搜索
|
||||||
|
advancedSearch: '高级搜索',
|
||||||
|
searchByTitle: '按标题搜索',
|
||||||
|
searchByContent: '按内容搜索',
|
||||||
|
searchByAuthor: '按作者搜索',
|
||||||
|
dateRange: '日期范围',
|
||||||
|
startDate: '开始日期',
|
||||||
|
endDate: '结束日期'
|
||||||
|
};
|
||||||
|
|
||||||
|
// 英文翻译
|
||||||
|
export const announcements_en = {
|
||||||
|
// 页面标题和导航
|
||||||
|
title: 'Announcement Center',
|
||||||
|
latestTitle: 'Latest Announcements',
|
||||||
|
searchPlaceholder: 'Search',
|
||||||
|
|
||||||
|
// 面包屑导航
|
||||||
|
homeCenter: 'Home Center',
|
||||||
|
|
||||||
|
// 公告相关
|
||||||
|
viewAll: 'View All',
|
||||||
|
articles: 'Articles',
|
||||||
|
noData: 'No announcements',
|
||||||
|
loadError: 'Failed to load announcements',
|
||||||
|
|
||||||
|
// 公告类型
|
||||||
|
importantNotice: 'Important Notice',
|
||||||
|
systemAnnouncement: 'System Announcement',
|
||||||
|
marketDynamic: 'Market Dynamic',
|
||||||
|
serviceNotice: 'Service Notice',
|
||||||
|
faultDescription: 'Fault Description',
|
||||||
|
offlineNotice: 'Offline Notice',
|
||||||
|
|
||||||
|
// 时间相关
|
||||||
|
today: 'Today',
|
||||||
|
yesterday: 'Yesterday',
|
||||||
|
daysAgo: 'days ago',
|
||||||
|
|
||||||
|
// 操作
|
||||||
|
back: 'Back',
|
||||||
|
refresh: 'Refresh',
|
||||||
|
search: 'Search',
|
||||||
|
reset: 'Reset',
|
||||||
|
|
||||||
|
// 分页
|
||||||
|
total: 'Total',
|
||||||
|
page: 'Page',
|
||||||
|
items: 'Items',
|
||||||
|
goToPage: 'Go to',
|
||||||
|
|
||||||
|
// 错误信息
|
||||||
|
networkError: 'Network connection failed, please check network settings',
|
||||||
|
serverError: 'Server error, please try again later',
|
||||||
|
dataError: 'Data loading failed',
|
||||||
|
|
||||||
|
// 提示信息
|
||||||
|
searchTip: 'Please enter keywords to search',
|
||||||
|
noSearchResult: 'No related announcements found',
|
||||||
|
loadingTip: 'Loading...',
|
||||||
|
|
||||||
|
// 公告状态
|
||||||
|
published: 'Published',
|
||||||
|
draft: 'Draft',
|
||||||
|
archived: 'Archived',
|
||||||
|
|
||||||
|
// 公告详情
|
||||||
|
detail: 'Detail',
|
||||||
|
content: 'Content',
|
||||||
|
publishTime: 'Publish Time',
|
||||||
|
updateTime: 'Update Time',
|
||||||
|
author: 'Author',
|
||||||
|
category: 'Category',
|
||||||
|
tags: 'Tags',
|
||||||
|
readCount: 'Read Count',
|
||||||
|
|
||||||
|
// 操作按钮
|
||||||
|
viewDetail: 'View Detail',
|
||||||
|
share: 'Share',
|
||||||
|
favorite: 'Favorite',
|
||||||
|
print: 'Print',
|
||||||
|
download: 'Download',
|
||||||
|
|
||||||
|
// 筛选和排序
|
||||||
|
filter: 'Filter',
|
||||||
|
sort: 'Sort',
|
||||||
|
sortByTime: 'Sort by Time',
|
||||||
|
sortByPopularity: 'Sort by Popularity',
|
||||||
|
filterByType: 'Filter by Type',
|
||||||
|
filterByDate: 'Filter by Date',
|
||||||
|
allTypes: 'All Types',
|
||||||
|
allDates: 'All Dates',
|
||||||
|
|
||||||
|
// 高级搜索
|
||||||
|
advancedSearch: 'Advanced Search',
|
||||||
|
searchByTitle: 'Search by Title',
|
||||||
|
searchByContent: 'Search by Content',
|
||||||
|
searchByAuthor: 'Search by Author',
|
||||||
|
dateRange: 'Date Range',
|
||||||
|
startDate: 'Start Date',
|
||||||
|
endDate: 'End Date'
|
||||||
|
};
|
|
@ -72,7 +72,6 @@ export const backendSystem_zh = {
|
||||||
text:"邮件内容",
|
text:"邮件内容",
|
||||||
send:"发送",
|
send:"发送",
|
||||||
emailRemind:"可输入多个邮箱,用逗号隔开",
|
emailRemind:"可输入多个邮箱,用逗号隔开",
|
||||||
|
|
||||||
pleaseInputCorrectEmail:"请输入正确的邮箱地址",
|
pleaseInputCorrectEmail:"请输入正确的邮箱地址",
|
||||||
pleaseInputSubject:"请输入邮件主题",
|
pleaseInputSubject:"请输入邮件主题",
|
||||||
pleaseInputText:"请输入邮件内容",
|
pleaseInputText:"请输入邮件内容",
|
||||||
|
@ -81,8 +80,21 @@ export const backendSystem_zh = {
|
||||||
pleaseInputQueryConditions:"请输入查询条件(挖矿账号、邮箱)",
|
pleaseInputQueryConditions:"请输入查询条件(挖矿账号、邮箱)",
|
||||||
emailContent:"邮件内容",
|
emailContent:"邮件内容",
|
||||||
leftNavigation:"选择左侧导航栏查看页面",
|
leftNavigation:"选择左侧导航栏查看页面",
|
||||||
|
broadcastPause:"广播已暂停,移开鼠标继续滚动",
|
||||||
|
broadcastResume:"鼠标悬停可暂停滚动",
|
||||||
|
transactionAmount:"交易金额",
|
||||||
|
walletBalance:"钱包余额",
|
||||||
|
bthContent:"广播按钮内容(输入多个按钮,用逗号隔开):",
|
||||||
|
bthContent2:"广播按钮内容",
|
||||||
|
bthPath:"按钮对应跳转页面路径(根据按钮数量顺序输入):",
|
||||||
|
bthPath2:"按钮对应跳转页面路径",
|
||||||
|
broadcastContent:"广播内容:",
|
||||||
|
bthPathStandard:"跳转路径以逗号分隔,最多5个(例:/reportBlock,/miningAccount,/personalMining,/workOrderRecords,/UserWorkDetails),输入换行符无效",
|
||||||
|
pleaseInputButtonContentAndPath:"请输入广播按钮内容和跳转路径",
|
||||||
|
max5:"跳转路径最多输入5个",
|
||||||
|
invalidPath:"无效的跳转地址",
|
||||||
|
invalidPathSuggestions:"建议使用",
|
||||||
|
pathNumberNotMatch:"按钮数量和路径数量不匹配",
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,6 +182,22 @@ export const backendSystem_en = {
|
||||||
emailContent:"Email Content",
|
emailContent:"Email Content",
|
||||||
leftNavigation:"Select the left navigation bar to view the page",
|
leftNavigation:"Select the left navigation bar to view the page",
|
||||||
|
|
||||||
|
broadcastPause:"Broadcast has been paused, move the mouse to continue scrolling",
|
||||||
|
broadcastResume:"Hover to pause scrolling",
|
||||||
|
transactionAmount:"Transaction Amount",
|
||||||
|
walletBalance:"Wallet Balance",
|
||||||
|
bthContent:"Broadcast Button Content:",
|
||||||
|
bthPath:"Button Corresponding Jump Page Path:",
|
||||||
|
broadcastContent:"Broadcast Content:",
|
||||||
|
bthPathStandard:"Jump paths separated by commas, up to 5 (example: /reportBlock,/miningAccount,/personalMining,/workOrderRecords,/UserWorkDetails), invalid for line breaks",
|
||||||
|
pleaseInputButtonContentAndPath:"Please input broadcast button content and jump path",
|
||||||
|
bthContent2:"Broadcast Button Content",
|
||||||
|
bthPath2:"Button Corresponding Jump Page Path",
|
||||||
|
max5:"The number of jump paths is up to 5",
|
||||||
|
invalidPath:"Invalid jump address",
|
||||||
|
invalidPathSuggestions:"Suggested paths",
|
||||||
|
pathNumberNotMatch:"The number of buttons and paths does not match",
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -78,7 +78,15 @@ export const home_zh = {
|
||||||
view:"详情",
|
view:"详情",
|
||||||
describe:"奖励分配:1天/次,每日0时(utc+0), 转账:1天/次,每日4时(utc+0)起,具体取决于区块成熟条件",
|
describe:"奖励分配:1天/次,每日0时(utc+0), 转账:1天/次,每日4时(utc+0)起,具体取决于区块成熟条件",
|
||||||
networkReconnected:"网络已重新连接,正在恢复数据...",
|
networkReconnected:"网络已重新连接,正在恢复数据...",
|
||||||
networkOffline:"网络连接已断开,系统将在恢复连接后自动重试"
|
networkOffline:"网络连接已断开,系统将在恢复连接后自动重试",
|
||||||
|
helpCenter:"帮助中心",
|
||||||
|
announcements:"公告中心",
|
||||||
|
latestTitle:"最新公告",
|
||||||
|
noData:"暂无公告",
|
||||||
|
viewAll:"查看所有",
|
||||||
|
articles:"篇文章",
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,6 +170,13 @@ export const home_en = {
|
||||||
view:"Details",
|
view:"Details",
|
||||||
describe:"Reward distribution: 1 day/times, daily at 0:00 (utc+0), transfer: 1 day/times, daily from 4:00 (utc+0), depending on block maturity conditions ",
|
describe:"Reward distribution: 1 day/times, daily at 0:00 (utc+0), transfer: 1 day/times, daily from 4:00 (utc+0), depending on block maturity conditions ",
|
||||||
networkReconnected:"Network has been reconnected, recovering data...",
|
networkReconnected:"Network has been reconnected, recovering data...",
|
||||||
networkOffline:"Network connection has been disconnected, the system will automatically retry after recovery"
|
networkOffline:"Network connection has been disconnected, the system will automatically retry after recovery",
|
||||||
|
helpCenter:"Help Center",
|
||||||
|
announcements:"Announcements",
|
||||||
|
latestTitle:"Latest Announcements",
|
||||||
|
noData:"No data",
|
||||||
|
viewAll:"View all",
|
||||||
|
articles:" articles",
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -13,6 +13,7 @@ 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'
|
import {ChatWidget_zh,ChatWidget_en} from'./ChatWidget'
|
||||||
import {backendSystem_zh,backendSystem_en} from'./backendSystem'
|
import {backendSystem_zh,backendSystem_en} from'./backendSystem'
|
||||||
|
import {announcements_zh,announcements_en} from'./announcements'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ export default {
|
||||||
...chooseUs_zh,
|
...chooseUs_zh,
|
||||||
...ChatWidget_zh,
|
...ChatWidget_zh,
|
||||||
...backendSystem_zh,
|
...backendSystem_zh,
|
||||||
|
...announcements_zh,
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
|
@ -56,6 +58,7 @@ export default {
|
||||||
...chooseUs_en,
|
...chooseUs_en,
|
||||||
...ChatWidget_en,
|
...ChatWidget_en,
|
||||||
...backendSystem_en,
|
...backendSystem_en,
|
||||||
|
...announcements_en,
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -31,7 +31,8 @@ export const seo_zh = {
|
||||||
alphAccess: "Alephium(alph) 接入页面,详细介绍如何接入 M2Pool 矿池进行 Alephium(alph) 币种挖矿,提供用户便捷的接入指南,轻松开启挖矿之旅。",
|
alphAccess: "Alephium(alph) 接入页面,详细介绍如何接入 M2Pool 矿池进行 Alephium(alph) 币种挖矿,提供用户便捷的接入指南,轻松开启挖矿之旅。",
|
||||||
broadcast: "M2Pool 矿池广播页面,管理员可在此查看已发布的广播信息,包括广播内容、发布时间、发布者等。",
|
broadcast: "M2Pool 矿池广播页面,管理员可在此查看已发布的广播信息,包括广播内容、发布时间、发布者等。",
|
||||||
userManagement: "M2Pool 矿池用户管理页面,管理员可在此查看所有用户信息,包括用户名、邮箱、注册时间、登录时间等。",
|
userManagement: "M2Pool 矿池用户管理页面,管理员可在此查看所有用户信息,包括用户名、邮箱、注册时间、登录时间等。",
|
||||||
|
helpCenter:"M2Pool 矿池帮助中心页面,提供用户全面的帮助中心信息,包括新手入门、矿业数据、常见问题、公告中心、其他等。",
|
||||||
|
announcements:"M2Pool 矿池公告中心页面,提供最新的矿池公告、系统通知、服务更新和重要信息,让用户及时了解矿池动态和政策变更。",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export const seo_en = {
|
export const seo_en = {
|
||||||
|
@ -57,6 +58,8 @@ export const seo_en = {
|
||||||
alphAccess: "The M2Pool Alephium(alph) access page provides detailed instructions on how to access the M2Pool mining pool for Alephium(alph) currency mining, offering users a convenient access guide to easily start their mining journey.",
|
alphAccess: "The M2Pool Alephium(alph) access page provides detailed instructions on how to access the M2Pool mining pool for Alephium(alph) currency mining, offering users a convenient access guide to easily start their mining journey.",
|
||||||
broadcast: "M2Pool mining pool broadcast page, where administrators can view published broadcast information, including broadcast content, release time, publisher, etc.",
|
broadcast: "M2Pool mining pool broadcast page, where administrators can view published broadcast information, including broadcast content, release time, publisher, etc.",
|
||||||
userManagement: "M2Pool mining pool user management page, where administrators can view all user information, including user name, email, registration time, login time, etc.",
|
userManagement: "M2Pool mining pool user management page, where administrators can view all user information, including user name, email, registration time, login time, etc.",
|
||||||
|
helpCenter:"M2Pool mining pool help center page, providing users with comprehensive help center information, including beginner's guide, mining data, frequently asked questions, announcement center, and other information.",
|
||||||
|
announcements:"M2Pool mining pool announcement center page, providing the latest mining pool announcements, system notifications, service updates and important information, allowing users to stay informed of mining pool dynamics and policy changes.",
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ Vue.use(ElementUI, {
|
||||||
});
|
});
|
||||||
Vue.prototype.$axios = axios
|
Vue.prototype.$axios = axios
|
||||||
|
|
||||||
console.log = ()=>{} //全局关闭打印
|
// console.log = ()=>{} //全局关闭打印
|
||||||
// 全局注册混入
|
// 全局注册混入
|
||||||
Vue.mixin(loadingStateMixin);//loading状态管理
|
Vue.mixin(loadingStateMixin);//loading状态管理
|
||||||
Vue.mixin(networkRecoveryMixin);//网络恢复后数据刷新
|
Vue.mixin(networkRecoveryMixin);//网络恢复后数据刷新
|
||||||
|
@ -52,6 +52,11 @@ router.beforeEach((to, from, next) => {
|
||||||
if (i18n.locale !== lang) {
|
if (i18n.locale !== lang) {
|
||||||
i18n.locale = lang;
|
i18n.locale = lang;
|
||||||
}
|
}
|
||||||
|
const token = localStorage.getItem('token');
|
||||||
|
if (!token || token === 'null') {
|
||||||
|
// 通知断开所有客服连接
|
||||||
|
localStorage.setItem('cs_disconnect_all', Date.now().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
next();
|
next();
|
||||||
|
|
|
@ -76,7 +76,23 @@ const childrenRoutes = [
|
||||||
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{//报块页面
|
{//帮助中心
|
||||||
|
path: 'helpCenter',
|
||||||
|
name: 'HelpCenter',
|
||||||
|
component: () => import('../views/helpCenter/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '帮助中心',
|
||||||
|
description: i18n.t(`seo.helpCenter`),
|
||||||
|
allAuthority: [`all`],
|
||||||
|
keywords: {
|
||||||
|
en: 'Help Center,Beginner Guide,Mining Data,Frequently Asked Questions,Announcement Center,Other',
|
||||||
|
zh: '帮助中心,新手入门,常见问题,公告中心'
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{//广播页面
|
||||||
path: 'broadcast',
|
path: 'broadcast',
|
||||||
name: 'Broadcast',
|
name: 'Broadcast',
|
||||||
component: () => import('../views/broadcast/index.vue'),
|
component: () => import('../views/broadcast/index.vue'),
|
||||||
|
@ -456,6 +472,20 @@ const childrenRoutes = [
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{//公告中心
|
||||||
|
path: 'announcements',
|
||||||
|
name: 'Announcements',
|
||||||
|
component: () => import('../views/announcements/index.vue'),
|
||||||
|
meta: {
|
||||||
|
title: '公告中心',
|
||||||
|
description: i18n.t(`seo.announcements`) || 'M2Pool 矿池公告中心,提供最新的公告、通知和重要信息,让用户及时了解矿池的最新动态和服务变更。',
|
||||||
|
allAuthority: [`all`],
|
||||||
|
keywords: {
|
||||||
|
en: 'Announcement Center,Latest Announcements,Mining Pool News,Service Updates,Important Notices',
|
||||||
|
zh: 'M2Pool 矿池,公告中心,最新公告,矿池动态,服务更新,重要通知'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{//警报通知
|
{//警报通知
|
||||||
path: 'alerts',
|
path: 'alerts',
|
||||||
name: 'Alerts',
|
name: 'Alerts',
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
export default {
|
export default {
|
||||||
// '401': '认证失败,无法访问系统资源,请重新登录',
|
'401': '认证失败,无法访问系统资源,请重新登录',
|
||||||
'403': '当前操作没有权限',
|
'403': '当前操作没有权限',
|
||||||
'404': '访问资源不存在',
|
'404': '访问资源不存在',
|
||||||
'default': '系统未知错误,请反馈给管理员'
|
'default': '系统未知错误,请反馈给管理员'
|
||||||
|
|
|
@ -260,6 +260,7 @@ service.interceptors.response.use(res => {
|
||||||
// 获取错误信息
|
// 获取错误信息
|
||||||
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
const msg = errorCode[code] || res.data.msg || errorCode['default']
|
||||||
if (code === 421) {
|
if (code === 421) {
|
||||||
|
localStorage.setItem('cs_disconnect_all', Date.now().toString()); //告知客服页面断开连接
|
||||||
localStorage.removeItem('token')
|
localStorage.removeItem('token')
|
||||||
// 系统状态已过期,请重新点击SUPPORT按钮进入
|
// 系统状态已过期,请重新点击SUPPORT按钮进入
|
||||||
superReportError = localStorage.getItem('superReportError')
|
superReportError = localStorage.getItem('superReportError')
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
/**
|
||||||
|
* 路由工具函数
|
||||||
|
* 用于获取项目中所有有效的路由路径
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取所有有效的路由路径
|
||||||
|
* @returns {Array} 有效路径数组
|
||||||
|
*/
|
||||||
|
export function getAllValidPaths() {
|
||||||
|
const validPaths = [
|
||||||
|
// 主要页面路径
|
||||||
|
'/',
|
||||||
|
'/home',
|
||||||
|
'/miningAccount',
|
||||||
|
'/readOnlyDisplay',
|
||||||
|
'/reportBlock',
|
||||||
|
'/broadcast',
|
||||||
|
'/userManagement',
|
||||||
|
'/userDetails',
|
||||||
|
'/rate',
|
||||||
|
'/allocationExplanation',
|
||||||
|
'/apiFile',
|
||||||
|
'/customerService',
|
||||||
|
'/ServiceTerms',
|
||||||
|
'/submitWorkOrder',
|
||||||
|
'/workOrderRecords',
|
||||||
|
'/userWorkDetails',
|
||||||
|
'/workOrderBackend',
|
||||||
|
'/BKWorkDetails',
|
||||||
|
'/dataDisplay',
|
||||||
|
'/alerts',
|
||||||
|
|
||||||
|
// 个人中心子页面
|
||||||
|
'/personalCenter',
|
||||||
|
'/personalCenter/personalMining',
|
||||||
|
'/personalCenter/readOnly',
|
||||||
|
'/personalCenter/securitySetting',
|
||||||
|
'/personalCenter/personal',
|
||||||
|
'/personalCenter/miningReport',
|
||||||
|
'/personalCenter/personalAPI',
|
||||||
|
|
||||||
|
// 接入矿池页面
|
||||||
|
'/AccessMiningPool',
|
||||||
|
'/AccessMiningPool/nexaAccess',
|
||||||
|
'/AccessMiningPool/rxdAccess',
|
||||||
|
'/AccessMiningPool/monaAccess',
|
||||||
|
'/AccessMiningPool/grsAccess',
|
||||||
|
'/AccessMiningPool/dgbqAccess',
|
||||||
|
'/AccessMiningPool/dgboAccess',
|
||||||
|
'/AccessMiningPool/dgbsAccess',
|
||||||
|
'/AccessMiningPool/enxAccess',
|
||||||
|
'/AccessMiningPool/alphminingPool',
|
||||||
|
|
||||||
|
// 独立页面
|
||||||
|
'/login',
|
||||||
|
'/register',
|
||||||
|
'/simulation',
|
||||||
|
'/resetPassword',
|
||||||
|
'/404'
|
||||||
|
];
|
||||||
|
|
||||||
|
return validPaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 验证路径是否有效
|
||||||
|
* @param {string} path - 要验证的路径
|
||||||
|
* @returns {boolean} 是否为有效路径
|
||||||
|
*/
|
||||||
|
export function isValidPath(path) {
|
||||||
|
if (!path || typeof path !== 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const trimmedPath = path.trim();
|
||||||
|
|
||||||
|
// 主页路径的特殊处理
|
||||||
|
if (trimmedPath === '/' || trimmedPath === '') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去除开头的斜杠进行标准化
|
||||||
|
const normalizedPath = trimmedPath.startsWith('/') ? trimmedPath.substring(1) : trimmedPath;
|
||||||
|
|
||||||
|
const validPaths = getAllValidPaths();
|
||||||
|
|
||||||
|
// 检查标准化路径是否匹配
|
||||||
|
const pathsWithoutSlash = validPaths.map(p => p.replace(/^\//, '')).filter(p => p !== '');
|
||||||
|
|
||||||
|
return pathsWithoutSlash.includes(normalizedPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取路径建议列表(用于错误提示或自动完成)
|
||||||
|
* @param {string} input - 用户输入的路径
|
||||||
|
* @returns {Array} 建议的路径列表
|
||||||
|
*/
|
||||||
|
export function getPathSuggestions(input) {
|
||||||
|
if (!input) {
|
||||||
|
return getAllValidPaths().slice(0, 10); // 返回前10个路径作为建议
|
||||||
|
}
|
||||||
|
|
||||||
|
const validPaths = getAllValidPaths();
|
||||||
|
const normalizedInput = input.toLowerCase().trim();
|
||||||
|
|
||||||
|
// 查找包含输入内容的路径
|
||||||
|
return validPaths.filter(path =>
|
||||||
|
path.toLowerCase().includes(normalizedInput)
|
||||||
|
).slice(0, 5); // 最多返回5个建议
|
||||||
|
}
|
|
@ -0,0 +1,625 @@
|
||||||
|
<template>
|
||||||
|
<div class="announcements-container">
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 主要内容区域 -->
|
||||||
|
<div class="main-content" v-loading="loading">
|
||||||
|
<!-- 页面标题 -->
|
||||||
|
<div class="page-header">
|
||||||
|
<div class="header-left">
|
||||||
|
<h1 class="page-title">{{ $t('home.announcements') || '公告中心' }}</h1>
|
||||||
|
</div>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-button-group>
|
||||||
|
<el-button
|
||||||
|
:type="viewMode === 'list' ? 'primary' : 'default'"
|
||||||
|
@click="switchMode('list')"
|
||||||
|
icon="el-icon-menu"
|
||||||
|
>
|
||||||
|
查看公告
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
:type="viewMode === 'editor' ? 'primary' : 'default'"
|
||||||
|
@click="switchMode('editor')"
|
||||||
|
icon="el-icon-edit"
|
||||||
|
>
|
||||||
|
Markdown编辑器
|
||||||
|
</el-button>
|
||||||
|
</el-button-group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 最新公告标题 -->
|
||||||
|
<div class="latest-section">
|
||||||
|
<h2 class="section-title">{{ $t('home.latestTitle') || '最新公告' }}</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 公告列表 -->
|
||||||
|
<div class="announcement-list" v-if="!loading && announcements.length > 0">
|
||||||
|
<div
|
||||||
|
v-for="announcement in announcements"
|
||||||
|
:key="announcement.id"
|
||||||
|
class="announcement-item"
|
||||||
|
@click="handleAnnouncementClick(announcement)"
|
||||||
|
>
|
||||||
|
<div class="announcement-content">
|
||||||
|
<h3 class="announcement-title">{{ announcement.title }}</h3>
|
||||||
|
<div class="announcement-meta">
|
||||||
|
<span class="announcement-date">{{ formatDate(announcement.createTime) }}</span>
|
||||||
|
<span class="announcement-type" v-if="announcement.type">{{ announcement.type }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="announcement-summary" v-if="announcement.summary">
|
||||||
|
{{ announcement.summary }}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div class="announcement-arrow">
|
||||||
|
<i class="el-icon-arrow-right"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 空状态 -->
|
||||||
|
<div v-else-if="!loading && announcements.length === 0" class="empty-state">
|
||||||
|
<div class="empty-content">
|
||||||
|
<i class="el-icon-document"></i>
|
||||||
|
<p>{{ $t('home.noData') || '暂无公告' }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分页信息和查看更多 -->
|
||||||
|
<div class="pagination-section" v-if="announcements.length > 0">
|
||||||
|
<div class="view-all-button">
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
class="view-all-link"
|
||||||
|
@click="handleViewAll"
|
||||||
|
>
|
||||||
|
{{ $t('home.viewAll') || '查看所有' }} {{ totalCount }} {{ $t('home.articles') || '篇文章' }}
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 分页组件 -->
|
||||||
|
<el-pagination
|
||||||
|
v-if="totalCount > pageSize"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
|
:current-page="currentPage"
|
||||||
|
:page-size="pageSize"
|
||||||
|
:total="totalCount"
|
||||||
|
layout="prev, pager, next"
|
||||||
|
class="pagination"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 公告中心页面组件
|
||||||
|
* 提供公告列表展示、搜索、分页等功能
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'Announcements',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
// 加载状态
|
||||||
|
loading: false,
|
||||||
|
|
||||||
|
// 搜索关键词
|
||||||
|
searchKeyword: '',
|
||||||
|
|
||||||
|
// 分页参数
|
||||||
|
currentPage: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
totalCount: 275,
|
||||||
|
|
||||||
|
// 公告数据
|
||||||
|
announcements: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: 'ZEN挖矿服务即将结束',
|
||||||
|
summary: 'ZEN挖矿服务将于近期结束,请及时调整您的挖矿设置。',
|
||||||
|
type: '重要通知',
|
||||||
|
createTime: '2025-01-20T10:00:00.000Z',
|
||||||
|
isTop: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: 'LKY即将减半',
|
||||||
|
summary: 'LKY币种将在近期进行减半操作,请关注相关通知。',
|
||||||
|
type: '系统公告',
|
||||||
|
createTime: '2025-01-19T15:30:00.000Z',
|
||||||
|
isTop: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: 'PEP即将减产',
|
||||||
|
summary: 'PEP币种挖矿难度调整,产量将有所减少。',
|
||||||
|
type: '市场动态',
|
||||||
|
createTime: '2025-01-18T09:15:00.000Z',
|
||||||
|
isTop: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: 'ETC+ZIL挖矿服务已结束',
|
||||||
|
summary: 'ETC+ZIL双挖服务已正式结束,感谢您的支持。',
|
||||||
|
type: '服务通知',
|
||||||
|
createTime: '2025-01-17T14:45:00.000Z',
|
||||||
|
isTop: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
title: '有关2025年06月19日SCT池异常的说明',
|
||||||
|
summary: 'SCT矿池在指定时间出现异常情况,现已修复并提供补偿方案。',
|
||||||
|
type: '故障说明',
|
||||||
|
createTime: '2025-01-16T11:20:00.000Z',
|
||||||
|
isTop: false
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 6,
|
||||||
|
title: 'FB单挖矿池下线公告',
|
||||||
|
summary: 'FB单挖矿池将于本月底正式下线,请及时转移算力。',
|
||||||
|
type: '下线通知',
|
||||||
|
createTime: '2025-01-15T16:10:00.000Z',
|
||||||
|
isTop: false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
// 搜索防抖定时器
|
||||||
|
searchTimer: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
mounted() {
|
||||||
|
this.loadAnnouncements();
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 加载公告数据
|
||||||
|
*/
|
||||||
|
async loadAnnouncements() {
|
||||||
|
this.loading = true;
|
||||||
|
try {
|
||||||
|
// 这里应该调用API获取公告数据
|
||||||
|
// const response = await this.$api.getAnnouncements({
|
||||||
|
// page: this.currentPage,
|
||||||
|
// pageSize: this.pageSize,
|
||||||
|
// keyword: this.searchKeyword
|
||||||
|
// });
|
||||||
|
// this.announcements = response.data;
|
||||||
|
// this.totalCount = response.total;
|
||||||
|
|
||||||
|
// 模拟API调用延迟
|
||||||
|
await new Promise(resolve => setTimeout(resolve, 500));
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载公告失败:', error);
|
||||||
|
this.$message.error(this.$t('announcements.loadError') || '加载公告失败');
|
||||||
|
} finally {
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理搜索输入
|
||||||
|
*/
|
||||||
|
handleSearchInput() {
|
||||||
|
if (this.searchTimer) {
|
||||||
|
clearTimeout(this.searchTimer);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 防抖处理,500ms后执行搜索
|
||||||
|
this.searchTimer = setTimeout(() => {
|
||||||
|
this.handleSearch();
|
||||||
|
}, 500);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 执行搜索
|
||||||
|
*/
|
||||||
|
handleSearch() {
|
||||||
|
this.currentPage = 1;
|
||||||
|
this.loadAnnouncements();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理页码变化
|
||||||
|
*/
|
||||||
|
handleCurrentChange(page) {
|
||||||
|
this.currentPage = page;
|
||||||
|
this.loadAnnouncements();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理公告点击
|
||||||
|
*/
|
||||||
|
handleAnnouncementClick(announcement) {
|
||||||
|
// 跳转到公告详情页
|
||||||
|
this.$router.push({
|
||||||
|
name: 'AnnouncementDetail',
|
||||||
|
params: { id: announcement.id }
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查看所有公告
|
||||||
|
*/
|
||||||
|
handleViewAll() {
|
||||||
|
// 可以跳转到完整的公告列表页面或展开显示更多
|
||||||
|
this.pageSize = 20;
|
||||||
|
this.loadAnnouncements();
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回首页
|
||||||
|
*/
|
||||||
|
goHome() {
|
||||||
|
this.$router.push('/');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化日期
|
||||||
|
*/
|
||||||
|
formatDate(dateString) {
|
||||||
|
if (!dateString) return '';
|
||||||
|
|
||||||
|
const date = new Date(dateString);
|
||||||
|
const now = new Date();
|
||||||
|
const diffTime = now - date;
|
||||||
|
const diffDays = Math.floor(diffTime / (1000 * 60 * 60 * 24));
|
||||||
|
|
||||||
|
if (diffDays === 0) {
|
||||||
|
return '今天';
|
||||||
|
} else if (diffDays === 1) {
|
||||||
|
return '昨天';
|
||||||
|
} else if (diffDays < 7) {
|
||||||
|
return `${diffDays}天前`;
|
||||||
|
} else {
|
||||||
|
return date.toLocaleDateString('zh-CN');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
beforeDestroy() {
|
||||||
|
// 清理定时器
|
||||||
|
if (this.searchTimer) {
|
||||||
|
clearTimeout(this.searchTimer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
/**
|
||||||
|
* 公告中心页面样式
|
||||||
|
*/
|
||||||
|
.announcements-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f8f9fa;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 面包屑导航区域 */
|
||||||
|
.breadcrumb-section {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
background: white;
|
||||||
|
padding: 16px 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb {
|
||||||
|
font-size: 14px;
|
||||||
|
|
||||||
|
.breadcrumb-item {
|
||||||
|
color: #666;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #5721E4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-section {
|
||||||
|
.search-input {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主要内容区域 */
|
||||||
|
.main-content {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
background: white;
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 页面标题 */
|
||||||
|
.page-header {
|
||||||
|
padding: 40px 40px 20px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 32px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 最新公告标题 */
|
||||||
|
.latest-section {
|
||||||
|
padding: 30px 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #34495e;
|
||||||
|
margin: 0;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: -20px;
|
||||||
|
top: 50%;
|
||||||
|
transform: translateY(-50%);
|
||||||
|
width: 4px;
|
||||||
|
height: 20px;
|
||||||
|
background: #5721E4;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 公告列表 */
|
||||||
|
.announcement-list {
|
||||||
|
padding: 0 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 20px 0;
|
||||||
|
border-bottom: 1px solid #f5f5f5;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f8f9ff;
|
||||||
|
transform: translateX(5px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #2c3e50;
|
||||||
|
margin: 0 0 8px;
|
||||||
|
line-height: 1.4;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
|
.announcement-item:hover & {
|
||||||
|
color: #5721E4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-meta {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-date {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-type {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #5721E4;
|
||||||
|
background: #f0edff;
|
||||||
|
padding: 2px 8px;
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-summary {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.5;
|
||||||
|
margin: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: 2;
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-arrow {
|
||||||
|
color: #ccc;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
|
.announcement-item:hover & {
|
||||||
|
color: #5721E4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 空状态 */
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 80px 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-content {
|
||||||
|
i {
|
||||||
|
font-size: 64px;
|
||||||
|
color: #ddd;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #999;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分页区域 */
|
||||||
|
.pagination-section {
|
||||||
|
padding: 30px 40px 40px;
|
||||||
|
text-align: center;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-all-button {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-all-link {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #5721E4;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.pagination {
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
:deep(.el-pager li) {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
background-color: #5721E4;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover:not(.active) {
|
||||||
|
background-color: #f0edff;
|
||||||
|
color: #5721E4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.btn-prev),
|
||||||
|
:deep(.btn-next) {
|
||||||
|
background-color: transparent;
|
||||||
|
color: #666;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f0edff;
|
||||||
|
color: #5721E4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 移动端适配 */
|
||||||
|
@media screen and (max-width: 768px) {
|
||||||
|
.announcements-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-section {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 16px;
|
||||||
|
align-items: stretch;
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-section .search-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-header,
|
||||||
|
.latest-section,
|
||||||
|
.announcement-list,
|
||||||
|
.pagination-section {
|
||||||
|
padding-left: 20px;
|
||||||
|
padding-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 18px;
|
||||||
|
|
||||||
|
&::before {
|
||||||
|
left: -16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-item {
|
||||||
|
padding: 16px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-title {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-meta {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 深色模式适配 */
|
||||||
|
@media (prefers-color-scheme: dark) {
|
||||||
|
.announcements-container {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.breadcrumb-section,
|
||||||
|
.main-content {
|
||||||
|
background: #2d2d2d;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.page-title,
|
||||||
|
.section-title,
|
||||||
|
.announcement-title {
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-item:hover {
|
||||||
|
background-color: #3a3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-summary {
|
||||||
|
color: #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.announcement-date {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,146 @@
|
||||||
|
import { documentsList, findDataInfo } from '../../api/staticDocumentManagement'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ApiFile',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
listParams: {
|
||||||
|
lang: `${this.$i18n.locale}`,
|
||||||
|
type: 2
|
||||||
|
},
|
||||||
|
navContentParams: {
|
||||||
|
lang: `${this.$i18n.locale}`,
|
||||||
|
id: 4
|
||||||
|
},
|
||||||
|
navContent: {},
|
||||||
|
documentLoading: false,
|
||||||
|
documentContent: '',
|
||||||
|
documentError: ''
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mounted() {
|
||||||
|
this.getDocumentsList(this.listParams)
|
||||||
|
this.getDocumentsInfo(this.navContentParams)
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
async getDocumentsList(params) {
|
||||||
|
try {
|
||||||
|
const res = await documentsList(params)
|
||||||
|
console.log('文档列表:', res)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取文档列表失败:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
async getDocumentsInfo(params) {
|
||||||
|
this.documentLoading = true
|
||||||
|
this.documentError = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
const res = await findDataInfo(params)
|
||||||
|
console.log('文档详情:', res)
|
||||||
|
|
||||||
|
if (res && res.code === 200 && res.data) {
|
||||||
|
this.navContent = res.data
|
||||||
|
// 将Markdown内容转换为HTML
|
||||||
|
this.documentContent = this.markdownToHtml(res.data.content || '')
|
||||||
|
} else {
|
||||||
|
this.documentError = '获取文档失败: ' + (res?.msg || '未知错误')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取文档详情失败:', error)
|
||||||
|
this.documentError = '获取文档失败: ' + (error.message || '网络错误')
|
||||||
|
} finally {
|
||||||
|
this.documentLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 改进的Markdown转HTML方法
|
||||||
|
* @param {string} markdown - Markdown内容
|
||||||
|
* @returns {string} - HTML内容
|
||||||
|
*/
|
||||||
|
markdownToHtml(markdown) {
|
||||||
|
if (!markdown) return ''
|
||||||
|
|
||||||
|
// 先处理表格(在换行处理之前)
|
||||||
|
let html = this.processMarkdownTables(markdown)
|
||||||
|
|
||||||
|
html = html
|
||||||
|
// 标题处理
|
||||||
|
.replace(/^### (.*$)/gim, '<h3>$1</h3>')
|
||||||
|
.replace(/^## (.*$)/gim, '<h2>$1</h2>')
|
||||||
|
.replace(/^# (.*$)/gim, '<h1>$1</h1>')
|
||||||
|
// 粗体和斜体
|
||||||
|
.replace(/\*\*(.*?)\*\*/gim, '<strong>$1</strong>')
|
||||||
|
.replace(/\*(.*?)\*/gim, '<em>$1</em>')
|
||||||
|
// 代码块
|
||||||
|
.replace(/```([\s\S]*?)```/gim, '<pre><code>$1</code></pre>')
|
||||||
|
.replace(/`([^`]*)`/gim, '<code>$1</code>')
|
||||||
|
// 链接
|
||||||
|
.replace(/\[([^\]]*)\]\(([^)]*)\)/gim, '<a href="$2" target="_blank">$1</a>')
|
||||||
|
|
||||||
|
// 处理段落和换行(避免影响表格)
|
||||||
|
const parts = html.split(/(<table[\s\S]*?<\/table>)/g)
|
||||||
|
html = parts.map(part => {
|
||||||
|
if (part.startsWith('<table')) {
|
||||||
|
return part // 保持表格不变
|
||||||
|
}
|
||||||
|
return part
|
||||||
|
.replace(/\n\n/gim, '</p><p>')
|
||||||
|
.replace(/\n/gim, '<br>')
|
||||||
|
}).join('')
|
||||||
|
|
||||||
|
// 包装段落
|
||||||
|
if (html && !html.startsWith('<') && !html.includes('<table')) {
|
||||||
|
html = '<p>' + html + '</p>'
|
||||||
|
}
|
||||||
|
|
||||||
|
return html
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理Markdown表格
|
||||||
|
* @param {string} text - 包含表格的文本
|
||||||
|
* @returns {string} - 转换后的HTML
|
||||||
|
*/
|
||||||
|
processMarkdownTables(text) {
|
||||||
|
// 匹配Markdown表格的正则表达式
|
||||||
|
const tableRegex = /(\|.*\|[\r\n]+)+/g
|
||||||
|
|
||||||
|
return text.replace(tableRegex, (match) => {
|
||||||
|
const lines = match.trim().split(/[\r\n]+/)
|
||||||
|
if (lines.length < 2) return match
|
||||||
|
|
||||||
|
let html = '<table border="1" style="border-collapse: collapse; width: 100%; margin: 15px 0;">\n'
|
||||||
|
|
||||||
|
lines.forEach((line, index) => {
|
||||||
|
line = line.trim()
|
||||||
|
if (!line.startsWith('|') || !line.endsWith('|')) return
|
||||||
|
|
||||||
|
// 移除首尾的 |
|
||||||
|
const content = line.slice(1, -1)
|
||||||
|
const cells = content.split('|').map(cell => cell.trim())
|
||||||
|
|
||||||
|
// 跳过分隔线(包含 --- 的行)
|
||||||
|
if (cells.some(cell => /^-+$/.test(cell))) return
|
||||||
|
|
||||||
|
const isHeader = index === 0
|
||||||
|
const tag = isHeader ? 'th' : 'td'
|
||||||
|
const style = isHeader ?
|
||||||
|
' style="background: #f8f9fa; font-weight: 600; padding: 8px 12px; border: 1px solid #ddd; text-align: left;"' :
|
||||||
|
' style="padding: 8px 12px; border: 1px solid #ddd; text-align: left;"'
|
||||||
|
|
||||||
|
html += ' <tr>\n'
|
||||||
|
cells.forEach(cell => {
|
||||||
|
html += ` <${tag}${style}>${cell}</${tag}>\n`
|
||||||
|
})
|
||||||
|
html += ' </tr>\n'
|
||||||
|
})
|
||||||
|
|
||||||
|
html += '</table>\n'
|
||||||
|
return html
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,6 +1,22 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="rate">
|
<div class="rate">
|
||||||
|
|
||||||
|
<section class="navContent">
|
||||||
|
<!-- 动态文档内容渲染区域 -->
|
||||||
|
<div v-if="documentLoading" class="loading-container">
|
||||||
|
<p>正在加载文档...</p>
|
||||||
|
</div>
|
||||||
|
<div v-else-if="documentContent" class="document-content" v-html="documentContent"></div>
|
||||||
|
<div v-else-if="documentError" class="error-container">
|
||||||
|
<p>{{ documentError }}</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<section v-if="$isMobile" class="rateMobile">
|
<section v-if="$isMobile" class="rateMobile">
|
||||||
|
|
||||||
<section class="rightText">
|
<section class="rightText">
|
||||||
|
@ -1860,7 +1876,9 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
import Index from './index.js'
|
||||||
export default {
|
export default {
|
||||||
|
mixins: [Index],
|
||||||
metaInfo: {
|
metaInfo: {
|
||||||
meta: [
|
meta: [
|
||||||
{
|
{
|
||||||
|
@ -1942,6 +1960,94 @@ export default {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 移动端 navContent 样式 */
|
||||||
|
.navContent {
|
||||||
|
width: 100%;
|
||||||
|
padding: 15px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.loading-container, .error-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 30px 15px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-container {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-content {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: #333;
|
||||||
|
word-wrap: break-word;
|
||||||
|
|
||||||
|
h1, h2, h3 {
|
||||||
|
margin: 15px 0 10px 0;
|
||||||
|
color: #2c3e50;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 { font-size: 1.4rem; }
|
||||||
|
h2 { font-size: 1.2rem; }
|
||||||
|
h3 { font-size: 1.1rem; }
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 8px 0;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: #f4f4f4;
|
||||||
|
padding: 2px 4px;
|
||||||
|
border-radius: 2px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: #f4f4f4;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 10px 0;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 10px 0;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 6px 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: #f8f9fa;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #5721E4;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -2156,5 +2262,126 @@ a{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* navContent 动态文档内容样式 */
|
||||||
|
.navContent {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
padding: 20px;
|
||||||
|
background: #fff;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
overflow: hidden; /* 确保不超出容器 */
|
||||||
|
|
||||||
|
.loading-container, .error-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 40px 20px;
|
||||||
|
color: #666;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-container {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.document-content {
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 1.6;
|
||||||
|
color: #333;
|
||||||
|
word-wrap: break-word;
|
||||||
|
overflow-wrap: break-word;
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
margin: 20px 0 15px 0;
|
||||||
|
color: #2c3e50;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 { font-size: 28px; }
|
||||||
|
h2 { font-size: 24px; }
|
||||||
|
h3 { font-size: 20px; }
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 10px 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: #f4f4f4;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
pre {
|
||||||
|
background: #f4f4f4;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 5px;
|
||||||
|
overflow-x: auto;
|
||||||
|
margin: 15px 0;
|
||||||
|
|
||||||
|
code {
|
||||||
|
background: none;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin: 15px 0;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
th, td {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 8px 12px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
th {
|
||||||
|
background: #f8f9fa;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
tr:nth-child(even) {
|
||||||
|
background: #f9f9f9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #5721E4;
|
||||||
|
text-decoration: none;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #2c3e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-style: italic;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保所有内容不超出容器 */
|
||||||
|
* {
|
||||||
|
max-width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 图片自适应 */
|
||||||
|
img {
|
||||||
|
max-width: 100%;
|
||||||
|
height: auto;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
|
@ -1,4 +1,5 @@
|
||||||
import { listBroadcast, getAddBroadcast, updateBroadcast, DeleteBroadcast, getBroadcast,dataInfo } from '../../api/broadcast'
|
import { listBroadcast, getAddBroadcast, updateBroadcast, DeleteBroadcast, getBroadcast, dataInfo } from '../../api/broadcast'
|
||||||
|
import { isValidPath, getPathSuggestions } from '../../utils/routeUtils'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
@ -27,10 +28,14 @@ export default {
|
||||||
},
|
},
|
||||||
addParams: {
|
addParams: {
|
||||||
content: "",
|
content: "",
|
||||||
|
buttonContent: "",
|
||||||
|
buttonPath: "",
|
||||||
},
|
},
|
||||||
editParams: {
|
editParams: {
|
||||||
content: "",
|
content: "",
|
||||||
id: "",
|
id: "",
|
||||||
|
buttonContent: "",
|
||||||
|
buttonPath: "",
|
||||||
},
|
},
|
||||||
dialogVisible: false,
|
dialogVisible: false,
|
||||||
bthLoading: false,
|
bthLoading: false,
|
||||||
|
@ -49,9 +54,9 @@ export default {
|
||||||
|
|
||||||
|
|
||||||
let token
|
let token
|
||||||
try{
|
try {
|
||||||
token =JSON.parse(localStorage.getItem('token'))
|
token = JSON.parse(localStorage.getItem('token'))
|
||||||
}catch(e){
|
} catch (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
}
|
}
|
||||||
if (token) {
|
if (token) {
|
||||||
|
@ -72,10 +77,18 @@ export default {
|
||||||
},
|
},
|
||||||
async addBroadcast(params) {
|
async addBroadcast(params) {
|
||||||
this.setLoading('bthLoading', true);
|
this.setLoading('bthLoading', true);
|
||||||
|
|
||||||
|
// 发送请求前打印参数,用于调试
|
||||||
|
console.log('发送广播请求的参数:', params);
|
||||||
|
|
||||||
const res = await getAddBroadcast(params)
|
const res = await getAddBroadcast(params)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.$message.success(this.$t("backendSystem.addSuccess"))
|
this.$message.success(this.$t("backendSystem.addSuccess"))
|
||||||
this.dialogVisible = false;
|
this.dialogVisible = false;
|
||||||
|
// 清空表单参数
|
||||||
|
this.addParams.content = ""
|
||||||
|
this.addParams.buttonContent = ""
|
||||||
|
this.addParams.buttonPath = ""
|
||||||
this.fetchList(this.listParams);
|
this.fetchList(this.listParams);
|
||||||
}
|
}
|
||||||
this.setLoading('bthLoading', false);
|
this.setLoading('bthLoading', false);
|
||||||
|
@ -84,17 +97,28 @@ export default {
|
||||||
this.setLoading('editLoading', true);
|
this.setLoading('editLoading', true);
|
||||||
const res = await dataInfo(params)
|
const res = await dataInfo(params)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.editParams = res.data
|
this.editParams = {
|
||||||
|
...res.data,
|
||||||
|
// 确保按钮路径是字符串格式(如果是数组则转换为逗号分隔的字符串)
|
||||||
|
buttonPath: Array.isArray(res.data.buttonPath)
|
||||||
|
? res.data.buttonPath.join(',')
|
||||||
|
: (res.data.buttonPath || '')
|
||||||
|
};
|
||||||
this.editDialogVisible = true;
|
this.editDialogVisible = true;
|
||||||
}
|
}
|
||||||
this.setLoading('editLoading', false);
|
this.setLoading('editLoading', false);
|
||||||
},
|
},
|
||||||
async editBroadcast(params) {
|
async editBroadcast(params) {
|
||||||
this.setLoading('editLoading', true);
|
this.setLoading('editLoading', true);
|
||||||
|
|
||||||
|
// 发送请求前打印参数,用于调试
|
||||||
|
console.log('发送编辑广播请求的参数:', params);
|
||||||
|
|
||||||
const res = await updateBroadcast(params)
|
const res = await updateBroadcast(params)
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
this.$message.success(this.$t("backendSystem.editSuccess"))
|
this.$message.success(this.$t("backendSystem.editSuccess"))
|
||||||
this.editDialogVisible = false;
|
this.editDialogVisible = false;
|
||||||
|
|
||||||
this.fetchList(this.listParams);
|
this.fetchList(this.listParams);
|
||||||
}
|
}
|
||||||
this.setLoading('editLoading', false);
|
this.setLoading('editLoading', false);
|
||||||
|
@ -112,25 +136,153 @@ export default {
|
||||||
handleClose() {
|
handleClose() {
|
||||||
this.dialogVisible = false;
|
this.dialogVisible = false;
|
||||||
this.addParams.content = ""
|
this.addParams.content = ""
|
||||||
|
this.addParams.buttonContent = ""
|
||||||
|
this.addParams.buttonPath = ""
|
||||||
this.setLoading('bthLoading', false);
|
this.setLoading('bthLoading', false);
|
||||||
},
|
},
|
||||||
sureAddBroadcast() {
|
sureAddBroadcast() {
|
||||||
|
|
||||||
this.addParams.content = this.addParams.content.trim()
|
this.addParams.content = this.addParams.content.trim()
|
||||||
this.addParams.content = this.addParams.content.replace(/[\r\n]/g, '');
|
this.addParams.content = this.addParams.content.replace(/[\r\n]/g, '');
|
||||||
|
this.addParams.buttonPath = this.addParams.buttonPath.trim()
|
||||||
|
this.addParams.buttonPath = this.addParams.buttonPath.replace(/[\r\n]/g, '');
|
||||||
if (!this.addParams.content) {
|
if (!this.addParams.content) {
|
||||||
this.$message.warning(this.$t("backendSystem.pleaseInputContent"))
|
this.$message.warning(this.$t("backendSystem.pleaseInputContent"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((this.addParams.buttonContent && this.addParams.buttonPath) || (!this.addParams.buttonContent && !this.addParams.buttonPath)) {
|
||||||
|
|
||||||
|
if (this.addParams.buttonPath) {
|
||||||
|
// 兼容中英文逗号分割
|
||||||
|
let pathArray = this.addParams.buttonPath.split(/[,,]/).map(e => e.trim()).filter(e => e);
|
||||||
|
if (pathArray.length > 5) {
|
||||||
|
this.$message.warning(this.$t("backendSystem.max5"))
|
||||||
|
return
|
||||||
|
} else if (pathArray.length === 0) {
|
||||||
|
this.$message.warning(this.$t("backendSystem.pleaseInputButtonContentAndPath"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (this.addParams.buttonContent) {
|
||||||
|
// 兼容中英文逗号分割
|
||||||
|
let pathArray2 = this.addParams.buttonContent.split(/[,,]/).map(e => e.trim()).filter(e => e);
|
||||||
|
if (pathArray2.length > 5) {
|
||||||
|
this.$message.warning(this.$t("backendSystem.max5"))
|
||||||
|
return
|
||||||
|
} else if (pathArray2.length === 0) {
|
||||||
|
this.$message.warning(this.$t("backendSystem.pleaseInputButtonContentAndPath"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (this.addParams.buttonContent && this.addParams.buttonPath) {
|
||||||
|
if (pathArray.length !== pathArray2.length) {
|
||||||
|
this.$message.warning(this.$t("backendSystem.pathNumberNotMatch"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证每个路径是否有效
|
||||||
|
const invalidPaths = pathArray.filter(path => !isValidPath(path));
|
||||||
|
if (invalidPaths.length > 0) {
|
||||||
|
const suggestions = getPathSuggestions(invalidPaths[0]);
|
||||||
|
this.$message.error(`${this.$t("backendSystem.invalidPath")}: ${invalidPaths.join(', ')}。${this.$t("backendSystem.invalidPathSuggestions")}: ${suggestions.slice(0, 3).join(', ')}`);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证通过,保持字符串格式传给后端(不转换为数组)
|
||||||
|
// this.addParams.buttonPath 保持原始字符串格式
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.addBroadcast(this.addParams);
|
this.addBroadcast(this.addParams);
|
||||||
|
} else {
|
||||||
|
this.$message.warning(this.$t("backendSystem.pleaseInputButtonContentAndPath"))
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
},
|
},
|
||||||
sureEditBroadcast() {
|
sureEditBroadcast() {
|
||||||
this.editParams.content=this.editParams.content.trim()
|
this.editParams.content = this.editParams.content.trim()
|
||||||
this.editParams.content = this.editParams.content.replace(/[\r\n]/g, '');
|
this.editParams.content = this.editParams.content.replace(/[\r\n]/g, '');
|
||||||
|
let buttonContentArray
|
||||||
|
let pathArray
|
||||||
if (!this.editParams.content) {
|
if (!this.editParams.content) {
|
||||||
this.$message.warning(this.$t("backendSystem.pleaseInputContent"))
|
this.$message.warning(this.$t("backendSystem.pleaseInputContent"))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if (this.editParams.buttonContent) {
|
||||||
|
this.editParams.buttonContent = this.editParams.buttonContent.trim().replace(/[\r\n]/g, '');
|
||||||
|
|
||||||
|
// 验证按钮内容和路径是否匹配
|
||||||
|
if ((this.editParams.buttonContent && this.editParams.buttonPath) || (!this.editParams.buttonContent && !this.editParams.buttonPath)) {
|
||||||
|
if (this.editParams.buttonContent) {
|
||||||
|
// 兼容中英文逗号分割
|
||||||
|
buttonContentArray = this.editParams.buttonContent.split(/[,,]/).map(e => e.trim()).filter(e => e);
|
||||||
|
if (buttonContentArray.length > 5) {
|
||||||
|
this.$message.warning(this.$t("backendSystem.max5"))
|
||||||
|
return
|
||||||
|
} else if (buttonContentArray.length === 0) {
|
||||||
|
this.$message.warning(this.$t("backendSystem.pleaseInputButtonContentAndPath"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有按钮路径,进行验证
|
||||||
|
if (this.editParams.buttonPath) {
|
||||||
|
this.editParams.buttonPath = this.editParams.buttonPath.trim().replace(/[\r\n]/g, '');
|
||||||
|
|
||||||
|
// 验证按钮内容和路径是否匹配
|
||||||
|
if ((this.editParams.buttonContent && this.editParams.buttonPath) || (!this.editParams.buttonContent && !this.editParams.buttonPath)) {
|
||||||
|
if (this.editParams.buttonPath) {
|
||||||
|
// 兼容中英文逗号分割
|
||||||
|
pathArray = this.editParams.buttonPath.split(/[,,]/).map(e => e.trim()).filter(e => e);
|
||||||
|
if (pathArray.length > 5) {
|
||||||
|
this.$message.warning(this.$t("backendSystem.max5"))
|
||||||
|
return
|
||||||
|
} else if (pathArray.length === 0) {
|
||||||
|
this.$message.warning(this.$t("backendSystem.pleaseInputButtonContentAndPath"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 验证每个路径是否有效
|
||||||
|
const invalidPaths = pathArray.filter(path => !isValidPath(path));
|
||||||
|
if (invalidPaths.length > 0) {
|
||||||
|
const suggestions = getPathSuggestions(invalidPaths[0]);
|
||||||
|
this.$message.error(`${this.$t("backendSystem.invalidPath")}: ${invalidPaths.join(', ')}。${this.$t("backendSystem.invalidPathSuggestions")}: ${suggestions.slice(0, 3).join(', ')}`);
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.$message.warning(this.$t("backendSystem.pleaseInputButtonContentAndPath"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.editParams.buttonContent && this.editParams.buttonPath) {
|
||||||
|
if (buttonContentArray.length !== pathArray.length) {
|
||||||
|
this.$message.warning(this.$t("backendSystem.pathNumberNotMatch"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
this.$message.warning(this.$t("backendSystem.pleaseInputButtonContentAndPath"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.editBroadcast(this.editParams);
|
this.editBroadcast(this.editParams);
|
||||||
},
|
},
|
||||||
handleEdit(row) {
|
handleEdit(row) {
|
||||||
|
@ -140,6 +292,8 @@ export default {
|
||||||
handleEditClose() {
|
handleEditClose() {
|
||||||
this.editDialogVisible = false;
|
this.editDialogVisible = false;
|
||||||
this.editParams.content = ""
|
this.editParams.content = ""
|
||||||
|
this.editParams.buttonContent = ""
|
||||||
|
this.editParams.buttonPath = ""
|
||||||
this.setLoading('editLoading', false);
|
this.setLoading('editLoading', false);
|
||||||
},
|
},
|
||||||
handelDelete(row) {
|
handelDelete(row) {
|
||||||
|
@ -202,9 +356,67 @@ export default {
|
||||||
console.log(`当前页: ${val}`);
|
console.log(`当前页: ${val}`);
|
||||||
this.listParams.pageNum = val
|
this.listParams.pageNum = val
|
||||||
this.fetchList(this.listParams);
|
this.fetchList(this.listParams);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取有效路径建议信息
|
||||||
|
* @returns {Array} 路径建议列表
|
||||||
|
*/
|
||||||
|
getValidPathSuggestions() {
|
||||||
|
return getPathSuggestions('').slice(0, 8); // 返回前8个常用路径
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 显示路径使用帮助
|
||||||
|
*/
|
||||||
|
showPathHelp() {
|
||||||
|
const suggestions = this.getValidPathSuggestions();
|
||||||
|
const helpMessage = `
|
||||||
|
常用路径示例:
|
||||||
|
${suggestions.slice(0, 10).join('\n')}
|
||||||
|
|
||||||
|
路径规则:
|
||||||
|
• 多个路径用逗号分隔
|
||||||
|
• 路径必须以 / 开头
|
||||||
|
• 支持动态路由参数
|
||||||
|
• 不要包含域名,只写路径部分
|
||||||
|
`;
|
||||||
|
|
||||||
|
this.$alert(helpMessage, '路径格式说明', {
|
||||||
|
confirmButtonText: '知道了',
|
||||||
|
type: 'info',
|
||||||
|
customClass: 'path-help-dialog'
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化按钮内容显示
|
||||||
|
* @param {string} buttonContent - 逗号分隔的按钮内容字符串
|
||||||
|
* @returns {Array} 按钮内容数组
|
||||||
|
*/
|
||||||
|
formatButtonContent(buttonContent) {
|
||||||
|
if (!buttonContent || typeof buttonContent !== 'string') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return buttonContent
|
||||||
|
.split(',')
|
||||||
|
.map(btn => btn.trim())
|
||||||
|
.filter(btn => btn);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 格式化按钮路径显示
|
||||||
|
* @param {string} buttonPath - 逗号分隔的路径字符串
|
||||||
|
* @returns {Array} 路径数组
|
||||||
|
*/
|
||||||
|
formatButtonPath(buttonPath) {
|
||||||
|
if (!buttonPath || typeof buttonPath !== 'string') {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return buttonPath
|
||||||
|
.split(',')
|
||||||
|
.map(path => path.trim())
|
||||||
|
.filter(path => path);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,48 @@
|
||||||
width="160"
|
width="160"
|
||||||
show-overflow-tooltip
|
show-overflow-tooltip
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<el-table-column
|
||||||
|
prop="buttonContent"
|
||||||
|
:label="$t('backendSystem.bthContent2')"
|
||||||
|
width="200"
|
||||||
|
show-overflow-tooltip
|
||||||
|
|
||||||
|
>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div v-if="scope.row.buttonContent">
|
||||||
|
<el-tag
|
||||||
|
v-for="(button, index) in formatButtonContent(scope.row.buttonContent)"
|
||||||
|
:key="index"
|
||||||
|
size="small"
|
||||||
|
style="margin: 2px;"
|
||||||
|
>
|
||||||
|
{{ button }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<span v-else style="color: #999;">无</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column
|
||||||
|
prop="buttonPath"
|
||||||
|
:label="$t('backendSystem.bthPath2')"
|
||||||
|
width="250"
|
||||||
|
show-overflow-tooltip
|
||||||
|
>
|
||||||
|
<template slot-scope="scope">
|
||||||
|
<div v-if="scope.row.buttonPath">
|
||||||
|
<div
|
||||||
|
v-for="(path, index) in formatButtonPath(scope.row.buttonPath)"
|
||||||
|
:key="index"
|
||||||
|
class="path-item"
|
||||||
|
style="font-size: 12px;"
|
||||||
|
>
|
||||||
|
{{ index + 1 }}. {{ path }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span v-else style="color: #999;">无</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
prop="updateTime"
|
prop="updateTime"
|
||||||
:label="$t('backendSystem.updateTime')"
|
:label="$t('backendSystem.updateTime')"
|
||||||
|
@ -101,9 +143,9 @@
|
||||||
:before-close="handleClose"
|
:before-close="handleClose"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
>
|
>
|
||||||
<el-form :model="addParams">
|
<el-form :model="addParams" ref="addForm">
|
||||||
<!-- v-if="isOverLimit" -->
|
<!-- v-if="isOverLimit" -->
|
||||||
<el-form-item>
|
<el-form-item :label="$t('backendSystem.broadcastContent')" prop="content">
|
||||||
<el-input
|
<el-input
|
||||||
resize="none"
|
resize="none"
|
||||||
v-model="addParams.content"
|
v-model="addParams.content"
|
||||||
|
@ -127,6 +169,43 @@
|
||||||
<span> {{ $t("backendSystem.newlineInvalid") }}</span>
|
<span> {{ $t("backendSystem.newlineInvalid") }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="$t('backendSystem.bthContent')" prop="buttonContent">
|
||||||
|
<el-input
|
||||||
|
resize="none"
|
||||||
|
v-model="addParams.buttonContent"
|
||||||
|
type="text"
|
||||||
|
|
||||||
|
/>
|
||||||
|
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="$t('backendSystem.bthPath')" prop="buttonPath">
|
||||||
|
<el-input
|
||||||
|
resize="none"
|
||||||
|
v-model="addParams.buttonPath"
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span v-if="isOverLimit">
|
||||||
|
{{ $t("backendSystem.exceedingInput") }}</span
|
||||||
|
>
|
||||||
|
<span> {{ $t("backendSystem.bthPathStandard") }}</span>
|
||||||
|
<el-button type="text" size="mini" @click="showPathHelp">
|
||||||
|
查看路径示例
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button @click="handleClose">{{
|
<el-button @click="handleClose">{{
|
||||||
|
@ -140,6 +219,8 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
|
|
||||||
<!-- 修改广播弹窗 -->
|
<!-- 修改广播弹窗 -->
|
||||||
<el-dialog
|
<el-dialog
|
||||||
:title="$t('backendSystem.editContent')"
|
:title="$t('backendSystem.editContent')"
|
||||||
|
@ -149,7 +230,7 @@
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
>
|
>
|
||||||
<el-form :model="editParams">
|
<el-form :model="editParams">
|
||||||
<el-form-item>
|
<el-form-item :label="$t('backendSystem.broadcastContent')">
|
||||||
<el-input
|
<el-input
|
||||||
resize="none"
|
resize="none"
|
||||||
v-model="editParams.content"
|
v-model="editParams.content"
|
||||||
|
@ -173,6 +254,36 @@
|
||||||
<span> {{ $t("backendSystem.newlineInvalid") }}</span>
|
<span> {{ $t("backendSystem.newlineInvalid") }}</span>
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="$t('backendSystem.bthContent')">
|
||||||
|
<el-input
|
||||||
|
v-model="editParams.buttonContent"
|
||||||
|
type="text"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item :label="$t('backendSystem.bthPath')">
|
||||||
|
<el-input
|
||||||
|
v-model="editParams.buttonPath"
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
style="
|
||||||
|
color: #999;
|
||||||
|
font-size: 12px;
|
||||||
|
margin-top: 4px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<span>{{ $t("backendSystem.bthPathStandard") }}</span>
|
||||||
|
<el-button type="text" size="mini" @click="showPathHelp">
|
||||||
|
查看路径示例
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<div slot="footer" class="dialog-footer">
|
<div slot="footer" class="dialog-footer">
|
||||||
<el-button @click="handleEditClose">{{
|
<el-button @click="handleEditClose">{{
|
||||||
|
@ -231,4 +342,41 @@ export default {
|
||||||
border: none;
|
border: none;
|
||||||
margin-left: 18px;
|
margin-left: 18px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 按钮内容和路径显示优化 */
|
||||||
|
.el-table .cell {
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 路径帮助弹窗样式 */
|
||||||
|
::v-deep .path-help-dialog {
|
||||||
|
.el-message-box__message {
|
||||||
|
white-space: pre-line;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮标签样式优化 */
|
||||||
|
.el-tag {
|
||||||
|
max-width: 80px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 路径显示样式 */
|
||||||
|
.path-item {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
background: #f5f7fa;
|
||||||
|
padding: 2px 6px;
|
||||||
|
border-radius: 3px;
|
||||||
|
margin: 1px 0;
|
||||||
|
border-left: 3px solid #409EFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
::v-deep .el-tag{
|
||||||
|
max-width: 200px !important;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
|
@ -0,0 +1,162 @@
|
||||||
|
# 客服系统Socket连接优化说明
|
||||||
|
|
||||||
|
## 优化概述
|
||||||
|
|
||||||
|
本次优化采用**简洁的路由控制方案**,实现Socket连接的智能管理:
|
||||||
|
- **进入页面**:在`mounted`生命周期中自动建立Socket连接
|
||||||
|
- **离开页面**:在`beforeRouteLeave`路由守卫中立即断开所有Socket连接
|
||||||
|
|
||||||
|
## 核心功能
|
||||||
|
|
||||||
|
### 🎯 简洁实现
|
||||||
|
- **页面加载时**:自动建立Socket连接
|
||||||
|
- **路由离开时**:立即断开所有Socket连接
|
||||||
|
- **组件销毁时**:全面清理资源
|
||||||
|
|
||||||
|
## 技术实现
|
||||||
|
|
||||||
|
### 核心生命周期
|
||||||
|
|
||||||
|
#### `mounted` - 建立连接
|
||||||
|
```javascript
|
||||||
|
async mounted() {
|
||||||
|
// 获取聊天室列表
|
||||||
|
await this.fetchRoomList();
|
||||||
|
|
||||||
|
// 设置事件监听器
|
||||||
|
// ...其他初始化代码
|
||||||
|
|
||||||
|
// 添加存储变化监听
|
||||||
|
window.addEventListener("storage", this.handleStorageChange);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `beforeRouteLeave` - 断开连接
|
||||||
|
```javascript
|
||||||
|
beforeRouteLeave(to, from, next) {
|
||||||
|
console.log("🚪 离开客服页面,断开Socket连接");
|
||||||
|
this.disconnectWebSocket();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### `beforeDestroy` - 清理资源
|
||||||
|
```javascript
|
||||||
|
beforeDestroy() {
|
||||||
|
console.log("🧹 组件销毁,清理资源");
|
||||||
|
|
||||||
|
// 断开Socket连接
|
||||||
|
this.disconnectWebSocket();
|
||||||
|
|
||||||
|
// 清理定时器
|
||||||
|
// 移除事件监听
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用场景
|
||||||
|
|
||||||
|
### 1. 页面跳转 🚪
|
||||||
|
- 用户从任何页面进入客服页面:自动建立连接
|
||||||
|
- 用户从客服页面跳转到其他页面:立即断开连接
|
||||||
|
|
||||||
|
### 2. 浏览器操作 🔄
|
||||||
|
- 刷新页面:重新建立连接
|
||||||
|
- 关闭标签页:自动断开连接
|
||||||
|
|
||||||
|
### 3. 多窗口管理 🪟
|
||||||
|
- 每个窗口独立管理自己的连接
|
||||||
|
- 不会相互干扰
|
||||||
|
|
||||||
|
## 性能优势
|
||||||
|
|
||||||
|
### 1. **简洁高效** ⚡
|
||||||
|
- 代码量最少,逻辑最清晰
|
||||||
|
- 没有复杂的页面可见性检测
|
||||||
|
- 没有多余的活动时间监测
|
||||||
|
|
||||||
|
### 2. **资源节省** 💾
|
||||||
|
- 确保离开页面时立即断开连接
|
||||||
|
- 避免僵尸连接占用服务器资源
|
||||||
|
- 减少不必要的网络活动
|
||||||
|
|
||||||
|
### 3. **稳定可靠** 🛡️
|
||||||
|
- 路由级别的控制更加可靠
|
||||||
|
- 避免了复杂逻辑带来的潜在bug
|
||||||
|
- 易于维护和调试
|
||||||
|
|
||||||
|
## 关键代码
|
||||||
|
|
||||||
|
### Socket连接管理
|
||||||
|
```javascript
|
||||||
|
// 建立连接(在 created 中调用)
|
||||||
|
this.initWebSocket();
|
||||||
|
|
||||||
|
// 断开连接(在路由守卫中调用)
|
||||||
|
this.disconnectWebSocket();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 路由守卫
|
||||||
|
```javascript
|
||||||
|
beforeRouteLeave(to, from, next) {
|
||||||
|
console.log("🚪 离开客服页面,断开Socket连接");
|
||||||
|
this.disconnectWebSocket();
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 调试监控
|
||||||
|
|
||||||
|
### 控制台日志
|
||||||
|
```
|
||||||
|
🚪 离开客服页面,断开Socket连接
|
||||||
|
🧹 组件销毁,清理资源
|
||||||
|
🎉 [客服系统] WebSocket 连接成功
|
||||||
|
```
|
||||||
|
|
||||||
|
### 连接状态
|
||||||
|
- `isWebSocketConnected`: WebSocket连接状态
|
||||||
|
- `connectionStatus`: 应用层连接状态
|
||||||
|
|
||||||
|
## 优化亮点
|
||||||
|
|
||||||
|
### 1. **极简设计** ✨
|
||||||
|
- 移除了复杂的页面可见性检测
|
||||||
|
- 移除了用户活动时间监测
|
||||||
|
- 移除了多余的重连逻辑
|
||||||
|
|
||||||
|
### 2. **路由驱动** 🛣️
|
||||||
|
- 利用Vue Router的生命周期
|
||||||
|
- 在路由层面控制连接状态
|
||||||
|
- 更符合SPA应用的设计理念
|
||||||
|
|
||||||
|
### 3. **资源清理** 🧹
|
||||||
|
- 完善的组件销毁清理逻辑
|
||||||
|
- 确保没有内存泄漏
|
||||||
|
- 移除所有事件监听器
|
||||||
|
|
||||||
|
## 对比优势
|
||||||
|
|
||||||
|
| 特性 | 复杂方案 | 简洁方案 ✅ |
|
||||||
|
|------|---------|------------|
|
||||||
|
| 代码量 | 大量额外代码 | 最少必要代码 |
|
||||||
|
| 可维护性 | 复杂难维护 | 简单易维护 |
|
||||||
|
| 性能开销 | 需要监听多个事件 | 几乎无开销 |
|
||||||
|
| 可靠性 | 依赖浏览器API | 依赖路由机制 |
|
||||||
|
| 调试难度 | 复杂状态管理 | 简单直观 |
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **连接时机**:Socket连接在组件`created`时建立
|
||||||
|
2. **断开时机**:在路由离开时立即断开
|
||||||
|
3. **资源清理**:组件销毁时进行全面清理
|
||||||
|
4. **兼容性**:依赖Vue Router,适用于所有现代浏览器
|
||||||
|
|
||||||
|
## 总结
|
||||||
|
|
||||||
|
这个简洁的路由控制方案完美实现了用户的需求:
|
||||||
|
- ✅ **在当前页面才连接Socket**
|
||||||
|
- ✅ **离开页面就断开所有Socket连接**
|
||||||
|
- ✅ **不加多余代码,简洁实现**
|
||||||
|
|
||||||
|
通过移除复杂的页面可见性检测和用户活动监测,我们得到了一个更加稳定、高效、易维护的解决方案。
|
|
@ -408,6 +408,7 @@ export default {
|
||||||
hasMoreHistory: true, // 是否还有更多历史消息
|
hasMoreHistory: true, // 是否还有更多历史消息
|
||||||
noMoreHistoryMessage: "", // 无更多历史消息时的提示文字
|
noMoreHistoryMessage: "", // 无更多历史消息时的提示文字
|
||||||
networkStatus: "online",
|
networkStatus: "online",
|
||||||
|
token:"",
|
||||||
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -432,6 +433,23 @@ export default {
|
||||||
return this.messages[this.currentContactId] || [];
|
return this.messages[this.currentContactId] || [];
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
watch: {
|
||||||
|
token: {
|
||||||
|
handler(newVal) {
|
||||||
|
if (!newVal) {
|
||||||
|
console.log("token已过期,断开所有窗口连接");
|
||||||
|
|
||||||
|
// 通知所有窗口断开连接
|
||||||
|
localStorage.setItem('cs_disconnect_all', Date.now().toString());
|
||||||
|
|
||||||
|
// 断开当前窗口连接
|
||||||
|
this.disconnectWebSocket();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deep: true,
|
||||||
|
immediate: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
created() {
|
created() {
|
||||||
try {
|
try {
|
||||||
|
@ -454,17 +472,21 @@ export default {
|
||||||
console.error("初始化失败:", error);
|
console.error("初始化失败:", error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
mounted() {
|
||||||
|
|
||||||
|
|
||||||
// 获取聊天室列表
|
// 获取聊天室列表
|
||||||
await this.fetchRoomList();
|
this.fetchRoomList();
|
||||||
|
|
||||||
let userEmail = localStorage.getItem("userEmail");
|
let userEmail = localStorage.getItem("userEmail");
|
||||||
this.userEmail = JSON.parse(userEmail);
|
this.userEmail = JSON.parse(userEmail);
|
||||||
|
let token = localStorage.getItem("token");
|
||||||
|
this.token = JSON.parse(token);
|
||||||
window.addEventListener("setItem", () => {
|
window.addEventListener("setItem", () => {
|
||||||
let userEmail = localStorage.getItem("userEmail");
|
let userEmail = localStorage.getItem("userEmail");
|
||||||
this.userEmail = JSON.parse(userEmail);
|
this.userEmail = JSON.parse(userEmail);
|
||||||
|
let token = localStorage.getItem("token");
|
||||||
|
this.token = JSON.parse(token);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 添加网络状态变化监听
|
// 添加网络状态变化监听
|
||||||
|
@ -3671,6 +3693,8 @@ export default {
|
||||||
* 监听storage事件,实现多窗口未读同步和登录状态同步
|
* 监听storage事件,实现多窗口未读同步和登录状态同步
|
||||||
*/
|
*/
|
||||||
handleStorageChange(e) {
|
handleStorageChange(e) {
|
||||||
|
console.log("监听所有窗口状态统一 666",e);
|
||||||
|
|
||||||
// 监听未读消息同步
|
// 监听未读消息同步
|
||||||
if (e.key && e.key.startsWith("cs_unread_")) {
|
if (e.key && e.key.startsWith("cs_unread_")) {
|
||||||
const roomId = e.key.replace("cs_unread_", "");
|
const roomId = e.key.replace("cs_unread_", "");
|
||||||
|
@ -3680,6 +3704,13 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 监听断开所有连接通知
|
||||||
|
if (e.key === "cs_disconnect_all" && e.newValue) {
|
||||||
|
console.log("收到断开所有连接通知");
|
||||||
|
this.disconnectWebSocket();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 监听登录状态同步 - 当userEmail被清除时表示用户已退出登录
|
// 监听登录状态同步 - 当userEmail被清除时表示用户已退出登录
|
||||||
if (e.key === "userEmail" && e.oldValue && (!e.newValue || e.newValue === "null")) {
|
if (e.key === "userEmail" && e.oldValue && (!e.newValue || e.newValue === "null")) {
|
||||||
this.handleLogoutSync();
|
this.handleLogoutSync();
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
export default{
|
||||||
|
name: 'HelpCenter',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
/** 搜索查询字符串 */
|
||||||
|
searchQuery: '',
|
||||||
|
/** 选中的语言 */
|
||||||
|
selectedLanguage: 'zh',
|
||||||
|
/** 分类按钮数据 */
|
||||||
|
categories: [
|
||||||
|
{ id: 1, name: 'API文档', route: 'apiFile' },
|
||||||
|
{ id: 2, name: '挖矿教程', route: 'AccessMiningPool/nexaAccess' },
|
||||||
|
{ id: 3, name: '服务条款', route: 'ServiceTerms' },
|
||||||
|
{ id: 3, name: '费率', route: 'rate' },
|
||||||
|
{ id: 4, name: '公告中心', route: 'announcements' },
|
||||||
|
|
||||||
|
],
|
||||||
|
/** 推荐内容数据 */
|
||||||
|
recommendedItems: [
|
||||||
|
{ id: 1, description: '矿池分配及转账规则' ,route:"allocationExplanation"},
|
||||||
|
// { id: 2, description: '余额不足如何任何偿还,该如何规划运营?' },
|
||||||
|
// { id: 3, description: '矿池选择它已经拥有综合的优势时间,怎么办?' }
|
||||||
|
],
|
||||||
|
/** 活动数据 */
|
||||||
|
activities: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
type: '最新公告',
|
||||||
|
title: 'LKY将停减税',
|
||||||
|
author: '文章ID1星期 主发布',
|
||||||
|
comments: '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
type: '最新公告',
|
||||||
|
title: 'PFP将停减税',
|
||||||
|
author: '文章ID1星期 11发布',
|
||||||
|
comments: '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
type: '最新公告',
|
||||||
|
title: 'ETC+ZIL停研发综合日获得',
|
||||||
|
author: '文章ID1星期 15发布',
|
||||||
|
comments: '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
type: '最新公告',
|
||||||
|
title: '预计2023年06月19日SCC收益分配说明',
|
||||||
|
author: '文章ID1星期 18发布',
|
||||||
|
comments: '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 5,
|
||||||
|
type: '其他',
|
||||||
|
title: '关于临时(信为网站、备案管理、矿池编码)',
|
||||||
|
author: '文章ID1星期 19发布',
|
||||||
|
comments: '0'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 处理搜索功能
|
||||||
|
*/
|
||||||
|
handleSearch() {
|
||||||
|
if (this.searchQuery.trim()) {
|
||||||
|
console.log('搜索内容:', this.searchQuery);
|
||||||
|
// 实际项目中这里应该调用搜索API
|
||||||
|
this.$message.success(`搜索: ${this.searchQuery}`);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理分类按钮点击
|
||||||
|
* @param {Object} category - 分类对象
|
||||||
|
*/
|
||||||
|
handleCategoryClick(category) {
|
||||||
|
console.log(category,"i肯定看到");
|
||||||
|
|
||||||
|
const lang = this.$i18n.locale;
|
||||||
|
this.$router.push(`/${lang}/${category.route}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理查看更多按钮点击
|
||||||
|
*/
|
||||||
|
handleViewMore() {
|
||||||
|
console.log('查看更多活动');
|
||||||
|
// 实际项目中这里应该跳转到活动列表页面
|
||||||
|
this.$message.info('查看更多活动');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,387 @@
|
||||||
|
<template>
|
||||||
|
<div class="help-center">
|
||||||
|
<!-- 顶部蓝色渐变背景区域 -->
|
||||||
|
<div class="top-section">
|
||||||
|
<div class="filter-icon">
|
||||||
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M3 7H21L15 13V19L9 15V13L3 7Z" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 搜索框 -->
|
||||||
|
<div class="search-container">
|
||||||
|
<input
|
||||||
|
v-model="searchQuery"
|
||||||
|
type="text"
|
||||||
|
placeholder="搜索"
|
||||||
|
class="search-input"
|
||||||
|
@keyup.enter="handleSearch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 主要内容区域 -->
|
||||||
|
<div class="main-content">
|
||||||
|
<!-- 分类按钮网格 -->
|
||||||
|
<div class="category-grid">
|
||||||
|
<button
|
||||||
|
v-for="category in categories"
|
||||||
|
:key="category.id"
|
||||||
|
class="category-btn"
|
||||||
|
@click="handleCategoryClick(category)"
|
||||||
|
@keydown.enter="handleCategoryClick(category)"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
{{ category.name }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 推荐内容区域 -->
|
||||||
|
<div class="recommended-section">
|
||||||
|
<h3 class="section-title">推荐的内容</h3>
|
||||||
|
<div class="recommended-items">
|
||||||
|
<div
|
||||||
|
v-for="item in recommendedItems"
|
||||||
|
:key="item.id"
|
||||||
|
class="recommended-item"
|
||||||
|
@click="handleCategoryClick(item)"
|
||||||
|
>
|
||||||
|
<p class="item-description" >{{ item.description }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 最近活动区域 -->
|
||||||
|
<div class="recent-activities">
|
||||||
|
<h3 class="section-title">最近的活动</h3>
|
||||||
|
<div class="activities-list">
|
||||||
|
<div
|
||||||
|
v-for="activity in activities"
|
||||||
|
:key="activity.id"
|
||||||
|
class="activity-item"
|
||||||
|
>
|
||||||
|
<div class="activity-header">
|
||||||
|
<span class="activity-badge">{{ activity.type }}</span>
|
||||||
|
</div>
|
||||||
|
<p class="activity-title">{{ activity.title }}</p>
|
||||||
|
<div class="activity-meta">
|
||||||
|
<span class="activity-author">{{ activity.author }}</span>
|
||||||
|
<span class="activity-comments">{{ activity.comments }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 查看更多链接 -->
|
||||||
|
<div class="view-more">
|
||||||
|
<a href="#" class="view-more-link" @click="handleViewMore">
|
||||||
|
查看更多活动中的更多
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部帮助中心链接 -->
|
||||||
|
<div class="bottom-section">
|
||||||
|
<div class="help-center-link">
|
||||||
|
<span>帮助中心</span>
|
||||||
|
<select v-model="selectedLanguage" class="language-select">
|
||||||
|
<option value="zh">简体中文</option>
|
||||||
|
<option value="en">English</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 帮助中心/知识库页面组件
|
||||||
|
* 提供搜索、分类浏览、推荐内容和最近活动功能
|
||||||
|
*/
|
||||||
|
import Index from './index.js'
|
||||||
|
export default {
|
||||||
|
mixins:[Index],
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
/**
|
||||||
|
* 帮助中心页面样式
|
||||||
|
*/
|
||||||
|
.help-center {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 顶部蓝色渐变背景区域 */
|
||||||
|
.top-section {
|
||||||
|
position: relative;
|
||||||
|
height: 200px;
|
||||||
|
background: linear-gradient(135deg, #5721E4 0%, #5721E4 50%, #F4BDD1 100%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 过滤器图标 */
|
||||||
|
.filter-icon {
|
||||||
|
position: absolute;
|
||||||
|
top: 20px;
|
||||||
|
right: 20px;
|
||||||
|
color: white;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: rgba(255, 255, 255, 0.1);
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-icon:hover {
|
||||||
|
background: rgba(255, 255, 255, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 搜索框容器 */
|
||||||
|
.search-container {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 搜索输入框 */
|
||||||
|
.search-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px 20px;
|
||||||
|
border: none;
|
||||||
|
border-radius: 25px;
|
||||||
|
background: rgba(255, 255, 255, 0.9);
|
||||||
|
font-size: 16px;
|
||||||
|
outline: none;
|
||||||
|
transition: background 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input:focus {
|
||||||
|
background: rgba(255, 255, 255, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-input::placeholder {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主要内容区域 */
|
||||||
|
.main-content {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 40px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分类按钮网格 */
|
||||||
|
.category-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 分类按钮 */
|
||||||
|
.category-btn {
|
||||||
|
padding: 20px;
|
||||||
|
border: 2px solid #e0e0e0;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: white;
|
||||||
|
color: #333;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
text-align: center;
|
||||||
|
min-height: 60px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-btn:hover {
|
||||||
|
border-color: #5721E4;
|
||||||
|
background: #f8f9ff;
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(42, 82, 152, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-btn:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #2a5298;
|
||||||
|
box-shadow: 0 0 0 3px rgba(42, 82, 152, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 章节标题 */
|
||||||
|
.section-title {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 推荐内容区域 */
|
||||||
|
.recommended-section {
|
||||||
|
margin-bottom: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommended-items {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommended-item {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommended-item:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.item-description {
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 最近活动区域 */
|
||||||
|
.recent-activities {
|
||||||
|
margin-bottom: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activities-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-item {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-item:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-header {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 4px 12px;
|
||||||
|
background: #e3f2fd;
|
||||||
|
color: #1976d2;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-meta {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-comments {
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 查看更多链接 */
|
||||||
|
.view-more {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 40px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-more-link {
|
||||||
|
color: #2a5298;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view-more-link:hover {
|
||||||
|
color: #1e3c72;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 底部区域 */
|
||||||
|
.bottom-section {
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 20px;
|
||||||
|
border-top: 1px solid #e0e0e0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-center-link {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.language-select {
|
||||||
|
padding: 5px 10px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: white;
|
||||||
|
color: #333;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 响应式设计 */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.category-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.recommended-items {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-content {
|
||||||
|
padding: 20px 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-container {
|
||||||
|
max-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.help-center-link {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.activity-meta {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -769,11 +769,15 @@ export default {
|
||||||
},
|
},
|
||||||
isInternalChange: false,
|
isInternalChange: false,
|
||||||
broadcastList: [
|
broadcastList: [
|
||||||
{ id: `b1`, content: this.$t(`home.describe`) },
|
{ id: `b1`, content: this.$t(`home.describe`),buttonContent:[this.$t(`home.view`)],buttonPath:["/allocationExplanation"] },
|
||||||
|
// { id: `b2`, content: `广播内容2测试` },
|
||||||
|
// { id: `b3`, content: `广播内容3测试` },
|
||||||
|
// { id: `b4`, content: `广播内容4测试` },
|
||||||
|
|
||||||
],
|
],
|
||||||
currentBroadcastIndex: 0,
|
currentBroadcastIndex: 0,
|
||||||
scrollTimer: null,
|
scrollTimer: null,
|
||||||
|
delayScrollTimer: null, // 移动端延迟滚动定时器
|
||||||
isTransition: true,
|
isTransition: true,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -790,6 +794,13 @@ export default {
|
||||||
transition: this.isTransition ? 'transform 0.5s cubic-bezier(.4,0,.2,1)' : 'none',
|
transition: this.isTransition ? 'transform 0.5s cubic-bezier(.4,0,.2,1)' : 'none',
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
// 移动端专用滚动样式
|
||||||
|
mobileScrollStyle() {
|
||||||
|
return {
|
||||||
|
transform: `translateY(-${this.currentBroadcastIndex * 40}px)`, // 移动端使用更大的行高
|
||||||
|
transition: this.isTransition ? 'transform 0.5s cubic-bezier(.4,0,.2,1)' : 'none',
|
||||||
|
};
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
"$i18n.locale": (val) => {
|
"$i18n.locale": (val) => {
|
||||||
|
@ -931,13 +942,73 @@ export default {
|
||||||
|
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* 处理广播项点击事件
|
||||||
|
*/
|
||||||
|
handleCustomClick(item) {
|
||||||
|
console.log('广播项被点击:', item);
|
||||||
|
this.$message.info(`广播: ${item.content}`);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理广播错误
|
||||||
|
*/
|
||||||
|
handleBroadcastError(error) {
|
||||||
|
console.error('广播组件错误:', error);
|
||||||
|
},
|
||||||
//获取广播列表
|
//获取广播列表
|
||||||
async getBroadcastList(params){
|
async getBroadcastList(params){
|
||||||
// const data = await listBroadcast(params)
|
// const data = await listBroadcast(params)
|
||||||
const data = await getBroadcast(params)
|
const data = await getBroadcast(params)
|
||||||
|
|
||||||
if (data && data.code == 200) {
|
if (data && data.code == 200) {
|
||||||
this.broadcastList =[...this.broadcastList,...data.data]
|
|
||||||
|
let broadcastList = data.data
|
||||||
|
|
||||||
|
// 处理接口返回的数据,将字符串格式的buttonContent和buttonPath转换为数组
|
||||||
|
broadcastList = broadcastList.map(item => {
|
||||||
|
// 处理 buttonContent
|
||||||
|
if (item.buttonContent) {
|
||||||
|
if (typeof item.buttonContent === 'string') {
|
||||||
|
// 将逗号分隔的字符串转换为数组,兼容中英文逗号
|
||||||
|
item.buttonContent = item.buttonContent.split(/[,,]/).map(btn => btn.trim()).filter(btn => btn);
|
||||||
|
} else if (!Array.isArray(item.buttonContent)) {
|
||||||
|
// 如果不是字符串也不是数组,转为空数组
|
||||||
|
item.buttonContent = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果 buttonContent 不存在,设为空数组
|
||||||
|
item.buttonContent = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理 buttonPath
|
||||||
|
if (item.buttonPath) {
|
||||||
|
if (typeof item.buttonPath === 'string') {
|
||||||
|
// 将逗号分隔的字符串转换为数组,兼容中英文逗号
|
||||||
|
item.buttonPath = item.buttonPath.split(/[,,]/).map(path => path.trim()).filter(path => path);
|
||||||
|
} else if (!Array.isArray(item.buttonPath)) {
|
||||||
|
// 如果不是字符串也不是数组,转为空数组
|
||||||
|
item.buttonPath = [];
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果 buttonPath 不存在,设为空数组
|
||||||
|
item.buttonPath = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保 buttonContent 和 buttonPath 长度一致
|
||||||
|
if (item.buttonContent.length !== item.buttonPath.length) {
|
||||||
|
const minLength = Math.min(item.buttonContent.length, item.buttonPath.length);
|
||||||
|
item.buttonContent = item.buttonContent.slice(0, minLength);
|
||||||
|
item.buttonPath = item.buttonPath.slice(0, minLength);
|
||||||
|
}
|
||||||
|
|
||||||
|
return item;
|
||||||
|
});
|
||||||
|
|
||||||
|
this.broadcastList =[...this.broadcastList,...broadcastList]
|
||||||
|
|
||||||
|
console.log(this.broadcastList,'this.broadcastList');
|
||||||
|
|
||||||
this.currentBroadcastIndex = 0; // 新数据时重置索引
|
this.currentBroadcastIndex = 0; // 新数据时重置索引
|
||||||
if(this.broadcastList.length > 1){
|
if(this.broadcastList.length > 1){
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
@ -1736,6 +1807,13 @@ scrollRight() {
|
||||||
if (this.scrollTimer) clearInterval(this.scrollTimer);
|
if (this.scrollTimer) clearInterval(this.scrollTimer);
|
||||||
this.scrollTimer = null;
|
this.scrollTimer = null;
|
||||||
},
|
},
|
||||||
|
// 移动端延迟重启滚动(触摸结束后1秒再重启)
|
||||||
|
startScrollDelayed() {
|
||||||
|
if (this.delayScrollTimer) clearTimeout(this.delayScrollTimer);
|
||||||
|
this.delayScrollTimer = setTimeout(() => {
|
||||||
|
this.startScroll();
|
||||||
|
}, 1000);
|
||||||
|
},
|
||||||
nextScroll() {
|
nextScroll() {
|
||||||
if (this.broadcastList.length <= 1) return;
|
if (this.broadcastList.length <= 1) return;
|
||||||
this.isTransition = true;
|
this.isTransition = true;
|
||||||
|
@ -1751,6 +1829,7 @@ scrollRight() {
|
||||||
},
|
},
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
if (this.scrollTimer) clearInterval(this.scrollTimer);
|
if (this.scrollTimer) clearInterval(this.scrollTimer);
|
||||||
|
if (this.delayScrollTimer) clearTimeout(this.delayScrollTimer);
|
||||||
|
|
||||||
},
|
},
|
||||||
}
|
}
|
|
@ -1,6 +1,85 @@
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div>
|
||||||
<section v-if="$isMobile">
|
<section v-if="$isMobile">
|
||||||
|
<!-- <HorizontalBroadcast
|
||||||
|
:broadcast-data="broadcastListForLoop"
|
||||||
|
:auto-fetch="false"
|
||||||
|
:lang="$i18n.locale"
|
||||||
|
title="提示"
|
||||||
|
:clickable="true"
|
||||||
|
@item-click="handleCustomClick"
|
||||||
|
@error="handleBroadcastError"
|
||||||
|
/> -->
|
||||||
|
|
||||||
|
<!-- 广播 -->
|
||||||
|
<!-- <section class="describeBox">
|
||||||
|
<p class="describe-row">
|
||||||
|
<i class="iconfont icon-tishishuoming"></i>
|
||||||
|
<span class="describeTitle">{{ $t(`home.describeTitle`) }}</span>
|
||||||
|
<span class="broadcast-scroll-wrap">
|
||||||
|
<div
|
||||||
|
class="broadcast-scroll-list"
|
||||||
|
:style="scrollStyle"
|
||||||
|
ref="scrollList"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="(item, idx) in broadcastListForLoop"
|
||||||
|
:key="item.id + '-' + idx"
|
||||||
|
class="broadcast-scroll-item"
|
||||||
|
>
|
||||||
|
{{ item.content }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</span>
|
||||||
|
<span class="view" @click="handelJump(`/allocationExplanation`)">
|
||||||
|
{{ $t(`home.view`) }}
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</section> -->
|
||||||
|
|
||||||
|
<section class="describeBox">
|
||||||
|
<div class="describe-row">
|
||||||
|
<div class="broadcast-scroll-wrap">
|
||||||
|
<div
|
||||||
|
class="broadcast-scroll-list"
|
||||||
|
@mouseenter="stopScroll"
|
||||||
|
@mouseleave="startScroll"
|
||||||
|
:style="scrollStyle"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-for="item in broadcastList"
|
||||||
|
:key="item.id"
|
||||||
|
class="broadcast-scroll-item"
|
||||||
|
>
|
||||||
|
<i class="iconfont icon-tishishuoming"></i>
|
||||||
|
<span class="describeTitle">{{
|
||||||
|
$t(`home.describeTitle`)
|
||||||
|
}}</span>
|
||||||
|
<span class="describeContent" :title="item.content"> {{ item.content }}</span>
|
||||||
|
|
||||||
|
|
||||||
|
<!-- 动态渲染多个按钮 -->
|
||||||
|
<div
|
||||||
|
class="button-group"
|
||||||
|
v-if="item.buttonContent && Array.isArray(item.buttonContent) && item.buttonContent.length > 0"
|
||||||
|
>
|
||||||
|
|
||||||
|
<span
|
||||||
|
v-for="(buttonText, buttonIndex) in item.buttonContent"
|
||||||
|
:title="buttonText"
|
||||||
|
:key="`button-${item.id}-${buttonIndex}`"
|
||||||
|
class="view"
|
||||||
|
@click.stop="handelJump(item.buttonPath[buttonIndex])"
|
||||||
|
>
|
||||||
|
{{ buttonText || $t(`home.view`) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<div class="imgTop">
|
<div class="imgTop">
|
||||||
<img
|
<img
|
||||||
src="../../assets/mobile/home/home.png"
|
src="../../assets/mobile/home/home.png"
|
||||||
|
@ -75,30 +154,8 @@
|
||||||
style="width: 100%; height: 100%; min-width: 200px; min-height: 380px"
|
style="width: 100%; height: 100%; min-width: 200px; min-height: 380px"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
<section class="describeBox">
|
|
||||||
<p class="describe-row">
|
|
||||||
<i class="iconfont icon-tishishuoming"></i>
|
|
||||||
<span class="describeTitle">{{ $t(`home.describeTitle`) }}</span>
|
|
||||||
<span class="broadcast-scroll-wrap">
|
|
||||||
<div
|
|
||||||
class="broadcast-scroll-list"
|
|
||||||
:style="scrollStyle"
|
|
||||||
ref="scrollList"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-for="(item, idx) in broadcastListForLoop"
|
|
||||||
:key="item.id + '-' + idx"
|
|
||||||
class="broadcast-scroll-item"
|
|
||||||
>
|
|
||||||
{{ item.content }}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</span>
|
|
||||||
<span class="view" @click="handelJump(`/allocationExplanation`)">
|
|
||||||
{{ $t(`home.view`) }}
|
|
||||||
</span>
|
|
||||||
</p>
|
|
||||||
</section>
|
|
||||||
<div class="miningPoolRight">
|
<div class="miningPoolRight">
|
||||||
<ul class="dataBlockBox">
|
<ul class="dataBlockBox">
|
||||||
<li class="dataBlock">
|
<li class="dataBlock">
|
||||||
|
@ -419,31 +476,58 @@
|
||||||
<!-- 广播 -->
|
<!-- 广播 -->
|
||||||
<section class="describeBox">
|
<section class="describeBox">
|
||||||
<div class="describe-row">
|
<div class="describe-row">
|
||||||
|
|
||||||
<div class="broadcast-scroll-wrap">
|
<div class="broadcast-scroll-wrap">
|
||||||
<div class="broadcast-scroll-list" @mouseenter="stopScroll"
|
<div
|
||||||
@mouseleave="startScroll" :style="scrollStyle">
|
class="broadcast-scroll-list"
|
||||||
|
@mouseenter="stopScroll"
|
||||||
|
@mouseleave="startScroll"
|
||||||
|
:style="scrollStyle"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="item in broadcastList"
|
v-for="item in broadcastList"
|
||||||
:key="item.id"
|
:key="item.id"
|
||||||
class="broadcast-scroll-item"
|
class="broadcast-scroll-item"
|
||||||
>
|
>
|
||||||
|
|
||||||
<i class="iconfont icon-tishishuoming"></i>
|
<i class="iconfont icon-tishishuoming"></i>
|
||||||
<span class="describeTitle">{{ $t(`home.describeTitle`) }}</span>
|
<span class="describeTitle">{{
|
||||||
<span :title="item.content"> {{ item.content }}</span>
|
$t(`home.describeTitle`)
|
||||||
<span
|
}}</span>
|
||||||
class="view"
|
<span class="describeContent" :title="item.content"> {{ item.content }}</span>
|
||||||
v-show="item.id == 'b1'"
|
|
||||||
@click="handelJump(`/allocationExplanation`)"
|
|
||||||
|
<!-- 动态渲染多个按钮 -->
|
||||||
|
<div
|
||||||
|
class="button-group"
|
||||||
|
v-if="item.buttonContent && Array.isArray(item.buttonContent) && item.buttonContent.length > 0"
|
||||||
>
|
>
|
||||||
{{ $t(`home.view`) }}
|
|
||||||
|
<span
|
||||||
|
v-for="(buttonText, buttonIndex) in item.buttonContent"
|
||||||
|
:title="buttonText"
|
||||||
|
:key="`button-${item.id}-${buttonIndex}`"
|
||||||
|
class="view"
|
||||||
|
@click.stop="handelJump(item.buttonPath[buttonIndex])"
|
||||||
|
>
|
||||||
|
{{ buttonText || $t(`home.view`) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
<!--
|
||||||
|
<HorizontalBroadcast
|
||||||
|
style="width: 72%;margin: 0 auto;margin-bottom: 10px;border-radius: 8px;"
|
||||||
|
:broadcast-data="broadcastListForLoop"
|
||||||
|
:auto-fetch="false"
|
||||||
|
:lang="$i18n.locale"
|
||||||
|
title="提示"
|
||||||
|
:clickable="true"
|
||||||
|
@item-click="handleCustomClick"
|
||||||
|
@error="handleBroadcastError"
|
||||||
|
/> -->
|
||||||
|
|
||||||
<div class="contentBox">
|
<div class="contentBox">
|
||||||
<!-- 算力图 -->
|
<!-- 算力图 -->
|
||||||
<el-row>
|
<el-row>
|
||||||
|
@ -866,7 +950,11 @@
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import IndexJs from "./index";
|
import IndexJs from "./index";
|
||||||
|
import HorizontalBroadcast from "../../components/HorizontalBroadcast.vue";
|
||||||
export default {
|
export default {
|
||||||
|
components: {
|
||||||
|
HorizontalBroadcast,
|
||||||
|
},
|
||||||
metaInfo: {
|
metaInfo: {
|
||||||
meta: [
|
meta: [
|
||||||
{
|
{
|
||||||
|
@ -900,32 +988,7 @@ export default {
|
||||||
#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 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -1378,33 +1441,137 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// .el-card{
|
// .describe-row {
|
||||||
// padding: 0 !important;
|
// width: 96% !important;
|
||||||
|
// font-size: 0.8rem;
|
||||||
|
// padding: 0 10px;
|
||||||
|
// span {
|
||||||
|
// display: inline-block;
|
||||||
// }
|
// }
|
||||||
// ::v-deep .el-card__body, .el-main{
|
|
||||||
// padding: 10px !important;
|
|
||||||
// }
|
// }
|
||||||
// .monitor-list{
|
.describeBox {
|
||||||
// width: 100% !important;
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
margin: 0 !important;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
overflow: hidden;
|
||||||
|
max-height: 100px;
|
||||||
|
|
||||||
|
p {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100% !important;
|
||||||
|
text-align: left;
|
||||||
|
padding: 2px 5px !important;
|
||||||
|
// background: #e7dff3;
|
||||||
|
border-radius: 0px !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center !important;
|
||||||
|
// background: palegoldenrod !important;
|
||||||
|
.describeTitle {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #5721e4;
|
||||||
|
max-width: 63px;
|
||||||
|
min-width: 41px !important;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
color: #6e3edb;
|
||||||
|
margin-right: 5px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
// letter-spacing: 2px;
|
||||||
|
.view {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #6e3edb;
|
||||||
|
// background: palegoldenrod;
|
||||||
|
}
|
||||||
|
.view:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.describe-row {
|
||||||
|
width:100% !important;
|
||||||
|
margin: 0 auto;
|
||||||
|
// background: #e7dff3;
|
||||||
|
border-radius: 0px !important;
|
||||||
|
// padding: 8px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: left;
|
||||||
|
// align-items: center;
|
||||||
|
// overflow: hidden;
|
||||||
|
padding: 5px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.broadcast-scroll-item {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center; /* 垂直居中对齐 */
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
// background: papayawhip;
|
||||||
|
}
|
||||||
|
|
||||||
|
.describeContent{
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 70%;
|
||||||
|
min-width: 100px !important;
|
||||||
|
// background: palegoldenrod;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: top; /* 修复 baseline 对齐问题 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.broadcast-scroll-list {
|
||||||
|
|
||||||
|
padding-left: 5px !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.view{
|
||||||
|
color: #5721e4;
|
||||||
|
// font-size: 0.85rem;
|
||||||
|
// margin-left: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 2px;
|
||||||
|
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
max-width: 8vw !important;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #5721e4;
|
||||||
|
color: #fff;
|
||||||
|
// border-color: #5721e4;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// .left{
|
|
||||||
// width: 25px !important;
|
|
||||||
// }
|
|
||||||
// .right{
|
|
||||||
// width: 25px !important;
|
|
||||||
// }
|
|
||||||
// i{
|
|
||||||
// font-size: 14px !important;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .list-item{
|
|
||||||
// img{
|
|
||||||
// width: 25px !important;
|
|
||||||
// }
|
|
||||||
// span{
|
|
||||||
// font-size: 0.75rem !important;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
@media screen and (min-width: 800px) and (max-width: 1279px) {
|
@media screen and (min-width: 800px) and (max-width: 1279px) {
|
||||||
.imgTop {
|
.imgTop {
|
||||||
|
@ -1420,32 +1587,7 @@ export default {
|
||||||
#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 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
@ -1896,33 +2038,123 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// .el-card{
|
.describeBox {
|
||||||
// padding: 0 !important;
|
width: 100%;
|
||||||
// }
|
text-align: center;
|
||||||
// ::v-deep .el-card__body, .el-main{
|
margin: 0 !important;
|
||||||
// padding: 10px !important;
|
font-size: 0.85rem;
|
||||||
// }
|
overflow: hidden;
|
||||||
// .monitor-list{
|
max-height: 100px;
|
||||||
// width: 100% !important;
|
|
||||||
|
p {
|
||||||
|
margin: 0 auto;
|
||||||
|
width: 100% !important;
|
||||||
|
text-align: left;
|
||||||
|
padding: 2px 5px !important;
|
||||||
|
// background: #e7dff3;
|
||||||
|
border-radius: 0px !important;
|
||||||
|
display: flex;
|
||||||
|
align-items: center !important;
|
||||||
|
// background: palegoldenrod !important;
|
||||||
|
.describeTitle {
|
||||||
|
font-weight: bold;
|
||||||
|
margin-right: 8px;
|
||||||
|
color: #5721e4;
|
||||||
|
max-width: 63px;
|
||||||
|
min-width: 41px !important;
|
||||||
|
}
|
||||||
|
i {
|
||||||
|
color: #6e3edb;
|
||||||
|
margin-right: 5px;
|
||||||
|
line-height: 18px;
|
||||||
|
}
|
||||||
|
// letter-spacing: 2px;
|
||||||
|
.view {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-left: 8px;
|
||||||
|
color: #6e3edb;
|
||||||
|
// background: palegoldenrod;
|
||||||
|
}
|
||||||
|
.view:hover {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.describe-row {
|
||||||
|
width:100% !important;
|
||||||
|
margin: 0 auto;
|
||||||
|
// background: #e7dff3;
|
||||||
|
border-radius: 0px !important;
|
||||||
|
// padding: 8px 20px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: left;
|
||||||
|
// align-items: center;
|
||||||
|
// overflow: hidden;
|
||||||
|
padding: 5px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.broadcast-scroll-item {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center; /* 垂直居中对齐 */
|
||||||
|
height: 30px;
|
||||||
|
line-height: 30px;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
// background: papayawhip;
|
||||||
|
}
|
||||||
|
|
||||||
|
.describeContent{
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 70%;
|
||||||
|
min-width: 100px !important;
|
||||||
|
// background: palegoldenrod;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: top; /* 修复 baseline 对齐问题 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.broadcast-scroll-list {
|
||||||
|
|
||||||
|
padding-left: 5px !important;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.view{
|
||||||
|
color: #5721e4;
|
||||||
|
// font-size: 0.85rem;
|
||||||
|
// margin-left: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 2px;
|
||||||
|
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
max-width: 8vw !important;
|
||||||
|
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #5721e4;
|
||||||
|
color: #fff;
|
||||||
|
// border-color: #5721e4;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// .left{
|
|
||||||
// width: 25px !important;
|
|
||||||
// }
|
|
||||||
// .right{
|
|
||||||
// width: 25px !important;
|
|
||||||
// }
|
|
||||||
// i{
|
|
||||||
// font-size: 14px !important;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .list-item{
|
|
||||||
// img{
|
|
||||||
// width: 25px !important;
|
|
||||||
// }
|
|
||||||
// span{
|
|
||||||
// font-size: 0.75rem !important;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 500px) and (max-width: 800px) {
|
@media screen and (min-width: 500px) and (max-width: 800px) {
|
||||||
|
@ -2286,127 +2518,7 @@ export default {
|
||||||
justify-content: start;
|
justify-content: start;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
// background: #2EAEFF;
|
|
||||||
|
|
||||||
// .currencyBox {
|
|
||||||
// width: 75%;
|
|
||||||
// // background: palegoldenrod;
|
|
||||||
// // background: gold;
|
|
||||||
// display: flex;
|
|
||||||
// justify-content: center;
|
|
||||||
// align-items: center;
|
|
||||||
// boRder:1PX solid #ccc;
|
|
||||||
// box-shadow: 5PX 5PX 10PX 2PX #ccc;
|
|
||||||
// padding: 5PX 40PX;
|
|
||||||
// position: relative;
|
|
||||||
// border-radius:10PX;
|
|
||||||
// // overflow: auto;
|
|
||||||
// // background: #B6E6F1;
|
|
||||||
|
|
||||||
// .list-box{
|
|
||||||
// width: calc(100vw - 100PX);
|
|
||||||
|
|
||||||
// // overflow: hidden;
|
|
||||||
// // background: #5721E4;
|
|
||||||
// }
|
|
||||||
// .rollingBox{
|
|
||||||
// display: flex;
|
|
||||||
// width: calc(100vw - 100PX);
|
|
||||||
// // background: #433278;
|
|
||||||
// // min-width: 130%;
|
|
||||||
// justify-content: center;
|
|
||||||
// transform: all 2s;
|
|
||||||
// position: relative;
|
|
||||||
// left: 0;
|
|
||||||
// transition: left 1s;
|
|
||||||
// }
|
|
||||||
// .leftSliding{
|
|
||||||
// position:absolute;
|
|
||||||
// left: 0;
|
|
||||||
// top: 0;
|
|
||||||
// width: 3%;
|
|
||||||
// height: 100%;
|
|
||||||
// background: #D2C3E9;
|
|
||||||
// display: flex;
|
|
||||||
// align-items: center;
|
|
||||||
// justify-content: center;
|
|
||||||
// z-index: 99;
|
|
||||||
// i{
|
|
||||||
// font-size: 1.5em;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .rightSliding{
|
|
||||||
// position:absolute;
|
|
||||||
// top: 0;
|
|
||||||
// right: 0;
|
|
||||||
// width: 3%;
|
|
||||||
// height: 100%;
|
|
||||||
// background: #D2C3E9;
|
|
||||||
// display: flex;
|
|
||||||
// align-items: center;
|
|
||||||
// justify-content: center;
|
|
||||||
// i{
|
|
||||||
// font-size: 1.5em;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .sunCurrency {
|
|
||||||
// width: 7%;
|
|
||||||
// display: flex;
|
|
||||||
// justify-content: space-between;
|
|
||||||
// align-items: center;
|
|
||||||
// flex-direction: column;
|
|
||||||
// padding: 5PX 2PX;
|
|
||||||
// border-radius: 22PX;
|
|
||||||
// // border: 1PX solid rgba(223, 83, 52, 0.3);
|
|
||||||
// cursor: pointer;
|
|
||||||
// font-weight: 600;
|
|
||||||
// margin-left: 5PX;
|
|
||||||
// // width: 9%;
|
|
||||||
// // background: rgba(223, 83, 52, 0.05);
|
|
||||||
// height: 70PX;
|
|
||||||
// font-size: 0.7em;
|
|
||||||
// transition: all 0.2s linear;
|
|
||||||
// // background: #433278;
|
|
||||||
|
|
||||||
// img {
|
|
||||||
// width: 35PX;
|
|
||||||
// margin-left: 5%;
|
|
||||||
// }
|
|
||||||
// span{
|
|
||||||
// text-transform: uppercase;
|
|
||||||
// // margin-top: 5PX;
|
|
||||||
// // background: gold;
|
|
||||||
// width:90%;
|
|
||||||
// text-align: center;
|
|
||||||
// // height: 25PX;
|
|
||||||
// display: flex;
|
|
||||||
// align-items: center;
|
|
||||||
// justify-content: center;
|
|
||||||
// padding: 5PX 3PX;
|
|
||||||
// border-radius: 5PX;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// .sunCurrency:hover {
|
|
||||||
// // background: rgba(223, 83, 52, 0.05);
|
|
||||||
// // background: #D2C3E9;
|
|
||||||
// // font-size: 0.8em;
|
|
||||||
// span{
|
|
||||||
// background: #6E3EDB;
|
|
||||||
// color: #fff;
|
|
||||||
// }
|
|
||||||
// img {
|
|
||||||
// width: 30PX;
|
|
||||||
// margin-left: 5%;
|
|
||||||
// }
|
|
||||||
// // box-shadow: 0PX 0PX 5PX 3PX rgba(223, 83, 52, 0.05);
|
|
||||||
// // span{
|
|
||||||
// // background: #621BFF;
|
|
||||||
// // color: #fff;
|
|
||||||
// // border-radius: 10%;
|
|
||||||
|
|
||||||
// // }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
.currencyDescription2 {
|
.currencyDescription2 {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
// height: 900%;
|
// height: 900%;
|
||||||
|
@ -3369,17 +3481,7 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.describeBox {
|
|
||||||
margin: 16px 0;
|
|
||||||
// padding: 8px 16px;
|
|
||||||
border-radius: 8px;
|
|
||||||
font-size: 15px;
|
|
||||||
color: #333;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
min-height: 40px;
|
|
||||||
// background: pink;
|
|
||||||
}
|
|
||||||
.describe-row {
|
.describe-row {
|
||||||
width: 73%;
|
width: 73%;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
@ -3415,21 +3517,29 @@ export default {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
// margin-left: 8px;
|
// margin-left: 8px;
|
||||||
padding-left: 18px;
|
padding-left: 18px;
|
||||||
|
|
||||||
}
|
}
|
||||||
.broadcast-scroll-item {
|
.broadcast-scroll-item {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: inline-block;
|
display: flex;
|
||||||
|
align-items: center; /* 垂直居中对齐 */
|
||||||
height: 30px;
|
height: 30px;
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
// background: papayawhip;
|
// background: papayawhip;
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.describeContent{
|
||||||
|
display: inline-block;
|
||||||
|
max-width: 70%;
|
||||||
|
// background: palegoldenrod;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
vertical-align: top; /* 修复 baseline 对齐问题 */
|
||||||
|
}
|
||||||
|
|
||||||
.describeTitle {
|
.describeTitle {
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
color: #6e3edb;
|
color: #6e3edb;
|
||||||
|
@ -3448,6 +3558,48 @@ i {
|
||||||
}
|
}
|
||||||
.view:hover {
|
.view:hover {
|
||||||
color: #000;
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 按钮组样式 */
|
||||||
|
.button-group {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
// margin-left: 10px;
|
||||||
|
gap: 0px; /* 按钮之间的间距 */
|
||||||
|
}
|
||||||
|
|
||||||
|
.horizontal-item-separator {
|
||||||
|
color: #ccc;
|
||||||
|
margin: 0 15px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.view{
|
||||||
|
color: #5721e4;
|
||||||
|
// font-size: 0.85rem;
|
||||||
|
// margin-left: 5px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 2px;
|
||||||
|
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
max-width: 80px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #5721e4;
|
||||||
|
color: #fff;
|
||||||
|
// border-color: #5721e4;
|
||||||
|
transform: translateY(-1px);
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -115,7 +115,7 @@
|
||||||
</el-form>
|
</el-form>
|
||||||
</section> -->
|
</section> -->
|
||||||
|
|
||||||
|
<div class="time-box">
|
||||||
<el-date-picker
|
<el-date-picker
|
||||||
v-model="lineChatTimes"
|
v-model="lineChatTimes"
|
||||||
type="daterange"
|
type="daterange"
|
||||||
|
@ -129,6 +129,9 @@
|
||||||
>
|
>
|
||||||
</el-date-picker>
|
</el-date-picker>
|
||||||
|
|
||||||
|
<div>{{$t('backendSystem.walletBalance')}}:{{ userData.accountBalance || 0 }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
:data="tableData"
|
:data="tableData"
|
||||||
|
@ -151,7 +154,7 @@
|
||||||
<el-table-column prop="coin" :label="$t('backendSystem.coin2')" width="100" show-overflow-tooltip>
|
<el-table-column prop="coin" :label="$t('backendSystem.coin2')" width="100" show-overflow-tooltip>
|
||||||
|
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column prop="amount" :label="$t('backendSystem.amount2')" width="200" show-overflow-tooltip/>
|
<el-table-column prop="amount" :label="$t('backendSystem.transactionAmount')" width="200" show-overflow-tooltip/>
|
||||||
|
|
||||||
<el-table-column prop="user" :label="$t('backendSystem.minerUser2')" width="180" show-overflow-tooltip/>
|
<el-table-column prop="user" :label="$t('backendSystem.minerUser2')" width="180" show-overflow-tooltip/>
|
||||||
<el-table-column prop="address" :label="$t('backendSystem.address')" show-overflow-tooltip />
|
<el-table-column prop="address" :label="$t('backendSystem.address')" show-overflow-tooltip />
|
||||||
|
@ -240,8 +243,20 @@ export default {
|
||||||
height: 73vh;
|
height: 73vh;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
|
|
||||||
// background: palegoldenrod;
|
// background: palegoldenrod;
|
||||||
}
|
}
|
||||||
|
.time-box{
|
||||||
|
display: flex;
|
||||||
|
// background: palegoldenrod;
|
||||||
|
justify-content: space-between;
|
||||||
|
// align-items: center;
|
||||||
|
// padding: 0 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding-right: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
.user-details-form {
|
.user-details-form {
|
||||||
width: 80%;
|
width: 80%;
|
||||||
|
|
|
@ -2,11 +2,14 @@ import { getUserList, sendMail, } from '../../api/userManagement'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
|
// 从localStorage获取之前选择的币种,如果没有则使用默认值'nexa'
|
||||||
|
const savedCurrency = localStorage.getItem('userManagement_selectedCurrency') || 'nexa';
|
||||||
|
|
||||||
return {
|
return {
|
||||||
userList: [],
|
userList: [],
|
||||||
userListLoading: false,
|
userListLoading: false,
|
||||||
userListParams: {
|
userListParams: {
|
||||||
coin: "nexa",
|
coin: savedCurrency,
|
||||||
minerUser: "",
|
minerUser: "",
|
||||||
user: "",
|
user: "",
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
|
@ -19,7 +22,7 @@ export default {
|
||||||
region: "",
|
region: "",
|
||||||
},
|
},
|
||||||
currencyList: [],
|
currencyList: [],
|
||||||
screenCurrency: 'nexa',
|
screenCurrency: savedCurrency,
|
||||||
rules: {
|
rules: {
|
||||||
user: [
|
user: [
|
||||||
{
|
{
|
||||||
|
@ -138,6 +141,10 @@ export default {
|
||||||
}
|
}
|
||||||
this.setLoading('sendEmailLoading', false);
|
this.setLoading('sendEmailLoading', false);
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 币种选择变更处理
|
||||||
|
* @param {string} scope - 选择的币种
|
||||||
|
*/
|
||||||
changeScreen(scope) {
|
changeScreen(scope) {
|
||||||
let brand = scope
|
let brand = scope
|
||||||
for (let index in this.currencyList) {
|
for (let index in this.currencyList) {
|
||||||
|
@ -147,6 +154,10 @@ export default {
|
||||||
this.$refs.screen.$el.children[0].children[0].setAttribute('style', "background:url(" + aa.imgUrl + ") no-repeat 10PX;background-size: 20PX 20PX;color:#333;padding-left: 33PX;");
|
this.$refs.screen.$el.children[0].children[0].setAttribute('style', "background:url(" + aa.imgUrl + ") no-repeat 10PX;background-size: 20PX 20PX;color:#333;padding-left: 33PX;");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存用户选择的币种到localStorage
|
||||||
|
localStorage.setItem('userManagement_selectedCurrency', scope);
|
||||||
|
|
||||||
this.userListParams.coin = scope
|
this.userListParams.coin = scope
|
||||||
this.fetchUserList(this.userListParams);
|
this.fetchUserList(this.userListParams);
|
||||||
|
|
||||||
|
|
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.
|
@ -1 +1 @@
|
||||||
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><meta name=google-site-verification content=pKAZogQ0NQ6L4j9-V58WJMjm7zYCFwkJXSJzWu9UDM8><meta name=robots content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1"><meta name=googlebot content="index, follow"><meta name=googlebot-news content="index, follow"><meta name=bingbot content="index, follow"><link rel=alternate hreflang=zh href=https://m2pool.com/zh><link rel=alternate hreflang=en href=https://m2pool.com/en><link rel=alternate hreflang=x-default href=https://m2pool.com/en><meta property=og:title content="M2pool - Stable leading high-yield mining pool"><meta property=og:description content="M2Pool provides professional mining services, supporting multiple cryptocurrency mining"><meta property=og:url content=https://m2pool.com/en><meta property=og:site_name content=M2Pool><meta property=og:type content=website><meta property=og:image content=https://m2pool.com/logo.png><link rel=icon href=/favicon.ico><link rel=stylesheet href=//at.alicdn.com/t/c/font_4582735_7i8wfzc0art.css><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=description content="M2Pool provides professional mining services, supporting multiple cryptocurrency mining, including nexa, grs, mona, dgb, rxd, enx"><meta name=format-detection content="telephone=no"><meta name=apple-mobile-web-app-capable content=yes><script defer src=/js/chunk-vendors-945ce2fe.648a91a9.js></script><script defer src=/js/chunk-vendors-aacc2dbb.d317c558.js></script><script defer src=/js/chunk-vendors-bc050c32.3f2f14d2.js></script><script defer src=/js/chunk-vendors-3003db77.d0b93d36.js></script><script defer src=/js/chunk-vendors-9d134daf.bb668c99.js></script><script defer src=/js/chunk-vendors-439af1fa.48a48f35.js></script><script defer src=/js/chunk-vendors-5c533fba.b9c00e08.js></script><script defer src=/js/chunk-vendors-96cecd74.a7d9b845.js></script><script defer src=/js/chunk-vendors-c2f7d60e.3710fdc2.js></script><script defer src=/js/chunk-vendors-89d5c698.2190b4ca.js></script><script defer src=/js/chunk-vendors-377fed06.159de137.js></script><script defer src=/js/chunk-vendors-5a805870.4cfc0ae8.js></script><script defer src=/js/chunk-vendors-cf2e0a28.c6e99da0.js></script><script defer src=/js/app-42f9d7e6.2598fd7a.js></script><script defer src=/js/app-d363ae0c.40a47f4f.js></script><script defer src=/js/app-5c551db8.89b85533.js></script><script defer src=/js/app-b4c4f6ec.94c0ddb2.js></script><script defer src=/js/app-45954fd3.508cc9ca.js></script><script defer src=/js/app-72600b29.ee6f16e3.js></script><script defer src=/js/app-f035d474.34668127.js></script><script defer src=/js/app-7023e5b0.a956a90d.js></script><script defer src=/js/app-113c6c50.3dff300c.js></script><link href=/css/chunk-vendors-5c533fba.6f97509c.css rel=stylesheet><link href=/css/app-189e7968.9dadb641.css rel=stylesheet><link href=/css/app-b4c4f6ec.6e507abe.css rel=stylesheet><link href=/css/app-45954fd3.ebaa14f6.css rel=stylesheet><link href=/css/app-72600b29.6fce1895.css rel=stylesheet><link href=/css/app-f035d474.0348646a.css rel=stylesheet><link href=/css/app-7023e5b0.87f1eef9.css rel=stylesheet><link href=/css/app-113c6c50.1696c00b.css rel=stylesheet></head><body><div id=app></div></body></html>
|
<!DOCTYPE html><html lang=en><head><meta charset=utf-8><meta http-equiv=X-UA-Compatible content="IE=edge"><meta name=viewport content="width=device-width,initial-scale=1"><meta name=google-site-verification content=pKAZogQ0NQ6L4j9-V58WJMjm7zYCFwkJXSJzWu9UDM8><meta name=robots content="index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1"><meta name=googlebot content="index, follow"><meta name=googlebot-news content="index, follow"><meta name=bingbot content="index, follow"><link rel=alternate hreflang=zh href=https://m2pool.com/zh><link rel=alternate hreflang=en href=https://m2pool.com/en><link rel=alternate hreflang=x-default href=https://m2pool.com/en><meta property=og:title content="M2pool - Stable leading high-yield mining pool"><meta property=og:description content="M2Pool provides professional mining services, supporting multiple cryptocurrency mining"><meta property=og:url content=https://m2pool.com/en><meta property=og:site_name content=M2Pool><meta property=og:type content=website><meta property=og:image content=https://m2pool.com/logo.png><link rel=icon href=/favicon.ico><link rel=stylesheet href=//at.alicdn.com/t/c/font_4582735_7i8wfzc0art.css><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=description content="M2Pool provides professional mining services, supporting multiple cryptocurrency mining, including nexa, grs, mona, dgb, rxd, enx"><meta name=format-detection content="telephone=no"><meta name=apple-mobile-web-app-capable content=yes><script defer src=/js/chunk-vendors-945ce2fe.648a91a9.js></script><script defer src=/js/chunk-vendors-aacc2dbb.d317c558.js></script><script defer src=/js/chunk-vendors-bc050c32.3f2f14d2.js></script><script defer src=/js/chunk-vendors-3003db77.d0b93d36.js></script><script defer src=/js/chunk-vendors-9d134daf.bb668c99.js></script><script defer src=/js/chunk-vendors-439af1fa.48a48f35.js></script><script defer src=/js/chunk-vendors-5c533fba.b9c00e08.js></script><script defer src=/js/chunk-vendors-96cecd74.a7d9b845.js></script><script defer src=/js/chunk-vendors-c2f7d60e.3710fdc2.js></script><script defer src=/js/chunk-vendors-89d5c698.2190b4ca.js></script><script defer src=/js/chunk-vendors-377fed06.159de137.js></script><script defer src=/js/chunk-vendors-5a805870.4cfc0ae8.js></script><script defer src=/js/chunk-vendors-cf2e0a28.c6e99da0.js></script><script defer src=/js/app-42f9d7e6.d563e0a6.js></script><script defer src=/js/app-89ec8940.a029b1cd.js></script><script defer src=/js/app-d363ae0c.3ec67189.js></script><script defer src=/js/app-5c551db8.3a30163d.js></script><script defer src=/js/app-b4c4f6ec.81923b59.js></script><script defer src=/js/app-a6ba6ead.22ec49a2.js></script><script defer src=/js/app-72600b29.533abe28.js></script><script defer src=/js/app-f035d474.34668127.js></script><script defer src=/js/app-7023e5b0.a956a90d.js></script><script defer src=/js/app-113c6c50.7fb5dfad.js></script><link href=/css/chunk-vendors-5c533fba.6f97509c.css rel=stylesheet><link href=/css/app-189e7968.e9d72c1e.css rel=stylesheet><link href=/css/app-b4c4f6ec.dbc59290.css rel=stylesheet><link href=/css/app-a6ba6ead.1697153d.css rel=stylesheet><link href=/css/app-72600b29.60a65385.css rel=stylesheet><link href=/css/app-f035d474.0348646a.css rel=stylesheet><link href=/css/app-7023e5b0.87f1eef9.css rel=stylesheet><link href=/css/app-113c6c50.20159bae.css rel=stylesheet></head><body><div id=app></div></body></html>
|
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.
|
@ -1 +1 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://m2pool.com/en</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>daily</changefreq><priority>1.0</priority></url><url><loc>https://m2pool.com/en/dataDisplay</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url><url><loc>https://m2pool.com/en/ServiceTerms</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>monthly</changefreq><priority>0.6</priority></url><url><loc>https://m2pool.com/en/apiFile</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/rate</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/nexaAccess</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/grsAccess</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/monaAccess</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/dgbsAccess</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/dgbqAccess</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/dgboAccess</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/rxdAccess</loc><lastmod>2025-07-03T08:52:19.108Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url></urlset>
|
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://m2pool.com/en</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>daily</changefreq><priority>1.0</priority></url><url><loc>https://m2pool.com/en/dataDisplay</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url><url><loc>https://m2pool.com/en/ServiceTerms</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>monthly</changefreq><priority>0.6</priority></url><url><loc>https://m2pool.com/en/apiFile</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/rate</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>weekly</changefreq><priority>0.8</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/nexaAccess</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/grsAccess</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/monaAccess</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/dgbsAccess</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/dgbqAccess</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/dgboAccess</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/en/AccessMiningPool/rxdAccess</loc><lastmod>2025-07-10T03:13:18.219Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url></urlset>
|
Binary file not shown.
|
@ -1 +1 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://m2pool.com/zh</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/dataDisplay</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/ServiceTerms</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>monthly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/apiFile</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/rate</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/nexaAccess</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/grsAccess</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/monaAccess</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/dgbsAccess</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/dgbqAccess</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/dgboAccess</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/rxdAccess</loc><lastmod>2025-07-03T08:52:19.100Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url></urlset>
|
<?xml version="1.0" encoding="UTF-8"?><urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1"><url><loc>https://m2pool.com/zh</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>daily</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/dataDisplay</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/ServiceTerms</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>monthly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/apiFile</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/rate</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/nexaAccess</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/grsAccess</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/monaAccess</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/dgbsAccess</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/dgbqAccess</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/dgboAccess</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url><url><loc>https://m2pool.com/zh/AccessMiningPool/rxdAccess</loc><lastmod>2025-07-10T03:13:18.212Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url></urlset>
|
Binary file not shown.
Loading…
Reference in New Issue