基于antd的DatePicker 组件封装业务组件

先看一下我写的目录结构:

依次来看业务代码;

(1)RangeTime.tsx 

import {useState,uesCallback} from 'react';
import {DatePicker} from 'antd';
import {RangePickerProps as AntdRangePickerProps} from 'antd/es/date-picker';
import {Moment} from 'moment';
import type {RangeValue} from 'rc-picker/es/interface';
import {createPastTimeRange} from './utils/date';
import {toMomentRange} from './utils';
import Panel from './components/Panel';
import type {MomentRange} from './components/interface';const OSUIRangePicker = DatePicker.RangePicker;export type RangeValueMoment = Parameters<Parameters<NonNullable<React.ComponentProps<typeof OSUIRangePicker>['onChange']>
>[0];export const DATE_RANGE_FUNC_PRESETS={'30分钟':()=>toMomentRange(createPastTimeRange({minutes:30})),'1小时':()=>toMomentRange(createPastTimeRange({hours:1})),'3小时':()=>toMomentRange(createPastTimeRange({hours:3})),'1天':()=>toMomentRange(createPastTimeRange({days:1})),'7天':()=>toMomentRange(createPastTimeRange({days:7})),
}interface RangePickerProps extends Omit <AntdRangePickerProps ,'value'> {value:RangeValue<Moment> | [Moment,Moment];
}export default function RangePicker({value,onChange,...props}:RangePickerProps){const [stateRangeValue,setRange]=useState<MomentRange | []>([]);const [open,setOpen]=useState(false);const handleChange = uesCallback((value,dateString)=>{setRange(value);setOpen(false);onChange?.(value,dateString);},[onChange,setRange]);const handleQuickRangeSelect=uesCallback((rangeFunc:()=>MomentRange)=>{const range = rangeFunc();const dateString = range.map(d=>d.format('YYYY-MM-DD HH:mm:ss'));handleChange(range,dateString);} )const panelRender = uesCallback(panelNode =>(<Panel rangeFunctionRecord={DATE_RANGE_FUNC_PRESETS}panelNode={panelNode}onQuickRangeSelect={handleQuickRangeSelect}/>),[handleQuickRangeSelect])const handleFocus = uesCallback(()=>{setOpen(true);},[])//当panel打开时,点击panel内的input,会触发datePicker的blur事件,如果panel是打开状态,则保持打开状态//依赖onOpenChange时序,onOpenChange会先于blur触发,所以可以成功const handleBlur = uesCallback(()=>{if(open){setOpen(true);}},[open])const handleOpenChange=uesCallback(open=>{setOpen(open);},[])const format = uesCallback(value =>{return value.format('YYYY-MM-DD HH:mm:ss');},[])const innerValue = (value || stateRangeValue) as RangeValue<Moment>;return (<OSUIRangePicker {...props}showTimeopen={open}format={format}value={innerValue}onFocus={handleFocus}onBlur={handleBlur}onChange={handleChange}onOpenChange={handleOpenChange}panelRender={panelRender}/>)
}

(2)封装的时间日期模块组件

1.utils/date/index.ts

export * from './common';
export * from './manipulate';
export * from './formatAsString';
export * from './formatAsTimeStamp';
export * from './formatAsMoment';

2.common.ts

export const isMilliSecond = (time:number)=>String(time).length === 13;

3.manipulate.ts

