vue 自己捣鼓周日程日历组件(WSchedule)

需求:想要一个周日程表,记录每天的计划,点击可查看详情。可自定义时间段通过后台获取时间段显示

分析:

  1. 通过需求,我联想到在大学期间就用过超级课程表app这款软件,其中课表和这个需求很像,只不过这个需求第一列的时间段是自定义的,不是上午下午两个,但是原理都差不多
  2. 原本想找一些第三方插件使用,由于时间充足,而且自己也想封装成一个组件方便以后或许会碰到类似的需求,于是自己手动写了一个日程日历。

效果如下:自己写的肯定有不足的地方,如有问题请大佬们提出,进行改进

 优化修改:数据量大时,格子显示太长问题,这里进行了优化,如果超过2个就进行展开与收缩操作

 

 

 

 npm包 使用文档点击这里

npm i wschedule -S

 源码已经更新到github上了,点击这里欢迎大佬们来提问

 封装组件

使用

<template>
  <div id="app">
    <WSchedule:planList="timePeriodList" :isFirstDayOfMondayOrSunday="1" @handleDetail="handleDetail"
          @handleCardDetail="handleCardDetail" @changeWeek="changeWeek">
      <template v-slot:thing="{row}">
        <span>上课日期:{{ row.date }}</span>
        <span>上课时间:{{ row.timePeriod }}</span>
        <span>上课老师:{{ row.watchman }}</span>
        <span>上课地点:{{ row.place }}</span>
      </template>
    </WSchedule>
  </div>
</template>

<script>

import Vue from 'vue'
import WSchedule from 'wschedule'
import "wschedule/dist/wschedule.css";
Vue.use(WSchedule)

export default {
  name: 'App',
  components: {
  },
  data() {
    /**
     * 获取当天时间
     * @returns {string}
     */
    function getCurDay(num = 0) {
      var datetime = new Date();
      var year = datetime.getFullYear();
      var month = datetime.getMonth() + 1 < 10 ? "0" + (datetime.getMonth() + 1) : datetime.getMonth() + 1;
      let day = datetime.getDate()
      if ((day + num) > 0) {
        day = (day + num) < 10 ? "0" + (datetime.getDate() + num) : datetime.getDate() + num;
      } else {
        day = (day - num) < 10 ? "0" + (datetime.getDate() - num) : datetime.getDate() - num;
      }
      return `${year}-${month}-${day}`
    }
    return {
            timePeriodList: [
        {
          timePeriod: '8:00~10:00',
          schedule: [
            {
              isExpend: false,
              [getCurDay()]: [
                {
                  timePeriod: '8:00~10:00',
                  date: getCurDay(),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 1,
                },
                {
                  timePeriod: '8:00~10:00',
                  date: getCurDay(),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 2,
                },
                {
                  timePeriod: '8:00~10:00',
                  date: getCurDay(),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 3,
                },
              ],
            },
            {
              isExpend: false,
              [getCurDay(-1)]: [
                {
                  id: 1,
                  timePeriod: '8:00~10:00',
                  date: getCurDay(-1),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 1,
                },
              ]
            }
          ]
        },
        {
          timePeriod: '12:00~14:00',
          schedule: [
            {
              isExpend: false,
              [getCurDay()]: [
                {
                  timePeriod: '12:00~14:00',
                  date: getCurDay(),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 2,
                },
                {
                  timePeriod: '12:00~14:00',
                  date: getCurDay(),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 3,
                },
              ],
            },
            {
              isExpend: false,
              [getCurDay(-1)]: [
                {
                  timePeriod: '12:00~14:00',
                  date: getCurDay(-1),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 1,
                },
                {
                  timePeriod: '实验室1',
                  date: getCurDay(-1),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 1,
                },
                {
                  timePeriod: '实验室1',
                  date: getCurDay(-1),
                  course: '大学英语',
                  watchman: '井底的蜗牛',
                  place: '测试地点',
                  status: 1,
                },
              ]
            }
          ]
        },
        {
          timePeriod: '14:00~16:00',
          schedule: []
        },
      ],
    }
  },
  methods: {
    /**
     * 点击详情
     * @param row
     */
    handleDetail(row){
      console.log(row)
    },
    /**
     * 点击卡片查看全部内容
     */
    handleCardDetail(row) {
      console.log(row)
    },
    /**
     * 切换周
     * @param date
     */
    changeWeek(date){
      console.log(date)
    }
  },
}
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

