diff --git a/apps/web/src/api/admin/members.ts b/apps/web/src/api/admin/members.ts index 96aadf0..39af258 100644 --- a/apps/web/src/api/admin/members.ts +++ b/apps/web/src/api/admin/members.ts @@ -1,16 +1,22 @@ import type { MemberItem } from "@new-api-key-usage/core"; import { adminHttp } from "../http.ts"; +export type MemberWithToken = { + member: MemberItem; + token: string; +}; + export async function fetchMembers(): Promise { const { data } = await adminHttp.get("/members"); return data; } +/** 开户:响应含令牌明文(仅此一次) */ export async function createMember(body: { name: string; remark?: string; -}): Promise { - const { data } = await adminHttp.post("/members", body); +}): Promise { + const { data } = await adminHttp.post("/members", body); return data; } @@ -22,6 +28,7 @@ export async function patchMember( return data; } +/** 重发:旧令牌即刻失效,明文仅此一次 */ export async function issueMemberToken(id: number): Promise<{ token: string }> { const { data } = await adminHttp.post<{ token: string }>(`/members/${id}/token`); return data; diff --git a/apps/web/src/components/admin/member-token-modal.vue b/apps/web/src/components/admin/member-token-modal.vue new file mode 100644 index 0000000..2641215 --- /dev/null +++ b/apps/web/src/components/admin/member-token-modal.vue @@ -0,0 +1,122 @@ + + + + + diff --git a/apps/web/src/components/admin/register-key-modal.vue b/apps/web/src/components/admin/register-key-modal.vue index 16f2646..ab7da6c 100644 --- a/apps/web/src/components/admin/register-key-modal.vue +++ b/apps/web/src/components/admin/register-key-modal.vue @@ -6,6 +6,7 @@ import { computed, reactive, ref, watch } from "vue"; import type { MemberItem } from "@new-api-key-usage/core"; import type { ApiError } from "@/api/http.ts"; import { useCreateMember, useRegisterKey } from "@/queries/admin.ts"; +import MemberTokenModal from "@/components/admin/member-token-modal.vue"; const props = defineProps<{ open: boolean; @@ -30,6 +31,9 @@ const resultMsg = ref<{ type: "success" | "error" | "warning"; text: string } | const showNewMember = ref(false); const newMemberName = ref(""); const newMemberRemark = ref(""); +const tokenOpen = ref(false); +const plainToken = ref(""); +const tokenMemberName = ref(""); watch( () => props.open, @@ -106,15 +110,22 @@ async function onCreateMember() { const name = newMemberName.value.trim(); if (!name) return; try { - const m = await createMember.mutateAsync({ + const res = await createMember.mutateAsync({ name, remark: newMemberRemark.value.trim(), }); - form.member_id = m.id; + form.member_id = res.member.id; showNewMember.value = false; newMemberName.value = ""; newMemberRemark.value = ""; - resultMsg.value = { type: "success", text: `已新建成员「${m.name}」并选为归属` }; + // POST /members 必回明文令牌——立刻展示,避免遗失 + plainToken.value = res.token; + tokenMemberName.value = res.member.name; + tokenOpen.value = true; + resultMsg.value = { + type: "success", + text: `已新建成员「${res.member.name}」并选为归属,请保存下方令牌`, + }; } catch (e) { const err = e as ApiError; resultMsg.value = { type: "error", text: err.message || "新建成员失败" }; @@ -187,6 +198,12 @@ async function onCreateMember() { :message="resultMsg.text" /> + diff --git a/apps/web/src/queries/admin.ts b/apps/web/src/queries/admin.ts index fca70a7..80e9956 100644 --- a/apps/web/src/queries/admin.ts +++ b/apps/web/src/queries/admin.ts @@ -131,6 +131,7 @@ export function useCreateMember() { mutationFn: createMember, onSuccess: () => { void qc.invalidateQueries({ queryKey: qk.admin.members() }); + void qc.invalidateQueries({ queryKey: ["admin", "keys"] }); }, }); } diff --git a/apps/web/src/views/admin/members-view.vue b/apps/web/src/views/admin/members-view.vue index eee1310..131b18f 100644 --- a/apps/web/src/views/admin/members-view.vue +++ b/apps/web/src/views/admin/members-view.vue @@ -1,13 +1,227 @@ + +