diff --git a/src/api/im/conversation/index.ts b/src/api/im/conversation/index.ts new file mode 100644 index 00000000..a60956fb --- /dev/null +++ b/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 }) +} diff --git a/src/api/im/message/index.ts b/src/api/im/message/index.ts new file mode 100644 index 00000000..d8a9a54d --- /dev/null +++ b/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 }) +} diff --git a/src/components/Cropper/src/CropperAvatar.vue b/src/components/Cropper/src/CropperAvatar.vue index 9464c2a8..dc91bd26 100644 --- a/src/components/Cropper/src/CropperAvatar.vue +++ b/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; } } diff --git a/src/components/Im/SearchInput/src/SearchInput.vue b/src/components/Im/SearchInput/src/SearchInput.vue index 85b3cf62..a577b190 100644 --- a/src/components/Im/SearchInput/src/SearchInput.vue +++ b/src/components/Im/SearchInput/src/SearchInput.vue @@ -29,7 +29,7 @@ const querySearch = () => { diff --git a/src/views/im/Message/components/inputBox/index.scss b/src/views/im/Message/components/inputBox/index.scss index 4cb5c384..9373989e 100644 --- a/src/views/im/Message/components/inputBox/index.scss +++ b/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; diff --git a/src/views/im/Message/components/inputBox/index.vue b/src/views/im/Message/components/inputBox/index.vue index 238bd1f9..5e55ff56 100644 --- a/src/views/im/Message/components/inputBox/index.vue +++ b/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) diff --git a/src/views/im/Message/components/messageList/index.vue b/src/views/im/Message/components/messageList/index.vue index 7d4c40eb..3ca84165 100644 --- a/src/views/im/Message/components/messageList/index.vue +++ b/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 - } +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: 2, - type: 'text', - isRecall: false, - time: '2024-04-01 12:00:01', - from: '2', - msg: 'Hi, there!', - modifiedInfo: { - operationCount: 0 - } - }, - { - 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) - - - -
-

- {{ msgBody?.ext?.msgQuote?.msgSender }}:{{ msgBody?.ext?.msgQuote?.msgPreview }} -

-
- -
- {{ isMyself(msgBody) ? '你' : `${msgBody.from}` }}撤回了一条消息重新编辑 -
- -
-

- {{ msgBody.msg }} -

-
- - diff --git a/src/views/im/Message/index.scss b/src/views/im/Message/index.scss index 8bc33f6b..3a44ea39 100644 --- a/src/views/im/Message/index.scss +++ b/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); diff --git a/src/views/im/Message/index.vue b/src/views/im/Message/index.vue index 8a2062df..3b70e5f8 100644 --- a/src/views/im/Message/index.vue +++ b/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 }, { - immediate: true + 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 + }, + { + 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)
{{ nowPickInfo.id }}(非好友)
- - - - - - - - - - -
-
+
inputBox.value.handleQuoteMessage(msg) 消息加载中...
- +
diff --git a/src/views/im/NavBar/index.vue b/src/views/im/NavBar/index.vue index e450319f..e1447b40 100644 --- a/src/views/im/NavBar/index.vue +++ b/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; } diff --git a/src/views/im/index.vue b/src/views/im/index.vue index 650e4261..4c56a7db 100644 --- a/src/views/im/index.vue +++ b/src/views/im/index.vue @@ -1,8 +1,17 @@ + +