组件完整代码

Html代码

<template>
  <div class="course-week">
    <div class="week-top">
      <div class="week-btn-wrap">
        <span @click="getLastWeek">上周</span>
        <span @click="getCurWeek">本周</span>
        <span @click="getNextWeek">下周</span>
      </div>
      <span class="w-today-date"> {{ todayDate }}</span>
      <div class="w-choose-status">
        <div v-for="sta in cardStatus">
          <span class="square" :style="{background:sta.color}"></span>
          <span class="title">{{ sta.title }}</span>
        </div>
      </div>
    </div>
    <div class="week-table">
      <div class="table-header">
        <div class="table-week">
          <template v-for="(item,index) of weeks">
            <span class="w-first" v-if="index===0" :key="index">{{ item }}</span>
            <span v-else :key="index">{{ item }}</span>
          </template>
        </div>
        <div class="w-table-date">
          <template v-for="(item,index) of months">
            <span class="w-first" v-if="index===0" :key="index">
            </span>
            <template v-else>
              <span :key="index" class="w-day-item" :class="{'w-isCurDate':item&&item.isCurDate}">
                {{ `${item && item.isCurDate ? item && item.showDate + '(今天)' || '' : item && item.showDate || ''}` }}
              </span>
            </template>
          </template>
        </div>
      </div>
      <div class="w-time-period-list">
        <ul class="w-time-period-row">
          <!--循环时段,看时段有多少个-->
          <template v-if="planList.length>0">
            <li class="w-time-period-col" v-for="(period,p_index) in planList"
                :key="`period${p_index}`">
              <!--第一列显示时段-->
              <div class="w-time-period"> {{ period.timePeriod }}</div>
              <!-- 后面显示周一到周日的计划-->
              <div class="w-row-day">
                <!-- 循环显示每周的日期-->
                <template v-for="(month,m_index) of months">
                  <!-- v-if="month" 去除数据处理的时候移除数组第一个为empty的问题-->
                  <div v-if="month" :key="`month${m_index}`" class="w-things" @click="handleCardDetail(month,period)">
                    <!-- 循环每个时间段的计划-->
                    <template v-for="(card,t_index) of period.schedule">
                      <template v-for="(single,sIndex) in card[month.date]">

                        <template v-if="!card.isExpend">
                          <div v-if="single.date===month.date&&sIndex<2"
                               :key="`thing${sIndex}`"
                               class="w-thing-item"
                               @click="handleDetail(single)"
                               :style="{background: cardStatus[single.status].color}">
                            <slot name="thing" :row="single"></slot>
                          </div>
                        </template>
                        <template v-if="card.isExpend">
                          <div v-if="single.date===month.date"
                               :key="`thing${sIndex}`"
                               class="w-thing-item"
                               @click="handleDetail(single)"
                               :style="{background: cardStatus[single.status].color}">
                            <slot name="thing" :row="single"></slot>
                          </div>
                        </template>
                        <div class="w_expand"
                             v-if="card[month.date].length>2&&(card[month.date].length-1)===sIndex&&!card.isExpend&&single.date===month.date"
                             @click="handleExpand(card)">展开
                        </div>
                        <div class="w_shrink"
                             v-if="card[month.date].length>2&&(card[month.date].length-1)===sIndex&&card.isExpend&&single.date===month.date"
                             @click="handleExpand(card)">收缩
                        </div>
                      </template>

                    </template>

                  </div>
                </template>
              </div>
            </li>
          </template>
          <div class="w-noMore" v-else><span>暂无数据</span></div>
        </ul>
      </div>
    </div>
  </div>
</template>

js代码  MyTools点击这里

import { formatDate, getCurDay } from '../utils/MyTools'

