215 lines
23 KiB
Vue
215 lines
23 KiB
Vue
<script setup lang="ts">
|
||
import { computed, onMounted, reactive, ref } from 'vue'
|
||
import { Eye, MoreHorizontal, Pencil, Truck, XCircle } from '@lucide/vue'
|
||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||
import { useRouter } from 'vue-router'
|
||
import { getApiErrorDetails, type ApiErrorDetails } from '@/shared/api/http'
|
||
import AdminPage from '@/shared/components/AdminPage.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 OperationErrorToast from '@/shared/components/OperationErrorToast.vue'
|
||
import { useAccessPreviewStore } from '@/core/access/accessPreview'
|
||
import { cancelDispatch, listDispatches, transferDispatchToDelivery, updateDispatch, type DispatchSummaryDto } from '../api/dispatches'
|
||
import AppDialog from '@/shared/components/AppDialog.vue'
|
||
import type { DispatchListItem } from '../types'
|
||
import { formatOverseasDateTime } from '../lastMilePresentation'
|
||
import { overseasWarehousePermissions as permission } from '../permissions'
|
||
|
||
const router = useRouter()
|
||
const access = useAccessPreviewStore()
|
||
const rows = ref<DispatchListItem[]>([])
|
||
const loading = ref(false)
|
||
const loadError = ref<ApiErrorDetails | null>(null)
|
||
const page = ref(1)
|
||
const pageSize = ref(10)
|
||
const total = ref(0)
|
||
const filters = ref<QueryModel>({ dispatchNo: '', vehiclePlate: '', status: '', fbaAppointmentNo: '' })
|
||
const transferOpen = ref(false); const transferRow = ref<DispatchListItem | null>(null); const transferSaving = ref(false)
|
||
const mutationError = ref<ApiErrorDetails | null>(null)
|
||
const editOpen = ref(false); const editRow = ref<DispatchListItem | null>(null); const editSaving = ref(false)
|
||
const dispatchSnapshots = new Map<string, DispatchSummaryDto>()
|
||
const editForm = reactive({ vehiclePlate: '', driverName: '', driverPhone: '', carrierName: '', route: '', departAt: '', fbaAppointmentNo: '', fbaAppointmentStatus: '', vehicleAppointmentStatus: '', estimatedArrivalAt: '' })
|
||
const transferForm = reactive({ plannedDeliveryAt: '', deliveryMethod: '', waybillNo: '', carrierName: '', pickupMethod: '', loadingType: '', vehicleType: '', vehicleSize: '', driverName: '', driverPhone: '', vehiclePlate: '', appointmentAccount: '', sellerName: '', deliveryFee: undefined as number | undefined, loadingFee: undefined as number | undefined, appointmentFee: undefined as number | undefined, remark: '' })
|
||
|
||
const deliveryMethodOptions = [
|
||
{ 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' },
|
||
]
|
||
const pickupMethodOptions = [
|
||
{ label: '一提一卸', value: 'ONE_PICKUP_ONE_DROP' }, { label: '一提多卸', value: 'ONE_PICKUP_MULTI_DROP' },
|
||
{ label: '多提一卸', value: 'MULTI_PICKUP_ONE_DROP' }, { label: '多提多卸', value: 'MULTI_PICKUP_MULTI_DROP' },
|
||
{ label: '仓库自提', value: 'WAREHOUSE_PICKUP' },
|
||
]
|
||
const loadingTypeOptions = [{ label: '托盘', value: 'PALLET' }, { label: '地板', value: 'FLOOR' }, { label: '混装', value: 'MIXED' }]
|
||
const vehicleTypeOptions = ['厢式货车', '平板车', '冷藏车', '集装箱卡车', '尾板车'].map((label) => ({ label, value: label }))
|
||
const vehicleSizeOptions = ['53尺大车(30托)', '26尺中车(14托)', '16尺小车(8托)', 'Cargo Van(4托)'].map((label) => ({ label, value: label }))
|
||
const appointmentAccountOptions = [
|
||
{ label: '4号账号(美西)', value: '4' }, { label: '8号账号(美东)', value: '8' },
|
||
{ label: '10号账号(美中)', value: '10' }, { label: '12号账号(加拿大)', value: '12' }, { label: '买约', value: 'BUY_APPOINTMENT' },
|
||
]
|
||
const isBuyAppointment = computed(() => transferForm.appointmentAccount === 'BUY_APPOINTMENT')
|
||
const needsLoadingFee = computed(() => ['FLOOR', 'MIXED'].includes(transferForm.loadingType))
|
||
|
||
const statusOptions = [
|
||
{ label: '待发车', value: 'PENDING_DEPARTURE' },
|
||
{ label: '拣货中', value: 'PICKING' },
|
||
{ label: '待发车(拣货完成)', value: 'READY_TO_DEPART' },
|
||
{ label: '待发车(部分完成)', value: 'PARTIAL_READY_TO_DEPART' },
|
||
{ label: '已发车', value: 'DEPARTED' },
|
||
{ label: '已发车(部分完成)', value: 'PARTIAL_DEPARTED' },
|
||
{ label: '已取消', value: 'CANCELLED' },
|
||
]
|
||
|
||
const fields = computed<QueryField[]>(() => [
|
||
{ key: 'dispatchNo', type: 'input', label: '排车单号', placeholder: '请输入排车单号' },
|
||
{ key: 'vehiclePlate', type: 'input', label: '车牌号', placeholder: '请输入车牌号' },
|
||
{ key: 'status', type: 'select', label: '车辆状态', placeholder: '全部状态', options: statusOptions },
|
||
{ key: 'fbaAppointmentNo', type: 'input', label: 'FBA预约号', placeholder: '请输入FBA预约号' },
|
||
])
|
||
|
||
const columns: DataTableColumn[] = [
|
||
{ key: 'actions', label: '操作', width: 240, fixed: 'left', slotName: 'actions' },
|
||
{ key: 'dispatchNo', prop: 'dispatchNo', label: '排车单号', minWidth: 170, fixed: 'left' },
|
||
{ key: 'vehiclePlate', prop: 'vehiclePlate', label: '车牌号', minWidth: 130 },
|
||
{ key: 'driverName', prop: 'driverName', label: '司机', minWidth: 120 },
|
||
{ key: 'driverPhone', prop: 'driverPhone', label: '联系电话', minWidth: 140 },
|
||
{ key: 'carrierName', prop: 'carrierName', label: '托车公司名称', minWidth: 160 },
|
||
{ key: 'orderNos', prop: 'orderNos', label: '关联订单', minWidth: 220 },
|
||
{ key: 'route', prop: 'route', label: '路线', minWidth: 180 },
|
||
{ key: 'overseasWarehouse', prop: 'overseasWarehouse', label: '海外仓', minWidth: 160 },
|
||
{ key: 'departAt', prop: 'departAt', label: '发车时间', minWidth: 170 },
|
||
{ key: 'fbaAppointment', prop: 'fbaAppointment', label: 'FBA预约', minWidth: 180 },
|
||
{ key: 'vehicleAppointment', prop: 'vehicleAppointment', label: '车辆预约', minWidth: 130 },
|
||
{ key: 'estimatedArrivalAt', prop: 'estimatedArrivalAt', label: '预计到达时间', minWidth: 170 },
|
||
{ key: 'palletCount', prop: 'palletCount', label: '托盘数', width: 90, align: 'right' },
|
||
{ key: 'packageCount', prop: 'packageCount', label: '箱数', width: 80, align: 'right' },
|
||
{ key: 'status', prop: 'status', label: '车辆状态', width: 150, slotName: 'status' },
|
||
{ key: 'createdAt', prop: 'createdAt', label: '创建时间', minWidth: 170 },
|
||
]
|
||
|
||
function text(value: unknown) {
|
||
const normalized = String(value ?? '').trim()
|
||
return normalized || '—'
|
||
}
|
||
|
||
function number(value: unknown) {
|
||
const normalized = Number(value)
|
||
return Number.isFinite(normalized) ? normalized : 0
|
||
}
|
||
|
||
function statusLabel(value: unknown) {
|
||
const status = String(value || '')
|
||
return statusOptions.find((option) => option.value === status)?.label || text(value)
|
||
}
|
||
|
||
function appointmentLabel(numberValue: unknown, statusValue: unknown) {
|
||
const appointmentNo = text(numberValue)
|
||
const appointmentStatus = text(statusValue)
|
||
if (appointmentNo === '—') return appointmentStatus
|
||
return appointmentStatus === '—' ? appointmentNo : `${appointmentNo} · ${appointmentStatus}`
|
||
}
|
||
|
||
function toListItem(dto: DispatchSummaryDto): DispatchListItem {
|
||
return {
|
||
id: String(dto.id),
|
||
dispatchNo: text(dto.dispatchNo),
|
||
vehiclePlate: text(dto.vehiclePlate),
|
||
driverName: text(dto.driverName),
|
||
driverPhone: text(dto.driverPhone),
|
||
carrierName: text(dto.carrierName),
|
||
orderNos: text(dto.orderNos),
|
||
route: text(dto.route),
|
||
overseasWarehouse: text(dto.overseasWarehouseName || '仓库名称待补充'),
|
||
departAt: formatOverseasDateTime(dto.departAt),
|
||
fbaAppointment: appointmentLabel(dto.fbaAppointmentNo, dto.fbaAppointmentStatus),
|
||
vehicleAppointment: text(dto.vehicleAppointmentStatus),
|
||
estimatedArrivalAt: formatOverseasDateTime(dto.estimatedArrivalAt),
|
||
status: statusLabel(dto.status),
|
||
statusCode: String(dto.status || ''),
|
||
palletCount: number(dto.palletCount),
|
||
packageCount: number(dto.packageCount),
|
||
createdAt: formatOverseasDateTime(dto.createdAt),
|
||
}
|
||
}
|
||
|
||
function dispatchError(error: unknown) {
|
||
const details = getApiErrorDetails(error, '排车列表加载失败,请重试')
|
||
return details.code === 'HTTP_404'
|
||
? { ...details, message: '排车接口未启用或不存在,请确认海外尾程排车接口已发布' }
|
||
: details
|
||
}
|
||
|
||
async function load(model: QueryModel = filters.value) {
|
||
Object.assign(filters.value, model)
|
||
loading.value = true
|
||
loadError.value = null
|
||
try {
|
||
const result = await listDispatches({
|
||
page: page.value,
|
||
size: pageSize.value,
|
||
dispatchNo: String(filters.value.dispatchNo || '').trim() || undefined,
|
||
vehiclePlate: String(filters.value.vehiclePlate || '').trim() || undefined,
|
||
status: String(filters.value.status || '').trim() || undefined,
|
||
fbaAppointmentNo: String(filters.value.fbaAppointmentNo || '').trim() || undefined,
|
||
})
|
||
dispatchSnapshots.clear()
|
||
result.records.forEach((item) => dispatchSnapshots.set(String(item.dispatchNo), item))
|
||
rows.value = result.records.map(toListItem)
|
||
total.value = result.total
|
||
} catch (error) {
|
||
rows.value = []
|
||
total.value = 0
|
||
loadError.value = dispatchError(error)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
function search(model: QueryModel) { page.value = 1; void load(model) }
|
||
function reset(model: QueryModel) { page.value = 1; void load(model) }
|
||
function openDetail(row: DispatchListItem) { void router.push({ name: 'overseas-dispatch-detail', params: { dispatchNo: row.dispatchNo } }) }
|
||
function isPendingDeparture(row: DispatchListItem) { return row.statusCode === 'PENDING_DEPARTURE' }
|
||
function canEdit(row: DispatchListItem) { return access.can(permission.dispatchEdit) && isPendingDeparture(row) }
|
||
function canCancel(row: DispatchListItem) { return access.can(permission.dispatchCancel) && row.statusCode === 'PENDING_DEPARTURE' }
|
||
function canTransfer(row: DispatchListItem) { return access.can(permission.dispatchTransferDelivery) && isPendingDeparture(row) }
|
||
function openEdit(row: DispatchListItem) { if (!canEdit(row)) return; const source = dispatchSnapshots.get(row.dispatchNo); editRow.value = row; Object.assign(editForm, { vehiclePlate: source?.vehiclePlate || '', driverName: source?.driverName || '', driverPhone: source?.driverPhone || '', carrierName: source?.carrierName || '', route: source?.route || '', departAt: source?.departAt || '', fbaAppointmentNo: source?.fbaAppointmentNo || '', fbaAppointmentStatus: source?.fbaAppointmentStatus || '', vehicleAppointmentStatus: source?.vehicleAppointmentStatus || '', estimatedArrivalAt: source?.estimatedArrivalAt || '' }); mutationError.value = null; editOpen.value = true }
|
||
function openTransfer(row: DispatchListItem) { if (!canTransfer(row)) return; transferRow.value = row; Object.assign(transferForm, { plannedDeliveryAt: '', deliveryMethod: '', waybillNo: '', carrierName: row.carrierName === '—' ? '' : row.carrierName, pickupMethod: '', loadingType: '', vehicleType: '', vehicleSize: '', driverName: row.driverName === '—' ? '' : row.driverName, driverPhone: row.driverPhone === '—' ? '' : row.driverPhone, vehiclePlate: row.vehiclePlate === '—' ? '' : row.vehiclePlate, appointmentAccount: '', sellerName: '', deliveryFee: undefined, loadingFee: undefined, appointmentFee: undefined, remark: '' }); mutationError.value = null; transferOpen.value = true }
|
||
async function submitEdit() { if (!editRow.value || !canEdit(editRow.value) || editSaving.value) return; editSaving.value = true; mutationError.value = null; try { await updateDispatch(editRow.value.dispatchNo, { vehiclePlate: editForm.vehiclePlate.trim() || null, driverName: editForm.driverName.trim() || null, driverPhone: editForm.driverPhone.trim() || null, carrierName: editForm.carrierName.trim() || null, route: editForm.route.trim() || null, departAt: editForm.departAt || null, fbaAppointmentNo: editForm.fbaAppointmentNo.trim() || null, fbaAppointmentStatus: editForm.fbaAppointmentStatus || null, vehicleAppointmentStatus: editForm.vehicleAppointmentStatus || null, estimatedArrivalAt: editForm.estimatedArrivalAt || null }); editOpen.value = false; ElMessage.success('排车信息已保存'); await load() } catch (cause) { mutationError.value = getApiErrorDetails(cause, '排车信息保存失败,请重试') } finally { editSaving.value = false } }
|
||
async function cancel(row: DispatchListItem) { if (!canCancel(row)) return; try { await ElMessageBox.confirm(`确认取消排车单 ${row.dispatchNo}?取消后将释放托盘锁。`, '取消排车', { confirmButtonText: '确认取消', cancelButtonText: '返回', type: 'warning' }) } catch { return } mutationError.value = null; try { await cancelDispatch(row.dispatchNo); ElMessage.success('排车单已取消'); await load() } catch (cause) { mutationError.value = getApiErrorDetails(cause, '排车取消失败,请重试') } }
|
||
function handleMore(command: string, row: DispatchListItem) { if (command === 'edit') openEdit(row); else if (command === 'cancel') void cancel(row) }
|
||
async function submitTransfer() {
|
||
if (!transferRow.value || !canTransfer(transferRow.value)) return
|
||
if (!transferForm.plannedDeliveryAt || !transferForm.deliveryMethod || !transferForm.pickupMethod || !transferForm.loadingType) { ElMessage.warning('请填写预计派送时间、派送方式、提货方式和装车类型'); return }
|
||
if (transferForm.deliveryFee == null) { ElMessage.warning('请填写派送费用'); return }
|
||
if (needsLoadingFee.value && transferForm.loadingFee == null) { ElMessage.warning('当前装车类型需要填写装车费'); return }
|
||
if (isBuyAppointment.value && (!transferForm.sellerName.trim() || transferForm.appointmentFee == null)) { ElMessage.warning('买约时请填写卖家姓名和买约费用'); return }
|
||
const feeInputs = [['DELIVERY_FEE', transferForm.deliveryFee, '派送费用'], ['LOADING_FEE', needsLoadingFee.value ? transferForm.loadingFee : undefined, '装车费'], ['APPOINTMENT_FEE', isBuyAppointment.value ? transferForm.appointmentFee : undefined, '买约费用']] as const
|
||
const fees = feeInputs.filter(([, amount]) => amount != null).map(([feeType, amount, remark]) => ({ feeType, currency: 'USD', amount: Number(amount), remark }))
|
||
transferSaving.value = true
|
||
try { await transferDispatchToDelivery(transferRow.value.dispatchNo, { idempotencyKey: `web-transfer-${transferRow.value.dispatchNo}-${Date.now()}`, plannedDeliveryAt: transferForm.plannedDeliveryAt, deliveryMethod: transferForm.deliveryMethod, waybillNo: transferForm.waybillNo.trim() || undefined, carrierName: transferForm.carrierName.trim() || undefined, pickupMethod: transferForm.pickupMethod, loadingType: transferForm.loadingType, vehicleType: transferForm.vehicleType || undefined, vehicleSize: transferForm.vehicleSize || undefined, driverName: transferForm.driverName.trim() || undefined, driverPhone: transferForm.driverPhone.trim() || undefined, vehiclePlate: transferForm.vehiclePlate.trim() || undefined, appointmentAccount: transferForm.appointmentAccount || undefined, sellerName: isBuyAppointment.value ? transferForm.sellerName.trim() : undefined, remark: transferForm.remark.trim() || undefined, fees }); transferOpen.value = false; ElMessage.success('已生成出库单和派送单'); await load() } catch (cause) { mutationError.value = getApiErrorDetails(cause, '转派送失败,请重试') } finally { transferSaving.value = false }
|
||
}
|
||
|
||
onMounted(load)
|
||
</script>
|
||
|
||
<template>
|
||
<AdminPage><ListPageLayout aria-label="海外仓排车列表">
|
||
<OperationErrorToast :details="mutationError" />
|
||
<template #toolbar><QueryToolbar v-model="filters" class="surface-panel" :fields="fields" :loading="loading" ariaLabel="排车列表筛选" preference-key="overseas-dispatches-query" @search="search" @reset="reset" /></template>
|
||
<template v-if="loadError" #state><LoadErrorState title="排车列表加载失败" :message="loadError.message" :loading="loading" @retry="load"><template #details><ApiErrorMeta :details="loadError" /></template></LoadErrorState></template>
|
||
<section v-if="!loadError" 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-dispatches" row-key="id" empty-text="暂无排车记录" @update:page="load()" @update:page-size="page = 1; load()">
|
||
<template #actions="{ row }"><el-button size="small" plain type="primary" @click="openDetail(row as DispatchListItem)"><Eye :size="15" />查看</el-button><el-button v-if="canTransfer(row as DispatchListItem)" size="small" type="success" plain @click="openTransfer(row as DispatchListItem)"><Truck :size="15" />转派送</el-button><el-dropdown v-if="canEdit(row as DispatchListItem) || canCancel(row as DispatchListItem)" trigger="click" @command="handleMore(String($event), row as DispatchListItem)"><el-button size="small" plain><MoreHorizontal :size="15" />更多</el-button><template #dropdown><el-dropdown-menu><el-dropdown-item v-if="canEdit(row as DispatchListItem)" command="edit"><Pencil :size="15" />编辑</el-dropdown-item><el-dropdown-item v-if="canCancel(row as DispatchListItem)" command="cancel" divided><XCircle :size="15" />取消</el-dropdown-item></el-dropdown-menu></template></el-dropdown></template>
|
||
<template #status="{ row }"><StatusBadge :status="String((row as DispatchListItem).status || '—')" /></template>
|
||
<template #pagination-left>共 {{ total }} 条排车记录</template>
|
||
</DataTable></section>
|
||
</ListPageLayout></AdminPage><AppDialog v-model="transferOpen" title="转派送" width="min(1120px, calc(100vw - 28px))" confirm-text="确认提交" :confirm-loading="transferSaving" @confirm="submitTransfer"><el-form label-position="top"><h3>派送信息</h3><div class="transfer-grid"><el-form-item label="排车单号"><el-input :model-value="transferRow?.dispatchNo" readonly /></el-form-item><el-form-item label="预计派送时间" required><el-date-picker v-model="transferForm.plannedDeliveryAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择预计派送时间" /></el-form-item><el-form-item label="派送方式" required><el-select v-model="transferForm.deliveryMethod" placeholder="请选择"><el-option v-for="option in deliveryMethodOptions" :key="option.value" v-bind="option" /></el-select></el-form-item><el-form-item label="快递/车队"><el-input v-model="transferForm.carrierName" /></el-form-item><el-form-item label="运单号"><el-input v-model="transferForm.waybillNo" /></el-form-item><el-form-item label="提货方式" required><el-select v-model="transferForm.pickupMethod" placeholder="请选择"><el-option v-for="option in pickupMethodOptions" :key="option.value" v-bind="option" /></el-select></el-form-item><el-form-item label="装车类型" required><el-select v-model="transferForm.loadingType" placeholder="请选择"><el-option v-for="option in loadingTypeOptions" :key="option.value" v-bind="option" /></el-select></el-form-item><el-form-item label="司机/车队名称"><el-input v-model="transferForm.driverName" /></el-form-item><el-form-item label="联系电话"><el-input v-model="transferForm.driverPhone" /></el-form-item><el-form-item label="车牌号"><el-input v-model="transferForm.vehiclePlate" /></el-form-item><el-form-item label="车辆类型"><el-select v-model="transferForm.vehicleType" clearable placeholder="请选择"><el-option v-for="option in vehicleTypeOptions" :key="option.value" v-bind="option" /></el-select></el-form-item><el-form-item label="车辆大小"><el-select v-model="transferForm.vehicleSize" clearable placeholder="请选择"><el-option v-for="option in vehicleSizeOptions" :key="option.value" v-bind="option" /></el-select></el-form-item><el-form-item label="预约账号"><el-select v-model="transferForm.appointmentAccount" clearable placeholder="请选择"><el-option v-for="option in appointmentAccountOptions" :key="option.value" v-bind="option" /></el-select></el-form-item><el-form-item v-if="isBuyAppointment" label="卖家姓名" required><el-input v-model="transferForm.sellerName" placeholder="请输入卖家姓名" /></el-form-item></div><h3>费用信息(USD)</h3><div class="transfer-grid"><el-form-item label="派送费用" required><el-input-number v-model="transferForm.deliveryFee" :min="0" :precision="2" controls-position="right" placeholder="请输入派送费用" /></el-form-item><el-form-item v-if="needsLoadingFee" label="装车费" required><el-input-number v-model="transferForm.loadingFee" :min="0" :precision="2" controls-position="right" placeholder="请输入装车费" /></el-form-item><el-form-item v-if="isBuyAppointment" label="买约费用" required><el-input-number v-model="transferForm.appointmentFee" :min="0" :precision="2" controls-position="right" placeholder="请输入买约费用" /></el-form-item></div><el-form-item label="备注"><el-input v-model="transferForm.remark" type="textarea" :rows="3" /></el-form-item><el-alert title="费用不会自动带出,请手工填写后提交;提交后将生成待出库单、待派送单,并保存费用明细" type="info" :closable="false" /></el-form></AppDialog>
|
||
<AppDialog v-model="editOpen" title="编辑排车" width="min(900px, calc(100vw - 48px))" confirm-text="保存" :confirm-loading="editSaving" @confirm="submitEdit"><el-alert :title="`当前状态:${editRow?.status || '—'};普通编辑不会修改排车状态。`" type="info" :closable="false" /><el-form label-position="top"><div class="edit-grid"><el-form-item label="车牌号"><el-input v-model="editForm.vehiclePlate" /></el-form-item><el-form-item label="司机"><el-input v-model="editForm.driverName" /></el-form-item><el-form-item label="联系电话"><el-input v-model="editForm.driverPhone" /></el-form-item><el-form-item label="拖车公司名称"><el-input v-model="editForm.carrierName" /></el-form-item><el-form-item label="路线"><el-input v-model="editForm.route" /></el-form-item><el-form-item label="发车时间"><el-date-picker v-model="editForm.departAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" /></el-form-item><el-form-item label="预计到达时间"><el-date-picker v-model="editForm.estimatedArrivalAt" type="datetime" value-format="YYYY-MM-DDTHH:mm:ss" /></el-form-item><el-form-item label="FBA预约号"><el-input v-model="editForm.fbaAppointmentNo" /></el-form-item><el-form-item label="FBA预约状态"><el-select v-model="editForm.fbaAppointmentStatus" clearable><el-option label="待预约" value="PENDING" /><el-option label="已预约" value="BOOKED" /><el-option label="已确认" value="CONFIRMED" /><el-option label="已取消" value="CANCELLED" /></el-select></el-form-item><el-form-item label="车辆预约状态"><el-select v-model="editForm.vehicleAppointmentStatus" clearable><el-option label="待预约" value="PENDING" /><el-option label="已预约" value="BOOKED" /><el-option label="已确认" value="CONFIRMED" /><el-option label="已取消" value="CANCELLED" /></el-select></el-form-item></div></el-form></AppDialog>
|
||
</template>
|
||
<style scoped>.transfer-grid,.edit-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0 16px}.transfer-grid :deep(.el-select),.transfer-grid :deep(.el-date-editor),.transfer-grid :deep(.el-input-number),.edit-grid :deep(.el-select),.edit-grid :deep(.el-date-editor){width:100%}h3{margin:4px 0 12px}@media(max-width:900px){.transfer-grid,.edit-grid{grid-template-columns:1fr}}</style>
|