diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..8df4d17 --- /dev/null +++ b/.npmrc @@ -0,0 +1,2 @@ +http-proxy=http://localhost:10808 +registry=https://registry.npmmirror.com \ No newline at end of file diff --git a/README.md b/README.md index 5e8fab1..7a86252 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,7 @@ ### 下阶段 +- 后台首页改为 grid 布局,需要额外实现用于布局的客户端组件 - 检查页面,为后端请求标记 wait 实现防抖机制 - 页面切换动效 - 使用 pure js 的包代替 canvas,加快编译速度 diff --git a/eslint.config.mjs b/eslint.config.mjs index 1b4a285..6adbd31 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -19,6 +19,7 @@ const eslintConfig = [ }, rules: { 'semi': ['error', 'never'], + 'indent': ['error', 2], '@typescript-eslint/no-empty-object-type': 'off', '@typescript-eslint/no-unused-vars': 'off', '@stylistic/member-delimiter-style': ['error', { diff --git a/package.json b/package.json index d004d71..7aafade 100644 --- a/package.json +++ b/package.json @@ -25,10 +25,8 @@ "canvas": "^3.1.0", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", - "cmdk": "^1.1.1", "date-fns": "^4.1.0", "lucide-react": "^0.479.0", - "motion": "^12.5.0", "next": "15.2.4", "next-themes": "^0.4.6", "qrcode": "^1.5.4", @@ -36,6 +34,7 @@ "react-day-picker": "8.10.1", "react-dom": "^19.0.0", "react-hook-form": "^7.54.2", + "recharts": "^2.15.3", "sonner": "^2.0.1", "tailwind-merge": "^3.0.2", "tailwindcss-animate": "^1.0.7", @@ -62,6 +61,9 @@ "onlyBuiltDependencies": [ "canvas", "sharp" - ] + ], + "overrides": { + "react-is": "19.0.0-rc.1" + } } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5ed23dd..fb13ef5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,9 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + react-is: 19.0.0-rc.1 + importers: .: @@ -56,18 +59,12 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 - cmdk: - specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) date-fns: specifier: ^4.1.0 version: 4.1.0 lucide-react: specifier: ^0.479.0 version: 0.479.0(react@19.0.0) - motion: - specifier: ^12.5.0 - version: 12.5.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) next: specifier: 15.2.4 version: 15.2.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -89,6 +86,9 @@ importers: react-hook-form: specifier: ^7.54.2 version: 7.54.2(react@19.0.0) + recharts: + specifier: ^2.15.3 + version: 2.15.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0) sonner: specifier: ^2.0.1 version: 2.0.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) @@ -151,6 +151,10 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} + '@babel/runtime@7.27.1': + resolution: {integrity: sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==} + engines: {node: '>=6.9.0'} + '@emnapi/runtime@1.3.1': resolution: {integrity: sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==} @@ -1266,6 +1270,33 @@ packages: resolution: {integrity: sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA==} engines: {node: '>=12'} + '@types/d3-array@3.2.1': + resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + '@types/estree@1.0.6': resolution: {integrity: sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==} @@ -1492,12 +1523,6 @@ packages: resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} engines: {node: '>=6'} - cmdk@1.1.1: - resolution: {integrity: sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg==} - peerDependencies: - react: ^18 || ^19 || ^19.0.0-rc - react-dom: ^18 || ^19 || ^19.0.0-rc - color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} @@ -1522,6 +1547,50 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -1561,6 +1630,9 @@ packages: resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} engines: {node: '>=0.10.0'} + decimal.js-light@2.5.1: + resolution: {integrity: sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==} + decompress-response@6.0.0: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} @@ -1599,6 +1671,9 @@ packages: resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} engines: {node: '>=0.10.0'} + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} @@ -1768,6 +1843,9 @@ packages: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} + eventemitter3@4.0.7: + resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} + expand-template@2.0.3: resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} engines: {node: '>=6'} @@ -1775,6 +1853,10 @@ packages: fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + fast-equals@5.2.2: + resolution: {integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==} + engines: {node: '>=6.0.0'} + fast-glob@3.3.1: resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} @@ -1827,20 +1909,6 @@ packages: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} - framer-motion@12.5.0: - resolution: {integrity: sha512-buPlioFbH9/W7rDzYh1C09AuZHAk2D1xTA1BlounJ2Rb9aRg84OXexP0GLd+R83v0khURdMX7b5MKnGTaSg5iA==} - peerDependencies: - '@emotion/is-prop-valid': '*' - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/is-prop-valid': - optional: true - react: - optional: true - react-dom: - optional: true - fs-constants@1.0.0: resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} @@ -1958,6 +2026,10 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} @@ -2195,6 +2267,9 @@ packages: lodash.merge@4.6.2: resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + loose-envify@1.4.0: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true @@ -2233,26 +2308,6 @@ packages: mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} - motion-dom@12.5.0: - resolution: {integrity: sha512-uH2PETDh7m+Hjd1UQQ56yHqwn83SAwNjimNPE/kC+Kds0t4Yh7+29rfo5wezVFpPOv57U4IuWved5d1x0kNhbQ==} - - motion-utils@12.5.0: - resolution: {integrity: sha512-+hFFzvimn0sBMP9iPxBa9OtRX35ZQ3py0UHnb8U29VD+d8lQ8zH3dTygJWqK7av2v6yhg7scj9iZuvTS0f4+SA==} - - motion@12.5.0: - resolution: {integrity: sha512-BTAYKszMmTvXSsIyeHNMPSicjWgUA4j7OmZv1xPpthm4sPub3ch66fy9U7BhJ1uXNL3YeprsIegzuvps3FkEMw==} - peerDependencies: - '@emotion/is-prop-valid': '*' - react: ^18.0.0 || ^19.0.0 - react-dom: ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - '@emotion/is-prop-valid': - optional: true - react: - optional: true - react-dom: - optional: true - ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2454,8 +2509,8 @@ packages: peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 - react-is@16.13.1: - resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + react-is@19.0.0-rc.1: + resolution: {integrity: sha512-D6AbvUGS+i2lK3yC1a+iSicqWhIenYGxYUd7j0JJxunlk0RSAy/yRo58Mh5JJcAVQfNhej20nCwJVehYpNwNiA==} react-remove-scroll-bar@2.3.8: resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} @@ -2477,6 +2532,12 @@ packages: '@types/react': optional: true + react-smooth@4.0.4: + resolution: {integrity: sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} @@ -2487,6 +2548,12 @@ packages: '@types/react': optional: true + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + react@19.0.0: resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} engines: {node: '>=0.10.0'} @@ -2495,6 +2562,16 @@ packages: resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} engines: {node: '>= 6'} + recharts-scale@0.4.5: + resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} + + recharts@2.15.3: + resolution: {integrity: sha512-EdOPzTwcFSuqtvkDoaM5ws/Km1+WTAO2eizL7rqiG0V2UVhTnz0m7J2i0CjVPUCdEkZImaWvXLbZDS2H5t6GFQ==} + engines: {node: '>=14'} + peerDependencies: + react: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} @@ -2718,6 +2795,9 @@ packages: resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} engines: {node: '>=6'} + tiny-invariant@1.3.3: + resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyglobby@0.2.12: resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} @@ -2799,6 +2879,9 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + victory-vendor@36.9.2: + resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} + which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} @@ -2874,6 +2957,8 @@ snapshots: '@alloc/quick-lru@5.2.0': {} + '@babel/runtime@7.27.1': {} + '@emnapi/runtime@1.3.1': dependencies: tslib: 2.8.1 @@ -3845,6 +3930,30 @@ snapshots: '@tanstack/table-core@8.21.2': {} + '@types/d3-array@3.2.1': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + '@types/estree@1.0.6': {} '@types/json-schema@7.0.15': {} @@ -4128,18 +4237,6 @@ snapshots: clsx@2.1.1: {} - cmdk@1.1.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-dialog': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - transitivePeerDependencies: - - '@types/react' - - '@types/react-dom' - color-convert@2.0.1: dependencies: color-name: 1.1.4 @@ -4168,6 +4265,44 @@ snapshots: csstype@3.1.3: {} + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-color@3.1.0: {} + + d3-ease@3.0.1: {} + + d3-format@3.1.0: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + damerau-levenshtein@1.0.8: {} data-view-buffer@1.0.2: @@ -4200,6 +4335,8 @@ snapshots: decamelize@1.2.0: {} + decimal.js-light@2.5.1: {} + decompress-response@6.0.0: dependencies: mimic-response: 3.1.0 @@ -4232,6 +4369,11 @@ snapshots: dependencies: esutils: 2.0.3 + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.27.1 + csstype: 3.1.3 + dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 @@ -4547,10 +4689,14 @@ snapshots: esutils@2.0.3: {} + eventemitter3@4.0.7: {} + expand-template@2.0.3: {} fast-deep-equal@3.1.3: {} + fast-equals@5.2.2: {} + fast-glob@3.3.1: dependencies: '@nodelib/fs.stat': 2.0.5 @@ -4608,15 +4754,6 @@ snapshots: dependencies: is-callable: 1.2.7 - framer-motion@12.5.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - motion-dom: 12.5.0 - motion-utils: 12.5.0 - tslib: 2.8.1 - optionalDependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - fs-constants@1.0.0: {} function-bind@1.1.2: {} @@ -4730,6 +4867,8 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + internmap@2.0.3: {} + is-array-buffer@3.0.5: dependencies: call-bind: 1.0.8 @@ -4952,6 +5091,8 @@ snapshots: lodash.merge@4.6.2: {} + lodash@4.17.21: {} + loose-envify@1.4.0: dependencies: js-tokens: 4.0.0 @@ -4983,20 +5124,6 @@ snapshots: mkdirp-classic@0.5.3: {} - motion-dom@12.5.0: - dependencies: - motion-utils: 12.5.0 - - motion-utils@12.5.0: {} - - motion@12.5.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - framer-motion: 12.5.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - tslib: 2.8.1 - optionalDependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - ms@2.1.3: {} nanoid@3.3.8: {} @@ -5172,7 +5299,7 @@ snapshots: dependencies: loose-envify: 1.4.0 object-assign: 4.1.1 - react-is: 16.13.1 + react-is: 19.0.0-rc.1 pump@3.0.2: dependencies: @@ -5210,7 +5337,7 @@ snapshots: dependencies: react: 19.0.0 - react-is@16.13.1: {} + react-is@19.0.0-rc.1: {} react-remove-scroll-bar@2.3.8(@types/react@19.0.10)(react@19.0.0): dependencies: @@ -5231,6 +5358,14 @@ snapshots: optionalDependencies: '@types/react': 19.0.10 + react-smooth@4.0.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + fast-equals: 5.2.2 + prop-types: 15.8.1 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-transition-group: 4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react-style-singleton@2.2.3(@types/react@19.0.10)(react@19.0.0): dependencies: get-nonce: 1.0.1 @@ -5239,6 +5374,15 @@ snapshots: optionalDependencies: '@types/react': 19.0.10 + react-transition-group@4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + '@babel/runtime': 7.27.1 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react@19.0.0: {} readable-stream@3.6.2: @@ -5247,6 +5391,23 @@ snapshots: string_decoder: 1.3.0 util-deprecate: 1.0.2 + recharts-scale@0.4.5: + dependencies: + decimal.js-light: 2.5.1 + + recharts@2.15.3(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + dependencies: + clsx: 2.1.1 + eventemitter3: 4.0.7 + lodash: 4.17.21 + react: 19.0.0 + react-dom: 19.0.0(react@19.0.0) + react-is: 19.0.0-rc.1 + react-smooth: 4.0.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + recharts-scale: 0.4.5 + tiny-invariant: 1.3.3 + victory-vendor: 36.9.2 + reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.8 @@ -5535,6 +5696,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + tiny-invariant@1.3.3: {} + tinyglobby@0.2.12: dependencies: fdir: 6.4.3(picomatch@4.0.2) @@ -5630,6 +5793,23 @@ snapshots: util-deprecate@1.0.2: {} + victory-vendor@36.9.2: + dependencies: + '@types/d3-array': 3.2.1 + '@types/d3-ease': 3.0.2 + '@types/d3-interpolate': 3.0.4 + '@types/d3-scale': 4.0.9 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-timer': 3.0.2 + d3-array: 3.2.4 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-scale: 4.0.2 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-timer: 3.0.1 + which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 diff --git a/src/actions/announcement.ts b/src/actions/announcement.ts new file mode 100644 index 0000000..f828db6 --- /dev/null +++ b/src/actions/announcement.ts @@ -0,0 +1,19 @@ +'use server' + +import { PageRecord } from "@/lib/api" +import { Announcement } from "@/lib/models" +import { callByUser } from "./base" + +export async function listAnnouncements(props: { + page: number + size: number + title?: string + type?: number + status?: number + create_after?: Date + create_before?: Date + update_after?: Date + update_before?: Date +}) { + return await callByUser>('/api/announcement/list', props) +} \ No newline at end of file diff --git a/src/app/admin/(dashboard)/_assets/action-bill.webp b/src/app/admin/(dashboard)/_assets/action-bill.webp new file mode 100644 index 0000000..f2edd07 Binary files /dev/null and b/src/app/admin/(dashboard)/_assets/action-bill.webp differ diff --git a/src/app/admin/(dashboard)/_assets/action-buy.webp b/src/app/admin/(dashboard)/_assets/action-buy.webp new file mode 100644 index 0000000..1e13ccf Binary files /dev/null and b/src/app/admin/(dashboard)/_assets/action-buy.webp differ diff --git a/src/app/admin/(dashboard)/_assets/action-logout.webp b/src/app/admin/(dashboard)/_assets/action-logout.webp new file mode 100644 index 0000000..46ed2bf Binary files /dev/null and b/src/app/admin/(dashboard)/_assets/action-logout.webp differ diff --git a/src/app/admin/(dashboard)/_client/chart.tsx b/src/app/admin/(dashboard)/_client/chart.tsx new file mode 100644 index 0000000..96bd70d --- /dev/null +++ b/src/app/admin/(dashboard)/_client/chart.tsx @@ -0,0 +1,35 @@ +'use client' + +import { ChartConfig, ChartContainer } from "@/components/ui/chart" +import { Area, AreaChart, Bar, BarChart, CartesianGrid, Tooltip, XAxis, YAxis } from "recharts" +import { addDays, format } from "date-fns" + +const data = Array(100).fill(0).map((_, i) => { + let time = new Date() + time = addDays(time, i) + return { + time: format(time, `MM/dd`), + count: Math.floor(Math.random() * 100) + 1, + } +}) + +const config = { + count: { + label: `套餐使用量`, + color: `var(--color-primary)`, + } +} satisfies ChartConfig + +export default function DashboardChart() { + return ( + + + + + + + + + + ) +} \ No newline at end of file diff --git a/src/app/admin/(dashboard)/page.tsx b/src/app/admin/(dashboard)/page.tsx index b212b59..faa7a73 100644 --- a/src/app/admin/(dashboard)/page.tsx +++ b/src/app/admin/(dashboard)/page.tsx @@ -1,16 +1,23 @@ import Page from '@/components/page' import Image from 'next/image' -import banner from './_assets/banner.webp' import {Card, CardContent, CardHeader, CardTitle} from '@/components/ui/card' import {Tabs, TabsContent, TabsList, TabsTrigger} from '@/components/ui/tabs' -import soon from './_assets/coming-soon.svg' import {getProfile} from '@/actions/auth' import {format} from 'date-fns' -import {CheckCircleIcon, CircleAlertIcon, LinkIcon} from 'lucide-react' -import {Button} from '@/components/ui/button' +import {CheckCircleIcon, CircleAlertIcon} from 'lucide-react' +import {Button, buttonVariants} from '@/components/ui/button' import RechargeModal from '@/components/composites/recharge' import {merge} from '@/lib/utils' +import banner from './_assets/banner.webp' +import soon from './_assets/coming-soon.svg' +import actionBill from './_assets/action-bill.webp' +import actionBuy from './_assets/action-buy.webp' +import actionLogout from './_assets/action-logout.webp' +import Link from 'next/link' +import { listAnnouncements } from '@/actions/announcement' +import DashboardChart from './_client/chart' + export type DashboardPageProps = {} export default async function DashboardPage(props: DashboardPageProps) { @@ -29,84 +36,104 @@ export default async function DashboardPage(props: DashboardPageProps) { - {/* 短效 */} - - - 短效动态套餐 - - -
-

