vue日期滚动选择器实现

最近得到一个需求,要实现一个如下图所示的日期滚动选择器,在网上找了许久都没找到合适的,无奈只能自己写一个。

先实现单个滚动选择效果,scrollbarPicker.vue文件代码如下:

<template>
  <div>
    <el-scrollbar
      ref="scrollMenuRef"
      class="scrbar scrollbar"
    >
      <div @mouseleave="handleMouseLeave">
        <div
          class="scrbalScrollBox"
          v-for="(item, index) in list"
          :key="'li' + index"
          @click="scrollTo(item)"
          :class="{ active_li: item == activeItem }"
        >
          {{ item }}
        </div>
      </div>
    </el-scrollbar>
  </div>
</template>

<script>
export default {
  props: {
    value: String,
    data: {
      type: Number,
      default: 40,
    },
    type: {
      type: String,
      default: 'year',
    },
  },
  data() {
    return {
      itemHeight: 40,
      activeItem: '1950',
      scrollTopNum: 0,
      list: [],
    };
  },
  mounted() {
    this.getDataList();
  },
  methods: {
    getDataList() {
      switch (this.type) {
        case 'year':
          this.list = this.getYearArr();
          break;
        case 'month':
          this.list = this.generateNumberArray(11);
          break;
        case 'day':
          this.list = this.generateNumberArray(30);
          break;
        default:
          break;
      }
      this.activeItem = this.value;
      this.$nextTick(() => {
        this.scrollTo(this.value);
      });
      this.$refs.scrollMenuRef.wrap.addEventListener('scroll', this.scrollMenu);
    },
    generateNumberArray(n) {
      let arr = Array.from({ length: n + 1 }, (_, i) => String(i + 1).padStart(2, '0'));
      arr = ['', '', ...arr, '', ''];
      return arr;
    },
    getYearArr() {
      let start = 1950;
      let end = 2024;
      let arr = [];
      for (let i = start; i <= end; i++) {
        arr.push(`${i}`);
      }
      arr = ['', '', ...arr, '', ''];
      return arr;
    },
    handleMouseLeave() {
      if (this.scrollTopNum % 40 != 0) {
        this.$refs.scrollMenuRef.wrap.scrollTop = this.toggleNum(this.scrollTopNum);
      }
    },
    updateValue(value) {
      this.$emit('input', value);
    },
    scrollTo(titleNo) {
      let index = this.list.indexOf(titleNo);
      index -= 2;
      let num = index * this.itemHeight;
      this.$refs.scrollMenuRef.wrap.scrollTop = num;
    },
    toggleNum(num) {
      if (num % this.itemHeight == 0) {
        return num;
      } else {
        let num1 = num % this.itemHeight;
        return this.itemHeight - num1 + num;
      }
    },
    scrollMenu(e) {
      this.scrollTopNum = e.target.scrollTop;
      let val = Math.round(this.scrollTopNum / this.itemHeight);
      this.activeItem = `${this.list[val + 2]}`;
      this.updateValue(this.activeItem);
    },
  },
};
</script>
<style scoped>
.scrollbar ::v-deep .el-scrollbar__wrap {
  overflow-x: hidden;
}
.scrbalScrollBox {
  height: 40px;
  box-sizing: border-box;
  line-height: 40px;
  color: #bfc2c9;
  /* background: rgba(216, 216, 216, 0.4); */
}
.active_li {
  color: #000;
  border-top: 1px solid #bfc2c9;
  border-bottom: 1px solid #bfc2c9;
}
.scrollbar {
  height: 200px;
  width: 80px;
  border-radius: 10px;
  font-size: 18px;
  text-align: center;
}

.is-horizontal {
  display: block;
}

/* 不展示水平滚动条 */
::v-deep .is-horizontal {
  display: none;
}
</style>

再将其组合起来 index.vue文件代码如下:

