This commit is contained in:
oneao committed 2026-01-16 17:27:34 +08:00
1 parent 44ab5416a3
commit 1935906676
17 files changed
+322 -266

No files matched your search

@@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'
import { Pressable, Text, View } from 'react-native'
import { AButton } from '@/components/common/ao'
import { AButton, AInput } from '@/components/common/ao'
import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
import {
Drawer,
@@ -10,34 +10,38 @@ import {
DrawerFooter,
DrawerHeader,
} from '@/components/ui/drawer'
import { Input, InputField } from '@/components/ui/input'
import { useMoneyCategoryStore } from '@/store/moneyCategory'
import { MoneyCategoryEnum } from '@/types/money/category'
interface CreateCategoryProps {
export interface EditCategoryForm {
id?: number
name: string
icon: string
}
interface EditCategoryProps {
open: boolean
onClose?: () => void
type: MoneyCategoryEnum
}
interface CreateCategoryForm {
icon: string
name: string
onConfirm?: (form: EditCategoryForm) => Promise<void>
}
export default function CreateCategory({
open,
onClose,
type,
}: CreateCategoryProps) {
onConfirm,
}: EditCategoryProps) {
const moneyCategoryStore = useMoneyCategoryStore()
const [isOpen, setIsOpen] = useState(open)
const [form, setForm] = useState<CreateCategoryForm>({
const [form, setForm] = useState<EditCategoryForm>({
icon: '',
name: '',
})
const [confirmDisabled, setConfirmDisabled] = useState(true)
const [confirmLoading, setConfirmLoading] = useState(false)
@@ -46,13 +50,13 @@ export default function CreateCategory({
}, [open])
useEffect(() => {
if (form.name && form.name.length < 6) {
if (form.name && form.name.length <= 5 && form.icon) {
setConfirmDisabled(false)
}
else {
setConfirmDisabled(true)
}
}, [form.name])
}, [form.name, form.icon])
function handleClose() {
setIsOpen(false)
@@ -79,15 +83,49 @@ export default function CreateCategory({
<Text className="text-lg font-medium">{type === MoneyCategoryEnum.Expense ? '添加支出分类' : '添加收入分类'}</Text>
<AButton size="xs" disabled={confirmDisabled} loading={confirmLoading}>确认</AButton>
<AButton
size="xs"
disabled={confirmDisabled}
loading={confirmLoading}
onPress={async () => {
if (!onConfirm) {
return
}
try {
setConfirmLoading(true)
const trimmedForm = {
...form,
name: form.name.trim(),
}
await onConfirm(trimmedForm)
}
catch (err) {
console.error(err)
// 外部报错时,保持弹窗打开,loading 结束
}
finally {
setConfirmLoading(false)
}
}}
>
确认
</AButton>
</View>
<Input
variant="outline"
>
<InputField placeholder="请输入名称(不超过5个字符)" />
</Input>
<AInput
placeholder="请输入名称(不超过5个字符)"
onChangeText={(text) => {
setForm((val) => {
return {
...val,
name: text,
}
})
}}
/>
</DrawerHeader>
<DrawerBody className="mb-16">
{Object.entries(moneyCategoryStore.ICON_GROUPS).map(([groupName, icons]) => (
@@ -1,8 +1,9 @@
import type { SortableGridRenderItem } from 'react-native-sortables'
import type { EditCategoryForm } from './components/EditCategory'
import type { MoneyCategoryItem } from '@/types/money/category'
import { useCallback, useEffect, useState } from 'react'
import { Pressable, Text, Vibration, View } from 'react-native'
import { Pressable, Text, Vibration, View } from 'react-native'
import Animated, { useAnimatedRef } from 'react-native-reanimated'
import Sortable from 'react-native-sortables'
import { AButton, ATab, LucideIcon } from '@/components/common/ao'
@@ -10,7 +11,8 @@ import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
import CustomHeader from '@/components/custom/Header'
import { useMoneyCategoryStore } from '@/store/moneyCategory'
import { MoneyCategoryEnum } from '@/types/money/category'
import CreateCategory from './components/CreateCategory'
import { toast } from '@/utils/toast'
import EditCategory from './components/EditCategory'
export default function MoneyCategorySetting() {
const [selectedTab, setSelectedTab] = useState(MoneyCategoryEnum.Expense)
@@ -27,7 +29,7 @@ export default function MoneyCategorySetting() {
useEffect(() => {
setCategories(selectedTab === MoneyCategoryEnum.Expense ? moneyCategoryStore.getExpenseCategories() : moneyCategoryStore.getIncomeCategories())
}, [selectedTab])
}, [selectedTab, moneyCategoryStore.categories])
const renderItem = useCallback<SortableGridRenderItem<MoneyCategoryItem>>(
({ item }) => (
@@ -61,6 +63,42 @@ export default function MoneyCategorySetting() {
[],
)
async function handleConfirmEdit(form: EditCategoryForm) {
const isDuplicate = moneyCategoryStore.isDuplicateCategory(form.name, selectedTab)
if (isDuplicate) {
toast.show({
type: 'warning',
title: '类别不能重复',
})
return
}
try {
await moneyCategoryStore.createCategory({
id: 0,
name: form.name,
icon: form.icon,
sort_number: 0,
type: selectedTab,
})
setOpenCreateCategory(false)
}
catch (err) {
const message
= err instanceof Error
? err.message
: '新增失败,请稍后再试'
toast.show({
type: 'error',
title: message,
})
}
}
return (
<View className="flex-1">
<CustomHeader
@@ -146,7 +184,6 @@ export default function MoneyCategorySetting() {
// 设置 1 秒后删除
const timer = setTimeout(() => {
const deleteId = Number(draggingId)
setCategories(prev => prev.filter(c => c.id !== deleteId))
moneyCategoryStore.deleteCategoryById(deleteId)
@@ -189,10 +226,11 @@ export default function MoneyCategorySetting() {
</View>
</View>
<CreateCategory
<EditCategory
type={selectedTab}
open={openCreateCategory}
onClose={() => setOpenCreateCategory(false)}
onConfirm={handleConfirmEdit}
/>
</View>
)
@@ -83,6 +83,21 @@ export default function AmountKeyboard({
})
}, [record_time, amount, remark])
useEffect(() => {
if (!keydownInput) {
handleChange({ amount: '0' })
return
}
try {
const computed = calculateWithBig(keydownInput)
handleChange({ amount: computed })
}
catch {
handleChange({ amount: '0' })
}
}, [keydownInput])
const handleChange = (newData: Partial<typeof internalData>) => {
const updated = { ...internalData, ...newData }
setInternalData(updated)
@@ -98,9 +113,11 @@ export default function AmountKeyboard({
newInput = prev.slice(0, -1)
}
else if (key === 'clear') {
const currentDate = getCurrentDate()
handleChange({ record_time: currentDate, amount: '', remark: '' })
setInternalData({
record_time: getCurrentDate(),
amount: '0',
remark: '',
})
return ''
}
else if (key === '/') {
@@ -189,16 +206,7 @@ export default function AmountKeyboard({
newInput = prev + keyStr
}
}
// 计算结果
try {
const computed = calculateWithBig(newInput)
handleChange({ amount: computed })
}
catch (e) {
handleChange({ amount: '0' })
}
return newInput
})
}
@@ -66,7 +66,7 @@ export default function CategoryGrid({
>
<MoneyCategoryIcon
name={item.icon}
size={24}
size={28}
color={
isSelected ? '#fff' : '#000'
}
@@ -117,6 +117,7 @@ export default function MoenyRecordEditor() {
if (res.isSuccess) {
const result: MoneyRecordItem = {
...res.data,
category_id: selectedCategory.id,
category_type: selectedCategory.type,
category_name: selectedCategory.name,
category_icon: selectedCategory.icon,