This commit is contained in:
oneao committed 2026-02-27 17:29:28 +08:00
1 parent 1903861354
commit 1b1a54f7dd
35 files changed
+1185 -535

No files matched your search

+57 -27
View File
@@ -1,34 +1,64 @@
import { useRouter } from 'expo-router'
import { Text, View } from 'react-native'
import { ScrollView } from 'react-native-gesture-handler'
import { AButton, ACell, ACellGroup } from '@/components/common/ao'
import EmptyState from '@/components/common/EmptyStatus'
import { Grid, GridItem } from '@/components/ui/grid'
import { useMoneyCategoryStore } from '@/store/moneyCategory'
import { useSpaceStore } from '@/store/space'
import { useCallback } from 'react'
import { StyleSheet, Text, View } from 'react-native'
import Sortable from 'react-native-sortables'
export default function Demo() {
const router = useRouter()
const spaceStore = useSpaceStore()
const DATA = Array.from({ length: 10 }, (_, index) => `Item ${index + 1}`)
function NoSpaceTip() {
return (
<EmptyState
description="还没有加入空间,选择一个加入或自己创建一个,开始聊天!"
footer={
<AButton size="sm">创建 / 加入 空间</AButton>
}
/>
)
}
export default function CustomHandleExample() {
const renderItem = useCallback(
({ item }: { item: string }) => (
<View style={styles.card}>
<Text style={styles.text}>{item}</Text>
<Sortable.Handle>
<View style={styles.handle}>
<Text style={styles.handleText}>::</Text>
</View>
</Sortable.Handle>
</View>
),
[],
)
return (
<View className="flex-1">
{
spaceStore.inSpace()
? (<View></View>)
: <NoSpaceTip />
}
<View style={styles.container}>
<Sortable.Grid
activeItemScale={1.05}
columns={1}
data={DATA}
overDrag="vertical"
renderItem={renderItem}
rowGap={10}
highlight-next-line
customHandle // must be set to use a custom handle
/>
</View>
)
}
const styles = StyleSheet.create({
card: {
alignItems: 'center',
backgroundColor: '#2e8555',
borderRadius: 12,
flexDirection: 'row',
justifyContent: 'center',
padding: 24,
},
container: {
padding: 16,
},
handle: {
backgroundColor: 'rgba(0,0,0,0.2)',
borderRadius: 5,
padding: 10,
},
handleText: {
color: 'white',
fontWeight: 'bold',
},
text: {
color: 'white',
flex: 1,
fontWeight: 'bold',
},
})
+27 -32
View File
@@ -1,19 +1,19 @@
import type { ErrorBoundaryProps } from 'expo-router'
import { ToastProvider } from '@gluestack-ui/core/toast/creator'
import { useFonts } from 'expo-font'
import { Stack, usePathname } from 'expo-router'
import * as SplashScreen from 'expo-splash-screen'
import { StatusBar } from 'expo-status-bar'
import { useEffect } from 'react'
import { Modal, View } from 'react-native'
import { GestureHandlerRootView } from 'react-native-gesture-handler'
import { KeyboardProvider } from 'react-native-keyboard-controller'
import { MenuProvider } from 'react-native-popup-menu'
import { Toaster } from 'sonner-native'
import { AButton } from '@/components/common/ao'
import EmptyState from '@/components/common/EmptyStatus'
import { GluestackUIProvider } from '@/components/ui/gluestack-ui-provider'
import { useAppStore } from '@/store/app'
import { useNavStore } from '@/store/navigation'
import { ToastProviderWrapper } from '@/utils/toast'
import '@/styles/global.css'
import 'react-native-reanimated'
@@ -64,36 +64,31 @@ export default function RootLayout() {
}, [fontsLoaded])
return (
<MenuProvider>
<KeyboardProvider>
<GluestackUIProvider>
<ToastProvider>
<ToastProviderWrapper>
<GestureHandlerRootView>
<Stack
screenOptions={{
headerTitleAlign: 'center',
animation: 'slide_from_right',
contentStyle: {
backgroundColor: '#fff',
},
}}
>
<Stack.Screen
name="(tabs)"
options={{
headerShown: false,
}}
/>
<GestureHandlerRootView style={{ flex: 1 }}>
<MenuProvider>
<KeyboardProvider>
<GluestackUIProvider>
<Stack
screenOptions={{
headerTitleAlign: 'center',
animation: 'slide_from_right',
contentStyle: {
backgroundColor: '#fff',
},
}}
>
<Stack.Screen
name="(tabs)"
options={{ headerShown: false }}
/>
<Stack.Screen name="+not-found" />
</Stack>
<Stack.Screen name="+not-found" />
</Stack>
<StatusBar style="auto" />
</GestureHandlerRootView>
</ToastProviderWrapper>
</ToastProvider>
</GluestackUIProvider>
</KeyboardProvider>
</MenuProvider>
<StatusBar style="auto" />
</GluestackUIProvider>
</KeyboardProvider>
</MenuProvider>
<Toaster />
</GestureHandlerRootView>
)
}
+170 -170
View File
@@ -1,7 +1,7 @@
import { useEvent } from 'expo'
import ExpoQQ from 'expo-qq'
// import ExpoQQ from 'expo-qq'
import { useRouter } from 'expo-router'
import ExpoWeChat from 'expo-wechat'
// import ExpoWeChat from 'expo-wechat'
import { useEffect, useState } from 'react'
import { Image, Text, View } from 'react-native'
import { AButton, ACheckbox } from '@/components/common/ao'
@@ -25,174 +25,174 @@ export default function Auth() {
const userStore = useUserStore()
const appStore = useAppStore()
const onLoginFinished = useEvent(ExpoQQ, 'onLoginFinished')
const onGetUserInfo = useEvent(ExpoQQ, 'onGetUserInfo')
const authResult = useEvent(ExpoWeChat, 'onAuthResult')
// const onLoginFinished = useEvent(ExpoQQ, 'onLoginFinished')
// const onGetUserInfo = useEvent(ExpoQQ, 'onGetUserInfo')
// const authResult = useEvent(ExpoWeChat, 'onAuthResult')
async function onQQLogin() {
setLoading(true)
setForm(val => ({
...val,
type: 1,
}))
await ExpoQQ.login(['get_user_info'])
}
async function onWeChatLogin() {
setLoading(true)
// 检查微信是否安装
const isInstalled = await ExpoWeChat.isWXAppInstalled()
if (!isInstalled) {
toast.show({
title: onLoginFinished?.errorMessage || '请先安装微信',
})
setLoading(false)
return
}
setForm(val => ({
...val,
type: 0,
}))
ExpoWeChat.sendAuthRequest('snsapi_userinfo', 'state123')
}
useEffect(() => {
ExpoQQ.init('102826474', '')
ExpoWeChat.registerApp('wxdab3e21a1f7e392f', '')
}, [])
useEffect(() => {
if (!onLoginFinished) {
return
}
if (!onLoginFinished.success) {
toast.show({
title: onLoginFinished?.errorMessage || '授权失败,请检查手机是否安装 QQ 或确认授权',
})
setLoading(false)
return
}
const fetchUserInfo = async () => {
try {
const tokenInfo = await ExpoQQ.getLoginTokenInfo()
if (!tokenInfo.openId) {
toast.show({
title: tokenInfo?.errorMessage || '授权失败,请检查手机是否安装 QQ 或确认授权',
})
setLoading(false)
return
}
setForm(val => ({
...val,
openid: String(tokenInfo.openId),
}))
await ExpoQQ.sendGetUserInfoReq()
}
catch (e) {
console.error('QQ 登录未完成', e)
setLoading(false)
}
}
fetchUserInfo()
}, [onLoginFinished])
// QQ登录
useEffect(() => {
const handleLogin = async () => {
if (!onGetUserInfo)
return
try {
const loginRes = await loginQqApi({
openid: form.openid,
nickname: onGetUserInfo.nickname || '',
avatar: onGetUserInfo.figureurlQq_2 || onGetUserInfo.figureurl_2,
})
if (loginRes.isSuccess) {
userStore.setUserInfo({
token: loginRes.data.token,
id: loginRes.data.userId,
nickname: loginRes.data.nickname,
avatar: loginRes.data.avatar,
})
router.push('/auth/space')
}
}
catch (err) {
console.error('登录接口出错', err)
}
finally {
setLoading(false)
}
}
handleLogin()
}, [onGetUserInfo])
// 微信登录
useEffect(() => {
if (!authResult)
return
const handleLogin = async () => {
if (authResult.errorCode === 0) {
try {
const loginRes = await loginWxApi({
code: authResult.code,
})
if (loginRes.isSuccess) {
userStore.setUserInfo({
token: loginRes.data.token,
id: loginRes.data.userId,
nickname: loginRes.data.nickname,
avatar: loginRes.data.avatar,
})
router.push('/')
}
}
catch (err) {
console.error('登录接口出错', err)
}
finally {
setLoading(false)
}
}
else {
console.log('授权失败:', authResult.errorMessage)
setLoading(false)
}
}
handleLogin()
}, [authResult])
// function testLogin() {
// // eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo3NjY3ODU5ODIxNjUwNjEsImlhdCI6MTc2OTM0ODI0Nn0.17ZNaHD3HSLM8dVn_JQK-gvsG9MKj47IRYV9D4xZkzs
// userStore.setUserInfo({
// token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo3NzgxMTUwMzIxMDA5MzMsImlhdCI6MTc3MjExMDQ5N30.Ab7_YNwftJigktnPt8f-FwwcLeS45CkrH0jT2jLjRtA',
// id: 778115032100933,
// nickname: 'Zhang',
// avatar: 'http://thirdqq.qlogo.cn/ek_qqapp/AQHVialeUibE42oV8RvTTVZ4tyRlPYeJrUibWTwtCPfV68E4rSzn9cw00GkXQ3QDTiawwhjoqQb7/100',
// })
// appStore.init()
// router.push('/')
// async function onQQLogin() {
// setLoading(true)
// setForm(val => ({
// ...val,
// type: 1,
// }))
// await ExpoQQ.login(['get_user_info'])
// }
// async function onWeChatLogin() {
// setLoading(true)
// // 检查微信是否安装
// const isInstalled = await ExpoWeChat.isWXAppInstalled()
// if (!isInstalled) {
// toast.show({
// title: onLoginFinished?.errorMessage || '请先安装微信',
// })
// setLoading(false)
// return
// }
// setForm(val => ({
// ...val,
// type: 0,
// }))
// ExpoWeChat.sendAuthRequest('snsapi_userinfo', 'state123')
// }
// useEffect(() => {
// ExpoQQ.init('102826474', '')
// ExpoWeChat.registerApp('wxdab3e21a1f7e392f', '')
// }, [])
// useEffect(() => {
// if (!onLoginFinished) {
// return
// }
// if (!onLoginFinished.success) {
// toast.show({
// title: onLoginFinished?.errorMessage || '授权失败,请检查手机是否安装 QQ 或确认授权',
// })
// setLoading(false)
// return
// }
// const fetchUserInfo = async () => {
// try {
// const tokenInfo = await ExpoQQ.getLoginTokenInfo()
// if (!tokenInfo.openId) {
// toast.show({
// title: tokenInfo?.errorMessage || '授权失败,请检查手机是否安装 QQ 或确认授权',
// })
// setLoading(false)
// return
// }
// setForm(val => ({
// ...val,
// openid: String(tokenInfo.openId),
// }))
// await ExpoQQ.sendGetUserInfoReq()
// }
// catch (e) {
// console.error('QQ 登录未完成', e)
// setLoading(false)
// }
// }
// fetchUserInfo()
// }, [onLoginFinished])
// // QQ登录
// useEffect(() => {
// const handleLogin = async () => {
// if (!onGetUserInfo)
// return
// try {
// const loginRes = await loginQqApi({
// openid: form.openid,
// nickname: onGetUserInfo.nickname || '',
// avatar: onGetUserInfo.figureurlQq_2 || onGetUserInfo.figureurl_2,
// })
// if (loginRes.isSuccess) {
// userStore.setUserInfo({
// token: loginRes.data.token,
// id: loginRes.data.userId,
// nickname: loginRes.data.nickname,
// avatar: loginRes.data.avatar,
// })
// router.push('/auth/space')
// }
// }
// catch (err) {
// console.error('登录接口出错', err)
// }
// finally {
// setLoading(false)
// }
// }
// handleLogin()
// }, [onGetUserInfo])
// // 微信登录
// useEffect(() => {
// if (!authResult)
// return
// const handleLogin = async () => {
// if (authResult.errorCode === 0) {
// try {
// const loginRes = await loginWxApi({
// code: authResult.code,
// })
// if (loginRes.isSuccess) {
// userStore.setUserInfo({
// token: loginRes.data.token,
// id: loginRes.data.userId,
// nickname: loginRes.data.nickname,
// avatar: loginRes.data.avatar,
// })
// router.push('/')
// }
// }
// catch (err) {
// console.error('登录接口出错', err)
// }
// finally {
// setLoading(false)
// }
// }
// else {
// console.log('授权失败:', authResult.errorMessage)
// setLoading(false)
// }
// }
// handleLogin()
// }, [authResult])
function testLogin() {
// eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo3NjY3ODU5ODIxNjUwNjEsImlhdCI6MTc2OTM0ODI0Nn0.17ZNaHD3HSLM8dVn_JQK-gvsG9MKj47IRYV9D4xZkzs
userStore.setUserInfo({
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo3NzgxMTUwMzIxMDA5MzMsImlhdCI6MTc3MjExMDQ5N30.Ab7_YNwftJigktnPt8f-FwwcLeS45CkrH0jT2jLjRtA',
id: 778115032100933,
nickname: 'Zhang',
avatar: 'http://thirdqq.qlogo.cn/ek_qqapp/AQHVialeUibE42oV8RvTTVZ4tyRlPYeJrUibWTwtCPfV68E4rSzn9cw00GkXQ3QDTiawwhjoqQb7/100',
})
appStore.init()
router.push('/')
}
return (
<View className="flex-1 bg-white px-6 justify-center">
<CustomHeader
@@ -210,7 +210,7 @@ export default function Auth() {
</View>
<View className="flex flex-col gap-4">
<AButton
{/* <AButton
leftIcon={
<IconWeixin color="#fff" />
}
@@ -229,7 +229,7 @@ export default function Auth() {
onPress={onQQLogin}
>
QQ
</AButton>
</AButton> */}
<AButton
variant="outline"
@@ -237,7 +237,7 @@ export default function Auth() {
<IconQq />
}
loading={loading}
// onPress={testLogin}
onPress={testLogin}
>
模拟登录
</AButton>
+259 -42
View File
@@ -1,61 +1,278 @@
import { Text, View } from 'react-native'
import { AButton, LucideIcon } from '@/components/common/ao'
import type { Space } from '@/types/space/space'
import { useCallback, useState } from 'react'
import { Pressable, Text, View } from 'react-native'
import Animated, { useAnimatedRef } from 'react-native-reanimated'
import Sortable from 'react-native-sortables'
import { toast } from 'sonner-native'
import { AButton, ADivider, AInput, LucideIcon } from '@/components/common/ao'
import { AModal } from '@/components/common/ao/index'
import EmptyState from '@/components/common/EmptyStatus'
import CustomHeader from '@/components/custom/Header'
// name renshu
import { Badge, BadgeText } from '@/components/ui/badge'
import { createSpaceApi } from '@/request/api/space/space'
export default function AuthSpace() {
return (
<View className="flex-1">
<CustomHeader title="空间列表" />
<View className="flex-1 px-4 pt-2 pb-8">
<View>
<Text className="text-lg font-semibold">你的空间</Text>
<Text className="text-sm text-gray-600">选择要进入的空间</Text>
</View>
const scrollableRef = useAnimatedRef<Animated.ScrollView>()
<View className="flex-1 my-4">
{/* <View className="items-center justify-center">
<EmptyState title="暂无空间" description="请选择 创建 或 加入 空间" />
</View> */}
const [selectedId, setSelectedId] = useState<number | null>(null)
// 新建
const [createOpen, setCreateOpen] = useState(false)
const [createForm, setCreateForm] = useState({
name: '',
})
const spaces: Space[] = [
{
id: 1,
name: 'A 的空间',
role: 1,
member_count: 8,
is_default: 1,
sort_number: 1,
user_name: '张三',
},
{
id: 2,
name: 'B 的空间',
role: 2,
member_count: 5,
is_default: 0,
sort_number: 2,
user_name: '李四',
},
{
id: 3,
name: 'C 的空间',
role: 3,
member_count: 12,
is_default: 0,
sort_number: 3,
user_name: '王五',
},
{
id: 4,
name: 'D 的空间',
role: 2,
member_count: 6,
is_default: 0,
sort_number: 4,
user_name: '赵六',
},
{
id: 5,
name: 'E 的空间',
role: 1,
member_count: 3,
is_default: 0,
sort_number: 5,
user_name: '钱七',
},
{
id: 6,
name: 'F 的空间',
role: 2,
member_count: 9,
is_default: 0,
sort_number: 6,
user_name: '孙八',
},
{
id: 7,
name: 'G 的空间',
role: 3,
member_count: 4,
is_default: 0,
sort_number: 7,
user_name: '周九',
},
]
const renderItem = useCallback(
({ item }: { item: Space }) => (
<Pressable
key={item.id}
onPress={() => setSelectedId(item.id)}
className={`rounded-md flex-row items-center justify-between px-4 py-3 border
${item.id === selectedId ? 'border-gray-700' : 'border-gray-300'}`}
>
<View className="flex-row items-center">
<Sortable.Handle>
<LucideIcon
icon="Grip"
size={20}
className="mr-4"
/>
</Sortable.Handle>
<View>
<View className="rounded-lg p-3 flex flex-row items-center justify-between bg-gray-100">
<View className="flex-row items-center">
<View className="mr-4">
<LucideIcon
icon="Grip"
size={20}
color="#666"
/>
</View>
<View className="flex-row items-center gap-2">
<Text
className="font-semibold text-gray-900"
>
{item.name}
</Text>
<View className="">
<View>
<Text className="font-semibold">A 的空间</Text>
</View>
<Text className="text-sm text-gray-500 mt-1">张三 · 8 名成员</Text>
</View>
<Badge size="sm" variant="solid" action="muted">
<BadgeText>创建者</BadgeText>
</Badge>
<Badge size="sm" variant="solid" action="info">
<BadgeText>默认</BadgeText>
</Badge>
</View>
<View className="flex-row items-center gap-3 mt-2">
<View className="flex-row gap-1">
<LucideIcon icon="UserRound" size={12} />
<Text
className={`text-xs `}
>
用户A
</Text>
</View>
<View>
<LucideIcon
icon="Bolt"
size={20}
color="#aaa"
style={{ padding: 6 }}
/>
<View className="flex-row gap-1">
<LucideIcon icon="UsersRound" size={12} />
<Text
className={`text-xs `}
>
0
</Text>
</View>
</View>
</View>
</View>
<View className="flex gap-3">
<AButton>创建新空间</AButton>
<AButton variant="outline">加入其他空间</AButton>
<LucideIcon
icon="Bolt"
size={20}
className="p-1"
/>
</Pressable>
),
[],
)
return (
<View className="flex-1 bg-white">
{/* Header */}
<CustomHeader title="空间列表" />
<View className="flex-1 px-4">
<View className="">
<Text className="text-sm text-gray-500 mb-2">
选择或创建空间
</Text>
<View className="flex-row gap-3">
<AButton
className="flex-1 rounded-full"
size="sm"
leftIcon={<LucideIcon icon="Plus" color="#fff" />}
onPress={() => setCreateOpen(true)}
>
创建新空间
</AButton>
<AButton
variant="outline"
className="flex-1 rounded-full"
size="sm"
leftIcon={<LucideIcon icon="HousePlus" />}
>
加入其他空间
</AButton>
</View>
</View>
<ADivider text="我的空间" className="py-2" />
<Animated.ScrollView
className="flex-1"
ref={scrollableRef}
>
{spaces.length === 0
? (
<View className="flex-1 items-center justify-center mt-12">
<EmptyState
title="暂无空间"
description="请选择 创建 或 加入 空间"
/>
</View>
)
: (
<Sortable.Grid
scrollableRef={scrollableRef}
activeItemScale={1}
columns={1}
data={spaces}
overDrag="vertical"
renderItem={renderItem}
rowGap={10}
highlight-next-line
customHandle
autoScrollActivationOffset={200}
/>
)}
</Animated.ScrollView>
<View className="py-2">
<AButton size="sm" leftIcon={<LucideIcon icon="CircleArrowRight" color="#fff" />}>进入空间</AButton>
</View>
</View>
<AModal
title="创建新空间"
showCloseIcon
body={
(
<View className="gap-2">
<Text>空间名称</Text>
<AInput
className="flex-1"
value={createForm.name}
onChangeText={(val) => {
setCreateForm(prev => ({
...prev,
name: val,
}))
}}
/>
</View>
)
}
isOpen={createOpen}
onCancel={() => {
setCreateOpen(false)
setCreateForm({
name: '',
})
}}
onClose={() => {
setCreateOpen(false)
setCreateForm({
name: '',
})
}}
onConfirm={async () => {
if (!createForm.name) {
toast.warning('请输入空间名称')
return
}
await createSpaceApi({
name: createForm.name,
}).then((res) => {
console.log(res)
if (res.isSuccess) {
toast.success('创建成功')
setCreateOpen(false)
setCreateForm({
name: '',
})
}
})
}}
>
</AModal>
</View>
)
}
@@ -39,14 +39,9 @@ const Button: React.FC<CustomButtonProps> = ({
const isDisabled = disabled || loading
const hasChildren = !!children
const renderIcon = (
icon?: IconProp,
position: 'left' | 'right' = 'left',
) => {
if (!icon)
return null
if (loading && position === 'left') {
const renderLeftIcon = () => {
// 左侧 loading
if (loading) {
const spinnerColor = variant === 'outline'
? 'rgba(0,0,0,0.6)'
: 'rgba(255,255,255,0.9)'
@@ -57,17 +52,25 @@ const Button: React.FC<CustomButtonProps> = ({
)
}
const node = typeof icon === 'function'
? icon()
: icon
// 左侧 icon
if (!leftIcon)
return null
const node = typeof leftIcon === 'function' ? leftIcon() : leftIcon
return (
<View className={hasChildren
? position === 'left'
? 'mr-2'
: 'ml-2'
: undefined}
>
<View className={hasChildren ? 'mr-1' : undefined}>
{node}
</View>
)
}
const renderRightIcon = () => {
if (!rightIcon)
return null
const node = typeof rightIcon === 'function' ? rightIcon() : rightIcon
return (
<View className={hasChildren ? 'ml-1' : undefined}>
{node}
</View>
)
@@ -83,9 +86,9 @@ const Button: React.FC<CustomButtonProps> = ({
variant={variant}
size={size}
>
{renderIcon(leftIcon, 'left')}
{renderLeftIcon()}
{children && <ButtonText>{children}</ButtonText>}
{renderIcon(rightIcon, 'right')}
{renderRightIcon()}
</NativeButton>
)
}
@@ -1,7 +1,8 @@
import type { ViewStyle } from 'react-native'
import { useState } from 'react'
import { Text } from 'react-native'
import { Heading } from '@/components/ui/heading'
import { CloseIcon, Icon } from '@/components/ui/icon'
import {
Modal,
ModalBackdrop,
@@ -19,7 +20,7 @@ interface AModalProps {
body?: string | React.ReactNode
footer?: React.ReactNode
onClose?: () => void
onConfirm?: () => void
onConfirm?: () => Promise<void> | void
onCancel?: () => void
showBackdrop?: boolean
showCloseIcon?: boolean
@@ -41,6 +42,29 @@ export default function AModal({
className,
style,
}: AModalProps) {
const [innerLoading, setInnerLoading] = useState(false)
const handleConfirm = () => {
if (!onConfirm || innerLoading)
return
try {
const result = onConfirm()
// 只有当返回的是 Promise 才开启 loading
if (result && typeof (result as Promise<void>).then === 'function') {
setInnerLoading(true)
result.finally(() => {
setInnerLoading(false)
})
}
}
catch (error) {
console.error(error)
}
}
return (
<Modal
isOpen={isOpen}
@@ -55,7 +79,7 @@ export default function AModal({
}}
>
<ModalHeader>
{title && (typeof title === 'string' ? <Text>{title}</Text> : title)}
{title && (typeof title === 'string' ? <Heading>{title}</Heading> : title)}
{
showCloseIcon && (
<ModalCloseButton>
@@ -76,11 +100,14 @@ export default function AModal({
action="secondary"
className="mr-3"
onPress={onClose}
size="sm"
>
取消
</AButton>
<AButton
onPress={onConfirm}
onPress={handleConfirm}
size="sm"
loading={innerLoading}
>
确认
</AButton>
@@ -1,26 +1,28 @@
import React, { useEffect } from 'react';
import { config } from './config';
import { View, ViewProps } from 'react-native';
import { OverlayProvider } from '@gluestack-ui/core/overlay/creator';
import { ToastProvider } from '@gluestack-ui/core/toast/creator';
import { useColorScheme } from 'nativewind';
import type { ViewProps } from 'react-native'
import { OverlayProvider } from '@gluestack-ui/core/overlay/creator'
import { ToastProvider } from '@gluestack-ui/core/toast/creator'
import { useColorScheme } from 'nativewind'
import React, { useEffect } from 'react'
import { View } from 'react-native'
import { Toaster } from 'sonner-native'
import { config } from './config'
export type ModeType = 'light' | 'dark' | 'system';
export type ModeType = 'light' | 'dark' | 'system'
export function GluestackUIProvider({
mode = 'light',
...props
}: {
mode?: ModeType;
children?: React.ReactNode;
style?: ViewProps['style'];
mode?: ModeType
children?: React.ReactNode
style?: ViewProps['style']
}) {
const { colorScheme, setColorScheme } = useColorScheme();
const { colorScheme, setColorScheme } = useColorScheme()
useEffect(() => {
setColorScheme(mode);
setColorScheme(mode)
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [mode]);
}, [mode])
return (
<View
@@ -34,5 +36,5 @@ export function GluestackUIProvider({
<ToastProvider>{props.children}</ToastProvider>
</OverlayProvider>
</View>
);
)
}
+3 -5
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,
@@ -54,9 +54,7 @@ const alovaInstance = createAlova({
onError: (error: AxiosError) => {
const status = error.status
const config = error.config
console.warn(`[HTTP ${status}] ${config?.method?.toUpperCase()} ${config?.url}`)
console.log(error)
switch (status) {
case 400:
@@ -1,3 +1,14 @@
export interface Space {
id: number
name: string
role: number
member_count: number
is_default: number
user_name: string
sort_number: number
}
// 创建空间
export interface CreateSpaceParams {
name: string