mirror of
https://gitee.com/vant-contrib/vant.git
synced 2025-04-06 03:57:59 +08:00
refactor(Calendar): refactor Month/Header component
This commit is contained in:
parent
c54025ce83
commit
c71f5ff857
@ -12,25 +12,24 @@ export default createComponent({
|
|||||||
firstDayOfWeek: Number,
|
firstDayOfWeek: Number,
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
setup(props, { slots }) {
|
||||||
genTitle() {
|
const renderTitle = () => {
|
||||||
if (this.showTitle) {
|
if (props.showTitle) {
|
||||||
const title = this.$slots.title?.() || this.title || t('title');
|
const text = props.title || t('title');
|
||||||
|
const title = slots.title ? slots.title : text;
|
||||||
return <div class={bem('header-title')}>{title}</div>;
|
return <div class={bem('header-title')}>{title}</div>;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
genSubtitle() {
|
const renderSubtitle = () => {
|
||||||
if (this.showSubtitle) {
|
if (props.showSubtitle) {
|
||||||
return <div class={bem('header-subtitle')}>{this.subtitle}</div>;
|
return <div class={bem('header-subtitle')}>{props.subtitle}</div>;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
genWeekDays() {
|
const renderWeekDays = () => {
|
||||||
|
const { firstDayOfWeek } = props;
|
||||||
const weekdays = t('weekdays');
|
const weekdays = t('weekdays');
|
||||||
|
|
||||||
const { firstDayOfWeek } = this;
|
|
||||||
|
|
||||||
const renderWeekDays = [
|
const renderWeekDays = [
|
||||||
...weekdays.slice(firstDayOfWeek, 7),
|
...weekdays.slice(firstDayOfWeek, 7),
|
||||||
...weekdays.slice(0, firstDayOfWeek),
|
...weekdays.slice(0, firstDayOfWeek),
|
||||||
@ -38,20 +37,18 @@ export default createComponent({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div class={bem('weekdays')}>
|
<div class={bem('weekdays')}>
|
||||||
{renderWeekDays.map((item) => (
|
{renderWeekDays.map((text) => (
|
||||||
<span class={bem('weekday')}>{item}</span>
|
<span class={bem('weekday')}>{text}</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
};
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
return () => (
|
||||||
return (
|
|
||||||
<div class={bem('header')}>
|
<div class={bem('header')}>
|
||||||
{this.genTitle()}
|
{renderTitle()}
|
||||||
{this.genSubtitle()}
|
{renderSubtitle()}
|
||||||
{this.genWeekDays()}
|
{renderWeekDays()}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
|
import { ref, computed } from 'vue';
|
||||||
import { createNamespace, addUnit } from '../../utils';
|
import { createNamespace, addUnit } from '../../utils';
|
||||||
|
import { unitToPx } from '../../utils/format/unit';
|
||||||
import { setScrollTop } from '../../utils/dom/scroll';
|
import { setScrollTop } from '../../utils/dom/scroll';
|
||||||
import {
|
import {
|
||||||
t,
|
t,
|
||||||
@ -33,92 +35,62 @@ export default createComponent({
|
|||||||
|
|
||||||
emits: ['click'],
|
emits: ['click'],
|
||||||
|
|
||||||
data() {
|
setup(props, { emit }) {
|
||||||
return {
|
const visible = ref(false);
|
||||||
visible: false,
|
const daysRef = ref(null);
|
||||||
};
|
const monthRef = ref(null);
|
||||||
},
|
|
||||||
|
|
||||||
computed: {
|
const title = computed(() => formatMonthTitle(props.date));
|
||||||
title() {
|
|
||||||
return formatMonthTitle(this.date);
|
|
||||||
},
|
|
||||||
|
|
||||||
rowHeightWithUnit() {
|
const rowHeight = computed(() => {
|
||||||
if (this.rowHeight !== ROW_HEIGHT) {
|
if (props.rowHeight !== ROW_HEIGHT) {
|
||||||
return addUnit(this.rowHeight);
|
return addUnit(props.rowHeight);
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
|
|
||||||
offset() {
|
const offset = computed(() => {
|
||||||
const { firstDayOfWeek } = this;
|
const realDay = props.date.getDay();
|
||||||
|
|
||||||
const realDay = this.date.getDay();
|
if (props.firstDayOfWeek) {
|
||||||
|
return (realDay + 7 - props.firstDayOfWeek) % 7;
|
||||||
if (!firstDayOfWeek) {
|
|
||||||
return realDay;
|
|
||||||
}
|
}
|
||||||
|
return realDay;
|
||||||
|
});
|
||||||
|
|
||||||
return (realDay + 7 - this.firstDayOfWeek) % 7;
|
const totalDay = computed(() =>
|
||||||
},
|
getMonthEndDay(props.date.getFullYear(), props.date.getMonth() + 1)
|
||||||
|
);
|
||||||
|
|
||||||
totalDay() {
|
const shouldRender = computed(() => visible.value || !props.lazyRender);
|
||||||
return getMonthEndDay(this.date.getFullYear(), this.date.getMonth() + 1);
|
|
||||||
},
|
|
||||||
|
|
||||||
shouldRender() {
|
|
||||||
return this.visible || !this.lazyRender;
|
|
||||||
},
|
|
||||||
|
|
||||||
monthStyle() {
|
|
||||||
if (!this.shouldRender) {
|
|
||||||
const padding =
|
|
||||||
Math.ceil((this.totalDay + this.offset) / 7) * this.rowHeight;
|
|
||||||
|
|
||||||
|
const monthStyle = computed(() => {
|
||||||
|
if (!shouldRender.value) {
|
||||||
|
const rowCount = Math.ceil((totalDay.value + offset.value) / 7);
|
||||||
|
const padding = rowCount * unitToPx(props.rowHeight);
|
||||||
return {
|
return {
|
||||||
paddingBottom: `${padding}px`,
|
paddingBottom: `${padding}px`,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
});
|
||||||
|
|
||||||
days() {
|
let height;
|
||||||
const days = [];
|
const getHeight = () => {
|
||||||
const year = this.date.getFullYear();
|
if (!height) {
|
||||||
const month = this.date.getMonth();
|
({ height } = monthRef.value.getBoundingClientRect());
|
||||||
|
|
||||||
for (let day = 1; day <= this.totalDay; day++) {
|
|
||||||
const date = new Date(year, month, day);
|
|
||||||
const type = this.getDayType(date);
|
|
||||||
|
|
||||||
let config = {
|
|
||||||
date,
|
|
||||||
type,
|
|
||||||
text: day,
|
|
||||||
bottomInfo: this.getBottomInfo(type),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (this.formatter) {
|
|
||||||
config = this.formatter(config);
|
|
||||||
}
|
|
||||||
|
|
||||||
days.push(config);
|
|
||||||
}
|
}
|
||||||
|
return height;
|
||||||
|
};
|
||||||
|
|
||||||
return days;
|
const setVisible = (value) => {
|
||||||
},
|
visible.value = value;
|
||||||
},
|
};
|
||||||
|
|
||||||
methods: {
|
const getDate = () => props.data;
|
||||||
getHeight() {
|
|
||||||
if (!this.height) {
|
|
||||||
this.height = this.$el.getBoundingClientRect().height;
|
|
||||||
}
|
|
||||||
return this.height;
|
|
||||||
},
|
|
||||||
|
|
||||||
scrollIntoView(body) {
|
const getTitle = () => title.value;
|
||||||
const { days, month } = this.$refs;
|
|
||||||
const el = this.showSubtitle ? days : month;
|
const scrollIntoView = (body) => {
|
||||||
|
const el = props.showSubtitle ? daysRef.value : monthRef.value;
|
||||||
|
|
||||||
const scrollTop =
|
const scrollTop =
|
||||||
el.getBoundingClientRect().top -
|
el.getBoundingClientRect().top -
|
||||||
@ -126,11 +98,11 @@ export default createComponent({
|
|||||||
body.scrollTop;
|
body.scrollTop;
|
||||||
|
|
||||||
setScrollTop(body, scrollTop);
|
setScrollTop(body, scrollTop);
|
||||||
},
|
};
|
||||||
|
|
||||||
getMultipleDayType(day) {
|
const getMultipleDayType = (day) => {
|
||||||
const isSelected = (date) =>
|
const isSelected = (date) =>
|
||||||
this.currentDate.some((item) => compareDay(item, date) === 0);
|
props.currentDate.some((item) => compareDay(item, date) === 0);
|
||||||
|
|
||||||
if (isSelected(day)) {
|
if (isSelected(day)) {
|
||||||
const prevDay = getPrevDay(day);
|
const prevDay = getPrevDay(day);
|
||||||
@ -141,19 +113,20 @@ export default createComponent({
|
|||||||
if (prevSelected && nextSelected) {
|
if (prevSelected && nextSelected) {
|
||||||
return 'multiple-middle';
|
return 'multiple-middle';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (prevSelected) {
|
if (prevSelected) {
|
||||||
return 'end';
|
return 'end';
|
||||||
}
|
}
|
||||||
|
if (nextSelected) {
|
||||||
return nextSelected ? 'start' : 'multiple-selected';
|
return 'start';
|
||||||
|
}
|
||||||
|
return 'multiple-selected';
|
||||||
}
|
}
|
||||||
|
|
||||||
return '';
|
return '';
|
||||||
},
|
};
|
||||||
|
|
||||||
getRangeDayType(day) {
|
const getRangeDayType = (day) => {
|
||||||
const [startDay, endDay] = this.currentDate;
|
const [startDay, endDay] = props.currentDate;
|
||||||
|
|
||||||
if (!startDay) {
|
if (!startDay) {
|
||||||
return '';
|
return '';
|
||||||
@ -167,25 +140,22 @@ export default createComponent({
|
|||||||
|
|
||||||
const compareToEnd = compareDay(day, endDay);
|
const compareToEnd = compareDay(day, endDay);
|
||||||
|
|
||||||
if (compareToStart === 0 && compareToEnd === 0 && this.allowSameDay) {
|
if (compareToStart === 0 && compareToEnd === 0 && props.allowSameDay) {
|
||||||
return 'start-end';
|
return 'start-end';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compareToStart === 0) {
|
if (compareToStart === 0) {
|
||||||
return 'start';
|
return 'start';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compareToEnd === 0) {
|
if (compareToEnd === 0) {
|
||||||
return 'end';
|
return 'end';
|
||||||
}
|
}
|
||||||
|
|
||||||
if (compareToStart > 0 && compareToEnd < 0) {
|
if (compareToStart > 0 && compareToEnd < 0) {
|
||||||
return 'middle';
|
return 'middle';
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
getDayType(day) {
|
const getDayType = (day) => {
|
||||||
const { type, minDate, maxDate, currentDate } = this;
|
const { type, minDate, maxDate, currentDate } = props;
|
||||||
|
|
||||||
if (compareDay(day, minDate) < 0 || compareDay(day, maxDate) > 0) {
|
if (compareDay(day, minDate) < 0 || compareDay(day, maxDate) > 0) {
|
||||||
return 'disabled';
|
return 'disabled';
|
||||||
@ -193,18 +163,18 @@ export default createComponent({
|
|||||||
|
|
||||||
if (Array.isArray(currentDate)) {
|
if (Array.isArray(currentDate)) {
|
||||||
if (type === 'multiple') {
|
if (type === 'multiple') {
|
||||||
return this.getMultipleDayType(day);
|
return getMultipleDayType(day);
|
||||||
}
|
}
|
||||||
if (type === 'range') {
|
if (type === 'range') {
|
||||||
return this.getRangeDayType(day);
|
return getRangeDayType(day);
|
||||||
}
|
}
|
||||||
} else if (type === 'single') {
|
} else if (type === 'single') {
|
||||||
return compareDay(day, currentDate) === 0 ? 'selected' : '';
|
return compareDay(day, currentDate) === 0 ? 'selected' : '';
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
getBottomInfo(type) {
|
const getBottomInfo = (type) => {
|
||||||
if (this.type === 'range') {
|
if (props.type === 'range') {
|
||||||
if (type === 'start' || type === 'end') {
|
if (type === 'start' || type === 'end') {
|
||||||
return t(type);
|
return t(type);
|
||||||
}
|
}
|
||||||
@ -212,18 +182,19 @@ export default createComponent({
|
|||||||
return t('startEnd');
|
return t('startEnd');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
getDayStyle(type, index) {
|
const getDayStyle = (type, index) => {
|
||||||
|
const { color } = props;
|
||||||
const style = {
|
const style = {
|
||||||
height: this.rowHeightWithUnit,
|
height: rowHeight.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
style.marginLeft = `${(100 * this.offset) / 7}%`;
|
style.marginLeft = `${(100 * offset.value) / 7}%`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.color) {
|
if (color) {
|
||||||
if (
|
if (
|
||||||
type === 'start' ||
|
type === 'start' ||
|
||||||
type === 'end' ||
|
type === 'end' ||
|
||||||
@ -231,48 +202,35 @@ export default createComponent({
|
|||||||
type === 'multiple-selected' ||
|
type === 'multiple-selected' ||
|
||||||
type === 'multiple-middle'
|
type === 'multiple-middle'
|
||||||
) {
|
) {
|
||||||
style.background = this.color;
|
style.background = color;
|
||||||
} else if (type === 'middle') {
|
} else if (type === 'middle') {
|
||||||
style.color = this.color;
|
style.color = color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return style;
|
return style;
|
||||||
},
|
};
|
||||||
|
|
||||||
genTitle() {
|
const renderTitle = () => {
|
||||||
if (this.showMonthTitle) {
|
if (props.showMonthTitle) {
|
||||||
return <div class={bem('month-title')}>{this.title}</div>;
|
return <div class={bem('month-title')}>{title.value}</div>;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
genMark() {
|
const renderMark = () => {
|
||||||
if (this.showMark) {
|
if (props.showMark) {
|
||||||
return <div class={bem('month-mark')}>{this.date.getMonth() + 1}</div>;
|
return <div class={bem('month-mark')}>{props.date.getMonth() + 1}</div>;
|
||||||
}
|
}
|
||||||
},
|
};
|
||||||
|
|
||||||
genDays() {
|
const renderDay = (item, index) => {
|
||||||
if (this.shouldRender) {
|
|
||||||
return (
|
|
||||||
<div ref="days" role="grid" class={bem('days')}>
|
|
||||||
{this.genMark()}
|
|
||||||
{this.days.map(this.genDay)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return <div ref="days" />;
|
|
||||||
},
|
|
||||||
|
|
||||||
genDay(item, index) {
|
|
||||||
const { type, topInfo, bottomInfo } = item;
|
const { type, topInfo, bottomInfo } = item;
|
||||||
const style = this.getDayStyle(type, index);
|
const style = getDayStyle(type, index);
|
||||||
const disabled = type === 'disabled';
|
const disabled = type === 'disabled';
|
||||||
|
|
||||||
const onClick = () => {
|
const onClick = () => {
|
||||||
if (!disabled) {
|
if (!disabled) {
|
||||||
this.$emit('click', item);
|
emit('click', item);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -294,9 +252,9 @@ export default createComponent({
|
|||||||
<div
|
<div
|
||||||
class={bem('selected-day')}
|
class={bem('selected-day')}
|
||||||
style={{
|
style={{
|
||||||
width: this.rowHeightWithUnit,
|
width: rowHeight.value,
|
||||||
height: this.rowHeightWithUnit,
|
height: rowHeight.value,
|
||||||
background: this.color,
|
background: props.color,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{TopInfo}
|
{TopInfo}
|
||||||
@ -320,15 +278,60 @@ export default createComponent({
|
|||||||
{BottomInfo}
|
{BottomInfo}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
},
|
};
|
||||||
},
|
|
||||||
|
|
||||||
render() {
|
const days = computed(() => {
|
||||||
return (
|
const days = [];
|
||||||
<div class={bem('month')} ref="month" style={this.monthStyle}>
|
const year = props.date.getFullYear();
|
||||||
{this.genTitle()}
|
const month = props.date.getMonth();
|
||||||
{this.genDays()}
|
|
||||||
</div>
|
for (let day = 1; day <= totalDay.value; day++) {
|
||||||
);
|
const date = new Date(year, month, day);
|
||||||
|
const type = getDayType(date);
|
||||||
|
|
||||||
|
let config = {
|
||||||
|
date,
|
||||||
|
type,
|
||||||
|
text: day,
|
||||||
|
bottomInfo: getBottomInfo(type),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (props.formatter) {
|
||||||
|
config = props.formatter(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
days.push(config);
|
||||||
|
}
|
||||||
|
|
||||||
|
return days;
|
||||||
|
});
|
||||||
|
|
||||||
|
const renderDays = () => {
|
||||||
|
if (shouldRender.value) {
|
||||||
|
return (
|
||||||
|
<div ref={daysRef} role="grid" class={bem('days')}>
|
||||||
|
{renderMark()}
|
||||||
|
{days.value.map(renderDay)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <div ref={daysRef} />;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (vm) => {
|
||||||
|
vm.getDate = getDate;
|
||||||
|
vm.getTitle = getTitle;
|
||||||
|
vm.getHeight = getHeight;
|
||||||
|
vm.setVisible = setVisible;
|
||||||
|
vm.scrollIntoView = scrollIntoView;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class={bem('month')} ref={monthRef} style={monthStyle.value}>
|
||||||
|
{renderTitle()}
|
||||||
|
{renderDays()}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
@ -288,18 +288,18 @@ export default createComponent({
|
|||||||
|
|
||||||
if (!monthRefs[i].visible && visible) {
|
if (!monthRefs[i].visible && visible) {
|
||||||
this.$emit('month-show', {
|
this.$emit('month-show', {
|
||||||
date: monthRefs[i].date,
|
date: monthRefs[i].getDate(),
|
||||||
title: monthRefs[i].title,
|
title: monthRefs[i].getTitle(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
monthRefs[i].visible = visible;
|
monthRefs[i].setVisible(visible);
|
||||||
height += heights[i];
|
height += heights[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
/* istanbul ignore else */
|
/* istanbul ignore else */
|
||||||
if (currentMonth) {
|
if (currentMonth) {
|
||||||
this.subtitle = currentMonth.title;
|
this.subtitle = currentMonth.getTitle();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user