This commit is contained in:
2026-09-14 20:03:27 +08:00
parent 66f49b97e5
commit 349e8f60df
44 changed files with 658 additions and 459 deletions
@@ -1,4 +1,5 @@
<script setup lang="ts">
import '@/styles/adminDensity.css'
import { computed, ref, watch } from 'vue'
import { ElMessage, ElMessageBox, type UploadFile } from 'element-plus'
import AppDialog from '@/shared/components/AppDialog.vue'
@@ -43,7 +44,7 @@ async function load() {
async function reload() {
if (busy.value) return
if (snapshot.value && canEdit.value) {
try { await ElMessageBox.confirm('重新加载将放弃本次未保存的资料修改,是否继续?', '重新加载资料', { type: 'warning' }) } catch { return }
try { await ElMessageBox.confirm('重新加载将放弃本次未保存的资料修改,是否继续?', '重新加载资料', { customClass: 'admin-density-confirm', type: 'warning' }) } catch { return }
}
await load()
}
@@ -103,7 +104,7 @@ async function save() {
</script>
<template>
<AppDialog v-model="open" title="派送单所需资料" width="min(1040px, calc(100vw - 40px))" confirm-text="保存资料" cancel-text="关闭" :loading="loading" :show-confirm="canEdit" :confirm-loading="busy" :confirm-disabled="conflict || loading" @confirm="save">
<AppDialog class="admin-density-dialog" v-model="open" title="派送单所需资料" width="min(1040px, calc(100vw - 40px))" confirm-text="保存资料" cancel-text="关闭" :loading="loading" :show-confirm="canEdit" :confirm-loading="busy" :confirm-disabled="conflict || loading" @confirm="save">
<div class="delivery-documents">
<div class="document-identifiers"><label>派送单号<el-input :model-value="deliveryNo" readonly /></label><label>BOL号<el-input v-model="bolNo" :readonly="!canEdit" :disabled="busy || conflict" placeholder="未填写" /></label></div>
<LoadErrorState v-if="error" compact title="派送资料操作失败" :message="error.message" :show-retry="!snapshot || conflict" @retry="reload"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState>
@@ -111,11 +112,11 @@ async function save() {
<div class="document-summary"><span>{{ canEdit ? '上传、替换和删除将在保存资料后生效;关闭将放弃未保存修改。' : '当前账号仅可查看已保存资料。' }}</span><strong>{{ uploadedCount }}/3 已上传</strong></div>
<div class="document-groups">
<section v-for="group in draft" :key="group.category" class="document-group" :data-category="group.category">
<header><strong>{{ group.label }}</strong><el-tag :type="group.files.length ? 'success' : 'info'">{{ group.files.length ? '已上传' : '待上传' }}</el-tag><span>{{ group.files.length }}/20</span><el-upload v-if="canEdit" :key="uploadKey" :auto-upload="false" :show-file-list="false" :disabled="busy || conflict || group.files.length >= 20" :on-change="(file: UploadFile) => upload(group.category, file)"><el-button :disabled="busy || conflict || group.files.length >= 20">上传</el-button></el-upload></header>
<header><strong>{{ group.label }}</strong><el-tag :type="group.files.length ? 'success' : 'info'">{{ group.files.length ? '已上传' : '待上传' }}</el-tag><span>{{ group.files.length }}/20</span><el-upload v-if="canEdit" :key="uploadKey" :auto-upload="false" :show-file-list="false" :disabled="busy || conflict || group.files.length >= 20" :on-change="(file: UploadFile) => upload(group.category, file)"><el-button class="density-action" data-tone="primary" :disabled="busy || conflict || group.files.length >= 20">上传</el-button></el-upload></header>
<p v-if="!group.files.length" class="document-empty">尚未上传文件</p>
<div v-for="file in group.files" :key="file.fileId" class="document-file">
<div class="document-file-info"><button type="button" class="document-file-name" :title="file.fileName" :disabled="downloading || !isSaved(group.category, file.fileId)" @click="download(group.category, file)">{{ file.fileName }}</button><small>{{ file.uploadedAt?.replace('T', ' ') || '—' }}{{ isSaved(group.category, file.fileId) ? '' : ' · 待保存' }}</small></div>
<template v-if="canEdit"><el-upload :key="uploadKey" :auto-upload="false" :show-file-list="false" :disabled="busy || conflict" :on-change="(uploadFile: UploadFile) => upload(group.category, uploadFile, file.fileId)"><el-button :disabled="busy || conflict">替换</el-button></el-upload><el-button type="danger" link :disabled="busy || conflict" @click="remove(group.category, file.fileId)">删除</el-button></template>
<template v-if="canEdit"><el-upload :key="uploadKey" :auto-upload="false" :show-file-list="false" :disabled="busy || conflict" :on-change="(uploadFile: UploadFile) => upload(group.category, uploadFile, file.fileId)"><el-button class="density-action" data-tone="primary" :disabled="busy || conflict">替换</el-button></el-upload><el-button class="density-action" data-tone="danger" :disabled="busy || conflict" @click="remove(group.category, file.fileId)">删除</el-button></template>
</div>
</section>
</div>
@@ -125,10 +126,10 @@ async function save() {
</template>
<style scoped>
.delivery-documents { display:grid; gap:16px; max-height:66vh; overflow-y:auto; padding:2px 6px 4px 0; }
.document-identifiers { display:grid; grid-template-columns:1fr 1fr; gap:18px; }.document-identifiers label { display:grid; gap:8px; font-weight:600; }
.document-summary { display:flex; justify-content:space-between; align-items:center; gap:20px; padding:12px 16px; background:var(--el-color-primary-light-9); color:var(--el-color-primary); border-radius:8px; }.document-summary strong { white-space:nowrap; }
.document-groups { border:1px solid var(--cc-border); border-radius:8px; overflow:hidden; }.document-group { padding:14px 16px; }.document-group + .document-group { border-top:1px solid var(--cc-border); }
.document-group header { display:flex; align-items:center; gap:10px; }.document-group header > span { color:var(--cc-muted); font-size:12px; }.document-group header :deep(.el-upload) { margin-left:auto; }.document-group header > :last-child { margin-left:auto; }
.document-file { display:flex; align-items:center; gap:14px; padding-top:10px; }.document-file-info { display:grid; min-width:0; flex:1; gap:4px; }.document-file-name { border:0; background:none; padding:0; text-align:left; color:var(--cc-primary); overflow:hidden; white-space:nowrap; text-overflow:ellipsis; cursor:pointer; font:inherit; }.document-file-name:disabled { color:var(--cc-muted); cursor:default; }.document-file-info small,.document-empty { color:var(--cc-muted); }.document-empty { margin:10px 0 0; }
.delivery-documents { display:grid; gap:var(--admin-space-5); padding:2px var(--admin-space-2) var(--admin-space-1) 0; }
.document-identifiers { display:grid; grid-template-columns:1fr 1fr; gap:var(--admin-space-5); }.document-identifiers label { display:grid; gap:var(--admin-space-3); font-weight:600; }
.document-summary { display:flex; justify-content:space-between; align-items:center; gap:var(--admin-space-5); padding:var(--admin-space-5) var(--admin-space-5); background:var(--el-color-primary-light-9); color:var(--el-color-primary); border-radius:var(--admin-radius); }.document-summary strong { white-space:nowrap; }
.document-groups { border:1px solid var(--cc-border); border-radius:var(--admin-radius); overflow:hidden; }.document-group { padding:var(--admin-space-5) var(--admin-space-5); }.document-group + .document-group { border-top:1px solid var(--cc-border); }
.document-group header { display:flex; align-items:center; gap:var(--admin-space-4); }.document-group header > span { color:var(--cc-muted); font-size:var(--admin-font-caption); }.document-group header :deep(.el-upload) { margin-left:auto; }.document-group header > :last-child { margin-left:auto; }
.document-file { display:flex; align-items:center; gap:var(--admin-space-5); padding-top:var(--admin-space-4); }.document-file-info { display:grid; min-width:0; flex:1; gap:var(--admin-space-1); }.document-file-name { border:0; background:none; padding:0; text-align:left; color:var(--cc-primary); overflow:hidden; white-space:nowrap; text-overflow:ellipsis; cursor:pointer; font:inherit; }.document-file-name:disabled { color:var(--cc-muted); cursor:default; }.document-file-info small,.document-empty { color:var(--cc-muted); }.document-empty { margin:var(--admin-space-4) 0 0; }
</style>