import moment,{Moment} from 'moment';
import {subDays,subHours,subMinutes} from 'date-fns';
import {MomentRange} from '../../components/interface';type TimeRangeBy ={days:number} | {hours:number} | {minutes:number};/*** 从开始时间到结束时间,返回一个时间范围* 也可提供一个till Date参数,表示到给定时间结束* eg: createPastTimeRange({minutes:60}),就是从60分钟开始到现在* @param by* @param till 默认是now* @returns*/export function createPastTimeRange(by:{days:number},till?:Date):[Date,Date];export function createPastTimeRange(by:{hours:number},till?:Date):[Date,Date];export function createPastTimeRange(by:{minutes:number},till?:Date):[Date,Date];export function createPastTimeRange(by:TimeRangeBy,till?:Date = new Date()):[Date,Date]{if('days' in by){return [subDays(till,by.days),till];}if('hours' in by){return [subHours(till,by.hours),till];}if('minutes' in by){return [subMinutes(till,by.minutes),till];}return [till,till];};/*** [end-start]时间段转换成[天-小时-分钟-秒的格式] string* @param start Moment* @param end Moment* @returns string*/
export const formatDurationTime = (start:Moment,end:Moment)=>{const diff = moment(end).diff(moment(start)) / 1000;const d = Math.floor(diff / (60 * 60 * 24));const h = Math.floor(diff % (60 * 60 * 24) / (60 * 60));const m = Math.floor(diff % (60 * 60) / 60);const s = Math.floor(diff % 60);const days = d ? `${h}天` : '';const hours = h ? `${h}小时` : '';const minutes = m ? `${m}分钟` : '';const seconds = s ? `${s}秒` : '';return `${days}${hours}${minutes}${seconds}`;
}export function timeRangeLengrh([startTime,endTime]:MomentRange,unitOfTime:'days' | 'hours' | 'minutes' | 'seconds' | 'ms' = 'seconds'
){ return moment(endTime).diff(moment(startTime),unitOfTime);
}

这块附上date-fns插件的链接,date-fns的github地址 

4.formatAsString

import {chunk} from 'lodash';
import moment,{Moment} from 'moment';
import {assertNever} from './type';
import {timeStampToMoment} from './formatAsMoment';/*** 返回ISO格式的时间字符串* @param time* @example '2024-05-16T07:05:10.658Z'*/export function timeStampToISOString(time:number | Moment){if(typeof time === 'number'){return timeStampToMoment(time).toISOString();}return moment(time).toISOString();}/*** 返回ISO格式的时间字符串,但是没有毫秒* @param time* @example '2024-05-16T07:05:10.13Z'*/export function timeStampToShortISOString(time:number | Moment){return timeStampToISOString(time).replace(/\.\d+/,'');
}export const formatMomentAsLong = (m:Moment)=>{return m.format('YYYY-MM-DD HH:mm:ss');
}export const formatMomentAsTime = (m:Moment)=>{return m.format('HH:mm:ss');
}export const formatDateStringToLong = (isoString:string)=>{return formatMomentAsLong(moment(isoString));
}export const formatDateStringToTime = (isoString:string)=>{return formatMomentAsTime(moment(isoString));
}export function transToLocalTimeByDateString(dateString:string,formatBy: 'date' | 'time' = 'date'
){if(formatBy === 'date'){return formatDateStringToLong(dateString);}if(formatBy === 'time'){return formatDateStringToTime(dateString);}assertNever(formatBy);
}/*** @param serverForm {string} `001122`* @returns `09:12:22`*/
export const transformTimeString = (serverForm:string) =>{const hourMinutesSecondList = chunk(serverForm,2);return hourMinutesSecondList.map(x=>x.join('')).join(':');
}/*** @param clientTime 前端时间点* @param deltaInHours 需要增加的小时数,可以是负数 . 绝对值小于等于24* @returns {string} 调整小时之后的时间点的前端形式*/
export const addHourForClientTime = (clientTime:string,deltaInHours:number) => {const [hour,minute,second] = clientTime.split(':').map(Number);return [(hour + deltaInHours + 24) % 24,minute,second].map(num =>String(num).padStart(2,'0')).join(':')
} /*** 将服务端时间转换成前端需要时间* @description 服务端形式的时间范围UTC+0,eg:[`000022`,`000033`]* @param timeRange {[string,string]}* @returns {[string,string]} 前端形式的时间范围UTC+8,eg:[`09:00:22`,`09:00:33`]*/
export const normalizeServerTimeRangeToClientForm = (timeRange:string[]) =>{return timeRange.map(time=>addHourForClientTime(transformTimeString(time),8));
}/*** 将前端时间范围标准化为服务端形式* @param clientTimeRange* @returns 服务端形式的时间范围,UTC+0,[`000022`,`000033`]*/
export const normalizeClientTimeRangeToServerForm = (clientTimeRange:string[])=>{return clientTimeRange.map(time=>addHourForClientTime(transformTimeString(time),16));
}/*** 格式化服务端形式时间段为字符串* @param serverTimeRange {[srting,string]},形如['1000000','120000']* @returns 形如`[10:00:00-12:00:00]`的字符串*/
export const formatServerFormTimeRange = (serverTimeRange:string[])=>{const [start,end]=normalizeServerTimeRangeToClientForm(serverTimeRange);return `[${start}-${end}]`;
}/*** @param serverTimeRange {[string,string]},形如[['100000','120000'],['100000','140000']]* @returns 形如`[10:00:00 - 12:00:00]`,[10:00:00-14:00:00]`的字符串*/
export const formatTimeRangeList = (timeRanges:string[][]) =>{return timeRanges.map(formatServerFormTimeRange).join(', ');
}export function transToLocalTimeByUnixTimeStamp(timeStamp:number,formatBy: 'date' | 'time' = 'date'
){if(formatBy === 'date'){return formatMomentAsLong(timeStampToMoment(timeStamp));}if(formatBy === 'time'){return formatMomentAsTime(timeStampToMoment(timeStamp));}assertNever(formatBy);
}

