feat: 联系人
This commit is contained in:
@ -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
|
||||
|
||||
53
src/views/chat/components/Department/components/TreeNode.vue
Normal file
53
src/views/chat/components/Department/components/TreeNode.vue
Normal file
@ -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>
|
||||
28
src/views/chat/components/Department/components/TreeView.vue
Normal file
28
src/views/chat/components/Department/components/TreeView.vue
Normal file
@ -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>
|
||||
63
src/views/chat/components/Department/index.vue
Normal file
63
src/views/chat/components/Department/index.vue
Normal file
@ -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>
|
||||
|
||||
Reference in New Issue
Block a user