This commit is contained in:
oneao committed 2025-11-18 22:35:18 +08:00
1 parent 1445ed3b6c
commit 7419021927
19 files changed
+474 -327

No files matched your search

@@ -1,39 +1,125 @@
import { DatePicker } from '@yz1311/react-native-wheel-picker'
import { useState } from 'react'
import { Button, Text, View } from 'react-native'
import { ADatePicker } from '@/components/common/ao'
import * as React from 'react'
import { Pressable, StyleSheet, Text, useWindowDimensions, View } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { TabView } from 'react-native-tab-view'
export default function Chat() {
const [show, setShow] = useState(false)
// 两个示例页面
function PageOne() {
return (
<>
<View className="mt-12">
<Button
title="测试"
onPress={() => {
setShow(true)
}}
>
</Button>
{/* <DatePicker
isModal={true}
modalVisible={show}
onPickerCancel={() => {
setShow(false)
}}
/> */}
<ADatePicker
show={show}
onConfirm={(val) => {
setShow(false)
}}
onClose={() => {
setShow(false)
}}
/>
</View>
</>
<View style={styles.page}>
<Text style={{ fontSize: 18 }}>第一页内容</Text>
</View>
)
}
function PageTwo() {
return (
<View style={styles.page}>
<Text style={{ fontSize: 18 }}>第二页内容</Text>
</View>
)
}
export default function SimpleTabView() {
const layout = useWindowDimensions()
const [index, setIndex] = React.useState(0)
const routes = [
{ key: 'one', title: 'Tab 1' },
{ key: 'two', title: 'Tab 2' },
]
const renderScene = ({ route }: { route: { key: string } }) => {
switch (route.key) {
case 'one':
return <PageOne />
case 'two':
return <PageTwo />
default:
return null
}
}
return (
<SafeAreaView style={{ flex: 1, backgroundColor: '#fff' }}>
{/* Header + Tab */}
<View style={styles.header}>
<Pressable onPress={() => console.log('Left button clicked')}>
<Text style={styles.headerButton}>返回</Text>
</Pressable>
<View style={styles.tabContainer}>
{routes.map((r, i) => {
const focused = i === index
return (
<Pressable key={r.key} onPress={() => setIndex(i)} style={styles.tabButton}>
<Text style={[styles.tabText, focused && styles.tabTextActive]}>{r.title}</Text>
{focused && <View style={styles.underline} />}
</Pressable>
)
})}
</View>
<Pressable onPress={() => console.log('Right button clicked')}>
<Text style={styles.headerButton}>添加</Text>
</Pressable>
</View>
{/* TabView */}
<TabView
navigationState={{ index, routes }}
renderScene={renderScene}
onIndexChange={setIndex}
initialLayout={{ width: layout.width }}
renderTabBar={() => null} // 不使用默认 TabBar
/>
<View><Text>123</Text></View>
</SafeAreaView>
)
}
const styles = StyleSheet.create({
header: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 8,
borderBottomWidth: 0.5,
borderColor: '#ddd',
},
headerButton: {
fontSize: 16,
color: '#007AFF',
},
tabContainer: {
flexDirection: 'row',
justifyContent: 'center',
flex: 1,
},
tabButton: {
marginHorizontal: 12,
alignItems: 'center',
paddingVertical: 4,
},
tabText: {
fontSize: 16,
color: '#666',
},
tabTextActive: {
color: '#007AFF',
fontWeight: 'bold',
},
underline: {
height: 2,
backgroundColor: '#007AFF',
width: '100%',
marginTop: 2,
},
page: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
},
})
@@ -1,78 +1,18 @@
import React, { useState } from 'react'
import { FlatList, Text, View } from 'react-native'
import {
Menu,
MenuOption,
MenuOptions,
MenuProvider,
MenuTrigger,
} from 'react-native-popup-menu'
import { Button, ButtonText } from '@/components/ui/button'
import { useRouter } from 'expo-router'
import { Text, View } from 'react-native'
import { AButton } from '@/components/common/ao'
export default function Example() {
const data = Array.from({ length: 10 }, (_, i) => i + 1)
const [activeIndex, setActiveIndex] = useState<number | null>(null)
const renderItem = ({ item: index }: { item: number }) => {
const isActive = activeIndex === index
return (
<View
style={{
marginVertical: 10,
padding: 10,
borderWidth: 1,
borderColor: '#ccc',
borderRadius: 6,
}}
>
<Menu
onOpen={() => setActiveIndex(index)}
onClose={() => setActiveIndex(null)}
>
{/* MenuTrigger 应该包装一个可触摸的组件 */}
<MenuTrigger
customStyles={{
TriggerTouchableComponent: Button, // 使用你的 Button 组件
}}
>
{/* 触发区域的内容 */}
<ButtonText>
Item
{' '}
{index}
{' '}
{isActive ? '(active)' : ''}
</ButtonText>
</MenuTrigger>
<MenuOptions>
<MenuOption onSelect={() => alert(`Clicked Option A for ${index}`)}>
<Text>
Option A -
{index}
</Text>
</MenuOption>
<MenuOption onSelect={() => alert(`Clicked Option B for ${index}`)}>
<Text style={{ color: 'red' }}>
Option B -
{index}
</Text>
</MenuOption>
<MenuOption onSelect={() => setActiveIndex(null)}>
<Text>Close Menu</Text>
</MenuOption>
</MenuOptions>
</Menu>
</View>
)
}
export default function Love() {
const router = useRouter()
return (
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={item => item.toString()}
/>
<View className="mt-20">
<AButton onPress={() => {
router.navigate('/auth')
}}
>
跳转到登录页面
</AButton>
</View>
)
}
@@ -1,4 +1,4 @@
import type { MoneyCategoryItem, QueryMoneyCategoryResult } from '@/types/money/category'
import type { MoneyCategoryItem } from '@/types/money/category'
import type { MoneyRecordBase, MoneyRecordItem } from '@/types/money/record'
import Big from 'big.js'
import { useEffect, useMemo, useState } from 'react'
@@ -16,6 +16,7 @@ import {
import { useKeyboardHeight } from '@/hooks/useKeyboardHeight'
import { queryMoneyCategoryApi } from '@/request/api/money/category'
import { insertMoneyRecordApi, updateMoneyRecordApi } from '@/request/api/money/record'
import { useMoneyCategoryStore } from '@/store/moneyCategory'
import { getCurrentDate, toDateString } from '@/utils/time'
import { toast } from '@/utils/toast'
import MoneyCategoryIcon from './MoneyCategoryIcon'
@@ -65,6 +66,8 @@ export default function AddMoneyDrawer({
onConfirm,
updateData,
}: AddMoneyDrawerProps) {
const moneyCategoryStore = useMoneyCategoryStore()
const insets = useSafeAreaInsets()
// 选择顺序:0 支出 1收入
const [selectedIndex, setSelectedIndex] = useState(0)
@@ -76,11 +79,6 @@ export default function AddMoneyDrawer({
const [showDatePicker, setShowDatePicker] = useState(false)
// 备注聚焦
const [remarkIsFocused, setRemarkIsFocused] = useState(false)
// 分类
const [categories, setCategories] = useState<QueryMoneyCategoryResult>({
income: [],
expense: [],
})
// 选中分类
const [selectedCategory, setSelectedCategory] = useState<MoneyCategoryItem | null>(null)
@@ -106,10 +104,13 @@ export default function AddMoneyDrawer({
const fetchCategories = async () => {
try {
const res = await queryMoneyCategoryApi()
if (res.isSuccess) {
setCategories(res.data)
if (moneyCategoryStore.categories.length === 0) {
const res = await queryMoneyCategoryApi()
if (res.isSuccess) {
moneyCategoryStore.setCategories(res.data)
}
}
console.log(moneyCategoryStore.categories)
}
catch (err) {
console.error('获取分类失败', err)
@@ -128,7 +129,7 @@ export default function AddMoneyDrawer({
if (type === 0) {
// 支出
categories.expense.forEach((item) => {
moneyCategoryStore.expense.forEach((item) => {
if (item.id === updateData.category_id) {
setSelectedCategory(item)
}
@@ -136,14 +137,14 @@ export default function AddMoneyDrawer({
}
else if (type === 1) {
// 收入
categories.income.forEach((item) => {
moneyCategoryStore.income.forEach((item) => {
if (item.id === updateData.category_id) {
setSelectedCategory(item)
}
})
}
}
}, [categories])
}, [isOpen])
function resetData() {
// 清空键盘记录
@@ -399,7 +400,6 @@ export default function AddMoneyDrawer({
})
.then((res) => {
if (res.isSuccess) {
const result: MoneyRecordItem = {
...res.data,
category_name: selectedCategory.name,
@@ -466,9 +466,8 @@ export default function AddMoneyDrawer({
}}
>
{(() => {
const list = selectedIndex === 1 ? categories.income : categories.expense
const list = selectedIndex === 1 ? moneyCategoryStore.income : moneyCategoryStore.expense
// useMemo 生成补全后的列表
const memoItems = useMemo(() => {
const items = [...list]
const remainder = items.length % 4
@@ -10,7 +10,6 @@ import {
} from 'react-native-popup-menu'
import { LucideIcon } from '@/components/common/ao'
import EmptyState from '@/components/common/EmptyStatus'
import { Badge, BadgeText } from '@/components/ui/badge'
import MoneyCategoryIcon from './MoneyCategoryIcon'
interface StaticTransactionsProps {
@@ -112,7 +111,7 @@ export default function StaticTransactions({
{
index === 1 && (
<Text className="text-gray-500 text-xs italic">
操作人:张三
张三
</Text>
)
}
@@ -29,7 +29,7 @@ export default function ListItem({ icon, text, href, onPress }: ListItemProps) {
className="flex flex-row items-center py-3"
onPress={handlePress}
>
<LucideIcon icon={icon} size={24} strokeWidth={1.5} />
<LucideIcon icon={icon} size={20} strokeWidth={1.5} />
<Text className="text-base ml-3">{text}</Text>
@@ -1,29 +0,0 @@
import type { StepLayoutItem } from '@/components/custom/StepLayout'
import { Slot, Stack, useRouter } from 'expo-router'
import { Image, Text, View } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import StepLayout from '@/components/custom/StepLayout'
import CustomTabbar from '@/components/custom/Tabbar'
const steps: StepLayoutItem[] = [
{
path: '/auth/index',
showHearder: false,
},
{
title: '登录',
path: '/auth/login',
},
{
title: '注册',
path: '/auth/register',
},
]
export default function AuthLayout() {
return (
<StepLayout steps={steps}>
<Slot />
</StepLayout>
)
}
@@ -1,5 +1,6 @@
import { useRouter } from 'expo-router'
import { Image, Text, View } from 'react-native'
import CustomHeader from '@/components/custom/Header'
import { Button, ButtonText } from '@/components/ui/button'
export default function AuthIndex() {
@@ -10,33 +11,39 @@ export default function AuthIndex() {
}
return (
<View className="flex-1 bg-white items-center justify-center">
<Image
source={require('@/static/images/work.png')}
style={{ width: '100%', height: 280 }}
className="mb-6"
<View className="flex-1">
<CustomHeader
showHeader={false}
/>
<View className="flex-1 bg-white items-center justify-center">
<Image
source={require('@/static/images/work.png')}
style={{ width: '100%', height: 280 }}
className="mb-6"
/>
<Image
source={require('@/static/images/logo.png')}
style={{ width: 60, height: 60 }}
className="mb-6"
resizeMode="contain"
/>
<Image
source={require('@/static/images/logo.png')}
style={{ width: 60, height: 60 }}
className="mb-6"
resizeMode="contain"
/>
<Text className="font-bold text-3xl text-[#333] tracking-wide">
ONEAO
</Text>
<Text className="font-bold text-3xl text-[#333] tracking-wide">
ONEAO
</Text>
<View className="w-full flex flex-col gap-3 mt-12 px-3">
<Button onPress={() => toPath('/auth/login')}>
<ButtonText>登录</ButtonText>
</Button>
<View className="w-full flex flex-col gap-3 mt-12 px-3">
<Button onPress={() => toPath('/auth/login')}>
<ButtonText>登录</ButtonText>
</Button>
<Button action="secondary" onPress={() => toPath('/auth/register')}>
<ButtonText>注册</ButtonText>
</Button>
<Button action="secondary" onPress={() => toPath('/auth/register')}>
<ButtonText>注册</ButtonText>
</Button>
</View>
</View>
</View>
)
}
@@ -2,6 +2,7 @@ import { useRouter } from 'expo-router'
import { useState } from 'react'
import { Image, Text, View } from 'react-native'
import { AButton, ACheckbox, AForm, AFormItem, AInput } from '@/components/common/ao'
import CustomHeader from '@/components/custom/Header'
import { IconQq, IconWeixin, IconZhiwen } from '@/components/iconfont'
import { Divider } from '@/components/ui/divider'
import useFormValidation from '@/hooks/useFormValidation'
@@ -85,59 +86,63 @@ export default function AuthLogin() {
}
return (
<View className="flex flex-col items-center px-6 h-full">
<Image
source={require('@/static/images/logo.png')}
style={{ width: 120, height: 120 }}
className="my-12"
/>
<AForm form={form}>
<AFormItem label="账号">
<AInput
value={form.account}
onChangeText={text => handleInputChange('account', text)}
/>
</AFormItem>
<AFormItem label="密码">
<AInput
isPassword
value={form.password}
onChangeText={text => handleInputChange('password', text)}
/>
</AFormItem>
</AForm>
<View className="w-full flex-row justify-between items-center mb-6">
<ACheckbox
isChecked={form.rememberMe}
onChange={checked => setForm(prev => ({ ...prev, rememberMe: checked }))}
label="记住我"
<View className="flex-1">
<CustomHeader title="登录" />
<View className="flex flex-col items-center px-6 h-full">
<Image
source={require('@/static/images/logo.png')}
style={{ width: 120, height: 120 }}
className="my-12"
/>
<Text className="text-blue-500">忘记密码?</Text>
</View>
<AForm form={form}>
<AFormItem label="账号">
<AInput
value={form.account}
onChangeText={text => handleInputChange('account', text)}
/>
</AFormItem>
<AButton className="w-full" onPress={loginDefault} disabled={!isValid} loading={loading}>
登录
</AButton>
<AFormItem label="密码">
<AInput
isPassword
value={form.password}
onChangeText={text => handleInputChange('password', text)}
/>
</AFormItem>
</AForm>
<View className="flex-row items-center my-8 w-full">
<View className="flex-1">
<Divider />
<View className="w-full flex-row justify-between items-center mb-6">
<ACheckbox
isChecked={form.rememberMe}
onChange={checked => setForm(prev => ({ ...prev, rememberMe: checked }))}
label="记住我"
/>
<Text className="text-blue-500">忘记密码?</Text>
</View>
<Text className="mx-3 text-gray-500 text-sm">社交账号</Text>
<View className="flex-1">
<Divider />
</View>
</View>
<View className="w-full flex-row justify-between items-center">
<IconQq size={32} onPress={loginQq} />
<IconWeixin size={32} onPress={loginWx} />
<IconZhiwen size={32} onPress={loginZhiwen} />
<AButton className="w-full" onPress={loginDefault} disabled={!isValid} loading={loading}>
登录
</AButton>
<View className="flex-row items-center my-8 w-full">
<View className="flex-1">
<Divider />
</View>
<Text className="mx-3 text-gray-500 text-sm">社交账号</Text>
<View className="flex-1">
<Divider />
</View>
</View>
<View className="w-full flex-row justify-between items-center">
<IconQq size={32} onPress={loginQq} />
<IconWeixin size={32} onPress={loginWx} />
<IconZhiwen size={32} onPress={loginZhiwen} />
</View>
</View>
</View>
)
}
@@ -5,6 +5,7 @@ import { useRef, useState } from 'react'
import { Image, ScrollView, View } from 'react-native'
import { AButton, AForm, AFormItem, AInput } from '@/components/common/ao'
import CustomHeader from '@/components/custom/Header'
import { registerDefaultApi } from '@/request/api/auth'
import { toast } from '@/utils/toast'
@@ -57,68 +58,72 @@ export default function AuthRegister() {
}
return (
<ScrollView ref={scrollRef} style={{ flex: 1 }}>
<View className="flex flex-col items-center px-6 bg-white ">
<Image
source={require('@/static/images/logo.png')}
style={{ width: 120, height: 120 }}
className="my-12"
/>
<View className="flex-1">
<CustomHeader title="注册" />
<ScrollView ref={scrollRef} style={{ flex: 1 }}>
<View className="flex flex-col items-center px-6 bg-white ">
<Image
source={require('@/static/images/logo.png')}
style={{ width: 120, height: 120 }}
className="my-12"
/>
<AForm ref={formRef} form={form} scrollRef={scrollRef}>
<AFormItem
name="username"
label="账号"
isRequired
>
<AInput
placeholder="请输入账号"
leftIcon={UserRound}
value={form.username}
onChangeText={text => handleInputChange('username', text)}
/>
</AFormItem>
<AForm ref={formRef} form={form} scrollRef={scrollRef}>
<AFormItem
name="username"
label="账号"
isRequired
>
<AInput
placeholder="请输入账号"
leftIcon={UserRound}
value={form.username}
onChangeText={text => handleInputChange('username', text)}
/>
</AFormItem>
<AFormItem
name="password"
label="密码"
isRequired
>
<AInput
placeholder="请输入密码"
leftIcon={Lock}
isPassword
value={form.password}
onChangeText={text => handleInputChange('password', text)}
/>
</AFormItem>
<AFormItem
name="password"
label="密码"
isRequired
>
<AInput
placeholder="请输入密码"
leftIcon={Lock}
isPassword
value={form.password}
onChangeText={text => handleInputChange('password', text)}
/>
</AFormItem>
<AFormItem
name="confirmPassword"
label="确认密码"
isRequired
rules={[
{
validator: val => val === form.password,
message: '两次密码不一致',
},
]}
>
<AInput
placeholder="请再次输入密码"
leftIcon={Lock}
isPassword
value={form.confirmPassword}
onChangeText={text => handleInputChange('confirmPassword', text)}
/>
</AFormItem>
</AForm>
<AFormItem
name="confirmPassword"
label="确认密码"
isRequired
rules={[
{
validator: val => val === form.password,
message: '两次密码不一致',
},
]}
>
<AInput
placeholder="请再次输入密码"
leftIcon={Lock}
isPassword
value={form.confirmPassword}
onChangeText={text => handleInputChange('confirmPassword', text)}
/>
</AFormItem>
</AForm>
<AButton className="w-full mb-4" onPress={handleRegister} loading={loading}>
<AButton className="w-full mb-4" onPress={handleRegister} loading={loading}>
注册
</AButton>
</View>
</ScrollView>
</View>
注册
</AButton>
</View>
</ScrollView>
)
}
@@ -1,9 +1,14 @@
import { Text, View } from 'react-native'
import CustomHeader from '@/components/custom/Header'
export default function Settings() {
export default function Share() {
return (
<View>
<Text>Settings</Text>
<View className="flex-1">
<CustomHeader title="设置" />
<View className="flex-1">
<Text>123</Text>
</View>
</View>
)
}
@@ -1,17 +1,13 @@
import { Stack } from 'expo-router'
import { Text, View } from 'react-native'
import CustomHeader from '@/components/custom/Header'
export default function Share() {
return (
<>
<Stack.Screen options={{
headerShown: false,
}}
/>
<View>
<Text>Share</Text>
</View>
</>
<View className="flex-1">
<CustomHeader title="共享空间" />
<View className="flex-1"></View>
</View>
)
}