.m2pool 客服系统对接 1.订阅接口区分用户和客服 避免消息混乱 已处理 2.历史消息渲染isSelf返回值有问题 已处理 3.图片发送方式修改为用base64Image发送后soket自动断开 已处理 4.优化心跳机制 断线重连 最多自动重连5次 已处理 5.游客功能添加、删除列表离线游客 处理中
6.客服给游客发送消息 游客收不到推送消息 已处理
This commit is contained in:
parent
2e56d71b0c
commit
a326f62f81
|
@ -5,8 +5,8 @@ VUE_APP_TITLE = m2pool
|
||||||
ENV = 'development'
|
ENV = 'development'
|
||||||
|
|
||||||
#开发环境
|
#开发环境
|
||||||
VUE_APP_BASE_API = 'https://test.m2pool.com/api/'
|
# VUE_APP_BASE_API = 'https://test.m2pool.com/api/'
|
||||||
# VUE_APP_BASE_API = 'http://10.168.2.150:8101/'
|
VUE_APP_BASE_API = 'http://10.168.2.150:8101/'
|
||||||
VUE_APP_BASE_URL = 'https://test.m2pool.com/'
|
VUE_APP_BASE_URL = 'https://test.m2pool.com/'
|
||||||
# 路由懒加载
|
# 路由懒加载
|
||||||
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
VUE_CLI_BABEL_TRANSPILE_MODULES = true
|
||||||
|
|
|
@ -58,11 +58,11 @@ export function getUpdateRoom(data) {
|
||||||
|
|
||||||
|
|
||||||
//图根据当前用户邮箱查询聊天室id
|
//图根据当前用户邮箱查询聊天室id
|
||||||
export function getUserid() {
|
export function getUserid(data) {
|
||||||
return request({
|
return request({
|
||||||
url: `chat/rooms/find/room/by/userid`,
|
url: `chat/rooms/find/room/by/userid`,
|
||||||
method: 'get',
|
method: 'post',
|
||||||
|
data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -220,10 +220,16 @@ export default {
|
||||||
|
|
||||||
cachedMessages: {}, // 缓存各聊天室的消息
|
cachedMessages: {}, // 缓存各聊天室的消息
|
||||||
isMinimized: false, // 区分最小化和关闭状态
|
isMinimized: false, // 区分最小化和关闭状态
|
||||||
|
|
||||||
|
reconnectAttempts: 0,
|
||||||
|
maxReconnectAttempts: 5,
|
||||||
|
reconnectInterval: 5000, // 5秒
|
||||||
|
isReconnecting: false,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
|
this.determineUserType();
|
||||||
// 页面加载时立即获取用户信息
|
// 页面加载时立即获取用户信息
|
||||||
await this.initChatSystem();
|
await this.initChatSystem();
|
||||||
},
|
},
|
||||||
|
@ -247,7 +253,7 @@ export default {
|
||||||
async initChatSystem() {
|
async initChatSystem() {
|
||||||
try {
|
try {
|
||||||
// 获取用户ID和未读消息数
|
// 获取用户ID和未读消息数
|
||||||
const userData = await this.fetchUserid();
|
const userData = await this.fetchUserid({ email: this.userEmail });
|
||||||
if (userData) {
|
if (userData) {
|
||||||
this.roomId = userData.id;
|
this.roomId = userData.id;
|
||||||
this.receivingEmail = userData.userEmail;
|
this.receivingEmail = userData.userEmail;
|
||||||
|
@ -273,7 +279,7 @@ export default {
|
||||||
determineUserType() {
|
determineUserType() {
|
||||||
try {
|
try {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
|
console.log("token", token);
|
||||||
if (!token) {
|
if (!token) {
|
||||||
// 游客身份
|
// 游客身份
|
||||||
this.userType = 0;
|
this.userType = 0;
|
||||||
|
@ -293,11 +299,13 @@ export default {
|
||||||
if (userInfo.roleKey === "customer_service") {
|
if (userInfo.roleKey === "customer_service") {
|
||||||
// 客服用户
|
// 客服用户
|
||||||
this.userType = 2;
|
this.userType = 2;
|
||||||
|
this.userEmail = "";
|
||||||
} else {
|
} else {
|
||||||
// 登录用户
|
// 登录用户
|
||||||
this.userType = 1;
|
this.userType = 1;
|
||||||
}
|
|
||||||
this.userEmail = email;
|
this.userEmail = email;
|
||||||
|
}
|
||||||
|
|
||||||
} catch (parseError) {
|
} catch (parseError) {
|
||||||
console.error("解析用户信息失败:", parseError);
|
console.error("解析用户信息失败:", parseError);
|
||||||
// 解析失败时默认为游客
|
// 解析失败时默认为游客
|
||||||
|
@ -323,51 +331,80 @@ export default {
|
||||||
try {
|
try {
|
||||||
// 订阅个人消息频道
|
// 订阅个人消息频道
|
||||||
this.stompClient.subscribe(
|
this.stompClient.subscribe(
|
||||||
`/user/queue/${this.userEmail}`,
|
`/sub/queue/user/${this.userEmail}`,
|
||||||
this.onMessageReceived,
|
this.onMessageReceived,
|
||||||
{
|
// {
|
||||||
id: `chat_${this.userEmail}`,
|
// id: `chat_${this.userEmail}`,
|
||||||
}
|
// }
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log("成功订阅消息频道:", `/user/queue/${this.userEmail}`);
|
console.log("成功订阅消息频道:", `/sub/queue/user/${this.userEmail}`);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("订阅消息失败:", error);
|
console.error("订阅消息失败:", error);
|
||||||
this.$message.error("消息订阅失败,可能无法接收新消息");
|
this.$message.error("消息订阅失败,可能无法接收新消息");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
// 连接 WebSocket
|
// 连接 WebSocket
|
||||||
connectWebSocket() {
|
connectWebSocket() {
|
||||||
if (this.isWebSocketConnected) return;
|
if (this.isWebSocketConnected || this.isReconnecting) return;
|
||||||
|
|
||||||
this.connectionStatus = "connecting";
|
this.connectionStatus = "connecting";
|
||||||
|
this.isReconnecting = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const wsUrl = `${process.env.VUE_APP_BASE_API}chat/ws`;
|
const wsUrl = `${process.env.VUE_APP_BASE_API}chat/ws`;
|
||||||
this.stompClient = Stomp.client(wsUrl);
|
this.stompClient = Stomp.client(wsUrl);
|
||||||
|
// 配置 STOMP 客户端参数
|
||||||
|
this.stompClient.splitLargeFrames = true; // 启用大型消息帧分割
|
||||||
|
// 添加详细的调试日志
|
||||||
|
// this.stompClient.debug = (str) => {
|
||||||
|
// console.log("STOMP Debug:", str);
|
||||||
|
// // 记录连接状态变化
|
||||||
|
// if (str.includes("CONNECTED")) {
|
||||||
|
// console.log("WebSocket 连接成功");
|
||||||
|
// this.isWebSocketConnected = true;
|
||||||
|
// this.connectionStatus = "connected";
|
||||||
|
// this.reconnectAttempts = 0;
|
||||||
|
// this.isReconnecting = false;
|
||||||
|
// } else if (
|
||||||
|
// str.includes("DISCONNECTED") ||
|
||||||
|
// str.includes("Connection closed")
|
||||||
|
// ) {
|
||||||
|
// console.log("WebSocket 连接断开");
|
||||||
|
// this.handleDisconnect();
|
||||||
|
// }
|
||||||
|
// };
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
email: this.userEmail,
|
email: this.userEmail,
|
||||||
type: this.userType,
|
type: this.userType,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 添加连接状态监听
|
||||||
|
this.stompClient.onStompError = (frame) => {
|
||||||
|
console.error("STOMP 错误:", frame);
|
||||||
|
this.handleDisconnect();
|
||||||
|
};
|
||||||
|
|
||||||
this.stompClient.connect(
|
this.stompClient.connect(
|
||||||
headers,
|
headers,
|
||||||
(frame) => {
|
(frame) => {
|
||||||
console.log("WebSocket Connected:", frame);
|
console.log("WebSocket Connected:", frame);
|
||||||
this.isWebSocketConnected = true;
|
this.isWebSocketConnected = true;
|
||||||
this.connectionStatus = "connected";
|
this.connectionStatus = "connected";
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
this.isReconnecting = false;
|
||||||
|
|
||||||
// 连接成功后立即订阅消息
|
// 连接成功后立即订阅消息
|
||||||
this.subscribeToPersonalMessages();
|
this.subscribeToPersonalMessages();
|
||||||
|
|
||||||
|
// 显示连接成功提示
|
||||||
|
this.$message.success("连接成功");
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
console.error("WebSocket Error:", error);
|
console.error("WebSocket Error:", error);
|
||||||
this.isWebSocketConnected = false;
|
this.handleDisconnect();
|
||||||
this.connectionStatus = "error";
|
|
||||||
// 添加重连逻辑
|
|
||||||
setTimeout(() => this.connectWebSocket(), 5000);
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -376,7 +413,34 @@ export default {
|
||||||
this.stompClient.heartbeat.incoming = 20000;
|
this.stompClient.heartbeat.incoming = 20000;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("初始化 WebSocket 失败:", error);
|
console.error("初始化 WebSocket 失败:", error);
|
||||||
|
this.handleDisconnect();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// 添加新重连最多重连5次
|
||||||
|
handleDisconnect() {
|
||||||
|
this.isWebSocketConnected = false;
|
||||||
this.connectionStatus = "error";
|
this.connectionStatus = "error";
|
||||||
|
this.isReconnecting = false;
|
||||||
|
|
||||||
|
// 如果重连次数未超过最大尝试次数,则尝试重连
|
||||||
|
if (this.reconnectAttempts < this.maxReconnectAttempts) {
|
||||||
|
this.reconnectAttempts++;
|
||||||
|
console.log(
|
||||||
|
`尝试重连 (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`
|
||||||
|
);
|
||||||
|
|
||||||
|
this.$message.warning(
|
||||||
|
`连接断开,${this.reconnectInterval / 1000}秒后重试...`
|
||||||
|
);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.isWebSocketConnected) {
|
||||||
|
this.connectWebSocket();
|
||||||
|
}
|
||||||
|
}, this.reconnectInterval);
|
||||||
|
} else {
|
||||||
|
console.log("达到最大重连次数,停止重连");
|
||||||
|
this.$message.error("连接失败,请刷新页面重试");
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -387,11 +451,19 @@ export default {
|
||||||
|
|
||||||
// 发送消息
|
// 发送消息
|
||||||
sendMessage() {
|
sendMessage() {
|
||||||
if (!this.inputMessage.trim() || this.connectionStatus !== "connected")
|
if (!this.inputMessage.trim()) return;
|
||||||
|
|
||||||
|
// 检查 WebSocket 连接状态
|
||||||
|
if (!this.stompClient || !this.stompClient.connected) {
|
||||||
|
console.log('发送消息时连接已断开,尝试重连...');
|
||||||
|
this.$message.warning('连接已断开,正在重新连接...');
|
||||||
|
this.handleDisconnect();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const messageText = this.inputMessage.trim();
|
const messageText = this.inputMessage.trim();
|
||||||
|
|
||||||
|
try {
|
||||||
// 添加用户消息到界面
|
// 添加用户消息到界面
|
||||||
this.messages.push({
|
this.messages.push({
|
||||||
type: "user",
|
type: "user",
|
||||||
|
@ -410,7 +482,12 @@ export default {
|
||||||
roomId: this.roomId,
|
roomId: this.roomId,
|
||||||
};
|
};
|
||||||
// 发送消息
|
// 发送消息
|
||||||
this.stompClient.send("/point/send/message", {}, JSON.stringify(message));
|
this.stompClient.send(
|
||||||
|
"/point/send/message/to/customer",
|
||||||
|
{},
|
||||||
|
JSON.stringify(message)
|
||||||
|
);
|
||||||
|
|
||||||
// 通过 WebSocket 发送消息
|
// 通过 WebSocket 发送消息
|
||||||
// if (this.stompClient && this.stompClient.connected) {
|
// if (this.stompClient && this.stompClient.connected) {
|
||||||
// this.stompClient.send({
|
// this.stompClient.send({
|
||||||
|
@ -432,6 +509,11 @@ export default {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
});
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error("发送消息失败:", error);
|
||||||
|
this.$message.error("发送消息失败,请重试");
|
||||||
|
this.handleDisconnect();
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 断开 WebSocket 连接
|
// 断开 WebSocket 连接
|
||||||
|
@ -440,7 +522,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
// 取消所有订阅
|
// 取消所有订阅
|
||||||
if (this.stompClient.subscriptions) {
|
if (this.stompClient.subscriptions) {
|
||||||
Object.keys(this.stompClient.subscriptions).forEach(id => {
|
Object.keys(this.stompClient.subscriptions).forEach((id) => {
|
||||||
this.stompClient.unsubscribe(id);
|
this.stompClient.unsubscribe(id);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -449,6 +531,8 @@ export default {
|
||||||
this.stompClient.deactivate();
|
this.stompClient.deactivate();
|
||||||
this.isWebSocketConnected = false;
|
this.isWebSocketConnected = false;
|
||||||
this.connectionStatus = "disconnected";
|
this.connectionStatus = "disconnected";
|
||||||
|
this.reconnectAttempts = 0;
|
||||||
|
this.isReconnecting = false;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("断开 WebSocket 连接失败:", error);
|
console.error("断开 WebSocket 连接失败:", error);
|
||||||
}
|
}
|
||||||
|
@ -496,53 +580,61 @@ export default {
|
||||||
|
|
||||||
this.isLoadingHistory = true;
|
this.isLoadingHistory = true;
|
||||||
try {
|
try {
|
||||||
const response = await getHistory7({ roomId: this.roomId, userType: this.userType });
|
const response = await getHistory7({
|
||||||
|
roomId: this.roomId,
|
||||||
|
userType: this.userType,
|
||||||
|
email: this.userEmail,
|
||||||
|
});
|
||||||
console.log("历史消息数据:", response);
|
console.log("历史消息数据:", response);
|
||||||
|
|
||||||
if (response?.code === 200 && Array.isArray(response.data)) {
|
if (response?.code === 200 && Array.isArray(response.data)) {
|
||||||
// 处理历史消息
|
// 处理历史消息
|
||||||
const historyMessages = response.data.map(msg => ({
|
const historyMessages = response.data.map((msg) => ({
|
||||||
type: msg.isSelf === 1 ? "user" : "system", // 根据 isSelf 判断消息类型
|
type: msg.isSelf === 1 ? "user" : "system",
|
||||||
text: msg.content,
|
text: msg.content,
|
||||||
isImage: msg.type === 2,
|
isImage: msg.type === 2,
|
||||||
imageUrl: msg.type === 2 ? msg.content : null,
|
imageUrl: msg.type === 2 ? msg.content : null,
|
||||||
time: new Date(msg.createTime), // 使用 createTime 字段
|
time: new Date(msg.createTime),
|
||||||
id: msg.id,
|
id: msg.id,
|
||||||
roomId: msg.roomId,
|
roomId: msg.roomId,
|
||||||
sender: msg.sendEmail,
|
sender: msg.sendEmail,
|
||||||
isHistory: true,
|
isHistory: true,
|
||||||
isRead: true // 历史消息默认已读
|
isRead: true,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 按时间顺序排序
|
// 按时间顺序排序
|
||||||
this.messages = historyMessages.sort((a, b) =>
|
this.messages = historyMessages.sort(
|
||||||
new Date(a.time) - new Date(b.time)
|
(a, b) => new Date(a.time) - new Date(b.time)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 滚动到底部
|
// 等待 DOM 更新和图片加载完成后再滚动
|
||||||
this.$nextTick(() => {
|
await this.$nextTick();
|
||||||
this.scrollToBottom();
|
// 添加一个小延时确保所有内容都渲染完成
|
||||||
});
|
setTimeout(() => {
|
||||||
|
this.scrollToBottom(true); // 传入 true 表示强制滚动
|
||||||
|
}, 100);
|
||||||
} else {
|
} else {
|
||||||
// 没有历史消息时显示提示
|
this.messages = [
|
||||||
this.messages = [{
|
{
|
||||||
type: "system",
|
type: "system",
|
||||||
text: "暂无历史消息",
|
text: "暂无历史消息",
|
||||||
isSystemHint: true,
|
isSystemHint: true,
|
||||||
time: new Date()
|
time: new Date(),
|
||||||
}];
|
},
|
||||||
|
];
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("加载历史消息失败:", error);
|
console.error("加载历史消息失败:", error);
|
||||||
this.$message.error("加载历史消息失败");
|
this.$message.error("加载历史消息失败");
|
||||||
// 显示错误提示
|
this.messages = [
|
||||||
this.messages = [{
|
{
|
||||||
type: "system",
|
type: "system",
|
||||||
text: "加载历史消息失败,请重试",
|
text: "加载历史消息失败,请重试",
|
||||||
isSystemHint: true,
|
isSystemHint: true,
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
isError: true
|
isError: true,
|
||||||
}];
|
},
|
||||||
|
];
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoadingHistory = false;
|
this.isLoadingHistory = false;
|
||||||
}
|
}
|
||||||
|
@ -555,6 +647,14 @@ export default {
|
||||||
this.isLoadingHistory = true;
|
this.isLoadingHistory = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
// 获取当前消息列表中最旧消息的 ID
|
||||||
|
const oldestMessage = this.messages.find(msg => !msg.isSystemHint && !msg.isLoading);
|
||||||
|
if (!oldestMessage || !oldestMessage.id) {
|
||||||
|
console.warn('没有找到有效的消息ID');
|
||||||
|
this.hasMoreHistory = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// 显示加载中提示
|
// 显示加载中提示
|
||||||
const loadingMsg = {
|
const loadingMsg = {
|
||||||
type: "system",
|
type: "system",
|
||||||
|
@ -564,8 +664,13 @@ export default {
|
||||||
};
|
};
|
||||||
this.messages.unshift(loadingMsg);
|
this.messages.unshift(loadingMsg);
|
||||||
|
|
||||||
// 获取更早的聊天记录
|
// 获取更早的聊天记录,添加 id 参数
|
||||||
const response = await getHistory({ roomId: this.roomId });
|
const response = await getHistory7({
|
||||||
|
roomId: this.roomId,
|
||||||
|
userType: this.userType,
|
||||||
|
email: this.userEmail,
|
||||||
|
id: oldestMessage.id // 添加最旧消息的 ID
|
||||||
|
});
|
||||||
|
|
||||||
// 移除加载中提示
|
// 移除加载中提示
|
||||||
this.messages = this.messages.filter((msg) => !msg.isLoading);
|
this.messages = this.messages.filter((msg) => !msg.isLoading);
|
||||||
|
@ -613,7 +718,7 @@ export default {
|
||||||
} finally {
|
} finally {
|
||||||
this.isLoadingHistory = false;
|
this.isLoadingHistory = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 格式化历史消息数据
|
// 格式化历史消息数据
|
||||||
formatHistoryMessages(messagesData) {
|
formatHistoryMessages(messagesData) {
|
||||||
|
@ -630,25 +735,25 @@ export default {
|
||||||
roomId: msg.roomId,
|
roomId: msg.roomId,
|
||||||
sender: msg.sendEmail,
|
sender: msg.sendEmail,
|
||||||
isHistory: true,
|
isHistory: true,
|
||||||
isRead: true
|
isRead: true,
|
||||||
}))
|
}))
|
||||||
.sort((a, b) => new Date(a.time) - new Date(b.time));
|
.sort((a, b) => new Date(a.time) - new Date(b.time));
|
||||||
},
|
},
|
||||||
|
|
||||||
// 修改 fetchUserid 方法,添加 token 检查
|
// 修改 fetchUserid 方法,添加 token 检查
|
||||||
async fetchUserid() {
|
async fetchUserid(params) {
|
||||||
try {
|
try {
|
||||||
// 先检查是否有 token
|
// 先检查是否有 token
|
||||||
const token = localStorage.getItem("token");
|
// const token = localStorage.getItem("token");
|
||||||
if (!token) {
|
// if (!token) {
|
||||||
console.log("用户未登录,不发起 getUserid 请求");
|
// console.log("用户未登录,不发起 getUserid 请求");
|
||||||
// 对于未登录用户,可以生成一个临时 ID
|
// // 对于未登录用户,可以生成一个临时 ID
|
||||||
this.roomId = `guest_${Date.now()}`;
|
// this.roomId = `guest_${Date.now()}`;
|
||||||
this.receivingEmail = "customer_service@example.com"; // 或默认客服邮箱
|
// this.receivingEmail = "customer_service@example.com"; // 或默认客服邮箱
|
||||||
return null;
|
// return null;
|
||||||
}
|
// }
|
||||||
|
|
||||||
const res = await getUserid();
|
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;
|
||||||
|
@ -699,7 +804,7 @@ export default {
|
||||||
id: data.id,
|
id: data.id,
|
||||||
roomId: data.roomId,
|
roomId: data.roomId,
|
||||||
sender: data.sendEmail,
|
sender: data.sendEmail,
|
||||||
isRead: false
|
isRead: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 直接添加到消息列表
|
// 直接添加到消息列表
|
||||||
|
@ -724,11 +829,10 @@ export default {
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("处理消息失败:", error);
|
console.error("处理消息失败:", error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 显示消息通知
|
// 显示消息通知
|
||||||
showNotification(message) {
|
showNotification(message) {
|
||||||
|
@ -778,35 +882,46 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 切换聊天窗口
|
// 打开聊天框
|
||||||
async toggleChat() {
|
async toggleChat() {
|
||||||
this.isChatOpen = !this.isChatOpen;
|
this.isChatOpen = !this.isChatOpen;
|
||||||
|
|
||||||
|
// 1. 判别身份
|
||||||
|
this.determineUserType();
|
||||||
|
|
||||||
|
// 2. 如果是客服,跳转到客服页面
|
||||||
|
if (this.userType === 2) {
|
||||||
|
const lang = this.$i18n.locale;
|
||||||
|
this.$router.push(`/${lang}/customerService`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.isChatOpen) {
|
if (this.isChatOpen) {
|
||||||
// 打开聊天窗口时
|
|
||||||
try {
|
try {
|
||||||
// 确定用户类型
|
// 确定用户类型
|
||||||
this.determineUserType();
|
this.determineUserType();
|
||||||
|
|
||||||
// 如果未连接,则初始化 WebSocket
|
// 如果未连接或连接断开,则重新初始化 WebSocket
|
||||||
if (this.connectionStatus === "disconnected") {
|
if (!this.isWebSocketConnected || this.connectionStatus === "disconnected") {
|
||||||
await this.initWebSocket();
|
await this.connectWebSocket();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 如果消息列表为空,加载历史消息
|
// 如果消息列表为空,加载历史消息
|
||||||
if (this.messages.length === 0) {
|
if (this.messages.length === 0) {
|
||||||
await this.loadHistoryMessages();
|
await this.loadHistoryMessages();
|
||||||
|
} else {
|
||||||
|
// 如果已有消息,确保滚动到底部
|
||||||
|
await this.$nextTick();
|
||||||
|
setTimeout(() => {
|
||||||
|
this.scrollToBottom(true);
|
||||||
|
}, 100);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 标记消息为已读
|
// 标记消息为已读
|
||||||
await this.markMessagesAsRead();
|
await this.markMessagesAsRead();
|
||||||
|
|
||||||
// 滚动到底部
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.scrollToBottom();
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("初始化聊天失败:", error);
|
console.error("初始化聊天失败:", error);
|
||||||
|
this.$message.error("初始化聊天失败,请重试");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -879,12 +994,13 @@ export default {
|
||||||
this.loadMoreHistory();
|
this.loadMoreHistory();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
//滚动到底部
|
||||||
|
scrollToBottom(force = false) {
|
||||||
|
if (!this.$refs.chatBody) return;
|
||||||
|
|
||||||
scrollToBottom() {
|
|
||||||
if (this.$refs.chatBody) {
|
|
||||||
const scrollOptions = {
|
const scrollOptions = {
|
||||||
top: this.$refs.chatBody.scrollHeight,
|
top: this.$refs.chatBody.scrollHeight,
|
||||||
behavior: "smooth",
|
behavior: force ? 'auto' : 'smooth' // 强制滚动时使用 'auto'
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -893,8 +1009,7 @@ export default {
|
||||||
// 如果平滑滚动不支持,则直接设置
|
// 如果平滑滚动不支持,则直接设置
|
||||||
this.$refs.chatBody.scrollTop = this.$refs.chatBody.scrollHeight;
|
this.$refs.chatBody.scrollTop = this.$refs.chatBody.scrollHeight;
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
},
|
|
||||||
|
|
||||||
formatTime(date) {
|
formatTime(date) {
|
||||||
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
if (!date || !(date instanceof Date) || isNaN(date.getTime())) {
|
||||||
|
@ -950,7 +1065,10 @@ export default {
|
||||||
|
|
||||||
// 处理图片上传
|
// 处理图片上传
|
||||||
async handleImageUpload(event) {
|
async handleImageUpload(event) {
|
||||||
if (this.connectionStatus !== "connected") return;
|
if (this.connectionStatus !== "connected") {
|
||||||
|
console.log("当前连接状态:", this.connectionStatus);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
@ -981,62 +1099,71 @@ export default {
|
||||||
type: "info",
|
type: "info",
|
||||||
});
|
});
|
||||||
|
|
||||||
// 准备FormData
|
// 将文件转换为 base64
|
||||||
const formData = new FormData();
|
const reader = new FileReader();
|
||||||
formData.append("file", file);
|
reader.onload = (e) => {
|
||||||
|
const base64Image = e.target.result;
|
||||||
|
|
||||||
// 上传文件到后端
|
// 检查连接状态
|
||||||
const response = await getFileUpdate(formData);
|
if (!this.stompClient || !this.stompClient.connected) {
|
||||||
console.log("文件上传返回:", response);
|
console.error("发送图片时连接已断开");
|
||||||
|
this.$message.error("连接已断开,正在重新连接...");
|
||||||
// 检查上传结果
|
this.connectWebSocket();
|
||||||
if (response && response.code === 200 && response.data) {
|
return;
|
||||||
// 从后端响应中获取图片信息
|
}
|
||||||
const imageData = response.data;
|
|
||||||
// 使用后端返回的URL
|
|
||||||
const imageUrl = this.formatImageUrl(imageData.url);
|
|
||||||
|
|
||||||
console.log("图片URL:", imageUrl); // 调试用:打印URL
|
|
||||||
|
|
||||||
// 添加用户图片消息到界面(本地显示)
|
// 添加用户图片消息到界面(本地显示)
|
||||||
this.messages.push({
|
this.messages.push({
|
||||||
type: "user",
|
type: "user",
|
||||||
text: "", // 保留空字符串
|
text: "",
|
||||||
isImage: true,
|
isImage: true,
|
||||||
imageUrl: imageUrl, // 确保URL正确
|
imageUrl: base64Image,
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
email: this.receivingEmail,
|
email: this.receivingEmail,
|
||||||
sendUserType: this.userType,
|
sendUserType: this.userType,
|
||||||
roomId: this.roomId,
|
roomId: this.roomId,
|
||||||
isRead: false,
|
isRead: false,
|
||||||
});
|
});
|
||||||
// 通过WebSocket发送图片消息
|
|
||||||
if (this.stompClient && this.stompClient.connected) {
|
try {
|
||||||
|
// 通过 WebSocket 发送图片消息
|
||||||
const message = {
|
const message = {
|
||||||
content: imageUrl, // URL作为消息内容
|
content: base64Image,
|
||||||
type: 2, // 使用数字类型2表示图片消息
|
type: 2,
|
||||||
email: this.receivingEmail,
|
email: this.receivingEmail,
|
||||||
receiveUserType: 2,
|
receiveUserType: 2,
|
||||||
roomId: this.roomId,
|
roomId: this.roomId,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 使用WebSocket发送消息
|
console.log(
|
||||||
|
"准备发送图片消息,当前连接状态:",
|
||||||
|
this.stompClient.connected
|
||||||
|
);
|
||||||
this.stompClient.send(
|
this.stompClient.send(
|
||||||
"/point/send/message",
|
"/point/send/message",
|
||||||
{},
|
{},
|
||||||
JSON.stringify(message)
|
JSON.stringify(message)
|
||||||
);
|
);
|
||||||
}
|
console.log("图片消息发送完成");
|
||||||
|
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
});
|
});
|
||||||
} else {
|
} catch (sendError) {
|
||||||
this.$message.error("图片上传失败: " + (response?.msg || "未知错误"));
|
console.error("发送图片消息失败:", sendError);
|
||||||
|
this.$message.error("发送图片失败,请重试");
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.onerror = (error) => {
|
||||||
|
console.error("读取文件失败:", error);
|
||||||
|
this.$message.error("读取图片失败,请重试");
|
||||||
|
};
|
||||||
|
|
||||||
|
reader.readAsDataURL(file);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("图片上传异常:", error);
|
console.error("图片处理失败:", error);
|
||||||
this.$message.error("图片上传失败,请重试");
|
this.$message.error("图片处理失败,请重试");
|
||||||
} finally {
|
} finally {
|
||||||
// 清空input,允许重复选择同一文件
|
// 清空input,允许重复选择同一文件
|
||||||
this.$refs.imageUpload.value = "";
|
this.$refs.imageUpload.value = "";
|
||||||
|
@ -1070,7 +1197,7 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
beforeDestroy() {
|
beforeDestroy() {
|
||||||
this.disconnectWebSocket();
|
|
||||||
|
|
||||||
// 移除滚动监听
|
// 移除滚动监听
|
||||||
if (this.$refs.chatBody) {
|
if (this.$refs.chatBody) {
|
||||||
|
@ -1081,7 +1208,11 @@ export default {
|
||||||
"visibilitychange",
|
"visibilitychange",
|
||||||
this.handleVisibilityChange
|
this.handleVisibilityChange
|
||||||
);
|
);
|
||||||
|
// 确保在销毁时断开连接
|
||||||
|
if (this.stompClient) {
|
||||||
|
this.stompClient.disconnect();
|
||||||
|
this.stompClient = null;
|
||||||
|
}
|
||||||
// 断开 WebSocket 连接
|
// 断开 WebSocket 连接
|
||||||
this.disconnectWebSocket();
|
this.disconnectWebSocket();
|
||||||
},
|
},
|
||||||
|
|
|
@ -213,7 +213,6 @@
|
||||||
type="primary"
|
type="primary"
|
||||||
:disabled="!currentContact || !inputMessage.trim() || sending"
|
:disabled="!currentContact || !inputMessage.trim() || sending"
|
||||||
@click="sendMessage"
|
@click="sendMessage"
|
||||||
|
|
||||||
>
|
>
|
||||||
<i v-if="sending" class="el-icon-loading"></i>
|
<i v-if="sending" class="el-icon-loading"></i>
|
||||||
<span v-else>发送</span>
|
<span v-else>发送</span>
|
||||||
|
@ -273,6 +272,7 @@ export default {
|
||||||
id: "", //最后一条消息id
|
id: "", //最后一条消息id
|
||||||
roomId: "", //聊天室id
|
roomId: "", //聊天室id
|
||||||
userType: 2, //用户类型
|
userType: 2, //用户类型
|
||||||
|
email: "497681109@qq.com", //客服邮箱
|
||||||
},
|
},
|
||||||
historyAllParams: {
|
historyAllParams: {
|
||||||
//7天以前的历史消息
|
//7天以前的历史消息
|
||||||
|
@ -282,6 +282,9 @@ export default {
|
||||||
},
|
},
|
||||||
receiveUserType: "", //接收者类型
|
receiveUserType: "", //接收者类型
|
||||||
manualCreatedRooms: [], //手动创建的聊天室
|
manualCreatedRooms: [], //手动创建的聊天室
|
||||||
|
chatRooms: [], // 初始化聊天室列表数组
|
||||||
|
isWebSocketConnected: false,
|
||||||
|
connectionStatus: "disconnected",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
|
@ -307,18 +310,23 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
async created() {
|
async created() {
|
||||||
// 初始化 WebSocket 连接
|
try {
|
||||||
this.initWebSocket();
|
// 初始化用户信息
|
||||||
|
// this.determineUserType();
|
||||||
|
|
||||||
// 获取聊天室列表
|
// 获取聊天室列表
|
||||||
await this.fetchRoomList();
|
await this.fetchRoomList();
|
||||||
|
|
||||||
// 默认选择第一个联系人
|
// 默认选择第一个联系人
|
||||||
if (this.contacts.length > 0) {
|
if (this.contacts.length > 0) {
|
||||||
this.selectContact(this.contacts[0].roomId);
|
this.selectContact(this.contacts[0].roomId);
|
||||||
}
|
}
|
||||||
// 在组件创建时加载手动创建的聊天室
|
// 在组件创建时加载手动创建的聊天室
|
||||||
this.loadManualCreatedRooms();
|
this.loadManualCreatedRooms();
|
||||||
|
// 初始化 WebSocket 连接
|
||||||
|
this.initWebSocket();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("初始化失败:", error);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
async mounted() {
|
async mounted() {
|
||||||
// 获取聊天室列表
|
// 获取聊天室列表
|
||||||
|
@ -328,17 +336,14 @@ export default {
|
||||||
if (this.contacts.length > 0) {
|
if (this.contacts.length > 0) {
|
||||||
this.selectContact(this.contacts[0].roomId);
|
this.selectContact(this.contacts[0].roomId);
|
||||||
}
|
}
|
||||||
this.confirmUserType(); //确认用户类型
|
|
||||||
// 定时刷新聊天室列表,例如每30秒刷新一次
|
|
||||||
// this.roomListInterval = setInterval(() => {
|
|
||||||
// this.fetchRoomList();
|
|
||||||
|
|
||||||
// // 如果有选中联系人,刷新消息
|
let userEmail = localStorage.getItem("userEmail");
|
||||||
// if (this.currentContactId) {
|
this.userEmail = JSON.parse(userEmail);
|
||||||
// this.loadMessages(this.currentContactId);
|
window.addEventListener("setItem", () => {
|
||||||
// }
|
let userEmail = localStorage.getItem("userEmail");
|
||||||
// }, 30000);
|
this.userEmail = JSON.parse(userEmail);
|
||||||
|
|
||||||
|
});
|
||||||
// 确保初始加载后滚动到底部
|
// 确保初始加载后滚动到底部
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
|
@ -365,93 +370,120 @@ export default {
|
||||||
// 发送消息
|
// 发送消息
|
||||||
this.sendMessage();
|
this.sendMessage();
|
||||||
},
|
},
|
||||||
confirmUserType() {
|
|
||||||
try {
|
|
||||||
const token = localStorage.getItem("token");
|
|
||||||
|
|
||||||
if (!token) {
|
|
||||||
// 游客身份
|
|
||||||
this.userType = 0;
|
|
||||||
|
|
||||||
|
|
||||||
this.userEmail = `guest_${Date.now()}_${Math.random()
|
|
||||||
.toString(36)
|
|
||||||
.substr(2, 9)}`;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const userInfo = JSON.parse(
|
|
||||||
localStorage.getItem("jurisdiction") || "{}"
|
|
||||||
);
|
|
||||||
const email = JSON.parse(localStorage.getItem("userEmail") || "{}");
|
|
||||||
|
|
||||||
if (userInfo.roleKey === "customer_service") {
|
|
||||||
// 客服用户
|
|
||||||
this.userType = 2;
|
|
||||||
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// 登录用户
|
|
||||||
this.userType = 1;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
this.userEmail = email;
|
|
||||||
} catch (parseError) {
|
|
||||||
console.error("解析用户信息失败:", parseError);
|
|
||||||
// 解析失败时默认为游客
|
|
||||||
this.userType = 0;
|
|
||||||
this.userEmail = `guest_${Date.now()}_${Math.random()
|
|
||||||
.toString(36)
|
|
||||||
.substr(2, 9)}`;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error("获取用户信息失败:", error);
|
|
||||||
// 出错时默认为游客
|
|
||||||
this.userType = 0;
|
|
||||||
this.userEmail = `guest_${Date.now()}_${Math.random()
|
|
||||||
.toString(36)
|
|
||||||
.substr(2, 9)}`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 初始化 WebSocket 连接
|
// 初始化 WebSocket 连接
|
||||||
initWebSocket() {
|
initWebSocket() {
|
||||||
const wsUrl = `${process.env.VUE_APP_BASE_API}chat/ws`;
|
if (this.isWebSocketConnected) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const wsUrl = `${process.env.VUE_APP_BASE_API}chat/ws`;
|
||||||
this.stompClient = Stomp.client(wsUrl);
|
this.stompClient = Stomp.client(wsUrl);
|
||||||
|
|
||||||
// 配置连接参数
|
// 配置 STOMP 客户端参数
|
||||||
|
this.stompClient.splitLargeFrames = true; // 启用大型消息帧分割
|
||||||
|
|
||||||
|
// 修改调试日志的方式
|
||||||
|
this.stompClient.debug = (str) => {
|
||||||
|
// 只打印与客服相关的日志
|
||||||
|
if (
|
||||||
|
str.includes("CONNECTED") ||
|
||||||
|
str.includes("DISCONNECTED") ||
|
||||||
|
str.includes("ERROR")
|
||||||
|
) {
|
||||||
|
console.log("[客服系统]", str);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
email: this.userEmail,
|
email: this.userEmail,
|
||||||
type: 2,
|
type: this.userType,
|
||||||
};
|
};
|
||||||
// 连接并传入参数
|
|
||||||
this.stompClient.connect(
|
this.stompClient.connect(
|
||||||
headers, // 连接参数
|
headers,
|
||||||
(frame) => {
|
(frame) => {
|
||||||
// 连接成功回调
|
console.log("[客服系统] WebSocket 连接成功", frame);
|
||||||
console.log("WebSocket Connected:", frame);
|
this.isWebSocketConnected = true;
|
||||||
this.wsConnected = true;
|
this.connectionStatus = "connected";
|
||||||
this.subscribeToPersonalMessages();
|
this.subscribeToMessages();
|
||||||
},
|
},
|
||||||
(error) => {
|
(error) => {
|
||||||
// 连接错误回调
|
console.error("[客服系统] WebSocket 错误:", error);
|
||||||
console.error("WebSocket Error:", error);
|
this.handleDisconnect();
|
||||||
this.wsConnected = false;
|
|
||||||
this.$message.error("连接失败,正在重试...");
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 可选:配置心跳
|
// 配置心跳
|
||||||
this.stompClient.heartbeat.outgoing = 20000; // 20秒
|
this.stompClient.heartbeat.outgoing = 20000;
|
||||||
this.stompClient.heartbeat.incoming = 20000;
|
this.stompClient.heartbeat.incoming = 20000;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("初始化 CustomerService WebSocket 失败:", error);
|
||||||
|
this.handleDisconnect();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// 可选:开启调试日志
|
// // 订阅消息
|
||||||
this.stompClient.debug = function (str) {
|
subscribeToMessages() {
|
||||||
console.log("STOMP: " + str);
|
if (!this.stompClient || !this.isWebSocketConnected) return;
|
||||||
};
|
|
||||||
|
try {
|
||||||
|
// 修改订阅路径,使用客服特定的订阅路径
|
||||||
|
this.stompClient.subscribe(
|
||||||
|
`/sub/queue/customer/${this.userEmail}`,
|
||||||
|
this.handleIncomingMessage,
|
||||||
|
{
|
||||||
|
id: `customer_${this.userEmail}`,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
// this.stompClient.subscribe(
|
||||||
|
// `/user/queue/customer_service`, // 修改为客服专用的订阅路径
|
||||||
|
// this.handleIncomingMessage,
|
||||||
|
// {
|
||||||
|
// id: `customer_service_${this.userEmail}`,
|
||||||
|
// }
|
||||||
|
// );
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"CustomerService 成功订阅消息频道:",
|
||||||
|
`/sub/queue/customer/${this.userEmail}`
|
||||||
|
);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("CustomerService 订阅消息失败:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 断开连接
|
||||||
|
disconnectWebSocket() {
|
||||||
|
if (this.stompClient) {
|
||||||
|
try {
|
||||||
|
// 取消所有订阅
|
||||||
|
if (this.stompClient.subscriptions) {
|
||||||
|
Object.keys(this.stompClient.subscriptions).forEach((id) => {
|
||||||
|
this.stompClient.unsubscribe(id);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 断开连接
|
||||||
|
this.stompClient.deactivate();
|
||||||
|
this.isWebSocketConnected = false;
|
||||||
|
this.connectionStatus = "disconnected";
|
||||||
|
} catch (error) {
|
||||||
|
console.error("断开 CustomerService WebSocket 连接失败:", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理断开连接
|
||||||
|
handleDisconnect() {
|
||||||
|
this.isWebSocketConnected = false;
|
||||||
|
this.connectionStatus = "error";
|
||||||
|
|
||||||
|
// 尝试重新连接
|
||||||
|
setTimeout(() => {
|
||||||
|
if (!this.isWebSocketConnected) {
|
||||||
|
this.initWebSocket();
|
||||||
|
}
|
||||||
|
}, 5000);
|
||||||
},
|
},
|
||||||
|
|
||||||
// 获取当前的 UTC 时间
|
// 获取当前的 UTC 时间
|
||||||
|
@ -489,7 +521,7 @@ export default {
|
||||||
|
|
||||||
// 发送消息
|
// 发送消息
|
||||||
this.stompClient.send(
|
this.stompClient.send(
|
||||||
"/point/send/message",
|
"/point/send/message/to/user",
|
||||||
{},
|
{},
|
||||||
JSON.stringify(message)
|
JSON.stringify(message)
|
||||||
);
|
);
|
||||||
|
@ -502,7 +534,7 @@ export default {
|
||||||
isSelf: true,
|
isSelf: true,
|
||||||
isImage: false,
|
isImage: false,
|
||||||
roomId: this.currentContactId,
|
roomId: this.currentContactId,
|
||||||
type: 1
|
type: 1,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 重置当前聊天室的未读消息数
|
// 重置当前聊天室的未读消息数
|
||||||
|
@ -530,53 +562,38 @@ export default {
|
||||||
},
|
},
|
||||||
|
|
||||||
// 处理接收到的消息
|
// 处理接收到的消息
|
||||||
handleIncomingMessage(message) {
|
async handleIncomingMessage(message) {
|
||||||
|
try {
|
||||||
const msg = JSON.parse(message.body);
|
const msg = JSON.parse(message.body);
|
||||||
|
console.log("客服收到的消息", msg);
|
||||||
|
|
||||||
const messageData = {
|
const messageData = {
|
||||||
id: msg.id,
|
id: msg.id,
|
||||||
sender: msg.sendEmail,
|
sender: msg.sendEmail,
|
||||||
avatar: msg.sendUserType === 2 ? "iconfont icon-icon28" : "iconfont icon-user",
|
avatar:
|
||||||
|
msg.sendUserType === 2
|
||||||
|
? "iconfont icon-icon28"
|
||||||
|
: "iconfont icon-user",
|
||||||
content: msg.content,
|
content: msg.content,
|
||||||
time: new Date(msg.sendTime),
|
time: new Date(msg.sendTime),
|
||||||
isSelf: msg.sendUserType === this.userType && msg.sendEmail === this.userEmail,
|
isSelf:
|
||||||
|
msg.sendUserType === this.userType &&
|
||||||
|
msg.sendEmail === this.userEmail,
|
||||||
isImage: msg.type === 2,
|
isImage: msg.type === 2,
|
||||||
type: msg.type,
|
type: msg.type,
|
||||||
roomId: msg.roomId,
|
roomId: msg.roomId,
|
||||||
sendUserType: msg.sendUserType,
|
sendUserType: msg.sendUserType,
|
||||||
isCreate: msg.isCreate,
|
isCreate: msg.isCreate,
|
||||||
clientReadNum: msg.clientReadNum
|
clientReadNum: msg.clientReadNum,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 新聊天室处理
|
// 更新或创建聊天室
|
||||||
if (messageData.isCreate) {
|
|
||||||
this.handleNewChatRoom(messageData);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.updateChatRoomList(messageData);
|
|
||||||
|
|
||||||
// 当前聊天室消息
|
|
||||||
if (messageData.roomId === this.currentContactId) {
|
|
||||||
this.addMessageToChat(messageData);
|
|
||||||
this.markMessagesAsRead(messageData.roomId);
|
|
||||||
} else {
|
|
||||||
this.incrementUnreadCount(messageData.roomId, messageData.clientReadNum);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
// 处理新聊天室创建
|
|
||||||
handleNewChatRoom(messageData) {
|
|
||||||
// 检查是否已存在该聊天室
|
|
||||||
const existingContact = this.contacts.find(
|
const existingContact = this.contacts.find(
|
||||||
(c) => c.roomId === messageData.roomId
|
(c) => c.roomId === messageData.roomId
|
||||||
);
|
);
|
||||||
console.log("messageData:hhhhshshsshshshhh好的好的和", messageData);
|
|
||||||
|
|
||||||
|
if (!existingContact) {
|
||||||
|
// 如果聊天室不存在,创建新的聊天室
|
||||||
// && messageData.isCreate && messageData.sender
|
|
||||||
if (!existingContact) {//只需要判断是否在联系人列表中存在 如果不在就创建新的聊天室对象
|
|
||||||
// 创建新的聊天室对象
|
|
||||||
const newContact = {
|
const newContact = {
|
||||||
roomId: messageData.roomId,
|
roomId: messageData.roomId,
|
||||||
name: messageData.sender,
|
name: messageData.sender,
|
||||||
|
@ -586,8 +603,73 @@ export default {
|
||||||
important: false,
|
important: false,
|
||||||
isGuest: messageData.sendUserType === 0,
|
isGuest: messageData.sendUserType === 0,
|
||||||
sendUserType: messageData.sendUserType,
|
sendUserType: messageData.sendUserType,
|
||||||
isManualCreated: true, // 新增:标记为手动创建
|
isManualCreated: true,
|
||||||
clientReadNum: messageData.clientReadNum, //未读条数
|
};
|
||||||
|
|
||||||
|
this.contacts.push(newContact);
|
||||||
|
this.$set(this.messages, messageData.roomId, []);
|
||||||
|
} else {
|
||||||
|
// 如果聊天室已存在,更新最后一条消息
|
||||||
|
existingContact.lastMessage = messageData.isImage ? "[图片]" : messageData.content;
|
||||||
|
existingContact.lastTime = messageData.time;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加消息到聊天记录
|
||||||
|
if (!this.messages[messageData.roomId]) {
|
||||||
|
this.$set(this.messages, messageData.roomId, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.messages[messageData.roomId].push({
|
||||||
|
id: messageData.id,
|
||||||
|
sender: messageData.sender,
|
||||||
|
avatar: messageData.avatar,
|
||||||
|
content: messageData.content,
|
||||||
|
time: messageData.time,
|
||||||
|
isSelf: messageData.isSelf,
|
||||||
|
isImage: messageData.isImage,
|
||||||
|
type: messageData.type,
|
||||||
|
roomId: messageData.roomId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果是当前选中的聊天室,标记为已读
|
||||||
|
if (messageData.roomId === this.currentContactId) {
|
||||||
|
this.markMessagesAsRead(messageData.roomId);
|
||||||
|
} else {
|
||||||
|
// 更新未读消息数
|
||||||
|
const contact = this.contacts.find((c) => c.roomId === messageData.roomId);
|
||||||
|
if (contact) {
|
||||||
|
contact.unread = (contact.unread || 0) + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重新排序联系人列表
|
||||||
|
this.sortContacts();
|
||||||
|
} catch (error) {
|
||||||
|
console.error("处理新消息失败:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 处理新聊天室创建
|
||||||
|
handleNewChatRoom(messageData) {
|
||||||
|
// 检查是否已存在该聊天室
|
||||||
|
const existingContact = this.contacts.find(
|
||||||
|
(c) => c.roomId === messageData.roomId
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!existingContact) {
|
||||||
|
// 创建新的聊天室对象
|
||||||
|
const newContact = {
|
||||||
|
roomId: messageData.roomId,
|
||||||
|
name: messageData.sender,
|
||||||
|
// 修改这里:使用实际收到的消息内容作为最后一条消息
|
||||||
|
lastMessage: messageData.isImage ? "[图片]" : messageData.content,
|
||||||
|
lastTime: messageData.time,
|
||||||
|
unread: 1,
|
||||||
|
important: false,
|
||||||
|
isGuest: messageData.sendUserType === 0,
|
||||||
|
sendUserType: messageData.sendUserType,
|
||||||
|
isManualCreated: true,
|
||||||
|
clientReadNum: messageData.clientReadNum,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加到聊天列表
|
// 添加到聊天列表
|
||||||
|
@ -616,6 +698,7 @@ export default {
|
||||||
this.manualCreatedRooms.push(newContact);
|
this.manualCreatedRooms.push(newContact);
|
||||||
this.saveManualCreatedRooms();
|
this.saveManualCreatedRooms();
|
||||||
|
|
||||||
|
// 重新排序联系人列表
|
||||||
this.sortContacts();
|
this.sortContacts();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -657,23 +740,53 @@ export default {
|
||||||
console.error("加载手动创建的聊天室失败:", error);
|
console.error("加载手动创建的聊天室失败:", error);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 添加新方法:创建新聊天室
|
||||||
|
async createNewChatRoom(messageData) {
|
||||||
|
try {
|
||||||
|
// 调用后端 API 创建新的聊天室
|
||||||
|
const response = await createChatRoom({
|
||||||
|
userEmail: messageData.sender,
|
||||||
|
userType: messageData.sendUserType,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response && response.code === 200) {
|
||||||
|
const newRoom = {
|
||||||
|
userEmail: messageData.sender,
|
||||||
|
roomId: response.data.roomId,
|
||||||
|
lastMessage: messageData.content,
|
||||||
|
lastMessageTime: messageData.time,
|
||||||
|
unreadCount: messageData.clientReadNum || 0,
|
||||||
|
userType: messageData.sendUserType,
|
||||||
|
};
|
||||||
|
|
||||||
|
this.chatRooms.unshift(newRoom);
|
||||||
|
return newRoom;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("创建新聊天室失败:", error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
},
|
||||||
// 更新聊天室列表
|
// 更新聊天室列表
|
||||||
updateChatRoomList(messageData) {
|
updateChatRoomList(messageData) {
|
||||||
// 寻找现有联系人
|
const roomIndex = this.chatRooms.findIndex(
|
||||||
const contactIndex = this.contacts.findIndex(
|
(room) => room.roomId === messageData.roomId
|
||||||
(c) => c.roomId === messageData.roomId
|
|
||||||
);
|
);
|
||||||
|
|
||||||
if (contactIndex !== -1) {
|
if (roomIndex !== -1) {
|
||||||
// 更新现有联系人的最后消息和时间
|
// 更新现有聊天室信息
|
||||||
const contact = this.contacts[contactIndex];
|
this.chatRooms[roomIndex] = {
|
||||||
contact.lastMessage = messageData.isImage
|
...this.chatRooms[roomIndex],
|
||||||
? "[图片]"
|
lastMessage: messageData.content,
|
||||||
: messageData.content;
|
lastMessageTime: messageData.time,
|
||||||
contact.lastTime = messageData.time;
|
unreadCount:
|
||||||
|
messageData.clientReadNum || this.chatRooms[roomIndex].unreadCount,
|
||||||
|
};
|
||||||
|
|
||||||
// 重新排序联系人列表,将最新消息的联系人移到前面
|
// 将更新的聊天室移到列表顶部
|
||||||
this.sortContacts();
|
const updatedRoom = this.chatRooms.splice(roomIndex, 1)[0];
|
||||||
|
this.chatRooms.unshift(updatedRoom);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 修改标记为已读方法,添加参数支持
|
// 修改标记为已读方法,添加参数支持
|
||||||
|
@ -683,7 +796,7 @@ export default {
|
||||||
try {
|
try {
|
||||||
const data = {
|
const data = {
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
userType:2,
|
userType: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
const response = await getReadMessage(data);
|
const response = await getReadMessage(data);
|
||||||
|
@ -721,13 +834,15 @@ export default {
|
||||||
this.loadingRooms = true;
|
this.loadingRooms = true;
|
||||||
const response = await getRoomList();
|
const response = await getRoomList();
|
||||||
if (response?.code === 200) {
|
if (response?.code === 200) {
|
||||||
// 创建全新的联系人数组
|
|
||||||
const newContacts = response.rows.map((room) => {
|
const newContacts = response.rows.map((room) => {
|
||||||
// 查找旧联系人中是否存在相同roomId
|
|
||||||
const existingContact = this.contacts.find(
|
const existingContact = this.contacts.find(
|
||||||
(c) => c.roomId === room.id
|
(c) => c.roomId === room.id
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const manualRoom = this.manualCreatedRooms.find(
|
||||||
|
(c) => c.roomId === room.id
|
||||||
|
);
|
||||||
|
|
||||||
const isImportant =
|
const isImportant =
|
||||||
room.flag === 1
|
room.flag === 1
|
||||||
? true
|
? true
|
||||||
|
@ -743,27 +858,19 @@ export default {
|
||||||
roomId: room.id,
|
roomId: room.id,
|
||||||
name: room.userEmail || "未命名聊天室",
|
name: room.userEmail || "未命名聊天室",
|
||||||
avatar: this.getDefaultAvatar(room.roomName || "未命名聊天室"),
|
avatar: this.getDefaultAvatar(room.roomName || "未命名聊天室"),
|
||||||
lastMessage: room.lastMessage || "暂无消息",
|
// 修改这里:优先使用服务器返回的消息,其次是现有联系人的消息
|
||||||
|
lastMessage: room.lastMessage || (existingContact ? existingContact.lastMessage : "暂无消息"),
|
||||||
lastTime: room.lastUserSendTime
|
lastTime: room.lastUserSendTime
|
||||||
? new Date(room.lastUserSendTime)
|
? new Date(room.lastUserSendTime)
|
||||||
: new Date(),
|
: new Date(),
|
||||||
unread: existingContact?.unread ?? room.unreadCount ?? 0,
|
unread: existingContact?.unread ?? room.unreadCount ?? 0,
|
||||||
important: isImportant,
|
important: isImportant,
|
||||||
isManualCreated: existingContact?.isManualCreated || false, // 保持手动创建标记
|
isManualCreated: manualRoom ? true : false,
|
||||||
|
sendUserType: room.sendUserType,
|
||||||
|
isGuest: room.sendUserType === 0,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
// 合并手动创建的聊天室
|
|
||||||
this.manualCreatedRooms.forEach((room) => {
|
|
||||||
const exists = newContacts.find((c) => c.roomId === room.roomId);
|
|
||||||
if (!exists) {
|
|
||||||
newContacts.push({
|
|
||||||
...room,
|
|
||||||
lastTime: new Date(room.lastTime),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 更新联系人列表
|
// 更新联系人列表
|
||||||
this.contacts = newContacts;
|
this.contacts = newContacts;
|
||||||
this.sortContacts();
|
this.sortContacts();
|
||||||
|
@ -774,7 +881,7 @@ export default {
|
||||||
} finally {
|
} finally {
|
||||||
this.loadingRooms = false;
|
this.loadingRooms = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 加载更多历史消息
|
// 加载更多历史消息
|
||||||
async loadMoreHistory() {
|
async loadMoreHistory() {
|
||||||
|
@ -789,7 +896,7 @@ export default {
|
||||||
this.history7Params.id = lastMsg ? lastMsg.id : "";
|
this.history7Params.id = lastMsg ? lastMsg.id : "";
|
||||||
// this.history7Params.pageNum += 1; // 递增页码
|
// this.history7Params.pageNum += 1; // 递增页码
|
||||||
this.history7Params.roomId = this.currentContactId;
|
this.history7Params.roomId = this.currentContactId;
|
||||||
|
this.history7Params.email = this.userEmail;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.messagesLoading = true;
|
this.messagesLoading = true;
|
||||||
|
@ -842,7 +949,7 @@ export default {
|
||||||
id: "", // 首次加载为空
|
id: "", // 首次加载为空
|
||||||
// pageNum: 1, // 首次页码为1
|
// pageNum: 1, // 首次页码为1
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
userType:2,
|
userType: 2,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 加载历史消息
|
// 加载历史消息
|
||||||
|
@ -850,7 +957,6 @@ export default {
|
||||||
|
|
||||||
// 标记消息为已读
|
// 标记消息为已读
|
||||||
await this.markMessagesAsRead(roomId);
|
await this.markMessagesAsRead(roomId);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("选择联系人失败:", error);
|
console.error("选择联系人失败:", error);
|
||||||
this.$message.error("加载聊天记录失败");
|
this.$message.error("加载聊天记录失败");
|
||||||
|
@ -861,7 +967,7 @@ export default {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
//判断是否在聊天框底部
|
//判断是否在聊天框底部
|
||||||
isAtBottom() {
|
isAtBottom() {
|
||||||
const container = this.$refs.messageContainer;
|
const container = this.$refs.messageContainer;
|
||||||
|
@ -878,16 +984,21 @@ export default {
|
||||||
if (!roomId) return;
|
if (!roomId) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
this.history7Params.email = this.userEmail;
|
||||||
|
this.history7Params.roomId = roomId;
|
||||||
const response = await getHistory7(this.history7Params);
|
const response = await getHistory7(this.history7Params);
|
||||||
|
|
||||||
if (response?.code === 200 && response.data) {
|
if (response?.code === 200 && response.data) {
|
||||||
// 处理消息数据
|
// 处理消息数据
|
||||||
let roomMessages = response.data
|
let roomMessages = response.data
|
||||||
.filter(msg => msg.roomId == roomId)
|
.filter((msg) => msg.roomId == roomId)
|
||||||
.map(msg => ({
|
.map((msg) => ({
|
||||||
id: msg.id,
|
id: msg.id,
|
||||||
sender: msg.isSelf === 1 ? "我" : msg.sendEmail || "未知发送者",
|
sender: msg.isSelf === 1 ? "我" : msg.sendEmail || "未知发送者",
|
||||||
avatar: msg.sendUserType == 2 ? "iconfont icon-icon28" : "iconfont icon-user",
|
avatar:
|
||||||
|
msg.sendUserType == 2
|
||||||
|
? "iconfont icon-icon28"
|
||||||
|
: "iconfont icon-user",
|
||||||
content: msg.content,
|
content: msg.content,
|
||||||
time: new Date(msg.createTime),
|
time: new Date(msg.createTime),
|
||||||
isSelf: msg.isSelf === 1,
|
isSelf: msg.isSelf === 1,
|
||||||
|
@ -895,7 +1006,7 @@ export default {
|
||||||
isRead: msg.isRead === 1,
|
isRead: msg.isRead === 1,
|
||||||
type: msg.type,
|
type: msg.type,
|
||||||
roomId: msg.roomId,
|
roomId: msg.roomId,
|
||||||
sendUserType: msg.sendUserType
|
sendUserType: msg.sendUserType,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 按时间排序
|
// 按时间排序
|
||||||
|
@ -905,11 +1016,10 @@ export default {
|
||||||
this.$set(this.messages, roomId, roomMessages);
|
this.$set(this.messages, roomId, roomMessages);
|
||||||
|
|
||||||
// 更新联系人的未读状态
|
// 更新联系人的未读状态
|
||||||
const contact = this.contacts.find(c => c.roomId === roomId);
|
const contact = this.contacts.find((c) => c.roomId === roomId);
|
||||||
if (contact) {
|
if (contact) {
|
||||||
contact.unread = 0;
|
contact.unread = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// 如果没有消息数据,初始化为空数组
|
// 如果没有消息数据,初始化为空数组
|
||||||
this.$set(this.messages, roomId, []);
|
this.$set(this.messages, roomId, []);
|
||||||
|
@ -922,7 +1032,7 @@ export default {
|
||||||
this.$message.error("加载消息异常");
|
this.$message.error("加载消息异常");
|
||||||
this.$set(this.messages, roomId, []);
|
this.$set(this.messages, roomId, []);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 添加消息到聊天记录
|
// 添加消息到聊天记录
|
||||||
addMessageToChat(messageData) {
|
addMessageToChat(messageData) {
|
||||||
|
@ -935,7 +1045,7 @@ export default {
|
||||||
|
|
||||||
// 构造消息对象
|
// 构造消息对象
|
||||||
const message = {
|
const message = {
|
||||||
id: messageData.id,
|
id: messageData.id || Date.now(), // 如果没有id则使用时间戳
|
||||||
sender: messageData.sender,
|
sender: messageData.sender,
|
||||||
avatar: messageData.avatar || (messageData.isSelf ? "iconfont icon-icon28" : "iconfont icon-user"),
|
avatar: messageData.avatar || (messageData.isSelf ? "iconfont icon-icon28" : "iconfont icon-user"),
|
||||||
content: messageData.content,
|
content: messageData.content,
|
||||||
|
@ -944,7 +1054,7 @@ export default {
|
||||||
isImage: messageData.isImage || false,
|
isImage: messageData.isImage || false,
|
||||||
type: messageData.type || 1,
|
type: messageData.type || 1,
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
isRead: messageData.isRead || false
|
isRead: messageData.isRead || false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 添加消息到数组
|
// 添加消息到数组
|
||||||
|
@ -953,8 +1063,8 @@ export default {
|
||||||
// 更新最后一条消息
|
// 更新最后一条消息
|
||||||
this.updateContactLastMessage({
|
this.updateContactLastMessage({
|
||||||
roomId: roomId,
|
roomId: roomId,
|
||||||
content: message.content,
|
content: message.isImage ? "[图片]" : message.content,
|
||||||
isImage: message.isImage
|
isImage: message.isImage,
|
||||||
});
|
});
|
||||||
|
|
||||||
// 如果是当前聊天室且用户在底部,滚动到底部
|
// 如果是当前聊天室且用户在底部,滚动到底部
|
||||||
|
@ -963,11 +1073,19 @@ export default {
|
||||||
this.scrollToBottom();
|
this.scrollToBottom();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 处理图片上传
|
// 处理图片上传
|
||||||
async handleImageUpload(event) {
|
async handleImageUpload(event) {
|
||||||
if (!this.currentContact || !this.wsConnected) return;
|
// 检查是否有选中的联系人和 WebSocket 连接
|
||||||
|
if (!this.currentContact) {
|
||||||
|
this.$message.warning("请先选择联系人");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.stompClient || !this.isWebSocketConnected) {
|
||||||
|
this.$message.warning("聊天连接已断开,请刷新页面重试");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const file = event.target.files[0];
|
const file = event.target.files[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
@ -988,62 +1106,67 @@ export default {
|
||||||
this.sending = true;
|
this.sending = true;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 准备FormData
|
// 将图片转换为 base64
|
||||||
const formData = new FormData();
|
const reader = new FileReader();
|
||||||
formData.append("file", file);
|
reader.onload = (e) => {
|
||||||
|
const base64Image = e.target.result;
|
||||||
this.$message.info("正在上传图片...");
|
|
||||||
|
|
||||||
// 调用API上传图片到后端
|
|
||||||
const response = await getFileUpdate(formData);
|
|
||||||
console.log("图片上传响应:", response);
|
|
||||||
|
|
||||||
if (response && response.code === 200 && response.data) {
|
|
||||||
// 获取后端返回的图片信息
|
|
||||||
const imageData = response.data;
|
|
||||||
const imageUrl = imageData.url;
|
|
||||||
|
|
||||||
console.log("后端返回的图片URL:", imageUrl);
|
|
||||||
|
|
||||||
// 构建WebSocket消息对象
|
// 构建WebSocket消息对象
|
||||||
const message = {
|
const message = {
|
||||||
content: imageUrl, // 使用后端返回的URL作为内容
|
content: base64Image,
|
||||||
type: 2, // 类型2表示图片消息
|
type: 2, // 类型2表示图片消息
|
||||||
email: this.currentContact.name,
|
email: this.currentContact.name,
|
||||||
receiveUserType: this.userType,
|
receiveUserType: this.currentContact.sendUserType || 1, // 使用联系人的用户类型
|
||||||
roomId: this.currentContactId,
|
roomId: this.currentContactId,
|
||||||
};
|
};
|
||||||
|
|
||||||
// 发送WebSocket消息
|
// 发送WebSocket消息
|
||||||
this.stompClient.send(
|
this.stompClient.send(
|
||||||
"/point/send/message",
|
"/point/send/message/to/user",
|
||||||
{},
|
{},
|
||||||
JSON.stringify(message)
|
JSON.stringify(message)
|
||||||
);
|
);
|
||||||
|
|
||||||
// 添加到本地消息列表以在UI中显示
|
// 添加到本地消息列表
|
||||||
this.addMessageToChat({
|
this.addMessageToChat({
|
||||||
sender: "我",
|
sender: "我",
|
||||||
avatar: "iconfont icon-icon28",
|
content: base64Image,
|
||||||
content: imageUrl,
|
|
||||||
time: new Date(),
|
time: new Date(),
|
||||||
isSelf: true,
|
isSelf: true,
|
||||||
isImage: true, // 标记为图片消息
|
isImage: true,
|
||||||
|
type: 2,
|
||||||
|
roomId: this.currentContactId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 更新联系人最后一条消息
|
||||||
|
this.updateContactLastMessage({
|
||||||
|
roomId: this.currentContactId,
|
||||||
|
content: "[图片]",
|
||||||
|
isImage: true
|
||||||
});
|
});
|
||||||
|
|
||||||
this.$message.success("图片已发送");
|
this.$message.success("图片已发送");
|
||||||
} else {
|
};
|
||||||
this.$message.error("图片上传失败: " + (response?.msg || "未知错误"));
|
|
||||||
}
|
reader.onerror = () => {
|
||||||
|
this.$message.error("图片读取失败");
|
||||||
|
this.sending = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 开始读取文件
|
||||||
|
reader.readAsDataURL(file);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("上传图片异常:", error);
|
console.error("上传图片异常:", error);
|
||||||
this.$message.error("上传图片失败,请重试");
|
this.$message.error("上传图片失败,请重试");
|
||||||
} finally {
|
} finally {
|
||||||
this.sending = false;
|
this.sending = false;
|
||||||
// 清空文件选择器,以便于用户可以再次选择同一个文件
|
// 清空文件选择器
|
||||||
this.$refs.imageInput.value = "";
|
this.$refs.imageInput.value = "";
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 更新联系人最后一条消息
|
// 更新联系人最后一条消息
|
||||||
updateContactLastMessage(message) {
|
updateContactLastMessage(message) {
|
||||||
|
@ -1249,7 +1372,7 @@ export default {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
// 加载历史消息(7天前)
|
// 小时钟加载历史消息(7天前)
|
||||||
async loadHistory() {
|
async loadHistory() {
|
||||||
this.loadingHistory = true;
|
this.loadingHistory = true;
|
||||||
this.userViewHistory = true; // 用户主动查看历史
|
this.userViewHistory = true; // 用户主动查看历史
|
||||||
|
@ -1258,8 +1381,17 @@ export default {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
this.messagesLoading = true;
|
this.messagesLoading = true;
|
||||||
|
// 获取当前已加载的消息列表
|
||||||
|
const currentMsgs = this.messages[this.currentContactId] || [];
|
||||||
|
|
||||||
const response = await getHistory({ roomId: this.currentContactId });
|
// 取最后一条消息的id
|
||||||
|
const lastMsg =
|
||||||
|
currentMsgs.length > 0 ? currentMsgs[currentMsgs.length - 1] : null;
|
||||||
|
this.history7Params.id = lastMsg ? lastMsg.id : "";
|
||||||
|
// this.history7Params.pageNum += 1; // 递增页码
|
||||||
|
this.history7Params.roomId = this.currentContactId;
|
||||||
|
this.history7Params.email = this.userEmail;
|
||||||
|
const response = await getHistory7(this.history7Params);
|
||||||
|
|
||||||
if (response && response.code === 200 && response.data) {
|
if (response && response.code === 200 && response.data) {
|
||||||
let historyMessages = response.data
|
let historyMessages = response.data
|
||||||
|
@ -1320,6 +1452,9 @@ export default {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 组件销毁前断开连接
|
||||||
|
this.disconnectWebSocket();
|
||||||
|
|
||||||
// if (this.roomListInterval) {
|
// if (this.roomListInterval) {
|
||||||
// clearInterval(this.roomListInterval);
|
// clearInterval(this.roomListInterval);
|
||||||
// }
|
// }
|
||||||
|
|
|
@ -76,11 +76,11 @@ export default {
|
||||||
nameTextStyle: {
|
nameTextStyle: {
|
||||||
|
|
||||||
padding: [0, 0, 0, -40],
|
padding: [0, 0, 0, -40],
|
||||||
}
|
},
|
||||||
// min: `dataMin`,
|
// min: `dataMin`,
|
||||||
// max: `dataMax`,
|
// max: `dataMax`,
|
||||||
// axisLabel: {
|
axisLabel: {
|
||||||
// formatter: function (value) {
|
formatter: function (value) {
|
||||||
// let data
|
// let data
|
||||||
// if (value > 10000000) {
|
// if (value > 10000000) {
|
||||||
// data = `${(value / 10000000)} KW`
|
// data = `${(value / 10000000)} KW`
|
||||||
|
@ -89,9 +89,9 @@ export default {
|
||||||
// } else if (value / 10000) {
|
// } else if (value / 10000) {
|
||||||
// data = `${(value / 10000)} W`
|
// data = `${(value / 10000)} W`
|
||||||
// }
|
// }
|
||||||
// return data
|
return value
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -506,7 +506,8 @@ export default {
|
||||||
show: true,
|
show: true,
|
||||||
amount: 1,
|
amount: 1,
|
||||||
|
|
||||||
}
|
},
|
||||||
|
|
||||||
// { //告知已删除此币种 Radiant
|
// { //告知已删除此币种 Radiant
|
||||||
// value: "dgb2_odo",
|
// value: "dgb2_odo",
|
||||||
// label: "dgb-odocrypt-pool2",
|
// label: "dgb-odocrypt-pool2",
|
||||||
|
@ -825,9 +826,38 @@ export default {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}else{ //动态计算图表的grid.left 让左侧的Y轴标签显示完全
|
||||||
|
|
||||||
|
|
||||||
|
const yAxis = this.option.yAxis[0]; // 第一个 Y 轴
|
||||||
|
const maxValue = Math.max(...this.option.series[0].data); // 获取数据最大值
|
||||||
|
const formatter = yAxis.axisLabel.formatter;
|
||||||
|
const formattedValue = formatter(maxValue); // 格式化最大值
|
||||||
|
|
||||||
|
// 创建一个临时 DOM 元素计算宽度
|
||||||
|
const tempDiv = document.createElement('div');
|
||||||
|
tempDiv.style.position = 'absolute';
|
||||||
|
tempDiv.style.visibility = 'hidden';
|
||||||
|
tempDiv.style.fontSize = '12px'; // 与 axisLabel.fontSize 一致
|
||||||
|
tempDiv.innerText = formattedValue;
|
||||||
|
document.body.appendChild(tempDiv);
|
||||||
|
const labelWidth = tempDiv.offsetWidth;
|
||||||
|
document.body.removeChild(tempDiv);
|
||||||
|
|
||||||
|
// 动态设置 grid.left,加上安全边距
|
||||||
|
const safeMargin = 20;
|
||||||
|
this.option.grid.left = labelWidth + safeMargin + 'px';
|
||||||
|
// this.$nextTick(
|
||||||
|
// // 更新图表
|
||||||
|
// this.inCharts()
|
||||||
|
|
||||||
|
// )
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
this.getBlockInfoData(this.BlockInfoParams)
|
this.getBlockInfoData(this.BlockInfoParams)
|
||||||
this.getCoinInfoData(this.params)
|
this.getCoinInfoData(this.params)
|
||||||
this.getPoolPowerData(this.PowerParams)
|
this.getPoolPowerData(this.PowerParams)
|
||||||
|
@ -1395,52 +1425,111 @@ export default {
|
||||||
handelCalculation() {
|
handelCalculation() {
|
||||||
this.calculateIncome()
|
this.calculateIncome()
|
||||||
},
|
},
|
||||||
|
// 获取单个币种项目的完整宽度(包含margin)
|
||||||
|
getItemFullWidth() {
|
||||||
|
const listEl = this.$refs.currencyList;
|
||||||
|
if (!listEl) return 120;
|
||||||
|
|
||||||
|
const firstItem = listEl.querySelector('.list-item');
|
||||||
|
if (!firstItem) return 120;
|
||||||
|
|
||||||
|
const style = window.getComputedStyle(firstItem);
|
||||||
|
const width = firstItem.offsetWidth;
|
||||||
|
const marginLeft = parseInt(style.marginLeft) || 0;
|
||||||
|
const marginRight = parseInt(style.marginRight) || 0;
|
||||||
|
|
||||||
|
return width + marginLeft + marginRight;
|
||||||
|
},
|
||||||
|
|
||||||
// 左滑动逻辑
|
// 左滑动逻辑
|
||||||
scrollLeft() {
|
scrollLeft() {
|
||||||
const allLength = this.currencyList.length * 120
|
const listEl = this.$refs.currencyList;
|
||||||
const boxLength = document.getElementById('list-box').clientWidth
|
const listBox = this.$refs.listBox;
|
||||||
if (allLength < boxLength) return
|
if (!listEl || !listBox) return;
|
||||||
const listEl = document.getElementById('list')
|
|
||||||
const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
|
const itemFullWidth = this.getItemFullWidth();
|
||||||
if (leftMove + boxLength - 360 < boxLength) {
|
const step = 2 * itemFullWidth; // 每次滑动2个币种
|
||||||
// 到最开始的时候
|
const allLength = this.currencyList.length * itemFullWidth;
|
||||||
listEl.style.left = '0PX'
|
const boxLength = listBox.clientWidth;
|
||||||
|
|
||||||
|
if (allLength <= boxLength) return;
|
||||||
|
|
||||||
|
let currentLeft = Math.abs(parseInt(listEl.style.transform.replace('translateX(', '').replace('px)', '')) || 0);
|
||||||
|
let newLeft = currentLeft - step;
|
||||||
|
|
||||||
|
listEl.classList.add('scrolling');
|
||||||
|
|
||||||
|
if (newLeft <= 0) {
|
||||||
|
listEl.style.transform = 'translateX(0)';
|
||||||
} else {
|
} else {
|
||||||
listEl.style.left = '-' + (leftMove - 360) + 'PX'
|
listEl.style.transform = `translateX(-${newLeft}px)`;
|
||||||
}
|
|
||||||
},
|
|
||||||
// 右滑动逻辑
|
|
||||||
scrollRight() {
|
|
||||||
const allLength = this.currencyList.length * 120
|
|
||||||
const boxLength = document.getElementById('list-box').clientWidth
|
|
||||||
if (allLength < boxLength) return
|
|
||||||
const listEl = document.getElementById('list')
|
|
||||||
const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
|
|
||||||
if (leftMove + boxLength + 360 > allLength) {
|
|
||||||
listEl.style.left = '-' + (allLength - boxLength) + 'PX'
|
|
||||||
} else {
|
|
||||||
listEl.style.left = '-' + (leftMove + 360) + 'PX'
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 增加动画时间到 500ms
|
||||||
|
setTimeout(() => {
|
||||||
|
listEl.classList.remove('scrolling');
|
||||||
|
}, 500);
|
||||||
},
|
},
|
||||||
|
|
||||||
// clickCurrency: throttle(function(item) {
|
// 右滑动逻辑
|
||||||
// this.currency = item.label
|
scrollRight() {
|
||||||
// this.currencyPath = item.imgUrl
|
const listEl = this.$refs.currencyList;
|
||||||
// this.params.coin = item.value
|
const listBox = this.$refs.listBox;
|
||||||
// this.BlockInfoParams.coin = item.value
|
if (!listEl || !listBox) return;
|
||||||
// this.itemActive = item.value
|
|
||||||
// this.PowerParams.coin = item.value
|
const itemFullWidth = this.getItemFullWidth();
|
||||||
// this.getCoinInfoData(this.params)
|
const step = 2 * itemFullWidth; // 每次滑动2个币种
|
||||||
// this.getBlockInfoData(this.BlockInfoParams)
|
const allLength = this.currencyList.length * itemFullWidth;
|
||||||
// // this.getPoolPowerData(this.PowerParams)
|
const boxLength = listBox.clientWidth;
|
||||||
// // this.getMinerCountData(this.params)
|
|
||||||
// if (this.powerActive) {
|
if (allLength <= boxLength) return;
|
||||||
// this.handelPower()
|
|
||||||
// } else if (!this.powerActive) {
|
let currentLeft = Math.abs(parseInt(listEl.style.transform.replace('translateX(', '').replace('px)', '')) || 0);
|
||||||
// this.handelMiner()
|
let newLeft = currentLeft + step;
|
||||||
|
const maxLeft = allLength - boxLength;
|
||||||
|
|
||||||
|
listEl.classList.add('scrolling');
|
||||||
|
|
||||||
|
if (newLeft >= maxLeft) {
|
||||||
|
listEl.style.transform = `translateX(-${maxLeft}px)`;
|
||||||
|
} else {
|
||||||
|
listEl.style.transform = `translateX(-${newLeft}px)`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 增加动画时间到 500ms
|
||||||
|
setTimeout(() => {
|
||||||
|
listEl.classList.remove('scrolling');
|
||||||
|
}, 500);
|
||||||
|
},
|
||||||
|
// // 左滑动逻辑
|
||||||
|
// scrollLeft() {
|
||||||
|
// const allLength = this.currencyList.length * 120
|
||||||
|
// const boxLength = document.getElementById('list-box').clientWidth
|
||||||
|
// if (allLength < boxLength) return
|
||||||
|
// const listEl = document.getElementById('list')
|
||||||
|
// const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
|
||||||
|
// if (leftMove + boxLength - 360 < boxLength) {
|
||||||
|
// // 到最开始的时候
|
||||||
|
// listEl.style.left = '0PX'
|
||||||
|
// } else {
|
||||||
|
// listEl.style.left = '-' + (leftMove - 360) + 'PX'
|
||||||
// }
|
// }
|
||||||
// }, 1000),
|
// },
|
||||||
|
// // 右滑动逻辑
|
||||||
|
// scrollRight() {
|
||||||
|
// const allLength = this.currencyList.length * 120
|
||||||
|
// const boxLength = document.getElementById('list-box').clientWidth
|
||||||
|
// if (allLength < boxLength) return
|
||||||
|
// const listEl = document.getElementById('list')
|
||||||
|
// const leftMove = Math.abs(parseInt(window.getComputedStyle(listEl, null)?.left))
|
||||||
|
// if (leftMove + boxLength + 360 > allLength) {
|
||||||
|
// listEl.style.left = '-' + (allLength - boxLength) + 'PX'
|
||||||
|
// } else {
|
||||||
|
// listEl.style.left = '-' + (leftMove + 360) + 'PX'
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
|
||||||
|
|
||||||
handleActiveItemChange(item) {
|
handleActiveItemChange(item) {
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
|
|
||||||
|
|
|
@ -340,6 +340,29 @@
|
||||||
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
|
||||||
<el-card>
|
<el-card>
|
||||||
<div class="monitor-list">
|
<div class="monitor-list">
|
||||||
|
<div class="btn left" @click="scrollLeft">
|
||||||
|
<i class="iconfont icon-icon-prev" />
|
||||||
|
</div>
|
||||||
|
<div ref="listBox" class="list-box">
|
||||||
|
<div ref="currencyList" class="list">
|
||||||
|
<div
|
||||||
|
v-for="item in currencyList"
|
||||||
|
:key="item.value"
|
||||||
|
@click="clickCurrency(item)"
|
||||||
|
class="list-item"
|
||||||
|
>
|
||||||
|
<img :src="item.img" alt="coin" />
|
||||||
|
<span :class="{ active: itemActive === item.value }">
|
||||||
|
{{ item.label }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="btn right" @click="scrollRight">
|
||||||
|
<i class="iconfont icon-zuoyoujiantou1" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- <div class="monitor-list">
|
||||||
<div class="btn left" @click="scrollLeft">
|
<div class="btn left" @click="scrollLeft">
|
||||||
<i class="iconfont icon-icon-prev" />
|
<i class="iconfont icon-icon-prev" />
|
||||||
</div>
|
</div>
|
||||||
|
@ -361,7 +384,7 @@
|
||||||
<div class="btn right" @click="scrollRight">
|
<div class="btn right" @click="scrollRight">
|
||||||
<i class="iconfont icon-zuoyoujiantou1" />
|
<i class="iconfont icon-zuoyoujiantou1" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div> -->
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
@ -406,7 +429,6 @@
|
||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<div
|
<div
|
||||||
id="chart"
|
id="chart"
|
||||||
v-if="powerActive"
|
v-if="powerActive"
|
||||||
|
@ -3119,6 +3141,63 @@ export default {
|
||||||
transition: left 1s;
|
transition: left 1s;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.list-box {//加的
|
||||||
|
width: calc(100% - 100px);
|
||||||
|
overflow: hidden;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list {
|
||||||
|
display: flex;
|
||||||
|
will-change: transform;
|
||||||
|
padding-left: 2%;
|
||||||
|
|
||||||
|
&.scrolling {
|
||||||
|
// 增加动画时长到 0.5s,使用更平滑的缓动函数
|
||||||
|
transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-item {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 120px;
|
||||||
|
height: 95%;
|
||||||
|
margin-left: 18px;
|
||||||
|
// 增加过渡时间,使悬停效果更平滑
|
||||||
|
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-3px);
|
||||||
|
box-shadow: 0 6px 16px rgba(110, 62, 219, 0.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
// 增加按钮过渡时间
|
||||||
|
transition: all 0.4s cubic-bezier(0.34, 1.56, 0.64, 1);
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #6e3edb;
|
||||||
|
color: #fff;
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.95);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------
|
// -----------------------
|
||||||
|
|
Loading…
Reference in New Issue