From a1d7e81859f554f3a53680cc35f0f49bf1f77098 Mon Sep 17 00:00:00 2001
From: wwf <1971391498@qq.com>
Date: 星期四, 14 五月 2026 14:37:02 +0800
Subject: [PATCH] 导入项目
---
src/views/mall/promotion/kefu/components/KeFuConversationList.vue | 254 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 254 insertions(+), 0 deletions(-)
diff --git a/src/views/mall/promotion/kefu/components/KeFuConversationList.vue b/src/views/mall/promotion/kefu/components/KeFuConversationList.vue
new file mode 100644
index 0000000..318e27d
--- /dev/null
+++ b/src/views/mall/promotion/kefu/components/KeFuConversationList.vue
@@ -0,0 +1,254 @@
+<template>
+ <el-aside class="kefu pt-5px h-100%" width="260px">
+ <div class="color-[#999] font-bold my-10px">
+ 浼氳瘽璁板綍({{ kefuStore.getConversationList.length }})
+ </div>
+ <div
+ v-for="item in kefuStore.getConversationList"
+ :key="item.id"
+ :class="{ active: item.id === activeConversationId, pinned: item.adminPinned }"
+ class="kefu-conversation px-10px flex items-center"
+ @click="openRightMessage(item)"
+ @contextmenu.prevent="rightClick($event as PointerEvent, item)"
+ >
+ <div class="flex justify-center items-center w-100%">
+ <div class="flex justify-center items-center w-50px h-50px">
+ <!-- 澶村儚 + 鏈 -->
+ <el-badge
+ :hidden="item.adminUnreadMessageCount === 0"
+ :max="99"
+ :value="item.adminUnreadMessageCount"
+ >
+ <el-avatar :src="item.userAvatar" alt="avatar" />
+ </el-badge>
+ </div>
+ <div class="ml-10px w-100%">
+ <div class="flex justify-between items-center w-100%">
+ <span class="username">{{ item.userNickname }}</span>
+ <span class="color-[#999]" style="font-size: 13px">
+ {{ lastMessageTimeMap.get(item.id) ?? '璁$畻涓�' }}
+ </span>
+ </div>
+ <!-- 鏈�鍚庤亰澶╁唴瀹� -->
+ <div
+ v-dompurify-html="
+ getConversationDisplayText(item.lastMessageContentType, item.lastMessageContent)
+ "
+ class="last-message flex items-center color-[#999]"
+ >
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <!-- 鍙抽敭锛岃繘琛屾搷浣滐紙绫讳技寰俊锛� -->
+ <ul v-show="showRightMenu" :style="rightMenuStyle" class="right-menu-ul">
+ <li
+ v-show="!rightClickConversation.adminPinned"
+ class="flex items-center"
+ @click.stop="updateConversationPinned(true)"
+ >
+ <Icon class="mr-5px" icon="ep:top" />
+ 缃《浼氳瘽
+ </li>
+ <li
+ v-show="rightClickConversation.adminPinned"
+ class="flex items-center"
+ @click.stop="updateConversationPinned(false)"
+ >
+ <Icon class="mr-5px" icon="ep:bottom" />
+ 鍙栨秷缃《
+ </li>
+ <li class="flex items-center" @click.stop="deleteConversation">
+ <Icon class="mr-5px" color="red" icon="ep:delete" />
+ 鍒犻櫎浼氳瘽
+ </li>
+ <li class="flex items-center" @click.stop="closeRightMenu">
+ <Icon class="mr-5px" color="red" icon="ep:close" />
+ 鍙栨秷
+ </li>
+ </ul>
+ </el-aside>
+</template>
+
+<script lang="ts" setup>
+import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
+import { useEmoji } from './tools/emoji'
+import { formatPast } from '@/utils/formatTime'
+import { KeFuMessageContentTypeEnum } from './tools/constants'
+import { useAppStore } from '@/store/modules/app'
+import { useMallKefuStore } from '@/store/modules/mall/kefu'
+import { jsonParse } from '@/utils'
+
+defineOptions({ name: 'KeFuConversationList' })
+
+const message = useMessage() // 娑堟伅寮圭獥
+const appStore = useAppStore()
+const kefuStore = useMallKefuStore() // 瀹㈡湇缂撳瓨
+const { replaceEmoji } = useEmoji()
+const activeConversationId = ref(-1) // 閫変腑鐨勪細璇�
+const collapse = computed(() => appStore.getCollapse) // 鎶樺彔鑿滃崟
+
+/** 璁$畻娑堟伅鏈�鍚庡彂閫佹椂闂磋窛绂荤幇鍦ㄨ繃鍘讳簡澶氫箙 */
+const lastMessageTimeMap = ref<Map<number, string>>(new Map<number, string>())
+const calculationLastMessageTime = () => {
+ kefuStore.getConversationList?.forEach((item) => {
+ lastMessageTimeMap.value.set(item.id, formatPast(item.lastMessageTime, 'YYYY-MM-DD'))
+ })
+}
+defineExpose({ calculationLastMessageTime })
+
+/** 鎵撳紑鍙充晶鐨勬秷鎭垪琛� */
+const emits = defineEmits<{
+ (e: 'change', v: KeFuConversationRespVO): void
+}>()
+const openRightMessage = (item: KeFuConversationRespVO) => {
+ // 鍚屼竴涓細璇濆垯涓嶅鐞�
+ if (activeConversationId.value === item.id) {
+ return
+ }
+ activeConversationId.value = item.id
+ emits('change', item)
+}
+
+/** 鑾峰緱娑堟伅绫诲瀷 */
+const getConversationDisplayText = computed(
+ () => (lastMessageContentType: number, lastMessageContent: string) => {
+ switch (lastMessageContentType) {
+ case KeFuMessageContentTypeEnum.SYSTEM:
+ return '[绯荤粺娑堟伅]'
+ case KeFuMessageContentTypeEnum.VIDEO:
+ return '[瑙嗛娑堟伅]'
+ case KeFuMessageContentTypeEnum.IMAGE:
+ return '[鍥剧墖娑堟伅]'
+ case KeFuMessageContentTypeEnum.PRODUCT:
+ return '[鍟嗗搧娑堟伅]'
+ case KeFuMessageContentTypeEnum.ORDER:
+ return '[璁㈠崟娑堟伅]'
+ case KeFuMessageContentTypeEnum.VOICE:
+ return '[璇煶娑堟伅]'
+ case KeFuMessageContentTypeEnum.TEXT:
+ return replaceEmoji(jsonParse(lastMessageContent).text || lastMessageContent)
+ default:
+ return ''
+ }
+ }
+)
+
+//======================= 鍙抽敭鑿滃崟 =======================
+const showRightMenu = ref(false) // 鏄剧ず鍙抽敭鑿滃崟
+const rightMenuStyle = ref<any>({}) // 鍙抽敭鑿滃崟 Style
+const rightClickConversation = ref<KeFuConversationRespVO>({} as KeFuConversationRespVO) // 鍙抽敭閫変腑鐨勪細璇濆璞�
+
+/** 鎵撳紑鍙抽敭鑿滃崟 */
+const rightClick = (mouseEvent: PointerEvent, item: KeFuConversationRespVO) => {
+ rightClickConversation.value = item
+ // 鏄剧ず鍙抽敭鑿滃崟
+ showRightMenu.value = true
+ rightMenuStyle.value = {
+ top: mouseEvent.clientY - 110 + 'px',
+ left: collapse.value ? mouseEvent.clientX - 80 + 'px' : mouseEvent.clientX - 210 + 'px'
+ }
+}
+/** 鍏抽棴鍙抽敭鑿滃崟 */
+const closeRightMenu = () => {
+ showRightMenu.value = false
+}
+
+/** 缃《浼氳瘽 */
+const updateConversationPinned = async (adminPinned: boolean) => {
+ // 1. 浼氳瘽缃《/鍙栨秷缃《
+ await KeFuConversationApi.updateConversationPinned({
+ id: rightClickConversation.value.id,
+ adminPinned
+ })
+ message.notifySuccess(adminPinned ? '缃《鎴愬姛' : '鍙栨秷缃《鎴愬姛')
+ // 2. 鍏抽棴鍙抽敭鑿滃崟锛屾洿鏂颁細璇濆垪琛�
+ closeRightMenu()
+ await kefuStore.updateConversation(rightClickConversation.value.id)
+}
+
+/** 鍒犻櫎浼氳瘽 */
+const deleteConversation = async () => {
+ // 1. 鍒犻櫎浼氳瘽
+ await message.confirm('鎮ㄧ‘瀹氳鍒犻櫎璇ヤ細璇濆悧锛�')
+ await KeFuConversationApi.deleteConversation(rightClickConversation.value.id)
+ // 2. 鍏抽棴鍙抽敭鑿滃崟锛屾洿鏂颁細璇濆垪琛�
+ closeRightMenu()
+ kefuStore.deleteConversation(rightClickConversation.value.id)
+}
+
+/** 鐩戝惉鍙抽敭鑿滃崟鐨勬樉绀虹姸鎬侊紝娣诲姞鐐瑰嚮浜嬩欢鐩戝惉鍣� */
+watch(showRightMenu, (val) => {
+ if (val) {
+ document.body.addEventListener('click', closeRightMenu)
+ } else {
+ document.body.removeEventListener('click', closeRightMenu)
+ }
+})
+
+const timer = ref<any>()
+/** 鍒濆鍖� */
+onMounted(() => {
+ timer.value = setInterval(calculationLastMessageTime, 1000 * 10) // 鍗佺璁$畻涓�娆�
+})
+/** 缁勪欢鍗歌浇鍓� */
+onBeforeUnmount(() => {
+ clearInterval(timer.value)
+})
+</script>
+
+<style lang="scss" scoped>
+.kefu {
+ background-color: var(--app-content-bg-color);
+
+ &-conversation {
+ height: 60px;
+ //background-color: #fff;
+ //transition: border-left 0.05s ease-in-out; /* 璁剧疆杩囨浮鏁堟灉 */
+
+ .username {
+ min-width: 0;
+ max-width: 60%;
+ }
+
+ .last-message {
+ font-size: 13px;
+ }
+
+ .last-message,
+ .username {
+ overflow: hidden;
+ text-overflow: ellipsis;
+ display: -webkit-box;
+ -webkit-box-orient: vertical;
+ -webkit-line-clamp: 1;
+ }
+ }
+
+ .active {
+ background-color: rgba(128, 128, 128, 0.5); // 閫忔槑鑹诧紝鏆楅粦妯″紡涓嬩篃鑳戒綋鐜�
+ }
+
+ .right-menu-ul {
+ position: absolute;
+ background-color: var(--app-content-bg-color);
+ padding: 5px;
+ margin: 0;
+ list-style-type: none; /* 绉婚櫎榛樿鐨勯」鐩鍙� */
+ border-radius: 12px;
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* 闃村奖鏁堟灉 */
+ width: 130px;
+
+ li {
+ padding: 8px 16px;
+ cursor: pointer;
+ border-radius: 12px;
+ transition: background-color 0.3s; /* 骞虫粦杩囨浮 */
+ &:hover {
+ background-color: var(--left-menu-bg-active-color); /* 鎮仠鏃剁殑鑳屾櫙棰滆壊 */
+ }
+ }
+ }
+}
+</style>
--
Gitblit v1.8.0