Browse Source

calendar组件

songxy 1 year ago
parent
commit
728675cc0d

+ 205 - 0
client/src/components/Calendar/DateCalendar.vue

@@ -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>

+ 8 - 15
client/src/components/Calendar/calendar.ts

@@ -8,7 +8,8 @@ const getPreDate: DayFunType = (dmStr, day = 0) => {
       for (let k = preDay; k > 0; k--) {
         const kDom: moment.Moment = moment(dmStr).subtract(k, 'days')
         const kItem = {
-          dm: kDom.format('yyyy-MM-DD'),
+          dm: kDom.format('YYYY-MM-DD'),
+          y: kDom.format('YYYY'),
           m: kDom.format('MM'),
           d: kDom.format('DD'),
           w: useMomentDay(kDom)
@@ -27,7 +28,8 @@ const getNextDate: DayFunType = (dmStr, day = 0) => {
     for (let k = 1; k <= nextDay; k++) {
       const kDom: moment.Moment = moment(dmStr).add(k, 'days')
       const kItem = {
-        dm: kDom.format('yyyy-MM-DD'),
+        dm: kDom.format('YYYY-MM-DD'),
+        y: kDom.format('YYYY'),
         m: kDom.format('MM'),
         d: kDom.format('DD'),
         w: useMomentDay(kDom)
@@ -37,19 +39,13 @@ const getNextDate: DayFunType = (dmStr, day = 0) => {
   }
   return arr
 }
-
 /**
  * 获取当前时间处于星期几,并转将星期日转换成7
  * @param dateStr YYYY-MM-DD
  * @return number
  * **/
 export const useMomentDay = (date: dateType): number => {
-  let cDate: moment.Moment
-  if (typeof date === 'string') {
-    cDate = moment(date)
-  } else {
-    cDate = date
-  }
+  const cDate: moment.Moment = moment(date)
   const day = cDate.day()
 
   return day === 0 ? 7 : day
@@ -59,16 +55,12 @@ export const useMomentDay = (date: dateType): number => {
  * @param date 'YYYY' 统计当月 'YYYY-MM/YYYY-MM-DD' 统计传入的月份
  * @return IDate[]
  * **/
+
 export const useComputedDates: DatesFunType = (date: dateType) => {
   const dateList: Array<IDate> = []
   const datesArr: Array<Array<IDate>> = []
 
-  let momentDate: moment.Moment
-  if (typeof date === 'string') {
-    momentDate = moment(date)
-  } else {
-    momentDate = date
-  }
+  const momentDate: moment.Moment = moment(date)
   const currentM: string = momentDate.format('YYYY-MM') // 当前月
   const curDays: number = momentDate.daysInMonth() // 当前天数
   for (let i = 0; i < curDays; i++) {
@@ -85,6 +77,7 @@ export const useComputedDates: DatesFunType = (date: dateType) => {
     }
     dateList.push({
       dm: dmStr,
+      y: moment(dmStr).format('YYYY'),
       m: moment(dmStr).format('MM'),
       d: i + 1,
       w: dayNum

+ 1 - 1
client/src/plugins/elementPlus/index.ts

@@ -1,7 +1,7 @@
 import type { App } from 'vue'
 // 需要全局引入一些组件,如ElScrollbar,不然一些下拉项样式有问题
 import { ElLoading, ElScrollbar, ElButton } from 'element-plus'
-
+import 'dayjs/locale/zh-cn'
 const plugins = [ElLoading]
 
 const components = [ElScrollbar, ElButton]

+ 3 - 1
client/src/types/calendar.d.ts

@@ -1,10 +1,12 @@
 interface IDate {
   dm: string //年月日
+  y: string | number //年份
   m: string | number //月份
   d: string | number //日期
   w: number | number //星期几
 }
 
+type DateCalendarType = 'prev-month' | 'next-month' | 'prev-year' | 'next-year' | 'today'
 type DayFunType = (dmStr: string, day: number) => Array<IDate>
 type DatesFunType = (dateStr: string) => IDate[][]
-type dateType = string | moment.Moment
+type dateType = string | moment.Moment | Date

+ 2 - 65
client/src/views/OaSystem/home/components/CardItemFive1.vue

@@ -1,79 +1,16 @@
 <template>
   <div class="FiveDetailBox">
-    <el-calendar ref="calendar">
-      <template #header="{ date }">
-        <div class="calendarHeader">
-          <el-icon class="icon1" @click="selectDate('prev-month')"><ArrowLeftBold /></el-icon>
-          <span class="title">{{ date }}</span>
-          <el-icon class="icon1" @click="selectDate('next-month')"><ArrowRightBold /></el-icon>
-        </div>
-      </template>
-    </el-calendar>
+    <date-calendar />
   </div>
 </template>
 
 <script setup lang="ts">
-import type { CalendarDateType, CalendarInstance } from 'element-plus'
-const calendar = ref<CalendarInstance>()
-const selectDate = (val: CalendarDateType) => {
-  if (!calendar.value) return
-  calendar.value.selectDate(val)
-}
+import DateCalendar from '@/components/Calendar/DateCalendar.vue'
 </script>
 
 <style lang="scss" scoped>
 @import '../common.scss';
 .FiveDetailBox {
   height: 100%;
-  .calendarHeader {
-    width: 100%;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    .title {
-      font-size: 18px;
-      font-weight: bold;
-    }
-    padding: 20px 20px 0px 20px;
-    .icon1 {
-      cursor: pointer;
-      color: #8b969c;
-    }
-  }
-  ::v-deep {
-    .el-calendar {
-      .el-calendar__header {
-        border: 0px;
-      }
-      .el-calendar__body {
-        padding-bottom: 15px;
-      }
-      .el-calendar-table {
-        td {
-          border: 0px;
-        }
-        .el-calendar-day {
-          height: auto;
-          text-align: center;
-          border: 0px;
-          width: 40px;
-          height: 40px;
-          background-color: none;
-          margin: auto;
-        }
-        .is-today {
-          &.is-selected {
-            background: none;
-          }
-
-          .el-calendar-day {
-            border-radius: 50%;
-            background-color: #2d76e8;
-            color: #fff;
-          }
-        }
-      }
-    }
-  }
 }
 </style>

+ 16 - 23
client/src/views/OaSystem/home/components/CardItemFive2.vue

@@ -1,29 +1,19 @@
 <template>
   <div class="FiveDetailBox">
-    <el-calendar ref="calendar">
-      <template #header="{ date }">
-        <div class="calendarHeader">
-          <el-icon class="icon1" @click="selectDate('prev-month')"><ArrowLeftBold /></el-icon>
-          <span class="title">{{ date }}</span>
-          <el-icon class="icon1" @click="selectDate('next-month')"><ArrowRightBold /></el-icon>
-        </div>
-      </template>
-      <template #date-cell="{ data }">
-        <p :class="`isSelected_${mockData[data.day] ? mockData[data.day]['s'] : '0'}`">
-          {{ data.day.split('-').slice(1)[1] }}
+    <date-calendar>
+      <template #cell="{ data }">
+        <p
+          :class="`date-cell isSelected_${mockData[data['dm']] ? mockData[data['dm']]['s'] : '0'}`"
+        >
+          {{ data['d'] }}
         </p>
       </template>
-    </el-calendar>
+    </date-calendar>
   </div>
 </template>
 
 <script setup lang="ts">
-import type { CalendarDateType, CalendarInstance } from 'element-plus'
-const calendar = ref<CalendarInstance>()
-const selectDate = (val: CalendarDateType) => {
-  if (!calendar.value) return
-  calendar.value.selectDate(val)
-}
+import DateCalendar from '@/components/Calendar/DateCalendar.vue'
 
 const mockData = ref({
   '2023-11-01': {
@@ -32,8 +22,8 @@ const mockData = ref({
   '2023-11-02': {
     s: 2 //1 未填 2 已填 3 请假
   },
-  '2023-11-03': {
-    s: 2 //1 未填 2 已填 3 请假
+  '2023-11-13': {
+    s: 1 //1 未填 2 已填 3 请假
   }
 })
 </script>
@@ -65,7 +55,7 @@ const mockData = ref({
       .el-calendar__body {
         padding-bottom: 15px;
       }
-      .el-calendar-table {
+      .date_calendar_table {
         td {
           border: 0px;
         }
@@ -96,11 +86,14 @@ const mockData = ref({
       }
     }
   }
+  .date-cell {
+    width: auto;
+    padding: 3px 8px;
+    cursor: pointer;
+  }
   //控制不同状态的颜色
   .isSelected_1 {
     border: 1px solid #f83535;
-    display: inline-block;
-    padding: 3px 8px;
     border-radius: 2px;
     color: #f83535;
     position: relative;

+ 8 - 66
client/src/views/OaSystem/home/components/CardItemFive3.vue

@@ -1,14 +1,8 @@
 <template>
   <div class="FiveDetailBox">
-    <el-calendar ref="calendar">
-      <template #header="{ date }">
-        <div class="calendarHeader">
-          <el-icon class="icon1" @click="selectDate('prev-month')"><ArrowLeftBold /></el-icon>
-          <span class="title">{{ date }}</span>
-          <el-icon class="icon1" @click="selectDate('next-month')"><ArrowRightBold /></el-icon>
-        </div>
-      </template>
-    </el-calendar>
+    <div class="dateCalendarBox">
+      <date-calendar ref="calendar" />
+    </div>
     <ul class="weekNumBox">
       <li v-for="(v, i) in 5" :key="i">
         <span class="title">第{{ v }}周</span>
@@ -19,69 +13,17 @@
 </template>
 
 <script setup lang="ts">
-import type { CalendarDateType, CalendarInstance } from 'element-plus'
-const calendar = ref<CalendarInstance>()
-const selectDate = (val: CalendarDateType) => {
-  if (!calendar.value) return
-  calendar.value.selectDate(val)
-}
+import DateCalendar from '@/components/Calendar/DateCalendar.vue'
 </script>
 
 <style lang="scss" scoped>
 @import '../common.scss';
 .FiveDetailBox {
   height: 100%;
-  .calendarHeader {
-    width: 100%;
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    .title {
-      font-size: 18px;
-      font-weight: bold;
-    }
-    padding: 20px 20px 0px 20px;
-    .icon1 {
-      cursor: pointer;
-      color: #8b969c;
-    }
-  }
-  :deep {
-    .el-calendar {
-      .el-calendar__header {
-        border: 0px;
-      }
-      .el-calendar__body {
-        padding-bottom: 15px;
-      }
-      .el-calendar-table {
-        td {
-          border: 0px;
-        }
-        .el-calendar-day {
-          height: auto;
-          text-align: center;
-          border: 0px;
-          width: 30px;
-          height: 30px;
-          line-height: 30px;
-          padding: 0px 0px;
-          background-color: none;
-          margin: auto;
-        }
-        .is-today {
-          &.is-selected {
-            background: none;
-          }
-
-          .el-calendar-day {
-            border-radius: 50%;
-            background-color: #2d76e8;
-            color: #fff;
-          }
-        }
-      }
-    }
+  display: flex;
+  flex-direction: column;
+  > .dateCalendarBox {
+    flex: 1;
   }
   > .weekNumBox {
     display: flex;