111
This commit is contained in:
72
src/api/agentCardKeys.js
Normal file
72
src/api/agentCardKeys.js
Normal 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
72
src/api/agentUsers.js
Normal 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",
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user