5.formatAsTimeStamp.ts

import moment,{Moment} from 'moment';
import {isMilliSecond} from './common';export const momentToSeconds = (timeStamp:Moment)=>{if(isMilliSecond(timeStamp.valueOf())){return Math.floor(timeStamp.valueOf() / 1000);}return timeStamp.valueOf();
}export const monentToTimeStamp = (date:Moment) =>{return moment(date).unix();
}export const timeStampAsMillSeconds = (timeStamp:number) =>{if(isMilliSecond(timeStamp)){return timeStamp;}return timeStamp * 1000;
}

6.formatAsMoment.ts

import moment from 'moment';
import {MomentRange} from '../../components/interface';
import {isMilliSecond} from './common';/*** timeStamp number 转换成monent* @param time  number* @returns Moment*/
export function timeStampToMoment(time:number){let unixTimestamp = time;if(!isMilliSecond(time)){unixTimestamp = time * 1000;}// 和moment.unix不同,unix如果传进来的是毫秒,需要加上000return moment(unixTimestamp);
}export function timeStampRangeToMomentRange(timeStampRange:[number,number]):MomentRange{return [timeStampToMoment(timeStampRange[0]),timeStampToMoment(timeStampRange[1])];
}

(3)处理时间utils模块

utils.tsx

import moment,{Moment} from 'moment';
import { Time } from './components/interface';export const toMomentRange = ([start,end]:[Date,Date]):[Moment,Moment] =>[moment(start),moment(end)];export const timeToString = ({hour,min,sec}:Time)=>{return `${hour}:${min}:${sec}`;
}export const stringToTime = (timeString:string) =>{const [hour,min,sec] = timeString.split(':').map(v=>v.trim());return {hour,min,sec};
}

(4)业务组件Panel模块

1.components/Panel

import type {MomentRange} from './interface';
import * as Styled from './Styled';type MomentRangeFunc = ()=>MomentRange;interface PancelProps{rangeFunctionRecord:Record<string,MomentRangeFunc>;panelNode:React.ReactElement;onQuickRangeSelect:(rangeFun:MomentRangeFunc)=>void;// hover可以用来设置临时的时间范围,交互的话效果更好onQuickRangeHover?:(rangeFun:MomentRangeFunc)=>void;
}export default function Pancel({rangeFunctionRecord,panelNode,onQuickRangeSelect,onQuickRangeHover
}:PancelProps){return (<><Styled.PanelLayout><Styled.RangeLayout>{Object.entries(rangeFunctionRecord).map(([label,rangeFunc])=>{return (<Styled.RangeItemkey={label}onClick={()=>onQuickRangeSelect(rangeFunc)}onMouseEnter={()=>onQuickRangeHover && onQuickRangeHover(rangeFunc)}>{label}</Styled.RangeItem>)})}</Styled.RangeLayout>{panelNode.props.children[0]}</Styled.PanelLayout>{panelNode.props.children[1]}</>)
}

2.components/Styled

