feat: 版本管理支持多入口文件、安装选项(桌面快捷方式/开始菜单/自动运行/默认安装目录)
This commit is contained in:
@@ -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(() => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 更新类型选择(上传前选择) -->
|
||||
<div class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.updateType') }} <span class="text-destructive">*</span></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 v-if="formData.update_type === 'patch'" class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.baseVersion') }} <span class="text-destructive">*</span></UiLabel>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{{ t('admin.versions.createForm.baseVersionDesc') }}
|
||||
</p>
|
||||
<UiSelect v-model="formData.base_version_id" :disabled="saving || loadingBaseVersions">
|
||||
<UiSelectTrigger>
|
||||
<UiSelectValue :placeholder="t('admin.versions.createForm.baseVersionPlaceholder')" />
|
||||
</UiSelectTrigger>
|
||||
<UiSelectContent>
|
||||
<UiSelectItem
|
||||
v-for="v in baseVersions"
|
||||
:key="v.id"
|
||||
:value="String(v.id)"
|
||||
>
|
||||
{{ v.version }}
|
||||
</UiSelectItem>
|
||||
</UiSelectContent>
|
||||
</UiSelect>
|
||||
<p v-if="baseVersions.length === 0 && !loadingBaseVersions" class="text-sm text-destructive">
|
||||
{{ t('admin.versions.createForm.noBaseVersion') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.updateFile') }}</UiLabel>
|
||||
<div class="border-2 border-dashed rounded-lg p-4">
|
||||
@@ -534,22 +607,68 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 多选入口文件 -->
|
||||
<div v-if="executableFiles.length > 0" class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.entryFile') }}</UiLabel>
|
||||
<UiSelect v-model="formData.entry_file" :disabled="saving">
|
||||
<UiSelectTrigger>
|
||||
<UiSelectValue :placeholder="t('admin.versions.createForm.entryFilePlaceholder')" />
|
||||
</UiSelectTrigger>
|
||||
<UiSelectContent>
|
||||
<UiSelectItem
|
||||
v-for="file in executableFiles"
|
||||
:key="file.file_path"
|
||||
:value="file.file_path"
|
||||
<UiLabel>{{ t('admin.versions.createForm.entryFiles') }}</UiLabel>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{{ t('admin.versions.createForm.entryFilesDesc') }}
|
||||
</p>
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="file in executableFiles"
|
||||
:key="file.file_path"
|
||||
class="flex items-center gap-3 rounded-lg border p-3"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="formData.entry_files.includes(file.file_path)"
|
||||
class="h-4 w-4 rounded border-gray-300"
|
||||
@change="toggleEntryFile(file.file_path)"
|
||||
>
|
||||
{{ file.file_name }} ({{ formatFileSize(file.file_size) }})
|
||||
</UiSelectItem>
|
||||
</UiSelectContent>
|
||||
</UiSelect>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm font-medium">{{ file.file_name }}</p>
|
||||
<p class="text-xs text-muted-foreground">{{ file.file_path }} ({{ formatFileSize(file.file_size) }})</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 安装选项 -->
|
||||
<div class="space-y-4 rounded-lg border p-4">
|
||||
<h4 class="font-medium">{{ t('admin.versions.createForm.installOptions') }}</h4>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="space-y-0.5">
|
||||
<UiLabel class="text-sm">{{ t('admin.versions.createForm.createDesktopShortcut') }}</UiLabel>
|
||||
<p class="text-xs text-muted-foreground">{{ t('admin.versions.createForm.createDesktopShortcutDesc') }}</p>
|
||||
</div>
|
||||
<UiSwitch v-model="formData.create_desktop_shortcut" :disabled="saving" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="space-y-0.5">
|
||||
<UiLabel class="text-sm">{{ t('admin.versions.createForm.createStartMenu') }}</UiLabel>
|
||||
<p class="text-xs text-muted-foreground">{{ t('admin.versions.createForm.createStartMenuDesc') }}</p>
|
||||
</div>
|
||||
<UiSwitch v-model="formData.create_start_menu" :disabled="saving" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="space-y-0.5">
|
||||
<UiLabel class="text-sm">{{ t('admin.versions.createForm.autoRun') }}</UiLabel>
|
||||
<p class="text-xs text-muted-foreground">{{ t('admin.versions.createForm.autoRunDesc') }}</p>
|
||||
</div>
|
||||
<UiSwitch v-model="formData.auto_run" :disabled="saving" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<UiLabel class="text-sm">{{ t('admin.versions.createForm.defaultInstallDir') }}</UiLabel>
|
||||
<UiInput
|
||||
v-model="formData.default_install_dir"
|
||||
:placeholder="t('admin.versions.createForm.defaultInstallDirPlaceholder')"
|
||||
:disabled="saving"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between rounded-lg border p-4">
|
||||
@@ -567,48 +686,6 @@ onMounted(() => {
|
||||
/>
|
||||
</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 v-if="formData.update_type === 'patch'" class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.baseVersion') }}</UiLabel>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{{ t('admin.versions.createForm.baseVersionDesc') }}
|
||||
</p>
|
||||
<UiSelect v-model="formData.base_version_id" :disabled="saving || loadingBaseVersions">
|
||||
<UiSelectTrigger>
|
||||
<UiSelectValue :placeholder="t('admin.versions.createForm.baseVersionPlaceholder')" />
|
||||
</UiSelectTrigger>
|
||||
<UiSelectContent>
|
||||
<UiSelectItem
|
||||
v-for="v in baseVersions"
|
||||
:key="v.id"
|
||||
:value="String(v.id)"
|
||||
>
|
||||
{{ v.version }}
|
||||
</UiSelectItem>
|
||||
</UiSelectContent>
|
||||
</UiSelect>
|
||||
<p v-if="baseVersions.length === 0 && !loadingBaseVersions" class="text-sm text-destructive">
|
||||
{{ t('admin.versions.createForm.noBaseVersion') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.updateMethod') }}</UiLabel>
|
||||
<UiSelect v-model="formData.update_method" :disabled="saving">
|
||||
|
||||
@@ -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<string, string> = {
|
||||
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(() => {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 更新类型选择(上传前选择) -->
|
||||
<div class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.updateType') }} <span class="text-destructive">*</span></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 v-if="formData.update_type === 'patch'" class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.baseVersion') }} <span class="text-destructive">*</span></UiLabel>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{{ t('admin.versions.createForm.baseVersionDesc') }}
|
||||
</p>
|
||||
<UiSelect v-model="formData.base_version_id" :disabled="saving || loadingBaseVersions">
|
||||
<UiSelectTrigger>
|
||||
<UiSelectValue :placeholder="t('admin.versions.createForm.baseVersionPlaceholder')" />
|
||||
</UiSelectTrigger>
|
||||
<UiSelectContent>
|
||||
<UiSelectItem
|
||||
v-for="v in baseVersions"
|
||||
:key="v.id"
|
||||
:value="String(v.id)"
|
||||
>
|
||||
{{ v.version }}
|
||||
</UiSelectItem>
|
||||
</UiSelectContent>
|
||||
</UiSelect>
|
||||
<p v-if="baseVersions.length === 0 && !loadingBaseVersions" class="text-sm text-destructive">
|
||||
{{ t('admin.versions.createForm.noBaseVersion') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.updateFile') }}</UiLabel>
|
||||
<div class="border-2 border-dashed rounded-lg p-4">
|
||||
@@ -473,25 +537,68 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 多选入口文件 -->
|
||||
<div v-if="executableFiles.length > 0" class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.entryFile') }}</UiLabel>
|
||||
<UiLabel>{{ t('admin.versions.createForm.entryFiles') }}</UiLabel>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{{ t('admin.versions.createForm.entryFileDesc') }}
|
||||
{{ t('admin.versions.createForm.entryFilesDesc') }}
|
||||
</p>
|
||||
<UiSelect v-model="formData.entry_file" :disabled="saving">
|
||||
<UiSelectTrigger>
|
||||
<UiSelectValue :placeholder="t('admin.versions.createForm.entryFilePlaceholder')" />
|
||||
</UiSelectTrigger>
|
||||
<UiSelectContent>
|
||||
<UiSelectItem
|
||||
v-for="file in executableFiles"
|
||||
:key="file.file_path"
|
||||
:value="file.file_path"
|
||||
<div class="space-y-2">
|
||||
<div
|
||||
v-for="file in executableFiles"
|
||||
:key="file.file_path"
|
||||
class="flex items-center gap-3 rounded-lg border p-3"
|
||||
>
|
||||
<input
|
||||
type="checkbox"
|
||||
:checked="formData.entry_files.includes(file.file_path)"
|
||||
class="h-4 w-4 rounded border-gray-300"
|
||||
@change="toggleEntryFile(file.file_path)"
|
||||
>
|
||||
{{ file.file_name }} ({{ formatFileSize(file.file_size) }})
|
||||
</UiSelectItem>
|
||||
</UiSelectContent>
|
||||
</UiSelect>
|
||||
<div class="flex-1">
|
||||
<p class="text-sm font-medium">{{ file.file_name }}</p>
|
||||
<p class="text-xs text-muted-foreground">{{ file.file_path }} ({{ formatFileSize(file.file_size) }})</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 安装选项 -->
|
||||
<div class="space-y-4 rounded-lg border p-4">
|
||||
<h4 class="font-medium">{{ t('admin.versions.createForm.installOptions') }}</h4>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="space-y-0.5">
|
||||
<UiLabel class="text-sm">{{ t('admin.versions.createForm.createDesktopShortcut') }}</UiLabel>
|
||||
<p class="text-xs text-muted-foreground">{{ t('admin.versions.createForm.createDesktopShortcutDesc') }}</p>
|
||||
</div>
|
||||
<UiSwitch v-model="formData.create_desktop_shortcut" :disabled="saving" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="space-y-0.5">
|
||||
<UiLabel class="text-sm">{{ t('admin.versions.createForm.createStartMenu') }}</UiLabel>
|
||||
<p class="text-xs text-muted-foreground">{{ t('admin.versions.createForm.createStartMenuDesc') }}</p>
|
||||
</div>
|
||||
<UiSwitch v-model="formData.create_start_menu" :disabled="saving" />
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="space-y-0.5">
|
||||
<UiLabel class="text-sm">{{ t('admin.versions.createForm.autoRun') }}</UiLabel>
|
||||
<p class="text-xs text-muted-foreground">{{ t('admin.versions.createForm.autoRunDesc') }}</p>
|
||||
</div>
|
||||
<UiSwitch v-model="formData.auto_run" :disabled="saving" />
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<UiLabel class="text-sm">{{ t('admin.versions.createForm.defaultInstallDir') }}</UiLabel>
|
||||
<UiInput
|
||||
v-model="formData.default_install_dir"
|
||||
:placeholder="t('admin.versions.createForm.defaultInstallDirPlaceholder')"
|
||||
:disabled="saving"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center justify-between rounded-lg border p-4">
|
||||
@@ -509,48 +616,6 @@ onMounted(() => {
|
||||
/>
|
||||
</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 v-if="formData.update_type === 'patch'" class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.baseVersion') }}</UiLabel>
|
||||
<p class="text-sm text-muted-foreground">
|
||||
{{ t('admin.versions.createForm.baseVersionDesc') }}
|
||||
</p>
|
||||
<UiSelect v-model="formData.base_version_id" :disabled="saving || loadingBaseVersions">
|
||||
<UiSelectTrigger>
|
||||
<UiSelectValue :placeholder="t('admin.versions.createForm.baseVersionPlaceholder')" />
|
||||
</UiSelectTrigger>
|
||||
<UiSelectContent>
|
||||
<UiSelectItem
|
||||
v-for="v in baseVersions"
|
||||
:key="v.id"
|
||||
:value="String(v.id)"
|
||||
>
|
||||
{{ v.version }}
|
||||
</UiSelectItem>
|
||||
</UiSelectContent>
|
||||
</UiSelect>
|
||||
<p v-if="baseVersions.length === 0 && !loadingBaseVersions" class="text-sm text-destructive">
|
||||
{{ t('admin.versions.createForm.noBaseVersion') }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<UiLabel>{{ t('admin.versions.createForm.updateMethod') }}</UiLabel>
|
||||
<UiSelect v-model="formData.update_method" :disabled="saving">
|
||||
|
||||
@@ -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": "更新类型",
|
||||
|
||||
Reference in New Issue
Block a user