重构项目架构,引入启动页(SplashActivity)检查激活状态,未激活设备引导至激活页(ActivationActivity)完成provision+token流程。集成Retrofit+RxJava3网络层、Room数据库、Lifecycle组件、MMKV等依赖,升级OkHttp/Gson版本,并将构建配置从旧项目全面迁移至新项目结构。
259 lines
7.9 KiB
Vue
259 lines
7.9 KiB
Vue
<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>
|