<template>
  <div>
    <div
      class="content_date"
      @click="handleClick"
    >
      <span>请选择</span>
      <div
        class="dateView"
        v-if="divalog"
      >
        <div class="dateView-content">
          <div>
            <div class="date_box">
              <div class="box_item">
                <p>年</p>
                <scrollPicker
                  v-model="dateData.startYear"
                  :type="'year'"
                ></scrollPicker>
              </div>
              <div class="box_item">
                <p>月</p>
                <scrollPicker
                  v-model="dateData.startMonth"
                  :type="'month'"
                  :data="12"
                ></scrollPicker>
              </div>
              <div class="box_item">
                <p>日</p>
                <scrollPicker
                  v-model="dateData.startDay"
                  :type="'day'"
                  :data="31"
                ></scrollPicker>
              </div>
            </div>
          </div>
        </div>
        <div class="divider"></div>
        <div class="dateView_footer">
          <el-button
            size="small"
            @click.stop="handleClose"
            >取消</el-button
          >
          <el-button
            size="small"
            type="primary"
            @click.stop="handleConfirm"
            >确认</el-button
          >
        </div>
        <div class="box_arrow"></div>
      </div>
      <div
        ref="word"
        class="print-main"
      ></div>
    </div>
  </div>
</template>

<script>
import scrollPicker from './scrollbarPicker.vue';
export default {
  components: {
    scrollPicker,
  },
  data() {
    return {
      divalog: false,
      dateData: {
        startYear: '1950',
        startMonth: '01',
        startDay: '03',
      },
    };
  },
  mounted() {
    this.handleScrll();
  },
  methods: {
    handleScrll() {
      // this.$refs.testScroll.wrap.scrollTop += 100;
      // console.log(this.$refs.testScroll.wrap.scrollTop);
    },
    handleClick() {
      this.divalog = true;
    },
    handleClose() {
      this.divalog = false;
    },
    handleConfirm() {
      console.log(this.dateData);
      let date = this.dateData.startYear + '-' + this.dateData.startMonth + '-' + this.dateData.startDay;
      console.log(date);
      this.divalog = false;
    },
  },
};
</script>

<style lang="scss">
.scrollMenuBox {
  height: 200px;
}
.scroll_box {
  width: 200px;
  background-color: white;
  .testBox {
    height: 50px;
    width: 80px;
    line-height: 50px;
    text-align: center;
    border-bottom: 1px solid #000;
  }
}

.print-main {
  position: fixed;
  top: -10000px; //不在页面上显示
  /deep/.docx-wrapper {
    background: none;
  }
  /deep/.docx-wrapper > section.docx {
    margin-bottom: 0;
  }
}
.content_date {
  width: 180px;
  height: 40px;
  font-size: 14px;
  margin: 50px;
  display: inline-block;
  position: relative;
  border: 1px solid #dcdfe6;
  box-sizing: border-box;
  background-color: #fff;
  line-height: 40px;
  color: #606266;
  border-radius: 4px;
  cursor: pointer;
  padding: 0 15px;
}
.dateView {
  background-color: #fff;
  position: absolute;
  left: 0;
  top: 46px;
  // width: 300px;
  .dateView-content {
    padding: 10px 20px 0;
    display: flex;
  }
  .date_box {
    display: flex;
    justify-content: space-between;
    position: relative;
  }
  p {
    font-size: 18px;
    font-weight: 700;
    text-align: center;
    margin: 0;
  }
}
.box_item {
  width: 80px;
  text-align: center;
}
.divider {
  width: 100%;
  height: 1px;
  margin: 4px 0;
  background-color: #dcdfe6;
}
.dateView_footer {
  text-align: right;
  padding: 0 10px 4px;
}
.box_arrow {
  width: 0;
  height: 0;
  border: 10px solid transparent;
  border-bottom-color: #fff;
  position: absolute;
  top: -18px;
  left: 30px;
}
</style>

