Vue自定义多功能日期选择器

目录

功能点

组件图示

常用

自定义

源码

TDatePicker

TRangePicker

调用方式

参数列表

常用快捷日期文本对应

自定义日期


功能点

  • 支持快速选择常用的日期或日期范围
  • 可自定义选择的日期范围
  • 手动选择想要的日期范围

组件图示

常用

常用

自定义

自定义

源码

TDatePicker

<!--TDatePicker-->
<template>
  <div class="date_body">
    <div class="tangent-date-picker" ref="tangentDatePicker" style="width: 100%">
      <a-popover
        trigger="click"
        placement="bottomLeft"
        v-model:visible="visible"
        overlayClassName="popover-range-picker-dropdown"
        :getPopupContainer="(node) => node.parentNode || document.body"
      >
        <template #content>
          <TRangePicker
            :prop-dynamic="dynamic"
            :prop-range-date="rangeDate"
            :prop-dynamic-type="dynamicType"
            :prop-cus-date-active="customizeActive"
            @changeDate="changeDate"
          />
        </template>
        <a-input
          readOnly
          @click="showDatePicker"
          v-model:value="value"
          :bordered="false"
          size="small"
          class="inputClass"
          :placeholder="placeholder"
          :style="{ fontSize: inputSpanFontSize }"
        />
      </a-popover>
    </div>
  </div>
</template>

<script lang="ts">
  import { defineComponent, ref, watch } from 'vue';
  import dayjs from 'dayjs';
  import TRangePicker from './TRangePicker.vue';
  export default defineComponent({
    name: 'TDatePicker',
    components: { TRangePicker },
    props: {
      // 左侧常用快捷数据
      propDynamic: {
        type: String,
        default: '',
      },

      // 快捷日期类型:常用|自定义
      propDynamicType: {
        type: String,
        default: 'common',
      },

      // 自定义快捷日期高亮
      propCusDateActive: {
        type: String,
        default: '',
      },

      // 日期
      propRangeDate: {
        type: Array,
        default: () => {
          return [
            dayjs(dayjs().startOf('day').subtract(6, 'days').format('YYYY-MM-DD')),
            dayjs(dayjs().format('YYYY-MM-DD')),
          ];
        },
      },

      placeholder: {
        type: String,
        default: ' - ',
      },
      inputSpanFontSize: {
        type: String,
        default: '12px',
      },
    },
    setup(props, context) {
      // 初始近7日
      const rangeDate = ref();
      let tempDate = JSON.parse(JSON.stringify(props.propRangeDate));
      if (tempDate[0] !== '' && tempDate[1] !== '') {
        rangeDate.value = [
          dayjs(tempDate[0]).format('YYYY-MM-DD'),
          dayjs(tempDate[1]).format('YYYY-MM-DD'),
        ];
      } else {
        rangeDate.value = ['', ''];
      }

      const value = ref<string>('');
      // 日期范围
      if (rangeDate.value[0] && rangeDate.value[1]) {
        value.value = rangeDate.value.join(' - ');
      }

      // 初始快捷日期,包含常用和自定义
      const dynamic = ref('');
      dynamic.value = JSON.parse(JSON.stringify(props.propDynamic));

      // 是常用还是自定义日期
      const dynamicType = ref('common');
      dynamicType.value = JSON.parse(JSON.stringify(props.propDynamicType));

      // 自定义日期高亮文本
      const customizeActive = ref('');
      customizeActive.value = JSON.parse(JSON.stringify(props.propCusDateActive));

      // 点击显示日期选择器
      const visible = ref(false);
      function showDatePicker() {
        visible.value = true;
      }
      function hideDatePicker() {
        visible.value = false;
      }

      // 子组件日期选择反馈
      function changeDate(tempVal, isHideDatePicker) {
        if (isHideDatePicker) {
          hideDatePicker();
        }
        dynamic.value = tempVal.dynamic;
        rangeDate.value = tempVal.rangeDate;
        dynamicType.value = tempVal.dynamicType;
        customizeActive.value = tempVal.customizeActive ? tempVal.customizeActive : '';
        value.value = tempVal.rangeDate.join(' - ');

        // 将选择的日期反馈回父组件
        context.emit('changeDate', tempVal);
      }

      function setDateInfo() {
        let tempDate = JSON.parse(JSON.stringify(props.propRangeDate));
        if (tempDate[0] !== '' && tempDate[1] !== '') {
          rangeDate.value = [
            dayjs(tempDate[0]).format('YYYY-MM-DD'),
            dayjs(tempDate[1]).format('YYYY-MM-DD'),
          ];
        } else {
          rangeDate.value = ['', ''];
        }
        // 日期范围
        if (rangeDate.value[0] && rangeDate.value[1]) {
          value.value = rangeDate.value.join(' - ');
        } else {
          value.value = '';
        }
        dynamic.value = JSON.parse(JSON.stringify(props.propDynamic));
        dynamicType.value = JSON.parse(JSON.stringify(props.propDynamicType));
        customizeActive.value = JSON.parse(JSON.stringify(props.propCusDateActive));
      }

      watch(
        () => props.propRangeDate,
        () => {
          setDateInfo();
        },
      );

      return {
        value,
        rangeDate,
        dynamic,
        dynamicType,
        customizeActive,

        changeDate,

        // 日期选择器的显示与隐藏
        visible,
        showDatePicker,
        hideDatePicker,
      };
    },
  });
