feat(users): 实现用户列表页面的异步数据加载与UI优化
- 添加角色服务依赖以获取用户组信息 - 将用户列表和角色信息改为流式加载 - 更新用户列表页面布局与样式 - 增加搜索框和用户组筛选下拉菜单 - 添加加载状态提示与错误处理显示 - 引入sass-embedded支持SCSS样式编写 - 调整表格列宽并增加响应式设计 - 添加面包屑导航与页面标题展示 - 新增添加用户按钮及操作下拉菜单 - 使用Icon组件替换原有图标实现方式
This commit is contained in:
12
src/lib/api/services/roleService.ts
Normal file
12
src/lib/api/services/roleService.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { api } from '$lib/api/httpClient.ts';
|
||||
import type { RoleResponse } from '$lib/types/user.ts';
|
||||
|
||||
export const roleService = {
|
||||
getAllRoles: async (token:string) => {
|
||||
const response = await api.get<RoleResponse[]>('/role/', {headers: {Authorization: `${token}`}});
|
||||
if (response.code != 200 || !response.data){
|
||||
throw new Error(response.msg);
|
||||
}
|
||||
return response.data;
|
||||
},
|
||||
}
|
||||
@@ -2,11 +2,13 @@ export interface UserProfile{
|
||||
id: number;
|
||||
username : string;
|
||||
nickname : string;
|
||||
roles : {
|
||||
id: number;
|
||||
name: string;
|
||||
}[];
|
||||
roles : RoleResponse[];
|
||||
avatar? : string;
|
||||
}
|
||||
|
||||
|
||||
|
||||
export interface RoleResponse {
|
||||
id: number;
|
||||
name: string;
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<AppHeader />
|
||||
|
||||
<main class="flex-1 overflow-y-auto p-4">
|
||||
<main class="flex-1 overflow-y-auto px-4">
|
||||
{@render children()}
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,7 @@ 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';
|
||||
import { roleService } from '$lib/api/services/roleService.ts';
|
||||
|
||||
export const load:PageServerLoad = async ({ cookies }) => {
|
||||
|
||||
@@ -11,11 +12,21 @@ export const load:PageServerLoad = async ({ cookies }) => {
|
||||
throw redirect(302, '/auth/login');
|
||||
}
|
||||
|
||||
const getRoles = async() => {
|
||||
return await roleService.getAllRoles(token);
|
||||
}
|
||||
|
||||
const allUsers = await userService.getAllUsers({ page: 1, size: 10 ,token:token});
|
||||
|
||||
const getUserList = async() => {
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
return await userService.getAllUsers({ page: 1, size: 10 ,token:token});
|
||||
}
|
||||
|
||||
return {
|
||||
"userList": allUsers
|
||||
|
||||
streamed:{
|
||||
userList: getUserList(),
|
||||
roles: getRoles(),
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,49 +1,113 @@
|
||||
<script lang="ts">
|
||||
import { resolve } from '$app/paths';
|
||||
import Icon from '$lib/components/icon/Icon.svelte';
|
||||
|
||||
const {data} = $props();
|
||||
const { data } = $props();
|
||||
|
||||
console.log(data);
|
||||
|
||||
|
||||
|
||||
const {current,pages,size,total,records} = data.userList;
|
||||
const rowTitles = ['ID','用户名','昵称','头像','用户组']
|
||||
const newRowTitles = [
|
||||
{ title: 'ID', width: 5}
|
||||
, { title: '用户名', width: 15 }
|
||||
, { title: '昵称', width: 20 }
|
||||
, { title: '头像', width: 10 }
|
||||
, { title: '用户组', width: 45 }
|
||||
];
|
||||
</script>
|
||||
|
||||
<div class="p-6">
|
||||
<div class=" ">
|
||||
|
||||
<div class="flex justify-between items-center bg-base-100">
|
||||
<div>
|
||||
<h1 class="text-2xl font-bold">用户列表</h1>
|
||||
</div>
|
||||
<div class="breadcrumbs text-sm">
|
||||
<div class="flex justify-between items-center ">
|
||||
<p class="">用户管理</p>
|
||||
<div class="breadcrumbs ">
|
||||
<ul>
|
||||
<li><a href={resolve('/app/dashboard')} >仪表盘</a></li>
|
||||
<li><a href={resolve('/app/settings')} >系统设置</a></li>
|
||||
<li><a href={resolve('/app/settings/auth')} >认证管理</a></li>
|
||||
<li><a href={resolve('/app/settings/auth/users')} >用户管理</a></li>
|
||||
<li><a href={resolve('/app/dashboard')}>仪表盘</a></li>
|
||||
<li><a href={resolve('/app/settings')}>系统设置</a></li>
|
||||
<li><a href={resolve('/app/settings/auth')}>认证管理</a></li>
|
||||
<li><a href={resolve('/app/settings/auth/users')}>用户管理</a></li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-x-auto">
|
||||
<table class="table">
|
||||
|
||||
|
||||
<div class="overflow-x-auto rounded-box shadow bg-base-100 mt-1 ">
|
||||
<div class="flex items-center justify-between px-4 pt-4 pb-2">
|
||||
<div class="flex gap-4">
|
||||
<label class="input">
|
||||
<svg class="h-[1em] opacity-50" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
||||
<g
|
||||
stroke-linejoin="round"
|
||||
stroke-linecap="round"
|
||||
stroke-width="2.5"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
>
|
||||
<circle cx="11" cy="11" r="8"></circle>
|
||||
<path d="m21 21-4.3-4.3"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<input type="search" required placeholder="Search" />
|
||||
<button class="btn btn-sm btn-primary">搜索</button>
|
||||
</label>
|
||||
{#await data.streamed.roles}
|
||||
<div></div>
|
||||
{:then roles }
|
||||
<div class=" ">
|
||||
<select class="select w-36" aria-label="Category">
|
||||
<option disabled selected>用户组别</option>
|
||||
|
||||
{#each roles as role(role.id)}
|
||||
<option value="{role.id}">{role.name}</option>
|
||||
{/each}
|
||||
</select>
|
||||
</div>
|
||||
{:catch error}
|
||||
<p class="error">加载失败: {error.message}</p>
|
||||
{/await}
|
||||
</div>
|
||||
<div class=" flex items-center justify-center gap-4">
|
||||
<button class="btn btn-primary">添加用户</button>
|
||||
|
||||
<div class="dropdown dropdown-bottom dropdown-end">
|
||||
<div tabindex="0" role="button" class="btn" ><Icon id="menu" size="24" /></div>
|
||||
<ul tabindex="-1" class="dropdown-content menu bg-base-200 rounded-box z-1 w-52 p-2 mt-2 shadow-sm" >
|
||||
<li><div>删除</div></li>
|
||||
<li><div>封禁</div></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
<input type="checkbox" class="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
{#each rowTitles as title,index(index)}
|
||||
<th>{title}</th>
|
||||
{/each}
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width: 5%">
|
||||
<label>
|
||||
<input type="checkbox" class="checkbox" />
|
||||
</label>
|
||||
</th>
|
||||
{#each newRowTitles as item,index(index)}
|
||||
<th style="width: {item.width}%" >{item.title}</th>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each records as record(record.id)}
|
||||
|
||||
{#await data.streamed.userList}
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan={newRowTitles.length + 1} class="text-center py-4 ">
|
||||
<div class="min-h-96 flex items-center justify-center">
|
||||
<div class="loading text-base-content">Loading...</div>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
{:then userList}
|
||||
<tbody>
|
||||
{#each userList.records as record(record.id)}
|
||||
<tr>
|
||||
<th>
|
||||
<label>
|
||||
@@ -63,18 +127,27 @@
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<th>
|
||||
|
||||
</th>
|
||||
{#each rowTitles as title,index(index)}
|
||||
<th>{title}</th>
|
||||
{/each}
|
||||
</tr>
|
||||
</tfoot>
|
||||
</tbody>
|
||||
{:catch error}
|
||||
<p class="error">加载失败: {error.message}</p>
|
||||
{/await}
|
||||
|
||||
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<style lang="scss">
|
||||
.loading {
|
||||
padding: 20px;
|
||||
background: #f0f0f0;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.error {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user