refactor(frontend): split large reimbursement and audit modules
This commit is contained in:
545
web/src/views/scripts/travelRequestDetailExpenseModel.js
Normal file
545
web/src/views/scripts/travelRequestDetailExpenseModel.js
Normal file
@@ -0,0 +1,545 @@
|
||||
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}。`
|
||||
}
|
||||
Reference in New Issue
Block a user