input输入日期时间,自动格式化组件/工具

27 篇文章 0 订阅

input输入日期时间,自动格式化组件/工具

组件/工具使用说明

  • 只能输入日期时间,不能选择
  • 日期时间格式 yyyy-MM-dd HH:mm:ss
  • 基于VUE框架
  • 输入效果见下图
    在这里插入图片描述

组件/工具解决的痛点

  • 组件库日期时间组件,选日期时间很花时间
    • 例如想快速得到 2021-12-14 20:10:22,需要先选日期,然后再选时间
  • 可以快速输入目标日期和时间,不用关注日期和时间分隔符 - : 空格

组件/工具需要实现的功能

  • 只允许输入数字
  • 限制输入日期范围
  • 按键删除时自动补入合法数字
  • 输入数字自动删除后一位数字
  • 输入和删除时光标位置移动
  • 检测复制粘贴到输入框的内容
  • 输入完成时,按回车键自动失焦
  • 处理输入的不符合规则的日期

功能代码实现

  • HTML
<input 
    v-model="inOutTime"
    @focus="onFocus"
    @blur="onBlur"
    @input="inputDateTime($event)"
    @keydown="onKeydown"
    ref="inputDate"
    class="inputDate"
    :class="[`xxxx-${size}`]" />
<style scoped>
.inputDate {
  width: 100%;
  height: 40px;
  line-height: 40px;
  padding-left: 10px;
  border: none;
  outline: none;
  border-radius: 4px;
  border: 1px solid #dcdfe6;
}
.inputDate:focus {
  border-color: #409eff;
}
.xxxx-default {
  height: 40px;
  line-height: 40px;
}
.xxxx-medium {
  height: 36px;
  line-height: 36px;
}
.xxxx-small {
  height: 32px;
  line-height: 32px;
}
.xxxx-mini {
  height: 28px;
  line-height: 28px;
}
</style>
  • 实时获取输入框值和光标位置
    function inputDateTime(ev) {
      // 输入框光标起始位置
      let selectionStart = this.$refs.inputDate.selectionStart;
      let selectionEnd = this.$refs.inputDate.selectionEnd;
      // 输入框的value
      console.log(ev.target.value);
    }
  
  • 处理输入框的值和光标位置的功能函数
// 格式化日期对象
function dateToStr(datetime) {
  if (!datetime) {
    return '';
  }
  let year = datetime.getFullYear();
  let month = datetime.getMonth() + 1;
  let date = datetime.getDate();
  let hour = datetime.getHours();
  let minutes = datetime.getMinutes();
  let second = datetime.getSeconds();
  if (month < 10) {
    month = "0" + month;
  }
  if (date < 10) {
    date = "0" + date;
  }
  if (hour < 10) {
    hour = "0" + hour;
  }
  if (minutes < 10) {
    minutes = "0" + minutes;
  }
  if (second < 10) {
    second = "0" + second;
  }
  let time = year + "-" + month + "-" + date + " " + hour + ":" + minutes + ":" + second;
  return time;
}
// 定义分割符位置对象
const del = {
  4: '-',
  7: '-',
  10: ' ',
  13: ':',
  16: ':'
}
/**
 * @param {*} str 日期字符串 2021-02-23 22:00:00
 * @param {*} selectionStart 输入光标起始位置 从1开始
 */
