Files
VibeCoding/WebRTCSignalServerWeb/src/views/admins/AdminList.vue
TongTongStudio ded6fe5ab7 feat(android): add device activation flow and update dependencies
重构项目架构,引入启动页(SplashActivity)检查激活状态,未激活设备引导至激活页(ActivationActivity)完成provision+token流程。集成Retrofit+RxJava3网络层、Room数据库、Lifecycle组件、MMKV等依赖,升级OkHttp/Gson版本,并将构建配置从旧项目全面迁移至新项目结构。
2026-08-03 00:25:51 +08:00

259 lines
7.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<div class="page-container">
<el-card shadow="hover">
<div class="toolbar">
<div>
<h2 class="page-title">后台管理员</h2>
<span class="text-muted"> {{ admins.length }} 个管理员独立于普通用户体系</span>
</div>
<div class="toolbar-actions">
<el-button :icon="'Refresh'" :loading="loading" @click="load">刷新</el-button>
<el-button type="primary" :icon="'Plus'" @click="openCreate">添加管理员</el-button>
</div>
</div>
<el-table v-loading="loading" :data="admins" stripe>
<el-table-column prop="username" label="账号" min-width="160" />
<el-table-column prop="displayName" label="显示名" min-width="140">
<template #default="{ row }">{{ row.displayName || '-' }}</template>
</el-table-column>
<el-table-column label="状态" width="120">
<template #default="{ row }">
<el-tag :type="row.status === 'ACTIVE' ? 'success' : 'danger'" effect="dark" size="small">
{{ row.status === 'ACTIVE' ? '启用' : '禁用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" min-width="170">
<template #default="{ row }">{{ formatTime(row.createdAt) }}</template>
</el-table-column>
<el-table-column label="最后登录" min-width="170">
<template #default="{ row }">{{ row.lastLoginAt ? formatTime(row.lastLoginAt) : '从未登录' }}</template>
</el-table-column>
<el-table-column label="操作" min-width="240" fixed="right">
<template #default="{ row }">
<el-button
v-if="row.status === 'ACTIVE'"
link
type="warning"
@click="disable(row)"
>禁用</el-button>
<el-button
v-else
link
type="success"
@click="enable(row)"
>启用</el-button>
<el-button link type="primary" @click="openReset(row)">重置密码</el-button>
<el-button link type="danger" @click="remove(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 新增管理员 -->
<el-dialog v-model="createVisible" title="添加管理员" width="420px">
<el-form :model="createForm" label-width="80px">
<el-form-item label="账号">
<el-input v-model="createForm.username" placeholder="3-32 位字母/数字/下划线/点/连字符" />
</el-form-item>
<el-form-item label="显示名">
<el-input v-model="createForm.displayName" placeholder="可选" />
</el-form-item>
<el-form-item label="密码">
<el-input
v-model="createForm.password"
type="password"
show-password
placeholder="8-128 位,含两类字符"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="createVisible = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="submitCreate">确定</el-button>
</template>
</el-dialog>
<!-- 重置密码 -->
<el-dialog v-model="resetVisible" title="重置密码" width="420px">
<el-form :model="resetForm" label-width="80px">
<el-form-item label="账号">
<span>{{ resetForm.username }}</span>
</el-form-item>
<el-form-item label="新密码">
<el-input
v-model="resetForm.password"
type="password"
show-password
placeholder="8-128 位,含两类字符"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="resetVisible = false">取消</el-button>
<el-button type="primary" :loading="saving" @click="submitReset">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import {
getAdmins,
createAdmin,
disableAdmin,
enableAdmin,
deleteAdmin,
resetAdminPassword,
type AdminAccountView,
} from '@/api/admin';
const admins = ref<AdminAccountView[]>([]);
const loading = ref(false);
const saving = ref(false);
const createVisible = ref(false);
const createForm = ref({ username: '', displayName: '', password: '' });
const resetVisible = ref(false);
const resetForm = ref({ adminId: '', username: '', password: '' });
async function load() {
loading.value = true;
try {
admins.value = await getAdmins();
} catch (e: unknown) {
const err = e as { response?: { data?: { message?: string } } };
ElMessage.error(err?.response?.data?.message || '加载失败');
} finally {
loading.value = false;
}
}
function openCreate() {
createForm.value = { username: '', displayName: '', password: '' };
createVisible.value = true;
}
async function submitCreate() {
const { username, password, displayName } = createForm.value;
if (!username.trim()) {
ElMessage.warning('请输入账号');
return;
}
if (!password) {
ElMessage.warning('请输入密码');
return;
}
saving.value = true;
try {
await createAdmin(username.trim(), password, displayName.trim() || undefined);
ElMessage.success('管理员已添加');
createVisible.value = false;
await load();
} catch (e: unknown) {
const err = e as { response?: { data?: { message?: string } } };
ElMessage.error(err?.response?.data?.message || '添加失败');
} finally {
saving.value = false;
}
}
async function disable(row: AdminAccountView) {
await ElMessageBox.confirm(`确定禁用管理员「${row.username}」?`, '提示', { type: 'warning' });
try {
await disableAdmin(row.adminId);
ElMessage.success('已禁用');
await load();
} catch (e: unknown) {
const err = e as { response?: { data?: { message?: string } } };
ElMessage.error(err?.response?.data?.message || '操作失败');
}
}
async function enable(row: AdminAccountView) {
try {
await enableAdmin(row.adminId);
ElMessage.success('已启用');
await load();
} catch (e: unknown) {
const err = e as { response?: { data?: { message?: string } } };
ElMessage.error(err?.response?.data?.message || '操作失败');
}
}
function openReset(row: AdminAccountView) {
resetForm.value = { adminId: row.adminId, username: row.username, password: '' };
resetVisible.value = true;
}
async function submitReset() {
if (!resetForm.value.password) {
ElMessage.warning('请输入新密码');
return;
}
saving.value = true;
try {
await resetAdminPassword(resetForm.value.adminId, resetForm.value.password);
ElMessage.success('密码已重置');
resetVisible.value = false;
} catch (e: unknown) {
const err = e as { response?: { data?: { message?: string } } };
ElMessage.error(err?.response?.data?.message || '重置失败');
} finally {
saving.value = false;
}
}
async function remove(row: AdminAccountView) {
await ElMessageBox.confirm(
`确定删除管理员「${row.username}」?该操作不可恢复。`,
'危险操作',
{ type: 'error' },
);
try {
await deleteAdmin(row.adminId);
ElMessage.success('已删除');
await load();
} catch (e: unknown) {
const err = e as { response?: { data?: { message?: string } } };
ElMessage.error(err?.response?.data?.message || '删除失败');
}
}
function formatTime(ts: number): string {
if (!ts) return '-';
const d = new Date(ts);
return d.toLocaleString('zh-CN', { hour12: false });
}
onMounted(load);
</script>
<style scoped>
.page-container {
padding: 16px;
}
.toolbar {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.page-title {
margin: 0 0 4px;
font-size: 18px;
}
.text-muted {
color: #909399;
font-size: 13px;
}
.toolbar-actions {
display: flex;
gap: 8px;
}
</style>