export default {
  name: 'WSchedule',
  props: {
    planList: {
      type: Array,
      default: []
    },
    cardStatus: {
      type: Object,
      default: () => {
        return {
          1: {
            title: '已过期',
            color: '#9CADADB7'
          },
          2: {
            title: '进行中',
            color: '#FF6200'
          },
          3: {
            title: '未开始',
            color: '#3291F8'
          },
        }
      }
    },
    isFirstDayOfMondayOrSunday: {
      type: Number,
      default: 1,
    }
  },
  data () {
    return {
      weeks: [
        '时段', '周一', '周二', '周三', '周四', '周五', '周六', '周日',
      ],
      todayDate: '',
      months: [],
      curDate: '',
      nowDate: new Date(),
    }
  },
  watch: {
    isFirstDayOfMondayOrSunday: {
      handler (val) {
        if (val > 1) {
          let arr = ['周一', '周二', '周三', '周四', '周五', '周六', '周日']
          const arr1 = arr.slice(val - 1)
          const arr2 = arr.slice(0, val - 1)
          this.weeks = ['时段', ...arr1, ...arr2]
        }
      },
      immediate: true
    }
  },
  mounted () {
    this.getCurWeek()
  },
  methods: {
    //展开与缩放操作
    handleExpand (row) {
      row.isExpend = !row.isExpend
    },
    /**
     * 获取 时间
     * @param time
     */
    getWeek (time) {
      this.curDate = new Date(time)
      //当前是周几
      const whichDay = time.getDay()
      let num = 0
      if (this.isFirstDayOfMondayOrSunday <= whichDay) {
        num = this.isFirstDayOfMondayOrSunday
      } else {
        num = this.isFirstDayOfMondayOrSunday - 7
      }
      const weekDay = time.getDay() - num
      time = this.addDate(time, weekDay * -1)
      for (let i = 0; i < 7; i++) {
        const { year, month, day } = formatDate(i === 0 ? time : this.addDate(time, 1))
        this.months.push({
          date: `${year}-${month}-${day}`,
          showDate: `${month}-${day}`,
          timestamp: new Date(`${year}-${month}-${day}`).getTime()
        })
      }
      this.months.sort((a, b) => a.timestamp - b.timestamp)
      delete this.months[0]
      this.todayDate = `${this.months[1].date} ~ ${this.months[this.months.length - 1].date}`
    },
    /**
     * 处理日期
     * @param date
     * @param n
     * @returns {*}
     */
    addDate (date, n) {
      date.setDate(date.getDate() + n)
      return date
    },
    /**
     * 上周
     */
    getLastWeek () {
      const date = this.addDate(this.curDate, -7),
          { year, month, day } = formatDate(date),
          dateObj = {
            date: `${year}-${month}-${day}`,
            timestamp: new Date(`${year}-${month}-${day}`).getTime()
          }
      this.dealDate(date)
      this.$emit('changeWeek', dateObj)
    },
    /**
     * 本周
     */
    getCurWeek () {
      const { year, month, day } = formatDate(new Date()),
          dateObj = {
            date: `${year}-${month}-${day}`,
            timestamp: new Date(`${year}-${month}-${day}`).getTime()
          }
      this.dealDate(new Date())
      this.$emit('changeWeek', dateObj)
    },
    /**
     * 下周
     */
    getNextWeek () {
      const date = this.addDate(this.curDate, 7),
          { year, month, day } = formatDate(date),
          dateObj = {
            date: `${year}-${month}-${day}`,
            timestamp: new Date(`${year}-${month}-${day}`).getTime()
          }
      this.dealDate(date)
      this.$emit('changeWeek', dateObj)
    },
    /**
     * 显示当天日期状态
     * @param date
     */
    dealDate (date) {
      this.months = ['']
      this.getWeek(date)
      const curDate = getCurDay()
      this.months.forEach(item => {
        item.isCurDate = item.date === curDate
      })
    },
    /**
     * 点击卡片子内容查看详情
     * @param row
     */
    handleDetail (row) {
      this.$emit('handleDetail', row)
    },
    /**
     * 点击卡片查看全部内容
     * @param month
     * @param period
     */
    handleCardDetail (month, period) {
      this.$emit('handleCardDetail', { ...month, ...period })
    }
  }
}
</script>

css 代码

<style>
ul {
  list-style: none;
}

ul, li {
  margin: 0;
  padding: 0;
}

.course-week {
  width: 100%;
  border: 1px solid #ddd;
  padding: 1%;
  box-sizing: border-box;
}

