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
}
 
效果图
 





![[创业之路-199]:《华为战略管理法-DSTE实战体系》- 3 - 价值转移理论与利润区理论](https://i-blog.csdnimg.cn/direct/7145d465f82b46c5ae831a899a7d5639.png)













