This commit is contained in:
oneao committed 2026-01-29 22:54:15 +08:00
1 parent 295b3306a3
commit c62c9d080b
8 files changed
+878 -136

No files matched your search

+1
View File
@@ -58,6 +58,7 @@
"react-native-web": "^0.21.0",
"react-native-worklets": "0.5.1",
"tailwind-merge": "^3.4.0",
"victory-native": "^37.3.6",
"zustand": "^5.0.8"
},
"devDependencies": {
+615 -8
View File
@@ -152,6 +152,9 @@ importers:
tailwind-merge:
specifier: ^3.4.0
version: 3.4.0
victory-native:
specifier: ^37.3.6
version: 37.3.6(react@19.1.0)
zustand:
specifier: ^5.0.8
version: 5.0.8(@types/react@19.1.17)(react@19.1.0)(use-sync-external-store@1.6.0(react@19.1.0))
@@ -2205,6 +2208,33 @@ packages:
'@types/big.js@6.2.2':
resolution: {integrity: sha512-e2cOW9YlVzFY2iScnGBBkplKsrn2CsObHQ2Hiw4V1sSyiGbgWL8IyqE3zFi1Pt5o1pdAtYkDAIsF3KKUPjdzaA==}
'@types/d3-array@3.2.2':
resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
'@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.8':
resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==}
'@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/debug@4.1.12':
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
@@ -2851,6 +2881,53 @@ packages:
csstype@3.2.0:
resolution: {integrity: sha512-si++xzRAY9iPp60roQiFta7OFbhrgvcthrhlNAGeQptSY25uJjkfUV8OArC3KLocB8JT8ohz+qgxWCmz8RhjIg==}
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.2:
resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==}
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'}
d3-voronoi@1.1.4:
resolution: {integrity: sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==}
dayjs@1.11.19:
resolution: {integrity: sha512-t5EcLVS6QPBNqM2z8fakk/NKel+Xzshgt8FFKAn+qwlD1pzZWxh0nVCrvFK7ZDb6XucZeF9z8C7CBWTRIVApAw==}
@@ -2919,6 +2996,12 @@ packages:
resolution: {integrity: sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==}
engines: {node: '>=8'}
delaunator@4.0.1:
resolution: {integrity: sha512-WNPWi1IRKZfCt/qIDMfERkDp93+iZEmOxN2yy4Jg+Xhv8SLk2UTqqbe1sfiipn0and9QrE914/ihdx82Y/Giag==}
delaunay-find@0.0.6:
resolution: {integrity: sha512-1+almjfrnR7ZamBk0q3Nhg6lqSe6Le4vL0WJDSMx4IDbQwTpUTXPjxC00lqLBT8MYsJpPCbI16sIkw9cPsbi7Q==}
delayed-stream@1.0.0:
resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==}
engines: {node: '>=0.4.0'}
@@ -3778,6 +3861,10 @@ packages:
inline-style-prefixer@7.0.1:
resolution: {integrity: sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==}
internmap@2.0.3:
resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==}
engines: {node: '>=12'}
intl-messageformat@10.7.18:
resolution: {integrity: sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g==}
@@ -3934,6 +4021,9 @@ packages:
json-stable-stringify-without-jsonify@1.0.1:
resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
json-stringify-safe@5.0.1:
resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==}
json5@2.2.3:
resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
engines: {node: '>=6'}
@@ -4027,56 +4117,48 @@ packages:
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-gnu@1.30.2:
resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [glibc]
lightningcss-linux-arm64-musl@1.27.0:
resolution: {integrity: sha512-rCGBm2ax7kQ9pBSeITfCW9XSVF69VX+fm5DIpvDZQl4NnQoMQyRwhZQm9pd59m8leZ1IesRqWk2v/DntMo26lg==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-arm64-musl@1.30.2:
resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==}
engines: {node: '>= 12.0.0'}
cpu: [arm64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-gnu@1.27.0:
resolution: {integrity: sha512-Dk/jovSI7qqhJDiUibvaikNKI2x6kWPN79AQiD/E/KeQWMjdGe9kw51RAgoWFDi0coP4jinaH14Nrt/J8z3U4A==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-gnu@1.30.2:
resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [glibc]
lightningcss-linux-x64-musl@1.27.0:
resolution: {integrity: sha512-QKjTxXm8A9s6v9Tg3Fk0gscCQA1t/HMoF7Woy1u68wCk5kS4fR+q3vXa1p3++REW784cRAtkYKrPy6JKibrEZA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-linux-x64-musl@1.30.2:
resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==}
engines: {node: '>= 12.0.0'}
cpu: [x64]
os: [linux]
libc: [musl]
lightningcss-win32-arm64-msvc@1.27.0:
resolution: {integrity: sha512-/wXegPS1hnhkeG4OXQKEMQeJd48RDC3qdh+OA8pCuOPCyvnm/yEayrJdJVqzBsqpy1aJklRCVxscpFur80o6iQ==}
@@ -5680,6 +5762,182 @@ packages:
react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc
victory-area@37.3.6:
resolution: {integrity: sha512-wVC8LKrZJLiSySNuJLRCB449qZTsPiRyzLlNoJwe21y+XA/a2HJbmJSeywmo8P153aX8viKe1H8ygDsTFXQhHw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-axis@37.3.6:
resolution: {integrity: sha512-Vi0dZvgmXmnCdoqc49WckeG5cMXnl7FTtqVhXu9JweA9cgCnkZabBd5mRvAjblb3Lo4j0HZCSPKHYWUPW70qZg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-bar@37.3.6:
resolution: {integrity: sha512-jdATFRWL1LUW/yEpKWx/aId2BiU2o1pPF9+Kh1TFISBduJoI4ZqvZD90H1QK4f/z50PikqiqiDECaKoKM1jfOQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-box-plot@37.3.6:
resolution: {integrity: sha512-GOucnD63h14ScBuISC/nd1GBTEx6gIZfLE+0P0gyeH1poBKq0trTTvpQDvAMuGR8zICfEETG3ltmUMCwRrFyUg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-brush-container@37.3.6:
resolution: {integrity: sha512-LfZ2CgX1cYAqCtYxcSB68OfZS2v0T2VLXoEArd0lCXfRBY1Gya7GacCUcuo7GoK9XOXeslx7S/U95aVutt1VLg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-brush-line@37.3.6:
resolution: {integrity: sha512-zsZJfF1fUj4F7mUoIMV+h73qoTClPA4bKM1terlYrDBD8l/c/f0KBbEotu3E1X+n4QMmDRruswaB/YUdqK5QLA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-candlestick@37.3.6:
resolution: {integrity: sha512-h/mOmkCrsWrirn4dFnpLxJPXpxT+uHxuYxnXGrAyH+YUOrVj3iKaDJlEiVlz5vy30syE5j5hzTQCMsZ/hzHNdg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-canvas@37.3.6:
resolution: {integrity: sha512-1CD4S0uZ92sUGGSIEQferEfSqd/z9EXw9G6zkzPIoJeTKFshpfqCjUkNRx9Iu9Upxt3fUpId8Qwl1YfchmbrFg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-chart@37.3.6:
resolution: {integrity: sha512-IkPo/W4AJ7bPu902TGER09OseR9ODm+FQAKfOBw4JsdEhZZ7BiG9zgd/25+x0r5EsTLu81CYGQVkBa+ZazcOlA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-core@37.3.6:
resolution: {integrity: sha512-aFgO6KokxPbUCPznZP5UPhOdI22pMuwDXKDt6eoQOnkVim66Ia+K95TQar2nwVKGYV5j26aKVf/n9blwphGJRw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-create-container@37.3.6:
resolution: {integrity: sha512-Uf5bFQvqUsXCjqpvBW4LhrdrHkM6dBqxYgub6FCsBb86f84xZQ3vY7jFkg/JfvF0oGKMoWXYYrYLC1sk+fcWVA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-cursor-container@37.3.6:
resolution: {integrity: sha512-+Oiw57d5nE+iq8As8RvepknzmNtKq1Gsc50u1X3IRd4jXtX8zqZrgXGlVZ+BP/tkLsWnGYVjKulwKBf2oaEUuw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-errorbar@37.3.6:
resolution: {integrity: sha512-WGAv/qizOlfmwKv+Yfxr4q6pDgTfloNQwi3Z3M0h8povjMZt74tHYkvi/TASSRYr3zv5kjUqUJ28qAyGMWwryQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-group@37.3.6:
resolution: {integrity: sha512-kgy/Azl5BxwlJAV0KDPGypv35TMrOD1J2ZxnJW2Wyyq+e8i0GGBIv5MoBzou64BRsDlS9V0CYRIjnkHgrBpB5w==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-histogram@37.3.6:
resolution: {integrity: sha512-K4d43MpXHYnGCLEMzfRpJ+lCRRDKALPi/juxfMGVzBPzSMgjC8h9x6hKdxaejiTd/E04UdzNO7J24plL3Uz8rA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-legend@37.3.6:
resolution: {integrity: sha512-vRRrhj3/ENqKVLdaBMzEmR83N6BOjox1bthYT1eJjN2H5SIK35bxn30IkiV/Pz3y627EqZe4TAWaxc0jiJlCiA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-line@37.3.6:
resolution: {integrity: sha512-Ke817uf/qFbN9jU7Dba7CrcHXYO5wAZuKKnyeHJmLDeQeFST0773xejnIuC+dBgZipjFr4KIbSd+VcUafFNE1g==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-native@37.3.6:
resolution: {integrity: sha512-3m7kg4MtI4ZCzYuz4NT2N6AXiBMRwC3HiDoF5RhE8kaTeLd7iCi2YNi8y6F3FIU9gRrLiXWkyky054O3iZXVjw==}
peerDependencies:
react: '>=16.6.0'
victory-pie@37.3.6:
resolution: {integrity: sha512-tvdgAZ/HQWlo3KDDe0XAVbizHuaNMbgkkiF7zfA7Ww+3bHSs+0P9dsDtK2xP365D8gBCOv8pWmuzvKRhzNbqeA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-polar-axis@37.3.6:
resolution: {integrity: sha512-RpFsCkzHezJq5P+C/wtVdjEHX25JIFsSgs6qYSnfr/hayaFbWgK5HhRFpriQm5hg61cx47WxAOLyHvzf0nasvw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-scatter@37.3.6:
resolution: {integrity: sha512-fp95zMTPXgW1cmTowzDXhn+KxePMVDrzU0lotsHQMdBV7eB+ioXdu9hORlx4VHmMYg2ihsGwRTF+VAZ7rGxphA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-selection-container@37.3.6:
resolution: {integrity: sha512-gd3qODDlBtLEJM7+2jCXk2YcLBUmIpYEEHswytMhwc6zihxXipGBUHRulhLj/I05mKay2gaOAg5ewiJHd4Awgw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-shared-events@37.3.6:
resolution: {integrity: sha512-ygrbOtzLUTbtKebacZKyQRekhSAROnAvMkVI/PKsAGsz0ClY9P7qDEJG7eTUUygjO6ax0tI6WNE6JogQzeD1gw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-stack@37.3.6:
resolution: {integrity: sha512-ldod04RdqGJGH5p5eWXCofdTkbhZqIp3iwW7NpxSbMDLs8zPQIVvDFVtuJgMwQiC5vnIpbhMmxVeFbr8m64ZKA==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-tooltip@37.3.6:
resolution: {integrity: sha512-vqaJS9noauOqDDBBAV9Ln9duOY/i17h1DCfCPAqhwPFyvFbwKvAub9zPTeYWAm/14VvWX5O/0yekFCVbcC7hjg==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-vendor@37.3.6:
resolution: {integrity: sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==}
victory-voronoi-container@37.3.6:
resolution: {integrity: sha512-qAAG0rMuK7A4EoJ4cyUk5wNdOW+HuCXNKPOko+hYK6wWOYXJvFhiglYyA85a695YyAXECc6JyJS/crm4IOEFag==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-voronoi@37.3.6:
resolution: {integrity: sha512-Q+1FWHp8IAbmDL9pGWS0y0N4Cb5qmD9OOgxoxCfIDsLlhGvd6LddhRoknWsN7WnreaK+XiwjSfQkdMTCZ4hdhQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory-zoom-container@37.3.6:
resolution: {integrity: sha512-AGL+k20mI44OL5b0VgIxlmnNSefIoFmbbim5NraPmIxbtns9qQW/56ivIncJcYomBungIx99gUpsEpcQaMNHgQ==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
victory@37.3.6:
resolution: {integrity: sha512-CZ1vjvra0R1U3T2dMI4EsjI8Ng+JmQ2ox/EweSzjkTnHfW/Vn5ylryadawDiYjDMcBvABjO3uODsIlSEm4d/Sw==}
engines: {node: '>=18.0.0'}
peerDependencies:
react: '>=16.6.0'
vlq@1.0.1:
resolution: {integrity: sha512-gQpnTgkubC6hQgdIcRdYGDSDc+SaujOdyesZQMv6JlfQee/9Mp0Qhnys6WxDWvQnL5WZdT7o2Ul187aSt0Rq+w==}
@@ -8724,6 +8982,30 @@ snapshots:
'@types/big.js@6.2.2': {}
'@types/d3-array@3.2.2': {}
'@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.8':
dependencies:
'@types/d3-path': 3.1.1
'@types/d3-time@3.0.4': {}
'@types/d3-timer@3.0.2': {}
'@types/debug@4.1.12':
dependencies:
'@types/ms': 2.1.0
@@ -9482,6 +9764,46 @@ snapshots:
csstype@3.2.0: {}
d3-array@3.2.4:
dependencies:
internmap: 2.0.3
d3-color@3.1.0: {}
d3-ease@3.0.1: {}
d3-format@3.1.2: {}
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.2
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: {}
d3-voronoi@1.1.4: {}
dayjs@1.11.19: {}
debug@2.6.9:
@@ -9522,6 +9844,12 @@ snapshots:
define-lazy-prop@2.0.0: {}
delaunator@4.0.1: {}
delaunay-find@0.0.6:
dependencies:
delaunator: 4.0.1
delayed-stream@1.0.0: {}
depd@2.0.0: {}
@@ -10465,6 +10793,8 @@ snapshots:
dependencies:
css-in-js-utils: 3.1.0
internmap@2.0.3: {}
intl-messageformat@10.7.18:
dependencies:
'@formatjs/ecma402-abstract': 2.3.6
@@ -10637,6 +10967,8 @@ snapshots:
json-stable-stringify-without-jsonify@1.0.1: {}
json-stringify-safe@5.0.1: {}
json5@2.2.3: {}
jsonc-eslint-parser@2.4.1:
@@ -12690,6 +13022,281 @@ snapshots:
- '@types/react'
- '@types/react-dom'
victory-area@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-vendor: 37.3.6
victory-axis@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-bar@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-vendor: 37.3.6
victory-box-plot@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-vendor: 37.3.6
victory-brush-container@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
react-fast-compare: 3.2.2
victory-core: 37.3.6(react@19.1.0)
victory-brush-line@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
react-fast-compare: 3.2.2
victory-core: 37.3.6(react@19.1.0)
victory-candlestick@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-canvas@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-bar: 37.3.6(react@19.1.0)
victory-core: 37.3.6(react@19.1.0)
victory-chart@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
react-fast-compare: 3.2.2
victory-axis: 37.3.6(react@19.1.0)
victory-core: 37.3.6(react@19.1.0)
victory-polar-axis: 37.3.6(react@19.1.0)
victory-shared-events: 37.3.6(react@19.1.0)
victory-core@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
react-fast-compare: 3.2.2
victory-vendor: 37.3.6
victory-create-container@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-brush-container: 37.3.6(react@19.1.0)
victory-core: 37.3.6(react@19.1.0)
victory-cursor-container: 37.3.6(react@19.1.0)
victory-selection-container: 37.3.6(react@19.1.0)
victory-voronoi-container: 37.3.6(react@19.1.0)
victory-zoom-container: 37.3.6(react@19.1.0)
victory-cursor-container@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-errorbar@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-group@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
react-fast-compare: 3.2.2
victory-core: 37.3.6(react@19.1.0)
victory-shared-events: 37.3.6(react@19.1.0)
victory-histogram@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
react-fast-compare: 3.2.2
victory-bar: 37.3.6(react@19.1.0)
victory-core: 37.3.6(react@19.1.0)
victory-vendor: 37.3.6
victory-legend@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-line@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-vendor: 37.3.6
victory-native@37.3.6(react@19.1.0):
dependencies:
hoist-non-react-statics: 3.3.2
lodash: 4.17.21
react: 19.1.0
react-fast-compare: 3.2.2
victory: 37.3.6(react@19.1.0)
victory-area: 37.3.6(react@19.1.0)
victory-axis: 37.3.6(react@19.1.0)
victory-bar: 37.3.6(react@19.1.0)
victory-box-plot: 37.3.6(react@19.1.0)
victory-brush-container: 37.3.6(react@19.1.0)
victory-brush-line: 37.3.6(react@19.1.0)
victory-candlestick: 37.3.6(react@19.1.0)
victory-chart: 37.3.6(react@19.1.0)
victory-core: 37.3.6(react@19.1.0)
victory-create-container: 37.3.6(react@19.1.0)
victory-cursor-container: 37.3.6(react@19.1.0)
victory-errorbar: 37.3.6(react@19.1.0)
victory-group: 37.3.6(react@19.1.0)
victory-histogram: 37.3.6(react@19.1.0)
victory-legend: 37.3.6(react@19.1.0)
victory-line: 37.3.6(react@19.1.0)
victory-pie: 37.3.6(react@19.1.0)
victory-polar-axis: 37.3.6(react@19.1.0)
victory-scatter: 37.3.6(react@19.1.0)
victory-selection-container: 37.3.6(react@19.1.0)
victory-shared-events: 37.3.6(react@19.1.0)
victory-stack: 37.3.6(react@19.1.0)
victory-tooltip: 37.3.6(react@19.1.0)
victory-voronoi: 37.3.6(react@19.1.0)
victory-voronoi-container: 37.3.6(react@19.1.0)
victory-zoom-container: 37.3.6(react@19.1.0)
victory-pie@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-vendor: 37.3.6
victory-polar-axis@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-scatter@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-selection-container@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-shared-events@37.3.6(react@19.1.0):
dependencies:
json-stringify-safe: 5.0.1
lodash: 4.17.21
react: 19.1.0
react-fast-compare: 3.2.2
victory-core: 37.3.6(react@19.1.0)
victory-stack@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
react-fast-compare: 3.2.2
victory-core: 37.3.6(react@19.1.0)
victory-shared-events: 37.3.6(react@19.1.0)
victory-tooltip@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-vendor@37.3.6:
dependencies:
'@types/d3-array': 3.2.2
'@types/d3-ease': 3.0.2
'@types/d3-interpolate': 3.0.4
'@types/d3-scale': 4.0.9
'@types/d3-shape': 3.1.8
'@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
victory-voronoi-container@37.3.6(react@19.1.0):
dependencies:
delaunay-find: 0.0.6
lodash: 4.17.21
react: 19.1.0
react-fast-compare: 3.2.2
victory-core: 37.3.6(react@19.1.0)
victory-tooltip: 37.3.6(react@19.1.0)
victory-voronoi@37.3.6(react@19.1.0):
dependencies:
d3-voronoi: 1.1.4
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory-zoom-container@37.3.6(react@19.1.0):
dependencies:
lodash: 4.17.21
react: 19.1.0
victory-core: 37.3.6(react@19.1.0)
victory@37.3.6(react@19.1.0):
dependencies:
react: 19.1.0
victory-area: 37.3.6(react@19.1.0)
victory-axis: 37.3.6(react@19.1.0)
victory-bar: 37.3.6(react@19.1.0)
victory-box-plot: 37.3.6(react@19.1.0)
victory-brush-container: 37.3.6(react@19.1.0)
victory-brush-line: 37.3.6(react@19.1.0)
victory-candlestick: 37.3.6(react@19.1.0)
victory-canvas: 37.3.6(react@19.1.0)
victory-chart: 37.3.6(react@19.1.0)
victory-core: 37.3.6(react@19.1.0)
victory-create-container: 37.3.6(react@19.1.0)
victory-cursor-container: 37.3.6(react@19.1.0)
victory-errorbar: 37.3.6(react@19.1.0)
victory-group: 37.3.6(react@19.1.0)
victory-histogram: 37.3.6(react@19.1.0)
victory-legend: 37.3.6(react@19.1.0)
victory-line: 37.3.6(react@19.1.0)
victory-pie: 37.3.6(react@19.1.0)
victory-polar-axis: 37.3.6(react@19.1.0)
victory-scatter: 37.3.6(react@19.1.0)
victory-selection-container: 37.3.6(react@19.1.0)
victory-shared-events: 37.3.6(react@19.1.0)
victory-stack: 37.3.6(react@19.1.0)
victory-tooltip: 37.3.6(react@19.1.0)
victory-voronoi: 37.3.6(react@19.1.0)
victory-voronoi-container: 37.3.6(react@19.1.0)
victory-zoom-container: 37.3.6(react@19.1.0)
vlq@1.0.1: {}
vue-eslint-parser@10.2.0(eslint@9.39.1(jiti@1.21.7)):
+21 -40
View File
@@ -1,17 +1,6 @@
import { DateRangePicker } from '@yz1311/react-native-wheel-picker'
import React from 'react'
import { Button, ButtonText } from '@/components/ui/button'
import {
Drawer,
DrawerBackdrop,
DrawerBody,
DrawerCloseButton,
DrawerContent,
DrawerFooter,
DrawerHeader,
} from '@/components/ui/drawer'
import { Heading } from '@/components/ui/heading'
import { CloseIcon, Icon } from '@/components/ui/icon'
import { Text } from '@/components/ui/text'
export default function Example() {
const [showDrawer, setShowDrawer] = React.useState(false)
@@ -21,39 +10,31 @@ export default function Example() {
onPress={() => {
setShowDrawer(true)
}}
className="mt-10"
>
<ButtonText>Open Drawer</ButtonText>
</Button>
<Drawer
isOpen={showDrawer}
anchor="bottom"
size="lg"
onClose={() => {
<DateRangePicker
pickerTitle="预约时间"
pickerCancelBtnText="取消"
pickerConfirmBtnText="确认"
// 开启modal模式
isModal={true}
// 可以自定义modal的一些属性
modalProps={{
}}
modalVisible={showDrawer}
// modalVisible回调函数
onModalVisibleChange={(visible) => {
}}
onPickerCancel={() => {
}}
onPickerConfirm={(date) => {
setShowDrawer(false)
}}
>
<DrawerBackdrop />
<DrawerContent>
<DrawerHeader>
<Text>取消</Text>
<Text>分类设置</Text>
<Text>确认</Text>
</DrawerHeader>
<DrawerBody>
<Text>This is the basic drawer component.</Text>
</DrawerBody>
<DrawerFooter>
<Button
variant="outline"
onPress={() => {
setShowDrawer(false)
}}
>
<ButtonText>Cancel</ButtonText>
</Button>
</DrawerFooter>
</DrawerContent>
</Drawer>
/>
</>
)
}
@@ -1,5 +1,5 @@
import { useFocusEffect, useRouter } from 'expo-router'
import React, { useCallback, useEffect, useState } from 'react'
import React, { useCallback, useState } from 'react'
import { Image, Text, TouchableOpacity, View } from 'react-native'
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
import { ADatePicker, ADrawer, AListItem, LucideIcon } from '@/components/common/ao'
@@ -84,7 +84,6 @@ export default function Money() {
>
<LucideIcon icon="Menu" size={24} clickable={false} />
</TouchableOpacity>
</View>
<View
@@ -208,6 +207,7 @@ export default function Money() {
text="类别设置"
onPress={() => {
router.navigate('/money/category/setting')
setShowActionsMenu(false)
}}
/>
</View>
@@ -217,7 +217,14 @@ export default function Money() {
<Text className="text-gray-600 font-semibold text-sm">统计分析</Text>
<View className="p-1">
<AListItem icon="ChartSpline" text="图表分析" />
<AListItem
icon="ChartSpline"
text="图表分析"
onPress={() => {
router.navigate('/money/analysis')
setShowActionsMenu(false)
}}
/>
</View>
</View>
</SafeAreaView>
@@ -0,0 +1,126 @@
import dayjs from 'dayjs'
import { useState } from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import { VictoryChart, VictoryLine, VictoryPie, VictoryTheme, VictoryTooltip, VictoryVoronoiContainer } from 'victory-native'
import { ADatePicker, ASegmentedTabs, LucideIcon } from '@/components/common/ao'
import CustomHeader from '@/components/custom/Header'
import PagerTabs from '@/components/custom/PagerTabs'
type GroupBy = 'day' | 'month' | 'year'
type AnalysisType = 'expense' | 'income'
interface AnalysisContentProps {
analysisType: AnalysisType
}
export default function Analysis() {
function AnalysisContent({ analysisType }: AnalysisContentProps) {
const [timeType, setTimeType] = useState<number>(0)
const [groupBy, setGroupBy] = useState<GroupBy>('day')
const [startDate, setStartDate] = useState(
dayjs().format('YYYY-MM'),
)
const [endDate, setEndDate] = useState(
dayjs().format('YYYY-MM'),
)
const [showDatePicker, setShowDatePicker] = useState(false)
return (
<View className="p-4">
<ASegmentedTabs
value={timeType}
onChange={(index) => {
setTimeType(index)
}}
tabs={[
{ key: '1', label: '按月' },
{ key: '2', label: '按年' },
{ key: '3', label: '自定义' },
]}
/>
<TouchableOpacity
className="mt-14 flex flex-row justify-center items-center"
>
<LucideIcon icon="Calendar" size={16} color="#666" clickable={false} />
<Text className="font-semibold text-lg mr-2 ml-1">{startDate}</Text>
{timeType === 2 && (
<>
<Text> - </Text>
<Text className="font-semibold text-lg mx-1">
{endDate}
</Text>
</>
)}
<LucideIcon icon="ChevronDown" size={16} color="#666" clickable={false} />
</TouchableOpacity>
<View className="mt-4">
<Text className="text-lg font-semibold text-gray-700">趋势分析</Text>
<VictoryChart
height={220}
theme={VictoryTheme.material}
padding={{ top: 15, bottom: 30, left: 40, right: 40 }}
>
<VictoryLine
interpolation="catmullRom"
data={[
]}
/>
</VictoryChart>
</View>
<View className="mt-4">
<Text className="text-lg font-semibold text-gray-700">分类构成</Text>
<VictoryPie
height={250}
innerRadius={50}
data={[
]}
theme={VictoryTheme.material}
/>
</View>
<ADatePicker
show={showDatePicker}
mode="month"
onConfirm={(val) => {
setShowDatePicker(false)
}}
onClose={() => {
setShowDatePicker(false)
}}
/>
</View>
)
}
return (
<View className="flex-1">
<CustomHeader
showHeader={false}
/>
<PagerTabs
showCustomHeader={true}
routes={[
{
key: 'expense',
title: '支出',
element: <AnalysisContent analysisType="expense" />,
},
{
key: 'income',
title: '收入',
element: <AnalysisContent analysisType="income" />,
},
]}
/>
</View>
)
}
@@ -1,7 +1,10 @@
import type { ViewStyle } from 'react-native'
import { useRouter } from 'expo-router'
import { useEffect, useRef, useState } from 'react'
import { Animated, Text, TouchableOpacity, View } from 'react-native'
import { Animated, Image, Text, TouchableOpacity, View } from 'react-native'
import PagerView from 'react-native-pager-view'
import { LucideIcon } from '../common/ao'
export interface PagerRoute {
key: string
@@ -16,6 +19,7 @@ interface PagerTabsProps {
selectedIndex?: number // 外部控制选中页
onChange?: (key: string) => void
indicatorWidth?: number
showCustomHeader?: boolean
className?: string
style?: ViewStyle
@@ -28,9 +32,12 @@ export default function PagerTabs({
selectedIndex,
onChange,
indicatorWidth = 38,
showCustomHeader,
className,
style,
}: PagerTabsProps) {
const router = useRouter()
const pagerRef = useRef<PagerView>(null)
const [index, setIndex] = useState(selectedIndex ?? 0)
const [tabWidth, setTabWidth] = useState(0)
@@ -80,7 +87,15 @@ export default function PagerTabs({
>
{/* Header */}
<View className="flex flex-row items-center">
{left}
{left || (showCustomHeader
? (
<View className="w-10 items-center justify-center">
<TouchableOpacity onPress={() => router.back()}>
<LucideIcon icon="ChevronLeft" size={28} clickable={false} />
</TouchableOpacity>
</View>
)
: null)}
{/* Tabs */}
<View
@@ -116,7 +131,16 @@ export default function PagerTabs({
)}
</View>
{right}
{right || (showCustomHeader
? (
<View className="w-10 items-center justify-center">
<Image
source={require('@/static/images/logo.png')}
style={{ width: 22, height: 22 }}
/>
</View>
)
: null)}
</View>
{/* Pager */}
+2 -2
View File
@@ -28,8 +28,8 @@ function isWhiteListed(url: string) {
}
const alovaInstance = createAlova({
// baseURL: 'http://192.168.5.6:8086/app',
baseURL: 'http://192.168.1.137:8086/app',
baseURL: 'http://192.168.5.6:8086/app',
// baseURL: 'http://192.168.1.137:8086/app',
requestAdapter: axiosRequestAdapter(),
timeout: 5000,
cacheFor: null,
+76 -80
View File
@@ -1,91 +1,87 @@
<!DOCTYPE html>
<html lang="en">
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>全局底部导航 Demo</title>
<style>
body {
margin: 0;
font-family: sans-serif;
background-color: #f0f2f5;
display: flex;
justify-content: center;
align-items: flex-end;
height: 100vh;
}
/* 弹出面板 */
.bottom-panel {
width: 100%;
max-width: 400px;
background: white;
border-top-left-radius: 20px;
border-top-right-radius: 20px;
padding: 16px;
box-shadow: 0 -4px 12px rgba(0, 0, 0, 0.1);
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 12px;
}
/* 每个导航 item */
.nav-item {
background: #f9f9f9;
border-radius: 16px;
padding: 16px 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
cursor: pointer;
transition: transform 0.2s, background 0.2s;
text-align: center;
}
.nav-item:hover {
background: #e0e0ff;
transform: translateY(-3px) scale(1.05);
}
.nav-icon {
font-size: 28px;
margin-bottom: 6px;
}
.nav-name {
font-size: 14px;
font-weight: 600;
}
</style>
<title>财务分析 Demo</title>
<script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 p-4">
<body>
<!-- Header: 收支切换 -->
<div class="flex justify-center gap-4 mb-4">
<button id="expenseBtn" class="px-4 py-2 rounded-full bg-blue-500 text-white font-semibold">支出</button>
<button id="incomeBtn" class="px-4 py-2 rounded-full bg-gray-200 text-gray-700 font-semibold">收入</button>
</div>
<div class="bottom-panel">
<div class="nav-item" onclick="alert('跳转到首页')">
<div class="nav-icon">🏠</div>
<div class="nav-name">首页</div>
</div>
<div class="nav-item" onclick="alert('跳转到记账')">
<div class="nav-icon">📝</div>
<div class="nav-name">记账</div>
</div>
<div class="nav-item" onclick="alert('跳转到分类')">
<div class="nav-icon">📂</div>
<div class="nav-name">分类</div>
</div>
<div class="nav-item" onclick="alert('跳转到分析')">
<div class="nav-icon">📊</div>
<div class="nav-name">分析</div>
</div>
<div class="nav-item" onclick="alert('跳转到设置')">
<div class="nav-icon">⚙️</div>
<div class="nav-name">设置</div>
<!-- 时间维度选择 -->
<div class="flex justify-center gap-2 mb-4">
<button class="timeBtn px-3 py-1 rounded-full bg-blue-500 text-white font-medium" data-type="month">按月</button>
<button class="timeBtn px-3 py-1 rounded-full bg-gray-200 text-gray-700 font-medium" data-type="year">按年</button>
<button class="timeBtn px-3 py-1 rounded-full bg-gray-200 text-gray-700 font-medium" data-type="custom">自定义</button>
</div>
<!-- 日期选择 -->
<div id="datePickerContainer" class="flex justify-center mb-4">
<!-- 默认显示按月日期选择 -->
<input type="month" id="monthInput" class="p-2 rounded border border-gray-300">
<input type="month" id="yearInput" class="p-2 rounded border border-gray-300 hidden">
<div id="customInput" class="flex gap-2 hidden">
<input type="date" id="startDate" class="p-2 rounded border border-gray-300">
<span class="self-center">~</span>
<input type="date" id="endDate" class="p-2 rounded border border-gray-300">
</div>
</div>
</body>
<!-- 示例图表区域 -->
<div class="bg-white p-4 rounded shadow text-center">
<p class="text-gray-500">这里显示图表或分析结果</p>
</div>
</html>
<script>
const expenseBtn = document.getElementById('expenseBtn')
const incomeBtn = document.getElementById('incomeBtn')
const timeBtns = document.querySelectorAll('.timeBtn')
const monthInput = document.getElementById('monthInput')
const yearInput = document.getElementById('yearInput')
const customInput = document.getElementById('customInput')
// 收支切换逻辑
expenseBtn.addEventListener('click', () => {
expenseBtn.classList.add('bg-blue-500', 'text-white')
expenseBtn.classList.remove('bg-gray-200', 'text-gray-700')
incomeBtn.classList.add('bg-gray-200', 'text-gray-700')
incomeBtn.classList.remove('bg-blue-500', 'text-white')
})
incomeBtn.addEventListener('click', () => {
incomeBtn.classList.add('bg-blue-500', 'text-white')
incomeBtn.classList.remove('bg-gray-200', 'text-gray-700')
expenseBtn.classList.add('bg-gray-200', 'text-gray-700')
expenseBtn.classList.remove('bg-blue-500', 'text-white')
})
// 时间维度切换逻辑
timeBtns.forEach(btn => {
btn.addEventListener('click', () => {
timeBtns.forEach(b => {
b.classList.remove('bg-blue-500', 'text-white')
b.classList.add('bg-gray-200', 'text-gray-700')
})
btn.classList.add('bg-blue-500', 'text-white')
btn.classList.remove('bg-gray-200', 'text-gray-700')
// 显示对应日期选择控件
const type = btn.dataset.type
monthInput.classList.add('hidden')
yearInput.classList.add('hidden')
customInput.classList.add('hidden')
if (type === 'month') monthInput.classList.remove('hidden')
if (type === 'year') yearInput.classList.remove('hidden')
if (type === 'custom') customInput.classList.remove('hidden')
})
})
</script>
</body>
</html>