.week-top {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  height: 40px;
  padding: 0 1%;
  box-sizing: border-box;

}

.week-top .week-btn-wrap {
  width: 200px;
  display: flex;
  justify-content: space-around;
  color: #409EFF;
}

.week-top .week-btn-wrap span {
  cursor: pointer;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 15px;
}

.w-today-date {
  font-weight: bold;
  font-size: 16px;
}

.w-choose-status {
  display: flex;
  justify-content: flex-end;
  width: 200px;
}

.w-choose-status > div {
  width: 100%;
  flex: 1;
  display: flex;
  padding: 0 2%;
  white-space: nowrap;
  line-height: 20px;
  box-sizing: border-box;
}

.w-choose-status > div .square {
  display: flex;
  width: 16px;
  height: 16px;
  border-radius: 4px;
  box-sizing: border-box;
}

.w-choose-status > div .title {
  display: flex;
  align-items: center;
  line-height: 16px;
  padding-left: 4px;
  font-size: 14px;
  box-sizing: border-box;
}


.week-table {
  display: flex;
  flex-direction: column;
}

.week-table .table-header {
  width: 100%;
  height: 80px;
  background: #EAEDF2;
  display: flex;
  flex-direction: column;
  align-items: center;
  border-bottom: 1px solid #EAEDF2;
  box-sizing: border-box;
}

.table-header .w-table-date, .table-week {
  width: 100%;
  height: 40px;
  text-align: left;
  display: flex;
  justify-content: center;
  align-items: center;

}

.table-header .w-table-date > span, .table-week > span {
  flex: 1;
  color: #000;
  height: 100%;
  font-size: 14px;
  display: flex;
  justify-content: center;
  align-items: center;
  font-weight: bold;
}


.w-table-date .w-day-item, .table-week .w-day-item {
  color: #000;
  font-size: 14px;
  display: flex;
  justify-content: center;
  align-items: center;
}

.week-table .w-time-period-list {
  width: 100%;
}

.w-time-period-list .w-time-period-row {
  width: 100%;
  min-height: 60px;
}

.w-time-period-col {
  width: 100%;
  min-height: 60px;
  display: flex;
}

.w-time-period-col .w-time-period {
  width: 12.5%;
  display: flex;
  justify-content: center;
  align-items: center;
  border-left: 1px solid #EAEDF2;
  border-bottom: 1px solid #EAEDF2;
  box-sizing: border-box;
}

.w-time-period-col .w-row-day {
  width: 87.5%;
  display: flex;
  justify-content: center;
}

.w-row-day .w-things {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  border-left: 1px solid #EAEDF2;
  border-bottom: 1px solid #EAEDF2;
  box-sizing: border-box;
}

.w-row-day .w-things:last-child {
  border-right: 1px solid #EAEDF2;
}

.w-things .w-thing-item {
  display: flex;
  width: 80%;
  font-size: 14px;
  flex-direction: column;
  justify-content: space-around;
  min-height: 90px;
  border-radius: 10px;
  margin: 2% 1%;
  padding: 1% 2%;
  cursor: pointer;
  color: #fff;
  background: #FF6200;
  box-sizing: border-box;
  transition: all 1s linear .5s;
}


.w-isCurDate {
  color: #FF2525 !important;
}

.w-noMore {
  min-height: 200px;
  padding: 2%;
  display: flex;
  justify-content: center;
  align-items: center;
  border: 1px solid rgba(156, 173, 173, 0.3);
  color: #9CADADB7;
  box-sizing: border-box;
}

.w_expand, .w_shrink {
  color: #0A98D5;
  cursor: pointer;
  width: 100%;
  padding: 2% 0;
  display: flex;
  justify-content: center;
  align-items: center;
}
</style>

优化:

1、卡片空白数据无法获取,添加获取卡片全部详细数据方法handleCardDetail

2、日历的显示问题优化,比如第一天想显示周日,或者显示周三,这次加了一个属性isFirstDayOfMondayOrSunday,传入对应的数字,周一就是1,周日就是7,以此类推

3、优化 每个日期卡片显示多调数据时,可以进行展开、缩放功能问题,数据格式进行改进 

  • 18
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 31
    评论
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值