Enable mobile config of expense tree and advance/reimburse approval nodes. Co-authored-by: Cursor <cursoragent@cursor.com>
172 lines
4.8 KiB
JavaScript
172 lines
4.8 KiB
JavaScript
/** 对齐 bx-mobile/src/utils/format.ts(Batch2 所需子集) */
|
||
|
||
export function statusLabel(status) {
|
||
if (status == null || status === '') return '-'
|
||
const map = {
|
||
1: '启用',
|
||
0: '禁用',
|
||
draft: '待提交',
|
||
pending: '审批中',
|
||
approved: '已通过',
|
||
rejected: '已驳回',
|
||
withdrawn: '已撤回',
|
||
issued: '已发放',
|
||
paid: '已支付',
|
||
offset: '已冲抵',
|
||
partial: '部分冲抵',
|
||
processed: '已处理'
|
||
}
|
||
return map[status] ?? String(status)
|
||
}
|
||
|
||
export function statusTone(status) {
|
||
if (status == null) return 'info'
|
||
const map = {
|
||
1: 'done',
|
||
0: 'error',
|
||
draft: 'info',
|
||
pending: 'warn',
|
||
approved: 'done',
|
||
rejected: 'error',
|
||
withdrawn: 'warn',
|
||
issued: 'done',
|
||
paid: 'done',
|
||
offset: 'done',
|
||
partial: 'warn',
|
||
processed: 'done'
|
||
}
|
||
return map[status] ?? 'info'
|
||
}
|
||
|
||
/** StatusTag variant(对齐 bx-mobile statusTagClass) */
|
||
export function statusTagClass(status) {
|
||
const tone = statusTone(status)
|
||
const map = {
|
||
info: 'info',
|
||
warn: 'warning',
|
||
done: 'success',
|
||
error: 'danger',
|
||
processing: 'processing'
|
||
}
|
||
return map[tone] || 'info'
|
||
}
|
||
|
||
/** 小程序 status-tag class:启用/禁用或工作流状态 */
|
||
export function adminStatusTagClass(status) {
|
||
if (status === 1 || status === '1') return 's-approved'
|
||
if (status === 0 || status === '0') return 's-draft'
|
||
return workflowListStatusTagClass(status)
|
||
}
|
||
|
||
export function workflowListStatusTagClass(status) {
|
||
const raw = status == null || status === '' ? 'draft' : String(status)
|
||
return `s-${raw}`
|
||
}
|
||
|
||
export function ticketHeaderStatusToneClass(status) {
|
||
const tone = statusTone(status)
|
||
const map = {
|
||
info: 'advance-ticket-status--info',
|
||
warn: 'advance-ticket-status--warn',
|
||
done: 'advance-ticket-status--done',
|
||
error: 'advance-ticket-status--err',
|
||
processing: 'advance-ticket-status--processing'
|
||
}
|
||
return map[tone] || 'advance-ticket-status--info'
|
||
}
|
||
|
||
export function formatAmount(amount) {
|
||
const n = typeof amount === 'string' ? parseFloat(amount) : amount
|
||
if (Number.isNaN(n)) return '0.00'
|
||
return n.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
|
||
}
|
||
|
||
export function formatDate(dateStr) {
|
||
if (!dateStr) return '-'
|
||
return String(dateStr).substring(0, 10)
|
||
}
|
||
|
||
export function formatDateTime(dateStr) {
|
||
if (!dateStr) return '-'
|
||
return String(dateStr).substring(0, 16).replace('T', ' ')
|
||
}
|
||
|
||
export function pickApplyTime(row) {
|
||
if (!row || typeof row !== 'object') return undefined
|
||
const v = row.applyTime ?? row.createTime ?? row.apply_time ?? row.create_time
|
||
if (v == null || v === '') return undefined
|
||
return String(v)
|
||
}
|
||
|
||
export function formatApplyDateTime(row) {
|
||
return formatDateTime(pickApplyTime(row))
|
||
}
|
||
|
||
export function formatApplyDate(row) {
|
||
return formatDate(pickApplyTime(row))
|
||
}
|
||
|
||
/** 费用树扁平化(tree → flatList) */
|
||
export function flattenExpenseTree(tree) {
|
||
const result = []
|
||
for (const node of tree || []) {
|
||
result.push(node)
|
||
if (node.children?.length) result.push(...flattenExpenseTree(node.children))
|
||
}
|
||
return result
|
||
}
|
||
|
||
export function normalizeExpenseItem(raw) {
|
||
const children = Array.isArray(raw?.children)
|
||
? raw.children.map(normalizeExpenseItem)
|
||
: undefined
|
||
return {
|
||
id: String(raw?.id ?? ''),
|
||
parentId: raw?.parentId == null || raw?.parentId === '' ? '0' : String(raw.parentId),
|
||
itemName: raw?.itemName ?? '',
|
||
itemCode: raw?.itemCode ?? '',
|
||
level: Number(raw?.level ?? 0),
|
||
sortOrder: Number(raw?.sortOrder ?? 0),
|
||
status: raw?.status,
|
||
childCount: Number(raw?.childCount ?? children?.length ?? 0),
|
||
children
|
||
}
|
||
}
|
||
|
||
export function formatExpenseName(id, flatList) {
|
||
if (id == null || id === '') return '-'
|
||
const sid = String(id)
|
||
const parts = []
|
||
let cur = (flatList || []).find((i) => String(i.id) === sid)
|
||
const guard = new Set()
|
||
while (cur && !guard.has(cur.id)) {
|
||
guard.add(cur.id)
|
||
parts.unshift(cur.itemName)
|
||
const pid = cur.parentId
|
||
cur =
|
||
pid != null && pid !== '' && pid !== '0'
|
||
? (flatList || []).find((i) => String(i.id) === pid)
|
||
: undefined
|
||
}
|
||
return parts.length ? parts.join('/') : '-'
|
||
}
|
||
|
||
export function pickExpenseItemId(row) {
|
||
if (!row || typeof row !== 'object') return null
|
||
const raw = row.expenseId ?? row.expense_id ?? row.expenseItemId ?? row.expense_item_id
|
||
if (raw == null || raw === '') return null
|
||
return String(raw)
|
||
}
|
||
|
||
export function resolveExpenseDisplay(row, flatList) {
|
||
if (!row) return '-'
|
||
const id = pickExpenseItemId(row)
|
||
const nameFb = row.expenseName ?? row.expense_name
|
||
if (id != null && flatList && flatList.length) {
|
||
const p = formatExpenseName(id, flatList)
|
||
if (p && p !== '-') return p
|
||
}
|
||
const fb = nameFb != null ? String(nameFb).trim() : ''
|
||
return fb || '-'
|
||
}
|