日期計(jì)劃

gifhome_906x648_8s.gif
  • 計(jì)算年月日
    年月日的計(jì)算,有兩種模式,一種是只計(jì)算當(dāng)月日期,另一種則是將整年的日期都計(jì)算出來。在本篇文章里我想著重記錄第一種寫法。


    image.png

我們先來個(gè)看圖說話,這個(gè)二月份有28天,1號(hào)是星期五。那是不是說,我們只要從周五開始,按順序渲染出28個(gè)'main__block'就好了呢?其實(shí)就是這樣,關(guān)鍵是怎么把我們的1號(hào)定位到周五,只要這個(gè)能夠準(zhǔn)確定位到,我們的日歷自然就出來了。

// 定義每個(gè)月的天數(shù),如果是閏年第二月改為29天
// year=2019;month=1(js--month=0~11)
let daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
    daysInMonth[1] = 29;
}
// 獲得指定年月的1號(hào)是星期幾
let targetDay = new Date(year, month, 1).getDay();
// 將要在calendar__main中渲染的列表
let total_calendar_list = [];
let preNum = targetDay;
// 首先先說一下,我們的日期是(日--六)這個(gè)順序也就是(0--6)
// 有了上述的前提我們可以認(rèn)為targetDay為多少,我們就只需要在total_calendar_list的數(shù)組中push幾個(gè)content為''的obj作為占位
if (targetDay > 0) {
    for (let i = 0; i < preNum; i++) {
        let obj = {
            type: "pre",
            content: ""
        };
        total_calendar_list.push(obj);
    }
}

這樣一來,1號(hào)的位置自然而然就到了我們需要的星期五了,接下來就只需要按順序渲染就ok啦。下面是剩下日期數(shù)組填充,填充完畢之后return出來供我們view層使用。
for (let i = 0; i < daysInMonth[month]; i++) {
let obj = {
type: "normal",
content: i + 1
};
total_calendar_list.push(obj);
}
nextNum = 6 - new Date(year, month+1, 0).getDay()
// 與上面的type=pre同理
for (let i = 0; i < nextNum; i++) {
let obj = {
type: "next",
content: ""
};
total_calendar_list.push(obj);
}
return total_calendar_list;

  • 開發(fā)日歷相關(guān)功能
    如何選擇上一個(gè)月或下一個(gè)月?
data() {
    return {
        // ...
        selectedYear: new Date().getFullYear(),
        selectedMonth: new Date().getMonth(),
        selectedDate: new Date().getDate()
    };
}

handlePreMonth() {
    if (this.selectedMonth === 0) {
        this.selectedYear = this.selectedYear - 1
        this.selectedMonth = 11
        this.selectedDate = 1
    } else {
        this.selectedMonth = this.selectedMonth - 1
        this.selectedDate = 1
    }
}

handleNextMonth() {
    if (this.selectedMonth === 11) {
        this.selectedYear = this.selectedYear + 1
        this.selectedMonth = 0
        this.selectedDate = 1
    } else {
        this.selectedMonth = this.selectedMonth + 1
        this.selectedDate = 1
    }
}

就是這么簡(jiǎn)單,需要注意的點(diǎn)是跨年的時(shí)間轉(zhuǎn)換,我們需要在變更月份的同時(shí)把年份也改變,這樣才能渲染出正確的日期。
也許大家會(huì)有疑問,怎么變更了月份或年份之后不需要重新計(jì)算一次日期呢?其實(shí)是有計(jì)算的,不知大家是否還記得,vue可是數(shù)據(jù)驅(qū)動(dòng)變更的,我們只需要關(guān)注數(shù)據(jù)的變更即可,其他東西vue都會(huì)幫我們解決。

  • 如果選中某一天
handleDayClick(item) {
    if (item.type === 'normal') {
        // do anything...
        this.selectedDate = Number(item.content)
    }
}

在渲染列表的時(shí)候我就給每一個(gè)block綁定了click事件,這樣做的好處就是調(diào)用十分方便,點(diǎn)擊每一個(gè)block的時(shí)候,可以獲取該block的內(nèi)容然后do anything you like
當(dāng)然我們也可以給外層的父級(jí)元素綁定事件監(jiān)聽,通過事件流來解決每個(gè)block的點(diǎn)擊事件,這里看個(gè)人習(xí)慣~畢竟元素?cái)?shù)量不是特別多
日歷完成之后再進(jìn)行相關(guān)的數(shù)據(jù)操作,以下是實(shí)現(xiàn)gif圖的完整頁(yè)面代碼

