import type { QuotaConfiguration, ManualQuotaQuantity } from './quota-cert' /** * 运输计划管理 API */ import { ElMessage } from 'element-plus' import type { AxiosRequestConfig } from 'axios' import request from '@/utils/http/bx' import { pickBxList, pickBxPageResult } from '@/utils/http/bx' import type { TransportResourceSnapshot } from '@/utils/pl-transport-resource-snapshot' // ============================================================================= // 基础数据 API // ============================================================================= export interface PlListResponse { total: number list: T[] } // 承运商 export interface Carrier { id: string carrierName: string contactName?: string contactPhone?: string status: string remark?: string businessLicenseUrls?: string roadTransportLicenseUrls?: string transportAgreementUrls?: string transportAgreementExpireDate?: string auditStatus?: string auditBy?: string auditTime?: string auditRemark?: string qualificationComplete?: boolean transportAgreementExpired?: boolean createTime: string } export interface CarrierQuery { q?: string status?: string auditStatus?: string page?: number pageSize?: number } export const carrierApi = { list: (params?: CarrierQuery) => request.get>('/transport/carrier', { params }), export: (data: Record) => request.post('/transport/carrier/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/carrier/${id}`), create: (data: Partial) => request.post('/transport/carrier', data), update: (id: string, data: Partial) => request.put(`/transport/carrier/${id}`, data), /** @deprecated 承运商审核已移除,后端接口已禁用 */ approve: (id: string) => request.post(`/transport/carrier/${id}/approve`), reject: (id: string, data?: { remark?: string }) => request.post(`/transport/carrier/${id}/reject`, data ?? {}), delete: (id: string) => request.delete(`/transport/carrier/${id}`) } // 证书行(嵌套保存) export interface CertRow { certId?: string vehiclePart?: string certName: string certNo?: string expireDate?: string remindDays?: number attachmentUrls?: string } export interface CertRead extends CertRow { id: string ownerType: string ownerId: string status: string createTime?: string } // 车辆-人员关联行 export interface VehiclePersonnelRow { personnelId?: string personnelName: string phoneNo?: string age?: number | null role: string isDefault?: number remark?: string } // 车辆 export interface Vehicle { id: string vehicleType: string carrierId?: string carrierName?: string licensePlate: string trailerNo?: string defaultDriverId?: string defaultDriverName?: string personnelSummary?: string loadCapacity?: number vehicleStatus: string remark?: string photoUrls?: string certList?: CertRead[] createTime: string } export interface VehicleDetail extends Vehicle { personnelList: VehiclePersonnelRow[] certList: CertRead[] } export interface VehiclePayload { vehicleType: string carrierId?: string carrierName?: string licensePlate: string trailerNo?: string loadCapacity?: number | null vehicleStatus: string remark?: string photoUrls?: string personnelList?: VehiclePersonnelRow[] certList?: CertRow[] } export interface VehicleQuery { q?: string vehicleType?: string carrierId?: string vehicleStatus?: string excludeIneligibleCarrier?: boolean page?: number pageSize?: number } export const vehicleApi = { list: (params?: VehicleQuery) => request.get>('/transport/vehicle', { params }), export: (data: Record) => request.post('/transport/vehicle/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/vehicle/${id}`), create: (data: VehiclePayload) => request.post('/transport/vehicle', data), update: (id: string, data: Partial) => request.put(`/transport/vehicle/${id}`, data), delete: (id: string) => request.delete(`/transport/vehicle/${id}`) } // 人员 export interface Personnel { id: string personnelName: string phoneNo?: string age?: number idCardNo?: string personnelType: string personnelAffiliation: string status: string remark?: string photoUrls?: string /** 关联系统用户 ID */ sysUserId?: string | null nearestExpireDate?: string certList?: CertRead[] createTime: string } export interface PersonnelDetail extends Personnel { certList: CertRead[] } export interface PersonnelPayload { personnelName: string phoneNo?: string age?: number | null idCardNo?: string personnelType: string personnelAffiliation: string status: string remark?: string photoUrls?: string /** 关联系统用户;传空串/null 表示清空 */ sysUserId?: string | null certList?: CertRow[] } export interface PersonnelQuery { q?: string status?: string personnelAffiliation?: string /** driver | escort;不传则不过滤类型 */ personnelType?: string /** 逗号分隔多类型,如 escort,both */ personnelTypeIn?: string page?: number pageSize?: number } export const personnelApi = { list: (params?: PersonnelQuery) => request.get>('/transport/personnel', { params }), export: (data: Record) => request.post('/transport/personnel/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/personnel/${id}`), create: (data: PersonnelPayload) => request.post('/transport/personnel', data), update: (id: string, data: Partial) => request.put(`/transport/personnel/${id}`, data), delete: (id: string) => request.delete(`/transport/personnel/${id}`) } export interface PersonnelCertOcrResult { certNo?: string | null expireDate?: string | null expireDateRaw?: string | null monthlyUsed: number monthlyLimit: number } export const personnelCertOcrApi = { recognize: (data: { certName: string; attachmentUrl?: string; attachmentUrls?: string }) => request.post('/transport/ocr/recognize-cert', data) } export type PasteConflictAction = 'reuse' | 'overwrite' | 'cancel' export type PasteRecognizeMode = 'vehicle_bundle' | 'personnel_first' export interface PasteConflictItem { key: 'vehicle' | 'driver' | 'escort' existingId: string action: PasteConflictAction } export interface PastePersonnelPayload { personnelName: string phoneNo?: string idCardNo?: string personnelType: 'driver' | 'escort' } export interface PasteVehiclePayload { licensePlate: string trailerNo?: string loadCapacity?: number | null carrierId?: string carrierName?: string } export interface PasteCommitBody { mode: PasteRecognizeMode createVehicle?: boolean vehicle?: PasteVehiclePayload | null driver?: PastePersonnelPayload | null escort?: PastePersonnelPayload | null conflicts?: PasteConflictItem[] } export interface PasteCommitResult { vehicleId?: string | null driverId?: string | null escortId?: string | null } export const pasteRecognizeApi = { commit: (data: PasteCommitBody) => request.post('/transport/paste-recognize/commit', data) } // 运输通用地址库 export interface TransportAddress { id: string addressName: string fullAddress: string lng: number lat: number contactName?: string contactPhone?: string notes?: string status: string remark?: string createTime: string updateTime: string } export interface TransportAddressQuery { q?: string status?: string page?: number pageSize?: number } export interface TransportAddressPayload { addressName: string fullAddress: string lng: number lat: number contactName?: string contactPhone?: string notes?: string status?: string remark?: string } export const transportAddressApi = { list: (params?: TransportAddressQuery) => request.get>('/transport/address', { params }), export: (data: Record) => request.post('/transport/address/export', data, { responseType: 'blob' }), listAll: (params?: { q?: string }) => /** 选择器:仅按地址名称、详细地址模糊检索(不含联系人/电话) */ request.get>('/transport/address/all', { params }), get: (id: string) => request.get(`/transport/address/${id}`), create: (data: TransportAddressPayload) => request.post('/transport/address', data), update: (id: string, data: Partial) => request.put(`/transport/address/${id}`, data), delete: (id: string) => request.delete(`/transport/address/${id}`) } export type CustomerStopPrefStopType = 'load' | 'delivery' export interface CustomerStopPref { customerId: string stopType: CustomerStopPrefStopType address?: string lng?: number | null lat?: number | null contact?: string notes?: string } export const customerStopPrefApi = { get: (params: { customerId: string; stopType: CustomerStopPrefStopType }) => request.get('/transport/customer-stop-pref', { params }) } // 司机(兼容旧 API,映射至 personnel) export interface Driver { id: string driverName: string phoneNo?: string idCardNo?: string drivingLicenseNo?: string drivingLicenseExpire?: string vehicleLicenseNo?: string vehicleLicenseExpire?: string hazmatCertNo?: string hazmatCertExpire?: string remark?: string createTime: string } export interface DriverQuery { q?: string page?: number pageSize?: number } function personnelToDriver(p: Personnel): Driver { return { id: p.id, driverName: p.personnelName, phoneNo: p.phoneNo, idCardNo: p.idCardNo, remark: p.remark, createTime: p.createTime } } function driverToPersonnelPayload(data: Partial): PersonnelPayload { return { personnelName: data.driverName || '', phoneNo: data.phoneNo, idCardNo: data.idCardNo, status: '1', remark: data.remark } } export const driverApi = { export: (data: Record) => request.post('/transport/driver/export', data, { responseType: 'blob' }), list: async (params?: DriverQuery) => { const res = await personnelApi.list({ q: params?.q, page: params?.page, pageSize: params?.pageSize }) const { list, total } = pickBxPageResult(res) return { ...res, data: { total, list: list.map(personnelToDriver) } } }, get: async (id: string) => { const res = await personnelApi.get(id) const detail = (res.data ?? res) as PersonnelDetail return { ...res, data: personnelToDriver(detail) } }, create: async (data: Partial) => { const res = await personnelApi.create(driverToPersonnelPayload(data)) const detail = (res.data ?? res) as PersonnelDetail return { ...res, data: personnelToDriver(detail) } }, update: async (id: string, data: Partial) => { const res = await personnelApi.update(id, driverToPersonnelPayload(data)) const detail = (res.data ?? res) as PersonnelDetail return { ...res, data: personnelToDriver(detail) } }, delete: (id: string) => personnelApi.delete(id) } // ============================================================================= // 计划管理 API // ============================================================================= export interface PlanDocumentAttachmentItem { url: string label: string } export interface PlanContractLink { contractId: string contractNo?: string stopId?: string } /** 装货/送货段产品行 */ export interface PlanStopProduct { unlinkedDraft?: boolean planProductClientKey?: string mobileProductMeta?: { aliasName: string | null; brandMode: string; brandId: string; brandName: string } id?: string planProductId?: string productId: string productNo: string productName: string model?: string unit: string packageSpec?: string deliveryMethod?: string contractId?: string contractProductId?: string contractNo?: string planQty?: number inboundQty?: number inboundPrice?: number inboundAmount?: number outboundQty?: number outboundPrice?: number outboundAmount?: number /** 本段归档品牌数量(与归档弹窗填写一致) */ brandQtys?: Array<{ brandId: string; brandName?: string; qty: number; unitPrice?: number }> remark?: string /** 归档备注(与安排时 remark 分离) */ archiveRemark?: string } /** 装货段(计划 1:1) */ export interface PlanLoad { /** 只读:日历展示客户名称,简称优先 */ calendarCustomerName?: string id?: string planId?: string customerId?: string customerName?: string address?: string lng?: number | null lat?: number | null loadTime?: string contact?: string notes?: string generateDibang?: number dibangDirection?: string remark?: string products?: PlanStopProduct[] contracts?: PlanContractLink[] } /** 送货段(计划 1:N) */ export interface PlanDelivery { /** 只读:日历展示客户名称,简称优先 */ calendarCustomerName?: string id?: string planId?: string seq?: number customerId?: string customerName?: string address?: string lng?: number | null lat?: number | null deliveryTime?: string contact?: string notes?: string generateDibang?: number dibangDirection?: string remark?: string deliveryMethod?: string products?: PlanStopProduct[] contracts?: PlanContractLink[] } export interface PlanProduct { id: string planId: string productId: string productNo: string productName: string /** 列表展示用:由已存名称反推的别名 */ aliasName?: string brandMode?: string brandId?: string brandName?: string /** 归档品牌数量明细 */ brandQtys?: PlanArchiveBrandQtyItem[] model?: string unit: string categoryName?: string packageSpec?: string deliveryMethod?: string quantity?: number actualQty?: number taxRate?: number taxPrice?: number taxAmount?: number taxSubtotal?: number isExplosive: number isToxic: number purchaseCertUrl?: string transportCertUrl?: string legalUseCertUrl?: string inboundEstimateQty?: number inboundQty?: number inboundPrice?: number inboundAmount?: number outboundEstimateQty?: number outboundQty?: number outboundPrice?: number outboundAmount?: number remark?: string } export interface Plan { /** 日历专用:客户简称优先的路线 */ calendarRoute?: string /** 日历只读归属:安排快照优先,旧计划补查车辆档案 */ calendarVehicleType?: 'self' | 'carrier' | 'unknown' mobileProductMetaVersion?: number id: string planNo: string createDate: string planDate?: string | null deliveryTimes?: string[] belongsCompany: string planType: string followStatus: string transportStatus: string archiveStatus: string /** 已过磅、地磅已执行、现场已回传或出入库实际数量已修改 */ executionStarted?: boolean executionLockReason?: string | null completeStatus: string contractId?: string contractNo?: string supplierContracts?: PlanContractLink[] demanderContracts?: PlanContractLink[] supplierId?: string supplierName?: string destinationId?: string destinationName?: string customerId?: string customerName?: string contactName?: string contactPhone?: string partyA?: string partyB?: string contractStartDate?: string contractEndDate?: string deliveryMethod?: string settlementMethod?: string hasFreight: number pickupAddress?: string pickupLng?: number | null pickupLat?: number | null pickupTime?: string pickupContact?: string pickupNotes?: string destinationAddress?: string destinationLng?: number | null destinationLat?: number | null deliveryTime?: string deliveryContact?: string destinationNotes?: string vehicleId?: string licensePlate?: string trailerNo?: string driverId?: string driverName?: string escortId?: string escortName?: string escortPhone?: string personnelSummary?: string resourceSnapshot?: TransportResourceSnapshot transportId?: string transportNo?: string totalAmount: number freightAmount: number freightRemark?: string notifyMessage?: string followUserId?: string followUserName?: string hazardCertReady?: number hazardCertReadyBy?: string hazardCertReadyTime?: string erpEntered?: number remark?: string createBy?: string createTime: string products?: PlanProduct[] /** 列表:是否显示「复制给司机」 */ canCopyToDriver?: boolean /** 列表:是否显示「复制给现场」 */ canCopyToSite?: boolean /** 列表:是否显示「复制磅单二维码」 */ canCopyPoundQrcode?: boolean /** 列表:单据附件(司机/现场/出入库现场等) */ documentAttachments?: PlanDocumentAttachmentItem[] /** 装货段 */ load?: PlanLoad | null /** 送货段列表 */ deliveries?: PlanDelivery[] /** 列表:装货客户名称 */ loadCustomerName?: string /** 列表:送货客户摘要 */ deliveryCustomerSummary?: string /** 保存告警(如数量不平衡,HTTP 200 仍返回) */ warnings?: string[] } export interface PlanQuery { includeCustomerShortNames?: number q?: string planNo?: string planType?: string /** 单据类型多选(列头漏斗),逗号分隔 */ planTypeIn?: string followStatus?: string /** 排除的跟进状态,多个以英文逗号分隔 */ excludeFollowStatus?: string transportStatus?: string transportStatusIn?: string archiveStatus?: string archiveStatusIn?: string /** 归档列表:已安排运输或归档状态为已取消 */ archiveList?: number customerName?: string supplierName?: string destinationName?: string productName?: string licensePlate?: string planDateFrom?: string planDateTo?: string /** 1=仅含易制毒/易制爆产品的计划 */ hasHazardProduct?: number /** incomplete=至少一张必需危化证书未上传 */ certUploadStatus?: string /** 0=未确认齐备 1=已确认齐备 */ hazardCertReady?: number /** CRM 合同 ID:仅返回关联该合同的计划 */ contractId?: string page?: number pageSize?: number /** 列头漏斗等扩展键透传 */ [key: string]: unknown } export interface PlanListResponse { total: number list: Plan[] } export interface PlanTransportCreateBlock { vehicleId: string driverId?: string escortId?: string carrierId?: string distanceKm?: number freightType: string transportQty?: number freightPrice?: number freightAmount?: number hasInvoice?: number remark?: string } export interface PlanCreateFullPayload { mobileProductMeta?: boolean contractId?: string createDate: string planDate?: string | null belongsCompany: string planType?: string contractNo?: string supplierId?: string supplierName?: string destinationId?: string destinationName?: string customerId?: string customerName?: string contactName?: string contactPhone?: string partyA?: string partyB?: string contractStartDate?: string contractEndDate?: string deliveryMethod?: string settlementMethod?: string hasFreight: number freightRemark?: string pickupAddress?: string pickupLng?: number | null pickupLat?: number | null pickupTime?: string pickupContact?: string pickupNotes?: string destinationAddress?: string destinationLng?: number | null destinationLat?: number | null deliveryTime?: string deliveryContact?: string destinationNotes?: string followUserId?: string followUserName?: string remark?: string saveMode?: 'draft' | 'formal' supplierContracts?: PlanContractLink[] demanderContracts?: PlanContractLink[] load?: PlanLoad deliveries?: PlanDelivery[] products: Array<{ id?: string clientKey?: string productId: string productNo: string productName: string model?: string unit: string categoryName?: string packageSpec?: string deliveryMethod?: string quantity?: number actualQty?: number taxPrice?: number taxAmount?: number taxSubtotal?: number remark?: string isExplosive?: number isToxic?: number }> transport?: PlanTransportCreateBlock } export type PlanUpdatePayload = Partial> /** 展示计划保存返回的告警(数量不平衡等) */ export function showPlanSaveWarnings(plan?: Pick | null) { const warnings = plan?.warnings if (!warnings?.length) return const seen = new Set() for (const msg of warnings) { const text = (msg || '').trim() if (!text || seen.has(text)) continue seen.add(text) ElMessage.warning(text) } } export interface PlanArchiveBrandQtyItem { brandId: string brandName?: string qty: number /** 本品牌单价(金额 = qty * unitPrice) */ unitPrice?: number } export interface TransportDriverProductFeedback { planProductId: string productName: string productNo?: string model?: string unit?: string packageSpec?: string brandMode?: string brandId?: string brandName?: string actualQty?: number brandSummary?: string brandQtys?: PlanArchiveBrandQtyItem[] } export interface PlanArchiveProductItem { id: string actualQty?: number inboundQty?: number inboundPrice?: number outboundQty?: number outboundPrice?: number brandQtys?: PlanArchiveBrandQtyItem[] } export interface PlanArchiveStopProductItem { id: string inboundQty?: number inboundPrice?: number outboundQty?: number outboundPrice?: number archiveRemark?: string brandQtys?: PlanArchiveBrandQtyItem[] } export interface PlanArchiveLoadBlock { stopId: string products: PlanArchiveStopProductItem[] } export interface PlanArchiveDeliveryBlock { stopId: string products: PlanArchiveStopProductItem[] } export interface PlanArchiveFreightBlock { hasFreight: number freightRemark?: string distanceKm?: number freightType?: string freightPrice?: number freightAmount?: number transportQty?: number hasInvoice?: number } export interface PlanArchiveStopFeedbackAttachmentItem { stopType: 'load' | 'delivery' stopId: string attachmentUrls: string[] } export interface PlanArchiveFeedbacksBlock { driverStops?: PlanArchiveStopFeedbackAttachmentItem[] siteStops?: PlanArchiveStopFeedbackAttachmentItem[] } export interface PlanArchiveBody { operationId?: string quotaAllowSupplement?: boolean quotaConfiguration?: QuotaConfiguration quotaManualActuals?: ManualQuotaQuantity[] quotaReason?: string quotaEvidenceUrls?: string quotaSourceNote?: string products?: PlanArchiveProductItem[] load?: PlanArchiveLoadBlock deliveries?: PlanArchiveDeliveryBlock[] freight?: PlanArchiveFreightBlock feedbacks?: PlanArchiveFeedbacksBlock confirmOverage?: boolean } export interface PlanDriverFeedbackProductItem { planProductId: string actualQty: number brandQtys?: PlanArchiveBrandQtyItem[] } export interface PlanDriverFeedbackUpdateBody { feedbackQty?: number | null feedbackRemark?: string | null feedbackImages?: string[] feedbackTime?: string | null products?: PlanDriverFeedbackProductItem[] } export interface PlanSiteFeedbackUpdateBody { siteFeedbackRemark?: string | null siteFeedbackImages?: string[] siteFeedbackTime?: string | null } export interface PlanContractGapItem { id: string planNo: string planDate?: string deliveryTimes?: string[] planType?: string followStatus: string supplierId?: string supplierName?: string customerId?: string customerName?: string gapSides: Array<'supplier' | 'demander'> supplierContractExempt: boolean demanderContractExempt: boolean supplierContracts: PlanContractLink[] demanderContracts: PlanContractLink[] products?: PlanProduct[] /** 段次合同完善:装货段 */ load?: PlanLoad /** 段次合同完善:送货段列表 */ deliveries?: PlanDelivery[] } export interface PlanContractGapQuery { page?: number pageSize?: number planNo?: string gapType?: 'supplier' | 'demander' | 'both' | '' customerKeyword?: string planDateFrom?: string planDateTo?: string q?: string } export interface ContractProductArrangeQtyPlanItem { planId: string planNo?: string planDate?: string | null planQty: number planType?: string | null belongsCompany?: string | null supplierName?: string | null customerName?: string | null licensePlate?: string | null driverName?: string | null followUserName?: string | null followStatus?: string transportStatus?: string archiveStatus?: string createTime?: string | null } export interface ContractProductArrangeQtyDetail { /** 批量接口回传的客户端缓存键 */ key?: string contractId: string contractNo?: string | null customerName?: string | null contractProductId?: string | null productId: string packageSpec?: string | null productName?: string | null hasFixedQty: boolean contractQty?: number | null arrangedQty: number availableQty?: number | null plans: ContractProductArrangeQtyPlanItem[] } export const planApi = { mobileProductCapability: () => request.get<{ mobileProductMetaVersion: number }>('/transport/plan/mobile-product-capability'), list: (params?: PlanQuery | Record) => request.get('/transport/plan/', { params }), export: (data: Record) => request.post('/transport/plan/export', data, { responseType: 'blob', // 归档 ZIP 需拉取附件,易超过全局 60s timeout: 3600000 }), get: (id: string, includeMobileProductMeta = false) => includeMobileProductMeta ? request.get(`/transport/plan/${id}`, { params: { includeMobileProductMeta: true } }) : request.get(`/transport/plan/${id}`), countByContract: (contractIds: string[]) => request.get<{ counts: Record }>('/transport/plan/count-by-contract', { params: { contractIds: contractIds.join(',') } }), /** 表单可安排数量 / 问号弹窗 */ getContractProductArrangeQty: (params: { contractId: string productId?: string packageSpec?: string excludePlanId?: string contractProductId?: string }) => request.get( '/transport/plan/contract-product-arrange-qty', { params } ), /** 表单打开批量预取可安排数量(无 plans 明细;问号弹窗仍用单条) */ batchContractProductArrangeQty: ( items: Array<{ key: string contractId: string productId?: string packageSpec?: string excludePlanId?: string contractProductId?: string }> ) => request.post<{ items: ContractProductArrangeQtyDetail[] }>( '/transport/plan/contract-product-arrange-qty/batch', { items } ), create: (data: Partial) => request.post('/transport/plan/', data), createFull: (data: PlanCreateFullPayload) => request.post('/transport/plan/create-full', data), update: (id: string, data: PlanUpdatePayload) => request.put(`/transport/plan/${id}`, data), updateProductCerts: ( id: string, products: Array<{ id: string purchaseCertUrl?: string transportCertUrl?: string legalUseCertUrl?: string }> ) => request.patch(`/transport/plan/${id}/product-certs`, { products }), setHazardCertReady: (id: string, ready: boolean) => request.patch(`/transport/plan/${id}/hazard-cert-ready`, { ready }), setErpEntered: (id: string, entered: boolean) => request.patch(`/transport/plan/${id}/erp-entered`, { entered }), cancel: (id: string, data?: { cancelReason?: string }) => request.post(`/transport/plan/${id}/cancel`, data || {}), forceVoidPreview: (id: string) => request.get<{ executionLockReason: string weighingSlips: string[] inventories: string[] ioOrders: string[] notice: string }>(`/transport/plan/${id}/force-void-preview`), forceVoid: (id: string, data: { cancelReason: string }) => request.post(`/transport/plan/${id}/force-void`, data), waitNotice: (id: string) => request.post(`/transport/plan/${id}/wait-notice`), cancelWaitNotice: (id: string) => request.post(`/transport/plan/${id}/cancel-wait-notice`), forceFollowStatus: (id: string, followStatus: string) => request.post(`/transport/plan/${id}/force-follow-status`, { followStatus }), getArrangeInfo: (id: string) => request.get(`/transport/plan/${id}/arrange-info`), archive: (id: string, data?: PlanArchiveBody) => request.post(`/transport/plan/${id}/archive`, data), archiveDraft: (id: string, data: PlanArchiveBody) => request.post(`/transport/plan/${id}/archive-draft`, data), updateDriverFeedback: (id: string, data: PlanDriverFeedbackUpdateBody) => request.patch(`/transport/plan/${id}/driver-feedback`, data), updateSiteFeedback: (id: string, linkId: string, data: PlanSiteFeedbackUpdateBody) => request.patch(`/transport/plan/${id}/site-feedback/${linkId}`, data), delete: (id: string) => request.delete(`/transport/plan/${id}`), createFromContract: (data: { contractId: string planDate?: string | null createDate?: string followUserId?: string followUserName?: string }) => request.post('/transport/plan/create-from-contract', data), getInboundSlipTemplate: (id: string) => request.get(`/transport/plan/${id}/inbound-slip-template`), saveInboundSlipTemplate: (id: string, slipTemplateContent: string) => request.put(`/transport/plan/${id}/inbound-slip-template`, { slipTemplateContent }), getDeliverySlipTemplate: (id: string) => request.get(`/transport/plan/${id}/delivery-slip-template`), saveDeliverySlipTemplate: (id: string, slipTemplateContent: string) => request.put(`/transport/plan/${id}/delivery-slip-template`, { slipTemplateContent }), getPrintDeliverySlip: (id: string, customerId?: string) => request.get<{ planId: string planNo: string planDate?: string customerId?: string receivingUnit?: string receiveContact?: string deliveryAddress?: string products?: PlanStopProduct[] customers?: { customerId: string; customerName: string; deliveryCount: number }[] }>(`/transport/plan/${id}/print-delivery-slip`, { params: customerId ? { customerId } : undefined }), getSubpackSlipTemplate: (id: string) => request.get(`/transport/plan/${id}/subpack-slip-template`), saveSubpackSlipTemplate: (id: string, slipTemplateContent: string) => request.put(`/transport/plan/${id}/subpack-slip-template`, { slipTemplateContent }), ensureSubpackSlipNo: (id: string) => request.post(`/transport/plan/${id}/subpack-slip-no`), listContractGaps: (params?: PlanContractGapQuery | Record) => request.get>('/transport/plan/contract-gaps', { params }), exportContractGaps: (data: Record) => request.post('/transport/plan/contract-gaps/export', data, { responseType: 'blob' }), updateSideContracts: (id: string, side: 'supplier' | 'demander', contracts: PlanContractLink[]) => request.put(`/transport/plan/${id}/contracts/${side}`, { contracts }), updateStopContracts: ( id: string, data: { stopType: 'load' | 'delivery'; stopId: string; contracts: PlanContractLink[] } ) => request.put(`/transport/plan/${id}/stop-contracts`, data) } // ============================================================================= // 运输管理 API // ============================================================================= export interface TransportStopFeedback { stopType: 'load' | 'delivery' stopId: string label: string customerName: string businessType?: string quantity?: number attachmentUrls?: string[] products?: { stopProductId: string planProductId?: string productName?: string model?: string unit?: string packageSpec?: string quantity?: number actualQty?: number brandQtys?: { brandId: string; brandName?: string; qty: number }[] }[] remark?: string feedbackTime?: string } export interface Transport { id: string transportNo: string vehicleId?: string licensePlate?: string trailerNo?: string driverId?: string driverName?: string driverPhone?: string escortId?: string escortName?: string escortPhone?: string resourceSnapshot?: TransportResourceSnapshot carrierId?: string carrierName?: string transportMedium?: string startPoint?: string endPoint?: string distanceKm?: number unit?: string transportQty?: number freightPrice?: number freightAmount: number freightType: string hasInvoice: number qrCode?: string qrExpireDate?: string transportStatus: string approvalStatus: string feedbackTime?: string feedbackQty?: number feedbackImages?: string feedbackRemark?: string driverProductFeedbacks?: TransportDriverProductFeedback[] driverStopFeedbacks?: TransportStopFeedback[] siteStopFeedbacks?: TransportStopFeedback[] remark?: string createTime: string } export interface TransportStopDibangOption { stopType: 'load' | 'delivery' stopId: string generateDibang: boolean } export interface TransportArrange { operationId?: string quotaConfiguration?: QuotaConfiguration quotaManualActuals?: ManualQuotaQuantity[] vehicleId: string driverId?: string escortId?: string carrierId?: string transportMedium?: string pickupAddress?: string pickupLng?: number | null pickupLat?: number | null pickupTime?: string pickupContact?: string pickupNotes?: string destinationAddress?: string destinationLng?: number | null destinationLat?: number | null deliveryTime?: string deliveryContact?: string destinationNotes?: string distanceKm?: number unit?: string freightType: string freightPrice?: number freightAmount?: number hasInvoice?: number remark?: string generateDibangPlan?: boolean stopDibangOptions?: TransportStopDibangOption[] } export interface TransportInventoryLink { planProductId: string productName: string dbPurchasePlanId: string dbInventoryId: string batchNo: string planNo: string stopType?: 'load' | 'delivery' stopId?: string businessType?: string partnerCompany?: string } export interface TransportSiteLink { planProductId: string productName: string siteLinkId: string planNo: string businessType: string siteAccessToken?: string siteUrl?: string siteFeedbackRemark?: string siteFeedbackImages?: string[] siteFeedbackTime?: string } export interface TransportIoOrderBrief { kind: string orderId: string orderNo: string siteAccessToken?: string siteUrl?: string status?: string } export interface PlanArrangeInfo { requiresSiteLink?: boolean driverUrl?: string licensePlate?: string siteRecords?: TransportSiteLink[] inventoryRecords?: TransportInventoryLink[] ioOrder?: TransportIoOrderBrief } export interface TransportArrangeResult extends Transport { driverAccessToken?: string driverUrl?: string siteRecords?: TransportSiteLink[] inventoryRecords?: TransportInventoryLink[] ioOrder?: TransportIoOrderBrief contractQtyReminder?: ContractQtyReminder } export interface ContractQtyReminderItem { contractId: string contractNo?: string contractType?: string customerName?: string stopKind: string productId: string packageSpec?: string | null productName?: string model?: string unit?: string mode: 'remaining' | 'framework_cumulative' | string contractQty?: number arrangedTotalQty: number remainingQty?: number thisPlanQty: number } export interface ContractQtyReminder { items: ContractQtyReminderItem[] } export interface PlIoOrderItem { id: string orderId: string planProductId: string productId?: string productNo?: string productName?: string aliasName?: string brandSummary?: string brandName?: string brandMode?: string brandId?: string brandOptions?: Array<{ brandId: string; brandName?: string }> brandQtys?: PlanArchiveBrandQtyItem[] specification?: string unit?: string unitLabel?: string planQty?: number actualQty?: number qtySource?: string categoryName?: string packageSpec?: string deliveryMethod?: string remark?: string } export interface PlIoOrder { id: string orderNo: string planId: string transportId: string status: string partnerCompany?: string vehicleNo?: string planNo?: string planDate?: string businessType?: string planRemark?: string pickupAddress?: string pickupLng?: number | null pickupLat?: number | null pickupTime?: string pickupContact?: string pickupNotes?: string destinationName?: string destinationAddress?: string destinationLng?: number | null destinationLat?: number | null deliveryTime?: string deliveryContact?: string destinationNotes?: string siteAccessToken?: string siteUrl?: string siteFeedbackRemark?: string siteFeedbackImages?: string[] siteFeedbackTime?: string siteFirstSubmittedAt?: string remark?: string productSummary?: string actualQtyTotal?: number kind: string items?: PlIoOrderItem[] createTime?: string updateTime?: string } export interface PlIoOrderQuery { orderNo?: string planNo?: string status?: string partnerCompany?: string vehicleNo?: string q?: string planDateFrom?: string planDateTo?: string page?: number pageSize?: number } export interface PlIoOrderListResponse { total: number list: PlIoOrder[] } export const inboundOrderApi = { list: (params?: PlIoOrderQuery | Record) => request.get('/transport/inbound/', { params }), export: (data: Record) => request.post('/transport/inbound/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/inbound/${id}`), getByPlan: (planId: string) => request.get(`/transport/inbound/by-plan/${planId}`), patch: (id: string, data: Record) => request.patch(`/transport/inbound/${id}`, data), siteUrl: (id: string) => request.get<{ siteUrl?: string; siteAccessToken?: string }>(`/transport/inbound/${id}/site-url`) } export const outboundOrderApi = { list: (params?: PlIoOrderQuery | Record) => request.get('/transport/outbound/', { params }), export: (data: Record) => request.post('/transport/outbound/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/outbound/${id}`), getByPlan: (planId: string) => request.get(`/transport/outbound/by-plan/${planId}`), patch: (id: string, data: Record) => request.patch(`/transport/outbound/${id}`, data), siteUrl: (id: string) => request.get<{ siteUrl?: string; siteAccessToken?: string }>( `/transport/outbound/${id}/site-url` ) } export interface TransportQuery { q?: string transportNo?: string transportStatus?: string approvalStatus?: string vehicleId?: string driverId?: string page?: number pageSize?: number } export interface TransportListResponse { total: number list: Transport[] } export const transportApi = { list: (params?: TransportQuery) => request.get('/transport/order/', { params }), export: (data: Record) => request.post('/transport/order/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/order/${id}`), create: (data: Partial & { planIds?: string[] }) => request.post('/transport/order/', data), arrange: (planId: string, data: TransportArrange) => request.post(`/transport/order/arrange/${planId}`, data), generateQrcode: (id: string, expireDays?: number) => request.get<{ transportId: string; transportNo: string; qrCode: string; expireDate?: string }>( `/transport/order/${id}/qrcode`, { params: { expireDays } } ), submitApproval: (id: string, approverIds: string[]) => request.post(`/transport/order/${id}/submit-approval`, { approverIds }), scanFeedback: (data: { transportNo: string verifyCode?: string feedbackQty?: number feedbackImages?: string[] feedbackRemark?: string }) => request.post('/transport/order/scan/feedback', data), getApprovals: (id: string) => request.get(`/transport/order/${id}/approvals`) } // ============================================================================= // 运费结算 API // ============================================================================= export interface UnsettledPlanItem { id: string planNo: string planDate: string planType: string customerName?: string supplierName?: string loadCustomers?: string[] deliveryCustomers?: string[] licensePlate?: string transportCompany?: string freightAmount: number freightType?: string transportQty?: number freightPrice?: number hasInvoice?: number freightRemark?: string archiveTime?: string transportId: string transportNo?: string driverName?: string transportMedium?: string startPoint?: string endPoint?: string } export interface UnsettledPlanQuery { q?: string carrierId?: string planNo?: string planType?: string planTypeIn?: string licensePlate?: string page?: number pageSize?: number } export interface UnsettledPlanListResponse { total: number list: UnsettledPlanItem[] } export interface SettlementPlanSummary { id: string planNo: string planDate?: string licensePlate?: string freightAmount?: number freightRemark?: string } export interface SettlementApprovalFlowItem { id: string step: number stepName?: string approverId: string approverName?: string approvalType?: string approvalResult: string approvalOpinion?: string approvalTime?: string isCurrent: number } export interface RemittanceVoucher { id: string settlementId: string remittanceDate?: string remittanceAmount?: number voucherUrls?: string remark?: string createTime?: string } export interface FreightSettlementBrief { id: string settlementNo: string invoiceAmount?: number invoiceNo?: string invoiceDate?: string invoiceAttachmentUrls?: string settlementStatus: string } export interface FreightSettlement { id: string settlementNo: string totalFreight: number invoiceAmount?: number invoiceNo?: string invoiceDate?: string invoiceAttachmentUrls?: string settlementStatus: string remark?: string createBy?: string createTime: string updateTime?: string planCount?: number licensePlateSummary?: string currentApproverName?: string canApprove?: boolean canWithdraw?: boolean currentApprovalId?: string approvalFinishedTime?: string disburseTime?: string disburseAmount?: number submitterName?: string transports?: Transport[] plans?: SettlementPlanSummary[] approvalFlow?: SettlementApprovalFlowItem[] approvalHistory?: SettlementApprovalFlowItem[] vouchers?: RemittanceVoucher[] } export interface FreightSettlementQuery { q?: string carrierId?: string settlementNo?: string settlementStatus?: string settlementStatusIn?: string tab?: 'bills' | 'approval_pending' | 'approval_done' | 'disburse' | 'settled' page?: number pageSize?: number } export interface FreightSettlementListResponse { total: number list: FreightSettlement[] } export interface FreightSettlementSubmitBody { approvalType: 'sequential' | 'parallel' | 'or' approverIds: string[] remark?: string } export interface FreightSettlementApprovalBody { action: 'approve' | 'reject' comment?: string } export interface FreightSettlementDisburseBody { remittanceDate: string remittanceAmount: number voucherUrls: string[] remark?: string } export interface FreightSettlementUpdateBody { invoiceNo?: string invoiceDate?: string invoiceAmount?: number invoiceAttachmentUrls?: string remark?: string transportIds?: string[] } export const freightApi = { listUnsettledPlans: (params?: UnsettledPlanQuery) => request.get('/transport/freight/unsettled-plans', { params }), exportUnsettledPlans: (data: Record) => request.post('/transport/freight/unsettled-plans/export', data, { responseType: 'blob' }), getByTransport: (transportId: string) => request.get(`/transport/freight/by-transport/${transportId}`), list: (params?: FreightSettlementQuery) => request.get('/transport/freight/', { params }), export: (data: Record) => request.post('/transport/freight/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/freight/${id}`), create: (data: { planIds?: string[]; transportIds?: string[] }) => request.post('/transport/freight/', data), update: (id: string, data: FreightSettlementUpdateBody) => request.put(`/transport/freight/${id}`, data), cancel: (id: string, data?: { cancelReason?: string }) => request.post(`/transport/freight/${id}/cancel`, data || {}), delete: (id: string) => request.delete(`/transport/freight/${id}`), submitApproval: (id: string, data: FreightSettlementSubmitBody) => request.post(`/transport/freight/${id}/submit-approval`, data), processApproval: (approvalId: string, data: FreightSettlementApprovalBody) => request.post(`/transport/freight/approval/${approvalId}`, data), withdrawApproval: (id: string) => request.post(`/transport/freight/${id}/withdraw-approval`), getApprovalFlow: (id: string) => request.get(`/transport/freight/${id}/approval-flow`), disburse: (id: string, data: FreightSettlementDisburseBody) => request.post(`/transport/freight/${id}/disburse`, data), /** @deprecated 请使用 submitApproval */ submit: (id: string) => request.post(`/transport/freight/${id}/submit`), /** @deprecated 请使用 processApproval */ approve: (id: string) => request.post(`/transport/freight/${id}/approve`) } // ============================================================================= // 盘存统计 API // ============================================================================= export interface InventoryRecord { id: string productId: string brandId?: string brandName?: string productNo: string productName: string model?: string unit: string stockQty: number stockAmount: number lowStockWarning?: number highStockWarning?: number createTime: string } export interface InventoryTransaction { id: string productId: string brandId?: string brandName?: string productName: string transactionType: string planId?: string planNo?: string qty: number unitPrice: number amount: number stockAfter: number createTime: string } export interface InventoryWarning { id: string productId: string brandId?: string brandName?: string productName: string warningType: string currentQty: number thresholdValue: number status: string resolveTime?: string resolveRemark?: string createTime: string } export interface DocumentReminder { id: string certId?: string ownerType?: string ownerId?: string ownerLabel?: string driverId?: string driverName?: string documentType: string documentNo?: string expireDate: string daysRemaining: number status: string remark?: string createTime: string } export interface DocumentReminderStats { expiredPendingCount: number } export interface InventoryLedgerQuery { productName?: string page?: number pageSize?: number } export interface InventoryLedgerResponse { total: number list: InventoryRecord[] } export const inventoryApi = { ledger: (params?: InventoryLedgerQuery) => request.get('/transport/inventory/ledger', { params }), exportLedger: (data: Record) => request.post('/transport/inventory/ledger/export', data, { responseType: 'blob' }), transactions: (params?: any) => request.get<{ total: number; list: InventoryTransaction[] }>( '/transport/inventory/transactions', { params } ), summary: (params: { groupBy: string; dateFrom: string; dateTo: string; productId?: string }) => request.get<{ list: any[] }>('/transport/inventory/summary', { params }), warnings: (status?: string) => request.get('/transport/inventory/warnings', { params: { status } }), updateWarning: (id: string, data: { status: string; resolveRemark?: string }) => request.put(`/transport/inventory/warnings/${id}`, data), setWarningThreshold: (data: { productId: string brandId?: string brandName?: string lowStockWarning?: number highStockWarning?: number }) => request.post('/transport/inventory/warning-threshold', data), documentReminders: (params?: Record) => request.get('/transport/inventory/document-reminders', { params }), exportDocumentReminders: (data: Record) => request.post('/transport/inventory/document-reminders/export', data, { responseType: 'blob' }), documentReminderStats: () => request.get('/transport/inventory/document-reminders/stats'), updateDocumentReminder: (id: string, data: { status: string; remark?: string }) => request.put(`/transport/inventory/document-reminders/${id}`, data) } export interface PlMenuBadgeStats { counts: Record } export const menuBadgeApi = { stats: (codes?: string[]) => request.get('/transport/menu-badges/stats', { params: codes?.length ? { codes: codes.join(',') } : undefined }) } // ============================================================================= // ETC/加油充值 API // ============================================================================= export interface VehicleRechargeApproval { id: string applyId: string approvalRound: number nodeId?: string nodeName?: string approverId: string approverName: string approvalStatus: 'approved' | 'rejected' | 'disbursed' | string approvalOpinion?: string approvalTime: string } /** 审批进度/历史节点(对齐 BxApprovalFlowTabs) */ export interface VehicleRechargeApprovalFlowItem { id: string applyId: string nodeName: string nodeOrder: number approvalRound?: number | null approvalStatus: 'approved' | 'rejected' | 'disbursed' | 'pending' | string approvalOpinion?: string | null approverName?: string approvalTime?: string | null } export interface VehicleRechargeApply { id: string applyNo: string licensePlate: string boundFuelCardNo?: string | null rechargeType: 'etc' | 'fuel' | 'shanxia' applyDate: string odometerKm?: number | null attachmentUrls?: string applicantId: string applicantName?: string status: 'draft' | 'pending' | 'in_approval' | 'rejected' | 'disbursed' | 'cancelled' | string wfId?: string currentNode?: string currentNodeName?: string currentNodeActionType?: 'approve' | 'disburse' | string approvalRound?: number approvals?: VehicleRechargeApproval[] approvalFlow?: VehicleRechargeApprovalFlowItem[] approvalHistory?: VehicleRechargeApprovalFlowItem[] record?: VehicleRechargeRecord | null remark?: string createTime?: string updateTime?: string } export interface VehicleRechargeApplyListResponse { items: VehicleRechargeApply[] total: number page: number pageSize: number } export interface VehicleRechargeApplyOption { id: string applyNo: string licensePlate: string rechargeType: 'etc' | 'fuel' | 'shanxia' } export interface VehicleRechargeRecord { id: string rechargeOrderNo: string rechargeDate: string rechargeType: 'etc' | 'fuel' | 'shanxia' cardNo?: string gasStation?: string amount: number attachmentUrls?: string applyId?: string applyNo?: string licensePlate?: string remark?: string createBy?: string createByName?: string createTime?: string updateTime?: string } export interface VehicleRechargeRecordListResponse { items: VehicleRechargeRecord[] total: number page: number pageSize: number } export interface VehicleRechargeDisburseBoardItem { applyId: string applyNo: string licensePlate: string boundFuelCardNo?: string | null rechargeType: 'etc' | 'fuel' | 'shanxia' applyDate: string odometerKm?: number | null applicantName?: string applyStatus: 'draft' | 'pending' | 'in_approval' | 'rejected' | 'disbursed' | 'cancelled' | string applyRemark?: string recordId?: string rechargeOrderNo?: string rechargeDate?: string cardNo?: string gasStation?: string amount?: number | null recordCreateByName?: string recordRemark?: string recordAttachmentUrls?: string } export interface VehicleRechargeDisburseBoardListResponse { items: VehicleRechargeDisburseBoardItem[] total: number page: number pageSize: number } export const vehicleRechargeApplyApi = { list: (params?: Record) => request.get('/transport/vehicle-recharge-apply/', { params }), export: (data: Record) => request.post('/transport/vehicle-recharge-apply/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/vehicle-recharge-apply/${id}`), create: (data: Partial) => request.post('/transport/vehicle-recharge-apply/', data), update: (id: string, data: Partial) => request.put(`/transport/vehicle-recharge-apply/${id}`, data), submit: (id: string) => request.post(`/transport/vehicle-recharge-apply/${id}/submit`), cancel: (id: string, data?: { cancelReason?: string }) => request.post(`/transport/vehicle-recharge-apply/${id}/cancel`, data || {}), withdraw: (id: string) => request.post(`/transport/vehicle-recharge-apply/${id}/withdraw`), delete: (ids: string) => request.delete(`/transport/vehicle-recharge-apply/${ids}`), pendingOptions: () => request.get('/transport/vehicle-recharge-apply/pending-options') } export const vehicleRechargeApprovalApi = { pending: (params?: Record) => request.get('/transport/vehicle-recharge-approval/pending', { params }), done: (params?: Record) => request.get('/transport/vehicle-recharge-approval/done', { params }), approve: (id: string, data?: { opinion?: string }) => request.post( `/transport/vehicle-recharge-approval/${id}/approve`, data || {} ), reject: (id: string, data: { opinion?: string }) => request.post(`/transport/vehicle-recharge-approval/${id}/reject`, data), disburse: (id: string, data: Partial) => request.post(`/transport/vehicle-recharge-approval/${id}/disburse`, data) } export const vehicleRechargeRecordApi = { disburseBoard: (params?: Record) => request.get( '/transport/vehicle-recharge-record/disburse-board', { params } ), export: (data: Record) => request.post('/transport/vehicle-recharge-record/disburse-board/export', data, { responseType: 'blob' }), list: (params?: Record) => request.get('/transport/vehicle-recharge-record/', { params }), get: (id: string) => request.get(`/transport/vehicle-recharge-record/${id}`), create: (data: Partial) => request.post('/transport/vehicle-recharge-record/', data), update: (id: string, data: Partial) => request.put(`/transport/vehicle-recharge-record/${id}`, data), delete: (ids: string) => request.delete(`/transport/vehicle-recharge-record/${ids}`) } // ============================================================================= // 车辆维保 API // ============================================================================= export interface VehicleMaintenanceApply { id: string applyNo: string licensePlate: string applicantId: string applicantName?: string applyDate: string maintenanceType: string startDate: string expectedPickupDate?: string | null maintenanceContent?: string shopAddress?: string attachmentUrls?: string status: 'pending' | 'processed' | 'cancelled' remark?: string createTime?: string updateTime?: string } export interface VehicleMaintenanceApplyListResponse { items: VehicleMaintenanceApply[] total: number page: number pageSize: number } export interface VehicleMaintenanceApplyReminder { id: string applyNo: string licensePlate: string maintenanceType: string applyDate: string startDate: string expectedPickupDate?: string | null maintenanceContent?: string shopAddress?: string } export interface VehicleMaintenanceProcess { id: string processNo: string applyId?: string applyNo?: string licensePlate?: string amount?: number | null handlerId: string handlerName?: string processDate: string attachmentUrls?: string remark?: string createBy?: string createByName?: string createTime?: string updateTime?: string } export interface VehicleMaintenanceProcessBoardItem { applyId: string applyNo: string licensePlate: string maintenanceType: string applyDate: string startDate: string expectedPickupDate?: string | null maintenanceContent?: string shopAddress?: string applicantName?: string applyStatus: 'pending' | 'processed' | 'cancelled' applyRemark?: string processId?: string processNo?: string processDate?: string amount?: number | null handlerName?: string processRemark?: string } export interface VehicleMaintenanceProcessBoardListResponse { items: VehicleMaintenanceProcessBoardItem[] total: number page: number pageSize: number } export const vehicleMaintenanceApplyApi = { list: (params?: Record) => request.get('/transport/vehicle-maintenance-apply/', { params }), export: (data: Record) => request.post('/transport/vehicle-maintenance-apply/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/vehicle-maintenance-apply/${id}`), create: (data: Partial) => request.post('/transport/vehicle-maintenance-apply/', data), update: (id: string, data: Partial) => request.put(`/transport/vehicle-maintenance-apply/${id}`, data), cancel: (id: string, data?: { cancelReason?: string }) => request.post( `/transport/vehicle-maintenance-apply/${id}/cancel`, data || {} ), delete: (ids: string) => request.delete(`/transport/vehicle-maintenance-apply/${ids}`), reminders: (licensePlate: string, excludeApplyId?: string) => request.get( '/transport/vehicle-maintenance-apply/reminders', { params: { licensePlate, excludeApplyId } } ) } export const vehicleMaintenanceProcessApi = { processBoard: (params?: Record) => request.get( '/transport/vehicle-maintenance-process/process-board', { params } ), export: (data: Record) => request.post('/transport/vehicle-maintenance-process/process-board/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/vehicle-maintenance-process/${id}`), create: (data: Partial) => request.post('/transport/vehicle-maintenance-process/', data), update: (id: string, data: Partial) => request.put(`/transport/vehicle-maintenance-process/${id}`, data), delete: (ids: string) => request.delete(`/transport/vehicle-maintenance-process/${ids}`) } // ============================================================================= // 车辆尿素 API // ============================================================================= export interface VehicleUreaApply { id: string applyNo: string licensePlate: string applicantId: string applicantName?: string applyDate: string warehouseInDate: string unit: string quantity: number applyAmount: number attachmentUrls?: string status: 'pending' | 'disbursed' | 'cancelled' remark?: string createTime?: string updateTime?: string } export interface VehicleUreaApplyListResponse { items: VehicleUreaApply[] total: number page: number pageSize: number } export interface VehicleUreaDisburse { id: string disburseNo: string applyId?: string applyNo?: string licensePlate?: string amount: number handlerId: string handlerName?: string disburseDate: string attachmentUrls?: string remark?: string createBy?: string createByName?: string createTime?: string updateTime?: string } export interface VehicleUreaDisburseBoardItem { applyId: string applyNo: string licensePlate: string applyDate: string warehouseInDate: string unit: string quantity: number applyAmount: number applicantName?: string applyStatus: 'pending' | 'disbursed' | 'cancelled' applyRemark?: string disburseId?: string disburseNo?: string disburseDate?: string amount?: number | null handlerName?: string disburseRemark?: string } export interface VehicleUreaDisburseBoardListResponse { items: VehicleUreaDisburseBoardItem[] total: number page: number pageSize: number } export const vehicleUreaApplyApi = { list: (params?: Record) => request.get('/transport/vehicle-urea-apply/', { params }), export: (data: Record) => request.post('/transport/vehicle-urea-apply/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/vehicle-urea-apply/${id}`), create: (data: Partial) => request.post('/transport/vehicle-urea-apply/', data), update: (id: string, data: Partial) => request.put(`/transport/vehicle-urea-apply/${id}`, data), cancel: (id: string, data?: { cancelReason?: string }) => request.post(`/transport/vehicle-urea-apply/${id}/cancel`, data || {}), delete: (ids: string) => request.delete(`/transport/vehicle-urea-apply/${ids}`) } export const vehicleUreaDisburseApi = { disburseBoard: (params?: Record) => request.get( '/transport/vehicle-urea-disburse/disburse-board', { params } ), export: (data: Record) => request.post('/transport/vehicle-urea-disburse/disburse-board/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/vehicle-urea-disburse/${id}`), create: (data: Partial) => request.post('/transport/vehicle-urea-disburse/', data), update: (id: string, data: Partial) => request.put(`/transport/vehicle-urea-disburse/${id}`, data), delete: (ids: string) => request.delete(`/transport/vehicle-urea-disburse/${ids}`) } // ============================================================================= // 车辆加油登记 API // ============================================================================= export interface VehicleFuelRegister { id: string /** 租户内含软删的库内序号(create_time ASC) */ seqNo?: number registerNo: string fuelCategory: 'bailian' | 'gas_station' | string registerDate: string registrantId: string registrantName?: string licensePlate: string odometerKm?: number | null fuelLiters: number fuelTons?: number | null attachmentUrls?: string status: 'normal' | 'cancelled' | string remark?: string createTime?: string updateTime?: string } export interface VehicleFuelRegisterListResponse { items: VehicleFuelRegister[] total: number page: number pageSize: number } export type VehicleFuelMileageStatus = 'finalized' | 'estimated' | 'insufficient' | 'abnormal' export interface VehicleFuelPlateMonthItem { licensePlate: string trailerNo?: string | null defaultDriverName?: string | null vehicleStatus: string /** 当月 / 上月加油升数和行驶公里 */ liters: number | string prevLiters: number | string runKm: number | string | null prevRunKm: number | string | null monthMileageStatus?: VehicleFuelMileageStatus prevMonthMileageStatus?: VehicleFuelMileageStatus quarterMileageStatus?: VehicleFuelMileageStatus prevQuarterMileageStatus?: VehicleFuelMileageStatus /** 上季度百公里油耗,用于季度环比 */ prevQuarterRateL100?: number | string | null /** 当月百公里油耗 L/100km;里程不足时为 null */ monthRateL100?: number | string | null /** 当季百公里油耗 L/100km;里程不足时为 null */ quarterRateL100?: number | string | null /** 上个月百公里油耗 L/100km;里程不足时为 null */ prevMonthRateL100?: number | string | null /** 近三个月百公里油耗 L/100km;里程不足时为 null */ last3MonthsRateL100?: number | string | null } export interface VehicleFuelPlateMonthSummaryResponse { month: string quarter: string items: VehicleFuelPlateMonthItem[] } export const vehicleFuelRegisterApi = { list: (params?: Record) => request.get('/transport/vehicle-fuel-register/', { params }), export: (data: Record) => request.post('/transport/vehicle-fuel-register/export', data, { responseType: 'blob' }), get: (id: string) => request.get(`/transport/vehicle-fuel-register/${id}`), create: (data: Partial, params?: Record) => request.post('/transport/vehicle-fuel-register/', data, { params }), update: (id: string, data: Partial, params?: Record) => request.put(`/transport/vehicle-fuel-register/${id}`, data, { params }), cancel: ( id: string, data?: { cancelReason?: string }, params?: Record ) => request.post( `/transport/vehicle-fuel-register/${id}/cancel`, data || {}, { params } ), restore: (id: string, params?: Record) => request.post( `/transport/vehicle-fuel-register/${id}/restore`, {}, { params } ), plateMonthSummary: () => request.get( '/transport/vehicle-fuel-register/plate-month-summary' ) } /** 承运商列头可选值(名称/联系人/电话去重;可附带列表上下文) */ export function fetchCarrierColumnFacetValues( data: { field: string keyword?: string limit?: number } & Record ) { return request.post<{ list: Array<{ value: string; label: string; count: number }> }>( '/transport/carrier/column-facet-values', data, { showErrorMessage: false } as AxiosRequestConfig & { showErrorMessage?: boolean } ) } /** 车辆列头可选值(车牌号去重;可附带列表上下文) */ export function fetchVehicleColumnFacetValues( data: { field: string keyword?: string limit?: number } & Record ) { return request.post<{ list: Array<{ value: string; label: string; count: number }> }>( '/transport/vehicle/column-facet-values', data, { showErrorMessage: false } as AxiosRequestConfig & { showErrorMessage?: boolean } ) } /** 人员列头可选值(姓名/手机号去重;可附带列表上下文) */ export function fetchPersonnelColumnFacetValues( data: { field: string keyword?: string limit?: number } & Record ) { return request.post<{ list: Array<{ value: string; label: string; count: number }> }>( '/transport/personnel/column-facet-values', data, { showErrorMessage: false } as AxiosRequestConfig & { showErrorMessage?: boolean } ) } /** 运输地址列头可选值(地址名称/详细地址去重;可附带列表上下文) */ export function fetchTransportAddressColumnFacetValues( data: { field: string keyword?: string limit?: number } & Record ) { return request.post<{ list: Array<{ value: string; label: string; count: number }> }>( '/transport/address/column-facet-values', data, { showErrorMessage: false } as AxiosRequestConfig & { showErrorMessage?: boolean } ) } /** 运输计划列头可选值(编号/车牌/装送货客户/合同/产品/规格/包装/单位去重;可附带列表上下文过滤) */ export function fetchPlanColumnFacetValues( data: { field: string keyword?: string limit?: number } & Record ) { return request.post<{ list: Array<{ value: string; label: string; count: number }> }>( '/transport/plan/column-facet-values', data, { showErrorMessage: false } as AxiosRequestConfig & { showErrorMessage?: boolean } ) } /** 运费未结算列头可选值(计划号/车牌/承运商/备注去重;可附带列表上下文) */ export function fetchFreightColumnFacetValues( data: { field: string keyword?: string limit?: number } & Record ) { return request.post<{ list: Array<{ value: string; label: string; count: number }> }>( '/transport/freight/column-facet-values', data, { showErrorMessage: false } as AxiosRequestConfig & { showErrorMessage?: boolean } ) }