</script>

<style scoped>
  .date_body {
    display: inline-block;
    border: 1px solid #d9d9d9;
    background-color: #fff;
    height: 32px;
    border-radius: 4px;
    line-height: 32px;
    margin-left: 5px;
    /*text-align: center;*/
    /*width: 150px;*/
    width: 260px;
    text-align: left;
    &:hover {
      border: 1px solid #85a5ff;
    }
  }
  .tangent-date-picker .ant-input {
    border-top-color: transparent !important;
    border-left-color: transparent !important;
    border-right-color: transparent !important;
    /*border-bottom: 1px solid #d9d9d9 !important;*/
    border-top: 0 !important;
    border-left: 0 !important;
    border-right: 0 !important;
  }

  .tangent-date-picker .ant-input:hover {
    border-top-color: transparent !important;
    border-left-color: transparent !important;
    border-right-color: transparent !important;
    border-bottom-color: rgb(89, 126, 247) !important;
    border-top: 0 !important;
    border-left: 0 !important;
    border-right: 0 !important;
  }

  /*:deep(.tangent-date-picker .ant-popover-inner-content) {*/
  /*  color: rgba(0, 0, 0, 0.85);*/
  /*  padding: 12px 16px 0px 16px !important;*/
  /*}*/

  :deep(.popover-range-picker-dropdown .ant-popover-inner) {
    background-color: #fff;
    background-clip: padding-box;
    border-radius: 2px;
    box-shadow: 0 3px 6px -4px rgba(0, 0, 0, 0.12), 0 6px 16px 0 rgba(0, 0, 0, 0.08),
      0 9px 28px 8px rgba(0, 0, 0, 0.05);
    box-shadow: 0 0 8px rgba(0, 0, 0, 0.15) \9;
  }

  :deep(.popover-range-picker-dropdown .ant-popover-inner-content) {
    color: rgba(0, 0, 0, 0.85);
  }
</style>

 TRangePicker

<!--TRangePicker-->
<template>
  <div class="range-picker" ref="rangePicker">
    <div class="quick-date date-packer-opt">
      <a-tabs :default-active-key='"common"'
              v-model:activeKey="activeKey"
              :centered="true"
              @change="changeActive"
              class='tabsClass'
              size="small">
        <a-tab-pane tab='常用' key='common'>
          <template v-for="(item, index) in commonOptData" :key="index">
            <div v-if="index % 2 === 0">
              <span class='common-opt'
                    style='margin-right: 8%'
                    @click='commonOptSelect(commonOptData[index])'
                    :class='{"common-opt-select": commonOptActive === commonOptData[index]}'>
                {{ commonOptData[index] }}
              </span>
              <span class='common-opt'
                    @click='commonOptSelect(commonOptData[index + 1])'
                    :class='{"common-opt-select": commonOptActive === commonOptData[index + 1]}'>
                {{ commonOptData[index + 1] }}
              </span>
            </div>
          </template>
        </a-tab-pane>
        <a-tab-pane tab='自定义' key='customize'>
          <!--     近n日     -->
          <div
            class='Customize-opt'
            :class='{"Customize-opt-select": customizeActive == "近n日"}'>
            近
            <span>
              <a-input
                size="small"
                type='text'
                placeholder='n'
                v-model:value='customizeDate[0]'
                @input="customizeChange('近n日')"
                style="display: inline-block; width: 45px;"
                class='Customize-opt-input' />
            </span>
            日
          </div>

          <!--     过去n日     -->
          <div
            class='Customize-opt'
            :class='{"Customize-opt-select": customizeActive == "过去n日"}'>
            过去
            <span>
              <a-input
                no-border
                size="small"
                type='text'
                placeholder='n'
                v-model:value='customizeDate[1]'
                @input="customizeChange('过去n日')"
                style="display: inline-block; width: 45px;"
                class='Customize-opt-input' />
            </span>
            日
          </div>

          <!--     过去n-m日     -->
          <div
            class='Customize-opt'
            :class='{"Customize-opt-select": customizeActive == "过去n-m日"}'>
            过去
            <span>
              <a-input
                no-border
                size="small"
                type='text'
                placeholder='n'
                v-model:value='customizeDate[2]'
                @input="customizeChange('过去n-m日')"
                style="display: inline-block; width: 45px;"
                class='Customize-opt-input' />
            </span>
            -
            <span>
              <a-input
                no-border
                size="small"
                type='text'
                placeholder='m'
                v-model:value='customizeDate[3]'
                @input="customizeChange('过去n-m日')"
                style="display: inline-block; width: 45px;"
                class='Customize-opt-input' />
            </span>
            日
          </div>

          <!--     近n月     -->
          <div
            class='Customize-opt'
            :class='{"Customize-opt-select": customizeActive == "近n月"}'>
            近
            <span>
              <a-input
                no-border
                size="small"
                type='text'
                placeholder='n'
                v-model:value='customizeDate[4]'
                @input="customizeChange('近n月')"
                style="display: inline-block; width: 45px;"
                class='Customize-opt-input' />
            </span>
            月
          </div>

          <!--     过去n月     -->
          <div
            class='Customize-opt'
            :class='{"Customize-opt-select": customizeActive == "过去n月"}'>
            过去
            <span>
              <a-input
                no-border
                size="small"
                type='text'
                placeholder='n'
                v-model:value='customizeDate[5]'
                @input="customizeChange('过去n月')"
                style="display: inline-block; width: 45px;"
                class='Customize-opt-input' />
            </span>
            月
          </div>

          <!--     近n年     -->
          <div
            class='Customize-opt'
            :class='{"Customize-opt-select": customizeActive == "近n年"}'>
            近
            <span>
            <a-input
              no-border
              size="small"
              type='text'
              placeholder='n'
              v-model:value='customizeDate[6]'
              @input="customizeChange('近n年')"
              style="display: inline-block; width: 45px;"
              class='Customize-opt-input' />
          </span>
            年
          </div>

          <!--     过去n年     -->
          <div
            class='Customize-opt'
            :class='{"Customize-opt-select": customizeActive == "过去n年"}'>
            过去
            <span>
              <a-input
                no-border
                size="small"
                type='text'
                placeholder='n'
                v-model:value='customizeDate[7]'
                @input="customizeChange('过去n年')"
                style="display: inline-block; width: 45px;"
                class='Customize-opt-input' />
            </span>
            年
          </div>
        </a-tab-pane>
      </a-tabs>
    </div>
    <div style="display: inline-flex; border: 1px; margin-top: 5px; height: 270px">
      <n-config-provider :locale="zhCN" :date-locale="dateZhCN">
        <n-date-picker
          dropdownClassName="range-picker-dropdown"
          panel
          type="daterange"
          :actions="null"
          :is-date-disabled="disabledDate"
          :first-day-of-week="6"
          @update:value="changeDate"
          :getPopupContainer="(node) => node.parentNode || document.body"
          v-model:value="dateValue" />
      </n-config-provider>

    </div>
  </div>
