feat(transport): unify client truck and looseboard demand forms

This commit is contained in:
2026-10-19 15:50:39 +08:00
parent 34650b06df
commit 17580fb0a2
6 changed files with 285 additions and 167 deletions
@@ -1,8 +1,9 @@
<script setup lang="ts">
import { computed, onMounted, reactive, ref, watch } from 'vue'
import { Package } from '@lucide/vue'
import { useRoute, useRouter } from 'vue-router'
import { ElMessage, type FormInstance, type FormRules } from 'element-plus'
import { ElMessage, type FormInstance } from 'element-plus'
import TransportDemandForm from './TransportDemandForm.vue'
import { createTransportDemandForm, transportDemandError, buildTransportDemand, type TransportDemandForm as DemandFormModel } from '../transportDemandForm'
import DataTable, { type DataTableColumn } from '@/components/common/DataTable.vue'
import QueryToolbar, { type QueryField, type QueryModel } from '@/components/common/QueryToolbar.vue'
import type { TableAction } from '@/components/common/tableActions'
@@ -33,27 +34,7 @@ type PageMode = 'list' | 'create' | 'detail'
interface ColumnDefinition { key: string; label: string; width?: number }
interface BusinessForm {
productName: string
estimatedPalletCount: number
origin: string
originAddress: string
destination: string
destinationAddress: string
packageCount: number
totalWeightKg: number
totalVolumeCbm: number
budgetAmount?: number
currency: string
vehicleType: '' | 'PALLET_15' | 'PALLET_30'
originContactName: string
originContactPhone: string
destinationContactName: string
destinationContactPhone: string
pickupAt: string
deliveryAt: string
remark: string
}
type BusinessForm = DemandFormModel
const configuration = computed(() => ({
trucking: {
@@ -133,19 +114,6 @@ const queryFields = computed<QueryField[]>(() => [
])
const form = reactive<BusinessForm>(emptyForm())
const rules = computed<FormRules<BusinessForm>>(() => ({
origin: [{ required: true, message: '请输入起运地', trigger: 'blur' }],
destination: [{ required: true, message: '请输入目的地', trigger: 'blur' }],
...(props.kind === 'trucking' ? { vehicleType: [{ required: true, message: '请选择车辆类型', trigger: 'change' }] } : {}),
originContactName: [{ required: true, message: '请输入起运地联系人', trigger: 'blur' }],
originContactPhone: [{ required: true, message: '请输入起运地联系电话', trigger: 'blur' }],
destinationContactName: [{ required: true, message: '请输入目的地联系人', trigger: 'blur' }],
destinationContactPhone: [{ required: true, message: '请输入目的地联系电话', trigger: 'blur' }],
pickupAt: [{ required: true, message: '请选择取货时间', trigger: 'change' }],
deliveryAt: [{ required: true, message: '请选择送货时间', trigger: 'change' }, {
validator: (_rule: unknown, value: string, callback: (error?: Error) => void) => callback(value && form.pickupAt && value < form.pickupAt ? new Error('送货时间不能早于取货时间') : undefined), trigger: 'change',
}],
}))
const detailFields = computed(() => {
if (!detail.value) return []
@@ -177,14 +145,7 @@ const tableColumns = computed<DataTableColumn[]>(() => [
const detailStatus = computed(() => detail.value ? statusLabel(detail.value.summary.status) : '—')
function emptyForm(): BusinessForm {
return {
productName: '', estimatedPalletCount: 0, origin: '', originAddress: '',
destination: '', destinationAddress: '', packageCount: 0,
totalWeightKg: 0, totalVolumeCbm: 0, currency: 'USD',
remark: '', vehicleType: '', originContactName: '', originContactPhone: '', destinationContactName: '', destinationContactPhone: '', pickupAt: '', deliveryAt: '',
}
}
function emptyForm(): BusinessForm { return createTransportDemandForm() }
function resetForm() { Object.assign(form, emptyForm()); submitKey.value = ''; formRef.value?.clearValidate() }
function read(source: object, key: string) { return Reflect.get(source, key) as unknown }
@@ -287,35 +248,15 @@ async function loadDetail() {
}
async function submit() {
if (props.kind === 'looseboard' && form.pickupAt && form.deliveryAt && form.deliveryAt < form.pickupAt) { ElMessage.error('送达时间不能早于取货时间'); return }
const failure = transportDemandError(props.kind === 'trucking' ? 'truck' : 'looseboard', form)
if (failure) { ElMessage.error(failure); return }
if (!await formRef.value?.validate().catch(() => false)) return
submitting.value = true
const stableSubmitKey = submitKey.value || idempotencyKey()
submitKey.value = stableSubmitKey
try {
const base = {
origin: form.origin, originAddress: form.originAddress || undefined,
destination: form.destination, destinationAddress: form.destinationAddress || undefined,
packageCount: form.packageCount,
totalWeightKg: form.totalWeightKg, totalVolumeCbm: form.totalVolumeCbm,
budgetAmount: form.budgetAmount, currency: form.currency || undefined,
remark: form.remark || undefined,
idempotencyKey: stableSubmitKey,
}
if (props.kind === 'trucking') {
if (!form.vehicleType) return
await createTransport('trucking', { ...base, vehicleType: form.vehicleType,
originContactName: form.originContactName, originContactPhone: form.originContactPhone,
destinationContactName: form.destinationContactName, destinationContactPhone: form.destinationContactPhone,
pickupAt: form.pickupAt, deliveryAt: form.deliveryAt,
})
} else {
await createTransport('looseboard', { ...base, productName: form.productName || undefined, estimatedPalletCount: form.estimatedPalletCount,
originContactName: form.originContactName, originContactPhone: form.originContactPhone,
destinationContactName: form.destinationContactName, destinationContactPhone: form.destinationContactPhone,
pickupAt: form.pickupAt, deliveryAt: form.deliveryAt,
})
}
if (props.kind === 'trucking') await createTransport('trucking', { ...buildTransportDemand('truck', form), idempotencyKey: stableSubmitKey })
else await createTransport('looseboard', { ...buildTransportDemand('looseboard', form), idempotencyKey: stableSubmitKey })
ElMessage.success('订单提交成功')
resetForm()
if (props.createPage) await router.push({ name: listRouteName.value })
@@ -383,64 +324,8 @@ onMounted(() => { if (props.detailPage) void loadDetail(); else if (!props.creat
</section>
</section>
<el-card v-else-if="kind === 'looseboard'" shadow="never" class="form-card looseboard-form-card">
<el-form ref="formRef" :model="form" :rules="rules" label-position="top">
<div class="form-grid two-col lower-grid">
<el-form-item label="起运地" prop="origin"><el-input v-model.trim="form.origin" maxlength="255" placeholder="城市 / 港口 / 州" /></el-form-item>
<el-form-item label="目的地" prop="destination"><el-input v-model.trim="form.destination" maxlength="255" placeholder="城市 / 州" /></el-form-item>
<el-form-item label="起运地联系人" prop="originContactName"><el-input v-model.trim="form.originContactName" maxlength="128" placeholder="请输入起运地联系人" /></el-form-item>
<el-form-item label="目的地联系人" prop="destinationContactName"><el-input v-model.trim="form.destinationContactName" maxlength="128" placeholder="请输入目的地联系人" /></el-form-item>
<el-form-item label="起运地联系电话" prop="originContactPhone"><el-input v-model.trim="form.originContactPhone" type="tel" maxlength="128" placeholder="请输入起运地联系电话" /></el-form-item>
<el-form-item label="目的地联系电话" prop="destinationContactPhone"><el-input v-model.trim="form.destinationContactPhone" type="tel" maxlength="128" placeholder="请输入目的地联系电话" /></el-form-item>
</div>
<div class="form-section cargo-section"><h3><span class="section-icon"><Package :size="14" /></span>托载</h3><div class="form-grid cargo-grid">
<el-form-item label="商品名称"><el-input v-model.trim="form.productName" placeholder="请输入商品名称" /></el-form-item>
<el-form-item label="预计托数"><el-input-number v-model="form.estimatedPalletCount" :min="0" controls-position="right" placeholder="请输入预计托数" /></el-form-item>
<el-form-item label="总重量 (kg)"><el-input-number v-model="form.totalWeightKg" :min="0" :precision="2" controls-position="right" placeholder="请输入总重量" /></el-form-item>
<el-form-item label="总体积 (m³)"><el-input-number v-model="form.totalVolumeCbm" :min="0" :precision="3" controls-position="right" placeholder="请输入总体积" /></el-form-item>
</div></div>
<div class="form-grid two-col lower-grid">
<el-form-item label="预算总价"><el-input-number v-model="form.budgetAmount" :min="0" :precision="2" controls-position="right" placeholder="0.00" /></el-form-item>
<el-form-item label="预算币种"><el-select v-model="form.currency"><el-option label="USD" value="USD" /><el-option label="CNY" value="CNY" /></el-select></el-form-item>
<el-form-item label="取货时间" prop="pickupAt"><el-date-picker v-model="form.pickupAt" type="datetime" format="YYYY-MM-DD HH:mm" time-format="HH:mm" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择取货时间" @change="formRef?.validateField('deliveryAt').catch(() => undefined)" /></el-form-item>
<el-form-item label="送货时间" prop="deliveryAt"><el-date-picker v-model="form.deliveryAt" type="datetime" format="YYYY-MM-DD HH:mm" time-format="HH:mm" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择送货时间" /></el-form-item>
</div>
<el-collapse class="looseboard-extra"><el-collapse-item title="补充信息(选填)" name="extra"><div class="form-grid two-col">
<el-form-item label="件数"><el-input-number v-model="form.packageCount" :min="0" :precision="0" controls-position="right" placeholder="请输入件数" /></el-form-item>
<el-form-item label="备注"><el-input v-model="form.remark" type="textarea" maxlength="1000" :rows="2" placeholder="请输入备注(选填)" /></el-form-item>
<el-form-item label="起运地址补充"><el-input v-model.trim="form.originAddress" maxlength="500" placeholder="请输入详细起运地址(选填)" /></el-form-item>
<el-form-item label="目的地址补充"><el-input v-model.trim="form.destinationAddress" maxlength="500" placeholder="请输入详细目的地址(选填)" /></el-form-item>
</div></el-collapse-item></el-collapse>
<div class="form-actions"><el-button @click="returnToList">取消</el-button><el-button v-if="canCreate" type="primary" :loading="submitting" @click="submit">提交订单</el-button></div>
</el-form>
</el-card>
<el-card v-else shadow="never" class="form-card">
<el-form ref="formRef" :model="form" :rules="rules" label-width="140px">
<el-form-item v-if="kind === 'trucking'" label="车辆类型" prop="vehicleType"><el-select v-model="form.vehicleType" placeholder="请选择车辆类型"><el-option label="15拖" value="PALLET_15" /><el-option label="30拖" value="PALLET_30" /></el-select></el-form-item>
<el-form-item label="起运地" prop="origin"><el-input v-model.trim="form.origin" maxlength="255" placeholder="请输入起运地" /></el-form-item>
<el-form-item label="起运地址"><el-input v-model.trim="form.originAddress" maxlength="500" placeholder="请输入详细起运地址" /></el-form-item>
<template v-if="kind === 'trucking'">
<el-form-item label="起运地联系人" prop="originContactName"><el-input v-model.trim="form.originContactName" maxlength="128" placeholder="请输入起运地联系人" /></el-form-item>
<el-form-item label="起运地联系电话" prop="originContactPhone"><el-input v-model.trim="form.originContactPhone" type="tel" maxlength="128" placeholder="请输入起运地联系电话" /></el-form-item>
</template>
<el-form-item label="目的地" prop="destination"><el-input v-model.trim="form.destination" maxlength="255" placeholder="请输入目的地" /></el-form-item>
<el-form-item label="目的地址"><el-input v-model.trim="form.destinationAddress" maxlength="500" placeholder="请输入详细目的地址" /></el-form-item>
<template v-if="kind === 'trucking'">
<el-form-item label="目的地联系人" prop="destinationContactName"><el-input v-model.trim="form.destinationContactName" maxlength="128" placeholder="请输入目的地联系人" /></el-form-item>
<el-form-item label="目的地联系电话" prop="destinationContactPhone"><el-input v-model.trim="form.destinationContactPhone" type="tel" maxlength="128" placeholder="请输入目的地联系电话" /></el-form-item>
</template>
<el-form-item label="件数"><el-input-number v-model="form.packageCount" :min="0" placeholder="请输入件数" /></el-form-item>
<el-form-item label="总重量(kg)"><el-input-number v-model="form.totalWeightKg" :min="0" :precision="2" placeholder="请输入总重量" /></el-form-item>
<el-form-item label="总体积(m³)"><el-input-number v-model="form.totalVolumeCbm" :min="0" :precision="3" placeholder="请输入总体积" /></el-form-item>
<template v-if="kind === 'trucking'">
<el-form-item label="取货时间" prop="pickupAt"><el-date-picker v-model="form.pickupAt" type="datetime" format="YYYY-MM-DD HH:mm" time-format="HH:mm" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择取货日期和时间" @change="formRef?.validateField('deliveryAt').catch(() => undefined)" /></el-form-item>
<el-form-item label="送货时间" prop="deliveryAt"><el-date-picker v-model="form.deliveryAt" type="datetime" format="YYYY-MM-DD HH:mm" time-format="HH:mm" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择送货日期和时间" /></el-form-item>
</template>
<el-form-item class="budget-form-item" label="预算金额"><el-input-number v-model="form.budgetAmount" :min="0" :precision="2" placeholder="请输入预算金额" /><el-select v-model="form.currency" class="currency"><el-option label="USD" value="USD" /><el-option label="CNY" value="CNY" /></el-select></el-form-item>
<el-form-item label="备注"><el-input v-model="form.remark" type="textarea" :rows="4" maxlength="1000" show-word-limit placeholder="请输入其他要求" /></el-form-item>
<el-form-item><el-button @click="returnToList">取消</el-button><el-button v-if="canCreate" type="primary" :loading="submitting" @click="submit">提交订单</el-button></el-form-item>
</el-form>
<TransportDemandForm ref="formRef" :model="form" :kind="kind === 'trucking' ? 'truck' : 'looseboard'" :submitting="submitting" :can-submit="canCreate" @submit="submit" @cancel="returnToList" />
</el-card>
</section>
</template>
@@ -472,44 +357,7 @@ onMounted(() => { if (props.detailPage) void loadDetail(); else if (!props.creat
.arrangement-grid div { grid-template-columns: 124px minmax(0, 1fr); }
.arrangement-grid dt { white-space: normal; overflow-wrap: anywhere; }
.detail-grid dd { min-width: 0; margin: 0; overflow-wrap: anywhere; color: var(--navy); font-size: 12px; font-weight: 600; line-height: 1.4; }
.form-card :deep(.el-form) { max-width: 860px; }
.form-card :deep(.el-input), .form-card :deep(.el-textarea), .form-card :deep(.el-select), .form-card :deep(.el-date-editor) { width: 100%; }
.form-card .currency { width: 110px; margin-left: 12px; }
.warehouse-load-error { color: #b42318; font-size: 12px; }
.looseboard-form-card { padding: 0; border: 1px solid #e7ebf1; border-radius: 10px; background: #fff; box-shadow: 0 8px 22px rgb(31 45 61 / 4%); }
.looseboard-form-card :deep(.el-card__body) { padding: 0; }
.looseboard-form-card :deep(.el-form) { max-width: none; }
.looseboard-form-card :deep(.el-form-item) { margin-bottom: 16px; }
.looseboard-form-card :deep(.el-form-item__label) { height: auto; padding-bottom: 7px; color: #27364d; font-size: 13px; font-weight: 600; line-height: 1.2; }
.looseboard-form-card :deep(.el-input__wrapper), .looseboard-form-card :deep(.el-textarea__inner), .looseboard-form-card :deep(.el-select__wrapper), .looseboard-form-card :deep(.el-input-number) { min-height: 36px; border-radius: 8px; box-shadow: 0 0 0 1px #e1e6ed inset; }
.looseboard-form-card :deep(.el-input-number) { width: 100%; }
.form-section { padding: 25px 30px 5px; }
.form-section h3 { display: flex; align-items: center; gap: 7px; margin: 0 0 15px; color: #27364d; font-size: 14px; }
.form-section h3 em { color: #e05252; font-style: normal; }
.section-icon { display: inline-flex; width: 20px; height: 20px; align-items: center; justify-content: center; border-radius: 5px; background: #fff3e0; color: #dd8d2c; }
.mode-buttons :deep(.el-radio-group) { display: flex; gap: 10px; }
.mode-buttons :deep(.el-radio-button__inner) { display: inline-flex; align-items: center; gap: 6px; height: 36px; padding: 0 18px; border: 1px solid #dce3eb; border-radius: 8px !important; color: #4b5563; box-shadow: none; }
.mode-buttons :deep(.el-radio-button:first-child .el-radio-button__inner) { border-left: 1px solid #dce3eb; }
.mode-buttons :deep(.el-radio-button__original-radio:checked + .el-radio-button__inner) { border-color: #5965d8; background: #f0f2ff; color: #4f5bd5; box-shadow: none; }
.form-grid { display: grid; gap: 0 22px; padding: 0 30px; }
.two-col { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); max-width: 1180px; }
.two-col > .el-form-item { min-width: 0; }
.cargo-section { margin-top: 4px; padding-top: 7px; background: #fafafa; border-top: 1px solid #f0f2f5; border-bottom: 1px solid #f0f2f5; }
.cargo-grid { grid-template-columns: minmax(0, 1.35fr) repeat(3, minmax(150px, .9fr)); max-width: 1180px; }
.lower-grid { padding-top: 20px; }
.lower-grid :deep(.el-date-editor) { width: 100%; }
.form-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 5px; padding: 20px 30px 25px; border-top: 1px solid #f0f2f5; }
.looseboard-extra { margin: 0 30px; }
.looseboard-form-card .form-actions { padding-right: max(30px, calc(100% - 1150px)); }
.form-actions .el-button { min-width: 80px; height: 36px; border-radius: 7px; }
.form-actions .el-button--primary { background: linear-gradient(135deg, #6774df, #7553aa); border: 0; box-shadow: 0 5px 12px rgb(103 116 223 / 24%); }
@media (min-width: 1400px) {
.looseboard-form-card :deep(.el-input), .looseboard-form-card :deep(.el-select), .looseboard-form-card :deep(.el-date-editor) { max-width: 560px; }
.cargo-grid :deep(.el-input), .cargo-grid :deep(.el-input-number) { max-width: 300px; }
.lower-grid :deep(.el-input-number), .lower-grid :deep(.el-date-editor) { max-width: 560px; }
}
.form-card :deep(.el-card__body) { padding: 0; }
@media (max-width: 767px) {
.business-orders { gap: 14px; }
@@ -0,0 +1,87 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import type { FormInstance } from 'element-plus'
import { transportDemandRules, type TransportDemandForm, type TransportDemandKind } from '../transportDemandForm'
const props = withDefaults(defineProps<{
model: TransportDemandForm
kind: TransportDemandKind
disabled?: boolean
submitting?: boolean
canSubmit?: boolean
editing?: boolean
}>(), { disabled: false, submitting: false, canSubmit: true, editing: false })
const emit = defineEmits<{ submit: []; cancel: [] }>()
const formRef = ref<FormInstance>()
const rules = computed(() => transportDemandRules(props.kind, props.model))
defineExpose({
validate: () => formRef.value?.validate() ?? Promise.resolve(false),
clearValidate: (fields?: string | string[]) => formRef.value?.clearValidate(fields),
})
</script>
<template>
<el-form ref="formRef" class="transport-demand-form" :model="model" :rules="rules" label-position="top" :disabled="disabled || submitting">
<slot name="customer" />
<section class="transport-demand-section">
<h3>运输信息</h3>
<div class="transport-demand-grid">
<el-form-item v-if="kind === 'truck'" class="transport-demand-wide" label="车辆类型" prop="vehicleType">
<el-select v-model="model.vehicleType" placeholder="请选择车辆类型"><el-option label="15拖" value="PALLET_15" /><el-option label="30拖" value="PALLET_30" /></el-select>
</el-form-item>
<el-form-item label="起运地" prop="origin"><el-input v-model.trim="model.origin" maxlength="255" placeholder="城市 / 港口 / 州" /></el-form-item>
<el-form-item label="目的地" prop="destination"><el-input v-model.trim="model.destination" maxlength="255" placeholder="城市 / 州" /></el-form-item>
<el-form-item label="起运地联系人" prop="originContactName"><el-input v-model.trim="model.originContactName" maxlength="128" placeholder="请输入起运地联系人" /></el-form-item>
<el-form-item label="目的地联系人" prop="destinationContactName"><el-input v-model.trim="model.destinationContactName" maxlength="128" placeholder="请输入目的地联系人" /></el-form-item>
<el-form-item label="起运地联系电话" prop="originContactPhone"><el-input v-model.trim="model.originContactPhone" type="tel" maxlength="128" placeholder="请输入起运地联系电话" /></el-form-item>
<el-form-item label="目的地联系电话" prop="destinationContactPhone"><el-input v-model.trim="model.destinationContactPhone" type="tel" maxlength="128" placeholder="请输入目的地联系电话" /></el-form-item>
<el-form-item label="起运详细地址"><el-input v-model.trim="model.originAddress" maxlength="500" placeholder="街道 / 门牌等补充信息(选填)" /></el-form-item>
<el-form-item label="目的详细地址"><el-input v-model.trim="model.destinationAddress" maxlength="500" placeholder="街道 / 门牌等补充信息(选填)" /></el-form-item>
</div>
</section>
<section class="transport-demand-section">
<h3>货物信息</h3>
<div class="transport-demand-grid" :class="kind === 'looseboard' ? 'transport-demand-cargo-four' : 'transport-demand-cargo-three'">
<el-form-item v-if="kind === 'looseboard'" label="商品名称"><el-input v-model.trim="model.productName" maxlength="255" placeholder="请输入商品名称(选填)" /></el-form-item>
<el-form-item v-if="kind === 'looseboard'" label="预计托数" prop="estimatedPalletCount"><el-input-number v-model="model.estimatedPalletCount" :min="0" :precision="0" controls-position="right" placeholder="请输入预计托数" /></el-form-item>
<el-form-item v-if="kind === 'truck'" label="件数" prop="packageCount"><el-input-number v-model="model.packageCount" :min="0" :precision="0" controls-position="right" placeholder="请输入件数" /></el-form-item>
<el-form-item label="总重量 (kg)" prop="totalWeightKg"><el-input-number v-model="model.totalWeightKg" :min="0" :precision="3" controls-position="right" placeholder="请输入总重量" /></el-form-item>
<el-form-item label="总体积 (m³)" prop="totalVolumeCbm"><el-input-number v-model="model.totalVolumeCbm" :min="0" :precision="6" controls-position="right" placeholder="请输入总体积" /></el-form-item>
<el-form-item v-if="kind === 'looseboard'" label="件数" prop="packageCount"><el-input-number v-model="model.packageCount" :min="0" :precision="0" controls-position="right" placeholder="请输入件数" /></el-form-item>
</div>
</section>
<section class="transport-demand-section">
<h3>费用与时效</h3>
<div class="transport-demand-grid">
<el-form-item label="预算总价" prop="budgetAmount"><div class="transport-demand-budget">
<el-input-number v-model="model.budgetAmount" :min="0" :precision="2" controls-position="right" placeholder="请输入预算总价(选填)" />
<el-select v-model="model.currency" aria-label="预算币种" placeholder="请选择预算币种"><el-option label="USD" value="USD" /><el-option label="CNY" value="CNY" /></el-select>
</div></el-form-item>
<div />
<el-form-item label="取货时间" prop="pickupAt"><el-date-picker v-model="model.pickupAt" type="datetime" format="YYYY-MM-DD HH:mm" time-format="HH:mm" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择取货日期和时间" @change="formRef?.validateField('deliveryAt').catch(() => undefined)" /></el-form-item>
<el-form-item label="送货时间" prop="deliveryAt"><el-date-picker v-model="model.deliveryAt" type="datetime" format="YYYY-MM-DD HH:mm" time-format="HH:mm" value-format="YYYY-MM-DDTHH:mm:ss" placeholder="请选择送货日期和时间" /></el-form-item>
<el-form-item class="transport-demand-wide" label="备注"><el-input v-model="model.remark" type="textarea" :rows="3" maxlength="1000" show-word-limit placeholder="请输入其他要求(选填)" /></el-form-item>
</div>
</section>
<footer class="transport-demand-actions">
<el-button :disabled="submitting" @click="emit('cancel')">取消</el-button>
<el-button v-if="canSubmit" type="primary" :loading="submitting" :disabled="disabled" @click="emit('submit')">{{ editing ? '保存修改' : '提交订单' }}</el-button>
</footer>
</el-form>
</template>
<style scoped>
.transport-demand-form { --el-component-size: 36px; width: 100%; max-width: 1180px; margin: 0; background: #fff; }
.transport-demand-section { padding: 20px 24px 0; border-bottom: 1px solid #e7ebf1; }
.transport-demand-section h3 { margin: 0 0 16px; color: #27364d; font-size: 14px; font-weight: 600; }
.transport-demand-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 24px; }
.transport-demand-cargo-three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.transport-demand-cargo-four { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.transport-demand-wide { grid-column: 1 / -1; }
.transport-demand-form :deep(.el-form-item) { min-width: 0; margin-bottom: 18px; }
.transport-demand-form :deep(.el-form-item__label) { height: auto; padding-bottom: 6px; font-size: 13px; line-height: 20px; }
.transport-demand-form :deep(.el-input), .transport-demand-form :deep(.el-input-number), .transport-demand-form :deep(.el-select), .transport-demand-form :deep(.el-date-editor) { width: 100%; }
.transport-demand-budget { display: grid; width: 100%; grid-template-columns: minmax(0, 1fr) 100px; gap: 10px; }
.transport-demand-actions { display: flex; justify-content: flex-end; gap: 10px; padding: 18px 24px; }
.transport-demand-actions :deep(.el-button + .el-button) { margin-left: 0; }
</style>