天天看点

flex插件_uniapp撸一个日历插件

    最近在使用uni-app进行小程序开发,涉及到日历的开发,由于是多语言版本,需要找到能够自定义周一到周末各个名称的日历插件,逛了一圈插件市场没有找到满意的,只好自己动手,丰衣足食

    话不多说,直接开干,先看下我们想要实现的效果

flex插件_uniapp撸一个日历插件

准备工作:

    弹出板这些就不多说了,uni的插件市场还是比较丰富的,当然还是要推荐一下uview这个组件库,从文档到使用上,都有比较丝滑的体验

    然后dayjs建议引入,一个轻量的时间处理库

TIP

    由于业务的原因,日历部分的年月日显示那一栏,最终实现出来,只会显示年和月份,不会显示日期,否则会有逻辑上的错误,所以最终实现的效果是这样

flex插件_uniapp撸一个日历插件
  • 年月显示

先放上代码

template

class=      <button type="default" class="plain-btn" @click="reduceMonth">        <u-icon name="weibiaoti--11" custom-prefix="iconfont" size="34" class="text-grey-1">u-icon>      button>      <view class="text-18 text-bold text-primary">                <text>{{year}}text>        <text>-text>        <text>{{month > 9 ? month : `0${month}`}}text>      view>      <button type="default" class="plain-btn" @click="addMonth">        <u-icon name="weibiaoti--11" custom-prefix="iconfont" size="34" class="text-grey-1 trans-icon">u-icon>      button>    view>           

js

data () {      return {        year: '',        month: '',        date: '',        week: '',        now: '',        choose: '',        weekList: [          { week: 0, name: '周日' },          { week: 1, name: '周一' },          { week: 2, name: '周二' },          { week: 3, name: '周三' },          { week: 4, name: '周四' },          { week: 5, name: '周五' },          { week: 6, name: '周六' }        ]      }    },    // methods 上面template中涉及到的方法      reduceMonth () { // 月份前推        if (this.month !== 1) {          this.month--        } else {          this.month = 12          this.year--        }      },      addMonth () { // 月份后推        if (this.month !== 12) {          this.month++        } else {          this.month = 1          this.year++        }      }           

这一坨的显示就没有问题了

flex插件_uniapp撸一个日历插件

但还不够,我们得给一个初始值,生成一个setToday()方法做为初始方法,在created生命周期中调用

// watch    // watch     watch: {      now (val) { // 设置显示的年月日用作显示        this.year = val.getFullYear()        this.month = val.getMonth() + 1        this.date = val.getDate()        this.week = val.getDay()      }    },// methodsetToday () { // init设置当前日        const vm = this        vm.now = new Date()        vm.choose = { // choose存放用户选择的日期,初始为当天,用作提交          year: vm.now.getFullYear(),          month: vm.now.getMonth() + 1,          date: vm.now.getDate(),          week: vm.now.getDay(),          isInMonth: true, // 是否是当月内          isBetween: vm.compareDays(dayjs()) // 是否在可选范围内,下面会讲到compareDays()        }      },           

再提一嘴,这里用dayjs提供的方法要舒服得多,我是后面做着才想起引入,然后现在正在打字的我表示不想改了,将就看吧

flex插件_uniapp撸一个日历插件

,说一下choose.isInMonth,这个字段用来标识是否是当前显示的月份内的日期,眼睛多的朋友应该已经发现,日历面板上只显示了当月份的所有天,而前面空置的是空白

flex插件_uniapp撸一个日历插件
  • 周一到周末

    代码如下

// temlateclass=      class=// propsweekNames: { // 周末到周六的每一天的名称        type: Array,        default: function () {          return []        } }// created当中加入if (this.weekNames.length === 7) {        this.weekList.forEach((item, idx) => {          item.name = this.weekNames[idx]        }) }           

如上面代码所示,为了实现业务需求中多语言的周一到周末,在props中定义了weekNames,并将其传递进weekList

  • 最重要的日期面板
// templateclass=      <view v-for="(k,idx) in daysList" :key="idx" :class="k.isInMonth ? 'self-day' : 'out-month'" class="day-item">        <view          class="days-container flex-row flex-jst-center flex-ali-center"          v-if="k.isInMonth" @click="chooseDays(k)"          :class="{'is-btw': k.isBetween, 'choosed': k.date === choose.date && k.month === choose.month}">          {{k.date}}        view>      view>    </view>           

先说一下样式:

self-day: 是否显示出日期(即是否在所显示的月份内)

is-btw: 决定是否为可选日期(有border)

choosed: 决定是否为所选日期

先看下daysList的生成

