diff --git a/apps/web-antd/src/views/mall/promotion/kefu/modules/conversation-list.vue b/apps/web-antd/src/views/mall/promotion/kefu/modules/conversation-list.vue
index c176567d6..7110f53dd 100644
--- a/apps/web-antd/src/views/mall/promotion/kefu/modules/conversation-list.vue
+++ b/apps/web-antd/src/views/mall/promotion/kefu/modules/conversation-list.vue
@@ -210,7 +210,7 @@ onBeforeUnmount(() => {
-
+
diff --git a/apps/web-antd/src/views/mall/promotion/kefu/modules/message-list.vue b/apps/web-antd/src/views/mall/promotion/kefu/modules/message-list.vue
index 063479db4..fc1ed8bcc 100644
--- a/apps/web-antd/src/views/mall/promotion/kefu/modules/message-list.vue
+++ b/apps/web-antd/src/views/mall/promotion/kefu/modules/message-list.vue
@@ -419,7 +419,7 @@ function showTime(item: MallKefuMessageApi.Message, index: number) {
-
+
diff --git a/apps/web-ele/package.json b/apps/web-ele/package.json
index aa96e8564..4448978a8 100644
--- a/apps/web-ele/package.json
+++ b/apps/web-ele/package.json
@@ -43,6 +43,7 @@
"@vben/styles": "workspace:*",
"@vben/types": "workspace:*",
"@vben/utils": "workspace:*",
+ "@vueuse/components": "catalog:",
"@vueuse/core": "catalog:",
"@vueuse/integrations": "catalog:",
"cropperjs": "catalog:",
diff --git a/apps/web-ele/src/store/mall/kefu.ts b/apps/web-ele/src/store/mall/kefu.ts
new file mode 100644
index 000000000..85f4023e7
--- /dev/null
+++ b/apps/web-ele/src/store/mall/kefu.ts
@@ -0,0 +1,102 @@
+import type { MallKefuConversationApi } from '#/api/mall/promotion/kefu/conversation';
+import type { MallKefuMessageApi } from '#/api/mall/promotion/kefu/message';
+
+import { isEmpty } from '@vben/utils';
+
+import { acceptHMRUpdate, defineStore } from 'pinia';
+
+import * as KeFuConversationApi from '#/api/mall/promotion/kefu/conversation';
+
+interface MallKefuInfoVO {
+ conversationList: MallKefuConversationApi.Conversation[]; // 会话列表
+ conversationMessageList: Map; // 会话消息
+}
+
+export const useMallKefuStore = defineStore('mall-kefu', {
+ state: (): MallKefuInfoVO => ({
+ conversationList: [],
+ conversationMessageList: new Map(), // key 会话,value 会话消息列表
+ }),
+ getters: {
+ getConversationList(): MallKefuConversationApi.Conversation[] {
+ return this.conversationList;
+ },
+ getConversationMessageList(): (
+ conversationId: number,
+ ) => MallKefuMessageApi.Message[] | undefined {
+ return (conversationId: number) =>
+ this.conversationMessageList.get(conversationId);
+ },
+ },
+ actions: {
+ // ======================= 会话消息相关 =======================
+ /** 缓存历史消息 */
+ saveMessageList(
+ conversationId: number,
+ messageList: MallKefuMessageApi.Message[],
+ ) {
+ this.conversationMessageList.set(conversationId, messageList);
+ },
+
+ // ======================= 会话相关 =======================
+ /** 加载会话缓存列表 */
+ async setConversationList() {
+ // TODO @jave:idea linter 告警,修复下;
+ // TODO @jave:不使用 KeFuConversationApi.,直接用 getConversationList
+ this.conversationList = await KeFuConversationApi.getConversationList();
+ this.conversationSort();
+ },
+ /** 更新会话缓存已读 */
+ async updateConversationStatus(conversationId: number) {
+ if (isEmpty(this.conversationList)) {
+ return;
+ }
+ const conversation = this.conversationList.find(
+ (item) => item.id === conversationId,
+ );
+ conversation && (conversation.adminUnreadMessageCount = 0);
+ },
+ /** 更新会话缓存 */
+ async updateConversation(conversationId: number) {
+ if (isEmpty(this.conversationList)) {
+ return;
+ }
+
+ const conversation =
+ await KeFuConversationApi.getConversation(conversationId);
+ this.deleteConversation(conversationId);
+ conversation && this.conversationList.push(conversation);
+ this.conversationSort();
+ },
+ /** 删除会话缓存 */
+ deleteConversation(conversationId: number) {
+ const index = this.conversationList.findIndex(
+ (item) => item.id === conversationId,
+ );
+ // 存在则删除
+ if (index !== -1) {
+ this.conversationList.splice(index, 1);
+ }
+ },
+ conversationSort() {
+ // 按置顶属性和最后消息时间排序
+ this.conversationList.sort((a, b) => {
+ // 按照置顶排序,置顶的会在前面
+ if (a.adminPinned !== b.adminPinned) {
+ return a.adminPinned ? -1 : 1;
+ }
+ // 按照最后消息时间排序,最近的会在前面
+ return (
+ (b.lastMessageTime as unknown as number) -
+ (a.lastMessageTime as unknown as number)
+ );
+ });
+ },
+ },
+});
+
+// 解决热更新问题
+const hot = import.meta.hot;
+if (hot) {
+ hot.accept(acceptHMRUpdate(useMallKefuStore, hot));
+}
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/a.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/a.png
new file mode 100644
index 000000000..32939004d
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/a.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/aini.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/aini.png
new file mode 100644
index 000000000..02cf5c498
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/aini.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/aixin.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/aixin.png
new file mode 100644
index 000000000..25e642234
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/aixin.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/baiyan.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/baiyan.png
new file mode 100644
index 000000000..d16260afd
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/baiyan.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/bizui.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/bizui.png
new file mode 100644
index 000000000..a3b18002e
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/bizui.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/buhaoyisi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/buhaoyisi.png
new file mode 100644
index 000000000..54c4b3f71
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/buhaoyisi.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/bukesiyi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/bukesiyi.png
new file mode 100644
index 000000000..5f272e3e4
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/bukesiyi.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/dajing.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/dajing.png
new file mode 100644
index 000000000..8649727ec
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/dajing.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/danao.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/danao.png
new file mode 100644
index 000000000..aa85a2947
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/danao.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/daxiao.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/daxiao.png
new file mode 100644
index 000000000..26206bc05
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/daxiao.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/dianzan.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/dianzan.png
new file mode 100644
index 000000000..2e7f00eba
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/dianzan.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/emo.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/emo.png
new file mode 100644
index 000000000..9c8455165
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/emo.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/esi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/esi.png
new file mode 100644
index 000000000..84e9726f1
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/esi.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/fadai.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fadai.png
new file mode 100644
index 000000000..0772de262
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fadai.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/fankun.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fankun.png
new file mode 100644
index 000000000..6e18dac3e
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fankun.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/feiwen.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/feiwen.png
new file mode 100644
index 000000000..be9761658
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/feiwen.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/fennu.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fennu.png
new file mode 100644
index 000000000..20c57338c
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/fennu.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganga.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganga.png
new file mode 100644
index 000000000..30ec329d2
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganga.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganmao.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganmao.png
new file mode 100644
index 000000000..35bbb89f3
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ganmao.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/hanyan.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/hanyan.png
new file mode 100644
index 000000000..a0bc838b1
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/hanyan.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/haochi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/haochi.png
new file mode 100644
index 000000000..2e52b6bee
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/haochi.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/hongxin.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/hongxin.png
new file mode 100644
index 000000000..65b5de8f0
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/hongxin.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/huaixiao.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/huaixiao.png
new file mode 100644
index 000000000..bc0e76c4c
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/huaixiao.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingkong.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingkong.png
new file mode 100644
index 000000000..7aa65845f
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingkong.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingshu.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingshu.png
new file mode 100644
index 000000000..0e984d68d
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingshu.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingya.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingya.png
new file mode 100644
index 000000000..9ba6bab32
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/jingya.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/kaixin.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/kaixin.png
new file mode 100644
index 000000000..29c9f5ddb
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/kaixin.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/keai.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/keai.png
new file mode 100644
index 000000000..d3b582c69
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/keai.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/keshui.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/keshui.png
new file mode 100644
index 000000000..cef489ea4
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/keshui.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/kun.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/kun.png
new file mode 100644
index 000000000..1ddc388a6
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/kun.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/lengku.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/lengku.png
new file mode 100644
index 000000000..c5c6feebb
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/lengku.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/liuhan.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liuhan.png
new file mode 100644
index 000000000..e6ddc6f4d
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liuhan.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/liukoushui.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liukoushui.png
new file mode 100644
index 000000000..3e2fba656
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liukoushui.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/liulei.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liulei.png
new file mode 100644
index 000000000..dbf820404
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/liulei.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/mengbi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/mengbi.png
new file mode 100644
index 000000000..a4206eefb
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/mengbi.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/mianwubiaoqing.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/mianwubiaoqing.png
new file mode 100644
index 000000000..6f315b98e
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/mianwubiaoqing.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/nanguo.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/nanguo.png
new file mode 100644
index 000000000..19b9fb94a
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/nanguo.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/outu.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/outu.png
new file mode 100644
index 000000000..2f9a06d63
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/outu.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/picture.svg b/apps/web-ele/src/views/mall/promotion/kefu/asserts/picture.svg
new file mode 100644
index 000000000..8811d4957
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/asserts/picture.svg
@@ -0,0 +1,10 @@
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/shengqi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/shengqi.png
new file mode 100644
index 000000000..7dce41dc9
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/shengqi.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/shuizhuo.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/shuizhuo.png
new file mode 100644
index 000000000..97d0f0a67
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/shuizhuo.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/tianshi.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/tianshi.png
new file mode 100644
index 000000000..eb922dd7a
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/tianshi.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaodiaoya.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaodiaoya.png
new file mode 100644
index 000000000..29fbc0e19
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaodiaoya.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaoku.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaoku.png
new file mode 100644
index 000000000..88a169d4f
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiaoku.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/xinsui.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xinsui.png
new file mode 100644
index 000000000..a0f572a11
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xinsui.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiong.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiong.png
new file mode 100644
index 000000000..43dfd7090
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/xiong.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/yiwen.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/yiwen.png
new file mode 100644
index 000000000..4c0da7095
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/yiwen.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/yun.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/yun.png
new file mode 100644
index 000000000..56e5d0218
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/yun.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/asserts/ziya.png b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ziya.png
new file mode 100644
index 000000000..593ef5e68
Binary files /dev/null and b/apps/web-ele/src/views/mall/promotion/kefu/asserts/ziya.png differ
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/index.vue b/apps/web-ele/src/views/mall/promotion/kefu/index.vue
index bfa96fdff..3bfd58189 100644
--- a/apps/web-ele/src/views/mall/promotion/kefu/index.vue
+++ b/apps/web-ele/src/views/mall/promotion/kefu/index.vue
@@ -1,26 +1,116 @@
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/conversation-list.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/conversation-list.vue
new file mode 100644
index 000000000..3edb0e80c
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/conversation-list.vue
@@ -0,0 +1,247 @@
+
+
+
+
+
+ 会话记录({{ kefuStore.getConversationList.length }})
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ item.userNickname || 'null'
+ }}
+
+ {{ lastMessageTimeMap.get(item.id) ?? '计算中' }}
+
+
+
+
+
+
+
+
+
+
+ -
+
+ 置顶会话
+
+ -
+
+ 取消置顶
+
+ -
+
+ 删除会话
+
+ -
+
+ 取消
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/index.ts b/apps/web-ele/src/views/mall/promotion/kefu/modules/index.ts
new file mode 100644
index 000000000..eb34eb6ff
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/index.ts
@@ -0,0 +1,3 @@
+export { default as ConversationList } from './conversation-list.vue';
+export { default as MemberInfo } from './member/member-info.vue';
+export { default as MessageList } from './message-list.vue';
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/member/member-info.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/member-info.vue
new file mode 100644
index 000000000..61c496c8f
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/member-info.vue
@@ -0,0 +1,232 @@
+
+
+
+
+
+
+
+ 会员信息
+
+
+ 最近浏览
+
+
+ 交易订单
+
+
+
+
+
+
+
+
+ 基本信息
+
+
+
+
+
+ 账户信息
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/member/order-browsing-history.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/order-browsing-history.vue
new file mode 100644
index 000000000..a7a5fa65c
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/order-browsing-history.vue
@@ -0,0 +1,47 @@
+
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/member/product-browsing-history.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/product-browsing-history.vue
new file mode 100644
index 000000000..ef0a31e7c
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/member/product-browsing-history.vue
@@ -0,0 +1,58 @@
+
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/message-list.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/message-list.vue
new file mode 100644
index 000000000..15bf7f3bf
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/message-list.vue
@@ -0,0 +1,424 @@
+
+
+
+
+
+ {{ conversation.userNickname }}
+
+
+
+
+
+
+
+
+
+ {{ formatDate(item.createTime) }}
+
+
+
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 有新消息
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/message/message-item.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/message-item.vue
new file mode 100644
index 000000000..589628a75
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/message-item.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/message/order-item.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/order-item.vue
new file mode 100644
index 000000000..34eadc504
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/order-item.vue
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+ 订单号:
+
+
+
+ {{ formatOrderStatus(getMessageContent) }}
+
+
+
+
+
+
+ 共 {{ getMessageContent?.productCount }} 件商品,总金额:
+
+
+ ¥{{ fenToYuan(getMessageContent?.payPrice) }}
+
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/message/product-item.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/product-item.vue
new file mode 100644
index 000000000..6d48657f9
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/message/product-item.vue
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+
{{ title }}
+
+ 库存: {{ stock || 0 }}
+ 销量: {{ salesCount || 0 }}
+
+
+ ¥{{ fenToYuan(price) }}
+ 详情
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/constants.ts b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/constants.ts
new file mode 100644
index 000000000..266a6cf02
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/constants.ts
@@ -0,0 +1,17 @@
+/** 客服消息类型枚举类 */
+export const KeFuMessageContentTypeEnum = {
+ TEXT: 1, // 文本消息
+ IMAGE: 2, // 图片消息
+ VOICE: 3, // 语音消息
+ VIDEO: 4, // 视频消息
+ SYSTEM: 5, // 系统消息
+ // ========== 商城特殊消息 ==========
+ PRODUCT: 10, // 商品消息
+ ORDER: 11, // 订单消息"
+};
+
+/** Promotion 的 WebSocket 消息类型枚举类 */
+export const WebSocketMessageTypeConstants = {
+ KEFU_MESSAGE_TYPE: 'kefu_message_type', // 客服消息类型
+ KEFU_MESSAGE_ADMIN_READ: 'kefu_message_read_status_change', // 客服消息管理员已读
+};
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji-select-popover.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji-select-popover.vue
new file mode 100644
index 000000000..2d14ec189
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji-select-popover.vue
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji.ts b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji.ts
new file mode 100644
index 000000000..69b948d01
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/emoji.ts
@@ -0,0 +1,126 @@
+import { onMounted, ref } from 'vue';
+
+import { isEmpty } from '@vben/utils';
+
+const emojiList = [
+ { name: '[笑掉牙]', file: 'xiaodiaoya.png' },
+ { name: '[可爱]', file: 'keai.png' },
+ { name: '[冷酷]', file: 'lengku.png' },
+ { name: '[闭嘴]', file: 'bizui.png' },
+ { name: '[生气]', file: 'shengqi.png' },
+ { name: '[惊恐]', file: 'jingkong.png' },
+ { name: '[瞌睡]', file: 'keshui.png' },
+ { name: '[大笑]', file: 'daxiao.png' },
+ { name: '[爱心]', file: 'aixin.png' },
+ { name: '[坏笑]', file: 'huaixiao.png' },
+ { name: '[飞吻]', file: 'feiwen.png' },
+ { name: '[疑问]', file: 'yiwen.png' },
+ { name: '[开心]', file: 'kaixin.png' },
+ { name: '[发呆]', file: 'fadai.png' },
+ { name: '[流泪]', file: 'liulei.png' },
+ { name: '[汗颜]', file: 'hanyan.png' },
+ { name: '[惊悚]', file: 'jingshu.png' },
+ { name: '[困~]', file: 'kun.png' },
+ { name: '[心碎]', file: 'xinsui.png' },
+ { name: '[天使]', file: 'tianshi.png' },
+ { name: '[晕]', file: 'yun.png' },
+ { name: '[啊]', file: 'a.png' },
+ { name: '[愤怒]', file: 'fennu.png' },
+ { name: '[睡着]', file: 'shuizhuo.png' },
+ { name: '[面无表情]', file: 'mianwubiaoqing.png' },
+ { name: '[难过]', file: 'nanguo.png' },
+ { name: '[犯困]', file: 'fankun.png' },
+ { name: '[好吃]', file: 'haochi.png' },
+ { name: '[呕吐]', file: 'outu.png' },
+ { name: '[龇牙]', file: 'ziya.png' },
+ { name: '[懵比]', file: 'mengbi.png' },
+ { name: '[白眼]', file: 'baiyan.png' },
+ { name: '[饿死]', file: 'esi.png' },
+ { name: '[凶]', file: 'xiong.png' },
+ { name: '[感冒]', file: 'ganmao.png' },
+ { name: '[流汗]', file: 'liuhan.png' },
+ { name: '[笑哭]', file: 'xiaoku.png' },
+ { name: '[流口水]', file: 'liukoushui.png' },
+ { name: '[尴尬]', file: 'ganga.png' },
+ { name: '[惊讶]', file: 'jingya.png' },
+ { name: '[大惊]', file: 'dajing.png' },
+ { name: '[不好意思]', file: 'buhaoyisi.png' },
+ { name: '[大闹]', file: 'danao.png' },
+ { name: '[不可思议]', file: 'bukesiyi.png' },
+ { name: '[爱你]', file: 'aini.png' },
+ { name: '[红心]', file: 'hongxin.png' },
+ { name: '[点赞]', file: 'dianzan.png' },
+ { name: '[恶魔]', file: 'emo.png' },
+];
+
+export interface Emoji {
+ name: string;
+ url: string;
+}
+
+export const useEmoji = () => {
+ const emojiPathList = ref([]);
+
+ /** 加载本地图片 */
+ const initStaticEmoji = async () => {
+ const pathList = import.meta.glob('../../asserts/*.{png,jpg,jpeg,svg}');
+ for (const path in pathList) {
+ const imageModule: any = await pathList[path]?.();
+ emojiPathList.value.push({ path, src: imageModule.default });
+ }
+ };
+
+ /** 初始化 */
+ onMounted(async () => {
+ if (isEmpty(emojiPathList.value)) {
+ await initStaticEmoji();
+ }
+ });
+
+ /**
+ * 将文本中的表情替换成图片
+ *
+ * @return 替换后的文本
+ * @param content 消息内容
+ */
+ const replaceEmoji = (content: string) => {
+ let newData = content;
+ if (typeof newData !== 'object') {
+ const reg = /\[(.+?)\]/g; // [] 中括号
+ const zhEmojiName = newData.match(reg);
+ if (zhEmojiName) {
+ zhEmojiName.forEach((item) => {
+ const emojiFile = getEmojiFileByName(item);
+ newData = newData.replace(
+ item,
+ `
`,
+ );
+ });
+ }
+ }
+ return newData;
+ };
+
+ /** 获得所有表情 */
+ function getEmojiList(): Emoji[] {
+ return emojiList.map((item) => ({
+ url: getEmojiFileByName(item.name),
+ name: item.name,
+ })) as Emoji[];
+ }
+
+ function getEmojiFileByName(name: string) {
+ for (const emoji of emojiList) {
+ if (emoji.name === name) {
+ const emojiPath = emojiPathList.value.find(
+ (item: { path: string; src: string }) =>
+ item.path.includes(emoji.file),
+ );
+ return emojiPath ? emojiPath.src : undefined;
+ }
+ }
+ return false;
+ }
+
+ return { replaceEmoji, getEmojiList };
+};
diff --git a/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/picture-select-upload.vue b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/picture-select-upload.vue
new file mode 100644
index 000000000..b991f04a8
--- /dev/null
+++ b/apps/web-ele/src/views/mall/promotion/kefu/modules/tools/picture-select-upload.vue
@@ -0,0 +1,90 @@
+
+
+
+
+
+
+
+
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 654a6e56f..926f993cc 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -175,7 +175,7 @@ catalogs:
specifier: ^2.4.6
version: 2.4.6
'@vueuse/components':
- specifier: ^13.4.0
+ specifier: 13.9.0
version: 13.9.0
'@vueuse/core':
specifier: ^13.4.0
@@ -672,10 +672,10 @@ importers:
version: link:scripts/vsh
'@vitejs/plugin-vue':
specifier: 'catalog:'
- version: 6.0.1(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))
+ version: 6.0.1(vite@7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))
'@vitejs/plugin-vue-jsx':
specifier: 'catalog:'
- version: 5.1.1(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))
+ version: 5.1.1(vite@7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))
'@vue/test-utils':
specifier: 'catalog:'
version: 2.4.6
@@ -717,10 +717,10 @@ importers:
version: 3.6.1(sass@1.93.2)(typescript@5.9.3)(vue-tsc@2.2.10(typescript@5.9.3))(vue@3.5.22(typescript@5.9.3))
vite:
specifier: 'catalog:'
- version: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
+ version: 7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
vitest:
specifier: 'catalog:'
- version: 3.2.4(@types/node@22.18.12)(happy-dom@17.6.3)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
+ version: 3.2.4(@types/node@22.18.12)(happy-dom@17.6.3)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
vue:
specifier: ^3.5.17
version: 3.5.22(typescript@5.9.3)
@@ -917,6 +917,9 @@ importers:
'@vben/utils':
specifier: workspace:*
version: link:../../packages/utils
+ '@vueuse/components':
+ specifier: 'catalog:'
+ version: 13.9.0(vue@3.5.22(typescript@5.9.3))
'@vueuse/core':
specifier: 'catalog:'
version: 13.9.0(vue@3.5.22(typescript@5.9.3))
@@ -15123,14 +15126,14 @@ snapshots:
dependencies:
vite-plugin-pwa: 1.1.0(vite@5.4.21(@types/node@24.9.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0))(workbox-build@7.3.0)(workbox-window@7.3.0)
- '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))':
+ '@vitejs/plugin-vue-jsx@5.1.1(vite@7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))':
dependencies:
'@babel/core': 7.28.4
'@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4)
'@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4)
'@rolldown/pluginutils': 1.0.0-beta.44
'@vue/babel-plugin-jsx': 1.5.0(@babel/core@7.28.4)
- vite: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
+ vite: 7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
vue: 3.5.22(typescript@5.9.3)
transitivePeerDependencies:
- supports-color
@@ -15152,10 +15155,10 @@ snapshots:
vite: 5.4.21(@types/node@24.9.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)
vue: 3.5.22(typescript@5.9.3)
- '@vitejs/plugin-vue@6.0.1(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))':
+ '@vitejs/plugin-vue@6.0.1(vite@7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))':
dependencies:
'@rolldown/pluginutils': 1.0.0-beta.29
- vite: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
+ vite: 7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
vue: 3.5.22(typescript@5.9.3)
'@vitejs/plugin-vue@6.0.1(vite@7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))(vue@3.5.22(typescript@5.9.3))':
@@ -15172,13 +15175,13 @@ snapshots:
chai: 5.3.3
tinyrainbow: 2.0.0
- '@vitest/mocker@3.2.4(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))':
+ '@vitest/mocker@3.2.4(vite@7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))':
dependencies:
'@vitest/spy': 3.2.4
estree-walker: 3.0.3
magic-string: 0.30.19
optionalDependencies:
- vite: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
+ vite: 7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
'@vitest/pretty-format@3.2.4':
dependencies:
@@ -21604,13 +21607,13 @@ snapshots:
dependencies:
vite: 7.1.11(@types/node@24.9.1)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
- vite-node@3.2.4(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1):
+ vite-node@3.2.4(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1):
dependencies:
cac: 6.7.14
debug: 4.4.3
es-module-lexer: 1.7.0
pathe: 2.0.3
- vite: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
+ vite: 7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
transitivePeerDependencies:
- '@types/node'
- jiti
@@ -21779,7 +21782,7 @@ snapshots:
sass: 1.93.2
terser: 5.44.0
- vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1):
+ vite@7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1):
dependencies:
esbuild: 0.25.3
fdir: 6.5.0(picomatch@4.0.3)
@@ -21790,7 +21793,7 @@ snapshots:
optionalDependencies:
'@types/node': 22.18.12
fsevents: 2.3.3
- jiti: 2.6.1
+ jiti: 1.21.7
less: 4.4.2
sass: 1.93.2
terser: 5.44.0
@@ -21872,11 +21875,11 @@ snapshots:
- typescript
- universal-cookie
- vitest@3.2.4(@types/node@22.18.12)(happy-dom@17.6.3)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1):
+ vitest@3.2.4(@types/node@22.18.12)(happy-dom@17.6.3)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1):
dependencies:
'@types/chai': 5.2.3
'@vitest/expect': 3.2.4
- '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))
+ '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
@@ -21894,8 +21897,8 @@ snapshots:
tinyglobby: 0.2.15
tinypool: 1.1.1
tinyrainbow: 2.0.0
- vite: 7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
- vite-node: 3.2.4(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
+ vite: 7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
+ vite-node: 3.2.4(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1)
why-is-node-running: 2.3.0
optionalDependencies:
'@types/node': 22.18.12
@@ -21918,7 +21921,7 @@ snapshots:
dependencies:
'@types/chai': 5.2.3
'@vitest/expect': 3.2.4
- '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@22.18.12)(jiti@2.6.1)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))
+ '@vitest/mocker': 3.2.4(vite@7.1.11(@types/node@22.18.12)(jiti@1.21.7)(less@4.4.2)(sass@1.93.2)(terser@5.44.0)(yaml@2.8.1))
'@vitest/pretty-format': 3.2.4
'@vitest/runner': 3.2.4
'@vitest/snapshot': 3.2.4
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index 5031b8161..1b3083a49 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -12,6 +12,7 @@ packages:
- scripts/*
- docs
- playground
+
catalog:
'@ast-grep/napi': ^0.37.0
'@changesets/changelog-github': ^0.5.1
@@ -73,7 +74,7 @@ catalog:
'@vue/reactivity': ^3.5.17
'@vue/shared': ^3.5.17
'@vue/test-utils': ^2.4.6
- '@vueuse/components': ^13.4.0
+ '@vueuse/components': 13.9.0
'@vueuse/core': ^13.4.0
'@vueuse/integrations': ^13.4.0
'@vueuse/motion': ^3.0.3