diff --git a/bx-mobile/src/views/home/HomePage.vue b/bx-mobile/src/views/home/HomePage.vue
index ce81e9ff9..d09ffaf71 100644
--- a/bx-mobile/src/views/home/HomePage.vue
+++ b/bx-mobile/src/views/home/HomePage.vue
@@ -18,7 +18,7 @@
-
+
{{ todoItems.length }}
@@ -29,7 +29,13 @@
{{ item.title }}
{{ item.subText }} · {{ item.timeText }}
-
+
@@ -39,27 +45,27 @@
-
+
-
{{ group.menuName }}
+
{{ group.menuName }}
@@ -70,7 +76,7 @@
-
+
@@ -79,13 +85,13 @@ import { computed, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { showConfirmDialog } from 'vant'
import { PRIMARY_CONFIRM_BUTTON_COLOR } from '@/constants/dialog'
-import { useAuthStore, type MenuItem } from '@/stores/auth'
+import { useAuthStore } from '@/stores/auth'
+import type { BxMenuEntry } from '@/constants/bxMenuCatalog'
import { advanceApi, reimburseApi } from '@/api'
const router = useRouter()
const auth = useAuthStore()
-/* ───── Reactive Stats ───── */
const stats = ref({
submittedThisMonth: 0,
pendingCount: 0,
@@ -97,7 +103,7 @@ const stats = ref({
const pendingApprovalCounts = ref>({})
interface TodoItem {
- id: number
+ id: string
title: string
subText: string
statusText: string
@@ -111,13 +117,12 @@ interface TodoItem {
const todoItems = ref([])
onMounted(async () => {
- if (!auth.menuList.length) {
- await auth.fetchMenus()
+ if (!auth.user) {
+ await auth.fetchUser()
}
await fetchHomeData()
})
-/* ───── Fetch & Compute Stats ───── */
function pagedRows(res: any): any[] {
const d = res?.data
if (!d) return []
@@ -138,11 +143,15 @@ async function fetchHomeData() {
const approvedCount =
Number(r.approvedCount ?? 0) + Number(a.approvedPassCount ?? 0) + Number(a.issuedCount ?? 0)
const totalCount = Number(r.totalCount ?? 0) + Number(a.totalCount ?? 0)
- const totalAmount = 0
- stats.value = { submittedThisMonth, pendingCount, approvedCount, totalAmount, totalCount }
+ stats.value = {
+ submittedThisMonth,
+ pendingCount,
+ approvedCount,
+ totalAmount: 0,
+ totalCount,
+ }
- // Fetch pending approvals for todo area & red dots
const [pendingAdvanceRes, pendingReimburseRes] = await Promise.all([
advanceApi.pendingApproval({ page: 1, pageSize: 5 }).catch(() => null),
reimburseApi.pendingApproval({ page: 1, pageSize: 5 }).catch(() => null),
@@ -154,24 +163,22 @@ async function fetchHomeData() {
const pendingReimburseTotal = (pendingReimburseRes as any)?.data?.total || 0
pendingApprovalCounts.value = {
- 'advance-approve': pendingAdvanceTotal,
- 'reimburse-approve': pendingReimburseTotal,
- todo: pendingAdvanceTotal + pendingReimburseTotal,
- mine: pendingCount,
+ 'bx:advanceApprove:list': pendingAdvanceTotal,
+ 'bx:reimburseApprove:list': pendingReimburseTotal,
+ 'bx:viewTodo:list': pendingAdvanceTotal + pendingReimburseTotal,
}
- // Build todo items
const items: TodoItem[] = []
for (const item of pendingAdvanceItems) {
items.push({
- id: item.id,
+ id: String(item.id),
title: item.title || '备用金单待审批',
subText: [item.title, item.applicantName, item.departmentName].filter(Boolean).join(' · '),
statusText: '待处理',
statusVariant: 'warning',
amountText: `金额 ¥${Number(item.amount || 0).toFixed(2)}`,
- timeText: formatTime(item.createdAt),
+ timeText: formatTime(item.createTime || item.createdAt),
primaryRoute: '/approval',
secondaryRoute: '/approval',
})
@@ -179,13 +186,13 @@ async function fetchHomeData() {
for (const item of pendingReimburseItems) {
items.push({
- id: item.id,
+ id: String(item.id),
title: item.title || '报销申请待审批',
subText: [item.title, item.applicantName, item.departmentName].filter(Boolean).join(' · '),
statusText: '待处理',
statusVariant: 'warning',
amountText: `金额 ¥${Number(item.amount || 0).toFixed(2)}`,
- timeText: formatTime(item.createdAt),
+ timeText: formatTime(item.createTime || item.createdAt),
primaryRoute: '/approval',
secondaryRoute: '/approval',
})
@@ -194,7 +201,7 @@ async function fetchHomeData() {
items.sort((a, b) => b.timeText.localeCompare(a.timeText))
todoItems.value = items.slice(0, 5)
} catch {
- // Keep default empty state
+ /* keep empty */
}
}
@@ -211,7 +218,6 @@ function formatTime(dateStr: string): string {
return `${d.getMonth() + 1}/${d.getDate()} ${hh}:${mm}`
}
-/* ───── Computed for Template ───── */
const greeting = computed(() => {
const h = new Date().getHours()
if (h < 6) return '凌晨好'
@@ -221,109 +227,26 @@ const greeting = computed(() => {
return '晚上好'
})
-/* ───── Menu Logic ───── */
-function getItems(group: MenuItem): MenuItem[] {
- if (!group.children?.length) return []
- const result: MenuItem[] = []
- for (const child of group.children) {
- if (child.children?.length) {
- result.push(...child.children.filter(c => c.menuCode))
- } else if (child.menuCode) {
- result.push(child)
- }
+const groupsWithItems = computed(() => {
+ const map = new Map()
+ for (const e of auth.visibleEntries) {
+ const list = map.get(e.group) || []
+ list.push(e)
+ map.set(e.group, list)
}
- return result
-}
+ return [...map.entries()].map(([menuName, items]) => ({
+ id: menuName,
+ menuName,
+ items,
+ }))
+})
-/** 仅包含有可展示子菜单卡片的一级分组 */
-const groupsWithItems = computed(() => (auth.menuList || []).filter((g) => getItems(g).length > 0))
-
-/** 与列表顶栏命名统一;后端菜单名可能是旧文案(如「我的报销」)时覆盖展示 */
-const MOBILE_MENU_DISPLAY: Record = {
- 'reimburse-apply': '报销申请',
- 'advance-issue': '备用金\n发放管理',
- payment: '发放/冲抵\n支付管理',
-}
-
-function menuDisplayName(item: MenuItem): string {
- const code = item.menuCode || ''
- return MOBILE_MENU_DISPLAY[code] ?? item.menuName
-}
-
-const ROUTE_MAP: Record = {
- 'advance-apply': '/advance/list',
- 'advance-approve': '/advance/approve',
- 'advance-issue': '/advance/issue',
- 'reimburse-apply': '/reimburse/list',
- 'reimburse-approve': '/reimburse/approve',
- payment: '/payment',
- mine: '/mine',
- todo: '/approval',
- all: '/all',
- 'user-manage': '/admin/users',
- 'role-manage': '/admin/roles',
- 'menu-manage': '/admin/menus',
- project: '/admin/expenses',
- 'wf-advance': '/admin/wf-advance',
- 'wf-reimburse': '/admin/wf-reimburse',
- demo: '/advance/list',
-}
-
-function navigateTo(code: string | null) {
- if (!code) return
- const path = ROUTE_MAP[code]
+function navigateTo(path: string) {
if (path) router.push(path)
}
-const ICON_MAP: Record = {
- 'advance-apply': 'cash-back-record',
- 'advance-approve': 'checked',
- 'advance-issue': 'paid',
- 'reimburse-apply': 'description',
- 'reimburse-approve': 'todo-list-o',
- payment: 'balance-pay',
- mine: 'records',
- todo: 'clock-o',
- all: 'list-switch',
- 'user-manage': 'manager-o',
- 'role-manage': 'shield-o',
- 'menu-manage': 'apps-o',
- project: 'label-o',
- 'wf-advance': 'exchange',
- 'wf-reimburse': 'exchange',
- demo: 'smile-o',
-}
-
-function getIcon(code: string | null): string {
- return (code && ICON_MAP[code]) || 'bookmark-o'
-}
-
-const COLOR_MAP: Record = {
- 'advance-apply': '#10b981',
- 'advance-approve': '#3b82f6',
- 'advance-issue': '#8b5cf6',
- 'reimburse-apply': '#f59e0b',
- 'reimburse-approve': '#ef4444',
- payment: '#06b6d4',
- mine: '#6366f1',
- todo: '#f97316',
- all: '#64748b',
- 'user-manage': '#3b82f6',
- 'role-manage': '#8b5cf6',
- 'menu-manage': '#10b981',
- project: '#f59e0b',
- 'wf-advance': '#06b6d4',
- 'wf-reimburse': '#ec4899',
- demo: '#94a3b8',
-}
-
-function getColor(code: string | null): string {
- return (code && COLOR_MAP[code]) || '#64748b'
-}
-
-function hasDot(code: string | null): boolean {
- if (!code) return false
- return (pendingApprovalCounts.value[code] || 0) > 0
+function hasDot(menuCode: string): boolean {
+ return (pendingApprovalCounts.value[menuCode] || 0) > 0
}
async function handleLogout() {
@@ -333,7 +256,7 @@ async function handleLogout() {
message: '确定退出登录?',
confirmButtonColor: PRIMARY_CONFIRM_BUTTON_COLOR,
})
- auth.logout()
+ await auth.logout()
router.push('/login')
} catch {
/* cancelled */
@@ -347,4 +270,3 @@ async function handleLogout() {
color: var(--text-muted);
}
-