update:版本控制
This commit is contained in:
parent
f37c98a55c
commit
655ede8d3e
@ -49,7 +49,7 @@
|
||||
</el-upload>
|
||||
</div>
|
||||
</div>
|
||||
<div class="upload-tip"><el-icon><CircleCheck /></el-icon><span>建议从同一个打包输出目录选取两个文件;APK 将在服务端计算 SHA-256。</span></div>
|
||||
<div class="upload-tip"><el-icon><CircleCheck /></el-icon><span>建议从同一个打包输出目录选取两个文件;APK 由浏览器直传 OSS,后台校验文件并计算 SHA-256。</span></div>
|
||||
</section>
|
||||
|
||||
<section class="panel details-panel">
|
||||
@ -74,6 +74,10 @@
|
||||
<span>先保存为草稿,核对无误后再发布</span>
|
||||
<el-button type="primary" class="upload-button" :loading="uploading" :disabled="!file" @click="upload">上传为草稿</el-button>
|
||||
</div>
|
||||
<div v-if="uploading" class="upload-progress">
|
||||
<span>{{ uploadPhase }}</span>
|
||||
<el-progress :percentage="uploadProgress" :stroke-width="8" />
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
@ -99,6 +103,7 @@
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, ref } from 'vue';
|
||||
import axios from 'axios';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import type { UploadFile } from 'element-plus';
|
||||
import { CircleCheck, Document, Refresh, UploadFilled, WarningFilled } from '@element-plus/icons-vue';
|
||||
@ -128,6 +133,8 @@ const notesEn = ref('');
|
||||
const mandatory = ref(false);
|
||||
const loading = ref(false);
|
||||
const uploading = ref(false);
|
||||
const uploadProgress = ref(0);
|
||||
const uploadPhase = ref('');
|
||||
const channelLabel = import.meta.env.VITE_API_URL || '未配置';
|
||||
const publishedCount = computed(() => releases.value.filter((item) => item.releaseStatus === 1).length);
|
||||
const draftCount = computed(() => releases.value.filter((item) => item.releaseStatus === 0).length);
|
||||
@ -153,6 +160,7 @@ function selectApk(uploadFile: UploadFile) {
|
||||
if (!selected) return;
|
||||
if (!selected.name.toLowerCase().endsWith('.apk')) return ElMessage.warning('请选择 .apk 安装包');
|
||||
file.value = selected;
|
||||
uploadProgress.value = 0;
|
||||
}
|
||||
async function selectBuildInfo(uploadFile: UploadFile) {
|
||||
const selected = uploadFile.raw;
|
||||
@ -179,18 +187,59 @@ async function upload() {
|
||||
if (!versionName.value.trim() || !Number.isInteger(versionCode.value) || (versionCode.value ?? 0) <= 0 || !/^[A-F0-9]{64}$/.test(fingerprint)) {
|
||||
return ElMessage.warning('请核对版本名、整数版本号和 64 位公开签名指纹');
|
||||
}
|
||||
const form = new FormData();
|
||||
form.append('file', file.value);
|
||||
form.append('versionName', versionName.value.trim());
|
||||
form.append('versionCode', String(versionCode.value));
|
||||
form.append('certificateSha256', fingerprint);
|
||||
form.append('notesZh', notesZh.value);
|
||||
form.append('notesEn', notesEn.value);
|
||||
form.append('isMandatory', String(mandatory.value));
|
||||
const selected = file.value;
|
||||
uploading.value = true;
|
||||
try { const response = await http.post('/app-updates/admin/upload', form, { headers: { 'Content-Type': 'multipart/form-data' }, timeout: 300000 }); if (response.data?.statusCode !== 200) throw new Error('上传失败'); ElMessage.success('已上传为草稿,请核对后发布'); file.value = null; buildInfoName.value = ''; await load(); }
|
||||
catch { ElMessage.error('上传失败,请查看服务器返回的具体错误'); }
|
||||
finally { uploading.value = false; }
|
||||
uploadProgress.value = 0;
|
||||
uploadPhase.value = '正在获取临时上传地址…';
|
||||
try {
|
||||
const ticketResponse = await http.post('/app-updates/admin/upload-ticket', {
|
||||
versionCode: versionCode.value,
|
||||
fileSize: selected.size,
|
||||
});
|
||||
if (ticketResponse.data?.statusCode !== 200) throw new Error('获取 OSS 上传地址失败');
|
||||
const ticket = ticketResponse.data?.data;
|
||||
if (!ticket?.uploadUrl || !ticket?.objectKey || ticket?.contentType !== 'application/octet-stream') {
|
||||
throw new Error('后台返回的上传地址无效');
|
||||
}
|
||||
uploadPhase.value = '正在直传阿里云 OSS…';
|
||||
// Use a standalone Axios request: the signed OSS URL must not carry the admin JWT.
|
||||
await axios.put(ticket.uploadUrl, selected, {
|
||||
headers: { 'Content-Type': ticket.contentType },
|
||||
timeout: 0,
|
||||
onUploadProgress: (event) => {
|
||||
uploadProgress.value = Math.min(99, Math.round((event.loaded / selected.size) * 100));
|
||||
},
|
||||
});
|
||||
uploadProgress.value = 100;
|
||||
uploadPhase.value = 'OSS 上传完成,后台正在校验并生成草稿…';
|
||||
const response = await http.post('/app-updates/admin/upload-complete', {
|
||||
objectKey: ticket.objectKey,
|
||||
fileSize: selected.size,
|
||||
versionName: versionName.value.trim(),
|
||||
versionCode: versionCode.value,
|
||||
certificateSha256: fingerprint,
|
||||
notesZh: notesZh.value.trim(),
|
||||
notesEn: notesEn.value.trim(),
|
||||
isMandatory: mandatory.value,
|
||||
}, { timeout: 300000 });
|
||||
if (response.data?.statusCode !== 200) throw new Error('OSS 文件校验或草稿创建失败');
|
||||
ElMessage.success('已上传为草稿,请核对后发布');
|
||||
file.value = null;
|
||||
buildInfoName.value = '';
|
||||
await load();
|
||||
} catch (error) {
|
||||
if (axios.isAxiosError(error)) {
|
||||
const status = error.response?.status;
|
||||
ElMessage.error(status === 403
|
||||
? 'OSS 拒绝上传,请检查临时签名、Bucket 权限和系统时间'
|
||||
: '无法直传 OSS,请检查网络及 Bucket CORS 配置');
|
||||
} else {
|
||||
ElMessage.error(error instanceof Error ? error.message : '上传失败,请查看后台错误');
|
||||
}
|
||||
} finally {
|
||||
uploading.value = false;
|
||||
uploadPhase.value = '';
|
||||
}
|
||||
}
|
||||
async function publish(row: Release) {
|
||||
const confirmed = await ElMessageBox.confirm(`请确认安装包与构建信息匹配。发布 ${row.versionName}(${row.versionCode})后,裁判端将可检查到此版本。`, '发布确认', { type: 'warning' }).then(() => true, () => false);
|
||||
@ -267,6 +316,8 @@ onMounted(load);
|
||||
.form-footer span { color: #8b9cb0; font-size: 11px; }
|
||||
.upload-button { min-width: 130px; height: 38px; border: 0; border-radius: 9px; background: linear-gradient(110deg, #1686ee, #1265ce); box-shadow: 0 7px 15px rgba(27, 115, 214, .19); }
|
||||
.upload-button:hover { background: linear-gradient(110deg, #3198f1, #1d74da); }
|
||||
.upload-progress { display: grid; gap: 8px; margin-top: 15px; color: #54708f; font-size: 12px; }
|
||||
.upload-progress :deep(.el-progress-bar__inner) { background: linear-gradient(110deg, #1686ee, #43a5f4); }
|
||||
.history-panel { margin-top: 18px; padding: 23px 24px 12px; }
|
||||
.history-heading { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 17px; }
|
||||
.record-count { padding: 7px 10px; border-radius: 7px; background: #f3f7fc; color: #7890aa; font-size: 11px; }
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user