This commit is contained in:
oneao committed 2025-11-15 17:31:36 +08:00
1 parent 33113f279c
commit c2c5cc92af
16 files changed
+864 -352

No files matched your search

+3 -2
View File
@@ -16,6 +16,7 @@
"@gluestack/ui-next-adapter": "^3.0.3",
"@legendapp/motion": "^2.4.0",
"@react-native-async-storage/async-storage": "2.2.0",
"@react-native-community/datetimepicker": "8.4.4",
"@react-native-segmented-control/segmented-control": "2.5.7",
"alova": "^3.3.4",
"axios": "^1.13.2",
@@ -37,14 +38,14 @@
"react-native": "0.81.5",
"react-native-css-interop": "^0.2.1",
"react-native-dates-picker": "^0.2.0",
"react-native-drawer-layout": "^4.2.0",
"react-native-gesture-handler": "~2.28.0",
"react-native-keyboard-aware-scroll-view": "^0.9.5",
"react-native-keyboard-controller": "1.18.5",
"react-native-modal-datetime-picker": "^18.0.0",
"react-native-reanimated": "~4.1.3",
"react-native-safe-area-context": "~5.6.1",
"react-native-screens": "~4.16.0",
"react-native-svg": "15.12.1",
"react-native-ui-datepicker": "^3.1.2",
"react-native-web": "^0.21.0",
"react-native-worklets": "0.5.1",
"zustand": "^5.0.8"
+67 -53
View File
@@ -26,6 +26,9 @@ importers:
'@react-native-async-storage/async-storage':
specifier: 2.2.0
version: 2.2.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))
'@react-native-community/datetimepicker':
specifier: 8.4.4
version: 8.4.4(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
'@react-native-segmented-control/segmented-control':
specifier: 2.5.7
version: 2.5.7(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
@@ -89,15 +92,18 @@ importers:
react-native-dates-picker:
specifier: ^0.2.0
version: 0.2.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
react-native-drawer-layout:
specifier: ^4.2.0
version: 4.2.0(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
react-native-gesture-handler:
specifier: ~2.28.0
version: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
react-native-keyboard-aware-scroll-view:
specifier: ^0.9.5
version: 0.9.5(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))
react-native-keyboard-controller:
specifier: 1.18.5
version: 1.18.5(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
react-native-modal-datetime-picker:
specifier: ^18.0.0
version: 18.0.0(@react-native-community/datetimepicker@8.4.4(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))
react-native-reanimated:
specifier: ~4.1.3
version: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
@@ -110,9 +116,6 @@ importers:
react-native-svg:
specifier: 15.12.1
version: 15.12.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
react-native-ui-datepicker:
specifier: ^3.1.2
version: 3.1.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
react-native-web:
specifier: ^0.21.0
version: 0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
@@ -1606,6 +1609,19 @@ packages:
peerDependencies:
react-native: ^0.0.0-0 || >=0.65 <1.0
'@react-native-community/datetimepicker@8.4.4':
resolution: {integrity: sha512-bc4ZixEHxZC9/qf5gbdYvIJiLZ5CLmEsC3j+Yhe1D1KC/3QhaIfGDVdUcid0PdlSoGOSEq4VlB93AWyetEyBSQ==}
peerDependencies:
expo: '>=52.0.0'
react: '*'
react-native: '*'
react-native-windows: '*'
peerDependenciesMeta:
expo:
optional: true
react-native-windows:
optional: true
'@react-native-segmented-control/segmented-control@2.5.7':
resolution: {integrity: sha512-l84YeVX8xAU3lvOJSvV4nK/NbGhIm2gBfveYolwaoCbRp+/SLXtc6mYrQmM9ScXNwU14mnzjQTpTHWl5YPnkzQ==}
peerDependencies:
@@ -3578,9 +3594,6 @@ packages:
jackspeak@3.4.3:
resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
jalali-plugin-dayjs@1.1.4:
resolution: {integrity: sha512-d62QGMTufGQ1TSov4a85gBU58PA8aL6wVQs9BnC3+d/VA+v4Sq0OqM3o6RoY+/VZjliYJroGIPwlSVf7uLnZ3w==}
jest-environment-node@29.7.0:
resolution: {integrity: sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==}
engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0}
@@ -3757,48 +3770,56 @@ 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==}
@@ -4654,6 +4675,14 @@ packages:
react: '>=17.0.0'
react-native: '*'
react-native-drawer-layout@4.2.0:
resolution: {integrity: sha512-XFGK5RMcVhEqr2F4iH/cTXrcTm1uK3gIBNFPqHNO7rCXuXZTPBbQYA5yc9/Lqgw2KNQ3sAeHKvW6/WoTURo/eA==}
peerDependencies:
react: '>= 18.2.0'
react-native: '*'
react-native-gesture-handler: '>= 2.0.0'
react-native-reanimated: '>= 2.0.0'
react-native-gesture-handler@2.28.0:
resolution: {integrity: sha512-0msfJ1vRxXKVgTgvL+1ZOoYw3/0z1R+Ked0+udoJhyplC2jbVKIJ8Z1bzWdpQRCV3QcQ87Op0zJVE5DhKK2A0A==}
peerDependencies:
@@ -4668,22 +4697,12 @@ packages:
react-native: '*'
react-native-svg: '>=9.7.0'
react-native-iphone-x-helper@1.3.1:
resolution: {integrity: sha512-HOf0jzRnq2/aFUcdCJ9w9JGzN3gdEg0zFE4FyYlp4jtidqU03D5X7ZegGKfT1EWteR0gPBGp9ye5T5FvSWi9Yg==}
peerDependencies:
react-native: '>=0.42.0'
react-native-is-edge-to-edge@1.2.1:
resolution: {integrity: sha512-FLbPWl/MyYQWz+KwqOZsSyj2JmLKglHatd3xLZWskXOpRaio4LfEDEz8E/A6uD8QoTHW6Aobw1jbEwK7KMgR7Q==}
peerDependencies:
react: '*'
react-native: '*'
react-native-keyboard-aware-scroll-view@0.9.5:
resolution: {integrity: sha512-XwfRn+T/qBH9WjTWIBiJD2hPWg0yJvtaEw6RtPCa5/PYHabzBaWxYBOl0usXN/368BL1XktnZPh8C2lmTpOREA==}
peerDependencies:
react-native: '>=0.48.4'
react-native-keyboard-controller@1.18.5:
resolution: {integrity: sha512-wbYN6Tcu3G5a05dhRYBgjgd74KqoYWuUmroLpigRg9cXy5uYo7prTMIvMgvLtARQtUF7BOtFggUnzgoBOgk0TQ==}
peerDependencies:
@@ -4691,6 +4710,12 @@ packages:
react-native: '*'
react-native-reanimated: '>=3.0.0'
react-native-modal-datetime-picker@18.0.0:
resolution: {integrity: sha512-0jdvhhraZQlRACwr7pM6vmZ2kxgzJ4CpnmV6J3TVA6MrXMXK6Zo/upRBKkRp0+fTOiKuNblzesA2U59rYo6SGA==}
peerDependencies:
'@react-native-community/datetimepicker': '>=6.7.0'
react-native: '>=0.65.0'
react-native-reanimated@4.1.5:
resolution: {integrity: sha512-UA6VUbxwhRjEw2gSNrvhkusUq3upfD3Cv+AnB07V+kC8kpvwRVI+ivwY95ePbWNFkFpP+Y2Sdw1WHpHWEV+P2Q==}
peerDependencies:
@@ -4717,13 +4742,6 @@ packages:
react: '*'
react-native: '*'
react-native-ui-datepicker@3.1.2:
resolution: {integrity: sha512-1yukCKMLUuVb5daYOgb4mLZqpynZ6GMQyNF2NPwpiDB5F2D/NGpeOmoYb7qFwDrTNwr+m4IemNUa0TlYIFktlg==}
engines: {node: '>= 18'}
peerDependencies:
react: '*'
react-native: '*'
react-native-web@0.21.2:
resolution: {integrity: sha512-SO2t9/17zM4iEnFvlu2DA9jqNbzNhoUP+AItkoCOyFmDMOhUnBBznBDCYN92fGdfAkfQlWzPoez6+zLxFNsZEg==}
peerDependencies:
@@ -5124,9 +5142,6 @@ packages:
tailwind-merge@1.14.0:
resolution: {integrity: sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==}
tailwind-merge@3.4.0:
resolution: {integrity: sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==}
tailwind-variants@0.1.20:
resolution: {integrity: sha512-AMh7x313t/V+eTySKB0Dal08RHY7ggYK0MSn/ad8wKWOrDUIzyiWNayRUm2PIJ4VRkvRnfNuyRuKbLV3EN+ewQ==}
engines: {node: '>=16.x', pnpm: '>=7.x'}
@@ -7634,6 +7649,14 @@ snapshots:
merge-options: 3.0.4
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
'@react-native-community/datetimepicker@8.4.4(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)':
dependencies:
invariant: 2.2.4
react: 19.1.0
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
optionalDependencies:
expo: 54.0.23(@babel/core@7.28.5)(@expo/metro-runtime@6.1.2)(expo-router@6.0.14)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
'@react-native-segmented-control/segmented-control@2.5.7(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)':
dependencies:
react: 19.1.0
@@ -9953,8 +9976,6 @@ snapshots:
optionalDependencies:
'@pkgjs/parseargs': 0.11.0
jalali-plugin-dayjs@1.1.4: {}
jest-environment-node@29.7.0:
dependencies:
'@jest/environment': 29.7.0
@@ -11413,6 +11434,15 @@ snapshots:
react: 19.1.0
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
react-native-drawer-layout@4.2.0(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
dependencies:
color: 4.2.3
react: 19.1.0
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
react-native-gesture-handler: 2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
use-latest-callback: 0.2.6(react@19.1.0)
react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
dependencies:
'@egjs/hammerjs': 2.0.17
@@ -11436,21 +11466,11 @@ snapshots:
transitivePeerDependencies:
- supports-color
react-native-iphone-x-helper@1.3.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)):
dependencies:
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
react-native-is-edge-to-edge@1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
dependencies:
react: 19.1.0
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
react-native-keyboard-aware-scroll-view@0.9.5(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)):
dependencies:
prop-types: 15.8.1
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
react-native-iphone-x-helper: 1.3.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))
react-native-keyboard-controller@1.18.5(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
dependencies:
react: 19.1.0
@@ -11458,6 +11478,12 @@ snapshots:
react-native-is-edge-to-edge: 1.2.1(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
react-native-reanimated: 4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
react-native-modal-datetime-picker@18.0.0(@react-native-community/datetimepicker@8.4.4(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)):
dependencies:
'@react-native-community/datetimepicker': 8.4.4(expo@54.0.23)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)
prop-types: 15.8.1
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/core': 7.28.5
@@ -11488,16 +11514,6 @@ snapshots:
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
warn-once: 0.1.1
react-native-ui-datepicker@3.1.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0):
dependencies:
clsx: 2.1.1
dayjs: 1.11.19
jalali-plugin-dayjs: 1.1.4
lodash: 4.17.21
react: 19.1.0
react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)
tailwind-merge: 3.4.0
react-native-web@0.21.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
'@babel/runtime': 7.28.4
@@ -11951,8 +11967,6 @@ snapshots:
tailwind-merge@1.14.0: {}
tailwind-merge@3.4.0: {}
tailwind-variants@0.1.20(tailwindcss@3.4.18(yaml@2.8.1)):
dependencies:
tailwind-merge: 1.14.0
@@ -1,42 +1,48 @@
import dayjs from 'dayjs'
import { useState } from 'react'
import { StyleSheet, View } from 'react-native'
import DateTimePicker from 'react-native-dates-picker'
// DemoADatePicker.tsx
import React, { useState } from 'react'
import { Button, ScrollView, StyleSheet } from 'react-native'
import { ADateTimeDrawer } from '@/components/common/ao'
export default function App() {
export default function DemoADatePicker() {
const [show, setShow] = useState(false)
const [date, setDate] = useState(dayjs())
return (
<View style={styles.container}>
<DateTimePicker
mode="single"
date={date}
selectedItemColor="#fff"
selectedTextStyle={{
fontWeight: 'bold',
color: '#00d279',
<ScrollView contentContainerStyle={styles.container}>
<ADateTimeDrawer
isOpen={show}
defaultDate={date}
modeType="year-month"
onConfirm={(val) => {
console.log(val)
}}
headerTextStyle={{
fontSize: 18,
onClose={() => {
setShow(false)
}}
headerButtonStyle={{
backgroundColor: '#ffffff',
borderRadius: 8,
}}
headerButtonColor="#00d279"
headerButtonSize={18}
headerButtonsPosition="right"
onChange={params => setDate(params.date)}
/>
</View>
<Button
title="测试"
onPress={() => {
setShow(!show)
}}
>
</Button>
</ScrollView>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#F5FCFF',
marginTop: 128,
padding: 16,
padding: 20,
},
label: {
marginTop: 20,
fontSize: 16,
fontWeight: 'bold',
},
result: {
marginVertical: 5,
fontSize: 14,
color: '#333',
},
})
@@ -1,10 +1,10 @@
import type { MoneyCategoryResult } from '@/request/api/money'
import type { MoneyCategory } from '@/types/money'
import type { MoneyCategory, MoneyRecord } from '@/types/money'
import Big from 'big.js'
import { useEffect, useRef, useState } from 'react'
import { Keyboard, Pressable, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'
import { useEffect, useState } from 'react'
import { ActivityIndicator, Keyboard, Pressable, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { AInput, LucideIcon } from '@/components/common/ao'
import { ADatePicker, ADateTimeDrawer, AInput, LucideIcon } from '@/components/common/ao'
import SegmentedTabs from '@/components/common/ao/SegmentedTabs'
import {
Drawer,
@@ -15,7 +15,9 @@ import {
} from '@/components/ui/drawer'
import { Grid, GridItem } from '@/components/ui/grid'
import { useKeyboardHeight } from '@/hooks/useKeyboardHeight'
import { getMoneyCategoryApi } from '@/request/api/money'
import { getMoneyCategoryApi, insertMoneyRecordApi } from '@/request/api/money'
import { getCurrentDate } from '@/utils/time'
import { toast } from '@/utils/toast'
import MoneyCategoryIcon from './MoneyCategoryIcon'
interface AddMoneyDrawerProps {
@@ -27,6 +29,7 @@ interface KeydownItem {
key: string | number
content: React.ReactNode
}
const keys: KeydownItem[] = [
{ key: '1', content: <Text className="text-2xl">1</Text> },
{ key: '2', content: <Text className="text-2xl">2</Text> },
@@ -40,9 +43,9 @@ const keys: KeydownItem[] = [
{ key: '8', content: <Text className="text-2xl">8</Text> },
{ key: '9', content: <Text className="text-2xl">9</Text> },
{ key: '-', content: <LucideIcon icon="Minus" size={22} strokeWidth={1.5} clickable={false} /> },
{ key: 'repeat', content: (
{ key: 'clear', content: (
<View className="w-full h-full flex items-center justify-center">
<Text className="text-xl text-gray-600">再记</Text>
<Text className="text-xl text-gray-600">重置</Text>
</View>
) },
{ key: '0', content: <Text className="text-2xl">0</Text> },
@@ -59,21 +62,42 @@ export default function AddMoneyDrawer({
onClose,
}: AddMoneyDrawerProps) {
const insets = useSafeAreaInsets()
// 选择顺序:0 支出 1收入
const [selectedIndex, setSelectedIndex] = useState(0)
const [selectedCategoryId, setSelectedCategoryId] = useState<number | null>(null)
// 自定义键盘
const [showKeydown, setShowKeydown] = useState(false)
const [keydownInput, setKeydownInput] = useState<string>('')
const [result, setResult] = useState('0')
// 自定义键盘计算过程
const [keydownInput, setKeydownInput] = useState('')
// 日期选择
const [showDatePicker, setShowDatePicker] = useState(false)
// 备注聚焦
const [remarkIsFocused, setRemarkIsFocused] = useState(false)
// 分类
const [categories, setCategories] = useState<MoneyCategoryResult>({
income: [],
expense: [],
})
const keyboardHeight = useKeyboardHeight()
console.log(keyboardHeight)
const defaultMoneyRecord: MoneyRecord = {
id: -1,
category_id: -1,
user_id: -1,
amount: '0.00',
remark: '',
record_time: getCurrentDate(),
create_time: '',
}
const [moneyRecord, setMoneyRecord] = useState<MoneyRecord>(defaultMoneyRecord)
function selectCategory(item: MoneyCategory) {
setSelectedCategoryId(item.id)
setMoneyRecord({
...moneyRecord,
category_id: item.id,
})
setShowKeydown(true)
}
@@ -94,11 +118,23 @@ export default function AddMoneyDrawer({
}
}, [isOpen])
// 关闭整个drawer处理
function onMainDrawerClose() {
setShowKeydown(false)
onClose()
// 清空键盘记录
setKeydownInput('')
// 关闭默认键盘
setShowKeydown(false)
// 设置默认值
setMoneyRecord(defaultMoneyRecord)
// 选中支出
setSelectedIndex(0)
// 关闭日期
setShowDatePicker(false)
}
// 计算过程
const handlePress = (key: string | number) => {
setKeydownInput((prev) => {
let newInput = prev
@@ -106,8 +142,17 @@ export default function AddMoneyDrawer({
if (key === 'del') {
newInput = prev.slice(0, -1)
}
else if (key === 'clear') {
setMoneyRecord({
...moneyRecord,
amount: '0.00',
remark: '',
record_time: getCurrentDate(),
})
return ''
}
else if (key === '/') {
// 完成操作,可以在这里提交或关闭键盘
saveRecord()
return prev
}
else {
@@ -115,6 +160,11 @@ export default function AddMoneyDrawer({
// 检查是否要输入运算符(+ 或 -)
if (keyStr === '+' || keyStr === '-') {
// 如果当前输入为空,不允许以运算符开头
if (prev === '') {
return prev
}
// 获取最后一个字符
const lastChar = prev.slice(-1)
@@ -122,30 +172,43 @@ export default function AddMoneyDrawer({
if (lastChar === '+' || lastChar === '-') {
newInput = prev.slice(0, -1) + keyStr
}
// 如果最后一个字符是小数点,不允许在小数点后直接加运算符
else if (lastChar === '.') {
return prev
}
else {
newInput = prev + keyStr
}
}
// 检查是否要输入小数点(.)
else if (keyStr === '.') {
// 检查当前数字是否已经包含小数点
const parts = prev.split(/[+-]/)
const currentNumber = parts[parts.length - 1]
// 如果当前输入为空,不允许以小数点开头,改为输入"0."
if (prev === '') {
newInput = '0.'
}
else {
// 获取最后一个字符
const lastChar = prev.slice(-1)
// 如果当前数字中还没有小数点,可以添加
if (!currentNumber.includes('.')) {
// 检查当前数字的小数位数(应该是0,因为还没有小数点)
if (currentNumber === '' || /[+-]/.test(prev.slice(-1))) {
// 如果最后一个字符是运算符,则需要在运算符后添加"0."而不是单独的"."
if (lastChar === '+' || lastChar === '-') {
newInput = `${prev}0.`
}
else {
newInput = `${prev}.`
// 检查当前数字是否已经包含小数点
const parts = prev.split(/[+-]/)
const currentNumber = parts[parts.length - 1]
// 如果当前数字中还没有小数点,可以添加
if (!currentNumber.includes('.')) {
newInput = `${prev}.`
}
else {
// 已经有小数点了,不重复添加
newInput = prev
}
}
}
else {
// 已经有小数点了,不重复添加
newInput = prev
}
}
// 处理数字输入
else if (/^\d$/.test(keyStr)) {
@@ -178,10 +241,16 @@ export default function AddMoneyDrawer({
// 计算结果
try {
const computed = calculateWithBig(newInput)
setResult(computed)
setMoneyRecord({
...moneyRecord,
amount: computed,
})
}
catch (e) {
setResult('0')
setMoneyRecord({
...moneyRecord,
amount: '0',
})
}
return newInput
@@ -242,166 +311,213 @@ export default function AddMoneyDrawer({
return total.toFixed(2)
}
}
const inputRef = useRef<any>(null)
const [isFocused, setIsFocused] = useState(false)
// 保存
function saveRecord() {
if (moneyRecord.category_id === -1) {
toast.show({
title: '请选择分类',
})
return
}
if (moneyRecord.amount === '0.00') {
toast.show({
title: '请输入金额',
})
}
insertMoneyRecordApi({
...moneyRecord,
}).then((res) => {
if (res.isSuccess) {
onMainDrawerClose()
}
else {
toast.show({
title: res.message || '保存失败',
})
}
}).catch((err) => {
console.log(err)
})
}
return (
<Drawer
isOpen={isOpen}
size="full"
anchor="bottom"
onClose={onMainDrawerClose}
>
<DrawerBackdrop />
<DrawerContent
style={{
padding: 0,
flex: 1,
}}
<>
<Drawer
isOpen={isOpen}
size="full"
anchor="bottom"
onClose={onMainDrawerClose}
>
<DrawerHeader
className="flex items-center justify-between"
style={{ paddingTop: insets.top + 8, paddingLeft: 16, paddingRight: 16 }}
>
<View className="w-1" />
<SegmentedTabs
value={selectedIndex}
tabs={[{ label: '收入' }, { label: '支出' }]}
onChange={setSelectedIndex}
style={{ width: 180 }}
className="mx-auto"
/>
<Pressable onPress={onMainDrawerClose}>
<Text className="text-gray-500 font-medium">取消</Text>
</Pressable>
</DrawerHeader>
<DrawerBody style={{ flex: 1, paddingLeft: 16, paddingRight: 16, marginBottom: 0, backgroundColor: '#fff',
}}
>
<Grid className="gap-4 mb-8" _extra={{ className: 'grid-cols-4' }}>
{(() => {
const list = selectedIndex === 0 ? categories.income : categories.expense
const items = list.map((item) => {
const isSelected = selectedCategoryId === item.id
return (
<GridItem key={item.id} _extra={{ className: 'col-span-1' }}>
<Pressable
className="flex flex-col items-center justify-center rounded"
onPress={() => selectCategory(item)}
>
<View
className={`w-14 h-14 rounded-full flex items-center justify-center ${
isSelected ? 'bg-blue-100' : 'bg-gray-100'
}`}
>
<MoneyCategoryIcon name={item.icon} size={28} />
</View>
<Text className="text-sm mt-1">
{item.name}
</Text>
</Pressable>
</GridItem>
)
})
<DrawerBackdrop />
const remainder = list.length % 4
if (remainder > 0) {
const emptyCount = 4 - remainder
for (let i = 0; i < emptyCount; i++) {
items.push(<GridItem key={`empty-${i}`} _extra={{ className: 'col-span-1' }} />)
}
}
return items
})()}
</Grid>
</DrawerBody>
{showKeydown && (
<View style={{
backgroundColor: '#fff',
elevation: 20,
}}
<DrawerContent className="p-0 flex-1">
<DrawerHeader
className="flex items-center justify-between"
style={{ paddingTop: insets.top + 8, paddingLeft: 16, paddingRight: 16 }}
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View className="h-28 flex pl-4 pr-4 justify-center">
<View className="flex flex-row items-center justify-between">
<Pressable
className="flex flex-row items-center gap-1.5 px-2 py-1.5 rounded-md bg-gray-50 active:opacity-70"
>
<LucideIcon icon="Calendar" size={16} color="#60a5fa" />
<Text className="text-gray-600 text-sm">2024/11/05</Text>
<LucideIcon icon="ChevronDown" size={12} color="#aaa" />
</Pressable>
<Text className="text-3xl text-blue-700 text-right">{result}</Text>
<View className="w-1" />
<SegmentedTabs
value={selectedIndex}
tabs={[{ label: '支出' }, { label: '收入' }]}
onChange={setSelectedIndex}
style={{ width: 180 }}
className="mx-auto"
/>
<Pressable onPress={onMainDrawerClose}>
<Text className="text-gray-500 font-medium">取消</Text>
</Pressable>
</DrawerHeader>
<DrawerBody
style={{
flex: 1,
paddingLeft: 16,
paddingRight: 16,
marginBottom: 0,
backgroundColor: '#fff',
}}
>
<Grid className="gap-4 mb-8" _extra={{ className: 'grid-cols-4' }}>
{(() => {
const list = selectedIndex === 1 ? categories.income : categories.expense
const items = list.map((item) => {
const isSelected = moneyRecord.category_id === item.id
return (
<GridItem key={item.id} _extra={{ className: 'col-span-1' }}>
<Pressable
className="flex flex-col items-center justify-center rounded"
onPress={() => selectCategory(item)}
>
<View
className={`w-14 h-14 rounded-full flex items-center justify-center ${
isSelected ? 'bg-blue-100' : 'bg-gray-100'
}`}
>
<MoneyCategoryIcon name={item.icon} size={28} />
</View>
<Text className="text-sm mt-1">
{item.name}
</Text>
</Pressable>
</GridItem>
)
})
const remainder = list.length % 4
if (remainder > 0) {
const emptyCount = 4 - remainder
for (let i = 0; i < emptyCount; i++) {
items.push(<GridItem key={`empty-${i}`} _extra={{ className: 'col-span-1' }} />)
}
}
return items
})()}
</Grid>
</DrawerBody>
{showKeydown && (
<View style={{
backgroundColor: '#fff',
elevation: 20,
}}
>
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
<View className="h-28 flex pl-4 pr-4 justify-center">
<View className="flex flex-row items-center justify-between">
<Pressable
className="flex flex-row items-center gap-1.5 px-2 py-1.5 rounded-md bg-gray-50 active:opacity-70"
onPress={() => setShowDatePicker(true)}
>
<LucideIcon icon="Calendar" size={16} color="#60a5fa" clickable={false} />
<Text className="text-gray-600 text-sm">{moneyRecord.record_time}</Text>
<LucideIcon icon="ChevronDown" size={12} color="#aaa" clickable={false} />
</Pressable>
<Text className="text-3xl text-blue-700 text-right">{moneyRecord.amount}</Text>
</View>
<Text className="text-gray-400 text-right">{keydownInput}</Text>
<AInput
value={moneyRecord.remark}
placeholder="备注"
className="bg-[#f2f3f5] border-0"
size="sm"
onChangeText={(val) => {
setMoneyRecord({
...moneyRecord,
remark: val,
})
}}
onFocus={() => setRemarkIsFocused(true)}
onBlur={() => setRemarkIsFocused(false)}
/>
</View>
</TouchableWithoutFeedback>
<View className="flex flex-row flex-wrap" style={{ height: 220 }}>
{keys.map((item, idx) => {
const isFirstCol = idx % 4 === 0
const isFirstRow = idx < 4
const isLastCol = (idx + 1) % 4 === 0
const isLastRow = idx >= 12
<Text className="text-gray-400 text-right">{keydownInput}</Text>
<AInput
ref={inputRef}
value={keydownInput}
placeholder="备注"
className="bg-[#f2f3f5] border-0"
size="sm"
onFocus={() => setIsFocused(true)}
onBlur={() => setIsFocused(false)}
/>
return (
<Pressable
key={item.key}
onPress={() => handlePress(item.key)}
style={{
width: '25%',
height: '25%',
justifyContent: 'center',
alignItems: 'center',
borderTopWidth: isFirstRow ? 0.3 : 0,
borderLeftWidth: isFirstCol ? 0.3 : 0,
borderRightWidth: !isLastCol ? 0.3 : 0,
borderBottomWidth: !isLastRow ? 0.3 : 0,
borderColor: '#ddd',
}}
>
{({ pressed }) => {
const defaultBg = item.key === '/' ? '#e6d7b8' : '#fff'
const pressedBg = item.key === '/' ? '#f5e9d4' : '#f2f2f2'
return (
<>
<View
style={{
...StyleSheet.absoluteFillObject,
backgroundColor: pressed ? pressedBg : defaultBg,
}}
/>
{item.content}
</>
)
}}
</Pressable>
)
})}
</View>
</TouchableWithoutFeedback>
<View className="flex flex-row flex-wrap" style={{ height: 220 }}>
{keys.map((item, idx) => {
const isFirstCol = idx % 4 === 0
const isFirstRow = idx < 4
const isLastCol = (idx + 1) % 4 === 0
const isLastRow = idx >= 12
return (
<Pressable
key={item.key}
onPress={() => handlePress(item.key)}
style={{
width: '25%',
height: '25%',
justifyContent: 'center',
alignItems: 'center',
borderTopWidth: isFirstRow ? 0.3 : 0,
borderLeftWidth: isFirstCol ? 0.3 : 0,
borderRightWidth: !isLastCol ? 0.3 : 0,
borderBottomWidth: !isLastRow ? 0.3 : 0,
borderColor: '#ddd',
}}
>
{({ pressed }) => {
const defaultBg = item.key === '/' ? '#e6d7b8' : '#fff'
const pressedBg = item.key === '/' ? '#f5e9d4' : '#f2f2f2'
return (
<>
<View
style={{
...StyleSheet.absoluteFillObject,
backgroundColor: pressed ? pressedBg : defaultBg,
}}
/>
{item.content}
</>
)
}}
</Pressable>
)
})}
{remarkIsFocused && keyboardHeight > 220 && (
<View style={{ height: keyboardHeight - 220 }} />
)}
</View>
{isFocused && keyboardHeight > 220 && (
<View style={{ height: keyboardHeight - 220 }} />
)}
</View>
)}
</DrawerContent>
</Drawer>
)}
</DrawerContent>
</Drawer>
<ADateTimeDrawer
isOpen={showDatePicker}
defaultDate={moneyRecord.record_time}
onConfirm={(val) => {
setMoneyRecord({
...moneyRecord,
record_time: val,
})
setShowDatePicker(false)
}}
onClose={() => {
setShowDatePicker((false))
}}
/>
</>
)
}
@@ -1,26 +1,29 @@
import type { LayoutChangeEvent } from 'react-native'
import { BlurView } from 'expo-blur'
import { LinearGradient } from 'expo-linear-gradient'
import React, { useRef, useState } from 'react'
import { ScrollView, Text, View } from 'react-native'
import React, { useEffect, useRef, useState } from 'react'
import { ScrollView, Text, TouchableOpacity, View } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { LucideIcon } from '@/components/common/ao'
import { ADateTimeDrawer, LucideIcon } from '@/components/common/ao'
import { IconJiaotong } from '@/components/iconfont'
import { Fab, FabIcon } from '@/components/ui/fab'
import { Grid, GridItem } from '@/components/ui/grid'
import { AddIcon } from '@/components/ui/icon'
import { Progress, ProgressFilledTrack } from '@/components/ui/progress'
import { queryMoneyRecordsApi } from '@/request/api/money'
import { getCurrentYearMonth } from '@/utils/time'
import AddMoneyDrawer from './components/AddDrawer'
import TransactionsList from './components/TransactionsList'
export default function Money() {
const [showDrawer, setShowDrawer] = useState(false)
const [showAddDrawer, setShowAddDrawer] = useState(false)
// 安全高度
const insets = useSafeAreaInsets()
const [gradientHeight, setGradientHeight] = useState(200)
const gridLayout = useRef<{ y: number, height: number } | null>(null)
const onGridLayout = (e: LayoutChangeEvent) => {
// 仅首次布局计算高度
if (!gridLayout.current) {
const { y, height } = e.nativeEvent.layout
gridLayout.current = { y, height }
@@ -28,6 +31,20 @@ export default function Money() {
}
}
// 日期筛选
const [showDatePicker, setShowDatePicker] = useState(false)
const [searchParams, setSearchParams] = useState({
record_time: getCurrentYearMonth(),
})
useEffect(() => {
queryMoneyRecordsApi({
record_time: searchParams.record_time,
}).then((res) => {
console.log(res.data)
})
}, [searchParams])
return (
<View
className="flex-1 relative bg-white"
@@ -52,10 +69,16 @@ export default function Money() {
<View style={{ zIndex: 10, paddingLeft: 12, paddingRight: 12 }}>
<View className="flex flex-row justify-between items-center py-2">
<View className="flex flex-row items-center gap-2">
<Text className="font-bold text-base">2025/11/10</Text>
<LucideIcon icon="CircleChevronDown" size={14} />
</View>
<BlurView intensity={80} className="px-2 py-1">
<TouchableOpacity
onPress={() => setShowDatePicker(true)}
className="flex flex-row items-center gap-2"
>
<Text className="font-bold text-base">{searchParams.record_time}</Text>
<LucideIcon icon="ArrowBigDownDash" size={16} />
</TouchableOpacity>
</BlurView>
<View className="flex flex-row items-center gap-2">
<LucideIcon icon="SlidersHorizontal" />
</View>
@@ -144,16 +167,31 @@ export default function Money() {
<Fab
size="lg"
onPress={() => {
setShowDrawer(true)
setShowAddDrawer(true)
}}
>
<FabIcon as={AddIcon} size="xl" />
</Fab>
<AddMoneyDrawer
isOpen={showDrawer}
isOpen={showAddDrawer}
onClose={() => {
setShowDrawer(false)
setShowAddDrawer(false)
}}
/>
<ADateTimeDrawer
isOpen={showDatePicker}
modeType="year-month"
onConfirm={(val) => {
setSearchParams({
...searchParams,
record_time: val,
})
setShowDatePicker(false)
}}
onClose={() => {
setShowDatePicker(false)
}}
/>
</View>
@@ -0,0 +1,81 @@
import type { DateTimePickerProps } from 'react-native-modal-datetime-picker'
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import React, { useMemo } from 'react'
import DateTimePickerModal from 'react-native-modal-datetime-picker'
dayjs.extend(customParseFormat)
interface ADatePickerProps extends Omit<DateTimePickerProps, 'onConfirm' | 'onCancel' | 'date'> {
format?: string
onConfirm?: (value: string) => void
date?: Date | string
is24Hour?: boolean
onCancel?: () => void
}
export default function DatePicker({
mode = 'date',
format,
date,
onConfirm,
is24Hour = true,
onCancel,
...restProps
}: ADatePickerProps) {
const parsedDate = useMemo(() => {
if (!date)
return new Date()
if (typeof date === 'string') {
// 支持多种常见日期格式
const formats = ['YYYY-MM-DD', 'YYYY/MM/DD', 'YYYY-MM-DDTHH:mm:ssZ', 'YYYY/MM/DD HH:mm:ss', 'YYYY-MM-DD HH:mm:ss']
for (const fmt of formats) {
const d = dayjs(date, fmt, true)
if (d.isValid())
return d.toDate()
}
// fallback: 尝试直接解析
const d = dayjs(date)
if (d.isValid())
return d.toDate()
// 仍然不合法就返回当前时间
return new Date()
}
return date
}, [date])
const handleConfirm = (selectedDate: Date) => {
const fmt = format || (() => {
switch (mode) {
case 'time':
return 'HH:mm:00'
case 'datetime':
return 'YYYY-MM-DD HH:mm:00'
case 'date':
default:
return 'YYYY-MM-DD'
}
})()
const localTimeStr = dayjs(selectedDate).format(fmt)
onConfirm?.(localTimeStr)
}
const handleCancel = () => {
onCancel?.()
}
return (
<DateTimePickerModal
{...restProps}
mode={mode}
date={parsedDate}
locale="zh-CN"
onConfirm={handleConfirm}
onCancel={handleCancel}
is24Hour={is24Hour}
/>
)
}
@@ -0,0 +1,106 @@
import type { DateType, OrderedDateParts } from 'react-native-dates-picker/lib/typescript/types'
import dayjs from 'dayjs'
import { useEffect, useRef, useState } from 'react'
import { View } from 'react-native'
import DateTimePicker from 'react-native-dates-picker'
import { Drawer, DrawerBackdrop, DrawerContent, DrawerFooter, DrawerHeader } from '@/components/ui/drawer'
import AButton from './Button'
type DateWheelMode
= | 'year-month'
| 'year-month-day'
| 'hour-minute-second'
| 'year-month-day-hour-minute-second'
interface DateDrawerProps {
isOpen?: boolean
defaultDate?: DateType
modeType?: DateWheelMode
onClose?: () => void
onConfirm?: (dateStr: string) => void
}
const modeColumnsMap: Record<DateWheelMode, OrderedDateParts[]> = {
'year-month': ['year', 'month'],
'year-month-day': ['year', 'month', 'day'],
'hour-minute-second': ['hour', 'minute', 'second'],
'year-month-day-hour-minute-second': ['year', 'month', 'day', 'hour', 'minute', 'second'],
}
const modeFormatMap: Record<DateWheelMode, string> = {
'year-month': 'YYYY-MM',
'year-month-day': 'YYYY-MM-DD',
'hour-minute-second': 'HH:mm:ss',
'year-month-day-hour-minute-second': 'YYYY-MM-DD HH:mm:ss',
}
export default function DateWheelDrawer({
isOpen,
defaultDate = dayjs(),
onClose,
onConfirm,
modeType = 'year-month-day',
}: DateDrawerProps) {
const columns = modeColumnsMap[modeType]
const format = modeFormatMap[modeType]
// 当前选择的值
const [date, setDate] = useState<DateType>(defaultDate)
// 保存上一次确认的值
const lastConfirmedRef = useRef<DateType>(defaultDate)
// 每次弹窗打开时,把滚轮显示初始化为上一次确认的值
useEffect(() => {
if (isOpen) {
setDate(lastConfirmedRef.current)
}
}, [isOpen])
// 点击取消
const handleCancel = () => {
onClose?.()
}
// 点击确认
const handleConfirm = () => {
console.log("确实")
onClose?.()
lastConfirmedRef.current = date // 保存为上一次确认的值
if (onConfirm) {
const formatted = dayjs(date).format(format)
onConfirm(formatted)
}
}
return (
<Drawer
isOpen={isOpen}
anchor="bottom"
closeOnOverlayClick
onClose={onClose}
size="md"
>
<DrawerBackdrop />
<DrawerContent className="p-0 flex-1">
<DrawerHeader className="flex items-center justify-between p-4">
<AButton action="secondary" onPress={handleCancel}>取消</AButton>
<AButton onPress={handleConfirm}>确认</AButton>
</DrawerHeader>
<View className="flex-1 flex items-center justify-center">
<DateTimePicker
mode="wheel"
date={date}
columns={columns}
wheelPickerSelectedIndicatorStyle={{ backgroundColor: '#eee' }}
onChange={params => setDate(params.date)}
/>
</View>
<DrawerFooter></DrawerFooter>
</DrawerContent>
</Drawer>
)
}
@@ -1,5 +1,7 @@
import Button from './Button'
import Checkbox from './Checkbox'
import DatePicker from './DatePicker'
import DateTimeDrawer from './DateTimeDrawer'
import Form, { FormRef } from './Form'
import FormItem from './FormItem'
import Input from './Input'
@@ -8,6 +10,8 @@ import LucideIcon from './LucideIcon'
export {
Button as AButton,
Checkbox as ACheckbox,
DatePicker as ADatePicker,
DateTimeDrawer as ADateTimeDrawer,
Form as AForm,
FormItem as AFormItem,
FormRef as AFormRef,
@@ -1,8 +1,19 @@
import type { MoneyCategory } from '@/types/money'
import type { MoneyCategory, MoneyRecord } from '@/types/money'
import alovaInstance from '../index'
const API_PREFIX = '/money'
export interface QueryMoneyRecordsParams {
record_time: string
}
export interface QueryMoneyRecordsResult {
[key: string]: MoneyRecord[]
}
export function queryMoneyRecordsApi(params: QueryMoneyRecordsParams) {
return alovaInstance.Post<ResponseData<QueryMoneyRecordsResult>>(`${API_PREFIX}/query`, params)
}
export interface MoneyCategoryResult {
income: MoneyCategory[]
expense: MoneyCategory[]
@@ -30,8 +30,8 @@ function isWhiteListed(url: string) {
}
const alovaInstance = createAlova({
baseURL: 'http://192.168.5.6:8085/app',
// baseURL: 'http://192.168.1.136:8085/app',
// baseURL: 'http://192.168.5.6:8085/app',
baseURL: 'http://192.168.1.136:8085/app',
requestAdapter: axiosRequestAdapter(),
timeout: 5000,
cacheFor: null,
@@ -0,0 +1,29 @@
import dayjs from 'dayjs'
/**
* 获取当前日期,默认格式: YYYY-MM-DD
*/
export function getCurrentDate(format = 'YYYY-MM-DD'): string {
return dayjs().format(format)
}
/**
* 获取当前日期时间,默认格式: YYYY-MM-DD HH:mm:ss
*/
export function getCurrentDateTime(format = 'YYYY-MM-DD HH:mm:ss'): string {
return dayjs().format(format)
}
/**
* 获取当前时间,默认格式: HH:mm:ss
*/
export function getCurrentTime(format = 'HH:mm:ss'): string {
return dayjs().format(format)
}
/**
* 获取当前年月,默认格式: YYYY-MM
*/
export function getCurrentYearMonth(format = 'YYYY-MM'): string {
return dayjs().format(format)
}