这篇文章主要介绍“vue3怎么开发detePicker日期选择组件”,在日常操作中,相信很多人在vue3怎么开发detePicker日期选择组件问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue3怎么开发detePicker日期选择组件”的疑惑有所帮助!接下来,请跟着小编一起来学习吧!
技术栈
vue3 + tailwindcss + ts
页面开发
html结构
<template>
<div>
<-- 这里放input框 -->
<div></div>
<-- 点击input弹出日历 -->
<div>
<-- 头部 -->
<div></div>
<-- 星期 -->
<div></div>
<-- 日历主体 -->
<div></div>
<-- 按钮组 -->
<div></div>
</div>
</div>
</template>
input框,
代码如下:
<div class="block w-full h-[32px] relative border rounded-md">
<input
class="placeholder:text-[#B2B2B2] block w-full h-full rounded-md py-2 pl-2 pr-9 text-sm focus:outline-none focus:ring-0"
disabled
placeholder="选择时间"
type="text"
maxlength="32"
/>
<div
class="absolute inset-y-0 right-0 flex items-center pr-2 cursor-pointer"
>
<svg class="w-4 h-4 fill-[#C2C2C2]">
<use xlink:href="https://www.cnuseful.com">
头部,
代码如下
<div class="flex justify-center">
<svg class="w-4 h-4 fill-[#C2C2C2] rotate-90 cursor-pointer">
<use xlink:href="https://www.cnuseful.com">
星期,
代码如下:
<div
class="grid grid-cols-7 auto-cols-[20px] auto-rows-[20px] text-xs mt-2 mx-[9px] justify-items-center gap-x-2.5"
>
<span>一</span>
<span>二</span>
<span>三</span>
<span>四</span>
<span>五</span>
<span>六</span>
<span>日</span>
</div>
日历主体,
代码如下:
<div
class="grid grid-cols-7 auto-cols-[20px] auto-rows-[20px] text-xs mt-2 mx-[9px] gap-y-2 gap-x-2.5"
>
<span
:class="[
'cursor-pointer w-full h-full flex justify-center items-center',
v.color,
{
'!bg-[#60C2CC] rounded-full !text-white':
actives.one === v.time || actives.two === v.time,
},
setBgColor(v.time),
]"
v-for="(v, i) in days"
:key="i"
>{{ v.day }}</span
>
</div>
这里主要是用了,v-bind动态属性,来显示不同状态下的日历格子的样式(比如选中某天)
按钮组,
代码如下:
<div class="flex justify-between items-center mt-2.5 mx-[9px]">
<span class="text-xs text-[#9C9C9C]">跳到今天</span>
<div
class="w-[50px] h-5 bg-[#60C2CC] rounded-lg text-white leading-5 text-center text-xs"
>
确定
</div>
</div>
整体样式如下:
逻辑开发
显示切换逻辑,主要是点击input,弹出日历
// 控制显示的变量
const isShow = ref(false)
// 点击按钮的时候显示
const openTimeSelect = () => {
isShow.value = true
}
// 点击确定,或者点击日历主体外的任何窗体的时候关闭,关闭的时候清空选中
const closeTimeSelect = () => {
isShow.value = false
actives.one = ''
actives.two = ''
}
定义变量,主要是定义日历用到的数据,和接口
// 本组件唯一定义的类型
interface DaysType {
day: number
color: string
time: string
}
const date = new Date() // 时间
const year = ref(0) // 年
const month = ref(0) // 月
const days = ref<DaysType[]>([]) // 需要循环渲染的日历主体数据
// 选中某天的数据,可以选中两天
const actives = reactive({
one: '',
two: '',
})
// 计算属性,把当前点击选中的日期转换成时间戳
const oneTimeNum = computed(() => new Date(actives.one).getTime())
const twoTimeNum = computed(() => new Date(actives.two).getTime())
为什么说,使用ts写的代码,但是只定义了一个接口,在vue3 + ts的开发中,我推荐的是使用类型推导的方式去写代码,有时候,你会发现,你写的ts类型多此一举。当然,你是ts艺术体操选手,那另说。
获取日历主体渲染的数据,逻辑如下: 具体代码如下:
const updateTime = () => {
days.value = []
year.value = date.getFullYear() // 获取当前年
month.value = date.getMonth() + 1 // 获取当前月份
const curDays = new Date(year.value, month.value, 0).getDate() // 当前月的天数
let curWeek = new Date(year.value, month.value - 1, 1).getDay() // 这个月第一天星期几
curWeek <= 0 ? (curWeek = 7) : curWeek // 如果值是0的话,那么本月第一天是周日
const preDays = new Date(year.value, month.value - 1, 0).getDate() // 上个月的天数
const preLastDay = curWeek - 1 // 获取上一个月有多少天
// 插入上一个月的日期
for (let i = 0; i < preLastDay; i++) {
days.value.unshift({
day: preDays - i,
color: 'text-[#CECECE]',
time: `${year.value}-${month.value - 1}-${preDays - i}`,
})
}
// 插入本月的日期
for (let i = 1; i <= curDays; i++) {
days.value.push({
day: i,
color: 'text-[#191919]',
time: `${year.value}-${month.value}-${i}`,
})
}
const lastPreDays = 42 - curDays - preLastDay
// 插入下个月的日期
for (let i = 1; i <= lastPreDays; i++) {
days.value.push({
day: i,
color: 'text-[#CECECE]',
time: `${year.value}-${month.value + 1}-${i}`,
})
}
}
日历主体分为三个部分,1、前一个月的天数,2、当前月的天数,3、下一个月的天数
通过date 内置对象,获取到:当前年,当前月,当前月有几天,当前月的第一天是星期几,上个月有多少天
通过 当前月第一天是星期几 减去 1 得到,上个月一共有几天要显示
获取上个月的天数 循环 上个月一共有几天,得到上个月具体的日期,比如说:
上个月有2天要显示,上个月一共有31天,那么本月第一天往前两个格子是上月的。
2次循环
第一次,31 - 0 得到31
第二次,31 - 1 得到30
如下图所示:
获取本月的天数 通过循环当前月的天数获取
获取下月的天数 一共42个格子,那么42 减去当前月天数,减去上个月天数,就是下个月要显示多少天,同样循环获取
上一月和下一月
// 上一月
const prevMonth = () => {
date.setMonth(date.getMonth() - 1)
updateTime()
}
// 下一月
const nextMonth = () => {
date.setMonth(date.getMonth() + 1)
updateTime()
}
主要是通过 dete 对象的 setMonth 方法重置月份,月份重置后,调用获日历主体方法就可获取到上一个月和下一个月的日历主体
选择两个日期选中,逻辑如下:
const selectTime = (item: DaysType) => {
const timeNum = new Date(item.time).getTime()
if (!actives.one || timeNum < oneTimeNum.value) {
actives.one = item.time
}
if (!actives.two || timeNum > oneTimeNum.value) {
actives.two = item.time
}
}
首先在html中绑定点击事件,点击获取的时候把当前选中的对象传递下来
对象中有个字段是 2023-2-28 这样的格式,这个格式,可以通过 date 对象的 getTime 方法转换成时间戳
如果第一次选中没有值,或者 当前选中的值小于 第一次选中的日期,那么存入第一次选中
如果第二次选中没有纸,或者 当前选中的值大于 第一次选中的日期,那么存入第二次选中
请注意:这里存入的时候,会自动通过计算属性把值转换为时间戳
给两个点之间添加背景色,逻辑如下:
const setBgColor = (time: string) => {
const timeNum = new Date(time).getTime()
if (
oneTimeNum.value &&
twoTimeNum.value &&
oneTimeNum.value <= timeNum &&
timeNum <= twoTimeNum.value
) {
return 'bg-[#DFF3F5]'
}
}
还是通过转换时间戳的方式,去做对比
处于两个选中日期中间的 格子 会返回个背景色
然后通过动态class的方式插入颜色就可以了