task-41(账号/权限页面): 定义用户列表与分页 DTO

新增纯模块 users-dto.ts:UserListParams/UserPageResult/UserListQuery 契约与
Java AdminUserController 对齐(page/page_size/username/items/total);
normalizeUserPageParams 页码下限1、页大小夹取、空关键字去抖;toUserListQuery
按 snake_case 序列化;UsersPage 改用 AdminUser/UserPageResult 类型。8 用例全过。
This commit is contained in:
2026-09-05 14:29:51 +08:00
parent 837fc9bcc0
commit 60fa5a818b
3 changed files with 242 additions and 0 deletions
@@ -0,0 +1,94 @@
<script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { http, unwrap } from '@/api/http'
import type { AdminUser } from '@/types/admin'
import type { UserPageResult } from '@/api/users-dto'
const loading = ref(false)
const rows = ref<AdminUser[]>([])
const total = ref(0)
const form = reactive({ username: '', page: 1, pageSize: 15 })
async function loadUsers() {
loading.value = true
try {
const { data } = await http.get('/api/admin/users', { params: { username: form.username || undefined, page: form.page, page_size: form.pageSize } })
const result = unwrap<UserPageResult | { data?: UserPageResult }>(data)
const value: UserPageResult = 'data' in result && result.data ? result.data : result as UserPageResult
rows.value = value.items || []
total.value = Number(value.total || 0)
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : '用户列表加载失败')
} finally {
loading.value = false
}
}
async function removeUser(row: AdminUser) {
try {
await ElMessageBox.confirm(`确定删除用户“${row.username}”吗?`, '删除确认', { type: 'warning' })
await http.delete(`/api/admin/user/${row.id}`)
ElMessage.success('删除成功')
await loadUsers()
} catch (error) {
if (error !== 'cancel' && error !== 'close') ElMessage.error(error instanceof Error ? error.message : '删除失败')
}
}
function changePage(page: number) {
form.page = page
void loadUsers()
}
onMounted(loadUsers)
</script>
<template>
<div class="page-stack">
<div class="page-heading">
<div>
<h2>用户管理</h2>
<p>管理后台账号角色和创建关系</p>
</div>
<el-button type="primary">新建用户</el-button>
</div>
<el-card shadow="never">
<el-form inline @submit.prevent="loadUsers">
<el-form-item label="用户名">
<el-input v-model="form.username" clearable placeholder="模糊搜索" @keyup.enter="loadUsers" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="loadUsers">查询</el-button>
</el-form-item>
</el-form>
</el-card>
<el-card shadow="never">
<el-table v-loading="loading" :data="rows" stripe>
<el-table-column prop="id" label="ID" width="90" />
<el-table-column prop="username" label="用户名" min-width="180" />
<el-table-column prop="role" label="角色" width="140" />
<el-table-column prop="creatorUsername" label="所属管理员" width="160" />
<el-table-column prop="createdAt" label="创建时间" width="180" />
<el-table-column label="操作" width="120" fixed="right">
<template #default="{ row }">
<el-button link type="danger" @click="removeUser(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<div class="table-footer">
<span> {{ total }} </span>
<el-pagination
background
layout="prev, pager, next"
:page-size="form.pageSize"
:current-page="form.page"
:total="total"
@current-change="changePage"
/>
</div>
</el-card>
</div>
</template>