feat: 接入权限接口
This commit is contained in:
@@ -3,6 +3,7 @@ import { defineStore } from 'pinia'
|
||||
|
||||
import {
|
||||
changePassword as changePasswordRequest,
|
||||
getCurrentAuthorization,
|
||||
getCurrentUser,
|
||||
login as loginRequest,
|
||||
logout as logoutRequest,
|
||||
@@ -11,33 +12,27 @@ import type {
|
||||
AuthenticatedUser,
|
||||
LoginRequest,
|
||||
LoginResponse,
|
||||
CurrentAuthorization,
|
||||
PermissionResourceNode,
|
||||
PasswordChangeRequest,
|
||||
} from '@/types/auth'
|
||||
import { clearAuthStorage, readStoredAuthSession, saveAuthSession } from '@/utils/auth-storage'
|
||||
|
||||
const dataScopeLabels: Record<AuthenticatedUser['dataScope'], string> = {
|
||||
ALL: '全院',
|
||||
CAMPUS: '院区',
|
||||
DEPARTMENT: '科室',
|
||||
READ_ONLY_ALL: '全院只读',
|
||||
}
|
||||
|
||||
function getUserContext(user: AuthenticatedUser | null) {
|
||||
return user ? `${dataScopeLabels[user.dataScope]}数据范围` : '医签通用户'
|
||||
}
|
||||
|
||||
export const useLoginStore = defineStore('login', () => {
|
||||
const storedSession = readStoredAuthSession()
|
||||
const token = ref(storedSession?.token ?? '')
|
||||
const expiresAt = ref(storedSession?.expiresAt ?? '')
|
||||
const user = ref<AuthenticatedUser | null>(storedSession?.user ?? null)
|
||||
const authorization = ref<CurrentAuthorization | null>(null)
|
||||
const currentUserReady = ref(false)
|
||||
let currentUserRequest: Promise<AuthenticatedUser> | null = null
|
||||
let authorizationSyncedAt = 0
|
||||
|
||||
const isLoggedIn = computed(() => Boolean(token.value))
|
||||
const isReadOnly = computed(() => user.value?.dataScope === 'READ_ONLY_ALL')
|
||||
// Data scope belongs to roles now; it no longer determines whether a user can write.
|
||||
const isReadOnly = computed(() => false)
|
||||
const userName = computed(() => user.value?.displayName || user.value?.username || '医护用户')
|
||||
const department = computed(() => getUserContext(user.value))
|
||||
const department = computed(() => user.value?.departmentName || '医签通用户')
|
||||
|
||||
function hasPermission(required: string | string[]) {
|
||||
const requiredPermissions = Array.isArray(required) ? required : [required]
|
||||
@@ -46,12 +41,57 @@ export const useLoginStore = defineStore('login', () => {
|
||||
return true
|
||||
}
|
||||
|
||||
const permissions = user.value?.permissions ?? []
|
||||
return requiredPermissions.every(
|
||||
(permission) => permissions.includes(permission) || permissions.includes('*'),
|
||||
const permissions = authorization.value?.permissions ?? user.value?.permissions ?? []
|
||||
const legacyAliases: Record<string, string> = {
|
||||
'sign:task:query': 'sign:task:view',
|
||||
'sign:template:query': 'doc:template:view',
|
||||
'sign:template:create': 'doc:template:create',
|
||||
'sign:template:update': 'doc:template:edit',
|
||||
'sign:template:publish': 'doc:template:publish',
|
||||
'report:signing:query': 'report:sign:view',
|
||||
'report:signing:export': 'report:sign:export',
|
||||
'system:user:query': 'system:org:view',
|
||||
'system:user:create': 'system:org:emp',
|
||||
'system:user:update': 'system:org:emp',
|
||||
'system:user:disable': 'system:org:emp',
|
||||
'system:user:password:reset': 'system:org:pwd',
|
||||
'system:role:permission:query': 'system:role-permission:view',
|
||||
'system:role:permission:update': 'system:role-permission:edit',
|
||||
'audit:log:query': 'audit:log:view',
|
||||
}
|
||||
|
||||
return requiredPermissions.every((permission) => {
|
||||
const alias = legacyAliases[permission]
|
||||
return (
|
||||
permissions.includes(permission) ||
|
||||
(alias !== undefined && permissions.includes(alias)) ||
|
||||
permissions.includes('*')
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
const accessibleMenus = computed(() =>
|
||||
(authorization.value?.navigation ?? [])
|
||||
.filter((directory) => directory.resourceType === 'DIR' && directory.status === 'ENABLED')
|
||||
.flatMap((directory) => directory.children ?? []),
|
||||
)
|
||||
|
||||
function menuForPath(path: string): PermissionResourceNode | null {
|
||||
const normalizedPath = path.replace(/\/$/, '') || '/'
|
||||
return (
|
||||
accessibleMenus.value.find((menu) => menu.routePath?.replace(/\/$/, '') === normalizedPath) ??
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
const firstAuthorizedPath = computed(
|
||||
() =>
|
||||
accessibleMenus.value.find(
|
||||
(menu) =>
|
||||
menu.visible && menu.status === 'ENABLED' && menu.code && hasPermission(menu.code),
|
||||
)?.routePath ?? null,
|
||||
)
|
||||
|
||||
function hasWritePermission(required: string | string[]) {
|
||||
return !isReadOnly.value && hasPermission(required)
|
||||
}
|
||||
@@ -61,6 +101,8 @@ export const useLoginStore = defineStore('login', () => {
|
||||
token.value = session.token
|
||||
expiresAt.value = session.expiresAt
|
||||
user.value = session.user
|
||||
authorization.value = null
|
||||
authorizationSyncedAt = 0
|
||||
currentUserReady.value = false
|
||||
}
|
||||
|
||||
@@ -81,6 +123,8 @@ export const useLoginStore = defineStore('login', () => {
|
||||
token.value = ''
|
||||
expiresAt.value = ''
|
||||
user.value = null
|
||||
authorization.value = null
|
||||
authorizationSyncedAt = 0
|
||||
currentUserReady.value = false
|
||||
currentUserRequest = null
|
||||
}
|
||||
@@ -92,9 +136,25 @@ export const useLoginStore = defineStore('login', () => {
|
||||
}
|
||||
|
||||
async function refreshCurrentUser() {
|
||||
const currentUser = await getCurrentUser()
|
||||
setCurrentUser(currentUser)
|
||||
return currentUser
|
||||
const [currentUser, currentAuthorization] = await Promise.all([
|
||||
getCurrentUser(),
|
||||
getCurrentAuthorization(),
|
||||
])
|
||||
const effectiveUser = { ...currentUser, permissions: currentAuthorization.permissions }
|
||||
authorization.value = currentAuthorization
|
||||
authorizationSyncedAt = Date.now()
|
||||
setCurrentUser(effectiveUser)
|
||||
return effectiveUser
|
||||
}
|
||||
|
||||
async function refreshAuthorization() {
|
||||
const currentAuthorization = await getCurrentAuthorization()
|
||||
authorization.value = currentAuthorization
|
||||
authorizationSyncedAt = Date.now()
|
||||
if (user.value) {
|
||||
setCurrentUser({ ...user.value, permissions: currentAuthorization.permissions })
|
||||
}
|
||||
return currentAuthorization
|
||||
}
|
||||
|
||||
async function ensureCurrentUser() {
|
||||
@@ -102,7 +162,12 @@ export const useLoginStore = defineStore('login', () => {
|
||||
return null
|
||||
}
|
||||
|
||||
if (currentUserReady.value && user.value) {
|
||||
if (
|
||||
currentUserReady.value &&
|
||||
user.value &&
|
||||
authorization.value &&
|
||||
Date.now() - authorizationSyncedAt < 10_000
|
||||
) {
|
||||
return user.value
|
||||
}
|
||||
|
||||
@@ -142,6 +207,8 @@ export const useLoginStore = defineStore('login', () => {
|
||||
token,
|
||||
expiresAt,
|
||||
user,
|
||||
authorization,
|
||||
accessibleMenus,
|
||||
isLoggedIn,
|
||||
isReadOnly,
|
||||
userName,
|
||||
@@ -149,6 +216,9 @@ export const useLoginStore = defineStore('login', () => {
|
||||
login,
|
||||
refreshCurrentUser,
|
||||
ensureCurrentUser,
|
||||
refreshAuthorization,
|
||||
menuForPath,
|
||||
firstAuthorizedPath,
|
||||
hasPermission,
|
||||
hasWritePermission,
|
||||
changePassword,
|
||||
|
||||
Reference in New Issue
Block a user