From d97be506ce2f31ca766fde6871bcf16145e035e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=A4=A9=E9=AA=84?= <5307576@qq.com> Date: Fri, 5 Jun 2026 16:20:01 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=99=BB=E5=BD=95=E8=81=94=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/auth.ts | 80 +++++++++++++++++++++++++++++++++++++ src/assets/styles/main.scss | 30 +------------- src/stores/app.ts | 35 +++++++++++++--- src/views/LoginView.vue | 62 ++++++++++++++++------------ vite.config.ts | 8 +++- 5 files changed, 155 insertions(+), 60 deletions(-) create mode 100644 src/api/auth.ts diff --git a/src/api/auth.ts b/src/api/auth.ts new file mode 100644 index 00000000..a85c1218 --- /dev/null +++ b/src/api/auth.ts @@ -0,0 +1,80 @@ +export type LoginRole = 'super_admin' | 'doctor' + +export interface LoginPayload { + account: string + password: string + role: LoginRole +} + +export interface LoginResult { + token: string + raw: unknown +} + +function getTokenFromResponse(data: unknown): string { + if (!data || typeof data !== 'object') { + return '' + } + + const record = data as Record + const directToken = record.token || record.access_token || record.accessToken + if (typeof directToken === 'string') { + return directToken + } + + const nested = record.data + if (nested && typeof nested === 'object') { + return getTokenFromResponse(nested) + } + + return '' +} + +function getMessageFromResponse(data: unknown): string { + if (!data || typeof data !== 'object') { + return '' + } + + const record = data as Record + const message = record.message || record.msg || record.detail + if (typeof message === 'string') { + return message + } + + return getMessageFromResponse(record.data) +} + +function parseResponseText(text: string): unknown { + if (!text) { + return null + } + + try { + return JSON.parse(text) + } catch { + return null + } +} + +export async function login(payload: LoginPayload): Promise { + const response = await fetch('/server/api/user/auth/login/', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + }) + + const text = await response.text() + const data = parseResponseText(text) + + if (!response.ok) { + const message = getMessageFromResponse(data) || '登录失败,请检查账号、密码和角色' + throw new Error(message) + } + + return { + token: getTokenFromResponse(data), + raw: data + } +} diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss index 61dfd3b7..53edaa33 100644 --- a/src/assets/styles/main.scss +++ b/src/assets/styles/main.scss @@ -178,38 +178,12 @@ p { } } -.login-options { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 22px; -} - .login-submit { width: 100%; } -.login-divider { - display: flex; - align-items: center; - gap: 12px; - margin: 28px 0 18px; - color: var(--muted); - font-size: 13px; - - &::before, - &::after { - flex: 1; - height: 1px; - content: ""; - background: var(--border); - } -} - -.login-methods { - display: grid; - grid-template-columns: repeat(2, 1fr); - gap: 12px; +.login-role-select { + width: 100%; } .admin-shell { diff --git a/src/stores/app.ts b/src/stores/app.ts index 34e8c0e1..90eadf48 100644 --- a/src/stores/app.ts +++ b/src/stores/app.ts @@ -1,5 +1,6 @@ import { defineStore } from 'pinia' import { computed, ref } from 'vue' +import type { LoginRole } from '@/api/auth' import type { RoleKey, UserProfile } from '@/types' import { roleOptions } from '@/mock/dashboard' @@ -10,34 +11,55 @@ const roleProfiles: Record> = teacher: { name: '陈医生', avatarText: '陈' } } +const loginRoleMap: Record = { + super_admin: 'super-admin', + doctor: 'teacher' +} + +function getStoredRole(): RoleKey { + const storedRole = localStorage.getItem('mediai-role') + return storedRole && storedRole in roleProfiles ? (storedRole as RoleKey) : 'super-admin' +} + export const useAppStore = defineStore('app', () => { const token = ref(localStorage.getItem('mediai-token') || '') const collapsed = ref(false) const darkMode = ref(false) + const storedRole = getStoredRole() const user = ref({ - name: '张管理员', - avatarText: '张', - role: 'super-admin' + name: roleProfiles[storedRole].name, + avatarText: roleProfiles[storedRole].avatarText, + role: storedRole }) const roleLabel = computed(() => roleOptions.find(item => item.value === user.value.role)?.label || '管理员') const isLoggedIn = computed(() => Boolean(token.value)) - function login(username: string) { - token.value = `mock-token-${username}` + function login(username: string, role: LoginRole, authToken: string) { + const appRole = loginRoleMap[role] + token.value = authToken || `session-${username}` localStorage.setItem('mediai-token', token.value) + localStorage.setItem('mediai-role', appRole) + user.value.role = appRole user.value.name = username === 'admin' ? '张管理员' : username + user.value.avatarText = user.value.name.slice(0, 1).toUpperCase() } function logout() { token.value = '' localStorage.removeItem('mediai-token') + localStorage.removeItem('mediai-role') } function switchRole(role: RoleKey) { user.value.role = role user.value.name = roleProfiles[role].name user.value.avatarText = roleProfiles[role].avatarText + localStorage.setItem('mediai-role', role) + } + + function getRoleByLoginRole(role: LoginRole) { + return loginRoleMap[role] } return { @@ -49,6 +71,7 @@ export const useAppStore = defineStore('app', () => { isLoggedIn, login, logout, - switchRole + switchRole, + getRoleByLoginRole } }) diff --git a/src/views/LoginView.vue b/src/views/LoginView.vue index 8fb5245a..6b763518 100644 --- a/src/views/LoginView.vue +++ b/src/views/LoginView.vue @@ -26,24 +26,19 @@ - - + + - + + + - - - @@ -51,8 +46,11 @@ diff --git a/vite.config.ts b/vite.config.ts index a0d2590c..eee245a5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -10,6 +10,12 @@ export default defineConfig({ } }, server: { - port: 5173 + port: 5173, + proxy: { + '/server': { + target: 'http://8.160.178.88/', + changeOrigin: true + } + } } })