Files
shanxia2.0/bak/bx-mobile/src/hooks/useExpenseItemTree.ts
T
2026-09-12 19:42:20 +08:00

109 lines
3.2 KiB
TypeScript

import { ref, onMounted } from 'vue'
import { showToast } from 'vant'
import { expenseItemApi } from '@/api'
export interface ExpenseItem {
id: string
parentId: string
itemName: string
itemCode: string
level: number
sortOrder: number
status: number | string
childCount: number
children?: ExpenseItem[]
}
function normalizeItem(raw: any): ExpenseItem {
const children = Array.isArray(raw?.children) ? raw.children.map(normalizeItem) : 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 useExpenseItemTree() {
const expenseTree = ref<ExpenseItem[]>([])
const loading = ref(false)
const flatList = ref<ExpenseItem[]>([])
async function load() {
loading.value = true
try {
const res: any = await expenseItemApi.tree()
const tree: ExpenseItem[] = (res.data ?? []).map(normalizeItem)
expenseTree.value = tree
flatList.value = flattenTree(tree)
} catch (e: any) {
showToast(e?.message || '加载费用项目失败')
} finally {
loading.value = false
}
}
onMounted(load)
return { expenseTree, flatList, loading, load }
}
function flattenTree(tree: ExpenseItem[]): ExpenseItem[] {
const result: ExpenseItem[] = []
for (const node of tree) {
result.push(node)
if (node.children?.length) result.push(...flattenTree(node.children))
}
return result
}
export function formatExpenseName(
id: string | number | null | undefined,
flatList: ExpenseItem[],
): string {
if (id == null || id === '') return '-'
const sid = String(id)
const parts: string[] = []
let cur: ExpenseItem | undefined = flatList.find((i) => String(i.id) === sid)
const guard = new Set<string>()
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('/') : '-'
}
/** 从列表/详情行上常见的 camel/snake 字段取费用项 ID */
export function pickExpenseItemId(row: Record<string, any> | null | undefined): string | null {
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)
}
/**
* 优先用费用树拼出「一级/二级」;无 ID 或树未加载时用接口返回的 expenseName。
*/
export function resolveExpenseDisplay(
row: Record<string, any> | null | undefined,
flatList: ExpenseItem[],
): string {
if (!row) return '-'
const id = pickExpenseItemId(row)
const nameFb = row.expenseName ?? row.expense_name
if (id != null && flatList.length) {
const p = formatExpenseName(id, flatList)
if (p && p !== '-') return p
}
const fb = nameFb != null ? String(nameFb).trim() : ''
return fb || '-'
}