gaoluyang
昨天 b64a0deae5b5d33f9e20671a68936b27f0b9b00b
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
<script lang="ts" setup>
import { computed } from 'vue'
import { useRoute, useRouter } from 'vue-router'
 
import { IconifyIcon as Icon } from '#/packages/icons/src'
import { useUserStore } from '#/packages/stores/src'
 
import { Badge } from 'ant-design-vue'
 
import { useConversationStore } from '../store/conversationStore'
import { useFriendStore } from '../store/friendStore'
import { useImUiStore } from '../store/uiStore'
import { UserAvatar } from './user'
 
defineOptions({ name: 'ImToolBar' })
 
const route = useRoute()
const router = useRouter()
const userStore = useUserStore()
const conversationStore = useConversationStore()
const friendStore = useFriendStore()
const uiStore = useImUiStore()
 
/** 消息 Tab 的红点:所有非免打扰会话的未读总和 */
const totalUnread = computed(() => conversationStore.getTotalUnreadCount)
/** 通讯录 Tab 的红点:未处理好友申请数(接收方=我) */
const unhandledRequestCount = computed(() => friendStore.getUnhandledRequestCount)
 
const tabs = [
  { name: 'ImHomeConversation', icon: 'ep:chat-round' },
  { name: 'ImHomeContact', icon: 'mingcute:contacts-line' }
] // 两个主 Tab;用路由 name 而非 path,避免前缀 / 嵌套调整后失效
 
// 当前路由是否命中 Tab:直接比对 route.name
const isActive = (name: string) => route.name === name
 
// 切换 Tab:当前已选中时,消息 Tab 触发"滚动到下一个未读"(对齐微信 PC),其它 Tab 无动作
const goTab = (name: string) => {
  if (route.name === name) {
    if (name === 'ImHomeConversation') {
      uiStore.requestNextUnreadJump()
    }
    return
  }
  router.push({ name })
}
 
// 跳转个人中心(路由 name=Profile)
const goProfile = () => router.push({ name: 'Profile' })
</script>
 
<template>
  <!--
    ToolBar:IM 左侧工具栏
    布局:顶部头像 → 中间三 Tab(消息/好友/群聊)→ 底部设置
  -->
  <div
    class="flex flex-col items-center w-14 pt-4 pb-3 gap-2 flex-shrink-0 select-none bg-[#2b2b2b]"
  >
    <!-- 顶部用户头像,点击跳个人中心;走 UserAvatar 统一首字 / 哈希配色规则 -->
    <div class="mb-2 cursor-pointer" @click="goProfile">
      <UserAvatar
        :url="userStore.userInfo?.avatar"
        :name="userStore.userInfo?.nickname"
        :size="36"
        :clickable="false"
      />
    </div>
 
    <!-- 中间三 Tab -->
    <div class="flex flex-col items-center gap-2 flex-1 w-full">
      <div
        v-for="item in tabs"
        :key="item.name"
        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)"
      >
        <Badge
          v-if="item.name === 'ImHomeConversation' && totalUnread > 0"
          :count="totalUnread"
          :max="99"
          class="tool-bar__badge"
        >
          <Icon :icon="item.icon" class="tool-bar__icon" />
        </Badge>
        <Badge
          v-else-if="item.name === 'ImHomeContact' && unhandledRequestCount > 0"
          :count="unhandledRequestCount"
          :max="99"
          class="tool-bar__badge"
        >
          <Icon :icon="item.icon" class="tool-bar__icon" />
        </Badge>
        <Icon v-else :icon="item.icon" class="tool-bar__icon" />
      </div>
    </div>
 
    <!-- 底部设置按钮:点击跳个人中心 -->
    <div class="flex flex-col items-center gap-2 w-full">
      <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"
      >
        <Icon icon="ant-design:setting-outlined" class="tool-bar__icon" />
      </div>
    </div>
  </div>
</template>
 
<style scoped>
.tool-bar__icon {
  width: 22px;
  height: 22px;
}
 
/* 调整 AntD Badge 右上角红点位置 */
.tool-bar__badge :deep(.ant-badge-count) {
  top: 4px;
  right: 8px;
  border: none;
}
</style>