手把手编写一个Vue日历表组件

35次阅读

共计 6397 个字符,预计需要花费 16 分钟才能阅读完成。

项目需要,需要一个日历表组件,产品狗嫌弃第三方提供的组件样式太丑,功能不全,带刀和产品理论奈何产品有枪。无奈只能自己写一个,发布完这条博客我就提离职,再也不和产品多 BB。
Html=>
`<template>

<div class="calender">
  <div class="calender-title-wrapper">
    <div class="calender-title">
      <div class="calender-arrow-item left2" v-if="false"></div>
      <div class="calender-arrow-item left1" @click="month--"></div>
      <div class="calender-Date-text">{{year}}年 {{month}} 月 </div>
      <div class="calender-arrow-item right1" @click="month++"></div>
      <div class="calender-arrow-item right2" v-if="false"></div>
    </div>
  </div>
  <div class="calender-content-wrapper">
    <div class="calender-days-wrapper">
      <div class="calender-days"> 日 </div>
      <div class="calender-days"> 一 </div>
      <div class="calender-days"> 二 </div>
      <div class="calender-days"> 三 </div>
      <div class="calender-days"> 四 </div>
      <div class="calender-days"> 五 </div>
      <div class="calender-days"> 六 </div>
    </div>
    <div class="calender-content">
      <div class="calender-item-wrapper"
           v-for="item in daysArr"
           :class="{'no-current-day':item.month!=month,
                    active:item === clickItemObj,
                    currentDay:item.day===_CurrentDate.day&&
                               item.month===_CurrentDate.month&&
                               item.year===_CurrentDate.year}"@click="_clickDaysItem(item)"
      >
        <div class="calender-item">{{item.day}}</div>
        <div class="tem-wrapper">
          <span class="temp" v-for="d in markArr" :style="{background: d.color}" v-if="_visibleTemp(d,item)"></span>
        </div>
      </div>
    </div>
    <div class="calender-mask-wrapper">
      <div class="calender-mask-item" v-for="item in markArr">
        <span class="tem" :style="{background:item.color}"></span>
        <span class="text">{{item.name}}</span>
      </div>
    </div>
  </div>
</div>

</template>`
js=>

