fix: 菜单与API权限码双重校验,切换俱乐部后不再展示无权限页面

This commit is contained in:
XingQue
2026-07-06 19:21:38 +08:00
parent 1c57cea841
commit 0c08243a37
4 changed files with 71 additions and 13 deletions

View File

@@ -3,7 +3,7 @@ const CLUB_SCOPE_KEY = 'admin_club_scope';
const CLUB_CONTEXT_KEY = 'admin_club_context';
const MENU_ACCESS_KEY = 'kefu_menu_access';
const MENU_SESSION_KEY = 'kefu_menu_session_v';
const MENU_SESSION_VERSION = '21';
const MENU_SESSION_VERSION = '22';
const DEFAULT_CLUB_ID = 'xq';
let menuAccessInflight = null;
@@ -162,6 +162,7 @@ const KEFU_MENU_ROWS = [
];
const KEFU_MENU_PARENT_IDS = {
admin: ['admin.role', 'admin.data-scope', 'admin.club-config', 'admin.user', 'admin.operation-log'],
finance: ['finance.bankuai', 'finance.data'],
assessment: ['assessment.examiner', 'assessment.config', 'assessment.record'],
order: ['order.platform', 'order.merchant'],
@@ -214,9 +215,37 @@ export function canShowMenuPage(pageId) {
if (!access) return false;
const ids = access?.visible_page_ids;
if (!Array.isArray(ids) || ids.length === 0) return false;
const row = KEFU_MENU_ROWS.find((r) => r.page_id === pageId);
if (row?.path && row.perm_codes?.length) {
if (!ids.includes(pageId)) return false;
return hasPermCode(access, ...row.perm_codes);
}
if (ids.includes(pageId)) return true;
const children = KEFU_MENU_PARENT_IDS[pageId];
return children ? children.some((id) => ids.includes(id)) : false;
return children ? children.some((id) => canShowMenuPage(id)) : false;
}
/** 写入菜单缓存,并同步服务端校正后的 effective_club_id */
export function applyMenuAccessPayload(data) {
if (!data) return null;
const scope = getAdminClubScope();
const expectedClub = getAdminClubId();
if (
data.effective_club_id &&
scope !== 'all' &&
data.effective_club_id !== expectedClub
) {
const ctx = getAdminClubContext() || {};
setAdminClubContext({
...ctx,
club_id: data.effective_club_id,
scope: 'SINGLE_CLUB',
});
}
setMenuAccess(data);
return data;
}
export function getFirstVisiblePath() {
@@ -281,11 +310,13 @@ export async function ensureMenuAccess(fetcher, { force = false } = {}) {
menuAccessInflight = (async () => {
try {
const data = await fetcher();
if (data) setMenuAccess(data);
return data;
if (data) return applyMenuAccessPayload(data);
if (force) clearMenuAccess();
return null;
} catch (e) {
console.error('ensureMenuAccess failed:', e);
return getMenuAccess();
if (force) clearMenuAccess();
return null;
} finally {
menuAccessInflight = null;
}

View File

@@ -3,9 +3,11 @@
<el-result icon="warning" title="暂无可用功能菜单">
<template #sub-title>
<p v-if="yonghuid">用户ID{{ yonghuid }}</p>
<p v-if="roleNames.length">已绑角色{{ roleNames.join('') }}角色内可能未含功能权限码</p>
<p v-else>该账号未绑定任何角色</p>
<p class="hint">请超管在后台用户中为该用户ID绑定角色角色内需含对应权限考核=kaohepeizhi</p>
<p v-if="clubLabel">当前俱乐部{{ clubLabel }}</p>
<p v-if="roleNames.length">本俱乐部已绑角色{{ roleNames.join('') }}</p>
<p v-else-if="permCodes.length">权限码{{ permCodes.join('') }}无对应菜单</p>
<p v-else>当前俱乐部下未绑定任何功能角色</p>
<p class="hint">{{ hintText }}</p>
</template>
<template #extra>
<el-button type="primary" @click="goTry">刷新权限</el-button>
@@ -21,23 +23,40 @@ import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import request from '@/utils/request'
import {
applyMenuAccessPayload,
clearMenuAccess,
getAdminClubContext,
getAdminClubId,
getDefaultLandingPath,
getMenuAccess,
JITUAN_API,
setMenuAccess,
} from '@/utils/club-context'
const router = useRouter()
const access = computed(() => getMenuAccess() || {})
const yonghuid = computed(() => access.value.yonghuid || '')
const roleNames = computed(() => access.value.role_names || [])
const permCodes = computed(() => access.value.permission_codes || [])
const clubLabel = computed(() => {
const cid = access.value.effective_club_id || getAdminClubId()
const ctx = getAdminClubContext()
const hit = (ctx?.clubs || []).find((c) => c.club_id === cid)
return hit ? `${hit.name}${cid}` : cid
})
const hintText = computed(() => {
if (access.value.perm_hint) return access.value.perm_hint
return (
'数据范围(任职)与功能角色是两套配置:切换俱乐部后,须在该俱乐部单独绑定 gvsdsdk 角色。'
+ '请超管在「后台用户」→ 选择俱乐部 → 添加角色 → 重新登录。'
)
})
const goTry = async () => {
try {
clearMenuAccess()
const res = await request.get(JITUAN_API.menuAccess)
if (res.code === 0 && res.data) {
setMenuAccess(res.data)
applyMenuAccessPayload(res.data)
const landing = getDefaultLandingPath(res.data)
if (landing) {
ElMessage.success('权限已生效')
@@ -45,7 +64,7 @@ const goTry = async () => {
return
}
}
ElMessage.warning('仍无可用菜单,请确认角色与权限已绑定')
ElMessage.warning('仍无可用菜单,请确认已在当前俱乐部绑定功能角色')
} catch {
ElMessage.error('刷新失败')
}
@@ -71,5 +90,7 @@ onMounted(() => {
margin-top: 8px;
color: #909399;
font-size: 14px;
line-height: 1.6;
max-width: 520px;
}
</style>

View File

@@ -143,11 +143,12 @@ import { ElMessage } from 'element-plus'
import { Refresh } from '@element-plus/icons-vue'
import request from '@/utils/request'
import ClubScopeHint from '@/components/ClubScopeHint.vue'
import { getMenuAccess, hasPermCode } from '@/utils/club-context'
const route = useRoute()
const router = useRouter()
const loading = ref(false)
const hasPermission = ref(true)
const hasPermission = ref(false)
const PAGE_SIZE = 20
const pageNum = ref(1)
@@ -363,6 +364,8 @@ const getStatusType = (code) => {
watch(() => route.query, () => { restoreFromQuery(); fetchOrders() }, { deep: true })
onMounted(() => {
hasPermission.value = hasPermCode(getMenuAccess(), '002ac')
if (!hasPermission.value) return
restoreFromQuery()
fetchOrderTypes()
fetchOrders()

View File

@@ -148,11 +148,12 @@ import { ElMessage } from 'element-plus'
import { Refresh } from '@element-plus/icons-vue'
import request from '@/utils/request'
import ClubScopeHint from '@/components/ClubScopeHint.vue'
import { getMenuAccess, hasPermCode } from '@/utils/club-context'
const route = useRoute()
const router = useRouter()
const loading = ref(false)
const hasPermission = ref(true)
const hasPermission = ref(false)
const PAGE_SIZE = 20
const pageNum = ref(1)
@@ -363,6 +364,8 @@ const getStatusType = (code) => {
watch(() => route.query, () => { restoreFromQuery(); fetchOrders() }, { deep: true })
onMounted(() => {
hasPermission.value = hasPermCode(getMenuAccess(), '002ab')
if (!hasPermission.value) return
restoreFromQuery()
fetchOrderTypes()
fetchOrders()