項目須要,沒有合適的輪子,因此,,,css
效果(紅圈部分): html
傳入年份,月份建立日曆;日期價格數組;日期禁用數組web
建立一個二維數組,第一維存放一月的週數,第二維存放一週的天數;一維的週數最多有6個數組數組
每一個日期包含年月日三個屬性,價格對應的日期也是年月日,禁用的日期頁按照年月日,因此匹配一下就能夠了函數
每個月天數使用 js 原生判斷,即:
js 中new Date('2019/06/21')
傳入一個日期,能夠用來判斷某個月是否有某一天。
沒有的話,返回下一個月一號的構造函數;有的話返回這一天的構造函數;超出31號的則是Invalid。flex
// 根據傳入的日期構造函數獲取年月日
getYMD(time = new Date()) {
return {
_year: time.getFullYear(),
_month: time.getMonth() + 1,
_date: time.getDate(),
}
},
// 驗證日期是否有效:對應的月份是否有這一天
validDate(year, month, date) {
let time = new Date(`${year}/${month}/${date}`);
const { _year, _month, _date } = this.getYMD(time);
return _year === year && month === _month && date === _date
},
複製代碼
生成當月天數按周分割的數組ui
// 生成一個月的天數
generateCalendar(year, month) {
let that = this;
let weekLen = new Array(7).fill(''); // 一週七天
let weekday = new Date(`${year}/${month}/01`).getDay(); // 1號星期幾
// 重置
this.monthDay = [[], [], [], [], [], []];
// 生成一月對齊星期的天數,一週以週日開始
weekLen.map((item, index) => {
that.monthDay[0].push(
index < weekday
? ''
: (index === weekday)
? {year, month, date: 1}
: {year, month, date: that.monthDay[0][index-1].date+1}
);
that.monthDay[1].push({year, month, date: index + (7 - weekday + 1)});
that.monthDay[2].push({year, month, date: that.monthDay[1][index].date + 7});
that.monthDay[3].push({year, month, date: that.monthDay[2][index].date + 7});
if (
that.monthDay[3][index].date + 7 <= 31 &&
that.validDate(year, month, that.monthDay[3][index].date + 7)
) {
that.monthDay[4].push({year, month, date: that.monthDay[3][index].date + 7});
} else {
that.monthDay[4].push('');
}
if (
that.monthDay[4][index].date + 7 <= 31 &&
that.validDate(year, month, that.monthDay[4][index].date + 7)
) {
that.monthDay[5].push({year, month, date: that.monthDay[4][index].date + 7});
}
})
}
複製代碼
new Date('2019/06/21')
的參數須要用斜槓'/'
,用短橫線new Date('2019-06-21')
的話會是 Invalid Date,貌似IOS也是隻能使用'/'
來獲取,兼容性上'/'
最靠譜了。。。父組件使用this
<cus-calendar class="calendar" :year="monthActive.year" :month="monthActive.month" :dateActiveDefault="activeDate" :saleList="saleList" :disabledDate="disabledDate" @check-date="checkDate" />
複製代碼
其餘:spa
this.disabledDate = ['2019-06-21','2019-06-22','2019-06-23'];
this.saleList = [
{ date: '2019-06-24', price: 6188 },
{ date: '2019-06-25', price: 6188 },
{ date: '2019-06-26', price: 6188 },
{ date: '2019-07-26', price: 6188 },
{ date: '2019-06-27', price: 6188 },
{ date: '2019-06-28', price: 6188 },
];
複製代碼
<template>
<view class="calendar-container">
<view class="month-bg">
{{ month }}
</view>
<view class="week-title">
<text class="weekend">日</text>
<text>一</text>
<text>二</text>
<text>三</text>
<text>四</text>
<text>五</text>
<text class="weekend">六</text>
</view>
<view class="calendar-content">
<view v-for="(week, weekindex) in monthDay" :key="weekindex" class="week-month" >
<view class="date" v-for="(date, dateindex) in week" :key="dateindex" :class="{'date-disabled': !date || beforeToday(date) || disabledDateFn(date)}" @tap="dateTap(date)" >
<view class="date-item" :class="{ 'date-active': showPrice(date) && isActiveDate(date), 'date-active date-active2': !showPrice(date) && isActiveDate(date) }" >
<text v-if="isToday(date)">今天</text>
<text v-else>{{ date.date }}</text>
<view class="price" v-if="showPrice(date)">¥{{ showPrice(date) }}</view>
</view>
</view>
</view>
</view>
</view>
</template>
複製代碼
// <script>
// 旅遊產品 - 日曆
export default {
name: 'cus-calendar',
props: {
theme: {
type: String,
default: '#F87D72'
},
// 日期下面的價格
saleList: {
type: Array,
default: () => []
},
// 禁用的日期
disabledDate: {
type: Array,
default: () => []
},
// 默認選中的日期, 如2019-06-24
dateActiveDefault: {
type: String,
default: ''
},
year: {
type: Number,
default: () => new Date().getFullYear()
},
month: {
type: Number,
default: () => new Date().getMonth()+1
}
},
data() {
return {
newYear: '',
newMonth: '',
monthDay: [[], [], [], [], [], []],
dateActive: {
year: '',
month: '',
date: ''
}
}
},
mounted() {
// 默認選中
if (this.dateActiveDefault) {
const [year, month, date] = this.dateActiveDefault.split('-');
this.dateActive = {
year: +year,
month: +month,
date: +date
}
}
},
watch: {
month: {
handler(val) {
this.newYear = this.year;
this.newMonth = this.month;
this.generateCalendar(this.newYear, this.newMonth);
},
immediate: true
}
},
methods: {
// 根據傳入的日期構造函數獲取年月日
getYMD(time = new Date()) {
return {
_year: time.getFullYear(),
_month: time.getMonth() + 1,
_date: time.getDate(),
}
},
// 驗證日期是否有效:對應的月份是否有這一天
validDate(year, month, date) {
let time = new Date(`${year}/${month}/${date}`);
const { _year, _month, _date } = this.getYMD(time);
return _year === year && month === _month && date === _date
},
// 是否今天
isToday({ year, month, date }) {
let time = new Date();
const { _year, _month, _date } = this.getYMD(time);
return year === _year && month === _month && date === _date
},
// 今天以前
beforeToday({ year, month, date }) {
let time = new Date();
const { _year, _month, _date } = this.getYMD(time);
return year <= _year && month <= _month && date <= _date
},
// 禁用的日期
disabledDateFn({ year, month, date }) {
month = (month+'').padStart(2, '0');
date = (date+'').padStart(2, '0');
return this.disabledDate.includes(`${year}-${month}-${date}`);
},
// 是否選中
isActiveDate({ year, month, date }) {
const { year: _year, month: _month, date: _date } = this.dateActive;
return year === _year && month === _month && date === _date;
},
// 點擊有效的一天
dateTap({ year, month, date }) {
this.dateActive = {
year,
month,
date
};
this.$emit('check-date', this.dateActive);
},
// 日期下面顯示價格
showPrice({ year, month, date }) {
if (!year) return;
month = (month+'').padStart(2, '0');
date = (date+'').padStart(2, '0');
let obj = this.saleList.find(item => item.date === `${year}-${month}-${date}`);
return obj && obj.price
},
// 生成一個月的天數
generateCalendar(year, month) {
let that = this;
let weekLen = new Array(7).fill(''); // 一週七天
let weekday = new Date(`${year}/${month}/01`).getDay(); // 1號星期幾
// 重置
that.monthDay = [[], [], [], [], [], []];
// 生成一月對齊星期的天數,一週以週日開始
weekLen.map((item, index) => {
that.monthDay[0].push(
index < weekday
? ''
: (index === weekday)
? {year, month, date: 1}
: {year, month, date: that.monthDay[0][index-1].date+1}
);
that.monthDay[1].push({year, month, date: index + (7 - weekday + 1)});
that.monthDay[2].push({year, month, date: that.monthDay[1][index].date + 7});
that.monthDay[3].push({year, month, date: that.monthDay[2][index].date + 7});
if (
that.monthDay[3][index].date + 7 <= 31 &&
that.validDate(year, month, that.monthDay[3][index].date + 7)
) {
that.monthDay[4].push({year, month, date: that.monthDay[3][index].date + 7});
} else {
that.monthDay[4].push('');
}
if (
that.monthDay[4][index].date + 7 <= 31 &&
that.validDate(year, month, that.monthDay[4][index].date + 7)
) {
that.monthDay[5].push({year, month, date: that.monthDay[4][index].date + 7});
}
})
}
}
}
// </script>
複製代碼
/* <style lang="scss" scoped> */
.calendar-container {
width: 100%;
position: relative;
color: #999;
}
.month-bg {
position: absolute;
top: 50%;
left: 50%;
color: #f6f6f6;
font-size: 60px;
transform: translate(-50%, -50%);
z-index: -1;
}
.week-title {
padding: 20upx 40upx;
display: flex;
justify-content: space-between;
&>text {
flex: 1;
text-align: center;
}
}
.weekend {
color: #F87D72;
}
.week-month {
display: flex;
justify-content: flex-start;
padding: 20upx 40upx;
color: #2b2b2b;
&>.date {
flex: 14.285% 0 0;
text-align: center;
}
}
.date-item {
width: 60upx;
height: 60upx;
position: relative;
left: 50%;
margin-left: -30upx;
line-height: 1;
}
.date-disabled {
color: #999;
pointer-events: none;
}
.price {
color: #F87D72;
font-size: 18upx;
}
.date-active {
color: #fff;
&::after {
content: '';
width: 140%;
height: 140%;
display: block;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50px;
background: linear-gradient(right, #F87D72, #F29E97);
box-shadow: 0 6upx 16upx -6upx #F97C71;
z-index: -1;
}
&>.price {
color: #fff;
}
}
.date-active2::after {
transform: translate(-50%, -68%);
}
/* </style> */
複製代碼