feat(web-admin): 出入库现场页支持选择单据状态并作废只读

提交携带 status;作废需确认;作废后整页只读可查看。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
zlwuandCursor committed 2026-07-19 17:14:23 +08:00
1 parent 1df9bbe22a
commit dee85f182e
1 file changed
+51 -3
@@ -5,8 +5,14 @@
<van-loading v-if="loading" vertical class="page-loading">加载中...</van-loading>
<van-empty v-else-if="errorMessage" :description="errorMessage" />
<van-empty v-else-if="page?.voided" description="单据已作废" />
<template v-else-if="page">
<van-notice-bar
v-if="isReadOnly"
left-icon="info-o"
color="#92400e"
background="#fffbeb"
text="单据已作废,仅可查看"
/>
<van-cell-group inset class="section-card">
<van-cell title="业务类型" :value="page.businessType || '—'" />
<van-cell title="日期" :value="page.planDate || '—'" />
@@ -105,6 +111,7 @@
class="actual-box__field"
placeholder="请输入"
:border="false"
:readonly="isReadOnly"
/>
<span class="actual-box__unit">{{ item.unit || '吨' }}</span>
</div>
@@ -118,12 +125,24 @@
v-model="uploaderFiles"
multiple
:max-count="9"
:deletable="!isReadOnly"
:show-upload="!isReadOnly"
:after-read="handleAfterRead"
:before-delete="handleBeforeDelete"
/>
</div>
</van-cell-group>
<van-cell-group v-if="!isReadOnly" inset class="section-card" title="单据状态">
<div class="status-radio-wrap">
<van-radio-group v-model="selectedStatus" direction="horizontal">
<van-radio name="in_progress">进行中</van-radio>
<van-radio name="completed">已完成</van-radio>
<van-radio name="voided">已作废</van-radio>
</van-radio-group>
</div>
</van-cell-group>
<van-cell-group inset class="section-card" title="现场备注">
<van-field
v-model="feedbackRemark"
@@ -133,6 +152,7 @@
maxlength="500"
show-word-limit
placeholder="填写现场备注"
:readonly="isReadOnly"
/>
</van-cell-group>
@@ -140,7 +160,7 @@
<van-cell title="上次保存" :value="formatDateTime(page.siteFeedbackTime)" />
</van-cell-group>
<footer class="page-footer">
<footer v-if="!isReadOnly" class="page-footer">
<van-button type="primary" block round :loading="saving" @click="handleSave">
提交
</van-button>
@@ -152,7 +172,7 @@
<script setup lang="ts">
import type { UploaderFileListItem } from 'vant'
import { showFailToast, showSuccessToast } from 'vant'
import { showConfirmDialog, showFailToast, showSuccessToast } from 'vant'
import {
createPublicIoOrderApi,
type PublicIoOrderItem,
@@ -161,9 +181,12 @@
import PublicMobileShell from '@/layouts/PublicMobileShell.vue'
import { buildAmapNavigationUrl } from '@/utils/amap/navigation'
import 'vant/es/dialog/style'
import 'vant/es/toast/style'
import '@/styles/public-mobile-vant.scss'
type IoOrderStatus = 'in_progress' | 'completed' | 'voided'
const props = defineProps<{ kind: 'inbound' | 'outbound' }>()
const api = createPublicIoOrderApi(props.kind)
const route = useRoute()
@@ -180,6 +203,8 @@
const feedbackImages = ref<string[]>([])
const uploaderFiles = ref<UploaderFileListItem[]>([])
const editableItems = ref<(PublicIoOrderItem & { actualQtyText: string })[]>([])
const selectedStatus = ref<IoOrderStatus>('in_progress')
const isReadOnly = computed(() => Boolean(page.value?.voided))
const deliveryAddressDisplay = computed(() => {
if (!page.value) return '—'
@@ -258,6 +283,11 @@
...it,
actualQtyText: it.actualQty != null ? String(it.actualQty) : ''
}))
selectedStatus.value = (
['in_progress', 'completed', 'voided'].includes(data.status || '')
? data.status
: 'in_progress'
) as IoOrderStatus
syncUploaderFromImages()
} catch (e: any) {
errorMessage.value = e?.message || '链接无效或已失效'
@@ -268,6 +298,7 @@
}
async function handleAfterRead(item: UploaderFileListItem | UploaderFileListItem[]) {
if (isReadOnly.value) return
const files = Array.isArray(item) ? item : [item]
for (const fileItem of files) {
if (!fileItem.file) continue
@@ -290,6 +321,7 @@
}
function handleBeforeDelete(item: UploaderFileListItem) {
if (isReadOnly.value) return false
const fileUrl = (item as UploaderFileListItem & { fileUrl?: string }).fileUrl
if (fileUrl) {
feedbackImages.value = feedbackImages.value.filter(u => u !== fileUrl)
@@ -298,9 +330,21 @@
}
async function handleSave() {
if (isReadOnly.value) return
if (selectedStatus.value === 'voided') {
try {
await showConfirmDialog({
title: '确认作废',
message: '确认作废本出入库单?作废后不可再提交。'
})
} catch {
return
}
}
saving.value = true
try {
await api.saveFeedback(token.value, {
status: selectedStatus.value,
remark: feedbackRemark.value,
images: feedbackImages.value,
items: editableItems.value.map(it => ({
@@ -350,6 +394,10 @@
padding: 8px 14px 12px;
}
.status-radio-wrap {
padding: 12px 16px 16px;
}
.page-footer {
padding: 16px;
}