浏览代码

Merge branch 'feature/im' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/im

YunaiV 1 年之前
父节点
当前提交
82b3276204

+ 27 - 0
src/api/im/conversation/index.ts

@@ -0,0 +1,27 @@
+import request from '@/config/axios'
+
+export interface ImConversationRespVO {
+  id: number // 编号
+  userId: number // 所属用户
+  conversationType: number // 会话类型
+  targetId: number // 聊天对象编号
+  no: string // 会话标志
+  pinned: boolean // 是否置顶
+  lastReadTime: string // 最后已读时间
+  createTime: string // 创建时间
+}
+
+// 获得用户的会话列表
+export const getConversationList = async () => {
+  return await request.get({ url: `/im/conversation/list` })
+}
+
+// 置顶会话
+export const updatePinned = async (data: any) => {
+  return await request.post({ url: `/im/conversation/update-pinned`, data })
+}
+
+// 更新最后已读时间
+export const updateLastReadTime = async (data: any) => {
+  return await request.post({ url: `/im/conversation/update-last-read-time`, data })
+}

+ 42 - 0
src/api/im/message/index.ts

@@ -0,0 +1,42 @@
+import request from '@/config/axios'
+
+export interface ImMessageSendReqVO {
+  clientMessageId: string // 客户端消息编号
+  receiverId: number // 接收人编号
+  conversationType: number // 会话类型
+  contentType: number // 内容类型
+  content: string // 内容
+}
+
+export interface ImMessageSendRespVO {
+  id: number // 编号
+  sendTime: string // 发送时间
+}
+
+export interface ImMessageRespVO {
+  id: number // 编号
+  conversationType: number // 会话类型
+  senderId: number // 发送人编号
+  senderNickname: string // 发送人昵称
+  senderAvatar: string // 发送人头像
+  receiverId: number // 接收人编号
+  contentType: number // 内容类型
+  content: string // 内容
+  sendTime: string // 发送时间
+  sequence: number // 序号
+}
+
+// 发送消息
+export const sendMessage = async (data: ImMessageSendReqVO) => {
+  return await request.post({ url: `/im/message/send`, data })
+}
+
+// 消息列表-拉取大于 sequence 的消息列表
+export const pullMessageList = async (params: { sequence: number; size: number }) => {
+  return await request.get({ url: `/im/message/pull`, params })
+}
+
+// 消息列表-根据接收人和发送时间进行分页查询
+export const getMessageList = async (params: any) => {
+  return await request.get({ url: `/im/message/list`, params })
+}

+ 1 - 1
src/components/Cropper/src/CropperAvatar.vue

@@ -97,7 +97,7 @@ $prefix-cls: #{$namespace}--cropper-avatar;
     opacity: 0;
     transition: opacity 0.4s;
 
-    ::v-deep(svg) {
+    :deep(svg) {
       margin: auto;
     }
   }

+ 1 - 1
src/components/Im/SearchInput/src/SearchInput.vue

