From 7d627a45fbad4ad3501c8b4af139fe0cb1d7c0fe Mon Sep 17 00:00:00 2001 From: Chaos Date: Tue, 25 Nov 2025 23:33:32 +0800 Subject: [PATCH] =?UTF-8?q?feat(auth):=20=E5=AE=9E=E7=8E=B0=E5=9F=BA?= =?UTF-8?q?=E4=BA=8E=E4=BB=A4=E7=89=8C=E7=9A=84=E7=94=A8=E6=88=B7=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E5=92=8C=E8=AE=BF=E9=97=AE=E6=8E=A7=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在用户相关页面服务端加载函数中添加令牌检查,防止未授权访问 - 更新用户服务方法以支持携带认证令牌请求API - 修改用户资料和用户列表组件以适配新的认证流程 - 引入侧边栏状态管理并在布局中注册上下文 - 调整HTTP客户端逻辑以正确传递请求头信息 - 更新用户类型定义以匹配后端返回的角色结构 - 优化应用头部和侧边栏组件的UI细节和交互逻辑 --- src/lib/api/httpClient.ts | 20 +- src/lib/api/services/userService.ts | 13 +- .../components/layout/app/AppHeader.svelte | 21 +- .../components/layout/app/AppSidebar.svelte | 416 ++++++++++++++---- src/lib/stores/sidebar.svelte.ts | 20 + src/lib/types/user.ts | 7 +- src/routes/+layout.svelte | 2 + .../app/settings/auth/users/+page.server.ts | 13 +- .../app/settings/auth/users/+page.svelte | 101 +++-- src/routes/app/user/+page.server.ts | 16 + src/routes/app/user/+page.svelte | 9 + src/routes/app/user/[id]/+page.server.ts | 8 + src/routes/app/user/[id]/+page.svelte | 13 + src/routes/auth/login/+page.svelte | 1 + 14 files changed, 523 insertions(+), 137 deletions(-) create mode 100644 src/lib/stores/sidebar.svelte.ts create mode 100644 src/routes/app/user/+page.server.ts create mode 100644 src/routes/app/user/+page.svelte create mode 100644 src/routes/app/user/[id]/+page.server.ts create mode 100644 src/routes/app/user/[id]/+page.svelte diff --git a/src/lib/api/httpClient.ts b/src/lib/api/httpClient.ts index 97db7b9..6ae9f77 100644 --- a/src/lib/api/httpClient.ts +++ b/src/lib/api/httpClient.ts @@ -14,6 +14,7 @@ const API_BASE_URL = import.meta.env.VITE_PUBLIC_API_URL || 'http://localhost:18 const normalizeHeaders = (headers?: HeadersInit):Record =>{ const result:Record = {}; + console.log('normalizeHeaders', headers); if (!headers){ return result; } @@ -28,9 +29,14 @@ const normalizeHeaders = (headers?: HeadersInit):Record =>{ }) }else { Object.keys(headers).forEach(key => { - result[key.toLowerCase()] = headers[key.toLowerCase()] as string; + const value = (headers as Record)[key]; + if (value !== undefined && value !== null) { + result[key.toLowerCase()] = value; + } }) } + + console.log('normalizeHeaders result:', result); return result; } export class HttpError extends Error { @@ -59,13 +65,14 @@ const httpRequest = async ( const fullUrl = `${API_BASE_URL}${url}`; const { body, headers, ...rest } = options; + + const requestHeaders: Record = normalizeHeaders(headers); let requestBody: BodyInit | undefined; const canHaveBody = method !== 'GET' ; if (canHaveBody) { - console.log('body', body); if (body instanceof FormData) { requestBody = body; } else if (body) { @@ -74,12 +81,11 @@ const httpRequest = async ( } } - // ... Token 处理逻辑保持不变 ... - // if (currentToken && currentTokenHead) { - // requestHeaders['authorization'] = `${currentTokenHead} ${currentToken}`; - // } + try { + + const response = await fetch(fullUrl, { method, headers: requestHeaders, @@ -89,6 +95,8 @@ const httpRequest = async ( ...rest }); + console.log('response', response); + if (!response.ok) { let errorDetail; try { diff --git a/src/lib/api/services/userService.ts b/src/lib/api/services/userService.ts index 7a64817..a4c9deb 100644 --- a/src/lib/api/services/userService.ts +++ b/src/lib/api/services/userService.ts @@ -3,18 +3,23 @@ import type { UserProfile } from '$lib/types/user.ts'; import type { PageResult } from '$lib/types/dataTable.ts'; export const userService = { - getUserProfile: async ({ tokenHead, token}) => { - const response = await api.get('/user/profile'); + getUserProfile: async (token:string) => { + const response = await api.get('/user/profile', {headers: {Authorization: `${token}`}}); if (response.code != 200 || !response.data){ throw new Error(response.msg); } return response.data; }, - getAllUsers: async ({ page, size}: { page: number, size: number}) => { + getAllUsers: async ({ page, size,token}: { page: number, size: number, token:string}) => { const formData = new FormData(); formData.append('pageNum', page.toString()); formData.append('pageSize', size.toString()); - const response = await api.get[]>('/user/all', {body: formData}); + const response = await api.get>( + '/user/all', + { + body: formData, + headers: {Authorization: `${token}`} + }); if (response.code != 200 || !response.data){ throw new Error(response.msg); } diff --git a/src/lib/components/layout/app/AppHeader.svelte b/src/lib/components/layout/app/AppHeader.svelte index 7a94d5b..743ee3e 100644 --- a/src/lib/components/layout/app/AppHeader.svelte +++ b/src/lib/components/layout/app/AppHeader.svelte @@ -2,21 +2,24 @@ import { goto } from '$app/navigation'; import { resolve } from '$app/paths'; import { page } from '$app/state'; - import Icon from '$lib/components/icon/Icon.svelte'; import ThemeSelector from '$lib/widget/ThemeSelector.svelte';
- + + + + + + +
+ + +
@@ -25,11 +28,11 @@
{#if page.data.user.avatar} Avatar diff --git a/src/lib/components/layout/app/AppSidebar.svelte b/src/lib/components/layout/app/AppSidebar.svelte index 79a06ce..4313661 100644 --- a/src/lib/components/layout/app/AppSidebar.svelte +++ b/src/lib/components/layout/app/AppSidebar.svelte @@ -1,272 +1,522 @@ + + {#snippet menuItem(item: ProcessedNavItem)} +
  • + {#if item.subItems && item.subItems.length > 0} +
    + + {#if item.icon} + + {/if} + {item.label} + +
      + {#each item.subItems as subItem (subItem.id)} + + {@render menuItem(subItem)} + {/each} +
    +
    + {:else} + + {#if item.icon} + + {:else} + + + {/if} + {item.label} + + {/if} +
  • + {/snippet} -
    + role="button" - + \ No newline at end of file diff --git a/src/lib/stores/sidebar.svelte.ts b/src/lib/stores/sidebar.svelte.ts new file mode 100644 index 0000000..e69867e --- /dev/null +++ b/src/lib/stores/sidebar.svelte.ts @@ -0,0 +1,20 @@ +export class SidebarState { + isSidebarExpanded = $state(true); + + constructor(initialIsSidebarExpanded = true) { + this.isSidebarExpanded = initialIsSidebarExpanded; + } + toggleSidebar = ()=> { + this.isSidebarExpanded = !this.isSidebarExpanded; + } + + closeSidebar() { + this.isSidebarExpanded = false; + } + + openSidebar() { + this.isSidebarExpanded = true; + } +} + +export const SIDEBAR_KEY = Symbol('SIDEBAR'); \ No newline at end of file diff --git a/src/lib/types/user.ts b/src/lib/types/user.ts index 52b8cf0..eead657 100644 --- a/src/lib/types/user.ts +++ b/src/lib/types/user.ts @@ -1,8 +1,11 @@ export interface UserProfile{ - id: string; + id: number; username : string; nickname : string; - roles : string[]; + roles : { + id: number; + name: string; + }[]; avatar? : string; } diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index e1257aa..feaa6b6 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -8,10 +8,12 @@ import type { DaisyUIThemeID } from '$lib/types/theme.ts'; import { COOKIE_THEME_KEY } from '$lib/components/constants/cookiesConstants.ts'; import { TOAST_KEY, ToastState } from '$lib/stores/toast.svelte.ts'; + import { SIDEBAR_KEY, SidebarState } from '$lib/stores/sidebar.svelte.ts'; let { data ,children} = $props(); setContext(THEME_KEY, new ThemeState(data.theme as DaisyUIThemeID ?? 'dark')) setContext(TOAST_KEY,new ToastState()) + setContext(SIDEBAR_KEY,new SidebarState()) const themeState = getContext(THEME_KEY); diff --git a/src/routes/app/settings/auth/users/+page.server.ts b/src/routes/app/settings/auth/users/+page.server.ts index 83caa6b..027374e 100644 --- a/src/routes/app/settings/auth/users/+page.server.ts +++ b/src/routes/app/settings/auth/users/+page.server.ts @@ -1,9 +1,18 @@ import type { PageServerLoad } from './$types'; import { userService } from '$lib/api/services/userService.ts'; +import { COOKIE_TOKEN_KEY } from '$lib/components/constants/cookiesConstants.ts'; +import { redirect } from '@sveltejs/kit'; -export const load:PageServerLoad = async ({ locals }) => { +export const load:PageServerLoad = async ({ cookies }) => { - const allUsers = await userService.getAllUsers({ page: 1, size: 10 }); + const token = cookies.get(COOKIE_TOKEN_KEY); + + if (!token) { + throw redirect(302, '/auth/login'); + } + + + const allUsers = await userService.getAllUsers({ page: 1, size: 10 ,token:token}); return { diff --git a/src/routes/app/settings/auth/users/+page.svelte b/src/routes/app/settings/auth/users/+page.svelte index 39a4a5c..8c908de 100644 --- a/src/routes/app/settings/auth/users/+page.svelte +++ b/src/routes/app/settings/auth/users/+page.svelte @@ -1,41 +1,80 @@
    +
    +
    +

    用户列表

    +
    + +
    +
    + + + + + + {#each rowTitles as title,index(index)} + + {/each} + + + + {#each records as record(record.id)} + + + + + + + + + {/each} + + + + + {#each rowTitles as title,index(index)} + + {/each} + + +
    + + {title}
    + + {record.id}{record.username}{record.nickname} + {#each record.roles as role(role.id)} + + {role.name} + + {/each} +
    + + {title}
    +
    +
    \ No newline at end of file diff --git a/src/routes/app/user/+page.server.ts b/src/routes/app/user/+page.server.ts new file mode 100644 index 0000000..4ee7c91 --- /dev/null +++ b/src/routes/app/user/+page.server.ts @@ -0,0 +1,16 @@ +import { COOKIE_TOKEN_KEY } from '$lib/components/constants/cookiesConstants.ts'; +import { redirect } from '@sveltejs/kit'; +import { userService } from '$lib/api/services/userService.ts'; + +export async function load({cookies}) { + const token = cookies.get(COOKIE_TOKEN_KEY); + if (!token) { + throw redirect(302, '/auth/login'); + } + + const profile = await userService.getUserProfile(token); + + return { + profile + } +} \ No newline at end of file diff --git a/src/routes/app/user/+page.svelte b/src/routes/app/user/+page.svelte new file mode 100644 index 0000000..78ae662 --- /dev/null +++ b/src/routes/app/user/+page.svelte @@ -0,0 +1,9 @@ + + +
    + 这里展示个人信息 + {JSON.stringify(data.profile)} +
    \ No newline at end of file diff --git a/src/routes/app/user/[id]/+page.server.ts b/src/routes/app/user/[id]/+page.server.ts new file mode 100644 index 0000000..60e287c --- /dev/null +++ b/src/routes/app/user/[id]/+page.server.ts @@ -0,0 +1,8 @@ + + +export function load({ params }) { + + console.log('params:', params); + + +} \ No newline at end of file diff --git a/src/routes/app/user/[id]/+page.svelte b/src/routes/app/user/[id]/+page.svelte new file mode 100644 index 0000000..b0527ec --- /dev/null +++ b/src/routes/app/user/[id]/+page.svelte @@ -0,0 +1,13 @@ + +
    + {JSON.stringify(data)} +
    + +
    + {JSON.stringify(page.params)} +
    \ No newline at end of file diff --git a/src/routes/auth/login/+page.svelte b/src/routes/auth/login/+page.svelte index 17c6a16..30b0a8c 100644 --- a/src/routes/auth/login/+page.svelte +++ b/src/routes/auth/login/+page.svelte @@ -10,6 +10,7 @@ import { getContext } from 'svelte'; import { TOAST_KEY, ToastState } from '$lib/stores/toast.svelte.ts'; + const toast = getContext(TOAST_KEY); let loading = false;