diff --git a/backend/app/api/pl/transport.py b/backend/app/api/pl/transport.py index 78eb449bf..353333a14 100644 --- a/backend/app/api/pl/transport.py +++ b/backend/app/api/pl/transport.py @@ -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" diff --git a/backend/app/schemas/pl/plan.py b/backend/app/schemas/pl/plan.py index 6ebeface1..1e69e6384 100644 --- a/backend/app/schemas/pl/plan.py +++ b/backend/app/schemas/pl/plan.py @@ -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") diff --git a/backend/app/schemas/pl/transport.py b/backend/app/schemas/pl/transport.py index 749adc989..aef6ba88f 100644 --- a/backend/app/schemas/pl/transport.py +++ b/backend/app/schemas/pl/transport.py @@ -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") diff --git a/backend/app/services/pl_plan_service.py b/backend/app/services/pl_plan_service.py index ad93027c5..57d40632d 100644 --- a/backend/app/services/pl_plan_service.py +++ b/backend/app/services/pl_plan_service.py @@ -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" diff --git a/backend/tests/test_pl_plan_resolve_escort.py b/backend/tests/test_pl_plan_resolve_escort.py new file mode 100644 index 000000000..ae8cf776c --- /dev/null +++ b/backend/tests/test_pl_plan_resolve_escort.py @@ -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") diff --git a/backend/tests/test_pl_transport_arrange_optional_driver.py b/backend/tests/test_pl_transport_arrange_optional_driver.py index b437f3f1a..5f307a2c9 100644 --- a/backend/tests/test_pl_transport_arrange_optional_driver.py +++ b/backend/tests/test_pl_transport_arrange_optional_driver.py @@ -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", diff --git a/backend/tests/test_pl_transport_arrange_optional_escort.py b/backend/tests/test_pl_transport_arrange_optional_escort.py new file mode 100644 index 000000000..22b9ac476 --- /dev/null +++ b/backend/tests/test_pl_transport_arrange_optional_escort.py @@ -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 diff --git a/web-admin/src/api/pl/index.ts b/web-admin/src/api/pl/index.ts index 84ab201d6..60d2d546d 100644 --- a/web-admin/src/api/pl/index.ts +++ b/web-admin/src/api/pl/index.ts @@ -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 diff --git a/web-admin/src/components/business/pl-plan-form-dialog/index.vue b/web-admin/src/components/business/pl-plan-form-dialog/index.vue index c607f57d6..8ddc0fec9 100644 --- a/web-admin/src/components/business/pl-plan-form-dialog/index.vue +++ b/web-admin/src/components/business/pl-plan-form-dialog/index.vue @@ -80,7 +80,7 @@ label-width="100px" class="pl-dialog-form" > -
+
+ + + + +
([createEmptyDeliveryForm(1)]) const loadedPlanSnapshot = ref(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(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 { diff --git a/web-admin/src/composables/usePlPlanArrangeSubmit.ts b/web-admin/src/composables/usePlPlanArrangeSubmit.ts index 7f043c1a3..308147814 100644 --- a/web-admin/src/composables/usePlPlanArrangeSubmit.ts +++ b/web-admin/src/composables/usePlPlanArrangeSubmit.ts @@ -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,