4 changed files with 185 additions and 0 deletions
-
8src/router/index.ts
-
11src/stores/auth.ts
-
1src/views/admin/AdminLayout.vue
-
165src/views/admin/UserListView.vue
@ -0,0 +1,165 @@ |
|||
<script setup lang="ts"> |
|||
import { ref, onMounted } from 'vue' |
|||
import { userApi } from '@/api/client' |
|||
import type { UserInfo } from '@/generated/api' |
|||
|
|||
const users = ref<UserInfo[]>([]) |
|||
const page = ref(1) |
|||
const size = 10 |
|||
const total = ref(0) |
|||
const loading = ref(false) |
|||
const error = ref<string | null>(null) |
|||
|
|||
function roleLabel(role: string): string { |
|||
switch (role) { |
|||
case 'ADMIN': |
|||
return '管理员' |
|||
case 'EDITOR': |
|||
return '编辑' |
|||
case 'VISITOR': |
|||
return '访客' |
|||
default: |
|||
return role |
|||
} |
|||
} |
|||
|
|||
function formatDate(iso: string): string { |
|||
const d = new Date(iso) |
|||
if (isNaN(d.getTime())) return iso |
|||
return d.toLocaleString('zh-CN', { hour12: false }) |
|||
} |
|||
|
|||
async function load() { |
|||
loading.value = true |
|||
error.value = null |
|||
try { |
|||
const resp = await userApi.apiAdminUsersGet(page.value, size) |
|||
const data = resp.data.data |
|||
users.value = data?.list as UserInfo[] ?? [] |
|||
total.value = Number(data?.total ?? 0) |
|||
} catch (e) { |
|||
console.error(e) |
|||
error.value = '用户列表加载失败' |
|||
} finally { |
|||
loading.value = false |
|||
} |
|||
} |
|||
|
|||
onMounted(load) |
|||
</script> |
|||
|
|||
<template> |
|||
<div> |
|||
<h1 class="page-title">用户管理</h1> |
|||
<p v-if="error" class="error-text">{{ error }}</p> |
|||
<p v-else-if="loading" class="hint-text">加载中...</p> |
|||
<p v-else-if="users.length === 0" class="hint-text">暂无用户</p> |
|||
|
|||
<table v-else class="user-table"> |
|||
<thead> |
|||
<tr> |
|||
<th>用户名</th> |
|||
<th>邮箱</th> |
|||
<th>角色</th> |
|||
<th>状态</th> |
|||
<th>注册时间</th> |
|||
</tr> |
|||
</thead> |
|||
<tbody> |
|||
<tr v-for="user in users" :key="user.id"> |
|||
<td>{{ user.username }}</td> |
|||
<td>{{ user.email || '-' }}</td> |
|||
<td> |
|||
<span class="role-badge" :class="user.role.toLowerCase()">{{ roleLabel(user.role) }}</span> |
|||
</td> |
|||
<td> |
|||
<span class="status-badge" :class="user.enabled ? 'enabled' : 'disabled'"> |
|||
{{ user.enabled ? '启用' : '禁用' }} |
|||
</span> |
|||
</td> |
|||
<td>{{ formatDate(user.createdAt) }}</td> |
|||
</tr> |
|||
</tbody> |
|||
</table> |
|||
<p class="total-hint">共 {{ total }} 位用户</p> |
|||
</div> |
|||
</template> |
|||
|
|||
<style scoped> |
|||
.page-title { |
|||
margin: 0 0 16px; |
|||
font-size: 20px; |
|||
} |
|||
|
|||
.user-table { |
|||
width: 100%; |
|||
border-collapse: collapse; |
|||
background: #fff; |
|||
border: 1px solid #e5e5ea; |
|||
border-radius: 8px; |
|||
overflow: hidden; |
|||
} |
|||
|
|||
.user-table th, |
|||
.user-table td { |
|||
padding: 10px 14px; |
|||
text-align: left; |
|||
border-bottom: 1px solid #eee; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.user-table th { |
|||
background: #f7f8fa; |
|||
color: #5c6470; |
|||
} |
|||
|
|||
.role-badge { |
|||
padding: 2px 10px; |
|||
border-radius: 12px; |
|||
font-size: 12px; |
|||
} |
|||
|
|||
.role-badge.admin { |
|||
background: #f8d7da; |
|||
color: #8f1f2b; |
|||
} |
|||
|
|||
.role-badge.editor { |
|||
background: #cce5ff; |
|||
color: #004a99; |
|||
} |
|||
|
|||
.role-badge.visitor { |
|||
background: #e9ecef; |
|||
color: #6c757d; |
|||
} |
|||
|
|||
.status-badge { |
|||
padding: 2px 10px; |
|||
border-radius: 12px; |
|||
font-size: 12px; |
|||
} |
|||
|
|||
.status-badge.enabled { |
|||
background: #d4edda; |
|||
color: #1e7e34; |
|||
} |
|||
|
|||
.status-badge.disabled { |
|||
background: #e9ecef; |
|||
color: #6c757d; |
|||
} |
|||
|
|||
.total-hint { |
|||
color: #8a93a3; |
|||
font-size: 13px; |
|||
} |
|||
|
|||
.error-text { |
|||
color: #d33c3c; |
|||
} |
|||
|
|||
.hint-text { |
|||
color: #8a93a3; |
|||
} |
|||
</style> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue