当前位置: 首页 > news >正文

vue3二次封装ElDatePicker

前言

时间组件可以手动输入时间如 2025-01-01 或者 2025/01/01,enter后会对输入值进行格式化,但是对于 20250101这样省略间隔符的却不行,查看文档以及issue都没有类似的说明后,便决定对ElDatePicker进行二次封装

下载源码

到GitHub下载element-plus源码

element-plus-download

查找相关逻辑位置

date-picker组件位置
date-picker组件源码位置

date-picker组件介绍
CommonPicker 包裹 date-picker的子组件,组件根据type展示不同子组件

  • date-picker 位置:packages\components\date-picker\src\date-picker.tsx
    • CommonPicker 位置:packages\components\time-picker\src\common\picker.vue
      • date-picker子组件位置:packages\components\date-picker\src\date-picker-com

date-picker介绍

CommonPicker
CommonPicker组件700多行代码,代码太多直接搜索enter,定位607行代码,这里对enter事件进行了监听,当条件符合后执行handleChange以及关闭picker 。
再查看handleChange当输入值有效时就会对输入值使用parseUserInputToDayjs进行格式化覆盖。
parseUserInputToDayjs函数调用了 pickerOptions.value.parseUserInput,对parseUserInput进行溯源发现来自子组件。
CommonPicker
CommonPicker
CommonPicker
CommonPicker

Date-Picker下 子组件
panel-date-picker是date-picker 其中的一个组件。(其他组件对于parseUserInput的赋值都类同)
parseUserInput来自公共函数correctlyParseUserInput,观察函数发现只需要在 if (isString(value)) 添加我所需要的逻辑就行了
packages\components\date-picker\src\utils.ts
panel-date-picker01

编写组件

自身项目下src/components/DatePicker,我只改了DatePickPanel.vue子组件,其他暂时没改

  -DatePicker
    -index.ts
    -src
      -date-picker.tsx
      // -utils.ts
      // -panel-utils.ts
      -components
        -DatePickPanel.vue
        // -DateRangePickPanel.vue
        // -MonthRangePickPanel.vue
        // -YearRangePickPanel.vue

DatePickPanel.vue 其他子组件类同

<template>
  <PanelDatePick v-bind="{ ...$attrs, ...props }" @set-picker-option="onSetPickerOption">
    <template v-for="name in Object.keys($slots)" :key="name" #[name]="form">
      <slot :name="name" v-bind="form"></slot>
    </template>
  ></PanelDatePick>
</template>

<script lang="ts" setup>
import PanelDatePick from 'element-plus/es/components/date-picker/src/date-picker-com/panel-date-pick.mjs'
import { panelDatePickProps } from 'element-plus/es/components/date-picker/src/props/panel-date-pick.mjs'
import { PickerOptions } from 'element-plus/es/components/time-picker/src/common/props.mjs'
import type { Dayjs } from 'dayjs'
import dayjs from 'dayjs'
import { isString } from '@/utils/is'

const props = defineProps(panelDatePickProps)
// const contextEmit = defineEmits(['pick', 'set-picker-option', 'panel-change'])
const contextEmit = defineEmits(['set-picker-option'])

const onSetPickerOption = <T extends keyof PickerOptions>(e: [T, PickerOptions[T]]) => {
  // 重写 parseUserInput
  if (e[0] === 'parseUserInput') {
    contextEmit('set-picker-option', [
      'parseUserInput',
      (value: Dayjs | string) => {
        console.log('进入parseUserInput')
        const format = props.format || 'YYYY-MM-DD HH:mm:ss'
        if (isString(value)) {
          console.log(value, 'value.')
          // 判断为8个数字的格式,如:20250102
          if (/^[0-9]{8}$/.test(value)) {
            console.log('符合格式')
            value = value.slice(0, 4) + '-' + value.slice(4, 6) + '-' + value.slice(6)
          }
          const dayjsValue = dayjs(value, format)
          if (!dayjsValue.isValid()) {
            // return directly if not valid
            return dayjsValue
          }
        }
        return dayjs(value, format)
      }
    ])
  } else {
    contextEmit('set-picker-option', e)
  }
}
</script>

date-picker.tsx
就是把原本组件复制了过来再改了引入,以及该使用哪个子组件的判断

import { computed, defineComponent, provide, reactive, ref, toRef } from 'vue'
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat.js'
import advancedFormat from 'dayjs/plugin/advancedFormat.js'
import localeData from 'dayjs/plugin/localeData.js'
import weekOfYear from 'dayjs/plugin/weekOfYear.js'
import weekYear from 'dayjs/plugin/weekYear.js'
import dayOfYear from 'dayjs/plugin/dayOfYear.js'
import isSameOrAfter from 'dayjs/plugin/isSameOrAfter.js'
import isSameOrBefore from 'dayjs/plugin/isSameOrBefore.js'
import { useNamespace } from 'element-plus/es/hooks/use-namespace/index.mjs'
import {
  CommonPicker,
  DEFAULT_FORMATS_DATE,
  DEFAULT_FORMATS_DATEPICKER,
  type DateModelType,
  type SingleOrRange,
} from 'element-plus/es/components/time-picker/index.mjs'
import { UPDATE_MODEL_EVENT } from 'element-plus/es/constants/index.mjs'
import { ROOT_PICKER_INJECTION_KEY } from 'element-plus/es/components/date-picker/src/constants.mjs'
import { datePickerProps } from 'element-plus/es/components/date-picker/src/props/date-picker.mjs'
import { getPanel } from 'element-plus/es/components/date-picker/src/panel-utils.mjs'
import type { DatePickerExpose } from 'element-plus/es/components/date-picker/src/instance.mjs'
import DatePickPanel from './components/PanelDatePick.vue'


