Skip to content

Commit 74a7293

Browse files
committed
feat: 分组代理密钥优化
1 parent f90558f commit 74a7293

File tree

3 files changed

+196
-147
lines changed

3 files changed

+196
-147
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<script setup lang="ts">
2+
import { copy } from "@/utils/clipboard";
3+
import { Copy, Key } from "@vicons/ionicons5";
4+
import { NButton, NIcon, NInput, NInputNumber, NModal, NSpace, useMessage } from "naive-ui";
5+
import { ref } from "vue";
6+
7+
interface Props {
8+
modelValue: string;
9+
placeholder?: string;
10+
size?: "small" | "medium" | "large";
11+
}
12+
13+
interface Emits {
14+
(e: "update:modelValue", value: string): void;
15+
}
16+
17+
const props = withDefaults(defineProps<Props>(), {
18+
placeholder: "多个密钥请用英文逗号 , 分隔",
19+
size: "small",
20+
});
21+
22+
const emit = defineEmits<Emits>();
23+
24+
const message = useMessage();
25+
26+
// 密钥生成弹窗相关
27+
const showKeyGeneratorModal = ref(false);
28+
const keyCount = ref(1);
29+
const isGenerating = ref(false);
30+
31+
// 生成随机字符串
32+
function generateRandomString(length: number): string {
33+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
34+
let result = "";
35+
for (let i = 0; i < length; i++) {
36+
result += chars.charAt(Math.floor(Math.random() * chars.length));
37+
}
38+
return result;
39+
}
40+
41+
// 生成密钥
42+
function generateKeys(): string[] {
43+
const keys: string[] = [];
44+
for (let i = 0; i < keyCount.value; i++) {
45+
keys.push(`sk-${generateRandomString(48)}`);
46+
}
47+
return keys;
48+
}
49+
50+
// 打开密钥生成器弹窗
51+
function openKeyGenerator() {
52+
showKeyGeneratorModal.value = true;
53+
keyCount.value = 1;
54+
}
55+
56+
// 确认生成密钥
57+
function confirmGenerateKeys() {
58+
if (isGenerating.value) {
59+
return;
60+
}
61+
62+
try {
63+
isGenerating.value = true;
64+
const newKeys = generateKeys();
65+
const currentValue = props.modelValue || "";
66+
67+
let updatedValue = currentValue.trim();
68+
69+
// 处理逗号兼容情况
70+
if (updatedValue && !updatedValue.endsWith(",")) {
71+
updatedValue += ",";
72+
}
73+
74+
// 添加新生成的密钥
75+
if (updatedValue) {
76+
updatedValue += newKeys.join(",");
77+
} else {
78+
updatedValue = newKeys.join(",");
79+
}
80+
81+
emit("update:modelValue", updatedValue);
82+
showKeyGeneratorModal.value = false;
83+
84+
message.success(`成功生成 ${keyCount.value} 个密钥`);
85+
} finally {
86+
isGenerating.value = false;
87+
}
88+
}
89+
90+
// 复制代理密钥
91+
async function copyProxyKeys() {
92+
const proxyKeys = props.modelValue || "";
93+
if (!proxyKeys.trim()) {
94+
message.warning("暂无密钥可复制");
95+
return;
96+
}
97+
98+
// 将逗号分隔的密钥转换为换行分隔
99+
const formattedKeys = proxyKeys
100+
.split(",")
101+
.map(key => key.trim())
102+
.filter(key => key.length > 0)
103+
.join("\n");
104+
105+
const success = await copy(formattedKeys);
106+
if (success) {
107+
message.success("密钥已复制到剪贴板");
108+
} else {
109+
message.error("复制失败,请手动复制");
110+
}
111+
}
112+
113+
// 处理输入框值变化
114+
function handleInput(value: string) {
115+
emit("update:modelValue", value);
116+
}
117+
</script>
118+
119+
<template>
120+
<div class="proxy-keys-input">
121+
<n-input
122+
:value="modelValue"
123+
:placeholder="placeholder"
124+
clearable
125+
:size="size"
126+
@update:value="handleInput"
127+
>
128+
<template #suffix>
129+
<n-space :size="4" :wrap-item="false">
130+
<n-button text type="primary" :size="size" @click="openKeyGenerator">
131+
<template #icon>
132+
<n-icon :component="Key" />
133+
</template>
134+
生成
135+
</n-button>
136+
<n-button text type="tertiary" :size="size" @click="copyProxyKeys" style="opacity: 0.7">
137+
<template #icon>
138+
<n-icon :component="Copy" />
139+
</template>
140+
复制
141+
</n-button>
142+
</n-space>
143+
</template>
144+
</n-input>
145+
146+
<!-- 密钥生成器弹窗 -->
147+
<n-modal
148+
v-model:show="showKeyGeneratorModal"
149+
preset="dialog"
150+
title="生成代理密钥"
151+
positive-text="确认生成"
152+
negative-text="取消"
153+
:positive-button-props="{ loading: isGenerating }"
154+
@positive-click="confirmGenerateKeys"
155+
>
156+
<n-space vertical :size="16">
157+
<div>
158+
<p style="margin: 0 0 8px 0; color: #666; font-size: 14px">
159+
请输入要生成的密钥数量(最大100个):
160+
</p>
161+
<n-input-number
162+
v-model:value="keyCount"
163+
:min="1"
164+
:max="100"
165+
placeholder="请输入数量"
166+
style="width: 100%"
167+
:disabled="isGenerating"
168+
/>
169+
</div>
170+
<div style="color: #999; font-size: 12px; line-height: 1.4">
171+
<p style="margin: 0">密钥格式:sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</p>
172+
<p style="margin: 4px 0 0 0">生成的密钥将会插入到当前输入框内容的后面,以逗号分隔</p>
173+
</div>
174+
</n-space>
175+
</n-modal>
176+
</div>
177+
</template>
178+
179+
<style scoped>
180+
.proxy-keys-input {
181+
width: 100%;
182+
}
183+
</style>

web/src/components/keys/GroupFormModal.vue

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
<script setup lang="ts">
22
import { keysApi } from "@/api/keys";
33
import { settingsApi } from "@/api/settings";
4+
import ProxyKeysInput from "@/components/common/ProxyKeysInput.vue";
45
import type { Group, GroupConfigOption, UpstreamInfo } from "@/types/models";
56
import { Add, Close, HelpCircleOutline, Remove } from "@vicons/ionicons5";
67
import {
@@ -610,9 +611,10 @@ async function handleSubmit() {
610611
</n-tooltip>
611612
</div>
612613
</template>
613-
<n-input
614-
v-model:value="formData.proxy_keys"
614+
<proxy-keys-input
615+
v-model="formData.proxy_keys"
615616
placeholder="多个密钥请用英文逗号 , 分隔"
617+
size="medium"
616618
/>
617619
</n-form-item>
618620

0 commit comments

Comments
 (0)