<template>
  <div class="mywk__calendar">
    <div class="calendar">
      <div class="calendar__header">
        <div class="header__pre" @click="handlePreMonth">

        </div>
        <div class="header__title">
          {{selectedYear}}年{{selectedMonth + 1}}月{{selectedDate}}日
        </div>
        <div class="header__next" @click="handleNextMonth">

        </div>
        <div class="rightStyle">
          <div class="boxStyle">
            <div class="fontStyle" style="background:#1976d2;"></div>新建計(jì)劃
          </div>
          <div class="boxStyle">
            <div class="fontStyle" style="background:#4caf50;"></div>計(jì)劃進(jìn)行中
          </div>
          <div class="boxStyle">
            <div class="fontStyle" style="background:#3f51b5"></div>計(jì)劃已完成
          </div>
        </div>
      </div>

      <div class="calendar__main">
        <div class="main__block-head" v-for="(item, index) in calendarHeader" :key="index">
          {{item}}
        </div>

        <div :class="`main__block ${(item.type === 'pre' || item.type === 'next') ? 'main__block-not' : ''} ${item.type2 === 'pre' ? 'extraStyle' : ''} ${(item.content === selectedDate && item.type === 'normal') && 'main__block-today'}`"
             @click="handleDayClick(item)" v-for="(item, index) in total_calendar_list" :key="item.type + item.content + `${index}`">
          <p class="dateNum">{{item.content}}</p>
          <div class="content" v-for="(list, index) in item.arr" :key="index">
            <el-tooltip class="item" effect="dark" :content="list.storeCode+'-'+list.storeName" placement="left-start">
              <el-button class="btnClass" :style="changeColor(list)">{{list.storeCode}}-{{list.storeName}}</el-button>
            </el-tooltip>
            <div class="cancelDiv" v-if="list.state === 1"></div>
            <i @click="cancelStore(list)" v-if="list.state === 1" class="el-icon-circle-close cancelIcon"></i>
          </div>
          <img  @click="chooseStore(item)" class="iconClass" :style="`${item.content === selectedDate && item.type === 'normal' && item.type2 !== 'pre' ? 'top:4px' : 'display:none'}`" src="@/../static/add22.png" alt="">
        </div>
      </div>
    </div>

    <el-dialog
      title="選擇店鋪"
      :visible.sync="dialogVisible"
      width="380px">
      <el-autocomplete
        v-loading="loading"
        class="inline-input"
        v-model="searchInfo.storeName"
        :fetch-suggestions="querySearch"
        placeholder="請(qǐng)選擇店鋪列表"
        size="mini"
        clearable
        ref="input"
        :autofocus="focus"
        style="width:330px"
        @select="handleSelect"
      >
        <template slot-scope="props">
          <div class="name">{{props.item.storeCode}}-{{ props.item.storeName }}</div>
        </template>
      </el-autocomplete>
      <span slot="footer" class="dialog-footer">
        <el-button @click="cancel" size="mini">取 消</el-button>
        <el-button type="primary" @click="confirm" size="mini">確 定</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import Device2 from '@/api/device2'
