feat(auth): implement login and user management features

- Added server-side login action with form handling and cookie storage
- Implemented user authentication service with token management
- Created user list page with data fetching from userService
- Developed reusable DataTable component with selection and pagination
- Enhanced AppSidebar with nested navigation and active state tracking
- Updated icon definitions and sprite symbols for UI consistency
- Improved HTTP client to properly handle request bodies for different methods
- Refactored auth store to manage authentication state and cookies
- Added strict typing for navigation items and table columns
- Removed obsolete code and simplified authentication flow
This commit is contained in:
Chaos
2025-11-24 17:11:41 +08:00
parent 3515faa814
commit ed542f108c
16 changed files with 472 additions and 203 deletions

View File

@@ -1,56 +1,9 @@
<script lang="ts">
import { authService } from '$lib/api/services/authService.ts';
import type { LoginPayload } from '$lib/types/auth.ts';
import { goto } from '$app/navigation';
import { enhance } from '$app/forms';
import { resolve } from '$app/paths';
import { get } from 'svelte/store';
import { authStore } from '$lib/stores/authStore.ts';
import Icon from '$lib/components/icon/Icon.svelte';
import { toast } from '$lib/stores/toastStore.ts';
export let form;
// 使用 bind:value 直接绑定,不需要手动写 handleChange
let loginPayload: LoginPayload = {
username: '',
password: ''
};
let rememberMe = false; // 变量名语义更清晰,原 isSaving 容易歧义
let isLoading = false; // 新增:控制按钮加载状态
const handleSubmit = async () => {
if (isLoading) return;
isLoading = true;
try {
// 模拟延时效果,让用户感觉到正在处理 (可选)
// await new Promise(r => setTimeout(r, 500));
await authService.login(loginPayload);
if (get(authStore).isAuthenticated) {
toast.success('登录成功,正在跳转到首页')
setTimeout( async () => {
await goto(resolve('/app/dashboard'));
}, 1000)
}
} catch (e:unknown) {
if (e instanceof Error) {
if (e.name === 'TypeError' && e.message.includes('fetch')) {
toast.error("网络错误,请检查网络连接");
} else {
console.error(e);
toast.error(e.message || '登录失败,请重试');
}
} else {
console.error(e);
toast.error('登录失败,请重试');
}
} finally {
isLoading = false;
}
};
</script>
<div class="min-h-screen bg-base-200 flex items-center justify-center p-4">
@@ -64,8 +17,12 @@
<span>IT DTMS登录</span>
</h2>
</div>
<form on:submit|preventDefault={handleSubmit} class="space-y-4">
{#if form?.message}
<div class="alert alert-error text-sm py-2 mb-4">
{form.message}
</div>
{/if}
<form method="post" use:enhance class="space-y-4">
<div class="form-control">
<label class="label">
@@ -74,9 +31,10 @@
<div class="relative">
<input
type="text"
name="username"
placeholder="username"
class="input input-bordered w-full pl-10"
bind:value={loginPayload.username}
required
/>
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-base-content/50">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 opacity-70"><path d="M8 8a3 3 0 1 0 0-6 3 3 0 0 0 0 6ZM12.735 14c.618 0 1.093-.561.872-1.139a6.002 6.002 0 0 0-11.215 0c-.22.578.254 1.139.872 1.139h9.47Z" /></svg>
@@ -91,9 +49,10 @@
<div class="relative">
<input
type="password"
name="password"
placeholder="password"
class="input input-bordered w-full pl-10"
bind:value={loginPayload.password}
required
/>
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-base-content/50">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="w-4 h-4 opacity-70"><path fill-rule="evenodd" d="M14 6a4 4 0 0 1-4.899 3.899l-1.955 1.955a.5.5 0 0 1-.353.146H5v1.5a.5.5 0 0 1-.5.5h-2a.5.5 0 0 1-.5-.5v-2.293a.5.5 0 0 1 .146-.353l3.955-3.955A4 4 0 1 1 14 6Zm-4-2a.75.75 0 0 0 0 1.5.5.5 0 0 1 .5.5.75.75 0 0 0 1.5 0 2 2 0 0 0-2-2Z" clip-rule="evenodd" /></svg>
@@ -104,7 +63,7 @@
<div class="form-control mt-6 flex justify-between">
<label class="label cursor-pointer justify-start gap-2">
<input type="checkbox" bind:checked={rememberMe} class="checkbox checkbox-sm checkbox-primary" />
<input type="checkbox" class="checkbox checkbox-sm checkbox-primary" />
<span class="label-text">记住我</span>
</label>
<div class="label" >
@@ -113,13 +72,10 @@
</div>
<div class="form-control mt-2">
<button class="btn btn-primary w-full" disabled={isLoading}>
{#if isLoading}
<span class="loading loading-spinner loading-sm"></span>
登录中...
{:else}
<button class="btn btn-primary w-full" >
登录
{/if}
</button>
</div>