refactor: 拆分更新类型(全量/补丁)和更新方式(自动/手动),移除force_update字段
This commit is contained in:
@@ -225,10 +225,9 @@ type Version struct {
|
|||||||
FileSize int64 `json:"file_size"`
|
FileSize int64 `json:"file_size"`
|
||||||
FileHash string `gorm:"size:64" json:"file_hash"`
|
FileHash string `gorm:"size:64" json:"file_hash"`
|
||||||
EntryFile string `gorm:"size:255" json:"entry_file"`
|
EntryFile string `gorm:"size:255" json:"entry_file"`
|
||||||
ForceUpdate bool `gorm:"default:false" json:"force_update"`
|
|
||||||
UpdateStrategy string `gorm:"size:20;default:optional" json:"update_strategy"`
|
UpdateStrategy string `gorm:"size:20;default:optional" json:"update_strategy"`
|
||||||
UpdateMethod string `gorm:"size:20;default:manual" json:"update_method"`
|
UpdateType string `gorm:"size:20;default:full" json:"update_type"`
|
||||||
MinVersion string `gorm:"size:50" json:"min_version"`
|
UpdateMethod string `gorm:"size:20;default:auto" json:"update_method"`
|
||||||
Description string `gorm:"type:text" json:"description"`
|
Description string `gorm:"type:text" json:"description"`
|
||||||
Changelog string `gorm:"type:text" json:"changelog"`
|
Changelog string `gorm:"type:text" json:"changelog"`
|
||||||
Status string `gorm:"size:20;default:active" json:"status"`
|
Status string `gorm:"size:20;default:active" json:"status"`
|
||||||
@@ -236,6 +235,8 @@ type Version struct {
|
|||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
UpdatedAt time.Time `json:"updated_at"`
|
UpdatedAt time.Time `json:"updated_at"`
|
||||||
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
DeletedAt gorm.DeletedAt `gorm:"index" json:"-"`
|
||||||
|
ForceUpdate bool `gorm:"default:false" json:"force_update,omitempty"`
|
||||||
|
MinVersion string `gorm:"size:50" json:"-"`
|
||||||
|
|
||||||
Application Application `gorm:"foreignKey:ApplicationID" json:"application,omitempty"`
|
Application Application `gorm:"foreignKey:ApplicationID" json:"application,omitempty"`
|
||||||
Files []VersionFile `gorm:"foreignKey:VersionID" json:"files,omitempty"`
|
Files []VersionFile `gorm:"foreignKey:VersionID" json:"files,omitempty"`
|
||||||
|
|||||||
@@ -871,15 +871,14 @@ func handleCreateVersion(c *gin.Context) {
|
|||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
FilePath string `json:"file_path"`
|
FilePath string `json:"file_path"`
|
||||||
ForceUpdate bool `json:"force_update"`
|
|
||||||
}
|
}
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
response.Error(c, 400, "参数错误")
|
response.Error(c, 400, "参数错误")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("接收到的完整请求: Version=%s, Description=%s, FilePath=%s, ForceUpdate=%v\n",
|
fmt.Printf("接收到的完整请求: Version=%s, Description=%s, FilePath=%s\n",
|
||||||
req.Version, req.Description, req.FilePath, req.ForceUpdate)
|
req.Version, req.Description, req.FilePath)
|
||||||
|
|
||||||
finalFilePath := req.FilePath
|
finalFilePath := req.FilePath
|
||||||
fileSize := int64(0)
|
fileSize := int64(0)
|
||||||
@@ -904,7 +903,6 @@ func handleCreateVersion(c *gin.Context) {
|
|||||||
Version: req.Version,
|
Version: req.Version,
|
||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
FilePath: filePath,
|
FilePath: filePath,
|
||||||
ForceUpdate: req.ForceUpdate,
|
|
||||||
Status: "active",
|
Status: "active",
|
||||||
FileSize: fileSize,
|
FileSize: fileSize,
|
||||||
}
|
}
|
||||||
@@ -954,7 +952,6 @@ func handleUpdateVersion(c *gin.Context) {
|
|||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
FilePath string `json:"file_path"`
|
FilePath string `json:"file_path"`
|
||||||
ForceUpdate bool `json:"force_update"`
|
|
||||||
Status string `json:"status"`
|
Status string `json:"status"`
|
||||||
}
|
}
|
||||||
if err := c.ShouldBindJSON(&req); err != nil {
|
if err := c.ShouldBindJSON(&req); err != nil {
|
||||||
@@ -971,7 +968,6 @@ func handleUpdateVersion(c *gin.Context) {
|
|||||||
version.Version = req.Version
|
version.Version = req.Version
|
||||||
version.Description = req.Description
|
version.Description = req.Description
|
||||||
version.FilePath = req.FilePath
|
version.FilePath = req.FilePath
|
||||||
version.ForceUpdate = req.ForceUpdate
|
|
||||||
version.Status = req.Status
|
version.Status = req.Status
|
||||||
|
|
||||||
if err := database.DB.Save(&version).Error; err != nil {
|
if err := database.DB.Save(&version).Error; err != nil {
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ func handleGetAllVersions(c *gin.Context) {
|
|||||||
pageSize := c.DefaultQuery("page_size", "20")
|
pageSize := c.DefaultQuery("page_size", "20")
|
||||||
applicationIDFilter := c.Query("application_id")
|
applicationIDFilter := c.Query("application_id")
|
||||||
updateStrategyFilter := c.Query("update_strategy")
|
updateStrategyFilter := c.Query("update_strategy")
|
||||||
|
updateTypeFilter := c.Query("update_type")
|
||||||
updateMethodFilter := c.Query("update_method")
|
updateMethodFilter := c.Query("update_method")
|
||||||
searchFilter := c.Query("search")
|
searchFilter := c.Query("search")
|
||||||
|
|
||||||
@@ -82,6 +83,9 @@ func handleGetAllVersions(c *gin.Context) {
|
|||||||
if updateStrategyFilter != "" {
|
if updateStrategyFilter != "" {
|
||||||
countQuery = countQuery.Where("update_strategy = ?", updateStrategyFilter)
|
countQuery = countQuery.Where("update_strategy = ?", updateStrategyFilter)
|
||||||
}
|
}
|
||||||
|
if updateTypeFilter != "" {
|
||||||
|
countQuery = countQuery.Where("update_type = ?", updateTypeFilter)
|
||||||
|
}
|
||||||
if updateMethodFilter != "" {
|
if updateMethodFilter != "" {
|
||||||
countQuery = countQuery.Where("update_method = ?", updateMethodFilter)
|
countQuery = countQuery.Where("update_method = ?", updateMethodFilter)
|
||||||
}
|
}
|
||||||
@@ -98,6 +102,9 @@ func handleGetAllVersions(c *gin.Context) {
|
|||||||
if updateStrategyFilter != "" {
|
if updateStrategyFilter != "" {
|
||||||
query = query.Where("update_strategy = ?", updateStrategyFilter)
|
query = query.Where("update_strategy = ?", updateStrategyFilter)
|
||||||
}
|
}
|
||||||
|
if updateTypeFilter != "" {
|
||||||
|
query = query.Where("update_type = ?", updateTypeFilter)
|
||||||
|
}
|
||||||
if updateMethodFilter != "" {
|
if updateMethodFilter != "" {
|
||||||
query = query.Where("update_method = ?", updateMethodFilter)
|
query = query.Where("update_method = ?", updateMethodFilter)
|
||||||
}
|
}
|
||||||
@@ -223,8 +230,8 @@ func handleGetVersionByID(c *gin.Context) {
|
|||||||
"file_path": version.FilePath,
|
"file_path": version.FilePath,
|
||||||
"file_size": version.FileSize,
|
"file_size": version.FileSize,
|
||||||
"file_hash": version.FileHash,
|
"file_hash": version.FileHash,
|
||||||
"force_update": version.ForceUpdate,
|
|
||||||
"update_strategy": version.UpdateStrategy,
|
"update_strategy": version.UpdateStrategy,
|
||||||
|
"update_type": version.UpdateType,
|
||||||
"update_method": version.UpdateMethod,
|
"update_method": version.UpdateMethod,
|
||||||
"changelog": version.Changelog,
|
"changelog": version.Changelog,
|
||||||
"status": computedStatus,
|
"status": computedStatus,
|
||||||
@@ -242,8 +249,8 @@ type CreateVersionRequest struct {
|
|||||||
FileSize int64 `json:"file_size"`
|
FileSize int64 `json:"file_size"`
|
||||||
FileHash string `json:"file_hash"`
|
FileHash string `json:"file_hash"`
|
||||||
EntryFile string `json:"entry_file"`
|
EntryFile string `json:"entry_file"`
|
||||||
ForceUpdate bool `json:"force_update"`
|
|
||||||
UpdateStrategy string `json:"update_strategy"`
|
UpdateStrategy string `json:"update_strategy"`
|
||||||
|
UpdateType string `json:"update_type"`
|
||||||
UpdateMethod string `json:"update_method"`
|
UpdateMethod string `json:"update_method"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Changelog string `json:"changelog"`
|
Changelog string `json:"changelog"`
|
||||||
@@ -282,8 +289,8 @@ func handleCreateVersionGlobal(c *gin.Context) {
|
|||||||
FileSize: req.FileSize,
|
FileSize: req.FileSize,
|
||||||
FileHash: req.FileHash,
|
FileHash: req.FileHash,
|
||||||
EntryFile: req.EntryFile,
|
EntryFile: req.EntryFile,
|
||||||
ForceUpdate: req.ForceUpdate,
|
|
||||||
UpdateStrategy: req.UpdateStrategy,
|
UpdateStrategy: req.UpdateStrategy,
|
||||||
|
UpdateType: req.UpdateType,
|
||||||
UpdateMethod: req.UpdateMethod,
|
UpdateMethod: req.UpdateMethod,
|
||||||
Description: req.Description,
|
Description: req.Description,
|
||||||
Changelog: req.Changelog,
|
Changelog: req.Changelog,
|
||||||
@@ -293,17 +300,16 @@ func handleCreateVersionGlobal(c *gin.Context) {
|
|||||||
if version.UpdateStrategy == "" {
|
if version.UpdateStrategy == "" {
|
||||||
version.UpdateStrategy = "optional"
|
version.UpdateStrategy = "optional"
|
||||||
}
|
}
|
||||||
|
if version.UpdateType == "" {
|
||||||
|
version.UpdateType = "full"
|
||||||
|
}
|
||||||
if version.UpdateMethod == "" {
|
if version.UpdateMethod == "" {
|
||||||
version.UpdateMethod = "manual"
|
version.UpdateMethod = "auto"
|
||||||
}
|
}
|
||||||
|
|
||||||
if version.UpdateStrategy == "forced" {
|
if version.UpdateType == "patch" {
|
||||||
version.ForceUpdate = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if version.UpdateMethod == "hot" {
|
|
||||||
var lastFullVersion model.Version
|
var lastFullVersion model.Version
|
||||||
if err := database.DB.Where("application_id = ? AND update_method = ?", req.ApplicationID, "full").
|
if err := database.DB.Where("application_id = ? AND update_type = ?", req.ApplicationID, "full").
|
||||||
Order("id DESC").First(&lastFullVersion).Error; err == nil {
|
Order("id DESC").First(&lastFullVersion).Error; err == nil {
|
||||||
version.BaseVersionID = &lastFullVersion.ID
|
version.BaseVersionID = &lastFullVersion.ID
|
||||||
}
|
}
|
||||||
@@ -333,6 +339,7 @@ func handleCreateVersionGlobal(c *gin.Context) {
|
|||||||
type UpdateVersionRequest struct {
|
type UpdateVersionRequest struct {
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
UpdateStrategy string `json:"update_strategy"`
|
UpdateStrategy string `json:"update_strategy"`
|
||||||
|
UpdateType string `json:"update_type"`
|
||||||
UpdateMethod string `json:"update_method"`
|
UpdateMethod string `json:"update_method"`
|
||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
Changelog string `json:"changelog"`
|
Changelog string `json:"changelog"`
|
||||||
@@ -376,8 +383,8 @@ func handleUpdateVersionGlobal(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[DEBUG] handleUpdateVersionGlobal versionID=%s, req: version=%s, update_strategy=%s, update_method=%s, description=%s\n",
|
log.Printf("[DEBUG] handleUpdateVersionGlobal versionID=%s, req: version=%s, update_strategy=%s, update_type=%s, update_method=%s, description=%s\n",
|
||||||
versionID, req.Version, req.UpdateStrategy, req.UpdateMethod, req.Description)
|
versionID, req.Version, req.UpdateStrategy, req.UpdateType, req.UpdateMethod, req.Description)
|
||||||
|
|
||||||
updates := map[string]interface{}{}
|
updates := map[string]interface{}{}
|
||||||
if req.Version != "" {
|
if req.Version != "" {
|
||||||
@@ -385,11 +392,9 @@ func handleUpdateVersionGlobal(c *gin.Context) {
|
|||||||
}
|
}
|
||||||
if req.UpdateStrategy != "" {
|
if req.UpdateStrategy != "" {
|
||||||
updates["update_strategy"] = req.UpdateStrategy
|
updates["update_strategy"] = req.UpdateStrategy
|
||||||
if req.UpdateStrategy == "forced" {
|
|
||||||
updates["force_update"] = true
|
|
||||||
} else {
|
|
||||||
updates["force_update"] = false
|
|
||||||
}
|
}
|
||||||
|
if req.UpdateType != "" {
|
||||||
|
updates["update_type"] = req.UpdateType
|
||||||
}
|
}
|
||||||
if req.UpdateMethod != "" {
|
if req.UpdateMethod != "" {
|
||||||
updates["update_method"] = req.UpdateMethod
|
updates["update_method"] = req.UpdateMethod
|
||||||
|
|||||||
@@ -103,6 +103,7 @@ func handleAppCheckUpdate(c *gin.Context) {
|
|||||||
"entry_file": latestVersion.EntryFile,
|
"entry_file": latestVersion.EntryFile,
|
||||||
"update_notes": latestVersion.Description,
|
"update_notes": latestVersion.Description,
|
||||||
"update_strategy": latestVersion.UpdateStrategy,
|
"update_strategy": latestVersion.UpdateStrategy,
|
||||||
|
"update_type": latestVersion.UpdateType,
|
||||||
"update_method": latestVersion.UpdateMethod,
|
"update_method": latestVersion.UpdateMethod,
|
||||||
"changelog": latestVersion.Changelog,
|
"changelog": latestVersion.Changelog,
|
||||||
"files": files,
|
"files": files,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
@@ -122,10 +122,12 @@ GET /api/v1/app/{appKey}/check-update?version=1.0.0
|
|||||||
"message": "success",
|
"message": "success",
|
||||||
"data": {
|
"data": {
|
||||||
"has_update": true,
|
"has_update": true,
|
||||||
"force_update": false,
|
|
||||||
"latest_version": "2.0.0",
|
"latest_version": "2.0.0",
|
||||||
"download_url": "http://example.com/download/app-v2.0.0.zip",
|
"download_url": "http://example.com/download/app-v2.0.0.zip",
|
||||||
"file_size": 10240000,
|
"file_size": 10240000,
|
||||||
|
"update_strategy": "optional",
|
||||||
|
"update_type": "full",
|
||||||
|
"update_method": "auto",
|
||||||
"description": "新版本修复了若干bug"
|
"description": "新版本修复了若干bug"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,8 @@ const formData = ref({
|
|||||||
version: '',
|
version: '',
|
||||||
description: '',
|
description: '',
|
||||||
update_strategy: 'optional' as 'optional' | 'forced',
|
update_strategy: 'optional' as 'optional' | 'forced',
|
||||||
update_method: 'manual' as 'manual' | 'hot' | 'full',
|
update_type: 'full' as 'full' | 'patch',
|
||||||
|
update_method: 'auto' as 'auto' | 'manual',
|
||||||
changelog: '',
|
changelog: '',
|
||||||
entry_file: '',
|
entry_file: '',
|
||||||
file: null as File | null,
|
file: null as File | null,
|
||||||
@@ -94,7 +95,8 @@ async function fetchVersion() {
|
|||||||
formData.value.version = ver.version || ''
|
formData.value.version = ver.version || ''
|
||||||
formData.value.description = ver.description || ''
|
formData.value.description = ver.description || ''
|
||||||
formData.value.update_strategy = ver.update_strategy || 'optional'
|
formData.value.update_strategy = ver.update_strategy || 'optional'
|
||||||
formData.value.update_method = ver.update_method || 'manual'
|
formData.value.update_type = ver.update_type || 'full'
|
||||||
|
formData.value.update_method = ver.update_method || 'auto'
|
||||||
formData.value.changelog = ver.changelog || ''
|
formData.value.changelog = ver.changelog || ''
|
||||||
formData.value.entry_file = ver.entry_file || ''
|
formData.value.entry_file = ver.entry_file || ''
|
||||||
}
|
}
|
||||||
@@ -180,6 +182,7 @@ async function handleSubmit() {
|
|||||||
version: formData.value.version,
|
version: formData.value.version,
|
||||||
description: formData.value.description,
|
description: formData.value.description,
|
||||||
update_strategy: formData.value.update_strategy,
|
update_strategy: formData.value.update_strategy,
|
||||||
|
update_type: formData.value.update_type,
|
||||||
update_method: formData.value.update_method,
|
update_method: formData.value.update_method,
|
||||||
changelog: formData.value.changelog,
|
changelog: formData.value.changelog,
|
||||||
entry_file: formData.value.entry_file,
|
entry_file: formData.value.entry_file,
|
||||||
@@ -423,6 +426,23 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<UiLabel>{{ t('admin.versions.createForm.updateType') }}</UiLabel>
|
||||||
|
<UiSelect v-model="formData.update_type" :disabled="saving">
|
||||||
|
<UiSelectTrigger>
|
||||||
|
<UiSelectValue />
|
||||||
|
</UiSelectTrigger>
|
||||||
|
<UiSelectContent>
|
||||||
|
<UiSelectItem value="full">
|
||||||
|
{{ t('admin.versions.types.full') }}
|
||||||
|
</UiSelectItem>
|
||||||
|
<UiSelectItem value="patch">
|
||||||
|
{{ t('admin.versions.types.patch') }}
|
||||||
|
</UiSelectItem>
|
||||||
|
</UiSelectContent>
|
||||||
|
</UiSelect>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>{{ t('admin.versions.createForm.updateMethod') }}</UiLabel>
|
<UiLabel>{{ t('admin.versions.createForm.updateMethod') }}</UiLabel>
|
||||||
<UiSelect v-model="formData.update_method" :disabled="saving">
|
<UiSelect v-model="formData.update_method" :disabled="saving">
|
||||||
@@ -430,11 +450,8 @@ onMounted(() => {
|
|||||||
<UiSelectValue />
|
<UiSelectValue />
|
||||||
</UiSelectTrigger>
|
</UiSelectTrigger>
|
||||||
<UiSelectContent>
|
<UiSelectContent>
|
||||||
<UiSelectItem value="full">
|
<UiSelectItem value="auto">
|
||||||
{{ t('admin.versions.methods.full') }}
|
{{ t('admin.versions.methods.auto') }}
|
||||||
</UiSelectItem>
|
|
||||||
<UiSelectItem value="hot">
|
|
||||||
{{ t('admin.versions.methods.hot') }}
|
|
||||||
</UiSelectItem>
|
</UiSelectItem>
|
||||||
<UiSelectItem value="manual">
|
<UiSelectItem value="manual">
|
||||||
{{ t('admin.versions.methods.manual') }}
|
{{ t('admin.versions.methods.manual') }}
|
||||||
@@ -485,11 +502,17 @@ onMounted(() => {
|
|||||||
<span class="text-muted-foreground">{{ t('admin.versions.createForm.previewForcedUpdate') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.versions.createForm.previewForcedUpdate') }}</span>
|
||||||
<span>{{ formData.update_strategy === 'forced' ? t('common.yes') : t('common.no') }}</span>
|
<span>{{ formData.update_strategy === 'forced' ? t('common.yes') : t('common.no') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex justify-between text-sm">
|
||||||
|
<span class="text-muted-foreground">{{ t('admin.versions.createForm.previewUpdateType') }}</span>
|
||||||
|
<span>{{
|
||||||
|
formData.update_type === 'full' ? t('admin.versions.types.full')
|
||||||
|
: t('admin.versions.types.patch')
|
||||||
|
}}</span>
|
||||||
|
</div>
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">{{ t('admin.versions.createForm.previewUpdateMethod') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.versions.createForm.previewUpdateMethod') }}</span>
|
||||||
<span>{{
|
<span>{{
|
||||||
formData.update_method === 'full' ? t('admin.versions.methods.full')
|
formData.update_method === 'auto' ? t('admin.versions.methods.auto')
|
||||||
: formData.update_method === 'hot' ? t('admin.versions.methods.hot')
|
|
||||||
: t('admin.versions.methods.manual')
|
: t('admin.versions.methods.manual')
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -69,21 +69,34 @@ export function getColumns(options: ColumnOptions, t: (key: string) => string):
|
|||||||
return h(Badge, { class: strategyClasses[row.original.update_strategy] }, () => strategyLabels[row.original.update_strategy])
|
return h(Badge, { class: strategyClasses[row.original.update_strategy] }, () => strategyLabels[row.original.update_strategy])
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
accessorKey: 'update_type',
|
||||||
|
header: () => t('admin.versions.columns.type'),
|
||||||
|
cell: ({ row }) => {
|
||||||
|
const typeClasses: Record<string, string> = {
|
||||||
|
full: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',
|
||||||
|
patch: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400',
|
||||||
|
}
|
||||||
|
const typeLabels: Record<string, string> = {
|
||||||
|
full: t('admin.versions.types.full'),
|
||||||
|
patch: t('admin.versions.types.patch'),
|
||||||
|
}
|
||||||
|
return h(Badge, { class: typeClasses[row.original.update_type] || typeClasses.full }, () => typeLabels[row.original.update_type] || row.original.update_type)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
accessorKey: 'update_method',
|
accessorKey: 'update_method',
|
||||||
header: () => t('admin.versions.columns.method'),
|
header: () => t('admin.versions.columns.method'),
|
||||||
cell: ({ row }) => {
|
cell: ({ row }) => {
|
||||||
const methodClasses: Record<string, string> = {
|
const methodClasses: Record<string, string> = {
|
||||||
|
auto: 'bg-purple-100 text-purple-700 dark:bg-purple-900/30 dark:text-purple-400',
|
||||||
manual: 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400',
|
manual: 'bg-gray-100 text-gray-700 dark:bg-gray-900/30 dark:text-gray-400',
|
||||||
hot: 'bg-orange-100 text-orange-700 dark:bg-orange-900/30 dark:text-orange-400',
|
|
||||||
full: 'bg-green-100 text-green-700 dark:bg-green-900/30 dark:text-green-400',
|
|
||||||
}
|
}
|
||||||
const methodLabels: Record<string, string> = {
|
const methodLabels: Record<string, string> = {
|
||||||
|
auto: t('admin.versions.methods.auto'),
|
||||||
manual: t('admin.versions.methods.manual'),
|
manual: t('admin.versions.methods.manual'),
|
||||||
hot: t('admin.versions.methods.hot'),
|
|
||||||
full: t('admin.versions.methods.full'),
|
|
||||||
}
|
}
|
||||||
return h(Badge, { class: methodClasses[row.original.update_method] }, () => methodLabels[row.original.update_method])
|
return h(Badge, { class: methodClasses[row.original.update_method] || methodClasses.manual }, () => methodLabels[row.original.update_method] || row.original.update_method)
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -43,7 +43,8 @@ const formData = ref({
|
|||||||
version: '',
|
version: '',
|
||||||
description: '',
|
description: '',
|
||||||
update_strategy: 'optional' as 'optional' | 'forced',
|
update_strategy: 'optional' as 'optional' | 'forced',
|
||||||
update_method: 'manual' as 'manual' | 'hot' | 'full',
|
update_type: 'full' as 'full' | 'patch',
|
||||||
|
update_method: 'auto' as 'auto' | 'manual',
|
||||||
changelog: '',
|
changelog: '',
|
||||||
entry_file: '',
|
entry_file: '',
|
||||||
file: null as File | null,
|
file: null as File | null,
|
||||||
@@ -85,10 +86,17 @@ function formatFileSize(size: number): string {
|
|||||||
return `${(size / (1024 * 1024)).toFixed(2)} MB`
|
return `${(size / (1024 * 1024)).toFixed(2)} MB`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const typeLabel = computed(() => {
|
||||||
|
const map: Record<string, string> = {
|
||||||
|
full: t('admin.versions.types.full'),
|
||||||
|
patch: t('admin.versions.types.patch'),
|
||||||
|
}
|
||||||
|
return map[formData.value.update_type] || formData.value.update_type
|
||||||
|
})
|
||||||
|
|
||||||
const methodLabel = computed(() => {
|
const methodLabel = computed(() => {
|
||||||
const map: Record<string, string> = {
|
const map: Record<string, string> = {
|
||||||
full: t('admin.versions.methods.full'),
|
auto: t('admin.versions.methods.auto'),
|
||||||
hot: t('admin.versions.methods.hot'),
|
|
||||||
manual: t('admin.versions.methods.manual'),
|
manual: t('admin.versions.methods.manual'),
|
||||||
}
|
}
|
||||||
return map[formData.value.update_method] || formData.value.update_method
|
return map[formData.value.update_method] || formData.value.update_method
|
||||||
@@ -178,6 +186,7 @@ async function handleSubmit() {
|
|||||||
version: formData.value.version,
|
version: formData.value.version,
|
||||||
description: formData.value.description,
|
description: formData.value.description,
|
||||||
update_strategy: formData.value.update_strategy,
|
update_strategy: formData.value.update_strategy,
|
||||||
|
update_type: formData.value.update_type,
|
||||||
update_method: formData.value.update_method,
|
update_method: formData.value.update_method,
|
||||||
changelog: formData.value.changelog,
|
changelog: formData.value.changelog,
|
||||||
entry_file: formData.value.entry_file,
|
entry_file: formData.value.entry_file,
|
||||||
@@ -411,6 +420,23 @@ onMounted(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<UiLabel>{{ t('admin.versions.createForm.updateType') }}</UiLabel>
|
||||||
|
<UiSelect v-model="formData.update_type" :disabled="saving">
|
||||||
|
<UiSelectTrigger>
|
||||||
|
<UiSelectValue />
|
||||||
|
</UiSelectTrigger>
|
||||||
|
<UiSelectContent>
|
||||||
|
<UiSelectItem value="full">
|
||||||
|
{{ t('admin.versions.types.full') }}
|
||||||
|
</UiSelectItem>
|
||||||
|
<UiSelectItem value="patch">
|
||||||
|
{{ t('admin.versions.types.patch') }}
|
||||||
|
</UiSelectItem>
|
||||||
|
</UiSelectContent>
|
||||||
|
</UiSelect>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<UiLabel>{{ t('admin.versions.createForm.updateMethod') }}</UiLabel>
|
<UiLabel>{{ t('admin.versions.createForm.updateMethod') }}</UiLabel>
|
||||||
<UiSelect v-model="formData.update_method" :disabled="saving">
|
<UiSelect v-model="formData.update_method" :disabled="saving">
|
||||||
@@ -418,11 +444,8 @@ onMounted(() => {
|
|||||||
<UiSelectValue />
|
<UiSelectValue />
|
||||||
</UiSelectTrigger>
|
</UiSelectTrigger>
|
||||||
<UiSelectContent>
|
<UiSelectContent>
|
||||||
<UiSelectItem value="full">
|
<UiSelectItem value="auto">
|
||||||
{{ t('admin.versions.methods.full') }}
|
{{ t('admin.versions.methods.auto') }}
|
||||||
</UiSelectItem>
|
|
||||||
<UiSelectItem value="hot">
|
|
||||||
{{ t('admin.versions.methods.hot') }}
|
|
||||||
</UiSelectItem>
|
</UiSelectItem>
|
||||||
<UiSelectItem value="manual">
|
<UiSelectItem value="manual">
|
||||||
{{ t('admin.versions.methods.manual') }}
|
{{ t('admin.versions.methods.manual') }}
|
||||||
@@ -485,6 +508,10 @@ onMounted(() => {
|
|||||||
<span class="text-muted-foreground">{{ t('admin.versions.createForm.previewForcedUpdate') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.versions.createForm.previewForcedUpdate') }}</span>
|
||||||
<span>{{ formData.update_strategy === 'forced' ? t('common.yes') : t('common.no') }}</span>
|
<span>{{ formData.update_strategy === 'forced' ? t('common.yes') : t('common.no') }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="flex justify-between text-sm">
|
||||||
|
<span class="text-muted-foreground">{{ t('admin.versions.createForm.previewUpdateType') }}</span>
|
||||||
|
<span>{{ typeLabel }}</span>
|
||||||
|
</div>
|
||||||
<div class="flex justify-between text-sm">
|
<div class="flex justify-between text-sm">
|
||||||
<span class="text-muted-foreground">{{ t('admin.versions.createForm.previewUpdateMethod') }}</span>
|
<span class="text-muted-foreground">{{ t('admin.versions.createForm.previewUpdateMethod') }}</span>
|
||||||
<span>{{ methodLabel }}</span>
|
<span>{{ methodLabel }}</span>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
|
|
||||||
export const updateStrategySchema = z.enum(['optional', 'forced'])
|
export const updateStrategySchema = z.enum(['optional', 'forced'])
|
||||||
export const updateMethodSchema = z.enum(['manual', 'hot', 'full'])
|
export const updateTypeSchema = z.enum(['full', 'patch'])
|
||||||
|
export const updateMethodSchema = z.enum(['auto', 'manual'])
|
||||||
export const versionStatusSchema = z.enum(['active', 'superseded'])
|
export const versionStatusSchema = z.enum(['active', 'superseded'])
|
||||||
|
|
||||||
export const versionSchema = z.object({
|
export const versionSchema = z.object({
|
||||||
@@ -12,8 +13,8 @@ export const versionSchema = z.object({
|
|||||||
description: z.string(),
|
description: z.string(),
|
||||||
file_path: z.string(),
|
file_path: z.string(),
|
||||||
file_size: z.number(),
|
file_size: z.number(),
|
||||||
force_update: z.boolean(),
|
|
||||||
update_strategy: updateStrategySchema,
|
update_strategy: updateStrategySchema,
|
||||||
|
update_type: updateTypeSchema,
|
||||||
update_method: updateMethodSchema,
|
update_method: updateMethodSchema,
|
||||||
status: versionStatusSchema,
|
status: versionStatusSchema,
|
||||||
changelog: z.string(),
|
changelog: z.string(),
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ const tableRef = ref()
|
|||||||
|
|
||||||
const appFilter = ref<string>('')
|
const appFilter = ref<string>('')
|
||||||
const strategyFilter = ref<string>('')
|
const strategyFilter = ref<string>('')
|
||||||
|
const typeFilter = ref<string>('')
|
||||||
const methodFilter = ref<string>('')
|
const methodFilter = ref<string>('')
|
||||||
const searchFilter = ref<string>('')
|
const searchFilter = ref<string>('')
|
||||||
const currentPage = ref(1)
|
const currentPage = ref(1)
|
||||||
@@ -56,10 +57,14 @@ const strategyOptions = computed(() => [
|
|||||||
{ label: t('admin.versions.strategies.forced'), value: 'forced' },
|
{ label: t('admin.versions.strategies.forced'), value: 'forced' },
|
||||||
])
|
])
|
||||||
|
|
||||||
|
const typeOptions = computed(() => [
|
||||||
|
{ label: t('admin.versions.types.full'), value: 'full' },
|
||||||
|
{ label: t('admin.versions.types.patch'), value: 'patch' },
|
||||||
|
])
|
||||||
|
|
||||||
const methodOptions = computed(() => [
|
const methodOptions = computed(() => [
|
||||||
|
{ label: t('admin.versions.methods.auto'), value: 'auto' },
|
||||||
{ label: t('admin.versions.methods.manual'), value: 'manual' },
|
{ label: t('admin.versions.methods.manual'), value: 'manual' },
|
||||||
{ label: t('admin.versions.methods.hot'), value: 'hot' },
|
|
||||||
{ label: t('admin.versions.methods.full'), value: 'full' },
|
|
||||||
])
|
])
|
||||||
|
|
||||||
const totalSize = computed(() => {
|
const totalSize = computed(() => {
|
||||||
@@ -113,6 +118,9 @@ async function fetchVersions() {
|
|||||||
if (strategyFilter.value) {
|
if (strategyFilter.value) {
|
||||||
params.append('update_strategy', strategyFilter.value)
|
params.append('update_strategy', strategyFilter.value)
|
||||||
}
|
}
|
||||||
|
if (typeFilter.value) {
|
||||||
|
params.append('update_type', typeFilter.value)
|
||||||
|
}
|
||||||
if (methodFilter.value) {
|
if (methodFilter.value) {
|
||||||
params.append('update_method', methodFilter.value)
|
params.append('update_method', methodFilter.value)
|
||||||
}
|
}
|
||||||
@@ -179,6 +187,8 @@ function handleExport() {
|
|||||||
params.append('application_id', appFilter.value)
|
params.append('application_id', appFilter.value)
|
||||||
if (strategyFilter.value)
|
if (strategyFilter.value)
|
||||||
params.append('update_strategy', strategyFilter.value)
|
params.append('update_strategy', strategyFilter.value)
|
||||||
|
if (typeFilter.value)
|
||||||
|
params.append('update_type', typeFilter.value)
|
||||||
if (methodFilter.value)
|
if (methodFilter.value)
|
||||||
params.append('update_method', methodFilter.value)
|
params.append('update_method', methodFilter.value)
|
||||||
|
|
||||||
@@ -230,7 +240,7 @@ watch([currentPage, pageSize], () => {
|
|||||||
fetchVersions()
|
fetchVersions()
|
||||||
})
|
})
|
||||||
|
|
||||||
watch([appFilter, strategyFilter, methodFilter, searchFilter], () => {
|
watch([appFilter, strategyFilter, typeFilter, methodFilter, searchFilter], () => {
|
||||||
currentPage.value = 1
|
currentPage.value = 1
|
||||||
fetchVersions()
|
fetchVersions()
|
||||||
})
|
})
|
||||||
@@ -336,6 +346,11 @@ watch([appFilter, strategyFilter, methodFilter, searchFilter], () => {
|
|||||||
:title="t('admin.versions.strategy')"
|
:title="t('admin.versions.strategy')"
|
||||||
:options="strategyOptions"
|
:options="strategyOptions"
|
||||||
/>
|
/>
|
||||||
|
<SingleFilter
|
||||||
|
v-model="typeFilter"
|
||||||
|
:title="t('admin.versions.columns.type')"
|
||||||
|
:options="typeOptions"
|
||||||
|
/>
|
||||||
<SingleFilter
|
<SingleFilter
|
||||||
v-model="methodFilter"
|
v-model="methodFilter"
|
||||||
:title="t('admin.versions.method')"
|
:title="t('admin.versions.method')"
|
||||||
|
|||||||
@@ -1417,6 +1417,7 @@
|
|||||||
"description": "Description",
|
"description": "Description",
|
||||||
"fileSize": "File Size",
|
"fileSize": "File Size",
|
||||||
"strategy": "Strategy",
|
"strategy": "Strategy",
|
||||||
|
"type": "Type",
|
||||||
"method": "Method",
|
"method": "Method",
|
||||||
"status": "Status",
|
"status": "Status",
|
||||||
"createdAt": "Created At",
|
"createdAt": "Created At",
|
||||||
@@ -1426,10 +1427,13 @@
|
|||||||
"optional": "Optional",
|
"optional": "Optional",
|
||||||
"forced": "Forced"
|
"forced": "Forced"
|
||||||
},
|
},
|
||||||
|
"types": {
|
||||||
|
"full": "Full Update",
|
||||||
|
"patch": "Patch Update"
|
||||||
|
},
|
||||||
"methods": {
|
"methods": {
|
||||||
"manual": "Manual",
|
"auto": "Auto Update",
|
||||||
"hot": "Hot",
|
"manual": "Manual Update"
|
||||||
"full": "Full"
|
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"active": "Active",
|
"active": "Active",
|
||||||
@@ -1467,6 +1471,7 @@
|
|||||||
"entryFilePlaceholder": "Select entry file (optional)",
|
"entryFilePlaceholder": "Select entry file (optional)",
|
||||||
"forcedUpdate": "Force Update",
|
"forcedUpdate": "Force Update",
|
||||||
"forcedUpdateDesc": "When enabled, users must update to this version to continue using",
|
"forcedUpdateDesc": "When enabled, users must update to this version to continue using",
|
||||||
|
"updateType": "Update Type",
|
||||||
"updateMethod": "Update Method",
|
"updateMethod": "Update Method",
|
||||||
"baseVersion": "Base Version",
|
"baseVersion": "Base Version",
|
||||||
"baseVersionPlaceholder": "Select base version (patch mode)",
|
"baseVersionPlaceholder": "Select base version (patch mode)",
|
||||||
@@ -1486,6 +1491,7 @@
|
|||||||
"previewFileCount": "File Count",
|
"previewFileCount": "File Count",
|
||||||
"previewEntryFile": "Entry File",
|
"previewEntryFile": "Entry File",
|
||||||
"previewForcedUpdate": "Force Update",
|
"previewForcedUpdate": "Force Update",
|
||||||
|
"previewUpdateType": "Update Type",
|
||||||
"previewUpdateMethod": "Update Method",
|
"previewUpdateMethod": "Update Method",
|
||||||
"previewAutoUpdate": "Auto Update",
|
"previewAutoUpdate": "Auto Update",
|
||||||
"changelogPreview": "Changelog Preview",
|
"changelogPreview": "Changelog Preview",
|
||||||
|
|||||||
@@ -1384,6 +1384,7 @@
|
|||||||
"description": "描述",
|
"description": "描述",
|
||||||
"fileSize": "文件大小",
|
"fileSize": "文件大小",
|
||||||
"strategy": "更新策略",
|
"strategy": "更新策略",
|
||||||
|
"type": "更新类型",
|
||||||
"method": "更新方式",
|
"method": "更新方式",
|
||||||
"status": "状态",
|
"status": "状态",
|
||||||
"createdAt": "创建时间",
|
"createdAt": "创建时间",
|
||||||
@@ -1393,10 +1394,13 @@
|
|||||||
"optional": "可选更新",
|
"optional": "可选更新",
|
||||||
"forced": "强制更新"
|
"forced": "强制更新"
|
||||||
},
|
},
|
||||||
|
"types": {
|
||||||
|
"full": "全量更新",
|
||||||
|
"patch": "补丁更新"
|
||||||
|
},
|
||||||
"methods": {
|
"methods": {
|
||||||
"manual": "手动更新",
|
"auto": "自动更新",
|
||||||
"hot": "热更新",
|
"manual": "手动更新"
|
||||||
"full": "完整更新"
|
|
||||||
},
|
},
|
||||||
"status": {
|
"status": {
|
||||||
"active": "有效",
|
"active": "有效",
|
||||||
@@ -1434,6 +1438,7 @@
|
|||||||
"entryFilePlaceholder": "选择入口文件(可选)",
|
"entryFilePlaceholder": "选择入口文件(可选)",
|
||||||
"forcedUpdate": "强制更新",
|
"forcedUpdate": "强制更新",
|
||||||
"forcedUpdateDesc": "启用后用户必须更新到此版本才能继续使用",
|
"forcedUpdateDesc": "启用后用户必须更新到此版本才能继续使用",
|
||||||
|
"updateType": "更新类型",
|
||||||
"updateMethod": "更新方式",
|
"updateMethod": "更新方式",
|
||||||
"baseVersion": "基础版本",
|
"baseVersion": "基础版本",
|
||||||
"baseVersionPlaceholder": "选择基础版本(补丁模式)",
|
"baseVersionPlaceholder": "选择基础版本(补丁模式)",
|
||||||
@@ -1453,6 +1458,7 @@
|
|||||||
"previewFileCount": "文件数量",
|
"previewFileCount": "文件数量",
|
||||||
"previewEntryFile": "入口文件",
|
"previewEntryFile": "入口文件",
|
||||||
"previewForcedUpdate": "强制更新",
|
"previewForcedUpdate": "强制更新",
|
||||||
|
"previewUpdateType": "更新类型",
|
||||||
"previewUpdateMethod": "更新方式",
|
"previewUpdateMethod": "更新方式",
|
||||||
"previewAutoUpdate": "自动更新",
|
"previewAutoUpdate": "自动更新",
|
||||||
"changelogPreview": "更新日志预览",
|
"changelogPreview": "更新日志预览",
|
||||||
|
|||||||
Reference in New Issue
Block a user