基于AntDV日期组件和dayjs的日期处理

一、常见业务场景

toB业务中,经常有日期查询的需求,常见组件库都提供有日期组件,比如AntDV中提供了DatePicker和RangePicker,DatePicker组件用来选择单一时间,RangePicker组件用来选择时间范围,通过配置可以实现常用功能。

但是实际业务场景可能比较复杂,比如需要按一定的规则初始化、需要切换不同的日期类型,并且展示对应类型下的初始值、需要定制化的展示格式、需要校验时间范围、需要在单一日期和日期范围间切换、需要在满足一定条件后同步展示时间等,如果每个页面都去实现这些功能,还是比较繁杂的,所以我们基于实际业务场景采用AntDV提供的DatePicker和RangePicker组件结合dayjs封装了常用日期功能,提供丰富常用的默认配置,使页面可以通过简单配置和调用就能满足需求,不需要关注具体的实现细节,提高开发效率,代码也更简洁易维护,下面展示一些常见的需求场景:

1、多种日期类型切换

常用日期类型有分钟、小时、天、周、月、季度等,选择不同类型时,需要按一定格式展示对应类型下的默认日期。比如选择周类型时,默认日期为最近5周且展示格式为xxxx年第xx周 xx/xx ~ xx/xx,选择小时类型时,默认时间为当天00点到现在的时刻,需要显示的格式为xxxx-xx-xx xx点。下图展示日期为范围区间即有开始时间和结束时间时,类型切换和对应默认值展示的效果:

2、单一类型

不需要多种日期类型切换,只需要固定某种类型。下图展示的是,按天这种类型下单一日期展示效果:

3、单一日期和日期范围之间切换

有些业务场景需要在单一日期和日期范围之间切换。比如下图中,按天、周、月时,只需要单一日期,自定义选项下需要日期范围,并且需要自由配置显示方式,默认值等。

4、日期范围校验

当日期为范围区间时,希望用户选择的范围区间能满足一定条件,满足条件后才进行查询,否则给出错误提示且不查询。比如按分钟时,要求时间范围不超过30分钟;按天时,要求时间范围不超过31天等。下图展示的是分钟超出限制时的效果:

5、同步展示日期

如果页面内容比较多,比如查询结果有图也有表格,偏下的部分可能就看不到查询日期,为了更好的用户体验,可以在查询结果处也显示当前的查询日期,通常格式和日期组件中显示的一致,且只有在点击查询后才更新。下图展示的是在点击查询后,表格后面的日期同步为最新的查询日期:

二、使用方法及实现原理

1、多种日期类型切换

使用方法:html中RangePicker配置的参数是调用useDatePicker的返回,js部分通过简单配置即可实现

html 复制代码
<Col>
    <FormItem label="日期类型">
      <Select v-model:value="params.datatype">
        <SelectOption value="0">按分钟</SelectOption>
        <SelectOption value="1">按小时</SelectOption>
        <SelectOption value="2">按天</SelectOption>
        <SelectOption value="3">按周</SelectOption>
        <SelectOption value="4">按月</SelectOption>
        <SelectOption value="5">按季度</SelectOption>
      </Select>
    </FormItem>
  </Col>
  <Col>
    <FormItem name="queryTime" label="时间" >
      <RangePicker
        v-model:value="queryTime"
        :format="format"
        :show-time="showTime"
        :picker="picker"
      />
    </FormItem>
  </Col>
javascript 复制代码
  // 初始化日期
  const { queryTime, start, end, format, picker, showTime } = useDatePicker(params, {
      watchKey: 'datatype',
      componentType: 'rangePicker',
      list: {
        '0': {},
        '1': {},
        '2': {},
        '3': {},
        '4': {},
        '5': {},
      }
  })

实现原理:封装的功能函数里,提供了丰富的常用配置,所以list对象里只要提供对应的key值即可使用对应的默认配置。这里的key值,可以是语义化的,如minute,hour,week等,这也是默认的key值,如果实际参数是数字字符串类型的如'0','1'等,可以通过map参数进行映射。这里默认添加了一个映射,所以可以直接使用数字字符串类型的key值。