// computedmonthList () { // 每月天数        const vm = this        const list = [          { month: 1, days: 31 },          { month: 2, days: 28 },          { month: 3, days: 31 },          { month: 4, days: 30 },          { month: 5, days: 31 },          { month: 6, days: 30 },          { month: 7, days: 31 },          { month: 8, days: 31 },          { month: 9, days: 30 },          { month: 10, days: 31 },          { month: 11, days: 30 },          { month: 12, days: 31 }        ]        // isLeapYear为判断是否是闰年的方法,傻子才自己写,聪明的人都用dayjs里面现成的!!        if (vm.isLeapYear(vm.year)) {          list[1].days = 29        }        return list},daysList () { // 要显示在页面上的日期列        const vm = this        let result = []        if (vm.month) {          const len = vm.monthList.find(item => item.month === vm.month).days // 获得对应月份总天数          for (let k=1;k<=len;k++) {            const dayInfo = {              year: vm.year,              month: vm.month,              date: k,              week: new Date(`${vm.year}/${vm.month}/${k}`).getDay(),              isInMonth: true, // 是否是当月内              isBetween: vm.compareDays(dayjs(`${vm.year}/${vm.month}/${k}`))            }            result.push(dayInfo)          }          // 月前补足          const start = result[0].week          for (let k=0;k            result.unshift({isInMonth: false})          }        }        return result}           

要知道当前该显示多少个日期,我们需要知道该月到底有多少天,那么就需要定义一个monthList用作存放当年每月的天数,唯一的变动就是需要用isLeapYear去判断是否是闰年,以决定二月份是29天还是28天

dayList当中,我们增加了一个属性isBetween,这个属性决定了该日期是否在可选日期内

compareDays (day) {        const vm = this        return dayjs(day).isBetween(dayjs(vm.startDate), dayjs(vm.endDate), null, '[]')},           

如上,通过props内定义的startDate,和endDate,决定了可选日期范围范围,然后根据这个范围,决定对应日期是否可选(is-btw)

注意月前补足,为了显示出非本月份的空隙,所以要添加进对应数量的空对象填充进daysList前,并设置这类对象isInMonth = false,来控制其显示空白

到了这里,核心逻辑基本已经完善了,剩下的就是点击日期然后提交日期了

放上我的整个组件,当中有些公用类名,比如flex-row,text-primary眼睛多的应该看的懂我就不贴了,眼睛少的可以留言问我,我来告诉你

flex插件_uniapp撸一个日历插件

calendar.vue

