import React, { useCallback, useMemo, useRef } from 'react'; import { StyleSheet, Text, TextInput, View } from 'react-native'; import { useTheme, createCommonStyles } from '../theme'; import { BalanceIndicator } from './BalanceIndicator'; import type { Posting } from '../domain/types'; import { addDecimals } from '../domain/decimal'; interface PostingEditorProps { postings: Posting[]; onChange: (postings: Posting[]) => void; /** 可选账户列表(用于自动补全提示,当前为自由输入)。 */ accounts?: string[]; } /** 生成 Posting 的稳定 key(基于内容的复合 key)。 */ function postingKey(p: Posting, index: number): string { return `${p.account ?? ''}|${p.amount ?? ''}|${p.currency ?? ''}|${index}`; } /** 分类编辑器(主题化):编辑多行 account/amount/currency,实时显示平衡状态。 */ export function PostingEditor({ postings, onChange }: PostingEditorProps) { const { theme } = useTheme(); const commonStyles = useMemo(() => createCommonStyles(theme), [theme]); const postingsRef = useRef(postings); postingsRef.current = postings; // 使用函数式更新,避免 onChange 依赖 postings 导致每次渲染重建 const update = useCallback((index: number, patch: Partial) => { const current = postingsRef.current; onChange(current.map((p, i) => i === index ? { ...p, ...patch } : p)); }, [onChange]); // 计算各币种余额 const balances = useMemo(() => { const grouped = new Map(); for (const p of postings) { if (p.amount && p.currency) { grouped.set(p.currency, [...(grouped.get(p.currency) ?? []), p.amount]); } } const result: Record = {}; for (const [cur, amts] of grouped) result[cur] = addDecimals(amts); return result; }, [postings]); return ( {postings.map((p, i) => ( update(i, { account: v })} placeholder="账户" placeholderTextColor={theme.colors.fgSecondary} style={[commonStyles.input, { flex: 2 }]} /> update(i, { amount: v })} placeholder="金额" keyboardType="decimal-pad" placeholderTextColor={theme.colors.fgSecondary} style={[commonStyles.input, { flex: 1, fontFamily: 'monospace' }]} /> update(i, { currency: v })} placeholder="币种" placeholderTextColor={theme.colors.fgSecondary} style={[commonStyles.input, { width: 60 }]} /> ))} ); } const styles = StyleSheet.create({ row: { flexDirection: 'row', gap: 6 }, });