Files
yudao-ui-admin-vben/apps/web-antd/src/views/mall/promotion/kefu/index.vue
2025-10-30 15:39:08 +08:00

133 lines
3.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts" setup>
import { defineOptions, onBeforeUnmount, onMounted, ref, watch } from 'vue';
import { Page } from '@vben/common-ui';
import { useAccessStore } from '@vben/stores';
import { useWebSocket } from '@vueuse/core';
import message from 'ant-design-vue';
import { useMallKefuStore } from '#/store/mall/kefu';
import {
KeFuConversationList,
KeFuMessageList,
MemberInfo,
} from './components';
import { WebSocketMessageTypeConstants } from './components/tools/constants';
defineOptions({ name: 'KeFu' });
const accessStore = useAccessStore();
const kefuStore = useMallKefuStore(); // 客服缓存
// ======================= WebSocket start =======================
const url = `${`${import.meta.env.VITE_BASE_URL}/infra/ws`.replace(
'http',
'ws',
)}?token=${accessStore.refreshToken}`; // 使用 refreshToken() :WebSocket 无法方便的刷新访问令牌
const server = ref(url); // WebSocket 服务地址
/** 发起 WebSocket 连接 */
const { data, close, open } = useWebSocket(server.value, {
autoReconnect: true,
heartbeat: true,
});
/** 监听 WebSocket 数据 */
watch(
() => data.value,
(newData) => {
if (!newData) return;
try {
// 1. 收到心跳
if (newData === 'pong') return;
// 2.1 解析 type 消息类型
const jsonMessage = JSON.parse(newData);
const type = jsonMessage.type;
if (!type) {
message.error(`未知的消息类型:${newData}`);
return;
}
// 2.2 消息类型KEFU_MESSAGE_TYPE
if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_TYPE) {
const message = JSON.parse(jsonMessage.content);
// 刷新会话列表
kefuStore.updateConversation(message.conversationId);
// 刷新消息列表
keFuChatBoxRef.value?.refreshMessageList(message);
return;
}
// 2.3 消息类型KEFU_MESSAGE_ADMIN_READ
if (type === WebSocketMessageTypeConstants.KEFU_MESSAGE_ADMIN_READ) {
// 更新会话已读
const message = JSON.parse(jsonMessage.content);
kefuStore.updateConversationStatus(message.conversationId);
}
} catch (error) {
console.error(error);
}
},
{
immediate: false, // 不立即执行
},
);
// ======================= WebSocket end =======================
/** 加载指定会话的消息列表 */
const keFuChatBoxRef = ref<InstanceType<typeof KeFuMessageList>>();
const memberInfoRef = ref<InstanceType<typeof MemberInfo>>();
const handleChange = (conversation: KeFuConversationRespVO) => {
keFuChatBoxRef.value?.getNewMessageList(conversation);
memberInfoRef.value?.initHistory(conversation);
};
const keFuConversationRef = ref<InstanceType<typeof KeFuConversationList>>();
/** 初始化 */
onMounted(() => {
/** 加载会话列表 */
kefuStore.setConversationList().then(() => {
keFuConversationRef.value?.calculationLastMessageTime();
});
// 打开 websocket 连接
open();
});
/** 销毁 */
onBeforeUnmount(() => {
// 关闭 websocket 连接
close();
});
</script>
<template>
<Page>
<a-layout-content class="kefu-layout hrow">
<!-- 会话列表 -->
<KeFuConversationList ref="keFuConversationRef" @change="handleChange" />
<!-- 会话详情选中会话的消息列表 -->
<KeFuMessageList ref="keFuChatBoxRef" />
<!-- 会员信息选中会话的会员信息 -->
<MemberInfo ref="memberInfoRef" />
</a-layout-content>
</Page>
</template>
<style lang="scss">
.kefu-layout {
position: absolute;
top: 0;
left: 0;
flex: 1;
width: 100%;
height: 100%;
}
.hrow {
display: flex;
}
</style>