feat quickly

This commit is contained in:
2026-01-24 11:14:31 +08:00
parent 83d722d2f1
commit 4aa48a822e
9 changed files with 80 additions and 134 deletions

View File

@@ -23,7 +23,7 @@ type Settings struct {
MysqlModelPath string `json:"mysqlModelPath"` MysqlModelPath string `json:"mysqlModelPath"`
DefaultQueryPackagePath string `json:"defaultQueryPackagePath"` DefaultQueryPackagePath string `json:"defaultQueryPackagePath"`
ModelBasePath string `json:"modelBasePath"` ModelBasePath string `json:"modelBasePath"`
SwaggerDir string `json:"swaggerDir"` SwaggerFilePath string `json:"swaggerFilePath"`
Databases []DatabaseConfig `json:"databases"` Databases []DatabaseConfig `json:"databases"`
Projects []ProjectConfig `json:"projects"` Projects []ProjectConfig `json:"projects"`
} }

View File

@@ -31,7 +31,7 @@ func (a *App) loadSettings() {
MysqlModelPath: "", MysqlModelPath: "",
DefaultQueryPackagePath: "", DefaultQueryPackagePath: "",
ModelBasePath: "", ModelBasePath: "",
SwaggerDir: "", SwaggerFilePath: "",
Databases: []DatabaseConfig{}, Databases: []DatabaseConfig{},
Projects: []ProjectConfig{}, Projects: []ProjectConfig{},
} }
@@ -49,7 +49,7 @@ func (a *App) loadSettings() {
MysqlModelPath: "", MysqlModelPath: "",
DefaultQueryPackagePath: "", DefaultQueryPackagePath: "",
ModelBasePath: "", ModelBasePath: "",
SwaggerDir: "", SwaggerFilePath: "",
Databases: []DatabaseConfig{}, Databases: []DatabaseConfig{},
Projects: []ProjectConfig{}, Projects: []ProjectConfig{},
} }
@@ -69,7 +69,7 @@ func (a *App) loadSettings() {
MysqlModelPath: "", MysqlModelPath: "",
DefaultQueryPackagePath: "", DefaultQueryPackagePath: "",
ModelBasePath: "", ModelBasePath: "",
SwaggerDir: "", SwaggerFilePath: "",
Databases: []DatabaseConfig{}, Databases: []DatabaseConfig{},
Projects: []ProjectConfig{}, Projects: []ProjectConfig{},
} }

View File

