test(admin): harden booking document workflows

This commit is contained in:
2026-08-31 21:05:03 +08:00
parent 623dfce255
commit 49babd731a
5 changed files with 79 additions and 28 deletions
@@ -37,6 +37,7 @@ function saveBlob(blob: Blob, name: string) {
URL.revokeObjectURL(url)
}
async function download(item: OceanBookingDocument) {
if (busyDocumentId.value || busyType.value) return
busyDocumentId.value = item.id
error.value = null
try {
@@ -54,18 +55,15 @@ async function invalidate(item: OceanBookingDocument, expectedVersion = props.bo
return { documents: nextDocuments, version: result.bookingVersion }
}
async function remove(item: OceanBookingDocument) {
try {
await ElMessageBox.confirm(`确认作废“${item.fileName}”?文件历史仍会保留。`, '作废订舱资料', { confirmButtonText: '确认作废', cancelButtonText: '取消', type: 'warning' })
} catch (cause) {
if (cause === 'cancel' || cause === 'close') return
throw cause
}
if (busyDocumentId.value || busyType.value || !editable.value) return
busyDocumentId.value = item.id
error.value = null
try {
await ElMessageBox.confirm(`确认作废“${item.fileName}”?文件历史仍会保留。`, '作废订舱资料', { confirmButtonText: '确认作废', cancelButtonText: '取消', type: 'warning' })
await invalidate(item)
ElMessage.success('订舱资料已作废')
} catch (cause) {
if (cause === 'cancel' || cause === 'close') return
error.value = getApiErrorDetails(cause, '订舱资料作废失败,请稍后重试')
} finally {
busyDocumentId.value = ''
@@ -75,23 +73,18 @@ async function selectFile(event: Event, type: OceanBookingDocumentType) {
const input = event.target as HTMLInputElement
const file = input.files?.[0]
input.value = ''
if (!file || !editable.value) return
if (!file || !editable.value || busyType.value || busyDocumentId.value) return
if (file.size > maxFileBytes) {
error.value = { message: '单个订舱资料文件不能超过 50MB,请重新选择' }
return
}
const previous = documentsOfType(type)
if (previous.length) {
try {
await ElMessageBox.confirm(`将上传“${file.name}”并作废当前 ${previous.length} 份同类型文件,是否继续?`, '替换订舱资料', { confirmButtonText: '确认替换', cancelButtonText: '取消', type: 'warning' })
} catch (cause) {
if (cause === 'cancel' || cause === 'close') return
throw cause
}
}
busyType.value = type
error.value = null
try {
if (previous.length) {
await ElMessageBox.confirm(`将上传“${file.name}”并作废当前 ${previous.length} 份同类型文件,是否继续?`, '替换订舱资料', { confirmButtonText: '确认替换', cancelButtonText: '取消', type: 'warning' })
}
const uploaded = await uploadOceanBookingDocument(props.bookingId, props.bookingVersion, type, file)
let documents = [...props.documents, uploaded.document]
let version = uploaded.bookingVersion
@@ -104,6 +97,7 @@ async function selectFile(event: Event, type: OceanBookingDocumentType) {
}
ElMessage.success(previous.length ? '订舱资料已替换' : '订舱资料已上传')
} catch (cause) {
if (cause === 'cancel' || cause === 'close') return
error.value = getApiErrorDetails(cause, '订舱资料上传失败,请稍后重试')
} finally {
busyType.value = ''
@@ -129,4 +123,5 @@ async function selectFile(event: Event, type: OceanBookingDocumentType) {
<style scoped>
.booking-documents{padding:16px}.booking-documents__heading,.booking-document-card__heading,.booking-document-list li,.booking-document-actions{display:flex;align-items:center}.booking-documents__heading,.booking-document-card__heading,.booking-document-list li{justify-content:space-between;gap:16px}.booking-documents__heading{margin-bottom:14px}.booking-documents h2,.booking-document-card h3{margin:0}.booking-documents__heading h2{font-size:16px}.booking-documents__heading p,.booking-document-card__heading p{margin:4px 0 0;color:var(--cc-muted);font-size:12px}.booking-document-limit{display:block;margin-top:5px;color:var(--cc-muted);font-size:11px}.booking-document-types{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:14px}.booking-document-card{min-width:0;padding:14px;border:1px solid var(--cc-border);border-radius:8px}.booking-document-card h3{font-size:14px}.booking-document-upload{display:flex;flex:0 0 auto;align-items:center;gap:6px;min-height:32px;padding:0 12px;border:1px solid var(--cc-primary);border-radius:6px;color:var(--cc-primary);font-size:12px;cursor:pointer}.booking-document-upload.is-disabled{cursor:not-allowed;opacity:.55}.booking-document-upload input{position:absolute;width:1px;height:1px;overflow:hidden;opacity:0}.booking-document-list,.booking-document-history ul{display:grid;gap:8px;margin:12px 0 0;padding:0;list-style:none}.booking-document-list li{min-width:0;padding:9px 10px;border-radius:6px;background:var(--el-fill-color-lighter)}.booking-document-list li>div:first-child{display:grid;min-width:0;gap:3px}.booking-document-list strong{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.booking-document-list small,.booking-document-history small{color:var(--cc-muted);font-size:11px}.booking-document-actions{flex:0 0 auto;gap:6px}.booking-document-actions :deep(.el-button){margin-left:0}.booking-document-history{margin-top:14px;color:var(--cc-muted);font-size:12px}.booking-document-history summary{cursor:pointer}.booking-document-history li{display:flex;justify-content:space-between;gap:12px;padding:7px 10px;border-bottom:1px solid var(--cc-border)}@media(max-width:900px){.booking-document-types{grid-template-columns:1fr}}@media(max-width:560px){.booking-documents__heading,.booking-document-card__heading,.booking-document-list li{align-items:flex-start;flex-direction:column}.booking-document-actions{width:100%}}
.booking-document-history li span{min-width:0;overflow-wrap:anywhere}
</style>