u
This commit is contained in:
1 parent
bce259f165
commit
dc46d8034d
8 files changed
+430
-259
No files matched your search
@@ -1,14 +1,16 @@
|
||||
import type { DateMorePickerMode } from '@/components/common/ao/DateMorePicker'
|
||||
import { useFocusEffect, useRouter } from 'expo-router'
|
||||
import React, { useCallback, useState } from 'react'
|
||||
import { Image, Text, TouchableOpacity, View } from 'react-native'
|
||||
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
|
||||
import { ADatePicker, ADrawer, AListItem, LucideIcon } from '@/components/common/ao'
|
||||
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 { deleteMoneyRecordApi, listMoneyRecordsApi } from '@/request/api/money/record'
|
||||
import { useMoneyCategoryStore } from '@/store/moneyCategory'
|
||||
import { useMoneyRecordStore } from '@/store/moneyRecord'
|
||||
import { getCurrentYearMonth } from '@/utils/time'
|
||||
import { getCurrentYearMonth, normalizeDateTime } from '@/utils/time'
|
||||
import { toast } from '@/utils/toast'
|
||||
import CategorySheet from './components/CategorySheet'
|
||||
import TransactionsList from './components/TransactionsList'
|
||||
@@ -22,23 +24,30 @@ export default function Money() {
|
||||
|
||||
// 日期筛选
|
||||
const [showDatePicker, setShowDatePicker] = useState(false)
|
||||
const [dateMode, setDateMode] = useState<DateMorePickerMode>('month')
|
||||
|
||||
const [searchParams, setSearchParams] = useState({
|
||||
record_time: getCurrentYearMonth(),
|
||||
startTime: getCurrentYearMonth(),
|
||||
endTime: '',
|
||||
category: -1,
|
||||
})
|
||||
|
||||
const [showCategorySheet, setShowCategorySheet] = useState(false)
|
||||
|
||||
// 操作菜单
|
||||
const [showActionsMenu, setShowActionsMenu] = useState(false)
|
||||
|
||||
function fetchMoneyRecords() {
|
||||
console.log(searchParams)
|
||||
|
||||
const resolveDate = resolveDateRange(dateMode, searchParams.startTime)
|
||||
|
||||
listMoneyRecordsApi({
|
||||
// record_time: searchParams.record_time,
|
||||
start_time: '',
|
||||
end_time: '',
|
||||
start_time: resolveDate.startTime,
|
||||
end_time: resolveDate.endTime,
|
||||
}).then((res) => {
|
||||
if (res.isSuccess) {
|
||||
moneyRecordStore.setRecords(res.data.records || [], searchParams.record_time)
|
||||
moneyRecordStore.setRecords(res.data.records || [])
|
||||
}
|
||||
else {
|
||||
toast.show({
|
||||
@@ -52,7 +61,7 @@ export default function Money() {
|
||||
useFocusEffect(
|
||||
useCallback(() => {
|
||||
fetchMoneyRecords()
|
||||
}, [searchParams.record_time]),
|
||||
}, [searchParams.startTime, searchParams.endTime]),
|
||||
)
|
||||
|
||||
async function handleDeleteRecord(id: number) {
|
||||
@@ -63,6 +72,18 @@ export default function Money() {
|
||||
fetchMoneyRecords()
|
||||
}
|
||||
|
||||
function timeModeTip() {
|
||||
if (dateMode === 'unlimited') {
|
||||
return '全部'
|
||||
}
|
||||
else if (dateMode === 'range') {
|
||||
return `${searchParams.startTime} - ${searchParams.endTime}`
|
||||
}
|
||||
else {
|
||||
return searchParams.startTime
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<View
|
||||
className="flex-1"
|
||||
@@ -80,7 +101,13 @@ export default function Money() {
|
||||
onPress={() => setShowDatePicker(true)}
|
||||
className="flex flex-row items-center gap-1"
|
||||
>
|
||||
<Text className="font-semibold text-lg">{searchParams.record_time}</Text>
|
||||
<Text
|
||||
className="font-semibold text-lg"
|
||||
numberOfLines={1}
|
||||
ellipsizeMode="tail"
|
||||
>
|
||||
{timeModeTip()}
|
||||
</Text>
|
||||
<LucideIcon icon="ChevronDown" size={16} color="#666" clickable={false} />
|
||||
</TouchableOpacity>
|
||||
|
||||
@@ -182,7 +209,26 @@ export default function Money() {
|
||||
<FabIcon as={AddIcon} size="xl" />
|
||||
</Fab>
|
||||
|
||||
<ADatePicker
|
||||
<ADateMorePicker
|
||||
visible={showDatePicker}
|
||||
mode={dateMode}
|
||||
onClose={() => {
|
||||
setShowDatePicker(false)
|
||||
}}
|
||||
startDate={searchParams.startTime}
|
||||
endDate={searchParams.endTime}
|
||||
hiddenModes={['datetime', 'time']}
|
||||
onConfirm={(value) => {
|
||||
setDateMode(value.mode)
|
||||
|
||||
setSearchParams(val => ({
|
||||
...val,
|
||||
startTime: value.startTime,
|
||||
endTime: value.endTime,
|
||||
}))
|
||||
}}
|
||||
/>
|
||||
{/* <ADatePicker
|
||||
show={showDatePicker}
|
||||
mode="month"
|
||||
date={searchParams.record_time}
|
||||
@@ -196,7 +242,7 @@ export default function Money() {
|
||||
onClose={() => {
|
||||
setShowDatePicker(false)
|
||||
}}
|
||||
/>
|
||||
/> */}
|
||||
|
||||
<CategorySheet
|
||||
open={showCategorySheet}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Dayjs } from 'dayjs'
|
||||
import SegmentedControl from '@react-native-segmented-control/segmented-control'
|
||||
import { DatePicker, DateRangePicker } from '@yz1311/react-native-wheel-picker'
|
||||
import dayjs from 'dayjs'
|
||||
@@ -11,7 +12,7 @@ import Tab from './Tab'
|
||||
|
||||
/** ================= 类型 ================= */
|
||||
|
||||
export type PickerMode
|
||||
export type DateMorePickerMode
|
||||
= | 'unlimited'
|
||||
| 'date'
|
||||
| 'month'
|
||||
@@ -20,22 +21,83 @@ export type PickerMode
|
||||
| 'datetime'
|
||||
| 'range'
|
||||
|
||||
type PickerModeInternal = PickerMode | 'custom'
|
||||
type TabKey = 'custom' | PickerMode
|
||||
type PickerModeInternal = DateMorePickerMode | 'custom'
|
||||
type TabKey = 'custom' | DateMorePickerMode
|
||||
type DatePickerMode = 'date' | 'month' | 'year' | 'time' | 'datetime'
|
||||
|
||||
interface DateMorePickerProps {
|
||||
mode?: PickerMode
|
||||
mode?: DateMorePickerMode
|
||||
startDate?: Date | string
|
||||
endDate?: Date | string
|
||||
visible?: boolean
|
||||
onClose?: () => void
|
||||
onConfirm?: (value: {
|
||||
mode: PickerMode
|
||||
mode: DateMorePickerMode
|
||||
startTime: string
|
||||
endTime: string
|
||||
}) => void
|
||||
onChange?: (mode: PickerMode) => void
|
||||
hiddenModes?: DateMorePickerMode[]
|
||||
onChange?: (mode: DateMorePickerMode) => void
|
||||
}
|
||||
|
||||
interface DateRangeResult {
|
||||
startTime: string
|
||||
endTime: string
|
||||
}
|
||||
|
||||
export function resolveDateRange(
|
||||
dateMode: DateMorePickerMode,
|
||||
baseDate?: string | Date | Dayjs,
|
||||
): DateRangeResult {
|
||||
const now = dayjs()
|
||||
const base = baseDate ? dayjs(baseDate) : now
|
||||
|
||||
// 兜底非法时间
|
||||
if (!base.isValid()) {
|
||||
return { startTime: '', endTime: '' }
|
||||
}
|
||||
|
||||
let start: Dayjs
|
||||
let end: Dayjs
|
||||
|
||||
switch (dateMode) {
|
||||
case 'unlimited':
|
||||
start = dayjs('1970-01-01 00:00:00')
|
||||
end = now.add(10, 'year').endOf('day')
|
||||
break
|
||||
|
||||
case 'date':
|
||||
start = base.startOf('day')
|
||||
end = base.endOf('day')
|
||||
break
|
||||
|
||||
case 'month':
|
||||
start = base.startOf('month')
|
||||
end = base.endOf('month')
|
||||
break
|
||||
|
||||
case 'year':
|
||||
start = base.startOf('year')
|
||||
end = base.endOf('year')
|
||||
break
|
||||
|
||||
case 'time':
|
||||
case 'datetime':
|
||||
start = base
|
||||
end = base
|
||||
break
|
||||
|
||||
case 'range':
|
||||
default:
|
||||
start = base.startOf('day')
|
||||
end = base.endOf('day')
|
||||
break
|
||||
}
|
||||
|
||||
return {
|
||||
startTime: start.format('YYYY-MM-DD HH:mm:ss'),
|
||||
endTime: end.format('YYYY-MM-DD HH:mm:ss'),
|
||||
}
|
||||
}
|
||||
|
||||
export default function DateMorePicker({
|
||||
@@ -46,100 +108,85 @@ export default function DateMorePicker({
|
||||
onClose,
|
||||
onConfirm,
|
||||
onChange,
|
||||
hiddenModes = [],
|
||||
}: DateMorePickerProps) {
|
||||
const [localVisible, setLocalVisible] = useState(false)
|
||||
|
||||
const coreModes: DatePickerMode[] = ['date', 'month', 'year', 'time', 'datetime']
|
||||
const coreLabels = ['日', '月', '年', '时间', '日期']
|
||||
|
||||
/** ================= 内部编辑态 ================= */
|
||||
|
||||
const [internalMode, setInternalMode] = useState<PickerMode>('date')
|
||||
const [internalStart, setInternalStart] = useState<Date | string>(getCurrentDate())
|
||||
const [internalEnd, setInternalEnd] = useState<Date | string>(getCurrentDate())
|
||||
const [internalMode, setInternalMode] = useState<DateMorePickerMode>(mode || 'date')
|
||||
const [internalStart, setInternalStart] = useState<Date | string>(startDate || getCurrentDate())
|
||||
const [internalEnd, setInternalEnd] = useState<Date | string>(endDate || getCurrentDate())
|
||||
const [activeTab, setActiveTab] = useState<TabKey>('custom')
|
||||
|
||||
/** ================= 重置编辑态(关键) ================= */
|
||||
|
||||
const resetInternalState = () => {
|
||||
setInternalMode(mode || 'date')
|
||||
setInternalStart(startDate ?? getCurrentDate())
|
||||
setInternalEnd(endDate ?? getCurrentDate())
|
||||
setActiveTab('custom')
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
resetInternalState()
|
||||
if (mode === 'unlimited') {
|
||||
setActiveTab('unlimited')
|
||||
}
|
||||
else if (mode === 'range') {
|
||||
setActiveTab('range')
|
||||
}
|
||||
else {
|
||||
setActiveTab('custom')
|
||||
}
|
||||
|
||||
setLocalVisible(true)
|
||||
}
|
||||
}, [visible])
|
||||
|
||||
/** ================= 快速选择逻辑(修复时间归零) ================= */
|
||||
function getDefaultDateByMode(mode: DatePickerMode, now = dayjs()) {
|
||||
switch (mode) {
|
||||
case 'date':
|
||||
return now.startOf('day')
|
||||
case 'month':
|
||||
return now.startOf('month')
|
||||
case 'year':
|
||||
return now.startOf('year')
|
||||
case 'time':
|
||||
return now.second(0)
|
||||
case 'datetime':
|
||||
return now
|
||||
default:
|
||||
return now
|
||||
}
|
||||
}
|
||||
|
||||
const handleModeChange = (next: PickerModeInternal) => {
|
||||
const now = dayjs()
|
||||
|
||||
switch (next) {
|
||||
case 'custom':
|
||||
setInternalMode('date')
|
||||
break
|
||||
|
||||
case 'unlimited':
|
||||
setInternalStart('')
|
||||
setInternalEnd('')
|
||||
setInternalMode('unlimited')
|
||||
break
|
||||
|
||||
case 'range': {
|
||||
const base = now.startOf('day')
|
||||
setInternalStart(base.toDate())
|
||||
setInternalEnd(base.toDate())
|
||||
setInternalMode('range')
|
||||
break
|
||||
}
|
||||
|
||||
case 'date': {
|
||||
const base = now.startOf('day')
|
||||
setInternalStart(base.toDate())
|
||||
setInternalEnd(base.toDate())
|
||||
setInternalMode('date')
|
||||
break
|
||||
}
|
||||
|
||||
case 'month': {
|
||||
const base = now.startOf('month')
|
||||
setInternalStart(base.toDate())
|
||||
setInternalEnd(base.toDate())
|
||||
setInternalMode('month')
|
||||
break
|
||||
}
|
||||
|
||||
case 'year': {
|
||||
const base = now.startOf('year')
|
||||
setInternalStart(base.toDate())
|
||||
setInternalEnd(base.toDate())
|
||||
setInternalMode('year')
|
||||
break
|
||||
}
|
||||
|
||||
case 'time': {
|
||||
const base = now.second(0)
|
||||
setInternalStart(base.toDate())
|
||||
setInternalEnd(base.toDate())
|
||||
setInternalMode('time')
|
||||
break
|
||||
}
|
||||
|
||||
case 'datetime': {
|
||||
setInternalStart(now.toDate())
|
||||
setInternalEnd(now.toDate())
|
||||
setInternalMode('datetime')
|
||||
break
|
||||
}
|
||||
if (next === 'unlimited') {
|
||||
setInternalStart('')
|
||||
setInternalEnd('')
|
||||
setInternalMode('unlimited')
|
||||
onChange?.('unlimited')
|
||||
return
|
||||
}
|
||||
|
||||
onChange?.(next === 'custom' ? 'date' : next)
|
||||
if (next === 'range') {
|
||||
const base = now.startOf('day')
|
||||
setInternalStart(base.toDate())
|
||||
setInternalEnd(base.toDate())
|
||||
setInternalMode('range')
|
||||
onChange?.('range')
|
||||
return
|
||||
}
|
||||
|
||||
const targetMode: DatePickerMode
|
||||
= next === 'custom'
|
||||
? visibleCoreModeOptions[0].mode
|
||||
: next
|
||||
|
||||
const base = getDefaultDateByMode(targetMode, now)
|
||||
|
||||
setInternalStart(base.toDate())
|
||||
setInternalEnd(base.toDate())
|
||||
setInternalMode(targetMode)
|
||||
|
||||
onChange?.(targetMode)
|
||||
}
|
||||
|
||||
const handleClose = () => {
|
||||
@@ -149,7 +196,7 @@ export default function DateMorePicker({
|
||||
|
||||
/** ================= 确认(提交) ================= */
|
||||
|
||||
const formatMap: Record<PickerMode, string> = {
|
||||
const formatMap: Record<DateMorePickerMode, string> = {
|
||||
unlimited: '',
|
||||
range: 'YYYY-MM-DD',
|
||||
date: 'YYYY-MM-DD',
|
||||
@@ -189,6 +236,73 @@ export default function DateMorePicker({
|
||||
handleClose()
|
||||
}
|
||||
|
||||
const isHidden = (mode: DateMorePickerMode) =>
|
||||
hiddenModes.includes(mode)
|
||||
|
||||
const tabItems: { label: string, key: TabKey }[] = [
|
||||
{ label: '自定义', key: 'custom' },
|
||||
{ label: '不限', key: 'unlimited' },
|
||||
{ label: '范围', key: 'range' },
|
||||
{ label: '今天', key: 'date' },
|
||||
{ label: '本月', key: 'month' },
|
||||
{ label: '本年', key: 'year' },
|
||||
]
|
||||
|
||||
const visibleTabItems = tabItems.filter((item) => {
|
||||
if (item.key === 'unlimited' && isHidden('unlimited'))
|
||||
return false
|
||||
if (item.key === 'range' && isHidden('range'))
|
||||
return false
|
||||
if (item.key === 'date' && isHidden('date'))
|
||||
return false
|
||||
if (item.key === 'month' && isHidden('month'))
|
||||
return false
|
||||
if (item.key === 'year' && isHidden('year'))
|
||||
return false
|
||||
return true
|
||||
})
|
||||
|
||||
const coreModeOptions: { mode: DatePickerMode, label: string }[] = [
|
||||
{ mode: 'date', label: '日' },
|
||||
{ mode: 'month', label: '月' },
|
||||
{ mode: 'year', label: '年' },
|
||||
{ mode: 'time', label: '时间' },
|
||||
{ mode: 'datetime', label: '日期' },
|
||||
]
|
||||
|
||||
const visibleCoreModeOptions = coreModeOptions.filter((item) => {
|
||||
if (item.mode === 'date' && isHidden('date'))
|
||||
return false
|
||||
if (item.mode === 'month' && isHidden('month'))
|
||||
return false
|
||||
if (item.mode === 'year' && isHidden('year'))
|
||||
return false
|
||||
if (item.mode === 'time' && isHidden('time'))
|
||||
return false
|
||||
if (item.mode === 'datetime' && isHidden('datetime'))
|
||||
return false
|
||||
return true
|
||||
})
|
||||
|
||||
function isDatePickerMode(
|
||||
mode: DateMorePickerMode,
|
||||
): mode is DatePickerMode {
|
||||
return ['date', 'month', 'year', 'time', 'datetime'].includes(mode)
|
||||
}
|
||||
|
||||
const canConfirm = (() => {
|
||||
if (internalMode !== 'range')
|
||||
return true
|
||||
console.log(internalMode)
|
||||
console.log(internalStart)
|
||||
console.log(internalEnd)
|
||||
|
||||
const startValid = dayjs(internalStart).isValid()
|
||||
const endValid = dayjs(internalEnd).isValid()
|
||||
|
||||
return startValid && endValid
|
||||
})()
|
||||
|
||||
/** ================= UI ================= */
|
||||
|
||||
return (
|
||||
@@ -200,14 +314,7 @@ export default function DateMorePicker({
|
||||
<Text className="text-lg mb-1">快速选择</Text>
|
||||
<Tab
|
||||
defaultKey={activeTab}
|
||||
data={[
|
||||
{ label: '自定义', key: 'custom' },
|
||||
{ label: '不限', key: 'unlimited' },
|
||||
{ label: '今天', key: 'date' },
|
||||
{ label: '本月', key: 'month' },
|
||||
{ label: '本年', key: 'year' },
|
||||
{ label: '范围', key: 'range' },
|
||||
]}
|
||||
data={visibleTabItems}
|
||||
onChange={(key) => {
|
||||
setActiveTab(key as TabKey)
|
||||
handleModeChange(key as PickerModeInternal)
|
||||
@@ -219,18 +326,22 @@ export default function DateMorePicker({
|
||||
{!['unlimited', 'range'].includes(internalMode) && (
|
||||
<View className="mb-2">
|
||||
<Text className="text-lg mb-1">选择模式</Text>
|
||||
<SegmentedControl
|
||||
values={coreLabels}
|
||||
selectedIndex={coreModes.indexOf(internalMode as DatePickerMode)}
|
||||
style={{ width: '100%', borderRadius: 2 }}
|
||||
sliderStyle={{ borderRadius: 2 }}
|
||||
onChange={(e: any) => {
|
||||
const next = coreModes[e.nativeEvent.selectedSegmentIndex]
|
||||
setInternalMode(next)
|
||||
setInternalStart(dayjs().toDate())
|
||||
setActiveTab('custom')
|
||||
}}
|
||||
/>
|
||||
{visibleCoreModeOptions.length > 0 && (
|
||||
<SegmentedControl
|
||||
style={{ width: '100%', borderRadius: 2 }}
|
||||
sliderStyle={{ borderRadius: 2 }}
|
||||
values={visibleCoreModeOptions.map(i => i.label)}
|
||||
selectedIndex={visibleCoreModeOptions.findIndex(
|
||||
i => i.mode === internalMode,
|
||||
)}
|
||||
onChange={(e: any) => {
|
||||
const next = visibleCoreModeOptions[e.nativeEvent.selectedSegmentIndex].mode
|
||||
setInternalMode(next)
|
||||
setInternalStart(dayjs().toDate())
|
||||
setActiveTab('custom')
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
|
||||
@@ -238,14 +349,14 @@ export default function DateMorePicker({
|
||||
<ScrollView
|
||||
style={{ flex: 1 }}
|
||||
contentContainerStyle={{
|
||||
flexGrow: 1,
|
||||
width: '100%',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
paddingVertical: 10,
|
||||
}}
|
||||
>
|
||||
{internalMode === 'unlimited' && (
|
||||
<View className="items-center">
|
||||
<View className="flex-1 w-full h-full items-center justify-center">
|
||||
<LucideIcon icon="Clock" size={38} color="#555" />
|
||||
<Text className="mt-2 text-lg">不限时间</Text>
|
||||
</View>
|
||||
@@ -253,6 +364,8 @@ export default function DateMorePicker({
|
||||
|
||||
{internalMode === 'range' && (
|
||||
<DateRangePicker
|
||||
startDate={internalStart}
|
||||
endDate={internalEnd}
|
||||
isModal={false}
|
||||
showHeader={false}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
@@ -265,12 +378,13 @@ export default function DateMorePicker({
|
||||
/>
|
||||
)}
|
||||
|
||||
{coreModes.includes(internalMode as DatePickerMode) && (
|
||||
{isDatePickerMode(internalMode)
|
||||
&& visibleCoreModeOptions.some(i => i.mode === internalMode) && (
|
||||
<DatePicker
|
||||
key={internalMode}
|
||||
isModal={false}
|
||||
showHeader={false}
|
||||
mode={internalMode as DatePickerMode}
|
||||
mode={internalMode}
|
||||
date={toDate(internalStart)}
|
||||
style={{ height: '100%' }}
|
||||
onDateChange={(value) => {
|
||||
@@ -286,7 +400,11 @@ export default function DateMorePicker({
|
||||
<Button className="flex-1" variant="outline" onPress={handleClose}>
|
||||
取消
|
||||
</Button>
|
||||
<Button className="flex-1" onPress={handleConfirm}>
|
||||
<Button
|
||||
className="flex-1"
|
||||
onPress={handleConfirm}
|
||||
disabled={!canConfirm}
|
||||
>
|
||||
确认
|
||||
</Button>
|
||||
</View>
|
||||
|
||||
@@ -14,7 +14,7 @@ interface MoneyRecordStore {
|
||||
getTotalExpense: () => string
|
||||
getTotalBalance: () => string
|
||||
|
||||
setRecords: (records: MoneyRecord[], month: string) => void
|
||||
setRecords: (records: MoneyRecord[]) => void
|
||||
setCurrentMonth: (month: string) => void
|
||||
|
||||
getMoneyRecordById: (id: number) => MoneyRecord | null
|
||||
@@ -71,12 +71,9 @@ export const useMoneyRecordStore = create<MoneyRecordStore>((set, get) => ({
|
||||
},
|
||||
|
||||
/* ---------------- base ---------------- */
|
||||
setRecords: (records, month) => {
|
||||
const filtered = records.filter(r =>
|
||||
isInMonth(r.record_time, month),
|
||||
)
|
||||
const newGroups = recomputeGroups(filtered)
|
||||
set({ records: newGroups, currentMonth: month })
|
||||
setRecords: (records) => {
|
||||
const newGroups = recomputeGroups(records)
|
||||
set({ records: newGroups })
|
||||
},
|
||||
|
||||
setCurrentMonth: month => set({ currentMonth: month }),
|
||||
|
||||
@@ -119,3 +119,34 @@ export function getStartOfMonth() {
|
||||
export function getEndOfMonth() {
|
||||
return dayjs().endOf('month').format('YYYY-MM-DD')
|
||||
}
|
||||
|
||||
export function normalizeDateTime(
|
||||
input?: string | Date | dayjs.Dayjs,
|
||||
): string {
|
||||
if (!input)
|
||||
return ''
|
||||
|
||||
const raw = typeof input === 'string' ? input.trim() : input
|
||||
let d = dayjs(raw)
|
||||
|
||||
if (!d.isValid())
|
||||
return ''
|
||||
|
||||
// string 才需要“猜精度”,Date / dayjs 默认是完整的
|
||||
if (typeof raw === 'string') {
|
||||
if (/^\d{4}$/.test(raw)) {
|
||||
d = d.startOf('year')
|
||||
}
|
||||
else if (/^\d{4}-\d{2}$/.test(raw)) {
|
||||
d = d.startOf('month')
|
||||
}
|
||||
else if (/^\d{4}-\d{2}-\d{2}$/.test(raw)) {
|
||||
d = d.startOf('day')
|
||||
}
|
||||
else if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}$/.test(raw)) {
|
||||
d = d.second(0)
|
||||
}
|
||||
}
|
||||
|
||||
return d.format('YYYY-MM-DD HH:mm:ss')
|
||||
}
|
||||
Reference in new issue
Block a user