
移动端年、月、周、日时间选择器(基于Vant+Vue3)
vue
vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
项目地址:https://gitcode.com/gh_mirrors/vu/vue

·
开发移动app项目时,需要选择年、月、周、日下面的具体时间范围,比如年:xxxx年01月01日至xxxx年12月31日;月:当前年加上选择的月份的日期范围;周:当前年的
某一周的时间范围;日期:当前年+当前月份+选择的日期;于是封装了个组件,用的是vant组件库。
先安装组件库依赖
npm i vant
1.组件封装
tabsDateSelect/index.js
<!-- 时间选择控件 -->
<template>
<div>
<van-tabs v-model:active="baseActive" @change="changeActiveName" :swipeable="swipeable">
<van-tab v-for="(item,index) in tabsOptions" :key="index" :title="item.dictValueName||item.title" :name="item.dictValueCode||item.name">
<van-tabs v-model:active="item.baseActiveDetail" @change="changeActiveNameDetail" :swipeable="swipeable">
<van-tab v-for="(child,cindex) in item.childNodes" :key="cindex" :title="child.dictValueName||child.title" :name="child.dictValueCode||child.name">
</van-tab>
</van-tabs>
</van-tab>
</van-tabs>
</div>
</template>
<script setup>
import { getWeekRange, getMonthDayRange } from '../../utils'
import { ref, reactive, onMounted } from 'vue'
const props = defineProps({
activeName: {
type: [Number, String],
default: '0'
},
activeNameDetail: {
type: [Number, String],
default: '0'
},
swipeable: { // 是否开启手势左右滑动切换
type: Boolean,
default: false
},
// tabsOptions: {
// type: Array,
// default: () => {
// return []
// }
// },
tabsOptionsDetail: {
type: Array,
default: () => {
return []
}
}
})
onMounted(() => {
// changeActiveName()
createdWeek()
})
// 10以下的数 前面补0
const addZero = (val) => {
return val < 10 ? '0' + val : val
}
// 按月份生成天数
const createdDays = () => {
const year = new Date().getFullYear()
const month = new Date().getMonth() + 1 + ''
const day = new Date().getDate()
const arr = []
if (['1', '3', '5', '7', '8', '10', '12'].includes(month)) {
for (let i = 1; i < 32; i++) {
arr.push({
title: i === day ? '今天' : month + '-' + i,
name: i + '',
value: year + '-' + addZero(month) + '-' + addZero(i)
})
}
} else if (['4', '6', '9', '11'].includes(month)) {
for (let i = 1; i < 31; i++) {
arr.push({
title: i === day ? '今天' : month + '-' + i,
name: i + '',
value: year + '-' + addZero(month) + '-' + addZero(i)
})
}
} else {
const year = new Date().getFullYear()
// 平年 29天
if (year % 4 === 0 && year % 100 !== 0 || year & 400 === 0) {
for (let i = 1; i < 30; i++) {
arr.push({
title: i === day ? '今天' : month + '-' + i,
name: i + '',
value: year + '-' + addZero(month) + '-' + addZero(i)
})
}
} else {
// 闰年 28天
for (let i = 1; i < 29; i++) {
arr.push({
title: month + '-' + i,
name: i + '',
value: year + '-' + addZero(month) + '-' + addZero(i)
})
}
}
}
return arr
}
// 生成年份
const createdYear = () => {
const year = new Date().getFullYear()
const arr = []
for (let i = year; i > year - 10; i--) {
arr.push({
title: i + '年',
name: '',
value: '' + (i % year === 0 ? year : i % year)
})
}
arr.reverse()
arr.forEach((item, index) => {
item.name = index + ''
})
return arr
}
// 当前第几周
const nowWeek = () => {
const endDate = new Date()
// 本年的第一天
const beginDate = new Date(endDate.getFullYear(), 0, 1)
// 星期从0-6,0代表星期天,6代表星期六
let endWeek = endDate.getDay()
if (endWeek == 0) endWeek = 7
let beginWeek = beginDate.getDay()
if (beginWeek == 0) beginWeek = 7
// 计算两个日期的天数差
var millisDiff = endDate.getTime() - beginDate.getTime()
var dayDiff = Math.floor((millisDiff + (beginWeek - endWeek) * (24 * 60 * 60 * 1000)) / 86400000)
return Math.ceil(dayDiff / 7) + 1
}
// 生成周数
const createdWeek = () => {
const resNum = nowWeek()
const arr = []
for (let i = resNum; i > 0; i--) {
arr.push({
title: i === resNum ? '本周' : i + 1 === resNum ? '上周' : i + '周',
name: ''
})
}
arr.reverse()
arr.forEach((item, index) => {
item.name = index + 1 + ''
})
return arr
}
// 生成月份
const createdMonth = () => {
const arr = []
for (let i = 1; i < 13; i++) {
arr.push({
title: i + '月',
name: i + ''
})
}
return arr
}
const tabsOptions = reactive([
{ title: '日', name: 'day', baseActiveDetail: '0', childNodes: createdDays() },
{ title: '周', name: 'week', baseActiveDetail: '0', childNodes: createdWeek() },
{ title: '月', name: 'month', baseActiveDetail: '0', childNodes: createdMonth() },
{ title: '年', name: 'year', baseActiveDetail: '0', childNodes: createdYear() }
])
const emits = defineEmits(['update:activeName', 'changeActive', 'update:activeNameDetail', 'changeActiveDetail'])
const type = ref('')
// 年月日周点击
const changeActiveName = (e, other, other2) => {
// baseActiveDetail.value = '0' // 设置默认值 根据具体情况具体设置,当天,本月,本年...
type.value = e
const obj = { type: e, defaultValue: '' }
let baseActiveDetail = ''
if (e === 'day') {
const dayNow = '' + new Date().getDate()
tabsOptions.find((item) => { return item.name === 'day' }).baseActiveDetail = other2 || dayNow
baseActiveDetail = tabsOptions.find((item) => { return item.name === 'day' }).baseActiveDetail
obj.defaultValue = tabsOptions.find((item) => { return item.name === 'day' }).childNodes[(other2 || dayNow) - 1].name
} else if (e === 'year') {
const yearNow = '' + new Date().getFullYear()
// 获取年份下标
const nowYearIndex = tabsOptions.find((item) => { return item.name === 'year' }).childNodes.findIndex((item) => { return item.value === yearNow }) + ''
tabsOptions.find((item) => { return item.name === 'year' }).baseActiveDetail = other2 || nowYearIndex
baseActiveDetail = tabsOptions.find((item) => { return item.name === 'year' }).baseActiveDetail
obj.defaultValue = tabsOptions.find((item) => { return item.name === 'year' }).childNodes[other2 || nowYearIndex].value
} else if (e === 'week') {
const num = '' + nowWeek()
tabsOptions.find((item) => { return item.name === 'week' }).baseActiveDetail = other2 || num
baseActiveDetail = tabsOptions.find((item) => { return item.name === 'week' }).baseActiveDetail
obj.defaultValue = other2 || num
} else if (e === 'month') {
const monthNow = '' + (new Date().getMonth() + 1)
tabsOptions.find((item) => { return item.name === 'month' }).baseActiveDetail = other2 || monthNow
baseActiveDetail = tabsOptions.find((item) => { return item.name === 'month' }).baseActiveDetail
obj.defaultValue = other2 || monthNow
}
const timeArr = getTimeByType(obj.type, obj.defaultValue)
emits('update:activeName', obj)
emits('changeActive', timeArr, e, baseActiveDetail)
}
// 下排tab点击
const changeActiveNameDetail = (e) => {
// 判断点击的类型是年
if (type.value === 'year') {
const result = tabsOptions.find((item) => { return item.name === 'year' })
const res = result.childNodes[e].value
const title = result.childNodes[e].title
const baseActiveDetail = result.baseActiveDetail
const timeArr = getTimeByType('year', parseInt(title))
emits('update:activeNameDetail', res)
emits('changeActiveDetail', timeArr, baseActiveDetail)
return
}
// 判断点击的类型是日
if (type.value === 'day') {
const result = tabsOptions.find((item) => { return item.name === 'day' })
const res = result.childNodes[e - 1].value
const baseActiveDetail = result.baseActiveDetail
const timeArr = getTimeByType('day', e)
emits('update:activeNameDetail', res)
emits('changeActiveDetail', timeArr, baseActiveDetail)
return
}
let activeResult = ''
// 判断点击的类型是周
if (type.value === 'week') {
activeResult = tabsOptions.find((item) => { return item.name === 'week' }).baseActiveDetail
}
// 判断点击的类型是月
if (type.value === 'month') {
activeResult = tabsOptions.find((item) => { return item.name === 'month' }).baseActiveDetail
}
const timeRangeArr = getTimeByType(type.value, e)
emits('update:activeNameDetail', e)
emits('changeActiveDetail', timeRangeArr, activeResult)
}
const dayNumNow = new Date().getDate()
const baseActive = ref('')
// 根据类型和时间获取时间参数范围 返回值 timeArr 时间数组 []
const getTimeByType = (type, time) => {
let timeArr = []
const d = new Date()
const currentYear = d.getFullYear()
const currentMonth = d.getMonth() + 1
const str = currentYear + '-' + currentMonth
time = time < 10 ? `0${time}` : time
switch (type) {
case 'day':
timeArr = [str + '-' + time, str + '-' + time]
break
case 'week':
timeArr = getWeekRange(currentYear, time)
break
case 'month':
timeArr = getMonthDayRange(currentYear, time)
break
case 'year':
timeArr = getTimeByYear(time)
break
default:
break
}
return timeArr
}
// 根据年份获取该年的第一天和最后一天 year 年份
const getTimeByYear = (year) => {
if (!year) {
return
}
// 该年第一天
const firstDay = year + '-01-01'
const lastDay = getMonthDayRange(year, 12)[1]
return [firstDay, lastDay]
}
// 初始化组件 设置第一排tab和第二排tab的默认值
const init = (a, b) => {
baseActive.value = a
if(['day', 'week', 'month', 'year'].includes(a)) {
tabsOptions.find((item) => { return item.name === a }).baseActiveDetail = b
}
changeActiveName(a, '', b)
}
// 暴露init方法 用于父组件调用
defineExpose({ init })
</script>
<style lang="scss">
</style>
utils/index.js
// 通过年份、周数获取日期范围 year 年 weeks 周 getWeekRange(2019,12))--> 2019-03-18~2019-03-24
export function getWeekRange(year, weekNum) {
const yearStart = new Date(parseInt(year), 0, 1) // 设置该年1.1.
const firstDay = yearStart.getDay() // 星期
// 对1.1.所在周的前后几天特殊处理.
const other = (firstDay >= 0 && firstDay <= 4) ? firstDay - 1 : firstDay - 8
// 时间调整,得出要计算周的起/始时间.
// 考虑距离当年1.1.的总天数
const days = (parseInt(weekNum, 10) - 2) * 7 - other
// 一天时间的毫秒数
const oneDay = 24 * 60 * 60 * 1000
// 当前周 第一天 离1/1/70的毫秒数.
const dateInMs = oneDay * days + yearStart.getTime()
// 日期调整(设置1/1/70至今的毫秒数)
const weekStart = new Date(dateInMs)
// 当前所选周最后一天处理,同上.
const dateInMsE = oneDay * 6 + dateInMs
const weekEnd = new Date(dateInMsE)
// 月和日的处理,一位变两位,如:1->01.
let month = weekStart.getMonth() + 1
month = (month < 10) ? '0' + month : month
let day = weekStart.getDate()
day = (day < 10) ? '0' + day : day
let monthE = weekEnd.getMonth() + 1
monthE = (monthE < 10) ? '0' + monthE : monthE
let dayE = weekEnd.getDate()
dayE = (dayE < 10) ? '0' + dayE : dayE
return [weekStart.getFullYear() + '-' + month + '-' + day, weekEnd.getFullYear() + '-' + monthE + '-' + dayE]
// return (month + "-" + day) + "~" + (monthE + "-" + dayE)
}
// 根据年份和月份获取这个月的第一天和最后一天 year 年份 month月份
export function getMonthDayRange(year, month) {
if (!year || !month) {
return
}
const firstDay = year + '-' + month + '-01'
const lastDay = new Date(year, month, 0)
const lastDayYear = lastDay.getFullYear()
const lastDayMonth = lastDay.getMonth() + 1
const day = lastDay.getDate()
return [firstDay, lastDayYear + '-' + lastDayMonth + '-' + day]
}
main.js
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import { Tab, Tabs } from 'vant';
const app = createApp(App)
import 'vant/lib/index.css'
app.use(Tab)
app.use(Tabs)
app.mount('#app')
2.组件用法
app.vue
<script setup>
import tabsDateSelect from './components/tabsDateSelect/index.vue'
// 切换日周月年
const changeActive = (res, type, baseActiveDetail) => {
console.log('切换日周月年=====>', res, type)
}
// 切换日月周年次级tab
const changeActiveDetail = (res, baseActiveDetail) => {
console.log('切换日月周年次级---------- ', res)
}
</script>
<template>
<div style="height:200px">
<tabsDateSelect @changeActive="changeActive" @changeActiveDetail="changeActiveDetail"/>
</div>
</template>
<style scoped>
</style>
3.效果




vuejs/vue: 是一个用于构建用户界面的 JavaScript 框架,具有简洁的语法和丰富的组件库,可以用于开发单页面应用程序和多页面应用程序。
最近提交(Master分支:5 个月前 )
9e887079
[skip ci] 4 个月前
73486cb5
* chore: fix link broken
Signed-off-by: snoppy <michaleli@foxmail.com>
* Update packages/template-compiler/README.md [skip ci]
---------
Signed-off-by: snoppy <michaleli@foxmail.com>
Co-authored-by: Eduardo San Martin Morote <posva@users.noreply.github.com> 7 个月前
更多推荐
所有评论(0)