新增:接收 websocket 消息
parent
569b1b64bd
commit
063a61644a
|
@ -26,17 +26,22 @@ export interface ImMessageRespVO {
|
|||
sequence: number // 序号
|
||||
}
|
||||
|
||||
export interface pullParams {
|
||||
sequence: number
|
||||
size: number
|
||||
}
|
||||
|
||||
// 发送消息
|
||||
export const sendMessage = async (data: ImMessageSendReqVO) => {
|
||||
export const sendMessage = async (data: ImMessageSendReqVO): Promise<ImMessageSendRespVO> => {
|
||||
return await request.post({ url: `/im/message/send`, data })
|
||||
}
|
||||
|
||||
// 消息列表-拉取大于 sequence 的消息列表
|
||||
export const pullMessageList = async (params: { sequence: number; size: number }) => {
|
||||
export const pullMessageList = async (params: pullParams): Promise<ImMessageRespVO[]> => {
|
||||
return await request.get({ url: `/im/message/pull`, params })
|
||||
}
|
||||
|
||||
// 消息列表-根据接收人和发送时间进行分页查询
|
||||
export const getMessageList = async (params: any) => {
|
||||
export const getMessageList = async (params: any): Promise<ImMessageRespVO[]> => {
|
||||
return await request.get({ url: `/im/message/list`, params })
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<script setup lang="ts">
|
||||
import { Dialog } from '@/components/Dialog'
|
||||
import { shallowRef, defineAsyncComponent, DefineComponent } from 'vue'
|
||||
import { webSocketStore } from '@/store/modules/webSocketStore'
|
||||
|
||||
// 异步加载可能的对话框内容组件
|
||||
const IMComponent = defineAsyncComponent(() => import('@/views/im/index.vue'))
|
||||
|
@ -13,6 +14,17 @@ function openDialog() {
|
|||
dialogVisible.value = true
|
||||
currentComponent.value = IMComponent // 加载 IM 组件
|
||||
}
|
||||
// 登录成功后初始化 WebSocket 连接
|
||||
const { status, data, initWebSocket } = webSocketStore()
|
||||
|
||||
/** 监听接收到的数据 */
|
||||
const messageList = ref([] as { time: number; text: string }[]) // 消息列表
|
||||
|
||||
// ========== 初始化 =========
|
||||
onMounted(() => {
|
||||
// 首次加载 websocket
|
||||
initWebSocket()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
import { defineStore } from 'pinia'
|
||||
|
||||
export interface ImMessageWebSocket {
|
||||
id: number // 编号
|
||||
conversationType: number // 会话类型
|
||||
senderId: number // 发送人编号
|
||||
senderNickname: string // 发送人昵称
|
||||
senderAvatar: string // 发送人头像
|
||||
receiverId: number // 接收人编号
|
||||
contentType: number // 内容类型
|
||||
content: string // 内容
|
||||
sendTime: string // 发送时间
|
||||
sequence: number // 序号
|
||||
}
|
||||
|
||||
export const imMessageStore = defineStore({
|
||||
id: 'imMessage',
|
||||
state: () => ({
|
||||
messages: [] as ImMessageWebSocket[]
|
||||
}),
|
||||
actions: {
|
||||
addMessage(message: ImMessageWebSocket) {
|
||||
this.messages.push(message)
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,81 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { useWebSocket, WebSocketStatus } from '@vueuse/core'
|
||||
import { getAccessToken } from '@/utils/auth'
|
||||
import { imMessageStore, ImMessageWebSocket } from './imMessageStore'
|
||||
|
||||
// 从 imMessageStore 中导入 addMessage 方法
|
||||
const { addMessage } = imMessageStore()
|
||||
export const webSocketStore = defineStore({
|
||||
id: 'websocket',
|
||||
state: () => ({
|
||||
data: ref(null) as any,
|
||||
status: ref<WebSocketStatus> as any,
|
||||
close: null as ((code?: number | undefined, reason?: string | undefined) => void) | null,
|
||||
open: null as (() => void) | null,
|
||||
send: null as
|
||||
| ((data: string | ArrayBuffer | Blob, useBuffer?: boolean | undefined) => boolean)
|
||||
| null,
|
||||
pingInterval: null as NodeJS.Timeout | null
|
||||
}),
|
||||
actions: {
|
||||
initWebSocket() {
|
||||
const server =
|
||||
(import.meta.env.VITE_BASE_URL + '/infra/ws').replace('http', 'ws') +
|
||||
'?token=' +
|
||||
getAccessToken(),
|
||||
{ data, status, close, open, send } = useWebSocket(server, {
|
||||
autoReconnect: true,
|
||||
heartbeat: true,
|
||||
onMessage(ws, event) {
|
||||
// ws 状态不是 open 时不处理
|
||||
if (ws.readyState !== WebSocket.OPEN) return
|
||||
try {
|
||||
// 1. 收到心跳
|
||||
if (data.value === 'pong') {
|
||||
console.log('websocket 收到心跳包')
|
||||
return
|
||||
}
|
||||
console.log('websocket 收到消息', event.data)
|
||||
// 2.1 解析 type 消息类型
|
||||
const jsonMessage = JSON.parse(data.value)
|
||||
const type = jsonMessage.type
|
||||
const content = JSON.parse(jsonMessage.content)
|
||||
if (!type) {
|
||||
// message.error('未知的消息类型:' + data.value)
|
||||
return
|
||||
}
|
||||
// 2.2 消息类型:demo-message-receive
|
||||
if (type === 'im-message-receive') {
|
||||
const message: ImMessageWebSocket = {
|
||||
id: content.id,
|
||||
conversationType: content.conversationType,
|
||||
senderId: content.senderId,
|
||||
senderNickname: content.senderNickname,
|
||||
senderAvatar: content.senderAvatar,
|
||||
receiverId: content.receiverId,
|
||||
contentType: content.contentType,
|
||||
content: content.content,
|
||||
sendTime: content.sendTime,
|
||||
sequence: content.sequence
|
||||
}
|
||||
|
||||
// 保存到 pina imMessageStore
|
||||
addMessage(message)
|
||||
|
||||
return
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
}
|
||||
})
|
||||
this.status = status
|
||||
this.data = data
|
||||
this.send = send
|
||||
this.close = close
|
||||
this.open = open
|
||||
|
||||
console.log('websocket 初始化成功')
|
||||
}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,150 @@
|
|||
<script setup>
|
||||
import { useStore } from 'vuex'
|
||||
import router from '@/router'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { ArrowLeft } from '@element-plus/icons-vue'
|
||||
import { messageType } from '@/constant'
|
||||
/* 单人头像 */
|
||||
import defaultSingleAvatar from '@/assets/imgs/im/avatar/theme2x.png'
|
||||
/* store */
|
||||
const store = useStore()
|
||||
/* route */
|
||||
const route = useRoute()
|
||||
const { CHAT_TYPE } = messageType
|
||||
|
||||
/* 进入会话 */
|
||||
const toChatMessage = () => {
|
||||
console.log('>>>>>>>...route.query')
|
||||
router.push({
|
||||
path: '/chat/conversation/message',
|
||||
query: {
|
||||
id: route.query.id,
|
||||
chatType: route.query.chatType
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="app_container">
|
||||
<el-header class="contactInfo_header">
|
||||
<el-page-header style="margin-top: 12px" :icon="ArrowLeft" @click="$router.back(-1)" />
|
||||
<el-divider />
|
||||
</el-header>
|
||||
<el-main class="contactInfo_main">
|
||||
<div class="contactInfo_main_card">
|
||||
<div class="contactInfo_box">
|
||||
<div class="avatar">
|
||||
<el-avatar
|
||||
class="avatar_img"
|
||||
:src="nowContactInfo.avatarurl ? nowContactInfo.avatarurl : defaultSingleAvatar"
|
||||
/>
|
||||
</div>
|
||||
<div class="name">
|
||||
<p>
|
||||
{{
|
||||
nowContactInfo.nickname
|
||||
? `${nowContactInfo.nickname}(${nowContactInfo.hxId})`
|
||||
: nowContactInfo.hxId
|
||||
}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="contaactInfo_btn">
|
||||
<el-button type="primary" size="large" @click="toChatMessage">发起会话 </el-button>
|
||||
</div>
|
||||
</div>
|
||||
</el-main>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.app_container {
|
||||
background: #f1f2f4;
|
||||
height: 100%;
|
||||
border-radius: 0 5px 5px 0;
|
||||
overflow: hidden;
|
||||
|
||||
.contactInfo_header {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
}
|
||||
|
||||
.contactInfo_main {
|
||||
height: 100%;
|
||||
|
||||
.contactInfo_main_card {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin: 0 auto;
|
||||
border-radius: 5px;
|
||||
transition: all 0.5s;
|
||||
|
||||
&:hover {
|
||||
background: #fff;
|
||||
box-shadow: 12px 12px 2px 1px rgba(125, 125, 126, 0.068);
|
||||
}
|
||||
|
||||
.contactInfo_box {
|
||||
width: 80%;
|
||||
min-height: 500px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
|
||||
.avatar > .avatar_img {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.name {
|
||||
margin-top: 15px;
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.func_box {
|
||||
width: 100%;
|
||||
|
||||
.single_func {
|
||||
height: 100px;
|
||||
// background: #000;
|
||||
margin-top: 25px;
|
||||
cursor: pointer;
|
||||
|
||||
.add_black_list {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.del_friend {
|
||||
width: 100%;
|
||||
color: red;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.contaactInfo_btn {
|
||||
width: 80%;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//干掉原有样式里面的竖线
|
||||
::v-deep .el-page-header__left::after {
|
||||
width: 0px !important;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,116 @@
|
|||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
import { useStore } from 'vuex'
|
||||
// import router from '@/router'
|
||||
import { messageType } from '@/constant'
|
||||
/* 默认头像 */
|
||||
import defaultAvatar from '@/assets/images/avatar/theme2x.png'
|
||||
/* store */
|
||||
const store = useStore()
|
||||
|
||||
//处理friendList进行分类处理
|
||||
const classifyFriendList = computed(() => store.getters.sortedFriendList)
|
||||
//点击对应联系人跳转至用户详情页
|
||||
const { CHAT_TYPE } = messageType
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="friendItem_container">
|
||||
<div
|
||||
v-for="(friendName, friendItemKey) in classifyFriendList"
|
||||
:key="friendItemKey"
|
||||
>
|
||||
<div class="friend_main">
|
||||
<div class="friend_title">
|
||||
<p>
|
||||
{{
|
||||
friendItemKey === ' '
|
||||
? '#'
|
||||
: friendItemKey.toUpperCase()
|
||||
}}
|
||||
</p>
|
||||
|
||||
<el-divider style="margin: 0" />
|
||||
</div>
|
||||
|
||||
<el-row>
|
||||
<el-col
|
||||
class="friendItem_box"
|
||||
:span="24"
|
||||
v-for="item in friendName"
|
||||
:key="item.hxId"
|
||||
@click="
|
||||
$emit('toContacts', {
|
||||
id: item.hxId,
|
||||
chatType: CHAT_TYPE.SINGLE
|
||||
})
|
||||
"
|
||||
>
|
||||
<el-avatar
|
||||
style="margin-right: 11px"
|
||||
:size="33.03"
|
||||
:src="
|
||||
item.avatarurl ? item.avatarurl : defaultAvatar
|
||||
"
|
||||
>
|
||||
</el-avatar>
|
||||
<span class="friend_name">
|
||||
{{ item.nickname ? item.nickname : item.hxId }}
|
||||
</span>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.friendItem_box {
|
||||
height: 66px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
// background: #000;
|
||||
padding: 0 22px;
|
||||
background: #efefef;
|
||||
font-weight: 500;
|
||||
font-size: 14px;
|
||||
line-height: 20px;
|
||||
/* identical to box height */
|
||||
text-align: center;
|
||||
color: #333333;
|
||||
cursor: pointer;
|
||||
|
||||
.friend_name {
|
||||
display: inline-block;
|
||||
text-align: left;
|
||||
width: 80%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: #dcdcdc;
|
||||
}
|
||||
}
|
||||
|
||||
.friend_main {
|
||||
width: 100%;
|
||||
|
||||
.friend_title {
|
||||
width: 100%;
|
||||
height: 32px;
|
||||
mix-blend-mode: normal;
|
||||
opacity: 0.21;
|
||||
font-size: 14px;
|
||||
padding-left: 23px;
|
||||
font-weight: 400;
|
||||
font-size: 12px;
|
||||
line-height: 32px;
|
||||
letter-spacing: 0.342857px;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -1,5 +1,30 @@
|
|||
<script setup lang="ts"></script>
|
||||
<template>
|
||||
<el-container style="height: 100%">
|
||||
<el-aside class="contacts_box">
|
||||
<SearchInput :searchType="'contacts'" />
|
||||
<el-scrollbar class="contacts_collapse" tag="div" :always="false">
|
||||
<!-- 联系人群组列表 -->
|
||||
<el-collapse />
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
<el-main class="contacts_infors_main_box" />
|
||||
</el-container>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
/* 相关组件 */
|
||||
import { SearchInput } from '@/components/Im/SearchInput'
|
||||
import * as UserApi from '@/api/system/user'
|
||||
|
||||
<template></template>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
//好友列表
|
||||
const friendList = computed(() => {})
|
||||
const getUserList = async () => {
|
||||
const data = await UserApi.getSimpleUserList()
|
||||
console.log('data', data)
|
||||
friendList.value = data
|
||||
}
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getUserList()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
|
|
|
@ -116,9 +116,9 @@ const sendTextMessage = _.debounce(async () => {
|
|||
messageQuoteRef.value?.clearQuoteContent()
|
||||
try {
|
||||
console.log('imMessageSendReqVO', imMessageSendReqVO)
|
||||
await MessageApi.sendMessage(imMessageSendReqVO)
|
||||
const imMessageSendRespVO = await MessageApi.sendMessage(imMessageSendReqVO)
|
||||
console.log('>>>>>发送成功', imMessageSendRespVO)
|
||||
} catch (error) {
|
||||
//handleSDKErrorNotifi(error.type, error.message)
|
||||
console.log('>>>>>>>发送失败+++++++', error)
|
||||
} finally {
|
||||
isAtAll.value = false
|
||||
|
|
|
@ -25,21 +25,20 @@ const props = defineProps({
|
|||
})
|
||||
const { nowPickInfo } = toRefs(props)
|
||||
const { messageData } = toRefs(props)
|
||||
console.log('>>>>>messageData', messageData)
|
||||
/* 处理时间显示间隔 */
|
||||
const handleMsgTimeShow = (time, index) => {
|
||||
console.log('>>>>>时间显示', time, index)
|
||||
const handleMsgTimeShow = (sendTime, index) => {
|
||||
const msgList = Array.from(messageData.value)
|
||||
if (index !== 0) {
|
||||
const lastTime = msgList[index - 1].time
|
||||
console.log('>>>>>时间间隔', time - lastTime, time, lastTime)
|
||||
return time - lastTime > 50000 ? formatDate(time, 'MM/DD/HH:mm') : ''
|
||||
const lastTime = msgList[index - 1].sendTime
|
||||
return sendTime - lastTime > 50000 ? formatDate(sendTime, 'MM/DD/HH:mm') : ''
|
||||
} else {
|
||||
return formatDate(time, 'MM/DD/HH:mm')
|
||||
return formatDate(sendTime, 'MM/DD/HH:mm')
|
||||
}
|
||||
}
|
||||
/* computed-- 消息来源是否为自己 */
|
||||
const isMyself = (msgBody) => {
|
||||
return msgBody.from === '1'
|
||||
return msgBody.senderId === userStore.user.id || msgBody.receiverId === userStore.user.id
|
||||
}
|
||||
/* 文本中是否包含link */
|
||||
const isLink = computed(() => {
|
||||
|
@ -51,15 +50,11 @@ const isLink = computed(() => {
|
|||
const loginUserInfo = {
|
||||
avatarurl: avatar.value
|
||||
}
|
||||
/* 获取他人的用户信息 */
|
||||
const otherUserInfo = (from) => {
|
||||
return {
|
||||
avatarurl: 'https://avatars.githubusercontent.com/u/2?v=4'
|
||||
}
|
||||
}
|
||||
//处理聊天对方昵称展示
|
||||
const handleNickName = (from) => {
|
||||
return from === '1' ? '我' : '对方'
|
||||
const handleNickName = (msgBody) => {
|
||||
return msgBody.senderId === userStore.user.id || msgBody.receiverId === userStore.user.id
|
||||
? '我'
|
||||
: '对方'
|
||||
}
|
||||
//引用消息
|
||||
let clickQuoteMsgId = ref('')
|
||||
|
@ -102,121 +97,40 @@ const startplayAudio = (msgBody) => {
|
|||
>
|
||||
<!-- 普通消息气泡 -->
|
||||
<div
|
||||
v-if="!msgBody.isRecall && msgBody.type !== ALL_MESSAGE_TYPE.OA_NOTIFICATION"
|
||||
v-if="msgBody.conversationType !== 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.sendTime, index) }}
|
||||
</div>
|
||||
<el-avatar
|
||||
class="message_item_avator"
|
||||
:src="
|
||||
isMyself(msgBody)
|
||||
? loginUserInfo.avatarurl
|
||||
: otherUserInfo(msgBody.from).avatarurl || defaultAvatar
|
||||
"
|
||||
/>
|
||||
<el-avatar class="message_item_avator" :src="loginUserInfo.avatarurl" />
|
||||
<!-- 普通消息内容 -->
|
||||
<div class="message_box_card">
|
||||
<span v-show="!isMyself(msgBody)" class="message_box_nickname">{{
|
||||
handleNickName(msgBody.from)
|
||||
handleNickName(msgBody)
|
||||
}}</span>
|
||||
<el-dropdown
|
||||
class="message_box_content"
|
||||
:class="[
|
||||
isMyself(msgBody) ? 'message_box_content_mine' : 'message_box_content_other',
|
||||
clickQuoteMsgId === msgBody.id && 'quote_msg_avtive'
|
||||
]"
|
||||
:class="[isMyself(msgBody) ? 'message_box_content_mine' : 'message_box_content_other']"
|
||||
trigger="contextmenu"
|
||||
placement="bottom-end"
|
||||
>
|
||||
<!-- 文本类型消息 -->
|
||||
<p
|
||||
style="padding: 10px; line-height: 20px"
|
||||
v-if="msgBody.type === ALL_MESSAGE_TYPE.TEXT"
|
||||
v-if="msgBody.contentType === ALL_MESSAGE_TYPE.TEXT"
|
||||
>
|
||||
<template v-if="!isLink(msgBody.msg)">
|
||||
{{ msgBody.msg }}
|
||||
<template v-if="!isLink(msgBody.content)">
|
||||
{{ msgBody.content }}
|
||||
</template>
|
||||
<template v-else> <span v-html="paseLink(msgBody.msg).msg"> </span></template>
|
||||
<template v-else> <span v-html="paseLink(msgBody.content).msg"> </span></template>
|
||||
</p>
|
||||
<!-- 图片类型消息 -->
|
||||
<!-- <div> -->
|
||||
<el-image
|
||||
v-if="msgBody.type === ALL_MESSAGE_TYPE.IMAGE"
|
||||
style="border-radius: 5px"
|
||||
:src="msgBody.thumb"
|
||||
:preview-src-list="[msgBody.url]"
|
||||
:initial-index="1"
|
||||
fit="cover"
|
||||
/>
|
||||
<!-- </div> -->
|
||||
<!-- 语音类型消息 -->
|
||||
<div
|
||||
:class="[
|
||||
'message_box_content_audio',
|
||||
isMyself(msgBody)
|
||||
? 'message_box_content_audio_mine'
|
||||
: 'message_box_content_audio_other'
|
||||
]"
|
||||
v-if="msgBody.type === ALL_MESSAGE_TYPE.AUDIO"
|
||||
@click="startplayAudio(msgBody)"
|
||||
:style="`width:${msgBody.length * 10}px`"
|
||||
>
|
||||
<span class="audio_length_text"> {{ msgBody.length }}′′ </span>
|
||||
<div
|
||||
:class="[
|
||||
isMyself(msgBody) ? 'play_audio_icon_mine' : 'play_audio_icon_other',
|
||||
audioPlayStatus.playMsgId === msgBody.id && 'start_play_audio'
|
||||
]"
|
||||
style="background-size: 100% 100%"
|
||||
></div>
|
||||
</div>
|
||||
<div v-if="msgBody.type === ALL_MESSAGE_TYPE.LOCAL">
|
||||
<p style="padding: 10px">[暂不支持位置消息展示]</p>
|
||||
</div>
|
||||
<!-- 文件类型消息 -->
|
||||
<div v-if="msgBody.type === ALL_MESSAGE_TYPE.FILE" class="message_box_content_file">
|
||||
<div class="file_text_box">
|
||||
<div class="file_name">
|
||||
{{ msgBody.filename }}
|
||||
</div>
|
||||
<div class="file_size">
|
||||
{{ fileSizeFormat(msgBody.file_length) }}
|
||||
</div>
|
||||
<a class="file_download" :href="msgBody.url" download>点击下载</a>
|
||||
</div>
|
||||
<span class="iconfont icon-wenjian"></span>
|
||||
</div>
|
||||
<!-- 自定义类型消息 -->
|
||||
<div v-if="msgBody.type === ALL_MESSAGE_TYPE.CUSTOM" class="message_box_content_custom">
|
||||
<template v-if="msgBody.customEvent && CUSTOM_TYPE[msgBody.customEvent]">
|
||||
<div class="user_card">
|
||||
<div class="user_card_main">
|
||||
<!-- 头像 -->
|
||||
<el-avatar
|
||||
shape="circle"
|
||||
:size="50"
|
||||
:src="
|
||||
(msgBody.customExts && msgBody.customExts.avatarurl) ||
|
||||
msgBody.customExts.avatar ||
|
||||
defaultAvatar
|
||||
"
|
||||
fit="cover"
|
||||
/>
|
||||
<!-- 昵称 -->
|
||||
<span class="nickname">{{
|
||||
(msgBody.customExts && msgBody.customExts.nickname) || msgBody.customExts.uid
|
||||
}}</span>
|
||||
</div>
|
||||
<el-divider style="margin: 5px 0; border-top: 1px solid black" />
|
||||
<p style="font-size: 8px">个人名片</p>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -5,6 +5,7 @@ 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'
|
||||
import { ImMessageRespVO } from '@/api/im/message'
|
||||
|
||||
const { query } = useRoute() // 查询参数
|
||||
|
||||
|
@ -25,10 +26,10 @@ const delTheFriend = () => {
|
|||
}
|
||||
// 当前聊天对象信息
|
||||
const nowPickInfo = ref({
|
||||
id: 1,
|
||||
id: 100,
|
||||
chatType: CHAT_TYPE.SINGLE,
|
||||
userInfo: {
|
||||
nickname: '芋道源码',
|
||||
nickname: '芋道',
|
||||
userStatus: '1'
|
||||
},
|
||||
groupDetail: {
|
||||
|
@ -42,78 +43,16 @@ const nowPickInfo = ref({
|
|||
const groupDetail = computed(() => {
|
||||
return nowPickInfo.value.groupDetail
|
||||
})
|
||||
//获取其id对应的消息内容
|
||||
const messageData = computed(() => [
|
||||
{
|
||||
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
|
||||
},
|
||||
{
|
||||
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
|
||||
}
|
||||
])
|
||||
//获取对应的消息内容
|
||||
const pullParams = reactive({
|
||||
sequence: 0,
|
||||
size: 100
|
||||
})
|
||||
const messageData = ref([])
|
||||
const getMessageData = async () => {
|
||||
messageData.value = await MessageApi.pullMessageList(pullParams)
|
||||
}
|
||||
console.log(messageData)
|
||||
|
||||
/* 消息相关 */
|
||||
const loadingHistoryMsg = ref(false) //是否正在加载中
|
||||
|
@ -124,6 +63,7 @@ const fechHistoryMessage = (loadType) => {
|
|||
console.log(loadType)
|
||||
console.log('加载更多')
|
||||
loadingHistoryMsg.value = true
|
||||
getMessageData()
|
||||
setTimeout(() => {
|
||||
loadingHistoryMsg.value = false
|
||||
}, 1000)
|
||||
|
@ -137,6 +77,10 @@ const inputBox = ref(null)
|
|||
const reEditMessage = (msg) => (inputBox.value.textContent = msg)
|
||||
//消息引用
|
||||
const messageQuote = (msg) => inputBox.value.handleQuoteMessage(msg)
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getMessageData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
|
|
@ -1,17 +1,3 @@
|
|||
<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">
|
||||
|
@ -24,7 +10,19 @@ defineOptions({ name: 'IM' })
|
|||
</el-container>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { shallowRef, defineAsyncComponent, DefineComponent } from 'vue'
|
||||
import NavBar from './NavBar/index.vue'
|
||||
|
||||
defineOptions({ name: 'IM' })
|
||||
|
||||
// 定义异步加载的组件
|
||||
const conversationComponent = defineAsyncComponent(
|
||||
() => import('@/views/im/Conversation/index.vue')
|
||||
)
|
||||
|
||||
const currentComponent = shallowRef<DefineComponent | null>(conversationComponent) // 默认加载对话组件
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.app-container {
|
||||
position: fixed;
|
||||
|
|
Loading…
Reference in New Issue