From 4773a9239c6dc4bc6bdc4e717230ed14f7589249 Mon Sep 17 00:00:00 2001 From: huty Date: Tue, 9 Dec 2025 22:38:05 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E8=87=B3=20v0.1.1=5F20251209?= =?UTF-8?q?=20=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- App.tsx | 41 +++++++-- README.md | 88 +++++++++++++++++--- components/SettingsModal.tsx | 65 +++++++++++++++ releases/WORK-SH-XPC-SQL-0.1.1_20251209.zip | Bin 0 -> 16300 bytes services/gemini.ts | 16 +++- types.ts | 3 +- 6 files changed, 190 insertions(+), 23 deletions(-) create mode 100644 components/SettingsModal.tsx create mode 100644 releases/WORK-SH-XPC-SQL-0.1.1_20251209.zip diff --git a/App.tsx b/App.tsx index d468c02..1f18297 100644 --- a/App.tsx +++ b/App.tsx @@ -1,9 +1,10 @@ -import React, { useState } from 'react'; +import React, { useState, useEffect } from 'react'; import { generateSql } from './services/gemini'; import { Button } from './components/Button'; import { TextArea } from './components/TextArea'; import { Select } from './components/Select'; +import { SettingsModal } from './components/SettingsModal'; import { LoadingState, DatabaseType } from './types'; // Default placeholders to help the user understand what to input @@ -58,6 +59,23 @@ const App: React.FC = () => { const [status, setStatus] = useState(LoadingState.IDLE); const [errorMsg, setErrorMsg] = useState(null); + // API Key Management + const [apiKey, setApiKey] = useState(''); + const [isSettingsOpen, setIsSettingsOpen] = useState(false); + + useEffect(() => { + const storedKey = localStorage.getItem('user_api_key'); + if (storedKey) { + setApiKey(storedKey); + } + }, []); + + const handleSaveApiKey = (key: string) => { + setApiKey(key); + localStorage.setItem('user_api_key', key); + setIsSettingsOpen(false); + }; + const handleGenerate = async () => { if (!tableStructure.trim() || !requirement.trim()) { setErrorMsg("请至少填写表结构和查询需求。"); @@ -73,7 +91,8 @@ const App: React.FC = () => { tableStructure, dictionaryData, requirement, - databaseType + databaseType, + apiKey // Pass the custom API key }); setGeneratedSql(sql); setStatus(LoadingState.SUCCESS); @@ -86,12 +105,18 @@ const App: React.FC = () => { const copyToClipboard = () => { if (generatedSql) { navigator.clipboard.writeText(generatedSql); - // Optional: Show a toast here, but for now we'll just rely on user action } }; return (
+ setIsSettingsOpen(false)} + onSave={handleSaveApiKey} + currentApiKey={apiKey} + /> + {/* Header */}
@@ -106,7 +131,13 @@ const App: React.FC = () => {
- {/* Placeholder for future user settings or profile */} +
@@ -225,4 +256,4 @@ const App: React.FC = () => { ); }; -export default App; +export default App; \ No newline at end of file diff --git a/README.md b/README.md index 426703a..dd0c97d 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,82 @@ -
-GHBanner -
+# SQL Translate Pro -# Run and deploy your AI Studio app +**[English]** | [中文] | [日本語] -This contains everything you need to run your app locally. +**SQL Translate Pro** is an intelligent SQL generation tool powered by Google Gemini. It helps developers and data analysts quickly build complex SQL queries by analyzing table structures, dictionary definitions, and natural language requirements. It automatically handles table joins and dictionary code translations. -View your app in AI Studio: https://ai.studio/apps/drive/1ofeEEpnincTenGsYbd-peK7s-ltU-f1p +--- -## Run Locally +### 🌟 Key Features -**Prerequisites:** Node.js +* **Intelligent Generation**: Converts natural language requirements into precise SQL queries. +* **Dictionary Translation**: Automatically identifies code fields (e.g., `status_code`) and joins them with dictionary tables to retrieve readable names. +* **Multi-DB Support**: Optimized for MySQL, PostgreSQL, Oracle, SQL Server, Hive, Dameng (Dm), and SQLite. +* **Privacy Focused**: Your API Key is stored locally in your browser and used directly for API calls. +### 🚀 How to Use -1. Install dependencies: - `npm install` -2. Set the `GEMINI_API_KEY` in [.env.local](.env.local) to your Gemini API key -3. Run the app: - `npm run dev` +1. **Select Database**: Choose your target database dialect (e.g., MySQL, Oracle). +2. **Input Schema**: Paste your table DDL or field descriptions. +3. **Input Dictionary**: Describe your dictionary table structure and mapping rules. +4. **Describe Requirement**: Type what you want to query in plain text. +5. **Generate**: Click the button to get ready-to-use SQL. +6. **Settings**: Click the gear icon in the top right to configure your own Google Gemini API Key. + +--- + +# SQL Translate Pro (中文) + +**SQL Translate Pro** 是一款基于 Google Gemini 的智能 SQL 生成工具。它能够根据您提供的表结构、字典表定义以及自然语言查询需求,自动构建复杂的 SQL 查询语句,并智能处理字典代码的翻译(关联查询)。 + +### 🌟 主要功能 + +* **智能生成**:将自然语言需求转化为精确的 SQL 语句。 +* **字典自动翻译**:自动识别代码字段(如 `nation_code`)并关联字典表取值,无需手动编写繁琐的 JOIN。 +* **多数据库支持**:完美支持 MySQL, PostgreSQL, Oracle, SQL Server, Hive, 达梦数据库 (Dm) 和 SQLite 方言。 +* **隐私安全**:支持自定义 API Key,Key 仅保存在本地浏览器中,直接用于 API 请求。 + +### 🚀 使用指南 + +1. **选择数据库**:在左上角下拉框选择目标数据库类型。 +2. **输入表结构**:粘贴建表语句 (DDL) 或字段描述信息。 +3. **输入字典信息**:描述字典表的结构以及字段关联规则。 +4. **描述需求**:用自然语言描述您想要查询的数据(例如:“查询学生信息及其对应的政治面貌中文名”)。 +5. **点击生成**:获取纯净的 SQL 代码。 +6. **设置 Key**:点击右上角设置按钮,填入您的 Google Gemini API Key 以获得最佳体验。 + +--- + +# SQL Translate Pro (日本語) + +**SQL Translate Pro** は、Google Gemini を搭載したインテリジェントな SQL 生成ツールです。テーブル定義、辞書データ、および自然言語の要件に基づいて、辞書変換(コード値の名称変換)を含む複雑な SQL クエリを自動的に生成します。 + +### 🌟 主な機能 + +* **インテリジェント生成**: 自然言語の要件を正確な SQL クエリに変換します。 +* **辞書自動変換**: コード項目(例:`status_code`)を自動的に特定し、辞書テーブルと結合して名称を取得します。 +* **マルチDB対応**: MySQL, PostgreSQL, Oracle, SQL Server, Hive, Dameng (Dm), SQLite に最適化されています。 +* **プライバシー重視**: API キーはブラウザにローカル保存され、API 呼び出しに直接使用されます。 + +### 🚀 使い方 + +1. **データベース選択**: ターゲットとなるデータベースの種類を選択します。 +2. **スキーマ入力**: テーブル作成ステートメント (DDL) またはフィールドの説明を貼り付けます。 +3. **辞書情報入力**: 辞書テーブルの構造とマッピングルールを記述します。 +4. **要件の記述**: 抽出したいデータを自然言語で入力します。 +5. **生成**: ボタンをクリックすると、すぐに使える SQL が生成されます。 +6. **設定**: 右上の設定ボタンから、独自の Google Gemini API Key を設定できます。 + +--- + +## 🛠 Setup & Development + +```bash +# Install dependencies +npm install + +# Start development server +npm run dev + +# Build for production +npm run build +``` diff --git a/components/SettingsModal.tsx b/components/SettingsModal.tsx new file mode 100644 index 0000000..580505d --- /dev/null +++ b/components/SettingsModal.tsx @@ -0,0 +1,65 @@ +import React, { useState, useEffect } from 'react'; +import { Button } from './Button'; + +interface SettingsModalProps { + isOpen: boolean; + onClose: () => void; + onSave: (apiKey: string) => void; + currentApiKey: string; +} + +export const SettingsModal: React.FC = ({ isOpen, onClose, onSave, currentApiKey }) => { + const [key, setKey] = useState(currentApiKey); + + // Sync internal state when prop changes or modal opens + useEffect(() => { + setKey(currentApiKey); + }, [currentApiKey, isOpen]); + + if (!isOpen) return null; + + return ( +
+
+
+

