Files
accountbook/frontend/.next/cache/webpack/client-production/6.pack
T

2087 lines
234 KiB
Plaintext
Raw Normal View History

2026-03-12 11:24:10 +08:00
wpc§webpack/lib/cache/PackFileCacheStrategyPackContentItems `
5 ResolverCachePlugin|normal|default|alias=[|server-only$=|next/dist/compiled/server-only/index|client-only$=|next/dist/compiled/client-only/index|next/dist/compiled/client-only$=|next/dist/compiled/client-only/index|next/dist/compiled/server-only=|next/dist/compiled/server-only/index|]|fallback=[|assert=|/opt/accountbook/frontend/node_modules/next/dist/compiled/assert/assert.js|buffer=|/opt/accountbook/frontend/node_modules/next/dist/compiled/buffer/index.js|constants=|/opt/accountbook/frontend/node_modules/next/dist/compiled/constants-browserify/constants.json|crypto=|/opt/accountbook/frontend/node_modules/next/dist/compiled/crypto-browserify/index.js|domain=|/opt/accountbook/frontend/node_modules/next/dist/compiled/domain-browser/index.js|http=|/opt/accountbook/frontend/node_modules/next/dist/compiled/stream-http/index.js|https=|/opt/accountbook/frontend/node_modules/next/dist/compiled/https-browserify/index.js|os=|/opt/accountbook/frontend/node_modules/next/dist/compiled/os-browserify/browser.js|path=|/opt/accountbook/frontend/node_modules/next/dist/compiled/path-browserify/index.js|punycode=|/opt/accountbook/frontend/node_modules/next/dist/compiled/punycode/punycode.js|process=|/opt/accountbook/frontend/node_modules/next/dist/build/polyfills/process.js|querystring=|/opt/accountbook/frontend/node_modules/next/dist/compiled/querystring-es3/index.js|stream=|/opt/accountbook/frontend/node_modules/next/dist/compiled/stream-browserify/index.js|string_decoder=|/opt/accountbook/frontend/node_modules/next/dist/compiled/string_decoder/string_decoder.js|sys=|/opt/accountbook/frontend/node_modules/next/dist/compiled/util/util.js|timers=|/opt/accountbook/frontend/node_modules/next/dist/compiled/timers-browserify/main.js|tty=|/opt/accountbook/frontend/node_modules/next/dist/compiled/tty-browserify/index.js|util=|/opt/accountbook/frontend/node_modules/next/dist/compiled/util/util.js|vm=|/opt/accountbook/frontend/node_modules/next/dist/compiled/vm-browserify/index.js|zlib=|/opt/accountbook/frontend/node_modules/next/dist/compiled/browserify-zlib/index.js|events=|/opt/accountbook/frontend/node_modules/next/dist/compiled/events/events.js|setImmediate=|/opt/accountbook/frontend/node_modules/next/dist/compiled/setimmediate/setImmediate.js|]|dependencyType=|commonjs|path=|/opt/accountbook/frontend|request=|private-next-pages/index.js»Compilation/modules|/opt/accountbook/frontend/node_modules/next/dist/build/webpack/loaders/next-swc-loader.js??ruleSet[1].rules[7].oneOf[3].use[0]!/opt/accountbook/frontend/pages/index.jsÃFlagDependencyExportsPlugin|/opt/accountbook/frontend/node_modules/next/dist/build/webpack/loaders/next-swc-loader.js??ruleSet[1].rules[7].oneOf[3].use[0]!/opt/accountbook/frontend/pages/index.jsÊCompilation/codeGeneration|/opt/accountbook/frontend/node_modules/next/dist/build/webpack/loaders/next-swc-loader.js??ruleSet[1].rules[7].oneOf[3].use[0]!/opt/accountbook/frontend/pages/index.js|webpackÁTerserWebpackPlugin|static/chunks/pages/index-6cb4660fc497dc4e.jsÀTerserWebpackPlugin|static/7amLRQSMmeVKPiHOwuksA/_ssgManifest.jsÂTerserWebpackPlugin|static/7amLRQSMmeVKPiHOwuksA/_buildManifest.js²CssMinimizerPlugin|static/css/cc9ca4f8c39488a1.css½RealContentHashPlugin|analyse|static/css/cc9ca4f8c39488a1.cssËRealContentHashPlugin|analyse|static/chunks/pages/index-6cb4660fc497dc4e.jsÊRealContentHashPlugin|analyse|static/7amLRQSMmeVKPiHOwuksA/_ssgManifest.jsÌRealContentHashPlugin|analyse|static/7amLRQSMmeVKPiHOwuksA/_buildManifest.jsÍRealContentHashPlugin|generate|static/7amLRQSMmeVKPiHOwuksA/_buildManifest.js¥webpack/lib/cache/ResolverCachePlugin`_ResolverCachePluginCacheMiss‡context„path‡request…queryˆfragment†module‰directory„fileˆinternalŽfullySpecified“descriptionFilePath“descriptionFileData“descriptionFileRootŒrelativePath__innerRequest_request__innerRequest_relativePathŽ__innerRequest †issuerissuerLayerˆcompiler†client¨/opt/accountbook/frontend/pages/index.js€€ ¦/opt/accountbook/frontend/package.json„name‡
Å/opt/accountbook/frontend/opt/accountbook/frontend/pages/package.jsonÁ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.jsÄ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.jsÅ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.mjsÅ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.tsxÄ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.tsÅ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.jsxÆ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.jsonÆ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.wasm¬/opt/accountbook/frontend/pages/package.jsonƒ@€¿/opt/accountbook/frontend/opt/accountbook/frontend/package.json¶/opt/accountbook/frontend/opt/accountbook/package.jsonª/opt/accountbook/frontend/opt/package.json˜webpack/lib/NormalModule«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourceŸ‚import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getRecords, getStats, createRecord, updateRecord, deleteRecord } from "../lib/api";
import { DEFAULT_CATEGORIES } from "../lib/categories";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
import { useConfirm } from "../components/ConfirmProvider";
export default function Home() {
const router = useRouter();
const { showToast } = useToast();
const { confirm } = useConfirm();
const [currentMonth, setCurrentMonth] = useState(new Date());
const [typeFilter, setTypeFilter] = useState("all");
const [records, setRecords] = useState([]);
const [stats, setStats] = useState({
income: 0,
expense: 0,
balance: 0
});
const [modalOpen, setModalOpen] = useState(false);
const [editingRecord, setEditingRecord] = useState(null);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [deleting, setDeleting] = useState(false);
const [formError, setFormError] = useState("");
const [formData, setFormData] = useState({
type: "expense",
amount: "",
category: "",
date: new Date().toISOString().split("T")[0],
time: new Date().toTimeString().slice(0, 5),
note: ""
});
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadData();
}, [
router,
currentMonth,
typeFilter
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMoney = (num)=>(num || 0).toFixed(2);
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const resetForm = ()=>({
type: "expense",
amount: "",
category: "",
date: new Date().toISOString().split("T")[0],
time: new Date().toTimeString().slice(0, 5),
note: ""
});
const loadData = async ()=>{
setLoading(true);
try {
const [recordsData, statsData] = await Promise.all([
getRecords(getMonthStr(currentMonth), typeFilter),
getStats(getMonthStr(currentMonth))
]);
setRecords(recordsData);
setStats(statsData);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const prevMonth = ()=>{
const newDate = new Date(currentMonth);
newDate.setMonth(newDate.getMonth() - 1);
setCurrentMonth(newDate);
};
const nextMonth = ()=>{
const newDate = new Date(currentMonth);
newDate.setMonth(newDate.getMonth() + 1);
setCurrentMonth(newDate);
};
const handleCardClick = (type)=>{
setTypeFilter(type === typeFilter ? "all" : type);
};
const openAddModal = ()=>{
setEditingRecord(null);
setFormData(resetForm());
setFormError("");
setModalOpen(true);
};
const openEditModal = (record)=>{
const [datePart, timePart] = record.date.split(" ");
setEditingRecord(record);
setFormData({
type: record.type,
amount: record.amount.toString(),
category: record.category,
date: datePart,
time: timePart || "00:00",
note: record.note || ""
});
setFormError("");
setModalOpen(true);
};
const closeModal = ()=>{
if (submitting || deleting) return;
setModalOpen(false);
setEditingRecord(null);
setFormError("");
};
const handleSubmit = async (e)=>{
e.preventDefault();
if (!formData.amount || !formData.category || !formData.date) {
setFormError("请填写完整信æ¯");
return;
}
setSubmitting(true);
setFormError("");
try {
const data = {
type: formData.type,
amount: parseFloat(formData.amount),
category: formData.category,
date: "".concat(formData.date, " ").concat(formData.time || "00:00"),
note: formData.note
};
if (editingRecord) {
await updateRecord(editingRecord.id, data);
showToast("记录已更新", {
tone: "success"
});
} else {
await createRecord(data);
showToast("记录已添加", {
tone: "success"
});
}
closeModal();
await loadData();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setSubmitting(false);
}
};
const handleDelete = async ()=>{
if (!editingRecord) return;
const shouldDelete = await confirm({
title: "删除这æ¡è®°å½•?",
description: "删除åŽå°†æ— æ³•æ¢å¤ï¼Œè¯·ç¡®è®¤æ˜¯å¦ç»§ç»­ã€‚",
confirmText: "删除"
});
if (!shouldDelete) return;
setDeleting(true);
try {
await deleteRecord(editingRecord.id);
closeModal();
showToast("记录已删除", {
tone: "success"
});
await loadData();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setDeleting(false);
}
};
const groupedRecords = records.reduce((groups, record)=>{
const date = record.date.split(" ")[0];
if (!groups[date]) groups[date] = [];
groups[date].push(record);
return groups;
}, {});
const sortedDates = Object.keys(groupedRecords).sort((a, b)=>b.localeCompare(a));
const getDayExpense = (date)=>groupedRecords[date].filter((record)=>record.type === "expense").reduce((sum, record)=>sum + record.amount, 0);
const homeHeaderContent = /*#__PURE__*/ _jsxs("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ _jsx("div", {
className: "header-left-actions",
children: /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "æœç´¢è®°å½•",
onClick: ()=>router.push("/search"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 18
})
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "month-switcher-container",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "上个月",
onClick: prevMonth,
children: /*#__PURE__*/ _jsx(Icon, {
name: "chevronLeft",
size: 18
})
}),
/*#__PURE__*/ _jsx("span", {
className: "month-switcher__label",
children: formatMonth(currentMonth)
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "下个月",
onClick: nextMonth,
children: /*#__PURE__*/ _jsx(Icon, {
name: "chevronRight",
size: 18
})
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "header-right-actions",
children: /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "settings",
size: 18
})
})
})
]
});
const modalHeaderContent = /*#__PURE__*/ _jsxs("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ _jsx("div", {
className: "header-left-actions",
children: /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "关闭",
onClick: closeModal,
children: /*#__PURE__*/ _jsx(Icon, {
name: "chevronLeft",
size: 18
})
})
}),
/*#__PURE__*/ _jsx("div", {
className: "modal-title-container",
children: /*#__PURE__*/ _jsx("span", {
className: "modal-title",
children: editingRecord ? "编辑记录" : "新增记录"
})
}),
/*#__PURE__*/ _jsx("div", {
className: "header-right-actions"
})
]
});
return /*#__PURE__*/ _jsxs(AppShell, {
headerContent: modalOpen ? modalHeaderContent : homeHeaderContent,
compactHeader: true,
hideNav: modalOpen,
children: [
/*#__PURE__*/ _jsx("section", {
className: "dashboard-hero card",
children: /*#__PURE__*/ _jsxs("div", {
className: "stats-grid stats-grid--hero",
children: [
/*#__PURE__*/ _jsxs("button", {
type: "button",
className: "stat-card ".concat(typeFilter === "income" ? "active" : ""),
onClick: ()=>handleCardClick("income"),
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "æ”¶å…¥"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value income",
children: [
"+",
formatMoney(stats.income)
]
})
]
}),
/*#__PURE__*/ _jsxs("button", {
type: "button",
className: "stat-card ".concat(typeFilter === "expense" ? "active" : ""),
onClick: ()=>handleCardClick("expense"),
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "支出"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value expense",
children: [
"-",
formatMoney(stats.expense)
]
})
]
}),
/*#__PURE__*/ _jsxs("button", {
type: "button",
className: "stat-card stat-card--balance",
onClick: ()=>handleCardClick("all"),
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "结余"
}),
/*#__PURE__*/ _jsx("span", {
className: "value",
children: formatMoney(stats.balance)
})
]
})
]
})
}),
/*#__PURE__*/ _jsxs("section", {
className: "card section-card",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "section-heading section-heading--compact",
children: [
/*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "记录列表"
}),
/*#__PURE__*/ _jsx("h2", {
children: typeFilter === "all" ? "全部记录" : typeFilter === "income" ? "仅看收入" : "仅看支出"
})
]
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-secondary btn-inline",
onClick: openAddModal,
children: /*#__PURE__*/ _jsxs("span", {
className: "button-content",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "plus",
size: 16
}),
"新增记录"
]
})
})
]
}),
loading ? /*#__PURE__*/ _jsx("div", {
className: "loading-state",
children: "加载中..."
}) : records.length === 0 ? /*#__PURE__*/ _jsxs("div", {
className: "empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "info",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "暂无记录,点击上方按钮添加。"
})
]
}) : /*#__PURE__*/ _jsx("div", {
className: "records-list records-list--flush",
children: sortedDates.map((date)=>/*#__PURE__*/ _jsxs("div", {
className: "date-group",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "date-label",
children: [
/*#__PURE__*/ _jsx("span", {
children: new Date(date).toLocaleDateString("zh-CN", {
month: "short",
day: "numeric",
weekday: "short"
})
}),
/*#__PURE__*/ _jsxs("span", {
className: "day-expense",
children: [
"支出 ",
formatMoney(getDayExpense(date))
]
})
]
}),
groupedRecords[date].map((record)=>{
const categories = DEFAULT_CATEGORIES[record.type];
const cat = categories.find((item)=>item.name === record.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "record-item",
onClick: ()=>openEditModal(record),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "record-left",
children: [
/*#__PURE__*/ _jsx("div", {
className: "record-icon",
children: cat.icon
}),
/*#__PURE__*/ _jsxs("div", {
className: "record-info",
children: [
/*#__PURE__*/ _jsx("div", {
className: "record-category",
children: record.category
}),
record.note && /*#__PURE__*/ _jsx("div", {
className: "record-note",
children: record.note
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "record-amount ".concat(record.type),
children: [
record.type === "income" ? "+" : "-",
formatMoney(record.amount)
]
})
]
}, record.id);
})
]
}, date))
})
]
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "fab",
"aria-label": "新增记录",
onClick: openAddModal,
children: /*#__PURE__*/ _jsx(Icon, {
name: "plus",
size: 24
})
}),
modalOpen && /*#__PURE__*/ _jsx("div", {
className: "modal-overlay modal-overlay--fullscreen",
onClick: closeModal,
children: /*#__PURE__*/ _jsxs("div", {
className: "modal modal--fullscreen",
onClick: (e)=>e.stopPropagation(),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "modal-header",
children: [
/*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: editingRecord ? "编辑记录" : "新增记录"
}),
editingRecord ? /*#__PURE__*/ _jsx("h2", {
children: "更新账目"
}) : null
]
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button icon-button--ghost",
"aria-label": "关闭弹窗",
onClick: closeModal,
children: /*#__PURE__*/ _jsx(Icon, {
name: "close",
size: 18
})
})
]
}),
formError && /*#__PURE__*/ _jsx("div", {
className: "error-msg",
children: formError
}),
/*#__PURE__*/ _jsxs("form", {
onSubmit: handleSubmit,
children: [
/*#__PURE__*/ _jsxs("div", {
className: "type-switch",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "type-btn expense ".concat(formData.type === "expense" ? "active" : ""),
onClick: ()=>setFormData({
...formData,
type: "expense",
category: ""
}),
disabled: submitting || deleting,
children: "支出"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "type-btn income ".concat(formData.type === "income" ? "active" : ""),
onClick: ()=>setFormData({
...formData,
type: "income",
category: ""
}),
disabled: submitting || deleting,
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
htmlFor: "record-amount",
children: "金é¢"
}),
/*#__PURE__*/ _jsx("input", {
id: "record-amount",
type: "number",
className: "amount-input",
value: formData.amount,
onChange: (e)=>setFormData({
...formData,
amount: e.target.value
}),
placeholder: "0.00",
step: "0.01",
required: true,
disabled: submitting || deleting
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
children: "分类"
}),
/*#__PURE__*/ _jsx("div", {
className: "category-grid",
children: DEFAULT_CATEGORIES[formData.type].map((cat)=>/*#__PURE__*/ _jsxs("button", {
type: "button",
className: "category-btn ".concat(formData.category === cat.name ? "active" : ""),
onClick: ()=>setFormData({
...formData,
category: cat.name
}),
disabled: submitting || deleting,
children: [
/*#__PURE__*/ _jsx("span", {
className: "icon",
children: cat.icon
}),
/*#__PURE__*/ _jsx("span", {
children: cat.name
})
]
}, cat.name))
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
children: "日期"
}),
/*#__PURE__*/ _jsxs("div", {
className: "datetime-picker",
children: [
/*#__PURE__*/ _jsx("input", {
type: "date",
value: formData.date,
onChange: (e)=>setFormData({
...formData,
date: e.target.value
}),
required: true,
disabled: submitting || deleting
}),
/*#__PURE__*/ _jsx("input", {
type: "time",
value: formData.time || "00:00",
onChange: (e)=>setFormData({
...formData,
time: e.target.value
}),
disabled: submitting || deleting
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
htmlFor: "record-note",
children: "备注(å¯é€‰ï¼‰"
}),
/*#__PURE__*/ _jsx("input", {
id: "record-note",
type: "text",
value: formData.note,
onChange: (e)=>setFormData({
...formData,
note: e.target.value
}),
placeholder: "添加备注...",
disabled: submitting || deleting
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "modal-actions",
children: [
/*#__PURE__*/ _jsx("button", {
type: "submit",
className: "btn btn-primary",
disabled: submitting || deleting,
children: submitting ? /*#__PURE__*/ _jsxs("span", {
className: "button-content",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "spinner",
size: 16,
className: "is-spinning"
}),
"ä¿å­˜ä¸­..."
]
}) : "ä¿å­˜"
}),
editingRecord && /*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-danger",
onClick: handleDelete,
disabled: submitting || deleting,
children: deleting ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
exportsTypeŽsideEffectFree‰namespace 
javascript/auto…alias¤ˆfallbackŒserver-only$Œclient-only$Ÿnext/dist/compiled/client-only$žnext/dist/compiled/server-only¤next/dist/compiled/server-only/index¤next/dist/compiled/client-only/indexÿþ
`†assert†buffer‰constants†crypto†domain„http…httpsosŠˆpunycode‡processquerystring†streamŽstring_decoderƒsys†timersƒtty„utilvm„zlib†eventsŒsetImmediateÊ/opt/accountbook/frontend/node_modules/next/dist/compiled/assert/assert.jsÉ/opt/accountbook/frontend/node_modules/next/dist/compiled/buffer/index.jsÝ/opt/accountbook/frontend/node_modules/next/dist/compiled/constants-browserify/constants.jsonÔ/opt/accountbook/frontend/node_modules/next/dist/compiled/crypto-browserify/index.jsÑ/opt/accountbook/frontend/node_modules/next/dist/compiled/domain-browser/index.jsÎ/opt/accountbook/frontend/node_modules/next/dist/compiled/stream-http/index.jsÓ/opt/accountbook/frontend/node_modules/next/dist/compiled/https-browserify/index.jsÒ/opt/accountbook/frontend/node_modules/next/dist/compiled/os-browserify/browser.jsÒ/opt/accountbook/frontend/node_modules/next/dist/compiled/path-browserify/index.jsÎ/opt/accountbook/frontend/node_modules/next/dist/compiled/punycode/punycode.jsË/opt/accountbook/frontend/node_modules/next/dist/build/polyfills/process.jsÒ/opt/accountbook/frontend/node_modules/next/dist/compiled/querystring-es3/index.jsÔ/opt/accountbook/frontend/node_modules/next/dist/compiled/stream-browserify/index.jsÚ/opt/accountbook/frontend/node_modules/next/dist/compiled/string_decoder/string_decoder.jsÆ/opt/accountbook/frontend/node_modules/next/dist/compiled/util/util.jsÓ/opt/accountbook/frontend/node_modules/next/dist/compiled/timers-browserify/main.jsÑ/opt/accountbook/frontend/node_modules/next/dist/compiled/tty-browserify/index.jsýÐ/opt/accountbook/frontend/node_modules/next/dist/compiled/vm-browserify/index.jsÒ/opt/accountbook/frontend/node_modules/next/dist/compiled/browserify-zlib/index.jsÊ/opt/accountbook/frontend/node_modules/next/dist/compiled/events/events.jsÖ/opt/accountbook/frontend/node_modules/next/dist/compiled/setimmediate/setImmediate.js
Å`‰cacheable†parsedfileDependencies“contextDependencies“missingDependenciesbuildDependenciesvalueDependencies™†assetsŠassetsInfo†strictexportsArgument”topLevelDeclarationsˆsnapshot ˜webpack/lib/util/LazySetÙ/opt/accountbook/frontend/node_modules/next/dist/build/webpack/loaders/next-swc-loader.js ™webpack/DefinePlugin_hashˆ898e146963a13a4c04152a1a “__webpack_exports__‰*default*„Home PC“¸ÍyB dÿÿÿ` ·webpack/lib/dependencies/HarmonyCompatibilityDependency
dÿÿý¨webpack/lib/dependencies/ConstDependency`?
`?a@l
`,`m@•
`(Aþ
`hAÿ6
`7A7e
`.Af
`+AÉ
`7
`;¶webpack/lib/dependencies/HarmonyExportHeaderDependency AP AP
ºwebpack/lib/dependencies/HarmonyImportSideEffectDependencyreact/jsx-runtimeÿ
`?\ÿÿÿ\ÿÿÿ
`,next/routerÿ
`(Š../lib/apiÿ
`h../lib/categoriesÿ
`7../components/AppShellÿ
`.“../components/iconsÿ
`+../components/ToastProviderÿ
`7 ../components/ConfirmProviderÿ
`;¹webpack/lib/dependencies/HarmonyExportSpecifierDependency`Ó‡default
¹webpack/lib/dependencies/HarmonyImportSpecifierDependencya ‰useRouterþ A:C  
úëëý
c   ˆuseToastþ Aai  úððþ
c  " ŠuseConfirmþ A…  õ ííþ
c

" ˆuseStateþ A¿Ç  ð7ÿÿÿ7ÿÿÿþ
c,4 ûú   ì3ÿÿÿ3ÿÿÿþ
c(0 ÷ö A08  è/ÿÿÿ/ÿÿÿþ
c"* óò A\d  ä+ÿÿÿ+ÿÿÿþ
c& ïî ×  à'ÿÿÿ'ÿÿÿþ
c&. ëê A  Ü#ÿÿÿ#ÿÿÿþ
c.6 çæ A@H  Øÿÿÿÿÿÿþ
c"* ãâ Ax  Ôÿÿÿÿÿÿþ
c(0 ßÞ µ  Ðÿÿÿÿÿÿþ
c$, ÛÚ ì  Ìÿÿÿÿÿÿþ
c&. ×Ö A  Èÿÿÿÿÿÿþ
c$, ‰useEffectþ õ  Ã
ÿÿÿ
ÿÿÿþ
c$$
 ŠisLoggedInþ A   ¾¬¬þ
c%
% ŠgetRecordsþ Af p   ¹§§þ
c>> ˆgetStatsþ ±   ´¢¢þ
c?? ŒupdateRecordþ A+  ¯þ
@„`@„`" ŒcreateRecordþ Ù  ª˜˜þ
@‰`@‰`" ŒdeleteRecordþ ²  ¥þ
`` „jsxs…_jsxs A   Ÿˆˆþ
`,@º`1 ƒjsx„_jsx A†Š  þ
`` úû ò  ~ÿÿÿ~ÿÿÿþ
@¿`(@¿`, ö÷ ä  zÿÿÿzÿÿÿþ
`,@Ä`0 „Iconþ é 4Œþ
`1@Ä`5 çè   ˆqÿÿÿqÿÿÿþ
`` éê A  mÿÿÿmÿÿÿþ
`"@Í`& åæ A  iÿÿÿiÿÿÿþ
`0@Ò`4ðï A  4}ÿÿÿqÿÿÿqÿÿÿþ
`5@Ò`9 Þß ¾  yÿÿÿbÿÿÿbÿÿÿþ
@×`"@×`& ÚÛ A{  uÿÿÿ^ÿÿÿ^ÿÿÿþ
`"@Û`& Ö× Aos  qÿÿÿZÿÿÿZÿÿÿþ
`0@à`4áà Atx 4nÿÿÿbÿÿÿbÿÿÿþ
`5@à`9 ÏÐ AA E   jÿÿÿSÿÿÿSÿÿÿþ
`` ËÌ ®   fÿÿÿOÿÿÿOÿÿÿþ
`(@é`, ÇÈ Aœ! !  bÿÿÿKÿÿÿKÿÿÿþ
`,@î`0ÒÑ A¡!¥! 4_ÿÿÿSÿÿÿSÿÿÿþ
`1@î`5 º» Al"q"  [ÿÿÿDÿÿÿDÿÿÿþ
`-@ö`2 ¼½ A×"Û"  Wÿÿÿ@ÿÿÿ@ÿÿÿþ
`` ¸¹ A?#C#  Sÿÿÿ<ÿÿÿ<ÿÿÿþ
`(@û`, ´µ A$#$  Oÿÿÿ8ÿÿÿ8ÿÿÿþ
@`,@`0¿¾ A$$($ 4Lÿÿÿ@ÿÿÿ@ÿÿÿþ
@`1@`5 ­® AÎ$Ò$  Hÿÿÿ1ÿÿÿ1ÿÿÿþ
@`@` ©ª A8%<%  Dÿÿÿ-ÿÿÿ-ÿÿÿþ
@`(@`, ¥¦ Að%ô%  @ÿÿÿ)ÿÿÿ)ÿÿÿþ
@
`@
` œ Aj&o&  <ÿÿÿ%ÿÿÿ%ÿÿÿþ
@`@` 5ÿÿÿˆAppShell Ap&x& 47ÿÿÿ)ÿÿÿ)ÿÿÿþ
@`@`' ˜ A.'2'  3ÿÿÿÿÿÿÿÿÿþ
@`@` Ž Aš'Ÿ'  /ÿÿÿÿÿÿÿÿÿþ
@`(@`- Š A-(2(  +ÿÿÿÿÿÿÿÿÿþ
@`&@`+ Œ Aj)n)  'ÿÿÿÿÿÿÿÿÿþ
@!`.@!`2 ƒ A6*;*  #ÿÿÿ ÿÿÿ ÿÿÿþ
@%`.@%`3 ~ÿÿÿÿÿÿ AÍ+Ò+  ÿÿÿÿÿÿÿÿÿþ
@.`&@.`+  A --  ÿÿÿÿÿÿÿÿÿþ
@3`.@3`2 vÿÿÿwÿÿÿ AØ-Ý-  ÿÿÿÿÿÿÿÿÿþ
@7`.@7`3 rÿÿÿsÿÿÿ Aq/v/  ÿÿÿüþÿÿüþÿÿþ
@@`&@@`+ tÿÿÿuÿÿÿ A0‘0  ÿÿÿøþÿÿøþÿÿþ
@E`.@E`2 pÿÿÿqÿÿÿ AY1]1   ÿÿÿôþÿÿôþÿÿþ
@I`.@I`2 fÿÿÿgÿÿÿ A˜22  ÿÿÿðþÿÿðþÿÿþ
@R`@R` bÿÿÿcÿÿÿ A33  ÿÿÿìþÿÿìþÿÿþ
@U`"@U`' ^ÿÿÿ_ÿÿÿ AÅ3Ê3  ÿþÿÿèþÿÿèþÿÿþ
@X`*@X`/ `ÿÿÿaÿÿÿ A2464  ûþÿÿäþÿÿäþÿÿþ
@Z`2@Z`6 \ÿÿÿ]ÿÿÿ A55  ÷þÿÿàþÿÿàþÿÿþ
@^`2@^`6 XÿÿÿYÿÿÿ A+6/6  óþÿÿÜþÿÿÜþÿÿþ
@c`*@c`. NÿÿÿOÿÿÿ A&7+7  ïþÿÿØþÿÿØþÿÿþ
@g`8@g`= PÿÿÿQÿÿÿ AÝ7á7  ëþÿÿÔþÿÿÔþÿÿþ
@j`6@j`:[ÿÿÿZÿÿÿ Aâ7æ7 4èþÿÿÜþÿÿÜþÿÿþ
@j`;@j`? IÿÿÿJÿÿÿ Az9~9  äþÿÿÍþÿÿÍþÿÿþ
@t`,@t`0 ?ÿÿÿ@ÿÿÿ A%:*:  àþÿÿÉþÿÿÉþÿÿþ
@w`>@w`C AÿÿÿBÿÿÿ AÆ:Ê:  ÜþÿÿÅþÿÿÅþÿÿþ
@z`*@z`. =ÿÿÿ>ÿÿÿ 
CC 4¸þÿÿ¨þÿÿ¨þÿÿþ
`;@ž`M ÿÿÿÿÿÿ ADDID  ´þÿÿþÿÿþÿÿþ
`=@¢`B ÿÿÿÿÿÿ AžE£E  °þÿÿ™þÿÿ™þÿÿþ
`>@§`C ÿÿÿÿÿÿ AF…F  ¬þÿÿ•þÿÿ•þÿÿþ
`F@ª`J ÿÿÿÿÿÿ A¶G»G  ¨þÿÿ‘þÿÿ‘þÿÿþ
`F@®`K  ÿÿÿ
ÿÿÿ A±HµH  ¤þÿÿþÿÿþÿÿþ
`N@±`R ÿÿÿÿÿÿ A J$J   þÿÿ‰þÿÿ‰þÿÿþ
`]@µ`a ûþÿÿüþÿÿ AJLOL  œþÿÿ…þÿÿ…þÿÿþ
`>@½`C ýþÿÿþþÿÿ A?OCO  ˜þÿÿþÿÿþÿÿþ
`` ùþÿÿúþÿÿ AP P  ”þÿÿ}þÿÿ}þÿÿþ
`(@Ñ`,ÿÿÿÿÿÿ A PP 4‘þÿÿ…þÿÿ…þÿÿþ
`-@Ñ`1 òþÿÿóþÿÿ AP¡P  þÿÿvþÿÿvþÿÿþ
`'@Ö`+ èþÿÿéþÿÿ A>QCQ  ‰þÿÿrþÿÿrþÿÿþ
`(@Ù`- äþÿÿåþÿÿ AR R  …þÿÿnþÿÿnþÿÿþ
`&@Ý`+ àþÿÿáþÿÿ A R¥R  þÿÿjþÿÿjþÿÿþ
`.@à`3 âþÿÿãþÿÿ ASS  }þÿÿfþÿÿfþÿÿþ
`6@â`: Þþÿÿßþÿÿ A+T/T  yþÿÿbþÿÿbþÿÿþ
`F@æ`J ÚþÿÿÛþÿÿ AU#U  uþÿÿ^þÿÿ^þÿÿþ
`.@ë`2 Öþÿÿ×þÿÿ AdVhV  qþÿÿZþÿÿZþÿÿþ
`<@ð`@áþÿÿàþÿÿ AiVmV 4nþÿÿbþÿÿbþÿÿþ
`A@ð`E ÏþÿÿÐþÿÿ AW”W  jþÿÿSþÿÿSþÿÿþ
`3@÷`7 ÅþÿÿÆþÿÿ ADXIX  fþÿÿOþÿÿOþÿÿþ
`&@û`+ ÁþÿÿÂþÿÿ AÞXãX  bþÿÿKþÿÿKþÿÿþ
`.@þ`3 ÃþÿÿÄþÿÿ A‘Y•Y  ^þÿÿGþÿÿGþÿÿþ
@`6@`: ¿þÿÿÀþÿÿ A‹\\  ZþÿÿCþÿÿCþÿÿþ
@ `6@ `: µþÿÿ¶þÿÿ AÃ_È_  Vþÿÿ?þÿÿ?þÿÿþ
@`.@`3 ·þÿÿ¸þÿÿ Au`y`  Rþÿÿ;þÿÿ;þÿÿþ
@`6@`: ³þÿÿ´þÿÿ Ahala  Nþÿÿ7þÿÿ7þÿÿþ
@ `6@ `: ©þÿÿªþÿÿ A0e5e  Jþÿÿ3þÿÿ3þÿÿþ
@0`.@0`3 «þÿÿ¬þÿÿ Aâeæe  Fþÿÿ/þÿÿ/þÿÿþ
@3`6@3`: §þÿÿ¨þÿÿ Af“f  Bþÿÿ+þÿÿ+þÿÿþ
@6`6@6`: Ag-g 4?þÿÿ/þÿÿ/þÿÿþ
@8`6@8`H šþÿÿ›þÿÿ AVg[g  ;þÿÿ$þÿÿ$þÿÿþ
@8`q@8`v œþÿÿþÿÿ A2j6j  7þÿÿ þÿÿ þÿÿþ
@A`F@A`J ˜þÿÿ™þÿÿ Aakek  3þÿÿþÿÿþÿÿþ
@E`F@E`J Žþÿÿþÿÿ Amm  /þÿÿþÿÿþÿÿþ
@M`.@M`3 þÿÿ‘þÿÿ AÃmÇm  +þÿÿþÿÿþÿÿþ
@P`6@P`: †þÿÿ‡þÿÿ Apnun  'þÿÿþÿÿþÿÿþ
@S`6@S`; ˆþÿÿ‰þÿÿ A?oCo  #þÿÿ þÿÿ þÿÿþ
@V`>@V`B „þÿÿ…þÿÿ A
rr  þÿÿþÿÿþÿÿþ
@``>@``B zþÿÿ{þÿÿ A4u9u  þÿÿþÿÿþÿÿþ
@m`.@m`3 |þÿÿ}þÿÿ Aæuêu  þÿÿþÿÿþÿÿþ
@p`6@p`: xþÿÿyþÿÿ AÛvßv  þÿÿüýÿÿüýÿÿþ
@t`6@t`: nþÿÿoþÿÿ Aáyæy  þÿÿøýÿÿøýÿÿþ
@`.@`3 pþÿÿqþÿÿ A–zšz   þÿÿôýÿÿôýÿÿþ
@„`6@„`: fþÿÿgþÿÿ AÌ{Ñ{  þÿÿðýÿÿðýÿÿþ
@ˆ`Q@ˆ`V hþÿÿiþÿÿ A§|«|  þÿÿìýÿÿìýÿÿþ
@`B@`Fsþÿÿrþÿÿ A¬|°| 4þÿÿôýÿÿôýÿÿþ
@`G@`K aþÿÿbþÿÿ Aà~ä~  üýÿÿåýÿÿåýÿÿþ
@”`G@”`K —webpack/lib/ModuleGraph“RestoreProvidedData

-ýÿÿˆprovidedcanMangleProvideterminalBindingexportsInfoíýÿÿ

‡sources“runtimeRequirements„dataŠjavascript«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource 8«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourceñ__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Home; }
/* harmony export */ });
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5893);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7294);
/* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1163);
/* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(next_router__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _lib_api__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(610);
/* harmony import */ var _lib_categories__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(7063);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(5546);
/* harmony import */ var _components_ConfirmProvider__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(8846);
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceŸ‚import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getRecords, getStats, createRecord, updateRecord, deleteRecord } from "../lib/api";
import { DEFAULT_CATEGORIES } from "../lib/categories";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
import { useConfirm } from "../components/ConfirmProvider";
export default function Home() {
const router = useRouter();
const { showToast } = useToast();
const { confirm } = useConfirm();
const [currentMonth, setCurrentMonth] = useState(new Date());
const [typeFilter, setTypeFilter] = useState("all");
const [records, setRecords] = useState([]);
const [stats, setStats] = useState({
income: 0,
expense: 0,
balance: 0
});
const [modalOpen, setModalOpen] = useState(false);
const [editingRecord, setEditingRecord] = useState(null);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [deleting, setDeleting] = useState(false);
const [formError, setFormError] = useState("");
const [formData, setFormData] = useState({
type: "expense",
amount: "",
category: "",
date: new Date().toISOString().split("T")[0],
time: new Date().toTimeString().slice(0, 5),
note: ""
});
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadData();
}, [
router,
currentMonth,
typeFilter
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMoney = (num)=>(num || 0).toFixed(2);
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const resetForm = ()=>({
type: "expense",
amount: "",
category: "",
date: new Date().toISOString().split("T")[0],
time: new Date().toTimeString().slice(0, 5),
note: ""
});
const loadData = async ()=>{
setLoading(true);
try {
const [recordsData, statsData] = await Promise.all([
getRecords(getMonthStr(currentMonth), typeFilter),
getStats(getMonthStr(currentMonth))
]);
setRecords(recordsData);
setStats(statsData);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const prevMonth = ()=>{
const newDate = new Date(currentMonth);
newDate.setMonth(newDate.getMonth() - 1);
setCurrentMonth(newDate);
};
const nextMonth = ()=>{
const newDate = new Date(currentMonth);
newDate.setMonth(newDate.getMonth() + 1);
setCurrentMonth(newDate);
};
const handleCardClick = (type)=>{
setTypeFilter(type === typeFilter ? "all" : type);
};
const openAddModal = ()=>{
setEditingRecord(null);
setFormData(resetForm());
setFormError("");
setModalOpen(true);
};
const openEditModal = (record)=>{
const [datePart, timePart] = record.date.split(" ");
setEditingRecord(record);
setFormData({
type: record.type,
amount: record.amount.toString(),
category: record.category,
date: datePart,
time: timePart || "00:00",
note: record.note || ""
});
setFormError("");
setModalOpen(true);
};
const closeModal = ()=>{
if (submitting || deleting) return;
setModalOpen(false);
setEditingRecord(null);
setFormError("");
};
const handleSubmit = async (e)=>{
e.preventDefault();
if (!formData.amount || !formData.category || !formData.date) {
setFormError("请填写完整信æ¯");
return;
}
setSubmitting(true);
setFormError("");
try {
const data = {
type: formData.type,
amount: parseFloat(formData.amount),
category: formData.category,
date: "".concat(formData.date, " ").concat(formData.time || "00:00"),
note: formData.note
};
if (editingRecord) {
await updateRecord(editingRecord.id, data);
showToast("记录已更新", {
tone: "success"
});
} else {
await createRecord(data);
showToast("记录已添加", {
tone: "success"
});
}
closeModal();
await loadData();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setSubmitting(false);
}
};
const handleDelete = async ()=>{
if (!editingRecord) return;
const shouldDelete = await confirm({
title: "删除这æ¡è®°å½•?",
description: "删除åŽå°†æ— æ³•æ¢å¤ï¼Œè¯·ç¡®è®¤æ˜¯å¦ç»§ç»­ã€‚",
confirmText: "删除"
});
if (!shouldDelete) return;
setDeleting(true);
try {
await deleteRecord(editingRecord.id);
closeModal();
showToast("记录已删除", {
tone: "success"
});
await loadData();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setDeleting(false);
}
};
const groupedRecords = records.reduce((groups, record)=>{
const date = record.date.split(" ")[0];
if (!groups[date]) groups[date] = [];
groups[date].push(record);
return groups;
}, {});
const sortedDates = Object.keys(groupedRecords).sort((a, b)=>b.localeCompare(a));
const getDayExpense = (date)=>groupedRecords[date].filter((record)=>record.type === "expense").reduce((sum, record)=>sum + record.amount, 0);
const homeHeaderContent = /*#__PURE__*/ _jsxs("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ _jsx("div", {
className: "header-left-actions",
children: /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "æœç´¢è®°å½•",
onClick: ()=>router.push("/search"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 18
})
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "month-switcher-container",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "上个月",
onClick: prevMonth,
children: /*#__PURE__*/ _jsx(Icon, {
name: "chevronLeft",
size: 18
})
}),
/*#__PURE__*/ _jsx("span", {
className: "month-switcher__label",
children: formatMonth(currentMonth)
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "下个月",
onClick: nextMonth,
children: /*#__PURE__*/ _jsx(Icon, {
name: "chevronRight",
size: 18
})
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "header-right-actions",
children: /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "settings",
size: 18
})
})
})
]
});
const modalHeaderContent = /*#__PURE__*/ _jsxs("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ _jsx("div", {
className: "header-left-actions",
children: /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "关闭",
onClick: closeModal,
children: /*#__PURE__*/ _jsx(Icon, {
name: "chevronLeft",
size: 18
})
})
}),
/*#__PURE__*/ _jsx("div", {
className: "modal-title-container",
children: /*#__PURE__*/ _jsx("span", {
className: "modal-title",
children: editingRecord ? "编辑记录" : "新增记录"
})
}),
/*#__PURE__*/ _jsx("div", {
className: "header-right-actions"
})
]
});
return /*#__PURE__*/ _jsxs(AppShell, {
headerContent: modalOpen ? modalHeaderContent : homeHeaderContent,
compactHeader: true,
hideNav: modalOpen,
children: [
/*#__PURE__*/ _jsx("section", {
className: "dashboard-hero card",
children: /*#__PURE__*/ _jsxs("div", {
className: "stats-grid stats-grid--hero",
children: [
/*#__PURE__*/ _jsxs("button", {
type: "button",
className: "stat-card ".concat(typeFilter === "income" ? "active" : ""),
onClick: ()=>handleCardClick("income"),
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "æ”¶å…¥"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value income",
children: [
"+",
formatMoney(stats.income)
]
})
]
}),
/*#__PURE__*/ _jsxs("button", {
type: "button",
className: "stat-card ".concat(typeFilter === "expense" ? "active" : ""),
onClick: ()=>handleCardClick("expense"),
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "支出"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value expense",
children: [
"-",
formatMoney(stats.expense)
]
})
]
}),
/*#__PURE__*/ _jsxs("button", {
type: "button",
className: "stat-card stat-card--balance",
onClick: ()=>handleCardClick("all"),
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "结余"
}),
/*#__PURE__*/ _jsx("span", {
className: "value",
children: formatMoney(stats.balance)
})
]
})
]
})
}),
/*#__PURE__*/ _jsxs("section", {
className: "card section-card",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "section-heading section-heading--compact",
children: [
/*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "记录列表"
}),
/*#__PURE__*/ _jsx("h2", {
children: typeFilter === "all" ? "全部记录" : typeFilter === "income" ? "仅看收入" : "仅看支出"
})
]
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-secondary btn-inline",
onClick: openAddModal,
children: /*#__PURE__*/ _jsxs("span", {
className: "button-content",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "plus",
size: 16
}),
"新增记录"
]
})
})
]
}),
loading ? /*#__PURE__*/ _jsx("div", {
className: "loading-state",
children: "加载中..."
}) : records.length === 0 ? /*#__PURE__*/ _jsxs("div", {
className: "empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "info",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "暂无记录,点击上方按钮添加。"
})
]
}) : /*#__PURE__*/ _jsx("div", {
className: "records-list records-list--flush",
children: sortedDates.map((date)=>/*#__PURE__*/ _jsxs("div", {
className: "date-group",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "date-label",
children: [
/*#__PURE__*/ _jsx("span", {
children: new Date(date).toLocaleDateString("zh-CN", {
month: "short",
day: "numeric",
weekday: "short"
})
}),
/*#__PURE__*/ _jsxs("span", {
className: "day-expense",
children: [
"支出 ",
formatMoney(getDayExpense(date))
]
})
]
}),
groupedRecords[date].map((record)=>{
const categories = DEFAULT_CATEGORIES[record.type];
const cat = categories.find((item)=>item.name === record.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "record-item",
onClick: ()=>openEditModal(record),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "record-left",
children: [
/*#__PURE__*/ _jsx("div", {
className: "record-icon",
children: cat.icon
}),
/*#__PURE__*/ _jsxs("div", {
className: "record-info",
children: [
/*#__PURE__*/ _jsx("div", {
className: "record-category",
children: record.category
}),
record.note && /*#__PURE__*/ _jsx("div", {
className: "record-note",
children: record.note
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "record-amount ".concat(record.type),
children: [
record.type === "income" ? "+" : "-",
formatMoney(record.amount)
]
})
]
}, record.id);
})
]
}, date))
})
]
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "fab",
"aria-label": "新增记录",
onClick: openAddModal,
children: /*#__PURE__*/ _jsx(Icon, {
name: "plus",
size: 24
})
}),
modalOpen && /*#__PURE__*/ _jsx("div", {
className: "modal-overlay modal-overlay--fullscreen",
onClick: closeModal,
children: /*#__PURE__*/ _jsxs("div", {
className: "modal modal--fullscreen",
onClick: (e)=>e.stopPropagation(),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "modal-header",
children: [
/*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: editingRecord ? "编辑记录" : "新增记录"
}),
editingRecord ? /*#__PURE__*/ _jsx("h2", {
children: "更新账目"
}) : null
]
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button icon-button--ghost",
"aria-label": "关闭弹窗",
onClick: closeModal,
children: /*#__PURE__*/ _jsx(Icon, {
name: "close",
size: 18
})
})
]
}),
formError && /*#__PURE__*/ _jsx("div", {
className: "error-msg",
children: formError
}),
/*#__PURE__*/ _jsxs("form", {
onSubmit: handleSubmit,
children: [
/*#__PURE__*/ _jsxs("div", {
className: "type-switch",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "type-btn expense ".concat(formData.type === "expense" ? "active" : ""),
onClick: ()=>setFormData({
...formData,
type: "expense",
category: ""
}),
disabled: submitting || deleting,
children: "支出"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "type-btn income ".concat(formData.type === "income" ? "active" : ""),
onClick: ()=>setFormData({
...formData,
type: "income",
category: ""
}),
disabled: submitting || deleting,
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
htmlFor: "record-amount",
children: "金é¢"
}),
/*#__PURE__*/ _jsx("input", {
id: "record-amount",
type: "number",
className: "amount-input",
value: formData.amount,
onChange: (e)=>setFormData({
...formData,
amount: e.target.value
}),
placeholder: "0.00",
step: "0.01",
required: true,
disabled: submitting || deleting
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
children: "分类"
}),
/*#__PURE__*/ _jsx("div", {
className: "category-grid",
children: DEFAULT_CATEGORIES[formData.type].map((cat)=>/*#__PURE__*/ _jsxs("button", {
type: "button",
className: "category-btn ".concat(formData.category === cat.name ? "active" : ""),
onClick: ()=>setFormData({
...formData,
category: cat.name
}),
disabled: submitting || deleting,
children: [
/*#__PURE__*/ _jsx("span", {
className: "icon",
children: cat.icon
}),
/*#__PURE__*/ _jsx("span", {
children: cat.name
})
]
}, cat.name))
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
children: "日期"
}),
/*#__PURE__*/ _jsxs("div", {
className: "datetime-picker",
children: [
/*#__PURE__*/ _jsx("input", {
type: "date",
value: formData.date,
onChange: (e)=>setFormData({
...formData,
date: e.target.value
}),
required: true,
disabled: submitting || deleting
}),
/*#__PURE__*/ _jsx("input", {
type: "time",
value: formData.time || "00:00",
onChange: (e)=>setFormData({
...formData,
time: e.target.value
}),
disabled: submitting || deleting
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
htmlFor: "record-note",
children: "备注(å¯é€‰ï¼‰"
}),
/*#__PURE__*/ _jsx("input", {
id: "record-note",
type: "text",
value: formData.note,
onChange: (e)=>setFormData({
...formData,
note: e.target.value
}),
placeholder: "添加备注...",
disabled: submitting || deleting
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "modal-actions",
children: [
/*#__PURE__*/ _jsx("button", {
type: "submit",
className: "btn btn-primary",
disabled: submitting || deleting,
children: submitting ? /*#__PURE__*/ _jsxs("span", {
className: "button-content",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "spinner",
size: 16,
className: "is-spinning"
}),
"ä¿å­˜ä¸­..."
]
}) : "ä¿å­˜"
}),
editingRecord && /*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-danger",
onClick: handleDelete,
disabled: submitting || deleting,
children: deleting ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
c>@km_”ýÿ57dfÈÊ:BahŽ¿Æý07\cÏÖ_@Gx­´äëìô f o © ° *ÍØ¦±îñàå芎
º½{~ortwA D ª ­ œ!Ÿ!¡!¤!l"p"×"Ú"?#_B#$"$$$'$Î$Ñ$8%;%ð%ó%j&n&p&w&.'1'š'ž'-(1(j)m)6*:*Í+Ñ+ --Ø-Ü-q/_u/00Y1\1˜2œ233Å3É3245455+6.6&7*7Ý7à7â7å7z9}9%:):Æ:É:L;O;Q;_T;%<(<Ì<Ï<i=m=>>Í>Ð>­@±@
CCDDHDžE¢EF„F¶GºG±H´H J#JJLNL?OBOP_
P PPP P>QBQRR R¤RSS+T.TU"UdVgViVlVW“WDXHXÞXâXY”Y\Ž\Ã__Ç_u`x`haka0e4eâeåeffg,gVgZg2j5jakdkmmÃmÆmpntn?oBo
rr4u8uæuNéuÛvÞváyåy–z™zÌ{Ð{§|ª|¬|¯|à~ã~¶(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)Ì(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)Ð(0,_components_ConfirmProvider__WEBPACK_IMPORTED_MODULE_7__/* .useConfirm */ .N)¯(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)ÿÿÿÿÿÿÿÿÿÿ°(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)¾(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .isLoggedIn */ .jl)¾(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getRecords */ .Q6)¼(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getStats */ .fy)À(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecord */ .Vs)À(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .createRecord */ .ae)À(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .deleteRecord */ .JS)·(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)¶(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)ÿÿ¼_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .JýþþÿþþþÿþþþÿýþþþÿþþþýÅ_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .ZýüüýüüýüüýýüüüýýýüýþýüýýþýýüüýüÈ_lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .Sûûüûüüûüüýüûûûüüüüýüûûüüûüüûüüÿûüüûüûüüûüüûüûüýüvýÿÿ†source„size„maps?ýÿÿ__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Home; }
/* harmony export */ });
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(5893);
/* harmony import */ var react__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(7294);
/* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(1163);
/* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(next_router__WEBPACK_IMPORTED_MODULE_2__);
/* harmony import */ var _lib_api__WEBPACK_IMPORTED_MODULE_8__ = __webpack_require__(610);
/* harmony import */ var _lib_categories__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(7063);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(5546);
/* harmony import */ var _components_ConfirmProvider__WEBPACK_IMPORTED_MODULE_7__ = __webpack_require__(8846);
function Home() {
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const { showToast } = (0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)();
const { confirm } = (0,_components_ConfirmProvider__WEBPACK_IMPORTED_MODULE_7__/* .useConfirm */ .N)();
const [currentMonth, setCurrentMonth] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(new Date());
const [typeFilter, setTypeFilter] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("all");
const [records, setRecords] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [stats, setStats] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)({
income: 0,
expense: 0,
balance: 0
});
const [modalOpen, setModalOpen] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
const [editingRecord, setEditingRecord] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
const [loading, setLoading] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(true);
const [submitting, setSubmitting] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
const [deleting, setDeleting] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
const [formError, setFormError] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("");
const [formData, setFormData] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)({
type: "expense",
amount: "",
category: "",
date: new Date().toISOString().split("T")[0],
time: new Date().toTimeString().slice(0, 5),
note: ""
});
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .isLoggedIn */ .jl)()) {
router.push("/login");
return;
}
loadData();
}, [
router,
currentMonth,
typeFilter
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMoney = (num)=>(num || 0).toFixed(2);
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const resetForm = ()=>({
type: "expense",
amount: "",
category: "",
date: new Date().toISOString().split("T")[0],
time: new Date().toTimeString().slice(0, 5),
note: ""
});
const loadData = async ()=>{
setLoading(true);
try {
const [recordsData, statsData] = await Promise.all([
(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getRecords */ .Q6)(getMonthStr(currentMonth), typeFilter),
(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getStats */ .fy)(getMonthStr(currentMonth))
]);
setRecords(recordsData);
setStats(statsData);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const prevMonth = ()=>{
const newDate = new Date(currentMonth);
newDate.setMonth(newDate.getMonth() - 1);
setCurrentMonth(newDate);
};
const nextMonth = ()=>{
const newDate = new Date(currentMonth);
newDate.setMonth(newDate.getMonth() + 1);
setCurrentMonth(newDate);
};
const handleCardClick = (type)=>{
setTypeFilter(type === typeFilter ? "all" : type);
};
const openAddModal = ()=>{
setEditingRecord(null);
setFormData(resetForm());
setFormError("");
setModalOpen(true);
};
const openEditModal = (record)=>{
const [datePart, timePart] = record.date.split(" ");
setEditingRecord(record);
setFormData({
type: record.type,
amount: record.amount.toString(),
category: record.category,
date: datePart,
time: timePart || "00:00",
note: record.note || ""
});
setFormError("");
setModalOpen(true);
};
const closeModal = ()=>{
if (submitting || deleting) return;
setModalOpen(false);
setEditingRecord(null);
setFormError("");
};
const handleSubmit = async (e)=>{
e.preventDefault();
if (!formData.amount || !formData.category || !formData.date) {
setFormError("请填写完整信æ¯");
return;
}
setSubmitting(true);
setFormError("");
try {
const data = {
type: formData.type,
amount: parseFloat(formData.amount),
category: formData.category,
date: "".concat(formData.date, " ").concat(formData.time || "00:00"),
note: formData.note
};
if (editingRecord) {
await (0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecord */ .Vs)(editingRecord.id, data);
showToast("记录已更新", {
tone: "success"
});
} else {
await (0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .createRecord */ .ae)(data);
showToast("记录已添加", {
tone: "success"
});
}
closeModal();
await loadData();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setSubmitting(false);
}
};
const handleDelete = async ()=>{
if (!editingRecord) return;
const shouldDelete = await confirm({
title: "删除这æ¡è®°å½•?",
description: "删除åŽå°†æ— æ³•æ¢å¤ï¼Œè¯·ç¡®è®¤æ˜¯å¦ç»§ç»­ã€‚",
confirmText: "删除"
});
if (!shouldDelete) return;
setDeleting(true);
try {
await (0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .deleteRecord */ .JS)(editingRecord.id);
closeModal();
showToast("记录已删除", {
tone: "success"
});
await loadData();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setDeleting(false);
}
};
const groupedRecords = records.reduce((groups, record)=>{
const date = record.date.split(" ")[0];
if (!groups[date]) groups[date] = [];
groups[date].push(record);
return groups;
}, {});
const sortedDates = Object.keys(groupedRecords).sort((a, b)=>b.localeCompare(a));
const getDayExpense = (date)=>groupedRecords[date].filter((record)=>record.type === "expense").reduce((sum, record)=>sum + record.amount, 0);
const homeHeaderContent = /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "header-left-actions",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "icon-button",
"aria-label": "æœç´¢è®°å½•",
onClick: ()=>router.push("/search"),
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "search",
size: 18
})
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "month-switcher-container",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "icon-button",
"aria-label": "上个月",
onClick: prevMonth,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "chevronLeft",
size: 18
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "month-switcher__label",
children: formatMonth(currentMonth)
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "icon-button",
"aria-label": "下个月",
onClick: nextMonth,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "chevronRight",
size: 18
})
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "header-right-actions",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "settings",
size: 18
})
})
})
]
});
const modalHeaderContent = /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "header-left-actions",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "icon-button",
"aria-label": "关闭",
onClick: closeModal,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "chevronLeft",
size: 18
})
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "modal-title-container",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "modal-title",
children: editingRecord ? "编辑记录" : "新增记录"
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "header-right-actions"
})
]
});
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Z, {
headerContent: modalOpen ? modalHeaderContent : homeHeaderContent,
compactHeader: true,
hideNav: modalOpen,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("section", {
className: "dashboard-hero card",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "stats-grid stats-grid--hero",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "stat-card ".concat(typeFilter === "income" ? "active" : ""),
onClick: ()=>handleCardClick("income"),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "label",
children: "æ”¶å…¥"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("span", {
className: "value income",
children: [
"+",
formatMoney(stats.income)
]
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "stat-card ".concat(typeFilter === "expense" ? "active" : ""),
onClick: ()=>handleCardClick("expense"),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "label",
children: "支出"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("span", {
className: "value expense",
children: [
"-",
formatMoney(stats.expense)
]
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "stat-card stat-card--balance",
onClick: ()=>handleCardClick("all"),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "label",
children: "结余"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "value",
children: formatMoney(stats.balance)
})
]
})
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("section", {
className: "card section-card",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "section-heading section-heading--compact",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
className: "eyebrow",
children: "记录列表"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h2", {
children: typeFilter === "all" ? "全部记录" : typeFilter === "income" ? "仅看收入" : "仅看支出"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "btn btn-secondary btn-inline",
onClick: openAddModal,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("span", {
className: "button-content",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "plus",
size: 16
}),
"新增记录"
]
})
})
]
}),
loading ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "loading-state",
children: "加载中..."
}) : records.length === 0 ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "empty-state empty-state--card",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "info",
size: 24
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
children: "暂无记录,点击上方按钮添加。"
})
]
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "records-list records-list--flush",
children: sortedDates.map((date)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "date-group",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "date-label",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
children: new Date(date).toLocaleDateString("zh-CN", {
month: "short",
day: "numeric",
weekday: "short"
})