export function formatStringDate(str, selectionStart) {
  // 粘贴时间校验
  if (!/^[0-9-:\s]{18,20}$/.test(str) || str.length < 18) {
    return {newDate: dateToStr(new Date()), startB: 19};
  }
  // 校验纯数字是否小于13位
  let numStr = str.replace(/-|:|s|\s/g, '');
  if (numStr.length < 13) {
    return {newDate: dateToStr(new Date()), startB: 19};
  }
  // 校验当前输入的是不是数字
  const num = /^[0-9]$/;
  // 字符查询从0开始
  let startB = selectionStart;
  selectionStart = selectionStart - 1;
  let arrDate = str.split('');
  // 按键删除
  if (arrDate.length < 19) {
    if (del[startB]) {
      // 删除的是分隔符
      arrDate.splice(startB, 0, del[startB])
    } else {
      // 如果删除的是数字,补位1
      // 第一位补2 第二位补0
      let obj = {
        0: '2',
        1: '0',
        5: '0'
      }
      arrDate.splice(startB, 0, obj[startB] || '1')
    }
    str = arrDate.join('');
  } else {
    // 按键输入
    // 拿到输入的字符
    const str2 = str.slice(selectionStart, selectionStart + 1);
    if (!num.test(str2)) {
      // 删除非法字符
      arrDate.splice(selectionStart, 1);
      return {newDate: arrDate.join(''), startB};
    }
    // 输入的是数字,删除当前位置下一位数字,遇到- : 空格,往后移动一位
    const str3 = str.slice(selectionStart + 1, selectionStart + 2);
    const reg1 = /^(-|:|s|\s)$/;
    if (reg1.test(str3)) {
      // 下一位非数字,往后移动一位进行删除
      arrDate.splice(selectionStart + 2, 1);
      // 光标往后移动一位
      startB++;
    }
    // 下一位是数字
    if (num.test(str3)) {
      arrDate.splice(selectionStart + 1, 1);
    }
  }
  // 去除分隔符('2022-02-23 20:20:20')及空格
  let str1 = arrDate.join('').replace(/-|:|s|\s/g, '');
  // 年月日校验 2000 ~ 2099
  const regDate = /^20([0-9][0-9])-(0[1-9]|1[0-2])-([0-2][0-9]|3[01])$/;
  const regTime = /^(0[0-9]|1[0-9]|2[0-3]):[0-5][0-9]:[0-5][0-9]/;
  // 年月日
  let ymd = '';
  if (str1.length >= 8) {
    ymd = (str1).match(/(\d{4})(\d{2})(\d{2})/).filter((item, index) => index > 0).join('-');
  }
  if (!regDate.test(ymd)) {
    // 删除非法字符,返回原值
    const reArr = str.split('');
    reArr.splice(selectionStart, 1);
    return {newDate: reArr.join(''), startB};
  }
  // 时分秒
  let hmsStr = (str1).slice(8), hms;
  if (hmsStr.length >= 6) {
    hms = hmsStr.match(/(\d{2})(\d{2})(\d{2})/).filter((item, index) => index > 0).join(':');
  }
  if (!regTime.test(hms)) {
    // 删除非法字符,返回原值
    const reArr = str.split('');
    reArr.splice(selectionStart, 1);
    return {newDate: reArr.join(''), startB};
  }
  return {newDate: `${ymd} ${hms}`, startB};
}
  • 日期时间合法校验
function strDateTime(str) {
  var reg = /^(\d{4})(-|\/)(\d{2})\2(\d{2}) (\d{2}):(\d{2}):(\d{2})$/;
  var r = str.match(reg);
  if (r == null) return false;
  var d = new Date(r[1], r[3] - 1, r[4], r[5], r[6], r[7]);
  return (d.getFullYear() === Number(r[1]) && (d.getMonth() + 1) === Number(r[3]) && d.getDate() === Number(r[4]) && d.getHours() === Number(r[5]) && d.getMinutes() === Number(r[6]) && d.getSeconds() === Number(r[7]));
}
  • 设置光标位置
// 两个参数必传
this.$refs.inputDate.setSelectionRange(selectionStart, selectionStart);
  • 完整代码
<template>
  <div>
    <input 
          v-model="inOutTime"
          @focus="onFocus"
          @blur="onBlur"
          @input="inputDateTime($event)"
          @keydown="onKeydown"
          ref="inputDate"
          class="inputDate"
          :class="[`xxxx-${size}`]" />
  </div>
</template>
<script>
import { formatStringDate } from "utils";
import { dateToStr } from "public";
export default {
  props: {
    initTime: {
      type: String,
      required: true,
      default: dateToStr(new Date())
    },
    size: {
      type: String,
      default: 'small'
    }
  },
  data() {
    return {
      inOutTime: this.initTime
    }
  },
  methods: {
    inputDateTime(ev) {
      let selectionStart = this.$refs.inputDate.selectionStart;
      const options = formatStringDate(ev.target.value, selectionStart);
      this.inOutTime = options.newDate;
      if (options.startB !== selectionStart) {
        selectionStart = options.startB;
      }
      this.$nextTick(() => {
        this.$refs.inputDate.setSelectionRange(selectionStart, selectionStart);
      })
    },
    onFocus() {
    },
    onBlur() {
      if (!this.strDateTime(this.inOutTime)) {
        this.inOutTime = dateToStr(new Date());
      }
    },
    onKeydown(e) {
      let keyCode = window.event ? e.keyCode : e.which;
      // 回车键->输入框失焦
      if (keyCode === 13) {
        this.$refs.inputDate.blur();
        if (!this.strDateTime(this.inOutTime)) {
          this.inOutTime = dateToStr(new Date());
        }
        this.$emit('getTime', this.inOutTime);
      }
    },
    strDateTime(str) {
			var reg = /^(\d{4})(-|\/)(\d{2})\2(\d{2}) (\d{2}):(\d{2}):(\d{2})$/;
      var r = str.match(reg);
			if (r == null) return false;
			var d = new Date(r[1], r[3] - 1, r[4], r[5], r[6], r[7]);
			return (d.getFullYear() === Number(r[1]) && (d.getMonth() + 1) === Number(r[3]) && d.getDate() === Number(r[4]) && d.getHours() === Number(r[5]) && d.getMinutes() === Number(r[6]) && d.getSeconds() === Number(r[7]));
		}
  }
}
</script>

补充说明

  • 若需要实时获取处理之后的日期时间,可以在组件上使用v-model,具体代码请参考vue文档
  • 组件基于VUE编写,核心方法formatStringDate不依赖框架,原生JS、react均可使用
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值