修改:获取消息内容优化

feature/im
安浩浩 2024-05-19 15:30:07 +08:00
parent ac66084650
commit 35b97b1ec0
10 changed files with 146 additions and 165 deletions

View File

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

View File

@ -1,12 +1,12 @@
<script setup lang="ts">
import { Dialog } from '@/components/Dialog'
import { ref, defineAsyncComponent } from 'vue'
import { shallowRef, defineAsyncComponent, DefineComponent } from 'vue'
//
const IMComponent = defineAsyncComponent(() => import('@/views/im/index.vue'))
const dialogVisible = ref(false)
const currentComponent = ref(null)
const dialogVisible = shallowRef(false)
const currentComponent = shallowRef<DefineComponent | null>(null)
// IM
function openDialog() {

View File

@ -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
src/utils/paseLink.ts Normal file
View File

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

View File

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

View File

@ -1,23 +1,14 @@
<script setup lang="ts">
import { ref, defineAsyncComponent } from 'vue'
import { defineAsyncComponent, shallowRef } from 'vue'
import { SearchInput } from '@/components/Im/SearchInput'
import ConversationList from '../Conversation/components/ConversationList.vue'
import { Welcome } from '@/components/Im/Welcome'
//
const InformDetailsComponent = defineAsyncComponent(
() => import('@/views/im/InformDetails/index.vue')
)
const MessageComponent = defineAsyncComponent(() => import('@/views/im/Message/index.vue'))
const currentComponent = ref(Welcome) //
const currentComponent = shallowRef(Welcome) //
// 使
const toInformDetails = () => {
currentComponent.value = InformDetailsComponent //
}
const toChatMessage = (id, chatType) => {
const toChatMessage = (id) => {
console.log('>>>>>>>id', id)
currentComponent.value = MessageComponent //
}
@ -29,7 +20,7 @@ const toChatMessage = (id, chatType) => {
<!-- 搜索组件 -->
<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">

View File

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

View File

@ -2,6 +2,15 @@
import { formatDate } from '@/utils/formatTime'
/* 默认头像 */
import defaultAvatar from '@/assets/imgs/avatar.gif'
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: {
@ -16,25 +25,14 @@ const props = defineProps({
})
const { nowPickInfo } = toRefs(props)
const { messageData } = toRefs(props)
//
const ALL_MESSAGE_TYPE = {
TEXT: 'txt',
IMAGE: 'img',
AUDIO: 'audio',
LOCAL: 'loc',
VIDEO: 'video',
FILE: 'file',
CUSTOM: 'custom',
CMD: 'cmd',
INFORM: 'inform' //
}
/* 处理时间显示间隔 */
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')
}
@ -43,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) => {
@ -98,14 +102,14 @@ const startplayAudio = (msgBody) => {
>
<!-- 普通消息气泡 -->
<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"
@ -136,12 +140,6 @@ const startplayAudio = (msgBody) => {
>
<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>
@ -219,62 +217,10 @@ const startplayAudio = (msgBody) => {
</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>

View File

@ -4,6 +4,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'
const { query } = useRoute() //
@ -47,34 +48,70 @@ const messageData = computed(() => [
id: 1,
type: ALL_MESSAGE_TYPE.TEXT,
isRecall: false,
time: '1711944000000',
time: '1711944110000',
from: '1',
msg: 'Hello, world!',
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: '1711944001000',
time: '1711944221000',
from: '2',
msg: 'Hi, there!',
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: true,
time: '1711944002000',
isRecall: false,
time: '1711944332000',
from: '1',
msg: 'Hello, world!',
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
}
])
@ -117,12 +154,7 @@ const messageQuote = (msg) => inputBox.value.handleQuoteMessage(msg)
<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 !== ALL_MESSAGE_TYPE.OA_NOTIFICATION
"
class="load_more_msg"
>
<div v-show="messageData?.length" class="load_more_msg">
<el-link
v-show="!loadingHistoryMsg"
:disabled="!isMoreHistoryMsg"

View File

@ -1,5 +1,5 @@
<script lang="ts" setup>
import { ref, defineAsyncComponent } from 'vue'
import { shallowRef, defineAsyncComponent, DefineComponent } from 'vue'
import NavBar from './NavBar/index.vue'
//
@ -7,7 +7,7 @@ const ConversationComponent = defineAsyncComponent(
() => import('@/views/im/Conversation/index.vue')
)
const currentComponent = ref(ConversationComponent) //
const currentComponent = shallowRef<DefineComponent | null>(ConversationComponent) //
defineOptions({ name: 'IM' })
</script>