export default {
  data () {
    return {
      calendarHeader: ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"],
      selectedYear: new Date().getFullYear(),
      selectedMonth: new Date().getMonth(),
      selectedDate: new Date().getDate(),
      dialogVisible: false,
      reqStore: [],
      searchInfo: {
        storeName: '',
        storeId: '',
        storeCode: '',
        planDate: ''
      },
      focus: false,
      event: [],
      total_calendar_list: [],
      loading: false,
      planList: []
    }
  },
  mounted () {
    this.displayDaysPerMonthT(this.selectedYear)
  },
  methods: {
    changeColor (list) {
      switch (list.state) {
        case 1:
          return 'background:#1976d2;'
        case 2:
          return 'background:#4caf50;'
        case 3:
          return 'background:#3f51b5'
        default:
          return 'background:#fff'
      }
    },
    cancelStore (info) {
      if (info.state !== 1) {
        this.$message.warning('任務(wù)只有在新建狀態(tài)才能刪除')
        return
      }
      this.$confirm('此操作將永久刪除該計(jì)劃, 是否繼續(xù)?', '提示', {
        confirmButtonText: '確定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        var url = '/abnormal/plan/delete/' + info.planItemId
        Device2.req2(url, 'post').then(res => {
          this.$message.success('刪除成功')
          this.displayDaysPerMonthT(this.selectedYear)
          this.reqStore.push(info)
        })
      })
    },
    confirm () {
      var url = '/abnormal/plan/save'
      var json = this.searchInfo
      if (!this.searchInfo.storeName) {
        this.$message.warning('請(qǐng)選擇店鋪再進(jìn)行保存操作')
        return
      }
      Device2.req2(url, 'post', json).then(res => {
        if (res.data.code === 0) {
          this.$message.success('新增計(jì)劃成功')
          this.dialogVisible = false
          this.displayDaysPerMonthT(this.selectedYear)
          this.reqStore.forEach((item, index) => {
            if (item.storeId === json.storeId) {
              this.reqStore.splice(index, 1)
            }
            this.clearSearchInfo()
          })
        }
      })
    },
    chooseStore (info) {
      this.dialogVisible = true
      this.searchInfo.planDate = info.date + ' 00:00:00'
      var that = this
      var url = '/abnormal/plan/storeSelect'
      this.loading = true
      Device2.req(url, 'get').then(res => {
        this.loading = false
        if (res.data.code === 0) {
          var arr = res.data.stores
          this.reqStore = arr
          setTimeout(function () {
            that.$nextTick(() => {
              that.$refs.input.$el.querySelector('input').focus()
            })
          }, 300)
        }
      })
    },
    cancel () {
      this.dialogVisible = false
      this.focus = false
      this.clearSearchInfo()
    },
    clearSearchInfo () {
      this.searchInfo.storeName = ''
      this.searchInfo.storeCode = ''
      this.searchInfo.storeId = ''
      this.searchInfo.planDate = ''
    },
    displayDaysPerMonthT (year) {
      var month2 = this.selectedMonth + 1
      if (month2 < 10) {
        month2 = '0' + month2
      }
      var url = '/abnormal/plan/' + this.selectedYear + month2
      this.loading = true
      Device2.req(url, 'get').then(res => {
        this.loading = true
        if (res.data.code === 0) {
          this.loading = false
          var arr = res.data.planList
          this.planList = arr
          // 定義每個(gè)月的天數(shù),如果是閏年第二月改為29天
          let daysInMonth = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]

          if ((year % 4 === 0 && year % 100 !== 0) || year % 400 === 0) {
            daysInMonth[1] = 29
          }
          // 定義上個(gè)月的天數(shù)
          let daysPreMonth = [].concat(daysInMonth)
          daysPreMonth.unshift(daysPreMonth.pop())
          // 定義這個(gè)月月初需要添加的天數(shù)
          let addDaysFromPreMonth = new Array(12).fill(null).map((item, index) => {
            let day = new Date(year, index, 1).getDay()
            if (day === 0) {
              return 6
            } else {
              return day - 1
            }
          })
          // 定義的日歷列表數(shù)組
          var arr2 = new Array(12).fill([])
          let total_calendar_list = arr2.map((month, monthIndex) => {
            let addDays = addDaysFromPreMonth[monthIndex] + 1,
              daysCount = daysInMonth[monthIndex],
              daysCountPre = daysPreMonth[monthIndex],
              monthDate = []
            if (addDays >= 7) {
              addDays = addDays - 7
            }
            for (; addDays > 0; addDays--) {
              var days = daysCountPre--
              let obj = {
                content: days,
                type: 'pre',
                arr: []
              }
              monthDate.unshift(obj)
            }
            for (let i = 1; i <= daysCount; ++i) {
              let day = i
              if (i < 10) {
                day = '0' + i
              }
              let obj = {
                content: i,
                type: 'normal',
                date: this.selectedYear + '-' + month2 + '-' + day,
                arr: []
              }
              if (this.compareDate(i)) {
                obj.type2 = 'pre'
              }
              arr.forEach(item => {
                let planDate = new Date(item.planDate)
                var year = planDate.getFullYear()
                var month = planDate.getMonth()
                var dayTime = planDate.getDate()
                this.reqStore.forEach((item2, index) => {
                  if (item2.storeId === item.storeId) {
                    this.reqStore.splice(index, 1)
                  }
                })
                if (this.selectedYear === year && this.selectedMonth === month) {
                  if (i === dayTime) {
                    obj.arr.push({
                      storeName: item.storeName,
                      storeCode: item.storeCode,
                      storeId: item.storeId,
                      planDate: item.planDate,
                      state: item.state,
                      planItemId: item.planItemId
                    })
                  }
                }
              })
              monthDate.push(obj)
            }
            if (monthDate.length > 35) {
              for (let i = 42 - monthDate.length, j = 0; j < i; ) {
                let obj = {
                  content: ++j,
                  type: 'next',
                  arr: []
                }
                arr.forEach(item => {
                  let planDate = new Date(item.planDate)
                  var year = planDate.getFullYear()
                  var month = planDate.getMonth()
                  var dayTime = planDate.getDate()
                  if (this.selectedYear === year && this.selectedMonth === month) {
                    if (i === dayTime) {
                      obj.arr.push({
                        storeName: item.storeName,
                        storeCode: item.storeCode,
                        storeId: item.storeId,
                        planDate: item.planDate,
                        state: item.state,
                        planItemId: item.planItemId
                      })
                    }
                  }
                })
                monthDate.push(obj)
              }
            } else {
              for (let i = 35 - monthDate.length, j = 0; j < i;) {
                let obj = {
                  content: ++j,
                  type: 'next',
                  arr: []
                }
                arr.forEach(item => {
                  let planDate = new Date(item.planDate)
                  var year = planDate.getFullYear()
                  var month = planDate.getMonth()
                  var dayTime = planDate.getDate()
                  if (this.selectedYear === year && this.selectedMonth === month) {
                    if (i === dayTime) {
                      obj.arr.push({
                        storeName: item.storeName,
                        storeCode: item.storeCode,
                        storeId: item.storeId,
                        planDate: item.planDate,
                        state: item.state,
                        planItemId: item.planItemId
                      })
                    }
                  }
                })
                monthDate.push(obj)
              }
            }
            return monthDate
          })
          this.total_calendar_list = total_calendar_list[this.selectedMonth]
        }
      }).catch(err => {
        this.loading = false
      })
    },
    handleDayClick (item) {
      if (item.type === 'normal' && !item.type2) {
        // do anything...
        this.selectedDate = Number(item.content)
      }
    },
    handlePreMonth () {
      if (this.selectedMonth === 0) {
        this.selectedYear = this.selectedYear - 1
        this.selectedMonth = 11
      } else {
        this.selectedMonth = this.selectedMonth - 1
      }
      this.isSelectDate()
      this.displayDaysPerMonthT(this.selectedYear)
    },
    handleNextMonth () {
      if (this.selectedMonth === 11) {
        this.selectedYear = this.selectedYear + 1
        this.selectedMonth = 0
      } else {
        this.selectedMonth = this.selectedMonth + 1
      }
      this.isSelectDate()
      this.displayDaysPerMonthT(this.selectedYear)
    },
    compareDate (count) {
      var date = new Date()
      var year = date.getFullYear()
      var month = date.getMonth()
      var day = date.getDate()
      var result
      if (this.selectedYear <= year && this.selectedMonth < month) {
        result = true
      } else if (this.selectedMonth === month && count < day) {
        result = true
      } else {
        result = false
      }
      return result
    },
    isSelectDate () {
      var date = new Date()
      var year = date.getFullYear()
      var month = date.getMonth()
      var day = date.getDate()
      if (this.selectedYear === year && this.selectedMonth === month) {
        this.selectedDate = day
      } else {
        this.selectedDate = 1
      }
    },
    querySearch (queryString, cb) {
      var reqStore = this.reqStore
      var results = queryString ? reqStore.filter(this.createFilter(queryString)) : reqStore
      // // 調(diào)用 callback 返回建議列表的數(shù)據(jù)
      cb(results)
    },
    handleSelect (item) {
      this.searchInfo.storeName = item.storeCode + '-' + item.storeName
      this.searchInfo.storeId = item.storeId
      this.searchInfo.storeCode = item.storeCode
    },
    createFilter (queryString) {
      return (item) => {
        return ((item.storeCode + '-' + item.storeName).toLowerCase().indexOf(queryString.toLowerCase()) !== -1)
      }
    }
  }
}
</script>

