This commit is contained in:
oneao committed 2025-11-18 17:28:34 +08:00
1 parent eaba6cf17b
commit 1445ed3b6c
19 files changed
+613 -401

No files matched your search

@@ -14,9 +14,9 @@ export default function TabsLayout() {
icon: 'CircleDollarSign',
},
{
name: 'user',
href: '/user',
icon: 'CircleCheckBig',
name: 'love',
href: '/love',
icon: 'Heart',
},
{
name: 'app',
@@ -35,9 +35,9 @@ export default function TabsLayout() {
icon: 'MessageCircle',
},
{
name: 'love',
href: '/love',
icon: 'Heart',
name: 'user',
href: '/user',
icon: 'CircleUserRound',
},
]
@@ -1,68 +0,0 @@
import { Pressable, StyleSheet, Text, View } from 'react-native'
const Locales = ['zh-cn', 'en', 'de', 'es']
interface Props {
locale: string
setLocale: (locale: string) => void
mainColor?: string
activeTextColor?: string
}
export default function LocaleSelector({
locale,
setLocale,
mainColor,
activeTextColor,
}: Props) {
return (
<View style={styles.localeContainer}>
<Text style={{ marginRight: 8 }}>Locale:</Text>
{Locales.map((item, index) => (
<Pressable
key={index}
style={[
styles.localeButton,
item === locale && {
backgroundColor: mainColor,
},
]}
onPress={() => setLocale(item)}
accessibilityRole="button"
accessibilityLabel={item.toUpperCase()}
>
<Text
style={[
styles.localeButtonText,
item === locale && {
fontWeight: 'bold',
color: activeTextColor,
},
]}
>
{item.split('-')[0]?.toUpperCase()}
</Text>
</Pressable>
))}
</View>
)
}
const styles = StyleSheet.create({
localeContainer: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 20,
},
localeButton: {
alignItems: 'center',
justifyContent: 'center',
width: 36,
height: 36,
borderRadius: 36,
margin: 2,
},
localeButtonText: {
fontSize: 15,
},
})
@@ -1,54 +0,0 @@
import { Pressable, StyleSheet, View } from 'react-native'
export interface ITheme {
mainColor: string
activeTextColor: string
}
interface Props {
themes: ITheme[]
setTheme: (theme: ITheme) => void
}
export default function ThemeSelector({ themes = [], setTheme }: Props) {
return (
<View style={styles.themeContainer}>
{themes.map((item, index) => (
<Pressable
key={index}
style={[
styles.themeButton,
{
borderColor: item.activeTextColor,
backgroundColor: item.mainColor,
},
]}
onPress={() => setTheme(item)}
accessibilityRole="button"
accessibilityLabel="Set Active Theme"
/>
))}
</View>
)
}
const styles = StyleSheet.create({
themeContainer: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
marginBottom: 10,
width: 330,
},
themeButton: {
borderWidth: 4,
width: 32,
height: 32,
borderRadius: 32,
margin: 5,
shadowRadius: 20,
shadowColor: '#000',
shadowOpacity: 0.1,
shadowOffset: { width: 0, height: 0 },
},
})
@@ -1,6 +1,6 @@
import type { MoneyRecordGroup, MoneyRecordItem } from '@/types/money/record'
import React, { useCallback, useState } from 'react'
import { Pressable, SectionList, Text, View } from 'react-native'
import React, { useState } from 'react'
import { SectionList, Text, TouchableHighlight, View } from 'react-native'
import {
Menu,
MenuOption,
@@ -50,7 +50,7 @@ export default function StaticTransactions({
stickySectionHeadersEnabled={true}
showsVerticalScrollIndicator={false}
renderSectionHeader={({ section }) => (
<View className="bg-white py-2 px-4 border-b border-gray-100 mb-2">
<View className="bg-white py-2 px-4 border-b border-gray-50">
<View className="flex-row items-center justify-between">
<Text className="text-gray-700 font-semibold text-base">
{section.title}
@@ -80,7 +80,7 @@ export default function StaticTransactions({
</View>
)}
renderItem={({ item }) => {
renderItem={({ item, index }) => {
return (
<Menu
renderer={renderers.Popover}
@@ -89,36 +89,47 @@ export default function StaticTransactions({
preferredPlacement: 'bottom',
anchorStyle: { backgroundColor: 'transparent' },
}}
onSelect={() => setSelectedId(null)}
onBackdropPress={() => setSelectedId(null)}
onOpen={() => setSelectedId(item.id)}
onClose={() => setSelectedId(null)}
>
<MenuTrigger>
<MenuTrigger triggerOnLongPress>
<View
className="flex-row items-center gap-4 py-2 px-5"
style={{
backgroundColor: item.id === selectedId ? '#f2f2f2' : 'transparent',
}}
>
<View className="w-10 h-10 rounded-full bg-gray-100 items-center justify-center">
<MoneyCategoryIcon name={item.category_icon} size={22} color="#000" />
<View className="relative w-10 h-10 rounded-full bg-gray-100 items-center justify-center">
<MoneyCategoryIcon name={item.category_icon} size={20} color="#000" />
</View>
<View className="flex-1">
<Text className="text-gray-800 font-medium text-base">{item.category_name}</Text>
{item.remark && <Text className="text-gray-400 text-sm">{item.remark}</Text>}
<Text className="text-gray-800 text-base">
{item.remark ? item.remark : item.category_name}
</Text>
{
index === 1 && (
<Text className="text-gray-500 text-xs italic">
操作人:张三
</Text>
)
}
</View>
<View className="flex-row flex">
<Text className="text-sm text-gray-500">
<View>
<Text
className="text-sm text-gray-600 text-right"
>
{item.category_type === 1 ? '+' : '-'}
{' '}
{item.amount}
</Text>
</View>
<Badge size="sm" variant="solid" action="muted">
<BadgeText>宝贝</BadgeText>
</Badge>
</View>
</MenuTrigger>
<MenuOptions
customStyles={{
optionsContainer: {
@@ -129,25 +140,36 @@ export default function StaticTransactions({
>
<MenuOption
customStyles={{
OptionTouchableComponent: TouchableHighlight,
optionTouchable: {
underlayColor: '#f0f0f0', // 按压时的背景色
activeOpacity: 0.7, // (可选)按压时的透明度
},
// 如果需要,也可以设置默认状态下的背景色
optionWrapper: {
backgroundColor: 'transparent', // 默认背景色
underlayColor: '#f0f0f0',
activeOpacity: 0.7,
},
}}
onSelect={() => {
onUpdate?.(item)
}}
>
<View className="flex flex-row items-center h-10 px-2">
<LucideIcon icon="Pencil" />
<Text className="ml-2 text-lg">更新</Text>
</View>
</MenuOption>
<MenuOption onSelect={() => alert(`Clicked Option B fo`)}>
<MenuOption
customStyles={{
OptionTouchableComponent: TouchableHighlight,
optionTouchable: {
underlayColor: '#f0f0f0',
activeOpacity: 0.7,
},
}}
onSelect={() => {
onDelete?.(item.id)
}}
>
<View className="flex flex-row items-center h-10 px-2">
<LucideIcon icon="Trash" />
<Text className=" ml-2 text-lg">删除</Text>
<Text className="ml-2 text-lg">删除</Text>
</View>
</MenuOption>
</MenuOptions>
@@ -1,6 +1,6 @@
import type { MoneyRecordGroup, MoneyRecordItem, QueryMoneyRecordsResult } from '@/types/money/record'
import React, { useEffect, useState } from 'react'
import { Text, TouchableOpacity, View } from 'react-native'
import { Text, TouchableOpacity, TouchableWithoutFeedback, View } from 'react-native'
import { useSafeAreaInsets } from 'react-native-safe-area-context'
import { ADatePicker, LucideIcon } from '@/components/common/ao'
import { Fab, FabIcon } from '@/components/ui/fab'
@@ -277,13 +277,17 @@ export default function Money() {
}
return (
<View
className="flex-1"
style={{
paddingTop: insets.top,
}}
>
<View>
<View
style={{
paddingTop: insets.top,
backgroundColor: '#fff',
elevation: 3,
}}
>
<View className="flex flex-row justify-between items-center px-3">
<TouchableOpacity
onPress={() => setShowDatePicker(true)}
@@ -300,45 +304,51 @@ export default function Money() {
</TouchableOpacity>
</View>
<View className="mt-3 border border-gray-200 rounded-lg m-4 p-4 shadow-sm bg-white">
<View
style={{
borderTopLeftRadius: 8,
borderTopRightRadius: 8,
}}
>
<View className="mb-4">
<Text className="text-gray-500 text-sm">
余额
{/* <Text className="text-red-500">↓20%</Text> */}
<View className="mt-3 m-4 ">
<View className="flex flex-row gap-4">
<View className="flex-1">
<Text className="text-gray-500 text-sm">余额</Text>
<Text
className="text-lg font-semibold mt-0.5"
numberOfLines={1}
ellipsizeMode="tail"
>
0.00
</Text>
<Text className="font-bold text-2xl mt-0.5">22,313.00</Text>
</View>
<View className="flex-row flex gap-8">
<View>
<Text className="text-gray-500 text-sm">
收入
{/* <Text className="text-green-500">↑20%</Text> */}
</Text>
<Text className="text-lg font-semibold mt-0.5">{moneyRecords.total_income}</Text>
</View>
<View className="flex-1">
<Text className="text-gray-500 text-sm">收入</Text>
<Text
className="text-lg font-semibold mt-0.5"
numberOfLines={1}
ellipsizeMode="tail"
>
0.00
</Text>
</View>
<View>
<Text className="text-gray-500 text-sm">
支出
{/* <Text className="text-green-500">↑20%</Text> */}
</Text>
<Text className="text-lg font-semibold mt-0.5">{moneyRecords.total_expense}</Text>
</View>
{/*
<View>
<Text className="text-gray-500 text-sm">
结余
<Text className="text-green-500">↑20%</Text>
</Text>
<Text className="text-lg font-semibold mt-0.5">11.0</Text>
</View> */}
<View className="flex-1">
<Text className="text-gray-500 text-sm">支出</Text>
<Text
className="text-lg font-semibold mt-0.5"
numberOfLines={1}
ellipsizeMode="tail"
>
0.00
</Text>
</View>
<View className="flex-1">
<Text className="text-gray-500 text-sm">结余</Text>
<Text
className="text-lg font-semibold mt-0.5"
numberOfLines={1}
ellipsizeMode="tail"
>
0.00
</Text>
</View>
</View>
@@ -1,88 +0,0 @@
import { router } from 'expo-router'
import { StyleSheet, Text, View } from 'react-native'
import { AButton, AInput } from '@/components/common/ao'
import { LucideIcon } from '@/components/common/ao/index'
import { useUserStore } from '@/store/user'
export default function User() {
const userInfo = useUserStore(state => state.userInfo)
if (!userInfo) {
return (
<View style={styles.container}>
<Text style={styles.empty}>未登录</Text>
</View>
)
}
const genderText = userInfo.gender === 1 ? '男' : userInfo.gender === 2 ? '女' : '未设置'
return (
<View style={styles.container}>
<Text style={styles.title}>个人信息</Text>
<View style={styles.infoBox}>
<Text style={styles.item}>
账号:
{userInfo.account}
</Text>
<Text style={styles.item}>
用户名:
{userInfo.username}
</Text>
<Text style={styles.item}>
ID:
{userInfo.id}
</Text>
<Text style={styles.item}>
序号:
{userInfo.seqNo}
</Text>
<Text style={styles.item}>
性别:
{genderText}
</Text>
{userInfo.rememberMe && <Text style={styles.item}>已开启记住我功能</Text>}
</View>
<AButton onPress={() => {
router.replace('/auth')
}}
>
跳转到登陆页面
</AButton>
<AInput value="2" />
<LucideIcon icon="User" size={32} strokeWidth={1} />
</View>
)
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
padding: 20,
},
title: {
fontSize: 20,
fontWeight: 'bold',
marginBottom: 16,
},
infoBox: {
backgroundColor: '#f8f8f8',
borderRadius: 12,
padding: 16,
},
item: {
fontSize: 16,
marginBottom: 8,
color: '#333',
},
empty: {
textAlign: 'center',
marginTop: 100,
fontSize: 18,
color: '#888',
},
})
@@ -0,0 +1,45 @@
import type { icons } from 'lucide-react-native'
import { useRouter } from 'expo-router'
import { Text, TouchableOpacity } from 'react-native'
import { LucideIcon } from '@/components/common/ao/index'
export interface ListItemProps {
icon: keyof typeof icons
text: string
href?: string
onPress?: () => void
}
export default function ListItem({ icon, text, href, onPress }: ListItemProps) {
const router = useRouter()
const handlePress = () => {
if (onPress) {
onPress()
return
}
if (href) {
router.navigate(href)
}
}
return (
<TouchableOpacity
className="flex flex-row items-center py-3"
onPress={handlePress}
>
<LucideIcon icon={icon} size={24} strokeWidth={1.5} />
<Text className="text-base ml-3">{text}</Text>
<LucideIcon
icon="ChevronRight"
className="ml-auto"
color="#ccc"
size={22}
strokeWidth={1.5}
/>
</TouchableOpacity>
)
}
@@ -0,0 +1,30 @@
import { Text, TouchableOpacity, View } from 'react-native'
import { SafeAreaView } from 'react-native-safe-area-context'
import { LucideIcon } from '@/components/common/ao/index'
import {
Avatar,
AvatarImage,
} from '@/components/ui/avatar'
import ListItem from './components/ListItem'
export default function User() {
return (
<SafeAreaView className="flex-1 p-4">
<View className="flex flex-row items-center ">
<Avatar size="md" className="border-0">
<AvatarImage
className="bg-white"
source={require('@/static/images/logo.png')}
/>
</Avatar>
<Text className="text-xl font-semibold ml-3">张三</Text>
<LucideIcon icon="ChevronRight" size={26} className="ml-auto" color="#aaa" strokeWidth={1.5} />
</View>
<View className="mt-8 flex flex-col">
<ListItem icon="Earth" text="共享空间" href="/system/share" />
<ListItem icon="Settings" text="设置"href="/system/settings" />
</View>
</SafeAreaView>
)
}
@@ -0,0 +1,9 @@
import { Text, View } from 'react-native'
export default function Settings() {
return (
<View>
<Text>Settings</Text>
</View>
)
}
@@ -0,0 +1,17 @@
import { Stack } from 'expo-router'
import { Text, View } from 'react-native'
export default function Share() {
return (
<>
<Stack.Screen options={{
headerShown: false,
}}
/>
<View>
<Text>Share</Text>
</View>
</>
)
}