初步完成merchant和admin不同role的展示内容逻辑

This commit is contained in:
2026-03-12 10:18:04 +08:00
parent c90b549b89
commit f19dd093bf
13 changed files with 853 additions and 110 deletions

View File

@@ -116,7 +116,7 @@ import {
import type { TabItem } from '@/layouts/admin/store/adminNavStore.uts'
import { getComponent } from '@/layouts/admin/router/adminComponentMap.uts'
import { hasAdminModuleAccess } from '@/layouts/admin/utils/role.uts'
import { hasAdminModuleAccess, refreshAdminRole } from '@/layouts/admin/utils/role.uts'
const props = defineProps({
currentPage: {
@@ -389,7 +389,8 @@ function onNotify(): void {
let resizeTid: any = null
onMounted(() => {
onMounted(async () => {
await refreshAdminRole()
initNavState()
if (props.currentPage != '') {
openRoute(props.currentPage as string)

View File

@@ -62,6 +62,7 @@ import {
openRoute
} from '@/layouts/admin/store/adminNavStore.uts'
import { state, logout } from '@/utils/store.uts'
import { clearAdminRoleCache } from '@/layouts/admin/utils/role.uts'
const showUserMenu = ref(false)
const userName = computed((): string => state.userProfile.username || state.userProfile.email || 'admin')
@@ -116,7 +117,7 @@ function handleLogout(e: any) {
success: (res) => {
if (res.confirm) {
logout()
uni.removeStorageSync('adminRole')
clearAdminRoleCache()
uni.removeStorageSync('token')
// Force the layout cleanup to wait for the dialog to disappear
setTimeout(() => {

View File

@@ -1,49 +1,136 @@
// Admin role-based access control
export function getCurrentAdminRole(): string {
// 从本地存储获取当前 role。为不影响全局优先读 admin_role默认降级到读全局 role/userInfo再默认
// 根据实际系统中的 token/userInfo 结构可灵活调整
const roleType = uni.getStorageSync('admin_role') as string
if (roleType && typeof roleType === 'string' && roleType.length > 0) {
return roleType
}
// 检查是否有关联 merchant_id 或者是存了全局 role
const merchantId = uni.getStorageSync('merchant_id')
if (merchantId) return 'merchant'
const globalRole = uni.getStorageSync('role') as string
if (globalRole && typeof globalRole === 'string' && globalRole.length > 0) {
return globalRole
}
return 'admin' // 默认返回 admin 作为兜底(兼容原有全部显示逻辑)
import { state, getCurrentUser } from '@/utils/store.uts'
import supa from '@/components/supadb/aksupainstance.uts'
/**
* 将任意角色类型的原始值格式化为标准化的应用角色
*/
export function normalizeRole(rawRole: any | null): string {
if (rawRole == null || rawRole === undefined) return 'unknown'
const roleStr = String(rawRole).trim().toLowerCase()
if (roleStr === 'admin') return 'admin'
if (roleStr === 'merchant') return 'merchant'
return 'unknown'
}
// 获取不同 role 允许访问的顶级模块 ID (主侧边栏的顶层菜单 id)
export function getVisibleTopMenuIds(role: string): string[] {
if (role === 'admin') {
return ['home', 'user', 'order', 'product', 'marketing', 'distribution', 'kefu', 'finance', 'cms', 'decoration', 'app', 'setting', 'maintain']
/**
* 判断是否为纯后台管理员
*/
export function isAdminRole(role: string): boolean {
return normalizeRole(role) === 'admin'
}
/**
* 判断是否为商户角色
*/
export function isMerchantRole(role: string): boolean {
return normalizeRole(role) === 'merchant'
}
/**
* 获取当前的标准化角色 (同步方法)
*/
export function getCurrentAdminRole(): string {
// 1. 最高优先级:当前响应式内存 userProfile已查数据库
if (state.userProfile != null && state.userProfile!.role != null) {
const memRole = normalizeRole(state.userProfile!.role)
if (memRole === 'admin' || memRole === 'merchant') {
return memRole
}
}
// 2. 缓存兜底:为了刷新页面时立刻渲染,读取最新的 adminRole 缓存
const cachedRole = uni.getStorageSync('adminRole')
if (cachedRole != null && cachedRole != '') {
const normCached = normalizeRole(cachedRole)
if (normCached === 'admin' || normCached === 'merchant') {
return normCached
}
}
if (role === 'merchant') {
// merchant: 只能看到 主页、订单、商品、营销、财务
// 兼容旧的缓存字段以防万一
const oldCached = uni.getStorageSync('admin_role')
if (oldCached != null && oldCached != '') {
const normOld = normalizeRole(oldCached)
if (normOld === 'admin' || normOld === 'merchant') {
return normOld
}
}
console.warn('[AdminRole] 未能获取到有效的管理端角色,准备安全降级...')
return 'unknown'
}
/**
* 清理本地相关角色和管理端缓存 (登出时调用)
*/
export function clearAdminRoleCache(): void {
// 清理 admin 专属
uni.removeStorageSync('adminRole')
uni.removeStorageSync('admin_role')
}
/**
* 校验并写入最新的 adminRole (用于 Login 后或者 Layout 挂载时强制刷新)
*/
export async function refreshAdminRole(): Promise<string> {
const userStrProfile = await getCurrentUser()
let finalRole = 'unknown'
if (userStrProfile != null && userStrProfile.role != null) {
finalRole = normalizeRole(userStrProfile.role)
console.log('[AdminRole] 从 ak_users 读取真实身份成功:', finalRole)
} else {
// metadata fallback
const sessionInfo = supa.getSession()
if (sessionInfo.user != null) {
const meta = sessionInfo.user?.get("user_metadata") as UTSJSONObject | null
if (meta != null && meta.getString('role') != null) {
finalRole = normalizeRole(meta.getString('role'))
console.log('[AdminRole] 从 Auth Metadata 读取兜底身份:', finalRole)
}
}
}
if (finalRole !== 'unknown') {
uni.setStorageSync('adminRole', finalRole)
if (state.userProfile != null) {
state.userProfile!.role = finalRole
}
console.log('[AdminRole] 最新角色已写入状态和缓存:', finalRole)
}
return finalRole
}
export function getVisibleTopMenuIds(role: string): string[] {
const normRole = normalizeRole(role)
if (normRole === 'admin') {
return ['home', 'user', 'order', 'product', 'marketing', 'distribution', 'kefu', 'finance', 'cms', 'decoration', 'app', 'setting', 'maintain']
}
if (normRole === 'merchant') {
return ['home', 'order', 'product', 'marketing', 'finance']
}
// 其他 role: 安全兜底
return ['home']
}
// 判断是否有权限访问某模块或路由
export function hasAdminModuleAccess(moduleId: string | undefined): boolean {
if (!moduleId) return true // 没有指定的通常认为是公共的,比如一些通用组件
export function hasAdminModuleAccess(moduleId: string | undefined): boolean {
if (!moduleId) return true
const role = getCurrentAdminRole()
if (role === 'admin') return true
const normRole = normalizeRole(role)
if (normRole === 'unknown') {
return moduleId === 'home'
}
if (normRole === 'admin') {
return true
}
// 对于 merchant 角色,允许其访问的顶级 menu id 及其所属路由
if (role === 'merchant') {
const allowed = ['home', 'order', 'product', 'marketing', 'finance']
if (normRole === 'merchant') {
const allowed = ['home', 'order', 'product', 'marketing', 'finance']
return allowed.includes(moduleId)
}