vue横向滚动日期选择器组件
组件使用到了element-plus组件库和dayjs库,使用前先保证项目中已经下载导入
主要功能:选择日期,点击日期可以让此日期滚动到视图中间,左滑右滑同理,支持跳转至任意日期,支持自定义滚动日期的数量
组件中用到了other.ts
工具代码other.ts
import dayjs from 'dayjs'
import calendar from 'dayjs/plugin/calendar'
import 'dayjs/locale/zh-cn'
dayjs.locale('zh-cn')
dayjs.extend(calendar)
dayjs().calendar(null, {
sameDay: '[Today]', // The same day ( Today at 2:30 AM )
nextDay: '[Tomorrow]', // The next day ( Tomorrow at 2:30 AM )
nextWeek: 'dddd', // The next week ( Sunday at 2:30 AM )
lastDay: '[Yesterday]', // The day before ( Yesterday at 2:30 AM )
lastWeek: '[Last]', // Last week ( Last Monday at 2:30 AM )
sameElse: 'DD/MM/YYYY' // Everything else ( 7/10/2011 )
})
function judegSame(dj1: dayjs.Dayjs, dj2: dayjs.Dayjs) {
return dj1.isSame(dj2)
}
function getRelativeTime(dj: dayjs.Dayjs) {
let now = dayjs(dayjs().format('YYYY-MM-DD'))
if (judegSame(now, dj)) {
return '今天'
}
now = now.add(1, 'day')
if (judegSame(now, dj)) {
return '明天'
}
now = now.add(1, 'day')
if (judegSame(now, dj)) {
return '后天'
}
let d = dj.day()
const backArr = ['日', '一', '二', '三', '四', '五', '六']
return '周' + backArr[d]
}
export { dayjs, getRelativeTime }
组件代码SlideDatePicker.vue
<script setup lang="ts">
import { ArrowLeft, ArrowRight } from '@element-plus/icons-vue';
import { dayjs, getRelativeTime } from './other';
// 日期加载总量
const { count } = withDefaults(defineProps<{
count: number
}>(), {
count: 30
})
const activeIndex = ref(0)
const dateItemRefs = ref<HTMLElement[]>([])
const dateItmeWrapRef = ref<HTMLElement>()
const curDate = ref('') // 日期选择器 选择的日期
const showDateList = ref<Record<string, any>[]>([])
const emit = defineEmits(['dateChange'])
function calc(format?: string) {
if (!format) {
format = dayjs().format('YYYY-MM-DD')
}
showDateList.value = []
let beforeCount = Math.floor((count + 1) / 2) // 上取整
let afterCount = Math.floor(count / 2)
let cur = dayjs(dayjs(format).format('YYYY-MM-DD'))
for (let i = 0; i < beforeCount; i++) {
showDateList.value.push({
date: cur.format('YYYYMMDD'),
dateMd: cur.format('MMDD'),
week: getRelativeTime(cur)
})
cur = cur.subtract(1, 'day')
}
showDateList.value = showDateList.value.reverse() // 反转 让最早的日期排在第一位
cur = dayjs(dayjs(format).format('YYYY-MM-DD'))
for (let i = 0; i < afterCount; i++) {
cur = cur.add(1, 'day')
showDateList.value.push({
date: cur.format('YYYYMMDD'),
dateMd: cur.format('MMDD'),
week: getRelativeTime(cur)
})
}
}
const getMiddle = computed(() => {
return Math.floor((showDateList.value.length + 1) / 2) - 1
})
function move(step: number) {
// 越界无效
if (activeIndex.value + step >= showDateList.value.length || activeIndex.value + step < 0) {
return
}
activeIndex.value += step
dateItmeWrapRef.value?.scrollTo({
behavior: 'smooth',
left: (dateItemRefs.value[activeIndex.value].offsetLeft - dateItmeWrapRef.value.offsetWidth / 2)
})
curDate.value = dayjs(showDateList.value[activeIndex.value].date, 'YYYYMMDD').format('YYYY-MM-DD')
emit('dateChange', curDate.value)
}
function moveToIndex(index: number) {
if (index >= showDateList.value.length || index < 0) {
return
}
activeIndex.value = index
dateItmeWrapRef.value?.scrollTo({
behavior: 'smooth',
left: (dateItemRefs.value[activeIndex.value].offsetLeft - dateItmeWrapRef.value.offsetWidth / 2)
})
curDate.value = dayjs(showDateList.value[activeIndex.value].date, 'YYYYMMDD').format('YYYY-MM-DD')
emit('dateChange', curDate.value)
}
function datePickerChange(value: string) {
// 重新计算
curDate.value = dayjs(value).format('YYYY-MM-DD')
calc(curDate.value)
activeIndex.value = getMiddle.value
nextTick(() => {
dateItmeWrapRef.value?.scrollTo({
behavior: 'instant',
left: (dateItemRefs.value[activeIndex.value].offsetLeft - dateItmeWrapRef.value.offsetWidth / 2)
})
})
emit('dateChange', curDate.value)
}
onMounted(() => {
calc()
activeIndex.value = getMiddle.value
curDate.value = dayjs().format('YYYY-MM-DD')
nextTick(() => {
dateItmeWrapRef.value?.scrollTo({
behavior: 'instant',
left: (dateItemRefs.value[activeIndex.value].offsetLeft - dateItmeWrapRef.value.offsetWidth / 2)
})
})
})
</script>
<template>
<div class="date_picker_wrap">
<div class="left_icon">
<el-button :icon="ArrowLeft" link @click="move(-1)">
</el-button>
</div>
<div class="date_item_wrap" ref="dateItmeWrapRef">
<div class="date_item" :class="index === activeIndex ? 'active' : ''" v-for="(item, index) in showDateList"
ref="dateItemRefs" @click="moveToIndex(index)">
<span>{{ item.dateMd }}</span>
<span>{{ item.week }}</span>
</div>
</div>
<div class="right_icon">
<el-button :icon="ArrowRight" link @click="move(1)">
</el-button>
</div>
<div class="calendar_icon">
<el-date-picker style="width: 126px;" v-model="curDate" type="date" placeholder="选择日期" format="YYYY-MM-DD"
:clearable="false" @change="datePickerChange" />
</div>
</div>
</template>
<style scoped>
.date_picker_wrap {
background: #fff;
width: 100%;
height: 52px;
border-radius: 6px;
padding: 4px 8px;
display: flex;
align-items: center;
font-size: 14px;
color: #4b5563;
.date_item_wrap {
width: 100%;
display: flex;
flex: 1;
overflow: hidden;
.active {
color: #3c6cfe;
}
.date_item {
padding: 0 6px;
width: 96px;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
border-left: solid 1px #e5e7eb;
border-right: solid 1px #e5e7eb;
cursor: pointer;
&:hover {
color: #3c6cfe;
}
span {
padding: 0 2px;
}
}
}
.left_icon,
.right_icon,
.calendar_icon {
padding: 0 8px;
}
}
</style>
使用方式
传入count 30,组件初始化横向滚动日期数为30个,初始化数量不要太少,最好占满宽度,让其可以滚动。
<SlideDatePicker :count="30" @dateChange="dateChange" />
function dateChange(value: string) {
console.log('选中的日期', value); // 2024-12-19
}
效果图