181 lines
5.4 KiB
TypeScript
181 lines
5.4 KiB
TypeScript
/**
|
|
* Transaction Item Component
|
|
* 交易项组件 - 重构版
|
|
* Feature: ui-visual-redesign
|
|
*/
|
|
|
|
import React from 'react';
|
|
import { Icon } from '@iconify/react';
|
|
import type { Transaction, Category } from '../../../types';
|
|
import { isIconifyIcon } from '../../../utils/iconUtils';
|
|
import './TransactionItem.css';
|
|
|
|
interface TransactionItemProps {
|
|
/** 交易数据 */
|
|
transaction: Transaction;
|
|
/** 分类数据 */
|
|
category?: Category;
|
|
/** 账户数据 */
|
|
account?: import('../../../types').Account;
|
|
/** 点击回调 */
|
|
onClick?: (transaction: Transaction) => void;
|
|
/** 编辑回调 */
|
|
onEdit?: (transaction: Transaction) => void;
|
|
/** 删除回调 */
|
|
onDelete?: (transaction: Transaction) => void;
|
|
/** 是否选中 */
|
|
selected?: boolean;
|
|
/** 是否显示日期 */
|
|
showDate?: boolean;
|
|
/** 是否紧凑模式 */
|
|
compact?: boolean;
|
|
}
|
|
|
|
const TransactionItem: React.FC<TransactionItemProps> = ({
|
|
transaction,
|
|
category,
|
|
account: _account,
|
|
onClick,
|
|
onEdit: _onEdit,
|
|
onDelete: _onDelete,
|
|
selected = false,
|
|
showDate = true,
|
|
compact = false,
|
|
}) => {
|
|
// 格式化金额
|
|
const formatAmount = (amount: number): string => {
|
|
return new Intl.NumberFormat('zh-CN', {
|
|
style: 'currency',
|
|
currency: transaction.currency || 'CNY',
|
|
minimumFractionDigits: 2,
|
|
}).format(Math.abs(amount));
|
|
};
|
|
|
|
// 格式化日期
|
|
const formatDate = (dateString: string): string => {
|
|
const date = new Date(dateString);
|
|
const today = new Date();
|
|
const yesterday = new Date(today);
|
|
yesterday.setDate(yesterday.getDate() - 1);
|
|
|
|
if (date.toDateString() === today.toDateString()) {
|
|
return '今天';
|
|
} else if (date.toDateString() === yesterday.toDateString()) {
|
|
return '昨天';
|
|
} else {
|
|
return date.toLocaleDateString('zh-CN', { month: '2-digit', day: '2-digit' });
|
|
}
|
|
};
|
|
|
|
// 格式化时间
|
|
const formatTime = (dateString: string, timeString?: string): string => {
|
|
if (timeString) {
|
|
return timeString.slice(0, 5);
|
|
}
|
|
const date = new Date(dateString);
|
|
return date.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
|
|
};
|
|
|
|
// 获取交易类型图标
|
|
const getTypeIcon = () => {
|
|
switch (transaction.type) {
|
|
case 'income':
|
|
return 'solar:graph-up-bold-duotone';
|
|
case 'expense':
|
|
return 'solar:graph-down-bold-duotone';
|
|
case 'transfer':
|
|
return 'solar:transfer-horizontal-bold-duotone';
|
|
default:
|
|
return 'solar:document-text-bold-duotone';
|
|
}
|
|
};
|
|
|
|
// 获取分类图标
|
|
const getCategoryIcon = () => {
|
|
if (category?.icon) {
|
|
return category.icon;
|
|
}
|
|
return getTypeIcon();
|
|
};
|
|
|
|
const handleClick = () => {
|
|
onClick?.(transaction);
|
|
};
|
|
|
|
return (
|
|
<div
|
|
className={`transaction-item ${transaction.type} ${compact ? 'compact' : ''} ${onClick ? 'clickable' : ''} ${selected ? 'selected' : ''}`}
|
|
onClick={handleClick}
|
|
>
|
|
{/* 图标 */}
|
|
<div className={`transaction-item-icon ${transaction.type}`}>
|
|
{isIconifyIcon(getCategoryIcon()) ? (
|
|
<Icon icon={getCategoryIcon()} width={compact ? 18 : 22} />
|
|
) : (
|
|
<span style={{ fontSize: compact ? 18 : 22 }}>{getCategoryIcon()}</span>
|
|
)}
|
|
</div>
|
|
|
|
{/* 主要信息 */}
|
|
<div className="transaction-item-main">
|
|
<div className="transaction-item-title">
|
|
<span className="transaction-item-category">
|
|
{category?.name || '未分类'}
|
|
</span>
|
|
{transaction.note && !compact && (
|
|
<span className="transaction-item-note">{transaction.note}</span>
|
|
)}
|
|
</div>
|
|
|
|
{showDate && (
|
|
<div className="transaction-item-meta">
|
|
<span className="transaction-item-date">
|
|
{formatDate(transaction.transactionDate)}
|
|
</span>
|
|
{transaction.transactionTime && (
|
|
<span className="transaction-item-time">
|
|
{formatTime(transaction.transactionDate, transaction.transactionTime)}
|
|
</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 金额 */}
|
|
<div className="transaction-item-amount-wrapper">
|
|
<span className={`transaction-item-amount ${transaction.type}`}>
|
|
{transaction.type === 'income' ? '+' : transaction.type === 'expense' ? '-' : ''}
|
|
{formatAmount(transaction.amount)}
|
|
</span>
|
|
|
|
{/* 状态标签 */}
|
|
{(transaction.reimbursementStatus !== 'none' || transaction.refundStatus !== 'none') && (
|
|
<div className="transaction-item-badges">
|
|
{transaction.reimbursementStatus === 'pending' && (
|
|
<span className="badge badge-warning">待报销</span>
|
|
)}
|
|
{transaction.reimbursementStatus === 'completed' && (
|
|
<span className="badge badge-success">已报销</span>
|
|
)}
|
|
{transaction.refundStatus === 'partial' && (
|
|
<span className="badge badge-primary">部分退款</span>
|
|
)}
|
|
{transaction.refundStatus === 'full' && (
|
|
<span className="badge badge-success">已退款</span>
|
|
)}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* 箭头指示器 */}
|
|
{onClick && !compact && (
|
|
<div className="transaction-item-arrow">
|
|
<Icon icon="solar:alt-arrow-right-linear" width={16} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default TransactionItem;
|