546 lines
19 KiB
JavaScript
546 lines
19 KiB
JavaScript
export const EXPENSE_TYPE_OPTIONS = [
|
||
{ value: 'travel', label: '差旅费' },
|
||
{ value: 'train_ticket', label: '火车票' },
|
||
{ value: 'flight_ticket', label: '机票' },
|
||
{ value: 'hotel_ticket', label: '住宿票' },
|
||
{ value: 'ride_ticket', label: '乘车' },
|
||
{ value: 'entertainment', label: '业务招待费' },
|
||
{ value: 'office', label: '办公费' },
|
||
{ value: 'meeting', label: '会务费' },
|
||
{ value: 'training', label: '培训费' },
|
||
{ value: 'hotel', label: '住宿费' },
|
||
{ value: 'transport', label: '交通费' },
|
||
{ value: 'meal', label: '餐费' },
|
||
{ value: 'travel_allowance', label: '出差补贴' },
|
||
{ value: 'other', label: '其他费用' }
|
||
]
|
||
|
||
export const LOCATION_REQUIRED_EXPENSE_TYPES = new Set([
|
||
'travel',
|
||
'meeting',
|
||
'entertainment'
|
||
])
|
||
|
||
export const SYSTEM_GENERATED_EXPENSE_TYPES = new Set(['travel_allowance'])
|
||
export const LONG_DISTANCE_TRAVEL_EXPENSE_TYPES = new Set(['train_ticket', 'flight_ticket'])
|
||
export const ROUTE_DESCRIPTION_EXPENSE_TYPES = new Set(['train_ticket', 'flight_ticket', 'ship_ticket', 'ferry_ticket', 'ride_ticket'])
|
||
export const HOTEL_DESCRIPTION_EXPENSE_TYPES = new Set(['hotel_ticket'])
|
||
export const ROUTE_DESCRIPTION_PATTERN = /^[A-Za-z0-9\u4e00-\u9fa5()()·]{2,40}\s*-\s*[A-Za-z0-9\u4e00-\u9fa5()()·]{2,40}$/
|
||
|
||
export function parseCurrency(value) {
|
||
return Number.parseFloat(String(value).replace(/[^\d.]/g, '')) || 0
|
||
}
|
||
|
||
export function formatCurrency(value) {
|
||
return new Intl.NumberFormat('zh-CN', {
|
||
style: 'currency',
|
||
currency: 'CNY',
|
||
minimumFractionDigits: 0,
|
||
maximumFractionDigits: Number.isInteger(value) ? 0 : 2
|
||
}).format(value)
|
||
}
|
||
|
||
export function normalizeExpenseType(value) {
|
||
return String(value || '').trim() || 'other'
|
||
}
|
||
|
||
export function resolveExpenseTypeLabel(value) {
|
||
return EXPENSE_TYPE_OPTIONS.find((option) => option.value === normalizeExpenseType(value))?.label || '其他费用'
|
||
}
|
||
|
||
export function isSystemGeneratedExpenseItemSource(source) {
|
||
const itemType = normalizeExpenseType(source?.itemType || source?.item_type)
|
||
return Boolean(source?.isSystemGenerated || source?.is_system_generated || SYSTEM_GENERATED_EXPENSE_TYPES.has(itemType))
|
||
}
|
||
|
||
export function isLocationRequiredExpenseType(value) {
|
||
return LOCATION_REQUIRED_EXPENSE_TYPES.has(normalizeExpenseType(value))
|
||
}
|
||
|
||
export function resolveLocationSummaryLabel(value) {
|
||
return isLocationRequiredExpenseType(value) ? '业务地点' : '采购/收货地点'
|
||
}
|
||
|
||
export function isRouteDescriptionExpenseType(value) {
|
||
return ROUTE_DESCRIPTION_EXPENSE_TYPES.has(normalizeExpenseType(value))
|
||
}
|
||
|
||
export function isHotelDescriptionExpenseType(value) {
|
||
return HOTEL_DESCRIPTION_EXPENSE_TYPES.has(normalizeExpenseType(value))
|
||
}
|
||
|
||
export function resolveExpenseDetailHint(expenseType) {
|
||
if (isRouteDescriptionExpenseType(expenseType)) {
|
||
return '起始地-目的地'
|
||
}
|
||
if (isHotelDescriptionExpenseType(expenseType)) {
|
||
return '目的地酒店'
|
||
}
|
||
if (!isLocationRequiredExpenseType(expenseType)) {
|
||
return '非必填'
|
||
}
|
||
return '待补充'
|
||
}
|
||
|
||
export function resolveLocationDisplay(value, expenseType) {
|
||
return isPlaceholderValue(value) ? resolveExpenseDetailHint(expenseType) : value
|
||
}
|
||
|
||
export function isSyntheticLocationDisplay(value, expenseType) {
|
||
const text = String(value || '').trim()
|
||
return ['待补充', '非必填', resolveExpenseDetailHint(expenseType)].includes(text)
|
||
}
|
||
|
||
export function isValidRouteDescription(value) {
|
||
const text = String(value || '').trim()
|
||
return ROUTE_DESCRIPTION_PATTERN.test(text) && !/\d{4}[-/年.]\d{1,2}[-/月.]\d{1,2}/.test(text)
|
||
}
|
||
|
||
export function resolveExpenseReasonPlaceholder(itemType) {
|
||
if (isRouteDescriptionExpenseType(itemType)) {
|
||
return '起始地-目的地,例如:广州南-北京南'
|
||
}
|
||
if (isHotelDescriptionExpenseType(itemType)) {
|
||
return '目的地酒店,例如:北京中心酒店'
|
||
}
|
||
return '输入费用说明'
|
||
}
|
||
|
||
export function resolveExpenseReasonHelper(itemType) {
|
||
if (isRouteDescriptionExpenseType(itemType)) {
|
||
return '起始地-目的地'
|
||
}
|
||
if (isHotelDescriptionExpenseType(itemType)) {
|
||
return '目的地酒店'
|
||
}
|
||
return '业务报销说明'
|
||
}
|
||
|
||
export function buildFallbackProgressSteps() {
|
||
return [
|
||
{ index: 1, label: '创建单据', time: '已完成', done: true, active: true },
|
||
{ index: 2, label: '待提交', time: '进行中', active: true, current: true },
|
||
{ index: 3, label: 'AI预审', time: '待处理' },
|
||
{ index: 4, label: '直属领导审批', time: '待处理' },
|
||
{ index: 5, label: '财务审批', time: '待处理' },
|
||
{ index: 6, label: '归档入账', time: '待处理' }
|
||
]
|
||
}
|
||
|
||
export function buildFallbackExpenseItems(request) {
|
||
return [
|
||
buildExpenseItemViewModel({
|
||
id: 'fallback-1',
|
||
itemDate: '',
|
||
itemType: request.typeCode || 'other',
|
||
itemReason: request.reason,
|
||
itemLocation: request.sceneTarget,
|
||
itemAmount: parseCurrency(request.amountDisplay),
|
||
invoiceId: '',
|
||
time: '待补充',
|
||
dayLabel: request.detailVariant === 'travel' ? '出行日' : '业务发生日',
|
||
name: request.typeLabel,
|
||
category: request.typeLabel,
|
||
desc: request.reason,
|
||
detail: resolveLocationDisplay(request.sceneTarget, request.typeCode),
|
||
amount: request.amountDisplay,
|
||
status: '待补充',
|
||
tone: 'bad',
|
||
attachmentStatus: '待上传',
|
||
attachmentHint: '请在此单据中继续补充附件',
|
||
attachmentTone: 'missing',
|
||
attachments: [],
|
||
riskLabel: '待补材料',
|
||
riskText: request.riskSummary,
|
||
riskTone: 'medium'
|
||
}, 0, request)
|
||
]
|
||
}
|
||
|
||
export function isPlaceholderValue(value) {
|
||
const text = String(value || '').trim()
|
||
if (!text) {
|
||
return true
|
||
}
|
||
|
||
return ['待补充', '暂无', '无', '未知', '处理中'].includes(text.replace(/\s+/g, ''))
|
||
}
|
||
|
||
export function normalizeDetailNoteDraftValue(value) {
|
||
const text = String(value || '').trim()
|
||
return isPlaceholderValue(text) ? '' : text
|
||
}
|
||
|
||
export function isValidIsoDate(value) {
|
||
const normalized = String(value || '').trim()
|
||
if (!/^\d{4}-\d{2}-\d{2}$/.test(normalized)) {
|
||
return false
|
||
}
|
||
|
||
const [yearText, monthText, dayText] = normalized.split('-')
|
||
const year = Number(yearText)
|
||
const month = Number(monthText)
|
||
const day = Number(dayText)
|
||
|
||
if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)) {
|
||
return false
|
||
}
|
||
|
||
const candidate = new Date(Date.UTC(year, month - 1, day))
|
||
return (
|
||
candidate.getUTCFullYear() === year &&
|
||
candidate.getUTCMonth() === month - 1 &&
|
||
candidate.getUTCDate() === day
|
||
)
|
||
}
|
||
|
||
export function normalizeIsoDateValue(value) {
|
||
const normalized = String(value || '').trim()
|
||
if (isValidIsoDate(normalized)) {
|
||
return normalized
|
||
}
|
||
|
||
const match = normalized.match(/^(\d{4}-\d{2}-\d{2})/)
|
||
if (match && isValidIsoDate(match[1])) {
|
||
return match[1]
|
||
}
|
||
|
||
const candidate = value instanceof Date ? value : new Date(normalized)
|
||
if (Number.isNaN(candidate.getTime())) {
|
||
return ''
|
||
}
|
||
|
||
const year = candidate.getFullYear()
|
||
const month = String(candidate.getMonth() + 1).padStart(2, '0')
|
||
const day = String(candidate.getDate()).padStart(2, '0')
|
||
return `${year}-${month}-${day}`
|
||
}
|
||
|
||
export function formatExpenseFilledTime(value) {
|
||
const normalized = String(value || '').trim()
|
||
if (!normalized) {
|
||
return ''
|
||
}
|
||
|
||
const candidate = value instanceof Date ? value : new Date(normalized)
|
||
if (Number.isNaN(candidate.getTime())) {
|
||
return normalized
|
||
}
|
||
|
||
const year = candidate.getFullYear()
|
||
const month = String(candidate.getMonth() + 1).padStart(2, '0')
|
||
const day = String(candidate.getDate()).padStart(2, '0')
|
||
const hours = String(candidate.getHours()).padStart(2, '0')
|
||
const minutes = String(candidate.getMinutes()).padStart(2, '0')
|
||
return `${year}-${month}-${day} ${hours}:${minutes}`
|
||
}
|
||
|
||
export function resolveExpenseUploadHint(value) {
|
||
const normalized = String(value || '').trim()
|
||
return normalized || '仅支持上传 1 张 JPG、PNG、PDF 单据'
|
||
}
|
||
|
||
export function extractAttachmentDisplayName(value) {
|
||
const normalized = String(value || '').trim()
|
||
if (!normalized) {
|
||
return ''
|
||
}
|
||
|
||
return normalized.split('/').filter(Boolean).pop() || normalized
|
||
}
|
||
|
||
export function resolveExpenseItemViewId(source, index, requestModel) {
|
||
return String(source?.id || `${requestModel?.claimId || requestModel?.id || 'claim'}-item-${index}`)
|
||
}
|
||
|
||
export function buildTravelTimeLabelMap(items, requestModel) {
|
||
const travelItems = items
|
||
.map((item, index) => {
|
||
const itemType = normalizeExpenseType(item?.itemType || item?.item_type || requestModel?.typeCode || 'other')
|
||
return {
|
||
id: resolveExpenseItemViewId(item, index, requestModel),
|
||
index,
|
||
itemType,
|
||
itemDate: normalizeIsoDateValue(item?.itemDate ?? item?.item_date),
|
||
isSystemGenerated: isSystemGeneratedExpenseItemSource({ ...item, itemType })
|
||
}
|
||
})
|
||
.filter((item) => !item.isSystemGenerated && LONG_DISTANCE_TRAVEL_EXPENSE_TYPES.has(item.itemType))
|
||
.sort((left, right) => {
|
||
const dateCompare = String(left.itemDate || '').localeCompare(String(right.itemDate || ''))
|
||
return dateCompare || left.index - right.index
|
||
})
|
||
|
||
const labels = new Map()
|
||
if (!travelItems.length) {
|
||
return labels
|
||
}
|
||
|
||
travelItems.forEach((item, index) => {
|
||
if (index === 0) {
|
||
labels.set(item.id, '出发时间')
|
||
} else if (index === travelItems.length - 1) {
|
||
labels.set(item.id, '返回时间')
|
||
} else {
|
||
labels.set(item.id, '中转时间')
|
||
}
|
||
})
|
||
return labels
|
||
}
|
||
|
||
export function resolveExpenseTimeLabel({ id, itemType, isSystemGenerated, requestModel, travelTimeLabelMap }) {
|
||
if (isSystemGenerated) {
|
||
return '系统自动计算'
|
||
}
|
||
if (travelTimeLabelMap?.has(id)) {
|
||
return travelTimeLabelMap.get(id)
|
||
}
|
||
if (itemType === 'ride_ticket') {
|
||
return '乘车时间'
|
||
}
|
||
if (itemType === 'hotel_ticket') {
|
||
return '住宿时间'
|
||
}
|
||
return requestModel?.detailVariant === 'travel' ? '出行时间' : '业务发生时间'
|
||
}
|
||
|
||
export function buildExpenseItemViewModel(source, index, requestModel, travelTimeLabelMap = new Map()) {
|
||
const itemType = normalizeExpenseType(source?.itemType || source?.item_type || requestModel?.typeCode || 'other')
|
||
const isSystemGenerated = isSystemGeneratedExpenseItemSource({ ...source, itemType })
|
||
const id = resolveExpenseItemViewId(source, index, requestModel)
|
||
const itemReason = String(source?.itemReason ?? source?.item_reason ?? '').trim()
|
||
const itemLocation = String(source?.itemLocation ?? source?.item_location ?? '').trim()
|
||
const itemDate = normalizeIsoDateValue(source?.itemDate ?? source?.item_date)
|
||
const itemAmount = parseCurrency(source?.itemAmount ?? source?.item_amount)
|
||
const invoiceId = String(source?.invoiceId ?? source?.invoice_id ?? '').trim()
|
||
const attachmentName = String(source?.attachmentName || source?.attachment_name || extractAttachmentDisplayName(invoiceId)).trim()
|
||
const attachments = invoiceId ? [attachmentName || invoiceId] : []
|
||
const amountDisplay = itemAmount > 0 ? formatCurrency(itemAmount) : '待补充'
|
||
const riskText = String(source?.riskText || '').trim()
|
||
const filledAt = formatExpenseFilledTime(
|
||
source?.filledAt
|
||
|| source?.filled_at
|
||
|| source?.createdAt
|
||
|| source?.created_at
|
||
)
|
||
|
||
return {
|
||
id,
|
||
itemDate,
|
||
itemType,
|
||
itemReason,
|
||
itemLocation,
|
||
itemAmount,
|
||
invoiceId,
|
||
isSystemGenerated,
|
||
time: itemDate || '待补充',
|
||
filledAt: filledAt || '待同步',
|
||
dayLabel: resolveExpenseTimeLabel({
|
||
id,
|
||
itemType,
|
||
isSystemGenerated,
|
||
requestModel,
|
||
travelTimeLabelMap
|
||
}),
|
||
name: resolveExpenseTypeLabel(itemType),
|
||
category: resolveExpenseTypeLabel(itemType),
|
||
desc: itemReason || '待补充',
|
||
detail: resolveLocationDisplay(itemLocation, itemType),
|
||
amount: amountDisplay,
|
||
status: isSystemGenerated ? '系统计算' : attachments.length ? '已识别' : '待补充',
|
||
tone: isSystemGenerated ? 'system' : attachments.length ? 'ok' : 'bad',
|
||
attachmentStatus: isSystemGenerated ? '无需附件' : attachments.length ? '已关联票据' : '未上传',
|
||
attachmentHint: isSystemGenerated ? '根据出差天数与职级自动测算' : attachments.length ? attachmentName || attachments[0] : resolveExpenseUploadHint(),
|
||
attachmentTone: isSystemGenerated ? 'system' : attachments.length ? 'ok' : 'missing',
|
||
attachments,
|
||
riskLabel: String(source?.riskLabel || '').trim() || '无',
|
||
riskText,
|
||
riskTone: String(source?.riskTone || '').trim() || 'low'
|
||
}
|
||
}
|
||
|
||
export function rebuildExpenseItems(items, requestModel) {
|
||
const sortedItems = [...items]
|
||
.sort((left, right) => Number(isSystemGeneratedExpenseItemSource(left)) - Number(isSystemGeneratedExpenseItemSource(right)))
|
||
const travelTimeLabelMap = buildTravelTimeLabelMap(sortedItems, requestModel)
|
||
return sortedItems.map((item, index) => buildExpenseItemViewModel(item, index, requestModel, travelTimeLabelMap))
|
||
}
|
||
|
||
export function buildExpenseDraftIssues(item) {
|
||
const issues = []
|
||
if (item.isSystemGenerated) {
|
||
return issues
|
||
}
|
||
const locationRequired = isLocationRequiredExpenseType(item.itemType)
|
||
|
||
if (!isValidIsoDate(item.itemDate)) {
|
||
issues.push('缺少日期')
|
||
}
|
||
if (isPlaceholderValue(item.itemType)) {
|
||
issues.push('缺少费用项目')
|
||
}
|
||
if (isPlaceholderValue(item.itemReason)) {
|
||
issues.push('缺少说明')
|
||
} else if (isRouteDescriptionExpenseType(item.itemType) && !isValidRouteDescription(item.itemReason)) {
|
||
issues.push('行程说明格式错误')
|
||
}
|
||
if (locationRequired && isPlaceholderValue(item.itemLocation)) {
|
||
issues.push('缺少地点')
|
||
}
|
||
if (!Number.isFinite(Number(item.itemAmount)) || Number(item.itemAmount) <= 0) {
|
||
issues.push('缺少金额')
|
||
}
|
||
if (isPlaceholderValue(item.invoiceId)) {
|
||
issues.push('缺少票据标识')
|
||
}
|
||
|
||
return issues
|
||
}
|
||
|
||
export function buildOptionalTravelReceiptRiskCards(requestModel, items) {
|
||
const normalizedItems = Array.isArray(items) ? items : []
|
||
const isTravelContext =
|
||
requestModel?.detailVariant === 'travel' ||
|
||
requestModel?.typeCode === 'travel' ||
|
||
normalizedItems.some((item) => ['train_ticket', 'flight_ticket', 'hotel_ticket', 'ride_ticket', 'travel_allowance'].includes(item.itemType))
|
||
if (!isTravelContext) {
|
||
return []
|
||
}
|
||
|
||
const hasUploadedType = (itemType) =>
|
||
normalizedItems.some((item) => item.itemType === itemType && !item.isSystemGenerated && !isPlaceholderValue(item.invoiceId))
|
||
const cards = []
|
||
if (!hasUploadedType('hotel_ticket')) {
|
||
cards.push({
|
||
id: 'travel-optional-hotel-ticket',
|
||
tone: 'low',
|
||
label: '低风险',
|
||
title: '住宿票据提醒',
|
||
risk: '当前差旅单暂未上传住宿票据;如果本次出差发生住宿费用,请不要忘记补充酒店住宿票据。',
|
||
summary: '住宿票据缺失不阻断当前提交,但会影响住宿费用报销完整性。',
|
||
ruleBasis: ['差旅费可以包含交通、住宿和补贴等明细;住宿费用需要住宿票据支撑。'],
|
||
suggestion: '如有住宿费用,请新增住宿票明细并上传酒店发票或住宿清单;如未住宿,可忽略该提醒。'
|
||
})
|
||
}
|
||
if (!hasUploadedType('ride_ticket')) {
|
||
cards.push({
|
||
id: 'travel-optional-ride-ticket',
|
||
tone: 'low',
|
||
label: '低风险',
|
||
title: '乘车票据提醒',
|
||
risk: '当前差旅单暂未上传市内乘车票据;如果发生打车或市内交通费用,可以继续补充票据报销。',
|
||
summary: '市内交通票据缺失不阻断当前提交,但可能遗漏可报销费用。',
|
||
ruleBasis: ['差旅费可以补充市内交通/乘车票据;该类票据通常作为差旅费用的可选补充材料。'],
|
||
suggestion: '如有打车、网约车或市内交通费用,请新增乘车明细并上传对应票据。'
|
||
})
|
||
}
|
||
return cards
|
||
}
|
||
|
||
export function buildDraftBlockingIssues(request, expenseItems) {
|
||
const issues = []
|
||
const locationRequired = isLocationRequiredExpenseType(request.typeCode)
|
||
const normalizedItems = Array.isArray(expenseItems) ? expenseItems : []
|
||
const itemAmountTotal = normalizedItems.reduce((sum, item) => {
|
||
const amount = Number(item?.itemAmount || 0)
|
||
return Number.isFinite(amount) && amount > 0 ? sum + amount : sum
|
||
}, 0)
|
||
const hasValidItemDate = normalizedItems.some((item) => isValidIsoDate(item?.itemDate))
|
||
const hasValidItemType = normalizedItems.some((item) => !isPlaceholderValue(item?.itemType))
|
||
const hasValidItemReason = normalizedItems.some((item) => !isPlaceholderValue(item?.itemReason))
|
||
const hasValidItemLocation = normalizedItems.some((item) => !isPlaceholderValue(item?.itemLocation))
|
||
|
||
if (isPlaceholderValue(request.profileName)) {
|
||
issues.push('申请人未完善')
|
||
}
|
||
if (isPlaceholderValue(request.typeLabel) && !hasValidItemType) {
|
||
issues.push('报销类型未完善')
|
||
}
|
||
if (isPlaceholderValue(request.reason) && !hasValidItemReason) {
|
||
issues.push('报销事由未完善')
|
||
}
|
||
if (locationRequired && isPlaceholderValue(request.location) && !hasValidItemLocation) {
|
||
issues.push('业务地点未完善')
|
||
}
|
||
if (isPlaceholderValue(request.occurredDisplay) && !hasValidItemDate) {
|
||
issues.push('发生时间未完善')
|
||
}
|
||
if ((!Number.isFinite(Number(request.amountValue)) || Number(request.amountValue) <= 0) && itemAmountTotal <= 0) {
|
||
issues.push('报销金额未完善')
|
||
}
|
||
if (!normalizedItems.length) {
|
||
issues.push('费用明细不能为空')
|
||
}
|
||
|
||
normalizedItems.forEach((item, index) => {
|
||
buildExpenseDraftIssues(item).forEach((issue) => {
|
||
issues.push(`费用明细第 ${index + 1} 条${issue}`)
|
||
})
|
||
})
|
||
|
||
return [...new Set(issues)]
|
||
}
|
||
|
||
export function mapIssueToAdvice(issue) {
|
||
const text = String(issue || '').trim()
|
||
if (!text) {
|
||
return ''
|
||
}
|
||
|
||
if (text === '费用明细不能为空') {
|
||
return '先新增至少 1 条费用明细,再补充金额、用途和附件。'
|
||
}
|
||
if (text === '申请人未完善') {
|
||
return '补充申请人信息,确保审批单据归属明确。'
|
||
}
|
||
if (text === '所属部门未完善') {
|
||
return '补充所属部门,便于财务和审批人识别成本归属。'
|
||
}
|
||
if (text === '报销类型未完善') {
|
||
return '选择报销类型,明确本次费用归类。'
|
||
}
|
||
if (text === '报销事由未完善') {
|
||
return '补充报销事由,说明本次费用用途。'
|
||
}
|
||
if (text === '业务地点未完善') {
|
||
return '补充业务地点,方便审核业务发生场景。'
|
||
}
|
||
if (text === '发生时间未完善') {
|
||
return '补充费用发生时间,确保单据时间完整。'
|
||
}
|
||
if (text === '报销金额未完善') {
|
||
return '补充报销金额,并与费用明细金额保持一致。'
|
||
}
|
||
|
||
const itemMatch = text.match(/^费用明细第\s*(\d+)\s*条(.+)$/)
|
||
if (!itemMatch) {
|
||
return text
|
||
}
|
||
|
||
const [, indexText, fieldText] = itemMatch
|
||
const labelPrefix = `完善第 ${indexText} 条费用明细`
|
||
if (fieldText === '缺少日期') {
|
||
return `${labelPrefix}的发生日期。`
|
||
}
|
||
if (fieldText === '缺少费用项目') {
|
||
return `${labelPrefix}的费用项目。`
|
||
}
|
||
if (fieldText === '缺少说明') {
|
||
return `${labelPrefix}的用途说明。`
|
||
}
|
||
if (fieldText === '行程说明格式错误') {
|
||
return `${labelPrefix}的行程说明,格式应为“起始地-目的地”。`
|
||
}
|
||
if (fieldText === '缺少地点') {
|
||
return `${labelPrefix}的业务地点。`
|
||
}
|
||
if (fieldText === '缺少金额') {
|
||
return `${labelPrefix}的金额。`
|
||
}
|
||
if (fieldText === '缺少票据标识') {
|
||
return `为第 ${indexText} 条费用明细上传或关联票据附件。`
|
||
}
|
||
|
||
return `${labelPrefix}。`
|
||
}
|