11<script setup lang="ts">
22import { settingsApi , type SettingCategory } from " @/api/settings" ;
3- import { HelpCircle , Save } from " @vicons/ionicons5" ;
3+ import { copy } from " @/utils/clipboard" ;
4+ import { Copy , HelpCircle , Key , Save } from " @vicons/ionicons5" ;
45import {
56 NButton ,
67 NCard ,
@@ -11,6 +12,7 @@ import {
1112 NIcon ,
1213 NInput ,
1314 NInputNumber ,
15+ NModal ,
1416 NSpace ,
1517 NTooltip ,
1618 useMessage ,
@@ -23,6 +25,11 @@ const form = ref<Record<string, string | number>>({});
2325const isSaving = ref (false );
2426const message = useMessage ();
2527
28+ // 密钥生成弹窗相关
29+ const showKeyGeneratorModal = ref (false );
30+ const keyCount = ref (1 );
31+ const isGenerating = ref (false );
32+
2633fetchSettings ();
2734
2835async function fetchSettings() {
@@ -58,6 +65,88 @@ async function handleSubmit() {
5865 isSaving .value = false ;
5966 }
6067}
68+
69+ // 生成随机字符串
70+ function generateRandomString(length : number ): string {
71+ const chars = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" ;
72+ let result = " " ;
73+ for (let i = 0 ; i < length ; i ++ ) {
74+ result += chars .charAt (Math .floor (Math .random () * chars .length ));
75+ }
76+ return result ;
77+ }
78+
79+ // 生成密钥
80+ function generateKeys(): string [] {
81+ const keys: string [] = [];
82+ for (let i = 0 ; i < keyCount .value ; i ++ ) {
83+ keys .push (` sk-${generateRandomString (48 )} ` );
84+ }
85+ return keys ;
86+ }
87+
88+ // 打开密钥生成器弹窗
89+ function openKeyGenerator() {
90+ showKeyGeneratorModal .value = true ;
91+ keyCount .value = 1 ;
92+ }
93+
94+ // 确认生成密钥
95+ function confirmGenerateKeys() {
96+ if (isGenerating .value ) {
97+ return ;
98+ }
99+
100+ try {
101+ isGenerating .value = true ;
102+ const newKeys = generateKeys ();
103+ const currentValue = (form .value [" proxy_keys" ] as string ) || " " ;
104+
105+ let updatedValue = currentValue .trim ();
106+
107+ // 处理逗号兼容情况
108+ if (updatedValue && ! updatedValue .endsWith (" ," )) {
109+ updatedValue += " ," ;
110+ }
111+
112+ // 添加新生成的密钥
113+ if (updatedValue ) {
114+ updatedValue += newKeys .join (" ," );
115+ } else {
116+ updatedValue = newKeys .join (" ," );
117+ }
118+
119+ form .value [" proxy_keys" ] = updatedValue ;
120+ showKeyGeneratorModal .value = false ;
121+
122+ message .success (` 成功生成 ${keyCount .value } 个密钥 ` );
123+ } finally {
124+ isGenerating .value = false ;
125+ }
126+ }
127+
128+ // 复制代理密钥
129+ async function copyProxyKeys() {
130+ const proxyKeys = (form .value [" proxy_keys" ] as string ) || " " ;
131+ if (! proxyKeys .trim ()) {
132+ message .warning (" 暂无密钥可复制" );
133+ return ;
134+ }
135+
136+ // 将逗号分隔的密钥转换为换行分隔
137+ const formattedKeys = proxyKeys
138+ .split (" ," )
139+ .map (key => key .trim ())
140+ .filter (key => key .length > 0 )
141+ .join (" \n " );
142+
143+ const success = await copy (formattedKeys );
144+ if (success ) {
145+ message .success (" 密钥已复制到剪贴板" );
146+ } else {
147+ message .error (" 复制失败,请手动复制" );
148+ }
149+ }
61150 </script >
62151
63152<template >
@@ -72,11 +161,11 @@ async function handleSubmit() {
72161 hoverable
73162 bordered
74163 >
75- <n-grid :x-gap =" 24 " :y-gap =" 0" responsive =" screen" cols =" 1 s:2 m:2 l:3 xl:4 " >
164+ <n-grid :x-gap =" 36 " :y-gap =" 0" responsive =" screen" cols =" 1 s:2 m:2 l:3 xl:3 " >
76165 <n-grid-item
77166 v-for =" item in category.settings"
78167 :key =" item.key"
79- :span =" item.key === 'proxy_keys' ? 4 : 1"
168+ :span =" item.key === 'proxy_keys' ? 3 : 1"
80169 >
81170 <n-form-item
82171 :path =" item.key"
@@ -115,7 +204,30 @@ async function handleSubmit() {
115204 placeholder =" 请输入内容"
116205 clearable
117206 size =" small"
118- />
207+ >
208+ <template v-if =" item .key === ' proxy_keys' " #suffix >
209+ <n-space :size =" 4" :wrap-item =" false" >
210+ <n-button text type =" primary" size =" small" @click =" openKeyGenerator" >
211+ <template #icon >
212+ <n-icon :component =" Key" />
213+ </template >
214+ 生成
215+ </n-button >
216+ <n-button
217+ text
218+ type =" tertiary"
219+ size =" small"
220+ @click =" copyProxyKeys"
221+ style =" opacity : 0.7 "
222+ >
223+ <template #icon >
224+ <n-icon :component =" Copy" />
225+ </template >
226+ 复制
227+ </n-button >
228+ </n-space >
229+ </template >
230+ </n-input >
119231 </n-form-item >
120232 </n-grid-item >
121233 </n-grid >
@@ -141,5 +253,36 @@ async function handleSubmit() {
141253 {{ isSaving ? "保存中..." : "保存设置" }}
142254 </n-button >
143255 </div >
256+
257+ <!-- 密钥生成器弹窗 -->
258+ <n-modal
259+ v-model:show =" showKeyGeneratorModal"
260+ preset =" dialog"
261+ title =" 生成代理密钥"
262+ positive-text =" 确认生成"
263+ negative-text =" 取消"
264+ :positive-button-props =" { loading: isGenerating }"
265+ @positive-click =" confirmGenerateKeys"
266+ >
267+ <n-space vertical :size =" 16" >
268+ <div >
269+ <p style =" margin : 0 0 8px 0 ; color : #666 ; font-size : 14px " >
270+ 请输入要生成的密钥数量(最大100个):
271+ </p >
272+ <n-input-number
273+ v-model:value =" keyCount"
274+ :min =" 1"
275+ :max =" 100"
276+ placeholder =" 请输入数量"
277+ style =" width : 100% "
278+ :disabled =" isGenerating"
279+ />
280+ </div >
281+ <div style =" color : #999 ; font-size : 12px ; line-height : 1.4 " >
282+ <p style =" margin : 0 " >密钥格式:sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</p >
283+ <p style =" margin : 4px 0 0 0 " >生成的密钥将会插入到当前输入框内容的后面,以逗号分隔</p >
284+ </div >
285+ </n-space >
286+ </n-modal >
144287 </n-space >
145288</template >
0 commit comments