hongjli
2025-05-27 3a2d67fa9b765a3cad6306a0369976d4b0a0a892
实现对key值的功能
已修改1个文件
247 ■■■■ 文件已修改
src/app/chatroom/page.tsx 247 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/app/chatroom/page.tsx
@@ -4,10 +4,13 @@
export default function ChatRoomPage() {
  const [apiKey, setApiKey] = useState<string>('');
  const [originalApiKey, setOriginalApiKey] = useState<string>(''); // 存储从API获取的原始密钥
  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(() => {
    // 获取存储的API 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);
      // 按照接口文档格式解析响应
      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 || '获取密钥失败:响应格式不正确');
      }
    } catch (err) {
      console.error('获取API密钥失败:', err);
      const errorMessage = err instanceof Error ? err.message : '获取密钥时发生未知错误';
      showErrorMessage(`密钥获取失败: ${errorMessage}`);
      return null;
    } finally {
      setIsLoading(false);
    }
  };
  // 添加函数来显示API Key设置模态框,同时重新获取localStorage中的值
  const handleShowApiKeyModal = () => {
    // 重新获取存储的API 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);
      // 按照接口文档格式解析响应
      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 || '更新密钥失败:响应格式不正确');
      }
    } 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()) {
      // 如果密钥有变化,则调用更新API
      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);
      }
    }
  };
  // 添加函数来显示API Key设置模态框,同时从API获取当前密钥
  const handleShowApiKeyModal = async () => {
    setShowApiKeyInput(true);
    // 从API获取当前密钥
    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 ? "正在获取当前密钥..." : "请输入新的 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>