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(", ");
+}