diff --git a/backend/internal/model/models.go b/backend/internal/model/models.go index 588bc7f..a83b0d9 100644 --- a/backend/internal/model/models.go +++ b/backend/internal/model/models.go @@ -227,6 +227,11 @@ type Version struct { FileSize int64 `json:"file_size"` FileHash string `gorm:"size:64" json:"file_hash"` EntryFile string `gorm:"size:255" json:"entry_file"` + EntryFiles string `gorm:"type:text" json:"entry_files"` // JSON数组,多入口文件 + CreateDesktopShortcut bool `gorm:"default:false" json:"create_desktop_shortcut"` // 是否创建桌面快捷方式 + CreateStartMenu bool `gorm:"default:false" json:"create_start_menu"` // 是否创建开始菜单 + AutoRun bool `gorm:"default:false" json:"auto_run"` // 是否自动运行 + DefaultInstallDir string `gorm:"size:500" json:"default_install_dir"` // 默认安装目录 UpdateStrategy string `gorm:"size:20;default:optional" json:"update_strategy"` UpdateType string `gorm:"size:20;default:full" json:"update_type"` UpdateMethod string `gorm:"size:20;default:auto" json:"update_method"` diff --git a/backend/internal/router/admin/versions.go b/backend/internal/router/admin/versions.go index 121c4df..97f4cf8 100644 --- a/backend/internal/router/admin/versions.go +++ b/backend/internal/router/admin/versions.go @@ -260,20 +260,25 @@ func handleGetVersionByID(c *gin.Context) { } type CreateVersionRequest struct { - ApplicationID uint `json:"application_id"` - Version string `json:"version"` - FilePath string `json:"file_path"` - FileSize int64 `json:"file_size"` - FileHash string `json:"file_hash"` - EntryFile string `json:"entry_file"` - UpdateStrategy string `json:"update_strategy"` - UpdateType string `json:"update_type"` - UpdateMethod string `json:"update_method"` - CustomDownloadURL string `json:"custom_download_url"` - Description string `json:"description"` - Changelog string `json:"changelog"` - BaseVersionID *uint `json:"base_version_id"` - Files []VersionFileInput `json:"files"` + ApplicationID uint `json:"application_id"` + Version string `json:"version"` + FilePath string `json:"file_path"` + FileSize int64 `json:"file_size"` + FileHash string `json:"file_hash"` + EntryFile string `json:"entry_file"` + EntryFiles string `json:"entry_files"` + CreateDesktopShortcut bool `json:"create_desktop_shortcut"` + CreateStartMenu bool `json:"create_start_menu"` + AutoRun bool `json:"auto_run"` + DefaultInstallDir string `json:"default_install_dir"` + UpdateStrategy string `json:"update_strategy"` + UpdateType string `json:"update_type"` + UpdateMethod string `json:"update_method"` + CustomDownloadURL string `json:"custom_download_url"` + Description string `json:"description"` + Changelog string `json:"changelog"` + BaseVersionID *uint `json:"base_version_id"` + Files []VersionFileInput `json:"files"` } type VersionFileInput struct { @@ -312,19 +317,24 @@ func handleCreateVersionGlobal(c *gin.Context) { } version := model.Version{ - ApplicationID: req.ApplicationID, - Version: req.Version, - FilePath: req.FilePath, - FileSize: req.FileSize, - FileHash: req.FileHash, - EntryFile: req.EntryFile, - UpdateStrategy: req.UpdateStrategy, - UpdateType: req.UpdateType, - UpdateMethod: req.UpdateMethod, - CustomDownloadURL: req.CustomDownloadURL, - Description: req.Description, - Changelog: req.Changelog, - PublishStatus: "draft", + ApplicationID: req.ApplicationID, + Version: req.Version, + FilePath: req.FilePath, + FileSize: req.FileSize, + FileHash: req.FileHash, + EntryFile: req.EntryFile, + EntryFiles: req.EntryFiles, + CreateDesktopShortcut: req.CreateDesktopShortcut, + CreateStartMenu: req.CreateStartMenu, + AutoRun: req.AutoRun, + DefaultInstallDir: req.DefaultInstallDir, + UpdateStrategy: req.UpdateStrategy, + UpdateType: req.UpdateType, + UpdateMethod: req.UpdateMethod, + CustomDownloadURL: req.CustomDownloadURL, + Description: req.Description, + Changelog: req.Changelog, + PublishStatus: "draft", } if version.UpdateStrategy == "" { @@ -455,19 +465,24 @@ func handleCreateVersionGlobal(c *gin.Context) { } type UpdateVersionRequest struct { - Version string `json:"version"` - FilePath string `json:"file_path"` - FileSize int64 `json:"file_size"` - FileHash string `json:"file_hash"` - EntryFile string `json:"entry_file"` - UpdateStrategy string `json:"update_strategy"` - UpdateType string `json:"update_type"` - UpdateMethod string `json:"update_method"` - CustomDownloadURL string `json:"custom_download_url"` - Description string `json:"description"` - Changelog string `json:"changelog"` - BaseVersionID *uint `json:"base_version_id"` - Files []VersionFileInput `json:"files"` + Version string `json:"version"` + FilePath string `json:"file_path"` + FileSize int64 `json:"file_size"` + FileHash string `json:"file_hash"` + EntryFile string `json:"entry_file"` + EntryFiles string `json:"entry_files"` + CreateDesktopShortcut bool `json:"create_desktop_shortcut"` + CreateStartMenu bool `json:"create_start_menu"` + AutoRun bool `json:"auto_run"` + DefaultInstallDir string `json:"default_install_dir"` + UpdateStrategy string `json:"update_strategy"` + UpdateType string `json:"update_type"` + UpdateMethod string `json:"update_method"` + CustomDownloadURL string `json:"custom_download_url"` + Description string `json:"description"` + Changelog string `json:"changelog"` + BaseVersionID *uint `json:"base_version_id"` + Files []VersionFileInput `json:"files"` } func handleUpdateVersionGlobal(c *gin.Context) { @@ -525,6 +540,11 @@ func handleUpdateVersionGlobal(c *gin.Context) { updates["file_hash"] = req.FileHash } updates["entry_file"] = req.EntryFile + updates["entry_files"] = req.EntryFiles + updates["create_desktop_shortcut"] = req.CreateDesktopShortcut + updates["create_start_menu"] = req.CreateStartMenu + updates["auto_run"] = req.AutoRun + updates["default_install_dir"] = req.DefaultInstallDir if req.UpdateStrategy != "" { updates["update_strategy"] = req.UpdateStrategy } diff --git a/backend/internal/router/app/info.go b/backend/internal/router/app/info.go index 6438be2..82a0480 100644 --- a/backend/internal/router/app/info.go +++ b/backend/internal/router/app/info.go @@ -114,17 +114,22 @@ func handleAppCheckUpdate(c *gin.Context) { } result := gin.H{ - "has_update": true, - "latest_version": latestVersion.Version, - "download_url": downloadURL, - "file_size": downloadSize, - "file_hash": downloadHash, - "entry_file": latestVersion.EntryFile, - "update_notes": latestVersion.Description, - "update_strategy": latestVersion.UpdateStrategy, - "update_type": latestVersion.UpdateType, - "update_method": latestVersion.UpdateMethod, - "changelog": latestVersion.Changelog, + "has_update": true, + "latest_version": latestVersion.Version, + "download_url": downloadURL, + "file_size": downloadSize, + "file_hash": downloadHash, + "entry_file": latestVersion.EntryFile, + "entry_files": latestVersion.EntryFiles, + "create_desktop_shortcut": latestVersion.CreateDesktopShortcut, + "create_start_menu": latestVersion.CreateStartMenu, + "auto_run": latestVersion.AutoRun, + "default_install_dir": latestVersion.DefaultInstallDir, + "update_notes": latestVersion.Description, + "update_strategy": latestVersion.UpdateStrategy, + "update_type": latestVersion.UpdateType, + "update_method": latestVersion.UpdateMethod, + "changelog": latestVersion.Changelog, } // 同时返回补丁包信息(如果有),供客户端增量更新使用 @@ -250,18 +255,23 @@ func handleAppCheckUpdate(c *gin.Context) { // 构建返回结果 result := map[string]interface{}{ - "has_update": true, - "current_version": clientVersion, - "latest_version": latestVersion.Version, - "download_url": downloadURL, - "file_size": downloadSize, - "file_hash": downloadHash, - "entry_file": latestVersion.EntryFile, - "update_notes": latestVersion.Description, - "update_strategy": updateStrategy, - "update_type": latestVersion.UpdateType, - "update_method": latestVersion.UpdateMethod, - "changelog": latestVersion.Changelog, + "has_update": true, + "current_version": clientVersion, + "latest_version": latestVersion.Version, + "download_url": downloadURL, + "file_size": downloadSize, + "file_hash": downloadHash, + "entry_file": latestVersion.EntryFile, + "entry_files": latestVersion.EntryFiles, + "create_desktop_shortcut": latestVersion.CreateDesktopShortcut, + "create_start_menu": latestVersion.CreateStartMenu, + "auto_run": latestVersion.AutoRun, + "default_install_dir": latestVersion.DefaultInstallDir, + "update_notes": latestVersion.Description, + "update_strategy": updateStrategy, + "update_type": latestVersion.UpdateType, + "update_method": latestVersion.UpdateMethod, + "changelog": latestVersion.Changelog, } // 如果是补丁更新,添加补丁信息 diff --git a/frontend/src/pages/admin/versions/[id].vue b/frontend/src/pages/admin/versions/[id].vue index b83b11a..7952817 100644 --- a/frontend/src/pages/admin/versions/[id].vue +++ b/frontend/src/pages/admin/versions/[id].vue @@ -53,6 +53,11 @@ const formData = ref({ custom_download_url: '', changelog: '', entry_file: '', + entry_files: [] as string[], + create_desktop_shortcut: false, + create_start_menu: false, + auto_run: false, + default_install_dir: '', file: null as File | null, base_version_id: '' as string, }) @@ -92,6 +97,16 @@ function formatFileSize(size: number): string { return `${(size / (1024 * 1024)).toFixed(2)} MB` } +function toggleEntryFile(filePath: string) { + const index = formData.value.entry_files.indexOf(filePath) + if (index >= 0) { + formData.value.entry_files.splice(index, 1) + } else { + formData.value.entry_files.push(filePath) + } + formData.value.entry_file = formData.value.entry_files.length > 0 ? formData.value.entry_files[0] : '' +} + async function fetchVersion() { loading.value = true try { @@ -107,6 +122,16 @@ async function fetchVersion() { formData.value.changelog = ver.changelog || '' formData.value.entry_file = ver.entry_file || '' formData.value.base_version_id = ver.base_version_id ? String(ver.base_version_id) : '' + formData.value.create_desktop_shortcut = ver.create_desktop_shortcut || false + formData.value.create_start_menu = ver.create_start_menu || false + formData.value.auto_run = ver.auto_run || false + formData.value.default_install_dir = ver.default_install_dir || '' + // 解析 entry_files JSON + try { + formData.value.entry_files = ver.entry_files ? JSON.parse(ver.entry_files) : [] + } catch { + formData.value.entry_files = [] + } // 加载已有文件信息 if (ver.file_path && ver.file_size) { @@ -275,6 +300,11 @@ async function handleSubmit() { custom_download_url: formData.value.custom_download_url, changelog: formData.value.changelog, entry_file: formData.value.entry_file, + entry_files: JSON.stringify(formData.value.entry_files), + create_desktop_shortcut: formData.value.create_desktop_shortcut, + create_start_menu: formData.value.create_start_menu, + auto_run: formData.value.auto_run, + default_install_dir: formData.value.default_install_dir, } // 如果是补丁版本,添加基础版本ID @@ -385,6 +415,49 @@ onMounted(() => { /> + +
+ {{ t('admin.versions.createForm.updateType') }} * + + + + + + + {{ t('admin.versions.types.full') }} + + + {{ t('admin.versions.types.patch') }} + + + +
+ + +
+ {{ t('admin.versions.createForm.baseVersion') }} * +

+ {{ t('admin.versions.createForm.baseVersionDesc') }} +

+ + + + + + + {{ v.version }} + + + +

+ {{ t('admin.versions.createForm.noBaseVersion') }} +

+
+
{{ t('admin.versions.createForm.updateFile') }}
@@ -534,22 +607,68 @@ onMounted(() => {
+
- {{ t('admin.versions.createForm.entryFile') }} - - - - - - {{ t('admin.versions.createForm.entryFiles') }} +

+ {{ t('admin.versions.createForm.entryFilesDesc') }} +

+
+
+ - {{ file.file_name }} ({{ formatFileSize(file.file_size) }}) - - - +
+

{{ file.file_name }}

+

{{ file.file_path }} ({{ formatFileSize(file.file_size) }})

+
+
+
+
+ + +
+

{{ t('admin.versions.createForm.installOptions') }}

+ +
+
+ {{ t('admin.versions.createForm.createDesktopShortcut') }} +

{{ t('admin.versions.createForm.createDesktopShortcutDesc') }}

+
+ +
+ +
+
+ {{ t('admin.versions.createForm.createStartMenu') }} +

{{ t('admin.versions.createForm.createStartMenuDesc') }}

+
+ +
+ +
+
+ {{ t('admin.versions.createForm.autoRun') }} +

{{ t('admin.versions.createForm.autoRunDesc') }}

+
+ +
+ +
+ {{ t('admin.versions.createForm.defaultInstallDir') }} + +
@@ -567,48 +686,6 @@ onMounted(() => { />
-
- {{ t('admin.versions.createForm.updateType') }} - - - - - - - {{ t('admin.versions.types.full') }} - - - {{ t('admin.versions.types.patch') }} - - - -
- - -
- {{ t('admin.versions.createForm.baseVersion') }} -

- {{ t('admin.versions.createForm.baseVersionDesc') }} -

- - - - - - - {{ v.version }} - - - -

- {{ t('admin.versions.createForm.noBaseVersion') }} -

-
-
{{ t('admin.versions.createForm.updateMethod') }} diff --git a/frontend/src/pages/admin/versions/create.vue b/frontend/src/pages/admin/versions/create.vue index f70a2c5..42cf97f 100644 --- a/frontend/src/pages/admin/versions/create.vue +++ b/frontend/src/pages/admin/versions/create.vue @@ -52,6 +52,11 @@ const formData = ref({ custom_download_url: '', changelog: '', entry_file: '', + entry_files: [] as string[], + create_desktop_shortcut: false, + create_start_menu: false, + auto_run: false, + default_install_dir: '', file: null as File | null, base_version_id: '' as string, }) @@ -92,6 +97,17 @@ function formatFileSize(size: number): string { return `${(size / (1024 * 1024)).toFixed(2)} MB` } +function toggleEntryFile(filePath: string) { + const index = formData.value.entry_files.indexOf(filePath) + if (index >= 0) { + formData.value.entry_files.splice(index, 1) + } else { + formData.value.entry_files.push(filePath) + } + // 兼容旧字段:第一个选中的作为主入口 + formData.value.entry_file = formData.value.entry_files.length > 0 ? formData.value.entry_files[0] : '' +} + const typeLabel = computed(() => { const map: Record = { full: t('admin.versions.types.full'), @@ -260,6 +276,11 @@ async function handleSubmit() { custom_download_url: formData.value.custom_download_url, changelog: formData.value.changelog, entry_file: formData.value.entry_file, + entry_files: JSON.stringify(formData.value.entry_files), + create_desktop_shortcut: formData.value.create_desktop_shortcut, + create_start_menu: formData.value.create_start_menu, + auto_run: formData.value.auto_run, + default_install_dir: formData.value.default_install_dir, file_path: uploadedData.value.file_path, file_size: uploadedData.value.file_size, file_hash: uploadedData.value.file_hash, @@ -351,6 +372,49 @@ onMounted(() => { />
+ +
+ {{ t('admin.versions.createForm.updateType') }} * + + + + + + + {{ t('admin.versions.types.full') }} + + + {{ t('admin.versions.types.patch') }} + + + +
+ + +
+ {{ t('admin.versions.createForm.baseVersion') }} * +

+ {{ t('admin.versions.createForm.baseVersionDesc') }} +

+ + + + + + + {{ v.version }} + + + +

+ {{ t('admin.versions.createForm.noBaseVersion') }} +

+
+
{{ t('admin.versions.createForm.updateFile') }}
@@ -473,25 +537,68 @@ onMounted(() => {
+
- {{ t('admin.versions.createForm.entryFile') }} + {{ t('admin.versions.createForm.entryFiles') }}

- {{ t('admin.versions.createForm.entryFileDesc') }} + {{ t('admin.versions.createForm.entryFilesDesc') }}

- - - - - - +
+ - {{ file.file_name }} ({{ formatFileSize(file.file_size) }}) - - - +
+

{{ file.file_name }}

+

{{ file.file_path }} ({{ formatFileSize(file.file_size) }})

+
+
+
+ + + +
+

{{ t('admin.versions.createForm.installOptions') }}

+ +
+
+ {{ t('admin.versions.createForm.createDesktopShortcut') }} +

{{ t('admin.versions.createForm.createDesktopShortcutDesc') }}

+
+ +
+ +
+
+ {{ t('admin.versions.createForm.createStartMenu') }} +

{{ t('admin.versions.createForm.createStartMenuDesc') }}

+
+ +
+ +
+
+ {{ t('admin.versions.createForm.autoRun') }} +

{{ t('admin.versions.createForm.autoRunDesc') }}

+
+ +
+ +
+ {{ t('admin.versions.createForm.defaultInstallDir') }} + +
@@ -509,48 +616,6 @@ onMounted(() => { />
-
- {{ t('admin.versions.createForm.updateType') }} - - - - - - - {{ t('admin.versions.types.full') }} - - - {{ t('admin.versions.types.patch') }} - - - -
- - -
- {{ t('admin.versions.createForm.baseVersion') }} -

- {{ t('admin.versions.createForm.baseVersionDesc') }} -

- - - - - - - {{ v.version }} - - - -

- {{ t('admin.versions.createForm.noBaseVersion') }} -

-
-
{{ t('admin.versions.createForm.updateMethod') }} diff --git a/frontend/src/plugins/i18n/zh.json b/frontend/src/plugins/i18n/zh.json index 707f504..7619302 100644 --- a/frontend/src/plugins/i18n/zh.json +++ b/frontend/src/plugins/i18n/zh.json @@ -1495,6 +1495,17 @@ "entryFile": "入口文件", "entryFileDesc": "选择更新完成后要启动的主程序文件(可选)", "entryFilePlaceholder": "选择入口文件(可选)", + "entryFiles": "入口文件(多选)", + "entryFilesDesc": "勾选更新完成后可以启动的程序文件", + "installOptions": "安装选项", + "createDesktopShortcut": "创建桌面快捷方式", + "createDesktopShortcutDesc": "安装时在桌面创建快捷方式", + "createStartMenu": "创建开始菜单", + "createStartMenuDesc": "安装时在开始菜单创建快捷方式", + "autoRun": "安装后自动运行", + "autoRunDesc": "安装完成后自动启动主程序", + "defaultInstallDir": "默认安装目录", + "defaultInstallDirPlaceholder": "例如: C:\\Program Files\\MyApp", "forcedUpdate": "强制更新", "forcedUpdateDesc": "启用后用户必须更新到此版本才能继续使用", "updateType": "更新类型",