Files
admin-web/src/views/FirstLegOrdersView.vue
T
2026-07-30 17:59:43 +08:00

242 lines
26 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, reactive, ref, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { ClipboardCheck, Download, Eye, PackageCheck, Pencil, Plus, RotateCcw, Search, Ship, UserRoundCheck } from '@lucide/vue'
import { FIRST_LEG_ORDER_STATUSES } from '@chongchong/shared'
import MetricStrip from '@/components/MetricStrip.vue'
import StatusBadge from '@/components/StatusBadge.vue'
import { useUnsavedWorkRegistration } from '@/composables/useUnsavedWorkRegistration'
import { useAccessPreviewStore } from '@/stores/accessPreview'
import { useAdminDataStore } from '@/stores/adminData'
import type { FirstLegOrder, OrderStatus } from '@/types/admin'
type OrderForm = Omit<FirstLegOrder, 'no' | 'createdAt' | 'timeline'>
const store = useAdminDataStore()
const access = useAccessPreviewStore()
const page = ref(1)
const pageSize = 10
const editorOpen = ref(false)
const detailOpen = ref(false)
const statusDialogOpen = ref(false)
const deliveryDialogOpen = ref(false)
const currentOrder = ref<FirstLegOrder | null>(null)
const selectedRows = ref<FirstLegOrder[]>([])
const statusTargetIds = ref<string[]>([])
const dateRange = ref<[string, string] | []>([])
const filters = reactive({ keyword: '', status: '', mode: '', customerId: '' })
const statusForm = reactive<{ status: OrderStatus; operator: string; note: string }>({ status: '已受理', operator: '', note: '' })
const deliveryForm = reactive({ orderId: '', expressCompanyId: '', trackingNo: '', operator: '' })
const emptyForm = (): OrderForm => ({
id: '', customerId: '', customerName: '', referenceNo: '', transportMode: '海运整箱 FCL', businessType: '出口',
origin: '', destination: '', receivingWarehouseId: '', receivingWarehouseSnapshot: null, container: '40HQ x 1', etd: '', eta: '', amount: 0, currency: 'CNY', status: '草稿',
priority: '普通', operator: '未分配', shipper: '', consignee: '', cargoName: '', pieces: 1, weight: 0, volume: 0, remark: '',
})
const form = reactive(emptyForm())
const editorLabel = computed(() => form.id ? '编辑头程订单' : '创建头程订单')
const editorRequiredPermission = computed(() => form.id ? null : 'first-leg:create')
const { captureBaseline } = useUnsavedWorkRegistration({
key: 'first-leg-order-editor',
label: editorLabel,
isOpen: editorOpen,
model: form,
requiredPermission: editorRequiredPermission,
})
const statusOptions: OrderStatus[] = FIRST_LEG_ORDER_STATUSES.map((item) => item.label)
const statusTransitions: Record<OrderStatus, OrderStatus[]> = { 草稿: ['待审核', '已取消'], 待审核: ['已受理', '已取消'], 已受理: ['已订舱', '已取消'], 已订舱: ['运输中', '已取消'], 运输中: ['已完成', '已取消'], 已完成: [], 已取消: [] }
const availableStatusOptions = computed(() => {
if (statusTargetIds.value.length !== 1) return ['已取消'] as OrderStatus[]
const order = store.orders.find((item) => item.id === statusTargetIds.value[0])
return order ? [order.status, ...statusTransitions[order.status]] : []
})
const operatorOptions = computed(() => store.users.filter((item) => item.status === '正常' && ['运营部', '操作部', '平台管理'].includes(item.department)).map((item) => item.name))
const selectableWarehouses = computed(() => store.warehouses.filter((item) => item.status === 'ACTIVE' || item.id === form.receivingWarehouseId))
const filteredRows = computed(() => store.orders.filter((item) => {
const keyword = filters.keyword.trim().toLowerCase()
const keywordMatch = !keyword || [item.no, item.customerName, item.referenceNo, item.origin, item.destination].some((value) => value.toLowerCase().includes(keyword))
const dateMatch = dateRange.value.length !== 2 || (item.createdAt.slice(0, 10) >= dateRange.value[0] && item.createdAt.slice(0, 10) <= dateRange.value[1])
return keywordMatch && dateMatch && (!filters.status || item.status === filters.status) && (!filters.mode || item.transportMode === filters.mode) && (!filters.customerId || item.customerId === filters.customerId)
}))
const pagedRows = computed(() => filteredRows.value.slice((page.value - 1) * pageSize, page.value * pageSize))
const metrics = computed(() => [
{ label: '全部订单', value: store.orders.length, note: '当前数据范围' },
{ label: '待审核/受理', value: store.pendingOrders, note: '需要运营处理' },
{ label: '运输中', value: store.orders.filter((item) => item.status === '运输中').length, note: '正在履约' },
{ label: '应收金额', value: `¥${store.orders.reduce((total, item) => total + (item.currency === 'CNY' ? item.amount : 0), 0).toLocaleString()}`, note: '人民币订单合计' },
])
watch(filters, () => { page.value = 1 })
watch(dateRange, () => { page.value = 1 })
watch(
() => access.can('first-leg:create'),
(canCreate) => {
if (canCreate || !editorOpen.value || form.id) return
editorOpen.value = false
Object.assign(form, emptyForm())
},
{ flush: 'sync' },
)
function resetFilters() { Object.assign(filters, { keyword: '', status: '', mode: '', customerId: '' }); dateRange.value = [] }
function openCreate() { Object.assign(form, emptyForm()); captureBaseline(); editorOpen.value = true }
function openEdit(row: FirstLegOrder) { Object.assign(form, row); captureBaseline(); detailOpen.value = false; editorOpen.value = true }
function openDetail(row: FirstLegOrder) { currentOrder.value = row; detailOpen.value = true }
function handleCustomerChange(customerId: string) {
const customer = store.customers.find((item) => item.id === customerId)
if (!customer) return
form.customerName = customer.name
if (!form.shipper) form.shipper = customer.name
}
function saveOrder(status: '草稿' | '待审核') {
if (!form.customerId || !form.origin.trim() || !form.destination.trim() || !form.receivingWarehouseId || !form.cargoName.trim()) {
ElMessage.warning('请填写客户、起运地、目的地、平台收货仓和货物名称')
return
}
const customer = store.customers.find((item) => item.id === form.customerId)
try {
store.saveOrder({ ...form, id: form.id || undefined, customerName: customer?.name || form.customerName, status: form.id ? form.status : status })
editorOpen.value = false
ElMessage.success(status === '草稿' ? '订单草稿已保存' : '头程订单已创建并提交审核')
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : '订单保存失败')
}
}
function openStatusDialog(rows: FirstLegOrder[]) {
if (rows.length === 0) return
statusTargetIds.value = rows.map((item) => item.id)
statusForm.status = rows.length === 1 ? rows[0].status : '已受理'
statusForm.operator = rows.length === 1 && rows[0].operator !== '未分配' ? rows[0].operator : ''
statusForm.note = ''
statusDialogOpen.value = true
}
function applyStatus() {
if (!statusForm.operator && !['草稿', '待审核', '已取消'].includes(statusForm.status)) {
ElMessage.warning('请指定订单负责人')
return
}
try { statusTargetIds.value.forEach((id) => store.updateOrderStatus(id, statusForm.status, statusForm.operator, statusForm.note)) } catch (error) { ElMessage.error(error instanceof Error ? error.message : '订单状态更新失败'); return }
statusDialogOpen.value = false
selectedRows.value = []
if (currentOrder.value) currentOrder.value = store.orders.find((item) => item.id === currentOrder.value?.id) || null
ElMessage.success(statusTargetIds.value.length > 1 ? `已更新 ${statusTargetIds.value.length} 笔订单` : '订单状态已更新')
}
function openDeliveryDialog(order: FirstLegOrder) {
currentOrder.value = order
Object.assign(deliveryForm, {
orderId: order.id,
expressCompanyId: order.deliveryInfo?.expressCompanyId || '',
trackingNo: order.deliveryInfo?.trackingNo || '',
operator: order.deliveryInfo?.operator || (order.operator === '未分配' ? '' : order.operator),
})
deliveryDialogOpen.value = true
}
function saveDeliveryInfo() {
if (!deliveryForm.expressCompanyId || !deliveryForm.trackingNo.trim() || !deliveryForm.operator) {
ElMessage.warning('请选择快递公司,并填写快递单号和操作人')
return
}
try {
store.saveOrderDeliveryInfo(deliveryForm.orderId, deliveryForm.expressCompanyId, deliveryForm.trackingNo, deliveryForm.operator)
currentOrder.value = store.orders.find((item) => item.id === deliveryForm.orderId) || null
deliveryDialogOpen.value = false
ElMessage.success('履约快递信息已保存')
} catch (error) {
ElMessage.error(error instanceof Error ? error.message : '履约快递信息保存失败')
}
}
function deliveryCompanies(order: FirstLegOrder | null) {
return store.expressCompanies.filter((item) => item.status === '合作中' || item.id === order?.deliveryInfo?.expressCompanyId)
}
function statusStep(status: OrderStatus) {
const index = ['待审核', '已受理', '已订舱', '运输中', '已完成'].indexOf(status)
return index < 0 ? 0 : index + 1
}
function contactCustomerForModification(order: FirstLegOrder) {
try { store.recordCustomerModificationContact(order.id, order.operator === '未分配' ? '平台管理员' : order.operator); currentOrder.value = store.orders.find((item) => item.id === order.id) || null; ElMessage.success('已记录联系客户修改,订单继续保持待审核') }
catch (error) { ElMessage.error(error instanceof Error ? error.message : '记录失败') }
}
function canEditOrder(order: FirstLegOrder) { return access.can('first-leg:status') || (order.status === '草稿' && access.can('first-leg:create')) }
function exportOrders() { ElMessage.success(`已生成 ${filteredRows.value.length} 笔订单的导出任务`) }
</script>
<template>
<div class="business-page">
<MetricStrip :items="metrics" />
<section class="surface-panel filter-panel order-filter" aria-label="头程订单筛选">
<div class="filter-fields">
<el-input v-model="filters.keyword" clearable placeholder="订单号、客户、参考号或港口" aria-label="订单关键词"><template #prefix><Search :size="16" /></template></el-input>
<el-select v-model="filters.customerId" clearable filterable placeholder="全部客户" aria-label="订单客户"><el-option v-for="item in store.customers" :key="item.id" :label="item.name" :value="item.id" /></el-select>
<el-select v-model="filters.status" clearable placeholder="全部状态" aria-label="订单状态"><el-option v-for="item in statusOptions" :key="item" :label="item" :value="item" /></el-select>
<el-select v-model="filters.mode" clearable placeholder="全部运输方式" aria-label="运输方式"><el-option v-for="item in ['海运整箱 FCL','海运拼箱 LCL','空运','陆运']" :key="item" :label="item" :value="item" /></el-select>
<el-date-picker v-model="dateRange" type="daterange" value-format="YYYY-MM-DD" range-separator="至" start-placeholder="创建开始" end-placeholder="创建结束" aria-label="订单创建日期" />
</div>
<div class="filter-actions"><el-button @click="resetFilters"><RotateCcw :size="16" />重置</el-button><el-button v-if="access.can('first-leg:status')" :disabled="selectedRows.length === 0" @click="openStatusDialog(selectedRows)"><ClipboardCheck :size="16" />批量更新</el-button><el-button v-if="!access.usesServerPermissions" @click="exportOrders"><Download :size="16" />导出</el-button><el-button v-if="access.can('first-leg:create')" type="primary" @click="openCreate"><Plus :size="16" />创建订单</el-button></div>
</section>
<section class="surface-panel table-panel">
<header class="panel-heading"><div><h2>头程订单</h2><p>从审核、受理、订舱到运输完成,统一跟进订单状态与负责人。</p></div><span v-if="selectedRows.length" class="selection-note">已选择 {{ selectedRows.length }} 笔</span></header>
<el-table :data="pagedRows" row-key="id" empty-text="没有符合条件的头程订单" @selection-change="selectedRows = $event">
<el-table-column type="selection" width="44" />
<el-table-column label="订单信息" min-width="210"><template #default="{ row }"><div class="primary-cell"><strong class="order-no">{{ row.no }}</strong><span>{{ row.referenceNo || '无客户参考号' }} · {{ row.createdAt }}</span></div></template></el-table-column>
<el-table-column label="客户" min-width="185" show-overflow-tooltip><template #default="{ row }"><div class="primary-cell"><strong>{{ row.customerName }}</strong><span>{{ row.businessType }} · {{ row.priority }}</span></div></template></el-table-column>
<el-table-column label="运输方案" min-width="190"><template #default="{ row }"><div class="primary-cell"><strong>{{ row.transportMode }}</strong><span>{{ row.container }}</span></div></template></el-table-column>
<el-table-column label="路线" min-width="160"><template #default="{ row }"><div class="route-cell"><span>{{ row.origin }}</span><i>→</i><span>{{ row.destination }}</span></div></template></el-table-column>
<el-table-column label="平台收货仓" min-width="150"><template #default="{ row }"><div class="primary-cell"><strong>{{ row.receivingWarehouseSnapshot?.name || '-' }}</strong><span>{{ row.receivingWarehouseSnapshot?.code || '-' }}</span></div></template></el-table-column>
<el-table-column label="履约快递" min-width="160"><template #default="{ row }"><div class="primary-cell"><strong>{{ row.deliveryInfo?.expressCompanyName || '未维护' }}</strong><span>{{ row.deliveryInfo?.trackingNo || '—' }}</span></div></template></el-table-column>
<el-table-column label="应收" width="118" align="right"><template #default="{ row }"><strong>{{ row.currency === 'CNY' ? '¥' : '$' }}{{ row.amount.toLocaleString() }}</strong></template></el-table-column>
<el-table-column label="负责人" width="100"><template #default="{ row }"><span :class="{ 'muted-value': row.operator === '未分配' }">{{ row.operator }}</span></template></el-table-column>
<el-table-column label="状态" width="96"><template #default="{ row }"><StatusBadge :status="row.status" /></template></el-table-column>
<el-table-column label="操作" width="290" fixed="right"><template #default="{ row }"><div class="table-actions"><el-button link type="primary" @click="openDetail(row as FirstLegOrder)"><Eye :size="15" />详情</el-button><el-button v-if="canEditOrder(row as FirstLegOrder)" link type="primary" @click="openEdit(row as FirstLegOrder)"><Pencil :size="15" />编辑</el-button><el-button v-if="access.can('first-leg:delivery')" link type="primary" @click="openDeliveryDialog(row as FirstLegOrder)"><PackageCheck :size="15" />快递</el-button><el-button v-if="access.can('first-leg:status')" link type="success" @click="openStatusDialog([row as FirstLegOrder])"><UserRoundCheck :size="15" />处理</el-button></div></template></el-table-column>
</el-table>
<footer class="table-footer"><span>显示 {{ pagedRows.length }} 条,共 {{ filteredRows.length }} 条</span><el-pagination v-model:current-page="page" :page-size="pageSize" :total="filteredRows.length" layout="prev, pager, next" /></footer>
</section>
<el-drawer v-model="editorOpen" :title="form.id ? '编辑头程订单' : '创建头程订单'" size="780px" class="business-drawer editor-drawer order-editor" :modal="false" :close-on-click-modal="false">
<el-form label-position="top" class="drawer-form">
<div class="form-section"><h3>业务信息</h3><div class="form-grid three-cols"><el-form-item label="客户" required><el-select v-model="form.customerId" filterable placeholder="请选择客户" @change="handleCustomerChange"><el-option v-for="item in store.customers.filter((customer) => customer.status === '正常')" :key="item.id" :label="item.name" :value="item.id" /></el-select></el-form-item><el-form-item label="运输方式"><el-select v-model="form.transportMode"><el-option v-for="item in ['海运整箱 FCL','海运拼箱 LCL','空运','陆运']" :key="item" :label="item" :value="item" /></el-select></el-form-item><el-form-item label="业务类型"><el-radio-group v-model="form.businessType"><el-radio-button label="出口" value="出口" /><el-radio-button label="进口" value="进口" /></el-radio-group></el-form-item><el-form-item label="客户参考号"><el-input v-model="form.referenceNo" /></el-form-item><el-form-item label="优先级"><el-select v-model="form.priority"><el-option label="普通" value="普通" /><el-option label="紧急" value="紧急" /><el-option label="加急" value="加急" /></el-select></el-form-item><el-form-item label="负责人"><el-select v-model="form.operator" clearable placeholder="暂不分配"><el-option v-for="item in operatorOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></div></div>
<div class="form-section"><h3>运输信息</h3><div class="form-grid three-cols"><el-form-item label="起运地 / POL" required><el-input v-model="form.origin" placeholder="如 宁波" /></el-form-item><el-form-item label="目的地 / POD" required><el-input v-model="form.destination" placeholder="如 洛杉矶" /></el-form-item><el-form-item label="平台收货仓" required><el-select v-model="form.receivingWarehouseId" filterable placeholder="请选择收货仓"><el-option v-for="warehouse in selectableWarehouses" :key="warehouse.id" :label="`${warehouse.name} · ${warehouse.city}`" :value="warehouse.id" :disabled="warehouse.status !== 'ACTIVE'" /></el-select></el-form-item><el-form-item label="柜型 / 箱量"><el-input v-model="form.container" placeholder="如 40HQ x 2" /></el-form-item><el-form-item label="预计出发 ETD"><el-date-picker v-model="form.etd" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" /></el-form-item><el-form-item label="预计到达 ETA"><el-date-picker v-model="form.eta" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" /></el-form-item><el-form-item label="应收金额"><div class="amount-field"><el-select v-model="form.currency"><el-option label="CNY" value="CNY" /><el-option label="USD" value="USD" /></el-select><el-input-number v-model="form.amount" :min="0" :precision="2" controls-position="right" /></div></el-form-item></div></div>
<div class="form-section"><h3>货物与收发货方</h3><div class="form-grid three-cols"><el-form-item label="货物名称" required><el-input v-model="form.cargoName" /></el-form-item><el-form-item label="件数"><el-input-number v-model="form.pieces" :min="1" controls-position="right" /></el-form-item><el-form-item label="毛重 (kg)"><el-input-number v-model="form.weight" :min="0" :precision="2" controls-position="right" /></el-form-item><el-form-item label="体积 (m³)"><el-input-number v-model="form.volume" :min="0" :precision="2" controls-position="right" /></el-form-item><el-form-item label="发货方"><el-input v-model="form.shipper" /></el-form-item><el-form-item label="收货方"><el-input v-model="form.consignee" /></el-form-item></div><el-form-item label="内部备注"><el-input v-model="form.remark" type="textarea" :rows="3" placeholder="仅平台内部可见" /></el-form-item></div>
</el-form>
<template #footer><div class="drawer-footer"><el-button @click="editorOpen = false">取消</el-button><el-button v-if="!form.id && access.can('first-leg:create')" @click="saveOrder('草稿')">保存草稿</el-button><el-button v-if="form.id || access.can('first-leg:create')" type="primary" @click="saveOrder('待审核')">{{ form.id ? '保存修改' : '创建并提交' }}</el-button></div></template>
</el-drawer>
<el-drawer v-model="detailOpen" title="订单详情" size="720px" class="business-drawer">
<template v-if="currentOrder">
<div class="order-detail-head"><div><span>{{ currentOrder.transportMode }}</span><h2>{{ currentOrder.no }}</h2><p>{{ currentOrder.customerName }} · {{ currentOrder.referenceNo }}</p></div><StatusBadge :status="currentOrder.status" /></div>
<div class="order-progress"><el-steps :active="statusStep(currentOrder.status)" finish-status="success" align-center><el-step title="待审核" /><el-step title="已受理" /><el-step title="已订舱" /><el-step title="运输中" /><el-step title="已完成" /></el-steps></div>
<div class="detail-section"><h3>订单概要</h3><el-descriptions :column="2" border><el-descriptions-item label="运输路线">{{ currentOrder.origin }} → {{ currentOrder.destination }}</el-descriptions-item><el-descriptions-item label="业务类型">{{ currentOrder.businessType }}</el-descriptions-item><el-descriptions-item label="平台收货仓"><strong>{{ currentOrder.receivingWarehouseSnapshot?.name || '-' }}</strong><span v-if="currentOrder.receivingWarehouseSnapshot" class="warehouse-snapshot-detail">{{ currentOrder.receivingWarehouseSnapshot.code }} · {{ currentOrder.receivingWarehouseSnapshot.province }}{{ currentOrder.receivingWarehouseSnapshot.city }}{{ currentOrder.receivingWarehouseSnapshot.address }}</span></el-descriptions-item><el-descriptions-item label="柜型/箱量">{{ currentOrder.container }}</el-descriptions-item><el-descriptions-item label="负责人">{{ currentOrder.operator }}</el-descriptions-item><el-descriptions-item label="ETD / ETA">{{ currentOrder.etd || '-' }} / {{ currentOrder.eta || '-' }}</el-descriptions-item><el-descriptions-item label="应收金额">{{ currentOrder.currency === 'CNY' ? '¥' : '$' }}{{ currentOrder.amount.toLocaleString() }}</el-descriptions-item></el-descriptions></div>
<div class="detail-section"><h3>货物信息</h3><MetricStrip :items="[{ label: '品名', value: currentOrder.cargoName }, { label: '件数', value: currentOrder.pieces }, { label: '毛重', value: `${currentOrder.weight.toLocaleString()} kg` }, { label: '体积', value: `${currentOrder.volume} m³` }]" /><div class="party-grid"><div><span>发货方</span><strong>{{ currentOrder.shipper }}</strong></div><div><span>收货方</span><strong>{{ currentOrder.consignee }}</strong></div></div></div>
<div class="detail-section"><h3>履约快递</h3><el-empty v-if="!currentOrder.deliveryInfo" description="尚未维护快递公司和快递单号" :image-size="64" /><el-descriptions v-else :column="2" border><el-descriptions-item label="快递公司">{{ currentOrder.deliveryInfo.expressCompanyName }}({{ currentOrder.deliveryInfo.expressCompanyCode }})</el-descriptions-item><el-descriptions-item label="快递单号">{{ currentOrder.deliveryInfo.trackingNo }}</el-descriptions-item><el-descriptions-item label="维护人">{{ currentOrder.deliveryInfo.operator }}</el-descriptions-item><el-descriptions-item label="更新时间">{{ currentOrder.deliveryInfo.updatedAt }}</el-descriptions-item></el-descriptions></div>
<div class="detail-section"><h3>处理记录</h3><el-timeline><el-timeline-item v-for="item in [...currentOrder.timeline].reverse()" :key="`${item.time}-${item.title}`" :timestamp="`${item.time} · ${item.operator}`" placement="top"><strong>{{ item.title }}</strong><p>{{ item.description }}</p></el-timeline-item></el-timeline></div>
</template>
<template #footer><div class="drawer-footer"><el-button @click="detailOpen = false">关闭</el-button><el-button v-if="currentOrder && canEditOrder(currentOrder)" @click="openEdit(currentOrder)">编辑订单</el-button><el-button v-if="currentOrder?.status === '待审核' && access.can('first-leg:status')" @click="contactCustomerForModification(currentOrder)">联系客户修改</el-button><el-button v-if="access.can('first-leg:delivery')" @click="currentOrder && openDeliveryDialog(currentOrder)">维护快递</el-button><el-button v-if="access.can('first-leg:status')" type="primary" @click="currentOrder && openStatusDialog([currentOrder])">更新状态</el-button></div></template>
</el-drawer>
<el-dialog v-model="statusDialogOpen" :title="statusTargetIds.length > 1 ? `批量更新 ${statusTargetIds.length} 笔订单` : '处理订单'" width="520px" class="status-dialog">
<el-form label-position="top"><el-form-item label="订单状态"><el-select v-model="statusForm.status"><el-option v-for="item in availableStatusOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item><el-form-item label="负责人"><el-select v-model="statusForm.operator" clearable placeholder="请选择负责人"><el-option v-for="item in operatorOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item><el-form-item label="处理说明"><el-input v-model="statusForm.note" type="textarea" :rows="3" placeholder="记录本次状态变更的原因或下一步动作" /></el-form-item></el-form>
<template #footer><el-button @click="statusDialogOpen = false">取消</el-button><el-button v-if="access.can('first-leg:status')" type="primary" @click="applyStatus">确认更新</el-button></template>
</el-dialog>
<el-dialog v-model="deliveryDialogOpen" title="维护履约快递" width="520px">
<el-alert title="一期每个订单仅维护一组快递公司和快递单号;修改会覆盖当前值并追加审计记录。" type="info" :closable="false" show-icon />
<el-form label-position="top" class="delivery-form"><el-form-item label="快递公司" required><el-select v-model="deliveryForm.expressCompanyId" filterable placeholder="请选择合作中的快递公司"><el-option v-for="item in deliveryCompanies(currentOrder)" :key="item.id" :label="`${item.name} · ${item.code}`" :value="item.id" :disabled="item.status !== '合作中'" /></el-select></el-form-item><el-form-item label="快递单号" required><el-input v-model="deliveryForm.trackingNo" maxlength="80" placeholder="请输入完整快递单号" /></el-form-item><el-form-item label="操作人" required><el-select v-model="deliveryForm.operator" filterable placeholder="请选择操作人"><el-option v-for="item in operatorOptions" :key="item" :label="item" :value="item" /></el-select></el-form-item></el-form>
<template #footer><el-button @click="deliveryDialogOpen = false">取消</el-button><el-button v-if="access.can('first-leg:delivery')" type="primary" @click="saveDeliveryInfo">保存快递信息</el-button></template>
</el-dialog>
</div>
</template>
<style scoped>
.warehouse-snapshot-detail { display: block; margin-top: 4px; color: var(--cc-muted); font-size: 11px; line-height: 1.5; }
</style>