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

14199 lines
716 KiB
Plaintext
Raw Normal View History

2026-03-12 11:24:10 +08:00
wpcá/ §webpack/lib/cache/PackFileCacheStrategyPackContentItems B 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|]|fullySpecified=|false|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=|esm|path=|/opt/accountbook/frontend/pages|request=|../lib/categories¾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/lib/categories.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/lib/categories.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/lib/categories.js|webpackCompilation/assets|chunk405Compilation/assets|chunk845Compilation/assets|chunk603¥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¬/opt/accountbook/frontend/pages/recurring.js†client«/opt/accountbook/frontend/lib/categories.js€€ ¦/opt/accountbook/frontend/package.json„name‡version‡private‡scriptsŒdependencies”accountbook-frontend…1.0.0 ƒdev…build…startnext dev -p 3500Šnext buildnext start -p 3500ˆchart.js„next…react‰react-dom†^4.5.1‡^14.0.0‡^18.2.0ÿ™/opt/accountbook/frontend“./lib/categories.jsÿÿšwebpack/lib/FileSystemInfoˆSnapshot@‰ ¸ÀÍyBáâý„/opt/opt/accountbook//opt/accountbook/frontend/libˆsafeTime‰timestamp„hash!PݨÀÍyB@ݨÀÍyB6a51e60cacf85ff9ý!@07ØÊyB /7ØÊyBf3b4def2c53d3c
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
exportsType‰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ˆ898e1469bf0a79e01a013d70 “__webpack_exports__DEFAULT_CATEGORIES à ¸ÀÍyB tÿÿÿ·webpack/lib/dependencies/HarmonyCompatibilityDependency
dÿÿý¶webpack/lib/dependencies/HarmonyExportHeaderDependency
aj¹webpack/lib/dependencies/HarmonyExportSpecifierDependencyö
aj —webpack/lib/ModuleGraph“RestoreProvidedData
 
jÿÿÿˆprovidedcanMangleProvideterminalBindingexportsInfoí
 
‡sources“runtimeRequirements„data
Šjavascript«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource 
«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceZexport const DEFAULT_CATEGORIES = {
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
  ¤†source„size„maps|ÿÿÿ ConcatSourceRawSource­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
ReplaceSourceRawSource 06undefinedו__webpack_require__.d š´«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource`«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourceW(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([[405],{
/***/ 5557:
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource Ý«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource[/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ê«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource
(window.__NEXT_P = window.__NEXT_P || []).push([
"/",
function () {
return require("private-next-pages/index.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/"])
});
}
bdjlDˆ£¬¯
“__webpack_require__„9828…false{}†buffer†source„size„maps„hash¢
(window.__NEXT_P = window.__NEXT_P || []).push([
"/",
function () {
return __webpack_require__(9828);
}
]);
if(false) {}
”{"finalSource":true}ƒmapbufferedMap ReplaceSourceRawSource
(window.__NEXT_P = window.__NEXT_P || []).push([
"/",
function () {
return require("private-next-pages/index.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/"])
});
}
Z100106__webpack_require__undefined1081369828undefined163172falseundefined175266{}undefined
/***/ }) †buffer†source„size„maps„hashConcatSourceRawSource[/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
ReplaceSourceRawSource
(window.__NEXT_P = window.__NEXT_P || []).push([
"/",
function () {
return require("private-next-pages/index.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/"])
});
}
Z100106__webpack_require__undefined1081369828undefined163172falseundefined175266{}undefined RawSource
/***/ }),
/***/ 6248:
 F«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ó«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource´/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceUimport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { useRouter } from "next/router";
import { Icon } from "./icons";
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
export default function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = useRouter();
const activeTab = useMemo(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ _jsxs("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ _jsx("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ _jsx("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ _jsxs("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ _jsx("header", {
className: headerClassName,
children: /*#__PURE__*/ _jsxs("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ _jsx("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ _jsx("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ _jsx("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ _jsx("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ _jsx("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ _jsx(Icon, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ _jsx("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
e>@_a_ˆŠ¨þ ÁÉäêûÿ_bÁÄBFÏÒ48ßâM P <
?
¾
Á
þ 
}=@¶(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)®(0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)·(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)¶(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)ÿþÿþÿÿÿÿÿÿÿþÿ±_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .Jþ†buffer†source„size„maps„hashõ/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const activeTab = (0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("header", {
className: headerClassName,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .J, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSource´/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
ReplaceSourceRawSourceUimport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { useRouter } from "next/router";
import { Icon } from "./icons";
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
export default function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = useRouter();
const activeTab = useMemo(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ _jsxs("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ _jsx("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ _jsx("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ _jsxs("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ _jsx("header", {
className: headerClassName,
children: /*#__PURE__*/ _jsxs("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ _jsx("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ _jsx("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ _jsx("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ _jsx("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ _jsx("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ _jsx(Icon, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ _jsx("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
}062undefined6495undefined97136undefined138168undefined510524undefined705713(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined740746(0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)undefined10191023(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined11191122(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined12171220(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined16021606(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined17431746(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined18441848(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined20152018(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined21802183(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined23812384(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined26202623(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined27502753(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined29492952(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined30813084(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined33263330(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined37093712(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined37143717_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .Jundefined39013904(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined
/***/ }) òc/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const activeTab = (0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("header", {
className: headerClassName,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .J, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
/***/ }) ”{"finalSource":true}ƒmapbufferedMap  íd/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
ì´/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
íUimport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { useRouter } from "next/router";
import { Icon } from "./icons";
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
export default function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = useRouter();
const activeTab = useMemo(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ _jsxs("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ _jsx("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ _jsx("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ _jsxs("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ _jsx("header", {
className: headerClassName,
children: /*#__PURE__*/ _jsxs("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ _jsx("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ _jsx("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ _jsx("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ _jsx("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ _jsx("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ _jsx(Icon, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ _jsx("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
}062undefined6495undefined97136undefined138168undefined510524undefined705713(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined740746(0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)undefined10191023(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined11191122(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined12171220(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined16021606(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined17431746(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined18441848(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined20152018(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined21802183(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined23812384(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined26202623(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined27502753(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined29492952(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined30813084(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined33263330(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined37093712(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined37143717_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .Jundefined39013904(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefinedîï,
/***/ 610:
 O«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ö«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSource²const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = "object" !== "undefined" ? localStorage.getItem("token") : null;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
export async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export function logout() {
if ("object" !== "undefined") {
localStorage.removeItem("token");
}
}
export function isLoggedIn() {
if ("object" === "undefined") return false;
return !!localStorage.getItem("token");
}
// 账目
export async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
export async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
export async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
export async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
export async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
export async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
export async function getRecurringBills() {
return apiCall("/api/recurring");
}
export async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
export async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
export async function getCategories() {
return apiCall("/api/categories");
}
export async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
export async function exportData() {
return apiCall("/api/export");
}
export async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
bRi_ÆÌ©ÀúbhœÓÙú V\kqÌÒag ˆ Ž W

M S _ e ³ ¹ Q
W
þ
ŠhnŽØÞ… true0„trueÿÿ…false‚{}†buffer†source„size„maps„hashÄ/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = true ? localStorage.getItem("token") : 0;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
function logout() {
if (true) {
localStorage.removeItem("token");
}
}
function isLoggedIn() {
if (false) {}
return !!localStorage.getItem("token");
}
// 账目
async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
async function getRecurringBills() {
return apiCall("/api/recurring");
}
async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
async function getCategories() {
return apiCall("/api/categories");
}
async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
async function exportData() {
return apiCall("/api/export");
}
async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSource/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
ReplaceSourceRawSource²const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = "object" !== "undefined" ? localStorage.getItem("token") : null;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
export async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export function logout() {
if ("object" !== "undefined") {
localStorage.removeItem("token");
}
}
export function isLoggedIn() {
if ("object" === "undefined") return false;
return !!localStorage.getItem("token");
}
// 账目
export async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
export async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
export async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
export async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
export async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
export async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
export async function getRecurringBills() {
return apiCall("/api/recurring");
}
export async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
export async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
export async function getCategories() {
return apiCall("/api/categories");
}
export async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
export async function exportData() {
return apiCall("/api/export");
}
export async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
ý82105 trueundefined1411440undefined710716undefined937960trueundefined10411047undefined12741297trueundefined13781384undefined14131436trueundefined14911497undefined15301553falseundefined15561568{}undefined16221628undefined18991905undefined19962002undefined21452151undefined23092315undefined24402446undefined25772583undefined28932899undefined31673173undefined32513257undefined34093415undefined35823588undefined37163722undefined38633869undefined39443950undefined40984104undefined42384244undefined43124318undefined
/***/ }) â2/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = true ? localStorage.getItem("token") : 0;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
function logout() {
if (true) {
localStorage.removeItem("token");
}
}
function isLoggedIn() {
if (false) {}
return !!localStorage.getItem("token");
}
// 账目
async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
async function getRecurringBills() {
return apiCall("/api/recurring");
}
async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
async function getCategories() {
return apiCall("/api/categories");
}
async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
async function exportData() {
return apiCall("/api/export");
}
async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
/***/ }) ðó  áôá/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
â²const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = "object" !== "undefined" ? localStorage.getItem("token") : null;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
export async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export function logout() {
if ("object" !== "undefined") {
localStorage.removeItem("token");
}
}
export function isLoggedIn() {
if ("object" === "undefined") return false;
return !!localStorage.getItem("token");
}
// 账目
export async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
export async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
export async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
export async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
export async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
export async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
export async function getRecurringBills() {
return apiCall("/api/recurring");
}
export async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
export async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
export async function getCategories() {
return apiCall("/api/categories");
}
export async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
export async function exportData() {
return apiCall("/api/export");
}
export async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
ý82105 trueundefined1411440undefined710716undefined937960trueundefined10411047undefined12741297trueundefined13781384undefined14131436trueundefined14911497undefined15301553falseundefined15561568{}undefined16221628undefined18991905undefined19962002undefined21452151undefined23092315undefined24402446undefined25772583undefined28932899undefined31673173undefined32513257undefined34093415undefined35823588undefined37163722undefined38633869undefined39443950undefined40984104undefined42384244undefined43124318undefinedãä,
/***/ 7063:
 ò«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource 
«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceZexport const DEFAULT_CATEGORIES = {
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
†buffer†source„size„maps„hashConcatSourceRawSource­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
ReplaceSourceRawSourceZexport const DEFAULT_CATEGORIES = {
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
 06undefined
/***/ }) × ØëØ­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
ÙZexport const DEFAULT_CATEGORIES = {
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
 06undefinedÚÛ,
/***/ 9828:
 šß«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«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ûûüûüüûüüýüûûûüüüüýüûûüüûüüûüüÿûüüûüûüüûüüûüûüýü†buffer†source„size„maps„hash__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"
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("span", {
className: "day-expense",
children: [
"支出 ",
formatMoney(getDayExpense(date))
]
})
]
}),
groupedRecords[date].map((record)=>{
const categories = _lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .S[record.type];
const cat = categories.find((item)=>item.name === record.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "record-item",
onClick: ()=>openEditModal(record),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "record-left",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "record-icon",
children: cat.icon
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "record-info",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "record-category",
children: record.category
}),
record.note && /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "record-note",
children: record.note
})
]
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "record-amount ".concat(record.type),
children: [
record.type === "income" ? "+" : "-",
formatMoney(record.amount)
]
})
]
}, record.id);
})
]
}, date))
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "fab",
"aria-label": "新增记录",
onClick: openAddModal,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "plus",
size: 24
})
}),
modalOpen && /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "modal-overlay modal-overlay--fullscreen",
onClick: closeModal,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "modal modal--fullscreen",
onClick: (e)=>e.stopPropagation(),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "modal-header",
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: editingRecord ? "编辑记录" : "新增记录"
}),
editingRecord ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h2", {
children: "更新账目"
}) : null
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "icon-button icon-button--ghost",
"aria-label": "关闭弹窗",
onClick: closeModal,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "close",
size: 18
})
})
]
}),
formError && /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "error-msg",
children: formError
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("form", {
onSubmit: handleSubmit,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "type-switch",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "type-btn expense ".concat(formData.type === "expense" ? "active" : ""),
onClick: ()=>setFormData({
...formData,
type: "expense",
category: ""
}),
disabled: submitting || deleting,
children: "支出"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "type-btn income ".concat(formData.type === "income" ? "active" : ""),
onClick: ()=>setFormData({
...formData,
type: "income",
category: ""
}),
disabled: submitting || deleting,
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "form-group",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("label", {
htmlFor: "record-amount",
children: "金é¢"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.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__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "form-group",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("label", {
children: "分类"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "category-grid",
children: _lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .S[formData.type].map((cat)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "category-btn ".concat(formData.category === cat.name ? "active" : ""),
onClick: ()=>setFormData({
...formData,
category: cat.name
}),
disabled: submitting || deleting,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "icon",
children: cat.icon
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
children: cat.name
})
]
}, cat.name))
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "form-group",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("label", {
children: "日期"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "datetime-picker",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("input", {
type: "date",
value: formData.date,
onChange: (e)=>setFormData({
...formData,
date: e.target.value
}),
required: true,
disabled: submitting || deleting
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("input", {
type: "time",
value: formData.time || "00:00",
onChange: (e)=>setFormData({
...formData,
time: e.target.value
}),
disabled: submitting || deleting
})
]
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "form-group",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("label", {
htmlFor: "record-note",
children: "备注(å¯é€‰ï¼‰"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("input", {
id: "record-note",
type: "text",
value: formData.note,
onChange: (e)=>setFormData({
...formData,
note: e.target.value
}),
placeholder: "添加备注...",
disabled: submitting || deleting
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "modal-actions",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "submit",
className: "btn btn-primary",
disabled: submitting || deleting,
children: submitting ? /*#__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: "spinner",
size: 16,
className: "is-spinning"
}),
"ä¿å­˜ä¸­..."
]
}) : "ä¿å­˜"
}),
editingRecord && /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "btn btn-danger",
onClick: handleDelete,
disabled: submitting || deleting,
children: deleting ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSourceñ__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);
ReplaceSourceRawSourceŸ‚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 ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
E%062undefined64107undefined109148undefined150253undefined255309undefined311356undefined358400undefined402456undefined458516undefined518532undefined570578(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined609616(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)undefined645654(0,_components_ConfirmProvider__WEBPACK_IMPORTED_MODULE_7__/* .useConfirm */ .N)undefined703710(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined765772(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined816823(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined860867(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined975982(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10381045(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10881095(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11441151(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11971204(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined12521259(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined13021309(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined15161524(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined15451554(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .isLoggedIn */ .jl)undefined24062415(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getRecords */ .Q6)undefined24732480(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getStats */ .fy)undefined46394650(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecord */ .Vs)undefined48134824(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .createRecord */ .ae)undefined55425553(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .deleteRecord */ .JS)undefined64276431(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined65346537(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined66386641(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined68806883(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined68856888_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined70507054(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined71827185(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined74267429(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined74317434_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined76107613(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined78037806(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined80478050(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined80528055_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined82578260(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined83628365(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined86048607(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined86098612_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined88128816(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined89198922(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined90239026(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined92479250(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined92529255_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined94229425(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined95289531(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined97129715(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined98349838(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined98409847_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Zundefined1003010033(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1013810142(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1028510289(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1060210605(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1080610810(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1121311217(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1153211535(0,react_jsx_runtime__WEBPACK_IMPO
/***/ }) Î ÏâÏñ__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);
П‚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 ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
E%062undefined64107undefined109148undefined150253undefined255309undefined311356undefined358400undefined402456undefined458516undefined518532undefined570578(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined609616(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)undefined645654(0,_components_ConfirmProvider__WEBPACK_IMPORTED_MODULE_7__/* .useConfirm */ .N)undefined703710(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined765772(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined816823(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined860867(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined975982(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10381045(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10881095(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11441151(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11971204(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined12521259(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined13021309(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined15161524(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined15451554(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .isLoggedIn */ .jl)undefined24062415(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getRecords */ .Q6)undefined24732480(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getStats */ .fy)undefined46394650(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecord */ .Vs)undefined48134824(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .createRecord */ .ae)undefined55425553(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .deleteRecord */ .JS)undefined64276431(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined65346537(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined66386641(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined68806883(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined68856888_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined70507054(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined71827185(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined74267429(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined74317434_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined76107613(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined78037806(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined80478050(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined80528055_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined82578260(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined83628365(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined86048607(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined86098612_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined88128816(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined89198922(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined90239026(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined92479250(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined92529255_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined94229425(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined95289531(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined97129715(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined98349838(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined98409847_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Zundefined1003010033(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1013810142(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1028510289(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1060210605(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1080610810(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1121311217(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1153211535(0,react_jsx_runtime__WEBPACK_IMPO
/***/ 1163:
 g«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourceJ/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ý«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource1module.exports = require('./dist/client/router')
`/ªmodule.exports = __webpack_require__(3079)†buffer†source„size„maps„hash+module.exports = __webpack_require__(3079)
”{"finalSource":true}ƒmapbufferedMap ReplaceSourceRawSource1module.exports = require('./dist/client/router')
6047module.exports = __webpack_require__(3079)undefined
/***/ }) Å/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
module.exports = __webpack_require__(3079)
/***/ }) ÓÖ ÄJ/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
Å1module.exports = require('./dist/client/router')
6047module.exports = __webpack_require__(3079)undefinedÆÇ
},
«webpack/lib/util/registerExternalSerializerœwebpack-sources/PrefixSource‰/******/ 9function(__webpack_require__) { // webpackRuntimeModules
÷var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
__webpack_require__.O(0, [888,774,179], function() { return __webpack_exec__(5557); });
var __webpack_exports__ = __webpack_require__.O();
_N_E = __webpack_exports__;
}
]);  ó `HôW(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([[405],{
/***/ 5557:
ó[/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
ô
(window.__NEXT_P = window.__NEXT_P || []).push([
"/",
function () {
return require("private-next-pages/index.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/"])
});
}
Z100106__webpack_require__undefined1081369828undefined163172falseundefined175266{}undefined RawSource
/***/ })þ,
/***/ 6248:
íd/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
ì´/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
íUimport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { useRouter } from "next/router";
import { Icon } from "./icons";
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
export default function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = useRouter();
const activeTab = useMemo(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ _jsxs("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ _jsx("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ _jsx("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ _jsxs("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ _jsx("header", {
className: headerClassName,
children: /*#__PURE__*/ _jsxs("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ _jsx("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ _jsx("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ _jsx("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ _jsx("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ _jsx("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ _jsx(Icon, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ _jsx("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
}062undefined6495undefined97136undefined138168undefined510524undefined705713(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined740746(0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)undefined10191023(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined11191122(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined12171220(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined16021606(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined17431746(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined18441848(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined20152018(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined21802183(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined23812384(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined26202623(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined27502753(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined29492952(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined30813084(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined33263330(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined37093712(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined37143717_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .Jundefined39013904(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefinedùúù,
/***/ 610:
èûè/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
é²const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = "object" !== "undefined" ? localStorage.getItem("token") : null;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
export async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export function logout() {
if ("object" !== "undefined") {
localStorage.removeItem("token");
}
}
export function isLoggedIn() {
if ("object" === "undefined") return false;
return !!localStorage.getItem("token");
}
// 账目
export async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
export async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
export async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
export async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
export async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
export async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
export async function getRecurringBills() {
return apiCall("/api/recurring");
}
export async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
export async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
export async function getCategories() {
return apiCall("/api/categories");
}
export async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
export async function exportData() {
return apiCall("/api/export");
}
export async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
ý82105 trueundefined1411440undefined710716undefined937960trueundefined10411047undefined12741297trueundefined13781384undefined14131436trueundefined14911497undefined15301553falseundefined15561568{}undefined16221628undefined18991905undefined19962002undefined21452151undefined23092315undefined24402446undefined25772583undefined28932899undefined31673173undefined32513257undefined34093415undefined35823588undefined37163722undefined38633869undefined39443950undefined40984104undefined42384244undefined43124318undefinedõöõ,
/***/ 7063:
ä÷äåæpÿÿÿçôõô,
/***/ 9828:
ãöãñ__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);
䟂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 ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
E%062undefined64107undefined109148undefined150253undefined255309undefined311356undefined358400undefined402456undefined458516undefined518532undefined570578(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined609616(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)undefined645654(0,_components_ConfirmProvider__WEBPACK_IMPORTED_MODULE_7__/* .useConfirm */ .N)undefined703710(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined765772(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined816823(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined860867(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined975982(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10381045(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10881095(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11441151(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11971204(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined12521259(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined13021309(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined15161524(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined15451554(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .isLoggedIn */ .jl)undefined24062415(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getRecords */ .Q6)undefined24732480(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getStats */ .fy)undefined46394650(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecord */ .Vs)undefined48134824(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .createRecord */ .ae)undefined55425553(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .deleteRecord */ .JS)undefined64276431(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined65346537(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined66386641(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined68806883(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined68856888_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined70507054(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined71827185(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined74267429(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined74317434_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined76107613(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined78037806(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined80478050(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined80528055_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined82578260(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined83628365(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined86048607(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined86098612_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined88128816(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined89198922(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined90239026(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined92479250(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined92529255_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined94229425(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined95289531(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined97129715(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined98349838(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined98409847_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Zundefined1003010033(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1013810142(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1028510289(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1060210605(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1080610810(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1121311217(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1153211535(0,react_jsx_runtime__WEBPACK_IMPO
/***/ 1163:
ßJ/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
à1module.exports = require('./dist/client/router')
6047module.exports = __webpack_require__(3079)undefinedìíì
},
!PrefixSourceConcatSourceRawSource9function(__webpack_require__) { // webpackRuntimeModules
é÷var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
__webpack_require__.O(0, [888,774,179], function() { return __webpack_exec__(5557); });
var __webpack_exports__ = __webpack_require__.O();
è_N_E = __webpack_exports__;
}
/******/ RawSource]); GB«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource`«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourceW(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([[845],{
/***/ 8208:
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource «webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource[/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource «webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource$
(window.__NEXT_P = window.__NEXT_P || []).push([
"/recurring",
function () {
return require("private-next-pages/recurring.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/recurring"])
});
}
bmsuD•°¹¼ “__webpack_require__„5190…false{}†buffer†source„size„maps„hash«
(window.__NEXT_P = window.__NEXT_P || []).push([
"/recurring",
function () {
return __webpack_require__(5190);
}
]);
if(false) {}
”{"finalSource":true}ƒmapbufferedMap ReplaceSourceRawSource$
(window.__NEXT_P = window.__NEXT_P || []).push([
"/recurring",
function () {
return require("private-next-pages/recurring.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/recurring"])
});
}
Z109115__webpack_require__undefined1171495190undefined176185falseundefined188288{}undefined
/***/ }) †buffer†source„size„maps„hashConcatSourceRawSource[/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
ReplaceSourceRawSource$
(window.__NEXT_P = window.__NEXT_P || []).push([
"/recurring",
function () {
return require("private-next-pages/recurring.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/recurring"])
});
}
Z109115__webpack_require__undefined1171495190undefined176185falseundefined188288{}undefined RawSource
/***/ }),
/***/ 6248:
 F«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ó«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource´/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceUimport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { useRouter } from "next/router";
