feat: 联系人
parent
e5b90372a6
commit
5c1bb25237
|
@ -1,7 +1,7 @@
|
|||
import request from '@/config/axios'
|
||||
|
||||
export interface DeptVO {
|
||||
id?: number
|
||||
id: number
|
||||
name: string
|
||||
parentId: number
|
||||
status: number
|
||||
|
@ -10,6 +10,7 @@ export interface DeptVO {
|
|||
phone: string
|
||||
email: string
|
||||
createTime: Date
|
||||
children?: DeptVO[]
|
||||
}
|
||||
|
||||
// 查询部门(精简)列表
|
||||
|
|
|
@ -27,6 +27,15 @@ export const getAllUser = () => {
|
|||
return request.get({ url: '/system/user/all' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取部门成员
|
||||
* @param id
|
||||
* @returns
|
||||
*/
|
||||
export const getDeptUser = (id: number) => {
|
||||
return request.get({ url: '/system/user/listByDept?id='+ id })
|
||||
}
|
||||
|
||||
// 查询用户详情
|
||||
export const getUser = (id: number) => {
|
||||
return request.get({ url: '/system/user/get?id=' + id })
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
|
||||
import { ConversationModelType } from '@/views/chat/types/types'
|
||||
import { openDB, DBSchema, IDBPDatabase } from 'idb'
|
||||
|
||||
|
@ -14,7 +15,9 @@ let dbPromise: Promise<IDBPDatabase<MyDB>>
|
|||
export const initDB = () => {
|
||||
if (!dbPromise) {
|
||||
try {
|
||||
dbPromise = openDB<MyDB>('yudao-im-indexeddb', 1, {
|
||||
const { wsCache } = useCache()
|
||||
const user = wsCache.get(CACHE_KEY.USER).user
|
||||
dbPromise = openDB<MyDB>('yudao-im-indexeddb-' + user.id, 1, {
|
||||
upgrade(db) {
|
||||
db.createObjectStore('Conversations', { keyPath: 'conversationNo' })
|
||||
}
|
||||
|
|
|
@ -1,14 +1,17 @@
|
|||
<template>
|
||||
<view class="flex h-full flex-1">
|
||||
<ToolSection @menu-select-change="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">
|
||||
<Session v-if="chatStore.bussinessType === MENU_LIST_ENUM.CONVERSATION" />
|
||||
<view class="flex">
|
||||
<Department v-if="chatStore.bussinessType === MENU_LIST_ENUM.FRIENDS" />
|
||||
<Friends v-if="chatStore.bussinessType === MENU_LIST_ENUM.FRIENDS" />
|
||||
</view>
|
||||
<view v-if="chatStore.bussinessType === MENU_LIST_ENUM.CONVERSATION" class="flex w-full flex-col">
|
||||
<ChatHeader />
|
||||
<ChatMessage />
|
||||
<InputSection />
|
||||
</view>
|
||||
<FriendDetail v-if="bussinessType === MENU_LIST_ENUM.FRIENDS" />
|
||||
<FriendDetail v-if="chatStore.bussinessType === MENU_LIST_ENUM.FRIENDS && useFriendStore.currentFriend" />
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
@ -20,22 +23,35 @@
|
|||
import ToolSection from '../components/ToolSection/Index.vue'
|
||||
import Session from '../components/Conversation/index.vue'
|
||||
import Friends from '../components/Friends/Index.vue'
|
||||
import Department from '../components/Department/index.vue'
|
||||
import ChatHeader from '../components/ChatHeader/index.vue'
|
||||
import ChatMessage from '../components/ChatMessage/index.vue'
|
||||
import InputSection from '../components/InputSection/index.vue'
|
||||
import FriendDetail from '../components/FriendDetail/Index.vue'
|
||||
import { MENU_LIST_ENUM } from '../types/types'
|
||||
import { useWebSocketStore } from '../store/websocketStore'
|
||||
import { useFriendStoreWithOut } from '../store/friendstore'
|
||||
import { useChatStore } from '../store/chatstore'
|
||||
|
||||
defineOptions({ name: 'ChatPage' })
|
||||
|
||||
const bussinessType = ref(1)
|
||||
const webSocketStore = useWebSocketStore();
|
||||
const useFriendStore = useFriendStoreWithOut()
|
||||
const { resetFriendList } = useFriendStore
|
||||
const chatStore = useChatStore()
|
||||
const { setBussinessType } = useChatStore()
|
||||
|
||||
onMounted(() => {
|
||||
webSocketStore.connect()
|
||||
})
|
||||
|
||||
watch(() => chatStore.bussinessType, (newVal) => {
|
||||
if (newVal !== MENU_LIST_ENUM.FRIENDS) {
|
||||
resetFriendList()
|
||||
}
|
||||
})
|
||||
|
||||
const toolMenuSelectChange = (value) => {
|
||||
bussinessType.value = value
|
||||
setBussinessType(value)
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -2,12 +2,17 @@
|
|||
* @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
|
||||
* @Last Modified time: 2024-11-28 17:32:26
|
||||
*/
|
||||
|
||||
import request from '@/config/axios'
|
||||
import { ChatConversation } from '../model/ChatConversation'
|
||||
|
||||
interface createConversationParam {
|
||||
targetId: string,
|
||||
type: number
|
||||
}
|
||||
|
||||
/**
|
||||
* 会话接口
|
||||
*/
|
||||
|
@ -19,4 +24,17 @@ export default class SessionApi {
|
|||
static getSessionList(): Promise<Array<ChatConversation>> {
|
||||
return request.get({ url: '/im/conversation/list' })
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 创建会话
|
||||
* @param data createConversationParam
|
||||
* @returns Promise<ChatConversation>
|
||||
*/
|
||||
static createConversation(data: createConversationParam):Promise<ChatConversation> {
|
||||
return request.post({
|
||||
url: '/im/conversation/create',
|
||||
data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
style="height: 60px; min-height: 60px"
|
||||
>
|
||||
<label class="text-black text-size-xl font-medium mx-4">{{
|
||||
chatStore.currentSession?.name
|
||||
chatStore.currentSession?.nickname || chatStore.currentSession?.name
|
||||
}}</label>
|
||||
</view>
|
||||
</template>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<template>
|
||||
<view class="flex flex-col items-center h-full py-2 b-1 b-gray b-solid" style="width: 258px">
|
||||
<view class="flex flex-col items-center h-full py-2 b-1 b-gray b-solid overflow-auto" style="width: 258px">
|
||||
<view class="flex flex-col w-full">
|
||||
<SessionItem
|
||||
v-for="(item, index) in chatStore.sessionList"
|
||||
|
|
|
@ -53,8 +53,8 @@ const timefontColor = () => {
|
|||
*/
|
||||
const lastMessage = computed(() => {
|
||||
|
||||
if (props.conversation.msgList.length === 0) {
|
||||
return props.conversation.lastMessageDescription
|
||||
if (!props.conversation.msgList || props.conversation.msgList.length === 0) {
|
||||
return props.conversation.lastMessageDescription || ''
|
||||
}
|
||||
|
||||
const lastIndex = props.conversation.msgList.length - 1
|
||||
|
|
|
@ -0,0 +1,53 @@
|
|||
<template>
|
||||
<div class="tree-node" >
|
||||
<div class="node-title custom-hover" @click.stop="onClick">
|
||||
<ElAvatar class="m-2" shape="square" v-if="node.children.length > 0">{{ node.name.substring(0,1) }}</ElAvatar>
|
||||
<span class="mx-1 p-1">{{ node.name }}</span>
|
||||
</div>
|
||||
<div v-if="node.children && node.children.length > 0" class="children">
|
||||
<TreeNode
|
||||
v-for="child in node.children"
|
||||
:key="child.id"
|
||||
:node="child"
|
||||
@node-click="$emit('node-click', $event)"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import TreeNode from './TreeNode.vue';
|
||||
|
||||
// 传递 `node` 数据作为属性
|
||||
const props = defineProps({
|
||||
node: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['node-click'])
|
||||
|
||||
const onClick = () => {
|
||||
emit('node-click', props.node)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tree-node {
|
||||
padding-left: 8px;
|
||||
margin-left: 16px;
|
||||
border-left: 1px solid #ccc;
|
||||
}
|
||||
|
||||
.node-title {
|
||||
margin: 4px 0;
|
||||
font-weight: 500;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.children {
|
||||
margin-top: 4px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,28 @@
|
|||
<template>
|
||||
<div class="tree">
|
||||
<TreeNode v-for="item in hierarchy" :key="item.id" :node="item" @node-click="$emit('tree-click', $event)"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import TreeNode from './TreeNode.vue';
|
||||
|
||||
// 传递 `hierarchy` 数据作为组件属性
|
||||
defineProps({
|
||||
hierarchy: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['tree-click'])
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.tree {
|
||||
font-family: Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,63 @@
|
|||
<template>
|
||||
<view class="flex flex-col items-left h-full py-2 b-1 b-gray b-solid" style="width: 248px; min-width: 248px">
|
||||
<!-- <view v-for="item in departListState.list" class="w-full justify-left custom-hover border-b-gray border-1" :key="item.id" style="height: 70px;">
|
||||
<ElAvatar shape="square">{{ item.name.substring(0,1) }}</ElAvatar>
|
||||
<view class="text-size-sm ml-1">{{ item.name }}</view>
|
||||
</view> -->
|
||||
|
||||
<el-skeleton animated :loading="state.loading" :throttle="{ leading: 500, initVal: true, trailing: 500 }"
|
||||
>
|
||||
<template #template>
|
||||
<div v-for="item in 12" :key="item" class="flex flex-1 mx-2 my-3">
|
||||
<el-skeleton-item animated variant="rect" style="width: 50px; height: 50px; max-width: 50px;" />
|
||||
<div class="mx-2 flex flex-1 flex-col mt-2">
|
||||
<el-skeleton-item animated variant="rect" style="height: 10px;" />
|
||||
<el-skeleton-item animated variant="rect" style="height: 10px;" class="mt-3" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<template #default>
|
||||
<div class="w-full h-full">
|
||||
<TreeView :hierarchy="useFriendStore.departmentList" @tree-click="handleNodeClick" />
|
||||
</div>
|
||||
</template>
|
||||
</el-skeleton>
|
||||
|
||||
<view
|
||||
v-if="useFriendStore.departmentList.length === 0 && !state.loading"
|
||||
class="flex justify-center items-center h-full">No data</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted } from 'vue'
|
||||
import TreeView from './components/TreeView.vue'
|
||||
import { useFriendStoreWithOut } from '../../store/friendstore';
|
||||
|
||||
|
||||
defineOptions({ name: 'Department' })
|
||||
const useFriendStore = useFriendStoreWithOut()
|
||||
const { fetchDepartment, setCurrentDepartmentId, fetchDeptUser } = useFriendStore
|
||||
|
||||
const state = reactive({
|
||||
loading: true
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
fetchDepartment()
|
||||
})
|
||||
|
||||
watch(() => useFriendStore.departmentList, () => {
|
||||
setTimeout(() => {
|
||||
state.loading = false
|
||||
}, 1000);
|
||||
})
|
||||
|
||||
const handleNodeClick = (data: Tree) => {
|
||||
setCurrentDepartmentId(data.id)
|
||||
fetchDeptUser(data.id)
|
||||
}
|
||||
|
||||
|
||||
|
||||
</script>
|
|
@ -1,6 +1,6 @@
|
|||
<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"
|
||||
class="flex flex-col items-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
|
||||
|
@ -8,22 +8,34 @@
|
|||
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 class="flex flex-col ml-4 mt-10">
|
||||
<label class="font-500 text-black font-size-5">{{ friendStore.currentFriend?.name || '无名' }}</label>
|
||||
<label class="mt-2 text-size-sm">{{ 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>
|
||||
<el-button type="primary" class="mt-10" v-if="friendStore.currentFriend != null" @click="onSend"> 发送消息</el-button>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useChatStore } from '../../store/chatstore';
|
||||
import { useFriendStore } from '../../store/friendstore'
|
||||
import { CONVERSATION_TYPE } from '../../types/types';
|
||||
|
||||
defineOptions({ name: 'FriendDetail' })
|
||||
|
||||
const friendStore = useFriendStore()
|
||||
const chatStore = useChatStore()
|
||||
|
||||
const onSend = () => {
|
||||
const avatar = friendStore.currentFriend?.avatar || ''
|
||||
const nickname = friendStore.currentFriend?.name || ''
|
||||
chatStore.createConversation(friendStore.currentFriend?.id, CONVERSATION_TYPE.SINGLE, avatar, nickname)
|
||||
}
|
||||
|
||||
</script>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<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>
|
||||
<label :class="fontColor()">{{ friend.name }}</label>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
|
@ -25,4 +25,8 @@ const friendStore = useFriendStore()
|
|||
const bgColor = () => {
|
||||
return props.friend.id === friendStore.currentFriend?.id ? 'bg-blue' : 'bg-white'
|
||||
}
|
||||
|
||||
const fontColor = () => {
|
||||
return props.friend.id === friendStore.currentFriend?.id ? 'text-white' : 'text-black'
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<view
|
||||
class="flex flex-col items-center h-full py-2 b-1 b-gray b-solid"
|
||||
class="flex flex-col items-center h-full py-2 b-1 b-gray b-solid b-l-white"
|
||||
style="width: 248px; min-width: 248px"
|
||||
>
|
||||
<view class="flex flex-col w-full">
|
||||
|
@ -24,8 +24,9 @@ import Friend from '../../model/Friend'
|
|||
defineOptions({ name: 'Friends' })
|
||||
|
||||
const friendStore = useFriendStore()
|
||||
onMounted(() => {
|
||||
onMounted(async () => {
|
||||
// set default conversation
|
||||
// await friendStore.fetchFriend()
|
||||
})
|
||||
|
||||
const onFriendClick = (friend: Friend) => {
|
||||
|
|
|
@ -1,31 +1,35 @@
|
|||
<template>
|
||||
<view class="flex flex-col items-center bg-gray h-full py-2" style="width: 80px; min-width: 80px">
|
||||
<view class="flex flex-col items-center bg-gray-2 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"
|
||||
/>
|
||||
|
||||
<div
|
||||
class="flex flex-col items-center px-3 py-3 mt-4 rounded-2 hover:bg-white"
|
||||
:class="chatStore.bussinessType === MENU_LIST_ENUM.CONVERSATION ? 'bg-gray-3' : ''" style="width: 60px;"
|
||||
@click="onConversatonClicked">
|
||||
<icon icon="ep:chat-line-round" :size="24" color="#409EFF" />
|
||||
<span class="text-xs mt-1 text-gray-5">会 话</span>
|
||||
<span></span>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="flex flex-col items-center rounded-2 mt-4 p-3 hover:bg-white"
|
||||
:class="chatStore.bussinessType === MENU_LIST_ENUM.FRIENDS ? 'bg-gray-3' : ''" style="width: 60px;" @click="onFriendsClicked">
|
||||
<icon icon="ep:avatar" :size="24" color="#409EFF" />
|
||||
<span class="text-xs mt-1 text-gray-5">联系人</span>
|
||||
</div>
|
||||
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { useChatStore } from '../../store/chatstore';
|
||||
import { MENU_LIST_ENUM } from '../../types/types'
|
||||
|
||||
defineOptions({ name: 'ToolSection' })
|
||||
|
||||
const selectItem = ref(1)
|
||||
const chatStore = useChatStore()
|
||||
const { setBussinessType } = useChatStore()
|
||||
|
||||
const emit = defineEmits(['menuSelectChange'])
|
||||
watch(
|
||||
|
@ -36,10 +40,10 @@ watch(
|
|||
)
|
||||
|
||||
const onConversatonClicked = () => {
|
||||
selectItem.value = MENU_LIST_ENUM.CONVERSATION
|
||||
setBussinessType(MENU_LIST_ENUM.CONVERSATION)
|
||||
}
|
||||
|
||||
const onFriendsClicked = () => {
|
||||
selectItem.value = MENU_LIST_ENUM.FRIENDS
|
||||
setBussinessType(MENU_LIST_ENUM.FRIENDS)
|
||||
}
|
||||
</script>
|
||||
|
|
|
@ -4,12 +4,16 @@ export default class Friend {
|
|||
public name: string
|
||||
public description: string
|
||||
public createTime: number
|
||||
public deptId: number
|
||||
public deptName: string
|
||||
|
||||
constructor(id, avatar, name, description, createTime) {
|
||||
constructor(id, avatar, name, description, createTime, deptId, deptName) {
|
||||
this.id = id
|
||||
this.avatar = avatar
|
||||
this.name = name
|
||||
this.description = description
|
||||
this.createTime = createTime
|
||||
this.deptId = deptId
|
||||
this.deptName = deptName
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,7 +2,7 @@ import { store } from '@/store/index'
|
|||
import { defineStore } from 'pinia'
|
||||
import BaseConversation from '../model/BaseConversation'
|
||||
import BaseMessage from '../model/BaseMessage'
|
||||
import { ConversationModelType, MessageRole, ContentType, SendStatus } from '../types/types'
|
||||
import { ConversationModelType, MessageRole, ContentType, SendStatus, MENU_LIST_ENUM } from '../types/types'
|
||||
import SessionApi from '../api/sessionApi'
|
||||
import MessageApi, { SendMsg } from '../api/messageApi'
|
||||
import { useUserStore, useUserStoreWithOut } from '@/store/modules/user'
|
||||
|
@ -16,7 +16,8 @@ interface ChatStoreModel {
|
|||
sessionList: Array<ConversationModelType>
|
||||
currentSession: ConversationModelType | null
|
||||
currentSessionIndex: number
|
||||
inputText: string
|
||||
inputText: string,
|
||||
bussinessType: number // conversation 1, friends 2
|
||||
}
|
||||
|
||||
export const useChatStore = defineStore('chatStore', {
|
||||
|
@ -24,7 +25,8 @@ export const useChatStore = defineStore('chatStore', {
|
|||
sessionList: [],
|
||||
currentSession: null,
|
||||
currentSessionIndex: 0,
|
||||
inputText: ''
|
||||
inputText: '',
|
||||
bussinessType: 1,
|
||||
}),
|
||||
|
||||
getters: {
|
||||
|
@ -59,6 +61,10 @@ export const useChatStore = defineStore('chatStore', {
|
|||
this.inputText = content
|
||||
},
|
||||
|
||||
setBussinessType(type: number) {
|
||||
this.bussinessType = type
|
||||
},
|
||||
|
||||
async addMessageToCurrentSession<T extends BaseMessage>(message: T): Promise<void> {
|
||||
this.currentSession?.msgList.push(message)
|
||||
|
||||
|
@ -109,6 +115,7 @@ export const useChatStore = defineStore('chatStore', {
|
|||
* @param message
|
||||
*/
|
||||
addMessageToConversation<T extends BaseMessage>(message: T): void {
|
||||
|
||||
// 无论是converstionNo1还是converstionNo2都都需要试一下
|
||||
const converstionNo1 = generateConversationNo(
|
||||
message.senderId,
|
||||
|
@ -138,6 +145,11 @@ export const useChatStore = defineStore('chatStore', {
|
|||
|
||||
// 更新消息到indexeddb
|
||||
addConversation(toRaw(msgConversation) as ChatConversation )
|
||||
|
||||
// 更新当前会话
|
||||
if (conversationIndex === this.currentSessionIndex) {
|
||||
this.setCurrentConversation()
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -236,7 +248,60 @@ export const useChatStore = defineStore('chatStore', {
|
|||
} catch (error) {
|
||||
return error
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* 创建会话
|
||||
*/
|
||||
async createConversation(targetId, type, avatar, nickname) {
|
||||
try {
|
||||
const param = {
|
||||
targetId,
|
||||
type
|
||||
}
|
||||
const res = await SessionApi.createConversation(param)
|
||||
if (res) {
|
||||
|
||||
// 切换到聊天模式
|
||||
this.bussinessType = MENU_LIST_ENUM.CONVERSATION
|
||||
|
||||
// 插入用户名和昵称
|
||||
res.avatar = avatar;
|
||||
res.nickname = nickname;
|
||||
const localConversation = this.convertCoversationFromServer(res)
|
||||
// 存入到数据库
|
||||
addConversation(toRaw(localConversation) as ChatConversation)
|
||||
// 从数据库同步到内存
|
||||
await this.getConversationList()
|
||||
// 设置当前的会话
|
||||
const addIndex = this.sessionList.findIndex(item => item.conversationNo === localConversation.conversationNo)
|
||||
this.setCurrentSessionIndex(addIndex)
|
||||
this.setCurrentConversation()
|
||||
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.log(error)
|
||||
}
|
||||
},
|
||||
|
||||
convertCoversationFromServer(item: any) {
|
||||
return {
|
||||
...item,
|
||||
updateTime: item.updateTime,
|
||||
targetId: item.targetId,
|
||||
senderId: item.userId,
|
||||
conversationNo: item.no,
|
||||
unreadMessagesCount: item.unreadMessagesCount,
|
||||
description: item.lastMessageDescription,
|
||||
avatar: item.avatar,
|
||||
name: item.name,
|
||||
msgList: []
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -1,33 +1,23 @@
|
|||
import { defineStore } from 'pinia'
|
||||
import { store } from '@/store/index'
|
||||
import BaseConversation from '../model/BaseConversation'
|
||||
import Friend from '../model/Friend'
|
||||
import { getAllUser, getDeptUser } from '@/api/system/user'
|
||||
import * as DeptApi from '@/api/system/dept'
|
||||
|
||||
interface FriendStoreModel {
|
||||
friendList: Array<Friend>
|
||||
currentFriend: Friend | null
|
||||
currentFriend: Friend | null,
|
||||
selectedDepartmentId: number,
|
||||
departmentList: DeptApi.DeptVO[]
|
||||
}
|
||||
|
||||
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
|
||||
friendList: [],
|
||||
currentFriend: null,
|
||||
selectedDepartmentId: 0,
|
||||
departmentList: []
|
||||
}),
|
||||
|
||||
getters: {
|
||||
|
@ -42,6 +32,82 @@ export const useFriendStore = defineStore('friendStore', {
|
|||
},
|
||||
setCurrentFriend(friend: Friend) {
|
||||
this.currentFriend = friend
|
||||
}
|
||||
},
|
||||
setCurrentDepartmentId(id: number) {
|
||||
this.selectedDepartmentId = id
|
||||
},
|
||||
resetFriendList() {
|
||||
this.friendList = []
|
||||
this.currentFriend = null
|
||||
},
|
||||
async fetchDepartment () {
|
||||
try {
|
||||
const result = await DeptApi.getSimpleDeptList()
|
||||
this.departmentList = this.buildHierarchy(result)
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
},
|
||||
|
||||
async fetchFriend() {
|
||||
|
||||
try {
|
||||
const res = await getAllUser()
|
||||
this.friendList = res
|
||||
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
},
|
||||
|
||||
async fetchDeptUser(id) {
|
||||
|
||||
try {
|
||||
const res = await getDeptUser(id)
|
||||
if (res) {
|
||||
this.friendList = res.map(item => {
|
||||
return {
|
||||
...item,
|
||||
name: item.nickname
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.friendList = []
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
}
|
||||
},
|
||||
|
||||
buildHierarchy(data: DeptApi.DeptVO[]): DeptApi.DeptVO[] {
|
||||
const map = new Map<number, DeptApi.DeptVO>();
|
||||
|
||||
// 初始化 map,确保每个 id 都有一条记录
|
||||
data.forEach(item => map.set(item.id, { ...item, children: [] }));
|
||||
|
||||
const result: DeptApi.DeptVO[] = [];
|
||||
|
||||
data.forEach(item => {
|
||||
if (item.parentId === 0) {
|
||||
// 根节点
|
||||
result.push(map.get(item.id)!);
|
||||
} else {
|
||||
// 子节点,放入父节点的 children 数组
|
||||
const parent = map.get(item.parentId);
|
||||
if (parent) {
|
||||
parent.children!.push(map.get(item.id)!);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
export const useFriendStoreWithOut = () => {
|
||||
return useFriendStore(store)
|
||||
}
|
|
@ -11,6 +11,7 @@ import {
|
|||
import TextMessage from '../model/TextMessage'
|
||||
import { debug } from 'console'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
import BaseConversation from '../model/BaseConversation'
|
||||
|
||||
interface Message {
|
||||
type: string
|
||||
|
@ -101,6 +102,13 @@ export const useWebSocketStore = defineStore('webSocket', () => {
|
|||
|
||||
}
|
||||
|
||||
} else if (websoketMessage.type === WEBSOCKET_MESSAGE_TYPE_ENUM.IM_CONVERSATION_ADD.toString()) {
|
||||
const chatStore = useChatStore()
|
||||
const conversation = JSON.parse(websoketMessage.content) as BaseConversation
|
||||
chatStore.addSession(conversation)
|
||||
// 同步到内存
|
||||
chatStore.getConversationList()
|
||||
|
||||
} else {
|
||||
// TODO:[dylan]
|
||||
}
|
||||
|
|
|
@ -35,7 +35,8 @@ export const enum CONVERSATION_TYPE {
|
|||
}
|
||||
|
||||
export enum WEBSOCKET_MESSAGE_TYPE_ENUM {
|
||||
IM_MESSAGE_RECEIVE = 'im-message-receive'
|
||||
IM_MESSAGE_RECEIVE = 'im-message-receive',
|
||||
IM_CONVERSATION_ADD = 'im-conversation-add'
|
||||
}
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue