u
This commit is contained in:
1 parent
68a834dd18
commit
a52e73fbba
9 files changed
+255
-141
No files matched your search
@@ -22,7 +22,8 @@
|
||||
"backgroundColor": "#ffffff"
|
||||
},
|
||||
"edgeToEdgeEnabled": true,
|
||||
"predictiveBackGestureEnabled": false
|
||||
"predictiveBackGestureEnabled": false,
|
||||
"package": "com.oneao.baseapp"
|
||||
},
|
||||
"web": {
|
||||
"favicon": "./assets/favicon.png"
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"main": "expo-router/entry",
|
||||
"scripts": {
|
||||
"start": "expo start",
|
||||
"android": "expo start --android",
|
||||
"ios": "expo start --ios",
|
||||
"android": "expo run:android",
|
||||
"ios": "expo run:ios",
|
||||
"web": "expo start --web"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
@@ -1,39 +1,68 @@
|
||||
import React, { useState } from 'react'
|
||||
import { KeyboardAvoidingView, Platform, StyleSheet, View } from 'react-native'
|
||||
import { KeyboardProvider } from 'react-native-keyboard-controller'
|
||||
import { AInput } from '@/components/common/ao'
|
||||
import React from 'react'
|
||||
import {
|
||||
StyleSheet,
|
||||
Text,
|
||||
TextInput,
|
||||
TouchableOpacity,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import { KeyboardAvoidingView } from 'react-native-keyboard-controller'
|
||||
|
||||
export default function MyInput() {
|
||||
export default function KeyboardAvoidingViewExample() {
|
||||
return (
|
||||
<View style={styles.content}>
|
||||
<View style={styles.topSpace} />
|
||||
|
||||
{/* 输入框 */}
|
||||
<View style={styles.inputWrapper}>
|
||||
<AInput
|
||||
value=""
|
||||
placeholder="备注"
|
||||
className="bg-[#f2f3f5] border-0"
|
||||
size="sm"
|
||||
/>
|
||||
<KeyboardAvoidingView
|
||||
behavior="padding"
|
||||
keyboardVerticalOffset={100}
|
||||
style={styles.content}
|
||||
>
|
||||
<View style={styles.inner}>
|
||||
<Text style={styles.heading}>Header</Text>
|
||||
<View>
|
||||
<TextInput placeholder="Username" style={styles.textInput} />
|
||||
<TextInput placeholder="Password" style={styles.textInput} />
|
||||
</View>
|
||||
<TouchableOpacity style={styles.button}>
|
||||
<Text style={styles.text}>Submit</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
</View>
|
||||
</KeyboardAvoidingView>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
container: {
|
||||
flex: 1,
|
||||
},
|
||||
content: {
|
||||
flex: 1,
|
||||
maxHeight: 600,
|
||||
},
|
||||
heading: {
|
||||
fontSize: 36,
|
||||
marginBottom: 48,
|
||||
fontWeight: '600',
|
||||
},
|
||||
inner: {
|
||||
padding: 24,
|
||||
flex: 1,
|
||||
justifyContent: 'space-between',
|
||||
},
|
||||
topSpace: {
|
||||
flex: 1,
|
||||
textInput: {
|
||||
height: 45,
|
||||
borderColor: '#000000',
|
||||
borderWidth: 1,
|
||||
borderRadius: 10,
|
||||
marginBottom: 36,
|
||||
paddingLeft: 10,
|
||||
},
|
||||
inputWrapper: {
|
||||
marginHorizontal: 16,
|
||||
marginBottom: 20,
|
||||
button: {
|
||||
marginTop: 12,
|
||||
height: 45,
|
||||
borderRadius: 10,
|
||||
backgroundColor: 'rgb(40, 64, 147)',
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
text: {
|
||||
fontWeight: '500',
|
||||
fontSize: 16,
|
||||
color: 'white',
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,50 @@
|
||||
import type { KeyboardEvent } from 'react-native'
|
||||
import React, { useEffect, useRef } from 'react'
|
||||
import { Animated, Keyboard, Platform } from 'react-native'
|
||||
|
||||
interface BottomSectionProps {
|
||||
children: React.ReactNode
|
||||
safeMargin?: number // 输入框距离键盘的安全距离
|
||||
}
|
||||
|
||||
const BottomSection: React.FC<BottomSectionProps> = ({ children, safeMargin = 8 }) => {
|
||||
const translateY = useRef(new Animated.Value(0)).current
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyboardShow = (e: KeyboardEvent) => {
|
||||
const keyboardHeight = e.endCoordinates.height
|
||||
console.log(keyboardHeight)
|
||||
Animated.timing(translateY, {
|
||||
toValue: -(keyboardHeight - safeMargin),
|
||||
duration: e.duration ?? 250,
|
||||
useNativeDriver: true,
|
||||
}).start()
|
||||
}
|
||||
|
||||
const onKeyboardHide = (e: KeyboardEvent) => {
|
||||
Animated.timing(translateY, {
|
||||
toValue: 0,
|
||||
duration: e.duration ?? 250,
|
||||
useNativeDriver: true,
|
||||
}).start()
|
||||
}
|
||||
|
||||
const showSub = Keyboard.addListener(
|
||||
Platform.OS === 'ios' ? 'keyboardWillShow' : 'keyboardDidShow',
|
||||
onKeyboardShow,
|
||||
)
|
||||
const hideSub = Keyboard.addListener(
|
||||
Platform.OS === 'ios' ? 'keyboardWillHide' : 'keyboardDidHide',
|
||||
onKeyboardHide,
|
||||
)
|
||||
|
||||
return () => {
|
||||
showSub.remove()
|
||||
hideSub.remove()
|
||||
}
|
||||
}, [safeMargin])
|
||||
|
||||
return <Animated.View style={{ transform: [{ translateY }] }}>{children}</Animated.View>
|
||||
}
|
||||
|
||||
export default BottomSection
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { MoneyCategoryResult } from '@/request/api/money'
|
||||
import type { MoneyCategory } from '@/types/money'
|
||||
import Big from 'big.js'
|
||||
import dayjs from 'dayjs'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Keyboard, Pressable, StyleSheet, Text, View } from 'react-native'
|
||||
import { Keyboard, Platform, Pressable, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'
|
||||
import { KeyboardProvider } from 'react-native-keyboard-controller'
|
||||
import { useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { AInput, LucideIcon } from '@/components/common/ao'
|
||||
import SegmentedTabs from '@/components/common/ao/SegmentedTabs'
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
DrawerHeader,
|
||||
} from '@/components/ui/drawer'
|
||||
import { Grid, GridItem } from '@/components/ui/grid'
|
||||
import { useKeyboardHeight } from '@/hooks/useKeyboardHeight'
|
||||
import { getMoneyCategoryApi } from '@/request/api/money'
|
||||
import MoneyCategoryIcon from './MoneyCategoryIcon'
|
||||
|
||||
@@ -64,13 +65,13 @@ export default function AddMoneyDrawer({
|
||||
const [showKeydown, setShowKeydown] = useState(false)
|
||||
const [keydownInput, setKeydownInput] = useState<string>('')
|
||||
const [result, setResult] = useState('0')
|
||||
const [date, setDate] = useState(dayjs())
|
||||
|
||||
const [categories, setCategories] = useState<MoneyCategoryResult>({
|
||||
income: [],
|
||||
expense: [],
|
||||
})
|
||||
|
||||
const keyboardHeight = useKeyboardHeight()
|
||||
console.log(keyboardHeight)
|
||||
function selectCategory(item: MoneyCategory) {
|
||||
setSelectedCategoryId(item.id)
|
||||
setShowKeydown(true)
|
||||
@@ -299,7 +300,6 @@ export default function AddMoneyDrawer({
|
||||
}, [])
|
||||
|
||||
return (
|
||||
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
size="full"
|
||||
@@ -307,6 +307,7 @@ export default function AddMoneyDrawer({
|
||||
onClose={onMainDrawerClose}
|
||||
>
|
||||
<DrawerBackdrop />
|
||||
|
||||
<DrawerContent style={{ padding: 0, flex: 1 }}>
|
||||
<DrawerHeader
|
||||
className="flex items-center justify-between"
|
||||
@@ -324,7 +325,6 @@ export default function AddMoneyDrawer({
|
||||
<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' }}>
|
||||
{(() => {
|
||||
@@ -366,86 +366,86 @@ export default function AddMoneyDrawer({
|
||||
</DrawerBody>
|
||||
|
||||
{showKeydown && (
|
||||
<View style={[{
|
||||
backgroundColor: '#fff',
|
||||
elevation: 30,
|
||||
}]}
|
||||
>
|
||||
<View className="p-4">
|
||||
<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>
|
||||
<KeyboardProvider>
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
||||
<View className="p-4 h-28">
|
||||
<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>
|
||||
|
||||
<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)}
|
||||
/>
|
||||
</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>
|
||||
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'
|
||||
|
||||
<AInput
|
||||
ref={inputRef}
|
||||
value=""
|
||||
placeholder="备注"
|
||||
className="bg-[#f2f3f5] border-0"
|
||||
size="sm"
|
||||
onFocus={() => setIsFocused(true)}
|
||||
onBlur={() => setIsFocused(false)}
|
||||
/>
|
||||
return (
|
||||
<>
|
||||
<View
|
||||
style={{
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: pressed ? pressedBg : defaultBg,
|
||||
}}
|
||||
/>
|
||||
{item.content}
|
||||
</>
|
||||
)
|
||||
}}
|
||||
</Pressable>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
|
||||
{!isFocused && (
|
||||
<View style={{ height: 240 }}>
|
||||
<View className="flex flex-row flex-wrap h-full">
|
||||
{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>
|
||||
)
|
||||
})}
|
||||
</View>
|
||||
{/* {isFocused && (
|
||||
<View style={{ height:
|
||||
keyboardHeight + 10 - 220 }}
|
||||
>
|
||||
</View>
|
||||
)}
|
||||
|
||||
</View>
|
||||
)} */}
|
||||
</KeyboardProvider>
|
||||
)}
|
||||
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
)
|
||||
|
||||
@@ -6,6 +6,7 @@ import * as SplashScreen from 'expo-splash-screen'
|
||||
import { StatusBar } from 'expo-status-bar'
|
||||
import { RefreshCw } from 'lucide-react-native'
|
||||
import { useEffect } from 'react'
|
||||
import { KeyboardProvider } from 'react-native-keyboard-controller'
|
||||
import EmptyState from '@/components/common/EmptyStatus'
|
||||
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider'
|
||||
import { useUserStore } from '@/store/user'
|
||||
@@ -52,39 +53,41 @@ export default function RootLayout() {
|
||||
}, [fontsLoaded])
|
||||
|
||||
return (
|
||||
<GluestackUIProvider>
|
||||
<ToastProvider>
|
||||
<ToastProviderWrapper>
|
||||
<Stack
|
||||
screenOptions={{
|
||||
headerTitleAlign: 'center',
|
||||
animation: 'slide_from_right',
|
||||
gestureEnabled: true,
|
||||
gestureDirection: 'horizontal',
|
||||
contentStyle: {
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
}}
|
||||
>
|
||||
<Stack.Screen
|
||||
name="(tabs)"
|
||||
options={{
|
||||
headerShown: false,
|
||||
<KeyboardProvider>
|
||||
<GluestackUIProvider>
|
||||
<ToastProvider>
|
||||
<ToastProviderWrapper>
|
||||
<Stack
|
||||
screenOptions={{
|
||||
headerTitleAlign: 'center',
|
||||
animation: 'slide_from_right',
|
||||
gestureEnabled: true,
|
||||
gestureDirection: 'horizontal',
|
||||
contentStyle: {
|
||||
backgroundColor: '#fff',
|
||||
},
|
||||
}}
|
||||
/>
|
||||
>
|
||||
<Stack.Screen
|
||||
name="(tabs)"
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen
|
||||
name="auth"
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
<Stack.Screen
|
||||
name="auth"
|
||||
options={{
|
||||
headerShown: false,
|
||||
}}
|
||||
/>
|
||||
|
||||
<Stack.Screen name="+not-found" />
|
||||
</Stack>
|
||||
<StatusBar style="auto" />
|
||||
</ToastProviderWrapper>
|
||||
</ToastProvider>
|
||||
</GluestackUIProvider>
|
||||
<Stack.Screen name="+not-found" />
|
||||
</Stack>
|
||||
<StatusBar style="auto" />
|
||||
</ToastProviderWrapper>
|
||||
</ToastProvider>
|
||||
</GluestackUIProvider>
|
||||
</KeyboardProvider>
|
||||
)
|
||||
}
|
||||
@@ -55,7 +55,7 @@ const AInput = forwardRef<React.ComponentRef<typeof InputField>, AInputProps>(
|
||||
)}
|
||||
|
||||
<InputField
|
||||
ref={ref} // 这里直接传递给 InputField
|
||||
ref={ref}
|
||||
type={isPassword ? (showPassword ? 'text' : 'password') : 'text'}
|
||||
{...rest}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import type { KeyboardEvent } from 'react-native'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Keyboard } from 'react-native'
|
||||
|
||||
/**
|
||||
* useKeyboardHeight - 获取键盘高度的 Hooks
|
||||
* @returns {number} 当前键盘高度,键盘收起时为 0
|
||||
*/
|
||||
export function useKeyboardHeight() {
|
||||
const [keyboardHeight, setKeyboardHeight] = useState(0)
|
||||
|
||||
useEffect(() => {
|
||||
const onKeyboardShow = (e: KeyboardEvent) => {
|
||||
setKeyboardHeight(e.endCoordinates.height)
|
||||
}
|
||||
|
||||
const onKeyboardHide = () => {
|
||||
setKeyboardHeight(0)
|
||||
}
|
||||
|
||||
const showSub = Keyboard.addListener('keyboardDidShow', onKeyboardShow)
|
||||
const hideSub = Keyboard.addListener('keyboardDidHide', onKeyboardHide)
|
||||
|
||||
return () => {
|
||||
showSub.remove()
|
||||
hideSub.remove()
|
||||
}
|
||||
}, [])
|
||||
|
||||
return keyboardHeight
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user