<template>  <view class="calendar-contaienr border-box">    <view class="calendar-name text-18 text-center">{{name}}view>    <view class="title-show flex-row flex-jst-btw flex-ali-center ma-col-md">      <button type="default" class="plain-btn" @click="reduceMonth">        <u-icon name="weibiaoti--11" custom-prefix="iconfont" size="34" class="text-grey-1">u-icon>      button>      <view class="text-18 text-bold text-primary">        <text>{{year}}text>        <text>-text>        <text>{{month > 9 ? month : `0${month}`}}text>      view>      <button type="default" class="plain-btn" @click="addMonth">        <u-icon name="weibiaoti--11" custom-prefix="iconfont" size="34" class="text-grey-1 trans-icon">u-icon>      button>    view>    <view class="week-show flex-row flex-nowrap flex-jst-btw flex-ali-center">      <text class="text-12 text-grey flex-1 text-center" v-for="k in weekList" :key="k.week">{{k.name}}text>    view>    <view class="days-show flex-row flex-wrap flex-jst-start flex-ali-start ma-col-md">      <view v-for="(k,idx) in daysList" :key="idx" :class="k.isInMonth ? 'self-day' : 'out-month'" class="day-item">        <view          class="days-container flex-row flex-jst-center flex-ali-center"          v-if="k.isInMonth" @click="chooseDays(k)"          :class="{'is-btw': k.isBetween, 'choosed': k.date === choose.date && k.month === choose.month}">          {{k.date}}        view>      view>    view>        <view class="width-80 flex-row flex-jst-center flex-ali-center btn-container">      <button type="default" class="my-btn-primary text-white" @click="subDays">{{btnText}}button>    view>  view>template> <script>  import dayjs from 'dayjs'  const isBetween = require('dayjs/plugin/isBetween')  dayjs.extend(isBetween)  export default {    name: 'calendar',    props: {      formatValue: { // 获取最终值的format格式        type: String,        default: 'YYYY-MM-DD'      },      name: { // 标题名字        default: '日期选择'      },      btnText: { // 确认按钮文字        default: '确认'      },      weekNames: { // 周末到周六的每一天的名称        type: Array,        default: function () {          return []        }      },      startDate: { // 可选时间范围        default: function () {          return dayjs().format('YYYY/MM/DD')        }      },      endDate: { // 可选时间范围结束        default: function () {          return dayjs().add(30,'day').format('YYYY/MM/DD')        }      }    },    data () {      return {        year: '',        month: '',        date: '',        week: '',        now: '',        choose: '',        weekList: [          { week: 0, name: '周日' },          { week: 1, name: '周一' },          { week: 2, name: '周二' },          { week: 3, name: '周三' },          { week: 4, name: '周四' },          { week: 5, name: '周五' },          { week: 6, name: '周六' }        ]      }    },    created () {      this.setToday()      this.compareDays()      if (this.weekNames.length === 7) {        this.weekList.forEach((item, idx) => {          item.name = this.weekNames[idx]        })      }    },    watch: {      now (val) {        this.year = val.getFullYear()        this.month = val.getMonth() + 1        this.date = val.getDate()        this.week = val.getDay()      }    },    computed: {      monthList () { // 每月天数        const vm = this        const list = [          { month: 1, days: 31 },          { month: 2, days: 28 },          { month: 3, days: 31 },          { month: 4, days: 30 },          { month: 5, days: 31 },          { month: 6, days: 30 },          { month: 7, days: 31 },          { month: 8, days: 31 },          { month: 9, days: 30 },          { month: 10, days: 31 },          { month: 11, days: 30 },          { month: 12, days: 31 }        ]        if (vm.isLeapYear(vm.year)) {          list[1].days = 29        }        return list      },      daysList () { // 要显示在页面上的日期列        const vm = this        let result = []        if (vm.month) {          const len = vm.monthList.find(item => item.month === vm.month).days // 获得对应月份总天数          const arr = [1,2,3]          for (let k=1;k<=len;k++) {            const dayInfo = {              year: vm.year,              month: vm.month,              date: k,              week: new Date(`${vm.year}/${vm.month}/${k}`).getDay(),              isInMonth: true, // 是否是当月内              isBetween: vm.compareDays(dayjs(`${vm.year}/${vm.month}/${k}`))            }            result.push(dayInfo)          }          // 月前补足          const start = result[0].week          for (let k=0;k            result.unshift({isInMonth: false})          }        }        return result      }    },    methods: {      chooseDays (target) { // 点击日期        if (target.isBetween) {          this.choose = target        }      },      subDays () { // 提交日期        const str = `${this.choose.year}/${this.choose.month}/${this.choose.date}`        this.$emit('choose', dayjs(str).format(this.formatValue))      },      compareDays (day) {        const vm = this        return dayjs(day).isBetween(dayjs(vm.startDate), dayjs(vm.endDate), null, '[]')      },      isLeapYear (year) { // 判断是否是闰年        let result = false        const reason1 = (year % 4 === 0) && (year % 100 !== 0)        const reason2 = (year % 100 === 0) && (year % 400 === 0)        if (reason1 || reason2) {          result = true        }        return result      },      setToday () { // init设置当前日        const vm = this        vm.now = new Date()        vm.choose = {          year: vm.now.getFullYear(),          month: vm.now.getMonth() + 1,          date: vm.now.getDate(),          week: vm.now.getDay(),          isInMonth: true, // 是否是当月内          isBetween: vm.compareDays(dayjs())        }      },      reduceMonth () { // 月份前推        if (this.month !== 1) {          this.month--        } else {          this.month = 12          this.year--        }      },      addMonth () { // 月份后推        if (this.month !== 12) {          this.month++        } else {          this.month = 1          this.year++        }      }    }  }script><style lang="scss" scoped>  @import './calendar.scss';style>           

calendar.scss

.calendar-contaienr{  width: 100%;  min-height: 80vh;  background: #FFFFFF;  padding: 15px 15px 0 15px;  position: relative;  .btn-container{    margin: auto;    position: absolute;    bottom: 0;    left: 50%;    transform: translate(-50%, -100%);  }  .title-show{    box-sizing: border-box;    padding: 3px;    border-top: 1px solid #CCCCCC;    border-bottom: 1px solid #CCCCCC;    .plain-btn{      margin: 0;      .trans-icon{        transform: rotate(180deg);      }    }  }  .days-show{    .day-item{      width: calc(100% / 7);      box-sizing: border-box;      padding: 5px;    }    .self-day{      // background: green;      .days-container{        padding: 10px;        box-sizing: border-box;        width: 100%;        border-radius: 10.41rpx;        color: #aaaaaa;        &.is-btw{          border: 1px solid #cfcfcf;          color: #000000;          &.choosed{            color: #ffffff;            background: $uni-color-primary;            border-color: $uni-color-primary;          }        }      }    }    .out-month{      // background: grey;    }  }}