init
This commit is contained in:
175
src/components/transaction/TransactionItem/TransactionItem.tsx
Normal file
175
src/components/transaction/TransactionItem/TransactionItem.tsx
Normal file
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Transaction Item Component
|
||||
* 交易项组件 - 重构版
|
||||
* Feature: ui-visual-redesign
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Icon } from '@iconify/react';
|
||||
import type { Transaction, Category } from '../../../types';
|
||||
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}`}>
|
||||
<Icon icon={getCategoryIcon()} width={compact ? 18 : 22} />
|
||||
</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;
|
||||
Reference in New Issue
Block a user