@@ -2,7 +2,6 @@ import { SWRConfig } from 'swr';
|
||||
import ErrorBoundary from '../components/ErrorBoundary';
|
||||
import { ToastProvider } from '../components/ToastProvider';
|
||||
import { ConfirmProvider } from '../components/ConfirmProvider';
|
||||
import PageTransition from '../components/PageTransition';
|
||||
import { swrConfig } from '../lib/swr-config';
|
||||
import '../styles/globals.css';
|
||||
|
||||
@@ -12,9 +11,7 @@ export default function App({ Component, pageProps }) {
|
||||
<SWRConfig value={swrConfig}>
|
||||
<ConfirmProvider>
|
||||
<ToastProvider>
|
||||
<PageTransition>
|
||||
<Component {...pageProps} />
|
||||
</PageTransition>
|
||||
<Component {...pageProps} />
|
||||
</ToastProvider>
|
||||
</ConfirmProvider>
|
||||
</SWRConfig>
|
||||
|
||||
+17
-65
@@ -4,12 +4,10 @@ import { isLoggedIn, getRecords, getStats, createRecord, updateRecord, deleteRec
|
||||
import AppShell from '../components/AppShell';
|
||||
import { Icon } from '../components/icons';
|
||||
import RecordModal from '../components/RecordModal';
|
||||
import SwipeableItem from '../components/SwipeableItem';
|
||||
import EmptyState from '../components/EmptyState';
|
||||
import { useToast } from '../components/ToastProvider';
|
||||
import { useConfirm } from '../components/ConfirmProvider';
|
||||
import { useCategories } from '../hooks/useCategories';
|
||||
import { getMonthStr, formatMoney, formatMonth, parseDateTime } from '../lib/utils';
|
||||
import { getMonthStr, formatMoney, formatMonth, parseDateTime, getDatePart } from '../lib/utils';
|
||||
|
||||
export default function Home() {
|
||||
const router = useRouter();
|
||||
@@ -193,23 +191,6 @@ export default function Home() {
|
||||
}
|
||||
};
|
||||
|
||||
const handleQuickDelete = async (record) => {
|
||||
const shouldDelete = await confirm({
|
||||
title: '删除这条记录?',
|
||||
description: `${record.category} ${record.type === 'income' ? '+' : '-'}${formatMoney(record.amount)}`,
|
||||
confirmText: '删除',
|
||||
});
|
||||
if (!shouldDelete) return;
|
||||
|
||||
try {
|
||||
await deleteRecord(record.id);
|
||||
setRecords(prev => prev.filter(r => r.id !== record.id));
|
||||
showToast('记录已删除', { tone: 'success' });
|
||||
} catch (err) {
|
||||
showToast(err.message, { tone: 'error' });
|
||||
}
|
||||
};
|
||||
|
||||
const groupedRecords = useMemo(() => {
|
||||
return records.reduce((groups, record) => {
|
||||
const date = getDatePart(record.date);
|
||||
@@ -305,14 +286,10 @@ export default function Home() {
|
||||
{loading ? (
|
||||
<div className="loading-state">加载中...</div>
|
||||
) : records.length === 0 ? (
|
||||
<EmptyState
|
||||
icon="info"
|
||||
title="暂无记录"
|
||||
description={typeFilter === 'all' ? '本月还没有记账,点击下方按钮添加第一笔记录' : '该类型下暂无记录'}
|
||||
action
|
||||
actionLabel="添加记录"
|
||||
onAction={openAddModal}
|
||||
/>
|
||||
<div className="empty-state empty-state--card">
|
||||
<div className="empty-state__icon"><Icon name="info" size={24} /></div>
|
||||
<p>暂无记录,点击上方按钮添加。</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="records-list records-list--flush">
|
||||
{sortedDates.map((date) => (
|
||||
@@ -324,44 +301,19 @@ export default function Home() {
|
||||
{groupedRecords[date].map((record) => {
|
||||
const icon = categoryIconMap[`${record.type}:${record.category}`] || '📝';
|
||||
return (
|
||||
<SwipeableItem
|
||||
key={record.id}
|
||||
onSwipeLeft={() => openEditModal(record)}
|
||||
renderRightActions={(handleAction) => (
|
||||
<>
|
||||
<button
|
||||
type="button"
|
||||
className="swipeable-action swipeable-action--edit"
|
||||
onClick={() => handleAction(() => openEditModal(record))}
|
||||
aria-label="编辑"
|
||||
>
|
||||
<Icon name="edit" size={20} />
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="swipeable-action swipeable-action--delete"
|
||||
onClick={() => handleAction(() => handleQuickDelete(record))}
|
||||
aria-label="删除"
|
||||
>
|
||||
<Icon name="trash" size={20} />
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
>
|
||||
<button type="button" className="record-item" onClick={() => openEditModal(record)}>
|
||||
<div className="record-left">
|
||||
<div className="record-icon">{icon}</div>
|
||||
<div className="record-info">
|
||||
<div className="record-category">{record.category}</div>
|
||||
{record.note && <div className="record-note">{record.note}</div>}
|
||||
</div>
|
||||
<button key={record.id} type="button" className="record-item" onClick={() => openEditModal(record)}>
|
||||
<div className="record-left">
|
||||
<div className="record-icon">{icon}</div>
|
||||
<div className="record-info">
|
||||
<div className="record-category">{record.category}</div>
|
||||
{record.note && <div className="record-note">{record.note}</div>}
|
||||
</div>
|
||||
<div className={`record-amount ${record.type}`}>
|
||||
{record.type === 'income' ? '+' : '-'}
|
||||
{formatMoney(record.amount)}
|
||||
</div>
|
||||
</button>
|
||||
</SwipeableItem>
|
||||
</div>
|
||||
<div className={`record-amount ${record.type}`}>
|
||||
{record.type === 'income' ? '+' : '-'}
|
||||
{formatMoney(record.amount)}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user