From fbc6478496333cca45f9275cda03f64d808b03ac Mon Sep 17 00:00:00 2001 From: luorijun Date: Wed, 7 May 2025 16:48:51 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E6=A6=82=E8=A7=88=E9=A1=B5?= =?UTF-8?q?=EF=BC=8C=E5=AE=9E=E7=8E=B0=E5=85=AC=E5=91=8A=E6=9F=A5=E8=AF=A2?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=EF=BC=8C=E5=BC=95=E5=85=A5=20recharts=20?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=8F=96=E7=94=A8=E6=95=B0=E6=8D=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .npmrc | 2 + README.md | 1 + eslint.config.mjs | 1 + package.json | 10 +- pnpm-lock.yaml | 350 ++++++++++++----- src/actions/announcement.ts | 19 + .../(dashboard)/_assets/action-bill.webp | Bin 0 -> 6452 bytes .../admin/(dashboard)/_assets/action-buy.webp | Bin 0 -> 6422 bytes .../(dashboard)/_assets/action-logout.webp | Bin 0 -> 6490 bytes src/app/admin/(dashboard)/_client/chart.tsx | 35 ++ src/app/admin/(dashboard)/page.tsx | 238 ++++++++---- src/components/ui/card.tsx | 2 +- src/components/ui/chart.tsx | 353 ++++++++++++++++++ src/lib/models.ts | 12 + 14 files changed, 850 insertions(+), 173 deletions(-) create mode 100644 .npmrc create mode 100644 src/actions/announcement.ts create mode 100644 src/app/admin/(dashboard)/_assets/action-bill.webp create mode 100644 src/app/admin/(dashboard)/_assets/action-buy.webp create mode 100644 src/app/admin/(dashboard)/_assets/action-logout.webp create mode 100644 src/app/admin/(dashboard)/_client/chart.tsx create mode 100644 src/components/ui/chart.tsx 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 0000000000000000000000000000000000000000..f2edd071c5b4cf3308e05a4fbe1651782da4e2a8 GIT binary patch literal 6452 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%0AG%USb^j)^&?a2$wHqYyeN+n%ps(X7@6&mV2 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 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..46ed2bf0065f87dac7078782348534c3aa007d6e GIT binary patch literal 6490 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 literal 0 HcmV?d00001 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 ( +