u
This commit is contained in:
1 parent
6277fb2f9e
commit
8ecac0d7dd
270 files changed
+40438
-529
No files matched your search
@@ -0,0 +1,47 @@
|
||||
import type { TabbarItem } from '@/components/custom/Tabbar'
|
||||
import CustomTabbar from '@/components/custom/Tabbar'
|
||||
import { toast } from '@/utils/toast'
|
||||
|
||||
export const unstable_settings = {
|
||||
initialRouteName: 'money/index',
|
||||
}
|
||||
|
||||
export default function TabsLayout() {
|
||||
const tabbarOptions: TabbarItem[] = [
|
||||
{
|
||||
name: 'money',
|
||||
href: '/money',
|
||||
icon: 'CircleDollarSign',
|
||||
},
|
||||
{
|
||||
name: 'love',
|
||||
href: '/love',
|
||||
icon: 'Heart',
|
||||
},
|
||||
{
|
||||
name: 'app',
|
||||
icon: 'LayoutGrid',
|
||||
activeIcon: 'Smile',
|
||||
iconSize: 26,
|
||||
onPress: () => {
|
||||
toast.show({
|
||||
title: '该功能尚未开发,请尽情期待!',
|
||||
})
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'chat',
|
||||
href: '/chat',
|
||||
icon: 'MessageCircle',
|
||||
},
|
||||
{
|
||||
name: 'user',
|
||||
href: '/user',
|
||||
icon: 'CircleUserRound',
|
||||
},
|
||||
]
|
||||
|
||||
return (
|
||||
<CustomTabbar data={tabbarOptions} />
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Text, View } from 'react-native'
|
||||
import { AButton, ACell, ACellGroup } from '@/components/common/ao'
|
||||
import { useMoneyCategoryStore } from '@/store/moneyCategory'
|
||||
|
||||
export default function Demo() {
|
||||
const moneyCategoryStore = useMoneyCategoryStore()
|
||||
|
||||
function getCategories() {
|
||||
console.log(moneyCategoryStore.categories)
|
||||
}
|
||||
|
||||
async function resetCategory() {
|
||||
moneyCategoryStore.setCategories([])
|
||||
await moneyCategoryStore.init()
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
className="flex-1"
|
||||
style={{
|
||||
marginTop: 34,
|
||||
}}
|
||||
>
|
||||
<ACellGroup>
|
||||
<ACell leftIcon="Zap" title="测试" rightText="测试右边" isLink />
|
||||
<ACell leftIcon="Airplay" title="测试" rightText="测试右边" isLink />
|
||||
<ACell leftIcon="Airplay" title="测试" rightText="测试右边" isLink />
|
||||
<ACell leftIcon="Airplay" title="测试" rightText="测试右边" isLink />
|
||||
<ACell leftIcon="Airplay" title="测试" rightText="测试右边" isLink />
|
||||
</ACellGroup>
|
||||
|
||||
<AButton onPress={() => resetCategory()}>重置记账分类</AButton>
|
||||
<AButton onPress={() => getCategories()}>获取记账分类</AButton>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { Text, View } from 'react-native'
|
||||
|
||||
// 这个页面的作用是作为占位页,避免进入 (tabs) layout 时出现 404 页面。
|
||||
// 如果想指定默认页面,可以通过 router.replace 或 TabTrigger 指向具体子页面。
|
||||
export default function Index() {
|
||||
return (
|
||||
<View>
|
||||
<Text>Index</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import React from 'react'
|
||||
import { Button, ButtonText } from '@/components/ui/button'
|
||||
import {
|
||||
Drawer,
|
||||
DrawerBackdrop,
|
||||
DrawerBody,
|
||||
DrawerCloseButton,
|
||||
DrawerContent,
|
||||
DrawerFooter,
|
||||
DrawerHeader,
|
||||
} from '@/components/ui/drawer'
|
||||
import { Heading } from '@/components/ui/heading'
|
||||
import { CloseIcon, Icon } from '@/components/ui/icon'
|
||||
import { Text } from '@/components/ui/text'
|
||||
|
||||
export default function Example() {
|
||||
const [showDrawer, setShowDrawer] = React.useState(false)
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
onPress={() => {
|
||||
setShowDrawer(true)
|
||||
}}
|
||||
>
|
||||
<ButtonText>Open Drawer</ButtonText>
|
||||
</Button>
|
||||
<Drawer
|
||||
isOpen={showDrawer}
|
||||
anchor="bottom"
|
||||
size="lg"
|
||||
onClose={() => {
|
||||
setShowDrawer(false)
|
||||
}}
|
||||
>
|
||||
<DrawerBackdrop />
|
||||
<DrawerContent>
|
||||
<DrawerHeader>
|
||||
<Text>取消</Text>
|
||||
<Text>分类设置</Text>
|
||||
<Text>确认</Text>
|
||||
</DrawerHeader>
|
||||
<DrawerBody>
|
||||
<Text>This is the basic drawer component.</Text>
|
||||
</DrawerBody>
|
||||
<DrawerFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onPress={() => {
|
||||
setShowDrawer(false)
|
||||
}}
|
||||
>
|
||||
<ButtonText>Cancel</ButtonText>
|
||||
</Button>
|
||||
</DrawerFooter>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
import type { MoneyRecordGroup, MoneyRecordItem } from '@/types/money/record'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { RefreshControl, SectionList, Text, TouchableHighlight, Vibration, View } from 'react-native'
|
||||
import {
|
||||
Menu,
|
||||
MenuOption,
|
||||
MenuOptions,
|
||||
MenuTrigger,
|
||||
renderers,
|
||||
} from 'react-native-popup-menu'
|
||||
import { LucideIcon } from '@/components/common/ao'
|
||||
import EmptyState from '@/components/common/EmptyStatus'
|
||||
import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
interface StaticTransactionsProps {
|
||||
records: MoneyRecordGroup[]
|
||||
onUpdate?: (data: MoneyRecordItem) => void
|
||||
onDelete?: (id: number) => void
|
||||
onDetail?: (id: number) => void
|
||||
onRefreshData?: () => void
|
||||
onLoadMore?: () => void
|
||||
}
|
||||
|
||||
export default function StaticTransactions({
|
||||
records,
|
||||
onUpdate,
|
||||
onDelete,
|
||||
onDetail,
|
||||
onLoadMore,
|
||||
onRefreshData,
|
||||
}: StaticTransactionsProps) {
|
||||
const userID = useUserStore().userInfo?.id
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const [loadingMore, setLoadingMore] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<number | null>(null)
|
||||
|
||||
const handleRefresh = useCallback(async () => {
|
||||
if (!onRefreshData)
|
||||
return
|
||||
setRefreshing(true)
|
||||
try {
|
||||
await onRefreshData()
|
||||
}
|
||||
finally {
|
||||
setRefreshing(false)
|
||||
}
|
||||
}, [onRefreshData])
|
||||
|
||||
const handleLoadMore = useCallback(async () => {
|
||||
if (!onLoadMore || loadingMore)
|
||||
return
|
||||
setLoadingMore(true)
|
||||
try {
|
||||
await onLoadMore()
|
||||
}
|
||||
finally {
|
||||
setLoadingMore(false)
|
||||
}
|
||||
}, [onLoadMore, loadingMore])
|
||||
|
||||
const sections = records.map(group => ({
|
||||
title: group.record_time,
|
||||
totalIncome: group.income,
|
||||
totalExpense: group.expense,
|
||||
data: group.records,
|
||||
}))
|
||||
|
||||
if (records.length === 0) {
|
||||
return (
|
||||
<EmptyState
|
||||
type="empty"
|
||||
title="暂无记账记录"
|
||||
description="快去添加你的第一笔记账吧 "
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<SectionList
|
||||
sections={sections}
|
||||
keyExtractor={item => item.id.toString()}
|
||||
stickySectionHeadersEnabled={true}
|
||||
showsVerticalScrollIndicator={false}
|
||||
onEndReached={handleLoadMore}
|
||||
onEndReachedThreshold={0.1}
|
||||
refreshControl={
|
||||
<RefreshControl refreshing={refreshing} onRefresh={handleRefresh} />
|
||||
}
|
||||
renderSectionHeader={({ section }) => (
|
||||
<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}
|
||||
</Text>
|
||||
|
||||
<View className="flex-row items-center gap-4">
|
||||
{
|
||||
section.totalIncome !== '0' && (
|
||||
<Text className="text-gray-500 text-sm">
|
||||
收入
|
||||
{' '}
|
||||
{section.totalIncome}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
{
|
||||
section.totalExpense !== '0' && (
|
||||
<Text className="text-gray-500 text-sm">
|
||||
支出
|
||||
{' '}
|
||||
{section.totalExpense}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
|
||||
renderItem={({ item }) => {
|
||||
return (
|
||||
<Menu
|
||||
renderer={renderers.Popover}
|
||||
rendererProps={{
|
||||
placement: 'bottom',
|
||||
preferredPlacement: 'bottom',
|
||||
anchorStyle: { backgroundColor: 'transparent' },
|
||||
closeAnimationDuration: 0,
|
||||
}}
|
||||
onSelect={() => setSelectedId(null)}
|
||||
onBackdropPress={() => setSelectedId(null)}
|
||||
onOpen={() => setSelectedId(item.id)}
|
||||
onClose={() => setSelectedId(null)}
|
||||
>
|
||||
<MenuTrigger
|
||||
triggerOnLongPress
|
||||
onPress={() => {
|
||||
Vibration.vibrate(50)
|
||||
}}
|
||||
>
|
||||
<View
|
||||
className="flex-row items-center gap-4 py-2 px-5"
|
||||
style={{
|
||||
backgroundColor: item.id === selectedId ? '#f2f2f2' : 'transparent',
|
||||
}}
|
||||
>
|
||||
<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 text-base">
|
||||
{item.remark ? item.remark : item.category_name}
|
||||
</Text>
|
||||
{
|
||||
item.user_id !== userID && (
|
||||
<Text className="text-gray-500 text-xs italic">
|
||||
{item.username}
|
||||
</Text>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
|
||||
<View>
|
||||
<Text
|
||||
className="text-sm text-gray-600 text-right"
|
||||
>
|
||||
{item.category_type === 1 ? '+' : '-'}
|
||||
{' '}
|
||||
{item.amount}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</MenuTrigger>
|
||||
<MenuOptions
|
||||
customStyles={{
|
||||
optionsContainer: {
|
||||
backgroundColor: 'white',
|
||||
minWidth: 160,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{
|
||||
userID === item.user_id && (
|
||||
<>
|
||||
<MenuOption
|
||||
customStyles={{
|
||||
OptionTouchableComponent: TouchableHighlight,
|
||||
optionTouchable: {
|
||||
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
|
||||
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>
|
||||
</View>
|
||||
</MenuOption>
|
||||
</>
|
||||
)
|
||||
}
|
||||
<MenuOption
|
||||
customStyles={{
|
||||
OptionTouchableComponent: TouchableHighlight,
|
||||
optionTouchable: {
|
||||
underlayColor: '#f0f0f0',
|
||||
activeOpacity: 0.7,
|
||||
},
|
||||
}}
|
||||
onSelect={() => {
|
||||
onDetail?.(item.id)
|
||||
}}
|
||||
>
|
||||
<View className="flex flex-row items-center h-10 px-2">
|
||||
<LucideIcon icon="ReceiptText" />
|
||||
<Text className="ml-2 text-lg">详细</Text>
|
||||
</View>
|
||||
</MenuOption>
|
||||
</MenuOptions>
|
||||
</Menu>
|
||||
)
|
||||
}}
|
||||
/>
|
||||
)
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
import { useRouter } from 'expo-router'
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { Image, Text, TouchableOpacity, View } from 'react-native'
|
||||
import { Drawer } from 'react-native-drawer-layout'
|
||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { ADatePicker, AListItem, LucideIcon } from '@/components/common/ao'
|
||||
import { Fab, FabIcon } from '@/components/ui/fab'
|
||||
import { AddIcon } from '@/components/ui/icon'
|
||||
import { queryMoneyRecordsApi } from '@/request/api/money/record'
|
||||
import { useAppStore } from '@/store/app'
|
||||
import { useMoneyRecordStore } from '@/store/moneyRecord'
|
||||
import { getCurrentYearMonth } from '@/utils/time'
|
||||
import { toast } from '@/utils/toast'
|
||||
import TransactionsList from './components/TransactionsList'
|
||||
|
||||
export default function Money() {
|
||||
const appStore = useAppStore()
|
||||
const moneyRecordStore = useMoneyRecordStore()
|
||||
const router = useRouter()
|
||||
// 安全高度
|
||||
const insets = useSafeAreaInsets()
|
||||
|
||||
// 日期筛选
|
||||
const [showDatePicker, setShowDatePicker] = useState(false)
|
||||
const [searchParams, setSearchParams] = useState({
|
||||
record_time: getCurrentYearMonth(),
|
||||
})
|
||||
|
||||
// 操作菜单
|
||||
const [showActionsMenu, setShowActionsMenu] = useState(false)
|
||||
|
||||
function getMoneyRecords() {
|
||||
queryMoneyRecordsApi({
|
||||
record_time: searchParams.record_time,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
moneyRecordStore.setRecords(res.data.records, res.data.total_expense, res.data.total_income, res.data.total_balance, searchParams.record_time,
|
||||
)
|
||||
}
|
||||
else {
|
||||
toast.show({
|
||||
title: res.message || '查询错误,请稍后再试',
|
||||
})
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!appStore.ready)
|
||||
return
|
||||
getMoneyRecords()
|
||||
}, [appStore.ready, searchParams])
|
||||
|
||||
return (
|
||||
<View
|
||||
className="flex-1"
|
||||
>
|
||||
<Drawer
|
||||
open={showActionsMenu}
|
||||
onOpen={() => setShowActionsMenu(true)}
|
||||
onClose={() => setShowActionsMenu(false)}
|
||||
drawerPosition="right"
|
||||
drawerStyle={{
|
||||
width: '70%',
|
||||
}}
|
||||
renderDrawerContent={() => {
|
||||
return (
|
||||
<SafeAreaView className="flex-col px-3">
|
||||
<Image
|
||||
source={require('@/static/images/logo.png')}
|
||||
style={{ width: 40, height: 40 }}
|
||||
className="self-center"
|
||||
/>
|
||||
|
||||
<View className="mt-2">
|
||||
<Text className="text-gray-600 font-semibold text-sm">管理功能</Text>
|
||||
|
||||
<View className="p-1">
|
||||
<AListItem
|
||||
icon="LayoutGrid"
|
||||
text="类别设置"
|
||||
onPress={() => {
|
||||
router.navigate('/money/category/setting')
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="mt-2">
|
||||
<Text className="text-gray-600 font-semibold text-sm">统计分析</Text>
|
||||
|
||||
<View className="p-1">
|
||||
<AListItem icon="ChartSpline" text="图表分析" />
|
||||
</View>
|
||||
</View>
|
||||
</SafeAreaView>
|
||||
)
|
||||
}}
|
||||
>
|
||||
<View
|
||||
style={{
|
||||
paddingTop: insets.top,
|
||||
backgroundColor: '#fff',
|
||||
elevation: 3,
|
||||
}}
|
||||
>
|
||||
<View className="flex flex-row justify-between items-center px-3">
|
||||
<TouchableOpacity
|
||||
onPress={() => setShowDatePicker(true)}
|
||||
className="flex flex-row items-center gap-1"
|
||||
>
|
||||
<Text className="font-semibold text-lg">{searchParams.record_time}</Text>
|
||||
<LucideIcon icon="ChevronDown" size={16} color="#666" clickable={false} />
|
||||
</TouchableOpacity>
|
||||
|
||||
<TouchableOpacity
|
||||
className="flex flex-row items-center gap-2"
|
||||
onPress={() => {
|
||||
setShowActionsMenu(true)
|
||||
}}
|
||||
>
|
||||
<LucideIcon icon="Menu" size={24} clickable={false} />
|
||||
</TouchableOpacity>
|
||||
|
||||
</View>
|
||||
|
||||
<View
|
||||
className="px-3 py-3"
|
||||
>
|
||||
<View
|
||||
className="flex flex-row gap-4"
|
||||
style={{
|
||||
borderBottomLeftRadius: 16,
|
||||
borderBottomRightRadius: 18,
|
||||
}}
|
||||
>
|
||||
<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"
|
||||
>
|
||||
{moneyRecordStore.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"
|
||||
>
|
||||
{moneyRecordStore.total_expense}
|
||||
</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"
|
||||
>
|
||||
{moneyRecordStore.total_balance}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* <View
|
||||
className="flex-row items-center gap-3 mt-2"
|
||||
style={{
|
||||
borderBottomLeftRadius: 8,
|
||||
borderBottomRightRadius: 8,
|
||||
}}
|
||||
>
|
||||
<Text className="text-sm text-gray-600 text-right">预算</Text>
|
||||
<View className="flex-1">
|
||||
<Progress size="sm">
|
||||
<ProgressFilledTrack />
|
||||
</Progress>
|
||||
</View>
|
||||
<Text className="text-xs text-blue-500">点击设置</Text>
|
||||
</View> */}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<View className="flex-1 mt-2">
|
||||
<TransactionsList
|
||||
records={moneyRecordStore.records}
|
||||
onUpdate={(data) => {
|
||||
router.navigate({
|
||||
pathname: '/money/record/editor',
|
||||
params: { id: data.id },
|
||||
})
|
||||
}}
|
||||
onDetail={(id) => {
|
||||
router.navigate({
|
||||
pathname: '/money/record/detail',
|
||||
params: { id },
|
||||
})
|
||||
}}
|
||||
onDelete={(id) => {
|
||||
moneyRecordStore.deleteMoneyRecord(id)
|
||||
}}
|
||||
onRefreshData={() => {
|
||||
getMoneyRecords()
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
<Fab
|
||||
size="lg"
|
||||
onPress={() => {
|
||||
router.navigate('/money/record/editor')
|
||||
}}
|
||||
>
|
||||
<FabIcon as={AddIcon} size="xl" />
|
||||
</Fab>
|
||||
|
||||
<ADatePicker
|
||||
show={showDatePicker}
|
||||
mode="month"
|
||||
date={searchParams.record_time}
|
||||
onConfirm={(val) => {
|
||||
setSearchParams({
|
||||
...searchParams,
|
||||
record_time: val,
|
||||
})
|
||||
setShowDatePicker(false)
|
||||
}}
|
||||
onClose={() => {
|
||||
setShowDatePicker(false)
|
||||
}}
|
||||
/>
|
||||
</Drawer>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
@@ -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={20} 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,33 @@
|
||||
import { Text, 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 { useUserStore } from '@/store/user'
|
||||
import ListItem from './components/ListItem'
|
||||
|
||||
export default function User() {
|
||||
const userStore = useUserStore()
|
||||
|
||||
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">{userStore.userInfo?.username}</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>
|
||||
)
|
||||
}
|
||||
Reference in new issue
Block a user