fix: 菜单与API权限码双重校验,切换俱乐部后不再展示无权限页面
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user