import { Icon } from "./icons";
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
export default function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = useRouter();
const activeTab = useMemo(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ _jsxs("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ _jsx("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ _jsx("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ _jsxs("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ _jsx("header", {
className: headerClassName,
children: /*#__PURE__*/ _jsxs("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ _jsx("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ _jsx("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ _jsx("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ _jsx("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ _jsx("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ _jsx(Icon, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ _jsx("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
e>@_a_ˆŠ¨þ ÁÉäêûÿ_bÁÄBFÏÒ48ßâM P <
?
¾
Á
þ 
}=@¶(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)®(0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)·(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)¶(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)ÿþÿþÿÿÿÿÿÿÿþÿ±_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .Jþ†buffer†source„size„maps„hashõ/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const activeTab = (0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("header", {
className: headerClassName,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .J, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSource´/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
ReplaceSourceRawSourceUimport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { useRouter } from "next/router";
import { Icon } from "./icons";
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
export default function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = useRouter();
const activeTab = useMemo(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ _jsxs("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ _jsx("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ _jsx("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ _jsxs("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ _jsx("header", {
className: headerClassName,
children: /*#__PURE__*/ _jsxs("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ _jsx("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ _jsx("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ _jsx("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ _jsx("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ _jsx("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ _jsx(Icon, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ _jsx("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
}062undefined6495undefined97136undefined138168undefined510524undefined705713(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined740746(0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)undefined10191023(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined11191122(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined12171220(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined16021606(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined17431746(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined18441848(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined20152018(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined21802183(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined23812384(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined26202623(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined27502753(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined29492952(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined30813084(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined33263330(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined37093712(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined37143717_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .Jundefined39013904(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined
/***/ }) òc/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const activeTab = (0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("header", {
className: headerClassName,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .J, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
/***/ }) ”{"finalSource":true}ƒmapbufferedMap  íd/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
ì´/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
íUimport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { useRouter } from "next/router";
import { Icon } from "./icons";
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
export default function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = useRouter();
const activeTab = useMemo(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ _jsxs("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ _jsx("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ _jsx("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ _jsxs("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ _jsx("header", {
className: headerClassName,
children: /*#__PURE__*/ _jsxs("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ _jsx("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ _jsx("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ _jsx("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ _jsx("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ _jsx("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ _jsx(Icon, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ _jsx("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
}062undefined6495undefined97136undefined138168undefined510524undefined705713(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined740746(0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)undefined10191023(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined11191122(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined12171220(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined16021606(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined17431746(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined18441848(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined20152018(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined21802183(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined23812384(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined26202623(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined27502753(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined29492952(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined30813084(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined33263330(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined37093712(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined37143717_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .Jundefined39013904(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefinedîï,
/***/ 610:
 O«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ö«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSource²const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = "object" !== "undefined" ? localStorage.getItem("token") : null;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
export async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export function logout() {
if ("object" !== "undefined") {
localStorage.removeItem("token");
}
}
export function isLoggedIn() {
if ("object" === "undefined") return false;
return !!localStorage.getItem("token");
}
// 账目
export async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
export async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
export async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
export async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
export async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
export async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
export async function getRecurringBills() {
return apiCall("/api/recurring");
}
export async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
export async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
export async function getCategories() {
return apiCall("/api/categories");
}
export async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
export async function exportData() {
return apiCall("/api/export");
}
export async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
bRi_ÆÌ©ÀúbhœÓÙú V\kqÌÒag ˆ Ž W

M S _ e ³ ¹ Q
W
þ
ŠhnŽØÞ… true0„trueÿÿ…false‚{}†buffer†source„size„maps„hashÄ/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = true ? localStorage.getItem("token") : 0;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
function logout() {
if (true) {
localStorage.removeItem("token");
}
}
function isLoggedIn() {
if (false) {}
return !!localStorage.getItem("token");
}
// 账目
async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
async function getRecurringBills() {
return apiCall("/api/recurring");
}
async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
async function getCategories() {
return apiCall("/api/categories");
}
async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
async function exportData() {
return apiCall("/api/export");
}
async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSource/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
ReplaceSourceRawSource²const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = "object" !== "undefined" ? localStorage.getItem("token") : null;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
export async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export function logout() {
if ("object" !== "undefined") {
localStorage.removeItem("token");
}
}
export function isLoggedIn() {
if ("object" === "undefined") return false;
return !!localStorage.getItem("token");
}
// 账目
export async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
export async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
export async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
export async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
export async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
export async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
export async function getRecurringBills() {
return apiCall("/api/recurring");
}
export async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
export async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
export async function getCategories() {
return apiCall("/api/categories");
}
export async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
export async function exportData() {
return apiCall("/api/export");
}
export async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
ý82105 trueundefined1411440undefined710716undefined937960trueundefined10411047undefined12741297trueundefined13781384undefined14131436trueundefined14911497undefined15301553falseundefined15561568{}undefined16221628undefined18991905undefined19962002undefined21452151undefined23092315undefined24402446undefined25772583undefined28932899undefined31673173undefined32513257undefined34093415undefined35823588undefined37163722undefined38633869undefined39443950undefined40984104undefined42384244undefined43124318undefined
/***/ }) â2/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = true ? localStorage.getItem("token") : 0;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
function logout() {
if (true) {
localStorage.removeItem("token");
}
}
function isLoggedIn() {
if (false) {}
return !!localStorage.getItem("token");
}
// 账目
async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
async function getRecurringBills() {
return apiCall("/api/recurring");
}
async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
async function getCategories() {
return apiCall("/api/categories");
}
async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
async function exportData() {
return apiCall("/api/export");
}
async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
/***/ }) ðó  áôá/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
â²const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = "object" !== "undefined" ? localStorage.getItem("token") : null;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
export async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export function logout() {
if ("object" !== "undefined") {
localStorage.removeItem("token");
}
}
export function isLoggedIn() {
if ("object" === "undefined") return false;
return !!localStorage.getItem("token");
}
// 账目
export async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
export async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
export async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
export async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
export async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
export async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
export async function getRecurringBills() {
return apiCall("/api/recurring");
}
export async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
export async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
export async function getCategories() {
return apiCall("/api/categories");
}
export async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
export async function exportData() {
return apiCall("/api/export");
}
export async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
ý82105 trueundefined1411440undefined710716undefined937960trueundefined10411047undefined12741297trueundefined13781384undefined14131436trueundefined14911497undefined15301553falseundefined15561568{}undefined16221628undefined18991905undefined19962002undefined21452151undefined23092315undefined24402446undefined25772583undefined28932899undefined31673173undefined32513257undefined34093415undefined35823588undefined37163722undefined38633869undefined39443950undefined40984104undefined42384244undefined43124318undefinedãä,
/***/ 7063:
 ò«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource 
«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceZexport const DEFAULT_CATEGORIES = {
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
†buffer†source„size„maps„hashConcatSourceRawSource­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
ReplaceSourceRawSourceZexport const DEFAULT_CATEGORIES = {
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
 06undefined
/***/ }) × ØëØ­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
ÙZexport const DEFAULT_CATEGORIES = {
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
 06undefinedÚÛ,
/***/ 5190:
 «webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ºz«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 */ Recurring; }
/* 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\limport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getRecurringBills, createRecurringBill, updateRecurringBill, deleteRecurringBill, applyRecurringBill } 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 Recurring() {
const router = useRouter();
const { showToast } = useToast();
const { confirm } = useConfirm();
const [bills, setBills] = useState([]);
const [modalOpen, setModalOpen] = useState(false);
const [editingBill, setEditingBill] = useState(null);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [deleting, setDeleting] = useState(false);
const [applyingId, setApplyingId] = useState(null);
const [formError, setFormError] = useState("");
const [formData, setFormData] = useState({
type: "expense",
amount: "",
category: "",
day: "1",
note: "",
is_active: true
});
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadBills();
}, [
router
]);
const loadBills = async ()=>{
setLoading(true);
try {
const data = await getRecurringBills();
setBills(data);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const openAddModal = ()=>{
setEditingBill(null);
setFormData({
type: "expense",
amount: "",
category: "",
day: "1",
note: "",
is_active: true
});
setFormError("");
setModalOpen(true);
};
const openEditModal = (bill)=>{
setEditingBill(bill);
setFormData({
type: bill.type,
amount: bill.amount.toString(),
category: bill.category,
day: bill.day.toString(),
note: bill.note || "",
is_active: !!bill.is_active
});
setFormError("");
setModalOpen(true);
};
const closeModal = ()=>{
if (submitting || deleting) return;
setModalOpen(false);
setEditingBill(null);
setFormError("");
};
const handleSubmit = async (e)=>{
e.preventDefault();
if (!formData.amount || !formData.category || !formData.day) {
setFormError("请填写完整信æ¯");
return;
}
setSubmitting(true);
setFormError("");
try {
const data = {
type: formData.type,
amount: parseFloat(formData.amount),
category: formData.category,
day: parseInt(formData.day),
note: formData.note,
is_active: formData.is_active ? 1 : 0
};
if (editingBill) {
await updateRecurringBill(editingBill.id, data);
showToast("周期账å•已更新", {
tone: "success"
});
} else {
await createRecurringBill(data);
showToast("周期账å•已创建", {
tone: "success"
});
}
closeModal();
await loadBills();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setSubmitting(false);
}
};
const handleDelete = async ()=>{
if (!editingBill) return;
const shouldDelete = await confirm({
title: "删除这æ¡å‘¨æœŸè´¦å•?",
description: "删除åŽå°†æ— æ³•æ¢å¤ï¼Œè¯·ç¡®è®¤æ˜¯å¦ç»§ç»­ã€‚",
confirmText: "删除"
});
if (!shouldDelete) return;
setDeleting(true);
try {
await deleteRecurringBill(editingBill.id);
closeModal();
showToast("周期账å•已删除", {
tone: "success"
});
await loadBills();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setDeleting(false);
}
};
const handleApply = async (id)=>{
setApplyingId(id);
try {
await applyRecurringBill(id);
showToast("已添加到账目", {
tone: "success"
});
} catch (err) {
showToast(err.message, {
tone: "error"
});
} finally{
setApplyingId(null);
}
};
const handleToggleActive = async (bill, event)=>{
event.stopPropagation();
try {
await updateRecurringBill(bill.id, {
...bill,
is_active: bill.is_active ? 0 : 1
});
showToast(bill.is_active ? "å·²åœç”¨å‘¨æœŸè´¦å•" : "å·²å¯ç”¨å‘¨æœŸè´¦å•", {
tone: "success"
});
await loadBills();
} catch (err) {
showToast(err.message, {
tone: "error"
});
}
};
const days = Array.from({
length: 28
}, (_, index)=>index + 1);
const actions = /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "settings",
size: 18
})
});
return /*#__PURE__*/ _jsxs(AppShell, {
title: "周期账å•",
subtitle: "管ç†å›ºå®šæ”¶å…¥ä¸Žæ”¯å‡ºèŠ‚å¥",
actions: actions,
compactHeader: true,
children: [
loading ? /*#__PURE__*/ _jsx("div", {
className: "card loading-state",
children: "加载中..."
}) : bills.length === 0 ? /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "repeat",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "暂无周期性账å•"
}),
/*#__PURE__*/ _jsx("p", {
className: "sub",
children: "点击å³ä¸‹è§’新增定期收入或支出。"
})
]
}) : /*#__PURE__*/ _jsxs("section", {
className: "card section-card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "è´¦å•列表"
}),
/*#__PURE__*/ _jsxs("h2", {
children: [
bills.length,
" æ¡å‘¨æœŸè´¦å•"
]
})
]
})
}),
/*#__PURE__*/ _jsx("div", {
className: "bills-list",
children: bills.map((bill)=>{
const categories = DEFAULT_CATEGORIES[bill.type];
const cat = categories.find((item)=>item.name === bill.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bill-item ".concat(bill.is_active ? "" : "inactive"),
onClick: ()=>openEditModal(bill),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-left",
children: [
/*#__PURE__*/ _jsx("div", {
className: "bill-icon",
children: cat.icon
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-info",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-row",
children: [
/*#__PURE__*/ _jsx("div", {
className: "bill-category",
children: bill.category
}),
/*#__PURE__*/ _jsx("span", {
className: "status-chip ".concat(bill.is_active ? "status-chip--success" : ""),
children: bill.is_active ? "å¯ç”¨ä¸­" : "å·²åœç”¨"
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-day",
children: [
"æ¯æœˆ ",
bill.day,
" æ—¥"
]
}),
bill.note && /*#__PURE__*/ _jsx("div", {
className: "bill-note",
children: bill.note
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-right",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-amount ".concat(bill.type),
children: [
bill.type === "income" ? "+" : "-",
bill.amount.toFixed(2)
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-actions",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-secondary btn-inline btn-small",
onClick: (e)=>{
e.stopPropagation();
handleApply(bill.id);
},
disabled: applyingId === bill.id,
children: applyingId === bill.id ? "应用中..." : "应用"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-ghost btn-inline btn-small",
onClick: (event)=>handleToggleActive(bill, event),
children: bill.is_active ? "åœç”¨" : "å¯ç”¨"
})
]
})
]
})
]
}, bill.id);
})
})
]
}),
/*#__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",
onClick: closeModal,
children: /*#__PURE__*/ _jsxs("div", {
className: "modal",
onClick: (e)=>e.stopPropagation(),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "modal-header",
children: [
/*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: editingBill ? "编辑账å•" : "新增账å•"
}),
/*#__PURE__*/ _jsx("h2", {
children: editingBill ? "更新周期账å•" : "创建周期账å•"
})
]
}),
/*#__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: "bill-amount",
children: "金é¢"
}),
/*#__PURE__*/ _jsx("input", {
id: "bill-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", {
htmlFor: "bill-day",
children: "æ—¥æœŸï¼ˆæ¯æœˆå‡ å·ï¼‰"
}),
/*#__PURE__*/ _jsx("select", {
id: "bill-day",
value: formData.day,
onChange: (e)=>setFormData({
...formData,
day: e.target.value
}),
disabled: submitting || deleting,
children: days.map((day)=>/*#__PURE__*/ _jsxs("option", {
value: day,
children: [
day,
" æ—¥"
]
}, day))
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
htmlFor: "bill-note",
children: "备注(å¯é€‰ï¼‰"
}),
/*#__PURE__*/ _jsx("input", {
id: "bill-note",
type: "text",
value: formData.note,
onChange: (e)=>setFormData({
...formData,
note: e.target.value
}),
placeholder: "添加备注...",
disabled: submitting || deleting
})
]
}),
editingBill && /*#__PURE__*/ _jsx("div", {
className: "form-group form-group--inline",
children: /*#__PURE__*/ _jsxs("label", {
className: "checkbox-label",
children: [
/*#__PURE__*/ _jsx("input", {
type: "checkbox",
checked: formData.is_active,
onChange: (e)=>setFormData({
...formData,
is_active: e.target.checked
}),
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"
}),
"ä¿å­˜ä¸­..."
]
}) : "ä¿å­˜"
}),
editingBill && /*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-danger",
onClick: handleDelete,
disabled: submitting || deleting,
children: deleting ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
ej>@km_”#%[]ŠŒ¸îð*,:emŒ°¹ÜãKR}µ¼ê#*Y`!)>G-=

¸
Ê
­ƒ& ÁÄÆÉ-13_:æé|

ƒˆ36¥¨W[ØÛeiÊÍøûœ­¢ ¦ æ!_ê!£"¦"¦#ª#{$$g%j%³&¶&®(²(Û*Þ*¡,¥,_-c-F/J/0!0I3L3w6z6?7B7D7G7Õ7_Ø7\8`899¬9°9!:$:%;(;-<0<r=u=w=z=ž>¡>R?V?ì?ð?Ÿ@¢@™CœCÑFÕFƒG†GtH_wH:L>LìLïL™MœM%N6N`NdN<Q?QkRnRTTÍTÐTÁUÄU÷WûWZZÇZÊZº[½[Í^Ð^g_Nk_+`.`@cDcõcøc+e/ef f ff=h@h¶(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__/* .getRecurringBills */ .Vn)Ç(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecurringBill */ .uO)Ç(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .createRecurringBill */ .z0)Ç(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .deleteRecurringBill */ .TZ)Æ(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .applyRecurringBill */ .Xu)ü¶(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)ÿ¼_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J·(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)Å_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .ZüþüüýüüþüþüþüÈ_lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .Sýýûýýûûýûýýýûûûûüûýýýûûûûüûýýûûýûûýûûÿýûûýûûýýûûûýûýûýûüû†buffer†source„size„maps„hashÒ__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Recurring; }
/* 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 Recurring() {
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 [bills, setBills] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [modalOpen, setModalOpen] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
const [editingBill, setEditingBill] = (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 [applyingId, setApplyingId] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(null);
const [formError, setFormError] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("");
const [formData, setFormData] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)({
type: "expense",
amount: "",
category: "",
day: "1",
note: "",
is_active: true
});
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .isLoggedIn */ .jl)()) {
router.push("/login");
return;
}
loadBills();
}, [
router
]);
const loadBills = async ()=>{
setLoading(true);
try {
const data = await (0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getRecurringBills */ .Vn)();
setBills(data);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const openAddModal = ()=>{
setEditingBill(null);
setFormData({
type: "expense",
amount: "",
category: "",
day: "1",
note: "",
is_active: true
});
setFormError("");
setModalOpen(true);
};
const openEditModal = (bill)=>{
setEditingBill(bill);
setFormData({
type: bill.type,
amount: bill.amount.toString(),
category: bill.category,
day: bill.day.toString(),
note: bill.note || "",
is_active: !!bill.is_active
});
setFormError("");
setModalOpen(true);
};
const closeModal = ()=>{
if (submitting || deleting) return;
setModalOpen(false);
setEditingBill(null);
setFormError("");
};
const handleSubmit = async (e)=>{
e.preventDefault();
if (!formData.amount || !formData.category || !formData.day) {
setFormError("请填写完整信æ¯");
return;
}
setSubmitting(true);
setFormError("");
try {
const data = {
type: formData.type,
amount: parseFloat(formData.amount),
category: formData.category,
day: parseInt(formData.day),
note: formData.note,
is_active: formData.is_active ? 1 : 0
};
if (editingBill) {
await (0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecurringBill */ .uO)(editingBill.id, data);
showToast("周期账å•已更新", {
tone: "success"
});
} else {
await (0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .createRecurringBill */ .z0)(data);
showToast("周期账å•已创建", {
tone: "success"
});
}
closeModal();
await loadBills();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setSubmitting(false);
}
};
const handleDelete = async ()=>{
if (!editingBill) return;
const shouldDelete = await confirm({
title: "删除这æ¡å‘¨æœŸè´¦å•?",
description: "删除åŽå°†æ— æ³•æ¢å¤ï¼Œè¯·ç¡®è®¤æ˜¯å¦ç»§ç»­ã€‚",
confirmText: "删除"
});
if (!shouldDelete) return;
setDeleting(true);
try {
await (0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .deleteRecurringBill */ .TZ)(editingBill.id);
closeModal();
showToast("周期账å•已删除", {
tone: "success"
});
await loadBills();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setDeleting(false);
}
};
const handleApply = async (id)=>{
setApplyingId(id);
try {
await (0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .applyRecurringBill */ .Xu)(id);
showToast("已添加到账目", {
tone: "success"
});
} catch (err) {
showToast(err.message, {
tone: "error"
});
} finally{
setApplyingId(null);
}
};
const handleToggleActive = async (bill, event)=>{
event.stopPropagation();
try {
await (0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecurringBill */ .uO)(bill.id, {
...bill,
is_active: bill.is_active ? 0 : 1
});
showToast(bill.is_active ? "å·²åœç”¨å‘¨æœŸè´¦å•" : "å·²å¯ç”¨å‘¨æœŸè´¦å•", {
tone: "success"
});
await loadBills();
} catch (err) {
showToast(err.message, {
tone: "error"
});
}
};
const days = Array.from({
length: 28
}, (_, index)=>index + 1);
const actions = /*#__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
})
});
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Z, {
title: "周期账å•",
subtitle: "管ç†å›ºå®šæ”¶å…¥ä¸Žæ”¯å‡ºèŠ‚å¥",
actions: actions,
compactHeader: true,
children: [
loading ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "card loading-state",
children: "加载中..."
}) : bills.length === 0 ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "card 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: "repeat",
size: 24
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
children: "暂无周期性账å•"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
className: "sub",
children: "点击å³ä¸‹è§’新增定期收入或支出。"
})
]
}) : /*#__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__.jsx)("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__.jsxs)("h2", {
children: [
bills.length,
" æ¡å‘¨æœŸè´¦å•"
]
})
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "bills-list",
children: bills.map((bill)=>{
const categories = _lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .S[bill.type];
const cat = categories.find((item)=>item.name === bill.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "bill-item ".concat(bill.is_active ? "" : "inactive"),
onClick: ()=>openEditModal(bill),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "bill-left",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "bill-icon",
children: cat.icon
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "bill-info",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "bill-row",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "bill-category",
children: bill.category
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "status-chip ".concat(bill.is_active ? "status-chip--success" : ""),
children: bill.is_active ? "å¯ç”¨ä¸­" : "å·²åœç”¨"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "bill-day",
children: [
"æ¯æœˆ ",
bill.day,
" æ—¥"
]
}),
bill.note && /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "bill-note",
children: bill.note
})
]
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "bill-right",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "bill-amount ".concat(bill.type),
children: [
bill.type === "income" ? "+" : "-",
bill.amount.toFixed(2)
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "bill-actions",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "btn btn-secondary btn-inline btn-small",
onClick: (e)=>{
e.stopPropagation();
handleApply(bill.id);
},
disabled: applyingId === bill.id,
children: applyingId === bill.id ? "应用中..." : "应用"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "btn btn-ghost btn-inline btn-small",
onClick: (event)=>handleToggleActive(bill, event),
children: bill.is_active ? "åœç”¨" : "å¯ç”¨"
})
]
})
]
})
]
}, bill.id);
})
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "fab",
"aria-label": "新增账å•",
onClick: openAddModal,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "plus",
size: 24
})
}),
modalOpen && /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "modal-overlay",
onClick: closeModal,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "modal",
onClick: (e)=>e.stopPropagation(),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "modal-header",
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: editingBill ? "编辑账å•" : "新增账å•"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h2", {
children: editingBill ? "更新周期账å•" : "创建周期账å•"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "icon-button icon-button--ghost",
"aria-label": "关闭弹窗",
onClick: closeModal,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "close",
size: 18
})
})
]
}),
formError && /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "error-msg",
children: formError
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("form", {
onSubmit: handleSubmit,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "type-switch",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "type-btn expense ".concat(formData.type === "expense" ? "active" : ""),
onClick: ()=>setFormData({
...formData,
type: "expense",
category: ""
}),
disabled: submitting || deleting,
children: "支出"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "type-btn income ".concat(formData.type === "income" ? "active" : ""),
onClick: ()=>setFormData({
...formData,
type: "income",
category: ""
}),
disabled: submitting || deleting,
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "form-group",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("label", {
htmlFor: "bill-amount",
children: "金é¢"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("input", {
id: "bill-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__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "form-group",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("label", {
children: "分类"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "category-grid",
children: _lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .S[formData.type].map((cat)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "category-btn ".concat(formData.category === cat.name ? "active" : ""),
onClick: ()=>setFormData({
...formData,
category: cat.name
}),
disabled: submitting || deleting,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "icon",
children: cat.icon
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
children: cat.name
})
]
}, cat.name))
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "form-group",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("label", {
htmlFor: "bill-day",
children: "æ—¥æœŸï¼ˆæ¯æœˆå‡ å·ï¼‰"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("select", {
id: "bill-day",
value: formData.day,
onChange: (e)=>setFormData({
...formData,
day: e.target.value
}),
disabled: submitting || deleting,
children: days.map((day)=>/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("option", {
value: day,
children: [
day,
" æ—¥"
]
}, day))
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "form-group",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("label", {
htmlFor: "bill-note",
children: "备注(å¯é€‰ï¼‰"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("input", {
id: "bill-note",
type: "text",
value: formData.note,
onChange: (e)=>setFormData({
...formData,
note: e.target.value
}),
placeholder: "添加备注...",
disabled: submitting || deleting
})
]
}),
editingBill && /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "form-group form-group--inline",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("label", {
className: "checkbox-label",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("input", {
type: "checkbox",
checked: formData.is_active,
onChange: (e)=>setFormData({
...formData,
is_active: e.target.checked
}),
disabled: submitting || deleting
}),
¯ç”¨æ­¤è´¦å•"
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "modal-actions",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "submit",
className: "btn btn-primary",
disabled: submitting || deleting,
children: submitting ? /*#__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: "spinner",
size: 16,
className: "is-spinning"
}),
"ä¿å­˜ä¸­..."
]
}) : "ä¿å­˜"
}),
editingBill && /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: "btn btn-danger",
onClick: handleDelete,
disabled: submitting || deleting,
children: deleting ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSourceö__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Recurring; }
/* 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);
ReplaceSourceRawSource\limport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getRecurringBills, createRecurringBill, updateRecurringBill, deleteRecurringBill, applyRecurringBill } 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 Recurring() {
const router = useRouter();
const { showToast } = useToast();
const { confirm } = useConfirm();
const [bills, setBills] = useState([]);
const [modalOpen, setModalOpen] = useState(false);
const [editingBill, setEditingBill] = useState(null);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [deleting, setDeleting] = useState(false);
const [applyingId, setApplyingId] = useState(null);
const [formError, setFormError] = useState("");
const [formData, setFormData] = useState({
type: "expense",
amount: "",
category: "",
day: "1",
note: "",
is_active: true
});
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadBills();
}, [
router
]);
const loadBills = async ()=>{
setLoading(true);
try {
const data = await getRecurringBills();
setBills(data);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const openAddModal = ()=>{
setEditingBill(null);
setFormData({
type: "expense",
amount: "",
category: "",
day: "1",
note: "",
is_active: true
});
setFormError("");
setModalOpen(true);
};
const openEditModal = (bill)=>{
setEditingBill(bill);
setFormData({
type: bill.type,
amount: bill.amount.toString(),
category: bill.category,
day: bill.day.toString(),
note: bill.note || "",
is_active: !!bill.is_active
});
setFormError("");
setModalOpen(true);
};
const closeModal = ()=>{
if (submitting || deleting) return;
setModalOpen(false);
setEditingBill(null);
setFormError("");
};
const handleSubmit = async (e)=>{
e.preventDefault();
if (!formData.amount || !formData.category || !formData.day) {
setFormError("请填写完整信æ¯");
return;
}
setSubmitting(true);
setFormError("");
try {
const data = {
type: formData.type,
amount: parseFloat(formData.amount),
category: formData.category,
day: parseInt(formData.day),
note: formData.note,
is_active: formData.is_active ? 1 : 0
};
if (editingBill) {
await updateRecurringBill(editingBill.id, data);
showToast("周期账å•已更新", {
tone: "success"
});
} else {
await createRecurringBill(data);
showToast("周期账å•已创建", {
tone: "success"
});
}
closeModal();
await loadBills();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setSubmitting(false);
}
};
const handleDelete = async ()=>{
if (!editingBill) return;
const shouldDelete = await confirm({
title: "删除这æ¡å‘¨æœŸè´¦å•?",
description: "删除åŽå°†æ— æ³•æ¢å¤ï¼Œè¯·ç¡®è®¤æ˜¯å¦ç»§ç»­ã€‚",
confirmText: "删除"
});
if (!shouldDelete) return;
setDeleting(true);
try {
await deleteRecurringBill(editingBill.id);
closeModal();
showToast("周期账å•已删除", {
tone: "success"
});
await loadBills();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setDeleting(false);
}
};
const handleApply = async (id)=>{
setApplyingId(id);
try {
await applyRecurringBill(id);
showToast("已添加到账目", {
tone: "success"
});
} catch (err) {
showToast(err.message, {
tone: "error"
});
} finally{
setApplyingId(null);
}
};
const handleToggleActive = async (bill, event)=>{
event.stopPropagation();
try {
await updateRecurringBill(bill.id, {
...bill,
is_active: bill.is_active ? 0 : 1
});
showToast(bill.is_active ? "å·²åœç”¨å‘¨æœŸè´¦å•" : "å·²å¯ç”¨å‘¨æœŸè´¦å•", {
tone: "success"
});
await loadBills();
} catch (err) {
showToast(err.message, {
tone: "error"
});
}
};
const days = Array.from({
length: 28
}, (_, index)=>index + 1);
const actions = /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "settings",
size: 18
})
});
return /*#__PURE__*/ _jsxs(AppShell, {
title: "周期账å•",
subtitle: "管ç†å›ºå®šæ”¶å…¥ä¸Žæ”¯å‡ºèŠ‚å¥",
actions: actions,
compactHeader: true,
children: [
loading ? /*#__PURE__*/ _jsx("div", {
className: "card loading-state",
children: "加载中..."
}) : bills.length === 0 ? /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "repeat",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "暂无周期性账å•"
}),
/*#__PURE__*/ _jsx("p", {
className: "sub",
children: "点击å³ä¸‹è§’新增定期收入或支出。"
})
]
}) : /*#__PURE__*/ _jsxs("section", {
className: "card section-card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "è´¦å•列表"
}),
/*#__PURE__*/ _jsxs("h2", {
children: [
bills.length,
" æ¡å‘¨æœŸè´¦å•"
]
})
]
})
}),
/*#__PURE__*/ _jsx("div", {
className: "bills-list",
children: bills.map((bill)=>{
const categories = DEFAULT_CATEGORIES[bill.type];
const cat = categories.find((item)=>item.name === bill.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bill-item ".concat(bill.is_active ? "" : "inactive"),
onClick: ()=>openEditModal(bill),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-left",
children: [
/*#__PURE__*/ _jsx("div", {
className: "bill-icon",
children: cat.icon
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-info",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-row",
children: [
/*#__PURE__*/ _jsx("div", {
className: "bill-category",
children: bill.category
}),
/*#__PURE__*/ _jsx("span", {
className: "status-chip ".concat(bill.is_active ? "status-chip--success" : ""),
children: bill.is_active ? "å¯ç”¨ä¸­" : "å·²åœç”¨"
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-day",
children: [
"æ¯æœˆ ",
bill.day,
" æ—¥"
]
}),
bill.note && /*#__PURE__*/ _jsx("div", {
className: "bill-note",
children: bill.note
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-right",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-amount ".concat(bill.type),
children: [
bill.type === "income" ? "+" : "-",
bill.amount.toFixed(2)
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-actions",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-secondary btn-inline btn-small",
onClick: (e)=>{
e.stopPropagation();
handleApply(bill.id);
},
disabled: applyingId === bill.id,
children: applyingId === bill.id ? "应用中..." : "应用"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-ghost btn-inline btn-small",
onClick: (event)=>handleToggleActive(bill, event),
children: bill.is_active ? "åœç”¨" : "å¯ç”¨"
})
]
})
]
})
]
}, bill.id);
})
})
]
}),
/*#__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",
onClick: closeModal,
children: /*#__PURE__*/ _jsxs("div", {
className: "modal",
onClick: (e)=>e.stopPropagation(),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "modal-header",
children: [
/*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: editingBill ? "编辑账å•" : "新增账å•"
}),
/*#__PURE__*/ _jsx("h2", {
children: editingBill ? "更新周期账å•" : "创建周期账å•"
})
]
}),
/*#__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: "bill-amount",
children: "金é¢"
}),
/*#__PURE__*/ _jsx("input", {
id: "bill-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", {
htmlFor: "bill-day",
children: "æ—¥æœŸï¼ˆæ¯æœˆå‡ å·ï¼‰"
}),
/*#__PURE__*/ _jsx("select", {
id: "bill-day",
value: formData.day,
onChange: (e)=>setFormData({
...formData,
day: e.target.value
}),
disabled: submitting || deleting,
children: days.map((day)=>/*#__PURE__*/ _jsxs("option", {
value: day,
children: [
day,
" æ—¥"
]
}, day))
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
htmlFor: "bill-note",
children: "备注(å¯é€‰ï¼‰"
}),
/*#__PURE__*/ _jsx("input", {
id: "bill-note",
type: "text",
value: formData.note,
onChange: (e)=>setFormData({
...formData,
note: e.target.value
}),
placeholder: "添加备注...",
disabled: submitting || deleting
})
]
}),
editingBill && /*#__PURE__*/ _jsx("div", {
className: "form-group form-group--inline",
children: /*#__PURE__*/ _jsxs("label", {
className: "checkbox-label",
children: [
/*#__PURE__*/ _jsx("input", {
type: "checkbox",
checked: formData.is_active,
onChange: (e)=>setFormData({
...formData,
is_active: e.target.checked
}),
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"
}),
"ä¿å­˜ä¸­..."
]
}) : "ä¿å­˜"
}),
editingBill && /*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-danger",
onClick: handleDelete,
disabled: submitting || deleting,
children: deleting ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
H062undefined64107undefined109148undefined150291undefined293347undefined349394undefined396438undefined440494undefined496554undefined556570undefined613621(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined652659(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)undefined688697(0,_components_ConfirmProvider__WEBPACK_IMPORTED_MODULE_7__/* .useConfirm */ .N)undefined732739(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined784791(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined843850(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined893900(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined949956(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10021009(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10591066(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11131120(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11631170(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined13131321(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined13421351(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .isLoggedIn */ .jl)undefined15811597(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getRecurringBills */ .Vn)undefined33313349(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecurringBill */ .uO)undefined35123530(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .createRecurringBill */ .z0)undefined42514269(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .deleteRecurringBill */ .TZ)undefined47394756(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .applyRecurringBill */ .Xu)undefined51405158(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecurringBill */ .uO)undefined56435646(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined58255828(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined58305833_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined59335937(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined59395946_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Zundefined61186121(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined62686272(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined64106413(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined65286531(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined65336536_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined67076710(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined68216824(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined69997003(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined71287131(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined72697273(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined73707373(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined75757579(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined79287931(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined80928109_lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .Sundefined83548358(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined86788682(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined88678870(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined91269130(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined93399343(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined95759578(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined99079910(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1041410418(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1097110974(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1142511429(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1161511619(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1210212106(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1231812321(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1312913132(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1394313946(0,react_jsx_runtime__WEBPACK
/***/ }) Î ÏâÏö__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Recurring; }
/* 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);
Ð\limport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getRecurringBills, createRecurringBill, updateRecurringBill, deleteRecurringBill, applyRecurringBill } 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 Recurring() {
const router = useRouter();
const { showToast } = useToast();
const { confirm } = useConfirm();
const [bills, setBills] = useState([]);
const [modalOpen, setModalOpen] = useState(false);
const [editingBill, setEditingBill] = useState(null);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [deleting, setDeleting] = useState(false);
const [applyingId, setApplyingId] = useState(null);
const [formError, setFormError] = useState("");
const [formData, setFormData] = useState({
type: "expense",
amount: "",
category: "",
day: "1",
note: "",
is_active: true
});
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadBills();
}, [
router
]);
const loadBills = async ()=>{
setLoading(true);
try {
const data = await getRecurringBills();
setBills(data);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const openAddModal = ()=>{
setEditingBill(null);
setFormData({
type: "expense",
amount: "",
category: "",
day: "1",
note: "",
is_active: true
});
setFormError("");
setModalOpen(true);
};
const openEditModal = (bill)=>{
setEditingBill(bill);
setFormData({
type: bill.type,
amount: bill.amount.toString(),
category: bill.category,
day: bill.day.toString(),
note: bill.note || "",
is_active: !!bill.is_active
});
setFormError("");
setModalOpen(true);
};
const closeModal = ()=>{
if (submitting || deleting) return;
setModalOpen(false);
setEditingBill(null);
setFormError("");
};
const handleSubmit = async (e)=>{
e.preventDefault();
if (!formData.amount || !formData.category || !formData.day) {
setFormError("请填写完整信æ¯");
return;
}
setSubmitting(true);
setFormError("");
try {
const data = {
type: formData.type,
amount: parseFloat(formData.amount),
category: formData.category,
day: parseInt(formData.day),
note: formData.note,
is_active: formData.is_active ? 1 : 0
};
if (editingBill) {
await updateRecurringBill(editingBill.id, data);
showToast("周期账å•已更新", {
tone: "success"
});
} else {
await createRecurringBill(data);
showToast("周期账å•已创建", {
tone: "success"
});
}
closeModal();
await loadBills();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setSubmitting(false);
}
};
const handleDelete = async ()=>{
if (!editingBill) return;
const shouldDelete = await confirm({
title: "删除这æ¡å‘¨æœŸè´¦å•?",
description: "删除åŽå°†æ— æ³•æ¢å¤ï¼Œè¯·ç¡®è®¤æ˜¯å¦ç»§ç»­ã€‚",
confirmText: "删除"
});
if (!shouldDelete) return;
setDeleting(true);
try {
await deleteRecurringBill(editingBill.id);
closeModal();
showToast("周期账å•已删除", {
tone: "success"
});
await loadBills();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setDeleting(false);
}
};
const handleApply = async (id)=>{
setApplyingId(id);
try {
await applyRecurringBill(id);
showToast("已添加到账目", {
tone: "success"
});
} catch (err) {
showToast(err.message, {
tone: "error"
});
} finally{
setApplyingId(null);
}
};
const handleToggleActive = async (bill, event)=>{
event.stopPropagation();
try {
await updateRecurringBill(bill.id, {
...bill,
is_active: bill.is_active ? 0 : 1
});
showToast(bill.is_active ? "å·²åœç”¨å‘¨æœŸè´¦å•" : "å·²å¯ç”¨å‘¨æœŸè´¦å•", {
tone: "success"
});
await loadBills();
} catch (err) {
showToast(err.message, {
tone: "error"
});
}
};
const days = Array.from({
length: 28
}, (_, index)=>index + 1);
const actions = /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "settings",
size: 18
})
});
return /*#__PURE__*/ _jsxs(AppShell, {
title: "周期账å•",
subtitle: "管ç†å›ºå®šæ”¶å…¥ä¸Žæ”¯å‡ºèŠ‚å¥",
actions: actions,
compactHeader: true,
children: [
loading ? /*#__PURE__*/ _jsx("div", {
className: "card loading-state",
children: "加载中..."
}) : bills.length === 0 ? /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "repeat",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "暂无周期性账å•"
}),
/*#__PURE__*/ _jsx("p", {
className: "sub",
children: "点击å³ä¸‹è§’新增定期收入或支出。"
})
]
}) : /*#__PURE__*/ _jsxs("section", {
className: "card section-card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "è´¦å•列表"
}),
/*#__PURE__*/ _jsxs("h2", {
children: [
bills.length,
" æ¡å‘¨æœŸè´¦å•"
]
})
]
})
}),
/*#__PURE__*/ _jsx("div", {
className: "bills-list",
children: bills.map((bill)=>{
const categories = DEFAULT_CATEGORIES[bill.type];
const cat = categories.find((item)=>item.name === bill.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bill-item ".concat(bill.is_active ? "" : "inactive"),
onClick: ()=>openEditModal(bill),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-left",
children: [
/*#__PURE__*/ _jsx("div", {
className: "bill-icon",
children: cat.icon
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-info",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-row",
children: [
/*#__PURE__*/ _jsx("div", {
className: "bill-category",
children: bill.category
}),
/*#__PURE__*/ _jsx("span", {
className: "status-chip ".concat(bill.is_active ? "status-chip--success" : ""),
children: bill.is_active ? "å¯ç”¨ä¸­" : "å·²åœç”¨"
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-day",
children: [
"æ¯æœˆ ",
bill.day,
" æ—¥"
]
}),
bill.note && /*#__PURE__*/ _jsx("div", {
className: "bill-note",
children: bill.note
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-right",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-amount ".concat(bill.type),
children: [
bill.type === "income" ? "+" : "-",
bill.amount.toFixed(2)
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-actions",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-secondary btn-inline btn-small",
onClick: (e)=>{
e.stopPropagation();
handleApply(bill.id);
},
disabled: applyingId === bill.id,
children: applyingId === bill.id ? "应用中..." : "应用"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-ghost btn-inline btn-small",
onClick: (event)=>handleToggleActive(bill, event),
children: bill.is_active ? "åœç”¨" : "å¯ç”¨"
})
]
})
]
})
]
}, bill.id);
})
})
]
}),
/*#__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",
onClick: closeModal,
children: /*#__PURE__*/ _jsxs("div", {
className: "modal",
onClick: (e)=>e.stopPropagation(),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "modal-header",
children: [
/*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: editingBill ? "编辑账å•" : "新增账å•"
}),
/*#__PURE__*/ _jsx("h2", {
children: editingBill ? "更新周期账å•" : "创建周期账å•"
})
]
}),
/*#__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: "bill-amount",
children: "金é¢"
}),
/*#__PURE__*/ _jsx("input", {
id: "bill-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", {
htmlFor: "bill-day",
children: "æ—¥æœŸï¼ˆæ¯æœˆå‡ å·ï¼‰"
}),
/*#__PURE__*/ _jsx("select", {
id: "bill-day",
value: formData.day,
onChange: (e)=>setFormData({
...formData,
day: e.target.value
}),
disabled: submitting || deleting,
children: days.map((day)=>/*#__PURE__*/ _jsxs("option", {
value: day,
children: [
day,
" æ—¥"
]
}, day))
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
htmlFor: "bill-note",
children: "备注(å¯é€‰ï¼‰"
}),
/*#__PURE__*/ _jsx("input", {
id: "bill-note",
type: "text",
value: formData.note,
onChange: (e)=>setFormData({
...formData,
note: e.target.value
}),
placeholder: "添加备注...",
disabled: submitting || deleting
})
]
}),
editingBill && /*#__PURE__*/ _jsx("div", {
className: "form-group form-group--inline",
children: /*#__PURE__*/ _jsxs("label", {
className: "checkbox-label",
children: [
/*#__PURE__*/ _jsx("input", {
type: "checkbox",
checked: formData.is_active,
onChange: (e)=>setFormData({
...formData,
is_active: e.target.checked
}),
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"
}),
"ä¿å­˜ä¸­..."
]
}) : "ä¿å­˜"
}),
editingBill && /*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-danger",
onClick: handleDelete,
disabled: submitting || deleting,
children: deleting ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
H062undefined64107undefined109148undefined150291undefined293347undefined349394undefined396438undefined440494undefined496554undefined556570undefined613621(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined652659(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)undefined688697(0,_components_ConfirmProvider__WEBPACK_IMPORTED_MODULE_7__/* .useConfirm */ .N)undefined732739(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined784791(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined843850(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined893900(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined949956(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10021009(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10591066(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11131120(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11631170(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined13131321(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined13421351(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .isLoggedIn */ .jl)undefined15811597(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getRecurringBills */ .Vn)undefined33313349(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecurringBill */ .uO)undefined35123530(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .createRecurringBill */ .z0)undefined42514269(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .deleteRecurringBill */ .TZ)undefined47394756(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .applyRecurringBill */ .Xu)undefined51405158(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecurringBill */ .uO)undefined56435646(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined58255828(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined58305833_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined59335937(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined59395946_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Zundefined61186121(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined62686272(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined64106413(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined65286531(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined65336536_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined67076710(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined68216824(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined69997003(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined71287131(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined72697273(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined73707373(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined75757579(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined79287931(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined80928109_lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .Sundefined83548358(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined86788682(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined88678870(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined91269130(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined93399343(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined95759578(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined99079910(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1041410418(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1097110974(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1142511429(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1161511619(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1210212106(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1231812321(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1312913132(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1394313946(0,react_jsx_runtime__WEBPACK
/***/ 1163:
 g«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourceJ/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ý«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource1module.exports = require('./dist/client/router')
`/ªmodule.exports = __webpack_require__(3079)†buffer†source„size„maps„hash+module.exports = __webpack_require__(3079)
”{"finalSource":true}ƒmapbufferedMap ReplaceSourceRawSource1module.exports = require('./dist/client/router')
6047module.exports = __webpack_require__(3079)undefined
/***/ }) Å/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
module.exports = __webpack_require__(3079)
/***/ }) ÓÖ ÄJ/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
Å1module.exports = require('./dist/client/router')
6047module.exports = __webpack_require__(3079)undefinedÆÇ
},
«webpack/lib/util/registerExternalSerializerœwebpack-sources/PrefixSource‰/******/ 9function(__webpack_require__) { // webpackRuntimeModules
÷var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
__webpack_require__.O(0, [888,774,179], function() { return __webpack_exec__(8208); });
var __webpack_exports__ = __webpack_require__.O();
_N_E = __webpack_exports__;
}
]);  Ð `HÑW(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([[845],{
/***/ 8208:
ÐÝÒ$
(window.__NEXT_P = window.__NEXT_P || []).push([
"/recurring",
function () {
return require("private-next-pages/recurring.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/recurring"])
});
}
Z109115__webpack_require__undefined1171495190undefined176185falseundefined188288{}undefinedÞßÞàÎáÎâÐãäÞßÞåÎáÎæÐçèÞßÞéÎáÎÏÐZÿÿÿÑÞßÞ,
/***/ 5190:
ÍàÍö__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Recurring; }
/* 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);
Î\limport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getRecurringBills, createRecurringBill, updateRecurringBill, deleteRecurringBill, applyRecurringBill } 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 Recurring() {
const router = useRouter();
const { showToast } = useToast();
const { confirm } = useConfirm();
const [bills, setBills] = useState([]);
const [modalOpen, setModalOpen] = useState(false);
const [editingBill, setEditingBill] = useState(null);
const [loading, setLoading] = useState(true);
const [submitting, setSubmitting] = useState(false);
const [deleting, setDeleting] = useState(false);
const [applyingId, setApplyingId] = useState(null);
const [formError, setFormError] = useState("");
const [formData, setFormData] = useState({
type: "expense",
amount: "",
category: "",
day: "1",
note: "",
is_active: true
});
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadBills();
}, [
router
]);
const loadBills = async ()=>{
setLoading(true);
try {
const data = await getRecurringBills();
setBills(data);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const openAddModal = ()=>{
setEditingBill(null);
setFormData({
type: "expense",
amount: "",
category: "",
day: "1",
note: "",
is_active: true
});
setFormError("");
setModalOpen(true);
};
const openEditModal = (bill)=>{
setEditingBill(bill);
setFormData({
type: bill.type,
amount: bill.amount.toString(),
category: bill.category,
day: bill.day.toString(),
note: bill.note || "",
is_active: !!bill.is_active
});
setFormError("");
setModalOpen(true);
};
const closeModal = ()=>{
if (submitting || deleting) return;
setModalOpen(false);
setEditingBill(null);
setFormError("");
};
const handleSubmit = async (e)=>{
e.preventDefault();
if (!formData.amount || !formData.category || !formData.day) {
setFormError("请填写完整信æ¯");
return;
}
setSubmitting(true);
setFormError("");
try {
const data = {
type: formData.type,
amount: parseFloat(formData.amount),
category: formData.category,
day: parseInt(formData.day),
note: formData.note,
is_active: formData.is_active ? 1 : 0
};
if (editingBill) {
await updateRecurringBill(editingBill.id, data);
showToast("周期账å•已更新", {
tone: "success"
});
} else {
await createRecurringBill(data);
showToast("周期账å•已创建", {
tone: "success"
});
}
closeModal();
await loadBills();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setSubmitting(false);
}
};
const handleDelete = async ()=>{
if (!editingBill) return;
const shouldDelete = await confirm({
title: "删除这æ¡å‘¨æœŸè´¦å•?",
description: "删除åŽå°†æ— æ³•æ¢å¤ï¼Œè¯·ç¡®è®¤æ˜¯å¦ç»§ç»­ã€‚",
confirmText: "删除"
});
if (!shouldDelete) return;
setDeleting(true);
try {
await deleteRecurringBill(editingBill.id);
closeModal();
showToast("周期账å•已删除", {
tone: "success"
});
await loadBills();
} catch (err) {
setFormError(err.message);
showToast(err.message, {
tone: "error"
});
} finally{
setDeleting(false);
}
};
const handleApply = async (id)=>{
setApplyingId(id);
try {
await applyRecurringBill(id);
showToast("已添加到账目", {
tone: "success"
});
} catch (err) {
showToast(err.message, {
tone: "error"
});
} finally{
setApplyingId(null);
}
};
const handleToggleActive = async (bill, event)=>{
event.stopPropagation();
try {
await updateRecurringBill(bill.id, {
...bill,
is_active: bill.is_active ? 0 : 1
});
showToast(bill.is_active ? "å·²åœç”¨å‘¨æœŸè´¦å•" : "å·²å¯ç”¨å‘¨æœŸè´¦å•", {
tone: "success"
});
await loadBills();
} catch (err) {
showToast(err.message, {
tone: "error"
});
}
};
const days = Array.from({
length: 28
}, (_, index)=>index + 1);
const actions = /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "settings",
size: 18
})
});
return /*#__PURE__*/ _jsxs(AppShell, {
title: "周期账å•",
subtitle: "管ç†å›ºå®šæ”¶å…¥ä¸Žæ”¯å‡ºèŠ‚å¥",
actions: actions,
compactHeader: true,
children: [
loading ? /*#__PURE__*/ _jsx("div", {
className: "card loading-state",
children: "加载中..."
}) : bills.length === 0 ? /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "repeat",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "暂无周期性账å•"
}),
/*#__PURE__*/ _jsx("p", {
className: "sub",
children: "点击å³ä¸‹è§’新增定期收入或支出。"
})
]
}) : /*#__PURE__*/ _jsxs("section", {
className: "card section-card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "è´¦å•列表"
}),
/*#__PURE__*/ _jsxs("h2", {
children: [
bills.length,
" æ¡å‘¨æœŸè´¦å•"
]
})
]
})
}),
/*#__PURE__*/ _jsx("div", {
className: "bills-list",
children: bills.map((bill)=>{
const categories = DEFAULT_CATEGORIES[bill.type];
const cat = categories.find((item)=>item.name === bill.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bill-item ".concat(bill.is_active ? "" : "inactive"),
onClick: ()=>openEditModal(bill),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-left",
children: [
/*#__PURE__*/ _jsx("div", {
className: "bill-icon",
children: cat.icon
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-info",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-row",
children: [
/*#__PURE__*/ _jsx("div", {
className: "bill-category",
children: bill.category
}),
/*#__PURE__*/ _jsx("span", {
className: "status-chip ".concat(bill.is_active ? "status-chip--success" : ""),
children: bill.is_active ? "å¯ç”¨ä¸­" : "å·²åœç”¨"
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-day",
children: [
"æ¯æœˆ ",
bill.day,
" æ—¥"
]
}),
bill.note && /*#__PURE__*/ _jsx("div", {
className: "bill-note",
children: bill.note
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-right",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "bill-amount ".concat(bill.type),
children: [
bill.type === "income" ? "+" : "-",
bill.amount.toFixed(2)
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "bill-actions",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-secondary btn-inline btn-small",
onClick: (e)=>{
e.stopPropagation();
handleApply(bill.id);
},
disabled: applyingId === bill.id,
children: applyingId === bill.id ? "应用中..." : "应用"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-ghost btn-inline btn-small",
onClick: (event)=>handleToggleActive(bill, event),
children: bill.is_active ? "åœç”¨" : "å¯ç”¨"
})
]
})
]
})
]
}, bill.id);
})
})
]
}),
/*#__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",
onClick: closeModal,
children: /*#__PURE__*/ _jsxs("div", {
className: "modal",
onClick: (e)=>e.stopPropagation(),
children: [
/*#__PURE__*/ _jsxs("div", {
className: "modal-header",
children: [
/*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: editingBill ? "编辑账å•" : "新增账å•"
}),
/*#__PURE__*/ _jsx("h2", {
children: editingBill ? "更新周期账å•" : "创建周期账å•"
})
]
}),
/*#__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: "bill-amount",
children: "金é¢"
}),
/*#__PURE__*/ _jsx("input", {
id: "bill-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", {
htmlFor: "bill-day",
children: "æ—¥æœŸï¼ˆæ¯æœˆå‡ å·ï¼‰"
}),
/*#__PURE__*/ _jsx("select", {
id: "bill-day",
value: formData.day,
onChange: (e)=>setFormData({
...formData,
day: e.target.value
}),
disabled: submitting || deleting,
children: days.map((day)=>/*#__PURE__*/ _jsxs("option", {
value: day,
children: [
day,
" æ—¥"
]
}, day))
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "form-group",
children: [
/*#__PURE__*/ _jsx("label", {
htmlFor: "bill-note",
children: "备注(å¯é€‰ï¼‰"
}),
/*#__PURE__*/ _jsx("input", {
id: "bill-note",
type: "text",
value: formData.note,
onChange: (e)=>setFormData({
...formData,
note: e.target.value
}),
placeholder: "添加备注...",
disabled: submitting || deleting
})
]
}),
editingBill && /*#__PURE__*/ _jsx("div", {
className: "form-group form-group--inline",
children: /*#__PURE__*/ _jsxs("label", {
className: "checkbox-label",
children: [
/*#__PURE__*/ _jsx("input", {
type: "checkbox",
checked: formData.is_active,
onChange: (e)=>setFormData({
...formData,
is_active: e.target.checked
}),
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"
}),
"ä¿å­˜ä¸­..."
]
}) : "ä¿å­˜"
}),
editingBill && /*#__PURE__*/ _jsx("button", {
type: "button",
className: "btn btn-danger",
onClick: handleDelete,
disabled: submitting || deleting,
children: deleting ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
H062undefined64107undefined109148undefined150291undefined293347undefined349394undefined396438undefined440494undefined496554undefined556570undefined613621(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined652659(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)undefined688697(0,_components_ConfirmProvider__WEBPACK_IMPORTED_MODULE_7__/* .useConfirm */ .N)undefined732739(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined784791(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined843850(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined893900(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined949956(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10021009(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10591066(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11131120(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined11631170(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined13131321(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined13421351(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .isLoggedIn */ .jl)undefined15811597(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .getRecurringBills */ .Vn)undefined33313349(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecurringBill */ .uO)undefined35123530(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .createRecurringBill */ .z0)undefined42514269(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .deleteRecurringBill */ .TZ)undefined47394756(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .applyRecurringBill */ .Xu)undefined51405158(0,_lib_api__WEBPACK_IMPORTED_MODULE_8__/* .updateRecurringBill */ .uO)undefined56435646(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined58255828(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined58305833_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined59335937(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined59395946_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Zundefined61186121(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined62686272(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined64106413(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined65286531(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined65336536_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined67076710(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined68216824(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined69997003(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined71287131(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined72697273(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined73707373(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined75757579(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined79287931(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined80928109_lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .Sundefined83548358(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined86788682(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined88678870(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined91269130(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined93399343(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined95759578(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined99079910(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1041410418(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1097110974(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1142511429(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1161511619(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1210212106(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1231812321(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1312913132(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1394313946(0,react_jsx_runtime__WEBPACK
__webpack_require__.O(0, [888,774,179], function() { return __webpack_exec__(8208); });
var __webpack_exports__ = __webpack_require__.O();
Ùñòó B«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource`«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourceW(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([[603],{
/***/ 2774:
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ý«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource[/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ÷«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource
(window.__NEXT_P = window.__NEXT_P || []).push([
"/search",
function () {
return require("private-next-pages/search.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/search"])
});
}
bjprDª³“__webpack_require__„7073…false{}†buffer†source„size„maps„hash¨
(window.__NEXT_P = window.__NEXT_P || []).push([
"/search",
function () {
return __webpack_require__(7073);
}
]);
if(false) {}
”{"finalSource":true}ƒmapbufferedMap ReplaceSourceRawSource
(window.__NEXT_P = window.__NEXT_P || []).push([
"/search",
function () {
return require("private-next-pages/search.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/search"])
});
}
Z106112__webpack_require__undefined1141437073undefined170179falseundefined182279{}undefined
/***/ }) †buffer†source„size„maps„hashConcatSourceRawSource[/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
ReplaceSourceRawSource
(window.__NEXT_P = window.__NEXT_P || []).push([
"/search",
function () {
return require("private-next-pages/search.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/search"])
});
}
Z106112__webpack_require__undefined1141437073undefined170179falseundefined182279{}undefined RawSource
/***/ }),
/***/ 6248:
 F«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ó«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource´/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceUimport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { useRouter } from "next/router";
import { Icon } from "./icons";
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
export default function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = useRouter();
const activeTab = useMemo(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ _jsxs("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ _jsx("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ _jsx("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ _jsxs("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ _jsx("header", {
className: headerClassName,
children: /*#__PURE__*/ _jsxs("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ _jsx("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ _jsx("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ _jsx("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ _jsx("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ _jsx("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ _jsx(Icon, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ _jsx("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
e>@_a_ˆŠ¨þ ÁÉäêûÿ_bÁÄBFÏÒ48ßâM P <
?
¾
Á
þ 
}=@¶(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)®(0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)·(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)¶(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)ÿþÿþÿÿÿÿÿÿÿþÿ±_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .Jþ†buffer†source„size„maps„hashõ/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const activeTab = (0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("header", {
className: headerClassName,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .J, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSource´/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
ReplaceSourceRawSourceUimport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { useRouter } from "next/router";
import { Icon } from "./icons";
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
export default function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = useRouter();
const activeTab = useMemo(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ _jsxs("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ _jsx("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ _jsx("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ _jsxs("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ _jsx("header", {
className: headerClassName,
children: /*#__PURE__*/ _jsxs("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ _jsx("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ _jsx("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ _jsx("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ _jsx("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ _jsx("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ _jsx(Icon, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ _jsx("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
}062undefined6495undefined97136undefined138168undefined510524undefined705713(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined740746(0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)undefined10191023(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined11191122(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined12171220(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined16021606(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined17431746(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined18441848(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined20152018(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined21802183(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined23812384(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined26202623(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined27502753(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined29492952(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined30813084(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined33263330(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined37093712(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined37143717_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .Jundefined39013904(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined
/***/ }) òc/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const activeTab = (0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("header", {
className: headerClassName,
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .J, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
/***/ }) ”{"finalSource":true}ƒmapbufferedMap  íd/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
ì´/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Z: function() { return /* binding */ AppShell; }
/* 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 _icons__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(9415);
íUimport { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useMemo } from "react";
import { useRouter } from "next/router";
import { Icon } from "./icons";
const tabs = [
{
href: "/",
label: "首页",
icon: "home"
},
{
href: "/stats",
label: "统计",
icon: "chart"
},
{
href: "/recurring",
label: "周期",
icon: "repeat"
},
{
href: "/categories",
label: "分类",
icon: "tags"
}
];
export default function AppShell(param) {
let { title, subtitle, actions, headerContent, children, contentClassName = "", hideNav = false, compactHeader = false } = param;
const router = useRouter();
const activeTab = useMemo(()=>{
const matched = tabs.find((tab)=>tab.href === router.pathname);
return (matched === null || matched === void 0 ? void 0 : matched.href) || null;
}, [
router.pathname
]);
const headerNode = headerContent || (title ? /*#__PURE__*/ _jsxs("div", {
className: "app-header__text",
children: [
/*#__PURE__*/ _jsx("h1", {
children: title
}),
subtitle ? /*#__PURE__*/ _jsx("p", {
children: subtitle
}) : null
]
}) : null);
const headerClassName = [
"app-header",
compactHeader ? "app-header--compact" : "",
headerNode ? "app-header--with-content" : "app-header--actions-only",
actions ? "app-header--with-actions" : ""
].filter(Boolean).join(" ");
return /*#__PURE__*/ _jsxs("div", {
className: "app-shell".concat(hideNav ? " app-shell--standalone" : ""),
children: [
/*#__PURE__*/ _jsx("header", {
className: headerClassName,
children: /*#__PURE__*/ _jsxs("div", {
className: "app-shell__container app-header__inner",
children: [
headerNode ? /*#__PURE__*/ _jsx("div", {
className: "app-header__main",
children: headerNode
}) : /*#__PURE__*/ _jsx("div", {
className: "app-header__spacer",
"aria-hidden": "true"
}),
actions ? /*#__PURE__*/ _jsx("div", {
className: "app-header__actions",
children: actions
}) : null
]
})
}),
/*#__PURE__*/ _jsx("main", {
className: "app-content ".concat(contentClassName).trim(),
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container app-content__inner",
children: children
})
}),
!hideNav ? /*#__PURE__*/ _jsx("nav", {
className: "bottom-nav",
"aria-label": "主导航",
children: /*#__PURE__*/ _jsx("div", {
className: "app-shell__container bottom-nav__inner",
children: tabs.map((tab)=>{
const isActive = activeTab === tab.href;
return /*#__PURE__*/ _jsxs("button", {
type: "button",
className: "bottom-nav__item".concat(isActive ? " is-active" : ""),
onClick: ()=>router.push(tab.href),
"aria-current": isActive ? "page" : undefined,
children: [
/*#__PURE__*/ _jsx(Icon, {
name: tab.icon,
size: 20
}),
/*#__PURE__*/ _jsx("span", {
children: tab.label
})
]
}, tab.href);
})
})
}) : null
]
});
}
}062undefined6495undefined97136undefined138168undefined510524undefined705713(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined740746(0,react__WEBPACK_IMPORTED_MODULE_1__.useMemo)undefined10191023(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined11191122(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined12171220(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined16021606(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined17431746(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined18441848(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined20152018(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined21802183(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined23812384(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined26202623(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined27502753(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined29492952(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined30813084(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined33263330(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined37093712(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined37143717_icons__WEBPACK_IMPORTED_MODULE_3__/* .Icon */ .Jundefined39013904(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefinedîï,
/***/ 610:
 O«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ö«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSource²const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = "object" !== "undefined" ? localStorage.getItem("token") : null;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
export async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export function logout() {
if ("object" !== "undefined") {
localStorage.removeItem("token");
}
}
export function isLoggedIn() {
if ("object" === "undefined") return false;
return !!localStorage.getItem("token");
}
// 账目
export async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
export async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
export async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
export async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
export async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
export async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
export async function getRecurringBills() {
return apiCall("/api/recurring");
}
export async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
export async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
export async function getCategories() {
return apiCall("/api/categories");
}
export async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
export async function exportData() {
return apiCall("/api/export");
}
export async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
bRi_ÆÌ©ÀúbhœÓÙú V\kqÌÒag ˆ Ž W

M S _ e ³ ¹ Q
W
þ
ŠhnŽØÞ… true0„trueÿÿ…false‚{}†buffer†source„size„maps„hashÄ/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = true ? localStorage.getItem("token") : 0;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
function logout() {
if (true) {
localStorage.removeItem("token");
}
}
function isLoggedIn() {
if (false) {}
return !!localStorage.getItem("token");
}
// 账目
async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
async function getRecurringBills() {
return apiCall("/api/recurring");
}
async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
async function getCategories() {
return apiCall("/api/categories");
}
async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
async function exportData() {
return apiCall("/api/export");
}
async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSource/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
ReplaceSourceRawSource²const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = "object" !== "undefined" ? localStorage.getItem("token") : null;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
export async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export function logout() {
if ("object" !== "undefined") {
localStorage.removeItem("token");
}
}
export function isLoggedIn() {
if ("object" === "undefined") return false;
return !!localStorage.getItem("token");
}
// 账目
export async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
export async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
export async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
export async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
export async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
export async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
export async function getRecurringBills() {
return apiCall("/api/recurring");
}
export async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
export async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
export async function getCategories() {
return apiCall("/api/categories");
}
export async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
export async function exportData() {
return apiCall("/api/export");
}
export async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
ý82105 trueundefined1411440undefined710716undefined937960trueundefined10411047undefined12741297trueundefined13781384undefined14131436trueundefined14911497undefined15301553falseundefined15561568{}undefined16221628undefined18991905undefined19962002undefined21452151undefined23092315undefined24402446undefined25772583undefined28932899undefined31673173undefined32513257undefined34093415undefined35823588undefined37163722undefined38633869undefined39443950undefined40984104undefined42384244undefined43124318undefined
/***/ }) â2/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = true ? localStorage.getItem("token") : 0;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if (true) {
localStorage.setItem("token", data.token);
}
return data;
}
function logout() {
if (true) {
localStorage.removeItem("token");
}
}
function isLoggedIn() {
if (false) {}
return !!localStorage.getItem("token");
}
// 账目
async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
async function getRecurringBills() {
return apiCall("/api/recurring");
}
async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
async function getCategories() {
return apiCall("/api/categories");
}
async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
async function exportData() {
return apiCall("/api/export");
}
async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
/***/ }) ðó  áôá/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ Ap: function() { return /* binding */ getCategoryStats; },
/* harmony export */ CP: function() { return /* binding */ getCategories; },
/* harmony export */ JS: function() { return /* binding */ deleteRecord; },
/* harmony export */ Kp: function() { return /* binding */ searchRecords; },
/* harmony export */ Q6: function() { return /* binding */ getRecords; },
/* harmony export */ TZ: function() { return /* binding */ deleteRecurringBill; },
/* harmony export */ Vn: function() { return /* binding */ getRecurringBills; },
/* harmony export */ Vs: function() { return /* binding */ updateRecord; },
/* harmony export */ Xu: function() { return /* binding */ applyRecurringBill; },
/* harmony export */ ae: function() { return /* binding */ createRecord; },
/* harmony export */ dk: function() { return /* binding */ importData; },
/* harmony export */ fy: function() { return /* binding */ getStats; },
/* harmony export */ gR: function() { return /* binding */ getTrendStats; },
/* harmony export */ jl: function() { return /* binding */ isLoggedIn; },
/* harmony export */ k4: function() { return /* binding */ createCategory; },
/* harmony export */ kS: function() { return /* binding */ logout; },
/* harmony export */ u1: function() { return /* binding */ exportData; },
/* harmony export */ uO: function() { return /* binding */ updateRecurringBill; },
/* harmony export */ uu: function() { return /* binding */ deleteCategory; },
/* harmony export */ x4: function() { return /* binding */ login; },
/* harmony export */ z0: function() { return /* binding */ createRecurringBill; },
/* harmony export */ z2: function() { return /* binding */ register; }
/* harmony export */ });
â²const API_BASE = ""; // ä½¿ç”¨ç›¸å¯¹è·¯å¾„ï¼ŒåŒæºä»£ç
function getAuthHeaders() {
const token = "object" !== "undefined" ? localStorage.getItem("token") : null;
return token ? {
Authorization: "Bearer ".concat(token)
} : {};
}
async function apiCall(url) {
let options = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : {};
const res = await fetch("".concat(API_BASE).concat(url), {
...options,
headers: {
"Content-Type": "application/json",
...getAuthHeaders(),
...options.headers
}
});
const data = await res.json();
if (!res.ok) {
throw new Error(data.error || "请求失败");
}
return data;
}
// 认è¯
export async function login(username, password) {
const data = await apiCall("/api/auth/login", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export async function register(username, password) {
const data = await apiCall("/api/auth/register", {
method: "POST",
body: JSON.stringify({
username,
password
})
});
if ("object" !== "undefined") {
localStorage.setItem("token", data.token);
}
return data;
}
export function logout() {
if ("object" !== "undefined") {
localStorage.removeItem("token");
}
}
export function isLoggedIn() {
if ("object" === "undefined") return false;
return !!localStorage.getItem("token");
}
// 账目
export async function getRecords(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/records?month=".concat(month);
if (type !== "all") {
url += "&type=".concat(type);
}
return apiCall(url);
}
export async function getStats(month) {
return apiCall("/api/stats?month=".concat(month));
}
export async function createRecord(data) {
return apiCall("/api/records", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecord(id, data) {
return apiCall("/api/records/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecord(id) {
return apiCall("/api/records/".concat(id), {
method: "DELETE"
});
}
// æœç´¢
export async function searchRecords(keyword) {
return apiCall("/api/records/search?q=".concat(encodeURIComponent(keyword)));
}
// 统计
export async function getTrendStats() {
let period = arguments.length > 0 && arguments[0] !== void 0 ? arguments[0] : "month", month = arguments.length > 1 ? arguments[1] : void 0;
let url = "/api/stats/trend?period=".concat(period);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
export async function getCategoryStats(month) {
let type = arguments.length > 1 && arguments[1] !== void 0 ? arguments[1] : "all";
let url = "/api/stats/category?type=".concat(type);
if (month) url += "&month=".concat(month);
return apiCall(url);
}
// 周期性账å
export async function getRecurringBills() {
return apiCall("/api/recurring");
}
export async function createRecurringBill(data) {
return apiCall("/api/recurring", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function updateRecurringBill(id, data) {
return apiCall("/api/recurring/".concat(id), {
method: "PUT",
body: JSON.stringify(data)
});
}
export async function deleteRecurringBill(id) {
return apiCall("/api/recurring/".concat(id), {
method: "DELETE"
});
}
export async function applyRecurringBill(id) {
return apiCall("/api/recurring/".concat(id, "/apply"), {
method: "POST"
});
}
// 分类
export async function getCategories() {
return apiCall("/api/categories");
}
export async function createCategory(data) {
return apiCall("/api/categories", {
method: "POST",
body: JSON.stringify(data)
});
}
export async function deleteCategory(id) {
return apiCall("/api/categories/".concat(id), {
method: "DELETE"
});
}
// æ•°æ®å¯¼å…¥å¯¼å‡º
export async function exportData() {
return apiCall("/api/export");
}
export async function importData(data) {
return apiCall("/api/import", {
method: "POST",
body: JSON.stringify(data)
});
}
ý82105 trueundefined1411440undefined710716undefined937960trueundefined10411047undefined12741297trueundefined13781384undefined14131436trueundefined14911497undefined15301553falseundefined15561568{}undefined16221628undefined18991905undefined19962002undefined21452151undefined23092315undefined24402446undefined25772583undefined28932899undefined31673173undefined32513257undefined34093415undefined35823588undefined37163722undefined38633869undefined39443950undefined40984104undefined42384244undefined43124318undefinedãä,
/***/ 7063:
 ò«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource 
«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceZexport const DEFAULT_CATEGORIES = {
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
†buffer†source„size„maps„hashConcatSourceRawSource­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
ReplaceSourceRawSourceZexport const DEFAULT_CATEGORIES = {
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
 06undefined
/***/ }) × ØëØ­/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ S: function() { return /* binding */ DEFAULT_CATEGORIES; }
/* harmony export */ });
ÙZexport const DEFAULT_CATEGORIES = {
expense: [
{
name: "é¤é¥®",
icon: "\uD83C\uDF5C"
},
{
name: "ä¹°èœ",
icon: "\uD83E\uDD6C"
},
{
name: "交通",
icon: "\uD83D\uDE97"
},
{
name: "购物",
icon: "\uD83D\uDED2"
},
{
name: "娱ä¹",
icon: "\uD83C\uDFAE"
},
{
name: "房租",
icon: "\uD83C\uDFE0"
},
{
name: "房贷",
icon: "\uD83C\uDFE6"
},
{
name: "亲情å¡",
icon: "\uD83D\uDCB3"
},
{
name: "水电",
icon: "\uD83D\uDCA7"
},
{
name: "è¡£æœ",
icon: "\uD83D\uDC54"
},
{
name: "通讯",
icon: "\uD83D\uDCF1"
},
{
name: "日用å“",
icon: "\uD83E\uDDF4"
},
{
name: "美妆",
icon: "\uD83D\uDC84"
},
{
name: "医疗",
icon: "\uD83C\uDFE5"
},
{
name: "养娃",
icon: "\uD83D\uDC76"
},
{
name: "家电",
icon: "\uD83D\uDCFA"
},
{
name: "å®¶å…·",
icon: "\uD83D\uDECBï¸"
},
{
name: "装修",
icon: "\uD83D\uDD28"
},
{
name: "宠物",
icon: "\uD83D\uDC15"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
],
income: [
{
name: "工资",
icon: "\uD83D\uDCB0"
},
{
name: "奖金",
icon: "\uD83C\uDF81"
},
{
name: "ç†è´¢",
icon: "\uD83D\uDCC8"
},
{
name: "红包",
icon: "\uD83E\uDDE7"
},
{
name: "å…¶ä»–",
icon: "\uD83D\uDCDD"
}
]
};
 06undefinedÚÛ,
/***/ 7073:
 £Ï«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourced/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource Û?«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 */ Search; }
/* 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_7__ = __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);
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceÀ4import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, searchRecords } from "../lib/api";
import { DEFAULT_CATEGORIES } from "../lib/categories";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
export default function Search() {
const router = useRouter();
const { showToast } = useToast();
const [keyword, setKeyword] = useState("");
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
}
}, [
router
]);
const handleSearch = async (e)=>{
e.preventDefault();
if (!keyword.trim()) {
showToast("请输入æœç´¢å…³é”®è¯", {
tone: "info"
});
return;
}
setLoading(true);
try {
const data = await searchRecords(keyword);
setResults(data);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const formatMoney = (num)=>(num || 0).toFixed(2);
const groupedResults = results.reduce((groups, record)=>{
const date = record.date.split(" ")[0];
if (!groups[date]) groups[date] = [];
groups[date].push(record);
return groups;
}, {});
const sortedDates = Object.keys(groupedResults).sort((a, b)=>b.localeCompare(a));
const totalIncome = results.filter((record)=>record.type === "income").reduce((sum, record)=>sum + record.amount, 0);
const totalExpense = results.filter((record)=>record.type === "expense").reduce((sum, record)=>sum + record.amount, 0);
const totalBalance = totalIncome - totalExpense;
const actions = /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "settings",
size: 18
})
});
return /*#__PURE__*/ _jsxs(AppShell, {
title: "æœç´¢",
subtitle: "快速查找分类ã€å¤‡æ³¨ä¸Žé‡‘é¢",
actions: actions,
compactHeader: true,
children: [
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--search section-card--search-panel",
children: [
/*#__PURE__*/ _jsxs("form", {
className: "search-form search-form--refresh",
onSubmit: handleSearch,
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-input-wrap",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 18,
className: "search-input-wrap__icon"
}),
/*#__PURE__*/ _jsx("input", {
type: "text",
value: keyword,
onChange: (e)=>setKeyword(e.target.value),
placeholder: "æœç´¢åˆ†ç±»æˆ–备注...",
autoFocus: true,
disabled: loading
})
]
}),
/*#__PURE__*/ _jsx("button", {
type: "submit",
className: "btn btn-primary btn-inline",
disabled: loading,
children: loading ? /*#__PURE__*/ _jsxs("span", {
className: "button-content",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "spinner",
size: 16,
className: "is-spinning"
}),
œç´¢ä¸­..."
]
}) : "æœç´¢"
})
]
}),
results.length > 0 ? /*#__PURE__*/ _jsxs(_Fragment, {
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-summary",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "æ”¶å…¥"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value income",
children: [
"+",
formatMoney(totalIncome)
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "支出"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value expense",
children: [
"-",
formatMoney(totalExpense)
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "结余"
}),
/*#__PURE__*/ _jsx("span", {
className: "value",
children: formatMoney(totalBalance)
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "results-count",
children: [
"找到 ",
results.length,
" æ¡è®°å½•"
]
})
]
}) : null
]
}),
loading ? /*#__PURE__*/ _jsx("div", {
className: "card loading-state",
children: "æœç´¢ä¸­..."
}) : results.length > 0 ? /*#__PURE__*/ _jsx("section", {
className: "card section-card",
children: /*#__PURE__*/ _jsx("div", {
className: "records-list records-list--flush",
children: sortedDates.map((date)=>/*#__PURE__*/ _jsxs("div", {
className: "date-group",
children: [
/*#__PURE__*/ _jsx("div", {
className: "date-label",
children: new Date(date).toLocaleDateString("zh-CN", {
month: "short",
day: "numeric",
weekday: "short"
})
}),
groupedResults[date].map((record)=>{
const categories = DEFAULT_CATEGORIES[record.type];
const cat = categories.find((item)=>item.name === record.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ _jsxs("div", {
className: "record-item record-item--static",
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))
})
}) : keyword ? /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "未找到相关记录"
})
]
}) : /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "输入关键è¯å¼€å§‹æœç´¢"
})
]
})
]
});
}
cDUW_«­ãåJLvx®°¾æî
;Bkr¢°¸ÍÖAM_¹»¾" & ( / Ð Ô
V Z    !
$
;>:>ñôöùëïñLPöúµ¸±µ³·runrrv14-0Àă !!€!ƒ!""¯"_²"Å$Ö$ï%ó%Â&Æ&™'œ'¾(Â(­)°) ++--È/Ì/V0Y0Ì0Ï0Ñ0Ô011ÿ12223D33 3¶3¹3¶(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)Ì(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)¯(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)ÿÿ°(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)¾(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)Á(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .searchRecords */ .Kp)¶(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)ÿ¼_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J·(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)Å_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Zþþþüýüüþüýþ·react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.FragmentýýûýýûýýûûýûûûýûÈ_lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .Süüúüúúüüúúûúüúúûú†buffer†source„size„maps„hash!C__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Search; }
/* 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_7__ = __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);
function Search() {
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const { showToast } = (0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)();
const [keyword, setKeyword] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("");
const [results, setResults] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [loading, setLoading] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(false);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)()) {
router.push("/login");
}
}, [
router
]);
const handleSearch = async (e)=>{
e.preventDefault();
if (!keyword.trim()) {
showToast("请输入æœç´¢å…³é”®è¯", {
tone: "info"
});
return;
}
setLoading(true);
try {
const data = await (0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .searchRecords */ .Kp)(keyword);
setResults(data);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const formatMoney = (num)=>(num || 0).toFixed(2);
const groupedResults = results.reduce((groups, record)=>{
const date = record.date.split(" ")[0];
if (!groups[date]) groups[date] = [];
groups[date].push(record);
return groups;
}, {});
const sortedDates = Object.keys(groupedResults).sort((a, b)=>b.localeCompare(a));
const totalIncome = results.filter((record)=>record.type === "income").reduce((sum, record)=>sum + record.amount, 0);
const totalExpense = results.filter((record)=>record.type === "expense").reduce((sum, record)=>sum + record.amount, 0);
const totalBalance = totalIncome - totalExpense;
const actions = /*#__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
})
});
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Z, {
title: "æœç´¢",
subtitle: "快速查找分类ã€å¤‡æ³¨ä¸Žé‡‘é¢",
actions: actions,
compactHeader: true,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("section", {
className: "card section-card section-card--search section-card--search-panel",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("form", {
className: "search-form search-form--refresh",
onSubmit: handleSearch,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "search-input-wrap",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .J, {
name: "search",
size: 18,
className: "search-input-wrap__icon"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("input", {
type: "text",
value: keyword,
onChange: (e)=>setKeyword(e.target.value),
placeholder: "æœç´¢åˆ†ç±»æˆ–备注...",
autoFocus: true,
disabled: loading
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "submit",
className: "btn btn-primary btn-inline",
disabled: loading,
children: loading ? /*#__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: "spinner",
size: 16,
className: "is-spinning"
}),
œç´¢ä¸­..."
]
}) : "æœç´¢"
})
]
}),
results.length > 0 ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragment, {
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "search-summary",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "search-stat",
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(totalIncome)
]
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "search-stat",
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(totalExpense)
]
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "search-stat",
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(totalBalance)
})
]
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "results-count",
children: [
"找到 ",
results.length,
" æ¡è®°å½•"
]
})
]
}) : null
]
}),
loading ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "card loading-state",
children: "æœç´¢ä¸­..."
}) : results.length > 0 ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("section", {
className: "card section-card",
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__.jsx)("div", {
className: "date-label",
children: new Date(date).toLocaleDateString("zh-CN", {
month: "short",
day: "numeric",
weekday: "short"
})
}),
groupedResults[date].map((record)=>{
const categories = _lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .S[record.type];
const cat = categories.find((item)=>item.name === record.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "record-item record-item--static",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "record-left",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "record-icon",
children: cat.icon
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "record-info",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "record-category",
children: record.category
}),
record.note && /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "record-note",
children: record.note
})
]
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "record-amount ".concat(record.type),
children: [
record.type === "income" ? "+" : "-",
formatMoney(record.amount)
]
})
]
}, record.id);
})
]
}, date))
})
}) : keyword ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "card 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: "search",
size: 24
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
children: "未找到相关记录"
})
]
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "card 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: "search",
size: 24
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
children: "输入关键è¯å¼€å§‹æœç´¢"
})
]
})
]
});
}
”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSource__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Search; }
/* 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_7__ = __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);
ReplaceSourceRawSourceÀ4import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, searchRecords } from "../lib/api";
import { DEFAULT_CATEGORIES } from "../lib/categories";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
export default function Search() {
const router = useRouter();
const { showToast } = useToast();
const [keyword, setKeyword] = useState("");
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
}
}, [
router
]);
const handleSearch = async (e)=>{
e.preventDefault();
if (!keyword.trim()) {
showToast("请输入æœç´¢å…³é”®è¯", {
tone: "info"
});
return;
}
setLoading(true);
try {
const data = await searchRecords(keyword);
setResults(data);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const formatMoney = (num)=>(num || 0).toFixed(2);
const groupedResults = results.reduce((groups, record)=>{
const date = record.date.split(" ")[0];
if (!groups[date]) groups[date] = [];
groups[date].push(record);
return groups;
}, {});
const sortedDates = Object.keys(groupedResults).sort((a, b)=>b.localeCompare(a));
const totalIncome = results.filter((record)=>record.type === "income").reduce((sum, record)=>sum + record.amount, 0);
const totalExpense = results.filter((record)=>record.type === "expense").reduce((sum, record)=>sum + record.amount, 0);
const totalBalance = totalIncome - totalExpense;
const actions = /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "settings",
size: 18
})
});
return /*#__PURE__*/ _jsxs(AppShell, {
title: "æœç´¢",
subtitle: "快速查找分类ã€å¤‡æ³¨ä¸Žé‡‘é¢",
actions: actions,
compactHeader: true,
children: [
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--search section-card--search-panel",
children: [
/*#__PURE__*/ _jsxs("form", {
className: "search-form search-form--refresh",
onSubmit: handleSearch,
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-input-wrap",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 18,
className: "search-input-wrap__icon"
}),
/*#__PURE__*/ _jsx("input", {
type: "text",
value: keyword,
onChange: (e)=>setKeyword(e.target.value),
placeholder: "æœç´¢åˆ†ç±»æˆ–备注...",
autoFocus: true,
disabled: loading
})
]
}),
/*#__PURE__*/ _jsx("button", {
type: "submit",
className: "btn btn-primary btn-inline",
disabled: loading,
children: loading ? /*#__PURE__*/ _jsxs("span", {
className: "button-content",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "spinner",
size: 16,
className: "is-spinning"
}),
œç´¢ä¸­..."
]
}) : "æœç´¢"
})
]
}),
results.length > 0 ? /*#__PURE__*/ _jsxs(_Fragment, {
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-summary",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "æ”¶å…¥"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value income",
children: [
"+",
formatMoney(totalIncome)
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "支出"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value expense",
children: [
"-",
formatMoney(totalExpense)
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "结余"
}),
/*#__PURE__*/ _jsx("span", {
className: "value",
children: formatMoney(totalBalance)
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "results-count",
children: [
"找到 ",
results.length,
" æ¡è®°å½•"
]
})
]
}) : null
]
}),
loading ? /*#__PURE__*/ _jsx("div", {
className: "card loading-state",
children: "æœç´¢ä¸­..."
}) : results.length > 0 ? /*#__PURE__*/ _jsx("section", {
className: "card section-card",
children: /*#__PURE__*/ _jsx("div", {
className: "records-list records-list--flush",
children: sortedDates.map((date)=>/*#__PURE__*/ _jsxs("div", {
className: "date-group",
children: [
/*#__PURE__*/ _jsx("div", {
className: "date-label",
children: new Date(date).toLocaleDateString("zh-CN", {
month: "short",
day: "numeric",
weekday: "short"
})
}),
groupedResults[date].map((record)=>{
const categories = DEFAULT_CATEGORIES[record.type];
const cat = categories.find((item)=>item.name === record.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ _jsxs("div", {
className: "record-item record-item--static",
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))
})
}) : keyword ? /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "未找到相关记录"
})
]
}) : /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "输入关键è¯å¼€å§‹æœç´¢"
})
]
})
]
});
}
R085undefined87130undefined132171undefined173227undefined229283undefined285330undefined332374undefined376430undefined432446undefined486494(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined525532(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)undefined571578(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined619626(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined667674(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined688696(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined717726(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)undefined10891101(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .searchRecords */ .Kp)undefined20482051(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined22302233(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined22352238_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined23382342(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined23442351_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Zundefined25122516(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined26892693(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined29022906(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined30753078(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined30803083_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined33613364(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined38993902(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined41544158(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined43374340(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined43424345_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined48434847(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined48494857react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragmentundefined49404944(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined51105114(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined53015304(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined55535557(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined60676071(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined62586261(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined65106514(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined70267030(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined72177220(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined74697472(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined78727876(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined83208323(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined84708473(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined85768579(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined87258729(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined88798882(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined94139430_lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .Sundefined97119715(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined99229926(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1013710140(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1043010434(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1066910672(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1102011023(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1154211546(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1223212236(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1237412377(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1249212495(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1249712500_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined1267112674(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1279912803(0,react_jsx_runti
/***/ }) Î ÏâÏ__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Search; }
/* 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_7__ = __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);
ÐÀ4import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, searchRecords } from "../lib/api";
import { DEFAULT_CATEGORIES } from "../lib/categories";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
export default function Search() {
const router = useRouter();
const { showToast } = useToast();
const [keyword, setKeyword] = useState("");
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
}
}, [
router
]);
const handleSearch = async (e)=>{
e.preventDefault();
if (!keyword.trim()) {
showToast("请输入æœç´¢å…³é”®è¯", {
tone: "info"
});
return;
}
setLoading(true);
try {
const data = await searchRecords(keyword);
setResults(data);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const formatMoney = (num)=>(num || 0).toFixed(2);
const groupedResults = results.reduce((groups, record)=>{
const date = record.date.split(" ")[0];
if (!groups[date]) groups[date] = [];
groups[date].push(record);
return groups;
}, {});
const sortedDates = Object.keys(groupedResults).sort((a, b)=>b.localeCompare(a));
const totalIncome = results.filter((record)=>record.type === "income").reduce((sum, record)=>sum + record.amount, 0);
const totalExpense = results.filter((record)=>record.type === "expense").reduce((sum, record)=>sum + record.amount, 0);
const totalBalance = totalIncome - totalExpense;
const actions = /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "settings",
size: 18
})
});
return /*#__PURE__*/ _jsxs(AppShell, {
title: "æœç´¢",
subtitle: "快速查找分类ã€å¤‡æ³¨ä¸Žé‡‘é¢",
actions: actions,
compactHeader: true,
children: [
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--search section-card--search-panel",
children: [
/*#__PURE__*/ _jsxs("form", {
className: "search-form search-form--refresh",
onSubmit: handleSearch,
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-input-wrap",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 18,
className: "search-input-wrap__icon"
}),
/*#__PURE__*/ _jsx("input", {
type: "text",
value: keyword,
onChange: (e)=>setKeyword(e.target.value),
placeholder: "æœç´¢åˆ†ç±»æˆ–备注...",
autoFocus: true,
disabled: loading
})
]
}),
/*#__PURE__*/ _jsx("button", {
type: "submit",
className: "btn btn-primary btn-inline",
disabled: loading,
children: loading ? /*#__PURE__*/ _jsxs("span", {
className: "button-content",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "spinner",
size: 16,
className: "is-spinning"
}),
œç´¢ä¸­..."
]
}) : "æœç´¢"
})
]
}),
results.length > 0 ? /*#__PURE__*/ _jsxs(_Fragment, {
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-summary",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "æ”¶å…¥"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value income",
children: [
"+",
formatMoney(totalIncome)
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "支出"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value expense",
children: [
"-",
formatMoney(totalExpense)
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "结余"
}),
/*#__PURE__*/ _jsx("span", {
className: "value",
children: formatMoney(totalBalance)
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "results-count",
children: [
"找到 ",
results.length,
" æ¡è®°å½•"
]
})
]
}) : null
]
}),
loading ? /*#__PURE__*/ _jsx("div", {
className: "card loading-state",
children: "æœç´¢ä¸­..."
}) : results.length > 0 ? /*#__PURE__*/ _jsx("section", {
className: "card section-card",
children: /*#__PURE__*/ _jsx("div", {
className: "records-list records-list--flush",
children: sortedDates.map((date)=>/*#__PURE__*/ _jsxs("div", {
className: "date-group",
children: [
/*#__PURE__*/ _jsx("div", {
className: "date-label",
children: new Date(date).toLocaleDateString("zh-CN", {
month: "short",
day: "numeric",
weekday: "short"
})
}),
groupedResults[date].map((record)=>{
const categories = DEFAULT_CATEGORIES[record.type];
const cat = categories.find((item)=>item.name === record.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ _jsxs("div", {
className: "record-item record-item--static",
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))
})
}) : keyword ? /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "未找到相关记录"
})
]
}) : /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",
children: /*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 24
})
}),
/*#__PURE__*/ _jsx("p", {
children: "输入关键è¯å¼€å§‹æœç´¢"
})
]
})
]
});
}
R085undefined87130undefined132171undefined173227undefined229283undefined285330undefined332374undefined376430undefined432446undefined486494(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined525532(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_6__/* .useToast */ .p)undefined571578(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined619626(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined667674(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined688696(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined717726(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)undefined10891101(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .searchRecords */ .Kp)undefined20482051(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined22302233(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined22352238_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined23382342(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined23442351_components_AppShell__WEBPACK_IMPORTED_MODULE_4__/* ["default"] */ .Zundefined25122516(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined26892693(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined29022906(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined30753078(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined30803083_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined33613364(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined38993902(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined41544158(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined43374340(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined43424345_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined48434847(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined48494857react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragmentundefined49404944(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined51105114(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined53015304(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined55535557(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined60676071(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined62586261(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined65106514(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined70267030(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined72177220(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined74697472(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined78727876(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined83208323(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined84708473(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined85768579(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined87258729(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined88798882(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined94139430_lib_categories__WEBPACK_IMPORTED_MODULE_3__/* .DEFAULT_CATEGORIES */ .Sundefined97119715(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined99229926(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1013710140(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1043010434(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1066910672(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1102011023(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1154211546(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1223212236(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1237412377(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1249212495(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1249712500_components_icons__WEBPACK_IMPORTED_MODULE_5__/* .Icon */ .Jundefined1267112674(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1279912803(0,react_jsx_runti
/***/ 1163:
 g«webpack/lib/util/registerExternalSerializerœwebpack-sources/ConcatSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSourceJ/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource ý«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource1module.exports = require('./dist/client/router')
`/ªmodule.exports = __webpack_require__(3079)†buffer†source„size„maps„hash+module.exports = __webpack_require__(3079)
”{"finalSource":true}ƒmapbufferedMap ReplaceSourceRawSource1module.exports = require('./dist/client/router')
6047module.exports = __webpack_require__(3079)undefined
/***/ }) Å/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
module.exports = __webpack_require__(3079)
/***/ }) ÓÖ ÄJ/***/ (function(module, __unused_webpack_exports, __webpack_require__) {
Å1module.exports = require('./dist/client/router')
6047module.exports = __webpack_require__(3079)undefinedÆÇ
},
«webpack/lib/util/registerExternalSerializerœwebpack-sources/PrefixSource‰/******/ 9function(__webpack_require__) { // webpackRuntimeModules
÷var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
__webpack_require__.O(0, [888,774,179], function() { return __webpack_exec__(2774); });
var __webpack_exports__ = __webpack_require__.O();
_N_E = __webpack_exports__;
}
]);  Ä `HÅW(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([[603],{
/***/ 2774:
ÄÑÆ
(window.__NEXT_P = window.__NEXT_P || []).push([
"/search",
function () {
return require("private-next-pages/search.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/search"])
});
}
Z106112__webpack_require__undefined1141437073undefined170179falseundefined182279{}undefinedÒÓÒÔÂÕÂÖÄרÒÓÒÙÂÕÂÚÄÛÜÒÓÒÝÂÕÂÃÄNÿÿÿÅÒÓÒ,
/***/ 7073:
ÁÔÁ__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Search; }
/* 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_7__ = __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);
ÂÀ4import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { useState, useEffect } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, searchRecords } from "../lib/api";
import { DEFAULT_CATEGORIES } from "../lib/categories";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
export default function Search() {
const router = useRouter();
const { showToast } = useToast();
const [keyword, setKeyword] = useState("");
const [results, setResults] = useState([]);
const [loading, setLoading] = useState(false);
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
}
}, [
router
]);
const handleSearch = async (e)=>{
e.preventDefault();
if (!keyword.trim()) {
showToast("请输入æœç´¢å…³é”®è¯", {
tone: "info"
});
return;
}
setLoading(true);
try {
const data = await searchRecords(keyword);
setResults(data);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setLoading(false);
}
};
const formatMoney = (num)=>(num || 0).toFixed(2);
const groupedResults = results.reduce((groups, record)=>{
const date = record.date.split(" ")[0];
if (!groups[date]) groups[date] = [];
groups[date].push(record);
return groups;
}, {});
const sortedDates = Object.keys(groupedResults).sort((a, b)=>b.localeCompare(a));
const totalIncome = results.filter((record)=>record.type === "income").reduce((sum, record)=>sum + record.amount, 0);
const totalExpense = results.filter((record)=>record.type === "expense").reduce((sum, record)=>sum + record.amount, 0);
const totalBalance = totalIncome - totalExpense;
const actions = /*#__PURE__*/ _jsx("button", {
type: "button",
className: "icon-button",
"aria-label": "设置",
onClick: ()=>router.push("/settings"),
children: /*#__PURE__*/ _jsx(Icon, {
name: "settings",
size: 18
})
});
return /*#__PURE__*/ _jsxs(AppShell, {
title: "æœç´¢",
subtitle: "快速查找分类ã€å¤‡æ³¨ä¸Žé‡‘é¢",
actions: actions,
compactHeader: true,
children: [
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--search section-card--search-panel",
children: [
/*#__PURE__*/ _jsxs("form", {
className: "search-form search-form--refresh",
onSubmit: handleSearch,
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-input-wrap",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "search",
size: 18,
className: "search-input-wrap__icon"
}),
/*#__PURE__*/ _jsx("input", {
type: "text",
value: keyword,
onChange: (e)=>setKeyword(e.target.value),
placeholder: "æœç´¢åˆ†ç±»æˆ–备注...",
autoFocus: true,
disabled: loading
})
]
}),
/*#__PURE__*/ _jsx("button", {
type: "submit",
className: "btn btn-primary btn-inline",
disabled: loading,
children: loading ? /*#__PURE__*/ _jsxs("span", {
className: "button-content",
children: [
/*#__PURE__*/ _jsx(Icon, {
name: "spinner",
size: 16,
className: "is-spinning"
}),
œç´¢ä¸­..."
]
}) : "æœç´¢"
})
]
}),
results.length > 0 ? /*#__PURE__*/ _jsxs(_Fragment, {
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-summary",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "æ”¶å…¥"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value income",
children: [
"+",
formatMoney(totalIncome)
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "支出"
}),
/*#__PURE__*/ _jsxs("span", {
className: "value expense",
children: [
"-",
formatMoney(totalExpense)
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "search-stat",
children: [
/*#__PURE__*/ _jsx("span", {
className: "label",
children: "结余"
}),
/*#__PURE__*/ _jsx("span", {
className: "value",
children: formatMoney(totalBalance)
})
]
})
]
}),
/*#__PURE__*/ _jsxs("div", {
className: "results-count",
children: [
"找到 ",
results.length,
" æ¡è®°å½•"
]
})
]
}) : null
]
}),
loading ? /*#__PURE__*/ _jsx("div", {
className: "card loading-state",
children: "æœç´¢ä¸­..."
}) : results.length > 0 ? /*#__PURE__*/ _jsx("section", {
className: "card section-card",
children: /*#__PURE__*/ _jsx("div", {
className: "records-list records-list--flush",
children: sortedDates.map((date)=>/*#__PURE__*/ _jsxs("div", {
className: "date-group",
children: [
/*#__PURE__*/ _jsx("div", {
className: "date-label",
children: new Date(date).toLocaleDateString("zh-CN", {
month: "short",
day: "numeric",
weekday: "short"
})
}),
groupedResults[date].map((record)=>{
const categories = DEFAULT_CATEGORIES[record.type];
const cat = categories.find((item)=>item.name === record.category) || {
icon: "\uD83D\uDCDD"
};
return /*#__PURE__*/ _jsxs("div", {
className: "record-item record-item--static",
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))
})
}) : keyword ? /*#__PURE__*/ _jsxs("div", {
className: "card empty-state empty-state--card",
children: [
/*#__PURE__*/ _jsx("div", {
className: "empty-state__icon",