@@ -29,7 +29,7 @@ const querySearch = () => {
 </template>
 
 <style scoped lang="scss">
-::v-deep .el-input__wrapper {
+:deep(.el-input__wrapper) {
   box-shadow: none;
 }
 

+ 64 - 27
src/constant/im/messageType.js

@@ -1,43 +1,80 @@
 const SESSION_MESSAGE_TYPE = {
-    img: '[图片]',
-    file: '[文件]',
-    audio: '[语音]',
-    loc: '[位置]'
+  img: '[图片]',
+  file: '[文件]',
+  audio: '[语音]',
+  loc: '[位置]'
 }
 
 const CUSTOM_TYPE = {
-    userCard: '个人名片'
+  userCard: '个人名片'
 }
+// const ALL_MESSAGE_TYPE = {
+//   TEXT: 'txt',
+//   IMAGE: 'img',
+//   AUDIO: 'audio',
+//   LOCAL: 'loc',
+//   VIDEO: 'video',
+//   FILE: 'file',
+//   CUSTOM: 'custom',
+//   CMD: 'cmd',
+//   INFORM: 'inform' //这个类型不在环信消息类型内,属于自己定义的一种系统通知类的消息。
+// }
 const ALL_MESSAGE_TYPE = {
-    TEXT: 'txt',
-    IMAGE: 'img',
-    AUDIO: 'audio',
-    LOCAL: 'loc',
-    VIDEO: 'video',
-    FILE: 'file',
-    CUSTOM: 'custom',
-    CMD: 'cmd',
-    INFORM: 'inform' //这个类型不在环信消息类型内,属于自己定义的一种系统通知类的消息。
+  TEXT: 101,
+  IMAGE: 102,
+  AUDIO: 103,
+  VIDEO: 104,
+  FILE: 105,
+  AT_TEXT: 106,
+  MERGE: 107,
+  CARD: 108,
+  LOCATION: 109,
+  CUSTOM: 110,
+  REVOKE_RECEIPT: 111,
+  C2C_RECEIPT: 112,
+  TYPING: 113,
+  QUOTE: 114,
+  FACE: 115,
+  ADVANCED_REVOKE: 118,
+  FRIEND_ADDED: 1201,
+  OA_NOTIFICATION: 1400,
+  GROUP_CREATED: 1501,
+  GROUP_INFO_CHANGED: 1502,
+  MEMBER_QUIT: 1504,
+  GROUP_OWNER_CHANGED: 1507,
+  MEMBER_KICKED: 1508,
+  MEMBER_INVITED: 1509,
+  MEMBER_ENTER: 1510,
+  GROUP_DISMISSED: 1511,
+  GROUP_MEMBER_MUTED: 1512,
+  GROUP_MEMBER_CANCEL_MUTED: 1513,
+  GROUP_MUTED: 1514,
+  GROUP_CANCEL_MUTED: 1515,
+  GROUP_ANNOUNCEMENT_UPDATED: 1519,
+  GROUP_NAME_UPDATED: 1520,
+  BURN_CHANGE: 1701,
+  REVOKE: 2101
 }
 const CHAT_TYPE = {
-    SINGLE: 'singleChat',
-    GROUP: 'groupChat'
+  SINGLE: 1,
+  GROUP: 3,
+  NOTIFICATION: 4
 }
 
 const MENTION_ALL = {
-    TEXT: '所有人',
-    VALUE: 'ALL'
+  TEXT: '所有人',
+  VALUE: 'ALL'
 }
 const CHANGE_MESSAGE_BODAY_TYPE = {
-    RECALL: 0,
-    DELETE: 1,
-    MODIFY: 2
+  RECALL: 0,
+  DELETE: 1,
+  MODIFY: 2
 }
 export default {
-    SESSION_MESSAGE_TYPE,
-    CUSTOM_TYPE,
-    ALL_MESSAGE_TYPE,
-    CHAT_TYPE,
-    MENTION_ALL,
-    CHANGE_MESSAGE_BODAY_TYPE
+  SESSION_MESSAGE_TYPE,
+  CUSTOM_TYPE,
+  ALL_MESSAGE_TYPE,
+  CHAT_TYPE,
+  MENTION_ALL,
+  CHANGE_MESSAGE_BODAY_TYPE
 }

+ 13 - 13
src/layout/components/ImChat/src/ImChat.vue

@@ -1,28 +1,28 @@
 <script setup lang="ts">
 import { Dialog } from '@/components/Dialog'
-import { ref } from 'vue'
-import IM from '@/views/im/index.vue'
-import { useRouter } from 'vue-router' // 导入 useRouter 方法
+import { shallowRef, defineAsyncComponent, DefineComponent } from 'vue'
 
-defineOptions({ name: 'ImChat' })
+// 异步加载可能的对话框内容组件
+const IMComponent = defineAsyncComponent(() => import('@/views/im/index.vue'))
 
-const dialogVisible = ref(false)
-const router = useRouter() // 创建 router 实例
+const dialogVisible = shallowRef(false)
+const currentComponent = shallowRef<DefineComponent | null>(null)
 
-// 添加点击事件处理函数
-function handleClick() {
-  dialogVisible.value = !dialogVisible.value
-  router.push('/im/conversation') // 设置路由为 /im/conversation
+// 添加点击事件处理函数,显示对话框并加载 IM 组件
+function openDialog() {
+  dialogVisible.value = true
+  currentComponent.value = IMComponent // 加载 IM 组件
 }
 </script>
 
 <template>
-  <div class="custom-hover" v-bind="$attrs">
+  <div class="custom-hover" v-bind="$attrs" @click="openDialog">
     <ElBadge>
-      <Icon :size="18" class="cursor-pointer" icon="ep:chat-round" @click="handleClick" />
+      <Icon :size="18" class="cursor-pointer" icon="ep:chat-round" />
     </ElBadge>
   </div>
   <Dialog v-model="dialogVisible" width="90%" top="10vh">
-    <IM />
+    <component :is="currentComponent" />
+    <!-- 使用动态组件 -->
   </Dialog>
 </template>

+ 0 - 12
src/router/modules/remaining.ts

@@ -591,18 +591,6 @@ const remainingRouter: AppRouteRecordRaw[] = [
         },
         component: () => import('@/views/im/Conversation/index.vue'),
         children: [
-          {
-            // 会话详情
-            path: 'informDetails',
-            name: 'InformDetails',
-            meta: {
-              title: '通知详情',
-              noCache: true,
-              hidden: true,
-              noTagsView: true
-            },
-            component: () => import('@/views/im/InformDetails/index.vue')
-          },
           {
             //聊天对话框
             path: 'message',

+ 6 - 0
src/utils/fileSizeFormat.ts

@@ -0,0 +1,6 @@
+// fileSizeFormat.ts
+export default function fileSizeFormat(value: number): string {
+  const s = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB']
+  const e = Math.floor(Math.log(value) / Math.log(1024))
+  return (value / Math.pow(1024, Math.floor(e))).toFixed(2) + ' ' + s[e]
+}

+ 20 - 0
src/utils/paseLink.ts

@@ -0,0 +1,20 @@
+interface ParsedLinkResult {
+  isLink: boolean
+  msg: string
+}
+
+const paseLink = (msg: string): ParsedLinkResult => {
+  let isLink = false
+  const reg =
+    /(https?\:\/\/|www\.)([a-zA-Z0-9-]+(\.[a-zA-Z0-9]+)+)(\:[0-9]{2,4})?\/?((\.[:_0-9a-zA-Z-]+)|[:_0-9a-zA-Z-]*\/?)*\??[:_#@*&%0-9a-zA-Z-/=]*/gm
+
+  msg = msg.replace(reg, function (v: string): string {
+    const prefix = /^https?/gm.test(v)
+    isLink = prefix
+    return "<a href='" + (prefix ? v : '//' + v) + "' target='_blank'>" + v + '</a>'
+  })
+
+  return { isLink, msg }
+}
+
+export default paseLink

+ 16 - 50
src/views/im/Conversation/components/ConversationList.vue

@@ -1,26 +1,11 @@
 <script setup lang="ts">
 import { formatDate } from '@/utils/formatTime'
 import { reactive } from 'vue'
-/* 头像相关 */
-import informIcon from '@/assets/imgs/im/avatar/inform.png'
+import * as ConversationApi from '@/api/im/conversation'
 /* route */
 const route = useRoute()
 /* router */
 const router = useRouter()
-//取系统通知数据
-const informDetail = computed(() => {
-  const informDetailArr = reactive([
-    {
-      from: '系统通知',
-      desc: '您有一条新的通知',
-      time: new Date(),
-      untreated: 1
-    }
-  ])
-  const lastInformDeatail = informDetailArr[0] || {}
-  const untreated = 1
-  return { untreated, lastInformDeatail }
-})
 
 //取好友列表(主要使用好友下的用户属性相关)
 const friendList = reactive({
@@ -40,10 +25,22 @@ const conversationList = reactive([
       msg: 'hello word!'
     },
     latestSendTime: new Date(),
-    unreadMessageNum: 5,
-    isMention: false
+    unreadMessageNum: 5
   }
 ])
+// let conversationList = reactive({})
+// // 获得用户的会话列表
+// const getConversationList = async () => {
+//   const res = await ConversationApi.getConversationList()
+//   if (res.code === 200) {
+//     console.log('会话列表', res.data)
+//     conversationList = res.data
+//   }
+// }
+// // 初始化
+// onMounted(() => {
+//   getConversationList()
+// })
 
 //处理会话name
 const handleConversationName = computed(() => {
@@ -53,7 +50,7 @@ const handleConversationName = computed(() => {
 const handleLastMsgNickName = computed(() => {
   return ''
 })
-const emit = defineEmits(['toInformDetails', 'toChatMessage'])
+const emit = defineEmits(['toChatMessage'])
 //普通会话
 const checkedConverItemIndex = ref(null)
 const toChatMessage = (item, itemKey, index) => {
@@ -69,36 +66,6 @@ const deleteConversation = (itemKey) => {
 </script>
 <template>
   <el-scrollbar class="session_list" style="overflow: auto" tag="ul">
-    <!-- 系统通知会话 -->
-    <li
-      v-if="JSON.stringify(informDetail.lastInformDeatail) !== '{}' && informDetail.untreated >= 1"
-      class="session_list_item"
-      @click="$emit('toInformDetails')"
-    >
-      <div class="item_body item_left">
-        <!-- 通知头像 -->
-        <div class="session_other_avatar">
-          <el-avatar :size="34" :src="informIcon" />
-        </div>
-      </div>
-      <div class="item_body item_main">
-        <div class="name">系统通知</div>
-        <div class="last_msg_body">
-          {{ informDetail.lastInformDeatail.from }}:{{ informDetail.lastInformDeatail.desc }}
-        </div>
-      </div>
-      <div class="item_body item_right">
-        <span class="time">{{
-          formatDate(informDetail.lastInformDeatail.time, 'MM/DD/HH:mm')
-        }}</span>
-        <span class="unReadNum_box" v-if="informDetail.untreated >= 1">
-          <sup
-            class="unReadNum_count"
-            v-text="informDetail.untreated >= 99 ? '99+' : informDetail.untreated"
-          ></sup>
-        </span>
-      </div>
-    </li>
     <!-- 普通会话 -->
     <template v-if="Object.keys(conversationList).length > 0">
       <li
@@ -133,7 +100,6 @@ const deleteConversation = (itemKey) => {
               <div class="item_body item_main">
                 <div class="name"> 好友 </div>
                 <div class="last_msg_body">
-                  <span class="last_msg_body_mention" v-if="item.isMention">[有人@我]</span>
                   <span v-show="item.conversationType === 2">好友</span>
                   {{ item.latestMessage.msg }}
                 </div>

+ 10 - 20
src/views/im/Conversation/index.vue

@@ -1,40 +1,30 @@
 <script setup lang="ts">
-/* 搜索框组件 */
+import { defineAsyncComponent, shallowRef } from 'vue'
 import { SearchInput } from '@/components/Im/SearchInput'
-/* 欢迎页 */
-import { Welcome } from '@/components/Im/Welcome'
 import ConversationList from '../Conversation/components/ConversationList.vue'
-import router from '@/router'
+import { Welcome } from '@/components/Im/Welcome'
 
-//路由跳转-系统通知
-const toInformDetails = () => {
-  router.push('/im/conversation/informDetails')
-}
+const MessageComponent = defineAsyncComponent(() => import('@/views/im/Message/index.vue'))
 
-//路由跳转-对应好友会话
-const toChatMessage = (id, chatType) => {
+const currentComponent = shallowRef(Welcome) // 默认加载欢迎页组件
+
+const toChatMessage = (id) => {
   console.log('>>>>>>>id', id)
-  router.push({
-    path: '/im/conversation/message',
-    query: {
-      id,
-      chatType
-    }
-  })
+  currentComponent.value = MessageComponent // 加载消息组件
 }
 </script>
+
 <template>
   <el-container style="height: 100%">
     <el-aside class="chat_conversation_box">
       <!-- 搜索组件 -->
       <SearchInput :searchType="'conversation'" />
       <div class="chat_conversation_list">
-        <ConversationList @to-inform-details="toInformDetails" @to-chat-message="toChatMessage" />
+        <ConversationList @to-chat-message="toChatMessage" />
       </div>
     </el-aside>
     <el-main class="chat_conversation_main_box">
-      <router-view />
-      <Welcome />
+      <component :is="currentComponent" />
     </el-main>
   </el-container>
 </template>

+ 0 - 7
src/views/im/InformDetails/index.vue

@@ -1,7 +0,0 @@
-<script setup lang="ts"></script>
-
-<template>
-  <h2>系统通知</h2>
-</template>
-
-<style scoped lang="scss"></style>

+ 1 - 1
src/views/im/Message/components/inputBox/index.scss

@@ -55,7 +55,7 @@
 }
 
 /* loading svg大小调整 */
-::v-deep .circular {
+:deep(.circular) {
     margin-top: 8px;
     width: 25px;
     height: 25px;

+ 11 - 5
src/views/im/Message/components/inputBox/index.vue

@@ -7,6 +7,8 @@ import { onClickOutside } from '@vueuse/core'
 /* 组件 */
 import PreviewSendImg from '../suit/previewSendImg.vue'
 import VueAt from 'vue-at/dist/vue-at-textarea' // for textarea
+import * as MessageApi from '@/api/im/message'
+import { generateUUID } from '@/utils'
 const props = defineProps({
   nowPickInfo: {
     type: Object,
@@ -100,17 +102,21 @@ const sendTextMessage = _.debounce(async () => {
       em_at_list: isAtAll.value ? MENTION_ALL.VALUE : _.map(atMembers.value, 'value')
     }
   }
+  const imMessageSendReqVO = {
+    clientMessageId: generateUUID(),
+    receiverId: nowPickInfo.value.id,
+    conversationType: nowPickInfo.value.chatType,
+    contentType: ALL_MESSAGE_TYPE.TEXT,
+    content: textContent.value
+  }
   //关闭引用框
   if (messageQuoteRef.value?.isShowQuoteMsgBox) {
   }
   textContent.value = ''
   messageQuoteRef.value?.clearQuoteContent()
   try {
-    console.log('msgOptions', msgOptions)
-    // await store.dispatch('sendShowTypeMessage', {
-    //   msgType: ALL_MESSAGE_TYPE.TEXT,
-    //   msgOptions
-    // })
+    console.log('imMessageSendReqVO', imMessageSendReqVO)
+    await MessageApi.sendMessage(imMessageSendReqVO)
   } catch (error) {
     //handleSDKErrorNotifi(error.type, error.message)
     console.log('>>>>>>>发送失败+++++++', error)

+ 33 - 112
src/views/im/Message/components/messageList/index.vue

@@ -2,60 +2,37 @@
 import { formatDate } from '@/utils/formatTime'
 /* 默认头像 */
 import defaultAvatar from '@/assets/imgs/avatar.gif'
-/* emits */
-const emit = defineEmits(['scroll-message-list', 're-edit-message', 'message-quote'])
-const messageData = ref([
-  {
-    id: 1,
-    type: 'text',
-    isRecall: false,
-    time: '2024-04-01 12:00:00',
-    from: '1',
-    msg: 'Hello, world!',
-    modifiedInfo: {
-      operationCount: 1
-    }
-  },
-  {
-    id: 2,
-    type: 'text',
-    isRecall: false,
-    time: '2024-04-01 12:00:01',
-    from: '2',
-    msg: 'Hi, there!',
-    modifiedInfo: {
-      operationCount: 0
-    }
+import { useUserStore } from '@/store/modules/user'
+import avatarImg from '@/assets/imgs/avatar.gif'
+import paseLink from '@/utils/paseLink.ts'
+import fileSizeFormat from '@/utils/fileSizeFormat'
+import { messageType } from '@/constant/im'
+const { ALL_MESSAGE_TYPE, CUSTOM_TYPE } = messageType
+// 当前用户信息
+const userStore = useUserStore()
+const avatar = computed(() => userStore.user.avatar ?? avatarImg)
+/* props */
+const props = defineProps({
+  messageData: {
+    type: [Array, Object],
+    default: () => []
   },
-  {
-    id: 3,
-    type: 'text',
-    isRecall: true,
-    time: '2024-04-01 12:00:02',
-    from: '1',
-    msg: 'Hello, world!',
-    modifiedInfo: {
-      operationCount: 0
-    }
+  nowPickInfo: {
+    type: Object,
+    default: () => ({}),
+    required: true
   }
-])
-const ALL_MESSAGE_TYPE = {
-  TEXT: 'txt',
-  IMAGE: 'img',
-  AUDIO: 'audio',
-  LOCAL: 'loc',
-  VIDEO: 'video',
-  FILE: 'file',
-  CUSTOM: 'custom',
-  CMD: 'cmd',
-  INFORM: 'inform' //这个类型不在环信消息类型内,属于自己定义的一种系统通知类的消息。
-}
+})
+const { nowPickInfo } = toRefs(props)
+const { messageData } = toRefs(props)
 /* 处理时间显示间隔 */
 const handleMsgTimeShow = (time, index) => {
+  console.log('>>>>>时间显示', time, index)
   const msgList = Array.from(messageData.value)
   if (index !== 0) {
     const lastTime = msgList[index - 1].time
-    return time - lastTime > 50000 ? formatDate(time, 'MM/DD/HH:mm') : false
+    console.log('>>>>>时间间隔', time - lastTime, time, lastTime)
+    return time - lastTime > 50000 ? formatDate(time, 'MM/DD/HH:mm') : ''
   } else {
     return formatDate(time, 'MM/DD/HH:mm')
   }
@@ -64,9 +41,15 @@ const handleMsgTimeShow = (time, index) => {
 const isMyself = (msgBody) => {
   return msgBody.from === '1'
 }
+/* 文本中是否包含link */
+const isLink = computed(() => {
+  return (msg) => {
+    return paseLink(msg).isLink
+  }
+})
 /* 获取自己的用户信息 */
 const loginUserInfo = {
-  avatarurl: 'https://avatars.githubusercontent.com/u/1?v=4'
+  avatarurl: avatar.value
 }
 /* 获取他人的用户信息 */
 const otherUserInfo = (from) => {
@@ -108,10 +91,6 @@ const startplayAudio = (msgBody) => {
     audioPlayStatus.playMsgId = ''
   })
 }
-//父组件重新编辑方法
-const reEdit = (msg) => emit('reEditMessage', msg)
-//调用父组件引用消息
-const onMsgQuote = (msg) => emit('messageQuote', msg)
 </script>
 <template>
   <div>
@@ -123,14 +102,14 @@ const onMsgQuote = (msg) => emit('messageQuote', msg)
     >
       <!-- 普通消息气泡 -->
       <div
-        v-if="!msgBody.isRecall && msgBody.type !== ALL_MESSAGE_TYPE.INFORM"
+        v-if="!msgBody.isRecall && msgBody.type !== ALL_MESSAGE_TYPE.OA_NOTIFICATION"
         class="message_box_item"
         :style="{
           flexDirection: isMyself(msgBody) ? 'row-reverse' : 'row'
         }"
       >
         <div class="message_item_time">
-          {{ handleMsgTimeShow(msgBody.time, index) || '' }}
+          {{ handleMsgTimeShow(msgBody.time, index) }}
         </div>
         <el-avatar
           class="message_item_avator"
@@ -161,12 +140,6 @@ const onMsgQuote = (msg) => emit('messageQuote', msg)
             >
               <template v-if="!isLink(msgBody.msg)">
                 {{ msgBody.msg }}
-                <!-- 已编辑 -->
-                <sup
-                  style="font-size: 7px; color: #707784"
-                  v-show="msgBody?.modifiedInfo?.operationCount"
-                  >(已编辑)</sup
-                >
               </template>
               <template v-else> <span v-html="paseLink(msgBody.msg).msg"> </span></template>
             </p>
@@ -244,62 +217,10 @@ const onMsgQuote = (msg) => emit('messageQuote', msg)
                 </div>
               </template>
             </div>
-            <!-- 右键点击弹起更多功能栏 -->
-            <template #dropdown>
-              <el-dropdown-menu>
-                <el-dropdown-item
-                  v-if="msgBody.type === ALL_MESSAGE_TYPE.TEXT && isSupported"
-                  @click="copyTextMessages(msgBody.msg)"
-                >
-                  复制
-                </el-dropdown-item>
-                <el-dropdown-item v-if="isMyself(msgBody)" @click="recallMessage(msgBody)">
-                  撤回
-                </el-dropdown-item>
-                <el-dropdown-item
-                  v-if="msgBody.type === ALL_MESSAGE_TYPE.TEXT && isMyself(msgBody)"
-                  @click="showModifyMsgModal(msgBody)"
-                >
-                  编辑
-                </el-dropdown-item>
-                <el-dropdown-item @click="onMsgQuote(msgBody)"> 引用 </el-dropdown-item>
-                <el-dropdown-item @click="deleteMessage(msgBody)"> 删除 </el-dropdown-item>
-                <el-dropdown-item v-if="!isMyself(msgBody)" @click="informOnMessage(msgBody)">
-                  举报
-                </el-dropdown-item>
-              </el-dropdown-menu>
-            </template>
           </el-dropdown>
-          <!-- 引用消息展示框 -->
-          <div
-            class="message_quote_box"
-            v-if="msgBody?.ext?.msgQuote"
-            @click="clickQuoteMessage(msgBody.ext.msgQuote)"
-          >
-            <p>
-              {{ msgBody?.ext?.msgQuote?.msgSender }}:{{ msgBody?.ext?.msgQuote?.msgPreview }}
-            </p>
-          </div>
         </div>
       </div>
-      <!-- 撤回消息通知通知 -->
-      <div v-if="msgBody.isRecall" class="recall_style">
-        {{ isMyself(msgBody) ? '你' : `${msgBody.from}` }}撤回了一条消息<span
-          class="reEdit"
-          v-show="isMyself(msgBody) && msgBody.type === ALL_MESSAGE_TYPE.TEXT"
-          @click="reEdit(msgBody.msg)"
-          >重新编辑</span
-        >
-      </div>
-      <!-- 灰色系统通知 -->
-      <div v-if="msgBody.type === ALL_MESSAGE_TYPE.INFORM" class="inform_style">
-        <p>
-          {{ msgBody.msg }}
-        </p>
-      </div>
     </div>
-    <ReportMessage ref="reportMessage" />
-    <ModifyMessage ref="modifyMessageRef" />
   </div>
 </template>
 

+ 1 - 1
src/views/im/Message/index.scss

@@ -111,7 +111,7 @@
     border-radius: 0 0 3px 0;
 }
 
-::v-deep .el-drawer {
+:deep(.el-drawer) {
     margin-top: 60px;
     width: 150px;
     height: calc(100% - 60px);

+ 77 - 57
src/views/im/Message/index.vue

@@ -4,11 +4,11 @@ import { messageType } from '@/constant/im'
 /* 组件 */
 import MessageList from './components/messageList/index.vue'
 import InputBox from './components/inputBox/index.vue'
+import * as MessageApi from '@/api/im/message'
 
-const { push, currentRoute } = useRouter() // 路由
 const { query } = useRoute() // 查询参数
 
-const { CHAT_TYPE } = messageType
+const { CHAT_TYPE, ALL_MESSAGE_TYPE } = messageType
 /* header 操作 */
 const drawer = ref(false) //抽屉显隐
 const handleDrawer = () => {
@@ -25,10 +25,10 @@ const delTheFriend = () => {
 }
 // 当前聊天对象信息
 const nowPickInfo = ref({
-  id: '1',
+  id: 1,
   chatType: CHAT_TYPE.SINGLE,
   userInfo: {
-    nickname: '好友1',
+    nickname: '芋道源码',
     userStatus: '1'
   },
   groupDetail: {
@@ -45,27 +45,76 @@ const groupDetail = computed(() => {
 //获取其id对应的消息内容
 const messageData = computed(() => [
   {
-    type: 'text'
-  }
-])
-//监听路由改变获取对应的getIdInfo
-const stopWatchRoute = watch(
-  () => query,
-  (routeVal) => {
-    console.log('>>>>>>>>监听到路由参数变化', routeVal)
-    if (routeVal) {
-      // nowPickInfo.value = { ...routeVal }
-      // loginState.value && getIdInfo(routeVal)
-    }
+    id: 1,
+    type: ALL_MESSAGE_TYPE.TEXT,
+    isRecall: false,
+    time: '1711944110000',
+    from: '1',
+    msg: 'Hello, world!111',
+    modifiedInfo: {
+      operationCount: 1
+    },
+    customExts: {
+      nickname: '芋道源码',
+      avatar: 'https://avatars.githubusercontent.com/u/2?v=4'
+    },
+    customEvent: {
+      type: '1',
+      data: {
+        type: '1',
+        data: 'https://avatars.githubusercontent.com/u/2?v=4'
+      }
+    },
+    file_length: 0
+  },
+  {
+    id: 2,
+    type: ALL_MESSAGE_TYPE.TEXT,
+    isRecall: false,
+    time: '1711944221000',
+    from: '2',
+    msg: 'Hi, there!222',
+    modifiedInfo: {
+      operationCount: 0
+    },
+    customExts: {
+      nickname: '芋道源码',
+      avatar: 'https://avatars.githubusercontent.com/u/2?v=4'
+    },
+    customEvent: {
+      type: '1',
+      data: {
+        type: '1',
+        data: 'https://avatars.githubusercontent.com/u/2?v=4'
+      }
+    },
+    file_length: 0
   },
   {
-    immediate: true
+    id: 3,
+    type: ALL_MESSAGE_TYPE.TEXT,
+    isRecall: false,
+    time: '1711944332000',
+    from: '1',
+    msg: 'Hello, world!333',
+    modifiedInfo: {
+      operationCount: 0
+    },
+    customExts: {
+      nickname: '芋道源码',
+      avatar: 'https://avatars.githubusercontent.com/u/2?v=4'
+    },
+    customEvent: {
+      type: '1',
+      data: {
+        type: '1',
+        data: 'https://avatars.githubusercontent.com/u/2?v=4'
+      }
+    },
+    file_length: 0
   }
-)
-//离开该路由销毁route监听
-onBeforeRouteLeave(() => {
-  stopWatchRoute()
-})
+])
+
 /* 消息相关 */
 const loadingHistoryMsg = ref(false) //是否正在加载中
 const isMoreHistoryMsg = ref(true) //加载文案展示为加载更多还是已无更多。
@@ -74,6 +123,10 @@ const notScrollBottom = ref(false) //是否滚动置底
 const fechHistoryMessage = (loadType) => {
   console.log(loadType)
   console.log('加载更多')
+  loadingHistoryMsg.value = true
+  setTimeout(() => {
+    loadingHistoryMsg.value = false
+  }, 1000)
 }
 //控制消息滚动
 const scrollMessageList = (direction) => {
@@ -96,39 +149,12 @@ const messageQuote = (msg) => inputBox.value.handleQuoteMessage(msg)
         </div>
         <div v-else> {{ nowPickInfo.id }}<span style="font-size: 10px">(非好友)</span> </div>
       </template>
-      <!-- 单人展示删除拉黑 -->
-      <span class="more" v-if="nowPickInfo.chatType === CHAT_TYPE.SINGLE">
-        <el-dropdown placement="bottom-end" trigger="click">
-          <svg
-            width="18"
-            height="4"
-            viewBox="0 0 18 4"
-            fill="none"
-            xmlns="http://www.w3.org/2000/svg"
-          >
-            <circle cx="2" cy="2" r="2" fill="#333333" />
-            <circle cx="9" cy="2" r="2" fill="#333333" />
-            <circle cx="16" cy="2" r="2" fill="#333333" />
-          </svg>
-          <template #dropdown>
-            <el-dropdown-menu>
-              <el-dropdown-item @click="delTheFriend"> 删除好友 </el-dropdown-item>
-              <!-- <el-dropdown-item @click="addFriendToBlackList">
-              加入黑名单
-            </el-dropdown-item> -->
-            </el-dropdown-menu>
-          </template>
-        </el-dropdown>
-      </span>
     </el-header>
     <el-main class="chat_message_main">
       <el-scrollbar class="main_container" ref="messageContainer">
         <div class="innerRef">
           <div v-show="isMoreHistoryMsg" class="chat_message_tips">
-            <div
-              v-show="messageData?.length && messageData[0].type !== 'inform'"
-              class="load_more_msg"
-            >
+            <div v-show="messageData?.length" class="load_more_msg">
               <el-link
                 v-show="!loadingHistoryMsg"
                 :disabled="!isMoreHistoryMsg"
@@ -140,13 +166,7 @@ const messageQuote = (msg) => inputBox.value.handleQuoteMessage(msg)
               <el-link v-show="loadingHistoryMsg" disabled>消息加载中...</el-link>
             </div>
           </div>
-          <MessageList
-            :nowPickInfo="nowPickInfo"
-            :messageData="messageData"
-            @scroll-message-list="scrollMessageList"
-            @re-edit-message="reEditMessage"
-            @message-quote="messageQuote"
-          />
+          <MessageList :nowPickInfo="nowPickInfo" :messageData="messageData" />
         </div>
       </el-scrollbar>
     </el-main>

+ 7 - 7
src/views/im/NavBar/index.vue

@@ -250,36 +250,36 @@ watch(
 }
 
 .components {
-  ::v-deep .edit_userinfo_diglog {
+  :deep(.edit_userinfo_diglog) {
     border-radius: 4px;
     overflow: hidden;
   }
 
-  ::v-deep .setting_func_diglog > .el-dialog__body {
+  .setting_func_diglog :deep(.el-dialog__body) {
     padding: 28px 24px 24px 24px;
   }
 
-  ::v-deep .setting_func_diglog > .el-dialog__header {
+  .setting_func_diglog :deep(.el-dialog__header) {
     background: #f2f2f2;
     margin: 0;
   }
 
-  ::v-deep .edit_userinfo_diglog > .el-dialog__header {
+  .edit_userinfo_diglog :deep(.el-dialog__header) {
     padding: 0;
     margin-right: 0;
   }
 
-  ::v-deep .edit_userinfo_diglog > .el-dialog__body {
+  .edit_userinfo_diglog :deep(.el-dialog__body) {
     padding: 0;
     border-radius: 4px;
   }
 
-  ::v-deep .login_diglog > .el-dialog__header {
+  .login_diglog :deep(.el-dialog__header) {
     background: #f2f2f2;
     margin: 0;
   }
 
-  ::v-deep .personal_setting_card > .el-dialog__header {
+  .personal_setting_card :deep(.el-dialog__header) {
     background: #f2f2f2;
     margin: 0;
   }

+ 11 - 1
src/views/im/index.vue

@@ -1,8 +1,17 @@
 <script lang="ts" setup>
+import { shallowRef, defineAsyncComponent, DefineComponent } from 'vue'
 import NavBar from './NavBar/index.vue'
 
+// 定义异步加载的组件
+const ConversationComponent = defineAsyncComponent(
+  () => import('@/views/im/Conversation/index.vue')
+)
+
+const currentComponent = shallowRef<DefineComponent | null>(ConversationComponent) // 默认加载对话组件
+
 defineOptions({ name: 'IM' })
 </script>
+
 <template>
   <div class="app-container">
     <el-container class="chat_container">
@@ -10,11 +19,12 @@ defineOptions({ name: 'IM' })
         <NavBar />
       </el-aside>
       <el-main class="chat_main_box">
-        <router-view />
+        <component :is="currentComponent" />
       </el-main>
     </el-container>
   </div>
 </template>
+
 <style lang="scss" scoped>
 .app-container {
   position: fixed;