Files
admin-web/src/modules/overseas-warehouse/pages/DeliveryOrdersView.vue
T
2026-09-09 12:50:39 +08:00

173 lines
24 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { Eye, FileUp, Pencil, RotateCcw } from '@lucide/vue'
import { ElMessage } from 'element-plus'
import { useAccessPreviewStore } from '@/core/access/accessPreview'
import { useRouter } from 'vue-router'
import AdminPage from '@/shared/components/AdminPage.vue'
import AppDialog from '@/shared/components/AppDialog.vue'
import ApiErrorMeta from '@/shared/components/ApiErrorMeta.vue'
import DataTable, { type DataTableColumn } from '@/shared/components/DataTable.vue'
import ListPageLayout from '@/shared/components/ListPageLayout.vue'
import LoadErrorState from '@/shared/components/LoadErrorState.vue'
import QueryToolbar, { type QueryField, type QueryModel } from '@/shared/components/QueryToolbar.vue'
import StatusBadge from '@/shared/components/StatusBadge.vue'
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
import { formatOverseasDateTime } from '../lastMilePresentation'
import { getDeliveryOrder, listDeliveryOrders, listDeliveryReturnInboundOptions, markDeliveryReturn, type DeliveryOrderRow, type DeliveryReturnRelation, type DeliveryReturnSource, type MarkDeliveryReturnPayload } from '../api/deliveryOrders'
import { updateDeliveryOrderStatus } from '../api/customerVisits'
import { overseasWarehousePermissions as permission } from '../permissions'
import { listReceivingOverseasWarehouses, type ReceivingOverseasWarehouseDto } from '../api/pallets'
import DeliveryDocumentsDialog from '../components/DeliveryDocumentsDialog.vue'
import { deliveryDocumentCategories } from '../api/deliveryDocuments'
const documentsOpen = ref(false)
const documentsRow = ref<DeliveryOrderRow | null>(null)
function openDocuments(row: DeliveryOrderRow) { if (!access.can(permission.deliveryList)) return; documentsRow.value = row; documentsOpen.value = true }
const router = useRouter()
const access = useAccessPreviewStore()
const rows = ref<DeliveryOrderRow[]>([]); const page = ref(1); const pageSize = ref(10); const total = ref(0); const loading = ref(false); const error = ref<ApiErrorDetails | null>(null)
const warehouses = ref<ReceivingOverseasWarehouseDto[]>([]); const warehousesLoading = ref(false)
const filters = ref<QueryModel>({ documentNo: '', dispatchNo: '', waybillNo: '', warehouseId: '', status: '', vehiclePlate: '', driverName: '', deliveryMethod: '', plannedAt: [] })
const fields = computed<QueryField[]>(() => [
{ key: 'status', type: 'select', label: '状态', width: 160, placeholder: '全部状态', options: [{ label: '待派送', value: 'WAIT_DELIVERY' }, { label: '派送中', value: 'DELIVERING' }, { label: '派送完成', value: 'DELIVERED' }, { label: '已签收(历史)', value: 'SIGNED' }, { label: '已拒签', value: 'REJECTED' }, { label: '退货处理中', value: 'RETURN_PENDING' }, { label: '已取消', value: 'CANCELLED' }] },
{ key: 'documentNo', type: 'input', label: '派送单号', width: 190, placeholder: '请输入派送单号' },
{ key: 'dispatchNo', type: 'input', label: '排车单号', width: 190, placeholder: '请输入排车单号' },
{ key: 'waybillNo', type: 'input', label: '运单号', width: 180, placeholder: '请输入运单号' },
{ key: 'warehouseId', type: 'select', label: '海外仓', width: 220, placeholder: '全部海外仓', options: warehouses.value.map((item) => ({ label: `${item.warehouseName}(${item.warehouseCode})`, value: String(item.id) })), props: { filterable: true, clearable: true, loading: warehousesLoading.value } },
{ key: 'deliveryMethod', type: 'select', label: '派送方式', width: 170, placeholder: '全部方式', options: [{ label: 'FBA自提', value: 'FBA_PICKUP' }, { label: '客户自提', value: 'CUSTOMER_PICKUP' }, { label: '卡派打托', value: 'TRUCK_PALLET' }, { label: '卡派地板', value: 'TRUCK_FLOOR' }, { label: '快递', value: 'EXPRESS' }, { label: '外配', value: 'OUTSOURCED' }, { label: '专车直送', value: 'DEDICATED_TRUCK' }, { label: '零担派送', value: 'LTL' }] },
{ key: 'vehiclePlate', type: 'input', label: '车牌号', width: 160, placeholder: '请输入车牌号' },
{ key: 'driverName', type: 'input', label: '司机', width: 180, placeholder: '请输入司机或车队' },
{ key: 'plannedAt', type: 'daterange', label: '预计派送时间', width: 280 },
])
const columns: DataTableColumn[] = [
{ key: 'actions', label: '操作', width: 300, fixed: 'left', slotName: 'actions' },
{ key: 'deliveryNo', prop: 'deliveryNo', label: '派送单号', minWidth: 180, fixed: 'left' },
{ key: 'documents', label: '所需资料', minWidth: 380, slotName: 'documents' },
{ key: 'bolNo', prop: 'bolNo', label: 'BOL号', minWidth: 170 },
{ key: 'customerName', prop: 'customerName', label: '客户', minWidth: 160 },
{ key: 'overseasWarehouseName', prop: 'overseasWarehouseName', label: '仓库', minWidth: 150 },
{ key: 'dispatchNo', prop: 'dispatchNo', label: '排车单号', minWidth: 170 },
{ key: 'waybillNo', prop: 'waybillNo', label: '运单号', minWidth: 150 },
{ key: 'status', prop: 'status', label: '订单状态', width: 110, slotName: 'status' },
{ key: 'deliveryMethod', prop: 'deliveryMethod', label: '派送方式', width: 120, formatter: (row: DeliveryOrderRow) => deliveryMethodLabel(row.deliveryMethod) },
{ key: 'carrierName', prop: 'carrierName', label: '派送人', minWidth: 150 },
{ key: 'vehiclePlate', prop: 'vehiclePlate', label: '车牌', minWidth: 120 },
{ key: 'driverName', prop: 'driverName', label: '司机/车队', minWidth: 140 },
{ key: 'plannedDeliveryAt', prop: 'plannedDeliveryAt', label: '期望派送时间', minWidth: 170, formatter: (row: DeliveryOrderRow) => formatOverseasDateTime(row.plannedDeliveryAt) },
{ key: 'dispatchedAt', prop: 'dispatchedAt', label: '派送时间', minWidth: 170, formatter: (row: DeliveryOrderRow) => formatOverseasDateTime(row.dispatchedAt) },
{ key: 'pickupMethod', prop: 'pickupMethod', label: '提货方式', width: 110, formatter: (row: DeliveryOrderRow) => pickupMethodLabel(row.pickupMethod) },
{ key: 'loadingType', prop: 'loadingType', label: '装车类型', width: 110, formatter: (row: DeliveryOrderRow) => loadingTypeLabel(row.loadingType) },
{ key: 'packageCount', prop: 'packageCount', label: '总箱数', width: 90, align: 'right' },
{ key: 'totalWeightKg', prop: 'totalWeightKg', label: '总重量(kg)', width: 120, align: 'right' },
{ key: 'totalVolumeCbm', prop: 'totalVolumeCbm', label: '总体积(m³)', width: 120, align: 'right' },
{ key: 'expectedArrivalAt', prop: 'expectedArrivalAt', label: '预计到达时间', minWidth: 170, formatter: (row: DeliveryOrderRow) => formatOverseasDateTime(row.expectedArrivalAt) },
{ key: 'signedAt', prop: 'signedAt', label: '签收时间', minWidth: 170, formatter: (row: DeliveryOrderRow) => formatOverseasDateTime(row.signedAt) },
{ key: 'remark', prop: 'remark', label: '备注', minWidth: 160 },
]
const statusLabels: Record<string, string> = { WAIT_DELIVERY: '待派送', DELIVERING: '派送中', DELIVERED: '派送完成', SIGNED: '已签收', REJECTED: '已拒签', RETURN_PENDING: '退货处理中', CANCELLED: '已取消' }
function deliveryMethodLabel(value?: string | null) { return ({ FBA_PICKUP: 'FBA自提', CUSTOMER_PICKUP: '客户自提', TRUCK_PALLET: '卡派打托', TRUCK_FLOOR: '卡派地板', EXPRESS: '快递', OUTSOURCED: '外配', DEDICATED_TRUCK: '专车直送', LTL: '零担派送', SELF_DELIVERY: '自送', THIRD_PARTY: '第三方配送' } as Record<string, string>)[String(value)] || value || '—' }
function pickupMethodLabel(value?: string | null) { return ({ ONE_PICKUP_ONE_DROP: '一提一卸', ONE_PICKUP_MULTI_DROP: '一提多卸', MULTI_PICKUP_ONE_DROP: '多提一卸', MULTI_PICKUP_MULTI_DROP: '多提多卸', WAREHOUSE_PICKUP: '仓库自提', DOOR_PICKUP: '上门提货', SELF_PICKUP: '自提' } as Record<string, string>)[String(value)] || value || '—' }
function loadingTypeLabel(value?: string | null) { return ({ PALLET: '托盘', FLOOR: '地板', MIXED: '混装', CARTON: '散箱' } as Record<string, string>)[String(value)] || value || '—' }
async function load(model: QueryModel = filters.value) { Object.assign(filters.value, model); loading.value = true; error.value = null; try { const range = Array.isArray(filters.value.plannedAt) ? filters.value.plannedAt.map(String) : []; const result = await listDeliveryOrders({ page: page.value, size: pageSize.value, documentNo: text('documentNo'), dispatchNo: text('dispatchNo'), waybillNo: text('waybillNo'), warehouseId: text('warehouseId'), status: text('status'), vehiclePlate: text('vehiclePlate'), driverName: text('driverName'), deliveryMethod: text('deliveryMethod'), plannedFrom: range[0] ? `${range[0]}T00:00:00` : undefined, plannedTo: range[1] ? `${range[1]}T23:59:59` : undefined }); rows.value = result.records; total.value = result.total } catch (cause) { rows.value = []; total.value = 0; error.value = getApiErrorDetails(cause, '派送单列表加载失败') } finally { loading.value = false } }
function text(key: string) { return String(filters.value[key] || '').trim() || undefined }
function open(row: DeliveryOrderRow) { void router.push({ name: 'overseas-delivery-order-detail', params: { deliveryNo: row.deliveryNo }, query: { customerId: row.customerId } }) }
const returnOpen = ref(false); const returnLoading = ref(false); const returnSaving = ref(false); const returnRow = ref<DeliveryOrderRow | null>(null); const returnOptions = ref<DeliveryReturnRelation[]>([])
const returnError = ref<ApiErrorDetails | null>(null)
const returnForm = reactive({ inboundOrderId: '', reason: '', returnDirection: 'OVERSEAS_WAREHOUSE' as MarkDeliveryReturnPayload['returnDirection'], returnLogisticsNo: '' })
const returnItems = ref<Array<DeliveryReturnSource & { selected: boolean; packageCount: number | undefined }>>([])
let returnSequence = 0
let returnAttempt: { body: string; key: string } | null = null
function canReturn(row: DeliveryOrderRow) { return access.can(permission.deliveryReturn) && row.allowedActions?.includes('MARK_RETURN') }
watch(() => returnForm.inboundOrderId, () => {
returnItems.value = (returnOptions.value.find(option => option.inboundOrderId === returnForm.inboundOrderId)?.returnSources || []).map(source => ({ ...source, selected: false, packageCount: undefined }))
})
watch(returnOpen, visible => { if (!visible) returnSequence++ })
async function openReturn(row: DeliveryOrderRow) {
if (!canReturn(row) || returnSaving.value) return
const sequence = ++returnSequence
returnRow.value = row; Object.assign(returnForm, { inboundOrderId: '', reason: '', returnDirection: 'OVERSEAS_WAREHOUSE', returnLogisticsNo: '' })
returnOptions.value = []; returnItems.value = []; returnError.value = null; returnAttempt = null; returnOpen.value = true; returnLoading.value = true
try {
const options = await listDeliveryReturnInboundOptions(row.deliveryNo, row.customerId)
if (sequence !== returnSequence || !returnOpen.value) return
returnOptions.value = options
if (options.length === 1) returnForm.inboundOrderId = options[0]!.inboundOrderId
} catch (cause) { if (sequence === returnSequence) returnError.value = getApiErrorDetails(cause, '退货入仓单加载失败') }
finally { if (sequence === returnSequence) returnLoading.value = false }
}
async function submitReturn() {
if (!returnOpen.value || !returnRow.value || !canReturn(returnRow.value) || returnLoading.value || returnSaving.value) return
if (!returnOptions.value.some(option => option.inboundOrderId === returnForm.inboundOrderId) || !returnForm.reason.trim()) { ElMessage.warning('请选择入仓单并填写退货原因'); return }
const selected = returnItems.value.filter(item => item.selected)
if (!selected.length || selected.some(item => !Number.isSafeInteger(item.packageCount) || !item.packageCount || item.packageCount < 1 || item.packageCount > item.maxReturnPackages)) { ElMessage.warning('请选择原出库明细,并填写不超过可退上限的整数箱数'); return }
const body = { inboundOrderId: returnForm.inboundOrderId, reason: returnForm.reason.trim(), returnDirection: returnForm.returnDirection, returnLogisticsNo: returnForm.returnLogisticsNo.trim() || undefined, items: selected.map(item => ({ outboundPalletItemId: item.outboundPalletItemId, inventoryPositionId: item.inventoryPositionId, packageCount: item.packageCount! })) }
const serialized = JSON.stringify(body)
if (returnAttempt?.body !== serialized) returnAttempt = { body: serialized, key: crypto.randomUUID() }
returnSaving.value = true; returnError.value = null
try {
const result = await markDeliveryReturn(returnRow.value.deliveryNo, returnRow.value.customerId, { ...body, idempotencyKey: returnAttempt.key })
returnOpen.value = false; ElMessage.success(`已生成异常退货单 ${result.exceptionNo}`); await load()
} catch (cause) { returnError.value = getApiErrorDetails(cause, '标记退货失败') }
finally { returnSaving.value = false }
}
const statusOpen = ref(false)
const statusSaving = ref(false)
const statusLoading = ref(false)
const statusError = ref<ApiErrorDetails | null>(null)
const statusConflict = ref(false)
const statusRow = ref<DeliveryOrderRow | null>(null)
const statusForm = reactive({ status: '', remark: '' })
let statusSequence = 0
const statusTargets = [
{ action: 'START_DELIVERY', value: 'DELIVERING', label: '派送中' },
{ action: 'COMPLETE_DELIVERY', value: 'DELIVERED', label: '派送完成' },
{ action: 'REJECT_DELIVERY', value: 'REJECTED', label: '已拒签' },
{ action: 'CANCEL_DELIVERY', value: 'CANCELLED', label: '已取消' },
]
function availableStatuses(row: DeliveryOrderRow) { return statusTargets.filter(option => row.allowedActions?.includes(option.action)) }
const statusOptions = computed(() => statusRow.value ? availableStatuses(statusRow.value) : [])
async function refreshStatus() {
const row = statusRow.value
if (!row || statusSaving.value) return
const sequence = ++statusSequence
statusLoading.value = true; statusError.value = null
try {
const latest = await getDeliveryOrder(row.deliveryNo, row.customerId)
if (sequence !== statusSequence || !statusOpen.value) return
if (!Number.isSafeInteger(latest.order.version) || latest.order.version < 0) throw new Error('派送单缺少有效版本,请刷新后重试')
statusRow.value = latest.order
if (!availableStatuses(latest.order).some(option => option.value === statusForm.status)) statusForm.status = ''
statusConflict.value = false
} catch (cause) { if (sequence === statusSequence) { statusError.value = getApiErrorDetails(cause, '派送单最新状态加载失败'); statusConflict.value = true } }
finally { if (sequence === statusSequence) statusLoading.value = false }
}
async function openStatus(row: DeliveryOrderRow) {
if (!access.can(permission.deliveryStatus) || statusSaving.value || !availableStatuses(row).length) return
statusRow.value = row; Object.assign(statusForm, { status: '', remark: '' }); statusConflict.value = true; statusOpen.value = true
await refreshStatus()
}
async function submitStatus() {
if (!statusOpen.value || !statusRow.value || statusLoading.value || statusSaving.value || statusConflict.value || !access.can(permission.deliveryStatus) || !statusOptions.value.some(option => option.value === statusForm.status)) return
statusSaving.value = true; statusError.value = null
try {
const result = await updateDeliveryOrderStatus(statusRow.value.deliveryNo, statusRow.value.customerId, { status: statusForm.status, expectedVersion: statusRow.value.version, remark: statusForm.remark.trim() || undefined })
statusOpen.value = false
ElMessage.success(result.deliveryStatus === 'DELIVERED' ? '派送已完成,待回访记录已生成' : '派送状态已更新')
await load()
} catch (cause) {
const details = getApiErrorDetails(cause, '派送状态更新失败')
statusConflict.value = details.code === 'A0409'
statusError.value = statusConflict.value ? { ...details, message: '派送单已被更新,请刷新最新状态并重新确认后提交。' } : details
} finally { statusSaving.value = false }
}
function search(model: QueryModel) { page.value = 1; void load(model) }
function reset(model: QueryModel) { page.value = 1; void load(model) }
async function loadWarehouses() { if (!access.can(permission.warehouseList)) return; warehousesLoading.value = true; try { warehouses.value = (await listReceivingOverseasWarehouses()).records } catch { warehouses.value = [] } finally { warehousesLoading.value = false } }
onMounted(() => { void load(); void loadWarehouses() })
</script>
<template><AdminPage><ListPageLayout aria-label="派送单列表"><template #toolbar><QueryToolbar v-model="filters" class="surface-panel" :fields="fields" :loading="loading" ariaLabel="派送单筛选" preference-key="overseas-delivery-orders-query" :search-on-reset="false" @search="search" @reset="reset" /></template><template v-if="error" #state><LoadErrorState title="派送单列表加载失败" :message="error.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="error" /></template></LoadErrorState></template><section v-if="!error" class="surface-panel table-panel"><DataTable v-model:page="page" v-model:page-size="pageSize" :data="rows" :columns="columns" :loading="loading" :total="total" preference-key="overseas-delivery-orders" row-key="id" empty-text="暂无派送单" @update:page="load()" @update:page-size="page = 1; load()"><template #actions="{ row }"><el-button size="small" type="primary" plain @click="open(row as DeliveryOrderRow)"><Eye :size="15" />详情</el-button><el-button v-if="access.can(permission.deliveryStatus) && availableStatuses(row as DeliveryOrderRow).length > 0" size="small" plain @click="openStatus(row as DeliveryOrderRow)"><Pencil :size="15" />编辑状态</el-button><el-button v-if="canReturn(row as DeliveryOrderRow)" size="small" type="warning" plain @click="openReturn(row as DeliveryOrderRow)"><RotateCcw :size="15" />标记退货</el-button></template><template #documents="{ row }"><div class="delivery-document-summary"><div><strong>{{ row.documentSummary ? `${row.documentSummary.uploadedCategoryCount}/3 已上传` : '资料摘要暂无' }}</strong><el-button v-if="access.can(permission.deliveryList)" size="small" plain @click="openDocuments(row as DeliveryOrderRow)"><FileUp :size="15" />管理资料</el-button></div><div class="delivery-document-categories"><span v-for="category in deliveryDocumentCategories" :key="category.category" :class="{ uploaded: row.documentSummary?.categories.some((item: { category: string; status: string }) => item.category === category.category && item.status === 'UPLOADED') }">{{ category.label }} · {{ row.documentSummary?.categories.find((item: { category: string }) => item.category === category.category)?.status === 'UPLOADED' ? '已上传' : '未上传' }}</span></div></div></template><template #status="{ row }"><StatusBadge :status="statusLabels[String(row.status)] || String(row.status || '—')" /></template><template #pagination-left>共 {{ total }} 条派送单</template></DataTable></section></ListPageLayout></AdminPage><AppDialog v-model="statusOpen" title="编辑派送状态" width="min(520px, calc(100vw - 28px))" confirm-text="保存状态" :confirm-loading="statusSaving" :loading="statusLoading" :confirm-disabled="!statusForm.status || statusConflict || statusLoading" @confirm="submitStatus"><el-form label-position="top"><el-form-item label="派送单号"><el-input :model-value="statusRow?.deliveryNo" readonly /></el-form-item><el-form-item label="派送状态" required><el-select v-model="statusForm.status" placeholder="请选择派送状态"><el-option v-for="option in statusOptions" :key="option.value" :label="option.label" :value="option.value" /></el-select></el-form-item><el-form-item label="备注"><el-input v-model="statusForm.remark" type="textarea" :rows="3" maxlength="500" show-word-limit placeholder="请输入状态备注(选填)" /></el-form-item><el-alert v-if="statusForm.status === 'DELIVERED'" title="保存为派送完成后,将自动生成一条待回访记录。" type="info" :closable="false" /></el-form><LoadErrorState v-if="statusError" compact title="派送状态操作失败" :message="statusError.message" :show-retry="statusConflict" @retry="refreshStatus"><template #details><ApiErrorMeta :details="statusError" /></template></LoadErrorState></AppDialog><AppDialog v-model="returnOpen" title="标记退货" width="min(720px, calc(100vw - 28px))" confirm-text="确认转入退货" :loading="returnLoading" :confirm-loading="returnSaving" @confirm="submitReturn"><el-form label-position="top"><el-form-item label="派送单号"><el-input :model-value="returnRow?.deliveryNo" readonly /></el-form-item><el-form-item label="对应入仓单" required><el-select v-model="returnForm.inboundOrderId" placeholder="请选择该派送单对应的入仓单"><el-option v-for="option in returnOptions" :key="String(option.inboundOrderId)" :value="String(option.inboundOrderId)" :label="`${option.inboundCode} · ${option.lastMileOrderNo || '无尾程单号'}`" /></el-select></el-form-item><el-form-item label="退货原因" required><el-input v-model="returnForm.reason" type="textarea" :rows="4" maxlength="1000" show-word-limit placeholder="请输入退货原因" /></el-form-item><div class="return-grid"><el-form-item label="退货去向" required><el-select v-model="returnForm.returnDirection" placeholder="请选择退货去向"><el-option label="退回海外仓" value="OVERSEAS_WAREHOUSE" /><el-option label="退回客户" value="CUSTOMER" /><el-option label="其他" value="OTHER" /></el-select></el-form-item><el-form-item label="退回物流单号"><el-input v-model="returnForm.returnLogisticsNo" placeholder="可安排物流后补充" /></el-form-item></div><el-form-item label="原出库明细" required><div class="return-sources"><p v-if="!returnItems.length">暂无可退原出库明细</p><div v-for="item in returnItems" :key="`${item.outboundPalletItemId}:${item.inventoryPositionId}`" class="return-source"><el-checkbox v-model="item.selected" :disabled="returnSaving || item.maxReturnPackages < 1">{{ item.palletNo || '—' }} · {{ item.skuCode || '—' }}(最多 {{ item.maxReturnPackages }} 箱)</el-checkbox><el-input-number v-model="item.packageCount" placeholder="请输入退货箱数" :disabled="!item.selected || returnSaving" :min="1" :max="item.maxReturnPackages" :precision="0" :aria-label="`退货箱数 ${item.palletNo || '—'} ${item.skuCode || '—'}`" /></div></div></el-form-item><LoadErrorState v-if="returnError" compact title="标记退货失败" :message="returnError.message" :show-retry="false"><template #details><ApiErrorMeta :details="returnError" /></template></LoadErrorState><el-alert title="确认后会生成异常退货单,并将派送单标记为退货处理中。" type="warning" :closable="false" /></el-form></AppDialog><DeliveryDocumentsDialog v-if="documentsRow" v-model="documentsOpen" :delivery-no="documentsRow.deliveryNo" :customer-id="documentsRow.customerId" @saved="load()" /></template>
<style scoped>.delivery-document-summary{display:grid;gap:8px;padding:6px 0}.delivery-document-summary>div:first-child{display:flex;align-items:center;justify-content:space-between;gap:12px}.delivery-document-categories{display:flex;flex-wrap:wrap;gap:6px 12px;font-size:12px;color:var(--cc-muted)}.delivery-document-categories .uploaded{color:var(--el-color-success)}.return-sources { width:100%; }.return-source { display:flex; flex-wrap:wrap; align-items:center; justify-content:space-between; gap:12px; padding:8px 0; }.return-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 18px; }.return-grid :deep(.el-input),.return-grid :deep(.el-select),.return-grid :deep(.el-input-number){width:100%} @media (max-width: 720px) { .return-grid { grid-template-columns: 1fr; } }</style>