好的,我来为你介绍一下如何手写滚动日期选择器。我们可以利用Vue框架中的指令来实现这个效果。 首先,我们需要在HTML中创建一个容器来放置日期选择器组件,如下所示: ```html <div class="date-picker" v-click-outside="hidePicker"> <input type="text" v-model="selectedDate" @click="showPicker" readonly> <div class="picker-container" v-show="show"> <div class="picker-header"> <button @click="prevMonth"><</button> <span>{{ currentYear }}年{{ currentMonth }}月</span> <button @click="nextMonth">></button> </div> <div class="picker-body"> <div class="picker-row" v-for="week in weeks"> <div class="picker-cell" v-for="day in week" :class="{ 'is-today': isToday(day), 'is-selected': isSelected(day) }" @click="selectDate(day)"> {{ day }} </div> </div> </div> </div> </div> ``` 在上面的代码中,我们创建了一个`date-picker`的容器,其中包含一个`input`输入框和一个日期选择器的弹出框。输入框中绑定了`selectedDate`变量,用来显示当前选择的日期日期选择器的弹出框使用了Vue的指令`v-show`来控制其显示和隐藏状态。 接下来,我们需要在Vue组件的`data`属性中定义一些变量,如下所示: ```javascript data() { return { selectedDate: '', show: false, currentYear: new Date().getFullYear(), currentMonth: new Date().getMonth() + 1, currentDate: new Date().getDate(), weeks: [], days: [] } } ``` 在上面的代码中,我们定义了一些重要的变量。`selectedDate`用来保存用户选择的日期,`show`用来控制日期选择器的显示和隐藏状态,`currentYear`、`currentMonth`和`currentDate`用来记录当前日期,`weeks`和`days`用来保存渲染日期选择器所需要的数据。 接下来,我们需要在Vue组件的`mounted`钩子函数中初始化日期选择器的数据,如下所示: ```javascript mounted() { // 初始化日期选择器数据 this.initWeeks() this.initDays() } ``` 在上面的代码中,我们调用了`initWeeks`和`initDays`两个函数来初始化日期选择器的数据。 `initWeeks`函数用来初始化日期选择器的星期数据,如下所示: ```javascript initWeeks() { this.weeks = [ ['日', '一', '二', '三', '四', '五', '六'] ] } ``` 在上面的代码中,我们定义了一个数组`weeks`,其中包含了一周内的所有日期。 `initDays`函数用来初始化日期选择器日期数据,如下所示: ```javascript initDays() { const firstDay = new Date(this.currentYear, this.currentMonth - 1, 1).getDay() // 当月第一天是星期几 const lastDay = new Date(this.currentYear, this.currentMonth, 0).getDate() // 当月最后一天是几号 let days = [] let day = 1 for (let i = 0; i < 6; i++) { let week = [] for (let j = 0; j < 7; j++) { if (day > lastDay) { break } if (i === 0 && j < firstDay) { week.push('') } else { week.push(day++) } } days.push(week) } this.days = days } ``` 在上面的代码中,我们使用了`Date`对象来获取当前日期所在月份的第一天和最后一天,然后使用一个嵌套循环来遍历当前月份的所有日期,并将它们保存在一个二维数组中。 接下来,我们需要在Vue组件中定义一些方法来处理用户的交互操作,如下所示: ```javascript methods: { // 显示日期选择器 showPicker() { this.show = true }, // 隐藏日期选择器 hidePicker() { this.show = false }, // 上一个月 prevMonth() { if (this.currentMonth === 1) { this.currentYear -= 1 this.currentMonth = 12 } else { this.currentMonth -= 1 } this.initDays() }, // 下一个月 nextMonth() { if (this.currentMonth === 12) { this.currentYear += 1 this.currentMonth = 1 } else { this.currentMonth += 1 } this.initDays() }, // 选择日期 selectDate(day) { if (day === '') { return } this.selectedDate = `${this.currentYear}-${this.currentMonth}-${day}` this.hidePicker() }, // 判断是否为今天 isToday(day) { return this.currentYear === new Date().getFullYear() && this.currentMonth === new Date().getMonth() + 1 && day === new Date().getDate() }, // 判断是否为选择状态 isSelected(day) { return this.selectedDate === `${this.currentYear}-${this.currentMonth}-${day}` } } ``` 在上面的代码中,我们定义了一些方法来处理用户的交互操作。`showPicker`和`hidePicker`方法用来显示和隐藏日期选择器的弹出框。`prevMonth`和`nextMonth`方法用来切换日期选择器的月份。`selectDate`方法用来选择日期并保存到`selectedDate`变量中。`isToday`方法用来判断一个日期是否为今天。`isSelected`方法用来判断一个日期是否为选择状态。 最后,我们需要在Vue组件中使用`v-click-outside`指令来处理日期选择器的点击外部区域隐藏弹出框的功能,如下所示: ```javascript directives: { 'click-outside': { bind(el, binding, vnode) { el.clickOutsideEvent = function(event) { if (!(el === event.target || el.contains(event.target))) { vnode.context[binding.expression](event) } } document.body.addEventListener('click', el.clickOutsideEvent) }, unbind(el) { document.body.removeEventListener('click', el.clickOutsideEvent) } } } ``` 在上面的代码中,我们使用了`v-click-outside`指令来处理日期选择器的点击外部区域隐藏弹出框的功能。我们定义了一个`click-outside`指令,并在其中使用了`addEventListener`和`removeEventListener`方法来监听和移除点击事件。 以上就是手写滚动日期选择器Vue实现方法。希望对你有所帮助!
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值