refactor(auth): 将认证状态迁移至独立登录 store
This commit is contained in:
@@ -1,7 +1,6 @@
|
|||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
|
|
||||||
import { request } from '@/utils/request'
|
import { request } from '@/utils/request'
|
||||||
import { saveAuthSession } from '@/utils/auth-storage'
|
|
||||||
import type {
|
import type {
|
||||||
AuthenticatedUser,
|
AuthenticatedUser,
|
||||||
CurrentUserResponse,
|
CurrentUserResponse,
|
||||||
@@ -78,7 +77,6 @@ export async function login(payload: LoginRequest): Promise<LoginResponse> {
|
|||||||
user: normalizeAuthenticatedUser(session.user),
|
user: normalizeAuthenticatedUser(session.user),
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAuthSession(normalizedSession)
|
|
||||||
return normalizedSession
|
return normalizedSession
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { RouterLink, useRouter } from 'vue-router'
|
import { RouterLink, useRouter } from 'vue-router'
|
||||||
|
|
||||||
import { logout as logoutRequest } from '@/api/auth'
|
import { useLoginStore } from '@/stores/login'
|
||||||
import { useAppStore } from '@/stores/app'
|
|
||||||
|
|
||||||
interface MenuItem {
|
interface MenuItem {
|
||||||
path: string
|
path: string
|
||||||
@@ -14,7 +13,7 @@ interface MenuGroup {
|
|||||||
items: MenuItem[]
|
items: MenuItem[]
|
||||||
}
|
}
|
||||||
|
|
||||||
const appStore = useAppStore()
|
const loginStore = useLoginStore()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
|
|
||||||
const menuGroups: MenuGroup[] = [
|
const menuGroups: MenuGroup[] = [
|
||||||
@@ -39,11 +38,10 @@ const menuGroups: MenuGroup[] = [
|
|||||||
|
|
||||||
async function handleLogout() {
|
async function handleLogout() {
|
||||||
try {
|
try {
|
||||||
await logoutRequest()
|
await loginStore.logout()
|
||||||
} catch {
|
} catch {
|
||||||
// 即使服务暂时不可用,也要清理本地会话。
|
// 即使服务暂时不可用,也要清理本地会话。
|
||||||
} finally {
|
} finally {
|
||||||
appStore.logout()
|
|
||||||
await router.push('/login')
|
await router.push('/login')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,10 +90,10 @@ async function handleLogout() {
|
|||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="menu-profile">
|
<div class="menu-profile">
|
||||||
<div class="menu-profile-avatar">{{ appStore.userName.slice(0, 1) }}</div>
|
<div class="menu-profile-avatar">{{ loginStore.userName.slice(0, 1) }}</div>
|
||||||
<div class="menu-profile-copy">
|
<div class="menu-profile-copy">
|
||||||
<strong>{{ appStore.userName }}</strong>
|
<strong>{{ loginStore.userName }}</strong>
|
||||||
<span>{{ appStore.department }}</span>
|
<span>{{ loginStore.department }}</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="menu-logout" type="button" @click="handleLogout">退出</button>
|
<button class="menu-logout" type="button" @click="handleLogout">退出</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHistory } from 'vue-router'
|
||||||
|
|
||||||
import { useAppStore } from '@/stores/app'
|
|
||||||
import ClinicalLayout from '@/layouts/ClinicalLayout.vue'
|
import ClinicalLayout from '@/layouts/ClinicalLayout.vue'
|
||||||
import LoginView from '@/views/auth/LoginView.vue'
|
import LoginView from '@/views/auth/LoginView.vue'
|
||||||
import DocumentPermissionsView from '@/views/management/document-permissions/index.vue'
|
import DocumentPermissionsView from '@/views/management/document-permissions/index.vue'
|
||||||
@@ -10,6 +9,7 @@ import SettingsView from '@/views/management/settings/index.vue'
|
|||||||
import UserPermissionsView from '@/views/management/users/index.vue'
|
import UserPermissionsView from '@/views/management/users/index.vue'
|
||||||
import ConsentTasksView from '@/views/workbench/signing/index.vue'
|
import ConsentTasksView from '@/views/workbench/signing/index.vue'
|
||||||
import DashboardView from '@/views/workbench/home/index.vue'
|
import DashboardView from '@/views/workbench/home/index.vue'
|
||||||
|
import { useLoginStore } from '@/stores/login'
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(import.meta.env.BASE_URL),
|
history: createWebHistory(import.meta.env.BASE_URL),
|
||||||
@@ -93,13 +93,13 @@ const router = createRouter({
|
|||||||
})
|
})
|
||||||
|
|
||||||
router.beforeEach((to) => {
|
router.beforeEach((to) => {
|
||||||
const appStore = useAppStore()
|
const loginStore = useLoginStore()
|
||||||
|
|
||||||
if (to.name === 'login' && appStore.isLoggedIn) {
|
if (to.name === 'login' && loginStore.isLoggedIn) {
|
||||||
return { name: 'workbench-home' }
|
return { name: 'workbench-home' }
|
||||||
}
|
}
|
||||||
|
|
||||||
if (to.meta.requiresAuth && !appStore.isLoggedIn) {
|
if (to.meta.requiresAuth && !loginStore.isLoggedIn) {
|
||||||
return {
|
return {
|
||||||
name: 'login',
|
name: 'login',
|
||||||
query: { redirect: to.fullPath },
|
query: { redirect: to.fullPath },
|
||||||
|
|||||||
@@ -1,60 +1,16 @@
|
|||||||
|
import { ref } from 'vue'
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
import type { WorkbenchCampus } from '@/api/workbench/types'
|
import type { WorkbenchCampus } from '@/api/workbench/types'
|
||||||
import type { AuthenticatedUser, LoginResponse } from '@/types/auth'
|
|
||||||
import { clearAuthStorage, readStoredAuthSession, saveAuthSession } from '@/utils/auth-storage'
|
|
||||||
|
|
||||||
const dataScopeLabels: Record<AuthenticatedUser['dataScope'], string> = {
|
export const useAppStore = defineStore('app', () => {
|
||||||
ALL: '全院',
|
const environment = ref('接口联调环境')
|
||||||
CAMPUS: '院区',
|
const campuses = ref<WorkbenchCampus[]>(['本部院区', '东院区', '西院区'])
|
||||||
DEPARTMENT: '科室',
|
const selectedCampus = ref<WorkbenchCampus>('本部院区')
|
||||||
READ_ONLY_ALL: '全院只读',
|
|
||||||
}
|
|
||||||
|
|
||||||
function getUserContext(user: AuthenticatedUser | null) {
|
return {
|
||||||
return user ? `${dataScopeLabels[user.dataScope]}数据范围` : '医签通用户'
|
environment,
|
||||||
}
|
campuses,
|
||||||
|
selectedCampus,
|
||||||
export const useAppStore = defineStore('app', {
|
}
|
||||||
state: () => {
|
|
||||||
const session = readStoredAuthSession()
|
|
||||||
const user = session?.user ?? null
|
|
||||||
|
|
||||||
return {
|
|
||||||
isLoggedIn: Boolean(session?.token),
|
|
||||||
token: session?.token ?? '',
|
|
||||||
expiresAt: session?.expiresAt ?? '',
|
|
||||||
user,
|
|
||||||
userName: user?.displayName || user?.username || '医护用户',
|
|
||||||
department: getUserContext(user),
|
|
||||||
environment: '接口联调环境',
|
|
||||||
campuses: ['本部院区', '东院区', '西院区'] as WorkbenchCampus[],
|
|
||||||
selectedCampus: '本部院区' as WorkbenchCampus,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
actions: {
|
|
||||||
login(session: LoginResponse) {
|
|
||||||
saveAuthSession(session)
|
|
||||||
this.token = session.token
|
|
||||||
this.expiresAt = session.expiresAt
|
|
||||||
this.user = session.user
|
|
||||||
this.userName = session.user.displayName || session.user.username || '医护用户'
|
|
||||||
this.department = getUserContext(session.user)
|
|
||||||
this.isLoggedIn = true
|
|
||||||
},
|
|
||||||
updateCurrentUser(user: AuthenticatedUser) {
|
|
||||||
this.user = user
|
|
||||||
this.userName = user.displayName || user.username || '医护用户'
|
|
||||||
this.department = getUserContext(user)
|
|
||||||
},
|
|
||||||
logout() {
|
|
||||||
clearAuthStorage()
|
|
||||||
this.isLoggedIn = false
|
|
||||||
this.token = ''
|
|
||||||
this.expiresAt = ''
|
|
||||||
this.user = null
|
|
||||||
this.userName = '医护用户'
|
|
||||||
this.department = getUserContext(null)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
})
|
||||||
|
|||||||
87
clinical-web/src/stores/login.ts
Normal file
87
clinical-web/src/stores/login.ts
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
import { computed, ref } from 'vue'
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
|
||||||
|
import { getCurrentUser, login as loginRequest, logout as logoutRequest } from '@/api/auth'
|
||||||
|
import type { AuthenticatedUser, LoginRequest, LoginResponse } 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 isLoggedIn = computed(() => Boolean(token.value))
|
||||||
|
const userName = computed(() => user.value?.displayName || user.value?.username || '医护用户')
|
||||||
|
const department = computed(() => getUserContext(user.value))
|
||||||
|
|
||||||
|
function setSession(session: LoginResponse) {
|
||||||
|
saveAuthSession(session)
|
||||||
|
token.value = session.token
|
||||||
|
expiresAt.value = session.expiresAt
|
||||||
|
user.value = session.user
|
||||||
|
}
|
||||||
|
|
||||||
|
function setCurrentUser(nextUser: AuthenticatedUser) {
|
||||||
|
user.value = nextUser
|
||||||
|
|
||||||
|
if (token.value) {
|
||||||
|
saveAuthSession({
|
||||||
|
token: token.value,
|
||||||
|
expiresAt: expiresAt.value,
|
||||||
|
user: nextUser,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearSession() {
|
||||||
|
clearAuthStorage()
|
||||||
|
token.value = ''
|
||||||
|
expiresAt.value = ''
|
||||||
|
user.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
async function login(payload: LoginRequest) {
|
||||||
|
const session = await loginRequest(payload)
|
||||||
|
setSession(session)
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
|
||||||
|
async function refreshCurrentUser() {
|
||||||
|
const currentUser = await getCurrentUser()
|
||||||
|
setCurrentUser(currentUser)
|
||||||
|
return currentUser
|
||||||
|
}
|
||||||
|
|
||||||
|
async function logout() {
|
||||||
|
try {
|
||||||
|
await logoutRequest()
|
||||||
|
} finally {
|
||||||
|
clearSession()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
token,
|
||||||
|
expiresAt,
|
||||||
|
user,
|
||||||
|
isLoggedIn,
|
||||||
|
userName,
|
||||||
|
department,
|
||||||
|
login,
|
||||||
|
refreshCurrentUser,
|
||||||
|
logout,
|
||||||
|
clearSession,
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -2,12 +2,12 @@
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
|
||||||
import { getAuthErrorMessage, login as loginRequest } from '@/api/auth'
|
import { getAuthErrorMessage } from '@/api/auth'
|
||||||
import { useAppStore } from '@/stores/app'
|
import { useLoginStore } from '@/stores/login'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const appStore = useAppStore()
|
const loginStore = useLoginStore()
|
||||||
|
|
||||||
const username = ref('')
|
const username = ref('')
|
||||||
const password = ref('')
|
const password = ref('')
|
||||||
@@ -33,12 +33,11 @@ async function handleLogin() {
|
|||||||
isSubmitting.value = true
|
isSubmitting.value = true
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const session = await loginRequest({
|
await loginStore.login({
|
||||||
username: normalizedUsername,
|
username: normalizedUsername,
|
||||||
password: password.value,
|
password: password.value,
|
||||||
})
|
})
|
||||||
|
|
||||||
appStore.login(session)
|
|
||||||
await router.replace(getRedirectPath())
|
await router.replace(getRedirectPath())
|
||||||
} catch (error: unknown) {
|
} catch (error: unknown) {
|
||||||
errorMessage.value = getAuthErrorMessage(error)
|
errorMessage.value = getAuthErrorMessage(error)
|
||||||
|
|||||||
Reference in New Issue
Block a user