This commit is contained in:
fengchuanhn@gmail.com
2026-05-22 14:59:31 +08:00
parent ca1b254825
commit edaaf4d469
2 changed files with 50 additions and 1 deletions

View File

@@ -70,3 +70,10 @@ export function deleteAdminUserApi(token, userId) {
method: "DELETE",
});
}
/** 管理员解绑用户设备(清空 device_serial */
export function clearAdminUserDeviceSerialApi(token, userId) {
return adminRequest(token, `/admin/users/${userId}/clear-device-serial`, {
method: "POST",
});
}

View File

@@ -6,6 +6,7 @@ import {
createAdminUserApi,
updateAdminUserApi,
deleteAdminUserApi,
clearAdminUserDeviceSerialApi,
} from "../../api/adminUsers.js";
const auth = useAuthStore();
@@ -235,6 +236,31 @@ async function saveUser() {
await loadUsers();
}
function deviceBindLabel(row) {
return row.device_serial?.trim() ? "已绑定" : "未绑定";
}
async function clearDeviceSerial(row) {
if (!row.device_serial?.trim()) {
showMessage("warn", "该用户未绑定设备");
return;
}
const ok = window.confirm(
`确定解绑用户「${row.username}」的设备?解绑后可在其它电脑重新绑定登录。`,
);
if (!ok) return;
const res = await clearAdminUserDeviceSerialApi(auth.token, row.id);
if (!res.ok) {
showMessage("error", res.message || "解绑失败");
return;
}
showMessage("success", res.message || "设备已解绑");
await loadUsers();
}
async function removeUser(row) {
if (row.id === auth.currentUser?.id) {
showMessage("warn", "不能删除当前登录账号");
@@ -352,6 +378,14 @@ onMounted(async () => {
{{ ownerLabel(data.agent_id) }}
</template>
</Column>
<Column field="device_serial" header="设备" style="width: 5rem">
<template #body="{ data }">
<Tag
:value="deviceBindLabel(data)"
:severity="data.device_serial?.trim() ? 'success' : 'secondary'"
/>
</template>
</Column>
<Column field="vip_end_time" header="VIP 到期">
<template #body="{ data }">
{{ formatDateTime(data.vip_end_time) }}
@@ -362,7 +396,7 @@ onMounted(async () => {
{{ formatDateTime(data.created_at) }}
</template>
</Column>
<Column header="操作" style="width: 9rem">
<Column header="操作" style="width: 11rem">
<template #body="{ data }">
<div class="admin-users__actions">
<Button
@@ -372,6 +406,14 @@ onMounted(async () => {
text
@click="openEditDialog(data)"
/>
<Button
label="解绑"
size="small"
severity="secondary"
text
:disabled="!data.device_serial?.trim()"
@click="clearDeviceSerial(data)"
/>
<Button
label="删除"
size="small"