如果默认的配置不满足需求,也可以在list列表对应的字段里进行配置,优先读取list列表里的配置。

javascript 复制代码
const defaultConfig: IConfigList = {
  minute: {
    format: 'YYYY-MM-DD HH:mm',
    picker: '',
    showTime: { format: 'HH:mm' },
    limit: {
      value: 30,
      unit: 'minute',
      tip: '时间跨度不能超过30分钟',
    },
    start: dayjs().subtract(30, 'minute'),
  },

  hour: {
    format: defaultHourFormat,
    resultFormat: 'YYYYMMDDHH',
    picker: '',
    showTime: { format: 'HH' },
    limit: {
      value: 24,
      unit: 'hour',
      tip: '时间跨度不能超过24小时',
    },
    start: dayjs().startOf('date'),
  },
  day: {//...},
  //...
};

function defaultWeekFormat(value) {
  const weekFormat = 'MM/DD';
  const date = dayjs(value);
  return `${date.year()}年第${(date as any).week()}周 ${date
    .startOf('week')
    .format(weekFormat)} ~ ${date.endOf('week').format(weekFormat)}`;
}
//...

目前支持的配置项如下所示:

javascript 复制代码
export type limitType = string | {
  value: number;
  tip: string; // 超出限制提示
  unit: string; // 参考day.js difference 单位列表
}

interface IItem { // 日期类型配置参数
  picker: string;
  format: string | Function;
  // 结果需要的格式(默认和format一致)
  resultFormat?: string;
  showTime?: Object | string;
  // 下拉切换时支持改变组件类型
  componentType?: 'rangePicker' | 'datePicker';
  // 查询限制, 如限制31天,12个月等
  limit?: limitType;
  start?: string | Dayjs;
  end?: string | Dayjs;
}

监听watchKey的值响应不同日期类型间切换并更新配置。

javascript 复制代码
  watch(
    () => { 
      if(isReactive(params)) return params[config.watchKey]
      return params.value[config.watchKey]
    },
    (value) => {
      value = value || defaultKey
      let item = config.list[value]
      item && updateValue(value, item)
    }
  )
2、单一类型

使用方法:单一类型和多种类型切换场景的参数一致,使用起来更方便。

html 复制代码
 <Col>
   <FormItem name="queryTime" label="时间">
     <DatePicker
          v-model:value="queryTime"
          :format="format"
      />
    </FormItem>
  </Col>
javascript 复制代码
// 初始化日期
const { queryTime, start, format } = useDatePicker(params, {
   componentType: 'datePicker',
   list: {
     '2': {},
   }
})

实现原理:直接取list配置的第一项作为默认值

javascript 复制代码
  let [defaultKey, defaultItem] = Object.entries(config.list)[0]
3、单一日期和日期范围之间切换

使用方法:html部分会判断使用DatePicker还是RangePicker,配置参数是一样的。js部分在调用useDatePicker时配置了componentType,表示默认使用的组件类型,list列表每个具体项里也可以配置此属性,表示当前日期类型下需要的组件类型。

list列表里除了可以使用默认类型,还支持自定义,只要自定义里面的配置字段和要求一致即可。

html 复制代码
  <Col>
    <FormItem label="日期类型">
      <Select v-model:value="params.datatype" placeholder="日期类型">
        <SelectOption value="2">按天</SelectOption>
        <SelectOption value="3">按周</SelectOption>
        <SelectOption value="4">按月</SelectOption>
        <SelectOption value="custom">自定义</SelectOption>
      </Select>
    </FormItem>
  </Col>
  <Col >
    <FormItem name="queryTime" label="时间" v-if="params.datatype === 'custom'">
      <RangePicker
        v-model:value="queryTime"
        :format="format"
        :picker="picker"
      />
    </FormItem>
    <FormItem name="queryTime" label="时间" v-else>
      <DatePicker
        v-model:value="queryTime"
        :format="format"
        :picker="picker"
      />
    </FormItem>
  </Col>
javascript 复制代码
  // 初始化日期
  const { queryTime, format, picker, judgeDate } = useDatePicker(params, {
    watchKey: 'datatype',
    componentType: 'datePicker',
    list: {
      '2': {},
      '3': {
        start: dayjs().subtract(1, 'week'),
      },
      '4': {
        start: dayjs().subtract(0, 'month'),
      },
      'custom': {
        format: 'YYYY-MM-DD',
        pickerType: 'date',
        componentType: 'rangePicker',
        start: dayjs().subtract(6, 'day'),
        end: dayjs().subtract(0, 'day'),
        limit: {
          limit: 31,
          unit: 'day',
          tip: '时间跨度不能超过31天',
        },
      },
    },
  });

实现原理:在初始化和切换类型更新时,通过判断componentType的值,来决定queryTime的值,componentType的值,优先从当前项的配置里面获取

javascript 复制代码
  const queryTime = componentType === 'datePicker' ? 
    ref<Dayjs>(dayjs(defaultStart)):
    ref<[Dayjs, Dayjs]>([dayjs(defaultStart), dayjs(defaultEnd)])
4、日期范围校验

使用方法:在查询之前调用judgeDate()进行校验,校验通过再进行查询。

javascript 复制代码
function query(bool = true) {
  judgeDate().then(() => {
    // 实际查询
  })
}

实现原理:内部实现了判断方法,通过配置的limit项判断是否符合需求,不符合则给出错误提示

javascript 复制代码
  // 判断日期是否超出限制范围
  function judgeDate() {
    return new Promise<void>((resolve, reject) => {
      if(typeof toValue(limit) !== 'object' || !Array.isArray(queryTime.value)) resolve()
      const { value, tip, unit } = toValue(limit) as any
      if(dayjs(queryTime.value[1]).diff(dayjs(queryTime.value[0]), unit) > value ) {
        message.error(tip)
        reject()
      } else {
        resolve()
      }
    })
  }
5、同步展示日期

使用方法:需要更新时调用getDate()拿最新日期数据,页面同步更新

html 复制代码
<span>( {{ dateTips }} ) </span> 
js 复制代码
const dateTips = ref(getDate()) 
function query(bool = true) {
   judgeDate().then(() => {
     // todo something
     dateTips.value = getDate()
   })
}

实现原理:内部实现了格式化日期的方法

javascript 复制代码
  // 可用于页面日期展示,比如展示表格或图查询时间等
  function getDate(sep = '~') {
    let _format: string | Function = toValue(format)
    if(!queryTime.value) return sep
    if(Array.isArray(queryTime.value)) {
      if(typeof _format === 'function') {
        return `${(_format as Function)(queryTime.value[0])} ${sep} ${ (_format as Function)(queryTime.value[1])}`
      } 
      return `${dayjs(queryTime.value[0]).format(_format)} ${sep} ${dayjs(queryTime.value[1]).format(_format)}`
    } else {
      if(typeof _format === 'function') {
        return (_format as Function)(queryTime.value)
      }
      return dayjs(queryTime.value).format(_format)
    }
  }
相关推荐
_.Switch13 分钟前
Python Web 架构设计与性能优化
开发语言·前端·数据库·后端·python·架构·log4j
libai16 分钟前
STM32 USB HOST CDC 驱动CH340
java·前端·stm32
南斯拉夫的铁托44 分钟前
(PySpark)RDD实验实战——取最大数出现的次数
java·javascript·spark
Java搬砖组长1 小时前
html外部链接css怎么引用
前端
GoppViper1 小时前
uniapp js修改数组某个下标以外的所有值
开发语言·前端·javascript·前端框架·uni-app·前端开发
丶白泽1 小时前
重修设计模式-结构型-适配器模式
前端·设计模式·适配器模式
程序员小羊!1 小时前
UI自动化测试(python)Web端4.0
前端·python·ui
破z晓1 小时前
OpenLayers 开源的Web GIS引擎 - 地图初始化
前端·开源
好看资源平台1 小时前
JavaScript 数据可视化:前端开发的核心工具
开发语言·javascript·信息可视化
维生素C++1 小时前
【可变模板参数】
linux·服务器·c语言·前端·数据结构·c++·算法