111 lines
4.0 KiB
Vue
111 lines
4.0 KiB
Vue
<template>
|
||
<!--
|
||
ToolBar:IM 左侧工具栏
|
||
布局:顶部头像 → 中间三 Tab(消息/好友/群聊)→ 底部设置
|
||
-->
|
||
<div class="flex flex-col items-center w-14 pt-4 pb-3 gap-2 flex-shrink-0 bg-[#2b2b2b]">
|
||
<!-- 顶部用户头像,点击跳个人中心;方块小圆角对齐 UserAvatar 风格 -->
|
||
<div class="mb-2 cursor-pointer" @click="goProfile">
|
||
<el-avatar :size="36" :src="avatar" shape="square" :style="{ borderRadius: '6px' }">
|
||
{{ nicknameShort }}
|
||
</el-avatar>
|
||
</div>
|
||
|
||
<!-- 中间三 Tab -->
|
||
<div class="flex flex-col items-center gap-2 flex-1 w-full">
|
||
<el-tooltip v-for="item in tabs" :key="item.name" :content="item.label" placement="right">
|
||
<div
|
||
class="flex items-center justify-center w-10 h-10 rounded-lg text-[#a0a0a0] cursor-pointer transition-all hover:text-white hover:bg-white/10"
|
||
:class="{ 'bg-white/15 text-white': isActive(item.name) }"
|
||
@click="goTab(item.name)"
|
||
>
|
||
<el-badge
|
||
v-if="item.name === 'ImHomeConversation' && totalUnread > 0"
|
||
:value="totalUnread"
|
||
:max="99"
|
||
class="tool-bar__badge"
|
||
>
|
||
<Icon :icon="item.icon" :size="22" />
|
||
</el-badge>
|
||
<Icon v-else :icon="item.icon" :size="22" />
|
||
</div>
|
||
</el-tooltip>
|
||
</div>
|
||
|
||
<!-- 底部设置按钮:点击跳个人中心 -->
|
||
<div class="flex flex-col items-center gap-2 w-full">
|
||
<el-tooltip content="设置" placement="right">
|
||
<div
|
||
class="flex items-center justify-center w-10 h-10 rounded-lg text-[#a0a0a0] cursor-pointer transition-all hover:text-white hover:bg-white/10"
|
||
@click="goProfile"
|
||
>
|
||
<el-icon :size="22"><Setting /></el-icon>
|
||
</div>
|
||
</el-tooltip>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { computed } from 'vue'
|
||
import { useRoute, useRouter } from 'vue-router'
|
||
import { Setting } from '@element-plus/icons-vue'
|
||
|
||
import Icon from '@/components/Icon/src/Icon.vue'
|
||
import { useUserStore } from '@/store/modules/user'
|
||
import { useConversationStore } from '../store/conversationStore'
|
||
|
||
defineOptions({ name: 'ImToolBar' })
|
||
|
||
const route = useRoute()
|
||
const router = useRouter()
|
||
const userStore = useUserStore()
|
||
const conversationStore = useConversationStore()
|
||
|
||
/** 消息 Tab 的红点:所有非免打扰会话的未读总和 */
|
||
const totalUnread = computed(() => conversationStore.getTotalUnread)
|
||
|
||
/**
|
||
* 三个主 Tab 的配置,name 对应路由 ImHomeConversation/Friend/Group
|
||
* 用 name 而非 path:path 后期容易变(前缀调整、嵌套加层),name 更稳定
|
||
* icon 走通用 <Icon> 组件,支持 iconify 全部前缀(ep: / ant-design: / svg-icon: 等)
|
||
* 群聊用 ant-design:team(三人组合):ep 没有"群体"图标,三人剪影跟 ep:user(单人)一眼区分单人 / 群体
|
||
*/
|
||
const tabs = [
|
||
{ name: 'ImHomeConversation', label: '消息', icon: 'ep:chat-dot-round' },
|
||
{ name: 'ImHomeFriend', label: '好友', icon: 'ep:user' },
|
||
{ name: 'ImHomeGroup', label: '群聊', icon: 'ant-design:team' }
|
||
]
|
||
|
||
/** 当前路由是否命中 Tab:直接比对 route.name */
|
||
const isActive = (name: string) => route.name === name
|
||
|
||
const avatar = computed(() => userStore.getUser?.avatar || '')
|
||
|
||
/** 头像兜底:取昵称最后一个字符,避免空头像时的灰底过于突兀 */
|
||
const nicknameShort = computed(() => {
|
||
const name = userStore.getUser?.nickname || ''
|
||
return name ? name.slice(-1) : '我'
|
||
})
|
||
|
||
/** 切换 Tab:当前 Tab 已选中时跳过,避免无意义的导航 */
|
||
const goTab = (name: string) => {
|
||
if (route.name === name) {
|
||
return
|
||
}
|
||
router.push({ name })
|
||
}
|
||
|
||
/** 跳转个人中心(路由 name=Profile) */
|
||
const goProfile = () => router.push({ name: 'Profile' })
|
||
</script>
|
||
|
||
<style scoped>
|
||
/* el-badge 子组件内部类 UnoCSS 够不到,单独贴一条 :deep 覆盖 */
|
||
.tool-bar__badge :deep(.el-badge__content) {
|
||
top: 4px;
|
||
right: 8px;
|
||
border: none;
|
||
}
|
||
</style>
|