u
This commit is contained in:
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>
|
||||
</>
|
||||
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user