</template>

<script>
import { NDatePicker, NConfigProvider, zhCN, dateZhCN } from 'naive-ui';
import { defineComponent, ref, onMounted } from 'vue';
import dayjs from 'dayjs';
import { message } from "ant-design-vue";
import locale from "ant-design-vue/es/date-picker/locale/zh_CN";

export default defineComponent({
  name: "TRangePicker",
  components: { NDatePicker, NConfigProvider },

  props: {
    // 左侧常用快捷数据
    propDynamic: {
      type: String,
      default: ''
    },

    // 快捷日期类型:常用|自定义
    propDynamicType: {
      type: String,
      default: 'common'
    },

    // 自定义快捷日期高亮
    propCusDateActive: {
      type: String,
      default: ''
    },

    // 日期值
    propRangeDate: {
      type: Array,
      default: () => {
        return [dayjs(dayjs().startOf('day').subtract(6, 'days').format('YYYY-MM-DD')),
          dayjs(dayjs().format('YYYY-MM-DD'))]
      }
    }
  },

  setup(props, context) {
    // 常用按钮文本数据
    const commonOptData = ref()
    commonOptData.value = [
      '今日', '昨日',
      '本周', '上周',
      '本月', '上月',
      '今年', '去年',
      '近30日', '过去30日',
      '近90日', '过去90日',
    ]

    // 不可选择日期
    const disabledDate = (ts) => {
      // 无法选择今天之后的天数
      return ts && ts > dayjs().endOf('day');
    };

    // 日期
    const dateValue = ref()
    let tempDate = JSON.parse(JSON.stringify(props.propRangeDate))
    if (tempDate[0] !== '' && tempDate[1] !== '') {
      dateValue.value = [dayjs(tempDate[0]).valueOf(), dayjs(tempDate[1]).valueOf()]
    } else {
      dateValue.value = null
    }

    //
    const activeKey = ref('common')
    activeKey.value = JSON.parse(JSON.stringify(props.propDynamicType))

    // 常用快捷日期高亮文本
    const commonOptActive = ref('')

    // 自定义日期
    const customizeActive = ref('')
    const customizeDate = ref(['', '', '', '', '', '', '', ''])
    customizeActive.value = JSON.parse(JSON.stringify(props.propCusDateActive))

    // 常用动态日期文本
    const dynamicDate = ref('')
    let tempDynamic = JSON.parse(JSON.stringify(props.propDynamic))
    dynamicDate.value = tempDynamic

    // 根据不同的类型回显
    if (activeKey.value === 'common') {
      // 根据传入的常用快捷日期回显
      switchByDynamicText(dynamicDate.value)
    } else {
      switchByDynamicTextInCus(dynamicDate.value, customizeActive.value)
    }


    // 切换面板的回调
    function changeActive(value) {
      activeKey.value = value
    }

    // 常用快捷日期按钮选择
    function commonOptSelect(value) {
      commonOptActive.value = value
      // 根据选择的快捷常用按钮计算具体的日期值
      calculationDateByCommonOpt(value)
      // 将自定义日期输入框的值置空
      customizeDate.value = ['', '', '', '', '', '', '', '']
      // 将自定义日期高亮文本置空
      customizeActive.value = ''
    }


    // 根据选择的快捷常用按钮计算具体的日期值
    function calculationDateByCommonOpt (value) {
      const startDate = ref()
      const endDate = ref()
      switch (value) {
        case '今日':
          startDate.value = dayjs().format('YYYY-MM-DD')
          endDate.value = dayjs().format('YYYY-MM-DD')
          dynamicDate.value = 'near,day,1'
          break
        case '昨日':
          startDate.value = dayjs().startOf('day').subtract(1, 'days').format('YYYY-MM-DD')
          endDate.value = dayjs().startOf('day').subtract(1, 'days').format('YYYY-MM-DD')
          dynamicDate.value = 'past,day,1'
          break
        case '本周':
          startDate.value = dayjs().startOf('week').format('YYYY-MM-DD')
          endDate.value = dayjs().format('YYYY-MM-DD')
          dynamicDate.value = 'this week'
          break
        case '上周':
          startDate.value = dayjs().startOf('week').subtract(1, 'weeks').format('YYYY-MM-DD')
          endDate.value = dayjs().endOf('week').subtract(1, 'weeks').format('YYYY-MM-DD')
          dynamicDate.value = 'last week'
          break
        case '本月':
          startDate.value = dayjs().startOf('month').format('YYYY-MM-DD')
          endDate.value = dayjs().format('YYYY-MM-DD')
          dynamicDate.value = 'near,month,1'
          break
        case '上月':
          startDate.value = dayjs().startOf('month').subtract(1, 'months').format('YYYY-MM-DD')
          endDate.value = dayjs().subtract(1, 'months').endOf('month').format('YYYY-MM-DD')
          dynamicDate.value = 'past,month,1'
          break
        case '今年':
          startDate.value = dayjs().startOf('year').format('YYYY-MM-DD')
          endDate.value = dayjs().format('YYYY-MM-DD')
          dynamicDate.value = 'near,year,1'
          break
        case '去年':
          startDate.value = dayjs().startOf('year').subtract(1, 'years').format('YYYY-MM-DD')
          endDate.value = dayjs().startOf('year').subtract(1, 'days').format('YYYY-MM-DD')
          dynamicDate.value = 'past,year,1'
          break
        case '近30日':
          startDate.value = dayjs().startOf('day').subtract(29, 'days').format('YYYY-MM-DD')
          endDate.value = dayjs().format('YYYY-MM-DD')
          dynamicDate.value = 'near,day,30'
          break
        case '过去30日':
          startDate.value = dayjs().startOf('day').subtract(30, 'days').format('YYYY-MM-DD')
          endDate.value = dayjs().startOf('day').subtract(1, 'days').format('YYYY-MM-DD')
          dynamicDate.value = 'past,day,30'
          break
        case '近90日':
          startDate.value = dayjs().startOf('day').subtract(89, 'days').format('YYYY-MM-DD')
          endDate.value = dayjs().format('YYYY-MM-DD')
          dynamicDate.value = 'near,day,90'
          break
        case '过去90日':
          startDate.value = dayjs().startOf('day').subtract(90, 'days').format('YYYY-MM-DD')
          endDate.value = dayjs().startOf('day').subtract(1, 'days').format('YYYY-MM-DD')
          dynamicDate.value = 'past,day,90'
          break
        default:
          break
      }

      // 右侧日期选择器的绑定值
      dateValue.value = [dayjs(startDate.value).valueOf(), dayjs(endDate.value).valueOf()]

      // 传回父组件响应
      let param = {
        rangeDate: [startDate.value, endDate.value],
        dynamic: dynamicDate.value,
        dynamicType: activeKey.value
      }
      context.emit('changeDate', param, true)
    }


    // 根据传入的常用快捷日期回显
    function switchByDynamicText (value) {
      switch (value) {
        case 'near,day,1':
          commonOptActive.value = '今日'
          break
        case 'past,day,1':
          commonOptActive.value = '昨日'
          break
        case 'this week':
          commonOptActive.value = '本周'
          break
        case 'last week':
          commonOptActive.value = '上周'
          break
        case 'near,month,1':
          commonOptActive.value = '本月'
          break
        case 'past,month,1':
          commonOptActive.value = '上月'
          break
        case 'near,year,1':
          commonOptActive.value = '今年'
          break
        case 'past,year,1':
          commonOptActive.value = '去年'
          break
        case 'near,day,30':
          commonOptActive.value = '近30日'
          break
        case 'past,day,30':
          commonOptActive.value = '过去30日'
          break
        case 'near,day,90':
          commonOptActive.value = '近90日'
          break
        case 'past,day,90':
          commonOptActive.value = '过去90日'
          break
        default:
          break
      }
    }


    // 根据传入的自定义快捷日期回显
    function customizeChange (inputVal) {
      // 校验规则
      let test = ''
      const startDate = ref()
      const endDate = ref()
      // 输入内容
      let tempDateNum = ''
      switch (inputVal) {
        case '近n日':
          tempDateNum = customizeDate.value[0]
          // 校验是否是正整数
          test = tempDateNum.replace(/[^\d]/g, '')
          if (test !== '' && test === tempDateNum && parseInt(tempDateNum) > 0) {
            startDate.value = dayjs().startOf('day').subtract(parseInt(tempDateNum) - 1, 'days').format('YYYY-MM-DD')
            endDate.value = dayjs().format('YYYY-MM-DD')
            dynamicDate.value = 'near,day,' + tempDateNum
            customizeDate.value = [tempDateNum, '', '', '', '', '', '', '']
          } else {
            if (tempDateNum !== '') {
              customizeDate.value[0] = ''
              message.warning('请输入正整数!')
            }
          }
          break
        case '过去n日':
          tempDateNum = customizeDate.value[1]
          // 校验是否是正整数
          test = tempDateNum.replace(/[^\d]/g, '')
          if (test !== '' && test === tempDateNum && parseInt(tempDateNum) > 0) {
            startDate.value = dayjs().startOf('day').subtract(parseInt(tempDateNum), 'days').format('YYYY-MM-DD')
            endDate.value = dayjs().startOf('day').subtract(1, 'days').format('YYYY-MM-DD')
            dynamicDate.value = 'past,day,' + tempDateNum
            customizeDate.value = ['', tempDateNum, '', '', '', '', '', '']
          } else {
            if (tempDateNum !== '') {
              customizeDate.value[1] = ''
              message.warning('请输入正整数!')
            }
          }
          break
        case '过去n-m日':
          let tempDateNum3 = customizeDate.value[2]
          let tempDateNum4 = customizeDate.value[3]
          // 校验是否是正整数
          let test3 = tempDateNum3.replace(/[^\d]/g, '')
          let test4 = tempDateNum4.replace(/[^\d]/g, '')
          if ((test3 !== '' && test3 === tempDateNum3 && parseInt(tempDateNum3) > 0) || (test4 !== '' && test4 === tempDateNum4 && parseInt(tempDateNum3) > 0)) {
            if (tempDateNum3 !== '' && tempDateNum4 !== '' && parseInt(tempDateNum3) > 0 && parseInt(tempDateNum3) > 0) {
              let min = Math.min(parseInt(tempDateNum3), parseInt(tempDateNum4))
              let max = Math.max(parseInt(tempDateNum3), parseInt(tempDateNum4))
              startDate.value = dayjs().startOf('day').subtract(max, 'days').format('YYYY-MM-DD')
              endDate.value = dayjs().startOf('day').subtract(min, 'days').format('YYYY-MM-DD')
              customizeActive.value = inputVal
              dynamicDate.value = 'past,day,' + min + ',' + max
              customizeDate.value = ['', '', tempDateNum3, tempDateNum4, '', '', '', '']
              // 将常用日期高亮文本置空
              commonOptActive.value = ''

              // 右侧日期选择器的绑定值
              dateValue.value = [dayjs(startDate.value).valueOf(), dayjs(endDate.value).valueOf()]

              // 传回父组件响应
              let param = {
                rangeDate: [startDate.value, endDate.value],
                dynamic: dynamicDate.value,
                dynamicType: activeKey.value
              }
              context.emit('changeDate', param, false)
            }
          } else {
            if (tempDateNum3 !== '' || tempDateNum4 !== '') {
              customizeDate.value[2] = test3 === '' || test3 !== tempDateNum3 || parseInt(tempDateNum3) <= 0 ? '' : customizeDate.value[2]
              customizeDate.value[3] = test4 === '' || test4 !== tempDateNum4 || parseInt(tempDateNum4) <= 0 ? '' : customizeDate.value[3]
              message.warning('请输入正整数!')
            }
          }
          break
        case '近n月':
          tempDateNum = customizeDate.value[4]
          // 校验是否是正整数
          test = tempDateNum.replace(/[^\d]/g, '')
          if (test !== '' && test === tempDateNum && parseInt(tempDateNum) > 0) {
            startDate.value = dayjs().startOf('month').subtract(parseInt(customizeDate.value[4]) - 1, 'months').format('YYYY-MM-DD')
            endDate.value = dayjs().format('YYYY-MM-DD')
            dynamicDate.value = 'near,month,' + tempDateNum
            customizeDate.value = ['', '', '', '', tempDateNum, '', '', '']
          } else {
            if (tempDateNum !== '') {
              customizeDate.value[4] = ''
              message.warning('请输入正整数!')
            }
          }
          break
        case '过去n月':
          tempDateNum = customizeDate.value[5]
          // 校验是否是正整数
          test = tempDateNum.replace(/[^\d]/g, '')
          if (test !== '' && test === tempDateNum && parseInt(tempDateNum) > 0) {
            startDate.value = dayjs().startOf('month').subtract(parseInt(tempDateNum), 'months').format('YYYY-MM-DD')
            endDate.value = dayjs().startOf('month').subtract(0, 'months').subtract(1, 'days').format('YYYY-MM-DD')
            dynamicDate.value = 'past,month,' + tempDateNum
            customizeDate.value = ['', '', '', '', '', tempDateNum, '', '']
          } else {
            if (tempDateNum !== '') {
              customizeDate.value[5] = ''
              message.warning('请输入正整数!')
            }
          }
          break
        case '近n年':
          tempDateNum = customizeDate.value[6]
          // 校验是否是正整数
          test = tempDateNum.replace(/[^\d]/g, '')
          if (test !== '' && test === tempDateNum && parseInt(tempDateNum) > 0) {
            startDate.value = dayjs().startOf('year').subtract(parseInt(tempDateNum) - 1, 'years').format('YYYY-MM-DD')
            endDate.value = dayjs().format('YYYY-MM-DD')
            dynamicDate.value = 'near,year,' + tempDateNum
            customizeDate.value = ['', '', '', '', '', '', tempDateNum, '']
          } else {
            if (tempDateNum !== '') {
              customizeDate.value[6] = ''
              message.warning('请输入正整数!')
            }
          }
          break
        case '过去n年':
          tempDateNum = customizeDate.value[7]
          // 校验是否是正整数
          test = tempDateNum.replace(/[^\d]/g, '')
          if (test !== '' && test === tempDateNum && parseInt(tempDateNum) > 0) {
            startDate.value = dayjs().startOf('year').subtract(parseInt(tempDateNum), 'years').format('YYYY-MM-DD')
            endDate.value = dayjs().startOf('year').subtract(0, 'years').subtract(1, 'days').format('YYYY-MM-DD')
            dynamicDate.value = 'past,year,' + tempDateNum
            customizeDate.value = ['', '', '', '', '', '', '', tempDateNum]
          } else {
            if (tempDateNum !== '') {
              customizeDate.value[7] = ''
              message.warning('请输入正整数!')
            }
          }
          break
        default:
          break
      }

      // 校验通过才能继续
      if (test !== '' && test === tempDateNum && inputVal !== '过去n-m日') {
        // 右侧日期选择器的绑定值
        dateValue.value = [dayjs(startDate.value).valueOf(), dayjs(endDate.value).valueOf()]
        customizeActive.value = inputVal
        // 将常用日期高亮文本置空
        commonOptActive.value = ''

        // 传回父组件响应
        let param = {
          rangeDate: [startDate.value, endDate.value],
          dynamic: dynamicDate.value,
          dynamicType: activeKey.value,
          customizeActive: customizeActive.value
        }
        context.emit('changeDate', param, false)
      }
    }

    // 根据传入的自定义快捷日期回显
    function switchByDynamicTextInCus (tempDate, activeVal) {
      let tempText = tempDate.split(',')
      switch (activeVal) {
        case '近n日':
          dynamicDate.value = 'near,day,' + tempText[2]
          customizeDate.value = [tempText[2], '', '', '', '', '', '', '']
          break
        case '过去n日':
          dynamicDate.value = 'past,day,' + tempText[2]
          customizeDate.value = ['', tempText[2], '', '', '', '', '', '']
          break
        case '过去n-m日':
          dynamicDate.value = 'past,day,' + tempText[2] + ',' + tempText[3]
          customizeDate.value = ['', '', tempText[3], tempText[2], '', '', '', '']
          break
        case '近n月':
          dynamicDate.value = 'near,month,' + tempText[2]
          customizeDate.value = ['', '', '', '', tempText[2], '', '', '']
          break
        case '过去n月':
          dynamicDate.value = 'past,month,' + tempText[2]
          customizeDate.value = ['', '', '', '', '', tempText[2], '', '']
          break
        case '近n年':
          dynamicDate.value = 'near,year,' + tempText[2]
          customizeDate.value = ['', '', '', '', '', '', tempText[2], '']
          break
        case '过去n年':
          dynamicDate.value = 'past,year,' + tempText[2]
          customizeDate.value = ['', '', '', '', '', '', '', tempText[2]]
          break
        default:
          break
      }
    }


    // 右侧的日期选择器日期发生变化的回调
    function changeDate (date, dateString) {
      // 常用快捷日期按钮选择置空
      commonOptActive.value = ''
      dynamicDate.value = ''
      // console.log(date)
      // console.log(dateString)

      // 传回父组件响应
      let param = {
        rangeDate: dateString,
        dynamic: dynamicDate.value
      }
      context.emit('changeDate', param, false)
    }

    function panelChange(value) {
      console.log(value)
    }

    onMounted(() => {

    })

    return {
      zhCN,
      dateZhCN,
      locale,
      dateValue,
      // 不可选择日期
      disabledDate,
      // 日期发生变化的回调
      changeDate,
      panelChange,

      // 标签页绑定值
      activeKey,
      changeActive,

      // 当前选择的常用按钮
      commonOptActive,
      // 常用快捷按钮文本
      commonOptData,
      // 快捷常用按钮日期选择
      commonOptSelect,

      // 自定义日期
      customizeChange,
      customizeActive,
      customizeDate
    }
  }
});
</script>

