u
This commit is contained in:
1 parent
34240d2e16
commit
bf350d8d35
21 files changed
+670
-304
No files matched your search
@@ -10,7 +10,7 @@ export default function TestScreen() {
|
||||
const spaceStore = useSpaceStore()
|
||||
const navigation = useNavigation()
|
||||
|
||||
console.log(navigation.getState()?.routes)
|
||||
console.log(navigation.getState()?.routeNames)
|
||||
|
||||
return (
|
||||
<View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
|
||||
|
||||
@@ -46,6 +46,7 @@ export default function CategorySheet({
|
||||
|
||||
const handleSelect = useCallback(
|
||||
(item: FinanceCategory) => {
|
||||
console.log(item)
|
||||
onSelect?.(item)
|
||||
onClose?.()
|
||||
},
|
||||
@@ -74,9 +75,9 @@ export default function CategorySheet({
|
||||
{
|
||||
title: '筛选',
|
||||
data: [
|
||||
createVirtualCategory(-1, '全部', -1),
|
||||
createVirtualCategory(-2, '支出', 0),
|
||||
createVirtualCategory(-3, '收入', 1),
|
||||
createVirtualCategory(0, '全部', -1),
|
||||
createVirtualCategory(-1, '支出', 0),
|
||||
createVirtualCategory(-2, '收入', 1),
|
||||
],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
import type { FinanceRecord, FinanceRecordGroup } from '@/types/finance/record'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import type { FinanceRecord } from '@/types/finance/record'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { Pressable, RefreshControl, SectionList, Text, Vibration, View } from 'react-native'
|
||||
import { LucideIcon } from '@/components/common/ao'
|
||||
import EmptyState from '@/components/common/EmptyStatus'
|
||||
import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
|
||||
import { Menu, MenuItem, MenuItemLabel, MenuSeparator } from '@/components/ui/menu'
|
||||
import { useMoneyCategoryStore } from '@/store/moneyCategory'
|
||||
import { useFinanceStore } from '@/store/finance'
|
||||
import { useUserStore } from '@/store/user'
|
||||
import { MoneyCategoryEnum } from '@/types/money/category'
|
||||
import { FinanceCategoryEnum } from '@/types/finance/category'
|
||||
|
||||
interface StaticTransactionsProps {
|
||||
records: FinanceRecordGroup[]
|
||||
onUpdate?: (data: FinanceRecord) => void
|
||||
onDelete?: (id: number) => void
|
||||
onDetail?: (id: number) => void
|
||||
@@ -19,18 +18,18 @@ interface StaticTransactionsProps {
|
||||
}
|
||||
|
||||
export default function StaticTransactions({
|
||||
records,
|
||||
onUpdate,
|
||||
onDelete,
|
||||
onDetail,
|
||||
onLoadMore,
|
||||
onRefreshData,
|
||||
}: StaticTransactionsProps) {
|
||||
const moneyCategoryStore = useMoneyCategoryStore()
|
||||
const financeStore = useFinanceStore()
|
||||
const userID = useUserStore().userInfo?.id
|
||||
const [refreshing, setRefreshing] = useState(false)
|
||||
const [loadingMore, setLoadingMore] = useState(false)
|
||||
const [selectedId, setSelectedId] = useState<number | null>(null)
|
||||
const [sections, setSections] = useState<any[]>([])
|
||||
|
||||
const handleRefresh = useCallback(async () => {
|
||||
if (!onRefreshData)
|
||||
@@ -56,12 +55,14 @@ export default function StaticTransactions({
|
||||
}
|
||||
}, [onLoadMore, loadingMore])
|
||||
|
||||
const sections = records.map(group => ({
|
||||
title: group.record_time,
|
||||
totalIncome: group.income,
|
||||
totalExpense: group.expense,
|
||||
data: group.records,
|
||||
}))
|
||||
useEffect(() => {
|
||||
setSections(financeStore.records.map(group => ({
|
||||
title: group.record_time,
|
||||
totalIncome: group.income,
|
||||
totalExpense: group.expense,
|
||||
data: group.records,
|
||||
})))
|
||||
}, [financeStore.records])
|
||||
|
||||
return (
|
||||
<SectionList
|
||||
@@ -115,7 +116,7 @@ export default function StaticTransactions({
|
||||
)}
|
||||
|
||||
renderItem={({ item }) => {
|
||||
const category = moneyCategoryStore.getCategoryById(item.category_id)
|
||||
const category = financeStore.getCategoryById(item.category_id)
|
||||
return (
|
||||
<Menu
|
||||
style={{
|
||||
@@ -174,7 +175,7 @@ export default function StaticTransactions({
|
||||
<Text
|
||||
className="text-sm text-gray-600 text-right"
|
||||
>
|
||||
{category?.type === MoneyCategoryEnum.Income ? '+' : '-'}
|
||||
{category?.type === FinanceCategoryEnum.Income ? '+' : '-'}
|
||||
{' '}
|
||||
{item.amount}
|
||||
</Text>
|
||||
@@ -184,32 +185,27 @@ export default function StaticTransactions({
|
||||
)
|
||||
}}
|
||||
>
|
||||
{
|
||||
userID === item.member_id && (
|
||||
<>
|
||||
<MenuItem
|
||||
key="update"
|
||||
textValue="update"
|
||||
className="flex gap-3"
|
||||
style={{
|
||||
minWidth: 150,
|
||||
height: 42,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
alignItems: 'center',
|
||||
borderRadius: 0,
|
||||
}}
|
||||
onPress={() => {
|
||||
onUpdate?.(item)
|
||||
}}
|
||||
>
|
||||
<LucideIcon icon="Pencil" size={18} />
|
||||
<MenuItemLabel className="text-lg">更新</MenuItemLabel>
|
||||
</MenuItem>
|
||||
<MenuSeparator />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
<MenuItem
|
||||
key="update"
|
||||
textValue="update"
|
||||
className="flex gap-3"
|
||||
style={{
|
||||
minWidth: 150,
|
||||
height: 42,
|
||||
paddingTop: 0,
|
||||
paddingBottom: 0,
|
||||
alignItems: 'center',
|
||||
borderRadius: 0,
|
||||
}}
|
||||
onPress={() => {
|
||||
onUpdate?.(item)
|
||||
}}
|
||||
>
|
||||
<LucideIcon icon="Pencil" size={18} />
|
||||
<MenuItemLabel className="text-lg">更新</MenuItemLabel>
|
||||
</MenuItem>
|
||||
<MenuSeparator />
|
||||
|
||||
<MenuItem
|
||||
key="delete"
|
||||
@@ -231,6 +227,7 @@ export default function StaticTransactions({
|
||||
<MenuItemLabel className="text-lg">删除</MenuItemLabel>
|
||||
</MenuItem>
|
||||
<MenuSeparator />
|
||||
|
||||
<MenuItem
|
||||
key="detail"
|
||||
textValue="detail"
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
import type { DateMorePickerMode } from '@/components/common/ao/DateMorePicker'
|
||||
import type { FinanceCategory } from '@/types/finance/category'
|
||||
import { useFocusEffect, useRouter } from 'expo-router'
|
||||
import React, { useCallback, useEffect, useState } from 'react'
|
||||
import { Image, Text, TouchableOpacity, View } from 'react-native'
|
||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { toast } from 'sonner-native'
|
||||
import { ADateMorePicker, ADatePicker, ADrawer, AListItem, LucideIcon } from '@/components/common/ao'
|
||||
import { resolveDateRange } from '@/components/common/ao/DateMorePicker'
|
||||
import { Fab, FabIcon } from '@/components/ui/fab'
|
||||
import { AddIcon } from '@/components/ui/icon'
|
||||
import { listFinanceRecordsApi } from '@/request/api/finance/record'
|
||||
import { deleteMoneyRecordApi, listMoneyRecordsApi } from '@/request/api/money/record'
|
||||
import { deleteFinanceRecordApi, listFinanceRecordsApi } from '@/request/api/finance/record'
|
||||
import { useFinanceStore } from '@/store/finance'
|
||||
import { getCurrentYearMonth, normalizeDateTime } from '@/utils/time'
|
||||
import { toast } from '@/utils/toast'
|
||||
import { getCurrentYearMonth } from '@/utils/time'
|
||||
import CategorySheet from './components/CategorySheet'
|
||||
import TransactionsList from './components/TransactionsList'
|
||||
|
||||
@@ -27,10 +27,14 @@ export default function Money() {
|
||||
const [showDatePicker, setShowDatePicker] = useState(false)
|
||||
const [dateMode, setDateMode] = useState<DateMorePickerMode>('month')
|
||||
|
||||
const [searchParams, setSearchParams] = useState({
|
||||
const [searchParams, setSearchParams] = useState<{
|
||||
startTime: string
|
||||
endTime: string
|
||||
category: FinanceCategory | null
|
||||
}>({
|
||||
startTime: getCurrentYearMonth(),
|
||||
endTime: '',
|
||||
category: -1,
|
||||
category: null,
|
||||
})
|
||||
|
||||
const [showCategorySheet, setShowCategorySheet] = useState(false)
|
||||
@@ -41,21 +45,23 @@ export default function Money() {
|
||||
function fetchFinanceRecords() {
|
||||
const resolveDate = resolveDateRange(dateMode, searchParams.startTime)
|
||||
|
||||
let searchType = 0
|
||||
|
||||
if (searchParams.category) {
|
||||
if (searchParams.category.id === -1 || searchParams.category.id === -2) {
|
||||
searchType = searchParams.category.type
|
||||
}
|
||||
}
|
||||
|
||||
listFinanceRecordsApi({
|
||||
start_time: resolveDate.startTime,
|
||||
end_time: resolveDate.endTime,
|
||||
category_id: 0,
|
||||
category_id: searchParams.category ? searchParams.category.id : 0,
|
||||
category_type: searchType,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
financeStore.setRecords(res.data.records || [])
|
||||
}
|
||||
else {
|
||||
toast.show({
|
||||
title: res.message || '查询错误,请稍后再试',
|
||||
})
|
||||
}
|
||||
}).catch((err) => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -63,15 +69,25 @@ export default function Money() {
|
||||
useCallback(() => {
|
||||
financeStore.ensureCategoriesLoaded()
|
||||
fetchFinanceRecords()
|
||||
}, [searchParams.startTime, searchParams.endTime]),
|
||||
}, [searchParams.startTime, searchParams.endTime, searchParams.category]),
|
||||
)
|
||||
|
||||
async function handleDeleteRecord(id: number) {
|
||||
await deleteMoneyRecordApi({
|
||||
id,
|
||||
const toastId = toast.loading('正在删除...', {
|
||||
duration: Infinity,
|
||||
})
|
||||
|
||||
fetchFinanceRecords()
|
||||
await deleteFinanceRecordApi({
|
||||
id,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
toast.success('删除成功')
|
||||
|
||||
fetchFinanceRecords()
|
||||
}
|
||||
}).finally(() => {
|
||||
toast.dismiss(toastId)
|
||||
})
|
||||
}
|
||||
|
||||
function timeModeTip() {
|
||||
@@ -117,7 +133,11 @@ export default function Money() {
|
||||
onPress={() => setShowCategorySheet(true)}
|
||||
className="flex flex-row items-center gap-1"
|
||||
>
|
||||
<Text className="font-semibold text-lg">分类</Text>
|
||||
<Text className="font-semibold text-lg">
|
||||
{
|
||||
searchParams.category ? searchParams.category.name : '分类'
|
||||
}
|
||||
</Text>
|
||||
<LucideIcon icon="ChevronDown" size={16} color="#666" clickable={false} />
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
@@ -180,7 +200,6 @@ export default function Money() {
|
||||
|
||||
<View className="flex-1 mt-2">
|
||||
<TransactionsList
|
||||
records={financeStore.records}
|
||||
onUpdate={(data) => {
|
||||
router.push({
|
||||
pathname: '/finance/record/editor',
|
||||
@@ -188,7 +207,7 @@ export default function Money() {
|
||||
})
|
||||
}}
|
||||
onDetail={(id) => {
|
||||
router.navigate({
|
||||
router.push({
|
||||
pathname: '/finance/record/detail',
|
||||
params: { id },
|
||||
})
|
||||
@@ -248,15 +267,16 @@ export default function Money() {
|
||||
|
||||
<CategorySheet
|
||||
open={showCategorySheet}
|
||||
value={searchParams.category}
|
||||
value={searchParams.category?.id}
|
||||
moneyCategories={financeStore.categories}
|
||||
onClose={() => {
|
||||
setShowCategorySheet(false)
|
||||
}}
|
||||
onSelect={(category) => {
|
||||
console.log(category)
|
||||
setSearchParams(prev => ({
|
||||
...prev,
|
||||
category: category.id,
|
||||
category,
|
||||
}))
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -181,7 +181,7 @@ export default function Auth() {
|
||||
|
||||
userStore.setUserInfo({
|
||||
token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjo3Nzk4OTU0MTkxMjE3MzMsImlhdCI6MTc3MjU0MDU1OH0.M5KqfPWdpOAlCCtZrJRxwKc8bRpGBjEt9BNCfgBZgnc',
|
||||
id: 778115032100933,
|
||||
id: 779895419121733,
|
||||
nickname: 'Zhang',
|
||||
avatar: 'http://thirdqq.qlogo.cn/ek_qqapp/AQHVialeUibE42oV8RvTTVZ4tyRlPYeJrUibWTwtCPfV68E4rSzn9cw00GkXQ3QDTiawwhjoqQb7/100',
|
||||
})
|
||||
@@ -194,7 +194,7 @@ export default function Auth() {
|
||||
// name: '(tabs)' as never,
|
||||
// }],
|
||||
// })
|
||||
// console.log(navigation.getState()?.routeNames)
|
||||
console.log(navigation.getState()?.routeNames)
|
||||
|
||||
// ('/')
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ import { AButton, ATab, LucideIcon } from '@/components/common/ao'
|
||||
import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
|
||||
import CustomHeader from '@/components/custom/Header'
|
||||
import { Spinner } from '@/components/ui/spinner'
|
||||
import { createFinanceCategoryApi, deleteFinanceCategoryApi, updateFinanceCategoryApi } from '@/request/api/finance/category'
|
||||
import { createFinanceCategoryApi, deleteFinanceCategoryApi, updateFinanceCategoryApi, updateFinanceCategorySortNumberApi } from '@/request/api/finance/category'
|
||||
import { useFinanceStore } from '@/store/finance'
|
||||
import { useMoneyCategoryStore } from '@/store/moneyCategory'
|
||||
import { useSpaceStore } from '@/store/space'
|
||||
@@ -209,7 +209,23 @@ export default function MoneyCategorySetting() {
|
||||
const newData = dragData.data
|
||||
|
||||
if (JSON.stringify(categories) !== JSON.stringify(newData)) {
|
||||
moneyCategoryStore.updateSortNumbers(newData, selectedTab)
|
||||
const updateData: any[] = []
|
||||
newData.forEach((item, index) => {
|
||||
item.sort_number = index
|
||||
|
||||
updateData.push({
|
||||
id: item.id,
|
||||
sort_number: index,
|
||||
})
|
||||
})
|
||||
|
||||
financeStore.setCategories(newData)
|
||||
|
||||
updateFinanceCategorySortNumberApi({
|
||||
categories: updateData,
|
||||
}).then((res) => {
|
||||
console.log(res)
|
||||
})
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
import type { FinanceCategory } from '@/types/finance/category'
|
||||
import type { FinanceRecordDetail } from '@/types/finance/record'
|
||||
import { useFocusEffect, useLocalSearchParams, useRouter } from 'expo-router'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import { ActivityIndicator, Text, View } from 'react-native'
|
||||
import { toast } from 'sonner-native'
|
||||
import { AButton, ACell, ACellGroup, AModal } from '@/components/common/ao'
|
||||
import MoneyCategoryIcon from '@/components/common/MoneyCategoryIcon'
|
||||
import CustomHeader from '@/components/custom/Header'
|
||||
import { deleteFinanceRecordApi, getFinanceRecordDetailApi } from '@/request/api/finance/record'
|
||||
import { useFinanceStore } from '@/store/finance'
|
||||
import { useNavStore } from '@/store/navigation'
|
||||
import { MoneyCategoryEnum } from '@/types/money/category'
|
||||
import { toDateTimeString } from '@/utils/time'
|
||||
|
||||
export default function MoneyRecordDetail() {
|
||||
const router = useRouter()
|
||||
const financeStore = useFinanceStore()
|
||||
const navStore = useNavStore()
|
||||
|
||||
const { id: detailId } = useLocalSearchParams<{ id: string }>()
|
||||
|
||||
const [showDeleteModal, setShowDeleteModal] = useState(false)
|
||||
const [detail, setDetail] = useState<FinanceRecordDetail | null>(null)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
const [category, setCategory] = useState<FinanceCategory | null>(null)
|
||||
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
if (detailId) {
|
||||
setLoading(true)
|
||||
|
||||
getFinanceRecordDetailApi({
|
||||
id: Number(detailId),
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
setDetail(res.data)
|
||||
setLoading(false)
|
||||
|
||||
const category = financeStore.getCategoryById(res.data.category_id)
|
||||
if (category) {
|
||||
setCategory(category)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}, []),
|
||||
)
|
||||
|
||||
return (
|
||||
<>
|
||||
<View className="flex-1">
|
||||
<CustomHeader title="账单详情" />
|
||||
<View className="flex-1">
|
||||
{
|
||||
loading
|
||||
? (
|
||||
<View className="flex-1 items-center justify-center">
|
||||
<ActivityIndicator size="large" color="#333" />
|
||||
</View>
|
||||
)
|
||||
: (
|
||||
<>
|
||||
<View className="flex flex-col items-center">
|
||||
<MoneyCategoryIcon name={category?.icon} size={56} />
|
||||
<Text className="mt-1">{category?.name}</Text>
|
||||
</View>
|
||||
|
||||
<ACellGroup showDivider={false} className="mt-4">
|
||||
<ACell title="类别" rightText={category?.type === MoneyCategoryEnum.Expense ? '支出' : '收入'} />
|
||||
<ACell title="金额" rightText={detail?.amount} />
|
||||
<ACell title="备注" rightText={detail?.remark} />
|
||||
<ACell title="日期" rightText={detail?.record_time} />
|
||||
<ACell title="创建人" rightText={detail?.create_member_name} />
|
||||
<ACell title="创建时间" rightText={toDateTimeString(detail?.create_time || '')} />
|
||||
{
|
||||
detail?.create_member_id !== detail?.update_member_id && (
|
||||
<>
|
||||
<ACell title="更新人" rightText={detail?.update_member_name} />
|
||||
<ACell title="更新时间" rightText={toDateTimeString(detail?.update_time || '')} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
</ACellGroup>
|
||||
|
||||
<View className="flex flex-row px-4 mt-4 gap-4">
|
||||
|
||||
<AButton
|
||||
className="flex-1"
|
||||
action="negative"
|
||||
variant="outline"
|
||||
onPress={() => {
|
||||
setShowDeleteModal(true)
|
||||
}}
|
||||
>
|
||||
删除
|
||||
</AButton>
|
||||
<AButton
|
||||
className="flex-1"
|
||||
onPress={() => {
|
||||
router.navigate({
|
||||
pathname: '/finance/record/editor',
|
||||
params: { id: detailId },
|
||||
})
|
||||
}}
|
||||
>
|
||||
编辑
|
||||
</AButton>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
</View>
|
||||
|
||||
<AModal
|
||||
isOpen={showDeleteModal}
|
||||
title="提示"
|
||||
body="确认删除该条账单?"
|
||||
onConfirm={async () => {
|
||||
setShowDeleteModal(false)
|
||||
if (!detail) {
|
||||
toast.warning('当前记录无效')
|
||||
router.back()
|
||||
return
|
||||
}
|
||||
|
||||
const toastId = toast.loading('正在删除...', {
|
||||
duration: Infinity,
|
||||
})
|
||||
|
||||
await deleteFinanceRecordApi({
|
||||
id: detail?.id,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
toast.success('删除成功')
|
||||
|
||||
router.back()
|
||||
}
|
||||
}).finally(() => {
|
||||
toast.dismiss(toastId)
|
||||
})
|
||||
}}
|
||||
onCancel={() => {
|
||||
setShowDeleteModal(false)
|
||||
}}
|
||||
onClose={() => {
|
||||
setShowDeleteModal(false)
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { FinanceRecord } from '@/types/finance/record'
|
||||
import Big from 'big.js'
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Keyboard, Pressable, StyleSheet, Text, TouchableWithoutFeedback, View } from 'react-native'
|
||||
@@ -39,23 +40,15 @@ const keys: KeydownItem[] = [
|
||||
|
||||
interface AmountKeyboardProps {
|
||||
show?: boolean
|
||||
record_time?: string
|
||||
amount?: string
|
||||
remark?: string
|
||||
|
||||
// 回调:当键盘输入或修改数据时触发
|
||||
onChange?: (data: { record_time: string, amount: string, remark: string }) => void
|
||||
record: FinanceRecord
|
||||
|
||||
// 可选:点击完成按钮触发
|
||||
onDone?: () => void
|
||||
onDone?: (record: FinanceRecord) => void
|
||||
}
|
||||
|
||||
export default function AmountKeyboard({
|
||||
show = false,
|
||||
record_time = '',
|
||||
amount = '',
|
||||
remark = '',
|
||||
onChange,
|
||||
record,
|
||||
onDone,
|
||||
}: AmountKeyboardProps) {
|
||||
// 自定义键盘计算过程
|
||||
@@ -67,43 +60,36 @@ export default function AmountKeyboard({
|
||||
// 键盘高度
|
||||
const keyboardHeight = useKeyboardHeight()
|
||||
|
||||
// 使用一个对象管理三个字段
|
||||
const [internalData, setInternalData] = useState({
|
||||
record_time: record_time || getCurrentDate(),
|
||||
amount: amount || '0',
|
||||
remark: remark || '',
|
||||
const [innerRecord, setInnerRecord] = useState<FinanceRecord>({
|
||||
id: 0,
|
||||
category_id: 0,
|
||||
amount: '',
|
||||
remark: '',
|
||||
record_time: getCurrentDate(),
|
||||
member_id: 0,
|
||||
member_name: '',
|
||||
})
|
||||
|
||||
// 如果父组件 props 变化,需要同步
|
||||
useEffect(() => {
|
||||
setInternalData({
|
||||
record_time: record_time || getCurrentDate(),
|
||||
amount: amount || '0',
|
||||
remark: remark || '',
|
||||
setInnerRecord(record)
|
||||
setKeydownInput(record.amount || '')
|
||||
}, [record])
|
||||
|
||||
useEffect(() => {
|
||||
setInnerRecord((prev) => {
|
||||
if (!keydownInput) {
|
||||
return { ...prev, amount: '0' }
|
||||
}
|
||||
try {
|
||||
const result = calculateWithBig(keydownInput)
|
||||
return { ...prev, amount: result }
|
||||
}
|
||||
catch {
|
||||
return { ...prev, amount: '0' }
|
||||
}
|
||||
})
|
||||
}, [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)
|
||||
onChange?.(updated)
|
||||
}
|
||||
|
||||
// 计算过程
|
||||
const handlePress = (key: string | number) => {
|
||||
setKeydownInput((prev) => {
|
||||
@@ -113,7 +99,8 @@ export default function AmountKeyboard({
|
||||
newInput = prev.slice(0, -1)
|
||||
}
|
||||
else if (key === 'clear') {
|
||||
setInternalData({
|
||||
setInnerRecord({
|
||||
...innerRecord,
|
||||
record_time: getCurrentDate(),
|
||||
amount: '0',
|
||||
remark: '',
|
||||
@@ -121,7 +108,7 @@ export default function AmountKeyboard({
|
||||
return ''
|
||||
}
|
||||
else if (key === '/') {
|
||||
onDone?.()
|
||||
onDone?.(innerRecord)
|
||||
return prev
|
||||
}
|
||||
else {
|
||||
@@ -206,7 +193,7 @@ export default function AmountKeyboard({
|
||||
newInput = prev + keyStr
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return newInput
|
||||
})
|
||||
}
|
||||
@@ -271,10 +258,11 @@ export default function AmountKeyboard({
|
||||
}
|
||||
|
||||
return (
|
||||
<View style={{
|
||||
backgroundColor: '#fff',
|
||||
elevation: 20,
|
||||
}}
|
||||
<View
|
||||
style={{
|
||||
backgroundColor: '#fff',
|
||||
elevation: 20,
|
||||
}}
|
||||
>
|
||||
<TouchableWithoutFeedback onPress={Keyboard.dismiss}>
|
||||
<View className="h-28 flex pl-4 pr-4 justify-center">
|
||||
@@ -284,19 +272,24 @@ export default function AmountKeyboard({
|
||||
onPress={() => setShowDatePicker(true)}
|
||||
>
|
||||
<LucideIcon icon="Calendar" size={16} color="#60a5fa" clickable={false} />
|
||||
<Text className="text-gray-600 text-sm">{internalData.record_time}</Text>
|
||||
<Text className="text-gray-600 text-sm">{innerRecord.record_time}</Text>
|
||||
<LucideIcon icon="ChevronDown" size={12} color="#aaa" clickable={false} />
|
||||
</Pressable>
|
||||
<Text className="text-3xl text-blue-700 text-right">{internalData.amount}</Text>
|
||||
<Text className="text-3xl text-blue-700 text-right">{innerRecord.amount}</Text>
|
||||
</View>
|
||||
|
||||
<Text className="text-gray-400 text-right">{keydownInput}</Text>
|
||||
<AInput
|
||||
value={internalData.remark}
|
||||
value={innerRecord.remark}
|
||||
placeholder="备注"
|
||||
className="bg-[#f2f3f5] border-0"
|
||||
size="sm"
|
||||
onChangeText={val => handleChange({ remark: val })}
|
||||
onChangeText={(val) => {
|
||||
setInnerRecord({
|
||||
...innerRecord,
|
||||
remark: val,
|
||||
})
|
||||
}}
|
||||
onFocus={() => setRemarkIsFocused(true)}
|
||||
onBlur={() => setRemarkIsFocused(false)}
|
||||
/>
|
||||
@@ -352,14 +345,18 @@ export default function AmountKeyboard({
|
||||
<ADatePicker
|
||||
pickerTitle="记账时间"
|
||||
mode="date"
|
||||
date={internalData.record_time}
|
||||
date={innerRecord.record_time}
|
||||
show={showDatePicker}
|
||||
onClose={() => {
|
||||
setShowDatePicker(false)
|
||||
}}
|
||||
onConfirm={(date) => {
|
||||
setShowDatePicker(false)
|
||||
handleChange({ record_time: date })
|
||||
|
||||
setInnerRecord({
|
||||
...innerRecord,
|
||||
record_time: date,
|
||||
})
|
||||
}}
|
||||
/>
|
||||
</View>
|
||||
|
||||
@@ -29,7 +29,7 @@ export default function MoenyRecordEditor() {
|
||||
const defaultFinanceRecord: FinanceRecord = {
|
||||
id: -1,
|
||||
category_id: -1,
|
||||
amount: '0.00',
|
||||
amount: '',
|
||||
remark: '',
|
||||
record_time: getCurrentDate(),
|
||||
member_id: -1,
|
||||
@@ -58,26 +58,26 @@ export default function MoenyRecordEditor() {
|
||||
}, [])
|
||||
|
||||
// 保存记录
|
||||
function handleSaveRecord() {
|
||||
async function handleSaveRecord(record: FinanceRecord) {
|
||||
if (selectedCategory === null) {
|
||||
toast.warning('请选择分类')
|
||||
return
|
||||
}
|
||||
|
||||
if (financeRecord.amount === '0.00') {
|
||||
if (record.amount === '0.00') {
|
||||
toast.warning('请输入金额')
|
||||
return
|
||||
}
|
||||
|
||||
const amount = financeRecord.amount.replace(/^-+/, '')
|
||||
const innerAmount = record.amount.replace(/^-+/, '')
|
||||
|
||||
if (updateId) {
|
||||
updateFinanceRecordApi({
|
||||
await updateFinanceRecordApi({
|
||||
id: financeRecord.id,
|
||||
category_id: selectedCategory.id,
|
||||
amount,
|
||||
remark: financeRecord.remark,
|
||||
record_time: financeRecord.record_time,
|
||||
amount: innerAmount,
|
||||
remark: record.remark,
|
||||
record_time: record.record_time,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
router.back()
|
||||
@@ -85,11 +85,11 @@ export default function MoenyRecordEditor() {
|
||||
})
|
||||
}
|
||||
else {
|
||||
createFinanceRecordApi({
|
||||
amount,
|
||||
await createFinanceRecordApi({
|
||||
amount: innerAmount,
|
||||
category_id: selectedCategory.id,
|
||||
remark: financeRecord.remark,
|
||||
record_time: financeRecord.record_time,
|
||||
remark: record.remark,
|
||||
record_time: record.record_time,
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.isSuccess) {
|
||||
@@ -116,7 +116,7 @@ export default function MoenyRecordEditor() {
|
||||
<TouchableOpacity
|
||||
className="w-[30%] flex items-center px-4 flex-row"
|
||||
onPress={() => {
|
||||
router.navigate('/finance/category/setting')
|
||||
router.push('/finance/category')
|
||||
}}
|
||||
>
|
||||
<LucideIcon icon="LayoutList" />
|
||||
@@ -169,17 +169,7 @@ export default function MoenyRecordEditor() {
|
||||
|
||||
<AmountKeyboard
|
||||
show={showKeydown}
|
||||
record_time={financeRecord.record_time}
|
||||
amount={financeRecord.amount}
|
||||
remark={financeRecord.remark}
|
||||
onChange={(data) => {
|
||||
setFinanceRecord({
|
||||
...financeRecord,
|
||||
record_time: data.record_time,
|
||||
amount: data.amount,
|
||||
remark: data.remark,
|
||||
})
|
||||
}}
|
||||
record={financeRecord}
|
||||
onDone={handleSaveRecord}
|
||||
/>
|
||||
</View>
|
||||
|
||||
Reference in new issue
Block a user