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

21157 lines
1.8 MiB
Raw Normal View History

2026-03-12 11:24:10 +08:00
wpcd]§webpack/lib/cache/PackFileCacheStrategyPackContentItems `nÁTerserWebpackPlugin|static/chunks/pages/stats-bd200cb62ae7499f.jsÀTerserWebpackPlugin|static/FIUSX5NfjL6PTybQJcM93/_ssgManifest.jsÂTerserWebpackPlugin|static/FIUSX5NfjL6PTybQJcM93/_buildManifest.js²CssMinimizerPlugin|static/css/3ba7091422655791.css½RealContentHashPlugin|analyse|static/css/3ba7091422655791.cssËRealContentHashPlugin|analyse|static/chunks/pages/stats-bd200cb62ae7499f.jsÊRealContentHashPlugin|analyse|static/FIUSX5NfjL6PTybQJcM93/_ssgManifest.jsÌRealContentHashPlugin|analyse|static/FIUSX5NfjL6PTybQJcM93/_buildManifest.jsÍRealContentHashPlugin|generate|static/FIUSX5NfjL6PTybQJcM93/_buildManifest.jsÁTerserWebpackPlugin|static/chunks/pages/stats-72db3d460ed05be4.jsÀTerserWebpackPlugin|static/-OJYcmjW6BNq-LCgXrybu/_ssgManifest.jsÂTerserWebpackPlugin|static/-OJYcmjW6BNq-LCgXrybu/_buildManifest.js²CssMinimizerPlugin|static/css/bbbad6ccb1e67e5a.css½RealContentHashPlugin|analyse|static/css/bbbad6ccb1e67e5a.cssËRealContentHashPlugin|analyse|static/chunks/pages/stats-72db3d460ed05be4.jsÊRealContentHashPlugin|analyse|static/-OJYcmjW6BNq-LCgXrybu/_ssgManifest.jsÌRealContentHashPlugin|analyse|static/-OJYcmjW6BNq-LCgXrybu/_buildManifest.jsÍRealContentHashPlugin|generate|static/-OJYcmjW6BNq-LCgXrybu/_buildManifest.jsÀTerserWebpackPlugin|static/jxRrQQqdsOFYg34QNJsXi/_ssgManifest.jsÂTerserWebpackPlugin|static/jxRrQQqdsOFYg34QNJsXi/_buildManifest.js²CssMinimizerPlugin|static/css/c407e01aeed6e259.css½RealContentHashPlugin|analyse|static/css/c407e01aeed6e259.cssÊRealContentHashPlugin|analyse|static/jxRrQQqdsOFYg34QNJsXi/_ssgManifest.jsÌRealContentHashPlugin|analyse|static/jxRrQQqdsOFYg34QNJsXi/_buildManifest.jsÍRealContentHashPlugin|generate|static/jxRrQQqdsOFYg34QNJsXi/_buildManifest.jsÁTerserWebpackPlugin|static/chunks/pages/index-a50293e89481ffbd.jsËRealContentHashPlugin|analyse|static/chunks/pages/index-a50293e89481ffbd.jsÁTerserWebpackPlugin|static/chunks/pages/index-1ec596df3d601f3f.jsÀTerserWebpackPlugin|static/IEhGCqBnNgPWAkh47B6cf/_ssgManifest.jsÂTerserWebpackPlugin|static/IEhGCqBnNgPWAkh47B6cf/_buildManifest.js²CssMinimizerPlugin|static/css/336bcea150ab0fce.css½RealContentHashPlugin|analyse|static/css/336bcea150ab0fce.cssËRealContentHashPlugin|analyse|static/chunks/pages/index-1ec596df3d601f3f.jsÊRealContentHashPlugin|analyse|static/IEhGCqBnNgPWAkh47B6cf/_ssgManifest.jsÌRealContentHashPlugin|analyse|static/IEhGCqBnNgPWAkh47B6cf/_buildManifest.jsÍRealContentHashPlugin|generate|static/IEhGCqBnNgPWAkh47B6cf/_buildManifest.jsÀTerserWebpackPlugin|static/fWx1JW1BQkybJssI2Yd7y/_ssgManifest.jsÂTerserWebpackPlugin|static/fWx1JW1BQkybJssI2Yd7y/_buildManifest.jsÊRealContentHashPlugin|analyse|static/fWx1JW1BQkybJssI2Yd7y/_ssgManifest.jsÌRealContentHashPlugin|analyse|static/fWx1JW1BQkybJssI2Yd7y/_buildManifest.jsÍRealContentHashPlugin|generate|static/fWx1JW1BQkybJssI2Yd7y/_buildManifest.js±ResolverCachePlugin|loader|default|path=|/opt/accountbook/frontend/styles|request=|/opt/accountbook/frontend/node_modules/next/dist/build/webpack/loaders/css-loader/src/index.jsµResolverCachePlugin|loader|default|path=|/opt/accountbook/frontend/styles|request=|/opt/accountbook/frontend/node_modules/next/dist/build/webpack/loaders/postcss-loader/src/index.js´ResolverCachePlugin|normal|default|dependencyType=|esm|path=|/opt/accountbook/frontend/styles|request=|../node_modules/next/dist/build/webpack/loaders/css-loader/src/runtime/api.jsøCompilation/modules|/opt/accountbook/frontend/node_modules/next/dist/build/webpack/loaders/css-loader/src/runtime/api.jsŠCompilation/codeGeneration|/opt/accountbook/frontend/node_modules/next/dist/build/webpack/loaders/css-loader/src/runtime/api.js|build timeÏCompilation/codeGeneration|webpack/runtime/compat get default export|build timeËCompilation/codeGeneration|webpack/runtime/make namespace object|build timeÍCompilation/codeGeneration|webpack/runtime/define property getters|build timeÎCompilation/codeGeneration|webpack/runtime/hasOwnProperty shorthand|build timeÀTerserWebpackPlugin|static/naNTIsEyGXK2ivWQpP1_G/_ssgMani
a50293e89481ffbd4f9dfbe25cb315987b87248a8ad83adb84632b2968be1e4404563c7d5abdac2c409b77ecb657f4575384580e3bc2c9e67f08890fb6f1598d0cb57a344af69d33bd200cb62ae7499fsself.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[{has:s,source:"/api/:path*",destination:s}],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-5fc7f759529267fa.js"],"/_error":["static/chunks/pages/_error-7a92967bea80186d.js"],"/categories":["static/chunks/pages/categories-5a5eb0adb3353980.js"],"/login":["static/chunks/pages/login-f50164aa2a6805b4.js"],"/recurring":["static/chunks/pages/recurring-560011bee3956d16.js"],"/search":["static/chunks/pages/search-3f7f0eb3485675fc.js"],"/settings":["static/chunks/pages/settings-bb02e29d5162e9fa.js"],"/stats":["static/chunks/ee8b1517-5dfbbb6838544537.js","static/chunks/925-ca8bea7fb2a62185.js","static/chunks/pages/stats-3f0fcc9134bcb794.js"],sortedPages:["/","/_app","/_error","/categories","/login","/recurring","/search","/settings","/stats"]}}(void 0),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); Ùÿ'(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[368],{4841:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/stats",function(){return n(8011)}])},6248:function(e,t,n){"use strict";n.d(t,{Z:function(){return i}});var a=n(5893),r=n(7294),c=n(1163),o=n(9415);let s=[{href:"/",label:"首页",icon:"home"},{href:"/stats",label:"统计",icon:"chart"},{href:"/recurring",label:"周期",icon:"repeat"},{href:"/categories",label:"分类",icon:"tags"}];function i(e){let{title:t,subtitle:n,actions:i,headerContent:l,children:u,contentClassName:d="",hideNav:h=!1,compactHeader:p=!1}=e,m=(0,c.useRouter)(),f=(0,r.useMemo)(()=>{let e=s.find(e=>e.href===m.pathname);return(null==e?void 0:e.href)||null},[m.pathname]),g=l||(t?(0,a.jsxs)("div",{className:"app-header__text",children:[(0,a.jsx)("h1",{children:t}),n?(0,a.jsx)("p",{children:n}):null]}):null),y=["app-header",p?"app-header--compact":"",g?"app-header--with-content":"app-header--actions-only",i?"app-header--with-actions":""].filter(Boolean).join(" ");return(0,a.jsxs)("div",{className:"app-shell".concat(h?" app-shell--standalone":""),children:[(0,a.jsx)("header",{className:y,children:(0,a.jsxs)("div",{className:"app-shell__container app-header__inner",children:[g?(0,a.jsx)("div",{className:"app-header__main",children:g}):(0,a.jsx)("div",{className:"app-header__spacer","aria-hidden":"true"}),i?(0,a.jsx)("div",{className:"app-header__actions",children:i}):null]})}),(0,a.jsx)("main",{className:"app-content ".concat(d).trim(),children:(0,a.jsx)("div",{className:"app-shell__container app-content__inner",children:u})}),h?null:(0,a.jsx)("nav",{className:"bottom-nav","aria-label":"主导航",children:(0,a.jsx)("div",{className:"app-shell__container bottom-nav__inner",children:s.map(e=>{let t=f===e.href;return(0,a.jsxs)("button",{type:"button",className:"bottom-nav__item".concat(t?" is-active":""),onClick:()=>m.push(e.href),"aria-current":t?"page":void 0,children:[(0,a.jsx)(o.J,{name:e.icon,size:20}),(0,a.jsx)("span",{children:e.label})]},e.href)})})})]})}},610:function(e,t,n){"use strict";async function a(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=await fetch("".concat("").concat(e),{...t,headers:{"Content-Type":"application/json",...function(){let e=localStorage.getItem("token");return e?{Authorization:"Bearer ".concat(e)}:{}}(),...t.headers}}),a=await n.json();if(!n.ok)throw Error(a.error||"请求失败");return a}async function r(e,t){let n=await a("/api/auth/login",{method:"POST",body:JSON.stringify({username:e,password:t})});return localStorage.setItem("token",n.token),n}async function c(e,t){let n=await a("/api/auth/register",{method:"POST",body:JSON.stringify({username:e,password:t})});return localStorage.setItem("token",n.token),n}function o(){localStorage.removeItem("token")}function s(){return!!localStorage.getItem("token")}async function i(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",n="/api/records?month=".concat(e);return"all"!==t&&(n+="&type=".concat(t)
ÛÜÝÞßàáâã72db3d460ed05be4sself.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[{has:s,source:"/api/:path*",destination:s}],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-5fc7f759529267fa.js"],"/_error":["static/chunks/pages/_error-7a92967bea80186d.js"],"/categories":["static/chunks/pages/categories-5a5eb0adb3353980.js"],"/login":["static/chunks/pages/login-f50164aa2a6805b4.js"],"/recurring":["static/chunks/pages/recurring-560011bee3956d16.js"],"/search":["static/chunks/pages/search-3f7f0eb3485675fc.js"],"/settings":["static/chunks/pages/settings-bb02e29d5162e9fa.js"],"/stats":["static/chunks/ee8b1517-5dfbbb6838544537.js","static/chunks/925-ca8bea7fb2a62185.js","static/chunks/pages/stats-484a75e3fb56c7ab.js"],sortedPages:["/","/_app","/_error","/categories","/login","/recurring","/search","/settings","/stats"]}}(void 0),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); ½Á »Â 4q:root{--bg:#f4f6fb;--bg-accent:radial-gradient(circle at top,rgba(59,130,246,.12),transparent 30%),linear-gradient(180deg,#f9fbff,#f2f5fa);--surface:hsla(0,0%,100%,.9);--surface-strong:hsla(0,0%,100%,.98);--surface-muted:#eef3fb;--surface-soft:#f6f8fc;--surface-elevated:rgba(15,23,42,.86);--text:#0f172a;--text-secondary:#475569;--text-tertiary:#64748b;--text-on-dark:#f8fafc;--primary:#2563eb;--primary-dark:#1d4ed8;--primary-soft:rgba(37,99,235,.1);--income:#059669;--income-soft:rgba(5,150,105,.12);--expense:#dc2626;--expense-soft:rgba(220,38,38,.1);--warning:#d97706;--warning-soft:rgba(217,119,6,.12);--border:rgba(148,163,184,.2);--border-strong:rgba(100,116,139,.28);--shadow-xs:0 1px 3px rgba(15,23,42,.05);--shadow-sm:0 10px 28px rgba(15,23,42,.06);--shadow-md:0 18px 44px rgba(15,23,42,.1);--radius-xs:10px;--radius-sm:14px;--radius:18px;--radius-lg:24px;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-7:28px;--space-8:32px;--shell-width:760px;--header-height:62px;--bottom-nav-height:84px;--safe-bottom:max(24px,env(safe-area-inset-bottom));--safe-top:max(20px,env(safe-area-inset-top));--transition:180ms ease;--font-sans:"Inter","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;--font-mono:"SF Mono","JetBrains Mono",Monaco,monospace;color-scheme:light}*{box-sizing:border-box;margin:0;padding:0}#__next,body,html{min-height:100%}body{font-family:var(--font-sans);background:var(--bg-accent);color:var(--text);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}a,button,input,select,textarea{font:inherit}button,input,select,textarea{border:0}button{background:none;color:inherit}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:3px solid rgba(37,99,235,.18);outline-offset:2px}.app-shell{min-height:100vh;padding-bottom:calc(var(--bottom-nav-height) + var(--safe-bottom) + var(--space-3))}.app-shell--standalone{padding-bottom:0}.app-shell__container{width:min(100% - 28px,var(--shell-width));margin:0 auto}.app-header{position:-webkit-sticky;position:sticky;top:0;z-index:40;padding-top:var(--safe-top);-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);background:rgba(249,251,255,.9);border-bottom:1px solid rgba(226,232,240,.9)}.app-header:after{content:none}.app-header--compact{background:rgba(249,251,255,.86)}.app-header__inner{position:relative;min-height:62px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:10px 0 8px}.app-header--compact .app-header__inner{min-height:56px;gap:10px;padding:8px 0}.app-header__main,.app-header__spacer{min-width:0;flex:1 1}.app-header__main{display:flex;align-items:center;justify-content:center}.app-header--actions-only .app-header__main{display:none}.app-header__text{min-width:0;display:grid;grid-gap:2px;gap:2px}.app-header__text h1{font-size:1.02rem;line-height:1.15;letter-spacing:-.03em;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-header--compact .app-header__text h1{font-size:.98rem}.app-header__text p{color:var(--text-secondary);font-size:.76rem;line-height
ÆÇÈÉÊËÌÍÎÏsself.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[{has:s,source:"/api/:path*",destination:s}],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-5fc7f759529267fa.js"],"/_error":["static/chunks/pages/_error-7a92967bea80186d.js"],"/categories":["static/chunks/pages/categories-5a5eb0adb3353980.js"],"/login":["static/chunks/pages/login-f50164aa2a6805b4.js"],"/recurring":["static/chunks/pages/recurring-560011bee3956d16.js"],"/search":["static/chunks/pages/search-3f7f0eb3485675fc.js"],"/settings":["static/chunks/pages/settings-bb02e29d5162e9fa.js"],"/stats":["static/chunks/ee8b1517-5dfbbb6838544537.js","static/chunks/925-ca8bea7fb2a62185.js","static/chunks/pages/stats-3f0fcc9134bcb794.js"],sortedPages:["/","/_app","/_error","/categories","/login","/recurring","/search","/settings","/stats"]}}(void 0),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); ©<(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[405],{5557:function(e,n,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/",function(){return t(9828)}])},6248:function(e,n,t){"use strict";t.d(n,{Z:function(){return o}});var a=t(5893),c=t(7294),s=t(1163),i=t(9415);let r=[{href:"/",label:"首页",icon:"home"},{href:"/stats",label:"统计",icon:"chart"},{href:"/recurring",label:"周期",icon:"repeat"},{href:"/categories",label:"分类",icon:"tags"}];function o(e){let{title:n,subtitle:t,actions:o,headerContent:l,children:u,contentClassName:d="",hideNav:m=!1,compactHeader:h=!1}=e,p=(0,s.useRouter)(),y=(0,c.useMemo)(()=>{let e=r.find(e=>e.href===p.pathname);return(null==e?void 0:e.href)||null},[p.pathname]),x=l||(n?(0,a.jsxs)("div",{className:"app-header__text",children:[(0,a.jsx)("h1",{children:n}),t?(0,a.jsx)("p",{children:t}):null]}):null),f=["app-header",h?"app-header--compact":"",x?"app-header--with-content":"app-header--actions-only",o?"app-header--with-actions":""].filter(Boolean).join(" ");return(0,a.jsxs)("div",{className:"app-shell".concat(m?" app-shell--standalone":""),children:[(0,a.jsx)("header",{className:f,children:(0,a.jsxs)("div",{className:"app-shell__container app-header__inner",children:[x?(0,a.jsx)("div",{className:"app-header__main",children:x}):(0,a.jsx)("div",{className:"app-header__spacer","aria-hidden":"true"}),o?(0,a.jsx)("div",{className:"app-header__actions",children:o}):null]})}),(0,a.jsx)("main",{className:"app-content ".concat(d).trim(),children:(0,a.jsx)("div",{className:"app-shell__container app-content__inner",children:u})}),m?null:(0,a.jsx)("nav",{className:"bottom-nav","aria-label":"主导航",children:(0,a.jsx)("div",{className:"app-shell__container bottom-nav__inner",children:r.map(e=>{let n=y===e.href;return(0,a.jsxs)("button",{type:"button",className:"bottom-nav__item".concat(n?" is-active":""),onClick:()=>p.push(e.href),"aria-current":n?"page":void 0,children:[(0,a.jsx)(i.J,{name:e.icon,size:20}),(0,a.jsx)("span",{children:e.label})]},e.href)})})})]})}},610:function(e,n,t){"use strict";async function a(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},t=await fetch("".concat("").concat(e),{...n,headers:{"Content-Type":"application/json",...function(){let e=localStorage.getItem("token");return e?{Authorization:"Bearer ".concat(e)}:{}}(),...n.headers}}),a=await t.json();if(!t.ok)throw Error(a.error||"请求失败");return a}async function c(e,n){let t=await a("/api/auth/login",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}async function s(e,n){let t=await a("/api/auth/register",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}function i(){localStorage.removeItem("token")}function r(){return!!localStorage.getItem("token")}async function o(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/records?month=".concat(e);return"all"!==n&&(t+="&type=".concat(n)),a(t)}async function l(e){return a("/api/stats?month=".concat(e))}async function u(e){return a("/api/records",{method:"POST",body:JSON.stringify(e)})}asyn
1ec596df3d601f3f¥¦§¨©ª«¬42e73a7be1137208sself.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[{has:s,source:"/api/:path*",destination:s}],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-e3cd22daa5817dfd.js"],"/_error":["static/chunks/pages/_error-7a92967bea80186d.js"],"/categories":["static/chunks/pages/categories-5a5eb0adb3353980.js"],"/login":["static/chunks/pages/login-f50164aa2a6805b4.js"],"/recurring":["static/chunks/pages/recurring-560011bee3956d16.js"],"/search":["static/chunks/pages/search-3f7f0eb3485675fc.js"],"/settings":["static/chunks/pages/settings-bb02e29d5162e9fa.js"],"/stats":["static/chunks/ee8b1517-5dfbbb6838544537.js","static/chunks/925-ca8bea7fb2a62185.js","static/chunks/pages/stats-ce96160268c9c2d4.js"],sortedPages:["/","/_app","/_error","/categories","/login","/recurring","/search","/settings","/stats"]}}(void 0),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); Š sself.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[{has:s,source:"/api/:path*",destination:s}],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-6cb4660fc497dc4e.js"],"/_error":["static/chunks/pages/_error-4f9dfbe25cb31598.js"],"/categories":["static/chunks/pages/categories-7b87248a8ad83adb.js"],"/login":["static/chunks/pages/login-84632b2968be1e44.js"],"/recurring":["static/chunks/pages/recurring-04563c7d5abdac2c.js"],"/search":["static/chunks/pages/search-409b77ecb657f457.js"],"/settings":["static/chunks/pages/settings-5384580e3bc2c9e6.js"],"/stats":["static/chunks/ee8b1517-7f08890fb6f1598d.js","static/chunks/925-0cb57a344af69d33.js","static/chunks/pages/stats-42e73a7be1137208.js"],sortedPages:["/","/_app","/_error","/categories","/login","/recurring","/search","/settings","/stats"]}}(void 0),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); 
6cb4660fc497dc4e˜šœñsself.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[{has:s,source:"/api/:path*",destination:s}],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-9134a1c15f8e7e73.js"],"/_error":["static/chunks/pages/_error-7a92967bea80186d.js"],"/categories":["static/chunks/pages/categories-5a5eb0adb3353980.js"],"/login":["static/chunks/pages/login-f50164aa2a6805b4.js"],"/recurring":["static/chunks/pages/recurring-560011bee3956d16.js"],"/search":["static/chunks/pages/search-3f7f0eb3485675fc.js"],"/settings":["static/chunks/pages/settings-bb02e29d5162e9fa.js"],"/stats":["static/chunks/ee8b1517-5dfbbb6838544537.js","static/chunks/925-ca8bea7fb2a62185.js","static/chunks/pages/stats-ce96160268c9c2d4.js"],sortedPages:["/","/_app","/_error","/categories","/login","/recurring","/search","/settings","/stats"]}}(void 0),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB(); ¥webpack/lib/cache/ResolverCachePlugin`_ResolverCachePluginCacheMiss‡context„path‡request…queryˆfragment†module‰directory„fileˆinternalŽfullySpecified“descriptionFilePath“descriptionFileData“descriptionFileRootŒrelativePath †issuerissuerLayerˆcompiler¬/opt/accountbook/frontend/styles/globals.css†clientÞ/opt/accountbook/frontend/node_modules/next/dist/build/webpack/loaders/css-loader/src/index.js€€ ¸/opt/accountbook/frontend/node_modules/next/package.json`„name‡versiondescription„main‡licenseŠrepository„bugsˆhomepage…types…filesƒbin‡scripts…taskrŒdependenciespeerDependencies”peerDependenciesMetadevDependencies‡engines”optionalDependencies„next‡14.2.35“The React Framework•./dist/server/next.jsƒMITŽvercel/next.js¨https://github.com/vercel/next.js/issueshttps://nextjs.orgŠindex.d.ts`=„dist†app.jsˆapp.d.tsˆbabel.jsŠbabel.d.ts‰client.jsclient.d.ts†compatˆcache.jsŠcache.d.ts‰config.jsconfig.d.tsŒconstants.jsŽconstants.d.tsdocument.jsdocument.d.tsŠdynamic.jsŒdynamic.d.tsˆerror.jsŠerror.d.ts†future†legacy‰script.jsscript.d.ts‰server.jsserver.d.ts‡head.js‰head.d.tsˆimage.jsŠimage.d.ts‡link.js‰link.d.ts‰router.jsrouter.d.ts‡jest.js‰jest.d.ts†amp.jsˆamp.d.ts…og.js‡og.d.ts×types/index.d.tstypes/global.d.ts“types/compiled.d.ts—image-types/global.d.ts navigation-types/navigation.d.ts§navigation-types/compat/navigation.d.ts„fontnavigation.jsnavigation.d.tsŠheaders.jsŒheaders.d.tsnavigation-typesweb-vitals.jsweb-vitals.d.ts£experimental/testmode/playwright.js¥experimental/testmode/playwright.d.ts§experimental/testmode/playwright/msw.js©experimental/testmode/playwright/msw.d.tsžexperimental/testmode/proxy.js experimental/testmode/proxy.d.tsº./dist/bin/nextƒdev‡release…buildŽprepublishOnly¨ŠtypescriptŒncc-compiled©taskr releaseŒpnpm releaseœcd ../../ && turbo run buildÍtsc --declaration --emitDeclarationOnly --stripInternal --declarationDir disttsec --noEmitœncc cache clean && taskr nccˆrequires•./taskfile-webpack.js./taskfile-ncc.js./taskfile-swc.js“./taskfile-watch.js‰@next/envŒ@swc/helpers†busboyŒcaniuse-litegraceful-fs‡postcssŠstyled-jsxœ…0.5.5…1.6.0^1.0.30001579‡^4.2.11†8.4.31…5.1.1@opentelemetry/api@playwright/test…react‰react-dom„sass†^1.1.0‡^1.41.2‡^18.2.0ÿ†^1.3.0ùõöˆoptional þ ý @ampproject/toolbox-optimizer@babel/code-frame@babel/core”@babel/eslint-parser@babel/generator§@babel/plugin-proposal-class-properties¬@babel/plugin-proposal-export-namespace-from¨@babel/plugin-proposal-numeric-separator©@babel/plugin-proposal-object-rest-spread@babel/plugin-syntax-bigint£@babel/plugin-syntax-dynamic-import¦@babel/plugin-syntax-import-assertions˜@babel/plugin-syntax-jsx¨@babel/plugin-transform-modules-commonjsŸ@babel/plugin-transform-runtime@babel/preset-env“@babel/preset-react˜@babel/preset-typescriptŽ@babel/runtime@babel/traverseŒ@babel/types“@capsizecss/metrics•@edge-runtime/cookies@edge-runtime/ponyfill˜@edge-runtime/primitivesŒ@hapi/accept@jest/transform@jest/types“@mswjs/interceptors@napi-rs/triples•@next/polyfill-module—@next/polyfill-nomodule™@next/react-refresh-utils‰@next/swcÍÎŒ@ta
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/ // css base code, injected by the css-loader
// eslint-disable-next-line func-names
"use strict";
module.exports = function(useSourceMap) {
var list = [] // return the list of modules as css string
;
list.toString = function toString() {
return this.map(function(item) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
var content = cssWithMappingToString(item, useSourceMap);
if (item[2]) {
return "@media ".concat(item[2], " {").concat(content, "}");
}
return content;
}).join("");
} // import a list of modules into the list
;
// eslint-disable-next-line func-names
// @ts-expect-error TODO: fix type
list.i = function(modules, mediaQuery, dedupe) {
if (typeof modules === "string") {
// eslint-disable-next-line no-param-reassign
modules = [
[
null,
modules,
""
]
];
}
var alreadyImportedModules = {};
if (dedupe) {
for(var i = 0; i < this.length; i++){
// eslint-disable-next-line prefer-destructuring
var id = this[i][0];
if (id != null) {
alreadyImportedModules[id] = true;
}
}
}
for(var _i = 0; _i < modules.length; _i++){
var item = [].concat(modules[_i]);
if (dedupe && alreadyImportedModules[item[0]]) {
continue;
}
if (mediaQuery) {
if (!item[2]) {
item[2] = mediaQuery;
} else {
item[2] = "".concat(mediaQuery, " and ").concat(item[2]);
}
}
list.push(item);
}
};
return list;
};
function cssWithMappingToString(item, useSourceMap) {
var content = item[1] || "" // eslint-disable-next-line prefer-destructuring
;
var cssMapping = item[3];
if (!cssMapping) {
return content;
}
if (useSourceMap && typeof btoa === "function") {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
var sourceMapping = toComment(cssMapping);
var sourceURLs = cssMapping.sources.map(function(source) {
return "/*# sourceURL=".concat(cssMapping.sourceRoot || "").concat(source, " */");
});
return [
content
].concat(sourceURLs).concat([
sourceMapping
]).join("\n");
}
return [
content
].join("\n");
} // Adapted from convert-source-map (MIT)
function toComment(sourceMap) {
// eslint-disable-next-line no-undef
var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64);
return "/*# ".concat(data, " */");
}
//# sourceMappingURL=api.js.map   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ÿþ `‡þÿÿŽþÿÿ‰constants†crypto†domain„http…httpsos‰ýÿÿÌþÿÿËþÿÿ‹querystring†streamßþÿÿƒsys†timersƒttyçþÿÿ‚vm„zlib—þÿÿŒ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“missingDependenciesbuildDependenciesvalueDependenciesrÿÿÿ†assetsŠassetsInfo†strict”topLevelDeclarationsˆsnapshot  ™webpack/DefinePlugin_hashˆ898e14693f3cf348c1d721d7 cssWithMappingToString‰toComment@ @–·ÍyB _ÿÿÿkÿÿÿ¨jýÿÿ¨webpack/lib/dependencies/ConstDependencyÇ ` ¸webpack/lib/dependencies/CommonJsSelfReferenceDependencyÖŽmodule.exports`
‡sources“runtimeRequirements„data Šjavascript«webpack/lib/util/registerExternalSerializerœwebpack-sources/CachedSource å «webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSource«webpack/lib/util/registerExternalSerializer™webpack-sources/RawSource= /*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/ // css base code, injected by the css-loader
// eslint-disable-next-line func-names
"use strict";
module.exports = function(useSourceMap) {
var list = [] // return the list of modules as css string
;
list.toString = function toString() {
return this.map(function(item) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
var content = cssWithMappingToString(item, useSourceMap);
if (item[2]) {
return "@media ".concat(item[2], " {").concat(content, "}");
}
return content;
}).join("");
} // import a list of modules into the list
;
// eslint-disable-next-line func-names
// @ts-expect-error TODO: fix type
list.i = function(modules, mediaQuery, dedupe) {
if (typeof modules === "string") {
// eslint-disable-next-line no-param-reassign
modules = [
[
null,
modules,
""
]
];
}
var alreadyImportedModules = {};
if (dedupe) {
for(var i = 0; i < this.length; i++){
// eslint-disable-next-line prefer-destructuring
var id = this[i][0];
if (id != null) {
alreadyImportedModules[id] = true;
}
}
}
for(var _i = 0; _i < modules.length; _i++){
var item = [].concat(modules[_i]);
if (dedupe && alreadyImportedModules[item[0]]) {
continue;
}
if (mediaQuery) {
if (!item[2]) {
item[2] = mediaQuery;
} else {
item[2] = "".concat(mediaQuery, " and ").concat(item[2]);
}
}
list.push(item);
}
};
return list;
};
function cssWithMappingToString(item, useSourceMap) {
var content = item[1] || "" // eslint-disable-next-line prefer-destructuring
;
var cssMapping = item[3];
if (!cssMapping) {
return content;
}
if (useSourceMap && typeof btoa === "function") {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
var sourceMapping = toComment(cssMapping);
var sourceURLs = cssMapping.sources.map(function(source) {
return "/*# sourceURL=".concat(cssMapping.sourceRoot || "").concat(source, " */");
});
return [
content
].concat(sourceURLs).concat([
sourceMapping
]).join("\n");
}
return [
content
].join("\n");
} // Adapted from convert-source-map (MIT)
function toComment(sourceMap) {
// eslint-disable-next-line no-undef
var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64);
return "/*# ".concat(data, " */");
}
//# sourceMappingURL=api.js.map Æ Aþÿÿ¸üÿÿ„size„mapsPÿÿÿ0 /*
MIT License http://www.opensource.org/licenses/mit-license.php
Author Tobias Koppers @sokra
*/ // css base code, injected by the css-loader
// eslint-disable-next-line func-names
module.exports = function(useSourceMap) {
var list = [] // return the list of modules as css string
;
list.toString = function toString() {
return this.map(function(item) {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
var content = cssWithMappingToString(item, useSourceMap);
if (item[2]) {
return "@media ".concat(item[2], " {").concat(content, "}");
}
return content;
}).join("");
} // import a list of modules into the list
;
// eslint-disable-next-line func-names
// @ts-expect-error TODO: fix type
list.i = function(modules, mediaQuery, dedupe) {
if (typeof modules === "string") {
// eslint-disable-next-line no-param-reassign
modules = [
[
null,
modules,
""
]
];
}
var alreadyImportedModules = {};
if (dedupe) {
for(var i = 0; i < this.length; i++){
// eslint-disable-next-line prefer-destructuring
var id = this[i][0];
if (id != null) {
alreadyImportedModules[id] = true;
}
}
}
for(var _i = 0; _i < modules.length; _i++){
var item = [].concat(modules[_i]);
if (dedupe && alreadyImportedModules[item[0]]) {
continue;
}
if (mediaQuery) {
if (!item[2]) {
item[2] = mediaQuery;
} else {
item[2] = "".concat(mediaQuery, " and ").concat(item[2]);
}
}
list.push(item);
}
};
return list;
};
function cssWithMappingToString(item, useSourceMap) {
var content = item[1] || "" // eslint-disable-next-line prefer-destructuring
;
var cssMapping = item[3];
if (!cssMapping) {
return content;
}
if (useSourceMap && typeof btoa === "function") {
// eslint-disable-next-line @typescript-eslint/no-use-before-define
var sourceMapping = toComment(cssMapping);
var sourceURLs = cssMapping.sources.map(function(source) {
return "/*# sourceURL=".concat(cssMapping.sourceRoot || "").concat(source, " */");
});
return [
content
].concat(sourceURLs).concat([
sourceMapping
]).join("\n");
}
return [
content
].join("\n");
} // Adapted from convert-source-map (MIT)
function toComment(sourceMap) {
// eslint-disable-next-line no-undef
var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap))));
var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64);
return "/*# ".concat(data, " */");
}
//# sourceMappingURL=api.js.map  >ýÿÿ ñò ‡runtime
.// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function(module) {
var getter = module && module.__esModule ?
function() { return module['default']; } :
function() { return module; };
__webpack_require__.d(getter, { a: getter });
return getter;
};   ú û
// define __esModule on exports
__webpack_require__.r = function(exports) {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
}
Object.defineProperty(exports, '__esModule', { value: true });
};   ö ÷
9// define getter functions for harmony exports
__webpack_require__.d = function(exports, definition) {
for(var key in definition) {
if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
}
}
};   ò ó
g__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop); }   œüÿÿ
 üÿÿ  šüÿÿ
