2386 lines
70 KiB
TypeScript
2386 lines
70 KiB
TypeScript
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 }
|
||
)
|
||
}
|