import React, { useState, useEffect } from 'react'; import { Modal, Form, Input, InputNumber, Button, message, Checkbox, Divider, Select, Alert, Card, Row, Col, Typography, Collapse } from 'antd'; import { DatabaseOutlined, ConsoleSqlOutlined, FileTextOutlined, CloudServerOutlined, AppstoreAddOutlined } from '@ant-design/icons'; import { useStore } from '../store'; import { DBConnect, DBGetDatabases, TestConnection } from '../../wailsjs/go/app/App'; import { SavedConnection } from '../types'; const { Meta } = Card; const { Text } = Typography; const ConnectionModal: React.FC<{ open: boolean; onClose: () => void; initialValues?: SavedConnection | null }> = ({ open, onClose, initialValues }) => { const [form] = Form.useForm(); const [loading, setLoading] = useState(false); const [useSSH, setUseSSH] = useState(false); const [dbType, setDbType] = useState('mysql'); const [step, setStep] = useState(1); // 1: Select Type, 2: Configure const [testResult, setTestResult] = useState<{ type: 'success' | 'error', message: string } | null>(null); const [dbList, setDbList] = useState([]); const addConnection = useStore((state) => state.addConnection); const updateConnection = useStore((state) => state.updateConnection); useEffect(() => { if (open) { setTestResult(null); // Reset test result setDbList([]); if (initialValues) { // Edit mode: Go directly to step 2 setStep(2); form.setFieldsValue({ type: initialValues.config.type, name: initialValues.name, host: initialValues.config.host, port: initialValues.config.port, user: initialValues.config.user, password: initialValues.config.password, database: initialValues.config.database, includeDatabases: initialValues.includeDatabases, useSSH: initialValues.config.useSSH, sshHost: initialValues.config.ssh?.host, sshPort: initialValues.config.ssh?.port, sshUser: initialValues.config.ssh?.user, sshPassword: initialValues.config.ssh?.password, sshKeyPath: initialValues.config.ssh?.keyPath, driver: (initialValues.config as any).driver, dsn: (initialValues.config as any).dsn, timeout: (initialValues.config as any).timeout || 30 }); setUseSSH(initialValues.config.useSSH || false); setDbType(initialValues.config.type); } else { // Create mode: Start at step 1 setStep(1); form.resetFields(); setUseSSH(false); setDbType('mysql'); } } }, [open, initialValues]); const handleOk = async () => { try { const values = await form.validateFields(); setLoading(true); const config = await buildConfig(values); const res = await DBConnect(config as any); setLoading(false); if (res.success) { const newConn = { id: initialValues ? initialValues.id : Date.now().toString(), name: values.name || (values.type === 'sqlite' ? 'SQLite DB' : values.host), config: config, includeDatabases: values.includeDatabases }; if (initialValues) { updateConnection(newConn); message.success('连接已更新!'); } else { addConnection(newConn); message.success('连接已保存!'); } form.resetFields(); setUseSSH(false); setDbType('mysql'); setStep(1); onClose(); } else { message.error('连接失败: ' + res.message); } } catch (e) { setLoading(false); } }; const handleTest = async () => { try { const values = await form.validateFields(); setLoading(true); setTestResult(null); const config = await buildConfig(values); const res = await TestConnection(config as any); setLoading(false); if (res.success) { setTestResult({ type: 'success', message: res.message }); const dbRes = await DBGetDatabases(config as any); if (dbRes.success) { const dbs = (dbRes.data as any[]).map((row: any) => row.Database || row.database); setDbList(dbs); } } else { setTestResult({ type: 'error', message: "测试失败: " + res.message }); } } catch (e) { setLoading(false); } }; const buildConfig = async (values: any) => { const sshConfig = values.useSSH ? { host: values.sshHost, port: Number(values.sshPort), user: values.sshUser, password: values.sshPassword || "", keyPath: values.sshKeyPath || "" } : { host: "", port: 22, user: "", password: "", keyPath: "" }; return { type: values.type, host: values.host || "", port: Number(values.port || 0), user: values.user || "", password: values.password || "", database: values.database || "", useSSH: !!values.useSSH, ssh: sshConfig, driver: values.driver, dsn: values.dsn, timeout: Number(values.timeout || 30) }; }; const handleTypeSelect = (type: string) => { setDbType(type); form.setFieldsValue({ type: type }); // Auto-fill default port let defaultPort = 3306; switch (type) { case 'mysql': defaultPort = 3306; break; case 'postgres': defaultPort = 5432; break; case 'oracle': defaultPort = 1521; break; case 'dameng': defaultPort = 5236; break; case 'kingbase': defaultPort = 54321; break; default: defaultPort = 3306; } if (type !== 'sqlite' && type !== 'custom') { form.setFieldsValue({ port: defaultPort }); } setStep(2); }; const isSqlite = dbType === 'sqlite'; const isCustom = dbType === 'custom'; const dbTypes = [ { key: 'mysql', name: 'MySQL', icon: }, { key: 'postgres', name: 'PostgreSQL', icon: }, { key: 'sqlite', name: 'SQLite', icon: }, { key: 'oracle', name: 'Oracle', icon: }, { key: 'dameng', name: 'Dameng (达梦)', icon: }, { key: 'kingbase', name: 'Kingbase (人大金仓)', icon: }, { key: 'custom', name: 'Custom (自定义)', icon: }, ]; const renderStep1 = () => ( {dbTypes.map(item => ( handleTypeSelect(item.key)} style={{ textAlign: 'center', cursor: 'pointer' }} >
{item.icon}
{item.name}
))}
); const renderStep2 = () => (
{ if (testResult) setTestResult(null); // Clear result on change if (changed.useSSH !== undefined) setUseSSH(changed.useSSH); // Type change handled by step 1, but keep sync if select changes (hidden now) if (changed.type !== undefined) setDbType(changed.type); }} > {/* Hidden Type Field to keep form value synced */} {isCustom ? ( <> ) : ( <>
{!isSqlite && ( )}
{!isSqlite && (
)} {!isSqlite && ( )} {!isSqlite && ( <> 使用 SSH 隧道 (SSH Tunnel) {useSSH && (
)} ) }]} /> )} )} {testResult && ( )} ); const getFooter = () => { if (step === 1) { return [ ]; } return [ !initialValues && , , , ]; }; const getTitle = () => { if (step === 1) return "选择数据源类型"; const typeName = dbTypes.find(t => t.key === dbType)?.name || dbType; return initialValues ? "编辑连接" : `新建 ${typeName} 连接`; }; return ( {step === 1 ? renderStep1() : renderStep2()} ); }; export default ConnectionModal;