u
This commit is contained in:
1 parent
52a20dfa22
commit
03dfc75c3a
9 files changed
+332
-38
No files matched your search
@@ -1,3 +1,5 @@
|
||||
import { useEvent } from 'expo'
|
||||
import ExpoQQ from 'expo-qq'
|
||||
import { useRouter } from 'expo-router'
|
||||
import { useEffect } from 'react'
|
||||
import { Image, Text, View } from 'react-native'
|
||||
@@ -11,6 +13,39 @@ export default function AuthIndex() {
|
||||
router.push(url)
|
||||
}
|
||||
|
||||
/// 监听登录结果,你也可以使用ExpoQQ.addListener('onLoginFinished', callback)来实现,但请不要忘记移除监听
|
||||
const onLoginFinished = useEvent(ExpoQQ, 'onLoginFinished')
|
||||
|
||||
/// 国内应用需要在用户接受隐私协议后再初始化
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
await ExpoQQ.init('102826474', 'universal link')
|
||||
}
|
||||
|
||||
init()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
const run = async () => {
|
||||
if (!onLoginFinished)
|
||||
return
|
||||
|
||||
console.log('onLoginFinished', onLoginFinished)
|
||||
const tokenInfoRes = await ExpoQQ.getLoginTokenInfo()
|
||||
console.log(tokenInfoRes)
|
||||
const userInfoRes = await ExpoQQ.sendGetUserInfoReq()
|
||||
console.log(userInfoRes)
|
||||
}
|
||||
|
||||
run()
|
||||
}, [onLoginFinished])
|
||||
|
||||
/// 调用qq登陆
|
||||
const loginByQQ = async () => {
|
||||
const res = await ExpoQQ.login(['get_user_info'])
|
||||
console.log(res)
|
||||
}
|
||||
|
||||
return (
|
||||
<View className="flex-1">
|
||||
<CustomHeader
|
||||
@@ -35,7 +70,7 @@ export default function AuthIndex() {
|
||||
</Text>
|
||||
|
||||
<View className="w-full flex flex-col gap-3 mt-12 px-3">
|
||||
<Button onPress={() => toPath('/auth/login')}>
|
||||
<Button onPress={() => loginByQQ()}>
|
||||
<ButtonText>登录</ButtonText>
|
||||
</Button>
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import type { MoneyCategoryEnum, MoneyCategoryItem } from '@/types/money/category'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import { Pressable, Text, View } from 'react-native'
|
||||
import { Drawer } from 'react-native-drawer-layout'
|
||||
import { AButton, AInput } from '@/components/common/ao'
|
||||
import { AButton, ADrawer, AInput } from '@/components/common/ao'
|
||||
import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
|
||||
import { useMoneyCategoryStore } from '@/store/moneyCategory'
|
||||
|
||||
@@ -62,12 +61,7 @@ export default function CreateCategory({
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
open={isOpen}
|
||||
onClose={handleClose}
|
||||
drawerPosition="bottom"
|
||||
>
|
||||
</Drawer>
|
||||
<ADrawer visible={isOpen} hideModal={handleClose} title="你好" Height="80%" duration={300} />
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,261 @@
|
||||
import type { ReactNode } from 'react'
|
||||
import type { DimensionValue, ViewStyle } from 'react-native'
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
Animated,
|
||||
Dimensions,
|
||||
Modal,
|
||||
Platform,
|
||||
StyleSheet,
|
||||
Text,
|
||||
TouchableWithoutFeedback,
|
||||
View,
|
||||
} from 'react-native'
|
||||
import LucideIcon from './LucideIcon'
|
||||
|
||||
const WINDOW = Dimensions.get('window')
|
||||
|
||||
interface DrawerMenuProps {
|
||||
visible: boolean
|
||||
duration?: number
|
||||
menuPosition?: 'left' | 'right' | 'top' | 'bottom'
|
||||
Height?: DimensionValue
|
||||
hideModal: () => void
|
||||
children?: ReactNode
|
||||
isCircle?: boolean
|
||||
CircleNum?: number
|
||||
isShadowHideModal?: boolean
|
||||
title?: string
|
||||
}
|
||||
|
||||
export default function Drawer({
|
||||
visible,
|
||||
Height,
|
||||
hideModal,
|
||||
duration = 300,
|
||||
isCircle,
|
||||
menuPosition = 'bottom',
|
||||
children,
|
||||
CircleNum,
|
||||
isShadowHideModal = true,
|
||||
title,
|
||||
}: DrawerMenuProps) {
|
||||
const [menuPositionStyle, setMenuPositionStyle] = useState<ViewStyle>()
|
||||
const [drawerMenuBox, setDrawerMenuBox] = useState<ViewStyle>()
|
||||
const [innerVisible, setInnerVisible] = useState(false)
|
||||
|
||||
const anim = useRef(new Animated.Value(0)).current
|
||||
|
||||
/* ================== 同步外部 visible ================== */
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
setInnerVisible(true)
|
||||
open()
|
||||
}
|
||||
else {
|
||||
close()
|
||||
}
|
||||
}, [visible])
|
||||
|
||||
/* ================== 布局样式 ================== */
|
||||
useEffect(() => {
|
||||
switch (menuPosition) {
|
||||
case 'left':
|
||||
setMenuPositionStyle({
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
borderTopRightRadius: isCircle ? CircleNum : 0,
|
||||
borderBottomRightRadius: isCircle ? CircleNum : 0,
|
||||
})
|
||||
setDrawerMenuBox({
|
||||
width: Height,
|
||||
height: WINDOW.height,
|
||||
position: 'absolute',
|
||||
left: 0,
|
||||
top: 0,
|
||||
})
|
||||
break
|
||||
|
||||
case 'right':
|
||||
setMenuPositionStyle({
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
borderTopLeftRadius: isCircle ? CircleNum : 0,
|
||||
borderBottomLeftRadius: isCircle ? CircleNum : 0,
|
||||
})
|
||||
setDrawerMenuBox({
|
||||
width: Height,
|
||||
height: WINDOW.height,
|
||||
position: 'absolute',
|
||||
right: 0,
|
||||
top: 0,
|
||||
})
|
||||
break
|
||||
|
||||
case 'top':
|
||||
setMenuPositionStyle({
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
borderBottomLeftRadius: isCircle ? CircleNum : 0,
|
||||
borderBottomRightRadius: isCircle ? CircleNum : 0,
|
||||
})
|
||||
setDrawerMenuBox({
|
||||
width: WINDOW.width,
|
||||
height: Height,
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
})
|
||||
break
|
||||
|
||||
case 'bottom':
|
||||
default:
|
||||
setMenuPositionStyle({
|
||||
flex: 1,
|
||||
backgroundColor: '#fff',
|
||||
borderTopLeftRadius: isCircle ? CircleNum : 0,
|
||||
borderTopRightRadius: isCircle ? CircleNum : 0,
|
||||
})
|
||||
setDrawerMenuBox({
|
||||
width: WINDOW.width,
|
||||
height: Height,
|
||||
position: 'absolute',
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
})
|
||||
break
|
||||
}
|
||||
}, [menuPosition, Height, isCircle, CircleNum])
|
||||
|
||||
/* ================== 动画 ================== */
|
||||
const open = () => {
|
||||
Animated.timing(anim, {
|
||||
toValue: 1,
|
||||
duration,
|
||||
useNativeDriver: true,
|
||||
}).start()
|
||||
}
|
||||
|
||||
const close = () => {
|
||||
Animated.timing(anim, {
|
||||
toValue: 0,
|
||||
duration,
|
||||
useNativeDriver: true,
|
||||
}).start(({ finished }) => {
|
||||
if (finished) {
|
||||
setInnerVisible(false)
|
||||
hideModal()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/* ================== 位移 ================== */
|
||||
const getTransform = useCallback(() => {
|
||||
switch (menuPosition) {
|
||||
case 'left':
|
||||
return {
|
||||
translateX: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [-WINDOW.width, 0],
|
||||
}),
|
||||
}
|
||||
case 'right':
|
||||
return {
|
||||
translateX: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [WINDOW.width, 0],
|
||||
}),
|
||||
}
|
||||
case 'top':
|
||||
return {
|
||||
translateY: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [-WINDOW.height, 0],
|
||||
}),
|
||||
}
|
||||
case 'bottom':
|
||||
default:
|
||||
return {
|
||||
translateY: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [WINDOW.height, 0],
|
||||
}),
|
||||
}
|
||||
}
|
||||
}, [menuPosition])
|
||||
|
||||
if (!innerVisible)
|
||||
return null
|
||||
|
||||
return (
|
||||
<Modal transparent visible animationType="none">
|
||||
{/* 遮罩 */}
|
||||
<TouchableWithoutFeedback onPress={() => isShadowHideModal && close()}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.mask,
|
||||
{
|
||||
opacity: anim.interpolate({
|
||||
inputRange: [0, 1],
|
||||
outputRange: [0, 0.5],
|
||||
}),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</TouchableWithoutFeedback>
|
||||
|
||||
{/* Drawer */}
|
||||
<View style={drawerMenuBox}>
|
||||
<Animated.View
|
||||
style={[
|
||||
styles.container,
|
||||
{
|
||||
transform: [getTransform()],
|
||||
},
|
||||
]}
|
||||
>
|
||||
<View style={menuPositionStyle}>
|
||||
{menuPosition !== 'bottom' && (
|
||||
<View style={{ paddingTop: Platform.OS === 'ios' ? 50 : 0 }} />
|
||||
)}
|
||||
|
||||
<View style={styles.headTitle}>
|
||||
<Text style={styles.title}>{title}</Text>
|
||||
<LucideIcon
|
||||
icon="X"
|
||||
size={24}
|
||||
color="black"
|
||||
style={{ position: 'absolute', right: 15, top: 11 }}
|
||||
onPress={close}
|
||||
/>
|
||||
</View>
|
||||
|
||||
{children}
|
||||
</View>
|
||||
</Animated.View>
|
||||
</View>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
const styles = StyleSheet.create({
|
||||
mask: {
|
||||
...StyleSheet.absoluteFillObject,
|
||||
backgroundColor: '#000',
|
||||
},
|
||||
container: {
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
position: 'absolute',
|
||||
},
|
||||
headTitle: {
|
||||
height: 40,
|
||||
width: '100%',
|
||||
padding: 10,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
},
|
||||
title: {
|
||||
fontSize: 18,
|
||||
},
|
||||
})
|
||||
@@ -3,6 +3,8 @@ import { Cell, CellGroup } from './Cell'
|
||||
import Checkbox from './Checkbox'
|
||||
import DatePicker from './DatePicker'
|
||||
import Divider from './Divider'
|
||||
|
||||
import Drawer from './Drawer'
|
||||
import Form, { FormRef } from './Form'
|
||||
import FormItem from './FormItem'
|
||||
import Input from './Input'
|
||||
@@ -19,6 +21,7 @@ export {
|
||||
Checkbox as ACheckbox,
|
||||
DatePicker as ADatePicker,
|
||||
Divider as ADivider,
|
||||
Drawer as ADrawer,
|
||||
Form as AForm,
|
||||
FormItem as AFormItem,
|
||||
FormRef as AFormRef,
|
||||
|
||||
@@ -32,8 +32,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,
|
||||
|
||||
Reference in new issue
Block a user