shinnosuke
2026-08-11 a375ecc695efe1739871e9d9d0bf981454aa8061
talent-card-admin/src/layouts/AdminLayout.vue
@@ -34,6 +34,7 @@
const route = useRoute()
const router = useRouter()
const appStore = useAppStore()
const avatarText = computed(() => (appStore.user.name || 'A').trim().slice(0, 1).toUpperCase())
interface HeaderNotice {
  id: number
@@ -128,6 +129,24 @@
  { index: 'operation-management', label: '运营管理', icon: Operation, items: operationMenus },
  { index: 'system-management', label: '系统管理', icon: Setting, items: systemMenus },
]
// Keep the navigation in sync with the permissions returned at login.  Newer
// routes without a dedicated permission code intentionally remain visible.
const routePermissions: Record<string, string> = {
  '/talents': 'admin.talents.view', '/family-card-applications': 'admin.family-cards.view', '/upgrade-applications': 'admin.talents.view', '/family-cards': 'admin.family-cards.view', '/child-cards': 'admin.family-cards.view',
  '/merchants': 'admin.merchants.view', '/products': 'admin.products.view', '/orders': 'admin.orders.view', '/refund-records': 'admin.refunds.view', '/hotel-free-verifications': 'admin.orders.view',
  '/medical-reservations': 'admin.medical-reservations.view', '/medical-applications': 'admin.medical-reservations.view', '/parking-plates': 'admin.parking-plates.view', '/fuel-services': 'admin.parking-plates.view', '/spouse-employment': 'admin.parking-plates.view', '/hotel-reservations': 'admin.medical-reservations.view',
  '/merchant-settlement': 'admin.merchants.view', '/equity-images': 'admin.equity-images.view', '/talent-news-management': 'admin.news.view', '/talent-policy-management': 'admin.news.view', '/movie-tickets': 'admin.news.view',
  '/system-settings': 'admin.settings.view', '/protocol-settings': 'admin.settings.view', '/roles': 'admin.roles.view', '/staff': 'admin.staff.view',
}
const canAccess = (path?: string) => {
  if (!path || appStore.user.role === '平台管理') return true
  const required = routePermissions[path]
  return !required || Boolean(appStore.user.permissionCodes?.includes('*') || appStore.user.permissionCodes?.includes(required))
}
const visibleMenuGroups = computed(() => menuGroups
  .map((group) => ({ ...group, items: group.items.filter((item) => canAccess(item.path)) }))
  .filter((group) => group.items.length > 0))
const defaultOpeneds = computed(() => {
  const currentGroup = menuGroups.find((item) => findMenuLabel(item.items, activeMenu.value))
@@ -288,7 +307,7 @@
        <el-tooltip content="退出登录" placement="bottom">
          <el-button :icon="SwitchButton" circle @click="logout" />
        </el-tooltip>
        <div class="admin-avatar" title="平台管理员" aria-label="平台管理员头像">A</div>
        <div class="admin-avatar" :title="appStore.user.name" :aria-label="`${appStore.user.name}头像`">{{ avatarText }}</div>
      </div>
    </el-header>
@@ -306,7 +325,7 @@
            <span>首页</span>
          </el-menu-item>
          <el-sub-menu v-for="group in menuGroups" :key="group.index" :index="group.index">
          <el-sub-menu v-for="group in visibleMenuGroups" :key="group.index" :index="group.index">
            <template #title>
              <el-icon>
                <component :is="group.icon" />