feat: 支持按键分离事件与键盘捕获,并优化UI
- 后端与前端新增 keyAction 参数,支持按键按下/抬起独立事件,保持向下兼容 - 前端控制栏支持 pointerdown/pointerup 长按,新增键盘捕获输入框,映射浏览器按键到 Android KeyEvent - 重构顶栏布局:用户区右置并添加在线指示器、状态文本省略,优化窄屏响应式表现 - 控制栏录制按钮组防拆行,分辨率/帧率选择器自适应收缩,全局样式增强 - 修复 AdminAuthFilter 中 doFilter 被异常捕获导致误返回 401 的问题 - 移除管理后台中用户 admin 角色及相关 UI 与 API - 更新信令前端环境代理地址,修正路由名称大小写
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
|
||||
# 【代理目标】Vite dev server 把 /api/** 转发到这里(仅开发态生效,浏览器不直接访问)。
|
||||
# 后端 Spring Boot 地址,按需改成本机或远程信令服务器。
|
||||
VITE_API_TARGET=http://localhost:8080
|
||||
VITE_API_TARGET=http://192.168.5.224:8080
|
||||
|
||||
# 【请求基地址】留空表示走相对路径 /api(同源,经上面的代理转发)——推荐保持留空。
|
||||
# 仅在需要浏览器直连后端、绕过代理时才填绝对地址,例如:
|
||||
|
||||
@@ -130,7 +130,6 @@ export interface UserAccountView {
|
||||
status: AccountStatus;
|
||||
statusReason: string | null;
|
||||
statusUntil: number | null;
|
||||
admin: boolean;
|
||||
totpEnabled: boolean;
|
||||
createdAt: number;
|
||||
lastLoginAt: number | null;
|
||||
@@ -158,7 +157,6 @@ export function getUsers(query: UserQuery = {}): Promise<UserAccountView[]> {
|
||||
export function createUser(payload: {
|
||||
username: string;
|
||||
password: string;
|
||||
admin?: boolean;
|
||||
}): Promise<{ success: boolean; message?: string; user?: UserAccountView }> {
|
||||
return request.post('/api/admin/users', payload) as unknown as Promise<{
|
||||
success: boolean;
|
||||
@@ -182,12 +180,6 @@ export function resetUserPassword(
|
||||
}) as unknown as Promise<{ success: boolean; message?: string }>;
|
||||
}
|
||||
|
||||
export function setUserAdmin(userId: string, admin: boolean): Promise<{ success: boolean }> {
|
||||
return request.post(`/api/admin/users/${encodeURIComponent(userId)}/admin`, {
|
||||
admin,
|
||||
}) as unknown as Promise<{ success: boolean }>;
|
||||
}
|
||||
|
||||
/** 封禁账号,durationSeconds 省略或 <=0 表示永久。 */
|
||||
export function banUser(
|
||||
userId: string,
|
||||
|
||||
@@ -41,8 +41,8 @@ request.interceptors.response.use(
|
||||
if (status === 401) {
|
||||
localStorage.removeItem('admin_token');
|
||||
ElMessage.error('登录已失效,请重新登录');
|
||||
if (router.currentRoute.value.name !== 'Login') {
|
||||
router.replace({ name: 'Login' });
|
||||
if (router.currentRoute.value.name !== 'login') {
|
||||
router.replace({ name: 'login' });
|
||||
}
|
||||
} else {
|
||||
ElMessage.error(error.response?.data?.message || error.message || '请求失败');
|
||||
|
||||
@@ -31,9 +31,6 @@
|
||||
<el-table-column prop="username" label="用户名" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<span class="link-user" @click="openBindings(row)">{{ row.username }}</span>
|
||||
<el-tag v-if="row.admin" type="danger" size="small" style="margin-left: 6px">
|
||||
管理员
|
||||
</el-tag>
|
||||
<el-tag v-if="row.deviceCount > 0" type="info" size="small" style="margin-left: 6px">
|
||||
{{ row.deviceCount }} 台设备
|
||||
</el-tag>
|
||||
@@ -96,9 +93,6 @@
|
||||
<el-dropdown-item command="bindings">查看绑定设备</el-dropdown-item>
|
||||
<el-dropdown-item command="sessions">查看会话</el-dropdown-item>
|
||||
<el-dropdown-item command="reset">重置密码</el-dropdown-item>
|
||||
<el-dropdown-item command="admin">
|
||||
{{ row.admin ? '取消管理员' : '设为管理员' }}
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="delete" divided>删除账号</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
@@ -118,9 +112,6 @@
|
||||
<el-form-item label="密码" prop="password">
|
||||
<el-input v-model="createForm.password" type="password" show-password />
|
||||
</el-form-item>
|
||||
<el-form-item label="管理员">
|
||||
<el-switch v-model="createForm.admin" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="createVisible = false">取消</el-button>
|
||||
@@ -211,7 +202,6 @@ import {
|
||||
createUser,
|
||||
deleteUser,
|
||||
resetUserPassword,
|
||||
setUserAdmin,
|
||||
banUser,
|
||||
unbanUser,
|
||||
kickUser,
|
||||
@@ -249,7 +239,7 @@ watch(statusFilter, load);
|
||||
// ---------- 添加用户 ----------
|
||||
const createVisible = ref(false);
|
||||
const createFormRef = ref<FormInstance>();
|
||||
const createForm = ref({ username: '', password: '', admin: false });
|
||||
const createForm = ref({ username: '', password: '' });
|
||||
const createRules: FormRules = {
|
||||
username: [
|
||||
{ required: true, message: '请输入用户名', trigger: 'blur' },
|
||||
@@ -262,7 +252,7 @@ const createRules: FormRules = {
|
||||
};
|
||||
|
||||
function openCreate() {
|
||||
createForm.value = { username: '', password: '', admin: false };
|
||||
createForm.value = { username: '', password: '' };
|
||||
createVisible.value = true;
|
||||
}
|
||||
|
||||
@@ -328,19 +318,6 @@ async function handleDelete(row: UserAccountView) {
|
||||
await load();
|
||||
}
|
||||
|
||||
async function handleToggleAdmin(row: UserAccountView) {
|
||||
const next = !row.admin;
|
||||
const ok = await ElMessageBox.confirm(
|
||||
`确定${next ? '授予' : '取消'}「${row.username}」的管理员权限?`,
|
||||
'权限调整',
|
||||
{ type: 'warning', confirmButtonText: '确定', cancelButtonText: '取消' },
|
||||
).catch(() => false);
|
||||
if (!ok) return;
|
||||
await setUserAdmin(row.userId, next);
|
||||
ElMessage.success('权限已更新');
|
||||
await load();
|
||||
}
|
||||
|
||||
// ---------- 重置密码 ----------
|
||||
const resetVisible = ref(false);
|
||||
const newPassword = ref('');
|
||||
@@ -366,6 +343,9 @@ async function submitReset() {
|
||||
} else {
|
||||
ElMessage.error(resp.message || '重置失败');
|
||||
}
|
||||
} catch {
|
||||
// 错误提示已由 request 响应拦截器统一弹出,这里仅吞掉异常,
|
||||
// 避免未捕获的 Promise rejection,并保证弹窗保持打开以便重试。
|
||||
} finally {
|
||||
submitting.value = false;
|
||||
}
|
||||
@@ -418,7 +398,6 @@ function handleCommand(cmd: string, row: UserAccountView) {
|
||||
if (cmd === 'bindings') openBindings(row);
|
||||
else if (cmd === 'sessions') openSessions(row);
|
||||
else if (cmd === 'reset') openReset(row);
|
||||
else if (cmd === 'admin') handleToggleAdmin(row);
|
||||
else if (cmd === 'delete') handleDelete(row);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user