新增:发送消息

feature/im
安浩浩 2024-05-08 23:09:33 +08:00
parent 93ba6c579a
commit ac66084650
5 changed files with 172 additions and 124 deletions

View File

@ -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 })
}

View File

@ -1,43 +1,80 @@
const SESSION_MESSAGE_TYPE = { const SESSION_MESSAGE_TYPE = {
img: '[图片]', img: '[图片]',
file: '[文件]', file: '[文件]',
audio: '[语音]', audio: '[语音]',
loc: '[位置]' loc: '[位置]'
} }
const CUSTOM_TYPE = { 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 = { const ALL_MESSAGE_TYPE = {
TEXT: 'txt', TEXT: 101,
IMAGE: 'img', IMAGE: 102,
AUDIO: 'audio', AUDIO: 103,
LOCAL: 'loc', VIDEO: 104,
VIDEO: 'video', FILE: 105,
FILE: 'file', AT_TEXT: 106,
CUSTOM: 'custom', MERGE: 107,
CMD: 'cmd', CARD: 108,
INFORM: 'inform' //这个类型不在环信消息类型内,属于自己定义的一种系统通知类的消息。 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 = { const CHAT_TYPE = {
SINGLE: 'singleChat', SINGLE: 1,
GROUP: 'groupChat' GROUP: 3,
NOTIFICATION: 4
} }
const MENTION_ALL = { const MENTION_ALL = {
TEXT: '所有人', TEXT: '所有人',
VALUE: 'ALL' VALUE: 'ALL'
} }
const CHANGE_MESSAGE_BODAY_TYPE = { const CHANGE_MESSAGE_BODAY_TYPE = {
RECALL: 0, RECALL: 0,
DELETE: 1, DELETE: 1,
MODIFY: 2 MODIFY: 2
} }
export default { export default {
SESSION_MESSAGE_TYPE, SESSION_MESSAGE_TYPE,
CUSTOM_TYPE, CUSTOM_TYPE,
ALL_MESSAGE_TYPE, ALL_MESSAGE_TYPE,
CHAT_TYPE, CHAT_TYPE,
MENTION_ALL, MENTION_ALL,
CHANGE_MESSAGE_BODAY_TYPE CHANGE_MESSAGE_BODAY_TYPE
} }

View File

@ -7,6 +7,8 @@ import { onClickOutside } from '@vueuse/core'
/* 组件 */ /* 组件 */
import PreviewSendImg from '../suit/previewSendImg.vue' import PreviewSendImg from '../suit/previewSendImg.vue'
import VueAt from 'vue-at/dist/vue-at-textarea' // for textarea 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({ const props = defineProps({
nowPickInfo: { nowPickInfo: {
type: Object, type: Object,
@ -100,17 +102,21 @@ const sendTextMessage = _.debounce(async () => {
em_at_list: isAtAll.value ? MENTION_ALL.VALUE : _.map(atMembers.value, 'value') 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) { if (messageQuoteRef.value?.isShowQuoteMsgBox) {
} }
textContent.value = '' textContent.value = ''
messageQuoteRef.value?.clearQuoteContent() messageQuoteRef.value?.clearQuoteContent()
try { try {
console.log('msgOptions', msgOptions) console.log('imMessageSendReqVO', imMessageSendReqVO)
// await store.dispatch('sendShowTypeMessage', { await MessageApi.sendMessage(imMessageSendReqVO)
// msgType: ALL_MESSAGE_TYPE.TEXT,
// msgOptions
// })
} catch (error) { } catch (error) {
//handleSDKErrorNotifi(error.type, error.message) //handleSDKErrorNotifi(error.type, error.message)
console.log('>>>>>>>发送失败+++++++', error) console.log('>>>>>>>发送失败+++++++', error)

View File

@ -2,43 +2,21 @@
import { formatDate } from '@/utils/formatTime' import { formatDate } from '@/utils/formatTime'
/* 默认头像 */ /* 默认头像 */
import defaultAvatar from '@/assets/imgs/avatar.gif' import defaultAvatar from '@/assets/imgs/avatar.gif'
/* emits */ /* props */
const emit = defineEmits(['scroll-message-list', 're-edit-message', 'message-quote']) const props = defineProps({
const messageData = ref([ messageData: {
{ type: [Array, Object],
id: 1, default: () => []
type: 'text',
isRecall: false,
time: '2024-04-01 12:00:00',
from: '1',
msg: 'Hello, world!',
modifiedInfo: {
operationCount: 1
}
}, },
{ nowPickInfo: {
id: 2, type: Object,
type: 'text', default: () => ({}),
isRecall: false, required: true
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
}
} }
]) })
const { nowPickInfo } = toRefs(props)
const { messageData } = toRefs(props)
//
const ALL_MESSAGE_TYPE = { const ALL_MESSAGE_TYPE = {
TEXT: 'txt', TEXT: 'txt',
IMAGE: 'img', IMAGE: 'img',
@ -52,6 +30,7 @@ const ALL_MESSAGE_TYPE = {
} }
/* 处理时间显示间隔 */ /* 处理时间显示间隔 */
const handleMsgTimeShow = (time, index) => { const handleMsgTimeShow = (time, index) => {
console.log('>>>>>时间显示', time, index)
const msgList = Array.from(messageData.value) const msgList = Array.from(messageData.value)
if (index !== 0) { if (index !== 0) {
const lastTime = msgList[index - 1].time const lastTime = msgList[index - 1].time
@ -108,10 +87,6 @@ const startplayAudio = (msgBody) => {
audioPlayStatus.playMsgId = '' audioPlayStatus.playMsgId = ''
}) })
} }
//
const reEdit = (msg) => emit('reEditMessage', msg)
//
const onMsgQuote = (msg) => emit('messageQuote', msg)
</script> </script>
<template> <template>
<div> <div>

View File

@ -5,10 +5,9 @@ import { messageType } from '@/constant/im'
import MessageList from './components/messageList/index.vue' import MessageList from './components/messageList/index.vue'
import InputBox from './components/inputBox/index.vue' import InputBox from './components/inputBox/index.vue'
const { push, currentRoute } = useRouter() //
const { query } = useRoute() // const { query } = useRoute() //
const { CHAT_TYPE } = messageType const { CHAT_TYPE, ALL_MESSAGE_TYPE } = messageType
/* header 操作 */ /* header 操作 */
const drawer = ref(false) // const drawer = ref(false) //
const handleDrawer = () => { const handleDrawer = () => {
@ -25,10 +24,10 @@ const delTheFriend = () => {
} }
// //
const nowPickInfo = ref({ const nowPickInfo = ref({
id: '1', id: 1,
chatType: CHAT_TYPE.SINGLE, chatType: CHAT_TYPE.SINGLE,
userInfo: { userInfo: {
nickname: '好友1', nickname: '芋道源码',
userStatus: '1' userStatus: '1'
}, },
groupDetail: { groupDetail: {
@ -45,27 +44,40 @@ const groupDetail = computed(() => {
//id //id
const messageData = computed(() => [ const messageData = computed(() => [
{ {
type: 'text' id: 1,
} type: ALL_MESSAGE_TYPE.TEXT,
]) isRecall: false,
//getIdInfo time: '1711944000000',
const stopWatchRoute = watch( from: '1',
() => query, msg: 'Hello, world!',
(routeVal) => { modifiedInfo: {
console.log('>>>>>>>>监听到路由参数变化', routeVal) operationCount: 1
if (routeVal) {
// nowPickInfo.value = { ...routeVal }
// loginState.value && getIdInfo(routeVal)
} }
}, },
{ {
immediate: true id: 2,
type: ALL_MESSAGE_TYPE.TEXT,
isRecall: false,
time: '1711944001000',
from: '2',
msg: 'Hi, there!',
modifiedInfo: {
operationCount: 0
}
},
{
id: 3,
type: ALL_MESSAGE_TYPE.TEXT,
isRecall: true,
time: '1711944002000',
from: '1',
msg: 'Hello, world!',
modifiedInfo: {
operationCount: 0
}
} }
) ])
//route
onBeforeRouteLeave(() => {
stopWatchRoute()
})
/* 消息相关 */ /* 消息相关 */
const loadingHistoryMsg = ref(false) // const loadingHistoryMsg = ref(false) //
const isMoreHistoryMsg = ref(true) // const isMoreHistoryMsg = ref(true) //
@ -74,6 +86,10 @@ const notScrollBottom = ref(false) //是否滚动置底
const fechHistoryMessage = (loadType) => { const fechHistoryMessage = (loadType) => {
console.log(loadType) console.log(loadType)
console.log('加载更多') console.log('加载更多')
loadingHistoryMsg.value = true
setTimeout(() => {
loadingHistoryMsg.value = false
}, 1000)
} }
// //
const scrollMessageList = (direction) => { const scrollMessageList = (direction) => {
@ -96,37 +112,15 @@ const messageQuote = (msg) => inputBox.value.handleQuoteMessage(msg)
</div> </div>
<div v-else> {{ nowPickInfo.id }}<span style="font-size: 10px">(非好友)</span> </div> <div v-else> {{ nowPickInfo.id }}<span style="font-size: 10px">(非好友)</span> </div>
</template> </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-header>
<el-main class="chat_message_main"> <el-main class="chat_message_main">
<el-scrollbar class="main_container" ref="messageContainer"> <el-scrollbar class="main_container" ref="messageContainer">
<div class="innerRef"> <div class="innerRef">
<div v-show="isMoreHistoryMsg" class="chat_message_tips"> <div v-show="isMoreHistoryMsg" class="chat_message_tips">
<div <div
v-show="messageData?.length && messageData[0].type !== 'inform'" v-show="
messageData?.length && messageData[0].type !== ALL_MESSAGE_TYPE.OA_NOTIFICATION
"
class="load_more_msg" class="load_more_msg"
> >
<el-link <el-link
@ -140,13 +134,7 @@ const messageQuote = (msg) => inputBox.value.handleQuoteMessage(msg)
<el-link v-show="loadingHistoryMsg" disabled>消息加载中...</el-link> <el-link v-show="loadingHistoryMsg" disabled>消息加载中...</el-link>
</div> </div>
</div> </div>
<MessageList <MessageList :nowPickInfo="nowPickInfo" :messageData="messageData" />
:nowPickInfo="nowPickInfo"
:messageData="messageData"
@scroll-message-list="scrollMessageList"
@re-edit-message="reEditMessage"
@message-quote="messageQuote"
/>
</div> </div>
</el-scrollbar> </el-scrollbar>
</el-main> </el-main>