perf: 性能优化与代码质量改进
- 简化ToastProvider组件,移除未使用导入 - 简化Stats页面代码 - 添加React ESLint配置 - 添加数据库索引和API参数验证 - 清理重复文件server.js.backup - 移除前端未使用的UI代码和复杂样式 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.6
parent
3975bbed73
commit
63c97ab31a
+17
-12
@@ -7,7 +7,7 @@ import RecordModal from '../components/RecordModal';
|
||||
import { useToast } from '../components/ToastProvider';
|
||||
import { useConfirm } from '../components/ConfirmProvider';
|
||||
import { useCategories } from '../hooks/useCategories';
|
||||
import { getMonthStr, formatMoney, formatMonth, parseDateTime, getDatePart } from '../lib/utils';
|
||||
import { getMonthStr, formatMoney, formatMonth, parseDateTime, getDatePart, formatDate, getCurrentTimeStr } from '../lib/utils';
|
||||
|
||||
export default function Home() {
|
||||
const router = useRouter();
|
||||
@@ -31,12 +31,13 @@ export default function Home() {
|
||||
showToast('分类加载失败', { tone: 'error' });
|
||||
}
|
||||
}, [categoriesError]);
|
||||
|
||||
const [formData, setFormData] = useState({
|
||||
type: 'expense',
|
||||
amount: '',
|
||||
category: '',
|
||||
date: new Date().toISOString().split('T')[0],
|
||||
time: new Date().toTimeString().slice(0, 5),
|
||||
date: formatDate(new Date()),
|
||||
time: getCurrentTimeStr(),
|
||||
note: '',
|
||||
});
|
||||
|
||||
@@ -53,8 +54,8 @@ export default function Home() {
|
||||
type: 'expense',
|
||||
amount: '',
|
||||
category: '',
|
||||
date: new Date().toISOString().split('T')[0],
|
||||
time: new Date().toTimeString().slice(0, 5),
|
||||
date: formatDate(new Date()),
|
||||
time: getCurrentTimeStr(),
|
||||
note: '',
|
||||
});
|
||||
|
||||
@@ -206,12 +207,16 @@ export default function Home() {
|
||||
[groupedRecords]
|
||||
);
|
||||
|
||||
const getDayExpense = (date) => {
|
||||
const dayRecords = groupedRecords[date] || [];
|
||||
return dayRecords
|
||||
.filter((record) => record.type === 'expense')
|
||||
.reduce((sum, record) => sum + record.amount, 0);
|
||||
};
|
||||
const dayExpenses = useMemo(() => {
|
||||
const expenses = {};
|
||||
for (const date of Object.keys(groupedRecords)) {
|
||||
const dayRecords = groupedRecords[date] || [];
|
||||
expenses[date] = dayRecords
|
||||
.filter((record) => record.type === 'expense')
|
||||
.reduce((sum, record) => sum + record.amount, 0);
|
||||
}
|
||||
return expenses;
|
||||
}, [groupedRecords]);
|
||||
|
||||
const homeHeaderContent = (
|
||||
<div className="header-center-container">
|
||||
@@ -297,7 +302,7 @@ export default function Home() {
|
||||
<div key={date} className="date-group">
|
||||
<div className="date-label">
|
||||
<span>{new Date(date).toLocaleDateString('zh-CN', { month: 'short', day: 'numeric', weekday: 'short' })}</span>
|
||||
<span className="day-expense">支出 {formatMoney(getDayExpense(date))}</span>
|
||||
<span className="day-expense">支出 {formatMoney(dayExpenses[date] || 0)}</span>
|
||||
</div>
|
||||
{groupedRecords[date].map((record) => {
|
||||
const icon = categoryIconMap[`${record.type}:${record.category}`] || '📝';
|
||||
|
||||
@@ -49,7 +49,7 @@ export default function Search() {
|
||||
if (router.isReady && router.query.q) {
|
||||
const q = decodeURIComponent(router.query.q);
|
||||
// Skip if already have results for this keyword (prevents double API call)
|
||||
if (keyword === q && results.length > 0) return;
|
||||
if (keyword === q) return;
|
||||
setKeyword(q);
|
||||
// 自动搜索
|
||||
const doSearch = async () => {
|
||||
@@ -66,7 +66,7 @@ export default function Search() {
|
||||
};
|
||||
doSearch();
|
||||
}
|
||||
}, [router.isReady, router.query.q, keyword, results]);
|
||||
}, [router.isReady, router.query.q, keyword]);
|
||||
|
||||
const handleSearch = async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
+14
-7
@@ -44,13 +44,9 @@ export default function Stats() {
|
||||
router.push('/login');
|
||||
return;
|
||||
}
|
||||
loadTrendData();
|
||||
}, [router, period, currentMonth]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoggedIn()) return;
|
||||
loadCategoryData();
|
||||
}, [router, currentMonth, categoryType]);
|
||||
// 并行加载两个数据集
|
||||
Promise.all([loadTrendData(), loadCategoryData()]);
|
||||
}, [router, period, currentMonth, categoryType]);
|
||||
|
||||
const loadTrendData = async () => {
|
||||
setTrendLoading(true);
|
||||
@@ -75,9 +71,20 @@ export default function Stats() {
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
return () => {
|
||||
// 组件卸载时清理 Chart.js 实例
|
||||
if (trendChartInstance.current) {
|
||||
trendChartInstance.current.destroy();
|
||||
trendChartInstance.current = null;
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (trendChartInstance.current) {
|
||||
trendChartInstance.current.destroy();
|
||||
trendChartInstance.current = null;
|
||||
}
|
||||
if (trendChartRef.current && trendData.length > 0) {
|
||||
const ctx = trendChartRef.current.getContext('2d');
|
||||
|
||||
Reference in New Issue
Block a user