feat(pl): optional escort selection on transport arrange

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
wuzilongandCursor committed 2026-09-13 10:48:23 +08:00
1 parent 3dc71788d4
commit ca3062b0e2
10 files changed
+339 -10

No files matched your search

+24
View File
@@ -439,6 +439,7 @@ def _apply_transport_arrange_fields(
payload: TransportArrange,
vehicle_info: dict,
driver_info: Optional[dict],
escort_info: Optional[dict],
carrier_name: Optional[str],
start_point: Optional[str],
end_point: Optional[str],
@@ -450,6 +451,9 @@ def _apply_transport_arrange_fields(
transport.driver_id = driver_info["id"] if driver_info else None
transport.driver_name = driver_info["name"] if driver_info else None
transport.driver_phone = driver_info["phone"] if driver_info else None
transport.escort_id = escort_info["id"] if escort_info else None
transport.escort_name = escort_info["name"] if escort_info else None
transport.escort_phone = escort_info["phone"] if escort_info else None
transport.carrier_id = payload.carrier_id or vehicle_info.get("carrier_id")
transport.carrier_name = carrier_name
transport.transport_medium = payload.transport_medium
@@ -653,6 +657,9 @@ def create_transport(
driver_id=payload.driver_id,
driver_name=payload.driver_name,
driver_phone=payload.driver_phone,
escort_id=payload.escort_id,
escort_name=payload.escort_name,
escort_phone=payload.escort_phone,
carrier_id=payload.carrier_id,
carrier_name=payload.carrier_name,
transport_medium=payload.transport_medium,
@@ -696,12 +703,16 @@ def create_transport(
plan.license_plate = transport.license_plate
plan.driver_id = transport.driver_id
plan.driver_name = transport.driver_name
plan.escort_id = transport.escort_id
plan.escort_name = transport.escort_name
plan.escort_phone = transport.escort_phone
if transport.vehicle_id:
snapshot = build_transport_resource_snapshot(
db,
tenant_no,
transport.vehicle_id,
selected_driver_id=transport.driver_id,
selected_escort_id=transport.escort_id,
)
apply_resource_snapshot_json(
plan=plan, transport=transport, snapshot=snapshot
@@ -766,6 +777,13 @@ def arrange_transport(
if not driver_info:
raise HTTPException(status_code=400, detail="司机不存在")
# 获取押运员信息(可选)
escort_info = None
if payload.escort_id:
escort_info = _resolve_driver_info(db, tenant_no, payload.escort_id)
if not escort_info:
raise HTTPException(status_code=400, detail="押运员不存在")
# 检查产品属性(易制毒/易制爆需要证件,以 CRM 产品主数据为准)
products = db.query(PlPlanProduct).filter(
PlPlanProduct.plan_id == plan_id,
@@ -811,6 +829,7 @@ def arrange_transport(
payload=payload,
vehicle_info=vehicle_info,
driver_info=driver_info,
escort_info=escort_info,
carrier_name=carrier_name,
start_point=start_point,
end_point=end_point,
@@ -835,6 +854,7 @@ def arrange_transport(
payload=payload,
vehicle_info=vehicle_info,
driver_info=driver_info,
escort_info=escort_info,
carrier_name=carrier_name,
start_point=start_point,
end_point=end_point,
@@ -858,12 +878,16 @@ def arrange_transport(
plan.license_plate = transport.license_plate
plan.driver_id = transport.driver_id
plan.driver_name = transport.driver_name
plan.escort_id = transport.escort_id
plan.escort_name = transport.escort_name
plan.escort_phone = transport.escort_phone
if transport.vehicle_id:
snapshot = build_transport_resource_snapshot(
db,
tenant_no,
transport.vehicle_id,
selected_driver_id=transport.driver_id,
selected_escort_id=transport.escort_id,
)
apply_resource_snapshot_json(plan=plan, transport=transport, snapshot=snapshot)
plan.transport_status = "arranged"
+4
View File
@@ -287,6 +287,7 @@ class PlanCreate(PlanBase):
class PlanTransportCreateBlock(BaseModel):
vehicle_id: str = Field(..., alias="vehicleId", description="车辆ID")
driver_id: Optional[str] = Field(default=None, alias="driverId", description="司机/人员ID")
escort_id: Optional[str] = Field(default=None, alias="escortId", description="押运员人员ID(可选)")
carrier_id: Optional[str] = Field(default=None, alias="carrierId", description="承运商ID")
distance_km: Optional[Decimal] = Field(default=None, alias="distanceKm", description="运输公里数")
freight_type: str = Field(default="by_weight", alias="freightType", description="运费计算方式")
@@ -384,6 +385,9 @@ class PlanRead(PlanBase, PlanResourceSnapshotMixin):
license_plate: Optional[str] = Field(default=None, alias="licensePlate")
driver_id: Optional[str] = Field(default=None, alias="driverId")
driver_name: Optional[str] = Field(default=None, alias="driverName")
escort_id: Optional[str] = Field(default=None, alias="escortId")
escort_name: Optional[str] = Field(default=None, alias="escortName")
escort_phone: Optional[str] = Field(default=None, alias="escortPhone")
personnel_summary: Optional[str] = Field(default=None, alias="personnelSummary")
transport_id: Optional[str] = Field(default=None, alias="transportId")
transport_no: Optional[str] = Field(default=None, alias="transportNo")
+7
View File
@@ -21,6 +21,9 @@ class TransportBase(BaseModel):
driver_id: Optional[str] = Field(default=None, alias="driverId", description="司机ID")
driver_name: Optional[str] = Field(default=None, max_length=100, alias="driverName", description="司机姓名")
driver_phone: Optional[str] = Field(default=None, max_length=32, alias="driverPhone", description="司机电话")
escort_id: Optional[str] = Field(default=None, alias="escortId", description="押运员人员ID")
escort_name: Optional[str] = Field(default=None, max_length=100, alias="escortName", description="押运员姓名")
escort_phone: Optional[str] = Field(default=None, max_length=32, alias="escortPhone", description="押运员电话")
carrier_id: Optional[str] = Field(default=None, alias="carrierId", description="承运商ID")
carrier_name: Optional[str] = Field(default=None, max_length=200, alias="carrierName", description="承运商名称")
transport_medium: Optional[str] = Field(default=None, max_length=100, alias="transportMedium", description="运输介质")
@@ -54,6 +57,7 @@ class TransportStopDibangOption(BaseModel):
class TransportArrange(BaseModel):
vehicle_id: str = Field(..., alias="vehicleId", description="车辆ID")
driver_id: Optional[str] = Field(default=None, alias="driverId", description="司机ID(可选)")
escort_id: Optional[str] = Field(default=None, alias="escortId", description="押运员人员ID(可选)")
carrier_id: Optional[str] = Field(default=None, alias="carrierId", description="承运商ID")
transport_medium: Optional[str] = Field(default=None, alias="transportMedium", description="运输介质")
pickup_address: Optional[str] = Field(default=None, alias="pickupAddress", description="取货地址")
@@ -128,6 +132,9 @@ class TransportUpdate(BaseModel):
driver_id: Optional[str] = Field(default=None, alias="driverId")
driver_name: Optional[str] = Field(default=None, max_length=100, alias="driverName")
driver_phone: Optional[str] = Field(default=None, max_length=32, alias="driverPhone")
escort_id: Optional[str] = Field(default=None, alias="escortId")
escort_name: Optional[str] = Field(default=None, max_length=100, alias="escortName")
escort_phone: Optional[str] = Field(default=None, max_length=32, alias="escortPhone")
carrier_id: Optional[str] = Field(default=None, alias="carrierId")
carrier_name: Optional[str] = Field(default=None, max_length=200, alias="carrierName")
transport_medium: Optional[str] = Field(default=None, max_length=100, alias="transportMedium")
+75 -6
View File
@@ -225,28 +225,32 @@ def _apply_vehicle_snapshot_to_plan_transport(
transport: Optional[PlTransport],
vehicle: PlVehicle,
driver_id: Optional[str],
escort_id: Optional[str] = None,
) -> None:
snapshot = build_transport_resource_snapshot(
db,
tenant_no,
vehicle.id,
selected_driver_id=driver_id,
selected_escort_id=escort_id,
)
apply_resource_snapshot_json(plan=plan, transport=transport, snapshot=snapshot)
def _resolve_driver(
def _resolve_personnel_by_vehicle_role(
db: Session,
tenant_no: str,
vehicle_id: str,
driver_id: Optional[str],
personnel_id: Optional[str],
*,
role: str,
) -> tuple[Optional[str], Optional[str], Optional[str]]:
"""解析司机:显式选择优先,否则取车辆关联司机;均无则允许为空。"""
if driver_id:
"""解析驾押人员:显式选择优先,否则取车辆关联该角色;均无则允许为空。"""
if personnel_id:
personnel = (
db.query(PlPersonnel)
.filter(
PlPersonnel.id == driver_id,
PlPersonnel.id == personnel_id,
PlPersonnel.tenant_no == tenant_no,
PlPersonnel.del_flag == 0,
)
@@ -261,7 +265,7 @@ def _resolve_driver(
PlVehiclePersonnel.vehicle_id == vehicle_id,
PlVehiclePersonnel.tenant_no == tenant_no,
PlVehiclePersonnel.del_flag == 0,
PlVehiclePersonnel.role == "driver",
PlVehiclePersonnel.role == role,
)
.order_by(PlVehiclePersonnel.is_default.desc(), PlVehiclePersonnel.order_num.asc())
.first()
@@ -281,6 +285,30 @@ def _resolve_driver(
return None, None, None
def _resolve_driver(
db: Session,
tenant_no: str,
vehicle_id: str,
driver_id: Optional[str],
) -> tuple[Optional[str], Optional[str], Optional[str]]:
"""解析司机:显式选择优先,否则取车辆关联司机;均无则允许为空。"""
return _resolve_personnel_by_vehicle_role(
db, tenant_no, vehicle_id, driver_id, role="driver"
)
def _resolve_escort(
db: Session,
tenant_no: str,
vehicle_id: str,
escort_id: Optional[str],
) -> tuple[Optional[str], Optional[str], Optional[str]]:
"""解析押运员:显式选择优先,否则取车辆关联押运;均无则允许为空。"""
return _resolve_personnel_by_vehicle_role(
db, tenant_no, vehicle_id, escort_id, role="escort"
)
def _calc_freight_amount(
freight_type: str,
products: list[PlPlanProduct],
@@ -732,6 +760,9 @@ def _resolve_transport_context(
Optional[str],
Optional[str],
Optional[str],
Optional[str],
Optional[str],
Optional[str],
Decimal,
Optional[Decimal],
]:
@@ -750,6 +781,9 @@ def _resolve_transport_context(
driver_id, driver_name, driver_phone = _resolve_driver(
db, tenant_no, transport_payload.vehicle_id, transport_payload.driver_id
)
escort_id, escort_name, escort_phone = _resolve_escort(
db, tenant_no, transport_payload.vehicle_id, transport_payload.escort_id
)
carrier_id = transport_payload.carrier_id or vehicle.carrier_id
carrier_name = vehicle.carrier_name
if carrier_id:
@@ -777,6 +811,9 @@ def _resolve_transport_context(
driver_id,
driver_name,
driver_phone,
escort_id,
escort_name,
escort_phone,
carrier_id,
carrier_name,
freight_amount,
@@ -791,6 +828,9 @@ def _apply_payload_to_transport_row(
driver_id: Optional[str],
driver_name: Optional[str],
driver_phone: Optional[str],
escort_id: Optional[str],
escort_name: Optional[str],
escort_phone: Optional[str],
carrier_id: Optional[str],
carrier_name: Optional[str],
plan: PlPlan,
@@ -804,6 +844,9 @@ def _apply_payload_to_transport_row(
transport.driver_id = driver_id
transport.driver_name = driver_name
transport.driver_phone = driver_phone
transport.escort_id = escort_id
transport.escort_name = escort_name
transport.escort_phone = escort_phone
transport.carrier_id = carrier_id
transport.carrier_name = carrier_name
transport.start_point = plan.pickup_address
@@ -837,6 +880,9 @@ def upsert_draft_transport_for_plan(
driver_id,
driver_name,
driver_phone,
escort_id,
escort_name,
escort_phone,
carrier_id,
carrier_name,
freight_amount,
@@ -868,6 +914,9 @@ def upsert_draft_transport_for_plan(
driver_id=driver_id,
driver_name=driver_name,
driver_phone=driver_phone,
escort_id=escort_id,
escort_name=escort_name,
escort_phone=escort_phone,
carrier_id=carrier_id,
carrier_name=carrier_name,
plan=plan,
@@ -910,6 +959,9 @@ def upsert_draft_transport_for_plan(
driver_id=driver_id,
driver_name=driver_name,
driver_phone=driver_phone,
escort_id=escort_id,
escort_name=escort_name,
escort_phone=escort_phone,
carrier_id=carrier_id,
carrier_name=carrier_name,
plan=plan,
@@ -923,6 +975,9 @@ def upsert_draft_transport_for_plan(
plan.license_plate = vehicle.license_plate
plan.driver_id = driver_id
plan.driver_name = driver_name
plan.escort_id = escort_id
plan.escort_name = escort_name
plan.escort_phone = escort_phone
_apply_vehicle_snapshot_to_plan_transport(
db,
tenant_no=tenant_no,
@@ -930,6 +985,7 @@ def upsert_draft_transport_for_plan(
transport=transport,
vehicle=vehicle,
driver_id=driver_id,
escort_id=escort_id,
)
plan.freight_amount = freight_amount or 0
if plan.transport_id:
@@ -979,6 +1035,9 @@ def clear_draft_transport_for_plan(
plan.license_plate = None
plan.driver_id = None
plan.driver_name = None
plan.escort_id = None
plan.escort_name = None
plan.escort_phone = None
clear_resource_snapshot(plan=plan, transport=transport)
plan.freight_amount = 0
if plan.transport_status != "arranged":
@@ -1057,6 +1116,9 @@ def create_transport_for_plan(
driver_id, driver_name, driver_phone = _resolve_driver(
db, tenant_no, transport_payload.vehicle_id, transport_payload.driver_id
)
escort_id, escort_name, escort_phone = _resolve_escort(
db, tenant_no, transport_payload.vehicle_id, transport_payload.escort_id
)
carrier_name = vehicle.carrier_name
carrier_id = transport_payload.carrier_id or vehicle.carrier_id
@@ -1091,6 +1153,9 @@ def create_transport_for_plan(
driver_id=driver_id,
driver_name=driver_name,
driver_phone=driver_phone,
escort_id=escort_id,
escort_name=escort_name,
escort_phone=escort_phone,
carrier_id=carrier_id,
carrier_name=carrier_name,
start_point=plan.pickup_address,
@@ -1127,6 +1192,9 @@ def create_transport_for_plan(
plan.license_plate = vehicle.license_plate
plan.driver_id = driver_id
plan.driver_name = driver_name
plan.escort_id = escort_id
plan.escort_name = escort_name
plan.escort_phone = escort_phone
_apply_vehicle_snapshot_to_plan_transport(
db,
tenant_no=tenant_no,
@@ -1134,6 +1202,7 @@ def create_transport_for_plan(
transport=transport,
vehicle=vehicle,
driver_id=driver_id,
escort_id=escort_id,
)
plan.transport_status = "arranged"
plan.follow_status = "arranged"
@@ -0,0 +1,28 @@
"""运输计划解析押运员:车辆未关联押运时允许为空。"""
from __future__ import annotations
from unittest.mock import MagicMock
from app.services.pl_plan_service import _resolve_escort
def test_resolve_escort_without_vehicle_link_returns_none():
db = MagicMock()
db.query.return_value.filter.return_value.order_by.return_value.first.return_value = None
result = _resolve_escort(db, "tenant-1", "vehicle-1", None)
assert result == (None, None, None)
def test_resolve_escort_with_explicit_id_returns_personnel():
db = MagicMock()
personnel = MagicMock()
personnel.id = "escort-1"
personnel.personnel_name = "王押"
personnel.phone_no = "13900000001"
db.query.return_value.filter.return_value.first.return_value = personnel
result = _resolve_escort(db, "tenant-1", "vehicle-1", "escort-1")
assert result == ("escort-1", "王押", "13900000001")
@@ -43,6 +43,7 @@ def test_apply_transport_arrange_fields_without_driver():
payload=payload,
vehicle_info={"license_plate": "粤A12345", "carrier_id": None},
driver_info=None,
escort_info=None,
carrier_name=None,
start_point="A",
end_point="B",
@@ -0,0 +1,115 @@
"""安排运输:押运员可选。"""
from __future__ import annotations
from types import SimpleNamespace
from app.api.pl.transport import _apply_transport_arrange_fields
from app.schemas.pl.plan import PlanTransportCreateBlock
from app.schemas.pl.transport import TransportArrange
def test_transport_arrange_schema_allows_escort_id():
payload = TransportArrange.model_validate(
{"vehicleId": "veh-1", "escortId": "escort-1"}
)
assert payload.escort_id == "escort-1"
assert payload.driver_id is None
def test_plan_transport_block_allows_escort_id():
block = PlanTransportCreateBlock.model_validate(
{"vehicleId": "veh-1", "escortId": "escort-9"}
)
assert block.escort_id == "escort-9"
def test_apply_transport_arrange_fields_sets_escort():
transport = SimpleNamespace(
vehicle_id=None,
license_plate=None,
driver_id=None,
driver_name=None,
driver_phone=None,
escort_id=None,
escort_name=None,
escort_phone=None,
carrier_id=None,
carrier_name=None,
transport_medium=None,
start_point=None,
end_point=None,
distance_km=None,
unit=None,
freight_price=None,
freight_amount=0,
freight_type="by_weight",
has_invoice=0,
remark=None,
update_by=None,
)
payload = TransportArrange.model_validate(
{"vehicleId": "veh-1", "freightType": "by_weight", "escortId": "e1"}
)
_apply_transport_arrange_fields(
transport,
payload=payload,
vehicle_info={"license_plate": "粤A12345", "carrier_id": None},
driver_info=None,
escort_info={"id": "e1", "name": "李押", "phone": "13900000000"},
carrier_name=None,
start_point="A",
end_point="B",
freight_amount=0,
user_id="u1",
)
assert transport.escort_id == "e1"
assert transport.escort_name == "李押"
assert transport.escort_phone == "13900000000"
def test_apply_transport_arrange_fields_clears_escort():
transport = SimpleNamespace(
vehicle_id="veh-old",
license_plate="旧牌",
driver_id=None,
driver_name=None,
driver_phone=None,
escort_id="old-e",
escort_name="旧押",
escort_phone="1",
carrier_id=None,
carrier_name=None,
transport_medium=None,
start_point=None,
end_point=None,
distance_km=None,
unit=None,
freight_price=None,
freight_amount=0,
freight_type="by_weight",
has_invoice=0,
remark=None,
update_by=None,
)
payload = TransportArrange.model_validate(
{"vehicleId": "veh-1", "freightType": "by_weight"}
)
_apply_transport_arrange_fields(
transport,
payload=payload,
vehicle_info={"license_plate": "粤A12345", "carrier_id": None},
driver_info=None,
escort_info=None,
carrier_name=None,
start_point="A",
end_point="B",
freight_amount=0,
user_id="u1",
)
assert transport.escort_id is None
assert transport.escort_name is None
assert transport.escort_phone is None
+10
View File
@@ -194,6 +194,8 @@ export interface PersonnelQuery {
personnelAffiliation?: string
/** driver | escort;不传则不过滤类型 */
personnelType?: string
/** 逗号分隔多类型,如 escort,both */
personnelTypeIn?: string
page?: number
pageSize?: number
}
@@ -578,6 +580,9 @@ export interface Plan {
trailerNo?: string
driverId?: string
driverName?: string
escortId?: string
escortName?: string
escortPhone?: string
personnelSummary?: string
resourceSnapshot?: TransportResourceSnapshot
transportId?: string
@@ -658,6 +663,7 @@ export interface PlanListResponse {
export interface PlanTransportCreateBlock {
vehicleId: string
driverId?: string
escortId?: string
carrierId?: string
distanceKm?: number
freightType: string
@@ -1053,6 +1059,9 @@ export interface Transport {
driverId?: string
driverName?: string
driverPhone?: string
escortId?: string
escortName?: string
escortPhone?: string
resourceSnapshot?: TransportResourceSnapshot
carrierId?: string
carrierName?: string
@@ -1090,6 +1099,7 @@ export interface TransportStopDibangOption {
export interface TransportArrange {
vehicleId: string
driverId?: string
escortId?: string
carrierId?: string
transportMedium?: string
pickupAddress?: string
@@ -80,7 +80,7 @@
label-width="100px"
class="pl-dialog-form"
>
<div class="pl-plan-transport-row pl-plan-transport-row--three">
<div class="pl-plan-transport-row pl-plan-transport-row--four">
<el-form-item label="车牌号" class="pl-plan-transport-row__vehicle">
<div class="pl-plan-vehicle-with-library">
<el-select
@@ -136,6 +136,26 @@
/>
</el-select>
</el-form-item>
<el-form-item label="选择押运员" class="pl-plan-transport-row__escort">
<el-select
v-model="selectedEscortId"
placeholder="请选择押运员(可选)"
filterable
remote
clearable
:remote-method="searchEscorts"
:loading="escortSelectLoading"
:disabled="!formData.vehicleId"
@focus="onEscortSelectFocus"
>
<el-option
v-for="d in escortList"
:key="d.id"
:label="formatDriverOptionLabel(d)"
:value="d.id"
/>
</el-select>
</el-form-item>
<el-form-item label="运输公司" class="pl-plan-transport-row__carrier">
<div class="pl-plan-carrier-with-library">
<el-select
@@ -509,6 +529,7 @@
const deliveryForms = ref<PlanDeliveryForm[]>([createEmptyDeliveryForm(1)])
const loadedPlanSnapshot = ref<Plan | null>(null)
const selectedDriverId = ref('')
const selectedEscortId = ref('')
/** 已安排:禁止仅「保存」,须走保存并安排/调整运输,避免计划与地磅等联动字段脱节 */
const showSaveButton = computed(() => {
@@ -664,6 +685,16 @@
getSelectedId: () => selectedDriverId.value,
listParams: { status: '1', personnelType: 'driver' }
})
const {
list: escortList,
loading: escortSelectLoading,
search: searchEscorts,
onFocus: onEscortSelectFocus,
ensureById: ensureSelectedEscortOption
} = usePlRemotePersonnelOptions({
getSelectedId: () => selectedEscortId.value,
listParams: { status: '1', personnelTypeIn: 'escort,both' }
})
const vehicleBundle = ref<VehicleViewBundle | null>(null)
const formData = reactive({
@@ -1509,6 +1540,7 @@
return {
vehicleId: formData.vehicleId,
driverId: selectedDriverId.value || undefined,
escortId: selectedEscortId.value || undefined,
carrierId: formData.carrierId || undefined,
distanceKm: formData.distanceKm ?? undefined,
freightType: formData.freightType,
@@ -1580,6 +1612,7 @@
freightAmount?: number
carrierId?: string
driverId?: string
escortId?: string
}
formData.distanceKm = transport.distanceKm != null ? Number(transport.distanceKm) : null
formData.freightType = transport.freightType || 'by_weight'
@@ -1589,6 +1622,9 @@
if (transport.driverId) {
selectedDriverId.value = transport.driverId
}
if (transport.escortId) {
selectedEscortId.value = transport.escortId
}
freightCalc.freightPrice.value =
transport.freightPrice != null ? Number(transport.freightPrice) : null
freightCalc.freightAmount.value =
@@ -1660,6 +1696,7 @@
plan,
vehicleId: formData.vehicleId,
driverId: selectedDriverId.value,
escortId: selectedEscortId.value,
carrierId: formData.carrierId,
loadForm: loadForm.value,
deliveryForms: deliveryForms.value,
@@ -1689,6 +1726,7 @@
loadedPlanSnapshot.value = null
vehicleBundle.value = null
selectedDriverId.value = ''
selectedEscortId.value = ''
const d = todayYmd()
Object.assign(formData, {
createDate: d,
@@ -1794,11 +1832,17 @@
if (data.driverId) {
selectedDriverId.value = data.driverId
}
if (data.escortId) {
selectedEscortId.value = data.escortId
}
freightCalc.resetFreightCalc()
await restoreFreightFromTransport(data)
if (selectedDriverId.value) {
await ensureSelectedDriverOption(selectedDriverId.value)
}
if (selectedEscortId.value) {
await ensureSelectedEscortOption(selectedEscortId.value)
}
if (formData.carrierId) {
await ensureSelectedCarrierOption(formData.carrierId)
}
@@ -1865,6 +1909,7 @@
formData.vehicleId = ''
formData.carrierId = ''
selectedDriverId.value = ''
selectedEscortId.value = ''
vehicleBundle.value = null
reapplyStopDibangByVehiclePlate('')
freightCalc.resetFreightCalc()
@@ -1886,7 +1931,13 @@
}
async function loadMasterData() {
await Promise.all([searchVehicles(''), searchCarriers(''), searchDrivers(''), loadDefaultCustomerOptions()])
await Promise.all([
searchVehicles(''),
searchCarriers(''),
searchDrivers(''),
searchEscorts(''),
loadDefaultCustomerOptions()
])
}
function resolveDefaultDriver(personnelList: VehiclePersonnelRow[]) {
@@ -1895,6 +1946,12 @@
selectedDriverId.value = defaultDriver?.personnelId || ''
}
function resolveDefaultEscort(personnelList: VehiclePersonnelRow[]) {
const escorts = personnelList.filter((item) => item.role === 'escort' && item.personnelId)
const defaultEscort = escorts.find((item) => item.isDefault === 1) || escorts[0]
selectedEscortId.value = defaultEscort?.personnelId || ''
}
async function handleVehicleChange(
vehicleId: string,
opts?: { syncStopDibang?: boolean }
@@ -1902,6 +1959,7 @@
const syncStopDibang = opts?.syncStopDibang !== false
vehicleBundle.value = null
selectedDriverId.value = ''
selectedEscortId.value = ''
if (!vehicleId) {
formData.carrierId = ''
if (syncStopDibang) reapplyStopDibangByVehiclePlate('')
@@ -1922,9 +1980,13 @@
formData.carrierId = vehicleBundle.value.detail.carrierId
}
resolveDefaultDriver(vehicleBundle.value.personnelList || [])
resolveDefaultEscort(vehicleBundle.value.personnelList || [])
if (selectedDriverId.value) {
await ensureSelectedDriverOption(selectedDriverId.value)
}
if (selectedEscortId.value) {
await ensureSelectedEscortOption(selectedEscortId.value)
}
} finally {
vehicleLoading.value = false
}
@@ -2306,14 +2368,21 @@
gap: 12px 16px;
}
.pl-plan-transport-row--four {
display: grid;
grid-template-columns: max-content minmax(180px, 240px) minmax(180px, 240px) minmax(240px, 1fr);
gap: 12px 16px;
}
.pl-plan-transport-row__vehicle {
width: max-content;
max-width: 100%;
}
.pl-plan-transport-row__driver {
.pl-plan-transport-row__driver,
.pl-plan-transport-row__escort {
min-width: 0;
max-width: 340px;
max-width: 280px;
}
.pl-plan-transport-row__carrier {
@@ -99,6 +99,7 @@ export interface BuildArrangePayloadInput {
plan: Plan
vehicleId: string
driverId: string
escortId: string
carrierId: string
loadForm: PlanLoadForm
deliveryForms: PlanDeliveryForm[]
@@ -128,6 +129,7 @@ export function buildArrangePayloadFromPlanForm(input: BuildArrangePayloadInput)
return {
vehicleId: input.vehicleId,
driverId: input.driverId || undefined,
escortId: input.escortId || undefined,
carrierId: input.carrierId || undefined,
pickupAddress: input.loadForm.address || undefined,
pickupLng: input.loadForm.lng,