<style scoped>
/*--------------------------------左侧快捷选择日期模块样式-----------------------------*/
.quick-date {
  width: 200px;
  height: 270px;
  display: inline-flex;
  position: relative;
}

.date-packer-opt {
  background: #ecf3f8;
  border-right: 1px solid #e4eaf1;
  overflow: hidden;
  padding: 5px;
  -webkit-box-sizing: border-box;
  box-sizing: border-box;
  z-index: 0;
  /*box-shadow: 0px 0px 18px 0 rgba(0,0,0,.14);*/
  position: relative;
  top: 0px;
}

/*------------------------标签页 样式 开始------------------------*/
.tabsClass {
  background-color: #ECF3F8;
  padding-left:10px;
  font-size: 13px;
  width: 100%;
  /*margin-top: 5px;*/
}

:deep(.tabsClass .ant-tabs-nav) {
  position: relative;
  display: flex;
  flex: none;
  align-items: center;
  height: 30px !important;
  margin: 0 0 6px 0 !important;
}

/*-----------标签页文本------------*/
:deep(.tabsClass .ant-tabs-tab.ant-tabs-tab-active .ant-tabs-tab-btn) {
  color: #597EF7 !important;
  text-shadow: 0 0 0.25px currentcolor;
  /*padding: 0 17px;*/
}

