diff --git a/.codex/config.toml b/.codex/config.toml
new file mode 100644
index 000000000..4a39b9307
--- /dev/null
+++ b/.codex/config.toml
@@ -0,0 +1,6 @@
+sandbox_mode = "danger-full-access"
+[mcp_servers.context7]
+url = "https://mcp.context7.com/mcp"
+
+[sandbox_workspace_write]
+network_access = true
diff --git a/.codex/skills/chat2db-dev-server/SKILL.md b/.codex/skills/chat2db-dev-server/SKILL.md
new file mode 100644
index 000000000..ee0f98b0b
--- /dev/null
+++ b/.codex/skills/chat2db-dev-server/SKILL.md
@@ -0,0 +1,63 @@
+---
+name: chat2db-dev-server
+description: Start, restart, stop, and inspect the local Chat2DB development servers in E:\workspace\Chat2DB. Use when the user asks to 启动/重启/打开/检查 Chat2DB 前端后端, restart backend after Java changes, restart frontend after React changes, verify ports/logs, or fix dev-server startup issues for this workspace.
+---
+
+# Chat2DB Dev Server
+
+## Quick Start
+
+Use `scripts/manage-chat2db-dev.ps1` for repeatable server operations.
+
+```powershell
+# Start both frontend and backend.
+& "E:\workspace\Chat2DB\.codex\skills\chat2db-dev-server\scripts\manage-chat2db-dev.ps1" -Action start
+
+# Restart both. Install backend dependency modules first when Java backend modules changed.
+& "E:\workspace\Chat2DB\.codex\skills\chat2db-dev-server\scripts\manage-chat2db-dev.ps1" -Action restart -InstallBackendDeps
+
+# Check listening ports and log tails.
+& "E:\workspace\Chat2DB\.codex\skills\chat2db-dev-server\scripts\manage-chat2db-dev.ps1" -Action status
+```
+
+This is a project-local skill. Keep it under `E:\workspace\Chat2DB\.codex\skills`, not under the global user skills directory.
+
+## Workspace Defaults
+
+- Workspace: `E:\workspace\Chat2DB`
+- Frontend directory: `chat2db-client`
+- Backend directory: `chat2db-server`
+- Frontend URL: `http://localhost:8000`
+- Backend URL: `http://localhost:10821`
+- Logs: `E:\workspace\Chat2DB\logs\frontend.log` and `E:\workspace\Chat2DB\logs\backend.log`
+- Java: set `$env:JAVA_HOME = "D:\tool\Java\jdk-17"` before Maven commands.
+- Frontend package manager: use `D:\nvm4w\nodejs\yarn.cmd`; do not use npm.
+
+## Backend Restart Rule
+
+When backend code changed outside `chat2db-server-start`, run the script with `-InstallBackendDeps` before restart. This installs the modules that `chat2db-server-start` consumes so `spring-boot:run -pl chat2db-server-start` does not load stale local Maven artifacts.
+
+This is required after changes in:
+
+- `chat2db-server-domain-api`
+- `chat2db-server-domain-core`
+- `chat2db-server-domain-repository`
+- `chat2db-server-web-api`
+
+## Verification
+
+After starting or restarting:
+
+1. Confirm ports `8000` and `10821` are listening.
+2. Check backend log for `Started Chat2dbLiteApplication` and `[Startup] Chat2dbLiteApplication started successfully`.
+3. Check frontend log for `App listening at` and `Local: http://localhost:8000`.
+4. If an endpoint was added, verify it without mutating data when possible, for example:
+
+```powershell
+Invoke-WebRequest -Uri 'http://localhost:10821/api/task/cleanup' -Method Options -UseBasicParsing
+```
+
+## Notes
+
+- Early frontend proxy `ECONNREFUSED` entries can appear while the backend is still starting; treat them as transient if backend later starts and `/api` calls succeed.
+- If a requested endpoint returns `NoHandlerFoundException`, install backend dependencies with `-InstallBackendDeps` and restart backend again.
diff --git a/.codex/skills/chat2db-dev-server/agents/openai.yaml b/.codex/skills/chat2db-dev-server/agents/openai.yaml
new file mode 100644
index 000000000..6511529e7
--- /dev/null
+++ b/.codex/skills/chat2db-dev-server/agents/openai.yaml
@@ -0,0 +1,4 @@
+interface:
+ display_name: "Chat2DB Dev Server"
+ short_description: "Manage Chat2DB dev servers."
+ default_prompt: "Start or restart the Chat2DB frontend and backend, verify ports and logs, and report the URLs."
diff --git a/.codex/skills/chat2db-dev-server/scripts/manage-chat2db-dev.ps1 b/.codex/skills/chat2db-dev-server/scripts/manage-chat2db-dev.ps1
new file mode 100644
index 000000000..4eca06f7c
--- /dev/null
+++ b/.codex/skills/chat2db-dev-server/scripts/manage-chat2db-dev.ps1
@@ -0,0 +1,101 @@
+param(
+ [ValidateSet("start", "restart", "stop", "status")]
+ [string]$Action = "status",
+
+ [ValidateSet("all", "frontend", "backend")]
+ [string]$Component = "all",
+
+ [string]$Workspace = "E:\workspace\Chat2DB",
+
+ [string]$JavaHome = "D:\tool\Java\jdk-17",
+
+ [string]$YarnCmd = "D:\nvm4w\nodejs\yarn.cmd",
+
+ [switch]$InstallBackendDeps
+)
+
+$ErrorActionPreference = "Stop"
+
+$logDir = Join-Path $Workspace "logs"
+$frontendLog = Join-Path $logDir "frontend.log"
+$backendLog = Join-Path $logDir "backend.log"
+$frontendPort = 8000
+$backendPort = 10821
+
+function Ensure-LogDir {
+ New-Item -ItemType Directory -Force -Path $logDir | Out-Null
+}
+
+function Stop-Port {
+ param([int]$Port)
+ Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue |
+ ForEach-Object {
+ Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue
+ }
+}
+
+function Install-BackendDependencies {
+ $env:JAVA_HOME = $JavaHome
+ Push-Location (Join-Path $Workspace "chat2db-server")
+ try {
+ mvn -pl chat2db-server-web/chat2db-server-web-api,chat2db-server-domain/chat2db-server-domain-core -am -DskipTests install
+ } finally {
+ Pop-Location
+ }
+}
+
+function Start-Backend {
+ Ensure-LogDir
+ $command = "Set-Location '$Workspace\chat2db-server'; " +
+ "`$env:JAVA_HOME='$JavaHome'; " +
+ "mvn spring-boot:run -pl chat2db-server-start *> '$backendLog'"
+ Start-Process powershell.exe -WindowStyle Hidden -PassThru -ArgumentList @("-NoProfile", "-Command", $command)
+}
+
+function Start-Frontend {
+ Ensure-LogDir
+ $command = "Set-Location '$Workspace\chat2db-client'; " +
+ "& '$YarnCmd' run start:web *> '$frontendLog'"
+ Start-Process powershell.exe -WindowStyle Hidden -PassThru -ArgumentList @("-NoProfile", "-Command", $command)
+}
+
+function Show-Status {
+ $ports = @($frontendPort, $backendPort)
+ Get-NetTCPConnection -LocalPort $ports -State Listen -ErrorAction SilentlyContinue |
+ Select-Object LocalAddress, LocalPort, State, OwningProcess
+
+ foreach ($log in @($backendLog, $frontendLog)) {
+ if (Test-Path $log) {
+ Write-Host ""
+ Write-Host "== $log =="
+ Get-Content $log -Tail 20
+ }
+ }
+}
+
+if ($Action -in @("restart", "stop")) {
+ if ($Component -in @("all", "frontend")) {
+ Stop-Port -Port $frontendPort
+ }
+ if ($Component -in @("all", "backend")) {
+ Stop-Port -Port $backendPort
+ }
+ Start-Sleep -Seconds 2
+}
+
+if ($Action -in @("start", "restart")) {
+ if (($Component -in @("all", "backend")) -and $InstallBackendDeps) {
+ Install-BackendDependencies
+ }
+ if ($Component -in @("all", "backend")) {
+ $backend = Start-Backend
+ Write-Host "backendPid=$($backend.Id)"
+ }
+ if ($Component -in @("all", "frontend")) {
+ $frontend = Start-Frontend
+ Write-Host "frontendPid=$($frontend.Id)"
+ }
+ Start-Sleep -Seconds 12
+}
+
+Show-Status
diff --git a/.gitignore b/.gitignore
index f8a263aaa..e415c2575 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,4 +28,8 @@ package-lock.json
/chat2db-server/ali-dbhub-server-domain/ali-dbhub-server-domain-support/src/main/resources/lib/*
/chat2db-server/ali-dbhub-server-domain/ali-dbhub-server-domain-support/lib/*
/lib
-/out/*
\ No newline at end of file
+/out/*
+/chat2db-gateway/target
+.playwright-mcp
+*.png
+logs
diff --git a/.opencode/opencode.json b/.opencode/opencode.json
new file mode 100644
index 000000000..17ec01caa
--- /dev/null
+++ b/.opencode/opencode.json
@@ -0,0 +1,11 @@
+{
+ "$schema": "https://opencode.ai/config.json",
+ "lsp":true,
+ "mcp": {
+ "browsermcp": {
+ "type": "local",
+ "command": ["npx","@browsermcp/mcp@latest"],
+ "enabled": true
+ }
+ }
+}
diff --git a/.opencode/skills/build/SKILL.md b/.opencode/skills/build/SKILL.md
new file mode 100644
index 000000000..f6e8a571f
--- /dev/null
+++ b/.opencode/skills/build/SKILL.md
@@ -0,0 +1,51 @@
+---
+name: build
+description: 编译当前项目
+license: MIT
+compatibility: opencode
+---
+
+## Build Backend JAR
+
+编译后端 JAR 包:
+
+```powershell
+$env:JAVA_HOME="D:\tool\Java\jdk-17"; cd chat2db-server; mvn clean package -DskipTests
+```
+
+生成的 JAR 文件:
+- `chat2db-server/chat2db-server-start/target/chat2db-server-start.jar`
+
+运行方式:
+```powershell
+java -jar chat2db-server/chat2db-server-start/target/chat2db-server-start.jar
+```
+
+## Build Backend (Compile Only)
+
+仅编译不打包:
+
+```powershell
+$env:JAVA_HOME="D:\tool\Java\jdk-17"; cd chat2db-server; mvn clean compile -DskipTests
+```
+
+## Build Frontend
+
+编译前端:
+
+```powershell
+cd chat2db-client; yarn install; yarn run build:web
+```
+
+开发模式运行前端:
+```powershell
+cd chat2db-client; yarn install; yarn run start:web
+```
+
+```powershell
+# Requirements
+# - Java 17 (设置 JAVA_HOME 环境变量)
+# - Node.js 16+
+# - Maven 3.6.1+
+# - Yarn 4.x
+```
\ No newline at end of file
diff --git a/.opencode/skills/chat2db-dev-server/SKILL.md b/.opencode/skills/chat2db-dev-server/SKILL.md
new file mode 100644
index 000000000..ee0f98b0b
--- /dev/null
+++ b/.opencode/skills/chat2db-dev-server/SKILL.md
@@ -0,0 +1,63 @@
+---
+name: chat2db-dev-server
+description: Start, restart, stop, and inspect the local Chat2DB development servers in E:\workspace\Chat2DB. Use when the user asks to 启动/重启/打开/检查 Chat2DB 前端后端, restart backend after Java changes, restart frontend after React changes, verify ports/logs, or fix dev-server startup issues for this workspace.
+---
+
+# Chat2DB Dev Server
+
+## Quick Start
+
+Use `scripts/manage-chat2db-dev.ps1` for repeatable server operations.
+
+```powershell
+# Start both frontend and backend.
+& "E:\workspace\Chat2DB\.codex\skills\chat2db-dev-server\scripts\manage-chat2db-dev.ps1" -Action start
+
+# Restart both. Install backend dependency modules first when Java backend modules changed.
+& "E:\workspace\Chat2DB\.codex\skills\chat2db-dev-server\scripts\manage-chat2db-dev.ps1" -Action restart -InstallBackendDeps
+
+# Check listening ports and log tails.
+& "E:\workspace\Chat2DB\.codex\skills\chat2db-dev-server\scripts\manage-chat2db-dev.ps1" -Action status
+```
+
+This is a project-local skill. Keep it under `E:\workspace\Chat2DB\.codex\skills`, not under the global user skills directory.
+
+## Workspace Defaults
+
+- Workspace: `E:\workspace\Chat2DB`
+- Frontend directory: `chat2db-client`
+- Backend directory: `chat2db-server`
+- Frontend URL: `http://localhost:8000`
+- Backend URL: `http://localhost:10821`
+- Logs: `E:\workspace\Chat2DB\logs\frontend.log` and `E:\workspace\Chat2DB\logs\backend.log`
+- Java: set `$env:JAVA_HOME = "D:\tool\Java\jdk-17"` before Maven commands.
+- Frontend package manager: use `D:\nvm4w\nodejs\yarn.cmd`; do not use npm.
+
+## Backend Restart Rule
+
+When backend code changed outside `chat2db-server-start`, run the script with `-InstallBackendDeps` before restart. This installs the modules that `chat2db-server-start` consumes so `spring-boot:run -pl chat2db-server-start` does not load stale local Maven artifacts.
+
+This is required after changes in:
+
+- `chat2db-server-domain-api`
+- `chat2db-server-domain-core`
+- `chat2db-server-domain-repository`
+- `chat2db-server-web-api`
+
+## Verification
+
+After starting or restarting:
+
+1. Confirm ports `8000` and `10821` are listening.
+2. Check backend log for `Started Chat2dbLiteApplication` and `[Startup] Chat2dbLiteApplication started successfully`.
+3. Check frontend log for `App listening at` and `Local: http://localhost:8000`.
+4. If an endpoint was added, verify it without mutating data when possible, for example:
+
+```powershell
+Invoke-WebRequest -Uri 'http://localhost:10821/api/task/cleanup' -Method Options -UseBasicParsing
+```
+
+## Notes
+
+- Early frontend proxy `ECONNREFUSED` entries can appear while the backend is still starting; treat them as transient if backend later starts and `/api` calls succeed.
+- If a requested endpoint returns `NoHandlerFoundException`, install backend dependencies with `-InstallBackendDeps` and restart backend again.
diff --git a/.opencode/skills/chat2db-dev-server/agents/openai.yaml b/.opencode/skills/chat2db-dev-server/agents/openai.yaml
new file mode 100644
index 000000000..6511529e7
--- /dev/null
+++ b/.opencode/skills/chat2db-dev-server/agents/openai.yaml
@@ -0,0 +1,4 @@
+interface:
+ display_name: "Chat2DB Dev Server"
+ short_description: "Manage Chat2DB dev servers."
+ default_prompt: "Start or restart the Chat2DB frontend and backend, verify ports and logs, and report the URLs."
diff --git a/.opencode/skills/chat2db-dev-server/scripts/manage-chat2db-dev.ps1 b/.opencode/skills/chat2db-dev-server/scripts/manage-chat2db-dev.ps1
new file mode 100644
index 000000000..4eca06f7c
--- /dev/null
+++ b/.opencode/skills/chat2db-dev-server/scripts/manage-chat2db-dev.ps1
@@ -0,0 +1,101 @@
+param(
+ [ValidateSet("start", "restart", "stop", "status")]
+ [string]$Action = "status",
+
+ [ValidateSet("all", "frontend", "backend")]
+ [string]$Component = "all",
+
+ [string]$Workspace = "E:\workspace\Chat2DB",
+
+ [string]$JavaHome = "D:\tool\Java\jdk-17",
+
+ [string]$YarnCmd = "D:\nvm4w\nodejs\yarn.cmd",
+
+ [switch]$InstallBackendDeps
+)
+
+$ErrorActionPreference = "Stop"
+
+$logDir = Join-Path $Workspace "logs"
+$frontendLog = Join-Path $logDir "frontend.log"
+$backendLog = Join-Path $logDir "backend.log"
+$frontendPort = 8000
+$backendPort = 10821
+
+function Ensure-LogDir {
+ New-Item -ItemType Directory -Force -Path $logDir | Out-Null
+}
+
+function Stop-Port {
+ param([int]$Port)
+ Get-NetTCPConnection -LocalPort $Port -State Listen -ErrorAction SilentlyContinue |
+ ForEach-Object {
+ Stop-Process -Id $_.OwningProcess -Force -ErrorAction SilentlyContinue
+ }
+}
+
+function Install-BackendDependencies {
+ $env:JAVA_HOME = $JavaHome
+ Push-Location (Join-Path $Workspace "chat2db-server")
+ try {
+ mvn -pl chat2db-server-web/chat2db-server-web-api,chat2db-server-domain/chat2db-server-domain-core -am -DskipTests install
+ } finally {
+ Pop-Location
+ }
+}
+
+function Start-Backend {
+ Ensure-LogDir
+ $command = "Set-Location '$Workspace\chat2db-server'; " +
+ "`$env:JAVA_HOME='$JavaHome'; " +
+ "mvn spring-boot:run -pl chat2db-server-start *> '$backendLog'"
+ Start-Process powershell.exe -WindowStyle Hidden -PassThru -ArgumentList @("-NoProfile", "-Command", $command)
+}
+
+function Start-Frontend {
+ Ensure-LogDir
+ $command = "Set-Location '$Workspace\chat2db-client'; " +
+ "& '$YarnCmd' run start:web *> '$frontendLog'"
+ Start-Process powershell.exe -WindowStyle Hidden -PassThru -ArgumentList @("-NoProfile", "-Command", $command)
+}
+
+function Show-Status {
+ $ports = @($frontendPort, $backendPort)
+ Get-NetTCPConnection -LocalPort $ports -State Listen -ErrorAction SilentlyContinue |
+ Select-Object LocalAddress, LocalPort, State, OwningProcess
+
+ foreach ($log in @($backendLog, $frontendLog)) {
+ if (Test-Path $log) {
+ Write-Host ""
+ Write-Host "== $log =="
+ Get-Content $log -Tail 20
+ }
+ }
+}
+
+if ($Action -in @("restart", "stop")) {
+ if ($Component -in @("all", "frontend")) {
+ Stop-Port -Port $frontendPort
+ }
+ if ($Component -in @("all", "backend")) {
+ Stop-Port -Port $backendPort
+ }
+ Start-Sleep -Seconds 2
+}
+
+if ($Action -in @("start", "restart")) {
+ if (($Component -in @("all", "backend")) -and $InstallBackendDeps) {
+ Install-BackendDependencies
+ }
+ if ($Component -in @("all", "backend")) {
+ $backend = Start-Backend
+ Write-Host "backendPid=$($backend.Id)"
+ }
+ if ($Component -in @("all", "frontend")) {
+ $frontend = Start-Frontend
+ Write-Host "frontendPid=$($frontend.Id)"
+ }
+ Start-Sleep -Seconds 12
+}
+
+Show-Status
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 000000000..6fd00e24d
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,30 @@
+{
+ // 使用 IntelliSense 了解相关属性。
+ // 悬停以查看现有属性的描述。
+ // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+
+
+ {
+ "type": "java",
+ "name": "Chat2dbLiteApplication",
+ "request": "launch",
+ "mainClass": "ai.chat2db.server.start.Chat2dbLiteApplication",
+ "projectName": "chat2db-server-start"
+ },
+ {
+ "type": "java",
+ "name": "Current File",
+ "request": "launch",
+ "mainClass": "${file}"
+ },
+ {
+ "type": "java",
+ "name": "Application",
+ "request": "launch",
+ "mainClass": "ai.chat2db.server.start.Application",
+ "projectName": "chat2db-server-start"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
index b2967e218..19e6916d8 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -87,5 +87,9 @@
"yizhoumo",
"zustand"
],
- "java.compile.nullAnalysis.mode": "automatic"
+ "java.compile.nullAnalysis.mode": "automatic",
+ "java.debug.settings.onBuildFailureProceed": true,
+ "java.configuration.updateBuildConfiguration": "interactive",
+ "npm.packageManager": "yarn",
+ "js/ts.tsdk.path": "chat2db-client/node_modules/typescript/lib"
}
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 000000000..4a9a960c3
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,26 @@
+{
+ "version": "2.0.0",
+ "tasks": [
+ {
+ "label": "Frontend Dev (Node 24)",
+ "type": "shell",
+ "command": "powershell",
+ "args": [
+ "-Command",
+ "yarn start:web:hot"
+ ],
+ "group": {
+ "kind": "build",
+ "isDefault": true
+ },
+ "presentation": {
+ "reveal": "always",
+ "panel": "new"
+ },
+ "options": {
+ "cwd": "${workspaceFolder}/chat2db-client"
+ },
+ "problemMatcher": []
+ }
+ ]
+}
diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 000000000..ccc0ec582
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1,267 @@
+# AGENTS.md - Chat2DB Development Guide
+
+This document provides essential information for AI coding agents working on the Chat2DB codebase.
+
+## Project Overview
+
+Chat2DB is a multi-database client tool with AI capabilities. It consists of:
+- **chat2db-client**: Frontend React/Electron application (TypeScript)
+- **chat2db-server**: Backend Spring Boot application (Java 17)
+
+## Build/Lint/Test Commands
+
+### Frontend (chat2db-client)
+
+```powershell
+# Install dependencies (must use yarn)
+cd chat2db-client
+yarn install
+
+# Development server
+yarn run start:web # Web version
+yarn run start # Desktop version (Electron + Web)
+
+# Build
+yarn run build:web # Build for web
+yarn run build:desktop # Build for desktop
+yarn run build:prod # Production build
+
+# Linting
+yarn run lint # Run ESLint
+
+# Package manager: yarn 4.9.1 (REQUIRED - do not use npm)
+```
+
+### Backend (chat2db-server)
+
+```powershell
+# Build (from chat2db-server directory)
+cd chat2db-server
+mvn clean install -DskipTests # Build without tests
+mvn clean install # Build with tests
+
+# Run a single test class
+mvn test -Dtest=TableOperationsTest -pl chat2db-server-test
+
+# Run a single test method
+mvn test -Dtest=TableOperationsTest#table -pl chat2db-server-test
+
+# Run application
+mvn spring-boot:run -pl chat2db-server-start
+
+# Run packaged JAR
+java -jar chat2db-server-start/target/chat2db-server-start.jar
+```
+
+## Requirements
+
+- **Java**: 17 or higher
+ - **Environment Setup**: `$env:JAVA_HOME = "D:\tool\Java\jdk-17"` before compiling Java code
+- **Node.js**: 16 or higher
+- **Maven**: 3.6.1 or higher
+- **Yarn**: 4.x (required for frontend)
+
+## Code Style Guidelines
+
+### TypeScript/React (Frontend)
+
+#### Imports
+```typescript
+// React imports first
+import React, { memo, useCallback, useEffect, useRef } from 'react';
+
+// Third-party libraries
+import classnames from 'classnames';
+import { message } from 'antd';
+
+// Internal imports (use @ alias for src)
+import { useWorkspaceStore } from '@/pages/main/workspace/store';
+import DraggableContainer from '@/components/DraggableContainer';
+
+// Styles last
+import styles from './index.less';
+```
+
+#### Formatting (Prettier)
+- Print width: 120 characters
+- Single quotes
+- Trailing commas: 'all'
+- Use `prettier-plugin-organize-imports` for import ordering
+
+#### Naming Conventions
+- Components: PascalCase (`WorkspaceLeft`, `TableController`)
+- Files: camelCase for utilities, PascalCase for components
+- Hooks: camelCase with `use` prefix (`useWorkspaceStore`)
+- Stores: camelCase with `Store` suffix (`IWorkspaceStore`)
+- Interfaces: PascalCase with `I` prefix (`IConfigStore`, `IStore`)
+- Types: PascalCase with `Type` suffix or descriptive names
+
+#### React Patterns
+- Use `memo()` for component exports: `const Component = memo(() => { ... })`
+- Use functional components with hooks
+- Use Zustand for state management
+- Use `@/` alias for src imports
+
+#### ESLint Rules
+- Max line length: 120 characters
+- Prefer arrow functions
+- Prefer const
+- React hooks rules enforced
+- TypeScript strict mode (noImplicitAny: false in tsconfig)
+
+### Java (Backend)
+
+#### Package Structure
+```
+ai.chat2db.server.{module}
+├── controller/ # REST controllers
+├── service/ # Business logic interfaces and implementations
+├── param/ # Request parameters
+├── converter/ # Object converters/mappers
+├── vo/ # View Objects (response DTOs)
+└── model/ # Domain models
+```
+
+#### Naming Conventions
+- Classes: PascalCase (`TableServiceImpl`, `TableController`)
+- Methods: camelCase (`queryColumns`, `buildSql`)
+- Constants: UPPER_SNAKE_CASE (`TABLE_NAME`)
+- Parameters: camelCase with `Param` suffix (`TableQueryParam`)
+- Services: Interface without suffix, impl with `Impl` suffix
+- Converters: `*Converter` suffix
+- VOs: `*VO` suffix
+
+#### Code Style
+- Use Lombok annotations (`@Data`, `@Builder`, `@Slf4j`, `@AllArgsConstructor`)
+- Use Spring annotations (`@Service`, `@RestController`, `@Autowired`)
+- Use Jakarta validation (`@Valid`)
+- 4-space indentation
+- Opening braces on same line
+
+#### Service Layer Pattern
+```java
+public interface TableService {
+ DataResult
query(TableQueryParam param, TableSelector selector);
+ ActionResult drop(DropParam param);
+}
+
+@Service
+@Slf4j
+public class TableServiceImpl implements TableService {
+ @Autowired
+ private PinService pinService;
+
+ @Override
+ public DataResult query(TableQueryParam param, TableSelector selector) {
+ // Implementation
+ }
+}
+```
+
+#### Controller Pattern
+```java
+@Slf4j
+@ConnectionInfoAspect
+@RequestMapping("/api/rdb/table")
+@RestController
+public class TableController {
+ @Autowired
+ private TableService tableService;
+
+ @GetMapping("/list")
+ public WebPageResult list(@Valid TableBriefQueryRequest request) {
+ // Implementation
+ }
+}
+```
+
+#### Error Handling
+- Use `ActionResult` for operations without return data
+- Use `DataResult` for single object returns
+- Use `ListResult` for list returns
+- Use `PageResult` for paginated results
+- Log errors with `@Slf4j` and `log.error()`/`log.warn()`
+
+## Project Structure
+
+```
+Chat2DB/
+├── chat2db-client/ # Frontend
+│ ├── src/
+│ │ ├── blocks/ # Reusable UI blocks
+│ │ ├── components/ # React components
+│ │ ├── pages/ # Page components
+│ │ ├── service/ # API service layer
+│ │ ├── hooks/ # Custom React hooks
+│ │ ├── store/ # Zustand stores
+│ │ ├── typings/ # TypeScript types
+│ │ ├── utils/ # Utility functions
+│ │ └── i18n/ # Internationalization
+│ ├── .eslintrc.js
+│ ├── .prettierrc
+│ └── package.json
+│
+├── chat2db-server/ # Backend
+│ ├── chat2db-server-domain/ # Domain layer
+│ │ ├── chat2db-server-domain-api/ # Service interfaces
+│ │ └── chat2db-server-domain-core/ # Service implementations
+│ ├── chat2db-server-web/ # Web/API layer
+│ │ ├── chat2db-server-web-api/ # API controllers
+│ │ └── chat2db-server-common-api/ # Common APIs
+│ ├── chat2db-server-tools/ # Utilities
+│ ├── chat2db-spi/ # Service Provider Interface
+│ ├── chat2db-plugins/ # Database plugins (MySQL, PostgreSQL, etc.)
+│ ├── chat2db-server-start/ # Main application
+│ └── chat2db-server-test/ # Test module
+│
+└── docker/ # Docker configuration
+```
+
+## Testing
+
+### Backend Tests
+- Located in `chat2db-server-test/src/test/java/`
+- Use JUnit 5 (`@Test`, `@Order`)
+- Extend `BaseTest` for integration tests
+- Use `@SpringBootTest` for integration tests
+
+### Running Tests
+```powershell
+# All tests
+mvn test
+
+# Single test class
+mvn test -Dtest=ClassName
+
+# Single test method
+mvn test -Dtest=ClassName#methodName
+```
+
+## Key Technologies
+
+### Frontend
+- React 18 with TypeScript
+- UmiJS 4 framework
+- Ant Design 5
+- Zustand for state management
+- Monaco Editor for SQL editing
+- Electron for desktop app
+
+### Backend
+- Spring Boot 3.1
+- Java 17
+- MyBatis Plus for database access
+- Sa-Token for authentication
+- Lombok for boilerplate reduction
+- MapStruct for object mapping
+- H2 as embedded database
+
+## Important Notes
+
+1. **Always use yarn** for frontend package management, not npm
+2. **Java 17** is required for the backend
+3. **Node 16+** is required for the frontend
+4. Tests in the server use `@TestMethodOrder(OrderAnnotation.class)` for execution order
+5. Database plugins follow the SPI pattern in `chat2db-spi`
+6. Use `@Valid` annotation for request validation in controllers
+7. Follow existing patterns when adding new controllers or services
\ No newline at end of file
diff --git a/INITIALIZATION_GUIDE.md b/INITIALIZATION_GUIDE.md
new file mode 100644
index 000000000..6b473e681
--- /dev/null
+++ b/INITIALIZATION_GUIDE.md
@@ -0,0 +1,131 @@
+# Chat2DB Project Initialization Guide
+
+This document outlines the steps required to initialize and build the Chat2DB project from scratch.
+
+## Prerequisites
+
+Before starting, ensure you have the following software installed:
+
+- **Java**: Version 17 or higher (Java 17.0.9 was used in this initialization)
+- **Node.js**: Version 16 or higher (Node.js 20.19.1 was used in this initialization)
+- **Yarn**: Version 4.9.1 or higher (Yarn 4.9.1 was used in this initialization)
+- **Maven**: Version 3.6.1 or higher (Maven 3.6.1 was used in this initialization)
+
+## Project Structure
+
+Chat2DB is a full-stack application with the following main components:
+
+- `chat2db-client`: The frontend React/Electron application
+- `chat2db-server`: The backend Spring Boot application
+
+## Step-by-step Initialization Process
+
+### 1. Clone the Repository
+
+```bash
+git clone
+cd Chat2DB
+```
+
+### 2. Initialize Client Application
+
+#### Navigate to the client directory:
+```bash
+cd chat2db-client
+```
+
+#### Install client dependencies:
+```bash
+yarn install
+```
+
+This will install all necessary JavaScript/TypeScript dependencies for the frontend.
+
+#### Build the client application:
+```bash
+yarn run build:web
+```
+
+This compiles the React application for production use.
+
+### 3. Initialize Server Application
+
+#### Navigate to the server directory:
+```bash
+cd ../chat2db-server
+```
+
+#### Install server dependencies and build the project:
+```bash
+mvn clean install -DskipTests
+```
+
+This command:
+- Downloads all required Maven dependencies
+- Compiles all Java components
+- Builds executable JAR files for the server
+- Skips running tests to speed up the build process
+
+### 4. Verify Build Output
+
+After successful initialization, you should find these executable JAR files:
+
+- **Main Server**: `chat2db-server\chat2db-server-start\target\chat2db-server-start.jar`
+- **Web Server**: `chat2db-server\chat2db-server-web-start\target\chat2db-server-web-start.jar`
+
+## Running the Application
+
+### For Development
+
+#### Frontend development server:
+```bash
+cd chat2db-client
+yarn run start:web
+```
+
+#### Backend development:
+```bash
+cd chat2db-server
+mvn spring-boot:run -pl chat2db-server-start
+```
+
+### For Production
+
+Run the server application:
+```bash
+java -jar chat2db-server\chat2db-server-start\target\chat2db-server-start.jar
+```
+
+The application will be accessible via the web interface or through the Electron desktop application.
+
+## Troubleshooting
+
+### Common Issues
+
+1. **Maven Build Fails with Memory Issues**
+ - Ensure you have sufficient memory allocated to Maven
+ - You may need to set `MAVEN_OPTS` environment variable with increased heap size
+
+2. **Yarn Install Fails**
+ - Clear the yarn cache: `yarn cache clean`
+ - Try installing with network timeout tolerance: `yarn install --network-timeout 100000`
+
+3. **JDK Version Issues**
+ - Ensure you're using Java 17 or higher
+ - Verify with: `java -version`
+
+## Additional Notes
+
+- The project uses a multi-module Maven structure for server components
+- Database plugins are built as separate modules (MySQL, PostgreSQL, Oracle, etc.)
+- Client-side uses Umi framework with React and Ant Design
+- The application supports various databases through plugin architecture
+- AI integration features are available using ChatGPT-like APIs
+
+## Development Workflow
+
+After initialization, developers can:
+- Modify frontend components in the `chat2db-client` directory
+- Modify backend logic in the `chat2db-server` directory
+- Test with the development servers before building for production
+- Add new database plugins following the existing plugin architecture
\ No newline at end of file
diff --git a/chat2db-client/.gitignore b/chat2db-client/.gitignore
index c5fcd3df3..6ffb61b89 100644
--- a/chat2db-client/.gitignore
+++ b/chat2db-client/.gitignore
@@ -10,8 +10,10 @@
/dist
.swc
./yarn-error.log
+/.yarn/*
/release
/static
-/versions
\ No newline at end of file
+/versions
+/.yarn
diff --git a/chat2db-client/.nvmrc b/chat2db-client/.nvmrc
new file mode 100644
index 000000000..a45fd52cc
--- /dev/null
+++ b/chat2db-client/.nvmrc
@@ -0,0 +1 @@
+24
diff --git a/chat2db-client/.umirc.ts b/chat2db-client/.umirc.ts
index 6ea951156..49e3d7092 100644
--- a/chat2db-client/.umirc.ts
+++ b/chat2db-client/.umirc.ts
@@ -48,6 +48,10 @@ export default defineConfig({
path: '/workspace',
component: 'main',
},
+ {
+ path: '/taskCenter',
+ component: 'main',
+ },
{
path: '/',
component: 'main',
@@ -61,6 +65,14 @@ export default defineConfig({
plugins: ['@umijs/plugins/dist/dva'],
chainWebpack,
proxy: {
+ '/api/ai/chat': {
+ target: 'http://127.0.0.1:10821',
+ changeOrigin: true,
+ proxyTimeout: 3600000,
+ headers: {
+ Connection: 'keep-alive',
+ },
+ },
'/api': {
target: 'http://127.0.0.1:10821',
changeOrigin: true,
@@ -100,10 +112,10 @@ export default defineConfig({
// window.addEventListener("appinstalled", () => {
// deferredPrompt = null;
// })`,
- {
- src: 'https://www.googletagmanager.com/gtag/js?id=G-V8M4E5SF61',
- async: true,
- },
+ // {
+ // src: 'https://www.googletagmanager.com/gtag/js?id=G-V8M4E5SF61',
+ // async: true,
+ // },
// `window.dataLayer = window.dataLayer || [];
// function gtag() {
// window.dataLayer.push(arguments);
diff --git a/chat2db-client/.yarnrc.yml b/chat2db-client/.yarnrc.yml
new file mode 100644
index 000000000..3186f3f07
--- /dev/null
+++ b/chat2db-client/.yarnrc.yml
@@ -0,0 +1 @@
+nodeLinker: node-modules
diff --git a/chat2db-client/package.json b/chat2db-client/package.json
index b0b8afaa6..f7fb37f64 100644
--- a/chat2db-client/package.json
+++ b/chat2db-client/package.json
@@ -1,13 +1,13 @@
{
"name": "chat2db",
- "version": "1.0.0",
+ "version": "2.1.16",
"private": true,
"repository": {
"type": "git",
"url": "https://github.com/chat2db/Chat2DB"
},
"author": "fjy, hexi",
- "main": "src/main/main.js",
+ "main": "src/main/index.js",
"scripts": {
"build": "npm run build:web && npm run build:main",
"build:desktop": "npm run build:web:desktop && npm run build:main:prod",
@@ -15,18 +15,20 @@
"build:main:prod": "cross-env NODE_ENV=production electron-builder",
"build:prod": "npm run build:web:prod && npm run build:main:prod",
"build:web": "umi build",
- "build:web:desktop": "cross-env UMI_ENV=desktop cross-env APP_VERSION=${npm_config_app_version} cross-env APP_PORT=${npm_config_app_port} umi build",
- "build:web:prod": "cross-env UMI_ENV=prod cross-env APP_VERSION=${npm_config_app_version} cross-env APP_PORT=${npm_config_app_port} cross-env UMI_PublicPath=${npm_config_public_path} umi build",
+ "build:web:desktop": "cross-env UMI_ENV=desktop umi build",
+ "build:web:prod": "cross-env UMI_ENV=prod umi build",
"postinstall": "umi setup",
"lint": "umi lint",
"start": "concurrently \"npm run start:web\" \"npm run start:main\"",
"start:main": "cross-env NODE_ENV=development electron .",
"start:main:prod": "cross-env NODE_ENV=production electron .",
- "start:web": "cross-env UMI_ENV=local HMR=none cross-env APP_VERSION=${npm_config_app_version} umi dev",
- "start:web:hot": "cross-env UMI_ENV=local cross-env APP_VERSION=${npm_config_app_version} umi dev"
+ "start:web": "cross-env UMI_ENV=local HMR=none umi dev",
+ "start:web:hot": "cross-env UMI_ENV=local umi dev"
},
"dependencies": {
+ "@dagrejs/dagre": "^3.0.0",
"@dnd-kit/modifiers": "^6.0.1",
+ "@xyflow/react": "^12.10.2",
"ahooks": "^3.7.8",
"ali-react-table": "^2.6.1",
"antd": "^5.12.1",
@@ -35,13 +37,16 @@
"echarts-for-react": "^3.0.2",
"event-source-polyfill": "^1.0.31",
"highlight.js": "^11.9.0",
+ "html-to-image": "^1.11.13",
"lodash": "^4.17.21",
"markdown-it-link-attributes": "^4.0.1",
"monaco-editor": "^0.44.0",
"monaco-editor-esm-webpack-plugin": "^2.1.0",
"monaco-editor-webpack-plugin": "^7.0.1",
+ "react-markdown": "^8.0.7",
"react-monaco-editor": "^0.54.0",
"react-sortablejs": "^6.1.4",
+ "remark-gfm": "3",
"sql-formatter": "^13.0.4",
"styled-components": "^6.0.1",
"umi": "^4.0.87",
@@ -76,6 +81,8 @@
"prettier": "^2",
"prettier-plugin-organize-imports": "^2",
"prettier-plugin-packagejson": "^2",
+ "stylelint": "^17.8.0",
+ "stylelint-config-standard": "^40.0.0",
"tailwindcss": "^3",
"typescript": "^5.0.3"
},
@@ -83,6 +90,7 @@
"react": "^16.8.0",
"react-dom": "^16.8.0"
},
+ "packageManager": "yarn@4.13.0+sha512.5c20ba010c99815433e5c8453112165e673f1c7948d8d2b267f4b5e52097538658388ebc9f9580656d9b75c5cc996f990f611f99304a2197d4c56d21eea370e7",
"engines": {
"node": ">=16"
},
diff --git a/chat2db-client/src/assets/font/demo.css b/chat2db-client/src/assets/font/demo.css
deleted file mode 100644
index a67054a0a..000000000
--- a/chat2db-client/src/assets/font/demo.css
+++ /dev/null
@@ -1,539 +0,0 @@
-/* Logo 字体 */
-@font-face {
- font-family: "iconfont logo";
- src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
- src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
- url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
- url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
- url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
-}
-
-.logo {
- font-family: "iconfont logo";
- font-size: 160px;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-/* tabs */
-.nav-tabs {
- position: relative;
-}
-
-.nav-tabs .nav-more {
- position: absolute;
- right: 0;
- bottom: 0;
- height: 42px;
- line-height: 42px;
- color: #666;
-}
-
-#tabs {
- border-bottom: 1px solid #eee;
-}
-
-#tabs li {
- cursor: pointer;
- width: 100px;
- height: 40px;
- line-height: 40px;
- text-align: center;
- font-size: 16px;
- border-bottom: 2px solid transparent;
- position: relative;
- z-index: 1;
- margin-bottom: -1px;
- color: #666;
-}
-
-
-#tabs .active {
- border-bottom-color: #f00;
- color: #222;
-}
-
-.tab-container .content {
- display: none;
-}
-
-/* 页面布局 */
-.main {
- padding: 30px 100px;
- width: 960px;
- margin: 0 auto;
-}
-
-.main .logo {
- color: #333;
- text-align: left;
- margin-bottom: 30px;
- line-height: 1;
- height: 110px;
- margin-top: -50px;
- overflow: hidden;
- *zoom: 1;
-}
-
-.main .logo a {
- font-size: 160px;
- color: #333;
-}
-
-.helps {
- margin-top: 40px;
-}
-
-.helps pre {
- padding: 20px;
- margin: 10px 0;
- border: solid 1px #e7e1cd;
- background-color: #fffdef;
- overflow: auto;
-}
-
-.icon_lists {
- width: 100% !important;
- overflow: hidden;
- *zoom: 1;
-}
-
-.icon_lists li {
- width: 100px;
- margin-bottom: 10px;
- margin-right: 20px;
- text-align: center;
- list-style: none !important;
- cursor: default;
-}
-
-.icon_lists li .code-name {
- line-height: 1.2;
-}
-
-.icon_lists .icon {
- display: block;
- height: 100px;
- line-height: 100px;
- font-size: 42px;
- margin: 10px auto;
- color: #333;
- -webkit-transition: font-size 0.25s linear, width 0.25s linear;
- -moz-transition: font-size 0.25s linear, width 0.25s linear;
- transition: font-size 0.25s linear, width 0.25s linear;
-}
-
-.icon_lists .icon:hover {
- font-size: 100px;
-}
-
-.icon_lists .svg-icon {
- /* 通过设置 font-size 来改变图标大小 */
- width: 1em;
- /* 图标和文字相邻时,垂直对齐 */
- vertical-align: -0.15em;
- /* 通过设置 color 来改变 SVG 的颜色/fill */
- fill: currentColor;
- /* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
- normalize.css 中也包含这行 */
- overflow: hidden;
-}
-
-.icon_lists li .name,
-.icon_lists li .code-name {
- color: #666;
-}
-
-/* markdown 样式 */
-.markdown {
- color: #666;
- font-size: 14px;
- line-height: 1.8;
-}
-
-.highlight {
- line-height: 1.5;
-}
-
-.markdown img {
- vertical-align: middle;
- max-width: 100%;
-}
-
-.markdown h1 {
- color: #404040;
- font-weight: 500;
- line-height: 40px;
- margin-bottom: 24px;
-}
-
-.markdown h2,
-.markdown h3,
-.markdown h4,
-.markdown h5,
-.markdown h6 {
- color: #404040;
- margin: 1.6em 0 0.6em 0;
- font-weight: 500;
- clear: both;
-}
-
-.markdown h1 {
- font-size: 28px;
-}
-
-.markdown h2 {
- font-size: 22px;
-}
-
-.markdown h3 {
- font-size: 16px;
-}
-
-.markdown h4 {
- font-size: 14px;
-}
-
-.markdown h5 {
- font-size: 12px;
-}
-
-.markdown h6 {
- font-size: 12px;
-}
-
-.markdown hr {
- height: 1px;
- border: 0;
- background: #e9e9e9;
- margin: 16px 0;
- clear: both;
-}
-
-.markdown p {
- margin: 1em 0;
-}
-
-.markdown>p,
-.markdown>blockquote,
-.markdown>.highlight,
-.markdown>ol,
-.markdown>ul {
- width: 80%;
-}
-
-.markdown ul>li {
- list-style: circle;
-}
-
-.markdown>ul li,
-.markdown blockquote ul>li {
- margin-left: 20px;
- padding-left: 4px;
-}
-
-.markdown>ul li p,
-.markdown>ol li p {
- margin: 0.6em 0;
-}
-
-.markdown ol>li {
- list-style: decimal;
-}
-
-.markdown>ol li,
-.markdown blockquote ol>li {
- margin-left: 20px;
- padding-left: 4px;
-}
-
-.markdown code {
- margin: 0 3px;
- padding: 0 5px;
- background: #eee;
- border-radius: 3px;
-}
-
-.markdown strong,
-.markdown b {
- font-weight: 600;
-}
-
-.markdown>table {
- border-collapse: collapse;
- border-spacing: 0px;
- empty-cells: show;
- border: 1px solid #e9e9e9;
- width: 95%;
- margin-bottom: 24px;
-}
-
-.markdown>table th {
- white-space: nowrap;
- color: #333;
- font-weight: 600;
-}
-
-.markdown>table th,
-.markdown>table td {
- border: 1px solid #e9e9e9;
- padding: 8px 16px;
- text-align: left;
-}
-
-.markdown>table th {
- background: #F7F7F7;
-}
-
-.markdown blockquote {
- font-size: 90%;
- color: #999;
- border-left: 4px solid #e9e9e9;
- padding-left: 0.8em;
- margin: 1em 0;
-}
-
-.markdown blockquote p {
- margin: 0;
-}
-
-.markdown .anchor {
- opacity: 0;
- transition: opacity 0.3s ease;
- margin-left: 8px;
-}
-
-.markdown .waiting {
- color: #ccc;
-}
-
-.markdown h1:hover .anchor,
-.markdown h2:hover .anchor,
-.markdown h3:hover .anchor,
-.markdown h4:hover .anchor,
-.markdown h5:hover .anchor,
-.markdown h6:hover .anchor {
- opacity: 1;
- display: inline-block;
-}
-
-.markdown>br,
-.markdown>p>br {
- clear: both;
-}
-
-
-.hljs {
- display: block;
- background: white;
- padding: 0.5em;
- color: #333333;
- overflow-x: auto;
-}
-
-.hljs-comment,
-.hljs-meta {
- color: #969896;
-}
-
-.hljs-string,
-.hljs-variable,
-.hljs-template-variable,
-.hljs-strong,
-.hljs-emphasis,
-.hljs-quote {
- color: #df5000;
-}
-
-.hljs-keyword,
-.hljs-selector-tag,
-.hljs-type {
- color: #a71d5d;
-}
-
-.hljs-literal,
-.hljs-symbol,
-.hljs-bullet,
-.hljs-attribute {
- color: #0086b3;
-}
-
-.hljs-section,
-.hljs-name {
- color: #63a35c;
-}
-
-.hljs-tag {
- color: #333333;
-}
-
-.hljs-title,
-.hljs-attr,
-.hljs-selector-id,
-.hljs-selector-class,
-.hljs-selector-attr,
-.hljs-selector-pseudo {
- color: #795da3;
-}
-
-.hljs-addition {
- color: #55a532;
- background-color: #eaffea;
-}
-
-.hljs-deletion {
- color: #bd2c00;
- background-color: #ffecec;
-}
-
-.hljs-link {
- text-decoration: underline;
-}
-
-/* 代码高亮 */
-/* PrismJS 1.15.0
-https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
-/**
- * prism.js default theme for JavaScript, CSS and HTML
- * Based on dabblet (http://dabblet.com)
- * @author Lea Verou
- */
-code[class*="language-"],
-pre[class*="language-"] {
- color: black;
- background: none;
- text-shadow: 0 1px white;
- font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
- text-align: left;
- white-space: pre;
- word-spacing: normal;
- word-break: normal;
- word-wrap: normal;
- line-height: 1.5;
-
- -moz-tab-size: 4;
- -o-tab-size: 4;
- tab-size: 4;
-
- -webkit-hyphens: none;
- -moz-hyphens: none;
- -ms-hyphens: none;
- hyphens: none;
-}
-
-pre[class*="language-"]::-moz-selection,
-pre[class*="language-"] ::-moz-selection,
-code[class*="language-"]::-moz-selection,
-code[class*="language-"] ::-moz-selection {
- text-shadow: none;
- background: #b3d4fc;
-}
-
-pre[class*="language-"]::selection,
-pre[class*="language-"] ::selection,
-code[class*="language-"]::selection,
-code[class*="language-"] ::selection {
- text-shadow: none;
- background: #b3d4fc;
-}
-
-@media print {
-
- code[class*="language-"],
- pre[class*="language-"] {
- text-shadow: none;
- }
-}
-
-/* Code blocks */
-pre[class*="language-"] {
- padding: 1em;
- margin: .5em 0;
- overflow: auto;
-}
-
-:not(pre)>code[class*="language-"],
-pre[class*="language-"] {
- background: #f5f2f0;
-}
-
-/* Inline code */
-:not(pre)>code[class*="language-"] {
- padding: .1em;
- border-radius: .3em;
- white-space: normal;
-}
-
-.token.comment,
-.token.prolog,
-.token.doctype,
-.token.cdata {
- color: slategray;
-}
-
-.token.punctuation {
- color: #999;
-}
-
-.namespace {
- opacity: .7;
-}
-
-.token.property,
-.token.tag,
-.token.boolean,
-.token.number,
-.token.constant,
-.token.symbol,
-.token.deleted {
- color: #905;
-}
-
-.token.selector,
-.token.attr-name,
-.token.string,
-.token.char,
-.token.builtin,
-.token.inserted {
- color: #690;
-}
-
-.token.operator,
-.token.entity,
-.token.url,
-.language-css .token.string,
-.style .token.string {
- color: #9a6e3a;
- background: hsla(0, 0%, 100%, .5);
-}
-
-.token.atrule,
-.token.attr-value,
-.token.keyword {
- color: #07a;
-}
-
-.token.function,
-.token.class-name {
- color: #DD4A68;
-}
-
-.token.regex,
-.token.important,
-.token.variable {
- color: #e90;
-}
-
-.token.important,
-.token.bold {
- font-weight: bold;
-}
-
-.token.italic {
- font-style: italic;
-}
-
-.token.entity {
- cursor: help;
-}
diff --git a/chat2db-client/src/assets/font/demo_index.html b/chat2db-client/src/assets/font/demo_index.html
deleted file mode 100644
index 6a0d556a8..000000000
--- a/chat2db-client/src/assets/font/demo_index.html
+++ /dev/null
@@ -1,4880 +0,0 @@
-
-
-
-
- iconfont Demo
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- - Unicode
- - Font class
- - Symbol
-
-
-
查看项目
-
-
-
-
-
-
-
Unicode 引用
-
-
-
Unicode 是字体在网页端最原始的应用方式,特点是:
-
- - 支持按字体的方式去动态调整图标大小,颜色等等。
- - 默认情况下不支持多色,直接添加多色图标会自动去色。
-
-
- 注意:新版 iconfont 支持两种方式引用多色图标:SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)
-
-
Unicode 使用步骤如下:
-
第一步:拷贝项目下面生成的 @font-face
-
@font-face {
- font-family: 'iconfont';
- src: url('iconfont.woff2?t=1704794525154') format('woff2'),
- url('iconfont.woff?t=1704794525154') format('woff'),
- url('iconfont.ttf?t=1704794525154') format('truetype');
-}
-
-
第二步:定义使用 iconfont 的样式
-
.iconfont {
- font-family: "iconfont" !important;
- font-size: 16px;
- font-style: normal;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-
第三步:挑选相应图标并获取字体编码,应用于页面
-
-<span class="iconfont">3</span>
-
-
- "iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
-
-
-
-
-
-
- -
-
-
- right_on_5
-
- .icon-right_on_5
-
-
-
- -
-
-
- right_off_5-01
-
- .icon-right_off_5-01
-
-
-
- -
-
-
- left_on_2
-
- .icon-a-left_on_huaban11
-
-
-
- -
-
-
- left_off
-
- .icon-a-left_off_huaban1
-
-
-
- -
-
-
- minimize21
-
- .icon-minimize21
-
-
-
- -
-
-
- restore
-
- .icon-restore_button2
-
-
-
- -
-
-
- resize
-
- .icon-resize_button2
-
-
-
- -
-
-
- close
-
- .icon-close_button2
-
-
-
- -
-
-
- 筛选
-
- .icon-shaixuan
-
-
-
- -
-
-
- 排序
-
- .icon-a-44tubiao-122
-
-
-
- -
-
-
- 305信息-线性圆框
-
- .icon-xinxi-xianxingyuankuang
-
-
-
- -
-
-
- 加号
-
- .icon-jiahao
-
-
-
- -
-
-
- 列表
-
- .icon-liebiao
-
-
-
- -
-
-
- 减去
-
- .icon-jianqu
-
-
-
- -
-
-
- database
-
- .icon-database
-
-
-
- -
-
-
- 筛选
-
- .icon-shaixuan1
-
-
-
- -
-
-
- 刷新
-
- .icon-shuaxin2
-
-
-
- -
-
-
- 加号_o
-
- .icon-jiahao_o
-
-
-
- -
-
-
- 数据库_jurassic
-
- .icon-jurassic_data
-
-
-
- -
-
-
- 权限
-
- .icon-quanxian
-
-
-
- -
-
-
- sharpicons_add-database
-
- .icon-sharpicons_add-database
-
-
-
- -
-
-
- 组织管理
-
- .icon-zuzhiguanli-
-
-
-
- -
-
-
- 空间
-
- .icon-moxing-miaobian
-
-
-
- -
-
-
- 下箭头-copy
-
- .icon-xiajiantou1-copy
-
-
-
- -
-
-
- 查看
-
- .icon-chakan2
-
-
-
- -
-
-
- clone
-
- .icon-clone
-
-
-
- -
-
-
- 提交
-
- .icon-tijiao
-
-
-
- -
-
-
- 查看
-
- .icon-chakan1
-
-
-
- -
-
-
- 复制
-
- .icon-fuzhi
-
-
-
- -
-
-
- icon_answer
-
- .icon-icon_answer
-
-
-
- -
-
-
- icon_question
-
- .icon-icon_question
-
-
-
- -
-
-
- 发送
-
- .icon-fasong
-
-
-
- -
-
-
- 重启
-
- .icon-zhongqi
-
-
-
- -
-
-
- 提醒
-
- .icon-tixing2
-
-
-
- -
-
-
- 提醒
-
- .icon-tixing3
-
-
-
- -
-
-
- 提醒
-
- .icon-tixing1
-
-
-
- -
-
-
- 升级
-
- .icon-shengji
-
-
-
- -
-
-
- 全局_升级
-
- .icon-quanju_shengji
-
-
-
- -
-
-
- 关于我们
-
- .icon-guanyuwomen1
-
-
-
- -
-
-
- ico版本更新
-
- .icon-icobanbengengxin
-
-
-
- -
-
-
- 对话气泡
-
- .icon-duihuaqipao
-
-
-
- -
-
-
- 角色权限
-
- .icon-jiaosequanxian
-
-
-
- -
-
-
- preview
-
- .icon-preview1
-
-
-
- -
-
-
- 导入
-
- .icon-daoru
-
-
-
- -
-
-
- 终止
-
- .icon-zhongzhi
-
-
-
- -
-
-
- 退出
-
- .icon-tuichu
-
-
-
- -
-
-
- 控桩终端
-
- .icon-kongzhuangzhongduan
-
-
-
- -
-
-
- 撤销
-
- .icon-chexiao1
-
-
-
- -
-
-
- 向上
-
- .icon-xiangshang
-
-
-
- -
-
-
- 查看
-
- .icon-chakan-copy
-
-
-
- -
-
-
- 编辑数据_编辑录入操作_jurassic
-
- .icon-jurassic_edit-data
-
-
-
- -
-
-
- 编辑表格_编辑录入操作_jurassic
-
- .icon-jurassic_edit-table
-
-
-
- -
-
-
- 报表数据录入
-
- .icon-baobiaoshujuluru
-
-
-
- -
-
-
- 播放5
-
- .icon-bofang5
-
-
-
- -
-
-
- 清空@3x
-
- .icon-a-qingkong3x
-
-
-
- -
-
-
- 删除
-
- .icon-shanchu
-
-
-
- -
-
-
- new-document-worksheet
-
- .icon-newdocumentworksheet
-
-
-
- -
-
-
- file-excel
-
- .icon-file-excel
-
-
-
- -
-
-
- file-markdown
-
- .icon-file-markdown
-
-
-
- -
-
-
- file-word
-
- .icon-file-word
-
-
-
- -
-
-
- HTML5
-
- .icon-HTML
-
-
-
- -
-
-
- HTML
-
- .icon-HTML1
-
-
-
- -
-
-
- pdf
-
- .icon-pdf
-
-
-
- -
-
-
- 个人用户
-
- .icon-gerenyonghu
-
-
-
- -
-
-
- 后台管理
-
- .icon-houtaiguanli
-
-
-
- -
-
-
- 字体代码
-
- .icon-zitidaima
-
-
-
- -
-
-
- 版本
-
- .icon-banben
-
-
-
- -
-
-
- 车位管理
-
- .icon-cheweiguanli
-
-
-
- -
-
-
- dictate
-
- .icon-dianzhelidaochu
-
-
-
- -
-
-
- circle-f
-
- .icon-circle-f
-
-
-
- -
-
-
- 图表-函数
-
- .icon-tubiao-hanshu
-
-
-
- -
-
-
- 视图管理器
-
- .icon-shituguanliqi
-
-
-
- -
-
-
- 回车
-
- .icon-huiche
-
-
-
- -
-
-
- 缺省
-
- .icon-quesheng
-
-
-
- -
-
-
- 进入箭头
-
- .icon-jinrujiantou
-
-
-
- -
-
-
- 右箭头
-
- .icon-youjiantou_huaban
-
-
-
- -
-
-
- 向右箭头
-
- .icon-xiangyoujiantou1
-
-
-
- -
-
-
- 数据源
-
- .icon-shujuyuan
-
-
-
- -
-
-
- question
-
- .icon-question
-
-
-
- -
-
-
- 星星-copy
-
- .icon-xingxing
-
-
-
- -
-
-
- 控制台
-
- .icon-kongzhitai
-
-
-
- -
-
-
- 星系
-
- .icon-xingxi
-
-
-
- -
-
-
- 暂无数据 (1)
-
- .icon-a-zanwushuju1
-
-
-
- -
-
-
- 开始
-
- .icon-kaishi
-
-
-
- -
-
-
- 关闭
-
- .icon-guanbi
-
-
-
- -
-
-
- 下箭头
-
- .icon-xiajiantou
-
-
-
- -
-
-
- more
-
- .icon-gengduo
-
-
-
- -
-
-
- 设置
-
- .icon-shezhi
-
-
-
- -
-
-
- 对话-未选
-
- .icon-duihua-weixuan
-
-
-
- -
-
-
- 图表-未选
-
- .icon-tubiao-weixuan
-
-
-
- -
-
-
- 编组 13备份 3
-
- .icon-a-bianzu13beifen3
-
-
-
- -
-
-
- 编组备份
-
- .icon-bianzubeifen
-
-
-
- -
-
-
- 表格
-
- .icon-biaoge1
-
-
-
- -
-
-
- 收藏 (1)
-
- .icon-a-shoucang1
-
-
-
- -
-
-
- guthub-未选
-
- .icon-guthub-weixuan1
-
-
-
- -
-
-
- 数据-未选
-
- .icon-shuju-weixuan
-
-
-
- -
-
-
- 编组 4
-
- .icon-a-bianzu4
-
-
-
- -
-
-
- 编组 14备份
-
- .icon-a-bianzu14beifen
-
-
-
- -
-
-
- guthub-未选
-
- .icon-guthub-weixuan
-
-
-
- -
-
-
- 24gl-folderMinus
-
- .icon-24gl-folderMinus
-
-
-
- -
-
-
- 24gl-folderOpen
-
- .icon-24gl-folderOpen
-
-
-
- -
-
-
- 24gf-folderOpen
-
- .icon-24gf-folderOpen
-
-
-
- -
-
-
- 云数据库
-
- .icon-yunshujuku
-
-
-
- -
-
-
- 报表
-
- .icon-baobiao
-
-
-
- -
-
-
- 工作台
-
- .icon-gongzuotai
-
-
-
- -
-
-
- mongodb
-
- .icon-mongodb
-
-
-
- -
-
-
- Redis
-
- .icon-Redis
-
-
-
- -
-
-
- HIVE_2
-
- .icon-HIVE
-
-
-
- -
-
-
- Kingbase
-
- .icon-Kingbase
-
-
-
- -
-
-
- 仪表盘
-
- .icon-yibiaopan
-
-
-
- -
-
-
- presto
-
- .icon-presto_sql
-
-
-
- -
-
-
- DB2
-
- .icon-shujukuleixingtubiao-kuozhan-
-
-
-
- -
-
-
- oceanbase
-
- .icon-oceanbase
-
-
-
- -
-
-
- 达梦
-
- .icon-dameng1
-
-
-
- -
-
-
- proxy
-
- .icon-proxy
-
-
-
- -
-
-
- openai
-
- .icon-openai
-
-
-
- -
-
-
- 关于
-
- .icon-guanyu
-
-
-
- -
-
-
- 衣服
-
- .icon-yifu
-
-
-
- -
-
-
- 数据库
-
- .icon-shujuku4
-
-
-
- -
-
-
- 数据源配置
-
- .icon-shujuyuanpeizhi
-
-
-
- -
-
-
- 服务器_数据库_jurassic
-
- .icon-jurassic_server
-
-
-
- -
-
-
- 数据库
-
- .icon-shujuku2
-
-
-
- -
-
-
- 数据库
-
- .icon-shujuku3
-
-
-
- -
-
-
- 数据库数据
-
- .icon-shujukushuju
-
-
-
- -
-
-
- 数据库
-
- .icon-shujuku1
-
-
-
- -
-
-
- 配置数据源
-
- .icon-peizhishujuyuan
-
-
-
- -
-
-
- SQL历史查询
-
- .icon-SQLlishichaxun
-
-
-
- -
-
-
- 重命名
-
- .icon-zhongmingming
-
-
-
- -
-
-
- ico_数据查询与统计_预约情况查询
-
- .icon-ico_shujuchaxunyutongji_yuyueqingkuangchaxun
-
-
-
- -
-
-
- clickhouse-云数据库ClickHouse
-
- .icon-clickhouse-yunshujukuClickHouse
-
-
-
- -
-
-
- rds_mariadb
-
- .icon-rds_mariadb
-
-
-
- -
-
-
- 减少减去减号
-
- .icon-jianshaojianqujianhao
-
-
-
- -
-
-
- sqlserver
-
- .icon-sqlserver
-
-
-
- -
-
-
- sqlite
-
- .icon-sqlite
-
-
-
- -
-
-
- 缺省页_暂无数据
-
- .icon-queshengye_zanwushuju
-
-
-
- -
-
-
- 未完成
-
- .icon-weiwancheng
-
-
-
- -
-
-
- 完成-01
-
- .icon-wancheng-
-
-
-
- -
-
-
- 成功
-
- .icon-chenggong1
-
-
-
- -
-
-
- 机器人
-
- .icon-jiqiren
-
-
-
- -
-
-
- 换一换
-
- .icon-huanyihuan
-
-
-
- -
-
-
- icon_infomation
-
- .icon-icon_infomation
-
-
-
- -
-
-
- key
-
- .icon-key1
-
-
-
- -
-
-
- mysql
-
- .icon-mysql
-
-
-
- -
-
-
- oracle
-
- .icon-oracle
-
-
-
- -
-
-
- postgresql
-
- .icon-postgresql
-
-
-
- -
-
-
- h2
-
- .icon-h2
-
-
-
- -
-
-
- cc-schema
-
- .icon-cc-schema
-
-
-
- -
-
-
- 新建表格
-
- .icon-xinjianbiaoge
-
-
-
- -
-
-
- export
-
- .icon-export
-
-
-
- -
-
-
- 角色管理
-
- .icon-jiaoseguanli
-
-
-
- -
-
-
- console
-
- .icon-console
-
-
-
- -
-
-
- 24gf-folderMinus
-
- .icon-24gf-folderMinus
-
-
-
- -
-
-
- 查看
-
- .icon-chakan
-
-
-
- -
-
-
- 复制_o
-
- .icon-fuzhi_o
-
-
-
- -
-
-
- 执行
-
- .icon-zhihang
-
-
-
- -
-
-
- m-格式化文字
-
- .icon-m-geshihuawenzi
-
-
-
- -
-
-
- github-fill
-
- .icon-github-fill
-
-
-
- -
-
-
- 保存
-
- .icon-baocun2
-
-
-
- -
-
-
- 箭头_向左两次_o
-
- .icon-jiantou_xiangzuoliangci_o
-
-
-
- -
-
-
- 新建窗口
-
- .icon-xinjianchuangkou
-
-
-
- -
-
-
- loading
-
- .icon-loading2
-
-
-
- -
-
-
- 链接克隆
-
- .icon-lianjiekelong
-
-
-
- -
-
-
- SQL升级文件
-
- .icon-SQLshengjiwenjian
-
-
-
- -
-
-
- sql
-
- .icon-sql
-
-
-
- -
-
-
- 连接流
-
- .icon-lianjieliu
-
-
-
- -
-
-
- 跳转/退出
-
- .icon-tiaozhuan
-
-
-
- -
-
-
- key
-
- .icon-key
-
-
-
- -
-
-
- 播放记录
-
- .icon-bofangjilu
-
-
-
- -
-
-
- 成功
-
- .icon-chenggong
-
-
-
- -
-
-
- 失败
-
- .icon-shibai
-
-
-
- -
-
-
- 收回 上下
-
- .icon-shouhuishangxia
-
-
-
- -
-
-
- 展开 上下
-
- .icon-zhankaishangxia
-
-
-
- -
-
-
- 数据库
-
- .icon-shujuku
-
-
-
- -
-
-
- 保存
-
- .icon-baocun
-
-
-
- -
-
-
- 查询
-
- .icon-chaxun
-
-
-
- -
-
-
- 对勾
-
- .icon-duigou11
-
-
-
- -
-
-
- check
-
- .icon-check1
-
-
-
- -
-
-
- 概览
-
- .icon-gailan
-
-
-
- -
-
-
- 概览
-
- .icon-huaban2
-
-
-
- -
-
-
- 编辑
-
- .icon-bianji
-
-
-
- -
-
-
- 刷新
-
- .icon-shuaxin1
-
-
-
- -
-
-
- 菜单/列表
-
- .icon-caidan
-
-
-
- -
-
-
- 表格
-
- .icon-biaoge
-
-
-
- -
-
-
- 展开
-
- .icon-zhankai
-
-
-
- -
-
-
- 收起
-
- .icon-shouqi
-
-
-
- -
-
-
- 主题_o
-
- .icon-zhuti_o
-
-
-
- -
-
-
- 断开连接
-
- .icon-duankailianjie
-
-
-
- -
-
-
- 修改
-
- .icon-xiugai
-
-
-
- -
-
-
- 删除
-
- .icon-delete
-
-
-
- -
-
-
- 更多
-
- .icon-gengduo1
-
-
-
- -
-
-
- 减少
-
- .icon-jianshao
-
-
-
- -
-
-
- 加
-
- .icon-jia
-
-
-
- -
-
-
- 加号
-
- .icon-hao
-
-
-
- -
-
-
- arrow drop down
-
- .icon-right
-
-
-
- -
-
-
- search
-
- .icon-search1
-
-
-
- -
-
-
- download
-
- .icon-download1
-
-
-
- -
-
-
- 向右箭头
-
- .icon-xiangyoujiantou
-
-
-
- -
-
-
- 删除线型
-
- .icon-shanchuxianxing
-
-
-
- -
-
-
- cross
-
- .icon-cross-copy
-
-
-
- -
-
-
- 刷新
-
- .icon-shuaxin
-
-
-
- -
-
-
- 提醒
-
- .icon-tixing
-
-
-
- -
-
-
- 138设置、系统设置、功能设置、属性
-
- .icon-shezhixitongshezhigongnengshezhishuxing
-
-
-
- -
-
-
- 执行sql脚本
-
- .icon-zhihangsqljiaoben
-
-
-
- -
-
-
- 虚拟数据库管理
-
- .icon-xunishujukuguanli
-
-
-
-
-
-
font-class 引用
-
-
-
font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。
-
与 Unicode 使用方式相比,具有如下特点:
-
- - 相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。
- - 因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。
-
-
使用步骤如下:
-
第一步:引入项目下面生成的 fontclass 代码:
-
<link rel="stylesheet" href="./iconfont.css">
-
-
第二步:挑选相应图标并获取类名,应用于页面:
-
<span class="iconfont icon-xxx"></span>
-
-
- "
- iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。
-
-
-
-
-
-
- -
-
-
right_on_5
- #icon-right_on_5
-
-
- -
-
-
right_off_5-01
- #icon-right_off_5-01
-
-
- -
-
-
left_on_2
- #icon-a-left_on_huaban11
-
-
- -
-
-
left_off
- #icon-a-left_off_huaban1
-
-
- -
-
-
minimize21
- #icon-minimize21
-
-
- -
-
-
restore
- #icon-restore_button2
-
-
- -
-
-
resize
- #icon-resize_button2
-
-
- -
-
-
close
- #icon-close_button2
-
-
- -
-
-
筛选
- #icon-shaixuan
-
-
- -
-
-
排序
- #icon-a-44tubiao-122
-
-
- -
-
-
305信息-线性圆框
- #icon-xinxi-xianxingyuankuang
-
-
- -
-
-
加号
- #icon-jiahao
-
-
- -
-
-
列表
- #icon-liebiao
-
-
- -
-
-
减去
- #icon-jianqu
-
-
- -
-
-
database
- #icon-database
-
-
- -
-
-
筛选
- #icon-shaixuan1
-
-
- -
-
-
刷新
- #icon-shuaxin2
-
-
- -
-
-
加号_o
- #icon-jiahao_o
-
-
- -
-
-
数据库_jurassic
- #icon-jurassic_data
-
-
- -
-
-
权限
- #icon-quanxian
-
-
- -
-
-
sharpicons_add-database
- #icon-sharpicons_add-database
-
-
- -
-
-
组织管理
- #icon-zuzhiguanli-
-
-
- -
-
-
空间
- #icon-moxing-miaobian
-
-
- -
-
-
下箭头-copy
- #icon-xiajiantou1-copy
-
-
- -
-
-
查看
- #icon-chakan2
-
-
- -
-
-
clone
- #icon-clone
-
-
- -
-
-
提交
- #icon-tijiao
-
-
- -
-
-
查看
- #icon-chakan1
-
-
- -
-
-
复制
- #icon-fuzhi
-
-
- -
-
-
icon_answer
- #icon-icon_answer
-
-
- -
-
-
icon_question
- #icon-icon_question
-
-
- -
-
-
发送
- #icon-fasong
-
-
- -
-
-
重启
- #icon-zhongqi
-
-
- -
-
-
提醒
- #icon-tixing2
-
-
- -
-
-
提醒
- #icon-tixing3
-
-
- -
-
-
提醒
- #icon-tixing1
-
-
- -
-
-
升级
- #icon-shengji
-
-
- -
-
-
全局_升级
- #icon-quanju_shengji
-
-
- -
-
-
关于我们
- #icon-guanyuwomen1
-
-
- -
-
-
ico版本更新
- #icon-icobanbengengxin
-
-
- -
-
-
对话气泡
- #icon-duihuaqipao
-
-
- -
-
-
角色权限
- #icon-jiaosequanxian
-
-
- -
-
-
preview
- #icon-preview1
-
-
- -
-
-
导入
- #icon-daoru
-
-
- -
-
-
终止
- #icon-zhongzhi
-
-
- -
-
-
退出
- #icon-tuichu
-
-
- -
-
-
控桩终端
- #icon-kongzhuangzhongduan
-
-
- -
-
-
撤销
- #icon-chexiao1
-
-
- -
-
-
向上
- #icon-xiangshang
-
-
- -
-
-
查看
- #icon-chakan-copy
-
-
- -
-
-
编辑数据_编辑录入操作_jurassic
- #icon-jurassic_edit-data
-
-
- -
-
-
编辑表格_编辑录入操作_jurassic
- #icon-jurassic_edit-table
-
-
- -
-
-
报表数据录入
- #icon-baobiaoshujuluru
-
-
- -
-
-
播放5
- #icon-bofang5
-
-
- -
-
-
清空@3x
- #icon-a-qingkong3x
-
-
- -
-
-
删除
- #icon-shanchu
-
-
- -
-
-
new-document-worksheet
- #icon-newdocumentworksheet
-
-
- -
-
-
file-excel
- #icon-file-excel
-
-
- -
-
-
file-markdown
- #icon-file-markdown
-
-
- -
-
-
file-word
- #icon-file-word
-
-
- -
-
-
HTML5
- #icon-HTML
-
-
- -
-
-
HTML
- #icon-HTML1
-
-
- -
-
-
pdf
- #icon-pdf
-
-
- -
-
-
个人用户
- #icon-gerenyonghu
-
-
- -
-
-
后台管理
- #icon-houtaiguanli
-
-
- -
-
-
字体代码
- #icon-zitidaima
-
-
- -
-
-
版本
- #icon-banben
-
-
- -
-
-
车位管理
- #icon-cheweiguanli
-
-
- -
-
-
dictate
- #icon-dianzhelidaochu
-
-
- -
-
-
circle-f
- #icon-circle-f
-
-
- -
-
-
图表-函数
- #icon-tubiao-hanshu
-
-
- -
-
-
视图管理器
- #icon-shituguanliqi
-
-
- -
-
-
回车
- #icon-huiche
-
-
- -
-
-
缺省
- #icon-quesheng
-
-
- -
-
-
进入箭头
- #icon-jinrujiantou
-
-
- -
-
-
右箭头
- #icon-youjiantou_huaban
-
-
- -
-
-
向右箭头
- #icon-xiangyoujiantou1
-
-
- -
-
-
数据源
- #icon-shujuyuan
-
-
- -
-
-
question
- #icon-question
-
-
- -
-
-
星星-copy
- #icon-xingxing
-
-
- -
-
-
控制台
- #icon-kongzhitai
-
-
- -
-
-
星系
- #icon-xingxi
-
-
- -
-
-
暂无数据 (1)
- #icon-a-zanwushuju1
-
-
- -
-
-
开始
- #icon-kaishi
-
-
- -
-
-
关闭
- #icon-guanbi
-
-
- -
-
-
下箭头
- #icon-xiajiantou
-
-
- -
-
-
more
- #icon-gengduo
-
-
- -
-
-
设置
- #icon-shezhi
-
-
- -
-
-
对话-未选
- #icon-duihua-weixuan
-
-
- -
-
-
图表-未选
- #icon-tubiao-weixuan
-
-
- -
-
-
编组 13备份 3
- #icon-a-bianzu13beifen3
-
-
- -
-
-
编组备份
- #icon-bianzubeifen
-
-
- -
-
-
表格
- #icon-biaoge1
-
-
- -
-
-
收藏 (1)
- #icon-a-shoucang1
-
-
- -
-
-
guthub-未选
- #icon-guthub-weixuan1
-
-
- -
-
-
数据-未选
- #icon-shuju-weixuan
-
-
- -
-
-
编组 4
- #icon-a-bianzu4
-
-
- -
-
-
编组 14备份
- #icon-a-bianzu14beifen
-
-
- -
-
-
guthub-未选
- #icon-guthub-weixuan
-
-
- -
-
-
24gl-folderMinus
- #icon-24gl-folderMinus
-
-
- -
-
-
24gl-folderOpen
- #icon-24gl-folderOpen
-
-
- -
-
-
24gf-folderOpen
- #icon-24gf-folderOpen
-
-
- -
-
-
云数据库
- #icon-yunshujuku
-
-
- -
-
-
报表
- #icon-baobiao
-
-
- -
-
-
工作台
- #icon-gongzuotai
-
-
- -
-
-
mongodb
- #icon-mongodb
-
-
- -
-
-
Redis
- #icon-Redis
-
-
- -
-
-
HIVE_2
- #icon-HIVE
-
-
- -
-
-
Kingbase
- #icon-Kingbase
-
-
- -
-
-
仪表盘
- #icon-yibiaopan
-
-
- -
-
-
presto
- #icon-presto_sql
-
-
- -
-
-
DB2
- #icon-shujukuleixingtubiao-kuozhan-
-
-
- -
-
-
oceanbase
- #icon-oceanbase
-
-
- -
-
-
达梦
- #icon-dameng1
-
-
- -
-
-
proxy
- #icon-proxy
-
-
- -
-
-
openai
- #icon-openai
-
-
- -
-
-
关于
- #icon-guanyu
-
-
- -
-
-
衣服
- #icon-yifu
-
-
- -
-
-
数据库
- #icon-shujuku4
-
-
- -
-
-
数据源配置
- #icon-shujuyuanpeizhi
-
-
- -
-
-
服务器_数据库_jurassic
- #icon-jurassic_server
-
-
- -
-
-
数据库
- #icon-shujuku2
-
-
- -
-
-
数据库
- #icon-shujuku3
-
-
- -
-
-
数据库数据
- #icon-shujukushuju
-
-
- -
-
-
数据库
- #icon-shujuku1
-
-
- -
-
-
配置数据源
- #icon-peizhishujuyuan
-
-
- -
-
-
SQL历史查询
- #icon-SQLlishichaxun
-
-
- -
-
-
重命名
- #icon-zhongmingming
-
-
- -
-
-
ico_数据查询与统计_预约情况查询
- #icon-ico_shujuchaxunyutongji_yuyueqingkuangchaxun
-
-
- -
-
-
clickhouse-云数据库ClickHouse
- #icon-clickhouse-yunshujukuClickHouse
-
-
- -
-
-
rds_mariadb
- #icon-rds_mariadb
-
-
- -
-
-
减少减去减号
- #icon-jianshaojianqujianhao
-
-
- -
-
-
sqlserver
- #icon-sqlserver
-
-
- -
-
-
sqlite
- #icon-sqlite
-
-
- -
-
-
缺省页_暂无数据
- #icon-queshengye_zanwushuju
-
-
- -
-
-
未完成
- #icon-weiwancheng
-
-
- -
-
-
完成-01
- #icon-wancheng-
-
-
- -
-
-
成功
- #icon-chenggong1
-
-
- -
-
-
机器人
- #icon-jiqiren
-
-
- -
-
-
换一换
- #icon-huanyihuan
-
-
- -
-
-
icon_infomation
- #icon-icon_infomation
-
-
- -
-
-
key
- #icon-key1
-
-
- -
-
-
mysql
- #icon-mysql
-
-
- -
-
-
oracle
- #icon-oracle
-
-
- -
-
-
postgresql
- #icon-postgresql
-
-
- -
-
-
h2
- #icon-h2
-
-
- -
-
-
cc-schema
- #icon-cc-schema
-
-
- -
-
-
新建表格
- #icon-xinjianbiaoge
-
-
- -
-
-
export
- #icon-export
-
-
- -
-
-
角色管理
- #icon-jiaoseguanli
-
-
- -
-
-
console
- #icon-console
-
-
- -
-
-
24gf-folderMinus
- #icon-24gf-folderMinus
-
-
- -
-
-
查看
- #icon-chakan
-
-
- -
-
-
复制_o
- #icon-fuzhi_o
-
-
- -
-
-
执行
- #icon-zhihang
-
-
- -
-
-
m-格式化文字
- #icon-m-geshihuawenzi
-
-
- -
-
-
github-fill
- #icon-github-fill
-
-
- -
-
-
保存
- #icon-baocun2
-
-
- -
-
-
箭头_向左两次_o
- #icon-jiantou_xiangzuoliangci_o
-
-
- -
-
-
新建窗口
- #icon-xinjianchuangkou
-
-
- -
-
-
loading
- #icon-loading2
-
-
- -
-
-
链接克隆
- #icon-lianjiekelong
-
-
- -
-
-
SQL升级文件
- #icon-SQLshengjiwenjian
-
-
- -
-
-
sql
- #icon-sql
-
-
- -
-
-
连接流
- #icon-lianjieliu
-
-
- -
-
-
跳转/退出
- #icon-tiaozhuan
-
-
- -
-
-
key
- #icon-key
-
-
- -
-
-
播放记录
- #icon-bofangjilu
-
-
- -
-
-
成功
- #icon-chenggong
-
-
- -
-
-
失败
- #icon-shibai
-
-
- -
-
-
收回 上下
- #icon-shouhuishangxia
-
-
- -
-
-
展开 上下
- #icon-zhankaishangxia
-
-
- -
-
-
数据库
- #icon-shujuku
-
-
- -
-
-
保存
- #icon-baocun
-
-
- -
-
-
查询
- #icon-chaxun
-
-
- -
-
-
对勾
- #icon-duigou11
-
-
- -
-
-
check
- #icon-check1
-
-
- -
-
-
概览
- #icon-gailan
-
-
- -
-
-
概览
- #icon-huaban2
-
-
- -
-
-
编辑
- #icon-bianji
-
-
- -
-
-
刷新
- #icon-shuaxin1
-
-
- -
-
-
菜单/列表
- #icon-caidan
-
-
- -
-
-
表格
- #icon-biaoge
-
-
- -
-
-
展开
- #icon-zhankai
-
-
- -
-
-
收起
- #icon-shouqi
-
-
- -
-
-
主题_o
- #icon-zhuti_o
-
-
- -
-
-
断开连接
- #icon-duankailianjie
-
-
- -
-
-
修改
- #icon-xiugai
-
-
- -
-
-
删除
- #icon-delete
-
-
- -
-
-
更多
- #icon-gengduo1
-
-
- -
-
-
减少
- #icon-jianshao
-
-
- -
-
-
加
- #icon-jia
-
-
- -
-
-
加号
- #icon-hao
-
-
- -
-
-
arrow drop down
- #icon-right
-
-
- -
-
-
search
- #icon-search1
-
-
- -
-
-
download
- #icon-download1
-
-
- -
-
-
向右箭头
- #icon-xiangyoujiantou
-
-
- -
-
-
删除线型
- #icon-shanchuxianxing
-
-
- -
-
-
cross
- #icon-cross-copy
-
-
- -
-
-
刷新
- #icon-shuaxin
-
-
- -
-
-
提醒
- #icon-tixing
-
-
- -
-
-
138设置、系统设置、功能设置、属性
- #icon-shezhixitongshezhigongnengshezhishuxing
-
-
- -
-
-
执行sql脚本
- #icon-zhihangsqljiaoben
-
-
- -
-
-
虚拟数据库管理
- #icon-xunishujukuguanli
-
-
-
-
-
Symbol 引用
-
-
-
这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇文章
- 这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:
-
- - 支持多色图标了,不再受单色限制。
- - 通过一些技巧,支持像字体那样,通过
font-size, color 来调整样式。
- - 兼容性较差,支持 IE9+,及现代浏览器。
- - 浏览器渲染 SVG 的性能一般,还不如 png。
-
-
使用步骤如下:
-
第一步:引入项目下面生成的 symbol 代码:
-
<script src="./iconfont.js"></script>
-
-
第二步:加入通用 CSS 代码(引入一次就行):
-
<style>
-.icon {
- width: 1em;
- height: 1em;
- vertical-align: -0.15em;
- fill: currentColor;
- overflow: hidden;
-}
-</style>
-
-
第三步:挑选相应图标并获取类名,应用于页面:
-
<svg class="icon" aria-hidden="true">
- <use xlink:href="#icon-xxx"></use>
-</svg>
-
-
-
-
-
-
-
-
-
diff --git a/chat2db-client/src/assets/font/iconfont-he.ttf b/chat2db-client/src/assets/font/iconfont-he.ttf
new file mode 100644
index 000000000..6c5de7a91
Binary files /dev/null and b/chat2db-client/src/assets/font/iconfont-he.ttf differ
diff --git a/chat2db-client/src/assets/font/iconfont-he.woff b/chat2db-client/src/assets/font/iconfont-he.woff
new file mode 100644
index 000000000..7d4155420
Binary files /dev/null and b/chat2db-client/src/assets/font/iconfont-he.woff differ
diff --git a/chat2db-client/src/assets/font/iconfont-he.woff2 b/chat2db-client/src/assets/font/iconfont-he.woff2
new file mode 100644
index 000000000..846892de8
Binary files /dev/null and b/chat2db-client/src/assets/font/iconfont-he.woff2 differ
diff --git a/chat2db-client/src/assets/font/iconfont.css b/chat2db-client/src/assets/font/iconfont.css
index 52ce3cf9e..ce32ae206 100644
--- a/chat2db-client/src/assets/font/iconfont.css
+++ b/chat2db-client/src/assets/font/iconfont.css
@@ -829,3 +829,10 @@
content: "\e61a";
}
+.icon-Phoenix:before {
+ content: "\e712";
+}
+
+.icon-wuguan:before {
+ content: "\ec5f";
+}
diff --git a/chat2db-client/src/assets/font/iconfont.js b/chat2db-client/src/assets/font/iconfont.js
index 6b3f2a891..cd9f7f677 100644
--- a/chat2db-client/src/assets/font/iconfont.js
+++ b/chat2db-client/src/assets/font/iconfont.js
@@ -1 +1 @@
-window._iconfont_svg_string_3633546='',function(h){var a=(a=document.getElementsByTagName("script"))[a.length-1],c=a.getAttribute("data-injectcss"),a=a.getAttribute("data-disable-injectsvg");if(!a){var l,t,i,o,v,z=function(a,c){c.parentNode.insertBefore(a,c)};if(c&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}l=function(){var a,c=document.createElement("div");c.innerHTML=h._iconfont_svg_string_3633546,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?z(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),l()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=l,o=h.document,v=!1,s(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){v||(v=!0,i())}function s(){try{o.documentElement.doScroll("left")}catch(a){return void setTimeout(s,50)}p()}}(window);
\ No newline at end of file
+window._iconfont_svg_string_3633546='',function(h){var a=(a=document.getElementsByTagName("script"))[a.length-1]; var c=a.getAttribute("data-injectcss"); var a=a.getAttribute("data-disable-injectsvg");if(!a){var l; var t; var i; var o; var v; var z=function(a,c){c.parentNode.insertBefore(a,c)};if(c&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}l=function(){var a; var c=document.createElement("div");c.innerHTML=h._iconfont_svg_string_3633546,(c=c.getElementsByTagName("svg")[0])&&(c.setAttribute("aria-hidden","true"),c.style.position="absolute",c.style.width=0,c.style.height=0,c.style.overflow="hidden",c=c,(a=document.body).firstChild?z(c,a.firstChild):a.appendChild(c))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(l,0):(t=function(){document.removeEventListener("DOMContentLoaded",t,!1),l()},document.addEventListener("DOMContentLoaded",t,!1)):document.attachEvent&&(i=l,o=h.document,v=!1,s(),o.onreadystatechange=function(){"complete"==o.readyState&&(o.onreadystatechange=null,p())})}function p(){v||(v=!0,i())}function s(){try{o.documentElement.doScroll("left")}catch(a){return void setTimeout(s,50)}p()}}(window);
diff --git a/chat2db-client/src/assets/img/databaseImg/phoenixLogo.png b/chat2db-client/src/assets/img/databaseImg/phoenixLogo.png
new file mode 100644
index 000000000..50bd4a59d
Binary files /dev/null and b/chat2db-client/src/assets/img/databaseImg/phoenixLogo.png differ
diff --git a/chat2db-client/src/blocks/DatabaseTableEditor/BaseInfo/index.tsx b/chat2db-client/src/blocks/DatabaseTableEditor/BaseInfo/index.tsx
index c82f2063c..82bc05d02 100644
--- a/chat2db-client/src/blocks/DatabaseTableEditor/BaseInfo/index.tsx
+++ b/chat2db-client/src/blocks/DatabaseTableEditor/BaseInfo/index.tsx
@@ -1,14 +1,15 @@
-import React, { useContext, useEffect, useImperativeHandle, ForwardedRef, forwardRef } from 'react';
-import styles from './index.less';
-import classnames from 'classnames';
-import { Form, Input } from 'antd';
-import { Context } from '../index';
-import { IBaseInfo } from '@/typings';
import { DatabaseTypeCode } from '@/constants';
import i18n from '@/i18n';
+import { IBaseInfo } from '@/typings';
+import { Form, Input } from 'antd';
+import classnames from 'classnames';
+import { ForwardedRef, forwardRef, useContext, useEffect, useImperativeHandle } from 'react';
+import { Context } from '../index';
+import styles from './index.less';
export interface IBaseInfoRef {
getBaseInfo: () => IBaseInfo;
+ setTableComment: (comment: string) => void;
}
interface IProps {
@@ -24,6 +25,7 @@ const BaseInfo = forwardRef((props: IProps, ref: ForwardedRef) =>
form.setFieldsValue({
name: tableDetails.name,
comment: tableDetails.comment,
+ aiComment: tableDetails.aiComment,
charset: tableDetails.charset,
engine: tableDetails.engine,
incrementValue: tableDetails.incrementValue,
@@ -36,6 +38,9 @@ const BaseInfo = forwardRef((props: IProps, ref: ForwardedRef) =>
useImperativeHandle(ref, () => ({
getBaseInfo,
+ setTableComment: (comment: string) => {
+ form.setFieldsValue({ comment:comment,aiComment:comment });
+ },
}));
return (
diff --git a/chat2db-client/src/blocks/DatabaseTableEditor/ColumnList/index.tsx b/chat2db-client/src/blocks/DatabaseTableEditor/ColumnList/index.tsx
index ad6b6718f..55431778b 100644
--- a/chat2db-client/src/blocks/DatabaseTableEditor/ColumnList/index.tsx
+++ b/chat2db-client/src/blocks/DatabaseTableEditor/ColumnList/index.tsx
@@ -1,19 +1,19 @@
-import React, { useContext, useEffect, useState, useRef, forwardRef, ForwardedRef, useImperativeHandle } from 'react';
-import styles from './index.less';
-import classnames from 'classnames';
+import CustomSelect from '@/components/CustomSelect';
+import Iconfont from '@/components/Iconfont';
+import { DatabaseTypeCode, EditColumnOperationType, NullableType } from '@/constants';
+import i18n from '@/i18n';
+import { IColumnItemNew, IColumnTypes } from '@/typings';
import { MenuOutlined } from '@ant-design/icons';
import { DndContext, type DragEndEvent } from '@dnd-kit/core';
import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
-import { Table, InputNumber, Input, Form, Select, Checkbox } from 'antd';
-import { v4 as uuidv4 } from 'uuid';
import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
import { CSS } from '@dnd-kit/utilities';
+import { Checkbox, Form, Input, InputNumber, Select, Table } from 'antd';
+import classnames from 'classnames';
+import React, { ForwardedRef, forwardRef, useContext, useEffect, useImperativeHandle, useRef, useState } from 'react';
+import { v4 as uuidv4 } from 'uuid';
import { Context } from '../index';
-import { IColumnItemNew, IColumnTypes } from '@/typings';
-import i18n from '@/i18n';
-import { EditColumnOperationType, DatabaseTypeCode, NullableType } from '@/constants';
-import CustomSelect from '@/components/CustomSelect';
-import Iconfont from '@/components/Iconfont';
+import styles from './index.less';
interface RowProps extends React.HTMLAttributes {
'data-row-key': string;
@@ -29,6 +29,7 @@ interface IEditingConfig extends IColumnTypes {
// 本组件暴露给父组件的方法
export interface IColumnListRef {
getColumnListInfo: () => IColumnItemNew[];
+ setColumnComment: (columnName: string, comment: string) => void;
}
const Row = ({ children, ...props }: RowProps) => {
@@ -71,6 +72,7 @@ const createInitialData = () => {
defaultValue: null,
autoIncrement: null,
comment: null,
+ aiComment: null,
primaryKey: null,
primaryKeyOrder: null,
schemaName: null,
@@ -109,8 +111,10 @@ const ColumnList = forwardRef((props: IProps, ref: ForwardedRef)
form.setFieldsValue({ ...record });
setEditingData(record);
// 根据当前字段类型,设置编辑配置
+ // 需要匹配基础类型名
+ const baseColumnType = record.dataType?.toUpperCase().trim();
databaseSupportField.columnTypes.forEach((i) => {
- if (i.typeName === record.columnType) {
+ if (i.typeName === baseColumnType) {
setEditingConfig({
...i,
editKey: record.key!,
@@ -178,14 +182,14 @@ const ColumnList = forwardRef((props: IProps, ref: ForwardedRef)
},
{
title: i18n('editTable.label.columnType'),
- dataIndex: 'columnType',
+ dataIndex: 'dataType',
width: '200px',
render: (text: string, record: IColumnItemNew) => {
const editable = isEditing(record);
return (
{editable ? (
-
+
) : (
@@ -408,8 +412,13 @@ const ColumnList = forwardRef((props: IProps, ref: ForwardedRef)
if (name === 'columnType') {
// 根据当前字段类型,设置编辑配置
+ // 需要匹配基础类型名,忽略长度等信息(如 bigint(20) -> BIGINT)
+ const baseType = value
+ ?.toUpperCase()
+ .replace(/\(.*\)/, '')
+ .trim();
databaseSupportField.columnTypes.forEach((i) => {
- if (i.typeName === value) {
+ if (i.typeName === baseType) {
setEditingConfig({
...editingConfig!,
...i,
@@ -424,6 +433,12 @@ const ColumnList = forwardRef((props: IProps, ref: ForwardedRef)
});
}
}
+
+ if (name === 'autoIncrement') {
+ // Checkbox value is boolean, convert to true/null for backend
+ editingDataItem.autoIncrement = value === true ? true : null;
+ }
+
return editingDataItem;
}
return item;
@@ -477,6 +492,21 @@ const ColumnList = forwardRef((props: IProps, ref: ForwardedRef)
useImperativeHandle(ref, () => ({
getColumnListInfo,
+ setColumnComment: (columnName: string, comment: string) => {
+ setDataSource((prevDataSource) =>
+ prevDataSource.map((column) => {
+ if (column.name === columnName) {
+ return {
+ ...column,
+ comment,
+ aiComment: comment,
+ editStatus: EditColumnOperationType.Modify,
+ };
+ }
+ return column;
+ }),
+ );
+ },
}));
const renderOtherInfoForm = () => {
diff --git a/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.less b/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.less
new file mode 100644
index 000000000..adac99e99
--- /dev/null
+++ b/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.less
@@ -0,0 +1,215 @@
+@import '../../../styles/var.less';
+
+.foreignKeyList {
+ height: 100%;
+ padding: 10px;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+}
+
+.foreignKeyListHeader {
+ margin: 0px -5px 10px;
+ flex-shrink: 0;
+ button {
+ margin: 0px 5px;
+ }
+}
+
+.formBox {
+ height: 0px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+}
+
+.tableBox {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ border-radius: 8px 8px 0px 0px;
+ overflow: hidden;
+}
+
+.addColumnButton {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px dashed var(--color-border);
+ line-height: 30px;
+ margin-top: 10px;
+ color: var(--color-text-secondary);
+ cursor: pointer;
+ i {
+ margin-right: 5px;
+ }
+ &:hover {
+ color: var(--color-primary);
+ border-color: var(--color-primary);
+ }
+}
+
+.actionBar {
+ flex-shrink: 0;
+ display: flex;
+ gap: 10px;
+ margin-top: 10px;
+}
+
+.syncButton {
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border: 1px dashed var(--color-border);
+ line-height: 30px;
+ padding: 0 12px;
+ color: var(--color-text-secondary);
+ cursor: pointer;
+ i {
+ margin-right: 5px;
+ }
+ &:hover {
+ color: var(--color-primary);
+ border-color: var(--color-primary);
+ }
+ &.syncing {
+ cursor: not-allowed;
+ opacity: 0.6;
+ &:hover {
+ color: var(--color-text-secondary);
+ border-color: var(--color-border);
+ }
+ }
+}
+
+.sourceTypeBadge {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ width: 24px;
+ height: 24px;
+ border-radius: 4px;
+ font-size: 12px;
+ font-weight: bold;
+ &.real {
+ background-color: var(--color-info-bg);
+ color: var(--color-info);
+ }
+ &.virtual {
+ background-color: var(--color-warning-bg);
+ color: var(--color-warning);
+ }
+}
+
+.otherInfo {
+ flex-shrink: 0;
+ margin: 10px -10px 0px;
+ padding: 10px;
+ height: 200px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ border-top: 1px solid var(--color-border);
+}
+
+.otherInfoFormBox {
+ min-height: 140px;
+ width: 400px;
+}
+
+.editableCell {
+ height: 28px;
+ line-height: 26px;
+ padding: 0px 7px;
+ box-sizing: border-box;
+ border: 1px solid transparent;
+ .f-single-line();
+ width: 100%;
+ cursor: pointer;
+}
+
+// .cellContent {
+// border: 1px solid transparent;
+// margin: -1px;
+// &:hover {
+// border: 1px solid var(--color-primary);
+// }
+// }
+
+.keyBox {
+ width: 26px;
+ height: 26px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ cursor: pointer;
+ position: relative;
+ i {
+ color: var(--color-warning);
+ }
+ span {
+ position: absolute;
+ font-weight: bold;
+ right: 4px;
+ bottom: -2px;
+ transform: scale(0.8);
+ }
+}
+
+.disabledKeyBox {
+ cursor: default;
+}
+
+.operationBar {
+ display: flex;
+ justify-content: end;
+ .deleteIconBox {
+ height: 26px;
+ width: 26px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ cursor: pointer;
+ &:hover {
+ color: var(--color-primary);
+ }
+ }
+}
+.foreignKeyList {
+ :global {
+ .ant-table-body {
+ border: 1px solid var(--color-border);
+ border-top: 0px;
+ border-bottom: 0px;
+ }
+ .ant-table-header {
+ border: 1px solid var(--color-border);
+ border-bottom: 0px;
+ }
+ .ant-table {
+ border-radius: 10px;
+ border-bottom: 0px;
+ }
+ .ant-table-wrapper .ant-table-tbody > tr > td {
+ // border: 0px;
+ padding: 4px 2px;
+ }
+ .ant-table-wrapper .ant-table-thead > tr > th {
+ padding: 8px 4px;
+ &::before {
+ display: none;
+ }
+ }
+ .ant-table-wrapper .ant-table-thead > tr > td {
+ &::before {
+ display: none;
+ }
+ }
+ // antd无法设置最小宽度,所以在这里设置最小列宽为100px
+ colgroup col:nth-last-child(2) {
+ min-width: 100px;
+ }
+ }
+}
diff --git a/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.tsx b/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.tsx
new file mode 100644
index 000000000..b1fbdb1a3
--- /dev/null
+++ b/chat2db-client/src/blocks/DatabaseTableEditor/ForeignKeyList/index.tsx
@@ -0,0 +1,450 @@
+import React, { useContext, useEffect, useState, useRef, forwardRef, ForwardedRef, useImperativeHandle } from 'react';
+import styles from './index.less';
+import classnames from 'classnames';
+import { MenuOutlined, LoadingOutlined, SyncOutlined } from '@ant-design/icons';
+import { DndContext, type DragEndEvent } from '@dnd-kit/core';
+import { restrictToVerticalAxis } from '@dnd-kit/modifiers';
+import { Table, Input, Form, Select, message, Tooltip } from 'antd';
+import { v4 as uuidv4 } from 'uuid';
+import { arrayMove, SortableContext, useSortable, verticalListSortingStrategy } from '@dnd-kit/sortable';
+import { CSS } from '@dnd-kit/utilities';
+import { Context } from '../index';
+import { IForeignKeyItemNew, IForeignKey } from '@/typings';
+import i18n from '@/i18n';
+import { EditColumnOperationType } from '@/constants';
+import Iconfont from '@/components/Iconfont';
+import sqlService from '@/service/sql';
+
+interface RowProps extends React.HTMLAttributes {
+ 'data-row-key': string;
+}
+
+interface IProps { }
+
+export type IForeignKeyListInfo = IForeignKeyItemNew[]; // 外键信息列表类型
+
+export interface IForeignKeyListRef {
+ getForeignKeyListInfo: () => IForeignKeyListInfo; // 获取外键信息列表的方法
+}
+
+// 创建一个空的外键数据结构
+const createInitialData = () => {
+ return {
+ key: uuidv4(),
+ name: null,
+ tableName: null,
+ schemaName: null,
+ databaseName: null,
+ column: null,
+ referencedTable: null,
+ referencedColumn: null,
+ updateRule: 0,
+ deleteRule: 0,
+ comment: null,
+ editStatus: EditColumnOperationType.Add,
+ };
+};
+
+const Row = ({ children, ...props }: RowProps) => {
+ const { attributes, listeners, setNodeRef, setActivatorNodeRef, transform, transition, isDragging } = useSortable({
+ id: props['data-row-key'],
+ });
+
+ const style: React.CSSProperties = {
+ ...props.style,
+ transform: CSS.Transform.toString(transform && { ...transform, scaleY: 1 }),
+ transition,
+ ...(isDragging ? { position: 'relative', zIndex: 9999 } : {}),
+ };
+
+ return (
+
+ {React.Children.map(children, (child) => {
+ if ((child as React.ReactElement).key === 'sort') {
+ return React.cloneElement(child as React.ReactElement, {
+ children: (
+
+ ),
+ });
+ }
+ return child;
+ })}
+
+ );
+};
+
+
+const ForeignKeyList = forwardRef((props: IProps, ref: ForwardedRef) => {
+ const { tableDetails } = useContext(Context);
+ const [dataSource, setDataSource] = useState([createInitialData()]);
+ const [form] = Form.useForm();
+ const [editingData, setEditingData] = useState(null);
+ const [syncing, setSyncing] = useState(false);
+ const tableRef = useRef(null);
+
+ const isEditing = (record: IForeignKeyItemNew) => record.key === editingData?.key;
+ const handleFieldsChange = (field: any) => {
+ const { value } = field[0];
+ const { name: nameList } = field[0];
+ const name = nameList[0];
+ const newData = dataSource.map((item) => {
+ if (item.key === editingData?.key) {
+ // 判断当前数据是新增的数据还是编辑后的数据
+ let editStatus = item.editStatus;
+ if (editStatus !== EditColumnOperationType.Add) {
+ editStatus = EditColumnOperationType.Modify;
+ }
+ const editingDataItem = {
+ ...item,
+ [name]: value,
+ editStatus,
+ };
+ return editingDataItem;
+ }
+ return item;
+ });
+ setDataSource(newData);
+ };
+
+ const edit = (record: IForeignKeyItemNew) => {
+ if (record.key) {
+ form.setFieldsValue({ ...record });
+ setEditingData(record);
+ }
+ };
+
+ // 整理服务端返回的数据,构造为前端需要的数据结构
+ useEffect(() => {
+ if (tableDetails) {
+ const list =
+ tableDetails?.foreignKeyList?.map((t) => {
+ return {
+ ...t,
+ key: uuidv4(),
+ };
+ }) || [];
+ setDataSource(list);
+ }
+ }, [tableDetails]);
+
+ const columns = [
+ {
+ key: 'sort',
+ width: '40px',
+ align: 'center',
+ fixed: 'left',
+ },
+ {
+ title: i18n('editTable.label.foreignKeyName'),
+ dataIndex: 'name',
+ width: '160px',
+ fixed: 'left',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
{text}
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.column'),
+ dataIndex: 'column',
+ width: '160px',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
{text}
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.referencedTable'),
+ dataIndex: 'referencedTable',
+ width: '160px',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
{text}
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.referencedColumn'),
+ dataIndex: 'referencedColumn',
+ width: '160px',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
{text}
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.updateRule'),
+ dataIndex: 'updateRule',
+ width: '160px',
+ render: (text: number, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
+ {text === 0 ? 'CASCADE' :
+ text === 1 ? 'SET NULL' :
+ text === 2 ? 'NO ACTION' :
+ text === 3 ? 'RESTRICT' :
+ text === 4 ? 'SET DEFAULT' : 'UNKNOWN'}
+
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.deleteRule'),
+ dataIndex: 'deleteRule',
+ width: '160px',
+ render: (text: number, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return (
+
+ {editable ? (
+
+
+
+ ) : (
+
+ {text === 0 ? 'CASCADE' :
+ text === 1 ? 'SET NULL' :
+ text === 2 ? 'NO ACTION' :
+ text === 3 ? 'RESTRICT' :
+ text === 4 ? 'SET DEFAULT' : 'UNKNOWN'}
+
+ )}
+
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.comment'),
+ dataIndex: 'comment',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const editable = isEditing(record);
+ return editable ? (
+
+
+
+ ) : (
+ {text}
+ );
+ },
+ },
+ {
+ title: i18n('editTable.label.sourceType'),
+ dataIndex: 'sourceType',
+ width: '100px',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ const isVirtual = text === 'VIRTUAL' || record.editable;
+ return (
+
+
+ {isVirtual ? 'V' : 'R'}
+
+
+ );
+ },
+ },
+ {
+ width: '40px',
+ render: (text: string, record: IForeignKeyItemNew) => {
+ return (
+ {
+ deleteData(record);
+ }}
+ >
+
+
+
+
+ );
+ },
+ },
+ ];
+
+ const onDragEnd = ({ active, over }: DragEndEvent) => {
+ if (active.id !== over?.id) {
+ setDataSource((previous) => {
+ const activeIndex = previous.findIndex((i) => i.key === active.id);
+ const overIndex = previous.findIndex((i) => i.key === over?.id);
+ return arrayMove(previous, activeIndex, overIndex);
+ });
+ }
+ };
+
+ const addData = () => {
+ const newData = {
+ ...createInitialData(),
+ };
+ setDataSource([...dataSource, newData]);
+ edit(newData);
+ setTimeout(() => {
+ tableRef.current?.scrollTo(0, tableRef.current?.scrollHeight + 100);
+ }, 0);
+ };
+
+ const deleteData = (record) => {
+ let list: any = [];
+ if (record?.editStatus === EditColumnOperationType.Add) {
+ list = dataSource.filter((i) => i.key !== record?.key);
+ } else {
+ list = dataSource.map((i) => {
+ if (i.key === record?.key) {
+ setEditingData(null);
+ return {
+ ...i,
+ editStatus: EditColumnOperationType.Delete,
+ };
+ }
+ return i;
+ });
+ }
+ setDataSource(list);
+ };
+
+ const handleSync = async () => {
+ if (!tableDetails) return;
+ const { dataSourceId, databaseName, schemaName, name: tableName } = tableDetails as any;
+ if (!dataSourceId || !databaseName || !tableName) {
+ message.warning(i18n('editTable.message.syncFKWarning'));
+ return;
+ }
+ setSyncing(true);
+ try {
+ const result = await sqlService.syncForeignKeys({
+ dataSourceId: Number(dataSourceId),
+ databaseName,
+ schemaName,
+ tableName,
+ });
+ message.success(i18n('editTable.message.syncFKSuccess', [result?.added || 0, result?.deleted || 0, result?.unchanged || 0]));
+ } catch (error) {
+ message.error(i18n('editTable.message.syncFKError'));
+ } finally {
+ setSyncing(false);
+ }
+ };
+
+ useImperativeHandle(ref, () => ({
+ getForeignKeyListInfo: () => {
+ return dataSource.map((i) => {
+ const data = {
+ ...i,
+ };
+ delete data.key;
+ return data;
+ });
+ },
+ }));
+
+ return (
+