This commit is contained in:
fengchuanhn@gmail.com
2026-05-21 16:24:49 +08:00
parent 7f92a9b773
commit 2aff213edd
6 changed files with 180 additions and 77 deletions

72
src/api/agentCardKeys.js Normal file
View File

@@ -0,0 +1,72 @@
import { apiRequest } from "./client.js";
/**
* @param {string} token
* @param {RequestInit} [options]
*/
function agentRequest(token, path, options = {}) {
return apiRequest(path, {
...options,
headers: {
Authorization: `Bearer ${token}`,
...options.headers,
},
});
}
/**
* @param {string} token
* @param {{
* page?: number,
* pageSize?: number,
* status?: string,
* username?: string,
* oemId?: number | null,
* agentId?: number | null,
* }} [params]
*/
export function listAgentCardKeysApi(
token,
{ page = 1, pageSize = 20, status = "all", username, oemId, agentId } = {},
) {
const query = new URLSearchParams({
page: String(page),
page_size: String(pageSize),
status,
});
const trimmed = username?.trim();
if (trimmed) query.set("username", trimmed);
if (oemId != null) query.set("oem_id", String(oemId));
if (agentId != null) query.set("agent_id", String(agentId));
return agentRequest(token, `/agent/card-keys?${query}`);
}
/**
* @param {string} token
* @param {object} payload
*/
export function createAgentCardKeysApi(token, payload) {
return agentRequest(token, "/agent/card-keys", {
method: "POST",
body: JSON.stringify(payload),
});
}
/**
* @param {string} token
* @param {number} cardId
* @param {object} payload
*/
export function updateAgentCardKeyApi(token, cardId, payload) {
return agentRequest(token, `/agent/card-keys/${cardId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
/** @param {string} token @param {number} cardId */
export function deleteAgentCardKeyApi(token, cardId) {
return agentRequest(token, `/agent/card-keys/${cardId}`, {
method: "DELETE",
});
}

72
src/api/agentUsers.js Normal file
View File

@@ -0,0 +1,72 @@
import { apiRequest } from "./client.js";
/**
* @param {string} token
* @param {RequestInit} [options]
*/
function agentRequest(token, path, options = {}) {
return apiRequest(path, {
...options,
headers: {
Authorization: `Bearer ${token}`,
...options.headers,
},
});
}
/**
* @param {string} token
* @param {{
* page?: number,
* pageSize?: number,
* username?: string,
* oemId?: number | null,
* agentId?: number | null,
* roleId?: number | null,
* }} [params]
*/
export function listAgentUsersApi(
token,
{ page = 1, pageSize = 20, username, oemId, agentId, roleId } = {},
) {
const query = new URLSearchParams({
page: String(page),
page_size: String(pageSize),
});
const trimmed = username?.trim();
if (trimmed) query.set("username", trimmed);
if (oemId != null) query.set("oem_id", String(oemId));
if (agentId != null) query.set("agent_id", String(agentId));
if (roleId != null) query.set("role_id", String(roleId));
return agentRequest(token, `/agent/users?${query}`);
}
/**
* @param {string} token
* @param {object} payload
*/
export function createAgentUserApi(token, payload) {
return agentRequest(token, "/agent/users", {
method: "POST",
body: JSON.stringify(payload),
});
}
/**
* @param {string} token
* @param {number} userId
* @param {object} payload
*/
export function updateAgentUserApi(token, userId, payload) {
return agentRequest(token, `/agent/users/${userId}`, {
method: "PATCH",
body: JSON.stringify(payload),
});
}
/** @param {string} token @param {number} userId */
export function deleteAgentUserApi(token, userId) {
return agentRequest(token, `/agent/users/${userId}`, {
method: "DELETE",
});
}

View File

@@ -6,10 +6,9 @@ const route = useRoute();
const menuItems = [ const menuItems = [
{ name: "admin-users", label: "用户管理", to: "/admin/users" }, { name: "agent-users", label: "用户管理", to: "/agent/users" },
{ name: "admin-card-keys", label: "卡密管理", to: "/admin/card-keys" }, { name: "agent-card-keys", label: "卡密管理", to: "/agent/card-keys" },
{ name: "admin-desktop-config", label: "桌面配置", to: "/admin/desktop-config" },
{ name: "admin-overview", label: "软件配置", to: "/admin" },
]; ];
const activeName = computed(() => route.name); const activeName = computed(() => route.name);

View File

@@ -1,14 +1,12 @@
<script setup> <script setup>
import { useAuthStore } from "../stores/auth"; import AgentSubNav from "../components/agent/AgentSubNav.vue";
const auth = useAuthStore();
</script> </script>
<template> <template>
<div class="flex h-full flex-col items-center justify-center gap-3 p-8"> <div class="admin-layout">
<h1 class="gradient-text text-xl font-semibold">代理工作台</h1> <AgentSubNav />
<p class="text-sm text-text-muted"> <div class="admin-layout__content custom-scrollbar">
当前用户{{ auth.currentUser?.username }}代理 <RouterView />
</p> </div>
</div> </div>
</template> </template>

View File

@@ -2,12 +2,12 @@
import { computed, onMounted, ref, watch } from "vue"; import { computed, onMounted, ref, watch } from "vue";
import { useAuthStore, ROLE_AGENT, ROLE_OEM } from "../../stores/auth.js"; import { useAuthStore, ROLE_AGENT, ROLE_OEM } from "../../stores/auth.js";
import { import {
listAdminCardKeysApi, listAgentCardKeysApi,
createAdminCardKeysApi, createAgentCardKeysApi,
updateAdminCardKeyApi, updateAgentCardKeyApi,
deleteAdminCardKeyApi, deleteAgentCardKeyApi,
} from "../../api/adminCardKeys.js"; } from "../../api/agentCardKeys.js";
import { listAdminUsersApi } from "../../api/adminUsers.js"; import { listAgentUsersApi } from "../../api/agentUsers.js";
const auth = useAuthStore(); const auth = useAuthStore();
@@ -92,8 +92,8 @@ function showMessage(severity, message) {
async function loadFilterOptions() { async function loadFilterOptions() {
const [oemRes, agentRes] = await Promise.all([ const [oemRes, agentRes] = await Promise.all([
listAdminUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_OEM }), listAgentUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_OEM }),
listAdminUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_AGENT }), listAgentUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_AGENT }),
]); ]);
if (oemRes.ok && Array.isArray(oemRes.data?.items)) { if (oemRes.ok && Array.isArray(oemRes.data?.items)) {
oemOptions.value = oemRes.data.items; oemOptions.value = oemRes.data.items;
@@ -106,7 +106,7 @@ async function loadFilterOptions() {
async function loadCards() { async function loadCards() {
loading.value = true; loading.value = true;
feedback.value = { severity: "", message: "" }; feedback.value = { severity: "", message: "" };
const res = await listAdminCardKeysApi(auth.token, { const res = await listAgentCardKeysApi(auth.token, {
page: page.value, page: page.value,
pageSize: pageSize.value, pageSize: pageSize.value,
status: statusFilter.value, status: statusFilter.value,
@@ -223,9 +223,9 @@ async function saveCard() {
} }
payload.serial_number = serial.toUpperCase(); payload.serial_number = serial.toUpperCase();
} }
res = await createAdminCardKeysApi(auth.token, payload); res = await createAgentCardKeysApi(auth.token, payload);
} else { } else {
res = await updateAdminCardKeyApi(auth.token, editingId.value, { res = await updateAgentCardKeyApi(auth.token, editingId.value, {
duration_days: isActivated.value ? undefined : form.value.duration_days, duration_days: isActivated.value ? undefined : form.value.duration_days,
remark: form.value.remark.trim() || null, remark: form.value.remark.trim() || null,
}); });
@@ -252,7 +252,7 @@ async function removeCard(row) {
const ok = window.confirm(`确定删除卡密「${row.serial_number}」?`); const ok = window.confirm(`确定删除卡密「${row.serial_number}」?`);
if (!ok) return; if (!ok) return;
const res = await deleteAdminCardKeyApi(auth.token, row.id); const res = await deleteAgentCardKeyApi(auth.token, row.id);
if (!res.ok) { if (!res.ok) {
showMessage("error", res.message || "删除失败"); showMessage("error", res.message || "删除失败");
return; return;
@@ -286,7 +286,7 @@ onMounted(async () => {
<div> <div>
<h1 class="admin-page__title gradient-text">卡密管理</h1> <h1 class="admin-page__title gradient-text">卡密管理</h1>
</div> </div>
<Button label="生成卡密" @click="openCreateDialog" />
</div> </div>
<Message <Message
@@ -306,26 +306,8 @@ onMounted(async () => {
class="admin-users__search" class="admin-users__search"
@keyup.enter="applyFilters" @keyup.enter="applyFilters"
/> />
<Select
v-model="filters.oem_id"
:options="oemOptions"
option-label="username"
option-value="id"
placeholder="全部 OEM"
show-clear
filter
class="admin-users__filter-select"
/>
<Select
v-model="filters.agent_id"
:options="agentOptions"
option-label="username"
option-value="id"
placeholder="全部代理"
show-clear
filter
class="admin-users__filter-select"
/>
<label class="text-sm text-text-muted">状态</label> <label class="text-sm text-text-muted">状态</label>
<Select <Select
v-model="statusFilter" v-model="statusFilter"
@@ -405,27 +387,7 @@ onMounted(async () => {
{{ data.remark || "—" }} {{ data.remark || "—" }}
</template> </template>
</Column> </Column>
<Column header="操作" style="width: 9rem">
<template #body="{ data }">
<div class="admin-users__actions">
<Button
label="编辑"
size="small"
severity="secondary"
text
@click="openEditDialog(data)"
/>
<Button
label="删除"
size="small"
severity="danger"
text
:disabled="!!data.activated_at"
@click="removeCard(data)"
/>
</div>
</template>
</Column>
<template #empty> <template #empty>
<p class="py-6 text-center text-sm text-text-muted">暂无卡密</p> <p class="py-6 text-center text-sm text-text-muted">暂无卡密</p>
</template> </template>

View File

@@ -2,11 +2,11 @@
import { computed, onMounted, ref } from "vue"; import { computed, onMounted, ref } from "vue";
import { useAuthStore, ROLE_ADMIN, ROLE_AGENT,ROLE_OEM } from "../../stores/auth.js"; import { useAuthStore, ROLE_ADMIN, ROLE_AGENT,ROLE_OEM } from "../../stores/auth.js";
import { import {
listAdminUsersApi, listAgentUsersApi,
createAdminUserApi, createAgentUserApi,
updateAdminUserApi, updateAgentUserApi,
deleteAdminUserApi, deleteAgentUserApi,
} from "../../api/adminUsers.js"; } from "../../api/agentUsers.js";
const auth = useAuthStore(); const auth = useAuthStore();
@@ -103,8 +103,8 @@ function showMessage(severity, message) {
async function loadFilterOptions() { async function loadFilterOptions() {
const [oemRes, agentRes] = await Promise.all([ const [oemRes, agentRes] = await Promise.all([
listAdminUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_OEM }), listAgentUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_OEM }),
listAdminUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_AGENT }), listAgentUsersApi(auth.token, { page: 1, pageSize: 200, roleId: ROLE_AGENT }),
]); ]);
if (oemRes.ok && Array.isArray(oemRes.data?.items)) { if (oemRes.ok && Array.isArray(oemRes.data?.items)) {
oemOptions.value = oemRes.data.items; oemOptions.value = oemRes.data.items;
@@ -117,7 +117,7 @@ async function loadFilterOptions() {
async function loadUsers() { async function loadUsers() {
loading.value = true; loading.value = true;
feedback.value = { severity: "", message: "" }; feedback.value = { severity: "", message: "" };
const res = await listAdminUsersApi(auth.token, { const res = await listAgentUsersApi(auth.token, {
page: page.value, page: page.value,
pageSize: pageSize.value, pageSize: pageSize.value,
username: filters.value.username, username: filters.value.username,
@@ -218,9 +218,9 @@ async function saveUser() {
showMessage("warn", "请设置初始密码"); showMessage("warn", "请设置初始密码");
return; return;
} }
res = await createAdminUserApi(auth.token, payload); res = await createAgentUserApi(auth.token, payload);
} else { } else {
res = await updateAdminUserApi(auth.token, editingId.value, payload); res = await updateAgentUserApi(auth.token, editingId.value, payload);
} }
saving.value = false; saving.value = false;
@@ -244,7 +244,7 @@ async function removeUser(row) {
const ok = window.confirm(`确定删除用户「${row.username}」?此操作不可恢复。`); const ok = window.confirm(`确定删除用户「${row.username}」?此操作不可恢复。`);
if (!ok) return; if (!ok) return;
const res = await deleteAdminUserApi(auth.token, row.id); const res = await deleteAgentUserApi(auth.token, row.id);
if (!res.ok) { if (!res.ok) {
showMessage("error", res.message || "删除失败"); showMessage("error", res.message || "删除失败");
return; return;