feat(home): 添加菜单项徽章显示功能

- 在首页菜单项中集成 VanBadge 组件用于展示待办数量
- 引入 todoStore 并使用 storeToRefs 获取响应式统计数据
- 将 menuList 改为计算属性以动态绑定统计数据
- 在组件挂载时调用 fetchTodoStatistics 方法加载数据
- 添加 .menu-badge 样式类定义徽章左侧间距
- 默认展开抽屉状态以便查看新增的徽章效果
master
chenhao 2025-12-10 17:11:06 +08:00
parent 292fc35d54
commit 3d3af1867b
3 changed files with 58 additions and 8 deletions

1
components.d.ts vendored
View File

@ -9,6 +9,7 @@ declare module 'vue' {
export interface GlobalComponents {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
VanBadge: typeof import('vant/es')['Badge']
VanButton: typeof import('vant/es')['Button']
VanCellGroup: typeof import('vant/es')['CellGroup']
VanEmpty: typeof import('vant/es')['Empty']

33
src/store/todo.ts 100644
View File

@ -0,0 +1,33 @@
import { defineStore } from 'pinia'
import { getTodoStatistics } from '@/api/todo'
import type { TodoStatistics } from '@/api/todo'
interface TodoState {
statistics: TodoStatistics | null
}
export const useTodoStore = defineStore('todo', {
state: (): TodoState => ({
statistics: null
}),
actions: {
/**
*
*/
async fetchTodoStatistics() {
try {
const response = await getTodoStatistics()
if (response.status === 200 && response.data.code === 0) {
// The actual data is in response.data.data
this.statistics = response.data.data
} else {
throw new Error(response.data.msg || '获取待办统计失败')
}
} catch (error: any) {
console.error('获取待办统计接口调用失败:', error)
throw error
}
}
}
})

View File

@ -38,8 +38,13 @@
:class="{ active: currentMenu === menu.key }"
@click="selectMenu(menu.key)"
>
<van-icon :name="menu.icon" class="menu-icon-left" />
<span class="menu-title">{{ menu.title }}</span>
<van-icon :name="menu.icon" class="menu-icon-left" >
</van-icon>
<span class="menu-title">{{ menu.title }} <van-badge :content="menu.count" class="menu-badge" v-if="menu.count > 0" /></span>
<van-icon name="arrow" class="menu-arrow" />
</div>
</div>
@ -62,36 +67,42 @@ import { ref, computed, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { showConfirmDialog, showSuccessToast, showFailToast } from 'vant'
import { useAuthStore } from '@/store/auth'
import { useTodoStore } from '@/store/todo'
import { storeToRefs } from 'pinia'
import OrderList from '@/views/List/index.vue'
import PurchaseList from '@/views/Purchase/index.vue'
const router = useRouter()
const route = useRoute()
const authStore = useAuthStore()
const todoStore = useTodoStore()
const { statistics } = storeToRefs(todoStore)
//
const showDrawer = ref(false)
const showDrawer = ref(true)
//
const currentMenu = ref('order')
//
const menuList = [
const menuList = computed(() => [
{
key: 'order',
title: '订单审批',
icon: 'notes-o'
icon: 'notes-o',
count: statistics.value?.order_approve || 0
},
{
key: 'purchase',
title: '采购审批',
icon: 'shopping-cart-o'
icon: 'shopping-cart-o',
count: statistics.value?.purchase_order_online || 0
}
]
])
//
const currentMenuTitle = computed(() => {
const menu = menuList.find(m => m.key === currentMenu.value)
const menu = menuList.value.find(m => m.key === currentMenu.value)
return menu ? menu.title : '审批'
})
@ -103,6 +114,7 @@ const selectMenu = (key: string) => {
//
onMounted(() => {
todoStore.fetchTodoStatistics()
const tab = route.query.tab as string
if (tab && ['order', 'purchase'].includes(tab)) {
currentMenu.value = tab
@ -287,6 +299,10 @@ const handleLogout = async () => {
transition: all 0.2s ease;
}
.menu-badge {
margin-left: 8px;
}
.menu-arrow {
font-size: 16px;
color: #999999;