From 7a9a22e02e218bab561912a36eb43006df6a18c8 Mon Sep 17 00:00:00 2001 From: oneao Date: Mon, 2 Feb 2026 22:42:58 +0800 Subject: [PATCH] u --- code/allapp/allapp-rn/package.json | 1 + code/allapp/allapp-rn/pnpm-lock.yaml | 14 +++ code/allapp/allapp-rn/src/app/(tabs)/love.tsx | 113 ++++++++++++------ .../(tabs)/money/components/CategorySheet.tsx | 51 +++++++- .../src/app/money/analysis/index.tsx | 24 ++-- .../components/common/ao/DateMorePicker.tsx | 3 + .../src/components/common/ao/DatePicker.tsx | 69 ++++++++++- .../components/common/ao/SegmentedTabs.tsx | 2 +- .../src/components/common/ao/Tab.tsx | 26 ++-- 9 files changed, 243 insertions(+), 60 deletions(-) create mode 100644 code/allapp/allapp-rn/src/components/common/ao/DateMorePicker.tsx diff --git a/code/allapp/allapp-rn/package.json b/code/allapp/allapp-rn/package.json index 3edec494..a5985f19 100644 --- a/code/allapp/allapp-rn/package.json +++ b/code/allapp/allapp-rn/package.json @@ -18,6 +18,7 @@ "@legendapp/list": "^2.0.19", "@legendapp/motion": "2.4.0", "@react-native-async-storage/async-storage": "2.2.0", + "@react-native-segmented-control/segmented-control": "^2.5.7", "@react-navigation/drawer": "^7.5.0", "@yz1311/react-native-wheel-picker": "^0.4.5", "alova": "^3.3.4", diff --git a/code/allapp/allapp-rn/pnpm-lock.yaml b/code/allapp/allapp-rn/pnpm-lock.yaml index 5bef9632..26be92a5 100644 --- a/code/allapp/allapp-rn/pnpm-lock.yaml +++ b/code/allapp/allapp-rn/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: '@react-native-async-storage/async-storage': specifier: 2.2.0 version: 2.2.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0)) + '@react-native-segmented-control/segmented-control': + specifier: ^2.5.7 + version: 2.5.7(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) '@react-navigation/drawer': specifier: ^7.5.0 version: 7.7.9(@react-navigation/native@7.1.19(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-gesture-handler@2.28.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-reanimated@4.1.5(@babel/core@7.28.5)(react-native-worklets@0.5.1(@babel/core@7.28.5)(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-safe-area-context@5.6.2(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native-screens@4.16.0(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0) @@ -1702,6 +1705,12 @@ packages: peerDependencies: react-native: ^0.0.0-0 || >=0.65 <1.0 + '@react-native-segmented-control/segmented-control@2.5.7': + resolution: {integrity: sha512-l84YeVX8xAU3lvOJSvV4nK/NbGhIm2gBfveYolwaoCbRp+/SLXtc6mYrQmM9ScXNwU14mnzjQTpTHWl5YPnkzQ==} + peerDependencies: + react: '>=16.0' + react-native: '>=0.62' + '@react-native/assets-registry@0.81.5': resolution: {integrity: sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==} engines: {node: '>= 20.19.4'} @@ -8278,6 +8287,11 @@ snapshots: merge-options: 3.0.4 react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + '@react-native-segmented-control/segmented-control@2.5.7(react-native@0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)': + dependencies: + react: 19.1.0 + react-native: 0.81.5(@babel/core@7.28.5)(@types/react@19.1.17)(react@19.1.0) + '@react-native/assets-registry@0.81.5': {} '@react-native/babel-plugin-codegen@0.81.5(@babel/core@7.28.5)': diff --git a/code/allapp/allapp-rn/src/app/(tabs)/love.tsx b/code/allapp/allapp-rn/src/app/(tabs)/love.tsx index b972112c..def56adc 100644 --- a/code/allapp/allapp-rn/src/app/(tabs)/love.tsx +++ b/code/allapp/allapp-rn/src/app/(tabs)/love.tsx @@ -1,40 +1,85 @@ -import { DateRangePicker } from '@yz1311/react-native-wheel-picker' -import React from 'react' -import { Button, ButtonText } from '@/components/ui/button' +import SegmentedControl from '@react-native-segmented-control/segmented-control' +import React, { useState } from 'react' +import { ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native' +import { ATab } from '@/components/common/ao' + +export default function DemoDatePickerUI() { + const [index, setIndex] = useState(0) -export default function Example() { - const [showDrawer, setShowDrawer] = React.useState(false) return ( - <> - + + {/* 快捷选项栏 */} + + + - { - }} - onPickerCancel={() => { - }} - onPickerConfirm={(date) => { - setShowDrawer(false) - }} + {/* SegmentedControl */} + setIndex(event.nativeEvent.selectedSegmentIndex)} + style={styles.segmentedControl} + sliderStyle={styles.sliderStyle} /> - + + {/* 日期选择器占位 */} + + 这里显示日期选择器 + + ) } + +const styles = StyleSheet.create({ + quickOption: { + paddingVertical: 6, + paddingHorizontal: 12, + backgroundColor: '#fff', + borderRadius: 6, + borderWidth: 1, + borderColor: '#ccc', + marginRight: 8, + height: 32, + }, + quickOptionText: { + fontSize: 14, + color: '#333', + }, + segmentedControl: { + borderRadius: 0, + height: 36, + backgroundColor: '#e5e7eb', + marginBottom: 12, + }, + sliderStyle: { + borderRadius: 0, + backgroundColor: '#2196f3', + }, + datePickerPlaceholder: { + height: 200, + backgroundColor: '#fff', + borderRadius: 6, + justifyContent: 'center', + alignItems: 'center', + }, +}) diff --git a/code/allapp/allapp-rn/src/app/(tabs)/money/components/CategorySheet.tsx b/code/allapp/allapp-rn/src/app/(tabs)/money/components/CategorySheet.tsx index 1c5c6234..83368594 100644 --- a/code/allapp/allapp-rn/src/app/(tabs)/money/components/CategorySheet.tsx +++ b/code/allapp/allapp-rn/src/app/(tabs)/money/components/CategorySheet.tsx @@ -1,5 +1,7 @@ import type { MoneyCategoryItem } from '@/types/money/category' -import { useCallback, useEffect, useMemo, useRef } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { AInput, LucideIcon } from '@/components/common/ao' +import EmptyState from '@/components/common/EmptyStatus' import { Actionsheet, ActionsheetBackdrop, @@ -34,6 +36,7 @@ export default function CategorySheet({ }: CategorySheetProps) { const listRef = useRef(null) const hasAutoScrolled = useRef(false) + const [searchText, setSearchText] = useState('') // 记住目标滚动位置 const pendingScrollRef = useRef<{ @@ -128,17 +131,45 @@ export default function CategorySheet({ } }, [open, value, sections]) + const filteredSections = useMemo(() => { + if (!searchText.trim()) { + return sections + } + + const lower = searchText.trim().toLowerCase() + + return sections.map(section => ({ + ...section, + data: section.data.filter(item => + item.name.toLowerCase().includes(lower), + ), + })).filter(section => section.data.length > 0) + }, [sections, searchText]) + + function handleClose() { + onClose?.() + setSearchText('') + } + return ( - + - + + } + /> + + {s.title} {' '} ( @@ -187,6 +224,10 @@ export default function CategorySheet({ ) }} /> + + {filteredSections.length === 0 && ( + + )} ) diff --git a/code/allapp/allapp-rn/src/app/money/analysis/index.tsx b/code/allapp/allapp-rn/src/app/money/analysis/index.tsx index 81434f0d..45932fdd 100644 --- a/code/allapp/allapp-rn/src/app/money/analysis/index.tsx +++ b/code/allapp/allapp-rn/src/app/money/analysis/index.tsx @@ -2,7 +2,6 @@ import type { AnalysisMoneyAmount } from '@/types/money/analysis' import dayjs from 'dayjs' import { useEffect, useState } from 'react' import { ScrollView, Text, TouchableOpacity, View } from 'react-native' -import Svg from 'react-native-svg' import { VictoryChart, VictoryLabel, VictoryLine, VictoryPie, VictoryTheme, VictoryTooltip } from 'victory-native' import { ADatePicker, ADateRangePicker, ASegmentedTabs, LucideIcon } from '@/components/common/ao' import EmptyState from '@/components/common/EmptyStatus' @@ -10,6 +9,7 @@ import CustomHeader from '@/components/custom/Header' import PagerTabs from '@/components/custom/PagerTabs' import { analysisMoneyLineApi, analysisMoneyPieApi } from '@/request/api/money/analysis' import { MoneyCategoryEnum } from '@/types/money/category' +import { add } from '@/utils/math' import { getCurrentYear, getCurrentYearMonth, getEndOfMonth, getStartOfMonth } from '@/utils/time' interface AnalysisContentProps { @@ -55,10 +55,6 @@ export default function Analysis() { periodType = 1 } else { - console.log(selectedStartDate.isSame(selectedEndDate, 'day')) - console.log(selectedStartDate.isSame(selectedEndDate, 'month')) - console.log(selectedStartDate.isSame(selectedEndDate, 'year')) - if (selectedStartDate.isSame(selectedEndDate, 'month')) { periodType = 0 start = selectedStartDate.startOf('month').format('YYYY-MM-DD') @@ -98,6 +94,10 @@ export default function Analysis() { } }) } + const totalAmount: number = pieRecords.reduce( + (sum, item) => Number(add(sum, Number(item.y) || 0)), + 0, + ) useEffect(() => { fetchChartsData() @@ -190,16 +190,24 @@ export default function Analysis() { - 分类详细 + + 分类详细 + + 总: + {totalAmount} + {' '} + 元 + + { pieRecords.length > 0 ? ( - + {pieRecords.map((item, index) => ( {item.x} diff --git a/code/allapp/allapp-rn/src/components/common/ao/DateMorePicker.tsx b/code/allapp/allapp-rn/src/components/common/ao/DateMorePicker.tsx new file mode 100644 index 00000000..8cb4ff42 --- /dev/null +++ b/code/allapp/allapp-rn/src/components/common/ao/DateMorePicker.tsx @@ -0,0 +1,3 @@ +export default function ADateMorePicker() { + +} diff --git a/code/allapp/allapp-rn/src/components/common/ao/DatePicker.tsx b/code/allapp/allapp-rn/src/components/common/ao/DatePicker.tsx index fa59a91d..a5e9f17f 100644 --- a/code/allapp/allapp-rn/src/components/common/ao/DatePicker.tsx +++ b/code/allapp/allapp-rn/src/components/common/ao/DatePicker.tsx @@ -1,9 +1,12 @@ import type { IDatePickerProps } from '@yz1311/react-native-wheel-picker' +import SegmentedControl from '@react-native-segmented-control/segmented-control' import { DatePicker } from '@yz1311/react-native-wheel-picker' import dayjs from 'dayjs' import React, { useEffect, useRef, useState } from 'react' -import { Animated, Modal, StyleSheet, TouchableOpacity } from 'react-native' +import { Animated, Modal, StyleSheet, Text, TouchableOpacity, View } from 'react-native' +import { AButton, ATab } from '@/components/common/ao' import { toDate } from '@/utils/time' + // 定义 mode 类型 export type DatePickerMode = 'year' | 'month' | 'date' | 'time' | 'datetime' @@ -32,13 +35,14 @@ export default function ADatePicker({ show = false, onClose, onConfirm, - mode = 'date', format, ...restProps }: ADatePickerProps) { const fadeAnim = useRef(new Animated.Value(0)).current const slideAnim = useRef(new Animated.Value(300)).current const [visible, setVisible] = useState(show) + const [index, setIndex] = useState(0) + const [mode, setMode] = useState('month') // 使用传入 format 或默认 format const finalFormat = format || defaultFormatMap[mode] @@ -80,6 +84,58 @@ export default function ADatePicker({ + + + 快速选择 + { + setMode('datetime') + }} + /> + + + + 选择模式 + setIndex(event.nativeEvent.selectedSegmentIndex)} + /> + + + + { + console.log(value) + }} /> + + + 取消 + 确认 + ) diff --git a/code/allapp/allapp-rn/src/components/common/ao/SegmentedTabs.tsx b/code/allapp/allapp-rn/src/components/common/ao/SegmentedTabs.tsx index 462207d4..cdc925d0 100644 --- a/code/allapp/allapp-rn/src/components/common/ao/SegmentedTabs.tsx +++ b/code/allapp/allapp-rn/src/components/common/ao/SegmentedTabs.tsx @@ -39,7 +39,7 @@ interface SegmentedTabsProps { width?: number } -export default function SegmentedTabs({ +export default function SegmentedTabs({ tabs, value, defaultIndex = 0, diff --git a/code/allapp/allapp-rn/src/components/common/ao/Tab.tsx b/code/allapp/allapp-rn/src/components/common/ao/Tab.tsx index 4016eefa..3b4b6474 100644 --- a/code/allapp/allapp-rn/src/components/common/ao/Tab.tsx +++ b/code/allapp/allapp-rn/src/components/common/ao/Tab.tsx @@ -1,5 +1,5 @@ import React, { useMemo, useRef, useState } from 'react' -import { Pressable, Text, View } from 'react-native' +import { Pressable, ScrollView, Text, View } from 'react-native' import PagerView from 'react-native-pager-view' interface TabItem { @@ -12,18 +12,12 @@ interface TabProps { data?: TabItem[] defaultKey?: TabItem['key'] onChange?: (key: TabItem['key']) => void - tabBarClass?: string - tabItemClass?: string - activeTabItemClass?: string } export default function Tab({ data, defaultKey, onChange, - tabBarClass = 'flex flex-row gap-2 overflow-hidden', - tabItemClass = 'items-center justify-center rounded-md px-4 py-2 bg-gray-200', - activeTabItemClass = 'items-center justify-center rounded-md px-4 py-2 bg-gray-800', }: TabProps) { if (!data || data.length === 0) return null @@ -72,22 +66,34 @@ export default function Tab({ return ( {/* tab bar */} - + {data.map((item) => { const isActive = item.key === selectedKey return ( handlePress(item.key)} - className={isActive ? activeTabItemClass : tabItemClass} + className={ + isActive + ? 'items-center justify-center rounded-md px-4 py-2 bg-gray-800' + : 'items-center justify-center rounded-md px-4 py-2 bg-gray-200' + } > + {item.label} ) })} - + {hasContent && (