Merge branch 'im' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into im
This commit is contained in:
commit
90461a8cdf
@ -1,12 +1,14 @@
|
||||
<template>
|
||||
<view class="flex h-full flex-1">
|
||||
<ToolSection />
|
||||
<Session />
|
||||
<view class="flex w-full flex-col">
|
||||
<ToolSection @menuSelectChange="toolMenuSelectChange" />
|
||||
<Session v-if="bussinessType === MENU_LIST_ENUM.CONVERSATION" />
|
||||
<Friends v-if="bussinessType === MENU_LIST_ENUM.FRIENDS" />
|
||||
<view v-if="bussinessType === MENU_LIST_ENUM.CONVERSATION" class="flex w-full flex-col">
|
||||
<ChatHeader />
|
||||
<ChatMessage />
|
||||
<InputSection />
|
||||
</view>
|
||||
<FriendDetail v-if="bussinessType === MENU_LIST_ENUM.FRIENDS" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
@ -17,14 +19,18 @@
|
||||
|
||||
import ToolSection from '../components/ToolSection/Index.vue'
|
||||
import Session from '../components/Session/Index.vue'
|
||||
import Friends from '../components/Friends/Index.vue'
|
||||
import ChatHeader from '../components/ChatHeader/Index.vue'
|
||||
import ChatMessage from '../components/ChatMessage/Index.vue'
|
||||
import InputSection from '../components/InputSection/index.vue'
|
||||
|
||||
/**
|
||||
* Define Data Structure of this page, then initialize it with reactive object
|
||||
*/
|
||||
interface State {}
|
||||
import FriendDetail from '../components/FriendDetail/Index.vue'
|
||||
import { MENU_LIST_ENUM } from '../types/index.d.ts'
|
||||
|
||||
defineOptions({ name: 'ChatPage' })
|
||||
|
||||
const bussinessType = ref(1)
|
||||
|
||||
const toolMenuSelectChange = (value) => {
|
||||
bussinessType.value = value
|
||||
}
|
||||
</script>
|
||||
|
58
src/views/chat/api/messageApi.ts
Normal file
58
src/views/chat/api/messageApi.ts
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* @Author: dylan.may@qq.com
|
||||
* @Date: 2024-10-16 11:30:31
|
||||
* @Last Modified by: dylan.may@qq.com
|
||||
* @Last Modified time: 2024-10-16 16:01:25
|
||||
*/
|
||||
|
||||
import request from '@/config/axios'
|
||||
import { MessageModelType } from '../types'
|
||||
|
||||
export interface SendMsg {
|
||||
clientMessageId: string
|
||||
receiverId: number
|
||||
conversationType: number
|
||||
contentType: number
|
||||
content: string
|
||||
}
|
||||
|
||||
export interface SessionMsgReq {
|
||||
receiverId: number
|
||||
conversationType: number
|
||||
sendTime: Date
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息接口
|
||||
*/
|
||||
export default class MessageApi {
|
||||
/**
|
||||
* 发送消息
|
||||
* @param data SendMsg
|
||||
* @returns Promise<{ id: number; sendTime: number }>
|
||||
*/
|
||||
static send(data: SendMsg): Promise<{ id: number; sendTime: number }> {
|
||||
return request.post({ url: '/im/message/send', data })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取会话消息
|
||||
* @param data SessionMsgReq
|
||||
* @returns Promise<Array<MessageModelType>>
|
||||
*/
|
||||
static getSessionMsg(params: SessionMsgReq): Promise<Array<MessageModelType>> {
|
||||
return request.get({ url: '/im/message/list', params })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有消息
|
||||
* @param data { sequence: number; size: number }
|
||||
* @returns Promise<Array<MessageModelType>>
|
||||
*/
|
||||
static getMessageForAllSession(params: {
|
||||
sequence: number
|
||||
size: number
|
||||
}): Promise<Array<MessageModelType>> {
|
||||
return request.get({ url: '/im/message/pull', params })
|
||||
}
|
||||
}
|
22
src/views/chat/api/sessionApi.ts
Normal file
22
src/views/chat/api/sessionApi.ts
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* @Author: dylan.may@qq.com
|
||||
* @Date: 2024-10-16 11:30:31
|
||||
* @Last Modified by: dylan.may@qq.com
|
||||
* @Last Modified time: 2024-10-16 16:01:25
|
||||
*/
|
||||
|
||||
import request from '@/config/axios'
|
||||
import { ChatConversation } from '../model/ChatConversation'
|
||||
|
||||
/**
|
||||
* 会话接口
|
||||
*/
|
||||
export default class SessionApi {
|
||||
/**
|
||||
* 获取会话列表
|
||||
* @returns Promise<Array<ChatConversation>>
|
||||
*/
|
||||
static getSessionList(): Promise<Array<ChatConversation>> {
|
||||
return request.get({ url: '/im/conversation/list' })
|
||||
}
|
||||
}
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view
|
||||
class="flex items-center w-full border-b-1 border-b-gray border-b-solid"
|
||||
style="height: 60px"
|
||||
style="height: 60px; min-height: 60px"
|
||||
>
|
||||
<label class="text-black text-size-xl font-medium mx-4">{{
|
||||
chatStore.currentSession?.name
|
||||
|
@ -1,19 +1,27 @@
|
||||
<template>
|
||||
<view
|
||||
class="flex flex-col items-start w-full border-b-1 border-b-gray border-b-solid flex-1 border-b-1 border-b-gray border-b-solid py-2"
|
||||
class="flex flex-col items-start w-full border-b-1 border-b-gray border-b-solid flex-1 border-b-1 border-b-gray border-b-solid py-2 overflow-scroll"
|
||||
>
|
||||
<template v-for="item in chatStore.currentSession?.msgList">
|
||||
<TextMessage v-if="item.messageType === MessageType.TEXT" :key="item.id" :message="item" />
|
||||
<ImageMessage v-if="item.messageType === MessageType.IMAGE" :key="item.id" :message="item" />
|
||||
<TextMsg
|
||||
v-if="item.contentType === ContentType.TEXT"
|
||||
:key="item.clientMessageId"
|
||||
:message="item"
|
||||
/>
|
||||
<ImageMsg
|
||||
v-if="item.contentType === ContentType.IMAGE"
|
||||
:key="item.clientMessageId"
|
||||
:message="item"
|
||||
/>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useChatStore } from '../../store/chatstore'
|
||||
import TextMessage from '@/views/chat/components/Message/TextMessage.vue'
|
||||
import ImageMessage from '@/views/chat/components/Message/ImageMessage.vue'
|
||||
import { MessageType } from '../../types/index.d.ts'
|
||||
import TextMsg from '@/views/chat/components/Message/TextMsg.vue'
|
||||
import ImageMsg from '@/views/chat/components/Message/ImageMsg.vue'
|
||||
import { ContentType } from '../../types/index.d.ts'
|
||||
|
||||
defineOptions({ name: 'ChatMessage' })
|
||||
|
||||
|
29
src/views/chat/components/FriendDetail/Index.vue
Normal file
29
src/views/chat/components/FriendDetail/Index.vue
Normal file
@ -0,0 +1,29 @@
|
||||
<template>
|
||||
<view
|
||||
class="flex justify-center w-full border-b-1 border-b-gray border-b-solid flex-1 border-b-1 border-b-gray border-b-solid py-2"
|
||||
>
|
||||
<view class="flex mt-20" v-if="friendStore.currentFriend != null">
|
||||
<el-image
|
||||
style="width: 8rem; height: 8rem"
|
||||
class="rounded"
|
||||
:src="friendStore.currentFriend.avatar"
|
||||
/>
|
||||
<view class="flex flex-col ml-2">
|
||||
<label class="font-500 text-black font-size-5">{{ friendStore.currentFriend?.name }}</label>
|
||||
<label>{{ friendStore.currentFriend?.description }}</label>
|
||||
</view>
|
||||
</view>
|
||||
<view v-else class="mt-50 flex flex-col items-center">
|
||||
<Icon icon="ep:coffee-cup" :size="64" />
|
||||
<label>空空如也</label>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useFriendStore } from '../../store/friendstore'
|
||||
|
||||
defineOptions({ name: 'FriendDetail' })
|
||||
|
||||
const friendStore = useFriendStore()
|
||||
</script>
|
28
src/views/chat/components/FriendItem/Index.vue
Normal file
28
src/views/chat/components/FriendItem/Index.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<template>
|
||||
<view class="flex py-2 border-b-gray-3 border-b-solid items-center px-2" :class="bgColor()">
|
||||
<el-avatar shape="square" size="default" class="mr-2" :src="friend.avatar" />
|
||||
<label>{{ friend.name }}</label>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue'
|
||||
import { useFriendStore } from '../../store/friendstore'
|
||||
|
||||
import Friend from '../../model/Friend'
|
||||
|
||||
defineOptions({ name: 'FriendItem' })
|
||||
|
||||
const props = defineProps({
|
||||
friend: {
|
||||
type: Object as PropType<Friend>,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
|
||||
const friendStore = useFriendStore()
|
||||
|
||||
const bgColor = () => {
|
||||
return props.friend.id === friendStore.currentFriend?.id ? 'bg-blue' : 'bg-white'
|
||||
}
|
||||
</script>
|
35
src/views/chat/components/Friends/Index.vue
Normal file
35
src/views/chat/components/Friends/Index.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<view
|
||||
class="flex flex-col items-center h-full py-2 b-1 b-gray b-solid"
|
||||
style="width: 248px; min-width: 248px"
|
||||
>
|
||||
<view class="flex flex-col w-full">
|
||||
<FriendItem
|
||||
v-for="(item, index) in friendStore.friendList"
|
||||
:key="item.id"
|
||||
:index="index"
|
||||
:friend="item"
|
||||
@click="() => onFriendClick(item)"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import FriendItem from '../FriendItem/Index.vue'
|
||||
import { useFriendStore } from '../../store/friendstore'
|
||||
import { onMounted } from 'vue'
|
||||
import Friend from '../../model/Friend'
|
||||
|
||||
defineOptions({ name: 'Friends' })
|
||||
|
||||
const friendStore = useFriendStore()
|
||||
onMounted(() => {
|
||||
// set default conversation
|
||||
})
|
||||
|
||||
const onFriendClick = (friend: Friend) => {
|
||||
console.log('====>', friend)
|
||||
friendStore.setCurrentFriend(friend)
|
||||
}
|
||||
</script>
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<view
|
||||
class="flex flex-col items-center w-full border-b-1 border-b-gray border-b-solid"
|
||||
style="height: 248px"
|
||||
style="height: 248px; min-height: 248px"
|
||||
>
|
||||
<view class="flex p-2 w-full" style="height: 20px">
|
||||
<Icon icon="ep:apple" color="var(--top-header-text-color)" class="custom-hover" />
|
||||
@ -23,12 +23,13 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import TextMessage from '../../model/TextMessage'
|
||||
import { useChatStore } from '../../store/chatstore'
|
||||
import { SendStatus, MessageRole, MessageType } from '../../types/index.d.ts'
|
||||
import { useChatStoreWithOut } from '../../store/chatstore'
|
||||
import { CONVERSATION_TYPE } from '../../types/index.d.ts'
|
||||
import { SendStatus, MessageRole, ContentType } from '../../types/index.d.ts'
|
||||
|
||||
defineOptions({ name: 'InputSection' })
|
||||
|
||||
const chatStore = useChatStore()
|
||||
const chatStore = useChatStoreWithOut()
|
||||
const onEnter = () => {
|
||||
console.log('enter pressed')
|
||||
const msg = createTextMessage(chatStore.inputText)
|
||||
@ -38,19 +39,19 @@ const onEnter = () => {
|
||||
|
||||
const createTextMessage = (content: string): TextMessage => {
|
||||
console.log('====>>>>', content)
|
||||
const _localId = `${new Date().getTime()}`
|
||||
// 部分信息从account信息里面获取
|
||||
const msg = new TextMessage(
|
||||
_localId,
|
||||
'https://img0.baidu.com/it/u=1121635512,1294972039&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889',
|
||||
'Dylan May',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
new Date().getTime(),
|
||||
false,
|
||||
content,
|
||||
MessageRole.SELF,
|
||||
SendStatus.SUCCESS,
|
||||
MessageType.TEXT,
|
||||
chatStore.currentSession?.id || ''
|
||||
SendStatus.SENDING,
|
||||
chatStore.currentSession?.id || '',
|
||||
chatStore.currentSession?.targetId,
|
||||
chatStore.currentSession?.type || CONVERSATION_TYPE.SINGLE
|
||||
)
|
||||
|
||||
return msg
|
||||
|
@ -6,14 +6,20 @@
|
||||
<el-avatar shape="square" size="default" class="mx-2" :src="props.message.avatar" />
|
||||
<view class="flex flex-col">
|
||||
<label class="text-xs text-gray-4 mb-1">{{ props.message.nickname }}</label>
|
||||
<slot name="content"></slot>
|
||||
<view class="flex items-center">
|
||||
<el-icon v-if="props.message.sendStatus === SendStatus.SENDING" class="is-loading"
|
||||
><Loading
|
||||
/></el-icon>
|
||||
<slot name="content"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue'
|
||||
import { MessageModelType, MessageRole } from '../../types/index.d.ts'
|
||||
import { Loading } from '@element-plus/icons-vue'
|
||||
import { MessageModelType, MessageRole, SendStatus } from '../../types/index.d.ts'
|
||||
|
||||
defineOptions({ name: 'BaseMessage' })
|
||||
|
@ -13,7 +13,7 @@ import { PropType } from 'vue'
|
||||
import { useChatStore } from '../../store/chatstore'
|
||||
import { onMounted } from 'vue'
|
||||
import { MessageModelType } from '../../types'
|
||||
import BaseMesageLayout from '../Message/BaseMessage.vue'
|
||||
import BaseMesageLayout from './BaseMsg.vue'
|
||||
|
||||
defineOptions({ name: 'ImageMessage' })
|
||||
|
@ -10,18 +10,15 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { PropType } from 'vue'
|
||||
import { useChatStore } from '../../store/chatstore'
|
||||
import BaseMesageLayout from '../Message/BaseMessage.vue'
|
||||
import { MessageModelType } from '../../types/index'
|
||||
import BaseMesageLayout from './BaseMsg.vue'
|
||||
import TextMessage from '../../model/TextMessage'
|
||||
|
||||
defineOptions({ name: 'TextMessage' })
|
||||
|
||||
const props = defineProps({
|
||||
message: {
|
||||
type: Object as PropType<MessageModelType>,
|
||||
type: Object as PropType<TextMessage>,
|
||||
default: () => {}
|
||||
}
|
||||
})
|
||||
|
||||
const { sessionList, setCurrentConversation, setCurrentSessionIndex } = useChatStore()
|
||||
</script>
|
@ -2,7 +2,7 @@
|
||||
<view class="flex flex-col items-center h-full py-2 b-1 b-gray b-solid" style="width: 248px">
|
||||
<view class="flex flex-col w-full">
|
||||
<SessionItem
|
||||
v-for="(item, index) in sessionList"
|
||||
v-for="(item, index) in chatStore.sessionList"
|
||||
:key="item.id"
|
||||
:index="index"
|
||||
:conversation="item"
|
||||
@ -14,16 +14,20 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import SessionItem from '../SessionItem/Index.vue'
|
||||
import { useChatStore } from '../../store/chatstore'
|
||||
import { useChatStoreWithOut } from '../../store/chatstore'
|
||||
import { onMounted } from 'vue'
|
||||
|
||||
defineOptions({ name: 'Session' })
|
||||
|
||||
const { sessionList, setCurrentConversation, setCurrentSessionIndex } = useChatStore()
|
||||
const chatStore = useChatStoreWithOut()
|
||||
const { setCurrentConversation, setCurrentSessionIndex, getSession } = useChatStoreWithOut()
|
||||
|
||||
onMounted(() => {
|
||||
getSession()
|
||||
// set default conversation
|
||||
setCurrentConversation()
|
||||
nextTick(() => {
|
||||
setCurrentConversation()
|
||||
})
|
||||
})
|
||||
|
||||
const onSessionItemClick = (index: number) => {
|
||||
|
@ -1,19 +1,23 @@
|
||||
<template>
|
||||
<view class="flex py-2 border-b-gray-3 border-b-solid items-center px-2" :class="bgColor()">
|
||||
<el-avatar shape="square" size="default" class="mr-2" />
|
||||
<view class="flex flex-col flex-1">
|
||||
<el-avatar shape="square" size="default" class="mr-2">
|
||||
{{ props.conversation.name || '' }}
|
||||
</el-avatar>
|
||||
<view class="flex flex-col flex-1 tems-end h-full">
|
||||
<label
|
||||
class="text-black-c text-size-sm font-medium text-ellipsis text-nowrap"
|
||||
:class="fontColor()"
|
||||
:class="namefontColor()"
|
||||
>{{ props.conversation.name }}</label
|
||||
>
|
||||
<label class="text-gray-f text-size-sm text-ellipsis text-nowrap mr-1" :class="fontColor()">{{
|
||||
props.conversation.description
|
||||
}}</label>
|
||||
<label
|
||||
class="text-gray-f text-size-sm text-ellipsis text-nowrap mr-1"
|
||||
:class="timefontColor()"
|
||||
>{{ props.conversation.description }}</label
|
||||
>
|
||||
</view>
|
||||
<view class="flex items-end h-full">
|
||||
<label class="text-gray-f text-size-sm text-nowrap" :class="fontColor()">{{
|
||||
formatPast(new Date(props.conversation.updateTime))
|
||||
<view class="flex items-end h-full flex-col">
|
||||
<label class="text-gray-f text-size-xs text-nowrap" :class="timefontColor()">{{
|
||||
formatPast(new Date(props.conversation.updateTime), 'YYYY-MM-DD')
|
||||
}}</label>
|
||||
</view>
|
||||
</view>
|
||||
@ -41,7 +45,20 @@ const bgColor = () => {
|
||||
return props.index === chatStore.currentSessionIndex ? 'bg-blue' : 'bg-white'
|
||||
}
|
||||
|
||||
const fontColor = () => {
|
||||
return props.index === chatStore.currentSessionIndex ? 'text-white' : 'text-gray-f'
|
||||
const namefontColor = () => {
|
||||
return props.index === chatStore.currentSessionIndex ? 'text-white' : 'nameColor'
|
||||
}
|
||||
const timefontColor = () => {
|
||||
return props.index === chatStore.currentSessionIndex ? 'text-white' : 'timeColor'
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.timeColor {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.nameColor {
|
||||
color: black;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,9 +1,45 @@
|
||||
<template>
|
||||
<view class="flex flex-col items-center bg-gray h-full py-2" style="width: 80px">
|
||||
<view class="flex flex-col items-center bg-gray h-full py-2" style="width: 80px; min-width: 80px">
|
||||
<el-avatar shape="square" />
|
||||
<icon
|
||||
icon="ep:chat-line-round"
|
||||
:size="24"
|
||||
color="white"
|
||||
class="px-4 py-4 mt-1 rounded-2"
|
||||
:class="selectItem === MENU_LIST_ENUM.CONVERSATION ? 'bg-red' : ''"
|
||||
@click="onConversatonClicked"
|
||||
/>
|
||||
<icon
|
||||
icon="ep:avatar"
|
||||
:size="24"
|
||||
color="white"
|
||||
class="px-4 py-4 rounded-2 mt-2"
|
||||
:class="selectItem === MENU_LIST_ENUM.FRIENDS ? 'bg-red' : ''"
|
||||
@click="onFriendsClicked"
|
||||
/>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { MENU_LIST_ENUM } from '../../types/index.d.ts'
|
||||
|
||||
defineOptions({ name: 'ToolSection' })
|
||||
|
||||
const selectItem = ref(1)
|
||||
|
||||
const emit = defineEmits(['menuSelectChange'])
|
||||
watch(
|
||||
() => selectItem.value,
|
||||
(newValue) => {
|
||||
emit('menuSelectChange', newValue)
|
||||
}
|
||||
)
|
||||
|
||||
const onConversatonClicked = () => {
|
||||
selectItem.value = MENU_LIST_ENUM.CONVERSATION
|
||||
}
|
||||
|
||||
const onFriendsClicked = () => {
|
||||
selectItem.value = MENU_LIST_ENUM.FRIENDS
|
||||
}
|
||||
</script>
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { MessageModelType } from '../types'
|
||||
import { ConversationType, MessageModelType } from '../types'
|
||||
|
||||
export default class BaseConversation {
|
||||
public id: string
|
||||
@ -9,7 +9,8 @@ export default class BaseConversation {
|
||||
public updateTime: number
|
||||
public unreadCount: number
|
||||
public msgList: Array<MessageModelType>
|
||||
|
||||
public type: ConversationType
|
||||
public targetId: number
|
||||
constructor(
|
||||
id: string,
|
||||
avatar: string,
|
||||
@ -18,7 +19,9 @@ export default class BaseConversation {
|
||||
createTime: number,
|
||||
updateTime: number,
|
||||
unreadCount: number,
|
||||
msgList: Array<MessageModelType>
|
||||
msgList: Array<MessageModelType>,
|
||||
type: ConversationType,
|
||||
targetId: number
|
||||
) {
|
||||
this.id = id
|
||||
this.avatar = avatar
|
||||
@ -28,5 +31,7 @@ export default class BaseConversation {
|
||||
this.updateTime = updateTime
|
||||
this.unreadCount = unreadCount
|
||||
this.msgList = msgList
|
||||
this.type = type
|
||||
this.targetId = targetId
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,18 @@
|
||||
import { MessageRole, MessageType, SendStatus } from '../types'
|
||||
import { MessageRole, ContentType, SendStatus } from '../types'
|
||||
|
||||
export default class BaseMessage {
|
||||
id: string
|
||||
avatar: string
|
||||
nickname: string
|
||||
id?: string
|
||||
avatar?: string
|
||||
nickname?: string
|
||||
createTime: number
|
||||
isRead: boolean
|
||||
role: MessageRole
|
||||
sendStatus: SendStatus
|
||||
messageType: MessageType
|
||||
contentType: ContentType
|
||||
conversationId: string
|
||||
|
||||
clientMessageId: string
|
||||
receiverId: number
|
||||
conversationType: number
|
||||
constructor(
|
||||
id: string,
|
||||
avatar: string,
|
||||
@ -19,8 +21,10 @@ export default class BaseMessage {
|
||||
isRead: boolean,
|
||||
role: MessageRole,
|
||||
sendStauts: SendStatus,
|
||||
messageType: MessageType,
|
||||
conversationId: string
|
||||
contentType: ContentType,
|
||||
conversationId: string,
|
||||
receiverId: number,
|
||||
conversationType: number
|
||||
) {
|
||||
this.id = id
|
||||
this.avatar = avatar
|
||||
@ -29,7 +33,21 @@ export default class BaseMessage {
|
||||
this.isRead = isRead
|
||||
this.role = role
|
||||
this.sendStatus = sendStauts
|
||||
this.messageType = messageType
|
||||
this.contentType = contentType
|
||||
this.conversationId = conversationId
|
||||
this.receiverId = receiverId
|
||||
this.clientMessageId = this.generateClientMessageId()
|
||||
this.conversationType = conversationType
|
||||
}
|
||||
|
||||
private generateClientMessageId() {
|
||||
const timestamp = Date.now().toString() // 获取当前时间戳
|
||||
const randomPart = 'xxxx-xxxx-4xxx-yxxx-xxxx'.replace(/[xy]/g, function (c) {
|
||||
const r = (Math.random() * 16) | 0,
|
||||
v = c === 'x' ? r : (r & 0x3) | 0x8
|
||||
return v.toString(16)
|
||||
})
|
||||
|
||||
return `${timestamp}-${randomPart}`
|
||||
}
|
||||
}
|
||||
|
5
src/views/chat/model/BaseResponse.ts
Normal file
5
src/views/chat/model/BaseResponse.ts
Normal file
@ -0,0 +1,5 @@
|
||||
export interface BaseResponse<T> {
|
||||
code: number // 0表示成功其他表示失败
|
||||
message: string // 返回的信息,可以是成功或错误信息
|
||||
data: T // 泛型数据,成功时返回数据,失败时为 null
|
||||
}
|
@ -10,8 +10,21 @@ export class ChatConversation extends BaseConversation {
|
||||
createTime: number,
|
||||
updateTime: number,
|
||||
unreadCount: number,
|
||||
msgList: Array<BaseMessage>
|
||||
msgList: Array<BaseMessage>,
|
||||
type: number,
|
||||
targetId: number
|
||||
) {
|
||||
super(id, avatar, name, descrition, createTime, updateTime, unreadCount, msgList)
|
||||
super(
|
||||
id,
|
||||
avatar,
|
||||
name,
|
||||
descrition,
|
||||
createTime,
|
||||
updateTime,
|
||||
unreadCount,
|
||||
msgList,
|
||||
type,
|
||||
targetId
|
||||
)
|
||||
}
|
||||
}
|
||||
|
15
src/views/chat/model/Friend.ts
Normal file
15
src/views/chat/model/Friend.ts
Normal file
@ -0,0 +1,15 @@
|
||||
export default class Friend {
|
||||
public id: string
|
||||
public avatar: string
|
||||
public name: string
|
||||
public description: string
|
||||
public createTime: number
|
||||
|
||||
constructor(id, avatar, name, description, createTime) {
|
||||
this.id = id
|
||||
this.avatar = avatar
|
||||
this.name = name
|
||||
this.description = description
|
||||
this.createTime = createTime
|
||||
}
|
||||
}
|
@ -1,4 +1,4 @@
|
||||
import { MessageRole, MessageType, SendStatus } from '../types'
|
||||
import { MessageRole, ContentType, SendStatus } from '@/views/chat/types/index.d.ts'
|
||||
import BaseMessage from './BaseMessage'
|
||||
|
||||
export default class ImageMessage extends BaseMessage {
|
||||
@ -13,10 +13,23 @@ export default class ImageMessage extends BaseMessage {
|
||||
content: string,
|
||||
role: MessageRole,
|
||||
sendStatus: SendStatus,
|
||||
messageType: MessageType,
|
||||
conversationId: string
|
||||
conversationId: string,
|
||||
receiverId: number,
|
||||
conversationType: number
|
||||
) {
|
||||
super(id, avatar, nickname, createTime, isRead, role, sendStatus, messageType, conversationId)
|
||||
super(
|
||||
id,
|
||||
avatar,
|
||||
nickname,
|
||||
createTime,
|
||||
isRead,
|
||||
role,
|
||||
sendStatus,
|
||||
ContentType.IMAGE,
|
||||
conversationId,
|
||||
receiverId,
|
||||
conversationType
|
||||
)
|
||||
this.content = content
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { MessageRole, MessageType, SendStatus } from '../types'
|
||||
import { MessageRole, ContentType, SendStatus } from '@/views/chat/types/index.d.ts'
|
||||
import BaseMessage from './BaseMessage'
|
||||
|
||||
export default class TextMessage extends BaseMessage {
|
||||
@ -13,10 +13,23 @@ export default class TextMessage extends BaseMessage {
|
||||
content: string,
|
||||
role: MessageRole,
|
||||
sendStatus: SendStatus,
|
||||
messageType: MessageType,
|
||||
conversationId: string
|
||||
conversationId: string,
|
||||
receiverId: number,
|
||||
conversationType: number
|
||||
) {
|
||||
super(id, avatar, nickname, createTime, isRead, role, sendStatus, messageType, conversationId)
|
||||
super(
|
||||
id,
|
||||
avatar,
|
||||
nickname,
|
||||
createTime,
|
||||
isRead,
|
||||
role,
|
||||
sendStatus,
|
||||
ContentType.TEXT,
|
||||
conversationId,
|
||||
receiverId,
|
||||
conversationType
|
||||
)
|
||||
this.content = content
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,11 @@
|
||||
import { store } from '@/store/index'
|
||||
import { defineStore } from 'pinia'
|
||||
import BaseConversation from '../model/BaseConversation'
|
||||
import BaseMessage from '../model/BaseMessage'
|
||||
import { ConversationModelType, MessageRole, MessageType, SendStatus } from '../types/index.d.ts'
|
||||
import { ConversationModelType, MessageRole, ContentType, SendStatus } from '../types/index.d.ts'
|
||||
import SessionApi from '../api/sessionApi'
|
||||
import MessageApi, { SendMsg } from '../api/messageApi'
|
||||
import { useUserStoreWithOut } from '@/store/modules/user'
|
||||
|
||||
interface ChatStoreModel {
|
||||
sessionList: Array<ConversationModelType>
|
||||
@ -12,71 +16,7 @@ interface ChatStoreModel {
|
||||
|
||||
export const useChatStore = defineStore('chatStore', {
|
||||
state: (): ChatStoreModel => ({
|
||||
sessionList: [
|
||||
{
|
||||
id: '11111',
|
||||
name: '张三',
|
||||
avatar:
|
||||
'https://img.zcool.cn/community/019fb65925bc32a801216a3ef77f7b.png@1280w_1l_2o_100sh.png',
|
||||
description: 'sss',
|
||||
createTime: 1693970987760,
|
||||
updateTime: 1693970987760,
|
||||
unreadCount: 1,
|
||||
msgList: [
|
||||
{
|
||||
avatar:
|
||||
'https://img0.baidu.com/it/u=1121635512,1294972039&fm=253&fmt=auto&app=138&f=JPEG?w=500&h=889',
|
||||
nickname: 'Dylan May',
|
||||
id: '222221111',
|
||||
isRead: false,
|
||||
messageType: MessageType.TEXT,
|
||||
sendStatus: SendStatus.SUCCESS,
|
||||
role: MessageRole.SELF,
|
||||
createTime: 1693970987760,
|
||||
conversationId: '11111',
|
||||
content: 'hello MUSK'
|
||||
},
|
||||
{
|
||||
avatar:
|
||||
'https://img0.baidu.com/it/u=4211304696,1059959254&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=1174',
|
||||
nickname: 'Elon Musk',
|
||||
id: '2222222222',
|
||||
isRead: false,
|
||||
messageType: MessageType.TEXT,
|
||||
sendStatus: SendStatus.SUCCESS,
|
||||
role: MessageRole.OTHER,
|
||||
createTime: 1693970987760,
|
||||
conversationId: '11111',
|
||||
content: 'hello DYLAN'
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '22222',
|
||||
name: '搞笑的一家人',
|
||||
avatar:
|
||||
'https://img.zcool.cn/community/019fb65925bc32a801216a3ef77f7b.png@1280w_1l_2o_100sh.png',
|
||||
description: '今天晚上吃啥',
|
||||
createTime: 1693970987760,
|
||||
updateTime: 1693970987760,
|
||||
unreadCount: 1,
|
||||
msgList: [
|
||||
{
|
||||
avatar:
|
||||
'https://img.zcool.cn/community/019fb65925bc32a801216a3ef77f7b.png@1280w_1l_2o_100sh.png',
|
||||
nickname: '小艳',
|
||||
id: '22222',
|
||||
isRead: false,
|
||||
messageType: MessageType.TEXT,
|
||||
sendStatus: SendStatus.SUCCESS,
|
||||
role: MessageRole.OTHER,
|
||||
createTime: 1693970987760,
|
||||
conversationId: '22222',
|
||||
content: 'what your name'
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
sessionList: reactive<Array<ConversationModelType>>([]),
|
||||
currentSession: null,
|
||||
currentSessionIndex: 0,
|
||||
inputText: ''
|
||||
@ -103,6 +43,7 @@ export const useChatStore = defineStore('chatStore', {
|
||||
|
||||
setCurrentConversation() {
|
||||
this.currentSession = this.sessionList[this.currentSessionIndex]
|
||||
this.fetchSessionMsg()
|
||||
},
|
||||
|
||||
setCurrentSessionIndex(index: number) {
|
||||
@ -113,8 +54,32 @@ export const useChatStore = defineStore('chatStore', {
|
||||
this.inputText = content
|
||||
},
|
||||
|
||||
addMessageToCurrentSession<T extends BaseMessage>(message: T): void {
|
||||
async addMessageToCurrentSession<T extends BaseMessage>(message: T): Promise<void> {
|
||||
this.currentSession?.msgList.push(message)
|
||||
|
||||
try {
|
||||
const res = await MessageApi.send(message as unknown as SendMsg)
|
||||
console.log(res)
|
||||
if (res.id) {
|
||||
// 更新发送状态
|
||||
const updateMsg = {
|
||||
...message,
|
||||
id: res.id,
|
||||
sendTime: res.sendTime,
|
||||
sendStatus: SendStatus.SUCCESS
|
||||
}
|
||||
|
||||
this.updateMsgToCurrentSession(updateMsg)
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
const updateMsg = {
|
||||
...message,
|
||||
sendStatus: SendStatus.SUCCESS
|
||||
}
|
||||
|
||||
this.updateMsgToCurrentSession(updateMsg)
|
||||
}
|
||||
},
|
||||
|
||||
addMessageToSesstion<T extends BaseMessage>(message: T): void {
|
||||
@ -130,6 +95,71 @@ export const useChatStore = defineStore('chatStore', {
|
||||
|
||||
// replace the old Conversation
|
||||
this.sessionList.splice(conversationIndex, 1, msgConversation)
|
||||
},
|
||||
|
||||
/**
|
||||
* 更新消息到当前会话
|
||||
* @param updatedMsg
|
||||
*/
|
||||
updateMsgToCurrentSession<T extends BaseMessage>(updatedMsg: T): void {
|
||||
if (this.currentSession) {
|
||||
this.currentSession.msgList = this.currentSession?.msgList.map((item) => {
|
||||
if (item.clientMessageId === updatedMsg.clientMessageId) {
|
||||
return updatedMsg
|
||||
} else {
|
||||
return item
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
async getSession() {
|
||||
try {
|
||||
const res = await SessionApi.getSessionList()
|
||||
this.sessionList = res.map((item) => ({
|
||||
...item,
|
||||
updateTime: item.lastReadTime,
|
||||
name: item.targetId,
|
||||
targetId: item.targetId,
|
||||
msgList: []
|
||||
}))
|
||||
} catch (error) {
|
||||
return error
|
||||
}
|
||||
},
|
||||
|
||||
async fetchSessionMsg() {
|
||||
if (!this.currentSession) {
|
||||
return
|
||||
}
|
||||
|
||||
const receiverId = this.currentSession.targetId
|
||||
const type = this.currentSession.type
|
||||
|
||||
try {
|
||||
const res = await MessageApi.getSessionMsg({
|
||||
receiverId: receiverId,
|
||||
conversationType: type,
|
||||
sendTime: new Date()
|
||||
})
|
||||
|
||||
const userStore = useUserStoreWithOut()
|
||||
|
||||
this.currentSession.msgList = res.map((item) => {
|
||||
return {
|
||||
...item,
|
||||
role: item.senderId === userStore.user.id ? MessageRole.SELF : MessageRole.OTHER,
|
||||
nickname: item.senderNickname,
|
||||
avatar: item.senderAvatar
|
||||
}
|
||||
})
|
||||
} catch (error) {
|
||||
return error
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
export const useChatStoreWithOut = () => {
|
||||
return useChatStore(store)
|
||||
}
|
||||
|
47
src/views/chat/store/friendstore.ts
Normal file
47
src/views/chat/store/friendstore.ts
Normal file
@ -0,0 +1,47 @@
|
||||
import { defineStore } from 'pinia'
|
||||
import BaseConversation from '../model/BaseConversation'
|
||||
import Friend from '../model/Friend'
|
||||
|
||||
interface FriendStoreModel {
|
||||
friendList: Array<Friend>
|
||||
currentFriend: Friend | null
|
||||
}
|
||||
|
||||
export const useFriendStore = defineStore('friendStore', {
|
||||
state: (): FriendStoreModel => ({
|
||||
friendList: [
|
||||
{
|
||||
id: '1111',
|
||||
name: 'Elon Musk',
|
||||
avatar:
|
||||
'https://img0.baidu.com/it/u=4211304696,1059959254&fm=253&fmt=auto&app=120&f=JPEG?w=800&h=1174',
|
||||
description: 'cool boy',
|
||||
createTime: 1695201147622
|
||||
},
|
||||
{
|
||||
id: '2222',
|
||||
name: 'Spider Man',
|
||||
avatar:
|
||||
'https://www.hottoys.com.cn/wp-content/uploads/2019/06/bloggerreview_spiderman_advanced_ben-9.jpg',
|
||||
description: 'hero',
|
||||
createTime: 1695201147622
|
||||
}
|
||||
],
|
||||
currentFriend: null
|
||||
}),
|
||||
|
||||
getters: {
|
||||
getFriendList(state: FriendStoreModel): Array<Friend> {
|
||||
return state.friendList
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
addFriend(session: BaseConversation) {
|
||||
this.friendList.push(session)
|
||||
},
|
||||
setCurrentFriend(friend: Friend) {
|
||||
this.currentFriend = friend
|
||||
}
|
||||
}
|
||||
})
|
22
src/views/chat/types/index.d.ts
vendored
22
src/views/chat/types/index.d.ts
vendored
@ -16,12 +16,24 @@ export enum SendStatus {
|
||||
SUCCESS = 3
|
||||
}
|
||||
|
||||
export enum MessageType {
|
||||
TEXT = 1,
|
||||
IMAGE = 2,
|
||||
AUDIO = 3,
|
||||
SYSTEM = 4
|
||||
export enum ContentType {
|
||||
TEXT = 101,
|
||||
IMAGE = 102,
|
||||
AUDIO = 103,
|
||||
SYSTEM = 1400
|
||||
}
|
||||
|
||||
export const enum MENU_LIST_ENUM {
|
||||
CONVERSATION = 1,
|
||||
FRIENDS = 2
|
||||
}
|
||||
|
||||
export const enum CONVERSATION_TYPE {
|
||||
SINGLE = 1,
|
||||
GROUP = 3,
|
||||
NOTIFICATION = 4
|
||||
}
|
||||
|
||||
export type MessageModelType = BaseMessage | TextMessage | ImageMessage
|
||||
export type ConversationModelType = BaseConversation | ChatConversation
|
||||
export type ConversationType = CONVERSATION_TYPE
|
||||
|
Loading…
Reference in New Issue
Block a user