u
This commit is contained in:
1 parent
e51f4948d4
commit
2130be4e49
6 files changed
+165
-67
No files matched your search
@@ -1,5 +1,7 @@
|
|||||||
import { Text, View } from 'react-native'
|
import { Text, View } from 'react-native'
|
||||||
|
import { ScrollView } from 'react-native-gesture-handler'
|
||||||
import { AButton, ACell, ACellGroup } from '@/components/common/ao'
|
import { AButton, ACell, ACellGroup } from '@/components/common/ao'
|
||||||
|
import { Grid, GridItem } from '@/components/ui/grid'
|
||||||
import { useMoneyCategoryStore } from '@/store/moneyCategory'
|
import { useMoneyCategoryStore } from '@/store/moneyCategory'
|
||||||
|
|
||||||
export default function Demo() {
|
export default function Demo() {
|
||||||
@@ -15,22 +17,13 @@ export default function Demo() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View
|
<ScrollView>
|
||||||
className="flex-1"
|
<Grid className="gap-4 p-4" _extra={{ className: 'grid-cols-12' }}>
|
||||||
style={{
|
<GridItem className="bg-background-50 p-6 rounded-md" _extra={{ className: 'col-span-3' }} />
|
||||||
marginTop: 34,
|
<GridItem className="bg-background-50 p-6 rounded-md" _extra={{ className: 'col-span-3' }} />
|
||||||
}}
|
<GridItem className="bg-background-50 p-6 rounded-md" _extra={{ className: 'col-span-3' }} />
|
||||||
>
|
<GridItem className="bg-background-50 p-6 rounded-md" _extra={{ className: 'col-span-3' }} />
|
||||||
<ACellGroup>
|
</Grid>
|
||||||
<ACell leftIcon="Zap" title="测试" rightText="测试右边" isLink />
|
</ScrollView>
|
||||||
<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>
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -238,7 +238,7 @@ export default function MoneyCategorySetting() {
|
|||||||
>
|
>
|
||||||
<LucideIcon icon="Trash" size={18} color="#fff" />
|
<LucideIcon icon="Trash" size={18} color="#fff" />
|
||||||
<Text className="text-base text-white ">
|
<Text className="text-base text-white ">
|
||||||
拖到这里删除
|
{draggingDeleteZone ? '松手删除(将删除该类下所有记录)' : '拖到这里删除'}
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
</Sortable.BaseZone>
|
</Sortable.BaseZone>
|
||||||
|
|||||||
@@ -0,0 +1,111 @@
|
|||||||
|
import type { ViewStyle } from 'react-native'
|
||||||
|
import type { MoneyCategoryItem } from '@/types/money/category'
|
||||||
|
import React, { useEffect, useMemo, useRef } from 'react'
|
||||||
|
import { FlatList, Pressable, Text, View } from 'react-native'
|
||||||
|
import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
|
||||||
|
|
||||||
|
interface CategoryGridProps {
|
||||||
|
data: MoneyCategoryItem[]
|
||||||
|
selectedCategory?: MoneyCategoryItem | null
|
||||||
|
onSelectCategory?: (item: MoneyCategoryItem | null) => void
|
||||||
|
|
||||||
|
className?: string
|
||||||
|
style?: ViewStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function CategoryGrid({
|
||||||
|
data,
|
||||||
|
selectedCategory,
|
||||||
|
onSelectCategory,
|
||||||
|
className,
|
||||||
|
style,
|
||||||
|
}: CategoryGridProps) {
|
||||||
|
const flatListRef = useRef<FlatList<any>>(null)
|
||||||
|
|
||||||
|
// 每行 4 个,补齐空项
|
||||||
|
const memoItems = useMemo(() => {
|
||||||
|
const items = [...data]
|
||||||
|
const remainder = items.length % 4
|
||||||
|
if (remainder > 0) {
|
||||||
|
const emptyCount = 4 - remainder
|
||||||
|
for (let i = 0; i < emptyCount; i++) {
|
||||||
|
items.push({ id: `empty-${i}` } as any)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return items
|
||||||
|
}, [data])
|
||||||
|
|
||||||
|
// 自动滚动到选中行
|
||||||
|
useEffect(() => {
|
||||||
|
if (!selectedCategory || !flatListRef.current)
|
||||||
|
return
|
||||||
|
|
||||||
|
// 找到选中项下标
|
||||||
|
const itemIndex = data.findIndex(item => item.id === selectedCategory.id)
|
||||||
|
if (itemIndex === -1 || !flatListRef.current)
|
||||||
|
return
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (flatListRef.current) {
|
||||||
|
flatListRef.current.scrollToIndex({
|
||||||
|
index: Math.floor(itemIndex / 4),
|
||||||
|
animated: true,
|
||||||
|
viewPosition: 0.5,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}, 0)
|
||||||
|
}, [selectedCategory])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FlatList
|
||||||
|
ref={flatListRef}
|
||||||
|
data={memoItems}
|
||||||
|
numColumns={4}
|
||||||
|
keyExtractor={item => item.id.toString()}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
className={`${className}`}
|
||||||
|
style={[style]}
|
||||||
|
getItemLayout={(data, index) => {
|
||||||
|
return { length: 80, offset: 80 * index, index }
|
||||||
|
}}
|
||||||
|
renderItem={({ item }) => {
|
||||||
|
if (!('name' in item))
|
||||||
|
return <View style={{ flex: 1 / 4, margin: 4 }} />
|
||||||
|
|
||||||
|
const isSelected = selectedCategory?.id === item.id
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Pressable
|
||||||
|
style={{ flex: 1 / 4, height: 80, alignItems: 'center' }}
|
||||||
|
onPress={() => onSelectCategory?.(item)}
|
||||||
|
onLayout={(event) => {
|
||||||
|
const { height } = event.nativeEvent.layout
|
||||||
|
console.log('Pressable 高度:', height)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
style={{
|
||||||
|
width: 46,
|
||||||
|
height: 46,
|
||||||
|
borderRadius: 28,
|
||||||
|
marginTop: 4,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: isSelected ? '#333' : '#F3F4F6',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<MoneyCategoryIcon
|
||||||
|
name={item.icon}
|
||||||
|
size={28}
|
||||||
|
color={isSelected ? '#fff' : '#000'}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
<Text style={{ marginBottom: 4, fontSize: 14, textAlign: 'center' }}>
|
||||||
|
{item.name}
|
||||||
|
</Text>
|
||||||
|
</Pressable>
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -1,56 +1,57 @@
|
|||||||
import type { ViewStyle } from 'react-native'
|
import type { FlatList } from 'react-native'
|
||||||
import type { MoneyCategoryItem } from '@/types/money/category'
|
import type { MoneyCategoryItem } from '@/types/money/category'
|
||||||
import React, { useMemo } from 'react'
|
import React, { useEffect, useRef } from 'react'
|
||||||
import { FlatList, Pressable, Text, View } from 'react-native'
|
import { Pressable, Text, View } from 'react-native'
|
||||||
|
import { ScrollView } from 'react-native-gesture-handler'
|
||||||
import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
|
import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
|
||||||
|
import { Grid, GridItem } from '@/components/ui/grid'
|
||||||
|
|
||||||
interface CategoryGridProps {
|
interface CategoryGridProps {
|
||||||
data: MoneyCategoryItem[]
|
data: MoneyCategoryItem[]
|
||||||
selectedCategory?: MoneyCategoryItem | null
|
selectedCategory?: MoneyCategoryItem | null
|
||||||
onSelectCategory?: (item: MoneyCategoryItem | null) => void
|
onSelectCategory?: (item: MoneyCategoryItem | null) => void
|
||||||
|
|
||||||
className?: string
|
|
||||||
style?: ViewStyle
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function CategoryGrid({
|
export default function CategoryGrid({
|
||||||
data,
|
data,
|
||||||
selectedCategory,
|
selectedCategory,
|
||||||
onSelectCategory,
|
onSelectCategory,
|
||||||
className,
|
|
||||||
style,
|
|
||||||
}: CategoryGridProps) {
|
}: CategoryGridProps) {
|
||||||
// 计算补齐数组,让每行4个
|
const flatListRef = useRef<FlatList<any>>(null)
|
||||||
const memoItems = useMemo(() => {
|
|
||||||
const items = [...data]
|
// 自动滚动到选中行
|
||||||
const remainder = items.length % 4
|
useEffect(() => {
|
||||||
if (remainder > 0) {
|
if (!selectedCategory || !flatListRef.current)
|
||||||
const emptyCount = 4 - remainder
|
return
|
||||||
for (let i = 0; i < emptyCount; i++) {
|
|
||||||
items.push({ id: `empty-${i}` } as any)
|
// 找到选中项下标
|
||||||
|
const itemIndex = data.findIndex(item => item.id === selectedCategory.id)
|
||||||
|
if (itemIndex === -1 || !flatListRef.current)
|
||||||
|
return
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
if (flatListRef.current) {
|
||||||
|
flatListRef.current.scrollToIndex({
|
||||||
|
index: Math.floor(itemIndex / 4),
|
||||||
|
animated: true,
|
||||||
|
viewPosition: 0.5,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}, 0)
|
||||||
return items
|
}, [selectedCategory])
|
||||||
}, [data])
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<FlatList
|
<ScrollView>
|
||||||
data={memoItems}
|
<Grid className="gap-4 px-2 py-4 justify-start" _extra={{ className: 'grid-cols-12' }}>
|
||||||
numColumns={4}
|
{data.map((item) => {
|
||||||
keyExtractor={item => item.id.toString()}
|
|
||||||
showsVerticalScrollIndicator={false}
|
|
||||||
className={`${className}`}
|
|
||||||
style={[style]}
|
|
||||||
renderItem={({ item }) => {
|
|
||||||
if (!('name' in item)) {
|
|
||||||
return <View style={{ flex: 1 / 4, margin: 4 }} />
|
|
||||||
}
|
|
||||||
|
|
||||||
const isSelected = selectedCategory?.id === item.id
|
const isSelected = selectedCategory?.id === item.id
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<GridItem
|
||||||
|
_extra={{ className: 'col-span-3' }}
|
||||||
|
key={item.id}
|
||||||
|
>
|
||||||
<Pressable
|
<Pressable
|
||||||
style={{ flex: 1 / 4, margin: 4, alignItems: 'center' }}
|
style={{ alignItems: 'center' }}
|
||||||
onPress={() => onSelectCategory?.(item)}
|
onPress={() => onSelectCategory?.(item)}
|
||||||
>
|
>
|
||||||
<View
|
<View
|
||||||
@@ -58,7 +59,6 @@ export default function CategoryGrid({
|
|||||||
width: 46,
|
width: 46,
|
||||||
height: 46,
|
height: 46,
|
||||||
borderRadius: 28,
|
borderRadius: 28,
|
||||||
marginTop: 4,
|
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
alignItems: 'center',
|
alignItems: 'center',
|
||||||
backgroundColor: isSelected ? '#333' : '#F3F4F6',
|
backgroundColor: isSelected ? '#333' : '#F3F4F6',
|
||||||
@@ -67,23 +67,17 @@ export default function CategoryGrid({
|
|||||||
<MoneyCategoryIcon
|
<MoneyCategoryIcon
|
||||||
name={item.icon}
|
name={item.icon}
|
||||||
size={28}
|
size={28}
|
||||||
color={
|
color={isSelected ? '#fff' : '#000'}
|
||||||
isSelected ? '#fff' : '#000'
|
|
||||||
}
|
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
<Text
|
<Text style={{ fontSize: 14, textAlign: 'center' }}>
|
||||||
style={{
|
|
||||||
marginBottom: 4,
|
|
||||||
fontSize: 14,
|
|
||||||
textAlign: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{item.name}
|
{item.name}
|
||||||
</Text>
|
</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
|
</GridItem>
|
||||||
)
|
)
|
||||||
}}
|
})}
|
||||||
/>
|
</Grid>
|
||||||
|
</ScrollView>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -32,8 +32,8 @@ function isWhiteListed(url: string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const alovaInstance = createAlova({
|
const alovaInstance = createAlova({
|
||||||
baseURL: 'http://192.168.5.6:8086/app',
|
// baseURL: 'http://192.168.5.6:8086/app',
|
||||||
// baseURL: 'http://192.168.1.137:8086/app',
|
baseURL: 'http://192.168.1.137:8086/app',
|
||||||
requestAdapter: axiosRequestAdapter(),
|
requestAdapter: axiosRequestAdapter(),
|
||||||
timeout: 5000,
|
timeout: 5000,
|
||||||
cacheFor: null,
|
cacheFor: null,
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ export const useMoneyCategoryStore = create<MoneyCategoryStore>()(
|
|||||||
try {
|
try {
|
||||||
await queryMoneyCategoryApi().then((res) => {
|
await queryMoneyCategoryApi().then((res) => {
|
||||||
if (res.isSuccess) {
|
if (res.isSuccess) {
|
||||||
set({ categories: res.data.categories || [] })
|
get().setCategories(res.data.categories || [])
|
||||||
}
|
}
|
||||||
}).catch((err) => {
|
}).catch((err) => {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
|||||||
Reference in new issue
Block a user