/*-----------标签页激活的下杠线------------*/
:deep(.tabsClass .ant-tabs-ink-bar) {
  position: absolute;
  background: #597EF7 !important;
  pointer-events: none;
}

/*-----------常用标签页内的按钮样式------------*/
.common-opt {
  display: inline-block;
  width: 43%;
  padding: 3px 0;
  margin: 6px 0;
  cursor: pointer;
  font-size: 12px;
  background-color: #fff;
  text-align: center;
  border-radius: 20px;
}

.common-opt:hover{
  background-color: #688ff4;
  color: #FFFFFF;
}

.common-opt-select {
  display: inline-block;
  width: 43%;
  padding: 3px 0;
  margin: 6px 0;
  cursor: pointer;
  font-size: 12px;
  background-color: #688FF4;
  color: white;
  text-align: center;
  border-radius: 20px;
}
/*------------------------常用标签页的按钮 样式 结束------------------------*/
/*------------------------自定义标签页的按钮 样式 开始------------------------*/
.Customize-opt {
  background-color: #fff;
  height: 25px;
  width: 179px;
  text-align:center;
  border-radius:20px;
  margin-bottom: 8px
}

.Customize-opt-select {
  background-color: #fff;
  height: 25px;
  width: 179px;
  text-align: center;
  border-radius: 20px;
  margin-bottom: 8px;
  border: 1px solid #688FF4;
}

