feat(android): add device activation flow and update dependencies

重构项目架构,引入启动页(SplashActivity)检查激活状态,未激活设备引导至激活页(ActivationActivity)完成provision+token流程。集成Retrofit+RxJava3网络层、Room数据库、Lifecycle组件、MMKV等依赖,升级OkHttp/Gson版本,并将构建配置从旧项目全面迁移至新项目结构。
This commit is contained in:
TongTongStudio
2026-08-03 00:25:51 +08:00
parent 61398ff4ff
commit ded6fe5ab7
105 changed files with 5363 additions and 1503 deletions

View File

@@ -0,0 +1,258 @@
<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>