| | |
| | | <script setup lang="tsx"> |
| | | import { useMessage } from 'naive-ui' |
| | | import type { FormInst, FormRules, UploadFileInfo } from 'naive-ui' |
| | | import { ref } from 'vue' |
| | | import { useRouter } from 'vue-router' |
| | | import { useMessage } from 'naive-ui' |
| | | import type { UploadFileInfo, FormRules, FormInst } from 'naive-ui' |
| | | import { customUploadRequest, beforeUploadCheck } from '@/utils/upload' |
| | | import { beforeUploadCheck, customUploadRequest } from '@/utils/upload' |
| | | import { releaseAppVersion } from '@/service/api/appVersion' |
| | | |
| | | const router = useRouter() |
| | | const message = useMessage() |
| | | const formRef = ref<FormInst | null>(null) |
| | | const androidUploading = ref(false) |
| | | const iosUploading = ref(false) |
| | | |
| | | const formModel = ref({ |
| | | version: '', |
| | | androidFiles: [] as UploadFileInfo[], |
| | | iosFiles: [] as UploadFileInfo[], |
| | | updateNote: '', |
| | | isForceUpdate: false |
| | | isForceUpdate: false, |
| | | }) |
| | | |
| | | const rules: FormRules = { |
| | | version: [ |
| | | { required: true, message: '请输入版本号', trigger: ['blur', 'input'] } |
| | | { required: true, message: '请输入版本号', trigger: ['blur', 'input'] }, |
| | | ], |
| | | androidFiles: [ |
| | | { required: true, type: 'array', message: '请上传安卓安装包', trigger: ['blur', 'change'] } |
| | | { required: true, type: 'array', message: '请上传安卓安装包', trigger: ['blur', 'change'] }, |
| | | ], |
| | | iosFiles: [ |
| | | { required: true, type: 'array', message: '请上传 iOS 安装包', trigger: ['blur', 'change'] } |
| | | { required: true, type: 'array', message: '请上传 iOS 安装包', trigger: ['blur', 'change'] }, |
| | | ], |
| | | updateNote: [ |
| | | { required: true, message: '请输入更新说明', trigger: ['blur', 'input'] } |
| | | ] |
| | | { required: true, message: '请输入更新说明', trigger: ['blur', 'input'] }, |
| | | ], |
| | | } |
| | | |
| | | function beforeUploadApk(data: { file: UploadFileInfo; fileList: UploadFileInfo[] }) { |
| | | function beforeUploadApk(data: { file: UploadFileInfo, fileList: UploadFileInfo[] }) { |
| | | return beforeUploadCheck(data, { maxSize: 500, allowedTypes: [] }) |
| | | } |
| | | |
| | | function beforeUploadIpa(data: { file: UploadFileInfo; fileList: UploadFileInfo[] }) { |
| | | function beforeUploadIpa(data: { file: UploadFileInfo, fileList: UploadFileInfo[] }) { |
| | | return beforeUploadCheck(data, { maxSize: 500, allowedTypes: [] }) |
| | | } |
| | | |
| | | async function customRequest(options: any) { |
| | | await customUploadRequest(options) |
| | | async function customRequestAndroid(options: any) { |
| | | androidUploading.value = true |
| | | const originalOnError = options.onError |
| | | options.onError = () => { |
| | | formModel.value.androidFiles = [] |
| | | originalOnError?.() |
| | | } |
| | | try { |
| | | let res = await customUploadRequest(options) |
| | | if(!res){ |
| | | formModel.value.androidFiles = [] |
| | | return |
| | | } |
| | | } |
| | | finally { |
| | | androidUploading.value = false |
| | | } |
| | | } |
| | | |
| | | async function customRequestIos(options: any) { |
| | | iosUploading.value = true |
| | | const originalOnError = options.onError |
| | | options.onError = () => { |
| | | formModel.value.iosFiles = [] |
| | | originalOnError?.() |
| | | } |
| | | try { |
| | | let res = await customUploadRequest(options) |
| | | console.log(res,'22222222') |
| | | if(!res){ |
| | | formModel.value.iosFiles = [] |
| | | return |
| | | } |
| | | } |
| | | finally { |
| | | iosUploading.value = false |
| | | } |
| | | } |
| | | |
| | | async function handleConfirm() { |
| | | await formRef.value?.validate(async (errors) => { |
| | | if (!errors) { |
| | | const params = { |
| | | version: formModel.value.version, |
| | | androidFile: formModel.value.androidFiles[0]?.url || '', |
| | | iosFile: formModel.value.iosFiles[0]?.url || '', |
| | | updateDescription: formModel.value.updateNote, |
| | | forceUpdateFlag: formModel.value.isForceUpdate ? 1 : 0 |
| | | } |
| | | |
| | | if (!params.androidFile || !params.iosFile) { |
| | | message.error('请等待文件上传完成') |
| | | return |
| | | } |
| | | |
| | | const { isSuccess } = await releaseAppVersion(params) |
| | | if (isSuccess) { |
| | | message.success('发布成功') |
| | | router.back() |
| | | } |
| | | } else { |
| | | if (errors) { |
| | | message.error('请完善必填信息') |
| | | return |
| | | } |
| | | |
| | | const params = { |
| | | version: formModel.value.version, |
| | | androidFile: formModel.value.androidFiles[0]?.url || '', |
| | | iosFile: formModel.value.iosFiles[0]?.url || '', |
| | | updateDescription: formModel.value.updateNote, |
| | | forceUpdateFlag: formModel.value.isForceUpdate ? 1 : 0, |
| | | } |
| | | |
| | | if (androidUploading.value || iosUploading.value) { |
| | | message.error('请等待文件上传完成') |
| | | return |
| | | } |
| | | |
| | | if (!params.androidFile || !params.iosFile) { |
| | | message.error('请等待文件上传完成') |
| | | return |
| | | } |
| | | |
| | | const { isSuccess } = await releaseAppVersion(params) |
| | | if (isSuccess) { |
| | | message.success('发布成功') |
| | | router.back() |
| | | } |
| | | }) |
| | | } |
| | |
| | | |
| | | <template> |
| | | <div class="h-full flex flex-col bg-white rounded-md"> |
| | | <div class="flex items-center px-6 pt-6 mb-6"> |
| | | <div class="flex items-center relative pl-3"> |
| | | <div class="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-4 bg-[#0068FF] rounded-sm"></div> |
| | | <span class="text-base font-bold text-[#1D2129] ml-2">新增APP版本号</span> |
| | | </div> |
| | | <div class="flex items-center px-6 pt-6 mb-6"> |
| | | <div class="flex items-center relative pl-3"> |
| | | <div class="absolute left-0 top-1/2 -translate-y-1/2 w-1 h-4 bg-[#0068FF] rounded-sm" /> |
| | | <span class="text-base font-bold text-[#1D2129] ml-2">新增APP版本号</span> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="flex-1 overflow-y-auto px-6"> |
| | | <n-form |
| | | ref="formRef" |
| | | :model="formModel" |
| | | :rules="rules" |
| | | label-placement="left" |
| | | inline |
| | | require-mark-placement="left" |
| | | :show-feedback="false" |
| | | class="max-w-4xl flex flex-col gap-16px" |
| | | > |
| | | <n-form-item label="版本号" path="version"> |
| | | <n-input v-model:value="formModel.version" placeholder="请输入版本号" class="!w-[400px]" /> |
| | | </n-form-item> |
| | | <div class="flex-1 overflow-y-auto px-6"> |
| | | <n-form |
| | | ref="formRef" |
| | | :model="formModel" |
| | | :rules="rules" |
| | | label-placement="left" |
| | | inline |
| | | require-mark-placement="left" |
| | | :show-feedback="false" |
| | | class="max-w-4xl flex flex-col gap-16px" |
| | | > |
| | | <n-form-item label="版本号" path="version"> |
| | | <n-input v-model:value="formModel.version" placeholder="请输入版本号" class="!w-[400px]" /> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="安卓文件" path="androidFiles"> |
| | | <div class="flex flex-col gap-2"> |
| | | <n-upload |
| | | v-model:file-list="formModel.androidFiles" |
| | | :max="1" |
| | | accept=".apk" |
| | | :on-before-upload="beforeUploadApk" |
| | | :custom-request="customRequest" |
| | | :show-file-list="false" |
| | | > |
| | | <n-button style="width: 100px"> |
| | | <template #icon> |
| | | <icon-park-outline-upload /> |
| | | </template> |
| | | {{ formModel.androidFiles.length > 0 ? '重新上传' : '点击上传' }} |
| | | </n-button> |
| | | </n-upload> |
| | | |
| | | <div v-if="formModel.androidFiles.length > 0" class="flex items-center bg-[#F5F6F8] px-3 py-2 rounded w-fit gap-3"> |
| | | <svg-icons-android-filled class="text-[#00B42A] text-lg" /> |
| | | <span class="text-sm text-[#1D2129] max-w-[200px] truncate" :title="formModel.androidFiles[0].name">{{ formModel.androidFiles[0].name }}</span> |
| | | <icon-park-outline-close class="text-gray-400 cursor-pointer hover:text-gray-600" @click="formModel.androidFiles = []" /> |
| | | </div> |
| | | </div> |
| | | </n-form-item> |
| | | <n-form-item label="安卓文件" path="androidFiles"> |
| | | <div class="flex flex-col gap-2"> |
| | | <n-upload |
| | | v-model:file-list="formModel.androidFiles" |
| | | :max="1" |
| | | accept=".apk" |
| | | :on-before-upload="beforeUploadApk" |
| | | :custom-request="customRequestAndroid" |
| | | :show-file-list="false" |
| | | > |
| | | <n-button style="width: 100px" :loading="androidUploading" :disabled="androidUploading"> |
| | | <template #icon> |
| | | <icon-park-outline-upload /> |
| | | </template> |
| | | {{ formModel.androidFiles.length > 0 ? '重新上传' : '点击上传' }} |
| | | </n-button> |
| | | </n-upload> |
| | | |
| | | <n-form-item label="iOS文件" path="iosFiles"> |
| | | <div class="flex flex-col gap-2"> |
| | | <n-upload |
| | | v-model:file-list="formModel.iosFiles" |
| | | :max="1" |
| | | accept=".ipa" |
| | | :on-before-upload="beforeUploadIpa" |
| | | :custom-request="customRequest" |
| | | :show-file-list="false" |
| | | > |
| | | <n-button style="width: 100px"> |
| | | <template #icon> |
| | | <icon-park-outline-upload /> |
| | | </template> |
| | | {{ formModel.iosFiles.length > 0 ? '重新上传' : '点击上传' }} |
| | | </n-button> |
| | | </n-upload> |
| | | <div v-if="formModel.androidFiles.length > 0" class="flex items-center bg-[#F5F6F8] px-3 py-2 rounded w-fit gap-3"> |
| | | <svg-icons-android-filled class="text-[#00B42A] text-lg" /> |
| | | <span class="text-sm text-[#1D2129] max-w-[200px] truncate" :title="formModel.androidFiles[0].name">{{ formModel.androidFiles[0].name }}</span> |
| | | <n-spin v-if="androidUploading" size="small" /> |
| | | <icon-park-outline-close v-else class="text-gray-400 cursor-pointer hover:text-gray-600" @click="formModel.androidFiles = []" /> |
| | | </div> |
| | | </div> |
| | | </n-form-item> |
| | | |
| | | <div v-if="formModel.iosFiles.length > 0" class="flex items-center bg-[#F5F6F8] px-3 py-2 rounded w-fit gap-3"> |
| | | <ri-apple-fill class="text-black text-lg" /> |
| | | <span class="text-sm text-[#1D2129] max-w-[200px] truncate" :title="formModel.iosFiles[0].name">{{ formModel.iosFiles[0].name }}</span> |
| | | <icon-park-outline-close class="text-gray-400 cursor-pointer hover:text-gray-600" @click="formModel.iosFiles = []" /> |
| | | </div> |
| | | </div> |
| | | </n-form-item> |
| | | <n-form-item label="iOS文件" path="iosFiles"> |
| | | <div class="flex flex-col gap-2"> |
| | | <n-upload |
| | | v-model:file-list="formModel.iosFiles" |
| | | :max="1" |
| | | accept=".ipa" |
| | | :on-before-upload="beforeUploadIpa" |
| | | :custom-request="customRequestIos" |
| | | :show-file-list="false" |
| | | > |
| | | <n-button style="width: 100px" :loading="iosUploading" :disabled="iosUploading"> |
| | | <template #icon> |
| | | <icon-park-outline-upload /> |
| | | </template> |
| | | {{ formModel.iosFiles.length > 0 ? '重新上传' : '点击上传' }} |
| | | </n-button> |
| | | </n-upload> |
| | | |
| | | <n-form-item label="更新说明" path="updateNote" label-placement="top"> |
| | | <n-input |
| | | v-model:value="formModel.updateNote" |
| | | type="textarea" |
| | | placeholder="请输入" |
| | | :autosize="{ minRows: 6, maxRows: 10 }" |
| | | show-count |
| | | style="width: 750px;" |
| | | :maxlength="200" |
| | | /> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="强制更新" path="isForceUpdate" label-placement="left"> |
| | | <n-switch v-model:value="formModel.isForceUpdate" /> |
| | | </n-form-item> |
| | | </n-form> |
| | | </div> |
| | | |
| | | <div class="flex justify-end gap-3 mt-auto px-6 py-4 border-t border-gray-100"> |
| | | <n-button style="padding: 8px 29px;" @click="handleCancel">返回</n-button> |
| | | <n-button style="padding: 8px 29px;" type="primary" @click="handleConfirm">保存</n-button> |
| | | </div> |
| | | <div v-if="formModel.iosFiles.length > 0" class="flex items-center bg-[#F5F6F8] px-3 py-2 rounded w-fit gap-3"> |
| | | <ri-apple-fill class="text-black text-lg" /> |
| | | <span class="text-sm text-[#1D2129] max-w-[200px] truncate" :title="formModel.iosFiles[0].name">{{ formModel.iosFiles[0].name }}</span> |
| | | <n-spin v-if="iosUploading" size="small" /> |
| | | <icon-park-outline-close v-else class="text-gray-400 cursor-pointer hover:text-gray-600" @click="formModel.iosFiles = []" /> |
| | | </div> |
| | | </div> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="更新说明" path="updateNote" label-placement="top"> |
| | | <n-input |
| | | v-model:value="formModel.updateNote" |
| | | type="textarea" |
| | | placeholder="请输入" |
| | | :autosize="{ minRows: 6, maxRows: 10 }" |
| | | show-count |
| | | style="width: 750px;" |
| | | :maxlength="200" |
| | | /> |
| | | </n-form-item> |
| | | |
| | | <n-form-item label="强制更新" path="isForceUpdate" label-placement="left"> |
| | | <n-switch v-model:value="formModel.isForceUpdate" /> |
| | | </n-form-item> |
| | | </n-form> |
| | | </div> |
| | | |
| | | <div class="flex justify-end gap-3 mt-auto px-6 py-4 border-t border-gray-100"> |
| | | <n-button style="padding: 8px 29px;" @click="handleCancel"> |
| | | 返回 |
| | | </n-button> |
| | | <n-button style="padding: 8px 29px;" type="primary" @click="handleConfirm"> |
| | | 保存 |
| | | </n-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | </style> |
| | | </style> |