diff --git a/tenant-platform/components.d.ts b/tenant-platform/components.d.ts index 35b94b1..1092e50 100644 --- a/tenant-platform/components.d.ts +++ b/tenant-platform/components.d.ts @@ -17,6 +17,7 @@ declare module 'vue' { ElDescriptions: typeof import('element-plus/es')['ElDescriptions'] ElDescriptionsItem: typeof import('element-plus/es')['ElDescriptionsItem'] ElDialog: typeof import('element-plus/es')['ElDialog'] + ElDivider: typeof import('element-plus/es')['ElDivider'] ElDropdown: typeof import('element-plus/es')['ElDropdown'] ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem'] ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu'] diff --git a/tenant-platform/src/api/update.ts b/tenant-platform/src/api/update.ts index 10b536c..1773179 100644 --- a/tenant-platform/src/api/update.ts +++ b/tenant-platform/src/api/update.ts @@ -61,6 +61,31 @@ export interface RnBundle { createdAt: string } +export interface UnifiedAppUploadItem { + fileKey: string + platform: 'ANDROID' | 'IOS' + versionName: string + versionCode: number + changeLog?: string + forceUpdate: boolean + appStoreUrl?: string + marketUrl?: string +} + +export interface UnifiedRnUploadItem { + fileKey: string + moduleId: string + platform: 'ANDROID' | 'IOS' + version: string + minCommonVersion?: string + note?: string +} + +export interface UnifiedReleaseManifest { + appVersions: UnifiedAppUploadItem[] + rnBundles: UnifiedRnUploadItem[] +} + export const updateAdminApi = { listAppVersions(appId: string, platform: 'ANDROID' | 'IOS') { return updateClient.get<{ data: AppVersion[] }>('/api/v1/updates/app/list', { @@ -109,4 +134,10 @@ export const updateAdminApi = { headers: { 'Content-Type': 'multipart/form-data' }, }) }, + + uploadUnifiedRelease(formData: FormData) { + return updateClient.post('/api/v1/updates/unified/upload', formData, { + headers: { 'Content-Type': 'multipart/form-data' }, + }) + }, } diff --git a/tenant-platform/src/views/update/VersionManagementView.vue b/tenant-platform/src/views/update/VersionManagementView.vue index a914147..37d9ca1 100644 --- a/tenant-platform/src/views/update/VersionManagementView.vue +++ b/tenant-platform/src/views/update/VersionManagementView.vue @@ -3,6 +3,9 @@ + + 一键上传 + @@ -186,6 +189,73 @@ 上传 + + + + + Android / iOS 整包 + + + {{ item.platform === 'ANDROID' ? 'Android' : 'iOS' }} + + + + + + + + + + + 选择文件 + + + + + + + + Bundle 插件上传 + 新增插件 + + + + 插件 {{ index + 1 }} + 删除 + + + + + + + + + + + + + + + 选择文件 + + + + + + + 取消 + 上传 + + + @@ -200,7 +270,7 @@ const appId = route.params.appId as string const activeTab = ref('app') const appPlatform = ref<'ANDROID' | 'IOS'>('ANDROID') -const rnPlatform = ref('') +const rnPlatform = ref<'ANDROID' | 'IOS' | ''>('') const rnModuleFilter = ref('') const appVersions = ref([]) @@ -235,6 +305,42 @@ const rnUploadForm = ref({ file: null as File | null, }) +const showUnifiedUpload = ref(false) +const uploadingUnified = ref(false) +const unifiedAppForms = ref([ + { + key: 'ANDROID', + enabled: true, + platform: 'ANDROID' as const, + versionName: '', + versionCode: 1, + forceUpdate: false, + changeLog: '', + file: null as File | null, + }, + { + key: 'IOS', + enabled: true, + platform: 'IOS' as const, + versionName: '', + versionCode: 1, + forceUpdate: false, + changeLog: '', + file: null as File | null, + }, +]) +const unifiedBundleForms = ref([ + { + key: 'bundle-0', + moduleId: '', + platform: 'ANDROID' as 'ANDROID' | 'IOS', + version: '', + minCommonVersion: '', + note: '', + file: null as File | null, + }, +]) + function formatTime(t: string) { return t ? new Date(t).toLocaleString('zh-CN') : '-' } @@ -356,6 +462,86 @@ async function submitRnUpload() { } finally { uploadingRn.value = false } } +function addUnifiedBundle() { + unifiedBundleForms.value.push({ + key: `bundle-${Date.now()}-${unifiedBundleForms.value.length}`, + moduleId: '', + platform: 'ANDROID', + version: '', + minCommonVersion: '', + note: '', + file: null, + }) +} + +function removeUnifiedBundle(index: number) { + unifiedBundleForms.value.splice(index, 1) + if (unifiedBundleForms.value.length === 0) { + addUnifiedBundle() + } +} + +async function submitUnifiedUpload() { + const appItems = unifiedAppForms.value + .filter(item => item.enabled && item.file) + .map(item => ({ + fileKey: item.key, + platform: item.platform, + versionName: item.versionName, + versionCode: item.versionCode, + changeLog: item.changeLog || undefined, + forceUpdate: item.forceUpdate, + })) + const bundleItems = unifiedBundleForms.value + .filter(item => item.file) + .map(item => ({ + fileKey: item.key, + moduleId: item.moduleId, + platform: item.platform, + version: item.version, + minCommonVersion: item.minCommonVersion || undefined, + note: item.note || undefined, + })) + + if (!appItems.length && !bundleItems.length) { + ElMessage.warning('请至少选择一个 App 包或 Bundle 文件') + return + } + + for (const item of unifiedAppForms.value) { + if (item.enabled && item.file && (!item.versionName || !item.versionCode)) { + ElMessage.warning('请填写整包版本信息') + return + } + } + for (const item of unifiedBundleForms.value) { + if (item.file && (!item.moduleId || !item.version)) { + ElMessage.warning('请填写 Bundle 版本信息') + return + } + } + + uploadingUnified.value = true + try { + const fd = new FormData() + fd.append('appId', appId) + fd.append('manifest', JSON.stringify({ appVersions: appItems, rnBundles: bundleItems })) + for (const item of unifiedAppForms.value) { + if (item.enabled && item.file) fd.append(item.key, item.file) + } + for (const item of unifiedBundleForms.value) { + if (item.file) fd.append(item.key, item.file) + } + await updateAdminApi.uploadUnifiedRelease(fd) + ElMessage.success('一键上传成功') + showUnifiedUpload.value = false + loadAppVersions() + loadRnBundles() + } finally { + uploadingUnified.value = false + } +} + onMounted(() => { loadAppVersions() loadRnBundles() @@ -364,4 +550,27 @@ onMounted(() => {