|
@@ -0,0 +1,205 @@
|
|
|
+<template>
|
|
|
+ <div class="date_calendar_box">
|
|
|
+ <template v-if="$slots['header']">
|
|
|
+ <slot name="header" :data="cIDate"></slot>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <div class="date_calendar_header">
|
|
|
+ <el-icon class="icon1" @click="selectDate('prev-month')"><ArrowLeftBold /></el-icon>
|
|
|
+ <span class="date_title">{{ `${cIDate['y']}年${cIDate['m']}月` }}</span>
|
|
|
+ <el-icon class="icon1" @click="selectDate('next-month')"><ArrowRightBold /></el-icon>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ <div class="date_calendar_body">
|
|
|
+ <table class="date_calendar_table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th v-for="(v, i) in weeks" :key="`date_header_${i}`">{{ v }}</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="(item, i) in dates" :key="i">
|
|
|
+ <td
|
|
|
+ v-for="(cItem, index) in item"
|
|
|
+ :key="index"
|
|
|
+ :class="[
|
|
|
+ cItem['m'] < cIDate['m'] ? 'prev' : cItem['m'] > cIDate['m'] ? 'next' : 'current'
|
|
|
+ ]"
|
|
|
+ @click="selectHandle(cItem)"
|
|
|
+ >
|
|
|
+ <template v-if="$slots['cell']">
|
|
|
+ <slot name="cell" :data="cItem"></slot>
|
|
|
+ </template>
|
|
|
+ <template v-else>
|
|
|
+ <span
|
|
|
+ :class="{
|
|
|
+ is_today: cIDate['dm'] === cItem['dm'],
|
|
|
+ is_selected: selectDateStr === cItem['dm']
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ {{ cItem['d'] }}
|
|
|
+ </span>
|
|
|
+ </template>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="ts">
|
|
|
+import moment from 'moment'
|
|
|
+import { useComputedDates, useMomentDay } from './calendar'
|
|
|
+
|
|
|
+const $props = defineProps<{
|
|
|
+ modelValue?: string | Date
|
|
|
+}>()
|
|
|
+
|
|
|
+const $emits = defineEmits<{
|
|
|
+ (e: 'input', data: string | Date): void
|
|
|
+ (e: 'select', data: IDate): void
|
|
|
+}>()
|
|
|
+
|
|
|
+const $slots = defineSlots<{
|
|
|
+ header(props: { data: IDate }): any
|
|
|
+ cell(props: { data: IDate }): any
|
|
|
+}>()
|
|
|
+
|
|
|
+const weeks: Array<String> = ['一', '二', '三', '四', '五', '六', '日']
|
|
|
+const cDate = ref<moment.Moment>()
|
|
|
+if ($props.modelValue) {
|
|
|
+ cDate.value = moment($props.modelValue)
|
|
|
+} else {
|
|
|
+ cDate.value = moment(new Date())
|
|
|
+}
|
|
|
+const dates = ref<IDate[][]>()
|
|
|
+const cIDate = ref<IDate>({
|
|
|
+ dm: '',
|
|
|
+ y: '',
|
|
|
+ m: '',
|
|
|
+ d: '',
|
|
|
+ w: 0
|
|
|
+})
|
|
|
+const initDateCalendar = (date: moment.Moment): void => {
|
|
|
+ cIDate.value = {
|
|
|
+ dm: date.format('YYYY-MM-DD'),
|
|
|
+ y: date.format('YYYY'),
|
|
|
+ m: date.format('MM'),
|
|
|
+ d: date.format('DD'),
|
|
|
+ w: useMomentDay(date)
|
|
|
+ }
|
|
|
+ //@ts-ignore
|
|
|
+ dates.value = useComputedDates(date)
|
|
|
+}
|
|
|
+onMounted(() => {
|
|
|
+ //@ts-ignore
|
|
|
+ initDateCalendar(cDate.value)
|
|
|
+})
|
|
|
+const selectDateStr = ref<string>()
|
|
|
+const selectHandle = (data: IDate) => {
|
|
|
+ selectDateStr.value = data.dm
|
|
|
+ $emits('select', data)
|
|
|
+}
|
|
|
+const selectDate = (val: string): void => {
|
|
|
+ switch (val) {
|
|
|
+ case 'prev-month':
|
|
|
+ cDate.value?.subtract(1, 'M')
|
|
|
+ break
|
|
|
+ case 'next-month':
|
|
|
+ cDate.value?.add(1, 'M')
|
|
|
+ break
|
|
|
+ case 'prev-year':
|
|
|
+ cDate.value?.subtract(1, 'year')
|
|
|
+ break
|
|
|
+ case 'next-year':
|
|
|
+ cDate.value?.add(1, 'year')
|
|
|
+ break
|
|
|
+ case 'today':
|
|
|
+ cDate.value = moment(new Date())
|
|
|
+ break
|
|
|
+ }
|
|
|
+ //@ts-ignore
|
|
|
+ initDateCalendar(cDate.value)
|
|
|
+}
|
|
|
+defineExpose({
|
|
|
+ selectDate
|
|
|
+})
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.date_calendar_box {
|
|
|
+ height: 100%;
|
|
|
+ background-color: #fff;
|
|
|
+ padding: 15px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ user-select: none;
|
|
|
+ > .date_calendar_header {
|
|
|
+ width: 100%;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding-top: 10px;
|
|
|
+ padding-bottom: 20px;
|
|
|
+ .date_title {
|
|
|
+ font-size: 18px;
|
|
|
+ font-weight: bold;
|
|
|
+ }
|
|
|
+ .icon1 {
|
|
|
+ cursor: pointer;
|
|
|
+ color: #8b969c;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ > .date_calendar_body {
|
|
|
+ flex: 1;
|
|
|
+ > table {
|
|
|
+ width: 100%;
|
|
|
+ height: 100%;
|
|
|
+ border-collapse: collapse;
|
|
|
+ table-layout: fixed;
|
|
|
+ th,
|
|
|
+ td {
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ thead {
|
|
|
+ th {
|
|
|
+ color: #2d333c;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ tbody {
|
|
|
+ td {
|
|
|
+ > span {
|
|
|
+ display: inline-block;
|
|
|
+ width: 30px;
|
|
|
+ height: 30px;
|
|
|
+ line-height: 30px;
|
|
|
+ cursor: pointer;
|
|
|
+ &.is_selected,
|
|
|
+ &:hover {
|
|
|
+ background-color: rgba(147, 197, 253, 0.5);
|
|
|
+ }
|
|
|
+ &.is_today {
|
|
|
+ background-color: #2e77e6;
|
|
|
+ border-radius: 50%;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .prev,
|
|
|
+ .next {
|
|
|
+ color: #b9c3c9;
|
|
|
+ span {
|
|
|
+ color: #b9c3c9;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .current {
|
|
|
+ span {
|
|
|
+ color: #2d333c;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|