Files
shanxia2.0/web-admin/src/utils/pl-display.ts
T

815 lines
25 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.
export function getPlVehicleStatusTagType(status: string) {
const map: Record<string, string> = {
available: 'success',
maintaining: 'warning',
scrapped: 'danger'
}
return map[status] || 'info'
}
export function getPlVehicleStatusLabel(status: string) {
const map: Record<string, string> = {
available: '可用',
maintaining: '维修中',
scrapped: '报废'
}
return map[status] || status
}
export function getPlVehicleTypeLabel(type: string) {
return type === 'self' ? '自有' : type === 'carrier' ? '承运商' : type
}
export function getPlVehicleTypeTagType(type: string) {
return type === 'self' ? 'success' : 'warning'
}
type VehicleTrailerLookup = ReadonlyMap<string, string> | Record<string, string | undefined | null>
function resolveTrailerFromLookup(plate: string, lookup?: VehicleTrailerLookup) {
if (!lookup || !plate) return ''
if (lookup instanceof Map) return lookup.get(plate)?.trim() || ''
return lookup[plate]?.trim() || ''
}
/** 按车牌构建挂车号查找表(仅 licensePlate 的业务列表用) */
export function buildVehicleTrailerLookup(
vehicles: Array<{ licensePlate?: string | null; trailerNo?: string | null }> = []
) {
const map = new Map<string, string>()
for (const vehicle of vehicles) {
const plate = vehicle.licensePlate?.trim()
const trailer = vehicle.trailerNo?.trim()
if (plate && trailer) map.set(plate, trailer)
}
return map
}
/** 列表/摘要:车牌号挂车号(无分隔符) */
export function formatPlVehiclePlateDisplay(
licensePlate?: string | null,
trailerNo?: string | null,
trailerLookup?: VehicleTrailerLookup
) {
const plate = licensePlate?.trim() || ''
const trailer = trailerNo?.trim() || resolveTrailerFromLookup(plate, trailerLookup)
if (!plate && !trailer) return '—'
if (plate && trailer) return `${plate}${trailer}`
return plate || trailer
}
/** 下拉选项:主车号(挂车号);无挂车号时仅主车号 */
export function formatPlVehiclePlateOptionLabel(
licensePlate?: string | null,
trailerNo?: string | null
) {
const plate = licensePlate?.trim() || ''
const trailer = trailerNo?.trim() || ''
if (!plate && !trailer) return '—'
if (plate && trailer) return `${plate}(${trailer})`
return plate || trailer
}
export function getPlCarrierStatusLabel(status: string) {
return status === '1' ? '合作中' : '已终止'
}
export function getPlCarrierStatusTagType(status: string) {
return status === '1' ? 'success' : 'danger'
}
export function getPlCarrierAuditStatusLabel(status?: string) {
const map: Record<string, string> = {
pending: '待审核',
approved: '已通过',
rejected: '已驳回'
}
return status ? map[status] || status : '待审核'
}
export function getPlCarrierAuditStatusTagType(status?: string) {
const map: Record<string, string> = {
pending: 'warning',
approved: 'success',
rejected: 'danger'
}
return map[status || ''] || 'info'
}
export function getPlanProductSpec(product: { model?: string; packageSpec?: string }) {
return product.model || product.packageSpec || '—'
}
/** 列表「型号/规格」列:仅型号,不用包装规格兜底 */
export function getPlanProductModel(product: { model?: string | null }) {
return product.model || '—'
}
export function getPlanProductPackageSpec(product: { packageSpec?: string | null }) {
return product.packageSpec || '—'
}
export function getPlanProductQuantity(product: {
quantity?: number | string | null
actualQty?: number | string | null
inboundEstimateQty?: number | string | null
outboundEstimateQty?: number | string | null
}) {
const actual = product.actualQty
// 实际数量为空或为 0 时,展示合同/计划数量
if (actual !== undefined && actual !== null && actual !== '' && Number(actual) !== 0) {
return String(actual)
}
const qty = product.quantity ?? product.inboundEstimateQty ?? product.outboundEstimateQty
if (qty === undefined || qty === null || qty === '') return '—'
return String(qty)
}
export function getPlanProductPlannedQuantity(product: {
quantity?: number | string | null
}) {
const qty = product.quantity
if (qty === undefined || qty === null || qty === '') return '—'
return String(qty)
}
/** 列表「实际数量」列:仅实际数量,无回退 */
export function getPlanProductActualQuantity(product: {
actualQty?: number | string | null
}) {
const qty = product.actualQty
if (qty === undefined || qty === null || qty === '') return '—'
return String(qty)
}
/** 列表「入库数量」列 */
export function getPlanProductInboundQuantity(product: {
inboundQty?: number | string | null
}) {
const qty = product.inboundQty
if (qty === undefined || qty === null || qty === '') return '—'
return String(qty)
}
/** 列表「出库数量」列 */
export function getPlanProductOutboundQuantity(product: {
outboundQty?: number | string | null
}) {
const qty = product.outboundQty
if (qty === undefined || qty === null || qty === '') return '—'
return String(qty)
}
export function formatPlanProductListText(
product: {
productName?: string
model?: string
packageSpec?: string
unit?: string
quantity?: number | string | null
actualQty?: number | string | null
inboundEstimateQty?: number | string | null
outboundEstimateQty?: number | string | null
},
getUnitLabel?: (unit: string) => string,
options?: { usePlannedQuantity?: boolean }
) {
const name = product.productName || '—'
const spec = getPlanProductSpec(product)
const qty = options?.usePlannedQuantity
? getPlanProductPlannedQuantity(product)
: getPlanProductQuantity(product)
const unitRaw = product.unit || ''
const unit = unitRaw ? (getUnitLabel ? getUnitLabel(unitRaw) || unitRaw : unitRaw) : ''
const specPart = spec && spec !== '—' ? `(${spec})` : ''
let qtyUnit = '—'
if (qty !== '—' && unit) {
qtyUnit = `${qty}${unit}`
} else if (qty !== '—') {
qtyUnit = qty
} else if (unit) {
qtyUnit = unit
}
return `${name}${specPart},${qtyUnit}`
}
/** 入库单/出库单列表产品一行:名称 + 品牌/型号规格/单位/包装规格/送货方式 */
export function formatIoOrderProductListText(item: {
productName?: string | null
brandSummary?: string | null
brandName?: string | null
specification?: string | null
unit?: string | null
unitLabel?: string | null
packageSpec?: string | null
deliveryMethod?: string | null
}) {
const name = (item.productName || '').trim() || '—'
const bits: string[] = []
const brand = (item.brandSummary || item.brandName || '').trim()
if (brand) bits.push(`品牌 ${brand}`)
const spec = (item.specification || '').trim()
if (spec) bits.push(`型号/规格 ${spec}`)
const unit = (item.unitLabel || item.unit || '').trim()
if (unit) bits.push(`单位 ${unit}`)
const packageSpec = (item.packageSpec || '').trim()
if (packageSpec) bits.push(`包装规格 ${packageSpec}`)
const delivery = (item.deliveryMethod || '').trim()
if (delivery) {
const deliveryLabels: Record<string, string> = {
self_pick: '山峡自提',
one_ticket: '一票制',
customer_pick: '客户自提'
}
bits.push(`送货方式 ${deliveryLabels[delivery] || delivery}`)
}
return bits.length ? `${name},${bits.join(',')}` : name
}
/** 品牌数量摘要,如 `君正 16 / 红四方 12` */
export function formatBrandQtySummary(
rows?: Array<{ brandName?: string | null; qty?: number | string | null }> | null
): string {
if (!rows?.length) return ''
const parts = rows
.map(row => {
const qty = Number(row.qty ?? 0)
if (!Number.isFinite(qty) || qty <= 0) return ''
const name = (row.brandName || '').trim() || '未命名品牌'
return `${name} ${qty}`
})
.filter(Boolean)
return parts.join(' / ')
}
/** 现场入出库明细品牌摘要(优先 brandQtys,其次 brandSummary) */
export function resolveSiteBrandSummary(item: {
brandQtys?: Array<{ brandName?: string | null; qty?: number | string | null }> | null
brandSummary?: string | null
brandName?: string | null
}) {
const fromQtys = formatBrandQtySummary(item.brandQtys)
if (fromQtys) return fromQtys
const summary = (item.brandSummary || item.brandName || '').trim()
return summary || '—'
}
/**
* 出入库「品牌数量」列:优先现场 brandQtys;
* 其次归档拼出的「品牌 数量」摘要(排除纯品牌名)。
*/
export function resolveIoBrandQtyDisplay(item: {
brandQtys?: Array<{ brandName?: string | null; qty?: number | string | null }> | null
brandSummary?: string | null
brandName?: string | null
}) {
const fromQtys = formatBrandQtySummary(item.brandQtys)
if (fromQtys) return fromQtys
const summary = (item.brandSummary || '').trim()
if (!summary) return '—'
const brandName = (item.brandName || '').trim()
if (brandName && summary === brandName) return '—'
// 归档摘要形如「君正 12 / 红四方 13」
if (/\s\d/.test(summary)) return summary
return '—'
}
/** 计划品牌约束展示:指定品牌名 / 不限品牌 */
export function resolvePlanBrandDisplayLabel(options: {
brandMode?: string | null
brandName?: string | null
}) {
const mode = (options.brandMode || '').trim()
const name = (options.brandName || '').trim()
if (mode === 'required') return name || '指定品牌'
if (mode === 'any') return name || '不限品牌'
return name || ''
}
/** 司机/现场公开页产品一行展示(含别名、品牌、运输计划产品备注) */
export function formatPublicTransportProductLine(product: {
productName?: string
aliasName?: string | null
model?: string
packageSpec?: string
unit?: string
quantity?: number | string | null
remark?: string
brandMode?: string | null
brandName?: string | null
}) {
const name = product.productName?.trim() || '—'
const parts: string[] = []
const alias = product.aliasName?.trim()
if (alias) parts.push(`别名 ${alias}`)
const brand = resolvePlanBrandDisplayLabel(product)
if (brand) parts.push(`品牌 ${brand}`)
const model = product.model?.trim()
const packageSpec = product.packageSpec?.trim()
if (model) parts.push(model)
if (packageSpec) parts.push(packageSpec)
const qty = getPlanProductPlannedQuantity(product)
const unitRaw = product.unit || '吨'
parts.push(qty !== '—' ? `${qty} ${unitRaw}` : unitRaw)
const remark = product.remark?.trim()
parts.push(`备注:${remark || '—'}`)
return `${name}(${parts.join(',')})`
}
/** 运输计划归属公司选项 */
export const PL_PLAN_COMPANY_OPTIONS = [
{ value: 'shanxia', label: '安庆山峡' },
{ value: 'yundong', label: '安徽云栋' }
] as const
export function getPlPlanCompanyLabel(company?: string) {
const map: Record<string, string> = { shanxia: '安庆山峡', yundong: '安徽云栋' }
return company ? map[company] || company : '—'
}
export function getPlPlanTypeTagType(type?: string) {
const map: Record<string, string> = { direct: 'primary', inbound: 'success', outbound: 'warning' }
return map[type || ''] || 'info'
}
export function getPlPlanTypeLabel(type?: string) {
const map: Record<string, string> = { direct: '直销单', inbound: '入库单', outbound: '出库单' }
return type ? map[type] || type : '—'
}
/** 打印单据下拉项:入库单 / 送(提)货单 / 分装单 */
export type PlPrintSlipCommand = 'inbound' | 'delivery' | 'subpack'
/** 运输计划可打印的单据命令(不区分单据类型,由用户自行选择) */
export const PL_PRINT_SLIP_COMMANDS: PlPrintSlipCommand[] = ['inbound', 'delivery', 'subpack']
export function getPrintableSlipCommands(_planType?: string): PlPrintSlipCommand[] {
return PL_PRINT_SLIP_COMMANDS
}
export function canPrintSlipCommand(
_planType: string | undefined,
_command: PlPrintSlipCommand
) {
return true
}
export type PlPlanTypeTab = 'all' | 'inbound' | 'outbound' | 'direct'
/** 运输计划列表 Tab(单据类型前增加「待安排」「已安排」「草稿中」) */
export type PlPlanListTab = PlPlanTypeTab | 'pending_arrange' | 'arranged' | 'drafting'
/** 危化品证上传页 Tab(在单据类型后增加「未上传证书」) */
export type PlPlanCertTab = PlPlanTypeTab | 'cert_incomplete'
export const PL_PLAN_TYPE_TABS: { label: string; name: PlPlanTypeTab }[] = [
{ label: '全部', name: 'all' },
{ label: '入库单', name: 'inbound' },
{ label: '出库单', name: 'outbound' },
{ label: '直销单', name: 'direct' }
]
export const PL_PLAN_LIST_TABS: { label: string; name: PlPlanListTab }[] = [
{ label: '待安排', name: 'pending_arrange' },
{ label: '已安排', name: 'arranged' },
{ label: '草稿中', name: 'drafting' },
...PL_PLAN_TYPE_TABS
]
export const PL_PLAN_CERT_TABS: { label: string; name: PlPlanCertTab }[] = [
{ label: '未上传证书', name: 'cert_incomplete' },
...PL_PLAN_TYPE_TABS
]
export function isPlPlanFollowStatusListTab(
tab: PlPlanListTab | string
): tab is 'pending_arrange' | 'arranged' | 'drafting' {
return tab === 'pending_arrange' || tab === 'arranged' || tab === 'drafting'
}
export function plPlanTypeTabToFilter(tab: PlPlanTypeTab): string {
return tab === 'all' ? '' : tab
}
export function plPlanListTabToFilters(tab: PlPlanListTab): {
planType: string
followStatus?: string
} {
if (tab === 'pending_arrange') {
return { planType: '', followStatus: 'pending_arrange' }
}
if (tab === 'arranged') {
return { planType: '', followStatus: 'arranged' }
}
if (tab === 'drafting') {
return { planType: '', followStatus: 'drafting' }
}
return { planType: plPlanTypeTabToFilter(tab) }
}
/** 危化品证上传页:证书齐备筛选选项 */
export const PL_PLAN_HAZARD_CERT_READY_FILTER_OPTIONS = [
{ label: '未齐备', value: '0' },
{ label: '已齐备', value: '1' }
] as const
export function plPlanCertTabToFilters(tab: PlPlanCertTab): {
planType: string
certUploadStatus?: string
hazardCertReady?: string
} {
if (tab === 'cert_incomplete') {
return {
planType: '',
certUploadStatus: 'incomplete',
hazardCertReady: '0'
}
}
return { planType: plPlanTypeTabToFilter(tab), hazardCertReady: '' }
}
export function plPlanTypeFilterToTab(planType?: string): PlPlanTypeTab {
if (planType === 'inbound' || planType === 'outbound' || planType === 'direct') {
return planType
}
return 'all'
}
/** 运输计划跟进状态选项(筛选) */
export const PL_PLAN_FOLLOW_STATUS_OPTIONS = [
{ label: '草稿中', value: 'drafting' },
{ label: '等通知', value: 'wait_notice' },
{ label: '待安排', value: 'pending_arrange' },
{ label: '已安排', value: 'arranged' },
{ label: '已完成', value: 'completed' },
{ label: '已取消', value: 'cancelled' }
] as const
/** 运输计划/危化品证等列表:跟进状态默认勾选(不含已取消) */
export const PL_PLAN_DEFAULT_FOLLOW_STATUSES = PL_PLAN_FOLLOW_STATUS_OPTIONS.filter(
(opt) => opt.value !== 'cancelled'
).map((opt) => opt.value)
export function getPlPlanFollowStatusTagType(status?: string) {
const map: Record<string, string> = {
drafting: 'info',
wait_notice: 'warning',
pending_arrange: 'danger',
arranged: 'success',
completed: 'success',
cancelled: 'info',
// 旧值兼容(迁移前)
draft: 'danger',
processing: 'warning'
}
return map[status || ''] || 'info'
}
export function getPlPlanFollowStatusLabel(status?: string) {
const map: Record<string, string> = {
drafting: '草稿中',
wait_notice: '等通知',
pending_arrange: '待安排',
arranged: '已安排',
completed: '已完成',
cancelled: '已取消',
draft: '待安排',
processing: '进行中'
}
return status ? map[status] || status : '—'
}
export function getPlPlanArchiveStatusTagType(status?: string) {
if (status === 'archived') return 'info'
if (status === 'draft') return 'warning'
return 'danger'
}
export function getPlPlanArchiveStatusLabel(status?: string) {
if (status === 'archived') return '已归档'
if (status === 'draft') return '已暂存'
return '未归档'
}
export function getPlPlanCompleteStatusLabel(status?: string) {
return status === 'yes' ? '是' : '否'
}
export function getPlFreightTypeLabel(type?: string) {
const map: Record<string, string> = { by_weight: '按重量', by_trip: '按趟次' }
return type ? map[type] || type : '—'
}
export function getPlYesNoLabel(value?: number | boolean | null) {
return value === 1 || value === true ? '是' : '否'
}
export function getPlHasInvoiceLabelFromSettlement(settlement?: {
invoiceNo?: string | null
invoiceDate?: string | null
invoiceAmount?: number | null
invoiceAttachmentUrls?: string | null
} | null) {
if (!settlement) return '—'
const hasInvoice =
Boolean(settlement.invoiceNo?.trim()) ||
Boolean(settlement.invoiceDate) ||
(settlement.invoiceAmount != null && Number(settlement.invoiceAmount) > 0) ||
Boolean(settlement.invoiceAttachmentUrls?.trim())
return hasInvoice ? '是' : '否'
}
/** 解析运输单司机回传附件(JSON 数组或逗号分隔) */
export function parsePlFeedbackImages(raw?: string | string[] | null): string[] {
if (!raw) return []
if (Array.isArray(raw)) return raw.map((item) => String(item).trim()).filter(Boolean)
const text = String(raw).trim()
if (!text) return []
try {
const parsed = JSON.parse(text)
if (Array.isArray(parsed)) {
return parsed.map((item) => String(item).trim()).filter(Boolean)
}
} catch {
// fall through
}
return text
.split(',')
.map((item) => item.trim())
.filter(Boolean)
}
export function getPlRechargeTypeLabel(type: string) {
return type === 'etc' ? 'ETC' : type === 'fuel' ? '油卡' : type
}
export function getPlRechargeTypeTagType(type: string) {
// primary/success 同为翠绿,油卡用 warning(琥珀)区分
return type === 'etc' ? 'primary' : type === 'fuel' ? 'warning' : 'info'
}
export function getPlFuelCategoryLabel(category: string) {
const map: Record<string, string> = {
bailian: '百链',
gas_station: '加油站',
shanxia_factory: '山峡'
}
return map[category] || category || '—'
}
export function getPlFuelCategoryTagType(category: string) {
// 注意:本项目 global 样式下 primary 与 success 同为翠绿,不可并用区分类别
const map: Record<string, string> = {
bailian: 'primary',
gas_station: 'warning',
shanxia_factory: 'info'
}
return map[category] || 'info'
}
export function getPlFuelRegisterStatusLabel(status: string) {
const map: Record<string, string> = {
normal: '正常',
cancelled: '已作废'
}
return map[status] || status || '—'
}
export function getPlFuelRegisterStatusTagType(status: string) {
const map: Record<string, string> = {
normal: 'success',
cancelled: 'info'
}
return map[status] || 'info'
}
/** 附件 URL 逗号串 → 非空个数 */
export function countPlAttachmentUrls(raw?: string | null): number {
return String(raw || '')
.split(',')
.map((s) => s.trim())
.filter(Boolean).length
}
/** 加油登记发微信群纯文本(含库内序号、附件个数) */
export function buildFuelRegisterCopyText(row: {
seqNo?: number | null
registerNo?: string | null
fuelCategory?: string | null
registerDate?: string | null
registrantName?: string | null
licensePlate?: string | null
odometerKm?: number | null
fuelLiters?: number | null
fuelTons?: number | null
attachmentUrls?: string | null
remark?: string | null
}): string {
const dash = '—'
const seq =
row.seqNo != null && Number.isFinite(Number(row.seqNo)) ? String(row.seqNo) : dash
const liters =
row.fuelLiters != null && Number.isFinite(Number(row.fuelLiters))
? Number(row.fuelLiters).toFixed(2)
: dash
let tons = dash
if (row.fuelTons != null && Number.isFinite(Number(row.fuelTons))) {
tons = Number(row.fuelTons).toFixed(3)
} else if (row.fuelLiters != null && Number.isFinite(Number(row.fuelLiters))) {
tons = (Number(row.fuelLiters) / 1176).toFixed(3)
}
const odometer =
row.odometerKm != null && Number.isFinite(Number(row.odometerKm))
? `${Number(row.odometerKm).toFixed(1)} KM`
: dash
const plate = row.licensePlate?.trim() || dash
const lines = [
`【${plate} - 加油登记】`,
`序号:${seq}`,
`登记单号:${row.registerNo || dash}`,
`加油类别:${getPlFuelCategoryLabel(String(row.fuelCategory || '')) || dash}`,
`日期:${row.registerDate || dash}`,
`登记人:${row.registrantName || dash}`,
`车牌号:${plate}`,
`期初公里数:${odometer}`,
`加油升数:${liters === dash ? dash : `${liters} L`}`,
`加油/吨:${tons}`,
`附件个数:${countPlAttachmentUrls(row.attachmentUrls)}`,
`备注:${row.remark?.trim() || dash}`
]
return lines.join('\n')
}
export function getPlRechargeApplyStatusLabel(status: string) {
const map: Record<string, string> = {
draft: '草稿',
pending: '待发放',
in_approval: '审批中',
rejected: '已驳回',
disbursed: '已发放',
cancelled: '已作废'
}
return map[status] || status
}
export function getPlRechargeApplyStatusTagType(status: string) {
const map: Record<string, string> = {
draft: 'info',
pending: 'warning',
in_approval: 'warning',
rejected: 'danger',
disbursed: 'success',
cancelled: 'info'
}
return map[status] || 'info'
}
export function getPlRechargeApprovalStatusLabel(status: string) {
const map: Record<string, string> = {
approved: '通过',
rejected: '驳回',
disbursed: '已发放'
}
return map[status] || status
}
export function getPlRechargeApprovalStatusTagType(status: string) {
const map: Record<string, string> = {
approved: 'success',
rejected: 'danger',
disbursed: 'primary'
}
return map[status] || 'info'
}
/** ISO 日期时间展示:将「T」换成空格,如 2026-09-27 17:13:51 */
export function formatPlDateTime(value?: string | null, empty = '—') {
if (value == null || value === '') return empty
const text = String(value).replace('T', ' ')
return text.length > 19 ? text.slice(0, 19) : text
}
/** 充值审批节点 → BxApprovalFlowTabs / ApprovalFlowView */
export function mapVehicleRechargeApprovalFlowToNodes(
flow: Array<{
id: string
nodeName?: string
nodeOrder?: number
approvalRound?: number | null
approvalStatus: string
approvalOpinion?: string | null
approverName?: string
approvalTime?: string | null
}>,
options?: { withRoundPrefix?: boolean }
) {
return flow.map((item, index) => {
const baseName = item.nodeName || `节点${item.nodeOrder ?? index + 1}`
const nodeName =
options?.withRoundPrefix && item.approvalRound
? `第${item.approvalRound}轮 · ${baseName}`
: baseName
return {
id: item.id as unknown as number,
nodeName,
nodeOrder: item.nodeOrder ?? index + 1,
approvalStatus: item.approvalStatus,
approvalOpinion: item.approvalOpinion,
approverName: item.approverName || '',
approvalTime: item.approvalTime ? formatPlDateTime(item.approvalTime, '') : item.approvalTime
}
})
}
export const PL_GAS_STATION_PRESETS = ['中石化', '线上etc-app', '百链', '现金转账', '其他']
export const PL_MAINTENANCE_TYPE_PRESETS = ['保养', '维修', '年检']
export function getPlMaintenanceTypeLabel(type: string) {
return type || '—'
}
export function getPlMaintenanceTypeTagType(type: string) {
const map: Record<string, string> = {
保养: 'success',
维修: 'warning',
年检: 'primary'
}
return map[type] || 'info'
}
export function getPlMaintenanceApplyStatusLabel(status: string) {
const map: Record<string, string> = {
pending: '待处理',
processed: '已处理',
cancelled: '已作废'
}
return map[status] || status
}
export function getPlMaintenanceApplyStatusTagType(status: string) {
const map: Record<string, string> = {
pending: 'warning',
processed: 'success',
cancelled: 'info'
}
return map[status] || 'info'
}
export const PL_UREA_UNIT_PRESETS = ['吨桶']
export function getPlUreaApplyStatusLabel(status: string) {
const map: Record<string, string> = {
pending: '待发放',
disbursed: '已发放',
cancelled: '已作废'
}
return map[status] || status
}
export function getPlUreaApplyStatusTagType(status: string) {
const map: Record<string, string> = {
pending: 'warning',
disbursed: 'success',
cancelled: 'info'
}
return map[status] || 'info'
}
const PL_PERSONNEL_ROLE_SHORT_LABELS: Record<string, string> = {
driver: '驾',
escort: '押'
}
/** 人员角色短标签(列表/摘要等紧凑展示) */
export function getPlPersonnelRoleShortLabel(role?: string | null) {
if (!role) return ''
return PL_PERSONNEL_ROLE_SHORT_LABELS[role] || role
}
/** 姓名 + 角色短标签,如 刘道银(驾) */
export function formatPlPersonnelNameWithRole(
name?: string | null,
role?: string | null,
options?: { parentheses?: 'full' | 'half' }
) {
const displayName = name?.trim() || '—'
const roleLabel = getPlPersonnelRoleShortLabel(role)
if (!roleLabel) return displayName
const open = options?.parentheses === 'half' ? '(' : '('
const close = options?.parentheses === 'half' ? ')' : ')'
return `${displayName}${open}${roleLabel}${close}`
}
/** 兼容历史 personnelSummary 快照中的完整角色文案 */
export function normalizePlPersonnelSummaryDisplay(text?: string | null) {
if (!text?.trim()) return text ?? ''
return text
.replace(/\(司机\)/g, '(驾)')
.replace(/\(押运员\)/g, '(押)')
.replace(/(司机)/g, '(驾)')
.replace(/(押运员)/g, '(押)')
}