u
This commit is contained in:
1 parent
bde3487da3
commit
7a9a22e02e
9 files changed
+242
-59
No files matched your search
@@ -18,6 +18,7 @@
|
|||||||
"@legendapp/list": "^2.0.19",
|
"@legendapp/list": "^2.0.19",
|
||||||
"@legendapp/motion": "2.4.0",
|
"@legendapp/motion": "2.4.0",
|
||||||
"@react-native-async-storage/async-storage": "2.2.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",
|
"@react-navigation/drawer": "^7.5.0",
|
||||||
"@yz1311/react-native-wheel-picker": "^0.4.5",
|
"@yz1311/react-native-wheel-picker": "^0.4.5",
|
||||||
"alova": "^3.3.4",
|
"alova": "^3.3.4",
|
||||||
|
|||||||
Generated
+14
@@ -32,6 +32,9 @@ importers:
|
|||||||
'@react-native-async-storage/async-storage':
|
'@react-native-async-storage/async-storage':
|
||||||
specifier: 2.2.0
|
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))
|
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':
|
'@react-navigation/drawer':
|
||||||
specifier: ^7.5.0
|
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)
|
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:
|
peerDependencies:
|
||||||
react-native: ^0.0.0-0 || >=0.65 <1.0
|
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':
|
'@react-native/assets-registry@0.81.5':
|
||||||
resolution: {integrity: sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==}
|
resolution: {integrity: sha512-705B6x/5Kxm1RKRvSv0ADYWm5JOnoiQ1ufW7h8uu2E6G9Of/eE6hP/Ivw3U5jI16ERqZxiKQwk34VJbB0niX9w==}
|
||||||
engines: {node: '>= 20.19.4'}
|
engines: {node: '>= 20.19.4'}
|
||||||
@@ -8278,6 +8287,11 @@ snapshots:
|
|||||||
merge-options: 3.0.4
|
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: 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/assets-registry@0.81.5': {}
|
||||||
|
|
||||||
'@react-native/babel-plugin-codegen@0.81.5(@babel/core@7.28.5)':
|
'@react-native/babel-plugin-codegen@0.81.5(@babel/core@7.28.5)':
|
||||||
|
|||||||
@@ -1,40 +1,85 @@
|
|||||||
import { DateRangePicker } from '@yz1311/react-native-wheel-picker'
|
import SegmentedControl from '@react-native-segmented-control/segmented-control'
|
||||||
import React from 'react'
|
import React, { useState } from 'react'
|
||||||
import { Button, ButtonText } from '@/components/ui/button'
|
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 (
|
return (
|
||||||
<>
|
<View style={{ flex: 1, backgroundColor: '#f3f4f6', padding: 16 }}>
|
||||||
<Button
|
{/* 快捷选项栏 */}
|
||||||
onPress={() => {
|
<ScrollView horizontal showsHorizontalScrollIndicator={false} style={{ marginBottom: 12 }}>
|
||||||
setShowDrawer(true)
|
<ATab
|
||||||
}}
|
data={[
|
||||||
className="mt-10"
|
{
|
||||||
>
|
label: '不限',
|
||||||
<ButtonText>Open Drawer</ButtonText>
|
key: '不限',
|
||||||
</Button>
|
},
|
||||||
|
{
|
||||||
|
label: '今天',
|
||||||
|
key: '今天',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '本月',
|
||||||
|
key: '本月',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '本年',
|
||||||
|
key: '本年',
|
||||||
|
},
|
||||||
|
|
||||||
<DateRangePicker
|
]}
|
||||||
pickerTitle="预约时间"
|
/>
|
||||||
pickerCancelBtnText="取消"
|
</ScrollView>
|
||||||
pickerConfirmBtnText="确认"
|
|
||||||
// 开启modal模式
|
|
||||||
isModal={true}
|
|
||||||
// 可以自定义modal的一些属性
|
|
||||||
modalProps={{
|
|
||||||
|
|
||||||
}}
|
{/* SegmentedControl */}
|
||||||
modalVisible={showDrawer}
|
<SegmentedControl
|
||||||
// modalVisible回调函数
|
values={['时间', '日', '月', '年', '范围']}
|
||||||
onModalVisibleChange={(visible) => {
|
selectedIndex={index}
|
||||||
}}
|
onChange={event => setIndex(event.nativeEvent.selectedSegmentIndex)}
|
||||||
onPickerCancel={() => {
|
style={styles.segmentedControl}
|
||||||
}}
|
sliderStyle={styles.sliderStyle}
|
||||||
onPickerConfirm={(date) => {
|
|
||||||
setShowDrawer(false)
|
|
||||||
}}
|
|
||||||
/>
|
/>
|
||||||
</>
|
|
||||||
|
{/* 日期选择器占位 */}
|
||||||
|
<View style={styles.datePickerPlaceholder}>
|
||||||
|
<Text style={{ color: '#888' }}>这里显示日期选择器</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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',
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
import type { MoneyCategoryItem } from '@/types/money/category'
|
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 {
|
import {
|
||||||
Actionsheet,
|
Actionsheet,
|
||||||
ActionsheetBackdrop,
|
ActionsheetBackdrop,
|
||||||
@@ -34,6 +36,7 @@ export default function CategorySheet({
|
|||||||
}: CategorySheetProps) {
|
}: CategorySheetProps) {
|
||||||
const listRef = useRef<any>(null)
|
const listRef = useRef<any>(null)
|
||||||
const hasAutoScrolled = useRef(false)
|
const hasAutoScrolled = useRef(false)
|
||||||
|
const [searchText, setSearchText] = useState('')
|
||||||
|
|
||||||
// 记住目标滚动位置
|
// 记住目标滚动位置
|
||||||
const pendingScrollRef = useRef<{
|
const pendingScrollRef = useRef<{
|
||||||
@@ -128,17 +131,45 @@ export default function CategorySheet({
|
|||||||
}
|
}
|
||||||
}, [open, value, sections])
|
}, [open, value, sections])
|
||||||
|
|
||||||
|
const filteredSections = useMemo<CategorySection[]>(() => {
|
||||||
|
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 (
|
return (
|
||||||
<Actionsheet isOpen={open} onClose={onClose}>
|
<Actionsheet isOpen={open} onClose={handleClose}>
|
||||||
<ActionsheetBackdrop />
|
<ActionsheetBackdrop />
|
||||||
<ActionsheetContent style={{ maxHeight: '60%' }}>
|
<ActionsheetContent style={{ maxHeight: '60%', minHeight: '60%' }}>
|
||||||
<ActionsheetDragIndicatorWrapper>
|
<ActionsheetDragIndicatorWrapper>
|
||||||
<ActionsheetDragIndicator />
|
<ActionsheetDragIndicator />
|
||||||
</ActionsheetDragIndicatorWrapper>
|
</ActionsheetDragIndicatorWrapper>
|
||||||
|
|
||||||
|
<AInput
|
||||||
|
placeholder="请输入搜索关键词"
|
||||||
|
className="mt-2"
|
||||||
|
value={searchText}
|
||||||
|
onChangeText={setSearchText}
|
||||||
|
leftIcon={() => <LucideIcon icon="Search" size={16} color="#666" />}
|
||||||
|
/>
|
||||||
|
|
||||||
<ActionsheetSectionList
|
<ActionsheetSectionList
|
||||||
ref={listRef}
|
ref={listRef}
|
||||||
sections={sections}
|
sections={filteredSections}
|
||||||
stickySectionHeadersEnabled={true}
|
stickySectionHeadersEnabled={true}
|
||||||
showsVerticalScrollIndicator={false}
|
showsVerticalScrollIndicator={false}
|
||||||
onEndReachedThreshold={0.1}
|
onEndReachedThreshold={0.1}
|
||||||
@@ -177,7 +208,13 @@ export default function CategorySheet({
|
|||||||
const s = section as CategorySection
|
const s = section as CategorySection
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ActionsheetSectionHeaderText className="font-semibold text-[black] text-sm bg-white">
|
<ActionsheetSectionHeaderText
|
||||||
|
className="font-semibold text-[black] text-sm bg-white"
|
||||||
|
style={{
|
||||||
|
paddingLeft: 0,
|
||||||
|
paddingRight: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{s.title}
|
{s.title}
|
||||||
{' '}
|
{' '}
|
||||||
(
|
(
|
||||||
@@ -187,6 +224,10 @@ export default function CategorySheet({
|
|||||||
)
|
)
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{filteredSections.length === 0 && (
|
||||||
|
<EmptyState title="空" />
|
||||||
|
)}
|
||||||
</ActionsheetContent>
|
</ActionsheetContent>
|
||||||
</Actionsheet>
|
</Actionsheet>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ import type { AnalysisMoneyAmount } from '@/types/money/analysis'
|
|||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useState } from 'react'
|
||||||
import { ScrollView, Text, TouchableOpacity, View } from 'react-native'
|
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 { VictoryChart, VictoryLabel, VictoryLine, VictoryPie, VictoryTheme, VictoryTooltip } from 'victory-native'
|
||||||
import { ADatePicker, ADateRangePicker, ASegmentedTabs, LucideIcon } from '@/components/common/ao'
|
import { ADatePicker, ADateRangePicker, ASegmentedTabs, LucideIcon } from '@/components/common/ao'
|
||||||
import EmptyState from '@/components/common/EmptyStatus'
|
import EmptyState from '@/components/common/EmptyStatus'
|
||||||
@@ -10,6 +9,7 @@ import CustomHeader from '@/components/custom/Header'
|
|||||||
import PagerTabs from '@/components/custom/PagerTabs'
|
import PagerTabs from '@/components/custom/PagerTabs'
|
||||||
import { analysisMoneyLineApi, analysisMoneyPieApi } from '@/request/api/money/analysis'
|
import { analysisMoneyLineApi, analysisMoneyPieApi } from '@/request/api/money/analysis'
|
||||||
import { MoneyCategoryEnum } from '@/types/money/category'
|
import { MoneyCategoryEnum } from '@/types/money/category'
|
||||||
|
import { add } from '@/utils/math'
|
||||||
import { getCurrentYear, getCurrentYearMonth, getEndOfMonth, getStartOfMonth } from '@/utils/time'
|
import { getCurrentYear, getCurrentYearMonth, getEndOfMonth, getStartOfMonth } from '@/utils/time'
|
||||||
|
|
||||||
interface AnalysisContentProps {
|
interface AnalysisContentProps {
|
||||||
@@ -55,10 +55,6 @@ export default function Analysis() {
|
|||||||
periodType = 1
|
periodType = 1
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
console.log(selectedStartDate.isSame(selectedEndDate, 'day'))
|
|
||||||
console.log(selectedStartDate.isSame(selectedEndDate, 'month'))
|
|
||||||
console.log(selectedStartDate.isSame(selectedEndDate, 'year'))
|
|
||||||
|
|
||||||
if (selectedStartDate.isSame(selectedEndDate, 'month')) {
|
if (selectedStartDate.isSame(selectedEndDate, 'month')) {
|
||||||
periodType = 0
|
periodType = 0
|
||||||
start = selectedStartDate.startOf('month').format('YYYY-MM-DD')
|
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(() => {
|
useEffect(() => {
|
||||||
fetchChartsData()
|
fetchChartsData()
|
||||||
@@ -190,16 +190,24 @@ export default function Analysis() {
|
|||||||
</View>
|
</View>
|
||||||
|
|
||||||
<View className="mt-4" style={{ paddingBottom: 24 }}>
|
<View className="mt-4" style={{ paddingBottom: 24 }}>
|
||||||
<Text className="text-lg font-semibold text-gray-700">分类详细</Text>
|
<View className="flex flex-row justify-between">
|
||||||
|
<Text className="text-lg font-semibold text-gray-700">分类详细</Text>
|
||||||
|
<Text>
|
||||||
|
总:
|
||||||
|
{totalAmount}
|
||||||
|
{' '}
|
||||||
|
元
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
{
|
{
|
||||||
pieRecords.length > 0
|
pieRecords.length > 0
|
||||||
? (
|
? (
|
||||||
<View className="mt-4 space-y-3">
|
<View className="mt-4 flex flex-col gap-2">
|
||||||
{pieRecords.map((item, index) => (
|
{pieRecords.map((item, index) => (
|
||||||
<View
|
<View
|
||||||
key={index}
|
key={index}
|
||||||
className={`flex flex-row justify-between items-center p-3 bg-white shadow-md rounded-lg ${index !== pieRecords.length - 1 ? 'border-b border-gray-300' : ''}`}
|
className="flex flex-row justify-between items-center py-3 border-b border-gray-300"
|
||||||
>
|
>
|
||||||
<Text className="text-base font-medium text-gray-800">{item.x}</Text>
|
<Text className="text-base font-medium text-gray-800">{item.x}</Text>
|
||||||
<Text className="text-base font-semibold text-gray-700">
|
<Text className="text-base font-semibold text-gray-700">
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export default function ADateMorePicker() {
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
import type { IDatePickerProps } from '@yz1311/react-native-wheel-picker'
|
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 { DatePicker } from '@yz1311/react-native-wheel-picker'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import React, { useEffect, useRef, useState } from 'react'
|
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'
|
import { toDate } from '@/utils/time'
|
||||||
|
|
||||||
// 定义 mode 类型
|
// 定义 mode 类型
|
||||||
export type DatePickerMode = 'year' | 'month' | 'date' | 'time' | 'datetime'
|
export type DatePickerMode = 'year' | 'month' | 'date' | 'time' | 'datetime'
|
||||||
|
|
||||||
@@ -32,13 +35,14 @@ export default function ADatePicker({
|
|||||||
show = false,
|
show = false,
|
||||||
onClose,
|
onClose,
|
||||||
onConfirm,
|
onConfirm,
|
||||||
mode = 'date',
|
|
||||||
format,
|
format,
|
||||||
...restProps
|
...restProps
|
||||||
}: ADatePickerProps) {
|
}: ADatePickerProps) {
|
||||||
const fadeAnim = useRef(new Animated.Value(0)).current
|
const fadeAnim = useRef(new Animated.Value(0)).current
|
||||||
const slideAnim = useRef(new Animated.Value(300)).current
|
const slideAnim = useRef(new Animated.Value(300)).current
|
||||||
const [visible, setVisible] = useState(show)
|
const [visible, setVisible] = useState(show)
|
||||||
|
const [index, setIndex] = useState(0)
|
||||||
|
const [mode, setMode] = useState<DatePickerMode>('month')
|
||||||
|
|
||||||
// 使用传入 format 或默认 format
|
// 使用传入 format 或默认 format
|
||||||
const finalFormat = format || defaultFormatMap[mode]
|
const finalFormat = format || defaultFormatMap[mode]
|
||||||
@@ -80,6 +84,58 @@ export default function ADatePicker({
|
|||||||
<Animated.View
|
<Animated.View
|
||||||
style={[styles.pickerContainer, { transform: [{ translateY: slideAnim }] }]}
|
style={[styles.pickerContainer, { transform: [{ translateY: slideAnim }] }]}
|
||||||
>
|
>
|
||||||
|
<View className="px-3 pt-2 flex flex-col gap-2">
|
||||||
|
<View className="w-full">
|
||||||
|
<Text>快速选择</Text>
|
||||||
|
<ATab
|
||||||
|
data={[
|
||||||
|
{
|
||||||
|
label: '自定义',
|
||||||
|
key: '自定义',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '不限',
|
||||||
|
key: '不限',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '今天',
|
||||||
|
key: '今天',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '本月',
|
||||||
|
key: '本月',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '本年',
|
||||||
|
key: '本年',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: '范围',
|
||||||
|
key: '范围',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
onChange={(key) => {
|
||||||
|
setMode('datetime')
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="w-full">
|
||||||
|
<Text>选择模式</Text>
|
||||||
|
<SegmentedControl
|
||||||
|
values={['日', '月', '年', '时间', '日期']}
|
||||||
|
selectedIndex={index}
|
||||||
|
style={{ borderRadius: 2, width: '100%' }}
|
||||||
|
sliderStyle={{
|
||||||
|
borderRadius: 2,
|
||||||
|
}}
|
||||||
|
|
||||||
|
onChange={(event: any) => setIndex(event.nativeEvent.selectedSegmentIndex)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
</View>
|
||||||
|
|
||||||
<DatePicker
|
<DatePicker
|
||||||
{...restProps}
|
{...restProps}
|
||||||
date={toDate(date)}
|
date={toDate(date)}
|
||||||
@@ -87,7 +143,16 @@ export default function ADatePicker({
|
|||||||
isModal={false}
|
isModal={false}
|
||||||
onPickerCancel={handleCancel}
|
onPickerCancel={handleCancel}
|
||||||
onPickerConfirm={handleConfirm}
|
onPickerConfirm={handleConfirm}
|
||||||
|
showHeader={false}
|
||||||
|
onDateChange={(value) => {
|
||||||
|
console.log(value)
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<View className="flex flex-row gap-4 px-3 pb-1 v">
|
||||||
|
<AButton variant="outline" className="flex-1">取消</AButton>
|
||||||
|
<AButton className="flex-1">确认</AButton>
|
||||||
|
</View>
|
||||||
</Animated.View>
|
</Animated.View>
|
||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ interface SegmentedTabsProps {
|
|||||||
width?: number
|
width?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function SegmentedTabs({
|
export default function SegmentedTabs({
|
||||||
tabs,
|
tabs,
|
||||||
value,
|
value,
|
||||||
defaultIndex = 0,
|
defaultIndex = 0,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import React, { useMemo, useRef, useState } from 'react'
|
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'
|
import PagerView from 'react-native-pager-view'
|
||||||
|
|
||||||
interface TabItem {
|
interface TabItem {
|
||||||
@@ -12,18 +12,12 @@ interface TabProps {
|
|||||||
data?: TabItem[]
|
data?: TabItem[]
|
||||||
defaultKey?: TabItem['key']
|
defaultKey?: TabItem['key']
|
||||||
onChange?: (key: TabItem['key']) => void
|
onChange?: (key: TabItem['key']) => void
|
||||||
tabBarClass?: string
|
|
||||||
tabItemClass?: string
|
|
||||||
activeTabItemClass?: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function Tab({
|
export default function Tab({
|
||||||
data,
|
data,
|
||||||
defaultKey,
|
defaultKey,
|
||||||
onChange,
|
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) {
|
}: TabProps) {
|
||||||
if (!data || data.length === 0)
|
if (!data || data.length === 0)
|
||||||
return null
|
return null
|
||||||
@@ -72,22 +66,34 @@ export default function Tab({
|
|||||||
return (
|
return (
|
||||||
<View className="flex-1">
|
<View className="flex-1">
|
||||||
{/* tab bar */}
|
{/* tab bar */}
|
||||||
<View className={tabBarClass}>
|
<ScrollView
|
||||||
|
horizontal
|
||||||
|
showsHorizontalScrollIndicator={false}
|
||||||
|
contentContainerStyle={{
|
||||||
|
flexDirection: 'row',
|
||||||
|
gap: 8,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{data.map((item) => {
|
{data.map((item) => {
|
||||||
const isActive = item.key === selectedKey
|
const isActive = item.key === selectedKey
|
||||||
return (
|
return (
|
||||||
<Pressable
|
<Pressable
|
||||||
key={item.key}
|
key={item.key}
|
||||||
onPress={() => handlePress(item.key)}
|
onPress={() => 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'
|
||||||
|
}
|
||||||
>
|
>
|
||||||
|
|
||||||
<Text className={isActive ? 'text-white' : 'text-gray-700'}>
|
<Text className={isActive ? 'text-white' : 'text-gray-700'}>
|
||||||
{item.label}
|
{item.label}
|
||||||
</Text>
|
</Text>
|
||||||
</Pressable>
|
</Pressable>
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</View>
|
</ScrollView>
|
||||||
|
|
||||||
{hasContent && (
|
{hasContent && (
|
||||||
<PagerView
|
<PagerView
|
||||||
|
|||||||
Reference in new issue
Block a user