/*-------输入框边框只留下bottom--------*/
.Customize-opt-input {
  display: inline-block;
  width: 45px;
  border-right: none;
  border-top: none;
  border-left: none;
}

/*-------自定义输入框文本居中显示--------*/
.ant-input {
  text-align: center;
}

/*-------自定义输入框提示语n居中显示--------*/
.ant-input:placeholder-shown {
  text-overflow: ellipsis;
  text-align: center;
}


/*-------------------------------右侧日期面板样式-----------------------------------*/
/*----------------当天日期的右上角圆点-------------------*/
:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date.n-date-panel-date--current .n-date-panel-date__sup) {
  position: absolute;
  top: 1px;
  right: 1px;
  content: "";
  height: 5px;
  width: 5px;
  border-radius: 50%;
  background-color: #688ff4;
}

:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date--excluded) {
  /*opacity: 0;*/
}

/*--------------选择择的开始、结束日期样式-----------------*/
:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date.n-date-panel-date--selected:not(.n-date-panel-date--excluded)::after) {
  background-color: #688ff4 !important;
}

:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date.n-date-panel-date--selected.n-date-panel-date--start:not(.n-date-panel-date--excluded)::after) {
  background-color: #688ff4 !important;
  margin-right: -1px;
}

:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date.n-date-panel-date--selected.n-date-panel-date--end:not(.n-date-panel-date--excluded)::after) {
  background-color: #688ff4 !important;
  margin-left: -1px;
}