设置

+ +
+ +
+
+ + setKey(e.target.value)} + placeholder="AIzaSy..." + className="w-full px-3 py-2 border border-slate-300 rounded-md focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-indigo-500 text-sm font-mono" + /> +

+ 您的 Key 仅存储在本地浏览器中,用于直接调用 Google Gemini API。 +
+ 如果留空,将尝试使用系统默认配置。 +

+
+
+ +
+ + +
+
+
+ ); +}; \ No newline at end of file diff --git a/releases/WORK-SH-XPC-SQL-0.1.1_20251209.zip b/releases/WORK-SH-XPC-SQL-0.1.1_20251209.zip new file mode 100644 index 0000000000000000000000000000000000000000..5f782d9077aeef30e14aa9c26fab03bb3f4d9a83 GIT binary patch literal 16300 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 literal 0 HcmV?d00001 diff --git a/services/gemini.ts b/services/gemini.ts index 38d0d80..e6cfd4c 100644 --- a/services/gemini.ts +++ b/services/gemini.ts @@ -33,7 +33,14 @@ ${data.requirement} export const generateSql = async (requestData: SqlGenerationRequest): Promise => { try { - const ai = new GoogleGenAI({ apiKey: process.env.API_KEY }); + // Prioritize the user-provided key, fallback to the environment variable + const apiKey = requestData.apiKey || process.env.API_KEY; + + if (!apiKey) { + throw new Error("未配置 API Key。请点击右上角设置按钮输入您的 Google Gemini API Key,或联系管理员配置环境变量。"); + } + + const ai = new GoogleGenAI({ apiKey: apiKey }); // Using gemini-2.5-flash for speed and good reasoning capabilities on coding tasks const response = await ai.models.generateContent({ @@ -51,8 +58,9 @@ export const generateSql = async (requestData: SqlGenerationRequest): Promise