95 lines
3.0 KiB
Vue
95 lines
3.0 KiB
Vue
<template>
|
|
<div>
|
|
<h2 style="margin-top: 0; color: var(--color-primary-dark)">👥 用户管理</h2>
|
|
<div class="toolbar">
|
|
<n-input v-model:value="search" placeholder="搜索用户名..." style="width: 240px" @update:value="loadUsers" />
|
|
<n-select v-model:value="statusFilter" :options="statusOptions" style="width: 140px" @update:value="loadUsers" />
|
|
</div>
|
|
<n-data-table :columns="columns" :data="users" :pagination="{ pageSize: 20 }" :loading="loading" striped />
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref, h, onMounted } from 'vue'
|
|
import { NButton, useMessage, useDialog } from 'naive-ui'
|
|
import { adminApi } from '@/api/admin'
|
|
|
|
const message = useMessage()
|
|
const dialog = useDialog()
|
|
const users = ref<any[]>([])
|
|
const loading = ref(false)
|
|
const search = ref('')
|
|
const statusFilter = ref<string | null>(null)
|
|
|
|
const statusOptions = [
|
|
{ label: '全部', value: null },
|
|
{ label: '在线', value: 'online' },
|
|
{ label: '已封禁', value: 'banned' },
|
|
]
|
|
|
|
const columns = [
|
|
{ title: '用户名', key: 'username', width: 120 },
|
|
{ title: '邮箱', key: 'email', width: 180 },
|
|
{ title: '状态', key: 'status', width: 80, render: (row: any) => row.is_banned ? '🚫 已封禁' : row.status === 'online' ? '🟢 在线' : '⚫ 离线' },
|
|
{ title: '注册时间', key: 'created_at', width: 160, render: (row: any) => new Date(row.created_at).toLocaleString('zh-CN') },
|
|
{
|
|
title: '操作', key: 'actions', width: 160,
|
|
render: (row: any) => [
|
|
h(NButton, {
|
|
size: 'small', type: row.is_banned ? 'success' : 'warning',
|
|
onClick: () => toggleBan(row),
|
|
}, { default: () => row.is_banned ? '解封' : '封禁' }),
|
|
h(NButton, {
|
|
size: 'small', type: 'error', style: 'margin-left: 8px',
|
|
onClick: () => deleteUser(row),
|
|
}, { default: () => '删除' }),
|
|
],
|
|
},
|
|
]
|
|
|
|
async function loadUsers() {
|
|
loading.value = true
|
|
try {
|
|
const { data } = await adminApi.getUsers({
|
|
search: search.value || undefined,
|
|
status: statusFilter.value || undefined,
|
|
})
|
|
users.value = data.items
|
|
} finally { loading.value = false }
|
|
}
|
|
|
|
function toggleBan(user: any) {
|
|
dialog.warning({
|
|
title: user.is_banned ? '解封用户' : '封禁用户',
|
|
content: `确定要${user.is_banned ? '解封' : '封禁'} ${user.username} 吗?`,
|
|
positiveText: '确定',
|
|
negativeText: '取消',
|
|
onPositiveClick: async () => {
|
|
await adminApi.banUser(user.id, !user.is_banned)
|
|
message.success('操作成功')
|
|
await loadUsers()
|
|
},
|
|
})
|
|
}
|
|
|
|
function deleteUser(user: any) {
|
|
dialog.error({
|
|
title: '⚠️ 删除用户',
|
|
content: `确定要永久删除 ${user.username} 吗?此操作不可撤销!`,
|
|
positiveText: '确定删除',
|
|
negativeText: '取消',
|
|
onPositiveClick: async () => {
|
|
await adminApi.deleteUser(user.id)
|
|
message.success('已删除')
|
|
await loadUsers()
|
|
},
|
|
})
|
|
}
|
|
|
|
onMounted(loadUsers)
|
|
</script>
|
|
|
|
<style scoped>
|
|
.toolbar { display: flex; gap: 12px; margin-bottom: 16px; }
|
|
</style>
|