import styled from '@emotion/styled';export const PanelLayout = styled.div`display:flex;
`export const RangeLayout = styled.div`display:flex;flex-direction: column;align-items: center;min-width: 56px;
`export const RangeItem = styled.div`line-height: 1.5;font-size: 12px;padding: 0px 8px;&:not(:last-child){margin-bottom: 12px;}cursor: pointer;&:hover{background-color:'#dce1e3'}
`

3.components/interface.tsx

import type {Moment} from 'monent';
export interface Time{hour:string;min:string;sec:string;
}
export type MomentRange=[Moment,Moment];

(5)使用时

1.PanelComponent.tsx

import React,{useCallback,useState} from 'react';
import {useBoolean} from 'huse';
import {MomentRange} from './components/interface';
import {useTimeRange} from './hooks/useTimeRange';
export default function PanelComponent(){const {data:fetchData}= useFetchData();// eg:useFetchData是从接口返回的数据并封装的hooksconst [isConnectNulls,{on:onConnectNulls,off:offConnectNulls}] = useBoolean(false);const onChangeConnectNulls = useCallback((checked:boolean)=>{if(checked){onConnectNulls();}else{offConnectNulls();}},[offConnectNulls,onConnectNulls])const {eventStartTimeMoment,defaultRange}=useTimeRange();const [range,setRange] = useState<MomentRange>(defaultRange);return (<RangeTimeComponentisConnectNulls={isConnectNulls}onChangeConnectNulls={onChangeConnectNulls}momentRange={range}setRange={setRange}eventStartTimeMoment={eventStartTimeMoment}/>)
};

2.处理时间模块hooks/useTimeRange.tsx

import {useMemo} from 'react';
import moment from 'moment';
import {MomentRange} from '../components/interface';
import {timeStampToMoment} from '../utils/date';export const useTimeRange = () =>{const {data:fetchData}= useFetchData();// eg:useFetchData是从接口返回的数据并封装的hooksconst eventStartTimeMoment = useMemo(()=>timeStampToMoment(fetchData?.eventStartTime || 0),[fetchData?.eventStartTime ]);const defaultRange = useMemo(():MomentRange =>{const startPointMoment = eventStartTimeMoment.clone().subtract(2,'hours');const endPointMoment = eventStartTimeMoment.clone().add(2,'hours');return [startPointMoment,endPointMoment.isBefore(moment()) ? endPointMoment : moment(),]},[eventStartTimeMoment])return {eventStartTimeMoment,defaultRange, }
}

3.处理时间组件RangeTimeComponent.tsx

