From bfb08c37cb549dd7440f97a12dfc9a546fdeab45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=99=88=E5=98=89=E6=B6=B5?= Date: Wed, 25 Dec 2019 15:54:07 +0800 Subject: [PATCH] feat(Calendar): lazy render --- src/calendar/components/Header.js | 10 +-- src/calendar/components/Month.js | 138 +++++++++++++++++++++++++++--- src/calendar/index.js | 112 +++++++----------------- 3 files changed, 158 insertions(+), 102 deletions(-) diff --git a/src/calendar/components/Header.js b/src/calendar/components/Header.js index 8a4519af2..10fdf0ebb 100644 --- a/src/calendar/components/Header.js +++ b/src/calendar/components/Header.js @@ -1,12 +1,12 @@ import { createNamespace } from '../../utils'; -import { t, bem, formatMonthTitle } from '../utils'; +import { t, bem } from '../utils'; const [createComponent] = createNamespace('calendar-header'); export default createComponent({ props: { title: String, - currentMonth: Date + monthTitle: String }, methods: { @@ -19,11 +19,7 @@ export default createComponent({ }, genMonth() { - return ( -
- {formatMonthTitle(this.currentMonth)} -
- ); + return
{this.monthTitle}
; }, genWeekDays() { diff --git a/src/calendar/components/Month.js b/src/calendar/components/Month.js index 909872f0c..f7b9ad01f 100644 --- a/src/calendar/components/Month.js +++ b/src/calendar/components/Month.js @@ -1,14 +1,73 @@ import { createNamespace } from '../../utils'; -import { t, bem } from '../utils'; +import { t, bem, compareDay, formatMonthTitle } from '../utils'; +import { getMonthEndDay } from '../../datetime-picker/utils'; const [createComponent] = createNamespace('calendar-month'); export default createComponent({ props: { date: Date, - days: Array, - title: String, - showMark: Boolean + type: String, + minDate: Date, + maxDate: Date, + showMark: Boolean, + showTitle: Boolean, + rowHeight: Number, + currentValue: [Date, Array] + }, + + data() { + return { + visible: false + }; + }, + + computed: { + title() { + return formatMonthTitle(this.date); + }, + + offset() { + const day = this.date.getDay(); + return day > 0 ? day - 1 : 6; + }, + + totalDay() { + return getMonthEndDay(this.date.getFullYear(), this.date.getMonth() + 1); + }, + + monthStyle() { + if (!this.visible) { + const padding = + Math.ceil((this.totalDay + this.offset) / 7) * this.rowHeight; + + return { + paddingBottom: `${padding}px` + }; + } + }, + + days() { + if (!this.visible) { + return null; + } + + const days = []; + const year = this.date.getFullYear(); + const month = this.date.getMonth(); + + for (let i = 1; i <= this.totalDay; i++) { + const date = new Date(year, month, i); + + days.push({ + day: i, + date, + type: this.getDayType(date) + }); + } + + return days; + } }, mounted() { @@ -16,6 +75,44 @@ export default createComponent({ }, methods: { + getDayType(day) { + const { type, minDate, maxDate, currentValue } = this; + + if (compareDay(day, minDate) < 0 || compareDay(day, maxDate) > 0) { + return 'disabled'; + } + + if (type === 'single') { + return compareDay(day, currentValue) === 0 ? 'selected' : ''; + } + + if (type === 'range') { + const [startDay, endDay] = this.currentValue; + + if (!startDay) { + return; + } + + const compareToStart = compareDay(day, startDay); + if (compareToStart === 0) { + return 'start'; + } + + if (!endDay) { + return; + } + + const compareToEnd = compareDay(day, endDay); + if (compareToEnd === 0) { + return 'end'; + } + + if (compareToStart > 0 && compareToEnd < 0) { + return 'middle'; + } + } + }, + getLabel(item) { if (item.type === 'start') { return t('start'); @@ -27,7 +124,7 @@ export default createComponent({ }, genTitle() { - if (this.title) { + if (this.showTitle) { return
{this.title}
; } }, @@ -38,9 +135,27 @@ export default createComponent({ } }, - genDay(item) { + genDays() { + if (this.visible) { + return ( +
+ {this.genMark()} + {this.days.map(this.genDay)} +
+ ); + } + }, + + genDay(item, index) { const { type } = item; + let style; + if (index === 0) { + style = { + marginLeft: `${(100 * this.offset) / 7}%` + }; + } + const onClick = () => { if (type !== 'disabled') { this.$emit('click', item); @@ -49,7 +164,7 @@ export default createComponent({ if (type === 'selected') { return ( -
+
{item.day}
); @@ -59,7 +174,7 @@ export default createComponent({ const Label = label &&
{label}
; return ( -
+
{item.day} {Label}
@@ -69,12 +184,9 @@ export default createComponent({ render() { return ( -
+
{this.genTitle()} -
- {this.genMark()} - {this.days.map(this.genDay)} -
+ {this.genDays()}
); } diff --git a/src/calendar/index.js b/src/calendar/index.js index 004043e60..4dc55f710 100644 --- a/src/calendar/index.js +++ b/src/calendar/index.js @@ -6,8 +6,7 @@ import { getNextDay, compareDay, compareMonth, - createComponent, - formatMonthTitle + createComponent } from './utils'; import Button from '../button'; @@ -37,6 +36,10 @@ export default createComponent({ }, validator: isDate }, + rowHeight: { + type: Number, + default: 64 + }, showMark: { type: Boolean, default: true @@ -49,7 +52,7 @@ export default createComponent({ data() { return { - currentMonth: this.minDate, + monthTitle: '', currentValue: this.getDefaultValue() }; }, @@ -57,20 +60,14 @@ export default createComponent({ computed: { months() { const months = []; - const { minDate, maxDate } = this; - const cursor = new Date(minDate); + const cursor = new Date(this.minDate); cursor.setDate(1); do { - months.push({ - date: new Date(cursor), - days: this.getDays(cursor), - title: formatMonthTitle(cursor) - }); - + months.push(new Date(cursor)); cursor.setMonth(cursor.getMonth() + 1); - } while (compareMonth(cursor, maxDate) !== 1); + } while (compareMonth(cursor, this.maxDate) !== 1); return months; } @@ -82,6 +79,11 @@ export default createComponent({ } }, + mounted() { + this.bodyHeight = this.$refs.body.getBoundingClientRect().height; + this.onScroll(); + }, + methods: { getDefaultValue() { const { type, value, minDate } = this; @@ -96,77 +98,19 @@ export default createComponent({ } }, - getDayType(day) { - const { type, minDate, maxDate, currentValue } = this; - - if (compareDay(day, minDate) < 0 || compareDay(day, maxDate) > 0) { - return 'disabled'; - } - - if (type === 'single') { - return compareDay(day, currentValue) === 0 ? 'selected' : ''; - } - - if (type === 'range') { - const [startDay, endDay] = this.currentValue; - - if (!startDay) { - return; - } - - const compareToStart = compareDay(day, startDay); - if (compareToStart === 0) { - return 'start'; - } - - if (!endDay) { - return; - } - - const compareToEnd = compareDay(day, endDay); - if (compareToEnd === 0) { - return 'end'; - } - - if (compareToStart > 0 && compareToEnd < 0) { - return 'middle'; - } - } - }, - - getDays(date) { - const days = []; - const placeholderCount = date.getDay() === 0 ? 6 : date.getDay() - 1; - - for (let i = 1; i <= placeholderCount; i++) { - days.push({ day: '' }); - } - - const cursor = new Date(date); - - do { - days.push({ - day: cursor.getDate(), - date: new Date(cursor), - type: this.getDayType(cursor) - }); - - cursor.setDate(cursor.getDate() + 1); - } while (compareMonth(cursor, date) === 0); - - return days; - }, - onScroll() { const scrollTop = getScrollTop(this.$refs.body); const monthsHeight = this.$refs.month.map(item => item.height); + const bodyTop = scrollTop; + const bodyBottom = scrollTop + this.bodyHeight; let height = 0; for (let i = 0; i < this.months.length; i++) { - if (scrollTop < height) { - this.currentMonth = this.months[i - 1].date; - return; - } + const monthTop = height; + const monthBottom = height + monthsHeight[i]; + const visible = monthTop <= bodyBottom && monthBottom >= bodyTop; + + this.$refs.month[i].visible = visible; height += monthsHeight[i]; } @@ -204,15 +148,19 @@ export default createComponent({ this.$emit('select', this.currentValue); }, - genMonth(month, index) { + genMonth(date, index) { return ( ); @@ -262,7 +210,7 @@ export default createComponent({
this.slots('title') }}