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

news2025/1/15 19:52:08

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

依次来看业务代码;

(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}
           showTime
           open={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如果传进来的是毫秒,需要加上000
   return 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.RangeItem
                                key={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是从接口返回的数据并封装的hooks
    const [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 (
        <RangeTimeComponent
            isConnectNulls={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是从接口返回的数据并封装的hooks
    const 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.coloradmin.cn/o/1691145.html

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

相关文章

【Spring Security系列】权限之旅:SpringSecurity小程序登录深度探索

作者&#xff1a;后端小肥肠 创作不易&#xff0c;未经允许严禁转载。 姊妹篇&#xff1a; 【Spring Security系列】Spring SecurityJWTRedis实现用户认证登录及登出_spring security jwt 退出登录-CSDN博客 1. 前言 欢迎来到【Spring Security系列】&#xff01;在当今数字化…

【Java】/*类和对象(下)*/

目录 一、封装 1.1 初识封装 1.2 如何封装成员变量 1.3 如何使用封装后的成员变量 二、访问限定符 三、包 3.1 包的概念 3.2 如何自定义包 3.3 导入包中的类 3.4 包的访问权限控制举例 示例一&#xff1a;private修饰成员变量 示例二&#xff1a; 不去修饰成员变…

网络拓扑—DNS服务搭建

文章目录 DNS服务搭建网络拓扑配置网络DNSPC 安装DNS服务配置DNS服务创建正向查找区域创建反向查找区域创建子域名 PC机DNS域名解析 DNS服务搭建 网络拓扑 为了节省我的U盘空间&#xff0c;没有用路由器&#xff0c;所以搭建的环境只要在同网段即可。 //交换机不用考虑 DNS&a…

hugging face笔记:PEFT

1 介绍 PEFT (Parameter-Efficient Fine Tuning) 方法在微调时冻结预训练模型参数&#xff0c;并在其上添加少量可训练的参数&#xff08;称为适配器&#xff09;这些适配器被训练用来学习特定任务的信息。这种方法已被证明在内存效率和计算使用上非常高效&#xff0c;同时能产…

靠着单干实现财富自由,可太爽了

这里所说的“单干”&#xff0c;并不是单打独斗的意思&#xff0c;而是一种商业认知&#xff0c;以及由这种认知衍生出来的商业模式、商业方法和商业实践。 之前提到单干&#xff0c;会本能地以为它是指脱离公司等组织形式&#xff0c;自己一个人做生意。现在单干有了更丰富的…

第十二章 网络编程

第十二章 网络编程 网络协议概述 通信协议&#xff1a; 协议即规则&#xff0c;就好比汽车上路要遵守交通规则一样&#xff0c;为了使全世界不同类型的计算机都可以连接起来&#xff0c;所以制定了一套全球通用的通信协议——Internet协议。有了Internet协议&#xff0c;任何…

【网络技术】【Kali Linux】Wireshark嗅探(十二)NBNS协议报文捕获及分析

往期 Kali Linux 上的 Wireshark 嗅探实验见博客&#xff1a; 【网络技术】【Kali Linux】Wireshark嗅探&#xff08;一&#xff09;ping 和 ICMP 【网络技术】【Kali Linux】Wireshark嗅探&#xff08;二&#xff09;TCP 协议 【网络技术】【Kali Linux】Wireshark嗅探&…

Swift 类和结构体

类和结构体 一、结构体和类对比1、类型定义的语法2、结构体和类的实例3、属性访问4、结构体类型的成员逐一构造器 二、结构体和枚举是值类型三、类是引用类型1、恒等运算符2、指针 结构体和类作为一种通用而又灵活的结构&#xff0c;成为了人们构建代码的基础。你可以使用定义常…

汇舟问卷:海外问卷调查如何闭坑?

大家好&#xff0c;我是汇舟问卷。有很多人问我这行有什么骗局吗&#xff1f;怎么说呢&#xff1f;其实每个行业都是真实存在的&#xff0c;也都有赚到钱的和赚不到钱的&#xff0c;那区别在哪里呢&#xff1f; 在你的源头&#xff0c;也就是教你或者说是代理加盟的上家&#…

LabVIEW舱段测控系统开发

LabVIEW舱段测控系统开发 在航空技术飞速发展的当下&#xff0c;对于航空器的测控系统的需求日益增加&#xff0c;特别是对舱段测控系统的设计与实现。开发了一款基于LabVIEW开发的舱段测控系统&#xff0c;包括系统设计需求、系统组成、工作原理以及系统实现等方面。 开发了…

电赛经验分享——赛前准备

⏩ 大家好哇&#xff01;我是小光&#xff0c;想要成为系统架构师的嵌入式爱好者。 ⏩在之前的电赛中取得了省一的成绩&#xff0c;本文对电赛比赛前需要准备什么做一个经验分享。 ⏩感谢你的阅读&#xff0c;不对的地方欢迎指正。 加入小光嵌入式交流群&#xff08;qq群号&…

【问题解决】Android Studio Jellyfish新建Kotlin项目后Gradle Sync及Maven下载很慢

创建新项目之后&#xff0c;Gradle Sync和Build都很慢&#xff0c;因为下载Gradle和Maven等工具。 代码默认配置 settings.gradle.kts pluginManagement {repositories {google {content {includeGroupByRegex("com\\.android.*")includeGroupByRegex("com\\.g…

框架学习之SpringMVC学习笔记(一)

一、SpringMVC简介 1-介绍 Spring Web MVC是基于Servlet API构建的原始Web框架&#xff0c;从一开始就包含在Spring Framework中。正式名称“Spring Web MVC”来自其源模块的名称&#xff08; spring-webmvc &#xff09;&#xff0c;但它通常被称为“Spring MVC”。 在控制层…

C语言中的 ?: :三元运算符详解

C语言中的 ?: &#xff1a;三元运算符详解 在C语言的浩瀚代码海洋中&#xff0c;三元运算符&#xff08;?:&#xff09;如同一位优雅的舞者&#xff0c;以简洁的姿态完成条件判断与赋值的双重任务。它以问号&#xff08;?&#xff09;和冒号&#xff08;:&#xff09;这两个…

魔众文库系统v6.6.0分销功能,后台日志重构,文档转换优化

分销功能&#xff0c;后台日志重构&#xff0c;文档转换优化 [新功能] 升级支持支付宝授权登录最新方式 [新功能] 后台左上角标题支持自定义&#xff0c;修改 modstart.php 中 admin.title 配置 [新功能] 日志界面重构&#xff0c;全新日志查看体验 [新功能] 链接选择弹窗增…

C语言 | Leetcode C语言题解之第110题平衡二叉树

题目&#xff1a; 题解&#xff1a; int height(struct TreeNode* root) {if (root NULL) {return 0;}int leftHeight height(root->left);int rightHeight height(root->right);if (leftHeight -1 || rightHeight -1 || fabs(leftHeight - rightHeight) > 1) {…

SpringBoot3整合阿里云短信服务-1(配置阿里云短信服务)

SpringBoot3整合阿里云短信服务-1(配置阿里云短信服务) 一、开通阿里云短信服务 阿里云官网:阿里云官网 选择产品中企业服务与云通信中的短信服务 选择免费开通 选择快速学习和测试 根据这几个全部配置一下我这里是配置好了所以学习进度是100% 1.1 添加资质 首先选择新增资质 …

三台泵恒压供水站电控系统及PLC程序设计实例

本文由艺捷自动化编写&#xff0c;其旗下产品有艺捷自动化网站和易为二维码说明书小程序&#xff08;微信&#xff09; 本文以一个具体的项目案例&#xff0c;来讲述一个恒压供水站的电控柜设计过程。包括用户需求&#xff0c;材料选型&#xff0c;图纸设计&#xff0c;柜内布…

微软新功能Recall引发隐私担忧,英国数据监管机构展开调查

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

大语言模型的工程技巧(三)——分布式计算

相关说明 这篇文章的大部分内容参考自我的新书《解构大语言模型&#xff1a;从线性回归到通用人工智能》&#xff0c;欢迎有兴趣的读者多多支持。 本文将讨论如何利用多台机器进行神经网络的分布式训练。利用多台机器来加速大语言模型的训练&#xff0c;是其获得成功的重要原…