<script>
    export default {
        name: "calender",
        props:{
          markArr:{
            type:Array,
            default(){return []
            }
          }
        },
        data(){
          return {
            day:'',
            month:'',
            year:'',
            currentDays:'',
            daysArr:[],
            clickItemObj:{}}
        },
        mounted() {this._getCurrentDate()
        },
        methods:{
          // 点击日期
          _clickDaysItem(item){
            // 选中日期标记与解除标记
            if(this.clickItemObj === item){this.clickItemObj = {}
            }else {this.clickItemObj = item}
            // 非当前月份跳转
            if(item.month!=this.month){this.month = item.month}
            // 传入事件筛选回返
            this._user_defined_events(item)
          },
          //mark 渲染
          _user_defined_events(item){for(let i=0;i<this.markArr.length;i++){if(this.markArr[i].days.includes(`${item.year}-${item.month}-${item.day}`)){this.$emit(this.markArr[i].clickEvent,item)
              }
            }
          },
          // 是否显示 mark
          _visibleTemp(d,item){let dateStr = `${item.year}-${item.month}-${item.day}`
            return d.days.includes(dateStr)
          },
          // 获取月份天数
          _getDaysForMonth(year,month,type){let temp=new Date(year,month,type);
            let day = new Date(temp.getTime() - 864e5).getDate();
            this.currentDays = day
            this._getFirstDaysForMonth(this.year,this.month)
          },
          // 获取当前日期
          _getCurrentDate(){let date = new Date()
            this.day = date.getDate()
            this.month = date.getMonth()+1
            this.year = date.getFullYear()
            this._getDaysForMonth(this.year,this.month,1)

          },
          // 获取切换页月份第一天是周几与最后一天并获取月前与月后填补数组
          _getFirstDaysForMonth(year,month){let weekdays = new Date(`${year}-${month}-01`).getDay()
            let beforeMonthDaysArr = []
            let currentMonthDaysArr = []
            let afterMonthDaysArr = []
            // 获取上一月总天数
            if(month){let temp=new Date(year,month-1,1);
              let day = new Date(temp.getTime() - 864e5).getDate();
              for(let i=0;i<day;i++){
                beforeMonthDaysArr.push({
                  year:year,
                  month:month-1,
                  day:i+1
                })
              }
              beforeMonthDaysArr.splice(0,beforeMonthDaysArr.length-weekdays)
            }else{//12 月}
            // 获取当前月总天数
            let temp=new Date(year,month,1);
            let day = new Date(temp.getTime() - 864e5).getDate();
            let afterWeekdays = new Date(`${year}-${month}-${day}`).getDay()
            for(let i=0;i<day;i++){
              currentMonthDaysArr.push({
                year:year,
                month:month,
                day:i+1
              })
            }

            if(beforeMonthDaysArr.length+currentMonthDaysArr.length+6-afterWeekdays===35){for(let i=0;i<13-afterWeekdays;i++){
                afterMonthDaysArr.push({
                  year:year,
                  month:month+1,
                  day:i+1
                })
              }
            }else{for(let i=0;i<6-afterWeekdays;i++){
                afterMonthDaysArr.push({
                  year:year,
                  month:month+1,
                  day:i+1
                })
              }
            }
            this.daysArr = [...beforeMonthDaysArr,...currentMonthDaysArr,...afterMonthDaysArr]
            // 获取下一月总天数
          }
        },
        watch:{'month'(){if(this.month===0){
              this.month = 12
              this.year--
            }
            if(this.month===13){
              this.month = 1
              this.year++
            }
            this._getDaysForMonth(this.year,this.month,1)
          }
        },
        computed:{_CurrentDate(){let date = new Date()
            let {day,month ,year} = {day:date.getDate(),
              month:date.getMonth()+1,
              year:date.getFullYear()}
            return {day,month ,year}
          }
        }
    }
</script>

css=>
tips: 我用的 css 解析器是 stylus,用 less 和 sass 之类的大佬麻烦动动你们的小手加个花括号

<style scoped lang="stylus">
  .calender
    text-align center
    min-width 280px
    height 400px
    .calender-title-wrapper
      height 44px
      line-height 44px
      .calender-title
        display flex
        margin 0 auto
        width 280px
        .calender-arrow-item
          flex 0 0 40px
          cursor pointer
          &.left1
            background url("icon_left.svg") no-repeat center
          &.left2
            background url("icon_left2.svg") no-repeat center
          &.right1
            background url("icon_right.svg") no-repeat center
          &.right2
            background url("icon_right2.svg") no-repeat center
        .calender-Date-text
          flex 1
          color #424242
          font-size 16px
    .calender-content-wrapper
      .calender-days-wrapper
        display flex
        height 44px
        line-height 44px
        margin-bottom 11px
        .calender-days
          flex 1
          color #A7A7A7
          font-size 14px
      .calender-content
        .calender-item-wrapper
          position relative
          float left
          width 14.285%
          height 44px
          line-height 44px
          color #686868
          cursor pointer
          .tem-wrapper
            position absolute
            left 50%
            height 10px
            width 40px
            transform translate3d(-50%,35px,0)
            font-size 0
            .temp
              position relative
              display inline-block
              line-height 10px
              width 6px
              height 6px
              background #4fff41
              border-radius 3px
              top -16px
              margin 0 2px
          .calender-item
            position absolute
            left calc(50% - 20px)
            top calc(50% - 20px)
            height 40px
            width 40px
            border-radius 20px
            line-height 40px
            &:hover
              background #FFC912
          &.no-current-day
            color #DCDCDC
          &.currentDay
            color #ffffff
            .calender-item
              background #FFC912
          &.active
            color #ffffff
            .calender-item
              background #FFC912
      .calender-mask-wrapper
        height 37px
        .calender-mask-item
          height 37px
          line-height 37px
          width 25%
          float left
          .tem
            margin-right 3px
            display inline-block
            height 8px
            width 8px
            border-radius 4px
          .text
            font-size 12px
</style>

给各位大佬呈上 APP.vue 中的调用,以及参数详细用法
App.vue 中的 @test1 事件是在在入参 markArr 中定义的,其中在 markArr 中 color 是标记日期的样式颜色,name 是显示的样式名称,days 是日期数组,clickEvent 就是自定义事件名称

<template>
  <div id="app">
    <calender
      @test1="test"
      :markArr="[
      {
        color:'#76ff19',
        name:'测试一',
        days:['2019-5-8','2019-5-11','2019-5-14','2019-5-22'],
        clickEvent:'test1'
      },
      {
        color:'#ff1323',
        name:'测试二',
        days:['2019-5-9','2019-5-11','2019-5-3','2019-5-1'],
        clickEvent:'test2'
      },
      {
        color:'#ffc518',
        name:'测试三',
        days:['2019-5-18','2019-5-11','2019-5-7','2019-5-4'],
        clickEvent:'test3'
      },
      {
        color:'#ff26fc',
        name:'测试四',
        days:['2019-5-28','2019-5-11','2019-5-30','2019-5-31'],
        clickEvent:'test4'
      }
      ]"

    ></calender>
    <!--<backwards></backwards>-->
  </div>
</template>

<script>
  import calender from './components/calender/calender'
  import backwards from './components/backwards/backwards'
export default {
  name: 'App',
  components:{
    calender,
    // backwards
  },
  methods:{test(item){console.log(item)
    }
  }
}
</script>

<style lang="stylus">
#app
  position absolute
  height 100%
  width 100%
</style>

丢个成果图

是不是很丑?我故意的,想要好看自己改去吧,大致功能全都实现了

正文完
 0