This commit is contained in:
oneao committed 2025-11-13 17:29:44 +08:00
1 parent 68a834dd18
commit a52e73fbba
9 files changed
+255 -141

No files matched your search

+2 -1
View File
@@ -22,7 +22,8 @@
"backgroundColor": "#ffffff"
},
"edgeToEdgeEnabled": true,
"predictiveBackGestureEnabled": false
"predictiveBackGestureEnabled": false,
"package": "com.oneao.baseapp"
},
"web": {
"favicon": "./assets/favicon.png"
+2 -2
View File
@@ -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>
)
+34 -31
View File
@@ -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,