From 3a2d67fa9b765a3cad6306a0369976d4b0a0a892 Mon Sep 17 00:00:00 2001 From: hongjli <3117313295@qq.com> Date: 星期二, 27 五月 2025 14:11:04 +0800 Subject: [PATCH] 实现对key值的功能 --- src/app/chatroom/page.tsx | 247 +++++++++++++++++++++++++++++++++++++++++-------- 1 files changed, 205 insertions(+), 42 deletions(-) diff --git a/src/app/chatroom/page.tsx b/src/app/chatroom/page.tsx index 0978f9d..e6e67b4 100644 --- a/src/app/chatroom/page.tsx +++ b/src/app/chatroom/page.tsx @@ -4,10 +4,13 @@ export default function ChatRoomPage() { const [apiKey, setApiKey] = useState<string>(''); + const [originalApiKey, setOriginalApiKey] = useState<string>(''); // 瀛樺偍浠嶢PI鑾峰彇鐨勫師濮嬪瘑閽� const [showApiKeyInput, setShowApiKeyInput] = useState(false); const [showApiKey, setShowApiKey] = useState(false); const [error, setError] = useState<string | null>(null); const [showError, setShowError] = useState(false); + const [isLoading, setIsLoading] = useState(false); + const [isUpdating, setIsUpdating] = useState(false); useEffect(() => { // 鑾峰彇瀛樺偍鐨凙PI Key @@ -17,82 +20,242 @@ } }, []); - const handleApiKeySubmit = () => { - if (apiKey.trim()) { - localStorage.setItem('api-key', apiKey); - setShowApiKeyInput(false); - setError(null); + // 鏄剧ず閿欒娑堟伅 + const showErrorMessage = (message: string) => { + setError(message); + setShowError(true); + setTimeout(() => { + setShowError(false); + setTimeout(() => setError(null), 300); + }, 3000); + }; + + // 鑾峰彇褰撳墠瀵嗛挜 + const fetchCurrentApiKey = async () => { + setIsLoading(true); + try { + const response = await fetch('http://121.43.139.99:8080/api/secret-key', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error(`鑾峰彇瀵嗛挜澶辫触: HTTP ${response.status}`); + } + + const result = await response.json(); + console.log('API鍝嶅簲:', result); + + // 鎸夌収鎺ュ彛鏂囨。鏍煎紡瑙f瀽鍝嶅簲 + if (result.code === 200 && result.data && result.data.key) { + const keyValue = result.data.key; + setOriginalApiKey(keyValue); + setApiKey(keyValue); + console.log('鎴愬姛鑾峰彇API瀵嗛挜:', keyValue); + return keyValue; + } else { + throw new Error(result.message || '鑾峰彇瀵嗛挜澶辫触锛氬搷搴旀牸寮忎笉姝g‘'); + } + } catch (err) { + console.error('鑾峰彇API瀵嗛挜澶辫触:', err); + const errorMessage = err instanceof Error ? err.message : '鑾峰彇瀵嗛挜鏃跺彂鐢熸湭鐭ラ敊璇�'; + showErrorMessage(`瀵嗛挜鑾峰彇澶辫触: ${errorMessage}`); + return null; + } finally { + setIsLoading(false); } }; - // 娣诲姞鍑芥暟鏉ユ樉绀篈PI Key璁剧疆妯℃�佹锛屽悓鏃堕噸鏂拌幏鍙杔ocalStorage涓殑鍊� - const handleShowApiKeyModal = () => { - // 閲嶆柊鑾峰彇瀛樺偍鐨凙PI Key - const storedApiKey = localStorage.getItem('api-key'); - if (storedApiKey) { - setApiKey(storedApiKey); + // 鏇存柊瀵嗛挜 + const updateApiKey = async (newKey: string) => { + if (!newKey.trim()) { + showErrorMessage('瀵嗛挜涓嶈兘涓虹┖'); + return false; } + + setIsUpdating(true); + try { + const response = await fetch(`http://121.43.139.99:8080/api/secret-key/update/${encodeURIComponent(newKey)}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }); + + if (!response.ok) { + throw new Error(`鏇存柊瀵嗛挜澶辫触: HTTP ${response.status}`); + } + + const result = await response.json(); + console.log('鏇存柊API鍝嶅簲:', result); + + // 鎸夌収鎺ュ彛鏂囨。鏍煎紡瑙f瀽鍝嶅簲 + if (result.code === 200 && result.data && result.data.key) { + const updatedKey = result.data.key; + setOriginalApiKey(updatedKey); + setApiKey(updatedKey); + localStorage.setItem('api-key', updatedKey); + console.log('鎴愬姛鏇存柊API瀵嗛挜:', updatedKey); + showErrorMessage('瀵嗛挜鏇存柊鎴愬姛锛�'); + return true; + } else { + throw new Error(result.message || '鏇存柊瀵嗛挜澶辫触锛氬搷搴旀牸寮忎笉姝g‘'); + } + } catch (err) { + console.error('鏇存柊API瀵嗛挜澶辫触:', err); + const errorMessage = err instanceof Error ? err.message : '鏇存柊瀵嗛挜鏃跺彂鐢熸湭鐭ラ敊璇�'; + showErrorMessage(`瀵嗛挜鏇存柊澶辫触: ${errorMessage}`); + return false; + } finally { + setIsUpdating(false); + } + }; + + const handleApiKeySubmit = async () => { + if (apiKey.trim()) { + // 濡傛灉瀵嗛挜鏈夊彉鍖栵紝鍒欒皟鐢ㄦ洿鏂癆PI + if (apiKey.trim() !== originalApiKey) { + const success = await updateApiKey(apiKey.trim()); + if (success) { + setShowApiKeyInput(false); + setError(null); + } + } else { + // 瀵嗛挜娌℃湁鍙樺寲锛岀洿鎺ュ叧闂ā鎬佹 + localStorage.setItem('api-key', apiKey); + setShowApiKeyInput(false); + setError(null); + } + } + }; + + // 娣诲姞鍑芥暟鏉ユ樉绀篈PI Key璁剧疆妯℃�佹锛屽悓鏃朵粠API鑾峰彇褰撳墠瀵嗛挜 + const handleShowApiKeyModal = async () => { setShowApiKeyInput(true); + // 浠嶢PI鑾峰彇褰撳墠瀵嗛挜 + await fetchCurrentApiKey(); }; return ( <div className="min-h-screen bg-gradient-to-b from-[#1E2B63] to-[#0A1033] flex items-center justify-center relative"> + {/* 閿欒鎻愮ず */} + {error && ( + <div + className={`fixed top-20 left-1/2 transform -translate-x-1/2 + flex items-center gap-2 px-4 py-2 text-sm text-white + transition-all duration-400 ease-out z-[60] + ${showError + ? 'opacity-100 translate-y-0 scale-100' + : 'opacity-0 -translate-y-2 scale-95' + } + before:content-[''] before:absolute before:inset-0 before:bg-blue-500 + before:rounded-lg before:opacity-90 before:-z-10 + after:content-[''] after:absolute after:inset-0 after:bg-blue-500/50 + after:blur-md after:rounded-lg after:-z-20`} + > + <svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4" viewBox="0 0 20 20" fill="currentColor"> + <path fillRule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clipRule="evenodd" /> + </svg> + <span className="relative">{error}</span> + </div> + )} + {/* API Key Modal */} {showApiKeyInput && ( <div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50"> - <div className="bg-white rounded-2xl p-6 w-[400px] space-y-4 shadow-xl"> + <div className="bg-white rounded-2xl p-6 w-[450px] space-y-4 shadow-xl"> <div className="flex justify-between items-center"> - <h2 className="text-xl font-semibold">璁剧疆 API Key</h2> + <h2 className="text-xl font-semibold">绠$悊 API Key</h2> <button onClick={() => setShowApiKeyInput(false)} className="text-gray-400 hover:text-gray-600" + disabled={isLoading || isUpdating} > <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> <path fillRule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clipRule="evenodd" /> </svg> </button> </div> + + {/* 褰撳墠瀵嗛挜鏄剧ず */} + {originalApiKey && ( + <div className="bg-gray-50 rounded-lg p-3"> + <label className="text-sm font-medium text-gray-700 mb-1 block">褰撳墠瀵嗛挜</label> + <div className="text-sm text-gray-600 font-mono break-all"> + {showApiKey ? originalApiKey : '鈥⑩�⑩�⑩�⑩�⑩�⑩�⑩�⑩�⑩�⑩�⑩�⑩�⑩�⑩�⑩��'} + </div> + </div> + )} - <div className="relative"> - <input - type={showApiKey ? "text" : "password"} - value={apiKey} - onChange={(e) => setApiKey(e.target.value)} - placeholder="璇疯緭鍏ユ偍鐨� API Key" - className="w-full px-4 py-2 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-100/50 text-gray-900" - /> - <button - type="button" - onClick={() => setShowApiKey(!showApiKey)} - className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600" - > - {showApiKey ? ( - // 鐪肩潧鍏抽棴鍥炬爣 - <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> - <path fillRule="evenodd" d="M3.707 2.293a1 1 0 00-1.414 1.414l14 14a1 1 0 001.414-1.414l-1.473-1.473A10.014 10.014 0 0019.542 10C18.268 5.943 14.478 3 10 3a9.958 9.958 0 00-4.512 1.074l-1.78-1.781zm4.261 4.26l1.514 1.515a2.003 2.003 0 012.45 2.45l1.514 1.514a4 4 0 00-5.478-5.478z" clipRule="evenodd" /> - <path d="M12.454 16.697L9.75 13.992a4 4 0 01-3.742-3.741L2.335 6.578A9.98 9.98 0 00.458 10c1.274 4.057 5.065 7 9.542 7 .847 0 1.669-.105 2.454-.303z" /> - </svg> - ) : ( - // 鐪肩潧鍥炬爣 - <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> - <path d="M10 12a2 2 0 100-4 2 2 0 000 4z" /> - <path fillRule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clipRule="evenodd" /> - </svg> + <div className="space-y-2"> + <label className="text-sm font-medium text-gray-700">鏂板瘑閽�</label> + <div className="relative"> + <input + type={showApiKey ? "text" : "password"} + value={apiKey} + onChange={(e) => setApiKey(e.target.value)} + placeholder={isLoading ? "姝e湪鑾峰彇褰撳墠瀵嗛挜..." : "璇疯緭鍏ユ柊鐨� API Key"} + disabled={isLoading || isUpdating} + className="w-full px-4 py-2 rounded-xl border border-gray-200 focus:outline-none focus:ring-2 focus:ring-blue-100/50 text-gray-900 disabled:bg-gray-100 disabled:cursor-not-allowed" + /> + <button + type="button" + onClick={() => setShowApiKey(!showApiKey)} + disabled={isLoading || isUpdating} + className="absolute right-3 top-1/2 -translate-y-1/2 text-gray-400 hover:text-gray-600 disabled:cursor-not-allowed" + > + {showApiKey ? ( + // 鐪肩潧鍏抽棴鍥炬爣 + <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> + <path fillRule="evenodd" d="M3.707 2.293a1 1 0 00-1.414 1.414l14 14a1 1 0 001.414-1.414l-1.473-1.473A10.014 10.014 0 0019.542 10C18.268 5.943 14.478 3 10 3a9.958 9.958 0 00-4.512 1.074l-1.78-1.781zm4.261 4.26l1.514 1.515a2.003 2.003 0 012.45 2.45l1.514 1.514a4 4 0 00-5.478-5.478z" clipRule="evenodd" /> + <path d="M12.454 16.697L9.75 13.992a4 4 0 01-3.742-3.741L2.335 6.578A9.98 9.98 0 00.458 10c1.274 4.057 5.065 7 9.542 7 .847 0 1.669-.105 2.454-.303z" /> + </svg> + ) : ( + // 鐪肩潧鍥炬爣 + <svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5" viewBox="0 0 20 20" fill="currentColor"> + <path d="M10 12a2 2 0 100-4 2 2 0 000 4z" /> + <path fillRule="evenodd" d="M.458 10C1.732 5.943 5.522 3 10 3s8.268 2.943 9.542 7c-1.274 4.057-5.064 7-9.542 7S1.732 14.057.458 10zM14 10a4 4 0 11-8 0 4 4 0 018 0z" clipRule="evenodd" /> + </svg> + )} + </button> + + {/* 鍔犺浇鎸囩ず鍣� */} + {isLoading && ( + <div className="absolute right-12 top-1/2 -translate-y-1/2"> + <div className="w-4 h-4 border-2 border-blue-500/30 border-t-blue-500 rounded-full animate-spin"></div> + </div> )} - </button> + </div> + + {/* 鎻愮ず淇℃伅 */} + <div className="text-xs text-gray-500"> + {apiKey.trim() !== originalApiKey && apiKey.trim() !== '' ? ( + <span className="text-orange-600">瀵嗛挜宸蹭慨鏀癸紝鐐瑰嚮纭畾灏嗘洿鏂板埌鏈嶅姟鍣�</span> + ) : ( + <span>淇敼瀵嗛挜鍚庡皢鑷姩鍚屾鍒版湇鍔″櫒</span> + )} + </div> </div> + <div className="flex justify-end gap-2"> <button onClick={() => setShowApiKeyInput(false)} - className="px-4 py-2 text-gray-600 hover:text-gray-900" + disabled={isLoading || isUpdating} + className="px-4 py-2 text-gray-600 hover:text-gray-900 disabled:cursor-not-allowed disabled:opacity-50" > 鍙栨秷 </button> <button onClick={handleApiKeySubmit} - className="px-4 py-2 bg-blue-500 text-white rounded-xl hover:bg-blue-600" + disabled={isLoading || isUpdating || !apiKey.trim()} + className="px-4 py-2 bg-blue-500 text-white rounded-xl hover:bg-blue-600 disabled:cursor-not-allowed disabled:opacity-50 flex items-center gap-2" > - 纭畾 + {isUpdating && ( + <div className="w-4 h-4 border-2 border-white/30 border-t-white rounded-full animate-spin"></div> + )} + {isUpdating ? '鏇存柊涓�...' : '纭畾'} </button> </div> </div> -- Gitblit v1.9.3