KeFuConversationBox.vue 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. <template>
  2. <div class="kefu">
  3. <div
  4. v-for="(item, index) in conversationList"
  5. :key="item.id"
  6. :class="{ active: index === activeConversationIndex }"
  7. class="kefu-conversation flex justify-between items-center"
  8. @click="openRightMessage(item, index)"
  9. >
  10. <div class="flex justify-center items-center w-100%">
  11. <el-avatar :src="item.userAvatar" alt="avatar" />
  12. <div class="ml-10px w-100%">
  13. <div class="flex justify-between items-center w-100%">
  14. <span>{{ item.userNickname }}</span>
  15. <span class="color-[#989EA6]">
  16. {{ formatDate(item.lastMessageTime) }}
  17. </span>
  18. </div>
  19. <!-- 文本消息 -->
  20. <template v-if="KeFuMessageContentTypeEnum.TEXT === item.lastMessageContentType">
  21. <div
  22. v-dompurify-html="replaceEmoji(item.lastMessageContent)"
  23. class="last-message flex items-center color-[#989EA6]"
  24. ></div>
  25. </template>
  26. <!-- 图片消息 -->
  27. <template v-if="KeFuMessageContentTypeEnum.IMAGE === item.lastMessageContentType">
  28. <div class="last-message flex items-center color-[#989EA6]">【图片消息】</div>
  29. </template>
  30. </div>
  31. </div>
  32. </div>
  33. </div>
  34. </template>
  35. <script lang="ts" setup>
  36. import { KeFuConversationApi, KeFuConversationRespVO } from '@/api/mall/promotion/kefu/conversation'
  37. import { useEmoji } from './tools/emoji'
  38. import { formatDate, getNowDateTime } from '@/utils/formatTime'
  39. import { KeFuMessageContentTypeEnum } from './tools/constants'
  40. defineOptions({ name: 'KeFuConversationBox' })
  41. const { replaceEmoji } = useEmoji()
  42. const activeConversationIndex = ref(-1) // 选中的会话
  43. const conversationList = ref<KeFuConversationRespVO[]>([]) // 会话列表
  44. const getConversationList = async () => {
  45. conversationList.value = await KeFuConversationApi.getConversationList()
  46. // 测试数据
  47. for (let i = 0; i < 5; i++) {
  48. conversationList.value.push({
  49. id: 1,
  50. userId: 283,
  51. userAvatar:
  52. 'https://thirdwx.qlogo.cn/mmopen/vi_32/Q0j4TwGTfTKMezSxtOImrC9lbhwHiazYwck3xwrEcO7VJfG6WQo260whaeVNoByE5RreiaGsGfOMlIiaDhSaA991w/132',
  53. userNickname: '辉辉鸭' + i,
  54. lastMessageTime: getNowDateTime(),
  55. lastMessageContent:
  56. '[爱心][爱心]你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇你好哇',
  57. lastMessageContentType: 1,
  58. adminPinned: false,
  59. userDeleted: false,
  60. adminDeleted: false,
  61. adminUnreadMessageCount: 19
  62. })
  63. }
  64. }
  65. defineExpose({ getConversationList })
  66. const emits = defineEmits<{
  67. (e: 'change', v: KeFuConversationRespVO): void
  68. }>()
  69. // 打开右侧消息
  70. const openRightMessage = (item: KeFuConversationRespVO, index: number) => {
  71. activeConversationIndex.value = index
  72. emits('change', item)
  73. }
  74. const poller = ref<any>(null) // TODO puhui999: 轮训定时器,暂时模拟 websocket
  75. onMounted(() => {
  76. // TODO puhui999: 轮训相关,功能完善后移除
  77. if (!poller.value) {
  78. poller.value = setInterval(() => {
  79. getConversationList()
  80. }, 1000)
  81. }
  82. })
  83. // TODO puhui999: 轮训相关,功能完善后移除
  84. onBeforeUnmount(() => {
  85. if (!poller.value) {
  86. return
  87. }
  88. clearInterval(poller.value)
  89. })
  90. </script>
  91. <style lang="scss" scoped>
  92. .kefu {
  93. &-conversation {
  94. height: 60px;
  95. padding: 10px;
  96. background-color: #fff;
  97. transition: border-left 0.05s ease-in-out; /* 设置过渡效果 */
  98. .last-message {
  99. width: 200px;
  100. overflow: hidden; // 隐藏超出的文本
  101. white-space: nowrap; // 禁止换行
  102. text-overflow: ellipsis; // 添加省略号
  103. }
  104. }
  105. .active {
  106. border-left: 5px #3271ff solid;
  107. background-color: #eff0f1;
  108. }
  109. }
  110. </style>