input输入日期时间,自动格式化组件/工具
组件/工具使用说明
- 只能输入日期时间,不能选择
- 日期时间格式 yyyy-MM-dd HH:mm:ss
- 基于VUE框架
- 输入效果见下图
组件/工具解决的痛点
- 组件库日期时间组件,选日期时间很花时间
- 例如想快速得到 2021-12-14 20:10:22,需要先选日期,然后再选时间
- 可以快速输入目标日期和时间,不用关注日期和时间分隔符 - : 空格
组件/工具需要实现的功能
- 只允许输入数字
- 限制输入日期范围
- 按键删除时自动补入合法数字
- 输入数字自动删除后一位数字
- 输入和删除时光标位置移动
- 检测复制粘贴到输入框的内容
- 输入完成时,按回车键自动失焦
- 处理输入的不符合规则的日期
功能代码实现
<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;
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: ':'
}
export function formatStringDate(str, selectionStart) {
if (!/^[0-9-:\s]{18,20}$/.test(str) || str.length < 18) {
return {newDate: dateToStr(new Date()), startB: 19};
}
let numStr = str.replace(/-|:|s|\s/g, '');
if (numStr.length < 13) {
return {newDate: dateToStr(new Date()), startB: 19};
}
const num = /^[0-9]$/;
let startB = selectionStart;
selectionStart = selectionStart - 1;
let arrDate = str.split('');
if (arrDate.length < 19) {
if (del[startB]) {
arrDate.splice(startB, 0, del[startB])
} else {
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);
}
}
let str1 = arrDate.join('').replace(/-|:|s|\s/g, '');
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均可使用