const leapYear = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; const commonYear = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; const FIRST_MONTH = 0; const LAST_MONTH = 11; const DAYS_PER_ROW = 7; const COLOR_MAP = { 1: "#f5a911", 2: "#e8541e", 3: "#07a89b", 4: "#108ee9", 5: "rgba(51, 51, 51, 0.4)" }; // 获取某月第某天是星期几 function getDay(month, year, index) { return new Date(year, month, index).getDay(); } // 获取某月有几天 function getMonthLength(month, year) { if (year % 400 === 0 || (year % 100 !== 0 && year % 4 === 0)) { return leapYear[month]; } else { return commonYear[month]; } } // 数字补位 1 -> 01 function prefixNum(num) { if (num < 10) { return `0${num}`; } else { return `${num}`; } } Component({ data: { selectedYear: 0, selectedMonth: 0, currentDate: null, dates: [], blockType: 1 // 1.没有待办纯数字 2.有待办 用于区分不同类型日期块的样式。 }, props: { className: "", tagData: [], type: "single", haveYear: false, selectedDate: null }, didMount() { this.tapTimes = 1; const date = this.props.selectedDate ? new Date(this.props.selectedDate) : new Date(); date.setHours(0); date.setMinutes(0); date.setSeconds(0); date.setMilliseconds(0); const year = date.getFullYear(); const month = date.getMonth(); this.setData({ selectedYear: year, selectedMonth: month, currentDate: date }); this.refreshdates(month, year); }, didUpdate() { const { dates } = this.data; let blockType = 1; for (let i = 0; i < dates.length; i++) { for (let j = 0; j < dates[i].length; j++) { if (this.hasTag(dates[i][j])) { blockType = 2; } } } this.setData({ dates, blockType }); }, methods: { onPrevYearTap() { const { selectedMonth, selectedYear } = this.data; let year = selectedYear; const month = selectedMonth; year = selectedYear - 1; if (this.props.onYearChange) { this.props.onYearChange(year, selectedYear); } this.setData({ selectedYear: year }); this.refreshdates(month, year); }, onNextYearTap() { const { selectedMonth, selectedYear } = this.data; let year = selectedYear; const month = selectedMonth; year = selectedYear + 1; if (this.props.onYearChange) { this.props.onYearChange(year, selectedYear); } this.setData({ selectedYear: year }); this.refreshdates(month, year); }, onPrevMonthTap() { const { selectedMonth, selectedYear } = this.data; let year = selectedYear; let month = selectedMonth; // 如果当前选中是一月份,前一月是去年的12月 if (selectedMonth === FIRST_MONTH) { year = selectedYear - 1; month = LAST_MONTH; } else { month = selectedMonth - 1; } if (this.props.onMonthChange) { this.props.onMonthChange(month, selectedMonth); } this.setData({ selectedYear: year, selectedMonth: month }); this.refreshdates(month, year); }, onNextMonthTap() { const { selectedMonth, selectedYear } = this.data; let year = selectedYear; let month = selectedMonth; // 如果当前选中是十二月份,下一月是去年的12月 if (selectedMonth === LAST_MONTH) { year = selectedYear + 1; month = FIRST_MONTH; } else { month = selectedMonth + 1; } if (this.props.onMonthChange) { this.props.onMonthChange(month, selectedMonth); } this.setData({ selectedYear: year, selectedMonth: month }); this.refreshdates(month, year); }, refreshdates(month, year) { this.tapTimes = 1; const { selectedYear, selectedMonth, currentDate } = this.data; const firstDay = getDay(month, year, 1); const days = getMonthLength(month, year); const datesArray = []; const currentDateTimeStamp = +currentDate; let num = 0; for (let i = 0; i < firstDay; i++) { num += 1; // 如果当前选中的是一月份,前一个月是去年的12月 let _year = selectedYear; let _month = selectedMonth; if (selectedMonth === 0) { _year = selectedYear - 1; _month = LAST_MONTH; } else { _year = selectedYear; _month = selectedMonth - 1; } const date = getMonthLength(_month, _year) - i; datesArray.unshift({ year: _year, month: _month, date, isToday: false, isGray: true, isSelected: false, tag: "" }); } for (let i = 0; i < days; i++) { num += 1; const date = i + 1; const dateTimeStamp = +new Date(selectedYear, selectedMonth, date); datesArray.push({ year: selectedYear, month: selectedMonth, date, isToday: dateTimeStamp === currentDateTimeStamp, isGray: false, isSelected: dateTimeStamp === currentDateTimeStamp, tag: "" }); } let nextDate = 0; let daysPerPage = 35; if (num > 35) { daysPerPage = 42; } for (let i = 0; i < daysPerPage - days - firstDay; i++) { // 如果是12月,下月是第二年的1月份 nextDate += 1; let _year = selectedYear; let _month = selectedMonth; if (selectedMonth === LAST_MONTH) { _year = selectedYear + 1; _month = FIRST_MONTH; } else { _year = selectedYear; _month = selectedMonth + 1; } datesArray.push({ year: _year, month: _month, date: nextDate, isToday: false, isGray: true, isSelected: false, tag: "" }); } let blockType = 1; for (let i = 0; i < datesArray.length; i++) { if (this.hasTag(datesArray[i])) { blockType = 2; } } const dates = []; let weekDates = []; for (let i = 0; i < datesArray.length; i++) { weekDates.push(datesArray[i]); if ((i + 1) % DAYS_PER_ROW === 0) { dates.push([...weekDates]); weekDates = []; } } this.setData({ dates, blockType }); }, hasTag(dateObj) { const { tagData } = this.props; // 去重由调用者处理 if (tagData.length === 0) { dateObj.tag = ""; return false; } return tagData.some(item => { const dateArr = item.date.split("-"); const dateStr = []; // 兼容ios下new Date('2018-1-1')格式返回invalid Date的问题 for (let i = 0; i < dateArr.length; i++) { dateStr.push(dateArr[i].length > 1 ? dateArr[i] : `0${dateArr[i]}`); } const date = new Date(dateStr.join("-")); if ( dateObj.year === date.getFullYear() && dateObj.month === date.getMonth() && dateObj.date === date.getDate() ) { dateObj.tag = item.tag; dateObj.color = COLOR_MAP[item.tagColor]; dateObj.disable = item.disable; return true; } else { dateObj.tag = ""; return false; } }); }, getDateGap(day1, day2) { const date1 = +new Date( day1.year, prefixNum(day1.month), prefixNum(day1.date) ); const date2 = +new Date( day2.year, prefixNum(day2.month), prefixNum(day2.date) ); return (date1 - date2) / (24 * 3600 * 1000); }, makeDate(dateObj) { return new Date( `${dateObj.year}-${prefixNum(dateObj.month + 1)}-${prefixNum( dateObj.date )}` ); }, onDateTap(event) { const { dates } = this.data; const { year, month, date } = event.currentTarget.dataset; const { type } = this.props; if (type === "range") { if (this.tapTimes % 2 === 0) { this.tapTimes += 1; this.endDate = { year, month, date }; const dateGap = this.getDateGap(this.startDate, this.endDate); if (dateGap > 0) { [this.startDate, this.endDate] = [this.endDate, this.startDate]; } let hasDisable = false; for (let i = 0; i < dates.length; i++) { for (let j = 0; j < dates[i].length; j++) { const dateObj = dates[i][j]; dateObj.isStart = false; dateObj.isMiddle = false; dateObj.isEnd = false; const startDateGap = this.getDateGap(dateObj, this.startDate); const endDateGap = this.getDateGap(dateObj, this.endDate); if ( dateObj.year === year && dateObj.month === month && dateObj.date === date && dateObj.disable ) { hasDisable = true; } if (startDateGap > 0 && endDateGap < 0) { if (dateObj.disable) { hasDisable = true; } if (dateGap !== 0) { if (j === 0) { dateObj.isStart = true; } else if (j === 6) { dateObj.isEnd = true; } else { dateObj.isMiddle = true; } } else { dateObj.isSelected = true; } } if ( this.startDate.year === dateObj.year && this.startDate.month === dateObj.month && this.startDate.date === dateObj.date && dateGap !== 0 ) { if (j === 6) { dateObj.isSelected = true; } else { dateObj.isStart = true; } } if ( this.endDate.year === dateObj.year && this.endDate.month === dateObj.month && this.endDate.date === dateObj.date && dateGap !== 0 ) { if (j === 0) { dateObj.isSelected = true; } else { dateObj.isEnd = true; } } } } if (hasDisable) { this.props.onSelectHasDisableDate([ this.makeDate(this.startDate), this.makeDate(this.endDate) ]); return; } if (this.props.onSelect) { this.props.onSelect([ this.makeDate(this.startDate), this.makeDate(this.endDate) ]); } } else { let isDisable = false; for (let i = 0; i < dates.length; i++) { for (let j = 0; j < dates[i].length; j++) { const dateObj = dates[i][j]; if ( dateObj.year === year && dateObj.month === month && dateObj.date === date ) { if (dateObj.disable) { // console.log(1111); isDisable = true; dateObj.isSelected = false; } else { dateObj.isSelected = true; } dateObj.isStart = false; dateObj.isMiddle = false; dateObj.isEnd = false; } else { dateObj.isSelected = false; dateObj.isStart = false; dateObj.isMiddle = false; dateObj.isEnd = false; } } } if (!isDisable) { this.tapTimes += 1; } this.startDate = { year, month, date }; } this.setData({ dates }); } else { let isDisable = false; for (let i = 0; i < dates.length; i++) { for (let j = 0; j < dates[i].length; j++) { const dateObj = dates[i][j]; if ( dateObj.year === year && dateObj.month === month && dateObj.date === date ) { dateObj.isSelected = true; if (dateObj.disable) { isDisable = true; } } else { dateObj.isSelected = false; } } } if (isDisable) { return; } this.setData({ dates }); if (this.props.onSelect) { this.props.onSelect([ this.makeDate({ year, month, date }), undefined ]); } } } } });