diff --git a/src/components/CommonPopup/CommonPopup.tsx b/src/components/CommonPopup/CommonPopup.tsx index 36cacd6..654a464 100644 --- a/src/components/CommonPopup/CommonPopup.tsx +++ b/src/components/CommonPopup/CommonPopup.tsx @@ -68,7 +68,7 @@ const CommonPopup: React.FC = ({ // 只允许向下拖动,限制最大拖动距离 if (deltaY > 0) { - setDragOffset(Math.min(deltaY, 200)) + setDragOffset(Math.min(deltaY, 100)) } } @@ -78,7 +78,7 @@ const CommonPopup: React.FC = ({ setIsDragging(false) // 如果拖动距离超过阈值,关闭弹窗 - if (dragOffset > 100) { + if (dragOffset > 50) { onClose() } @@ -100,7 +100,10 @@ const CommonPopup: React.FC = ({ }} > {enableDragToClose && ( - + = ({ opacity: isDragging ? 0.8 : 1, transition: isDragging ? 'none' : 'all 0.3s ease-out' }} - onTouchStart={handleTouchStart} - onTouchMove={handleTouchMove} - onTouchEnd={handleTouchEnd} + /> )} diff --git a/src/components/CommonPopup/index.module.scss b/src/components/CommonPopup/index.module.scss index 7caeab6..36570dd 100644 --- a/src/components/CommonPopup/index.module.scss +++ b/src/components/CommonPopup/index.module.scss @@ -12,16 +12,20 @@ position: absolute; top: 6px; left: 50%; - width: 32px; - height: 4px; - background-color: rgba(22, 24, 35, 0.2); - border-radius: 2px; + width: 90px; + height: 30px; z-index: 10; cursor: pointer; transition: background-color 0.2s ease; - - &:active { - background-color: #9ca3af; + display: flex; + justify-content: center; + align-items: flex-start; + &::before{ + content: ''; + width: 32px; + height: 4px; + background-color: rgba(22, 24, 35, 0.2); + border-radius: 2px; } } diff --git a/src/components/Picker/DayDialog/index.tsx b/src/components/Picker/DayDialog/index.tsx new file mode 100644 index 0000000..a986fd5 --- /dev/null +++ b/src/components/Picker/DayDialog/index.tsx @@ -0,0 +1,132 @@ +import React, { useState, useEffect, useRef } from "react"; +import CommonPopup from "@/components/CommonPopup"; +import { View } from "@tarojs/components"; +import CalendarUI, { + CalendarUIRef, +} from "@/components/Picker/CalendarUI/CalendarUI"; +import dayjs from "dayjs"; +import styles from "../CalendarDialog/index.module.scss"; +export interface DayDialogProps { + value?: Date | Date[]; + searchType?: "single" | "range" | "multiple"; + onChange?: (date: string | string[]) => void; + visible: boolean; + onClose: () => void; + title?: React.ReactNode; +} + +const DayDialog: React.FC = ({ + visible, + searchType, + onClose, + title, + value, + onChange, +}) => { + const [selected, setSelected] = useState(value || new Date()); + const [selectedBackup, setSelectedBackup] = useState( + Array.isArray(value) ? [...(value as Date[])] : [value as Date] + ); + const calendarRef = useRef(null); + const [type, setType] = useState<"year" | "month" | "time">("year"); + const [pendingJump, setPendingJump] = useState<{ + year: number; + month: number; + } | null>(null); + const handleConfirm = () => { + console.log(selected, 'selectedselected'); + const finalDate = dayjs(selected as Date).format("YYYY-MM-DD"); + if (onChange){ + onChange(finalDate) + } + onClose(); + }; + const dialogClose = () => { + setType("year"); + onClose(); + } + + const handleChange = (d: Date | Date[]) => { + if (searchType === "range") { + if (Array.isArray(d)) { + if (d.length === 2) { + return; + } else if (d.length === 1) { + if (selectedBackup.length === 0 || selectedBackup.length === 2) { + setSelected([...d]); + setSelectedBackup([...d]); + } else { + setSelected( + [...selectedBackup, d[0]].sort( + (a, b) => a.getTime() - b.getTime() + ) + ); + setSelectedBackup([]); + } + } + return; + } + } + if (Array.isArray(d)) { + setSelected(d[0]); + } else { + setSelected(d); + } + }; + const onHeaderClick = (date: Date) => { + console.log("onHeaderClick", date); + setSelected(date); + setType("month"); + }; + const getConfirmText = () => { + return "完成" + }; + const onCancel = () => { + onClose(); + }; + + useEffect(() => { + if (visible && value) { + setSelected(value || new Date()); + } + }, [value, visible]); + + useEffect(() => { + if (type === "year" && pendingJump && calendarRef.current) { + calendarRef.current.jumpTo(pendingJump.year, pendingJump.month); + setPendingJump(null); + } + }, [type, pendingJump]); + + + return ( + + + + + + ); +}; + +export default DayDialog; diff --git a/src/components/Picker/HourDialog/index.tsx b/src/components/Picker/HourDialog/index.tsx new file mode 100644 index 0000000..615b2c2 --- /dev/null +++ b/src/components/Picker/HourDialog/index.tsx @@ -0,0 +1,85 @@ +import React, { useState, useEffect, useRef } from "react"; +import CommonPopup from "@/components/CommonPopup"; +import { View } from "@tarojs/components"; +import { PickerCommonRef, PickerCommon } from "@/components/Picker"; +import dayjs from "dayjs"; +import styles from "../CalendarDialog/index.module.scss"; +export interface HourDialogProps { + value?: Date | Date[]; + searchType?: "single" | "range" | "multiple"; + onChange?: (any) => void; + visible: boolean; + onClose: () => void; + title?: React.ReactNode; +} + +const HourDialog: React.FC = ({ + visible, + onClose, + title, + value, + onChange, +}) => { + const [selectedTime, setSelectTime] = useState<(string | number)[]>([8,0]); + const hourMinutePickerRef = useRef(null); + const handleConfirm = () => { + const value = hourMinutePickerRef.current?.getValue(); + if (onChange) { + onChange(value); + } + dialogClose(); + }; + const dialogClose = () => { + onClose(); + } + + const getConfirmText = () => { + return "完成"; + }; + + const onCancel = () => { + onClose(); + }; + + + useEffect(() => { + if (visible && value) { + // setSelectedHour(value ? dayjs(value as Date).hour() : 8); + // setSelectedMinute(value ? dayjs(value as Date).minute() : 0); + const hour = value ? dayjs(value as Date).hour() : 8; + const minute = value ? dayjs(value as Date).minute() : 0 + setSelectTime([hour, minute]) + } + }, [value, visible]); + + + + + return ( + + + + + + ); +}; + +export default HourDialog; diff --git a/src/components/Picker/Picker.tsx b/src/components/Picker/Picker.tsx index e0a1f31..e221a3f 100644 --- a/src/components/Picker/Picker.tsx +++ b/src/components/Picker/Picker.tsx @@ -28,7 +28,7 @@ const CustomPicker = ({ // 当外部 defaultValue 变化时,同步更新内部状态 useEffect(() => { - setCurrentValue(defaultValue) + handleValuesChange(defaultValue); }, [defaultValue]) const confirmPicker = ( @@ -44,15 +44,29 @@ const CustomPicker = ({ onConfirm(options, values) } } + + const handleValuesChange = (valuesList) => { + const isSame = + Array.isArray(valuesList) && + Array.isArray(currentValue) && + valuesList.length === currentValue.length && + valuesList.every((v: any, idx: number) => v === currentValue[idx]) + + console.log(isSame,valuesList, 'isSameisSameisSameisSame'); + if (!isSame) { + setCurrentValue(valuesList) + } + return isSame; + } const changePicker = useCallback((options: any[], values: any, columnIndex: number) => { - // 更新内部状态 - setCurrentValue(values) - - if (onChange) { + // 值相同则不触发更新,避免受控/非受控同步造成的回流循环 + + const isSame = handleValuesChange(values) + if (onChange && !isSame) { onChange(options, values, columnIndex) } - }, [onChange]) + }, [onChange, currentValue]) return ( <> diff --git a/src/components/Picker/index.ts b/src/components/Picker/index.ts index 33a37f7..74e86a0 100644 --- a/src/components/Picker/index.ts +++ b/src/components/Picker/index.ts @@ -4,4 +4,6 @@ export { default as PickerCommon } from './PickerCommon' export type { PickerCommonRef } from './PickerCommon' export { default as CalendarUI } from './CalendarUI/CalendarUI' export { default as DialogCalendarCard } from './CalendarDialog/DialogCalendarCard' -export { default as CityPicker } from './CityPicker' \ No newline at end of file +export { default as CityPicker } from './CityPicker' +export { default as DayDialog } from './DayDialog'; +export { default as HourDialog } from './HourDialog'; diff --git a/src/components/TextareaTag/TextareaTag.scss b/src/components/TextareaTag/TextareaTag.scss index 7d16b62..7dbc0e6 100644 --- a/src/components/TextareaTag/TextareaTag.scss +++ b/src/components/TextareaTag/TextareaTag.scss @@ -9,7 +9,7 @@ margin-top: 8px; .additional-input { width: 100%; - height: 46px; + min-height: 46px; font-size: 14px; color: #333; background: transparent; diff --git a/src/components/TextareaTag/TextareaTag.tsx b/src/components/TextareaTag/TextareaTag.tsx index 97899c6..c967faf 100644 --- a/src/components/TextareaTag/TextareaTag.tsx +++ b/src/components/TextareaTag/TextareaTag.tsx @@ -72,7 +72,7 @@ const TextareaTag: React.FC = ({ placeholderClass='textarea-placeholder' onInput={handleTextChange} maxlength={maxLength} - autoHeight={false} + autoHeight={true} onFocus={onFocus} onBlur={onBlur} /> diff --git a/src/components/TimeSelector/TimeSelector.tsx b/src/components/TimeSelector/TimeSelector.tsx index d1adb58..3a5d03f 100644 --- a/src/components/TimeSelector/TimeSelector.tsx +++ b/src/components/TimeSelector/TimeSelector.tsx @@ -2,6 +2,7 @@ import React, { useState, useEffect } from 'react' import { View, Text, } from '@tarojs/components' import { getDate, getTime, getDateStr, getEndTime } from '@/utils/timeUtils' import { DialogCalendarCard } from '@/components/index' +import { DayDialog, HourDialog } from '@/components/Picker/index'; import './TimeSelector.scss' import dayjs from 'dayjs' @@ -35,6 +36,9 @@ const TimeSelector: React.FC = ({ const [currentTimeValue, setCurrentTimeValue] = useState(new Date()) const [currentTimeType, setCurrentTimeType] = useState<'start' | 'end'>('start') const [showEndTime, setShowEndTime] = useState(false) + const [visibleDay, setVisibleDay] = useState(false); + const [visibleHour, setVisibleHour] = useState(false); + const handleConfirm = (date: Date) => { console.log('选择的日期:', date) const start_time = currentTimeType === 'start' ? getDateStr(date) : value.start_time; @@ -46,12 +50,56 @@ const TimeSelector: React.FC = ({ } if (onChange) onChange({start_time, end_time}) } + const onChangeDay = (years:any) => { + if (onChange){ + if (currentTimeType === 'start') { + const hour = dayjs(value.start_time).format('HH:mm') + onChange({start_time: `${years} ${hour}`, end_time: value.end_time}) + } else { + const hour = dayjs(value.end_time).format('HH:mm') + onChange({start_time: value.start_time, end_time: `${years} ${hour}`}) + } + } + } + const onChangeHour = (dates) => { + const [hour, minute] = dates; + if (onChange){ + if (currentTimeType === 'start') { + const year = dayjs(value.start_time).format('YYYY-MM-DD') + onChange({start_time: `${year} ${hour}:${minute}`, end_time: value.end_time}) + } else { + const year = dayjs(value.end_time).format('YYYY-MM-DD') + onChange({start_time: value.start_time, end_time: `${year} ${hour}:${minute}`}) + } + } + + } const openPicker = (type: 'start' | 'end') => { setCurrentTimeValue(type === 'start' ? parseDate(value.start_time) : parseDate(value.end_time)) setCurrentTimeType(type) setVisible(true) } + const openDay = (type: 'start' | 'end') => { + setCurrentTimeValue(type === 'start' ? parseDate(value.start_time) : parseDate(value.end_time)) + setCurrentTimeType(type) + setVisibleDay(true) + } + + const openHour = (type: 'start' | 'end') => { + setCurrentTimeValue(type === 'start' ? parseDate(value.start_time) : parseDate(value.end_time)) + setVisibleHour(true) + setCurrentTimeType(type) + } + + const endHandle = () => { + if (showEndTime) { + openHour('end') + } else { + openPicker('end') + } + } + useEffect(() => { if (value.start_time && value.end_time) { const start_time = dayjs(value.start_time).format('YYYY-MM-DD') @@ -71,14 +119,14 @@ const TimeSelector: React.FC = ({ - openPicker('start')}> + 开始时间 {value.start_time && (<> - {getDate(value.start_time)} - {getTime(value.start_time)} + openDay('start')}>{getDate(value.start_time)} + openHour('start')}>{getTime(value.start_time)} )} - {!value.start_time && (请选择开始时间)} + {!value.start_time && ( openPicker('start')}>请选择开始时间)} @@ -88,24 +136,40 @@ const TimeSelector: React.FC = ({ - openPicker('end')}> + 结束时间 - {value.end_time && (<>{showEndTime && ({getDate(value.end_time)})} - {getTime(value.end_time)})} - {!value.end_time && (请选择结束时间)} + {value.end_time && (<>{showEndTime && ( openDay('end')}>{getDate(value.end_time)})} + endHandle()}>{getTime(value.end_time)})} + {!value.end_time && ( openPicker('end')}>请选择结束时间)} { visible && setVisible(false)} + visible={visible} + value={currentTimeValue} + onChange={handleConfirm} + onClose={() => setVisible(false)} /> } + { + visibleDay && setVisibleDay(false)} + /> + } + { + visibleHour && setVisibleHour(false)} + /> + } ) }