task-260(admin.html观感对齐): 软件版本页对齐(zip 校验文案/发布成功含链接/下载列/空态)
This commit is contained in:
@@ -32,23 +32,29 @@ function onFileChange(file: File) {
|
||||
|
||||
async function submitUpload() {
|
||||
if (!isNonEmptyVersion(newVersion.value)) {
|
||||
ElMessage.warning('版本号不能为空')
|
||||
ElMessage.warning('请填写版本号')
|
||||
return
|
||||
}
|
||||
if (!pickedFile.value) {
|
||||
ElMessage.warning('请选择程序压缩包')
|
||||
ElMessage.warning('请选择 zip 压缩包')
|
||||
return
|
||||
}
|
||||
if (!/\.zip$/i.test(pickedFile.value.name)) {
|
||||
ElMessage.warning('仅支持 .zip 格式')
|
||||
return
|
||||
}
|
||||
uploading.value = true
|
||||
try {
|
||||
await uploadSoftwareVersion(newVersion.value.trim(), pickedFile.value, pickedFile.value.name)
|
||||
ElMessage.success('上传成功')
|
||||
const created = await uploadSoftwareVersion(newVersion.value.trim(), pickedFile.value, pickedFile.value.name)
|
||||
const version = created?.version || newVersion.value.trim()
|
||||
const link = created?.fileUrl
|
||||
ElMessage.success(link ? `发布成功。版本:${version},链接:${link}` : `发布成功。版本:${version}`)
|
||||
uploadVisible.value = false
|
||||
newVersion.value = ''
|
||||
pickedFile.value = null
|
||||
load()
|
||||
} catch (error) {
|
||||
ElMessage.error(error instanceof Error ? error.message : '上传失败')
|
||||
ElMessage.error(error instanceof Error ? error.message : '发布失败')
|
||||
} finally {
|
||||
uploading.value = false
|
||||
}
|
||||
@@ -70,9 +76,9 @@ onMounted(load)
|
||||
</div>
|
||||
|
||||
<el-card shadow="never">
|
||||
<el-table v-loading="loading" :data="items" stripe border>
|
||||
<el-table v-loading="loading" :data="items" stripe border empty-text="暂无版本">
|
||||
<el-table-column prop="version" label="版本号" min-width="180" />
|
||||
<el-table-column label="下载" min-width="260">
|
||||
<el-table-column label="下载链接" min-width="240">
|
||||
<template #default="{ row }">
|
||||
<el-link v-if="(row as SoftwareVersionItem).fileUrl" type="primary" :href="(row as SoftwareVersionItem).fileUrl" target="_blank">{{ (row as SoftwareVersionItem).fileUrl }}</el-link>
|
||||
<span v-else class="dim">—</span>
|
||||
@@ -81,6 +87,12 @@ onMounted(load)
|
||||
<el-table-column prop="createdAt" label="创建时间" min-width="190">
|
||||
<template #default="{ row }">{{ formatDateTime(row.createdAt) }}</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="90" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-link v-if="(row as SoftwareVersionItem).fileUrl" type="primary" :href="(row as SoftwareVersionItem).fileUrl" target="_blank">下载</el-link>
|
||||
<span v-else class="dim">—</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-card>
|
||||
|
||||
@@ -90,7 +102,7 @@ onMounted(load)
|
||||
<el-input v-model="newVersion" placeholder="如 1.0.2" />
|
||||
</el-form-item>
|
||||
<el-form-item label="程序压缩包" required>
|
||||
<el-upload :auto-upload="false" :limit="1" accept=".zip,.rar,.7z,.exe" :on-change="(file: any) => onFileChange(file.raw)" :on-remove="() => (pickedFile = null)">
|
||||
<el-upload :auto-upload="false" :limit="1" accept=".zip" :on-change="(file: any) => onFileChange(file.raw)" :on-remove="() => (pickedFile = null)">
|
||||
<el-button>选择文件</el-button>
|
||||
</el-upload>
|
||||
<div v-if="pickedFile" class="dim">{{ pickedFile.name }}</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/** 软件版本列表加载适配(任务 127):GET /api/admin/versions。 */
|
||||
import { http } from '@/api/http'
|
||||
import { parseSoftwareVersionList } from './version-model.ts'
|
||||
import type { SoftwareVersionList } from './version-dto.ts'
|
||||
import { parseSoftwareVersionList, parseSoftwareVersionUpload } from './version-model.ts'
|
||||
import type { SoftwareVersionItem, SoftwareVersionList } from './version-dto.ts'
|
||||
|
||||
export const SOFTWARE_VERSIONS_ENDPOINT = '/api/admin/versions'
|
||||
export const SOFTWARE_VERSION_UPLOAD_ENDPOINT = '/api/admin/version'
|
||||
@@ -11,10 +11,11 @@ export async function fetchSoftwareVersions(): Promise<SoftwareVersionList> {
|
||||
return parseSoftwareVersionList(data)
|
||||
}
|
||||
|
||||
/** 上传客户端软件版本(multipart version + file)。 */
|
||||
export async function uploadSoftwareVersion(version: string, file: Blob, filename = ''): Promise<void> {
|
||||
/** 上传客户端软件版本(multipart version + file);返回新版本行(含下载链接)。 */
|
||||
export async function uploadSoftwareVersion(version: string, file: Blob, filename = ''): Promise<SoftwareVersionItem | null> {
|
||||
const form = new FormData()
|
||||
form.append('version', version)
|
||||
form.append('file', file, filename || (file instanceof File ? file.name : 'version.zip'))
|
||||
await http.post<unknown>(SOFTWARE_VERSION_UPLOAD_ENDPOINT, form)
|
||||
const { data } = await http.post<unknown>(SOFTWARE_VERSION_UPLOAD_ENDPOINT, form)
|
||||
return parseSoftwareVersionUpload(data)
|
||||
}
|
||||
|
||||
@@ -33,3 +33,11 @@ export function parseSoftwareVersionList(payload: unknown): SoftwareVersionList
|
||||
: []
|
||||
return { items }
|
||||
}
|
||||
|
||||
/** 上传成功后返回的新版本行(data.item);无 item 回 null。 */
|
||||
export function parseSoftwareVersionUpload(payload: unknown): SoftwareVersionItem | null {
|
||||
const data = unwrap<unknown>(payload)
|
||||
if (!data || typeof data !== 'object') return null
|
||||
const item = (data as Record<string, unknown>).item
|
||||
return item ? toSoftwareVersionItem(item) : null
|
||||
}
|
||||
|
||||
@@ -10,14 +10,17 @@ export interface VersionUploadErrors {
|
||||
file?: string
|
||||
}
|
||||
|
||||
/** 校验上传表单(版本号必填非空、文件已选且非空);返回逐字段错误。 */
|
||||
/** 校验上传表单(版本号必填非空、zip 已选且非空);文案对齐 admin.js。 */
|
||||
export function validateSoftwareVersionUpload(form: VersionUploadForm): { valid: boolean; errors: VersionUploadErrors } {
|
||||
const errors: VersionUploadErrors = {}
|
||||
const version = (form.version || '').trim()
|
||||
if (!version) errors.version = '版本号不能为空'
|
||||
if (!version) errors.version = '请填写版本号'
|
||||
const file = form.file
|
||||
if (!file) errors.file = '请选择安装包文件'
|
||||
else if (typeof file.size === 'number' && file.size <= 0) errors.file = '安装包为空或无效'
|
||||
if (!file) errors.file = '请选择 zip 压缩包'
|
||||
else {
|
||||
if (typeof file.size === 'number' && file.size <= 0) errors.file = '安装包为空或无效'
|
||||
else if (file.name && !/\.zip$/i.test(file.name)) errors.file = '仅支持 .zip 格式'
|
||||
}
|
||||
return { valid: Object.keys(errors).length === 0, errors }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user