|
@@ -1,254 +1,251 @@
|
|
-<template>
|
|
|
|
- <table
|
|
|
|
- :class="['el-calendar-table', { 'is-range': isInRange }]"
|
|
|
|
- cellspacing="0"
|
|
|
|
- cellpadding="0"
|
|
|
|
- >
|
|
|
|
- <thead v-if="!hideHeader">
|
|
|
|
- <tr>
|
|
|
|
- <th v-for="day in weekDays" :key="day">{{ day }}</th>
|
|
|
|
- </tr>
|
|
|
|
- </thead>
|
|
|
|
- <tbody>
|
|
|
|
- <tr
|
|
|
|
- v-for="(row, rowIndex) in rows"
|
|
|
|
- :key="rowIndex"
|
|
|
|
- :class="[
|
|
|
|
- 'el-calendar-table__row',
|
|
|
|
- {
|
|
|
|
- 'el-calendar-table__row--hide-border': rowIndex === 0 && hideHeader,
|
|
|
|
- },
|
|
|
|
- ]"
|
|
|
|
- >
|
|
|
|
- <td
|
|
|
|
- v-for="(cell, cellIndex) in row"
|
|
|
|
- :key="cellIndex"
|
|
|
|
- :class="getCellClass(cell)"
|
|
|
|
- @click="pickDay(cell)"
|
|
|
|
- >
|
|
|
|
- <div class="el-calendar-day">
|
|
|
|
- {{ cellRenderProxy(cell) }}
|
|
|
|
- </div>
|
|
|
|
- </td>
|
|
|
|
- </tr>
|
|
|
|
- </tbody>
|
|
|
|
- </table>
|
|
|
|
-</template>
|
|
|
|
-
|
|
|
|
-<script>
|
|
|
|
-import { formatDate } from "@dage/utils";
|
|
|
|
-import {
|
|
|
|
- range as rangeArr,
|
|
|
|
- getFirstDayOfMonth,
|
|
|
|
- getPrevMonthLastDays,
|
|
|
|
- getMonthDays,
|
|
|
|
- getI18nSettings,
|
|
|
|
- validateRangeInOneMonth,
|
|
|
|
-} from "./utils";
|
|
|
|
-import { ref, computed, inject, toRefs, defineComponent } from "vue";
|
|
|
|
-
|
|
|
|
-export default defineComponent({
|
|
|
|
- name: "CalendarComponent",
|
|
|
|
- props: {
|
|
|
|
- selectedDay: String,
|
|
|
|
- range: {
|
|
|
|
- type: Array,
|
|
|
|
- validator(val) {
|
|
|
|
- if (!(val && val.length)) return true;
|
|
|
|
- const [start, end] = val;
|
|
|
|
- return validateRangeInOneMonth(start, end);
|
|
|
|
- },
|
|
|
|
- },
|
|
|
|
- date: Date,
|
|
|
|
- hideHeader: Boolean,
|
|
|
|
- firstDayOfWeek: Number,
|
|
|
|
- },
|
|
|
|
- setup(props, { emit }) {
|
|
|
|
- const { selectedDay, range, date, hideHeader, firstDayOfWeek } =
|
|
|
|
- toRefs(props);
|
|
|
|
- const elCalendar = inject("elCalendar");
|
|
|
|
-
|
|
|
|
- const WEEK_DAYS = computed(() => getI18nSettings().dayNames);
|
|
|
|
-
|
|
|
|
- const prevMonthDatePrefix = computed(() => {
|
|
|
|
- const temp = new Date(date.value.getTime());
|
|
|
|
- temp.setDate(0);
|
|
|
|
- return formatDate(temp, "YYYY-MM");
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- const curMonthDatePrefix = computed(() =>
|
|
|
|
- formatDate(date.value, "YYYY-MM")
|
|
|
|
- );
|
|
|
|
-
|
|
|
|
- const nextMonthDatePrefix = computed(() => {
|
|
|
|
- const temp = new Date(
|
|
|
|
- date.value.getFullYear(),
|
|
|
|
- date.value.getMonth() + 1,
|
|
|
|
- 1
|
|
|
|
- );
|
|
|
|
- return formatDate(temp, "YYYY-MM");
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- const formatedToday = computed(() => elCalendar.formatedToday);
|
|
|
|
-
|
|
|
|
- const isInRange = computed(() => range.value && range.value.length);
|
|
|
|
-
|
|
|
|
- const weekDays = computed(() => {
|
|
|
|
- const start = firstDayOfWeek.value;
|
|
|
|
- const { WEEK_DAYS } = { WEEK_DAYS: getI18nSettings().dayNames };
|
|
|
|
-
|
|
|
|
- if (typeof start !== "number" || start === 0) {
|
|
|
|
- return WEEK_DAYS.slice();
|
|
|
|
- } else {
|
|
|
|
- return WEEK_DAYS.slice(start).concat(WEEK_DAYS.slice(0, start));
|
|
|
|
- }
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- const rows = computed(() => {
|
|
|
|
- let days = [];
|
|
|
|
- if (isInRange.value) {
|
|
|
|
- const [start, end] = range.value;
|
|
|
|
- const currentMonthRange = rangeArr(
|
|
|
|
- end.getDate() - start.getDate() + 1
|
|
|
|
- ).map((_, index) => ({
|
|
|
|
- text: start.getDate() + index,
|
|
|
|
- type: "current",
|
|
|
|
- }));
|
|
|
|
- let remaining = currentMonthRange.length % 7;
|
|
|
|
- remaining = remaining === 0 ? 0 : 7 - remaining;
|
|
|
|
- const nextMonthRange = rangeArr(remaining).map((_, index) => ({
|
|
|
|
- text: index + 1,
|
|
|
|
- type: "next",
|
|
|
|
- }));
|
|
|
|
- days = currentMonthRange.concat(nextMonthRange);
|
|
|
|
- } else {
|
|
|
|
- let firstDay = getFirstDayOfMonth(date.value);
|
|
|
|
- firstDay = firstDay === 0 ? 7 : firstDay;
|
|
|
|
- const firstDayOfWeek =
|
|
|
|
- typeof props.firstDayOfWeek === "number" ? props.firstDayOfWeek : 1;
|
|
|
|
- const offset = (7 + firstDay - firstDayOfWeek) % 7;
|
|
|
|
- const prevMonthDays = getPrevMonthLastDays(date.value, offset).map(
|
|
|
|
- (day) => ({
|
|
|
|
- text: day,
|
|
|
|
- type: "prev",
|
|
|
|
- })
|
|
|
|
- );
|
|
|
|
- const currentMonthDays = getMonthDays(date.value).map((day) => ({
|
|
|
|
- text: day,
|
|
|
|
- type: "current",
|
|
|
|
- }));
|
|
|
|
- days = [...prevMonthDays, ...currentMonthDays];
|
|
|
|
- const nextMonthDays = rangeArr(42 - days.length).map((_, index) => ({
|
|
|
|
- text: index + 1,
|
|
|
|
- type: "next",
|
|
|
|
- }));
|
|
|
|
- days = days.concat(nextMonthDays);
|
|
|
|
- }
|
|
|
|
- return toNestedArr(days);
|
|
|
|
- });
|
|
|
|
-
|
|
|
|
- function toNestedArr(days) {
|
|
|
|
- return rangeArr(days.length / 7).map((_, index) => {
|
|
|
|
- const start = index * 7;
|
|
|
|
- return days.slice(start, start + 7);
|
|
|
|
- });
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function getFormateDate(day, type) {
|
|
|
|
- if (!day || ["prev", "current", "next"].indexOf(type) === -1) {
|
|
|
|
- throw new Error("invalid day or type");
|
|
|
|
- }
|
|
|
|
- let prefix = curMonthDatePrefix.value;
|
|
|
|
- if (type === "prev") {
|
|
|
|
- prefix = prevMonthDatePrefix.value;
|
|
|
|
- } else if (type === "next") {
|
|
|
|
- prefix = nextMonthDatePrefix.value;
|
|
|
|
- }
|
|
|
|
- day = `00${day}`.slice(-2);
|
|
|
|
- return `${prefix}-${day}`;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function getCellClass({ text, type }) {
|
|
|
|
- const classes = [type];
|
|
|
|
- if (type === "current") {
|
|
|
|
- const date = getFormateDate(text, type);
|
|
|
|
- if (date === selectedDay.value) {
|
|
|
|
- classes.push("is-selected");
|
|
|
|
- }
|
|
|
|
- if (date === formatedToday.value) {
|
|
|
|
- classes.push("is-today");
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- return classes;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function pickDay({ text, type }) {
|
|
|
|
- const date = getFormateDate(text, type);
|
|
|
|
- emit("pick", date);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- function cellRenderProxy({ text, type }) {
|
|
|
|
- let render = elCalendar.$slots.dateCell;
|
|
|
|
- if (!render) return text;
|
|
|
|
-
|
|
|
|
- const day = getFormateDate(text, type);
|
|
|
|
- const date = new Date(day);
|
|
|
|
- const data = {
|
|
|
|
- isSelected: selectedDay.value === day,
|
|
|
|
- type: `${type}-month`,
|
|
|
|
- day,
|
|
|
|
- };
|
|
|
|
- return render({ date, data });
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return {
|
|
|
|
- WEEK_DAYS,
|
|
|
|
- prevMonthDatePrefix,
|
|
|
|
- curMonthDatePrefix,
|
|
|
|
- nextMonthDatePrefix,
|
|
|
|
- formatedToday,
|
|
|
|
- isInRange,
|
|
|
|
- rows,
|
|
|
|
- weekDays,
|
|
|
|
- toNestedArr,
|
|
|
|
- getFormateDate,
|
|
|
|
- getCellClass,
|
|
|
|
- pickDay,
|
|
|
|
- cellRenderProxy,
|
|
|
|
- };
|
|
|
|
- },
|
|
|
|
-});
|
|
|
|
-</script>
|
|
|
|
-
|
|
|
|
-<style lang="scss">
|
|
|
|
-.el-calendar-table {
|
|
|
|
- table-layout: fixed;
|
|
|
|
- width: 100%;
|
|
|
|
-
|
|
|
|
- thead th {
|
|
|
|
- padding: 12px 0;
|
|
|
|
- color: #606266;
|
|
|
|
- font-weight: 400;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- td {
|
|
|
|
- border-bottom: 1px solid #ebeef5;
|
|
|
|
- border-right: 1px solid #ebeef5;
|
|
|
|
- vertical-align: top;
|
|
|
|
- transition: background-color 0.2s ease;
|
|
|
|
-
|
|
|
|
- &.prev {
|
|
|
|
- color: #c0c4cc;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
- tr:first-child td {
|
|
|
|
- border-top: 1px solid #ebeef5;
|
|
|
|
- }
|
|
|
|
- .el-calendar-day {
|
|
|
|
- box-sizing: border-box;
|
|
|
|
- padding: 8px;
|
|
|
|
- height: 85px;
|
|
|
|
- cursor: pointer;
|
|
|
|
- }
|
|
|
|
-}
|
|
|
|
-</style>
|
|
|
|
|
|
+<template>
|
|
|
|
+ <table
|
|
|
|
+ :class="['el-calendar-table', { 'is-range': isInRange }]"
|
|
|
|
+ cellspacing="0"
|
|
|
|
+ cellpadding="0"
|
|
|
|
+ >
|
|
|
|
+ <thead v-if="!hideHeader">
|
|
|
|
+ <tr>
|
|
|
|
+ <th v-for="day in weekDays" :key="day">{{ day }}</th>
|
|
|
|
+ </tr>
|
|
|
|
+ </thead>
|
|
|
|
+ <tbody>
|
|
|
|
+ <tr
|
|
|
|
+ v-for="(row, rowIndex) in rows"
|
|
|
|
+ :key="rowIndex"
|
|
|
|
+ :class="[
|
|
|
|
+ 'el-calendar-table__row',
|
|
|
|
+ {
|
|
|
|
+ 'el-calendar-table__row--hide-border': rowIndex === 0 && hideHeader,
|
|
|
|
+ },
|
|
|
|
+ ]"
|
|
|
|
+ >
|
|
|
|
+ <td
|
|
|
|
+ v-for="(cell, cellIndex) in row"
|
|
|
|
+ :key="cellIndex"
|
|
|
|
+ :class="getCellClass(cell)"
|
|
|
|
+ @click="pickDay(cell)"
|
|
|
|
+ >
|
|
|
|
+ <div class="el-calendar-day">
|
|
|
|
+ <slot name="dateCell" :data="cellRenderProxy(cell)" />
|
|
|
|
+ </div>
|
|
|
|
+ </td>
|
|
|
|
+ </tr>
|
|
|
|
+ </tbody>
|
|
|
|
+ </table>
|
|
|
|
+</template>
|
|
|
|
+
|
|
|
|
+<script>
|
|
|
|
+import { formatDate } from "@dage/utils";
|
|
|
|
+import {
|
|
|
|
+ range as rangeArr,
|
|
|
|
+ getFirstDayOfMonth,
|
|
|
|
+ getPrevMonthLastDays,
|
|
|
|
+ getMonthDays,
|
|
|
|
+ getI18nSettings,
|
|
|
|
+ validateRangeInOneMonth,
|
|
|
|
+} from "./utils";
|
|
|
|
+import { ref, computed, inject, toRefs, defineComponent } from "vue";
|
|
|
|
+
|
|
|
|
+export default defineComponent({
|
|
|
|
+ name: "CalendarComponent",
|
|
|
|
+ props: {
|
|
|
|
+ selectedDay: String,
|
|
|
|
+ range: {
|
|
|
|
+ type: Array,
|
|
|
|
+ validator(val) {
|
|
|
|
+ if (!(val && val.length)) return true;
|
|
|
|
+ const [start, end] = val;
|
|
|
|
+ return validateRangeInOneMonth(start, end);
|
|
|
|
+ },
|
|
|
|
+ },
|
|
|
|
+ date: Date,
|
|
|
|
+ hideHeader: Boolean,
|
|
|
|
+ firstDayOfWeek: Number,
|
|
|
|
+ },
|
|
|
|
+ setup(props, { emit }) {
|
|
|
|
+ const { selectedDay, range, date, hideHeader, firstDayOfWeek } =
|
|
|
|
+ toRefs(props);
|
|
|
|
+ const elCalendar = inject("elCalendar");
|
|
|
|
+
|
|
|
|
+ const WEEK_DAYS = computed(() => getI18nSettings().dayNames);
|
|
|
|
+
|
|
|
|
+ const prevMonthDatePrefix = computed(() => {
|
|
|
|
+ const temp = new Date(date.value.getTime());
|
|
|
|
+ temp.setDate(0);
|
|
|
|
+ return formatDate(temp, "YYYY-MM");
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const curMonthDatePrefix = computed(() =>
|
|
|
|
+ formatDate(date.value, "YYYY-MM")
|
|
|
|
+ );
|
|
|
|
+
|
|
|
|
+ const nextMonthDatePrefix = computed(() => {
|
|
|
|
+ const temp = new Date(
|
|
|
|
+ date.value.getFullYear(),
|
|
|
|
+ date.value.getMonth() + 1,
|
|
|
|
+ 1
|
|
|
|
+ );
|
|
|
|
+ return formatDate(temp, "YYYY-MM");
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const formatedToday = computed(() => elCalendar.formatedToday);
|
|
|
|
+
|
|
|
|
+ const isInRange = computed(() => range.value && range.value.length);
|
|
|
|
+
|
|
|
|
+ const weekDays = computed(() => {
|
|
|
|
+ const start = firstDayOfWeek.value;
|
|
|
|
+ const { WEEK_DAYS } = { WEEK_DAYS: getI18nSettings().dayNames };
|
|
|
|
+
|
|
|
|
+ if (typeof start !== "number" || start === 0) {
|
|
|
|
+ return WEEK_DAYS.slice();
|
|
|
|
+ } else {
|
|
|
|
+ return WEEK_DAYS.slice(start).concat(WEEK_DAYS.slice(0, start));
|
|
|
|
+ }
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ const rows = computed(() => {
|
|
|
|
+ let days = [];
|
|
|
|
+ if (isInRange.value) {
|
|
|
|
+ const [start, end] = range.value;
|
|
|
|
+ const currentMonthRange = rangeArr(
|
|
|
|
+ end.getDate() - start.getDate() + 1
|
|
|
|
+ ).map((_, index) => ({
|
|
|
|
+ text: start.getDate() + index,
|
|
|
|
+ type: "current",
|
|
|
|
+ }));
|
|
|
|
+ let remaining = currentMonthRange.length % 7;
|
|
|
|
+ remaining = remaining === 0 ? 0 : 7 - remaining;
|
|
|
|
+ const nextMonthRange = rangeArr(remaining).map((_, index) => ({
|
|
|
|
+ text: index + 1,
|
|
|
|
+ type: "next",
|
|
|
|
+ }));
|
|
|
|
+ days = currentMonthRange.concat(nextMonthRange);
|
|
|
|
+ } else {
|
|
|
|
+ let firstDay = getFirstDayOfMonth(date.value);
|
|
|
|
+ firstDay = firstDay === 0 ? 7 : firstDay;
|
|
|
|
+ const firstDayOfWeek =
|
|
|
|
+ typeof props.firstDayOfWeek === "number" ? props.firstDayOfWeek : 1;
|
|
|
|
+ const offset = (7 + firstDay - firstDayOfWeek) % 7;
|
|
|
|
+ const prevMonthDays = getPrevMonthLastDays(date.value, offset).map(
|
|
|
|
+ (day) => ({
|
|
|
|
+ text: day,
|
|
|
|
+ type: "prev",
|
|
|
|
+ })
|
|
|
|
+ );
|
|
|
|
+ const currentMonthDays = getMonthDays(date.value).map((day) => ({
|
|
|
|
+ text: day,
|
|
|
|
+ type: "current",
|
|
|
|
+ }));
|
|
|
|
+ days = [...prevMonthDays, ...currentMonthDays];
|
|
|
|
+ const nextMonthDays = rangeArr(42 - days.length).map((_, index) => ({
|
|
|
|
+ text: index + 1,
|
|
|
|
+ type: "next",
|
|
|
|
+ }));
|
|
|
|
+ days = days.concat(nextMonthDays);
|
|
|
|
+ }
|
|
|
|
+ return toNestedArr(days);
|
|
|
|
+ });
|
|
|
|
+
|
|
|
|
+ function toNestedArr(days) {
|
|
|
|
+ return rangeArr(days.length / 7).map((_, index) => {
|
|
|
|
+ const start = index * 7;
|
|
|
|
+ return days.slice(start, start + 7);
|
|
|
|
+ });
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function getFormateDate(day, type) {
|
|
|
|
+ if (!day || ["prev", "current", "next"].indexOf(type) === -1) {
|
|
|
|
+ throw new Error("invalid day or type");
|
|
|
|
+ }
|
|
|
|
+ let prefix = curMonthDatePrefix.value;
|
|
|
|
+ if (type === "prev") {
|
|
|
|
+ prefix = prevMonthDatePrefix.value;
|
|
|
|
+ } else if (type === "next") {
|
|
|
|
+ prefix = nextMonthDatePrefix.value;
|
|
|
|
+ }
|
|
|
|
+ day = `00${day}`.slice(-2);
|
|
|
|
+ return `${prefix}-${day}`;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function getCellClass({ text, type }) {
|
|
|
|
+ const classes = [type];
|
|
|
|
+ if (type === "current") {
|
|
|
|
+ const date = getFormateDate(text, type);
|
|
|
|
+ if (date === selectedDay.value) {
|
|
|
|
+ classes.push("is-selected");
|
|
|
|
+ }
|
|
|
|
+ if (date === formatedToday.value) {
|
|
|
|
+ classes.push("is-today");
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ return classes;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function pickDay({ text, type }) {
|
|
|
|
+ const date = getFormateDate(text, type);
|
|
|
|
+ emit("pick", date);
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ function cellRenderProxy({ text, type }) {
|
|
|
|
+ const day = getFormateDate(text, type);
|
|
|
|
+ const date = new Date(day);
|
|
|
|
+ const data = {
|
|
|
|
+ isSelected: selectedDay.value === day,
|
|
|
|
+ type: `${type}-month`,
|
|
|
|
+ day,
|
|
|
|
+ };
|
|
|
|
+ return { date, data };
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return {
|
|
|
|
+ WEEK_DAYS,
|
|
|
|
+ prevMonthDatePrefix,
|
|
|
|
+ curMonthDatePrefix,
|
|
|
|
+ nextMonthDatePrefix,
|
|
|
|
+ formatedToday,
|
|
|
|
+ isInRange,
|
|
|
|
+ rows,
|
|
|
|
+ weekDays,
|
|
|
|
+ toNestedArr,
|
|
|
|
+ getFormateDate,
|
|
|
|
+ getCellClass,
|
|
|
|
+ pickDay,
|
|
|
|
+ cellRenderProxy,
|
|
|
|
+ };
|
|
|
|
+ },
|
|
|
|
+});
|
|
|
|
+</script>
|
|
|
|
+
|
|
|
|
+<style lang="scss">
|
|
|
|
+.el-calendar-table {
|
|
|
|
+ table-layout: fixed;
|
|
|
|
+ width: 100%;
|
|
|
|
+
|
|
|
|
+ thead th {
|
|
|
|
+ padding: 12px 0;
|
|
|
|
+ color: #606266;
|
|
|
|
+ font-weight: 400;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ td {
|
|
|
|
+ border-bottom: 1px solid #ebeef5;
|
|
|
|
+ border-right: 1px solid #ebeef5;
|
|
|
|
+ vertical-align: top;
|
|
|
|
+ transition: background-color 0.2s ease;
|
|
|
|
+
|
|
|
|
+ &.prev {
|
|
|
|
+ color: #c0c4cc;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ tr:first-child td {
|
|
|
|
+ border-top: 1px solid #ebeef5;
|
|
|
|
+ }
|
|
|
|
+ .el-calendar-day {
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
+ padding: 8px;
|
|
|
|
+ height: 85px;
|
|
|
|
+ cursor: pointer;
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+</style>
|