feat quickly
This commit is contained in:
@@ -1,27 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
// App struct
|
|
||||||
type App struct {
|
|
||||||
ctx context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewApp creates a new App application struct
|
|
||||||
func NewApp() *App {
|
|
||||||
return &App{}
|
|
||||||
}
|
|
||||||
|
|
||||||
// startup is called when the app starts. The context is saved
|
|
||||||
// so we can call the runtime methods
|
|
||||||
func (a *App) startup(ctx context.Context) {
|
|
||||||
a.ctx = ctx
|
|
||||||
}
|
|
||||||
|
|
||||||
// Greet returns a greeting for the given name
|
|
||||||
func (a *App) Greet(name string) string {
|
|
||||||
return fmt.Sprintf("Hello %s, It's show time!", name)
|
|
||||||
}
|
|
||||||
14
Tools/quickly/backend/app.go
Normal file
14
Tools/quickly/backend/app.go
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewApp() *App {
|
||||||
|
return &App{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) Startup(ctx context.Context) {
|
||||||
|
a.ctx = ctx
|
||||||
|
a.loadSettings()
|
||||||
|
}
|
||||||
56
Tools/quickly/backend/database.go
Normal file
56
Tools/quickly/backend/database.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *App) AddDatabaseConfig(name string, targetPath string, modelPackagePath string) error {
|
||||||
|
if name == "" {
|
||||||
|
return fmt.Errorf("database name is empty")
|
||||||
|
}
|
||||||
|
if targetPath == "" {
|
||||||
|
return fmt.Errorf("target path is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, db := range a.settings.Databases {
|
||||||
|
if db.Name == name {
|
||||||
|
return fmt.Errorf("database with name '%s' already exists", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.settings.Databases = append(a.settings.Databases, DatabaseConfig{
|
||||||
|
Name: name,
|
||||||
|
TargetPath: targetPath,
|
||||||
|
ModelPackagePath: modelPackagePath,
|
||||||
|
})
|
||||||
|
return a.saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) RemoveDatabaseConfig(name string) error {
|
||||||
|
for i, db := range a.settings.Databases {
|
||||||
|
if db.Name == name {
|
||||||
|
a.settings.Databases = append(a.settings.Databases[:i], a.settings.Databases[i+1:]...)
|
||||||
|
return a.saveSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("database with name '%s' not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) UpdateDatabaseConfig(oldName string, newName string, targetPath string, modelPackagePath string) error {
|
||||||
|
if newName == "" {
|
||||||
|
return fmt.Errorf("database name is empty")
|
||||||
|
}
|
||||||
|
if targetPath == "" {
|
||||||
|
return fmt.Errorf("target path is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, db := range a.settings.Databases {
|
||||||
|
if db.Name == oldName {
|
||||||
|
a.settings.Databases[i].Name = newName
|
||||||
|
a.settings.Databases[i].TargetPath = targetPath
|
||||||
|
a.settings.Databases[i].ModelPackagePath = modelPackagePath
|
||||||
|
return a.saveSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("database with name '%s' not found", oldName)
|
||||||
|
}
|
||||||
9
Tools/quickly/backend/greet.go
Normal file
9
Tools/quickly/backend/greet.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *App) Greet(name string) string {
|
||||||
|
return fmt.Sprintf("Hello %s, It's show time!", name)
|
||||||
|
}
|
||||||
56
Tools/quickly/backend/helpers.go
Normal file
56
Tools/quickly/backend/helpers.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func copyDir(src string, dst string) error {
|
||||||
|
srcInfo, err := os.Stat(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(dst, srcInfo.Mode()); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
srcPath := filepath.Join(src, entry.Name())
|
||||||
|
dstPath := filepath.Join(dst, entry.Name())
|
||||||
|
|
||||||
|
if entry.IsDir() {
|
||||||
|
if err := copyDir(srcPath, dstPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := copyFile(srcPath, dstPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func copyFile(src string, dst string) error {
|
||||||
|
source, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer source.Close()
|
||||||
|
|
||||||
|
destination, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer destination.Close()
|
||||||
|
|
||||||
|
_, err = destination.ReadFrom(source)
|
||||||
|
return err
|
||||||
|
}
|
||||||
34
Tools/quickly/backend/models.go
Normal file
34
Tools/quickly/backend/models.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DatabaseConfig struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
TargetPath string `json:"targetPath"`
|
||||||
|
ModelPackagePath string `json:"modelPackagePath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectConfig struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Settings struct {
|
||||||
|
Theme string `json:"theme"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
Notifications bool `json:"notifications"`
|
||||||
|
AutoStart bool `json:"autoStart"`
|
||||||
|
MysqlModelPath string `json:"mysqlModelPath"`
|
||||||
|
DefaultQueryPackagePath string `json:"defaultQueryPackagePath"`
|
||||||
|
ModelBasePath string `json:"modelBasePath"`
|
||||||
|
SwaggerDir string `json:"swaggerDir"`
|
||||||
|
Databases []DatabaseConfig `json:"databases"`
|
||||||
|
Projects []ProjectConfig `json:"projects"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
ctx context.Context
|
||||||
|
settings Settings
|
||||||
|
}
|
||||||
171
Tools/quickly/backend/mysql_model.go
Normal file
171
Tools/quickly/backend/mysql_model.go
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/wailsapp/wails/v2/pkg/runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *App) SelectFile(title string, defaultDir string, filter string) (string, error) {
|
||||||
|
return runtime.OpenFileDialog(a.ctx, runtime.OpenDialogOptions{
|
||||||
|
Title: title,
|
||||||
|
DefaultDirectory: defaultDir,
|
||||||
|
Filters: []runtime.FileFilter{{Pattern: filter}},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) SelectDirectory(title string, defaultDir string) (string, error) {
|
||||||
|
return runtime.OpenDirectoryDialog(a.ctx, runtime.OpenDialogOptions{
|
||||||
|
Title: title,
|
||||||
|
DefaultDirectory: defaultDir,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) CheckGenPs1Exists(filePath string) (bool, error) {
|
||||||
|
if filePath == "" {
|
||||||
|
return false, fmt.Errorf("file path is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) ReadGoModModule(filePath string) (string, error) {
|
||||||
|
if filePath == "" {
|
||||||
|
return "", fmt.Errorf("file path is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
dirPath := filepath.Dir(filePath)
|
||||||
|
goModPath := filepath.Join(dirPath, "go.mod")
|
||||||
|
|
||||||
|
content, err := os.ReadFile(goModPath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("go.mod not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
line = strings.TrimSpace(line)
|
||||||
|
if strings.HasPrefix(line, "module ") {
|
||||||
|
moduleName := strings.TrimPrefix(line, "module ")
|
||||||
|
moduleName = strings.TrimSpace(moduleName)
|
||||||
|
return moduleName, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("module declaration not found in go.mod")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) ExecuteGenPs1(genPs1Path string, dbName string, targetPath string, modelPackagePath string) (string, error) {
|
||||||
|
if genPs1Path == "" {
|
||||||
|
return "", fmt.Errorf("gen.ps1 path is empty")
|
||||||
|
}
|
||||||
|
if dbName == "" {
|
||||||
|
return "", fmt.Errorf("database name is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(genPs1Path); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return "", fmt.Errorf("gen.ps1 not found")
|
||||||
|
}
|
||||||
|
return "", fmt.Errorf("error checking gen.ps1: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
dirPath := filepath.Dir(genPs1Path)
|
||||||
|
|
||||||
|
psPath, err := exec.LookPath("powershell.exe")
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("PowerShell not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
args := []string{
|
||||||
|
"-NoProfile",
|
||||||
|
"-ExecutionPolicy", "Bypass",
|
||||||
|
"-Command",
|
||||||
|
fmt.Sprintf("Set-Location -Path '%s'; & .\\gen.ps1 -dbName '%s'", dirPath, dbName),
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd := exec.Command(psPath, args...)
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("script execution failed: %w\nOutput: %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
if targetPath != "" {
|
||||||
|
sourceDir := filepath.Join(dirPath, dbName)
|
||||||
|
if _, err := os.Stat(sourceDir); err == nil {
|
||||||
|
modelSource := filepath.Join(sourceDir, "model")
|
||||||
|
querySource := filepath.Join(sourceDir, "query")
|
||||||
|
|
||||||
|
modelTarget := filepath.Join(targetPath, "model")
|
||||||
|
queryTarget := filepath.Join(targetPath, "query")
|
||||||
|
|
||||||
|
if _, err := os.Stat(modelSource); err == nil {
|
||||||
|
if err := copyDir(modelSource, modelTarget); err != nil {
|
||||||
|
return string(output), fmt.Errorf("failed to copy model directory: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := os.Stat(querySource); err == nil {
|
||||||
|
if err := copyDir(querySource, queryTarget); err != nil {
|
||||||
|
return string(output), fmt.Errorf("failed to copy query directory: %w", err)
|
||||||
|
}
|
||||||
|
if modelPackagePath != "" {
|
||||||
|
if err := replaceImportPaths(queryTarget, dbName, modelPackagePath, a.settings.DefaultQueryPackagePath); err != nil {
|
||||||
|
return string(output), fmt.Errorf("failed to replace import paths: %w", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func replaceImportPaths(queryDir string, dbName string, newPackagePath string, basePath string) error {
|
||||||
|
err := filepath.Walk(queryDir, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.IsDir() && strings.HasSuffix(path, ".gen.go") {
|
||||||
|
if err := replaceImportInFile(path, dbName, newPackagePath, basePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func replaceImportInFile(filePath string, dbName string, newPackagePath string, basePath string) error {
|
||||||
|
content, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if basePath == "" {
|
||||||
|
basePath = "git.hlsq.asia/mmorpg"
|
||||||
|
}
|
||||||
|
|
||||||
|
oldImport := fmt.Sprintf(`"%s/%s/model"`, basePath, dbName)
|
||||||
|
newImport := fmt.Sprintf(`"%s"`, newPackagePath)
|
||||||
|
|
||||||
|
newContent := bytes.ReplaceAll(content, []byte(oldImport), []byte(newImport))
|
||||||
|
|
||||||
|
return os.WriteFile(filePath, newContent, 0644)
|
||||||
|
}
|
||||||
73
Tools/quickly/backend/project.go
Normal file
73
Tools/quickly/backend/project.go
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *App) AddProjectConfig(name string, path string) error {
|
||||||
|
if path == "" {
|
||||||
|
return fmt.Errorf("project path is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if name == "" {
|
||||||
|
name = filepath.Base(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
goModPath := filepath.Join(path, "go.mod")
|
||||||
|
if _, err := os.Stat(goModPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("go.mod file not found in the specified path")
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error checking go.mod file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, project := range a.settings.Projects {
|
||||||
|
if project.Name == name {
|
||||||
|
return fmt.Errorf("project with name '%s' already exists", name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.settings.Projects = append(a.settings.Projects, ProjectConfig{
|
||||||
|
Name: name,
|
||||||
|
Path: path,
|
||||||
|
})
|
||||||
|
return a.saveSettings()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) RemoveProjectConfig(name string) error {
|
||||||
|
for i, project := range a.settings.Projects {
|
||||||
|
if project.Name == name {
|
||||||
|
a.settings.Projects = append(a.settings.Projects[:i], a.settings.Projects[i+1:]...)
|
||||||
|
return a.saveSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("project with name '%s' not found", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) UpdateProjectConfig(oldName string, newName string, path string) error {
|
||||||
|
if newName == "" {
|
||||||
|
return fmt.Errorf("project name is empty")
|
||||||
|
}
|
||||||
|
if path == "" {
|
||||||
|
return fmt.Errorf("project path is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
goModPath := filepath.Join(path, "go.mod")
|
||||||
|
if _, err := os.Stat(goModPath); err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("go.mod file not found in the specified path")
|
||||||
|
}
|
||||||
|
return fmt.Errorf("error checking go.mod file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i, project := range a.settings.Projects {
|
||||||
|
if project.Name == oldName {
|
||||||
|
a.settings.Projects[i].Name = newName
|
||||||
|
a.settings.Projects[i].Path = path
|
||||||
|
return a.saveSettings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fmt.Errorf("project with name '%s' not found", oldName)
|
||||||
|
}
|
||||||
104
Tools/quickly/backend/settings.go
Normal file
104
Tools/quickly/backend/settings.go
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *App) getSettingsPath() (string, error) {
|
||||||
|
configDir, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
appConfigDir := filepath.Join(configDir, "quickly")
|
||||||
|
if err := os.MkdirAll(appConfigDir, 0755); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(appConfigDir, "settings.json"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) loadSettings() {
|
||||||
|
settingsPath, err := a.getSettingsPath()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error getting settings path:", err)
|
||||||
|
a.settings = Settings{
|
||||||
|
Theme: "light",
|
||||||
|
Language: "zh-CN",
|
||||||
|
Notifications: true,
|
||||||
|
AutoStart: false,
|
||||||
|
MysqlModelPath: "",
|
||||||
|
DefaultQueryPackagePath: "",
|
||||||
|
ModelBasePath: "",
|
||||||
|
SwaggerDir: "",
|
||||||
|
Databases: []DatabaseConfig{},
|
||||||
|
Projects: []ProjectConfig{},
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := os.ReadFile(settingsPath)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
a.settings = Settings{
|
||||||
|
Theme: "light",
|
||||||
|
Language: "zh-CN",
|
||||||
|
Notifications: true,
|
||||||
|
AutoStart: false,
|
||||||
|
MysqlModelPath: "",
|
||||||
|
DefaultQueryPackagePath: "",
|
||||||
|
ModelBasePath: "",
|
||||||
|
SwaggerDir: "",
|
||||||
|
Databases: []DatabaseConfig{},
|
||||||
|
Projects: []ProjectConfig{},
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Println("Error reading settings file:", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := json.Unmarshal(data, &a.settings); err != nil {
|
||||||
|
fmt.Println("Error parsing settings file:", err)
|
||||||
|
a.settings = Settings{
|
||||||
|
Theme: "light",
|
||||||
|
Language: "zh-CN",
|
||||||
|
Notifications: true,
|
||||||
|
AutoStart: false,
|
||||||
|
MysqlModelPath: "",
|
||||||
|
DefaultQueryPackagePath: "",
|
||||||
|
ModelBasePath: "",
|
||||||
|
SwaggerDir: "",
|
||||||
|
Databases: []DatabaseConfig{},
|
||||||
|
Projects: []ProjectConfig{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) saveSettings() error {
|
||||||
|
settingsPath, err := a.getSettingsPath()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error getting settings path: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.MarshalIndent(a.settings, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error marshaling settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.WriteFile(settingsPath, data, 0644); err != nil {
|
||||||
|
return fmt.Errorf("error writing settings file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetSettings() Settings {
|
||||||
|
return a.settings
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) SaveSettings(settings Settings) error {
|
||||||
|
a.settings = settings
|
||||||
|
return a.saveSettings()
|
||||||
|
}
|
||||||
239
Tools/quickly/backend/swagger.go
Normal file
239
Tools/quickly/backend/swagger.go
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SwaggerFile struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
ModifiedTime string `json:"modifiedTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SwaggerServer struct {
|
||||||
|
server *http.Server
|
||||||
|
port int
|
||||||
|
running bool
|
||||||
|
mu sync.Mutex
|
||||||
|
}
|
||||||
|
|
||||||
|
var swaggerServer *SwaggerServer
|
||||||
|
var swaggerServerMu sync.Mutex
|
||||||
|
|
||||||
|
func (a *App) GetSwaggerFiles(dirPath string) ([]SwaggerFile, error) {
|
||||||
|
if dirPath == "" {
|
||||||
|
return nil, fmt.Errorf("directory path is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
var files []SwaggerFile
|
||||||
|
|
||||||
|
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.HasSuffix(strings.ToLower(info.Name()), ".swagger.json") {
|
||||||
|
files = append(files, SwaggerFile{
|
||||||
|
Name: info.Name(),
|
||||||
|
Path: path,
|
||||||
|
Size: info.Size(),
|
||||||
|
ModifiedTime: info.ModTime().Format("2006-01-02 15:04:05"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to walk directory: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) ReadSwaggerFile(filePath string) (string, error) {
|
||||||
|
if filePath == "" {
|
||||||
|
return "", fmt.Errorf("file path is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to read file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(content), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) StartSwaggerServer(dirPath string) (string, error) {
|
||||||
|
if dirPath == "" {
|
||||||
|
return "", fmt.Errorf("directory path is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
swaggerServerMu.Lock()
|
||||||
|
defer swaggerServerMu.Unlock()
|
||||||
|
|
||||||
|
if swaggerServer != nil && swaggerServer.running {
|
||||||
|
return fmt.Sprintf("http://localhost:%d", swaggerServer.port), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
port := 8080
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
listener, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
||||||
|
if err == nil {
|
||||||
|
listener.Close()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
port++
|
||||||
|
}
|
||||||
|
|
||||||
|
mux := http.NewServeMux()
|
||||||
|
|
||||||
|
mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
swaggerHTML := `<!DOCTYPE html>
|
||||||
|
<html lang="zh-CN">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Swagger UI</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui.css">
|
||||||
|
<style>
|
||||||
|
html { box-sizing: border-box; overflow: -moz-scrollbars-vertical; overflow-y: scroll; }
|
||||||
|
*, *:before, *:after { box-sizing: inherit; }
|
||||||
|
body { margin: 0; background: #fafafa; }
|
||||||
|
.topbar { display: none; }
|
||||||
|
.swagger-ui .topbar { display: none; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="swagger-ui"></div>
|
||||||
|
<script src="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-bundle.js"></script>
|
||||||
|
<script>
|
||||||
|
window.onload = function() {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const fileParam = urlParams.get('file');
|
||||||
|
const swaggerUrl = fileParam ? '/swagger.json?file=' + encodeURIComponent(fileParam) : '/swagger.json';
|
||||||
|
const ui = SwaggerUIBundle({
|
||||||
|
url: swaggerUrl,
|
||||||
|
dom_id: '#swagger-ui',
|
||||||
|
presets: [
|
||||||
|
SwaggerUIBundle.presets.apis,
|
||||||
|
SwaggerUIBundle.SwaggerUIStandalonePreset
|
||||||
|
],
|
||||||
|
layout: "BaseLayout",
|
||||||
|
deepLinking: true,
|
||||||
|
showExtensions: true,
|
||||||
|
showCommonExtensions: true,
|
||||||
|
docExpansion: "list",
|
||||||
|
filter: true,
|
||||||
|
tryItOutEnabled: true
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>`
|
||||||
|
w.Header().Set("Content-Type", "text/html; charset=utf-8")
|
||||||
|
w.Write([]byte(swaggerHTML))
|
||||||
|
})
|
||||||
|
|
||||||
|
mux.HandleFunc("/swagger.json", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
files, err := a.GetSwaggerFiles(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(files) == 0 {
|
||||||
|
http.Error(w, "No swagger files found", http.StatusNotFound)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileName := r.URL.Query().Get("file")
|
||||||
|
selectedFile := files[0]
|
||||||
|
if fileName != "" {
|
||||||
|
for _, file := range files {
|
||||||
|
if file.Name == fileName {
|
||||||
|
selectedFile = file
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
content, err := os.ReadFile(selectedFile.Path)
|
||||||
|
if err != nil {
|
||||||
|
http.Error(w, err.Error(), http.StatusInternalServerError)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
w.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||||
|
w.Write(content)
|
||||||
|
})
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: fmt.Sprintf(":%d", port),
|
||||||
|
Handler: mux,
|
||||||
|
ReadTimeout: 15 * time.Second,
|
||||||
|
WriteTimeout: 15 * time.Second,
|
||||||
|
}
|
||||||
|
|
||||||
|
swaggerServer = &SwaggerServer{
|
||||||
|
server: server,
|
||||||
|
port: port,
|
||||||
|
running: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||||
|
fmt.Printf("Swagger server error: %v\n", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return fmt.Sprintf("http://localhost:%d", port), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) StopSwaggerServer() error {
|
||||||
|
swaggerServerMu.Lock()
|
||||||
|
defer swaggerServerMu.Unlock()
|
||||||
|
|
||||||
|
if swaggerServer == nil || !swaggerServer.running {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := swaggerServer.server.Close(); err != nil {
|
||||||
|
return fmt.Errorf("failed to stop server: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
swaggerServer.running = false
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) IsSwaggerServerRunning() bool {
|
||||||
|
swaggerServerMu.Lock()
|
||||||
|
defer swaggerServerMu.Unlock()
|
||||||
|
|
||||||
|
if swaggerServer == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return swaggerServer.running
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetSwaggerServerURL() string {
|
||||||
|
swaggerServerMu.Lock()
|
||||||
|
defer swaggerServerMu.Unlock()
|
||||||
|
|
||||||
|
if swaggerServer == nil || !swaggerServer.running {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("http://localhost:%d", swaggerServer.port)
|
||||||
|
}
|
||||||
90
Tools/quickly/backend/update_service_common.go
Normal file
90
Tools/quickly/backend/update_service_common.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package backend
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a *App) UpdateServiceCommon(commitId string, projectNames []string) (string, error) {
|
||||||
|
if commitId == "" {
|
||||||
|
return "", fmt.Errorf("commit id is empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(projectNames) == 0 {
|
||||||
|
return "", fmt.Errorf("no projects selected")
|
||||||
|
}
|
||||||
|
|
||||||
|
var results strings.Builder
|
||||||
|
results.WriteString(fmt.Sprintf("开始更新 Common 版本: %s\n", commitId))
|
||||||
|
results.WriteString(fmt.Sprintf("共选择 %d 个项目\n\n", len(projectNames)))
|
||||||
|
|
||||||
|
for _, projectName := range projectNames {
|
||||||
|
var projectPath string
|
||||||
|
found := false
|
||||||
|
for _, project := range a.settings.Projects {
|
||||||
|
if project.Name == projectName {
|
||||||
|
projectPath = project.Path
|
||||||
|
found = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
results.WriteString(fmt.Sprintf("❌ 项目 '%s' 未找到配置\n\n", projectName))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
results.WriteString(fmt.Sprintf("正在更新项目: %s\n", projectName))
|
||||||
|
results.WriteString(fmt.Sprintf("路径: %s\n", projectPath))
|
||||||
|
|
||||||
|
if err := updateProjectCommonVersion(projectPath, commitId, &results, a.settings.ModelBasePath); err != nil {
|
||||||
|
results.WriteString(fmt.Sprintf("❌ 更新失败: %v\n\n", err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
results.WriteString(fmt.Sprintf("✅ 更新成功\n\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
results.WriteString("所有项目更新完成!")
|
||||||
|
return results.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateProjectCommonVersion(projectPath string, commitId string, results *strings.Builder, moduleName string) error {
|
||||||
|
goPath, err := exec.LookPath("go")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Go not found: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if moduleName == "" {
|
||||||
|
return fmt.Errorf("Go Module Name 未配置,请在设置中填写")
|
||||||
|
}
|
||||||
|
|
||||||
|
results.WriteString("执行: go get -u " + moduleName + "@" + commitId + "\n")
|
||||||
|
|
||||||
|
cmd := exec.Command(goPath, "get", "-u", fmt.Sprintf("%s@%s", moduleName, commitId))
|
||||||
|
cmd.Dir = projectPath
|
||||||
|
|
||||||
|
output, err := cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("go get failed: %w\nOutput: %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
results.WriteString(string(output))
|
||||||
|
results.WriteString("\n")
|
||||||
|
|
||||||
|
results.WriteString("执行: go mod tidy\n")
|
||||||
|
|
||||||
|
cmd = exec.Command(goPath, "mod", "tidy")
|
||||||
|
cmd.Dir = projectPath
|
||||||
|
|
||||||
|
output, err = cmd.CombinedOutput()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("go mod tidy failed: %w\nOutput: %s", err, string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
results.WriteString(string(output))
|
||||||
|
results.WriteString("\n")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
24
Tools/quickly/frontend/package-lock.json
generated
24
Tools/quickly/frontend/package-lock.json
generated
@@ -10,7 +10,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
"element-plus": "^2.13.1",
|
"element-plus": "^2.13.1",
|
||||||
"vue": "^3.2.37"
|
"vue": "^3.2.37",
|
||||||
|
"vue-router": "^4.6.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/types": "^7.18.10",
|
"@babel/types": "^7.18.10",
|
||||||
@@ -276,6 +277,12 @@
|
|||||||
"@vue/shared": "3.5.26"
|
"@vue/shared": "3.5.26"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/@vue/devtools-api": {
|
||||||
|
"version": "6.6.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
|
||||||
|
"integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/@vue/language-core": {
|
"node_modules/@vue/language-core": {
|
||||||
"version": "1.8.27",
|
"version": "1.8.27",
|
||||||
"resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-1.8.27.tgz",
|
"resolved": "https://registry.npmmirror.com/@vue/language-core/-/language-core-1.8.27.tgz",
|
||||||
@@ -1264,6 +1271,21 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/vue-router": {
|
||||||
|
"version": "4.6.4",
|
||||||
|
"resolved": "https://registry.npmmirror.com/vue-router/-/vue-router-4.6.4.tgz",
|
||||||
|
"integrity": "sha512-Hz9q5sa33Yhduglwz6g9skT8OBPii+4bFn88w6J+J4MfEo4KRRpmiNG/hHHkdbRFlLBOqxN8y8gf2Fb0MTUgVg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@vue/devtools-api": "^6.6.4"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/posva"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"vue": "^3.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/vue-template-compiler": {
|
"node_modules/vue-template-compiler": {
|
||||||
"version": "2.7.16",
|
"version": "2.7.16",
|
||||||
"resolved": "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",
|
"resolved": "https://registry.npmmirror.com/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz",
|
||||||
|
|||||||
@@ -11,7 +11,8 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.2",
|
"@element-plus/icons-vue": "^2.3.2",
|
||||||
"element-plus": "^2.13.1",
|
"element-plus": "^2.13.1",
|
||||||
"vue": "^3.2.37"
|
"vue": "^3.2.37",
|
||||||
|
"vue-router": "^4.6.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/types": "^7.18.10",
|
"@babel/types": "^7.18.10",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
b777c503b422e1e0e3f70b49d2291cdc
|
27251dcea746309433392e41407ed6c9
|
||||||
@@ -1,18 +1,88 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import HelloWorld from './components/HelloWorld.vue'
|
import {ref} from 'vue'
|
||||||
|
import {useRoute, useRouter} from 'vue-router'
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const activeMenu = ref('home')
|
||||||
|
|
||||||
|
const menuItems = [
|
||||||
|
{index: 'home', path: '/home', title: '主页'},
|
||||||
|
{index: 'settings', path: '/settings', title: '设置'},
|
||||||
|
{index: 'swagger', path: '/swagger', title: 'Swagger'},
|
||||||
|
{index: 'mysql-model', path: '/mysql-model', title: 'MySQL Model'},
|
||||||
|
{index: 'update-common', path: '/update-common', title: '更新 Common'}
|
||||||
|
]
|
||||||
|
|
||||||
|
function handleMenuSelect(index: string) {
|
||||||
|
activeMenu.value = index
|
||||||
|
const menuItem = menuItems.find(item => item.index === index)
|
||||||
|
if (menuItem) {
|
||||||
|
router.push(menuItem.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
router.afterEach((to) => {
|
||||||
|
const menuItem = menuItems.find(item => item.path === to.path)
|
||||||
|
if (menuItem) {
|
||||||
|
activeMenu.value = menuItem.index
|
||||||
|
}
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<el-container class="app-container">
|
<el-container class="app-container">
|
||||||
<el-header>
|
<el-aside width="200px" class="app-aside">
|
||||||
<div class="header-content">
|
<div class="logo-container">
|
||||||
<img src="./assets/images/logo-universal.png" alt="Wails logo" class="logo"/>
|
<img src="./assets/images/logo-universal.png" alt="logo" class="logo"/>
|
||||||
<h1>Quickly 金牌助手</h1>
|
<span class="app-title">Quickly</span>
|
||||||
</div>
|
</div>
|
||||||
</el-header>
|
<el-menu
|
||||||
<el-main>
|
:default-active="activeMenu"
|
||||||
<HelloWorld/>
|
class="el-menu-vertical"
|
||||||
</el-main>
|
@select="handleMenuSelect"
|
||||||
|
>
|
||||||
|
<el-menu-item index="home">
|
||||||
|
<el-icon>
|
||||||
|
<House/>
|
||||||
|
</el-icon>
|
||||||
|
<span>主页</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="settings">
|
||||||
|
<el-icon>
|
||||||
|
<Setting/>
|
||||||
|
</el-icon>
|
||||||
|
<span>设置</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="swagger">
|
||||||
|
<el-icon>
|
||||||
|
<Document/>
|
||||||
|
</el-icon>
|
||||||
|
<span>Swagger</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="mysql-model">
|
||||||
|
<el-icon>
|
||||||
|
<DataLine/>
|
||||||
|
</el-icon>
|
||||||
|
<span>MySQL Model</span>
|
||||||
|
</el-menu-item>
|
||||||
|
<el-menu-item index="update-common">
|
||||||
|
<el-icon>
|
||||||
|
<Refresh/>
|
||||||
|
</el-icon>
|
||||||
|
<span>更新 Common</span>
|
||||||
|
</el-menu-item>
|
||||||
|
</el-menu>
|
||||||
|
</el-aside>
|
||||||
|
<el-container class="main-container">
|
||||||
|
<el-header class="app-header">
|
||||||
|
<div class="header-title">{{ route.meta.title || 'Quickly 金牌助手' }}</div>
|
||||||
|
</el-header>
|
||||||
|
<el-main class="app-main">
|
||||||
|
<router-view/>
|
||||||
|
</el-main>
|
||||||
|
</el-container>
|
||||||
</el-container>
|
</el-container>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -21,33 +91,74 @@ import HelloWorld from './components/HelloWorld.vue'
|
|||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-header {
|
.app-aside {
|
||||||
background-color: #1b2636;
|
background-color: #1b2636;
|
||||||
color: #fff;
|
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: column;
|
||||||
padding: 0 20px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-content {
|
.logo-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 15px;
|
padding: 20px;
|
||||||
|
gap: 10px;
|
||||||
|
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
.logo {
|
.logo {
|
||||||
width: 40px;
|
width: 32px;
|
||||||
height: 40px;
|
height: 32px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.header-content h1 {
|
.app-title {
|
||||||
margin: 0;
|
color: #fff;
|
||||||
font-size: 20px;
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-vertical {
|
||||||
|
border: none;
|
||||||
|
background-color: transparent;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-vertical .el-menu-item {
|
||||||
|
color: rgba(255, 255, 255, 0.7);
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-vertical .el-menu-item:hover {
|
||||||
|
background-color: rgba(255, 255, 255, 0.1);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.el-menu-vertical .el-menu-item.is-active {
|
||||||
|
background-color: #409eff;
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.main-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-header {
|
||||||
|
background-color: #fff;
|
||||||
|
border-bottom: 1px solid #e4e7ed;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0 20px;
|
||||||
|
height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
font-size: 18px;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
color: #303133;
|
||||||
}
|
}
|
||||||
|
|
||||||
.el-main {
|
.app-main {
|
||||||
background-color: #f5f7fa;
|
background-color: #f5f7fa;
|
||||||
padding: 20px;
|
padding: 0;
|
||||||
|
overflow: auto;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
<script lang="ts" setup>
|
|
||||||
import {reactive} from 'vue'
|
|
||||||
import {Greet} from '../../wailsjs/go/main/App'
|
|
||||||
|
|
||||||
const data = reactive({
|
|
||||||
name: "",
|
|
||||||
resultText: "Please enter your name below 👇",
|
|
||||||
})
|
|
||||||
|
|
||||||
function greet() {
|
|
||||||
Greet(data.name).then(result => {
|
|
||||||
data.resultText = result
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<el-card class="greeting-card">
|
|
||||||
<template #header>
|
|
||||||
<div class="card-header">
|
|
||||||
<span>Greeting Demo</span>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<div class="result">
|
|
||||||
<el-text type="primary" size="large">{{ data.resultText }}</el-text>
|
|
||||||
</div>
|
|
||||||
<div class="input-box">
|
|
||||||
<el-input
|
|
||||||
v-model="data.name"
|
|
||||||
placeholder="Please enter your name"
|
|
||||||
clearable
|
|
||||||
class="input"
|
|
||||||
/>
|
|
||||||
<el-button type="primary" @click="greet">Greet</el-button>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.greeting-card {
|
|
||||||
max-width: 500px;
|
|
||||||
margin: 40px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.card-header {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.result {
|
|
||||||
text-align: center;
|
|
||||||
margin: 30px 0;
|
|
||||||
min-height: 40px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-box {
|
|
||||||
display: flex;
|
|
||||||
gap: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,9 +1,17 @@
|
|||||||
import {createApp} from 'vue'
|
import {createApp} from 'vue'
|
||||||
import ElementPlus from 'element-plus'
|
import ElementPlus from 'element-plus'
|
||||||
import 'element-plus/dist/index.css'
|
import 'element-plus/dist/index.css'
|
||||||
|
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import router from './router'
|
||||||
import './style.css'
|
import './style.css'
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
|
|
||||||
|
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||||
|
app.component(key, component)
|
||||||
|
}
|
||||||
|
|
||||||
app.use(ElementPlus)
|
app.use(ElementPlus)
|
||||||
|
app.use(router)
|
||||||
app.mount('#app')
|
app.mount('#app')
|
||||||
|
|||||||
50
Tools/quickly/frontend/src/router/index.ts
Normal file
50
Tools/quickly/frontend/src/router/index.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import { createRouter, createWebHashHistory, RouteRecordRaw } from 'vue-router'
|
||||||
|
import Home from '../views/Home.vue'
|
||||||
|
import Settings from '../views/Settings.vue'
|
||||||
|
import Swagger from '../views/Swagger.vue'
|
||||||
|
import MySQLModel from '../views/MySQLModel.vue'
|
||||||
|
import UpdateCommon from '../views/UpdateCommon.vue'
|
||||||
|
|
||||||
|
const routes: RouteRecordRaw[] = [
|
||||||
|
{
|
||||||
|
path: '/',
|
||||||
|
redirect: '/home'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/home',
|
||||||
|
name: 'Home',
|
||||||
|
component: Home,
|
||||||
|
meta: { title: '主页' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/settings',
|
||||||
|
name: 'Settings',
|
||||||
|
component: Settings,
|
||||||
|
meta: { title: '设置' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/swagger',
|
||||||
|
name: 'Swagger',
|
||||||
|
component: Swagger,
|
||||||
|
meta: { title: 'Swagger' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/mysql-model',
|
||||||
|
name: 'MySQLModel',
|
||||||
|
component: MySQLModel,
|
||||||
|
meta: { title: 'MySQL Model' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/update-common',
|
||||||
|
name: 'UpdateCommon',
|
||||||
|
component: UpdateCommon,
|
||||||
|
meta: { title: '更新 Common' }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
const router = createRouter({
|
||||||
|
history: createWebHashHistory(),
|
||||||
|
routes
|
||||||
|
})
|
||||||
|
|
||||||
|
export default router
|
||||||
143
Tools/quickly/frontend/src/views/Home.vue
Normal file
143
Tools/quickly/frontend/src/views/Home.vue
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import {reactive} from 'vue'
|
||||||
|
|
||||||
|
const features = reactive([
|
||||||
|
{
|
||||||
|
title: '更新 Common',
|
||||||
|
description: '批量更新多个项目中 Common 库的版本,支持自定义 Go Module Name,自动执行 go get 和 go mod tidy 命令。',
|
||||||
|
icon: '🔄',
|
||||||
|
path: '/update-common'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'MySQL Model',
|
||||||
|
description: '执行 gen.ps1 脚本生成 model 和 query 文件,自动替换 import 路径,支持多数据库配置管理。',
|
||||||
|
icon: '🗄️',
|
||||||
|
path: '/mysql-model'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'Swagger',
|
||||||
|
description: '配置 Swagger 目录,自动扫描所有 .swagger.json 文件,启动本地 HTTP 服务查看 API 文档。',
|
||||||
|
icon: '📚',
|
||||||
|
path: '/swagger'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '设置',
|
||||||
|
description: '自定义主题、语言、通知等应用设置,管理全局配置。',
|
||||||
|
icon: '⚙️',
|
||||||
|
path: '/settings'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="home-container">
|
||||||
|
<div class="welcome-section">
|
||||||
|
<h1 class="welcome-title">欢迎使用 Quickly</h1>
|
||||||
|
<p class="welcome-subtitle">高效开发工具,提升工作效率</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="features-section">
|
||||||
|
<h2 class="section-title">功能介绍</h2>
|
||||||
|
<div class="features-grid">
|
||||||
|
<el-card v-for="(feature, index) in features" :key="index" class="feature-card">
|
||||||
|
<div class="feature-icon">{{ feature.icon }}</div>
|
||||||
|
<h3 class="feature-title">{{ feature.title }}</h3>
|
||||||
|
<p class="feature-description">{{ feature.description }}</p>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.home-container {
|
||||||
|
padding: 20px;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-section {
|
||||||
|
text-align: center;
|
||||||
|
margin: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-title {
|
||||||
|
font-size: 48px;
|
||||||
|
font-weight: 700;
|
||||||
|
margin: 0 0 10px 0;
|
||||||
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.welcome-subtitle {
|
||||||
|
font-size: 18px;
|
||||||
|
color: #666;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features-section {
|
||||||
|
margin: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin: 0 0 30px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.features-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card {
|
||||||
|
text-align: center;
|
||||||
|
transition: transform 0.3s, box-shadow 0.3s;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 30px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-card:hover {
|
||||||
|
transform: translateY(-5px);
|
||||||
|
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-icon {
|
||||||
|
font-size: 64px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-title {
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0 0 15px 0;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-description {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-card {
|
||||||
|
margin: 40px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-content {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
431
Tools/quickly/frontend/src/views/MySQLModel.vue
Normal file
431
Tools/quickly/frontend/src/views/MySQLModel.vue
Normal file
@@ -0,0 +1,431 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import {onMounted, reactive, ref} from 'vue'
|
||||||
|
import {ElMessage} from 'element-plus'
|
||||||
|
import {
|
||||||
|
AddDatabaseConfig,
|
||||||
|
CheckGenPs1Exists,
|
||||||
|
ExecuteGenPs1,
|
||||||
|
GetSettings,
|
||||||
|
ReadGoModModule,
|
||||||
|
RemoveDatabaseConfig,
|
||||||
|
SaveSettings,
|
||||||
|
SelectDirectory,
|
||||||
|
SelectFile,
|
||||||
|
UpdateDatabaseConfig
|
||||||
|
} from '../../wailsjs/go/backend/App'
|
||||||
|
|
||||||
|
interface DatabaseConfig {
|
||||||
|
name: string
|
||||||
|
targetPath: string
|
||||||
|
modelPackagePath: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AppSettings {
|
||||||
|
theme: string
|
||||||
|
language: string
|
||||||
|
notifications: boolean
|
||||||
|
autoStart: boolean
|
||||||
|
mysqlModelPath: string
|
||||||
|
defaultQueryPackagePath: string
|
||||||
|
databases: DatabaseConfig[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = reactive<AppSettings>({
|
||||||
|
theme: 'light',
|
||||||
|
language: 'zh-CN',
|
||||||
|
notifications: true,
|
||||||
|
autoStart: false,
|
||||||
|
mysqlModelPath: '',
|
||||||
|
defaultQueryPackagePath: '',
|
||||||
|
databases: []
|
||||||
|
})
|
||||||
|
|
||||||
|
const dbForm = reactive<DatabaseConfig>({
|
||||||
|
name: '',
|
||||||
|
targetPath: '',
|
||||||
|
modelPackagePath: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const scriptForm = reactive({
|
||||||
|
selectedDbName: '',
|
||||||
|
output: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const isExecuting = ref(false)
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const editingIndex = ref(-1)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const loadedSettings = await GetSettings() as any
|
||||||
|
settings.theme = loadedSettings.theme
|
||||||
|
settings.language = loadedSettings.language
|
||||||
|
settings.notifications = loadedSettings.notifications
|
||||||
|
settings.autoStart = loadedSettings.autoStart
|
||||||
|
settings.mysqlModelPath = loadedSettings.mysqlModelPath
|
||||||
|
settings.defaultQueryPackagePath = loadedSettings.defaultQueryPackagePath || ''
|
||||||
|
if (loadedSettings.databases) {
|
||||||
|
settings.databases = loadedSettings.databases
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load settings:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function validatePath(path: string): Promise<boolean> {
|
||||||
|
if (!path) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const exists = await CheckGenPs1Exists(path)
|
||||||
|
if (!exists) {
|
||||||
|
ElMessage.warning('所选文件不是有效的 gen.ps1 文件')
|
||||||
|
}
|
||||||
|
return exists
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to check gen.ps1:', error)
|
||||||
|
ElMessage.error('检查文件时出错')
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectMysqlModelPath() {
|
||||||
|
try {
|
||||||
|
const path = await SelectFile('选择 gen.ps1 文件', settings.mysqlModelPath, '*.ps1')
|
||||||
|
if (path) {
|
||||||
|
const isValid = await validatePath(path)
|
||||||
|
if (isValid) {
|
||||||
|
settings.mysqlModelPath = path
|
||||||
|
|
||||||
|
try {
|
||||||
|
const modulePath = await ReadGoModModule(path)
|
||||||
|
if (modulePath) {
|
||||||
|
settings.defaultQueryPackagePath = modulePath
|
||||||
|
ElMessage.success(`已自动读取 go.mod 中的 module: ${modulePath}`)
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to read go.mod:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to select file:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handlePathChange() {
|
||||||
|
if (settings.mysqlModelPath) {
|
||||||
|
await validatePath(settings.mysqlModelPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveSettings() {
|
||||||
|
if (!settings.mysqlModelPath) {
|
||||||
|
ElMessage.warning('请先设置 MySQL Model 路径')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValid = await validatePath(settings.mysqlModelPath)
|
||||||
|
if (!isValid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const currentSettings = await GetSettings() as any
|
||||||
|
currentSettings.mysqlModelPath = settings.mysqlModelPath
|
||||||
|
currentSettings.defaultQueryPackagePath = settings.defaultQueryPackagePath
|
||||||
|
currentSettings.databases = settings.databases
|
||||||
|
await SaveSettings(currentSettings)
|
||||||
|
ElMessage.success('设置保存成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save settings:', error)
|
||||||
|
ElMessage.error('设置保存失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function selectTargetPath() {
|
||||||
|
try {
|
||||||
|
const path = await SelectDirectory('选择生成文件目标路径', dbForm.targetPath)
|
||||||
|
if (path) {
|
||||||
|
dbForm.targetPath = path
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to select directory:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAddDialog() {
|
||||||
|
editingIndex.value = -1
|
||||||
|
dbForm.name = ''
|
||||||
|
dbForm.targetPath = ''
|
||||||
|
dbForm.modelPackagePath = ''
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEditDialog(index: number) {
|
||||||
|
editingIndex.value = index
|
||||||
|
const db = settings.databases[index]
|
||||||
|
dbForm.name = db.name
|
||||||
|
dbForm.targetPath = db.targetPath
|
||||||
|
dbForm.modelPackagePath = db.modelPackagePath || ''
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleAddDb() {
|
||||||
|
if (!dbForm.name) {
|
||||||
|
ElMessage.warning('请输入数据库名称')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!dbForm.targetPath) {
|
||||||
|
ElMessage.warning('请选择目标路径')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (editingIndex.value === -1) {
|
||||||
|
await AddDatabaseConfig(dbForm.name, dbForm.targetPath, dbForm.modelPackagePath)
|
||||||
|
ElMessage.success('添加成功')
|
||||||
|
} else {
|
||||||
|
await UpdateDatabaseConfig(settings.databases[editingIndex.value].name, dbForm.name, dbForm.targetPath, dbForm.modelPackagePath)
|
||||||
|
ElMessage.success('更新成功')
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
|
||||||
|
const loadedSettings = await GetSettings() as any
|
||||||
|
settings.theme = loadedSettings.theme
|
||||||
|
settings.language = loadedSettings.language
|
||||||
|
settings.notifications = loadedSettings.notifications
|
||||||
|
settings.autoStart = loadedSettings.autoStart
|
||||||
|
settings.mysqlModelPath = loadedSettings.mysqlModelPath
|
||||||
|
settings.defaultQueryPackagePath = loadedSettings.defaultQueryPackagePath || ''
|
||||||
|
if (loadedSettings.databases) {
|
||||||
|
settings.databases = loadedSettings.databases
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save database config:', error)
|
||||||
|
ElMessage.error('保存失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDeleteDb(index: number) {
|
||||||
|
const db = settings.databases[index]
|
||||||
|
try {
|
||||||
|
await RemoveDatabaseConfig(db.name)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
|
||||||
|
const loadedSettings = await GetSettings() as any
|
||||||
|
settings.theme = loadedSettings.theme
|
||||||
|
settings.language = loadedSettings.language
|
||||||
|
settings.notifications = loadedSettings.notifications
|
||||||
|
settings.autoStart = loadedSettings.autoStart
|
||||||
|
settings.mysqlModelPath = loadedSettings.mysqlModelPath
|
||||||
|
if (loadedSettings.databases) {
|
||||||
|
settings.databases = loadedSettings.databases
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to delete database config:', error)
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function executeScript() {
|
||||||
|
if (!settings.mysqlModelPath) {
|
||||||
|
ElMessage.warning('请先设置 MySQL Model 路径')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!scriptForm.selectedDbName) {
|
||||||
|
ElMessage.warning('请选择要执行的数据库')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const isValid = await validatePath(settings.mysqlModelPath)
|
||||||
|
if (!isValid) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const selectedDb = settings.databases.find(db => db.name === scriptForm.selectedDbName)
|
||||||
|
if (!selectedDb) {
|
||||||
|
ElMessage.warning('未找到数据库配置')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isExecuting.value = true
|
||||||
|
scriptForm.output = '正在执行脚本...'
|
||||||
|
|
||||||
|
try {
|
||||||
|
const output = await ExecuteGenPs1(settings.mysqlModelPath, scriptForm.selectedDbName, selectedDb.targetPath, selectedDb.modelPackagePath || '')
|
||||||
|
scriptForm.output = output + '\n\n文件已复制到目标路径: ' + selectedDb.targetPath
|
||||||
|
ElMessage.success('脚本执行成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to execute script:', error)
|
||||||
|
scriptForm.output = `执行失败: ${error}`
|
||||||
|
ElMessage.error('脚本执行失败')
|
||||||
|
} finally {
|
||||||
|
isExecuting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="mysql-model-container">
|
||||||
|
<el-card class="script-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>生成 model&query 文件</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form label-width="140px" class="script-form">
|
||||||
|
<el-form-item label="选择数据库">
|
||||||
|
<el-select
|
||||||
|
v-model="scriptForm.selectedDbName"
|
||||||
|
placeholder="请选择要执行的数据库"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="db in settings.databases"
|
||||||
|
:key="db.name"
|
||||||
|
:label="db.name"
|
||||||
|
:value="db.name"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="executeScript"
|
||||||
|
:loading="isExecuting"
|
||||||
|
>
|
||||||
|
执行脚本
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="执行输出">
|
||||||
|
<el-input
|
||||||
|
v-model="scriptForm.output"
|
||||||
|
type="textarea"
|
||||||
|
:rows="10"
|
||||||
|
placeholder="脚本执行结果将显示在这里"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="mysql-model-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>MySQL Model 设置</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form label-width="140px" class="settings-form">
|
||||||
|
<el-form-item label="gen.ps1 路径">
|
||||||
|
<el-input
|
||||||
|
v-model="settings.mysqlModelPath"
|
||||||
|
placeholder="请输入或选择 gen.ps1 文件路径"
|
||||||
|
clearable
|
||||||
|
@change="handlePathChange"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="selectMysqlModelPath">选择文件</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Query包名">
|
||||||
|
<el-input
|
||||||
|
v-model="settings.defaultQueryPackagePath"
|
||||||
|
placeholder="请输入默认 Query 包名,例如:git.hlsq.asia/mmorpg/service-user/internal/dao/model"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="saveSettings">保存设置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="database-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>数据库配置管理</span>
|
||||||
|
<el-button type="primary" size="small" @click="openAddDialog">添加配置</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-table :data="settings.databases" style="width: 100%">
|
||||||
|
<el-table-column prop="name" label="数据库名称"/>
|
||||||
|
<el-table-column prop="targetPath" label="目标路径" show-overflow-tooltip/>
|
||||||
|
<el-table-column prop="modelPackagePath" label="Query包名" show-overflow-tooltip/>
|
||||||
|
<el-table-column label="操作" width="200">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" @click="openEditDialog(scope.$index)">编辑</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="handleDeleteDb(scope.$index)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="editingIndex === -1 ? '添加数据库配置' : '编辑数据库配置'"
|
||||||
|
width="500px"
|
||||||
|
>
|
||||||
|
<el-form label-width="120px">
|
||||||
|
<el-form-item label="数据库名称">
|
||||||
|
<el-input
|
||||||
|
v-model="dbForm.name"
|
||||||
|
placeholder="请输入数据库名称,例如:user_db"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="目标路径">
|
||||||
|
<el-input
|
||||||
|
v-model="dbForm.targetPath"
|
||||||
|
placeholder="请输入或选择生成文件的目标路径"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="selectTargetPath">选择文件夹</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Query包名">
|
||||||
|
<el-input
|
||||||
|
v-model="dbForm.modelPackagePath"
|
||||||
|
placeholder="请输入package name,例如:git.hlsq.asia/mmorpg/service-user/internal/dao/model"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleAddDb">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.mysql-model-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mysql-model-card,
|
||||||
|
.database-card,
|
||||||
|
.script-card {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-form,
|
||||||
|
.script-form {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
114
Tools/quickly/frontend/src/views/Settings.vue
Normal file
114
Tools/quickly/frontend/src/views/Settings.vue
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import {onMounted, reactive} from 'vue'
|
||||||
|
import {ElMessage} from 'element-plus'
|
||||||
|
import {GetSettings, SaveSettings} from '../../wailsjs/go/backend/App'
|
||||||
|
|
||||||
|
interface DatabaseConfig {
|
||||||
|
name: string
|
||||||
|
targetPath: string
|
||||||
|
modelPackagePath: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AppSettings {
|
||||||
|
theme: string
|
||||||
|
language: string
|
||||||
|
notifications: boolean
|
||||||
|
autoStart: boolean
|
||||||
|
databases?: DatabaseConfig[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = reactive<AppSettings>({
|
||||||
|
theme: 'light',
|
||||||
|
language: 'zh-CN',
|
||||||
|
notifications: true,
|
||||||
|
autoStart: false
|
||||||
|
})
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const loadedSettings = await GetSettings() as any
|
||||||
|
settings.theme = loadedSettings.theme
|
||||||
|
settings.language = loadedSettings.language
|
||||||
|
settings.notifications = loadedSettings.notifications
|
||||||
|
settings.autoStart = loadedSettings.autoStart
|
||||||
|
if (loadedSettings.databases) {
|
||||||
|
settings.databases = loadedSettings.databases
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load settings:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function saveSettings() {
|
||||||
|
try {
|
||||||
|
const currentSettings = await GetSettings() as any
|
||||||
|
currentSettings.theme = settings.theme
|
||||||
|
currentSettings.language = settings.language
|
||||||
|
currentSettings.notifications = settings.notifications
|
||||||
|
currentSettings.autoStart = settings.autoStart
|
||||||
|
await SaveSettings(currentSettings)
|
||||||
|
ElMessage.success('设置保存成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save settings:', error)
|
||||||
|
ElMessage.error('设置保存失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="settings-container">
|
||||||
|
<el-card class="settings-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>设置</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form label-width="120px" class="settings-form">
|
||||||
|
<el-form-item label="主题">
|
||||||
|
<el-select v-model="settings.theme" placeholder="选择主题">
|
||||||
|
<el-option label="浅色" value="light"/>
|
||||||
|
<el-option label="深色" value="dark"/>
|
||||||
|
<el-option label="跟随系统" value="auto"/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="语言">
|
||||||
|
<el-select v-model="settings.language" placeholder="选择语言">
|
||||||
|
<el-option label="简体中文" value="zh-CN"/>
|
||||||
|
<el-option label="English" value="en-US"/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="通知">
|
||||||
|
<el-switch v-model="settings.notifications"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="开机自启">
|
||||||
|
<el-switch v-model="settings.autoStart"/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="saveSettings">保存设置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.settings-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-card {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-form {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
349
Tools/quickly/frontend/src/views/Swagger.vue
Normal file
349
Tools/quickly/frontend/src/views/Swagger.vue
Normal file
@@ -0,0 +1,349 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import {onMounted, reactive, ref} from 'vue'
|
||||||
|
import {ElMessage} from 'element-plus'
|
||||||
|
import {
|
||||||
|
GetSettings,
|
||||||
|
GetSwaggerFiles,
|
||||||
|
GetSwaggerServerURL,
|
||||||
|
IsSwaggerServerRunning,
|
||||||
|
ReadSwaggerFile,
|
||||||
|
SaveSettings,
|
||||||
|
SelectDirectory,
|
||||||
|
StartSwaggerServer,
|
||||||
|
StopSwaggerServer
|
||||||
|
} from '../../wailsjs/go/backend/App'
|
||||||
|
|
||||||
|
interface SwaggerFile {
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
size: number
|
||||||
|
modifiedTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AppSettings {
|
||||||
|
theme: string
|
||||||
|
language: string
|
||||||
|
notifications: boolean
|
||||||
|
autoStart: boolean
|
||||||
|
swaggerDir: string
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = reactive<AppSettings>({
|
||||||
|
theme: 'light',
|
||||||
|
language: 'zh-CN',
|
||||||
|
notifications: true,
|
||||||
|
autoStart: false,
|
||||||
|
swaggerDir: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const swaggerFiles = ref<SwaggerFile[]>([])
|
||||||
|
const selectedFile = ref<SwaggerFile | null>(null)
|
||||||
|
const selectedSwaggerFile = ref<SwaggerFile | null>(null)
|
||||||
|
const fileContent = ref('')
|
||||||
|
const isLoading = ref(false)
|
||||||
|
const serverURL = ref('')
|
||||||
|
const isServerRunning = ref(false)
|
||||||
|
|
||||||
|
async function selectSwaggerDir() {
|
||||||
|
try {
|
||||||
|
const path = await SelectDirectory('选择 Swagger 目录', settings.swaggerDir)
|
||||||
|
if (path) {
|
||||||
|
settings.swaggerDir = path
|
||||||
|
await loadSwaggerFiles()
|
||||||
|
await saveSettings()
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to select directory:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadSwaggerFiles() {
|
||||||
|
if (!settings.swaggerDir) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isLoading.value = true
|
||||||
|
try {
|
||||||
|
const files = await GetSwaggerFiles(settings.swaggerDir)
|
||||||
|
swaggerFiles.value = files
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load swagger files:', error)
|
||||||
|
ElMessage.error('加载 Swagger 文件失败')
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveSettings() {
|
||||||
|
try {
|
||||||
|
const currentSettings = await GetSettings() as any
|
||||||
|
currentSettings.swaggerDir = settings.swaggerDir
|
||||||
|
await SaveSettings(currentSettings)
|
||||||
|
ElMessage.success('设置保存成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save settings:', error)
|
||||||
|
ElMessage.error('设置保存失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function viewFile(file: SwaggerFile) {
|
||||||
|
selectedFile.value = file
|
||||||
|
isLoading.value = true
|
||||||
|
try {
|
||||||
|
const content = await ReadSwaggerFile(file.path)
|
||||||
|
fileContent.value = content
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to read swagger file:', error)
|
||||||
|
ElMessage.error('读取文件失败')
|
||||||
|
} finally {
|
||||||
|
isLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatFileSize(bytes: number): string {
|
||||||
|
if (bytes < 1024) {
|
||||||
|
return bytes + ' B'
|
||||||
|
} else if (bytes < 1024 * 1024) {
|
||||||
|
return (bytes / 1024).toFixed(2) + ' KB'
|
||||||
|
} else {
|
||||||
|
return (bytes / (1024 * 1024)).toFixed(2) + ' MB'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function startSwaggerServer() {
|
||||||
|
if (!settings.swaggerDir) {
|
||||||
|
ElMessage.warning('请先配置 Swagger 目录')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const url = await StartSwaggerServer(settings.swaggerDir)
|
||||||
|
serverURL.value = url
|
||||||
|
isServerRunning.value = true
|
||||||
|
if (swaggerFiles.value.length > 0) {
|
||||||
|
selectedSwaggerFile.value = swaggerFiles.value[0]
|
||||||
|
}
|
||||||
|
ElMessage.success(`Swagger 服务已启动: ${url}`)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to start swagger server:', error)
|
||||||
|
ElMessage.error('启动 Swagger 服务失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function stopSwaggerServer() {
|
||||||
|
try {
|
||||||
|
await StopSwaggerServer()
|
||||||
|
serverURL.value = ''
|
||||||
|
isServerRunning.value = false
|
||||||
|
ElMessage.success('Swagger 服务已停止')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to stop swagger server:', error)
|
||||||
|
ElMessage.error('停止 Swagger 服务失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function checkServerStatus() {
|
||||||
|
try {
|
||||||
|
const running = await IsSwaggerServerRunning()
|
||||||
|
isServerRunning.value = running
|
||||||
|
if (running) {
|
||||||
|
const url = await GetSwaggerServerURL()
|
||||||
|
serverURL.value = url
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to check server status:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openSwaggerURL() {
|
||||||
|
if (serverURL.value) {
|
||||||
|
window.open(serverURL.value, '_blank')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateServerURL() {
|
||||||
|
if (serverURL.value && selectedSwaggerFile.value) {
|
||||||
|
const baseUrl = serverURL.value.split('?')[0]
|
||||||
|
serverURL.value = `${baseUrl}?file=${encodeURIComponent(selectedSwaggerFile.value.name)}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const loadedSettings = await GetSettings() as any
|
||||||
|
settings.theme = loadedSettings.theme
|
||||||
|
settings.language = loadedSettings.language
|
||||||
|
settings.notifications = loadedSettings.notifications
|
||||||
|
settings.autoStart = loadedSettings.autoStart
|
||||||
|
settings.swaggerDir = loadedSettings.swaggerDir || ''
|
||||||
|
if (settings.swaggerDir) {
|
||||||
|
await loadSwaggerFiles()
|
||||||
|
}
|
||||||
|
await checkServerStatus()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load settings:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="swagger-container">
|
||||||
|
<el-card class="settings-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>Swagger 设置</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form label-width="140px" class="settings-form">
|
||||||
|
<el-form-item label="Swagger 目录">
|
||||||
|
<el-input
|
||||||
|
v-model="settings.swaggerDir"
|
||||||
|
placeholder="请输入或选择 Swagger 文件目录"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="selectSwaggerDir">选择文件夹</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="Swagger 服务">
|
||||||
|
<div class="server-controls">
|
||||||
|
<el-button
|
||||||
|
v-if="!isServerRunning"
|
||||||
|
type="primary"
|
||||||
|
@click="startSwaggerServer"
|
||||||
|
>
|
||||||
|
启动服务
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
v-else
|
||||||
|
type="danger"
|
||||||
|
@click="stopSwaggerServer"
|
||||||
|
>
|
||||||
|
停止服务
|
||||||
|
</el-button>
|
||||||
|
<el-select
|
||||||
|
v-if="isServerRunning && swaggerFiles.length > 1"
|
||||||
|
v-model="selectedSwaggerFile"
|
||||||
|
placeholder="选择 Swagger 文件"
|
||||||
|
class="file-select"
|
||||||
|
@change="updateServerURL"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="file in swaggerFiles"
|
||||||
|
:key="file.name"
|
||||||
|
:label="file.name"
|
||||||
|
:value="file"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
<el-input
|
||||||
|
v-if="serverURL"
|
||||||
|
v-model="serverURL"
|
||||||
|
readonly
|
||||||
|
class="server-url-input"
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="openSwaggerURL">打开</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="files-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>Swagger 文件列表</span>
|
||||||
|
<el-button type="primary" size="small" @click="loadSwaggerFiles" :loading="isLoading">刷新</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-table :data="swaggerFiles" style="width: 100%" v-loading="isLoading">
|
||||||
|
<el-table-column prop="name" label="文件名" show-overflow-tooltip/>
|
||||||
|
<el-table-column prop="size" label="大小" width="120">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ formatFileSize(scope.row.size) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="modifiedTime" label="修改时间" width="180"/>
|
||||||
|
<el-table-column label="操作" width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" @click="viewFile(scope.row)">查看</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-empty v-if="swaggerFiles.length === 0 && !isLoading" description="暂无 Swagger 文件"/>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="content-card" v-if="selectedFile">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>{{ selectedFile.name }}</span>
|
||||||
|
<el-button size="small" @click="selectedFile = null">关闭</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div class="file-content" v-loading="isLoading">
|
||||||
|
<pre>{{ fileContent }}</pre>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.swagger-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.files-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-form {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-controls {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.server-url-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-select {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-content {
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-content pre {
|
||||||
|
margin: 0;
|
||||||
|
padding: 15px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 1.5;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
327
Tools/quickly/frontend/src/views/UpdateCommon.vue
Normal file
327
Tools/quickly/frontend/src/views/UpdateCommon.vue
Normal file
@@ -0,0 +1,327 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import {onMounted, reactive, ref} from 'vue'
|
||||||
|
import {ElMessage} from 'element-plus'
|
||||||
|
import {
|
||||||
|
SelectDirectory,
|
||||||
|
GetSettings,
|
||||||
|
AddProjectConfig,
|
||||||
|
RemoveProjectConfig,
|
||||||
|
UpdateProjectConfig,
|
||||||
|
UpdateServiceCommon,
|
||||||
|
SaveSettings
|
||||||
|
} from '../../wailsjs/go/backend/App'
|
||||||
|
|
||||||
|
interface ProjectConfig {
|
||||||
|
name: string
|
||||||
|
path: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface AppSettings {
|
||||||
|
theme: string
|
||||||
|
language: string
|
||||||
|
notifications: boolean
|
||||||
|
autoStart: boolean
|
||||||
|
modelBasePath: string
|
||||||
|
databases?: any[]
|
||||||
|
projects?: ProjectConfig[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = reactive<AppSettings>({
|
||||||
|
theme: 'light',
|
||||||
|
language: 'zh-CN',
|
||||||
|
notifications: true,
|
||||||
|
autoStart: false,
|
||||||
|
modelBasePath: '',
|
||||||
|
projects: []
|
||||||
|
})
|
||||||
|
|
||||||
|
const projectForm = reactive<ProjectConfig>({
|
||||||
|
name: '',
|
||||||
|
path: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const updateForm = reactive({
|
||||||
|
commitId: '',
|
||||||
|
selectedProjects: [] as string[]
|
||||||
|
})
|
||||||
|
|
||||||
|
const isUpdating = ref(false)
|
||||||
|
const output = ref('')
|
||||||
|
const projectDialogVisible = ref(false)
|
||||||
|
const editingProjectIndex = ref(-1)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
try {
|
||||||
|
const loadedSettings = await GetSettings() as any
|
||||||
|
settings.theme = loadedSettings.theme
|
||||||
|
settings.language = loadedSettings.language
|
||||||
|
settings.notifications = loadedSettings.notifications
|
||||||
|
settings.autoStart = loadedSettings.autoStart
|
||||||
|
settings.modelBasePath = loadedSettings.modelBasePath || ''
|
||||||
|
if (loadedSettings.projects) {
|
||||||
|
settings.projects = loadedSettings.projects
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to load settings:', error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
async function selectProjectPath() {
|
||||||
|
try {
|
||||||
|
const path = await SelectDirectory('选择项目路径', projectForm.path)
|
||||||
|
if (path) {
|
||||||
|
projectForm.path = path
|
||||||
|
const pathParts = path.replace(/\\/g, '/').split('/')
|
||||||
|
projectForm.name = pathParts[pathParts.length - 1] || ''
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to select directory:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openAddProjectDialog() {
|
||||||
|
editingProjectIndex.value = -1
|
||||||
|
projectForm.name = ''
|
||||||
|
projectForm.path = ''
|
||||||
|
projectDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
function openEditProjectDialog(index: number) {
|
||||||
|
editingProjectIndex.value = index
|
||||||
|
const project = settings.projects![index]
|
||||||
|
projectForm.name = project.name
|
||||||
|
projectForm.path = project.path
|
||||||
|
projectDialogVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleAddProject() {
|
||||||
|
if (!projectForm.name) {
|
||||||
|
ElMessage.warning('请输入项目名称')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!projectForm.path) {
|
||||||
|
ElMessage.warning('请选择项目路径')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (editingProjectIndex.value === -1) {
|
||||||
|
await AddProjectConfig(projectForm.name, projectForm.path)
|
||||||
|
ElMessage.success('添加成功')
|
||||||
|
} else {
|
||||||
|
await UpdateProjectConfig(settings.projects![editingProjectIndex.value].name, projectForm.name, projectForm.path)
|
||||||
|
ElMessage.success('更新成功')
|
||||||
|
}
|
||||||
|
projectDialogVisible.value = false
|
||||||
|
|
||||||
|
const loadedSettings = await GetSettings() as any
|
||||||
|
settings.theme = loadedSettings.theme
|
||||||
|
settings.language = loadedSettings.language
|
||||||
|
settings.notifications = loadedSettings.notifications
|
||||||
|
settings.autoStart = loadedSettings.autoStart
|
||||||
|
settings.modelBasePath = loadedSettings.modelBasePath || ''
|
||||||
|
if (loadedSettings.projects) {
|
||||||
|
settings.projects = loadedSettings.projects
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save project config:', error)
|
||||||
|
ElMessage.error('保存失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDeleteProject(index: number) {
|
||||||
|
const project = settings.projects![index]
|
||||||
|
try {
|
||||||
|
await RemoveProjectConfig(project.name)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
|
||||||
|
const loadedSettings = await GetSettings() as any
|
||||||
|
settings.theme = loadedSettings.theme
|
||||||
|
settings.language = loadedSettings.language
|
||||||
|
settings.notifications = loadedSettings.notifications
|
||||||
|
settings.autoStart = loadedSettings.autoStart
|
||||||
|
settings.modelBasePath = loadedSettings.modelBasePath || ''
|
||||||
|
if (loadedSettings.projects) {
|
||||||
|
settings.projects = loadedSettings.projects
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to delete project config:', error)
|
||||||
|
ElMessage.error('删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateCommonVersion() {
|
||||||
|
if (!updateForm.commitId) {
|
||||||
|
ElMessage.warning('请输入 Commit ID')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (updateForm.selectedProjects.length === 0) {
|
||||||
|
ElMessage.warning('请至少选择一个项目')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isUpdating.value = true
|
||||||
|
output.value = '正在更新 Common 版本...\n'
|
||||||
|
|
||||||
|
try {
|
||||||
|
const result = await UpdateServiceCommon(updateForm.commitId, updateForm.selectedProjects)
|
||||||
|
output.value = result
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to update common version:', error)
|
||||||
|
output.value = `更新失败: ${error}`
|
||||||
|
} finally {
|
||||||
|
isUpdating.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveSettings() {
|
||||||
|
try {
|
||||||
|
const currentSettings = await GetSettings() as any
|
||||||
|
currentSettings.modelBasePath = settings.modelBasePath
|
||||||
|
currentSettings.projects = settings.projects
|
||||||
|
await SaveSettings(currentSettings)
|
||||||
|
ElMessage.success('设置保存成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to save settings:', error)
|
||||||
|
ElMessage.error('设置保存失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="update-common-container">
|
||||||
|
<el-card class="update-version-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>更新 Common 版本</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form label-width="140px" class="settings-form">
|
||||||
|
<el-form-item label="Commit ID">
|
||||||
|
<el-input
|
||||||
|
v-model="updateForm.commitId"
|
||||||
|
placeholder="请输入 Commit ID"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="选择项目">
|
||||||
|
<el-checkbox-group v-model="updateForm.selectedProjects">
|
||||||
|
<el-checkbox
|
||||||
|
v-for="project in settings.projects"
|
||||||
|
:key="project.name"
|
||||||
|
:label="project.name"
|
||||||
|
>
|
||||||
|
{{ project.name }}
|
||||||
|
</el-checkbox>
|
||||||
|
</el-checkbox-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="updateCommonVersion"
|
||||||
|
:loading="isUpdating"
|
||||||
|
>
|
||||||
|
更新 Common 版本
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="执行输出">
|
||||||
|
<el-input
|
||||||
|
v-model="output"
|
||||||
|
type="textarea"
|
||||||
|
:rows="10"
|
||||||
|
placeholder="更新结果将显示在这里"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="project-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>项目配置管理</span>
|
||||||
|
<el-button type="primary" size="small" @click="openAddProjectDialog">添加配置</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-form label-width="140px" class="settings-form">
|
||||||
|
<el-form-item label="Go Module Name">
|
||||||
|
<el-input
|
||||||
|
v-model="settings.modelBasePath"
|
||||||
|
placeholder="请输入 Go Module Name,例如:git.hlsq.asia/mmorpg/service-common"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="saveSettings">保存设置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<el-table :data="settings.projects" style="width: 100%">
|
||||||
|
<el-table-column prop="name" label="项目名称"/>
|
||||||
|
<el-table-column prop="path" label="项目路径" show-overflow-tooltip/>
|
||||||
|
<el-table-column label="操作" width="200">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button size="small" @click="openEditProjectDialog(scope.$index)">编辑</el-button>
|
||||||
|
<el-button size="small" type="danger" @click="handleDeleteProject(scope.$index)">删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
v-model="projectDialogVisible"
|
||||||
|
:title="editingProjectIndex === -1 ? '添加项目配置' : '编辑项目配置'"
|
||||||
|
width="500px"
|
||||||
|
>
|
||||||
|
<el-form label-width="120px">
|
||||||
|
<el-form-item label="项目名称">
|
||||||
|
<el-input
|
||||||
|
v-model="projectForm.name"
|
||||||
|
placeholder="自动从路径提取,也可手动修改"
|
||||||
|
clearable
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目路径">
|
||||||
|
<el-input
|
||||||
|
v-model="projectForm.path"
|
||||||
|
placeholder="请输入或选择项目路径(需包含 go.mod 文件)"
|
||||||
|
clearable
|
||||||
|
>
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="selectProjectPath">选择文件夹</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="projectDialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleAddProject">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.update-common-container {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.update-common-card,
|
||||||
|
.project-card,
|
||||||
|
.update-version-card {
|
||||||
|
max-width: 900px;
|
||||||
|
margin: 20px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-form {
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
"useDefineForClassFields": true,
|
"useDefineForClassFields": true,
|
||||||
"module": "ESNext",
|
"module": "ESNext",
|
||||||
"moduleResolution": "Node",
|
"moduleResolution": "Node",
|
||||||
"strict": true,
|
"strict": false,
|
||||||
"jsx": "preserve",
|
"jsx": "preserve",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"resolveJsonModule": true,
|
"resolveJsonModule": true,
|
||||||
|
|||||||
45
Tools/quickly/frontend/wailsjs/go/backend/App.d.ts
vendored
Normal file
45
Tools/quickly/frontend/wailsjs/go/backend/App.d.ts
vendored
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
import {backend} from '../models';
|
||||||
|
|
||||||
|
export function AddDatabaseConfig(arg1:string,arg2:string,arg3:string):Promise<void>;
|
||||||
|
|
||||||
|
export function AddProjectConfig(arg1:string,arg2:string):Promise<void>;
|
||||||
|
|
||||||
|
export function CheckGenPs1Exists(arg1:string):Promise<boolean>;
|
||||||
|
|
||||||
|
export function ExecuteGenPs1(arg1:string,arg2:string,arg3:string,arg4:string):Promise<string>;
|
||||||
|
|
||||||
|
export function GetSettings():Promise<backend.Settings>;
|
||||||
|
|
||||||
|
export function GetSwaggerFiles(arg1:string):Promise<Array<backend.SwaggerFile>>;
|
||||||
|
|
||||||
|
export function GetSwaggerServerURL():Promise<string>;
|
||||||
|
|
||||||
|
export function Greet(arg1:string):Promise<string>;
|
||||||
|
|
||||||
|
export function IsSwaggerServerRunning():Promise<boolean>;
|
||||||
|
|
||||||
|
export function ReadGoModModule(arg1:string):Promise<string>;
|
||||||
|
|
||||||
|
export function ReadSwaggerFile(arg1:string):Promise<string>;
|
||||||
|
|
||||||
|
export function RemoveDatabaseConfig(arg1:string):Promise<void>;
|
||||||
|
|
||||||
|
export function RemoveProjectConfig(arg1:string):Promise<void>;
|
||||||
|
|
||||||
|
export function SaveSettings(arg1:backend.Settings):Promise<void>;
|
||||||
|
|
||||||
|
export function SelectDirectory(arg1:string,arg2:string):Promise<string>;
|
||||||
|
|
||||||
|
export function SelectFile(arg1:string,arg2:string,arg3:string):Promise<string>;
|
||||||
|
|
||||||
|
export function StartSwaggerServer(arg1:string):Promise<string>;
|
||||||
|
|
||||||
|
export function StopSwaggerServer():Promise<void>;
|
||||||
|
|
||||||
|
export function UpdateDatabaseConfig(arg1:string,arg2:string,arg3:string,arg4:string):Promise<void>;
|
||||||
|
|
||||||
|
export function UpdateProjectConfig(arg1:string,arg2:string,arg3:string):Promise<void>;
|
||||||
|
|
||||||
|
export function UpdateServiceCommon(arg1:string,arg2:Array<string>):Promise<string>;
|
||||||
87
Tools/quickly/frontend/wailsjs/go/backend/App.js
Normal file
87
Tools/quickly/frontend/wailsjs/go/backend/App.js
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
// @ts-check
|
||||||
|
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
||||||
|
// This file is automatically generated. DO NOT EDIT
|
||||||
|
|
||||||
|
export function AddDatabaseConfig(arg1, arg2, arg3) {
|
||||||
|
return window['go']['backend']['App']['AddDatabaseConfig'](arg1, arg2, arg3);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function AddProjectConfig(arg1, arg2) {
|
||||||
|
return window['go']['backend']['App']['AddProjectConfig'](arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function CheckGenPs1Exists(arg1) {
|
||||||
|
return window['go']['backend']['App']['CheckGenPs1Exists'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ExecuteGenPs1(arg1, arg2, arg3, arg4) {
|
||||||
|
return window['go']['backend']['App']['ExecuteGenPs1'](arg1, arg2, arg3, arg4);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetSettings() {
|
||||||
|
return window['go']['backend']['App']['GetSettings']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetSwaggerFiles(arg1) {
|
||||||
|
return window['go']['backend']['App']['GetSwaggerFiles'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function GetSwaggerServerURL() {
|
||||||
|
return window['go']['backend']['App']['GetSwaggerServerURL']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function Greet(arg1) {
|
||||||
|
return window['go']['backend']['App']['Greet'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function IsSwaggerServerRunning() {
|
||||||
|
return window['go']['backend']['App']['IsSwaggerServerRunning']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReadGoModModule(arg1) {
|
||||||
|
return window['go']['backend']['App']['ReadGoModModule'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function ReadSwaggerFile(arg1) {
|
||||||
|
return window['go']['backend']['App']['ReadSwaggerFile'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveDatabaseConfig(arg1) {
|
||||||
|
return window['go']['backend']['App']['RemoveDatabaseConfig'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function RemoveProjectConfig(arg1) {
|
||||||
|
return window['go']['backend']['App']['RemoveProjectConfig'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SaveSettings(arg1) {
|
||||||
|
return window['go']['backend']['App']['SaveSettings'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SelectDirectory(arg1, arg2) {
|
||||||
|
return window['go']['backend']['App']['SelectDirectory'](arg1, arg2);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function SelectFile(arg1, arg2, arg3) {
|
||||||
|
return window['go']['backend']['App']['SelectFile'](arg1, arg2, arg3);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function StartSwaggerServer(arg1) {
|
||||||
|
return window['go']['backend']['App']['StartSwaggerServer'](arg1);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function StopSwaggerServer() {
|
||||||
|
return window['go']['backend']['App']['StopSwaggerServer']();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UpdateDatabaseConfig(arg1, arg2, arg3, arg4) {
|
||||||
|
return window['go']['backend']['App']['UpdateDatabaseConfig'](arg1, arg2, arg3, arg4);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UpdateProjectConfig(arg1, arg2, arg3) {
|
||||||
|
return window['go']['backend']['App']['UpdateProjectConfig'](arg1, arg2, arg3);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function UpdateServiceCommon(arg1, arg2) {
|
||||||
|
return window['go']['backend']['App']['UpdateServiceCommon'](arg1, arg2);
|
||||||
|
}
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
export function Greet(arg1:string):Promise<string>;
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
// @ts-check
|
|
||||||
// Cynhyrchwyd y ffeil hon yn awtomatig. PEIDIWCH Â MODIWL
|
|
||||||
// This file is automatically generated. DO NOT EDIT
|
|
||||||
|
|
||||||
export function Greet(arg1) {
|
|
||||||
return window['go']['main']['App']['Greet'](arg1);
|
|
||||||
}
|
|
||||||
101
Tools/quickly/frontend/wailsjs/go/models.ts
Normal file
101
Tools/quickly/frontend/wailsjs/go/models.ts
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
export namespace backend {
|
||||||
|
|
||||||
|
export class DatabaseConfig {
|
||||||
|
name: string;
|
||||||
|
targetPath: string;
|
||||||
|
modelPackagePath: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new DatabaseConfig(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.name = source["name"];
|
||||||
|
this.targetPath = source["targetPath"];
|
||||||
|
this.modelPackagePath = source["modelPackagePath"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class ProjectConfig {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new ProjectConfig(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.name = source["name"];
|
||||||
|
this.path = source["path"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class Settings {
|
||||||
|
theme: string;
|
||||||
|
language: string;
|
||||||
|
notifications: boolean;
|
||||||
|
autoStart: boolean;
|
||||||
|
mysqlModelPath: string;
|
||||||
|
defaultQueryPackagePath: string;
|
||||||
|
modelBasePath: string;
|
||||||
|
swaggerDir: string;
|
||||||
|
databases: DatabaseConfig[];
|
||||||
|
projects: ProjectConfig[];
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new Settings(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.theme = source["theme"];
|
||||||
|
this.language = source["language"];
|
||||||
|
this.notifications = source["notifications"];
|
||||||
|
this.autoStart = source["autoStart"];
|
||||||
|
this.mysqlModelPath = source["mysqlModelPath"];
|
||||||
|
this.defaultQueryPackagePath = source["defaultQueryPackagePath"];
|
||||||
|
this.modelBasePath = source["modelBasePath"];
|
||||||
|
this.swaggerDir = source["swaggerDir"];
|
||||||
|
this.databases = this.convertValues(source["databases"], DatabaseConfig);
|
||||||
|
this.projects = this.convertValues(source["projects"], ProjectConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
convertValues(a: any, classs: any, asMap: boolean = false): any {
|
||||||
|
if (!a) {
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
if (a.slice && a.map) {
|
||||||
|
return (a as any[]).map(elem => this.convertValues(elem, classs));
|
||||||
|
} else if ("object" === typeof a) {
|
||||||
|
if (asMap) {
|
||||||
|
for (const key of Object.keys(a)) {
|
||||||
|
a[key] = new classs(a[key]);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
return new classs(a);
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
export class SwaggerFile {
|
||||||
|
name: string;
|
||||||
|
path: string;
|
||||||
|
size: number;
|
||||||
|
modifiedTime: string;
|
||||||
|
|
||||||
|
static createFrom(source: any = {}) {
|
||||||
|
return new SwaggerFile(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(source: any = {}) {
|
||||||
|
if ('string' === typeof source) source = JSON.parse(source);
|
||||||
|
this.name = source["name"];
|
||||||
|
this.path = source["path"];
|
||||||
|
this.size = source["size"];
|
||||||
|
this.modifiedTime = source["modifiedTime"];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
@@ -2,20 +2,17 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"embed"
|
"embed"
|
||||||
|
|
||||||
"github.com/wailsapp/wails/v2"
|
"github.com/wailsapp/wails/v2"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options"
|
"github.com/wailsapp/wails/v2/pkg/options"
|
||||||
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
"github.com/wailsapp/wails/v2/pkg/options/assetserver"
|
||||||
|
"quickly/backend"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed all:frontend/dist
|
//go:embed all:frontend/dist
|
||||||
var assets embed.FS
|
var assets embed.FS
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Create an instance of the app structure
|
app := backend.NewApp()
|
||||||
app := NewApp()
|
|
||||||
|
|
||||||
// Create application with options
|
|
||||||
err := wails.Run(&options.App{
|
err := wails.Run(&options.App{
|
||||||
Title: "Quickly 金牌助手",
|
Title: "Quickly 金牌助手",
|
||||||
Width: 1024,
|
Width: 1024,
|
||||||
@@ -24,7 +21,7 @@ func main() {
|
|||||||
Assets: assets,
|
Assets: assets,
|
||||||
},
|
},
|
||||||
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
|
BackgroundColour: &options.RGBA{R: 27, G: 38, B: 54, A: 1},
|
||||||
OnStartup: app.startup,
|
OnStartup: app.Startup,
|
||||||
Bind: []interface{}{
|
Bind: []interface{}{
|
||||||
app,
|
app,
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user