/*-----------选择的开始日期左半圆----------*/
:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date--start:not(.n-date-panel-date--excluded)) {
  border-bottom-left-radius: 50%;
  border-top-left-radius: 50%;
}

/*-----------选择的结束日期右半圆----------*/
:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date--end:not(.n-date-panel-date--excluded)) {
  border-bottom-right-radius: 50%;
  border-top-right-radius: 50%;
}

/*-----------选择的非开始、结束日期的背景颜色----------*/
:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date.n-date-panel-date--covered:not(.n-date-panel-date--excluded)::before) {
  background-color: #688ff4 !important;
  margin-left: 7px;
  padding: 0 1px;
  width: 26px;
}

:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date.n-date-panel-date--start:not(.n-date-panel-date--excluded)::before) {
  background-color: #688ff4 !important;
  display: none;
}

:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date.n-date-panel-date--end:not(.n-date-panel-date--excluded)::before) {
  background-color: #688ff4 !important;
  display: none;
}

/*-----------选择的非开始、结束日期的文本颜色----------*/
:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date--covered:not(.n-date-panel-date--excluded)) {
  color: #ffffff;
}

/*-----------面板宽度----------*/
:deep(.n-date-panel .n-date-panel-calendar) {
  width: 210px;
}

/*-----------面板首行年月行样式----------*/
:deep(.n-date-panel .n-date-panel-month) {
  width: 195px;
  margin-bottom: 7px;
  font-size: 13px;
  margin-top: -5px;
}

