Files
shanxia2.0/web-admin/src/api/pl/index.ts
T

2386 lines
70 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<T> {
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<PlListResponse<Carrier>>('/transport/carrier', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/carrier/export', data, { responseType: 'blob' }),
get: (id: string) => request.get<Carrier>(`/transport/carrier/${id}`),
create: (data: Partial<Carrier>) => request.post<Carrier>('/transport/carrier', data),
update: (id: string, data: Partial<Carrier>) =>
request.put<Carrier>(`/transport/carrier/${id}`, data),
/** @deprecated 承运商审核已移除,后端接口已禁用 */
approve: (id: string) => request.post<Carrier>(`/transport/carrier/${id}/approve`),
reject: (id: string, data?: { remark?: string }) =>
request.post<Carrier>(`/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<PlListResponse<Vehicle>>('/transport/vehicle', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/vehicle/export', data, { responseType: 'blob' }),
get: (id: string) => request.get<VehicleDetail>(`/transport/vehicle/${id}`),
create: (data: VehiclePayload) => request.post<VehicleDetail>('/transport/vehicle', data),
update: (id: string, data: Partial<VehiclePayload>) =>
request.put<VehicleDetail>(`/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<PlListResponse<Personnel>>('/transport/personnel', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/personnel/export', data, { responseType: 'blob' }),
get: (id: string) => request.get<PersonnelDetail>(`/transport/personnel/${id}`),
create: (data: PersonnelPayload) => request.post<PersonnelDetail>('/transport/personnel', data),
update: (id: string, data: Partial<PersonnelPayload>) =>
request.put<PersonnelDetail>(`/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<PersonnelCertOcrResult>('/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<PasteCommitResult>('/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<PlListResponse<TransportAddress>>('/transport/address', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/address/export', data, { responseType: 'blob' }),
listAll: (params?: { q?: string }) =>
/** 选择器:仅按地址名称、详细地址模糊检索(不含联系人/电话) */
request.get<PlListResponse<TransportAddress>>('/transport/address/all', { params }),
get: (id: string) => request.get<TransportAddress>(`/transport/address/${id}`),
create: (data: TransportAddressPayload) =>
request.post<TransportAddress>('/transport/address', data),
update: (id: string, data: Partial<TransportAddressPayload>) =>
request.put<TransportAddress>(`/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<CustomerStopPref | null>('/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<Driver>): PersonnelPayload {
return {
personnelName: data.driverName || '',
phoneNo: data.phoneNo,
idCardNo: data.idCardNo,
status: '1',
remark: data.remark
}
}
export const driverApi = {
export: (data: Record<string, any>) =>
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<Personnel>(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<Driver>) => {
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<Driver>) => {
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<Omit<PlanCreateFullPayload, 'contractId'>>
/** 展示计划保存返回的告警(数量不平衡等) */
export function showPlanSaveWarnings(plan?: Pick<Plan, 'warnings'> | null) {
const warnings = plan?.warnings
if (!warnings?.length) return
const seen = new Set<string>()
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<string, any>) =>
request.get<PlanListResponse>('/transport/plan/', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/plan/export', data, {
responseType: 'blob',
// 归档 ZIP 需拉取附件,易超过全局 60s
timeout: 3600000
}),
get: (id: string, includeMobileProductMeta = false) =>
includeMobileProductMeta
? request.get<Plan>(`/transport/plan/${id}`, { params: { includeMobileProductMeta: true } })
: request.get<Plan>(`/transport/plan/${id}`),
countByContract: (contractIds: string[]) =>
request.get<{ counts: Record<string, number> }>('/transport/plan/count-by-contract', {
params: { contractIds: contractIds.join(',') }
}),
/** 表单可安排数量 / 问号弹窗 */
getContractProductArrangeQty: (params: {
contractId: string
productId?: string
packageSpec?: string
excludePlanId?: string
contractProductId?: string
}) =>
request.get<ContractProductArrangeQtyDetail>(
'/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<Plan>) => request.post<Plan>('/transport/plan/', data),
createFull: (data: PlanCreateFullPayload) =>
request.post<Plan>('/transport/plan/create-full', data),
update: (id: string, data: PlanUpdatePayload) => request.put<Plan>(`/transport/plan/${id}`, data),
updateProductCerts: (
id: string,
products: Array<{
id: string
purchaseCertUrl?: string
transportCertUrl?: string
legalUseCertUrl?: string
}>
) => request.patch<Plan>(`/transport/plan/${id}/product-certs`, { products }),
setHazardCertReady: (id: string, ready: boolean) =>
request.patch<Plan>(`/transport/plan/${id}/hazard-cert-ready`, { ready }),
setErpEntered: (id: string, entered: boolean) =>
request.patch<Plan>(`/transport/plan/${id}/erp-entered`, { entered }),
cancel: (id: string, data?: { cancelReason?: string }) =>
request.post<Plan>(`/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<Plan>(`/transport/plan/${id}/force-void`, data),
waitNotice: (id: string) => request.post<Plan>(`/transport/plan/${id}/wait-notice`),
cancelWaitNotice: (id: string) => request.post<Plan>(`/transport/plan/${id}/cancel-wait-notice`),
forceFollowStatus: (id: string, followStatus: string) =>
request.post<Plan>(`/transport/plan/${id}/force-follow-status`, { followStatus }),
getArrangeInfo: (id: string) =>
request.get<PlanArrangeInfo>(`/transport/plan/${id}/arrange-info`),
archive: (id: string, data?: PlanArchiveBody) =>
request.post<Plan>(`/transport/plan/${id}/archive`, data),
archiveDraft: (id: string, data: PlanArchiveBody) =>
request.post<Plan>(`/transport/plan/${id}/archive-draft`, data),
updateDriverFeedback: (id: string, data: PlanDriverFeedbackUpdateBody) =>
request.patch<Plan>(`/transport/plan/${id}/driver-feedback`, data),
updateSiteFeedback: (id: string, linkId: string, data: PlanSiteFeedbackUpdateBody) =>
request.patch<Plan>(`/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<Plan>('/transport/plan/create-from-contract', data),
getInboundSlipTemplate: (id: string) =>
request.get<string>(`/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<string>(`/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<string>(`/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<string>(`/transport/plan/${id}/subpack-slip-no`),
listContractGaps: (params?: PlanContractGapQuery | Record<string, any>) =>
request.get<PlListResponse<PlanContractGapItem>>('/transport/plan/contract-gaps', { params }),
exportContractGaps: (data: Record<string, any>) =>
request.post('/transport/plan/contract-gaps/export', data, { responseType: 'blob' }),
updateSideContracts: (id: string, side: 'supplier' | 'demander', contracts: PlanContractLink[]) =>
request.put<PlanContractGapItem>(`/transport/plan/${id}/contracts/${side}`, { contracts }),
updateStopContracts: (
id: string,
data: { stopType: 'load' | 'delivery'; stopId: string; contracts: PlanContractLink[] }
) => request.put<PlanContractGapItem>(`/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<string, any>) =>
request.get<PlIoOrderListResponse>('/transport/inbound/', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/inbound/export', data, { responseType: 'blob' }),
get: (id: string) => request.get<PlIoOrder>(`/transport/inbound/${id}`),
getByPlan: (planId: string) =>
request.get<PlIoOrder | null>(`/transport/inbound/by-plan/${planId}`),
patch: (id: string, data: Record<string, unknown>) =>
request.patch<PlIoOrder>(`/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<string, any>) =>
request.get<PlIoOrderListResponse>('/transport/outbound/', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/outbound/export', data, { responseType: 'blob' }),
get: (id: string) => request.get<PlIoOrder>(`/transport/outbound/${id}`),
getByPlan: (planId: string) =>
request.get<PlIoOrder | null>(`/transport/outbound/by-plan/${planId}`),
patch: (id: string, data: Record<string, unknown>) =>
request.patch<PlIoOrder>(`/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<TransportListResponse>('/transport/order/', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/order/export', data, { responseType: 'blob' }),
get: (id: string) => request.get<Transport>(`/transport/order/${id}`),
create: (data: Partial<Transport> & { planIds?: string[] }) =>
request.post<Transport>('/transport/order/', data),
arrange: (planId: string, data: TransportArrange) =>
request.post<TransportArrangeResult>(`/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>(`/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<any[]>(`/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<UnsettledPlanListResponse>('/transport/freight/unsettled-plans', { params }),
exportUnsettledPlans: (data: Record<string, any>) =>
request.post('/transport/freight/unsettled-plans/export', data, { responseType: 'blob' }),
getByTransport: (transportId: string) =>
request.get<FreightSettlementBrief | null>(`/transport/freight/by-transport/${transportId}`),
list: (params?: FreightSettlementQuery) =>
request.get<FreightSettlementListResponse>('/transport/freight/', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/freight/export', data, { responseType: 'blob' }),
get: (id: string) => request.get<FreightSettlement>(`/transport/freight/${id}`),
create: (data: { planIds?: string[]; transportIds?: string[] }) =>
request.post<FreightSettlement>('/transport/freight/', data),
update: (id: string, data: FreightSettlementUpdateBody) =>
request.put<FreightSettlement>(`/transport/freight/${id}`, data),
cancel: (id: string, data?: { cancelReason?: string }) =>
request.post<FreightSettlement>(`/transport/freight/${id}/cancel`, data || {}),
delete: (id: string) => request.delete(`/transport/freight/${id}`),
submitApproval: (id: string, data: FreightSettlementSubmitBody) =>
request.post<FreightSettlement>(`/transport/freight/${id}/submit-approval`, data),
processApproval: (approvalId: string, data: FreightSettlementApprovalBody) =>
request.post<FreightSettlement>(`/transport/freight/approval/${approvalId}`, data),
withdrawApproval: (id: string) =>
request.post<FreightSettlement>(`/transport/freight/${id}/withdraw-approval`),
getApprovalFlow: (id: string) =>
request.get<SettlementApprovalFlowItem[]>(`/transport/freight/${id}/approval-flow`),
disburse: (id: string, data: FreightSettlementDisburseBody) =>
request.post<FreightSettlement>(`/transport/freight/${id}/disburse`, data),
/** @deprecated 请使用 submitApproval */
submit: (id: string) => request.post<FreightSettlement>(`/transport/freight/${id}/submit`),
/** @deprecated 请使用 processApproval */
approve: (id: string) => request.post<FreightSettlement>(`/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<InventoryLedgerResponse>('/transport/inventory/ledger', { params }),
exportLedger: (data: Record<string, any>) =>
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<InventoryWarning[]>('/transport/inventory/warnings', { params: { status } }),
updateWarning: (id: string, data: { status: string; resolveRemark?: string }) =>
request.put<InventoryWarning>(`/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<string, unknown>) =>
request.get<DocumentReminder[]>('/transport/inventory/document-reminders', {
params
}),
exportDocumentReminders: (data: Record<string, any>) =>
request.post('/transport/inventory/document-reminders/export', data, { responseType: 'blob' }),
documentReminderStats: () =>
request.get<DocumentReminderStats>('/transport/inventory/document-reminders/stats'),
updateDocumentReminder: (id: string, data: { status: string; remark?: string }) =>
request.put<DocumentReminder>(`/transport/inventory/document-reminders/${id}`, data)
}
export interface PlMenuBadgeStats {
counts: Record<string, number>
}
export const menuBadgeApi = {
stats: (codes?: string[]) =>
request.get<PlMenuBadgeStats>('/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<string, unknown>) =>
request.get<VehicleRechargeApplyListResponse>('/transport/vehicle-recharge-apply/', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/vehicle-recharge-apply/export', data, { responseType: 'blob' }),
get: (id: string) => request.get<VehicleRechargeApply>(`/transport/vehicle-recharge-apply/${id}`),
create: (data: Partial<VehicleRechargeApply>) =>
request.post<VehicleRechargeApply>('/transport/vehicle-recharge-apply/', data),
update: (id: string, data: Partial<VehicleRechargeApply>) =>
request.put<VehicleRechargeApply>(`/transport/vehicle-recharge-apply/${id}`, data),
submit: (id: string) =>
request.post<VehicleRechargeApply>(`/transport/vehicle-recharge-apply/${id}/submit`),
cancel: (id: string, data?: { cancelReason?: string }) =>
request.post<VehicleRechargeApply>(`/transport/vehicle-recharge-apply/${id}/cancel`, data || {}),
withdraw: (id: string) =>
request.post<VehicleRechargeApply>(`/transport/vehicle-recharge-apply/${id}/withdraw`),
delete: (ids: string) => request.delete(`/transport/vehicle-recharge-apply/${ids}`),
pendingOptions: () =>
request.get<VehicleRechargeApplyOption[]>('/transport/vehicle-recharge-apply/pending-options')
}
export const vehicleRechargeApprovalApi = {
pending: (params?: Record<string, unknown>) =>
request.get<VehicleRechargeApplyListResponse>('/transport/vehicle-recharge-approval/pending', {
params
}),
done: (params?: Record<string, unknown>) =>
request.get<VehicleRechargeApplyListResponse>('/transport/vehicle-recharge-approval/done', {
params
}),
approve: (id: string, data?: { opinion?: string }) =>
request.post<VehicleRechargeApply>(
`/transport/vehicle-recharge-approval/${id}/approve`,
data || {}
),
reject: (id: string, data: { opinion?: string }) =>
request.post<VehicleRechargeApply>(`/transport/vehicle-recharge-approval/${id}/reject`, data),
disburse: (id: string, data: Partial<VehicleRechargeRecord>) =>
request.post<VehicleRechargeApply>(`/transport/vehicle-recharge-approval/${id}/disburse`, data)
}
export const vehicleRechargeRecordApi = {
disburseBoard: (params?: Record<string, unknown>) =>
request.get<VehicleRechargeDisburseBoardListResponse>(
'/transport/vehicle-recharge-record/disburse-board',
{ params }
),
export: (data: Record<string, any>) =>
request.post('/transport/vehicle-recharge-record/disburse-board/export', data, {
responseType: 'blob'
}),
list: (params?: Record<string, unknown>) =>
request.get<VehicleRechargeRecordListResponse>('/transport/vehicle-recharge-record/', {
params
}),
get: (id: string) =>
request.get<VehicleRechargeRecord>(`/transport/vehicle-recharge-record/${id}`),
create: (data: Partial<VehicleRechargeRecord>) =>
request.post<VehicleRechargeRecord>('/transport/vehicle-recharge-record/', data),
update: (id: string, data: Partial<VehicleRechargeRecord>) =>
request.put<VehicleRechargeRecord>(`/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<string, unknown>) =>
request.get<VehicleMaintenanceApplyListResponse>('/transport/vehicle-maintenance-apply/', {
params
}),
export: (data: Record<string, any>) =>
request.post('/transport/vehicle-maintenance-apply/export', data, { responseType: 'blob' }),
get: (id: string) =>
request.get<VehicleMaintenanceApply>(`/transport/vehicle-maintenance-apply/${id}`),
create: (data: Partial<VehicleMaintenanceApply>) =>
request.post<VehicleMaintenanceApply>('/transport/vehicle-maintenance-apply/', data),
update: (id: string, data: Partial<VehicleMaintenanceApply>) =>
request.put<VehicleMaintenanceApply>(`/transport/vehicle-maintenance-apply/${id}`, data),
cancel: (id: string, data?: { cancelReason?: string }) =>
request.post<VehicleMaintenanceApply>(
`/transport/vehicle-maintenance-apply/${id}/cancel`,
data || {}
),
delete: (ids: string) => request.delete(`/transport/vehicle-maintenance-apply/${ids}`),
reminders: (licensePlate: string, excludeApplyId?: string) =>
request.get<VehicleMaintenanceApplyReminder[]>(
'/transport/vehicle-maintenance-apply/reminders',
{
params: { licensePlate, excludeApplyId }
}
)
}
export const vehicleMaintenanceProcessApi = {
processBoard: (params?: Record<string, unknown>) =>
request.get<VehicleMaintenanceProcessBoardListResponse>(
'/transport/vehicle-maintenance-process/process-board',
{ params }
),
export: (data: Record<string, any>) =>
request.post('/transport/vehicle-maintenance-process/process-board/export', data, {
responseType: 'blob'
}),
get: (id: string) =>
request.get<VehicleMaintenanceProcess>(`/transport/vehicle-maintenance-process/${id}`),
create: (data: Partial<VehicleMaintenanceProcess>) =>
request.post<VehicleMaintenanceProcess>('/transport/vehicle-maintenance-process/', data),
update: (id: string, data: Partial<VehicleMaintenanceProcess>) =>
request.put<VehicleMaintenanceProcess>(`/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<string, unknown>) =>
request.get<VehicleUreaApplyListResponse>('/transport/vehicle-urea-apply/', { params }),
export: (data: Record<string, any>) =>
request.post('/transport/vehicle-urea-apply/export', data, { responseType: 'blob' }),
get: (id: string) => request.get<VehicleUreaApply>(`/transport/vehicle-urea-apply/${id}`),
create: (data: Partial<VehicleUreaApply>) =>
request.post<VehicleUreaApply>('/transport/vehicle-urea-apply/', data),
update: (id: string, data: Partial<VehicleUreaApply>) =>
request.put<VehicleUreaApply>(`/transport/vehicle-urea-apply/${id}`, data),
cancel: (id: string, data?: { cancelReason?: string }) =>
request.post<VehicleUreaApply>(`/transport/vehicle-urea-apply/${id}/cancel`, data || {}),
delete: (ids: string) => request.delete(`/transport/vehicle-urea-apply/${ids}`)
}
export const vehicleUreaDisburseApi = {
disburseBoard: (params?: Record<string, unknown>) =>
request.get<VehicleUreaDisburseBoardListResponse>(
'/transport/vehicle-urea-disburse/disburse-board',
{ params }
),
export: (data: Record<string, any>) =>
request.post('/transport/vehicle-urea-disburse/disburse-board/export', data, {
responseType: 'blob'
}),
get: (id: string) => request.get<VehicleUreaDisburse>(`/transport/vehicle-urea-disburse/${id}`),
create: (data: Partial<VehicleUreaDisburse>) =>
request.post<VehicleUreaDisburse>('/transport/vehicle-urea-disburse/', data),
update: (id: string, data: Partial<VehicleUreaDisburse>) =>
request.put<VehicleUreaDisburse>(`/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<string, unknown>) =>
request.get<VehicleFuelRegisterListResponse>('/transport/vehicle-fuel-register/', {
params
}),
export: (data: Record<string, any>) =>
request.post('/transport/vehicle-fuel-register/export', data, { responseType: 'blob' }),
get: (id: string) =>
request.get<VehicleFuelRegister>(`/transport/vehicle-fuel-register/${id}`),
create: (data: Partial<VehicleFuelRegister>, params?: Record<string, unknown>) =>
request.post<VehicleFuelRegister>('/transport/vehicle-fuel-register/', data, { params }),
update: (id: string, data: Partial<VehicleFuelRegister>, params?: Record<string, unknown>) =>
request.put<VehicleFuelRegister>(`/transport/vehicle-fuel-register/${id}`, data, {
params
}),
cancel: (
id: string,
data?: { cancelReason?: string },
params?: Record<string, unknown>
) =>
request.post<VehicleFuelRegister>(
`/transport/vehicle-fuel-register/${id}/cancel`,
data || {},
{ params }
),
restore: (id: string, params?: Record<string, unknown>) =>
request.post<VehicleFuelRegister>(
`/transport/vehicle-fuel-register/${id}/restore`,
{},
{ params }
),
plateMonthSummary: () =>
request.get<VehicleFuelPlateMonthSummaryResponse>(
'/transport/vehicle-fuel-register/plate-month-summary'
)
}
/** 承运商列头可选值(名称/联系人/电话去重;可附带列表上下文) */
export function fetchCarrierColumnFacetValues(
data: {
field: string
keyword?: string
limit?: number
} & Record<string, unknown>
) {
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<string, unknown>
) {
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<string, unknown>
) {
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<string, unknown>
) {
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<string, unknown>
) {
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<string, unknown>
) {
return request.post<{ list: Array<{ value: string; label: string; count: number }> }>(
'/transport/freight/column-facet-values',
data,
{ showErrorMessage: false } as AxiosRequestConfig & { showErrorMessage?: boolean }
)
}