
-
計(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>