import React,{useCallback} from 'react';
import moment from 'moment';
import {range} from 'lodash';
import RangeTime from './RangeTime';
import {MomentRange} from './components/interface';interface Props{isConnectNulls:boolean;momentRange:MomentRange;setRange:(range:MomentRange)=>void;onChangeConnectNulls:(isConnectNulls:boolean)=>void;eventStartTimeMoment:moment.Moment;
}export default function RangeTimeComponent(props:Props){const {isConnectNulls,momentRange,onChangeConnectNulls,setRange,eventStartTimeMoment,}=props;const handleRangeChange = useCallback(value=>{setRange(value);},[setRange]);const disabledDateTime = useCallback((current:any)=>{const nowMoment=moment();if(current?.isSame(nowMoment,'days')){return {disabledHours:()=>range(nowMoment.hours() + 1 ,25),disabledMinutes:()=>range(nowMoment.minutes() + 1 ,61),disabledSeconds:()=>range(nowMoment.seconds() + 1 ,61),}}return {};},[]);const disabledDate = useCallback((current:Moment.Moment)=>{const currentMomentClock=moment(current.format('L'));const eventStartTimeMomentClock = moment(eventStartTimeMoment.format('L'));const toolate = currentMomentClock.diff(eventStartTimeMomentClock,'days') > 7|| current > moment().endOf('day');const tooEarly = eventStartTimeMomentClock.diff(currentMomentClock,'days') > 7;return tooEarly || toolate;},[eventStartTimeMoment]);return (<RangeTime allowClear={false}value={[...momentRange]}onChange={handleRangeChange}disabledDate={disabledDate}disabledDateTime={disabledDateTime}/>)
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/15644.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ROCm上来自Transformers的双向编码器表示(BERT)

14.8. 来自Transformers的双向编码器表示&#xff08;BERT&#xff09; — 动手学深度学习 2.0.0 documentation (d2l.ai) 代码 import torch from torch import nn from d2l import torch as d2l#save def get_tokens_and_segments(tokens_a, tokens_bNone):""&qu…

rk3568_atomic

文章目录 前言一、atomic是什么?二、原子操作API函数1.atomic原子操作2.原子位操作API三、atomic驱动实验总结前言 本文记录的是正点原子rk3568开发板的atomic实验 一、atomic是什么? 不同的线程在进行读写的过程中,可能会冲突乱入,导致会有预想不到的结果。所以为了让数…

开源模型应用落地-LangSmith试炼-入门初体验-数据集评估(三)

一、前言 LangSmith是一个用于构建生产级 LLM 应用程序的平台&#xff0c;它提供了调试、测试、评估和监控基于任何 LLM 框架构建的链和智能代理的功能&#xff0c;并能与LangChain无缝集成。通过使用LangSmith帮助开发者深入了解模型在不同场景下的表现&#xff0c;让开发者能…

webshell工具-冰蝎流量特征和加密方式

一、冰蝎原理 1.1 简介 冰蝎是一款基于Java开发的动态加密通信流量的新型Webshell客户端&#xff0c;由于通信流量被加密&#xff0c;传统的WAF、IDS 设备难以检测&#xff0c;给威胁狩猎带来较大挑战。冰蝎其最大特点就是对交互流量进行对称加密&#xff0c;且加密密钥是由随…

基于Ruoyi-Cloud-Plus重构黑马项目-学成在线

文章目录 一、系统介绍二、系统架构图三、参考教程四、演示图例机构端运营端用户端开发端 一、系统介绍 毕设&#xff1a;基于主流微服务技术栈的在线教育系统的设计与实现 前端仓库&#xff1a;https://github.com/Xiamu-ssr/Dragon-Edu-Vue3 后端仓库&#xff1a;https://g…

【NodeMCU实时天气时钟温湿度项目 9】为项目增加智能配网功能(和风天气版)

今天是第九专题&#xff0c;主要介绍智能配网的方法途径和具体实现。在项目开发和调试阶段&#xff0c;设置 WIFI 连接信息&#xff0c;通常是在项目中修改源程序代码完成的。项目调试完成后&#xff0c;客户应用环境中如何实现WIFI连接信息&#xff08;ssid 和 password&#…

【基础详解】快速入门入门 SQLite数据可

简介 SQLite 是一个开源的嵌入式关系数据库&#xff0c;实现了自给自足的、无服务器的、配置无需的、事务性的 SQL 数据库引擎。它是一个零配置的数据库&#xff0c;这意味着与其他数据库系统不同&#xff0c;比如 MySQL、PostgreSQL 等&#xff0c;SQLite 不需要在系统中设置…

Unity功能——设置Camera,实现玩家被攻击后晃动效果

一、方法说明&#xff1a; 来源&#xff1a;siki学院&#xff1a;Unity项目捕鱼达人&#xff0c;功能学习记录&#xff1b; 效果摘要&#xff1a;通过调整相机移动&#xff0c;视觉感觉玩家面板剧烈晃动&#xff0c;实现被boss攻击时的震动效果。 使用场景说明&#xff1a; …

通过继承React.Component创建React组件-5

在React中&#xff0c;V16版本之前有三种方式创建组件&#xff08;createClass() 被删除了)&#xff0c;之后只有两种方式创建组件。这两种方式的组件创建方式效果基本相同&#xff0c;但还是有一些区别&#xff0c;这两种方法在体如下&#xff1a; 本节先了解下用extnds Reac…

Java | Leetcode Java题解之第112题路径总和

题目&#xff1a; 题解&#xff1a; class Solution {public boolean hasPathSum(TreeNode root, int sum) {if (root null) {return false;}if (root.left null && root.right null) {return sum root.val;}return hasPathSum(root.left, sum - root.val) || has…

国内快速下载hugging face大模型的方法

由于众所周知的原因&#xff0c;从hugging face下载大模型比较困难&#xff0c;幸好国内有人做了镜像站&#xff0c;可以通过国内的镜像站进行快速下载&#xff0c;以下是配置方法。 我的系统是ubuntu 22&#xff0c;这里记录的方法只对debian/ubuntu有效。 git-lfs/INSTALLI…

信息系统项目管理师0130:工具与技术(8项目整合管理—8.7监控项目工作—8.7.2工具与技术)

点击查看专栏目录 文章目录 8.7.2 工具与技术8.7.2 工具与技术 专家判断监控项目工作过程中,应征求具备如下领域相关专业知识或接受过相关培训的个人或小组的意见,涉及的领域包括:挣值分析;数据的解释和情境化;持续时间和成本的估算技术;趋势分析;关于项目所在的行业以及…

【LSTM】LSTM cell的门结构学习笔记

文章目录 1. LSTM cell2. 门结构3. 门的公式4. 门的参数5. 重点关系厘清 1. LSTM cell 如文章 LSTM网络与参数学习笔记 中介绍, LSTM cell指的是一个包含隐藏层所有神经元的结构.但是LSTM门控单元的公式如何理解、门和LSTM cell神经元如何对应、门函数的参数维度、不同时间步不…

鸿蒙 DevEco Studio 3.1 Release 下载sdk报错的解决办法

鸿蒙 解决下载SDK报错的解决方法 最近在学习鸿蒙开发&#xff0c;以后也会记录一些关于鸿蒙相关的问题和解决方法&#xff0c;希望能帮助到大家。 总的来说一般有下面这样的报错 报错一&#xff1a; Components to install: - ArkTS 3.2.12.5 - System-image-phone 3.1.0.3…

leecode 1206|跳表的设计

跳表 跳表&#xff0c;一种链表数据结构&#xff0c;其增删改茶的效率能和平衡树相媲美 leecode1206 可以看上面的那个动画&#xff0c;动画效果很贴切。 我简单讲讲它的机制吧&#xff0c;每个节点不单单是一个&#xff0c;测试好几层&#xff0c;然后同一层的节点和统一节点…

Tomcat部署项目的方式

目录 1、Tomcat发布项目的方式 方式1&#xff1a; 直接把项目发布到webapps目录下 方式2&#xff1a;项目发布到ROOT目录 方式3&#xff1a;虚拟路径方式发布项目 方式4&#xff1a;(推荐)虚拟路径&#xff0c;另外的方式&#xff01; 方式5&#xff1a;发布多个网站 1、…

掩码生成蒸馏——知识蒸馏

摘要 https://arxiv.org/pdf/2205.01529 知识蒸馏已成功应用于各种任务。当前的蒸馏算法通常通过模仿教师的输出来提高学生的性能。本文表明&#xff0c;教师还可以通过指导学生的特征恢复来提高学生的表示能力。从这一观点出发&#xff0c;我们提出了掩码生成蒸馏&#xff08…

【字典树(前缀树) 异或 离线查询】1707. 与数组中元素的最大异或值

本文涉及知识点 字典树&#xff08;前缀树&#xff09; 位运算 异或 离线查询 LeetCode1707. 与数组中元素的最大异或值 给你一个由非负整数组成的数组 nums 。另有一个查询数组 queries &#xff0c;其中 queries[i] [xi, mi] 。 第 i 个查询的答案是 xi 和任何 nums 数组…

C++ | Leetcode C++题解之第97题交错字符串

题目&#xff1a; 题解&#xff1a; class Solution { public:bool isInterleave(string s1, string s2, string s3) {auto f vector <int> (s2.size() 1, false);int n s1.size(), m s2.size(), t s3.size();if (n m ! t) {return false;}f[0] true;for (int i …

264 基于matlab的自适应语音盲分离

基于matlab的自适应语音盲分离&#xff0c;当a和b同时对着传声器A,B说话且传声器靠得很近时&#xff0c;传声器A,B会同时接受到a和b的声音&#xff0c;即a和b产生了混叠干扰&#xff0c;此时通过自适应语音盲分离系统可以将a,b的声音分离开&#xff0c;使得一个信道只有一个人的…