<style lang="scss">
  @function pxWithVw($n) {
    @return 100vw * $n / 375;
  }

  @function pxWithVwMax($n) {
    @return 480px * $n / 375;
  }
  .rightStyle{
    position:absolute;right:10px;top:10px;
    display: flex;
  }
  .boxStyle{
    display: flex;align-items: center;font-size:12px;
  }
  .fontStyle{
    width:18px;height:10px;margin:0 10px;border-radius:5px;
  }
  p{
    padding: 0;
  }
  .content{
    position: relative;
  }
  .cancelDiv{
    position: absolute;
    right:6px;
    top:16px;
    width: 10px;
    height:10px;
    border-radius:50%;
    background: #ffffff;
    z-index:1
  }
  .cancelIcon{
    color:red;
    position: absolute;
    right:3px;
    top:14px;
    font-size:16px;
    cursor: pointer;
    z-index:2
  }
  .btnClass{
    width:100% !important;
    padding: 7px 5px !important;
    text-overflow: ellipsis;
    overflow: hidden;
    white-space: nowrap;
    font-size: 11px !important;
    margin-top:10px !important;
    color:#fff !important;
  }
  .dateNum{
    position: absolute;
    top:5px;right:5px;
  }
  .iconClass{
    position: absolute;top:0px;left:8px;
    font-size:16px;
    color:#eee;cursor: pointer;
    width:18px;
    transition: top 1s ease-in-out;
  }
  .mywk__calendar {
    width: 92%;
    margin: 0 auto;
    display: flex;
    justify-content: space-around;
    flex-wrap: wrap;
  }

  .tips {
    margin: 15px 0 0 0;
    text-align: center;
  }

  .calendar {
    position: relative;
    flex-shrink: 0;
    width: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    margin: 20px 0 0 0;
    border-radius: 4px;
    background-color: white;
    box-shadow: 0 0 10px rgba(208, 208, 208, 0.5);
    padding-bottom: 40px;
    padding-top: 20px;

    .calendar__header {
      color: #2c3135;
      font-size: 16px;
      width: pxWithVw(315);
      max-width: pxWithVwMax(315);
      display: flex;
      align-items: center;
      justify-content: space-between;
      line-height: 22px;
      margin-top: 17px;

      .header__title {
        font-size: 16px;
        letter-spacing: 1px;
      }
      .header__pre:hover{
        border: 1px solid #ddd;
        box-shadow: 0 0 3px #aaa;
      }
      .header__pre {
        height: 24px;
        width: 24px;
        position: relative;
        border-radius: 50%;
        cursor: pointer;
        &:after {
          content: " ";
          display: inline-block;
          height: 9px;
          width: 9px;
          border-width: 2px 2px 0 0;
          border-color: #c8c8cd;
          border-style: solid;
          transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0) rotate(180deg);
          position: absolute;
          top: 50%;
          margin-top: -4px;
          right: 4px;
        }
      }
      .header__next:hover{
        border: 1px solid #ddd;
        box-shadow: 0 0 3px #aaa;
      }
      .header__next {
        height: 24px;
        width: 24px;
        position: relative;
        border-radius: 50%;
        cursor: pointer;
        &:after {
          content: " ";
          display: inline-block;
          height: 9px;
          width: 9px;
          border-width: 2px 2px 0 0;
          border-color: #c8c8cd;
          border-style: solid;
          transform: matrix(0.71, 0.71, -0.71, 0.71, 0, 0);
          position: absolute;
          top: 50%;
          margin-top: -4px;
          right: 7px;
        }
      }
    }

    .calendar__main {
      width: 90%;
      display: flex;
      justify-content: space-around;
      flex-wrap: wrap;
      padding-top: 19px;

      .main__block {
        width: 14%;
        min-height: 70px;
        padding: 20px 5px 15px 5px;
        margin-bottom: 15px;
        border-radius: 2px;
        font-size: 12px;
        border: 1px solid #efefef;
        align-items: center;
        justify-content: center;
        color: #666666;
        background-color: #fff;

        flex-shrink: 0;
        position: relative;

        .main__block-piont {
          width: 5px;
          height: 5px;
          border-radius: 50%;
          background-color: #cce4ff;
          position: absolute;
          left: calc(50% - 2.5px);
          bottom: 0;
        }
      }
      .extraStyle{
        background-color: #efefef;
      }
      .main__block-not {
        background-color: #edf2f5;
        color: #7f8fa4;
      }
      .main__block-today {
        transition: 0.5s all;
        background-color: #cce4ff;
        box-shadow: 0 2px 6px rgba(171, 171, 171, 0.5);
      }
      .main__block-head {
        width: 14.2%;
        margin-bottom: 15px;
        border-radius: 2px;
        display: flex;
        align-items: center;
        justify-content: center;
        font-size: 12px;
        color: #7f8fa4;
        background-color: #fff;
        flex-shrink: 0;
      }
    }
  }
</style>



?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容