This commit is contained in:
oneao committed 2026-01-23 17:26:10 +08:00
1 parent ac724bbdaa
commit 500fab7a80
98 files changed
+24706 -109

No files matched your search

@@ -1,9 +1,10 @@
import type { MoneyCategoryEnum, MoneyCategoryItem } from '@/types/money/category'
import { useEffect, useRef, useState } from 'react'
import { Pressable, Text, View } from 'react-native'
import type { MoneyCategoryItem } from '@/types/money/category'
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { FlatList, Pressable, Text, View } from 'react-native'
import { AButton, ADrawer, AInput } from '@/components/common/ao'
import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
import { useMoneyCategoryStore } from '@/store/moneyCategory'
import { MoneyCategoryEnum } from '@/types/money/category'
export interface EditCategoryForm {
id?: number
@@ -60,86 +61,104 @@ export default function CreateCategory({
onClose?.()
}
// 缓存点击 Icon 的回调
const handlePressIcon = useCallback(
(icon: string) => {
setForm(v => ({ ...v, icon }))
},
[setForm],
)
// Memo 化单个 IconItem,避免重复渲染
const IconItem = useCallback(({ icon, selected }: { icon: string, selected: boolean }) => {
return (
<Pressable
className="w-1/5 mb-3 flex items-center"
onPress={() => handlePressIcon(icon)}
>
<View
style={{
width: 46,
height: 46,
borderRadius: 28,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: selected ? '#333' : '#F3F4F6',
}}
>
<MoneyCategoryIcon
name={icon}
size={32}
color={selected ? '#fff' : '#000'}
/>
</View>
</Pressable>
)
}, [handlePressIcon])
// Memo 化渲染每组 Icon 的组件
const renderGroup = useCallback(
({ item }: { item: [string, string[]] }) => {
const [groupName, icons] = item
return (
<View className="mb-4">
<Text className="text-center text-sm mb-3">{groupName}</Text>
<View className="flex flex-row flex-wrap">
{icons.map(icon => (
<IconItem key={icon} icon={icon} selected={form.icon === icon} />
))}
</View>
</View>
)
},
[IconItem, form.icon],
)
// 使用 useMemo 缓存 FlatList 的数据
const flatListData = useMemo(() => Object.entries(moneyCategoryStore.ICON_GROUPS), [moneyCategoryStore.ICON_GROUPS])
return (
<ADrawer visible={isOpen} hideModal={handleClose} title="你好" Height="80%" duration={300} />
<ADrawer
visible={isOpen}
hideModal={handleClose}
height="80%"
duration={300}
header={(
<View className="flex flex-row items-center justify-between w-full p-4">
<AButton size="xs" variant="outline" onPress={handleClose}>
取消
</AButton>
<Text className="text-lg font-medium">
{type === MoneyCategoryEnum.Expense ? '添加支出分类' : '添加收入分类'}
</Text>
<AButton
size="xs"
disabled={confirmDisabled}
loading={confirmLoading}
onPress={async () => {
if (!onConfirm)
return
try {
setConfirmLoading(true)
await onConfirm({ ...form, name: form.name.trim() })
}
finally {
setConfirmLoading(false)
}
}}
>
确认
</AButton>
</View>
)}
>
<FlatList
ref={scrollRef}
data={flatListData}
keyExtractor={([groupName]) => groupName}
renderItem={renderGroup}
initialNumToRender={1}
/>
</ADrawer>
)
}
// <Drawer isOpen={isOpen} onClose={handleClose} size="lg" anchor="bottom">
// <DrawerBackdrop />
// <DrawerContent style={{ paddingLeft: 0, paddingRight: 0 }}>
// <DrawerHeader className="px-4 flex flex-col gap-4">
// <View className="flex flex-row items-center justify-between w-full">
// <AButton size="xs" variant="outline" onPress={handleClose}>
// 取消
// </AButton>
// <Text className="text-lg font-medium">
// {type === MoneyCategoryEnum.Expense ? '添加支出分类' : '添加收入分类'}
// </Text>
// <AButton
// size="xs"
// disabled={confirmDisabled}
// loading={confirmLoading}
// onPress={async () => {
// if (!onConfirm)
// return
// try {
// setConfirmLoading(true)
// await onConfirm({ ...form, name: form.name.trim() })
// }
// finally {
// setConfirmLoading(false)
// }
// }}
// >
// 确认
// </AButton>
// </View>
// <AInput
// value={form.name}
// placeholder="请输入名称(不超过5个字符)"
// onChangeText={text => setForm(val => ({ ...val, name: text }))}
// />
// </DrawerHeader>
// <DrawerBody ref={scrollRef} className="mb-16">
// {Object.entries(moneyCategoryStore.ICON_GROUPS).map(([groupName, icons]) => (
// <View key={groupName} className="mb-4">
// <Text className="text-center text-sm mb-3">{groupName}</Text>
// <View className="flex flex-row flex-wrap">
// {icons.map(icon => (
// <Pressable
// key={icon}
// className="w-1/5 mb-3 flex items-center"
// onPress={() => setForm(val => ({ ...val, icon }))}
// >
// <View
// onLayout={(e) => {
// console.log(e.nativeEvent.layout)
// iconPositions.current[icon] = e.nativeEvent.layout.y
// }}
// style={{
// width: 46,
// height: 46,
// borderRadius: 28,
// justifyContent: 'center',
// alignItems: 'center',
// backgroundColor: icon === form.icon ? '#333' : '#F3F4F6',
// }}
// >
// <MoneyCategoryIcon
// name={icon}
// size={32}
// color={icon === form.icon ? '#fff' : '#000'}
// />
// </View>
// </Pressable>
// ))}
// </View>
// </View>
// ))}
// </DrawerBody>
// <DrawerFooter />
// </DrawerContent>
// </Drawer>
@@ -19,9 +19,10 @@ interface DrawerMenuProps {
visible: boolean
duration?: number
menuPosition?: 'left' | 'right' | 'top' | 'bottom'
Height?: DimensionValue
height?: DimensionValue
hideModal: () => void
children?: ReactNode
header?: ReactNode
isCircle?: boolean
CircleNum?: number
isShadowHideModal?: boolean
@@ -30,7 +31,7 @@ interface DrawerMenuProps {
export default function Drawer({
visible,
Height,
height,
hideModal,
duration = 300,
isCircle,
@@ -38,6 +39,7 @@ export default function Drawer({
children,
CircleNum,
isShadowHideModal = true,
header,
title,
}: DrawerMenuProps) {
const [menuPositionStyle, setMenuPositionStyle] = useState<ViewStyle>()
@@ -68,7 +70,7 @@ export default function Drawer({
borderBottomRightRadius: isCircle ? CircleNum : 0,
})
setDrawerMenuBox({
width: Height,
width: height,
height: WINDOW.height,
position: 'absolute',
left: 0,
@@ -84,7 +86,7 @@ export default function Drawer({
borderBottomLeftRadius: isCircle ? CircleNum : 0,
})
setDrawerMenuBox({
width: Height,
width: height,
height: WINDOW.height,
position: 'absolute',
right: 0,
@@ -101,7 +103,7 @@ export default function Drawer({
})
setDrawerMenuBox({
width: WINDOW.width,
height: Height,
height,
position: 'absolute',
top: 0,
right: 0,
@@ -118,14 +120,14 @@ export default function Drawer({
})
setDrawerMenuBox({
width: WINDOW.width,
height: Height,
height,
position: 'absolute',
bottom: 0,
right: 0,
})
break
}
}, [menuPosition, Height, isCircle, CircleNum])
}, [menuPosition, height, isCircle, CircleNum])
/* ================== 动画 ================== */
const open = () => {
@@ -219,17 +221,20 @@ export default function Drawer({
<View style={{ paddingTop: Platform.OS === 'ios' ? 50 : 0 }} />
)}
<View style={styles.headTitle}>
<Text style={styles.title}>{title}</Text>
<LucideIcon
icon="X"
size={24}
color="black"
style={{ position: 'absolute', right: 15, top: 11 }}
onPress={close}
/>
</View>
{
header || (
<View style={styles.headTitle}>
<Text style={styles.title}>{title}</Text>
<LucideIcon
icon="X"
size={22}
color="black"
style={{ position: 'absolute', right: 15, top: 11 }}
onPress={close}
/>
</View>
)
}
{children}
</View>
</Animated.View>
@@ -249,7 +254,6 @@ const styles = StyleSheet.create({
position: 'absolute',
},
headTitle: {
height: 40,
width: '100%',
padding: 10,
justifyContent: 'center',
+2 -2
View File
@@ -32,8 +32,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,