Files
shanxia2.0/web-admin/src/api/pl/index.ts
T
zlwuandCursor c60ebfc082 feat(pl): 三方粘贴识别批量提交接口
新增 paste-recognize/commit,事务内处理车/人冲突策略与关联写入。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-25 12:52:09 +08:00

1514 lines
43 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.
/**
* 运输计划管理 API
*/
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 }),
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
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
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 }),
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
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
certList?: CertRow[]
}
export interface PersonnelQuery {
q?: string
status?: string
personnelAffiliation?: string
page?: number
pageSize?: number
}
export const personnelApi = {
list: (params?: PersonnelQuery) =>
request.get<PlListResponse<Personnel>>('/transport/personnel', { params }),
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
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
status?: string
remark?: string
}
export const transportAddressApi = {
list: (params?: TransportAddressQuery) =>
request.get<PlListResponse<TransportAddress>>('/transport/address', { params }),
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}`)
}
// 司机(兼容旧 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 = {
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 PlanProduct {
id: string
planId: string
productId: string
productNo: string
productName: string
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 {
id: string
planNo: string
planDate: string
belongsCompany: string
planType: string
followStatus: string
transportStatus: string
archiveStatus: string
completeStatus: string
contractId?: 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
pickupAddress?: string
pickupLng?: number | null
pickupLat?: number | null
pickupTime?: string
pickupNotes?: string
destinationAddress?: string
destinationLng?: number | null
destinationLat?: number | null
deliveryTime?: string
destinationNotes?: string
vehicleId?: string
licensePlate?: string
trailerNo?: string
driverId?: string
driverName?: string
personnelSummary?: string
resourceSnapshot?: TransportResourceSnapshot
transportId?: string
transportNo?: string
totalAmount: number
freightAmount: number
notifyMessage?: string
followUserId?: string
followUserName?: string
hazardCertReady?: number
hazardCertReadyBy?: string
hazardCertReadyTime?: string
remark?: string
createBy?: string
createTime: string
products?: PlanProduct[]
}
export interface PlanQuery {
q?: string
planNo?: string
planType?: string
followStatus?: string
transportStatus?: string
archiveStatus?: string
customerName?: string
productName?: string
planDateFrom?: string
planDateTo?: string
page?: number
pageSize?: number
}
export interface PlanListResponse {
total: number
list: Plan[]
}
export interface PlanTransportCreateBlock {
vehicleId: string
driverId?: string
carrierId?: string
distanceKm?: number
freightType: string
transportQty?: number
freightPrice?: number
freightAmount?: number
hasInvoice?: number
remark?: string
}
export interface PlanCreateFullPayload {
contractId: string
planDate: string
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
pickupAddress?: string
pickupLng?: number | null
pickupLat?: number | null
pickupTime?: string
pickupNotes?: string
destinationAddress?: string
destinationLng?: number | null
destinationLat?: number | null
deliveryTime?: string
destinationNotes?: string
followUserId?: string
followUserName?: string
remark?: string
products: Array<{
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 interface PlanArchiveProductItem {
id: string
actualQty: number
}
export interface PlanArchiveFreightBlock {
hasFreight: number
distanceKm?: number
freightType?: string
freightPrice?: number
freightAmount?: number
transportQty?: number
hasInvoice?: number
}
export interface PlanArchiveBody {
products: PlanArchiveProductItem[]
freight?: PlanArchiveFreightBlock
}
export interface PlanDriverFeedbackUpdateBody {
feedbackQty?: number | null
feedbackRemark?: string | null
feedbackImages?: string[]
feedbackTime?: string | null
}
export interface PlanSiteFeedbackUpdateBody {
siteFeedbackRemark?: string | null
siteFeedbackImages?: string[]
siteFeedbackTime?: string | null
}
export const planApi = {
list: (params?: PlanQuery) => request.get<PlanListResponse>('/transport/plan/', { params }),
get: (id: string) => request.get<Plan>(`/transport/plan/${id}`),
countByContract: (contractIds: string[]) =>
request.get<{ counts: Record<string, number> }>('/transport/plan/count-by-contract', {
params: { contractIds: contractIds.join(',') }
}),
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 }),
cancel: (id: string) => request.post<Plan>(`/transport/plan/${id}/cancel`),
getArrangeInfo: (id: string) => request.get<PlanArrangeInfo>(`/transport/plan/${id}/arrange-info`),
archive: (id: string, data?: PlanArchiveBody) =>
request.post<Plan>(`/transport/plan/${id}/archive`, 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; 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 }),
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`)
}
// =============================================================================
// 运输管理 API
// =============================================================================
export interface Transport {
id: string
transportNo: string
vehicleId?: string
licensePlate?: string
trailerNo?: string
driverId?: string
driverName?: string
driverPhone?: 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
remark?: string
createTime: string
}
export interface TransportArrange {
vehicleId: string
driverId: string
carrierId?: string
transportMedium?: string
pickupAddress?: string
pickupLng?: number | null
pickupLat?: number | null
pickupTime?: string
pickupNotes?: string
destinationAddress?: string
destinationLng?: number | null
destinationLat?: number | null
deliveryTime?: string
destinationNotes?: string
distanceKm?: number
unit?: string
freightType: string
freightPrice?: number
freightAmount?: number
hasInvoice?: number
remark?: string
generateDibangPlan?: boolean
}
export interface TransportInventoryLink {
planProductId: string
productName: string
dbPurchasePlanId: string
dbInventoryId: string
batchNo: string
planNo: 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 {
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
}
export interface PlIoOrderItem {
id: string
orderId: string
planProductId: string
productId?: string
productNo?: string
productName?: string
specification?: string
unit?: string
unitLabel?: string
planQty?: number
actualQty?: number
qtySource?: string
packageSpec?: 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
pickupNotes?: string
destinationName?: string
destinationAddress?: string
destinationLng?: number | null
destinationLat?: number | null
deliveryTime?: 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
page?: number
pageSize?: number
}
export interface PlIoOrderListResponse {
total: number
list: PlIoOrder[]
}
export const inboundOrderApi = {
list: (params?: PlIoOrderQuery) =>
request.get<PlIoOrderListResponse>('/transport/inbound/', { params }),
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) =>
request.get<PlIoOrderListResponse>('/transport/outbound/', { params }),
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 }),
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
licensePlate?: string
transportCompany?: string
freightAmount: number
archiveTime?: string
transportId: string
transportNo?: string
driverName?: string
transportMedium?: string
startPoint?: string
endPoint?: string
}
export interface UnsettledPlanQuery {
q?: string
carrierId?: string
planType?: 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
}
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
settlementStatus?: 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 }),
getByTransport: (transportId: string) =>
request.get<FreightSettlementBrief | null>(`/transport/freight/by-transport/${transportId}`),
list: (params?: FreightSettlementQuery) =>
request.get<FreightSettlementListResponse>('/transport/freight/', { params }),
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) => request.post<FreightSettlement>(`/transport/freight/${id}/cancel`),
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
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
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
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 }),
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; lowStockWarning?: number; highStockWarning?: number }) =>
request.post('/transport/inventory/warning-threshold', data),
documentReminders: (status?: string) =>
request.get<DocumentReminder[]>('/transport/inventory/document-reminders', { params: { status } }),
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 VehicleRechargeApply {
id: string
applyNo: string
licensePlate: string
rechargeType: 'etc' | 'fuel'
applyDate: string
odometerKm?: number | null
attachmentUrls?: string
applicantId: string
applicantName?: string
status: 'pending' | 'disbursed' | 'cancelled'
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'
}
export interface VehicleRechargeRecord {
id: string
rechargeOrderNo: string
rechargeDate: string
rechargeType: 'etc' | 'fuel'
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
rechargeType: 'etc' | 'fuel'
applyDate: string
odometerKm?: number | null
applicantName?: string
applyStatus: 'pending' | 'disbursed' | 'cancelled'
applyRemark?: string
recordId?: string
rechargeOrderNo?: string
rechargeDate?: string
cardNo?: string
gasStation?: string
amount?: number | null
recordCreateByName?: string
recordRemark?: 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 }),
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),
cancel: (id: string) =>
request.post<VehicleRechargeApply>(`/transport/vehicle-recharge-apply/${id}/cancel`),
delete: (ids: string) => request.delete(`/transport/vehicle-recharge-apply/${ids}`),
pendingOptions: () =>
request.get<VehicleRechargeApplyOption[]>('/transport/vehicle-recharge-apply/pending-options')
}
export const vehicleRechargeRecordApi = {
disburseBoard: (params?: Record<string, unknown>) =>
request.get<VehicleRechargeDisburseBoardListResponse>(
'/transport/vehicle-recharge-record/disburse-board',
{ params }
),
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
}),
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) =>
request.post<VehicleMaintenanceApply>(`/transport/vehicle-maintenance-apply/${id}/cancel`),
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 }
),
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 }),
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) =>
request.post<VehicleUreaApply>(`/transport/vehicle-urea-apply/${id}/cancel`),
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 }
),
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}`)
}