@@ -63,6 +63,24 @@ func (a *App) GetSwaggerFiles(dirPath string) ([]SwaggerFile, error) {
return files, nil return files, nil
} }
func (a *App) GetSwaggerFileInfo(filePath string) (SwaggerFile, error) {
if filePath == "" {
return SwaggerFile{}, fmt.Errorf("file path is empty")
}
info, err := os.Stat(filePath)
if err != nil {
return SwaggerFile{}, fmt.Errorf("failed to get file info: %w", err)
}
return SwaggerFile{
Name: info.Name(),
Path: filePath,
Size: info.Size(),
ModifiedTime: info.ModTime().Format("2006-01-02 15:04:05"),
}, nil
}
func (a *App) ReadSwaggerFile(filePath string) (string, error) { func (a *App) ReadSwaggerFile(filePath string) (string, error) {
if filePath == "" { if filePath == "" {
return "", fmt.Errorf("file path is empty") return "", fmt.Errorf("file path is empty")
@@ -76,9 +94,9 @@ func (a *App) ReadSwaggerFile(filePath string) (string, error) {
return string(content), nil return string(content), nil
} }
func (a *App) StartSwaggerServer(dirPath string) (string, error) { func (a *App) StartSwaggerServer(filePath string) (string, error) {
if dirPath == "" { if filePath == "" {
return "", fmt.Errorf("directory path is empty") return "", fmt.Errorf("file path is empty")
} }
swaggerServerMu.Lock() swaggerServerMu.Lock()
@@ -121,11 +139,8 @@ func (a *App) StartSwaggerServer(dirPath string) (string, error) {
<script src="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-bundle.js"></script> <script src="https://unpkg.com/swagger-ui-dist@5.10.5/swagger-ui-bundle.js"></script>
<script> <script>
window.onload = function() { 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({ const ui = SwaggerUIBundle({
url: swaggerUrl, url: '/swagger.json',
dom_id: '#swagger-ui', dom_id: '#swagger-ui',
presets: [ presets: [
SwaggerUIBundle.presets.apis, SwaggerUIBundle.presets.apis,
@@ -148,29 +163,7 @@ func (a *App) StartSwaggerServer(dirPath string) (string, error) {
}) })
mux.HandleFunc("/swagger.json", func(w http.ResponseWriter, r *http.Request) { mux.HandleFunc("/swagger.json", func(w http.ResponseWriter, r *http.Request) {
files, err := a.GetSwaggerFiles(dirPath) content, err := os.ReadFile(filePath)
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 { if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError) http.Error(w, err.Error(), http.StatusInternalServerError)
return return

View File

@@ -38,7 +38,6 @@ const features = reactive([
</div> </div>
<div class="features-section"> <div class="features-section">
<h2 class="section-title">功能介绍</h2>
<div class="features-grid"> <div class="features-grid">
<el-card v-for="(feature, index) in features" :key="index" class="feature-card"> <el-card v-for="(feature, index) in features" :key="index" class="feature-card">
<div class="feature-icon">{{ feature.icon }}</div> <div class="feature-icon">{{ feature.icon }}</div>
@@ -82,13 +81,6 @@ const features = reactive([
margin: 40px 0; margin: 40px 0;
} }
.section-title {
font-size: 28px;
font-weight: 600;
color: #333;
margin: 0 0 30px 0;
}
.features-grid { .features-grid {
display: grid; display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));

View File

@@ -3,17 +3,17 @@ import {onMounted, reactive, ref} from 'vue'
import {ElMessage} from 'element-plus' import {ElMessage} from 'element-plus'
import { import {
GetSettings, GetSettings,
GetSwaggerFiles, GetSwaggerFileInfo,
GetSwaggerServerURL, GetSwaggerServerURL,
IsSwaggerServerRunning, IsSwaggerServerRunning,
ReadSwaggerFile, ReadSwaggerFile,
SaveSettings, SaveSettings,
SelectDirectory, SelectFile,
StartSwaggerServer, StartSwaggerServer,
StopSwaggerServer StopSwaggerServer
} from '../../wailsjs/go/backend/App' } from '../../wailsjs/go/backend/App'
interface SwaggerFile { interface SwaggerFileInfo {
name: string name: string
path: string path: string
size: number size: number
@@ -25,7 +25,7 @@ interface AppSettings {
language: string language: string
notifications: boolean notifications: boolean
autoStart: boolean autoStart: boolean
swaggerDir: string swaggerFilePath: string
} }
const settings = reactive<AppSettings>({ const settings = reactive<AppSettings>({
@@ -33,51 +33,39 @@ const settings = reactive<AppSettings>({
language: 'zh-CN', language: 'zh-CN',
notifications: true, notifications: true,
autoStart: false, autoStart: false,
swaggerDir: '' swaggerFilePath: ''
}) })
const swaggerFiles = ref<SwaggerFile[]>([]) const swaggerFileInfo = ref<SwaggerFileInfo | null>(null)
const selectedFile = ref<SwaggerFile | null>(null)
const selectedSwaggerFile = ref<SwaggerFile | null>(null)
const fileContent = ref('') const fileContent = ref('')
const isLoading = ref(false) const isLoading = ref(false)
const serverURL = ref('') const serverURL = ref('')
const isServerRunning = ref(false) const isServerRunning = ref(false)
async function selectSwaggerDir() { async function selectSwaggerFile() {
try { try {
const path = await SelectDirectory('选择 Swagger 目录', settings.swaggerDir) const path = await SelectFile('选择 Swagger 文件', settings.swaggerFilePath, '*.json')
if (path) { if (path) {
settings.swaggerDir = path settings.swaggerFilePath = path
await loadSwaggerFiles() const fileInfo = await GetSwaggerFileInfo(path)
if (fileInfo) {
swaggerFileInfo.value = fileInfo
}
await saveSettings() await saveSettings()
} }
} catch (error) { } catch (error) {
console.error('Failed to select directory:', error) console.error('Failed to select swagger file:', error)
} }
} }
async function loadSwaggerFiles() { async function loadSwaggerFiles() {
if (!settings.swaggerDir) {
return 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() { async function saveSettings() {
try { try {
const currentSettings = await GetSettings() as any const currentSettings = await GetSettings() as any
currentSettings.swaggerDir = settings.swaggerDir currentSettings.swaggerFilePath = settings.swaggerFilePath
await SaveSettings(currentSettings) await SaveSettings(currentSettings)
ElMessage.success('设置保存成功') ElMessage.success('设置保存成功')
} catch (error) { } catch (error) {
@@ -86,8 +74,8 @@ async function saveSettings() {
} }
} }
async function viewFile(file: SwaggerFile) { async function viewFile(file: SwaggerFileInfo) {
selectedFile.value = file swaggerFileInfo.value = file
isLoading.value = true isLoading.value = true
try { try {
const content = await ReadSwaggerFile(file.path) const content = await ReadSwaggerFile(file.path)
@@ -111,18 +99,15 @@ function formatFileSize(bytes: number): string {
} }
async function startSwaggerServer() { async function startSwaggerServer() {
if (!settings.swaggerDir) { if (!settings.swaggerFilePath) {
ElMessage.warning('请先配置 Swagger 目录') ElMessage.warning('请先配置 Swagger 文件')
return return
} }
try { try {
const url = await StartSwaggerServer(settings.swaggerDir) const url = await StartSwaggerServer(settings.swaggerFilePath)
serverURL.value = url serverURL.value = url
isServerRunning.value = true isServerRunning.value = true
if (swaggerFiles.value.length > 0) {
selectedSwaggerFile.value = swaggerFiles.value[0]
}
ElMessage.success(`Swagger 服务已启动: ${url}`) ElMessage.success(`Swagger 服务已启动: ${url}`)
} catch (error) { } catch (error) {
console.error('Failed to start swagger server:', error) console.error('Failed to start swagger server:', error)
@@ -162,9 +147,9 @@ function openSwaggerURL() {
} }
function updateServerURL() { function updateServerURL() {
if (serverURL.value && selectedSwaggerFile.value) { if (serverURL.value && swaggerFileInfo.value) {
const baseUrl = serverURL.value.split('?')[0] const baseUrl = serverURL.value.split('?')[0]
serverURL.value = `${baseUrl}?file=${encodeURIComponent(selectedSwaggerFile.value.name)}` serverURL.value = `${baseUrl}?file=${encodeURIComponent(swaggerFileInfo.value.name)}`
} }
} }
@@ -175,9 +160,12 @@ onMounted(async () => {
settings.language = loadedSettings.language settings.language = loadedSettings.language
settings.notifications = loadedSettings.notifications settings.notifications = loadedSettings.notifications
settings.autoStart = loadedSettings.autoStart settings.autoStart = loadedSettings.autoStart
settings.swaggerDir = loadedSettings.swaggerDir || '' settings.swaggerFilePath = loadedSettings.swaggerFilePath || ''
if (settings.swaggerDir) { if (settings.swaggerFilePath) {
await loadSwaggerFiles() const fileInfo = await GetSwaggerFileInfo(settings.swaggerFilePath)
if (fileInfo) {
swaggerFileInfo.value = fileInfo
}
} }
await checkServerStatus() await checkServerStatus()
} catch (error) { } catch (error) {
@@ -195,14 +183,14 @@ onMounted(async () => {
</div> </div>
</template> </template>
<el-form label-width="140px" class="settings-form"> <el-form label-width="140px" class="settings-form">
<el-form-item label="Swagger 目录"> <el-form-item label="Swagger 文件">
<el-input <el-input
v-model="settings.swaggerDir" v-model="settings.swaggerFilePath"
placeholder="请输入或选择 Swagger 文件目录" placeholder="请输入或选择 Swagger 文件"
clearable clearable
> >
<template #append> <template #append>
<el-button @click="selectSwaggerDir">选择文件</el-button> <el-button @click="selectSwaggerFile">选择文件</el-button>
</template> </template>
</el-input> </el-input>
</el-form-item> </el-form-item>
@@ -222,20 +210,6 @@ onMounted(async () => {
> >
停止服务 停止服务
</el-button> </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 <el-input
v-if="serverURL" v-if="serverURL"
v-model="serverURL" v-model="serverURL"
@@ -251,39 +225,19 @@ onMounted(async () => {
</el-form> </el-form>
</el-card> </el-card>
<el-card class="files-card"> <el-card class="content-card" v-if="swaggerFileInfo">
<template #header> <template #header>
<div class="card-header"> <div class="card-header">
<span>Swagger 文件列表</span> <span>{{ swaggerFileInfo.name }}</span>
<el-button type="primary" size="small" @click="loadSwaggerFiles" :loading="isLoading">刷新</el-button> <el-button size="small" @click="swaggerFileInfo = null">关闭</el-button>
</div> </div>
</template> </template>
<el-table :data="swaggerFiles" style="width: 100%" v-loading="isLoading"> <div class="info-content">
<el-table-column prop="name" label="文件名" show-overflow-tooltip/> <div class="file-info">
<el-table-column prop="size" label="大小" width="120"> <p><strong>文件路径</strong>{{ swaggerFileInfo.path }}</p>
<template #default="scope"> <p><strong>文件大小</strong>{{ formatFileSize(swaggerFileInfo.size) }}</p>
{{ formatFileSize(scope.row.size) }} <p><strong>修改时间</strong>{{ swaggerFileInfo.modifiedTime }}</p>
</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> </div>
</template>
<div class="file-content" v-loading="isLoading">
<pre>{{ fileContent }}</pre>
</div> </div>
</el-card> </el-card>
</div> </div>

View File

@@ -12,6 +12,8 @@ export function ExecuteGenPs1(arg1:string,arg2:string,arg3:string,arg4:string):P
export function GetSettings():Promise<backend.Settings>; export function GetSettings():Promise<backend.Settings>;
export function GetSwaggerFileInfo(arg1:string):Promise<backend.SwaggerFile>;
export function GetSwaggerFiles(arg1:string):Promise<Array<backend.SwaggerFile>>; export function GetSwaggerFiles(arg1:string):Promise<Array<backend.SwaggerFile>>;
export function GetSwaggerServerURL():Promise<string>; export function GetSwaggerServerURL():Promise<string>;

View File

@@ -22,6 +22,10 @@ export function GetSettings() {
return window['go']['backend']['App']['GetSettings'](); return window['go']['backend']['App']['GetSettings']();
} }
export function GetSwaggerFileInfo(arg1) {
return window['go']['backend']['App']['GetSwaggerFileInfo'](arg1);
}
export function GetSwaggerFiles(arg1) { export function GetSwaggerFiles(arg1) {
return window['go']['backend']['App']['GetSwaggerFiles'](arg1); return window['go']['backend']['App']['GetSwaggerFiles'](arg1);
} }

View File

@@ -38,7 +38,7 @@ export namespace backend {
mysqlModelPath: string; mysqlModelPath: string;
defaultQueryPackagePath: string; defaultQueryPackagePath: string;
modelBasePath: string; modelBasePath: string;
swaggerDir: string; swaggerFilePath: string;
databases: DatabaseConfig[]; databases: DatabaseConfig[];
projects: ProjectConfig[]; projects: ProjectConfig[];
@@ -55,7 +55,7 @@ export namespace backend {
this.mysqlModelPath = source["mysqlModelPath"]; this.mysqlModelPath = source["mysqlModelPath"];
this.defaultQueryPackagePath = source["defaultQueryPackagePath"]; this.defaultQueryPackagePath = source["defaultQueryPackagePath"];
this.modelBasePath = source["modelBasePath"]; this.modelBasePath = source["modelBasePath"];
this.swaggerDir = source["swaggerDir"]; this.swaggerFilePath = source["swaggerFilePath"];
this.databases = this.convertValues(source["databases"], DatabaseConfig); this.databases = this.convertValues(source["databases"], DatabaseConfig);
this.projects = this.convertValues(source["projects"], ProjectConfig); this.projects = this.convertValues(source["projects"], ProjectConfig);
} }

View File

@@ -2,10 +2,11 @@ package main
import ( import (
"embed" "embed"
"quickly/backend"
"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
@@ -15,8 +16,8 @@ func main() {
app := backend.NewApp() app := backend.NewApp()
err := wails.Run(&options.App{ err := wails.Run(&options.App{
Title: "Quickly 金牌助手", Title: "Quickly 金牌助手",
Width: 1024, Width: 1280,
Height: 768, Height: 720,
AssetServer: &assetserver.Options{ AssetServer: &assetserver.Options{
Assets: assets, Assets: assets,
}, },