包时

-

- 当日可提取数量 - todo -

-
-
-
-

包量

-

- 剩余可提取数量 - todo -

-
-
-
- - {/* 长效 */} - - - 长效动态套餐 - - - {`coming -

敬请期待

-
-
- - {/* 固定 */} - - - 固定IP套餐 - - - {`coming -

敬请期待

-
-
+ {/* 磁贴集 */} + {/* 图表 */} -
- - - 动态 IP 套餐 - 静态 IP 套餐 - - - dynamic - - - static - - -
+ {/* 信息 */} {/* 通知 */} - - - 通知 - - - todo - - + + ) } +async function Pins() { + return <> + {/* 短效 */} + + + 短效动态套餐 + + +
+

包时

+

+ 当日可提取数量 + todo +

+
+
+
+

包量

+

+ 剩余可提取数量 + todo +

+
+
+
+ + {/* 长效 */} + + + 长效动态套餐 + + + {`coming +

敬请期待

+
+
+ + {/* 固定 */} + + + 固定IP套餐 + + + {`coming +

敬请期待

+
+
+ +} + +async function Charts() { + + const data = [ + {time: `2023-10-01`, count: 100}, + {time: `2023-10-02`, count: 50}, + {time: `2023-10-03`, count: 80}, + {time: `2023-10-04`, count: 200}, + {time: `2023-10-05`, count: 150}, + ] + + return ( + + + + + 动态 IP 套餐 + 静态 IP 套餐 + + + + + + + {`coming +

敬请期待

+
+
+
+
+ ) +} + async function UserCenter() { const resp = await getProfile() @@ -160,21 +187,66 @@ async function UserCenter() {

快捷入口

- - - + + {`bill + 我的帐单 + + + {`buy + 购买产品 + + + {`logout + 个人中心 +
) } + +async function Announcements() { + + const resp = await listAnnouncements({ + page: 1, + size: 5, + }) + + if (!resp.success) { + return ( +
+ 获取公告数据失败 +
+ ) + } + const announcements = resp.data.list + + return ( + + + 公告 + + + {announcements.length === 0 + ? ( +
+ {`coming +

暂无公告

+
+ ) + : announcements.map(item => ( +
+

{item.title}

+

{format(item.created_at, 'yyyy-MM-dd HH:mm')}

+
+ )) + } +
+
+ ) +} \ No newline at end of file diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx index a976fa6..ed68ce8 100644 --- a/src/components/ui/card.tsx +++ b/src/components/ui/card.tsx @@ -69,7 +69,7 @@ function CardContent({className, ...props}: React.ComponentProps<'div'>) { return (
) diff --git a/src/components/ui/chart.tsx b/src/components/ui/chart.tsx new file mode 100644 index 0000000..6a045f3 --- /dev/null +++ b/src/components/ui/chart.tsx @@ -0,0 +1,353 @@ +"use client" + +import * as React from "react" +import * as RechartsPrimitive from "recharts" + +import { merge } from "@/lib/utils" + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: "", dark: ".dark" } as const + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode + icon?: React.ComponentType + } & ( + | { color?: string, theme?: never } + | { color?: never, theme: Record } + ) +} + +type ChartContextProps = { + config: ChartConfig +} + +const ChartContext = React.createContext(null) + +function useChart() { + const context = React.useContext(ChartContext) + + if (!context) { + throw new Error("useChart must be used within a ") + } + + return context +} + +function ChartContainer({ + id, + className, + children, + config, + ...props +}: React.ComponentProps<"div"> & { + config: ChartConfig + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >["children"] +}) { + const uniqueId = React.useId() + const chartId = `chart-${id || uniqueId.replace(/:/g, "")}` + + return ( + +
+ + + {children} + +
+
+ ) +} + +const ChartStyle = ({ id, config }: { id: string, config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([, config]) => config.theme || config.color + ) + + if (!colorConfig.length) { + return null + } + + return ( +