Merge branch fix/app-version-batch

This commit is contained in:
Zhou Weidong authored and trae-solo-agent committed 2026-10-23 16:46:29 +08:00
commit 18234ebd5b
2 files changed
+253 -58

No files matched your search

+4
View File
@@ -49,6 +49,10 @@ export const createAppVersionApi = (data: AppVersionSaveData) => {
return request.post<string>({ url: '/admin/app-versions', data })
}
export const batchCreateAppVersionsApi = (data: AppVersionSaveData[]) => {
return request.post<string[]>({ url: '/admin/app-versions/batch', data })
}
export const updateAppVersionApi = (id: string | number, data: AppVersionSaveData) => {
return request.post<void>({ url: `/admin/app-versions/${id}`, data })
}
+249 -58
View File
@@ -8,6 +8,7 @@ import PageHeading from '@/views/components/PageHeading.vue'
import { AppIcons } from '@/constants/icons'
import { dateCell } from '@/utils/format'
import {
batchCreateAppVersionsApi,
createAppVersionApi,
deleteAppVersionApi,
listAppVersionsApi,
@@ -43,16 +44,23 @@ const formLoading = ref(false)
const editingId = ref<string | null>(null)
const formRef = ref<FormInstance>()
const emptyForm: AppVersionSaveData = {
platform: 'ANDROID',
/** 新建时两个平台一起配(各自可启停),编辑时用单份。 */
interface PlatformBlock {
enabled: boolean
data: AppVersionSaveData
}
const emptyPlatform = (platform: 'ANDROID' | 'IOS'): AppVersionSaveData => ({
platform,
versionCode: 0,
versionName: '',
downloadUrl: '',
updateContent: '',
forceUpdate: 0,
minSupportedCode: 0
}
const form = ref<AppVersionSaveData>({ ...emptyForm })
})
const androidBlock = ref<PlatformBlock>({ enabled: true, data: emptyPlatform('ANDROID') })
const iosBlock = ref<PlatformBlock>({ enabled: true, data: emptyPlatform('IOS') })
const editForm = ref<AppVersionSaveData>(emptyPlatform('ANDROID'))
const rules: FormRules = {
platform: [{ required: true, message: '请选择平台', trigger: 'change' }],
@@ -91,13 +99,14 @@ async function fetchList() {
function openCreate() {
editingId.value = null
form.value = { ...emptyForm }
androidBlock.value = { enabled: true, data: emptyPlatform('ANDROID') }
iosBlock.value = { enabled: true, data: emptyPlatform('IOS') }
dialogOpen.value = true
}
function openEdit(row: AppVersionItem) {
editingId.value = row.id
form.value = {
editForm.value = {
platform: row.platform,
versionCode: row.versionCode,
versionName: row.versionName,
@@ -110,26 +119,29 @@ function openEdit(row: AppVersionItem) {
}
async function submit() {
if (!formRef.value) return
await formRef.value.validate(async (valid) => {
if (!valid) return
formLoading.value = true
try {
if (editingId.value) {
await updateAppVersionApi(editingId.value, form.value)
ElMessage.success('已更新')
} else {
await createAppVersionApi(form.value)
ElMessage.success('已创建')
formLoading.value = true
try {
if (editingId.value) {
await updateAppVersionApi(editingId.value, editForm.value)
ElMessage.success('已更新')
} else {
const toCreate: AppVersionSaveData[] = []
if (androidBlock.value.enabled) toCreate.push(androidBlock.value.data)
if (iosBlock.value.enabled) toCreate.push(iosBlock.value.data)
if (toCreate.length === 0) {
ElMessage.warning('至少启用一个平台')
return
}
dialogOpen.value = false
await fetchList()
} catch {
/* axios interceptor 已弹消息 */
} finally {
formLoading.value = false
await batchCreateAppVersionsApi(toCreate)
ElMessage.success(`已创建 ${toCreate.length} 条版本`)
}
})
dialogOpen.value = false
await fetchList()
} catch {
/* axios interceptor 已弹消息 */
} finally {
formLoading.value = false
}
}
async function toggleStatus(row: AppVersionItem) {
@@ -241,23 +253,23 @@ const forceText = computed(() => (v: number) => (v === 1 ? '强制' : '可选升
<el-dialog
v-model="dialogOpen"
:title="editingId ? '编辑版本' : '新建版本'"
width="640px"
width="820px"
@close="() => formRef?.resetFields()"
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px" v-loading="formLoading">
<!-- 编辑模式:单平台 -->
<el-form
v-if="editingId"
ref="formRef"
:model="editForm"
:rules="rules"
label-width="120px"
v-loading="formLoading"
>
<el-row :gutter="12">
<el-col :span="12">
<el-form-item label="平台" prop="platform">
<el-radio-group v-model="form.platform">
<el-radio-button value="ANDROID">Android</el-radio-button>
<el-radio-button value="IOS">iOS</el-radio-button>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="8">
<el-form-item label="整数版本" prop="versionCode">
<el-input-number
v-model="form.versionCode"
v-model="editForm.versionCode"
:min="1"
:max="999999"
controls-position="right"
@@ -265,39 +277,196 @@ const forceText = computed(() => (v: number) => (v === 1 ? '强制' : '可选升
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-col :span="16">
<el-form-item label="展示版本" prop="versionName">
<el-input v-model="form.versionName" placeholder="如 1.2.0" />
<el-input v-model="editForm.versionName" placeholder="如 1.2.0" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="下载地址" prop="downloadUrl">
<el-input v-model="form.downloadUrl" placeholder="Android apk / iOS App Store 链接" />
</el-form-item>
<el-form-item label="强制升级">
<el-switch
v-model="form.forceUpdate"
:active-value="1"
:inactive-value="0"
active-text="强制"
inactive-text="可选"
/>
</el-form-item>
<el-form-item label="兜底最低版本">
<el-input-number v-model="form.minSupportedCode" :min="0" controls-position="right" />
<span class="hint">低于此版本的客户端一律强制升级,0=不开启</span>
<el-input v-model="editForm.downloadUrl" placeholder="Android apk / iOS App Store 链接" />
</el-form-item>
<el-row :gutter="12">
<el-col :span="12">
<el-form-item label="强制升级">
<el-switch
v-model="editForm.forceUpdate"
:active-value="1"
:inactive-value="0"
active-text="强制"
inactive-text="可选"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="兜底最低版本">
<el-input-number
v-model="editForm.minSupportedCode"
:min="0"
controls-position="right"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="更新说明">
<el-input
v-model="form.updateContent"
v-model="editForm.updateContent"
type="textarea"
:rows="4"
:rows="3"
placeholder="换行分隔多条更新内容"
maxlength="2000"
show-word-limit
/>
</el-form-item>
</el-form>
<!-- 新建模式:Android + iOS 两块 -->
<div v-else class="dual-blocks" v-loading="formLoading">
<div class="platform-block" :class="{ disabled: !androidBlock.enabled }">
<header>
<span class="platform-title">Android</span>
<el-switch
v-model="androidBlock.enabled"
active-text="启用"
inactive-text="跳过"
inline-prompt
/>
</header>
<el-form
:model="androidBlock.data"
:rules="rules"
label-width="120px"
:disabled="!androidBlock.enabled"
>
<el-row :gutter="12">
<el-col :span="8">
<el-form-item label="整数版本" prop="versionCode">
<el-input-number
v-model="androidBlock.data.versionCode"
:min="1"
:max="999999"
controls-position="right"
class="w-full"
/>
</el-form-item>
</el-col>
<el-col :span="16">
<el-form-item label="展示版本" prop="versionName">
<el-input v-model="androidBlock.data.versionName" placeholder="如 1.2.0" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="下载地址" prop="downloadUrl">
<el-input v-model="androidBlock.data.downloadUrl" placeholder="apk 下载链接" />
</el-form-item>
<el-row :gutter="12">
<el-col :span="12">
<el-form-item label="强制升级">
<el-switch
v-model="androidBlock.data.forceUpdate"
:active-value="1"
:inactive-value="0"
active-text="强制"
inactive-text="可选"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="兜底最低">
<el-input-number
v-model="androidBlock.data.minSupportedCode"
:min="0"
controls-position="right"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="更新说明">
<el-input
v-model="androidBlock.data.updateContent"
type="textarea"
:rows="3"
placeholder="换行分隔多条"
maxlength="2000"
show-word-limit
/>
</el-form-item>
</el-form>
</div>
<div class="platform-block" :class="{ disabled: !iosBlock.enabled }">
<header>
<span class="platform-title">iOS</span>
<el-switch
v-model="iosBlock.enabled"
active-text="启用"
inactive-text="跳过"
inline-prompt
/>
</header>
<el-form
:model="iosBlock.data"
:rules="rules"
label-width="120px"
:disabled="!iosBlock.enabled"
>
<el-row :gutter="12">
<el-col :span="8">
<el-form-item label="整数版本" prop="versionCode">
<el-input-number
v-model="iosBlock.data.versionCode"
:min="1"
:max="999999"
controls-position="right"
class="w-full"
/>
</el-form-item>
</el-col>
<el-col :span="16">
<el-form-item label="展示版本" prop="versionName">
<el-input v-model="iosBlock.data.versionName" placeholder="如 1.2.0" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="下载地址" prop="downloadUrl">
<el-input v-model="iosBlock.data.downloadUrl" placeholder="App Store 链接" />
</el-form-item>
<el-row :gutter="12">
<el-col :span="12">
<el-form-item label="强制升级">
<el-switch
v-model="iosBlock.data.forceUpdate"
:active-value="1"
:inactive-value="0"
active-text="强制"
inactive-text="可选"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="兜底最低">
<el-input-number
v-model="iosBlock.data.minSupportedCode"
:min="0"
controls-position="right"
/>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="更新说明">
<el-input
v-model="iosBlock.data.updateContent"
type="textarea"
:rows="3"
placeholder="换行分隔多条"
maxlength="2000"
show-word-limit
/>
</el-form-item>
</el-form>
</div>
</div>
<template #footer>
<el-button @click="dialogOpen = false">取消</el-button>
<el-button type="primary" :loading="formLoading" @click="submit">
@@ -315,9 +484,31 @@ const forceText = computed(() => (v: number) => (v === 1 ? '强制' : '可选升
margin-top: 12px;
justify-content: flex-end;
}
.hint {
margin-left: 8px;
font-size: 12px;
color: var(--el-text-color-secondary);
.dual-blocks {
display: flex;
flex-direction: column;
gap: 16px;
}
.platform-block {
border: 1px solid var(--el-border-color-lighter);
border-radius: 8px;
padding: 14px 16px 4px;
background: var(--el-fill-color-blank);
header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 6px;
padding-bottom: 8px;
border-bottom: 1px solid var(--el-border-color-lighter);
}
.platform-title {
font-weight: 600;
font-size: 15px;
}
&.disabled {
opacity: 0.5;
border-style: dashed;
}
}
</style>