:deep(.n-date-panel .n-date-panel-month .n-date-panel-month__month-year .n-date-panel-month__text) {
  padding: 0;
  font-size: 12px;
  color: #4C6072;
  font-family: sans-serif;
}

/*-----------面板星期行样式----------*/
:deep(.n-date-panel .n-date-panel-weekdays) {
  display: block;
  margin-bottom: 3px;
  border: unset;
}

:deep(.n-date-panel .n-date-panel-weekdays .n-date-panel-weekdays__day) {
  width: 26px;
  display: inline-flex;
  color: #c5ced8;
  font-size: 13px;
}

/*-----------面板具体日期样式----------*/
:deep(.n-date-panel .n-date-panel-dates) {
  margin: auto;
  display: flex;
  align-items: center;
  justify-items: center;
  flex-wrap: wrap;
  width: 200px;
  height: 200px;
}

:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date) {
  width: 26px;
  display: inline-table;
}

/*-----------两个面板之间的竖线隐藏----------*/
:deep(.n-date-panel .n-date-panel__vertical-divider) {
  display: none;
}

/*-----------不可选日期样式----------*/
:deep(.n-date-panel .n-date-panel-dates .n-date-panel-date.n-date-panel-date--disabled) {
  cursor: not-allowed;
  color: #C5CED8;
  background-color: #ECF3F8;
}
</style>

 调用方式

<TDatePicker
      :input-span-font-size="'14px'"
      :prop-dynamic="dynamic"
      :prop-cus-date-active="customizeActive"
      :prop-dynamic-type="dynamicType"
      :prop-range-date="rangeDate"
      @changeDate="changeDate"
 />

<!-- js部分-->

// 初始快捷日期,包含常用和自定义,例如 'near,day,30',表示近30日
const dynamic = ref('');

// 快捷日期类型:常用common|自定义customize
const dynamicType = ref('common');

// 自定义快捷日期高亮文本,例如 '近n日'
const customizeActive = ref('');

// 日期,初始为近7日
const rangeDate = ref([
  dayjs().startOf('day').subtract(6, 'days').format('YYYY-MM-DD'),
  dayjs().format('YYYY-MM-DD'),
]);

// 选择日期后的回显
function changeDate(tempVal) {
  dynamic.value = tempVal.dynamic;
  customizeActive.value = tempVal.customizeActive ? tempVal.customizeActive : '';
  dynamicType.value = tempVal.dynamicType;
  rangeDate.value = tempVal.rangeDate;
}

参数列表

参数名类型必传说明
propRangeDatearray✔️ 日期,例如['2023-01-01', '2023-03-01']
propDynamicTypeString✔️ 快捷日期类型:常用common|自定义customize
propDynamicString✔️ 快捷日期数据,例如 'near,day,30',表示近30日
propCusDateActiveString✔️自定义快捷日期高亮,例如 '近n日',用于高亮样式显示

使用到的组件:除了日期面板外,都是使用的ant-design-vue中的组件;日期面板使用的是Naive UI

组件图示备注
Popover 气泡卡片如上示的效果图的悬浮显示效果
Tabs 标签页

n-date-picker 日期选择框如上效果图右侧的日期选择Naive UI提供了只使用面板的属性;更改部分样式
Input 输入框设置为只读输入框,且将样式处理为只有下边框的样式; 自定义日期里的输入框依旧是可输入的,且将样式处理为只有下边框的样式

常用快捷日期文本对应

显示文本传给后台的值
今日near,day,1
昨日past,day,1
本周this week
上周last week
本月near,month,1
上月past,month,1
今年near,year,1
去年past,year,1
近30日near,day,30
过去30日past,day,30
近90日near,day,90
过去90日past,day,90

自定义日期

  • 过去n-m日:past,day,min,max (min、max分别为输入的两个数值,小的在前,大的在后)
  • 其他的与常用类似,比如 过去n日,也就是 past,day,n      ;   近n月,也就是  near,month,n  (n为输入的数组)
  • 这里的所有输入框均设置了校验,只能输入正整数
  • 25
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

马可家的菠萝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值