ýÿÿ  s:root{--bg:#f4f6fb;--bg-accent:radial-gradient(circle at top,rgba(59,130,246,.12),transparent 30%),linear-gradient(180deg,#f9fbff,#f2f5fa);--surface:hsla(0,0%,100%,.9);--surface-strong:hsla(0,0%,100%,.98);--surface-muted:#eef3fb;--surface-soft:#f6f8fc;--surface-elevated:rgba(15,23,42,.86);--text:#0f172a;--text-secondary:#475569;--text-tertiary:#64748b;--text-on-dark:#f8fafc;--primary:#2563eb;--primary-dark:#1d4ed8;--primary-soft:rgba(37,99,235,.1);--income:#059669;--income-soft:rgba(5,150,105,.12);--expense:#dc2626;--expense-soft:rgba(220,38,38,.1);--warning:#d97706;--warning-soft:rgba(217,119,6,.12);--border:rgba(148,163,184,.2);--border-strong:rgba(100,116,139,.28);--shadow-xs:0 1px 3px rgba(15,23,42,.05);--shadow-sm:0 10px 28px rgba(15,23,42,.06);--shadow-md:0 18px 44px rgba(15,23,42,.1);--radius-xs:10px;--radius-sm:14px;--radius:18px;--radius-lg:24px;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-7:28px;--space-8:32px;--shell-width:760px;--header-height:44px;--bottom-nav-height:84px;--safe-bottom:max(24px,env(safe-area-inset-bottom));--safe-top:max(20px,env(safe-area-inset-top));--transition:180ms ease;--font-sans:"Inter","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;--font-mono:"SF Mono","JetBrains Mono",Monaco,monospace;color-scheme:light}*{box-sizing:border-box;margin:0;padding:0}#__next,body,html{min-height:100%}body{font-family:var(--font-sans);background:var(--bg-accent);color:var(--text);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}a,button,input,select,textarea{font:inherit}button,input,select,textarea{border:0}button{background:none;color:inherit}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:3px solid rgba(37,99,235,.18);outline-offset:2px}.app-shell{min-height:100vh;padding-bottom:calc(var(--bottom-nav-height) + var(--safe-bottom) + var(--space-3))}.app-shell--standalone{padding-bottom:0}.app-shell__container{width:min(100% - 28px,var(--shell-width));margin:0 auto}.app-header{position:-webkit-sticky;position:sticky;top:0;z-index:40;padding-top:var(--safe-top);-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);background:rgba(249,251,255,.9);border-bottom:1px solid rgba(226,232,240,.9)}.app-header:after{content:none}.app-header--compact{background:rgba(249,251,255,.86)}.app-header__inner{position:relative;min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:4px 0}.app-header--compact .app-header__inner{min-height:40px;gap:10px;padding:2px 0}.app-header__main,.app-header__spacer{min-width:0;flex:1 1}.app-header__main{display:flex;align-items:center;flex:1 1}.app-header--actions-only .app-header__main{display:none}.app-header__text{min-width:0;display:grid;grid-gap:2px;gap:2px}.app-header__text h1{font-size:1.02rem;line-height:1.15;letter-spacing:-.03em;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-header--compact .app-header__text h1{font-size:.98rem}.app-header__text p{color:var(--text-secondary);font-size:.76rem;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-header--compact .app-header__text p{font-size:.74rem}.app-header__actions{display:flex;align-items:center;justify-content:flex-end;gap:8px;flex-wrap:nowrap;min-width:0;margin-left:auto;flex-shrink:0}.app-header--compact .app-header__actions{gap:6px}.app-content{padding:10px 0 0}.app-content__inner{display:grid;grid-gap:14px;gap:14px;align-content:start}.card{position:relative;background:var(--surface);border:1px solid rgba(148,163,184,.18);border-radius:var(--radius);box-shadow:0 10px 26px rgba(15,23,42,.05);-webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px)}.auth-card,.category-section,.category-tip,.confirm-card,.dashboard-hero,.section-card,.settings-section{overflow:hidden}.auth-card,.category-section,.dashboard-hero,.section-card,.settings-section{padding:16px}.section-card--search{display:grid;grid-gap:14px;gap:14px}.section-card--search-panel{padding:12px;gap:12px;bac
ýÿÿ¦üÿÿ§üÿÿ¨üÿÿ©üÿÿªüÿÿ«üÿÿ¬üÿÿ­üÿÿýÿÿ
ýÿÿ  ‰üÿÿ
B(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[405],{5557:function(e,n,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/",function(){return t(9828)}])},6248:function(e,n,t){"use strict";t.d(n,{Z:function(){return r}});var a=t(5893),c=t(7294),s=t(1163),i=t(9415);let o=[{href:"/",label:"首页",icon:"home"},{href:"/stats",label:"统计",icon:"chart"},{href:"/recurring",label:"周期",icon:"repeat"},{href:"/categories",label:"分类",icon:"tags"}];function r(e){let{title:n,subtitle:t,actions:r,headerContent:l,children:u,contentClassName:d="",hideNav:m=!1,compactHeader:h=!1}=e,p=(0,s.useRouter)(),D=(0,c.useMemo)(()=>{let e=o.find(e=>e.href===p.pathname);return(null==e?void 0:e.href)||null},[p.pathname]),x=l||(n?(0,a.jsxs)("div",{className:"app-header__text",children:[(0,a.jsx)("h1",{children:n}),t?(0,a.jsx)("p",{children:t}):null]}):null),y=["app-header",h?"app-header--compact":"",x?"app-header--with-content":"app-header--actions-only",r?"app-header--with-actions":""].filter(Boolean).join(" ");return(0,a.jsxs)("div",{className:"app-shell".concat(m?" app-shell--standalone":""),children:[(0,a.jsx)("header",{className:y,children:(0,a.jsxs)("div",{className:"app-shell__container app-header__inner",children:[x?(0,a.jsx)("div",{className:"app-header__main",children:x}):(0,a.jsx)("div",{className:"app-header__spacer","aria-hidden":"true"}),r?(0,a.jsx)("div",{className:"app-header__actions",children:r}):null]})}),(0,a.jsx)("main",{className:"app-content ".concat(d).trim(),children:(0,a.jsx)("div",{className:"app-shell__container app-content__inner",children:u})}),m?null:(0,a.jsx)("nav",{className:"bottom-nav","aria-label":"主导航",children:(0,a.jsx)("div",{className:"app-shell__container bottom-nav__inner",children:o.map(e=>{let n=D===e.href;return(0,a.jsxs)("button",{type:"button",className:"bottom-nav__item".concat(n?" is-active":""),onClick:()=>p.push(e.href),"aria-current":n?"page":void 0,children:[(0,a.jsx)(i.J,{name:e.icon,size:20}),(0,a.jsx)("span",{children:e.label})]},e.href)})})})]})}},610:function(e,n,t){"use strict";async function a(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},t=await fetch("".concat("").concat(e),{...n,headers:{"Content-Type":"application/json",...function(){let e=localStorage.getItem("token");return e?{Authorization:"Bearer ".concat(e)}:{}}(),...n.headers}}),a=await t.json();if(!t.ok)throw Error(a.error||"请求失败");return a}async function c(e,n){let t=await a("/api/auth/login",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}async function s(e,n){let t=await a("/api/auth/register",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}function i(){localStorage.removeItem("token")}function o(){return!!localStorage.getItem("token")}async function r(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/records?month=".concat(e);return"all"!==n&&(t+="&type=".concat(n)),a(t)}async function l(e){return a("/api/stats?month=".concat(e))}async function u(e){return a("/api/records",{method:"POST",body:JSON.stringify(e)})}async function d(e,n){return a("/api/records/".concat(e),{method:"PUT",body:JSON.stringify(n)})}async function m(e){return a("/api/records/".concat(e),{method:"DELETE"})}async function h(e){return a("/api/records/search?q=".concat(encodeURIComponent(e)))}async function p(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"month",n=arguments.length>1?arguments[1]:void 0,t="/api/stats/trend?period=".concat(e);return n&&(t+="&month=".concat(n)),a(t)}async function D(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/stats/category?type=".concat(n);return e&&(t+="&month=".concat(e)),a(t)}async function x(){return a("/api/recurring")}async function y(e){return a("/api/recurring",{method:"POST",body:JSON.stringify(e)})}async function j(e,n){return a("/api/recurring/".concat(e),{method:"PUT",body:JSON.stringify(n)})}async function f(e){return a("/api/recurring/".concat(e),{method:"DE
:(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[845],{8208:function(e,n,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/recurring",function(){return t(5190)}])},6248:function(e,n,t){"use strict";t.d(n,{Z:function(){return r}});var a=t(5893),i=t(7294),c=t(1163),s=t(9415);let o=[{href:"/",label:"首页",icon:"home"},{href:"/stats",label:"统计",icon:"chart"},{href:"/recurring",label:"周期",icon:"repeat"},{href:"/categories",label:"分类",icon:"tags"}];function r(e){let{title:n,subtitle:t,actions:r,headerContent:l,children:u,contentClassName:d="",hideNav:m=!1,compactHeader:h=!1}=e,p=(0,c.useRouter)(),D=(0,i.useMemo)(()=>{let e=o.find(e=>e.href===p.pathname);return(null==e?void 0:e.href)||null},[p.pathname]),y=l||(n?(0,a.jsxs)("div",{className:"app-header__text",children:[(0,a.jsx)("h1",{children:n}),t?(0,a.jsx)("p",{children:t}):null]}):null),b=["app-header",h?"app-header--compact":"",y?"app-header--with-content":"app-header--actions-only",r?"app-header--with-actions":""].filter(Boolean).join(" ");return(0,a.jsxs)("div",{className:"app-shell".concat(m?" app-shell--standalone":""),children:[(0,a.jsx)("header",{className:b,children:(0,a.jsxs)("div",{className:"app-shell__container app-header__inner",children:[y?(0,a.jsx)("div",{className:"app-header__main",children:y}):(0,a.jsx)("div",{className:"app-header__spacer","aria-hidden":"true"}),r?(0,a.jsx)("div",{className:"app-header__actions",children:r}):null]})}),(0,a.jsx)("main",{className:"app-content ".concat(d).trim(),children:(0,a.jsx)("div",{className:"app-shell__container app-content__inner",children:u})}),m?null:(0,a.jsx)("nav",{className:"bottom-nav","aria-label":"主导航",children:(0,a.jsx)("div",{className:"app-shell__container bottom-nav__inner",children:o.map(e=>{let n=D===e.href;return(0,a.jsxs)("button",{type:"button",className:"bottom-nav__item".concat(n?" is-active":""),onClick:()=>p.push(e.href),"aria-current":n?"page":void 0,children:[(0,a.jsx)(s.J,{name:e.icon,size:20}),(0,a.jsx)("span",{children:e.label})]},e.href)})})})]})}},610:function(e,n,t){"use strict";async function a(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},t=await fetch("".concat("").concat(e),{...n,headers:{"Content-Type":"application/json",...function(){let e=localStorage.getItem("token");return e?{Authorization:"Bearer ".concat(e)}:{}}(),...n.headers}}),a=await t.json();if(!t.ok)throw Error(a.error||"请求失败");return a}async function i(e,n){let t=await a("/api/auth/login",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}async function c(e,n){let t=await a("/api/auth/register",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}function s(){localStorage.removeItem("token")}function o(){return!!localStorage.getItem("token")}async function r(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/records?month=".concat(e);return"all"!==n&&(t+="&type=".concat(n)),a(t)}async function l(e){return a("/api/stats?month=".concat(e))}async function u(e){return a("/api/records",{method:"POST",body:JSON.stringify(e)})}async function d(e,n){return a("/api/records/".concat(e),{method:"PUT",body:JSON.stringify(n)})}async function m(e){return a("/api/records/".concat(e),{method:"DELETE"})}async function h(e){return a("/api/records/search?q=".concat(encodeURIComponent(e)))}async function p(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"month",n=arguments.length>1?arguments[1]:void 0,t="/api/stats/trend?period=".concat(e);return n&&(t+="&month=".concat(n)),a(t)}async function D(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/stats/category?type=".concat(n);return e&&(t+="&month=".concat(e)),a(t)}async function y(){return a("/api/recurring")}async function b(e){return a("/api/recurring",{method:"POST",body:JSON.stringify(e)})}async function f(e,n){return a("/api/recurring/".concat(e),{method:"PUT",body:JSON.stringify(n)})}async function x(e){return a("/api/recurring/".concat(e),{m
v+(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[603],{2774:function(n,e,a){(window.__NEXT_P=window.__NEXT_P||[]).push(["/search",function(){return a(7073)}])},6248:function(n,e,a){"use strict";a.d(e,{Z:function(){return o}});var t=a(5893),c=a(7294),r=a(1163),i=a(9415);let s=[{href:"/",label:"首页",icon:"home"},{href:"/stats",label:"统计",icon:"chart"},{href:"/recurring",label:"周期",icon:"repeat"},{href:"/categories",label:"分类",icon:"tags"}];function o(n){let{title:e,subtitle:a,actions:o,headerContent:u,children:l,contentClassName:d="",hideNav:m=!1,compactHeader:h=!1}=n,D=(0,r.useRouter)(),p=(0,c.useMemo)(()=>{let n=s.find(n=>n.href===D.pathname);return(null==n?void 0:n.href)||null},[D.pathname]),f=u||(e?(0,t.jsxs)("div",{className:"app-header__text",children:[(0,t.jsx)("h1",{children:e}),a?(0,t.jsx)("p",{children:a}):null]}):null),y=["app-header",h?"app-header--compact":"",f?"app-header--with-content":"app-header--actions-only",o?"app-header--with-actions":""].filter(Boolean).join(" ");return(0,t.jsxs)("div",{className:"app-shell".concat(m?" app-shell--standalone":""),children:[(0,t.jsx)("header",{className:y,children:(0,t.jsxs)("div",{className:"app-shell__container app-header__inner",children:[f?(0,t.jsx)("div",{className:"app-header__main",children:f}):(0,t.jsx)("div",{className:"app-header__spacer","aria-hidden":"true"}),o?(0,t.jsx)("div",{className:"app-header__actions",children:o}):null]})}),(0,t.jsx)("main",{className:"app-content ".concat(d).trim(),children:(0,t.jsx)("div",{className:"app-shell__container app-content__inner",children:l})}),m?null:(0,t.jsx)("nav",{className:"bottom-nav","aria-label":"主导航",children:(0,t.jsx)("div",{className:"app-shell__container bottom-nav__inner",children:s.map(n=>{let e=p===n.href;return(0,t.jsxs)("button",{type:"button",className:"bottom-nav__item".concat(e?" is-active":""),onClick:()=>D.push(n.href),"aria-current":e?"page":void 0,children:[(0,t.jsx)(i.J,{name:n.icon,size:20}),(0,t.jsx)("span",{children:n.label})]},n.href)})})})]})}},610:function(n,e,a){"use strict";async function t(n){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},a=await fetch("".concat("").concat(n),{...e,headers:{"Content-Type":"application/json",...function(){let n=localStorage.getItem("token");return n?{Authorization:"Bearer ".concat(n)}:{}}(),...e.headers}}),t=await a.json();if(!a.ok)throw Error(t.error||"请求失败");return t}async function c(n,e){let a=await t("/api/auth/login",{method:"POST",body:JSON.stringify({username:n,password:e})});return localStorage.setItem("token",a.token),a}async function r(n,e){let a=await t("/api/auth/register",{method:"POST",body:JSON.stringify({username:n,password:e})});return localStorage.setItem("token",a.token),a}function i(){localStorage.removeItem("token")}function s(){return!!localStorage.getItem("token")}async function o(n){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",a="/api/records?month=".concat(n);return"all"!==e&&(a+="&type=".concat(e)),t(a)}async function u(n){return t("/api/stats?month=".concat(n))}async function l(n){return t("/api/records",{method:"POST",body:JSON.stringify(n)})}async function d(n,e){return t("/api/records/".concat(n),{method:"PUT",body:JSON.stringify(e)})}async function m(n){return t("/api/records/".concat(n),{method:"DELETE"})}async function h(n){return t("/api/records/search?q=".concat(encodeURIComponent(n)))}async function D(){let n=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"month",e=arguments.length>1?arguments[1]:void 0,a="/api/stats/trend?period=".concat(n);return e&&(a+="&month=".concat(e)),t(a)}async function p(n){let e=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",a="/api/stats/category?type=".concat(e);return n&&(a+="&month=".concat(n)),t(a)}async function f(){return t("/api/recurring")}async function y(n){return t("/api/recurring",{method:"POST",body:JSON.stringify(n)})}async function x(n,e){return t("/api/recurring/".concat(n),{method:"PUT",body:JSON.stringify(e)})}async function j(n){return t("/api/recurring/".concat(n),{meth
„üÿÿ  ~üÿÿ
sself.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[{has:s,source:"/api/:path*",destination:s}],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-3dacaebc4c4f645c.js"],"/_error":["static/chunks/pages/_error-4f9dfbe25cb31598.js"],"/categories":["static/chunks/pages/categories-7b87248a8ad83adb.js"],"/login":["static/chunks/pages/login-84632b2968be1e44.js"],"/recurring":["static/chunks/pages/recurring-4dfdffc7a6ebe5a9.js"],"/search":["static/chunks/pages/search-1198883987aea083.js"],"/settings":["static/chunks/pages/settings-5384580e3bc2c9e6.js"],"/stats":["static/chunks/ee8b1517-7f08890fb6f1598d.js","static/chunks/925-0cb57a344af69d33.js","static/chunks/pages/stats-42e73a7be1137208.js"],sortedPages:["/","/_app","/_error","/categories","/login","/recurring","/search","/settings","/stats"]}}(void 0),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();  Bs:root{--bg:#f4f6fb;--bg-accent:radial-gradient(circle at top,rgba(59,130,246,.12),transparent 30%),linear-gradient(180deg,#f9fbff,#f2f5fa);--surface:hsla(0,0%,100%,.9);--surface-strong:hsla(0,0%,100%,.98);--surface-muted:#eef3fb;--surface-soft:#f6f8fc;--surface-elevated:rgba(15,23,42,.86);--text:#0f172a;--text-secondary:#475569;--text-tertiary:#64748b;--text-on-dark:#f8fafc;--primary:#2563eb;--primary-dark:#1d4ed8;--primary-soft:rgba(37,99,235,.1);--income:#059669;--income-soft:rgba(5,150,105,.12);--expense:#dc2626;--expense-soft:rgba(220,38,38,.1);--warning:#d97706;--warning-soft:rgba(217,119,6,.12);--border:rgba(148,163,184,.2);--border-strong:rgba(100,116,139,.28);--shadow-xs:0 1px 3px rgba(15,23,42,.05);--shadow-sm:0 10px 28px rgba(15,23,42,.06);--shadow-md:0 18px 44px rgba(15,23,42,.1);--radius-xs:10px;--radius-sm:14px;--radius:18px;--radius-lg:24px;--space-1:4px;--space-2:8px;--space-3:12px;--space-4:16px;--space-5:20px;--space-6:24px;--space-7:28px;--space-8:32px;--shell-width:760px;--header-height:44px;--bottom-nav-height:84px;--safe-bottom:max(24px,env(safe-area-inset-bottom));--safe-top:max(20px,env(safe-area-inset-top));--transition:180ms ease;--font-sans:"Inter","PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;--font-mono:"SF Mono","JetBrains Mono",Monaco,monospace;color-scheme:light}*{box-sizing:border-box;margin:0;padding:0}#__next,body,html{min-height:100%}body{font-family:var(--font-sans);background:var(--bg-accent);color:var(--text);-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility}a,button,input,select,textarea{font:inherit}button,input,select,textarea{border:0}button{background:none;color:inherit}button:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible{outline:3px solid rgba(37,99,235,.18);outline-offset:2px}.app-shell{min-height:100vh;padding-bottom:calc(var(--bottom-nav-height) + var(--safe-bottom) + var(--space-3))}.app-shell--standalone{padding-bottom:0}.app-shell__container{width:min(100% - 28px,var(--shell-width));margin:0 auto}.app-header{position:-webkit-sticky;position:sticky;top:0;z-index:40;padding-top:var(--safe-top);-webkit-backdrop-filter:blur(20px);backdrop-filter:blur(20px);background:rgba(249,251,255,.9);border-bottom:1px solid rgba(226,232,240,.9)}.app-header:after{content:none}.app-header--compact{background:rgba(249,251,255,.86)}.app-header__inner{position:relative;min-height:44px;display:flex;align-items:center;justify-content:space-between;gap:12px;padding:4px 0}.app-header--compact .app-header__inner{min-height:40px;gap:10px;padding:2px 0}.app-header__main,.app-header__spacer{min-width:0;flex:1 1}.app-header__main{display:flex;align-items:center;flex:1 1}.app-header--actions-only .app-header__main{display:none}.app-header__text{min-width:0;display:grid;grid-gap:2px;gap:2px}.app-header__text h1{font-size:1.02rem;line-height:1.15;letter-spacing:-.03em;font-weight:700;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-header--compact .app-header__text h1{font-size:.98rem}.app-header__text p{color:var(--text-secondary);font-size:.76rem;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.app-header--compact
3dacaebc4c4f645cüÿÿ€üÿÿüÿÿ4dfdffc7a6ebe5a91198883987aea083‚üÿÿƒüÿÿ„üÿÿØüÿÿ
sself.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[{has:s,source:"/api/:path*",destination:s}],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-fd60318ee223c708.js"],"/_error":["static/chunks/pages/_error-7a92967bea80186d.js"],"/categories":["static/chunks/pages/categories-5a5eb0adb3353980.js"],"/login":["static/chunks/pages/login-f50164aa2a6805b4.js"],"/recurring":["static/chunks/pages/recurring-3e6650f88fc7215f.js"],"/search":["static/chunks/pages/search-b3a8ca37c7d3bcb2.js"],"/settings":["static/chunks/pages/settings-bb02e29d5162e9fa.js"],"/stats":["static/chunks/ee8b1517-5dfbbb6838544537.js","static/chunks/925-ca8bea7fb2a62185.js","static/chunks/pages/stats-ce96160268c9c2d4.js"],sortedPages:["/","/_app","/_error","/categories","/login","/recurring","/search","/settings","/stats"]}}(void 0),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();  _üÿÿ
E*(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[161],{5461:function(e,n,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/categories",function(){return t(2874)}])},6248:function(e,n,t){"use strict";t.d(n,{Z:function(){return o}});var a=t(5893),c=t(7294),s=t(1163),r=t(9415);let i=[{href:"/",label:"首页",icon:"home"},{href:"/stats",label:"统计",icon:"chart"},{href:"/recurring",label:"周期",icon:"repeat"},{href:"/categories",label:"分类",icon:"tags"}];function o(e){let{title:n,subtitle:t,actions:o,headerContent:l,children:u,contentClassName:d="",hideNav:h=!1,compactHeader:m=!1}=e,p=(0,s.useRouter)(),f=(0,c.useMemo)(()=>{let e=i.find(e=>e.href===p.pathname);return(null==e?void 0:e.href)||null},[p.pathname]),y=l||(n?(0,a.jsxs)("div",{className:"app-header__text",children:[(0,a.jsx)("h1",{children:n}),t?(0,a.jsx)("p",{children:t}):null]}):null),x=["app-header",m?"app-header--compact":"",y?"app-header--with-content":"app-header--actions-only",o?"app-header--with-actions":""].filter(Boolean).join(" ");return(0,a.jsxs)("div",{className:"app-shell".concat(h?" app-shell--standalone":""),children:[(0,a.jsx)("header",{className:x,children:(0,a.jsxs)("div",{className:"app-shell__container app-header__inner",children:[y?(0,a.jsx)("div",{className:"app-header__main",children:y}):(0,a.jsx)("div",{className:"app-header__spacer","aria-hidden":"true"}),o?(0,a.jsx)("div",{className:"app-header__actions",children:o}):null]})}),(0,a.jsx)("main",{className:"app-content ".concat(d).trim(),children:(0,a.jsx)("div",{className:"app-shell__container app-content__inner",children:u})}),h?null:(0,a.jsx)("nav",{className:"bottom-nav","aria-label":"主导航",children:(0,a.jsx)("div",{className:"app-shell__container bottom-nav__inner",children:i.map(e=>{let n=f===e.href;return(0,a.jsxs)("button",{type:"button",className:"bottom-nav__item".concat(n?" is-active":""),onClick:()=>p.push(e.href),"aria-current":n?"page":void 0,children:[(0,a.jsx)(r.J,{name:e.icon,size:20}),(0,a.jsx)("span",{children:e.label})]},e.href)})})})]})}},610:function(e,n,t){"use strict";async function a(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},t=await fetch("".concat("").concat(e),{...n,headers:{"Content-Type":"application/json",...function(){let e=localStorage.getItem("token");return e?{Authorization:"Bearer ".concat(e)}:{}}(),...n.headers}}),a=await t.json();if(!t.ok)throw Error(a.error||"请求失败");return a}async function c(e,n){let t=await a("/api/auth/login",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}async function s(e,n){let t=await a("/api/auth/register",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}function r(){localStorage.removeItem("token")}function i(){return!!localStorage.getItem("token")}async function o(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/records?month=".concat(e);return"all"!==n&&(t+="&type=".concat(n)),a(t)}async function l(e){return a("/api/stats?month=".concat(e))}async function u(e){return a("/api/records",{method:"POST",body:JSON.stringify(e)})}async function d(e,n){return a("/api/records/".concat(e),{method:"PUT",body:JSON.stringify(n)})}async function h(e){return a("/api/records/".concat(e),{method:"DELETE"})}async function m(e){return a("/api/records/search?q=".concat(encodeURIComponent(e)))}async function p(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"month",n=arguments.length>1?arguments[1]:void 0,t="/api/stats/trend?period=".concat(e);return n&&(t+="&month=".concat(n)),a(t)}async function f(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/stats/category?type=".concat(n);return e&&(t+="&month=".concat(e)),a(t)}async function y(){return a("/api/recurring")}async function x(e){return a("/api/recurring",{method:"POST",body:JSON.stringify(e)})}async function j(e,n){return a("/api/recurring/".concat(e),{method:"PUT",body:JSON.stringify(n)})}async function g(e){return a("/api/recurring/".concat(e),{
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"
}
]
};
` exportsType‰namespace  ÿÿÿOÿÿÿ þ `PÿÿÿQÿÿÿRÿÿÿSÿÿÿTÿÿÿUÿÿÿVÿÿÿÉþÿÿWÿÿÿXÿÿÿYÿÿÿexportsArgumentYÿÿÿZÿÿÿ ˜webpack/lib/util/LazySetÙ/opt/accountbook/frontend/node_modules/next/dist/build/webpack/loaders/next-swc-loader.js YÿÿÿZÿÿÿbf0a79e01a013d70 “__webpack_exports__DEFAULT_CATEGORIES à ¸ÀÍyB Ïã ·webpack/lib/dependencies/HarmonyCompatibilityDependency dÿÿý¶webpack/lib/dependencies/HarmonyExportHeaderDependencyb   aj ¹webpack/lib/dependencies/HarmonyExportSpecifierDependencyö aj —webpack/lib/ModuleGraph“RestoreProvidedData  »üÿÿˆprovidedcanMangleProvideterminalBindingexportsInfoí
XÿÿÿYÿÿÿ 
«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"
}
]
};
\ÿÿÿ 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__;
}
]); Pÿÿÿ `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:
ä÷äåæÀçôõô,
/***/ 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ÞßÞàÎáÎâÐãäÞßÞåÎáÎæÐçèÞßÞéÎáÎÏЪÑÞßÞ,
/***/ 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ÒÓÒÔÂÕÂÖÄרÒÓÒÙÂÕÂÚÄÛÜÒÓÒÝÂÕÂÃÄžÅÒÓÒ,
/***/ 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",
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__.O(0, [888,774,179], function() { return __webpack_exec__(2774); });
var __webpack_exports__ = __webpack_require__.O();
Íåæç §þÿÿ füÿÿ€@hüÿÿ¨/opt/accountbook/frontend/pages/stats.js€€ nþÿÿtÿÿÿuÿÿÿÐýÿÿ wÿÿÿxÿÿÿyÿÿÿzÿÿÿ|ÿÿÿ}ÿÿÿ~ÿÿÿñüÿÿñüÿÿoþÿÿ./pages/stats.jsÿÿ
@‰ `C®ÂÍyBúiþÿÿmþÿÿnþÿÿoþÿÿ/Ÿ/opt/accountbook/frontend/pages^þÿÿ!p§ÂÍyB`§ÂÍyB50c72b83a5e3839e\þÿÿ!@07ØÊyB /7ØÊyBgþÿÿ[þÿÿOüÿÿZþÿÿNüÿÿYþÿÿMüÿÿXþÿÿLüÿÿWþÿÿKüÿÿ
Å/opt/accountbook/frontend/opt/accountbook/frontend/pages/package.jsonÁ/opt/accountbook/frontend/opt/accountbook/frontend/pages/stats.jsÄ/opt/accountbook/frontend/opt/accountbook/frontend/pages/stats.js.jsÅ/opt/accountbook/frontend/opt/accountbook/frontend/pages/stats.js.mjsÅ/opt/accountbook/frontend/opt/accountbook/frontend/pages/stats.js.tsxÄ/opt/accountbook/frontend/opt/accountbook/frontend/pages/stats.js.tsÅ/opt/accountbook/frontend/opt/accountbook/frontend/pages/stats.js.jsxÆ/opt/accountbook/frontend/opt/accountbook/frontend/pages/stats.js.jsonÆ/opt/accountbook/frontend/opt/accountbook/frontend/pages/stats.js.wasmvÿÿÿƒ 
@€¿/opt/accountbook/frontend/opt/accountbook/frontend/package.json¶/opt/accountbook/frontend/opt/accountbook/package.jsonª/opt/accountbook/frontend/opt/package.json Ž8import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect, useRef } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getTrendStats, getCategoryStats } from "../lib/api";
import { Chart, registerables } from "chart.js";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
Chart.register(...registerables);
export default function Stats() {
const router = useRouter();
const { showToast } = useToast();
const [period, setPeriod] = useState("month");
const [currentMonth, setCurrentMonth] = useState(new Date());
const [trendData, setTrendData] = useState([]);
const [categoryData, setCategoryData] = useState([]);
const [categoryType, setCategoryType] = useState("expense");
const [trendLoading, setTrendLoading] = useState(true);
const [categoryLoading, setCategoryLoading] = useState(true);
const trendChartRef = useRef(null);
const categoryChartRef = useRef(null);
const trendChartInstance = useRef(null);
const categoryChartInstance = useRef(null);
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadTrendData();
}, [
router,
period,
currentMonth
]);
useEffect(()=>{
if (!isLoggedIn()) return;
loadCategoryData();
}, [
router,
currentMonth,
categoryType
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const loadTrendData = async ()=>{
setTrendLoading(true);
try {
const trend = await getTrendStats(period, getMonthStr(currentMonth));
setTrendData(trend);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setTrendLoading(false);
}
};
const loadCategoryData = async ()=>{
setCategoryLoading(true);
try {
const category = await getCategoryStats(getMonthStr(currentMonth), categoryType);
setCategoryData(category);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setCategoryLoading(false);
}
};
useEffect(()=>{
if (trendChartInstance.current) {
trendChartInstance.current.destroy();
}
if (trendChartRef.current && trendData.length > 0) {
const ctx = trendChartRef.current.getContext("2d");
trendChartInstance.current = new Chart(ctx, {
type: "line",
data: {
labels: trendData.map((item)=>item.period),
datasets: [
{
label: "æ”¶å…¥",
data: trendData.map((item)=>item.income),
borderColor: "#10b981",
backgroundColor: "rgba(16, 185, 129, 0.1)",
fill: true,
tension: 0.4
},
{
label: "支出",
data: trendData.map((item)=>item.expense),
borderColor: "#ef4444",
backgroundColor: "rgba(239, 68, 68, 0.1)",
fill: true,
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top"
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
}, [
trendData
]);
useEffect(()=>{
if (categoryChartInstance.current) {
categoryChartInstance.current.destroy();
}
if (categoryChartRef.current && categoryData.length > 0) {
const ctx = categoryChartRef.current.getContext("2d");
const colors = [
"#2563eb",
"#10b981",
"#f59e0b",
"#ef4444",
"#8b5cf6",
"#ec4899",
"#06b6d4",
"#84cc16",
"#f97316",
"#6366f1"
];
categoryChartInstance.current = new Chart(ctx, {
type: "doughnut",
data: {
labels: categoryData.map((item)=>item.category),
datasets: [
{
data: categoryData.map((item)=>item.total),
backgroundColor: colors.slice(0, categoryData.length)
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom"
}
}
}
});
}
}, [
categoryData
]);
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 totalCategoryAmount = categoryData.reduce((sum, item)=>sum + item.total, 0);
const headerContent = /*#__PURE__*/ _jsxs("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ _jsx("div", {
className: "header-spacer"
}),
/*#__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
})
})
})
]
});
return /*#__PURE__*/ _jsxs(AppShell, {
headerContent: headerContent,
compactHeader: true,
children: [
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--stats",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "趋势"
}),
/*#__PURE__*/ _jsx("h2", {
children: "收支趋势"
})
]
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "period-tabs",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "week" ? "active" : "",
onClick: ()=>setPeriod("week"),
children: "周"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "month" ? "active" : "",
onClick: ()=>setPeriod("month"),
children: "月"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "year" ? "active" : "",
onClick: ()=>setPeriod("year"),
children: "å¹´"
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "chart-container chart-container--large chart-container--stats",
children: trendLoading ? /*#__PURE__*/ _jsx("div", {
className: "loading-state",
children: "加载中..."
}) : /*#__PURE__*/ _jsx("canvas", {
ref: trendChartRef
})
})
]
}),
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--stats",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsx("div", {
children: /*#__PURE__*/ _jsx("h2", {
children: "分类明细"
})
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "category-type-tabs",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: categoryType === "expense" ? "active" : "",
onClick: ()=>setCategoryType("expense"),
children: "支出"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: categoryType === "income" ? "active" : "",
onClick: ()=>setCategoryType("income"),
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "category-breakdown category-breakdown--stats",
children: categoryData.length > 0 ? categoryData.map((cat, index)=>{
const percent = totalCategoryAmount ? (cat.total / totalCategoryAmount * 100).toFixed(1) : "0.0";
return /*#__PURE__*/ _jsxs("div", {
className: "stats-category-item",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "stats-category-header",
children: [
/*#__PURE__*/ _jsx("span", {
className: "stats-category-name",
children: cat.category
}),
/*#__PURE__*/ _jsx("span", {
className: "stats-category-amount",
children: cat.total.toFixed(2)
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "stats-category-bar-wrap",
children: /*#__PURE__*/ _jsx("div", {
className: "stats-category-bar",
style: {
width: "".concat(percent, "%")
}
})
})
]
}, index);
}) : /*#__PURE__*/ _jsx("div", {
className: "empty-state empty-state--inline",
children: "暂无数æ®"
})
})
]
})
]
});
}
`sÿÿÿtÿÿÿ “þÿÿÄþÿÿ þvÿÿÿ wÿÿÿÑþÿÿÒþÿÿb493a1635fba6b04 xÿÿÿ ‰*default*…Stats
9®ÂÍyBÔÜ
 dÿÿý `? `? a@t `4 `u@ `( ç `I  `0 AG `. AHs `+ At« `7 A8 A8
@\ `Mºwebpack/lib/dependencies/HarmonyImportSideEffectDependencyreact/jsx-runtimeÿ `?§üÿÿ§üÿÿ `4next/routerÿ `(Š../lib/apiÿ `I2ÿÿÿ2ÿÿÿ `0../components/AppShellÿ `.“../components/iconsÿ `+../components/ToastProviderÿ `7¹webpack/lib/dependencies/HarmonyImportSpecifierDependencya…Chartˆregisterýº±
 %ÿÿÿ%ÿÿÿý `registerablesþËt ÿÿÿ ÿÿÿþ b ̇default
@\‰useRouterþA  úââý c  ˆuseToastþA*2 úææþ c  "ˆuseStateþAV^ õ~üÿÿ~üÿÿþ c (ûúA• ñzüÿÿzüÿÿþ c,4÷öÙ ívüÿÿvüÿÿþ c&.óòA  érüÿÿrüÿÿþ c,4ïîAEM ånüÿÿnüÿÿþ c,4ëêA†Ž ájüÿÿjüÿÿþ c,4çæÐ Ýfüÿÿfüÿÿþ c2:†useRefþø Øaüÿÿaüÿÿþ c ûúA# Ô]üÿÿ]üÿÿþ c#÷öAJP ÐYüÿÿYüÿÿþ c%óòAz ÌUüÿÿUüÿÿþ c"(‰useEffectþ ÇPüÿÿPüÿÿþ c ŠisLoggedInþ³ §§þ c öõA]f ¾GüÿÿGüÿÿþ c## ÷öAz ºŸŸþ c$ $getTrendStatsþAIV µššþ c0 0-getCategoryStatsþû °þ c>#>3äãA'
0
 ¬5üÿÿ5üÿÿþ cII ˜AG L 4¨ÁþÿÿÁþÿÿþ cO-O2ÜÛø ¤-üÿÿ-üÿÿþ cyy øAZ_4¡ºþÿÿºþÿÿþ @‹`0@‹`5„jsxs…_jsxsA-2 {ÿÿÿ{ÿÿÿþ `(@¯`-ƒjsx„_jsxA˜œ uÿÿÿuÿÿÿþ ``ôõ qÿÿÿqÿÿÿþ ``ö÷Aƒ mÿÿÿmÿÿÿþ @¸`"@¸`&òóAsw iÿÿÿiÿÿÿþ `0@½`4„IconþAx|4nÿÿÿnÿÿÿþ `5@½`9éêA+/ `ÿÿÿ`ÿÿÿþ `"@Â`&åæð |ÿÿÿ\ÿÿÿ\ÿÿÿþ `"@Æ`&áâä xÿÿÿXÿÿÿXÿÿÿþ `0@Ë`4ðïé4uÿÿÿ_ÿÿÿ_ÿÿÿþ `5@Ë`9ÚÛ qÿÿÿQÿÿÿQÿÿÿþ ``Ö×A mÿÿÿMÿÿÿMÿÿÿþ `(@Ô`,ÒÓA  iÿÿÿIÿÿÿIÿÿÿþ `,@Ù`0áàA4fÿÿÿPÿÿÿPÿÿÿþ `1@Ù`5ÅÆÎ bÿÿÿBÿÿÿBÿÿÿþ ``[ÿÿÿˆAppShell×4]ÿÿÿEÿÿÿEÿÿÿþ ``'¼½AL Q  Yÿÿÿ9ÿÿÿ9ÿÿÿþ ``¾¿å  Uÿÿÿ5ÿÿÿ5ÿÿÿþ `"@è`&´µAn!s! Qÿÿÿ1ÿÿÿ1ÿÿÿþ `0@ê`5·AÓ!×! Mÿÿÿ-ÿÿÿ-ÿÿÿþ `.@ì`2²³Až"¢" Iÿÿÿ)ÿÿÿ)ÿÿÿþ `.@ð`2¨©Av#{# Eÿÿÿ%ÿÿÿ%ÿÿÿþ `"@ö`'ª«A$ $ Aÿÿÿ!ÿÿÿ!ÿÿÿþ `*@ù`.¦§AL%P% =ÿÿÿÿÿÿÿÿÿþ @ÿ`*@ÿ`.¢£A•&™& 9ÿÿÿÿÿÿÿÿÿþ @`*@`.žŸA( ( 5ÿÿÿÿÿÿÿÿÿþ @ `"@ `&šA¶(º( 1ÿÿÿÿÿÿÿÿÿþ @`?@`CAV)Z) -ÿÿÿ ÿÿÿ ÿÿÿþ @`+@`/ŒA* * )ÿÿÿ ÿÿÿ ÿÿÿþ @`@`ŽA™** %ÿÿÿÿÿÿÿÿÿþ @`"@`&ŠA&+*+ !ÿÿÿÿÿÿÿÿÿþ @`0@`4Ah+l+ ÿÿÿýþÿÿýþÿÿþ @`4@`8|ÿÿÿ}ÿÿÿA,, ÿÿÿùþÿÿùþÿÿþ @#`"@#`'~ÿÿÿÿÿÿA°,´, ÿÿÿõþÿÿõþÿÿþ @&`*@&`.zÿÿÿ{ÿÿÿA
.. ÿÿÿñþÿÿñþÿÿþ @,`*@,`.vÿÿÿwÿÿÿA‹//  ÿÿÿíþÿÿíþÿÿþ @4`"@4`&lÿÿÿmÿÿÿAø0ý0  ÿÿÿéþÿÿéþÿÿþ @8`1@8`6hÿÿÿiÿÿÿA§1¬1 ÿÿÿåþÿÿåþÿÿþ @;`2@;`7jÿÿÿkÿÿÿAp2t2 ÿÿÿáþÿÿáþÿÿþ @>`:@>`>fÿÿÿgÿÿÿA‚3†3 ýþÿÿÝþÿÿÝþÿÿþ @B`:@B`>bÿÿÿcÿÿÿAç4ë4 ùþÿÿÙþÿÿÙþÿÿþ @H`2@H`6^ÿÿÿ_ÿÿÿAƒ5‡5 õþÿÿÕþÿÿÕþÿÿþ @J`@@J`DZÿÿÿ[ÿÿÿAV7Z7 ñþÿÿÑþÿÿÑþÿÿþ @S`+@S`/=þÿÿçþÿÿ
–ýÿÿ—ýÿÿ `D«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 */ Stats; }
/* 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 chart_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3148);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5546);
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceŽ8import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect, useRef } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getTrendStats, getCategoryStats } from "../lib/api";
import { Chart, registerables } from "chart.js";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
Chart.register(...registerables);
export default function Stats() {
const router = useRouter();
const { showToast } = useToast();
const [period, setPeriod] = useState("month");
const [currentMonth, setCurrentMonth] = useState(new Date());
const [trendData, setTrendData] = useState([]);
const [categoryData, setCategoryData] = useState([]);
const [categoryType, setCategoryType] = useState("expense");
const [trendLoading, setTrendLoading] = useState(true);
const [categoryLoading, setCategoryLoading] = useState(true);
const trendChartRef = useRef(null);
const categoryChartRef = useRef(null);
const trendChartInstance = useRef(null);
const categoryChartInstance = useRef(null);
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadTrendData();
}, [
router,
period,
currentMonth
]);
useEffect(()=>{
if (!isLoggedIn()) return;
loadCategoryData();
}, [
router,
currentMonth,
categoryType
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const loadTrendData = async ()=>{
setTrendLoading(true);
try {
const trend = await getTrendStats(period, getMonthStr(currentMonth));
setTrendData(trend);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setTrendLoading(false);
}
};
const loadCategoryData = async ()=>{
setCategoryLoading(true);
try {
const category = await getCategoryStats(getMonthStr(currentMonth), categoryType);
setCategoryData(category);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setCategoryLoading(false);
}
};
useEffect(()=>{
if (trendChartInstance.current) {
trendChartInstance.current.destroy();
}
if (trendChartRef.current && trendData.length > 0) {
const ctx = trendChartRef.current.getContext("2d");
trendChartInstance.current = new Chart(ctx, {
type: "line",
data: {
labels: trendData.map((item)=>item.period),
datasets: [
{
label: "æ”¶å…¥",
data: trendData.map((item)=>item.income),
borderColor: "#10b981",
backgroundColor: "rgba(16, 185, 129, 0.1)",
fill: true,
tension: 0.4
},
{
label: "支出",
data: trendData.map((item)=>item.expense),
borderColor: "#ef4444",
backgroundColor: "rgba(239, 68, 68, 0.1)",
fill: true,
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top"
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
}, [
trendData
]);
useEffect(()=>{
if (categoryChartInstance.current) {
categoryChartInstance.current.destroy();
}
if (categoryChartRef.current && categoryData.length > 0) {
const ctx = categoryChartRef.current.getContext("2d");
const colors = [
"#2563eb",
"#10b981",
"#f59e0b",
"#ef4444",
"#8b5cf6",
"#ec4899",
"#06b6d4",
"#84cc16",
"#f97316",
"#6366f1"
];
categoryChartInstance.current = new Chart(ctx, {
type: "doughnut",
data: {
labels: categoryData.map((item)=>item.category),
datasets: [
{
data: categoryData.map((item)=>item.total),
backgroundColor: colors.slice(0, categoryData.length)
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom"
}
}
}
});
}
}, [
categoryData
]);
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 totalCategoryAmount = categoryData.reduce((sum, item)=>sum + item.total, 0);
const headerContent = /*#__PURE__*/ _jsxs("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ _jsx("div", {
className: "header-spacer"
}),
/*#__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
})
})
})
]
});
return /*#__PURE__*/ _jsxs(AppShell, {
headerContent: headerContent,
compactHeader: true,
children: [
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--stats",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "趋势"
}),
/*#__PURE__*/ _jsx("h2", {
children: "收支趋势"
})
]
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "period-tabs",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "week" ? "active" : "",
onClick: ()=>setPeriod("week"),
children: "周"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "month" ? "active" : "",
onClick: ()=>setPeriod("month"),
children: "月"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "year" ? "active" : "",
onClick: ()=>setPeriod("year"),
children: "å¹´"
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "chart-container chart-container--large chart-container--stats",
children: trendLoading ? /*#__PURE__*/ _jsx("div", {
className: "loading-state",
children: "加载中..."
}) : /*#__PURE__*/ _jsx("canvas", {
ref: trendChartRef
})
})
]
}),
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--stats",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsx("div", {
children: /*#__PURE__*/ _jsx("h2", {
children: "分类明细"
})
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "category-type-tabs",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: categoryType === "expense" ? "active" : "",
onClick: ()=>setCategoryType("expense"),
children: "支出"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: categoryType === "income" ? "active" : "",
onClick: ()=>setCategoryType("income"),
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "category-breakdown category-breakdown--stats",
children: categoryData.length > 0 ? categoryData.map((cat, index)=>{
const percent = totalCategoryAmount ? (cat.total / totalCategoryAmount * 100).toFixed(1) : "0.0";
return /*#__PURE__*/ _jsxs("div", {
className: "stats-category-item",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "stats-category-header",
children: [
/*#__PURE__*/ _jsx("span", {
className: "stats-category-name",
children: cat.category
}),
/*#__PURE__*/ _jsx("span", {
className: "stats-category-amount",
children: cat.total.toFixed(2)
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "stats-category-bar-wrap",
children: /*#__PURE__*/ _jsx("div", {
className: "stats-category-bar",
style: {
width: "".concat(percent, "%")
}
})
})
]
}, index);
}) : /*#__PURE__*/ _jsx("div", {
className: "empty-state empty-state--inline",
children: "暂无数æ®"
})
})
]
})
]
});
}
eM>@su_œžæèFHrtª¬°¾ÊÎÜ *1V]œÑØ EL_ÈÏò÷"JOzŒ©²]ezƒIUëú'
/
G K ï÷Z^-_1˜ûÿsvx{+.ìïàãåè²µ ÉÍÏÖL _P á ä n!r!Ó!Ö!ž"¡"v#z#$$L%O%•&˜&((¶(¹(V)Y)**™*œ*&+)+h+k+,T,°,³,
. ./Ž/ø0ü0§1«1p2s23…3ç4ê4ƒ5†5V7Y7µchart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL½chart_js__WEBPACK_IMPORTED_MODULE_6__/* .registerables */ .zX¶(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)Ì(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__/* .useToast */ .p)¯(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)ÿÿÿÿÿÿ­(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)ÿÿÿ°(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)¾(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)þÿÁ(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getTrendStats */ .gR)Ä(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getCategoryStats */ .Ap)üöüö·(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)¶(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)þÿÿ¼_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .JþþþÿþþþÿýÅ_components_AppShell__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .Züýüýýüýýýýýýüýýýüýýýüüýýýýýšýÿÿ$H__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Stats; }
/* 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 chart_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3148);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5546);
chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL.register(...chart_js__WEBPACK_IMPORTED_MODULE_6__/* .registerables */ .zX);
function Stats() {
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const { showToast } = (0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__/* .useToast */ .p)();
const [period, setPeriod] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("month");
const [currentMonth, setCurrentMonth] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(new Date());
const [trendData, setTrendData] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [categoryData, setCategoryData] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [categoryType, setCategoryType] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("expense");
const [trendLoading, setTrendLoading] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(true);
const [categoryLoading, setCategoryLoading] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(true);
const trendChartRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const categoryChartRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const trendChartInstance = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const categoryChartInstance = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)()) {
router.push("/login");
return;
}
loadTrendData();
}, [
router,
period,
currentMonth
]);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)()) return;
loadCategoryData();
}, [
router,
currentMonth,
categoryType
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const loadTrendData = async ()=>{
setTrendLoading(true);
try {
const trend = await (0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getTrendStats */ .gR)(period, getMonthStr(currentMonth));
setTrendData(trend);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setTrendLoading(false);
}
};
const loadCategoryData = async ()=>{
setCategoryLoading(true);
try {
const category = await (0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getCategoryStats */ .Ap)(getMonthStr(currentMonth), categoryType);
setCategoryData(category);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setCategoryLoading(false);
}
};
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (trendChartInstance.current) {
trendChartInstance.current.destroy();
}
if (trendChartRef.current && trendData.length > 0) {
const ctx = trendChartRef.current.getContext("2d");
trendChartInstance.current = new chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL(ctx, {
type: "line",
data: {
labels: trendData.map((item)=>item.period),
datasets: [
{
label: "æ”¶å…¥",
data: trendData.map((item)=>item.income),
borderColor: "#10b981",
backgroundColor: "rgba(16, 185, 129, 0.1)",
fill: true,
tension: 0.4
},
{
label: "支出",
data: trendData.map((item)=>item.expense),
borderColor: "#ef4444",
backgroundColor: "rgba(239, 68, 68, 0.1)",
fill: true,
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top"
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
}, [
trendData
]);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (categoryChartInstance.current) {
categoryChartInstance.current.destroy();
}
if (categoryChartRef.current && categoryData.length > 0) {
const ctx = categoryChartRef.current.getContext("2d");
const colors = [
"#2563eb",
"#10b981",
"#f59e0b",
"#ef4444",
"#8b5cf6",
"#ec4899",
"#06b6d4",
"#84cc16",
"#f97316",
"#6366f1"
];
categoryChartInstance.current = new chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL(ctx, {
type: "doughnut",
data: {
labels: categoryData.map((item)=>item.category),
datasets: [
{
data: categoryData.map((item)=>item.total),
backgroundColor: colors.slice(0, categoryData.length)
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom"
}
}
}
});
}
}, [
categoryData
]);
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 totalCategoryAmount = categoryData.reduce((sum, item)=>sum + item.total, 0);
const headerContent = /*#__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-spacer"
}),
/*#__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_4__/* .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_4__/* .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_4__/* .Icon */ .J, {
name: "settings",
size: 18
})
})
})
]
});
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components_AppShell__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .Z, {
headerContent: headerContent,
compactHeader: true,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("section", {
className: "card section-card section-card--stats",
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__.jsx)("h2", {
children: "收支趋势"
})
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "period-tabs",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "week" ? "active" : "",
onClick: ()=>setPeriod("week"),
children: "周"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "month" ? "active" : "",
onClick: ()=>setPeriod("month"),
children: "月"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "year" ? "active" : "",
onClick: ()=>setPeriod("year"),
children: "å¹´"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "chart-container chart-container--large chart-container--stats",
children: trendLoading ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "loading-state",
children: "加载中..."
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("canvas", {
ref: trendChartRef
})
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("section", {
className: "card section-card section-card--stats",
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__.jsx)("div", {
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h2", {
children: "分类明细"
})
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "category-type-tabs",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: categoryType === "expense" ? "active" : "",
onClick: ()=>setCategoryType("expense"),
children: "支出"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: categoryType === "income" ? "active" : "",
onClick: ()=>setCategoryType("income"),
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "category-breakdown category-breakdown--stats",
children: categoryData.length > 0 ? categoryData.map((cat, index)=>{
const percent = totalCategoryAmount ? (cat.total / totalCategoryAmount * 100).toFixed(1) : "0.0";
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "stats-category-item",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "stats-category-header",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "stats-category-name",
children: cat.category
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "stats-category-amount",
children: cat.total.toFixed(2)
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "stats-category-bar-wrap",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "stats-category-bar",
style: {
width: "".concat(percent, "%")
}
})
})
]
}, index);
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "empty-state empty-state--inline",
children: "暂无数æ®"
})
})
]
})
]
});
}
”{"finalSource":true}ƒmapbufferedMap 8þÿÿ|__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Stats; }
/* 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 chart_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3148);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5546);
9þÿÿ þÿÿn062undefined64115undefined117156undefined158230undefined232279undefined281326undefined328370undefined372426undefined428432chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kLundefined446458chart_js__WEBPACK_IMPORTED_MODULE_6__/* .registerables */ .zXundefined462476undefined515523(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined554561(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__/* .useToast */ .p)undefined598605(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined661668(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined721728(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined779786(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined837844(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined902909(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined968975(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10101015(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined10531058(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined10981103(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined11461151(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined11641172(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined11931202(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)undefined13731381(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined14021411(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)undefined18651877(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getTrendStats */ .gR)undefined22832298(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getCategoryStats */ .Ap)undefined25992607(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined28872891chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kLundefined43354343(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined49544958chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kLundefined61896193(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined62966299(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined63956399(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined65276530(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined67716774(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined67766779_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .Jundefined69556958(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined71487151(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined73927395(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined73977400_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .Jundefined76027605(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined77077710(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined79497952(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined79547957_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .Jundefined81378141(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined81438150_components_AppShell__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .Zundefined82688272(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined84178420(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined85588562(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined86598662(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined88628865(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined90789082(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined92219224(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined95489551(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined98779880(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1024510248(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1042210425(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1058210585(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1075610760(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1090510908(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1104611049(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0_
/***/ 4841:
«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([
"/stats",
function () {
return require("private-next-pages/stats.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/stats"])
});
}
bioqD¨±´“__webpack_require__„8011…false{}†buffer†source„size„maps„hash§
(window.__NEXT_P = window.__NEXT_P || []).push([
"/stats",
function () {
return __webpack_require__(8011);
}
]);
if(false) {}
”{"finalSource":true}ƒmapbufferedMap ReplaceSourceRawSource
(window.__NEXT_P = window.__NEXT_P || []).push([
"/stats",
function () {
return require("private-next-pages/stats.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/stats"])
});
}
Z105111__webpack_require__undefined1131418011undefined168177falseundefined180276{}undefined
/***/ }) †buffer†source„size„maps„hash /***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
(window.__NEXT_P = window.__NEXT_P || []).push([
"/stats",
function () {
return __webpack_require__(8011);
}
]);
if(false) {}
/***/ }) ”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSource[/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
ReplaceSourceRawSource
(window.__NEXT_P = window.__NEXT_P || []).push([
"/stats",
function () {
return require("private-next-pages/stats.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/stats"])
});
}
Z105111__webpack_require__undefined1131418011undefined168177falseundefined180276{}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
]
});
}
/***/ }) íð  ñ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çè,
/***/ 8011:
  ß«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 `D«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 */ Stats; }
/* 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 chart_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3148);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5546);
«webpack/lib/util/registerExternalSerializerwebpack-sources/ReplaceSourceŽ8import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect, useRef } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getTrendStats, getCategoryStats } from "../lib/api";
import { Chart, registerables } from "chart.js";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
Chart.register(...registerables);
export default function Stats() {
const router = useRouter();
const { showToast } = useToast();
const [period, setPeriod] = useState("month");
const [currentMonth, setCurrentMonth] = useState(new Date());
const [trendData, setTrendData] = useState([]);
const [categoryData, setCategoryData] = useState([]);
const [categoryType, setCategoryType] = useState("expense");
const [trendLoading, setTrendLoading] = useState(true);
const [categoryLoading, setCategoryLoading] = useState(true);
const trendChartRef = useRef(null);
const categoryChartRef = useRef(null);
const trendChartInstance = useRef(null);
const categoryChartInstance = useRef(null);
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadTrendData();
}, [
router,
period,
currentMonth
]);
useEffect(()=>{
if (!isLoggedIn()) return;
loadCategoryData();
}, [
router,
currentMonth,
categoryType
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const loadTrendData = async ()=>{
setTrendLoading(true);
try {
const trend = await getTrendStats(period, getMonthStr(currentMonth));
setTrendData(trend);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setTrendLoading(false);
}
};
const loadCategoryData = async ()=>{
setCategoryLoading(true);
try {
const category = await getCategoryStats(getMonthStr(currentMonth), categoryType);
setCategoryData(category);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setCategoryLoading(false);
}
};
useEffect(()=>{
if (trendChartInstance.current) {
trendChartInstance.current.destroy();
}
if (trendChartRef.current && trendData.length > 0) {
const ctx = trendChartRef.current.getContext("2d");
trendChartInstance.current = new Chart(ctx, {
type: "line",
data: {
labels: trendData.map((item)=>item.period),
datasets: [
{
label: "æ”¶å…¥",
data: trendData.map((item)=>item.income),
borderColor: "#10b981",
backgroundColor: "rgba(16, 185, 129, 0.1)",
fill: true,
tension: 0.4
},
{
label: "支出",
data: trendData.map((item)=>item.expense),
borderColor: "#ef4444",
backgroundColor: "rgba(239, 68, 68, 0.1)",
fill: true,
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top"
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
}, [
trendData
]);
useEffect(()=>{
if (categoryChartInstance.current) {
categoryChartInstance.current.destroy();
}
if (categoryChartRef.current && categoryData.length > 0) {
const ctx = categoryChartRef.current.getContext("2d");
const colors = [
"#2563eb",
"#10b981",
"#f59e0b",
"#ef4444",
"#8b5cf6",
"#ec4899",
"#06b6d4",
"#84cc16",
"#f97316",
"#6366f1"
];
categoryChartInstance.current = new Chart(ctx, {
type: "doughnut",
data: {
labels: categoryData.map((item)=>item.category),
datasets: [
{
data: categoryData.map((item)=>item.total),
backgroundColor: colors.slice(0, categoryData.length)
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom"
}
}
}
});
}
}, [
categoryData
]);
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 totalCategoryAmount = categoryData.reduce((sum, item)=>sum + item.total, 0);
const headerContent = /*#__PURE__*/ _jsxs("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ _jsx("div", {
className: "header-spacer"
}),
/*#__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
})
})
})
]
});
return /*#__PURE__*/ _jsxs(AppShell, {
headerContent: headerContent,
compactHeader: true,
children: [
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--stats",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "趋势"
}),
/*#__PURE__*/ _jsx("h2", {
children: "收支趋势"
})
]
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "period-tabs",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "week" ? "active" : "",
onClick: ()=>setPeriod("week"),
children: "周"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "month" ? "active" : "",
onClick: ()=>setPeriod("month"),
children: "月"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "year" ? "active" : "",
onClick: ()=>setPeriod("year"),
children: "å¹´"
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "chart-container chart-container--large chart-container--stats",
children: trendLoading ? /*#__PURE__*/ _jsx("div", {
className: "loading-state",
children: "加载中..."
}) : /*#__PURE__*/ _jsx("canvas", {
ref: trendChartRef
})
})
]
}),
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--stats",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsx("div", {
children: /*#__PURE__*/ _jsx("h2", {
children: "分类明细"
})
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "category-type-tabs",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: categoryType === "expense" ? "active" : "",
onClick: ()=>setCategoryType("expense"),
children: "支出"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: categoryType === "income" ? "active" : "",
onClick: ()=>setCategoryType("income"),
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "category-breakdown category-breakdown--stats",
children: categoryData.length > 0 ? categoryData.map((cat, index)=>{
const percent = totalCategoryAmount ? (cat.total / totalCategoryAmount * 100).toFixed(1) : "0.0";
return /*#__PURE__*/ _jsxs("div", {
className: "stats-category-item",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "stats-category-header",
children: [
/*#__PURE__*/ _jsx("span", {
className: "stats-category-name",
children: cat.category
}),
/*#__PURE__*/ _jsx("span", {
className: "stats-category-amount",
children: cat.total.toFixed(2)
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "stats-category-bar-wrap",
children: /*#__PURE__*/ _jsx("div", {
className: "stats-category-bar",
style: {
width: "".concat(percent, "%")
}
})
})
]
}, index);
}) : /*#__PURE__*/ _jsx("div", {
className: "empty-state empty-state--inline",
children: "暂无数æ®"
})
})
]
})
]
});
}
eM>@su_œžæèFHrtª¬°¾ÊÎÜ *1V]œÑØ EL_ÈÏò÷"JOzŒ©²]ezƒIUëú'
/
G K ï÷Z^-_1˜ûÿsvx{+.ìïàãåè²µ ÉÍÏÖL _P á ä n!r!Ó!Ö!ž"¡"v#z#$$L%O%•&˜&((¶(¹(V)Y)**™*œ*&+)+h+k+,T,°,³,
. ./Ž/ø0ü0§1«1p2s23…3ç4ê4ƒ5†5V7Y7µchart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL½chart_js__WEBPACK_IMPORTED_MODULE_6__/* .registerables */ .zX¶(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)Ì(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__/* .useToast */ .p)¯(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)ÿÿÿÿÿÿ­(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)ÿÿÿ°(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)¾(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)þÿÁ(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getTrendStats */ .gR)Ä(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getCategoryStats */ .Ap)üöüö·(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)¶(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)þÿÿ¼_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .JþþþÿþþþÿýÅ_components_AppShell__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .Züýüýýüýýýýýýüýýýüýýýüüýýýýý†buffer†source„size„maps„hash$H__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Stats; }
/* 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 chart_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3148);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5546);
chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL.register(...chart_js__WEBPACK_IMPORTED_MODULE_6__/* .registerables */ .zX);
function Stats() {
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const { showToast } = (0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__/* .useToast */ .p)();
const [period, setPeriod] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("month");
const [currentMonth, setCurrentMonth] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(new Date());
const [trendData, setTrendData] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [categoryData, setCategoryData] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [categoryType, setCategoryType] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("expense");
const [trendLoading, setTrendLoading] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(true);
const [categoryLoading, setCategoryLoading] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(true);
const trendChartRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const categoryChartRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const trendChartInstance = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const categoryChartInstance = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)()) {
router.push("/login");
return;
}
loadTrendData();
}, [
router,
period,
currentMonth
]);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)()) return;
loadCategoryData();
}, [
router,
currentMonth,
categoryType
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const loadTrendData = async ()=>{
setTrendLoading(true);
try {
const trend = await (0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getTrendStats */ .gR)(period, getMonthStr(currentMonth));
setTrendData(trend);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setTrendLoading(false);
}
};
const loadCategoryData = async ()=>{
setCategoryLoading(true);
try {
const category = await (0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getCategoryStats */ .Ap)(getMonthStr(currentMonth), categoryType);
setCategoryData(category);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setCategoryLoading(false);
}
};
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (trendChartInstance.current) {
trendChartInstance.current.destroy();
}
if (trendChartRef.current && trendData.length > 0) {
const ctx = trendChartRef.current.getContext("2d");
trendChartInstance.current = new chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL(ctx, {
type: "line",
data: {
labels: trendData.map((item)=>item.period),
datasets: [
{
label: "æ”¶å…¥",
data: trendData.map((item)=>item.income),
borderColor: "#10b981",
backgroundColor: "rgba(16, 185, 129, 0.1)",
fill: true,
tension: 0.4
},
{
label: "支出",
data: trendData.map((item)=>item.expense),
borderColor: "#ef4444",
backgroundColor: "rgba(239, 68, 68, 0.1)",
fill: true,
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top"
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
}, [
trendData
]);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (categoryChartInstance.current) {
categoryChartInstance.current.destroy();
}
if (categoryChartRef.current && categoryData.length > 0) {
const ctx = categoryChartRef.current.getContext("2d");
const colors = [
"#2563eb",
"#10b981",
"#f59e0b",
"#ef4444",
"#8b5cf6",
"#ec4899",
"#06b6d4",
"#84cc16",
"#f97316",
"#6366f1"
];
categoryChartInstance.current = new chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL(ctx, {
type: "doughnut",
data: {
labels: categoryData.map((item)=>item.category),
datasets: [
{
data: categoryData.map((item)=>item.total),
backgroundColor: colors.slice(0, categoryData.length)
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom"
}
}
}
});
}
}, [
categoryData
]);
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 totalCategoryAmount = categoryData.reduce((sum, item)=>sum + item.total, 0);
const headerContent = /*#__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-spacer"
}),
/*#__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_4__/* .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_4__/* .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_4__/* .Icon */ .J, {
name: "settings",
size: 18
})
})
})
]
});
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components_AppShell__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .Z, {
headerContent: headerContent,
compactHeader: true,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("section", {
className: "card section-card section-card--stats",
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__.jsx)("h2", {
children: "收支趋势"
})
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "period-tabs",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "week" ? "active" : "",
onClick: ()=>setPeriod("week"),
children: "周"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "month" ? "active" : "",
onClick: ()=>setPeriod("month"),
children: "月"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "year" ? "active" : "",
onClick: ()=>setPeriod("year"),
children: "å¹´"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "chart-container chart-container--large chart-container--stats",
children: trendLoading ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "loading-state",
children: "加载中..."
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("canvas", {
ref: trendChartRef
})
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("section", {
className: "card section-card section-card--stats",
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__.jsx)("div", {
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h2", {
children: "分类明细"
})
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "category-type-tabs",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: categoryType === "expense" ? "active" : "",
onClick: ()=>setCategoryType("expense"),
children: "支出"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: categoryType === "income" ? "active" : "",
onClick: ()=>setCategoryType("income"),
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "category-breakdown category-breakdown--stats",
children: categoryData.length > 0 ? categoryData.map((cat, index)=>{
const percent = totalCategoryAmount ? (cat.total / totalCategoryAmount * 100).toFixed(1) : "0.0";
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "stats-category-item",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "stats-category-header",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "stats-category-name",
children: cat.category
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "stats-category-amount",
children: cat.total.toFixed(2)
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "stats-category-bar-wrap",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "stats-category-bar",
style: {
width: "".concat(percent, "%")
}
})
})
]
}, index);
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "empty-state empty-state--inline",
children: "暂无数æ®"
})
})
]
})
]
});
}
”{"finalSource":true}ƒmapbufferedMap ConcatSourceRawSource|__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Stats; }
/* 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 chart_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3148);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5546);
ReplaceSourceRawSourceŽ8import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect, useRef } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getTrendStats, getCategoryStats } from "../lib/api";
import { Chart, registerables } from "chart.js";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
Chart.register(...registerables);
export default function Stats() {
const router = useRouter();
const { showToast } = useToast();
const [period, setPeriod] = useState("month");
const [currentMonth, setCurrentMonth] = useState(new Date());
const [trendData, setTrendData] = useState([]);
const [categoryData, setCategoryData] = useState([]);
const [categoryType, setCategoryType] = useState("expense");
const [trendLoading, setTrendLoading] = useState(true);
const [categoryLoading, setCategoryLoading] = useState(true);
const trendChartRef = useRef(null);
const categoryChartRef = useRef(null);
const trendChartInstance = useRef(null);
const categoryChartInstance = useRef(null);
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadTrendData();
}, [
router,
period,
currentMonth
]);
useEffect(()=>{
if (!isLoggedIn()) return;
loadCategoryData();
}, [
router,
currentMonth,
categoryType
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const loadTrendData = async ()=>{
setTrendLoading(true);
try {
const trend = await getTrendStats(period, getMonthStr(currentMonth));
setTrendData(trend);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setTrendLoading(false);
}
};
const loadCategoryData = async ()=>{
setCategoryLoading(true);
try {
const category = await getCategoryStats(getMonthStr(currentMonth), categoryType);
setCategoryData(category);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setCategoryLoading(false);
}
};
useEffect(()=>{
if (trendChartInstance.current) {
trendChartInstance.current.destroy();
}
if (trendChartRef.current && trendData.length > 0) {
const ctx = trendChartRef.current.getContext("2d");
trendChartInstance.current = new Chart(ctx, {
type: "line",
data: {
labels: trendData.map((item)=>item.period),
datasets: [
{
label: "æ”¶å…¥",
data: trendData.map((item)=>item.income),
borderColor: "#10b981",
backgroundColor: "rgba(16, 185, 129, 0.1)",
fill: true,
tension: 0.4
},
{
label: "支出",
data: trendData.map((item)=>item.expense),
borderColor: "#ef4444",
backgroundColor: "rgba(239, 68, 68, 0.1)",
fill: true,
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top"
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
}, [
trendData
]);
useEffect(()=>{
if (categoryChartInstance.current) {
categoryChartInstance.current.destroy();
}
if (categoryChartRef.current && categoryData.length > 0) {
const ctx = categoryChartRef.current.getContext("2d");
const colors = [
"#2563eb",
"#10b981",
"#f59e0b",
"#ef4444",
"#8b5cf6",
"#ec4899",
"#06b6d4",
"#84cc16",
"#f97316",
"#6366f1"
];
categoryChartInstance.current = new Chart(ctx, {
type: "doughnut",
data: {
labels: categoryData.map((item)=>item.category),
datasets: [
{
data: categoryData.map((item)=>item.total),
backgroundColor: colors.slice(0, categoryData.length)
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom"
}
}
}
});
}
}, [
categoryData
]);
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 totalCategoryAmount = categoryData.reduce((sum, item)=>sum + item.total, 0);
const headerContent = /*#__PURE__*/ _jsxs("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ _jsx("div", {
className: "header-spacer"
}),
/*#__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
})
})
})
]
});
return /*#__PURE__*/ _jsxs(AppShell, {
headerContent: headerContent,
compactHeader: true,
children: [
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--stats",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "趋势"
}),
/*#__PURE__*/ _jsx("h2", {
children: "收支趋势"
})
]
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "period-tabs",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "week" ? "active" : "",
onClick: ()=>setPeriod("week"),
children: "周"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "month" ? "active" : "",
onClick: ()=>setPeriod("month"),
children: "月"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "year" ? "active" : "",
onClick: ()=>setPeriod("year"),
children: "å¹´"
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "chart-container chart-container--large chart-container--stats",
children: trendLoading ? /*#__PURE__*/ _jsx("div", {
className: "loading-state",
children: "加载中..."
}) : /*#__PURE__*/ _jsx("canvas", {
ref: trendChartRef
})
})
]
}),
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--stats",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsx("div", {
children: /*#__PURE__*/ _jsx("h2", {
children: "分类明细"
})
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "category-type-tabs",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: categoryType === "expense" ? "active" : "",
onClick: ()=>setCategoryType("expense"),
children: "支出"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: categoryType === "income" ? "active" : "",
onClick: ()=>setCategoryType("income"),
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "category-breakdown category-breakdown--stats",
children: categoryData.length > 0 ? categoryData.map((cat, index)=>{
const percent = totalCategoryAmount ? (cat.total / totalCategoryAmount * 100).toFixed(1) : "0.0";
return /*#__PURE__*/ _jsxs("div", {
className: "stats-category-item",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "stats-category-header",
children: [
/*#__PURE__*/ _jsx("span", {
className: "stats-category-name",
children: cat.category
}),
/*#__PURE__*/ _jsx("span", {
className: "stats-category-amount",
children: cat.total.toFixed(2)
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "stats-category-bar-wrap",
children: /*#__PURE__*/ _jsx("div", {
className: "stats-category-bar",
style: {
width: "".concat(percent, "%")
}
})
})
]
}, index);
}) : /*#__PURE__*/ _jsx("div", {
className: "empty-state empty-state--inline",
children: "暂无数æ®"
})
})
]
})
]
});
}
n062undefined64115undefined117156undefined158230undefined232279undefined281326undefined328370undefined372426undefined428432chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kLundefined446458chart_js__WEBPACK_IMPORTED_MODULE_6__/* .registerables */ .zXundefined462476undefined515523(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined554561(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__/* .useToast */ .p)undefined598605(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined661668(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined721728(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined779786(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined837844(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined902909(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined968975(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10101015(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined10531058(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined10981103(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined11461151(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined11641172(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined11931202(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)undefined13731381(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined14021411(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)undefined18651877(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getTrendStats */ .gR)undefined22832298(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getCategoryStats */ .Ap)undefined25992607(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined28872891chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kLundefined43354343(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined49544958chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kLundefined61896193(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined62966299(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined63956399(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined65276530(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined67716774(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined67766779_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .Jundefined69556958(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined71487151(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined73927395(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined73977400_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .Jundefined76027605(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined77077710(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined79497952(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined79547957_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .Jundefined81378141(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined81438150_components_AppShell__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .Zundefined82688272(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined84178420(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined85588562(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined86598662(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined88628865(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined90789082(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined92219224(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined95489551(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined98779880(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1024510248(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1042210425(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1058210585(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1075610760(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1090510908(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1104611049(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)unde
/***/ }) ÕH/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Stats; }
/* 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 chart_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3148);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5546);
chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL.register(...chart_js__WEBPACK_IMPORTED_MODULE_6__/* .registerables */ .zX);
function Stats() {
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const { showToast } = (0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__/* .useToast */ .p)();
const [period, setPeriod] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("month");
const [currentMonth, setCurrentMonth] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(new Date());
const [trendData, setTrendData] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [categoryData, setCategoryData] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [categoryType, setCategoryType] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("expense");
const [trendLoading, setTrendLoading] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(true);
const [categoryLoading, setCategoryLoading] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(true);
const trendChartRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const categoryChartRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const trendChartInstance = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const categoryChartInstance = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)()) {
router.push("/login");
return;
}
loadTrendData();
}, [
router,
period,
currentMonth
]);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)()) return;
loadCategoryData();
}, [
router,
currentMonth,
categoryType
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const loadTrendData = async ()=>{
setTrendLoading(true);
try {
const trend = await (0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getTrendStats */ .gR)(period, getMonthStr(currentMonth));
setTrendData(trend);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setTrendLoading(false);
}
};
const loadCategoryData = async ()=>{
setCategoryLoading(true);
try {
const category = await (0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getCategoryStats */ .Ap)(getMonthStr(currentMonth), categoryType);
setCategoryData(category);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setCategoryLoading(false);
}
};
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (trendChartInstance.current) {
trendChartInstance.current.destroy();
}
if (trendChartRef.current && trendData.length > 0) {
const ctx = trendChartRef.current.getContext("2d");
trendChartInstance.current = new chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL(ctx, {
type: "line",
data: {
labels: trendData.map((item)=>item.period),
datasets: [
{
label: "æ”¶å…¥",
data: trendData.map((item)=>item.income),
borderColor: "#10b981",
backgroundColor: "rgba(16, 185, 129, 0.1)",
fill: true,
tension: 0.4
},
{
label: "支出",
data: trendData.map((item)=>item.expense),
borderColor: "#ef4444",
backgroundColor: "rgba(239, 68, 68, 0.1)",
fill: true,
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top"
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
}, [
trendData
]);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (categoryChartInstance.current) {
categoryChartInstance.current.destroy();
}
if (categoryChartRef.current && categoryData.length > 0) {
const ctx = categoryChartRef.current.getContext("2d");
const colors = [
"#2563eb",
"#10b981",
"#f59e0b",
"#ef4444",
"#8b5cf6",
"#ec4899",
"#06b6d4",
"#84cc16",
"#f97316",
"#6366f1"
];
categoryChartInstance.current = new chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL(ctx, {
type: "doughnut",
data: {
labels: categoryData.map((item)=>item.category),
datasets: [
{
data: categoryData.map((item)=>item.total),
backgroundColor: colors.slice(0, categoryData.length)
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom"
}
}
}
});
}
}, [
categoryData
]);
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 totalCategoryAmount = categoryData.reduce((sum, item)=>sum + item.total, 0);
const headerContent = /*#__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-spacer"
}),
/*#__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_4__/* .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_4__/* .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_4__/* .Icon */ .J, {
name: "settings",
size: 18
})
})
})
]
});
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components_AppShell__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .Z, {
headerContent: headerContent,
compactHeader: true,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("section", {
className: "card section-card section-card--stats",
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__.jsx)("h2", {
children: "收支趋势"
})
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "period-tabs",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "week" ? "active" : "",
onClick: ()=>setPeriod("week"),
children: "周"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "month" ? "active" : "",
onClick: ()=>setPeriod("month"),
children: "月"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "year" ? "active" : "",
onClick: ()=>setPeriod("year"),
children: "å¹´"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "chart-container chart-container--large chart-container--stats",
children: trendLoading ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "loading-state",
children: "加载中..."
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("canvas", {
ref: trendChartRef
})
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("section", {
className: "card section-card section-card--stats",
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__.jsx)("div", {
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h2", {
children: "分类明细"
})
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "category-type-tabs",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: categoryType === "expense" ? "active" : "",
onClick: ()=>setCategoryType("expense"),
children: "支出"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: categoryType === "income" ? "active" : "",
onClick: ()=>setCategoryType("income"),
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "category-breakdown category-breakdown--stats",
children: categoryData.length > 0 ? categoryData.map((cat, index)=>{
const percent = totalCategoryAmount ? (cat.total / totalCategoryAmount * 100).toFixed(1) : "0.0";
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "stats-category-item",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "stats-category-header",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "stats-category-name",
children: cat.category
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "stats-category-amount",
children: cat.total.toFixed(2)
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "stats-category-bar-wrap",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "stats-category-bar",
style: {
width: "".concat(percent, "%")
}
})
})
]
}, index);
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "empty-state empty-state--inline",
children: "暂无数æ®"
})
})
]
})
]
});
}
/***/ }) ÖÙ  ÚéÚ|__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Stats; }
/* 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 chart_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3148);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5546);
ÛŽ8import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useEffect, useRef } from "react";
import { useRouter } from "next/router";
import { isLoggedIn, getTrendStats, getCategoryStats } from "../lib/api";
import { Chart, registerables } from "chart.js";
import AppShell from "../components/AppShell";
import { Icon } from "../components/icons";
import { useToast } from "../components/ToastProvider";
Chart.register(...registerables);
export default function Stats() {
const router = useRouter();
const { showToast } = useToast();
const [period, setPeriod] = useState("month");
const [currentMonth, setCurrentMonth] = useState(new Date());
const [trendData, setTrendData] = useState([]);
const [categoryData, setCategoryData] = useState([]);
const [categoryType, setCategoryType] = useState("expense");
const [trendLoading, setTrendLoading] = useState(true);
const [categoryLoading, setCategoryLoading] = useState(true);
const trendChartRef = useRef(null);
const categoryChartRef = useRef(null);
const trendChartInstance = useRef(null);
const categoryChartInstance = useRef(null);
useEffect(()=>{
if (!isLoggedIn()) {
router.push("/login");
return;
}
loadTrendData();
}, [
router,
period,
currentMonth
]);
useEffect(()=>{
if (!isLoggedIn()) return;
loadCategoryData();
}, [
router,
currentMonth,
categoryType
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const loadTrendData = async ()=>{
setTrendLoading(true);
try {
const trend = await getTrendStats(period, getMonthStr(currentMonth));
setTrendData(trend);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setTrendLoading(false);
}
};
const loadCategoryData = async ()=>{
setCategoryLoading(true);
try {
const category = await getCategoryStats(getMonthStr(currentMonth), categoryType);
setCategoryData(category);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setCategoryLoading(false);
}
};
useEffect(()=>{
if (trendChartInstance.current) {
trendChartInstance.current.destroy();
}
if (trendChartRef.current && trendData.length > 0) {
const ctx = trendChartRef.current.getContext("2d");
trendChartInstance.current = new Chart(ctx, {
type: "line",
data: {
labels: trendData.map((item)=>item.period),
datasets: [
{
label: "æ”¶å…¥",
data: trendData.map((item)=>item.income),
borderColor: "#10b981",
backgroundColor: "rgba(16, 185, 129, 0.1)",
fill: true,
tension: 0.4
},
{
label: "支出",
data: trendData.map((item)=>item.expense),
borderColor: "#ef4444",
backgroundColor: "rgba(239, 68, 68, 0.1)",
fill: true,
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top"
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
}, [
trendData
]);
useEffect(()=>{
if (categoryChartInstance.current) {
categoryChartInstance.current.destroy();
}
if (categoryChartRef.current && categoryData.length > 0) {
const ctx = categoryChartRef.current.getContext("2d");
const colors = [
"#2563eb",
"#10b981",
"#f59e0b",
"#ef4444",
"#8b5cf6",
"#ec4899",
"#06b6d4",
"#84cc16",
"#f97316",
"#6366f1"
];
categoryChartInstance.current = new Chart(ctx, {
type: "doughnut",
data: {
labels: categoryData.map((item)=>item.category),
datasets: [
{
data: categoryData.map((item)=>item.total),
backgroundColor: colors.slice(0, categoryData.length)
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom"
}
}
}
});
}
}, [
categoryData
]);
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 totalCategoryAmount = categoryData.reduce((sum, item)=>sum + item.total, 0);
const headerContent = /*#__PURE__*/ _jsxs("div", {
className: "header-center-container",
children: [
/*#__PURE__*/ _jsx("div", {
className: "header-spacer"
}),
/*#__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
})
})
})
]
});
return /*#__PURE__*/ _jsxs(AppShell, {
headerContent: headerContent,
compactHeader: true,
children: [
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--stats",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsxs("div", {
children: [
/*#__PURE__*/ _jsx("p", {
className: "eyebrow",
children: "趋势"
}),
/*#__PURE__*/ _jsx("h2", {
children: "收支趋势"
})
]
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "period-tabs",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "week" ? "active" : "",
onClick: ()=>setPeriod("week"),
children: "周"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "month" ? "active" : "",
onClick: ()=>setPeriod("month"),
children: "月"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: period === "year" ? "active" : "",
onClick: ()=>setPeriod("year"),
children: "å¹´"
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "chart-container chart-container--large chart-container--stats",
children: trendLoading ? /*#__PURE__*/ _jsx("div", {
className: "loading-state",
children: "加载中..."
}) : /*#__PURE__*/ _jsx("canvas", {
ref: trendChartRef
})
})
]
}),
/*#__PURE__*/ _jsxs("section", {
className: "card section-card section-card--stats",
children: [
/*#__PURE__*/ _jsx("div", {
className: "section-heading section-heading--compact",
children: /*#__PURE__*/ _jsx("div", {
children: /*#__PURE__*/ _jsx("h2", {
children: "分类明细"
})
})
}),
/*#__PURE__*/ _jsxs("div", {
className: "category-type-tabs",
children: [
/*#__PURE__*/ _jsx("button", {
type: "button",
className: categoryType === "expense" ? "active" : "",
onClick: ()=>setCategoryType("expense"),
children: "支出"
}),
/*#__PURE__*/ _jsx("button", {
type: "button",
className: categoryType === "income" ? "active" : "",
onClick: ()=>setCategoryType("income"),
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "category-breakdown category-breakdown--stats",
children: categoryData.length > 0 ? categoryData.map((cat, index)=>{
const percent = totalCategoryAmount ? (cat.total / totalCategoryAmount * 100).toFixed(1) : "0.0";
return /*#__PURE__*/ _jsxs("div", {
className: "stats-category-item",
children: [
/*#__PURE__*/ _jsxs("div", {
className: "stats-category-header",
children: [
/*#__PURE__*/ _jsx("span", {
className: "stats-category-name",
children: cat.category
}),
/*#__PURE__*/ _jsx("span", {
className: "stats-category-amount",
children: cat.total.toFixed(2)
})
]
}),
/*#__PURE__*/ _jsx("div", {
className: "stats-category-bar-wrap",
children: /*#__PURE__*/ _jsx("div", {
className: "stats-category-bar",
style: {
width: "".concat(percent, "%")
}
})
})
]
}, index);
}) : /*#__PURE__*/ _jsx("div", {
className: "empty-state empty-state--inline",
children: "暂无数æ®"
})
})
]
})
]
});
}
n062undefined64115undefined117156undefined158230undefined232279undefined281326undefined328370undefined372426undefined428432chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kLundefined446458chart_js__WEBPACK_IMPORTED_MODULE_6__/* .registerables */ .zXundefined462476undefined515523(0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)undefined554561(0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__/* .useToast */ .p)undefined598605(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined661668(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined721728(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined779786(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined837844(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined902909(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined968975(0,react__WEBPACK_IMPORTED_MODULE_1__.useState)undefined10101015(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined10531058(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined10981103(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined11461151(0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)undefined11641172(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined11931202(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)undefined13731381(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined14021411(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)undefined18651877(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getTrendStats */ .gR)undefined22832298(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getCategoryStats */ .Ap)undefined25992607(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined28872891chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kLundefined43354343(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)undefined49544958chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kLundefined61896193(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined62966299(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined63956399(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined65276530(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined67716774(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined67766779_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .Jundefined69556958(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined71487151(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined73927395(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined73977400_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .Jundefined76027605(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined77077710(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined79497952(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined79547957_components_icons__WEBPACK_IMPORTED_MODULE_4__/* .Icon */ .Jundefined81378141(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined81438150_components_AppShell__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .Zundefined82688272(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined84178420(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined85588562(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined86598662(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined88628865(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined90789082(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined92219224(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined95489551(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined98779880(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1024510248(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1042210425(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1058210585(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1075610760(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)undefined1090510908(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)undefined1104611049(0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)unde
},
«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, [196,925,888,774,179], function() { return __webpack_exec__(4841); });
var __webpack_exports__ = __webpack_require__.O();
_N_E = __webpack_exports__;
}
]); ˆýÿÿJz(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([[368],{
/***/ 4841:
/***/ (function(__unused_webpack_module, __unused_webpack_exports, __webpack_require__) {
(window.__NEXT_P = window.__NEXT_P || []).push([
"/stats",
function () {
return __webpack_require__(8011);
}
]);
if(false) {}
/***/ }),
/***/ 6248:
/***/ (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
]
});
}
/***/ }),
/***/ 610:
/***/ (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)
});
}
/***/ }),
/***/ 8011:
/***/ (function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_require__.r(__webpack_exports__);
/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */ "default": function() { return /* binding */ Stats; }
/* 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 chart_js__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(3148);
/* harmony import */ var _components_AppShell__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(6248);
/* harmony import */ var _components_icons__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(9415);
/* harmony import */ var _components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(5546);
chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL.register(...chart_js__WEBPACK_IMPORTED_MODULE_6__/* .registerables */ .zX);
function Stats() {
const router = (0,next_router__WEBPACK_IMPORTED_MODULE_2__.useRouter)();
const { showToast } = (0,_components_ToastProvider__WEBPACK_IMPORTED_MODULE_5__/* .useToast */ .p)();
const [period, setPeriod] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("month");
const [currentMonth, setCurrentMonth] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(new Date());
const [trendData, setTrendData] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [categoryData, setCategoryData] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)([]);
const [categoryType, setCategoryType] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)("expense");
const [trendLoading, setTrendLoading] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(true);
const [categoryLoading, setCategoryLoading] = (0,react__WEBPACK_IMPORTED_MODULE_1__.useState)(true);
const trendChartRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const categoryChartRef = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const trendChartInstance = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
const categoryChartInstance = (0,react__WEBPACK_IMPORTED_MODULE_1__.useRef)(null);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)()) {
router.push("/login");
return;
}
loadTrendData();
}, [
router,
period,
currentMonth
]);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (!(0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .isLoggedIn */ .jl)()) return;
loadCategoryData();
}, [
router,
currentMonth,
categoryType
]);
const getMonthStr = (date)=>"".concat(date.getFullYear(), "-").concat(String(date.getMonth() + 1).padStart(2, "0"));
const formatMonth = (date)=>"".concat(date.getFullYear(), "年").concat(date.getMonth() + 1, "月");
const loadTrendData = async ()=>{
setTrendLoading(true);
try {
const trend = await (0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getTrendStats */ .gR)(period, getMonthStr(currentMonth));
setTrendData(trend);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setTrendLoading(false);
}
};
const loadCategoryData = async ()=>{
setCategoryLoading(true);
try {
const category = await (0,_lib_api__WEBPACK_IMPORTED_MODULE_7__/* .getCategoryStats */ .Ap)(getMonthStr(currentMonth), categoryType);
setCategoryData(category);
} catch (err) {
console.error(err);
showToast(err.message, {
tone: "error"
});
} finally{
setCategoryLoading(false);
}
};
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (trendChartInstance.current) {
trendChartInstance.current.destroy();
}
if (trendChartRef.current && trendData.length > 0) {
const ctx = trendChartRef.current.getContext("2d");
trendChartInstance.current = new chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL(ctx, {
type: "line",
data: {
labels: trendData.map((item)=>item.period),
datasets: [
{
label: "æ”¶å…¥",
data: trendData.map((item)=>item.income),
borderColor: "#10b981",
backgroundColor: "rgba(16, 185, 129, 0.1)",
fill: true,
tension: 0.4
},
{
label: "支出",
data: trendData.map((item)=>item.expense),
borderColor: "#ef4444",
backgroundColor: "rgba(239, 68, 68, 0.1)",
fill: true,
tension: 0.4
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "top"
}
},
scales: {
y: {
beginAtZero: true
}
}
}
});
}
}, [
trendData
]);
(0,react__WEBPACK_IMPORTED_MODULE_1__.useEffect)(()=>{
if (categoryChartInstance.current) {
categoryChartInstance.current.destroy();
}
if (categoryChartRef.current && categoryData.length > 0) {
const ctx = categoryChartRef.current.getContext("2d");
const colors = [
"#2563eb",
"#10b981",
"#f59e0b",
"#ef4444",
"#8b5cf6",
"#ec4899",
"#06b6d4",
"#84cc16",
"#f97316",
"#6366f1"
];
categoryChartInstance.current = new chart_js__WEBPACK_IMPORTED_MODULE_6__/* .Chart */ .kL(ctx, {
type: "doughnut",
data: {
labels: categoryData.map((item)=>item.category),
datasets: [
{
data: categoryData.map((item)=>item.total),
backgroundColor: colors.slice(0, categoryData.length)
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
plugins: {
legend: {
position: "bottom"
}
}
}
});
}
}, [
categoryData
]);
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 totalCategoryAmount = categoryData.reduce((sum, item)=>sum + item.total, 0);
const headerContent = /*#__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-spacer"
}),
/*#__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_4__/* .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_4__/* .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_4__/* .Icon */ .J, {
name: "settings",
size: 18
})
})
})
]
});
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(_components_AppShell__WEBPACK_IMPORTED_MODULE_3__/* ["default"] */ .Z, {
headerContent: headerContent,
compactHeader: true,
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("section", {
className: "card section-card section-card--stats",
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__.jsx)("h2", {
children: "收支趋势"
})
]
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "period-tabs",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "week" ? "active" : "",
onClick: ()=>setPeriod("week"),
children: "周"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "month" ? "active" : "",
onClick: ()=>setPeriod("month"),
children: "月"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: period === "year" ? "active" : "",
onClick: ()=>setPeriod("year"),
children: "å¹´"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "chart-container chart-container--large chart-container--stats",
children: trendLoading ? /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "loading-state",
children: "加载中..."
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("canvas", {
ref: trendChartRef
})
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("section", {
className: "card section-card section-card--stats",
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__.jsx)("div", {
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h2", {
children: "分类明细"
})
})
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "category-type-tabs",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: categoryType === "expense" ? "active" : "",
onClick: ()=>setCategoryType("expense"),
children: "支出"
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("button", {
type: "button",
className: categoryType === "income" ? "active" : "",
onClick: ()=>setCategoryType("income"),
children: "æ”¶å…¥"
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "category-breakdown category-breakdown--stats",
children: categoryData.length > 0 ? categoryData.map((cat, index)=>{
const percent = totalCategoryAmount ? (cat.total / totalCategoryAmount * 100).toFixed(1) : "0.0";
return /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "stats-category-item",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
className: "stats-category-header",
children: [
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "stats-category-name",
children: cat.category
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("span", {
className: "stats-category-amount",
children: cat.total.toFixed(2)
})
]
}),
/*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "stats-category-bar-wrap",
children: /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "stats-category-bar",
style: {
width: "".concat(percent, "%")
}
})
})
]
}, index);
}) : /*#__PURE__*/ (0,react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("div", {
className: "empty-state empty-state--inline",
children: "暂无数æ®"
})
})
]
})
]
});
}
/***/ })
},
/******/ function(__webpack_require__) { // webpackRuntimeModules
/******/ var __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
/******/ __webpack_require__.O(0, [196,925,888,774,179], function() { return __webpack_exec__(4841); });
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ _N_E = __webpack_exports__;
/******/ }
]); {}ð `4)þÿÿW(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([[368],{
/***/ 4841:
(þÿÿ5þÿÿ*þÿÿ
(window.__NEXT_P = window.__NEXT_P || []).push([
"/stats",
function () {
return require("private-next-pages/stats.js");
}
]);
if(module.hot) {
module.hot.dispose(function () {
window.__NEXT_P.push(["/stats"])
});
}
Z105111__webpack_require__undefined1131418011undefined168177falseundefined180276{}undefined6þÿÿ7þÿÿ6þÿÿ8þÿÿ&þÿÿ9þÿÿ&þÿÿ:þÿÿ(þÿÿ;þÿÿ<þÿÿ6þÿÿ7þÿÿ6þÿÿ=þÿÿ&þÿÿ9þÿÿ&þÿÿ>þÿÿ(þÿÿ?þÿÿ@þÿÿ6þÿÿ7þÿÿ6þÿÿ,
/***/ 8011:
%þÿÿ8þÿÿ%þÿÿí'þÿÿŽþÿÿî5þÿÿ6þÿÿ5þÿÿIþÿÿJþÿÿKþÿÿ5þÿÿÿvar __webpack_exec__ = function(moduleId) { return __webpack_require__(__webpack_require__.s = moduleId); }
__webpack_require__.O(0, [196,925,888,774,179], function() { return __webpack_exec__(4841); });
var __webpack_exports__ = __webpack_require__.O();
4þÿÿLþÿÿMþÿÿNþÿÿ2úÿÿ((self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[368],{4841:function(e,t,n){(window.__NEXT_P=window.__NEXT_P||[]).push(["/stats",function(){return n(8011)}])},6248:function(e,t,n){"use strict";n.d(t,{Z:function(){return i}});var a=n(5893),r=n(7294),c=n(1163),s=n(9415);let o=[{href:"/",label:"首页",icon:"home"},{href:"/stats",label:"统计",icon:"chart"},{href:"/recurring",label:"周期",icon:"repeat"},{href:"/categories",label:"分类",icon:"tags"}];function i(e){let{title:t,subtitle:n,actions:i,headerContent:l,children:u,contentClassName:d="",hideNav:h=!1,compactHeader:p=!1}=e,m=(0,c.useRouter)(),f=(0,r.useMemo)(()=>{let e=o.find(e=>e.href===m.pathname);return(null==e?void 0:e.href)||null},[m.pathname]),g=l||(t?(0,a.jsxs)("div",{className:"app-header__text",children:[(0,a.jsx)("h1",{children:t}),n?(0,a.jsx)("p",{children:n}):null]}):null),y=["app-header",p?"app-header--compact":"",g?"app-header--with-content":"app-header--actions-only",i?"app-header--with-actions":""].filter(Boolean).join(" ");return(0,a.jsxs)("div",{className:"app-shell".concat(h?" app-shell--standalone":""),children:[(0,a.jsx)("header",{className:y,children:(0,a.jsxs)("div",{className:"app-shell__container app-header__inner",children:[g?(0,a.jsx)("div",{className:"app-header__main",children:g}):(0,a.jsx)("div",{className:"app-header__spacer","aria-hidden":"true"}),i?(0,a.jsx)("div",{className:"app-header__actions",children:i}):null]})}),(0,a.jsx)("main",{className:"app-content ".concat(d).trim(),children:(0,a.jsx)("div",{className:"app-shell__container app-content__inner",children:u})}),h?null:(0,a.jsx)("nav",{className:"bottom-nav","aria-label":"主导航",children:(0,a.jsx)("div",{className:"app-shell__container bottom-nav__inner",children:o.map(e=>{let t=f===e.href;return(0,a.jsxs)("button",{type:"button",className:"bottom-nav__item".concat(t?" is-active":""),onClick:()=>m.push(e.href),"aria-current":t?"page":void 0,children:[(0,a.jsx)(s.J,{name:e.icon,size:20}),(0,a.jsx)("span",{children:e.label})]},e.href)})})})]})}},610:function(e,t,n){"use strict";async function a(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},n=await fetch("".concat("").concat(e),{...t,headers:{"Content-Type":"application/json",...function(){let e=localStorage.getItem("token");return e?{Authorization:"Bearer ".concat(e)}:{}}(),...t.headers}}),a=await n.json();if(!n.ok)throw Error(a.error||"请求失败");return a}async function r(e,t){let n=await a("/api/auth/login",{method:"POST",body:JSON.stringify({username:e,password:t})});return localStorage.setItem("token",n.token),n}async function c(e,t){let n=await a("/api/auth/register",{method:"POST",body:JSON.stringify({username:e,password:t})});return localStorage.setItem("token",n.token),n}function s(){localStorage.removeItem("token")}function o(){return!!localStorage.getItem("token")}async function i(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",n="/api/records?month=".concat(e);return"all"!==t&&(n+="&type=".concat(t)),a(n)}async function l(e){return a("/api/stats?month=".concat(e))}async function u(e){return a("/api/records",{method:"POST",body:JSON.stringify(e)})}async function d(e,t){return a("/api/records/".concat(e),{method:"PUT",body:JSON.stringify(t)})}async function h(e){return a("/api/records/".concat(e),{method:"DELETE"})}async function p(e){return a("/api/records/search?q=".concat(encodeURIComponent(e)))}async function m(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"month",t=arguments.length>1?arguments[1]:void 0,n="/api/stats/trend?period=".concat(e);return t&&(n+="&month=".concat(t)),a(n)}async function f(e){let t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",n="/api/stats/category?type=".concat(t);return e&&(n+="&month=".concat(e)),a(n)}async function g(){return a("/api/recurring")}async function y(e){return a("/api/recurring",{method:"POST",body:JSON.stringify(e)})}async function b(e,t){return a("/api/recurring/".concat(e),{method:"PUT",body:JSON.stringify(t)})}async function x(e){return a("/
žúÿÿ5úÿÿ0208c5ae4a3933c96úÿÿ7úÿÿ8úÿÿ9úÿÿ:úÿÿ;úÿÿa46a81a16144d550sself.__BUILD_MANIFEST=function(s){return{__rewrites:{afterFiles:[{has:s,source:"/api/:path*",destination:s}],beforeFiles:[],fallback:[]},"/":["static/chunks/pages/index-9134a1c15f8e7e73.js"],"/_error":["static/chunks/pages/_error-7a92967bea80186d.js"],"/categories":["static/chunks/pages/categories-a3255e0a23d19e86.js"],"/login":["static/chunks/pages/login-f50164aa2a6805b4.js"],"/recurring":["static/chunks/pages/recurring-560011bee3956d16.js"],"/search":["static/chunks/pages/search-3f7f0eb3485675fc.js"],"/settings":["static/chunks/pages/settings-bb02e29d5162e9fa.js"],"/stats":["static/chunks/ee8b1517-5dfbbb6838544537.js","static/chunks/925-ca8bea7fb2a62185.js","static/chunks/pages/stats-656db72deae83243.js"],sortedPages:["/","/_app","/_error","/categories","/login","/recurring","/search","/settings","/stats"]}}(void 0),self.__BUILD_MANIFEST_CB&&self.__BUILD_MANIFEST_CB();  ñüÿÿ °úÿÿ€@²úÿÿ¨/opt/accountbook/frontend/pages/index.js€€ ¸üÿÿ¾ýÿÿ¿ýÿÿüÿÿ ÁýÿÿÂýÿÿÃýÿÿÄýÿÿÆýÿÿÇýÿÿÈýÿÿ;ûÿÿ;ûÿÿ¹üÿÿ./pages/index.jsÿÿ @‰ M“¸ÍyBú³üÿÿ·üÿÿ¸üÿÿ¹üÿÿJþÿÿ/©üÿÿ!PN„¸ÍyB@N„¸ÍyBd642cff7a4eb3d3a§üÿÿ!@07ØÊyB /7ØÊyB²üÿÿ¦üÿÿšúÿÿ¥üÿÿ™úÿÿ¤üÿÿ˜úÿÿ£üÿÿ—úÿÿ¢üÿÿ–úÿÿ
KþÿÿÁ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.jsÄ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.jsÅ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.mjsÅ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.tsxÄ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.tsÅ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.jsxÆ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.jsonÆ/opt/accountbook/frontend/opt/accountbook/frontend/pages/index.js.wasmÂýÿÿƒ @€LþÿÿMþÿÿNþÿÿ þÿÿ `ÃýÿÿŽsideEffectFreeÂýÿÿ  áüÿÿ@.þÿÿýÿÿ þÄýÿÿ Åýÿÿýÿÿ ýÿÿ63a13a4c04152a1a ÆýÿÿNþÿÿ„Home PC“¸ÍyBÙà`  dÿÿý
`? `?
a@l `,
`m@• `(
A–þ `h
Aÿ6 `7
A7e `.
Af `+
A’É `7
 `;APAP
MþÿÿMþÿÿ `?õúÿÿõúÿÿ `,NþÿÿNþÿÿ `(OþÿÿOþÿÿ `h../lib/categoriesÿ `7OþÿÿOþÿÿ `.PþÿÿPþÿÿ `+QþÿÿQþÿÿ `7 ../components/ConfirmProviderÿ `;Ù\þÿÿ
]þÿÿ\þÿÿA:C Xþÿÿ@þÿÿ@þÿÿý c  ^þÿÿ]þÿÿAai ûEþÿÿEþÿÿþ c  "ŠuseConfirmþA… ö ððþ c  "ZþÿÿYþÿÿA¿Ç òÙúÿÿÙúÿÿþ c,4VþÿÿUþÿÿ îÕúÿÿÕúÿÿþ c(0RþÿÿQþÿÿA08 êÑúÿÿÑúÿÿþ c"*NþÿÿMþÿÿA\d æÍúÿÿÍúÿÿþ c&JþÿÿIþÿÿ× âÉúÿÿÉúÿÿþ c&.FþÿÿEþÿÿA ÞÅúÿÿÅúÿÿþ c.6BþÿÿAþÿÿA@H ÚÁúÿÿÁúÿÿþ c"*>þÿÿ=þÿÿAx Ö½úÿÿ½úÿÿþ c(0:þÿÿ9þÿÿµ Ò¹úÿÿ¹úÿÿþ c$,6þÿÿ5þÿÿì εúÿÿµúÿÿþ c&.2þÿÿ1þÿÿA ʱúÿÿ±úÿÿþ c$,\þÿÿ[þÿÿõ Æ­úÿÿ­úÿÿþ c$$ ]þÿÿ\þÿÿA  Âþÿÿþÿÿþ c% %ŠgetRecordsþAf p  ½þÿÿþÿÿþ c>>ˆgetStatsþ±  ¸ûýÿÿûýÿÿþ c??ŒupdateRecordþA+ ³öýÿÿöýÿÿþ @„`@„`"ŒcreateRecordþÙ ®ñýÿÿñýÿÿþ @‰`@‰`"ŒdeleteRecordþ² ©ìýÿÿìýÿÿþ ``fþÿÿgþÿÿA  ¥ãýÿÿãýÿÿþ `,@º`1hþÿÿiþÿÿA†Š ¡ßýÿÿßýÿÿþ ``dþÿÿeþÿÿò ÛýÿÿÛýÿÿþ @¿`(@¿`,`þÿÿaþÿÿä ×ýÿÿ×ýÿÿþ `,@Ä`0nþÿÿmþÿÿé4ÝýÿÿÝýÿÿþ `1@Ä`5RþÿÿSþÿÿ ÏýÿÿÏýÿÿþ ``TþÿÿUþÿÿA ËýÿÿËýÿÿþ `"@Í`&PþÿÿQþÿÿA ÇýÿÿÇýÿÿþ `0@Ò`4ð^þÿÿA 4ÎýÿÿÎýÿÿþ `5@Ò`9IþÿÿJþÿÿ¾ ÀýÿÿÀýÿÿþ @×`"@×`&EþÿÿFþÿÿA{ ~ÿÿÿ¼ýÿÿ¼ýÿÿþ `"@Û`&AþÿÿBþÿÿAos zÿÿÿ¸ýÿÿ¸ýÿÿþ `0@à`4áOþÿÿAtx4wÿÿÿ¿ýÿÿ¿ýÿÿþ `5@à`9:þÿÿ;þÿÿAA E  sÿÿÿ±ýÿÿ±ýÿÿþ ``6þÿÿ7þÿÿ®  oÿÿÿ­ýÿÿ­ýÿÿþ `(@é`,2þÿÿ3þÿÿAœ! ! kÿÿÿ©ýÿÿ©ýÿÿþ `,@î`0Ò@þÿÿA¡!¥!4hÿÿÿ°ýÿÿ°ýÿÿþ `1@î`5%þÿÿ&þÿÿAl"q" dÿÿÿ¢ýÿÿ¢ýÿÿþ `-@ö`2'þÿÿ(þÿÿA×"Û" `ÿÿÿžýÿÿžýÿÿþ ``#þÿÿ$þÿÿA?#C# \ÿÿÿšýÿÿšýÿÿþ `(@û`,þÿÿ þÿÿA$#$ Xÿÿÿ–ýÿÿ–ýÿÿþ @`,@`0¿-þÿÿA$$($4Uÿÿÿýÿÿýÿÿþ @`1@`5þÿÿþÿÿAÎ$Ò$ Qÿÿÿýÿÿýÿÿþ @`@`þÿÿþÿÿA8%<% Mÿÿÿ‹ýÿÿ‹ýÿÿþ @`(@`,þÿÿþÿÿAð%ô% Iÿÿÿ‡ýÿÿ‡ýÿÿþ @ `@ `þÿÿþÿÿAj&o& Eÿÿÿƒýÿÿƒýÿÿþ @`@`œýÿÿAþÿÿAp&x&4Aÿÿÿ‡ýÿÿ‡ýÿÿþ @`@`'þÿÿþÿÿA.'2' =ÿÿÿ{ýÿÿ{ýÿÿþ @`@`úýÿÿûýÿÿAš'Ÿ' 9ÿÿÿwýÿÿwýÿÿþ @`(@`-öýÿÿ÷ýÿÿA-(2( 5ÿÿÿsýÿÿsýÿÿþ @`&@`+øýÿÿùýÿÿAj)n) 1ÿÿÿoýÿÿoýÿÿþ @!`.@!`2îýÿÿïýÿÿA6*;* -ÿÿÿkýÿÿkýÿÿþ @%`.@%`3êýÿÿëýÿÿAÍ+Ò+ )ÿÿÿgýÿÿgýÿÿþ @.`&@.`+ìýÿÿíýÿÿA -- %ÿÿÿcýÿÿcýÿÿþ @3`.@3`2âýÿÿãýÿÿAØ-Ý- !ÿÿÿ_ýÿÿ_ýÿÿþ @7`
CC4Ãþÿÿ¸þÿÿ¸þÿÿþ `;@ž`M€ýÿÿýÿÿADDID ¿þÿÿýüÿÿýüÿÿþ `=@¢`B|ýÿÿ}ýÿÿAžE£E »þÿÿùüÿÿùüÿÿþ `>@§`C~ýÿÿýÿÿAF…F ·þÿÿõüÿÿõüÿÿþ `F@ª`JtýÿÿuýÿÿA¶G»G ³þÿÿñüÿÿñüÿÿþ `F@®`KvýÿÿwýÿÿA±HµH ¯þÿÿíüÿÿíüÿÿþ `N@±`RrýÿÿsýÿÿA J$J «þÿÿéüÿÿéüÿÿþ `]@µ`ahýÿÿiýÿÿAJLOL §þÿÿåüÿÿåüÿÿþ `>@½`CjýÿÿkýÿÿA?OCO £þÿÿáüÿÿáüÿÿþ ``fýÿÿgýÿÿAP P ŸþÿÿÝüÿÿÝüÿÿþ `(@Ñ`,ÿÿÿtýÿÿA PP4œþÿÿäüÿÿäüÿÿþ `-@Ñ`1_ýÿÿ`ýÿÿAP¡P ˜þÿÿÖüÿÿÖüÿÿþ `'@Ö`+UýÿÿVýÿÿA>QCQ ”þÿÿÒüÿÿÒüÿÿþ `(@Ù`-QýÿÿRýÿÿAR R þÿÿÎüÿÿÎüÿÿþ `&@Ý`+MýÿÿNýÿÿA R¥R ŒþÿÿÊüÿÿÊüÿÿþ `.@à`3OýÿÿPýÿÿASS ˆþÿÿÆüÿÿÆüÿÿþ `6@â`:KýÿÿLýÿÿA+T/T „þÿÿÂüÿÿÂüÿÿþ `F@æ`JGýÿÿHýÿÿAU#U €þÿÿ¾üÿÿ¾üÿÿþ `.@ë`2CýÿÿDýÿÿAdVhV |þÿÿºüÿÿºüÿÿþ `<@ð`@ãþÿÿQýÿÿAiVmV4yþÿÿÁüÿÿÁüÿÿþ `A@ð`E<ýÿÿ=ýÿÿAW”W uþÿÿ³üÿÿ³üÿÿþ `3@÷`72ýÿÿ3ýÿÿADXIX qþÿÿ¯üÿÿ¯üÿÿþ `&@û`+.ýÿÿ/ýÿÿAÞXãX mþÿÿ«üÿÿ«üÿÿþ `.@þ`30ýÿÿ1ýÿÿA‘Y•Y iþÿÿ§üÿÿ§üÿÿþ @`6@`:,ýÿÿ-ýÿÿA‹\\ eþÿÿ£üÿÿ£üÿÿþ @ `6@ `:"ýÿÿ#ýÿÿAÃ_È_ aþÿÿŸüÿÿŸüÿÿþ @`.@`3$ýÿÿ%ýÿÿAu`y` ]þÿÿ›üÿÿ›üÿÿþ @`6@`: ýÿÿ!ýÿÿAhala Yþÿÿ—üÿÿ—üÿÿþ @ `6@ `:ýÿÿýÿÿA0e5e Uþÿÿ“üÿÿ“üÿÿþ @0`.@0`3ýÿÿýÿÿAâeæe Qþÿÿüÿÿüÿÿþ @3`6@3`:ýÿÿýÿÿAf“f Mþÿÿ‹üÿÿ‹üÿÿþ @6`6@6`:èûÿÿAg-g4Jþÿÿ?þÿÿ?þÿÿþ @8`6@8`HýÿÿýÿÿAVg[g Fþÿÿ„üÿÿ„üÿÿþ @8`q@8`v ýÿÿ
ýÿÿA2j6j Bþÿÿ€üÿÿ€üÿÿþ @A`F@A`JýÿÿýÿÿAakek >þÿÿ|üÿÿ|üÿÿþ @E`F@E`JûüÿÿüüÿÿAmm :þÿÿxüÿÿxüÿÿþ @M`.@M`3ýüÿÿþüÿÿAÃmÇm 6þÿÿtüÿÿtüÿÿþ @P`6@P`:óüÿÿôüÿÿApnun 2þÿÿpüÿÿpüÿÿþ @S`6@S`;õüÿÿöüÿÿA?oCo .þÿÿlüÿÿlüÿÿþ @V`>@V`BñüÿÿòüÿÿA rr *þÿÿhüÿÿhüÿÿþ @``>@``BçüÿÿèüÿÿA4u9u &þÿÿdüÿÿdüÿÿþ @m`.@m`3éüÿÿêüÿÿAæuêu "þÿÿ`üÿÿ`üÿÿþ @p`6@p`:åüÿÿæüÿÿAÛvßv þÿÿ\üÿÿ\üÿÿþ @t`6@t`:ÛüÿÿÜüÿÿAáyæy þÿÿXüÿÿXüÿÿþ @`.@`3ÝüÿÿÞüÿÿA–zšz þÿÿTüÿÿTüÿÿþ @„`6@„`:ÓüÿÿÔüÿÿAÌ{Ñ{ þÿÿPüÿÿPüÿÿþ `Q@ˆ`VÕüÿÿÖüÿÿA§|«| þÿÿLüÿÿLüÿÿþ @‹`B@‹`FuþÿÿãüÿÿA¬|°|4 þÿÿSüÿÿSüÿÿþ @‹`G@‹`KÎüÿÿÏüÿÿAà~ä~ þÿÿEüÿÿEüÿÿþ @”`G@”`K±ûÿÿ[üÿÿ

ûÿÿ ûÿÿ 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ûûüûüüûüüýüûûûüüüüýüûûüüûüüûüüÿûüüûüûüüûüüûüûüýüûÿÿ__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 ? "删除中..." : "删除"
})
]
})
]
})
]
})
})
]
});
}
týÿÿwýÿÿ °ûÿÿÍûÿÿ²ûÿÿÎûÿÿÏûÿÿzýÿÿ{ýÿÿ|ýÿÿ”ûÿÿ´ûÿÿÉýÿÿ»÷ÿÿŠ>(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[405],{5557:function(e,n,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/",function(){return t(9828)}])},6248:function(e,n,t){"use strict";t.d(n,{Z:function(){return o}});var a=t(5893),s=t(7294),c=t(1163),i=t(9415);let r=[{href:"/",label:"首页",icon:"home"},{href:"/stats",label:"统计",icon:"chart"},{href:"/recurring",label:"周期",icon:"repeat"},{href:"/categories",label:"分类",icon:"tags"}];function o(e){let{title:n,subtitle:t,actions:o,headerContent:l,children:u,contentClassName:d="",hideNav:m=!1,compactHeader:h=!1}=e,p=(0,c.useRouter)(),x=(0,s.useMemo)(()=>{let e=r.find(e=>e.href===p.pathname);return(null==e?void 0:e.href)||null},[p.pathname]),y=l||(n?(0,a.jsxs)("div",{className:"app-header__text",children:[(0,a.jsx)("h1",{children:n}),t?(0,a.jsx)("p",{children:t}):null]}):null),j=["app-header",h?"app-header--compact":"",y?"app-header--with-content":"app-header--actions-only",o?"app-header--with-actions":""].filter(Boolean).join(" ");return(0,a.jsxs)("div",{className:"app-shell".concat(m?" app-shell--standalone":""),children:[(0,a.jsx)("header",{className:j,children:(0,a.jsxs)("div",{className:"app-shell__container app-header__inner",children:[y?(0,a.jsx)("div",{className:"app-header__main",children:y}):(0,a.jsx)("div",{className:"app-header__spacer","aria-hidden":"true"}),o?(0,a.jsx)("div",{className:"app-header__actions",children:o}):null]})}),(0,a.jsx)("main",{className:"app-content ".concat(d).trim(),children:(0,a.jsx)("div",{className:"app-shell__container app-content__inner",children:u})}),m?null:(0,a.jsx)("nav",{className:"bottom-nav","aria-label":"主导航",children:(0,a.jsx)("div",{className:"app-shell__container bottom-nav__inner",children:r.map(e=>{let n=x===e.href;return(0,a.jsxs)("button",{type:"button",className:"bottom-nav__item".concat(n?" is-active":""),onClick:()=>p.push(e.href),"aria-current":n?"page":void 0,children:[(0,a.jsx)(i.J,{name:e.icon,size:20}),(0,a.jsx)("span",{children:e.label})]},e.href)})})})]})}},610:function(e,n,t){"use strict";async function a(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},t=await fetch("".concat("").concat(e),{...n,headers:{"Content-Type":"application/json",...function(){let e=localStorage.getItem("token");return e?{Authorization:"Bearer ".concat(e)}:{}}(),...n.headers}}),a=await t.json();if(!t.ok)throw Error(a.error||"请求失败");return a}async function s(e,n){let t=await a("/api/auth/login",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}async function c(e,n){let t=await a("/api/auth/register",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}function i(){localStorage.removeItem("token")}function r(){return!!localStorage.getItem("token")}async function o(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/records?month=".concat(e);return"all"!==n&&(t+="&type=".concat(n)),a(t)}async function l(e){return a("/api/stats?month=".concat(e))}async function u(e){return a("/api/records",{method:"POST",body:JSON.stringify(e)})}async function d(e,n){return a("/api/records/".concat(e),{method:"PUT",body:JSON.stringify(n)})}async function m(e){return a("/api/records/".concat(e),{method:"DELETE"})}async function h(e){return a("/api/records/search?q=".concat(encodeURIComponent(e)))}async function p(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"month",n=arguments.length>1?arguments[1]:void 0,t="/api/stats/trend?period=".concat(e);return n&&(t+="&month=".concat(n)),a(t)}async function x(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/stats/category?type=".concat(n);return e&&(t+="&month=".concat(e)),a(t)}async function y(){return a("/api/recurring")}async function j(e){return a("/api/recurring",{method:"POST",body:JSON.stringify(e)})}async function f(e,n){return a("/api/recurring/".concat(e),{method:"P
(øÿÿ¿÷ÿÿÀ÷ÿÿÁ÷ÿÿÂ÷ÿÿÃ÷ÿÿÄ÷ÿÿÅ÷ÿÿÆ÷ÿÿøÿÿ)øÿÿ ¢÷ÿÿ€6(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[845],{8208:function(e,n,t){(window.__NEXT_P=window.__NEXT_P||[]).push(["/recurring",function(){return t(5190)}])},6248:function(e,n,t){"use strict";t.d(n,{Z:function(){return o}});var a=t(5893),i=t(7294),c=t(1163),s=t(9415);let r=[{href:"/",label:"首页",icon:"home"},{href:"/stats",label:"统计",icon:"chart"},{href:"/recurring",label:"周期",icon:"repeat"},{href:"/categories",label:"分类",icon:"tags"}];function o(e){let{title:n,subtitle:t,actions:o,headerContent:l,children:u,contentClassName:d="",hideNav:m=!1,compactHeader:h=!1}=e,p=(0,c.useRouter)(),y=(0,i.useMemo)(()=>{let e=r.find(e=>e.href===p.pathname);return(null==e?void 0:e.href)||null},[p.pathname]),b=l||(n?(0,a.jsxs)("div",{className:"app-header__text",children:[(0,a.jsx)("h1",{children:n}),t?(0,a.jsx)("p",{children:t}):null]}):null),f=["app-header",h?"app-header--compact":"",b?"app-header--with-content":"app-header--actions-only",o?"app-header--with-actions":""].filter(Boolean).join(" ");return(0,a.jsxs)("div",{className:"app-shell".concat(m?" app-shell--standalone":""),children:[(0,a.jsx)("header",{className:f,children:(0,a.jsxs)("div",{className:"app-shell__container app-header__inner",children:[b?(0,a.jsx)("div",{className:"app-header__main",children:b}):(0,a.jsx)("div",{className:"app-header__spacer","aria-hidden":"true"}),o?(0,a.jsx)("div",{className:"app-header__actions",children:o}):null]})}),(0,a.jsx)("main",{className:"app-content ".concat(d).trim(),children:(0,a.jsx)("div",{className:"app-shell__container app-content__inner",children:u})}),m?null:(0,a.jsx)("nav",{className:"bottom-nav","aria-label":"主导航",children:(0,a.jsx)("div",{className:"app-shell__container bottom-nav__inner",children:r.map(e=>{let n=y===e.href;return(0,a.jsxs)("button",{type:"button",className:"bottom-nav__item".concat(n?" is-active":""),onClick:()=>p.push(e.href),"aria-current":n?"page":void 0,children:[(0,a.jsx)(s.J,{name:e.icon,size:20}),(0,a.jsx)("span",{children:e.label})]},e.href)})})})]})}},610:function(e,n,t){"use strict";async function a(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},t=await fetch("".concat("").concat(e),{...n,headers:{"Content-Type":"application/json",...function(){let e=localStorage.getItem("token");return e?{Authorization:"Bearer ".concat(e)}:{}}(),...n.headers}}),a=await t.json();if(!t.ok)throw Error(a.error||"请求失败");return a}async function i(e,n){let t=await a("/api/auth/login",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}async function c(e,n){let t=await a("/api/auth/register",{method:"POST",body:JSON.stringify({username:e,password:n})});return localStorage.setItem("token",t.token),t}function s(){localStorage.removeItem("token")}function r(){return!!localStorage.getItem("token")}async function o(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/records?month=".concat(e);return"all"!==n&&(t+="&type=".concat(n)),a(t)}async function l(e){return a("/api/stats?month=".concat(e))}async function u(e){return a("/api/records",{method:"POST",body:JSON.stringify(e)})}async function d(e,n){return a("/api/records/".concat(e),{method:"PUT",body:JSON.stringify(n)})}async function m(e){return a("/api/records/".concat(e),{method:"DELETE"})}async function h(e){return a("/api/records/search?q=".concat(encodeURIComponent(e)))}async function p(){let e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:"month",n=arguments.length>1?arguments[1]:void 0,t="/api/stats/trend?period=".concat(e);return n&&(t+="&month=".concat(n)),a(t)}async function y(e){let n=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"all",t="/api/stats/category?type=".concat(n);return e&&(t+="&month=".concat(e)),a(t)}async function b(){return a("/api/recurring")}async function f(e){return a("/api/recurring",{method:"POST",body:JSON.stringify(e)})}async function x(e,n){return a("/api/recurring/".concat(e),{method:"PUT",body:JSON.