dayjs.extend(localeData)
dayjs.extend(advancedFormat)
dayjs.extend(customParseFormat)
dayjs.extend(weekOfYear)
dayjs.extend(weekYear)
dayjs.extend(dayOfYear)
dayjs.extend(isSameOrAfter)
dayjs.extend(isSameOrBefore)

export default defineComponent({
  name: 'ElDatePicker',
  install: null,
  props: datePickerProps,
  emits: [UPDATE_MODEL_EVENT],
  setup(props, { expose, emit, slots }) {
    const ns = useNamespace('picker-panel')
    const isDefaultFormat = computed(() => {
      return !props.format
    })
    provide('ElIsDefaultFormat', isDefaultFormat)
    provide('ElPopperOptions', reactive(toRef(props, 'popperOptions')))
    provide(ROOT_PICKER_INJECTION_KEY, {
      slots,
      pickerNs: ns,
    })

    const commonPicker = ref<InstanceType<typeof CommonPicker>>()
    const refProps: DatePickerExpose = {
      focus: () => {
        commonPicker.value?.focus()
      },
      blur: () => {
        commonPicker.value?.blur()
      },
      handleOpen: () => {
        commonPicker.value?.handleOpen()
      },
      handleClose: () => {
        commonPicker.value?.handleClose()
      },
    }

    expose(refProps)

    const onModelValueUpdated = (val: SingleOrRange<DateModelType> | null) => {
      emit(UPDATE_MODEL_EVENT, val)
    }

    return () => {
      // since props always have all defined keys on it, {format, ...props} will always overwrite format
      // pick props.format or provide default value here before spreading
      const format =
        props.format ??
        (DEFAULT_FORMATS_DATEPICKER[props.type] || DEFAULT_FORMATS_DATE)
        let Component =  getPanel(props.type)
        if (!['daterange', 'datetimerange', 'monthrange', 'yearrange'].includes(props.type)) {
          Component = DatePickPanel as any
        }

      return (
        <CommonPicker
          {...props}
          format={format}
          type={props.type}
          ref={commonPicker}
          onUpdate:modelValue={onModelValueUpdated}
        >
          {{
            default: (scopedProps: /**FIXME: remove any type */ any) => (
              <Component {...scopedProps}>
                {{
                  'prev-month': slots['prev-month'],
                  'next-month': slots['next-month'],
                  'prev-year': slots['prev-year'],
                  'next-year': slots['next-year'],
                }}
              </Component>
            ),
            'range-separator': slots['range-separator'],
          }}
        </CommonPicker>
      )
    }
  },
})

index.ts

import DatePicker from './src/date-picker'

export { DatePicker }

效果

参考

vue3中修改element plus组件源码并使用,比如自定义额外逻辑的解决思路方式(亲测有效)

http://www.dtcms.com/a/107901.html

相关文章:

  • Vue 组件命名及子组件接收参数命名
  • 汇编学习之《call, return指令》
  • 【前端安全】模板字符串动态拼接HTML的防XSS完全指南
  • 安装IIS 10
  • QML输入控件: RangeSlider的基础用法与样式
  • Flutter中实现中国省份地图
  • dom操作笔记、xml和document等
  • C语言学习笔记
  • 如何平衡元器件成本与性能
  • Day19 -实例:xcx逆向提取+微信开发者工具动态调试+bp动态抓包对小程序进行资产收集
  • React-Markdown详解
  • 解决大小写、保留字与特殊字符问题!Oracle双引号在SQL中的特殊应用
  • 论文阅读笔记:Denoising Diffusion Implicit Models (4)
  • PyTorch 激活函数
  • PyQt5和OpenCV车牌识别系统
  • Java基础 4.2
  • Mysql 在什么样的情况下会产生死锁?
  • Python爬虫第2节-网页基础和爬虫基本原理
  • 2.Linux的权限理解
  • mysql docker容器启动遇到的问题整理
  • 华为面试,机器学习深度学习知识点:
  • Windows C++ 排查死锁
  • MIT6.S081 - Lab6 Copy-on-Write(写时复制)
  • 模拟集成电路设计与仿真 : Mismatch
  • 数据库 第一章 MYSQL基础(4)
  • 《汽车噪声控制》课程作业
  • 英飞凌高信噪比MEMS麦克风驱动人工智能交互
  • Pandas基础及series对象
  • Token是什么?
  • 时序数据库 InfluxDB(六)