From ed2d9f0297cb17fcccd3b86169d3aece259c132e Mon Sep 17 00:00:00 2001 From: tbphp Date: Sat, 26 Jul 2025 22:58:48 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=88=86=E7=BB=84=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E4=BB=A3=E7=90=86=E5=AF=86=E9=92=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web/src/components/common/ProxyKeysInput.vue | 3 +- web/src/components/keys/GroupInfoCard.vue | 88 ++++++++++++++++++-- web/src/utils/display.ts | 15 ++++ 3 files changed, 99 insertions(+), 7 deletions(-) diff --git a/web/src/components/common/ProxyKeysInput.vue b/web/src/components/common/ProxyKeysInput.vue index 7952a3c..a6a129c 100644 --- a/web/src/components/common/ProxyKeysInput.vue +++ b/web/src/components/common/ProxyKeysInput.vue @@ -168,8 +168,7 @@ function handleInput(value: string) { />
-

密钥格式:sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

-

生成的密钥将会插入到当前输入框内容的后面,以逗号分隔

+

生成的密钥将会插入到当前输入框内容的后面,以逗号分隔

diff --git a/web/src/components/keys/GroupInfoCard.vue b/web/src/components/keys/GroupInfoCard.vue index c75ca50..507e20f 100644 --- a/web/src/components/keys/GroupInfoCard.vue +++ b/web/src/components/keys/GroupInfoCard.vue @@ -3,10 +3,11 @@ import { keysApi } from "@/api/keys"; import type { Group, GroupConfigOption, GroupStatsResponse } from "@/types/models"; import { appState } from "@/utils/app-state"; import { copy } from "@/utils/clipboard"; -import { getGroupDisplayName } from "@/utils/display"; -import { Pencil, Trash } from "@vicons/ionicons5"; +import { getGroupDisplayName, maskProxyKeys } from "@/utils/display"; +import { CopyOutline, EyeOffOutline, EyeOutline, Pencil, Trash } from "@vicons/ionicons5"; import { NButton, + NButtonGroup, NCard, NCollapse, NCollapseItem, @@ -20,7 +21,7 @@ import { NTooltip, useDialog, } from "naive-ui"; -import { onMounted, ref, watch } from "vue"; +import { computed, onMounted, ref, watch } from "vue"; import GroupFormModal from "./GroupFormModal.vue"; interface Props { @@ -43,6 +44,30 @@ const showEditModal = ref(false); const delLoading = ref(false); const expandedName = ref([]); const configOptions = ref([]); +const showProxyKeys = ref(false); + +const proxyKeysDisplay = computed(() => { + if (!props.group?.proxy_keys) { + return "-"; + } + if (showProxyKeys.value) { + return props.group.proxy_keys.replace(/,/g, "\n"); + } + return maskProxyKeys(props.group.proxy_keys); +}); + +async function copyProxyKeys() { + if (!props.group?.proxy_keys) { + return; + } + const keysToCopy = props.group.proxy_keys.replace(/,/g, "\n"); + const success = await copy(keysToCopy); + if (success) { + window.$message.success("代理密钥已复制到剪贴板"); + } else { + window.$message.error("复制失败"); + } +} onMounted(() => { loadStats(); @@ -385,10 +410,41 @@ function resetPage() { {{ group?.validation_endpoint }} - + + +
+ {{ proxyKeysDisplay }} + + + + {{ showProxyKeys ? "隐藏密钥" : "显示密钥" }} + + + + 复制密钥 + + +
+
+
+
- {{ group?.description }} + {{ group?.description || "-" }}
@@ -613,6 +669,28 @@ function resetPage() { color: #374151; } +.proxy-keys-content { + display: flex; + align-items: flex-start; + justify-content: space-between; + width: 100%; + gap: 8px; +} + +.key-text { + flex-grow: 1; + font-family: monospace; + white-space: pre-wrap; + word-break: break-all; + line-height: 1.5; + padding-top: 4px; /* Align with buttons */ + color: #374151; +} + +.key-actions { + flex-shrink: 0; +} + /* 配置项tooltip样式 */ .config-label { display: inline-flex; diff --git a/web/src/utils/display.ts b/web/src/utils/display.ts index 9d3c354..5ee4e40 100644 --- a/web/src/utils/display.ts +++ b/web/src/utils/display.ts @@ -49,3 +49,18 @@ export function maskKey(key: string): string { } return `${key.substring(0, 4)}...${key.substring(key.length - 4)}`; } + +/** + * Masks a comma-separated string of keys. + * @param keys The comma-separated keys string. + * @returns The masked keys string. + */ +export function maskProxyKeys(keys: string): string { + if (!keys) { + return ""; + } + return keys + .split(",") + .map(key => maskKey(key.trim())) + .join(", "); +}