Hwf há 6 meses atrás
pai
commit
654a6e9dd9
5 ficheiros alterados com 275 adições e 0 exclusões
  1. 1 0
      package.json
  2. 3 0
      pnpm-lock.yaml
  3. 226 0
      src/components/Calendar/index.vue
  4. 8 0
      src/router/routes.ts
  5. 37 0
      src/views/test.vue

+ 1 - 0
package.json

@@ -19,6 +19,7 @@
   },
   "dependencies": {
     "@amap/amap-jsapi-loader": "^1.0.1",
+    "@element-plus/icons-vue": "^2.3.1",
     "@types/file-saver": "^2.0.7",
     "@types/uuid": "^10.0.0",
     "@vueuse/core": "^11.0.3",

+ 3 - 0
pnpm-lock.yaml

@@ -11,6 +11,9 @@ importers:
       '@amap/amap-jsapi-loader':
         specifier: ^1.0.1
         version: 1.0.1
+      '@element-plus/icons-vue':
+        specifier: ^2.3.1
+        version: 2.3.1(vue@3.5.11(typescript@5.4.5))
       '@types/file-saver':
         specifier: ^2.0.7
         version: 2.0.7

+ 226 - 0
src/components/Calendar/index.vue

@@ -0,0 +1,226 @@
+<template>
+  <div class="calendar">
+    <div class="calendar-header">
+      <div class="calendar-header__left">
+        <el-icon class="hand" @click="nextYear(-1)"><DArrowLeft /></el-icon>
+        <el-icon class="hand" @click="nextMonth(-1)"><ArrowLeft /></el-icon>
+      </div>
+      <div class="calendar-header__center">{{ showDate.year }} 年 {{ showDate.month + 1 }} 月</div>
+      <div class="calendar-header__right">
+        <el-icon class="hand" @click="nextMonth(1)"><ArrowRight /></el-icon>
+        <el-icon class="hand" @click="nextYear(1)"><DArrowRight /></el-icon>
+      </div>
+    </div>
+    <div class="calendar-main">
+      <table class="calendar-table">
+        <thead class="calendar-thead">
+        <tr>
+          <th v-for="(day, index) in sevenDay" :key="index">{{ day }}</th>
+        </tr>
+        </thead>
+        <tbody class="calendar-tbody">
+        <tr v-for="row in rows" :key="row">
+          <td v-for="date in dateList.slice((row - 1) * 7, (row - 1) * 7 + 7)" :key="date.value">
+            <div
+                class="calendar-date hand"
+                :class="{
+                  'calendar-date--grey': date.month !== showDate.month,
+                  'calendar-date--today': date.value === currentDate.value
+                }"
+                :style="getStyle(date)"
+                @click="handleChangeCurrent(date.dayjs)"
+            >
+              <span>{{ date.date }}</span>
+            </div>
+          </td>
+        </tr>
+        </tbody>
+      </table>
+    </div>
+    <div class="calendar-footer" @click="handleSelectToday">今天</div>
+  </div>
+</template>
+<script lang="ts" setup name="Calendar">
+import dayjs, { Dayjs } from "dayjs";
+import {onMounted, ref} from "vue";
+import { ElIcon } from 'element-plus';
+import { DArrowLeft, ArrowLeft, DArrowRight, ArrowRight } from '@element-plus/icons-vue';
+
+interface CalendarDate {
+  year: number;
+  month: number;
+  date: number;
+  value: string;
+  dayjs: Dayjs;
+}
+interface DotDay {
+  date: string;
+  color: string;
+}
+interface Props {
+  dotDays?: DotDay[]; // 需要加点的日期
+  format?: string; // 日期格式,必须保证格式化后的日期是唯一的,默认YYYY-MM-DD(参考dayjs)
+  current?: string; // 当前选中的日期,格式与format保持一致,默认选择当前日期
+  disabled?: boolean;
+}
+
+const props = withDefaults(defineProps<Props>(), {
+  dotDays: () => [], // [{ date: '2024-10-19', color: '#ff0000' }]
+  format: "YYYY-MM-DD",
+  disabled: false
+});
+const sevenDay: Array<string> = ["日", "一", "二", "三", "四", "五", "六"];
+const dateList = ref<CalendarDate[]>([]);
+
+const getCalendarDate = (dayjs: Dayjs): CalendarDate => {
+  return {
+    year: dayjs.year(),
+    month: dayjs.month(),
+    date: dayjs.date(),
+    value: dayjs.format(props.format),
+    dayjs: dayjs
+  };
+};
+// 选中的日期
+const currentDate = ref<CalendarDate>(getCalendarDate(dayjs()));
+// 当前日历展示的日期
+const showDate = ref<CalendarDate>(currentDate.value);
+
+// 获取日期列表,6*7天
+const rows = 6;
+const getDateList = (): Array<CalendarDate> => {
+  const firstDay = showDate.value.dayjs.startOf("month").startOf("week");
+  return Array(rows * 7)
+      .fill(0)
+      .map((n, i) => {
+        const day = firstDay.add(i, "day");
+        return getCalendarDate(day);
+      });
+};
+
+const nextMonth = (number: number) => {
+  showDate.value = getCalendarDate(showDate.value.dayjs.add(number, "month"));
+  dateList.value = getDateList();
+};
+const nextYear = (number: number) => {
+  showDate.value = getCalendarDate(showDate.value.dayjs.add(number, "year"));
+  dateList.value = getDateList();
+};
+
+// 修改选中日期
+const emit = defineEmits(["change"]);
+const handleChangeCurrent = (dayjs: Dayjs) => {
+  if (props.disabled) return;
+  currentDate.value = getCalendarDate(dayjs);
+  showDate.value = currentDate.value;
+  dateList.value = getDateList();
+  emit("change", currentDate.value);
+};
+
+// 给不同颜色赋值
+const getStyle = (date) => {
+  let style = { backgroundColor: '' }
+  for (let i = 0; i < props.dotDays.length; i++) {
+    if (props.dotDays[i].date === date.value) {
+      style.backgroundColor = props.dotDays[i].color;
+      break;
+    }
+  }
+  return style;
+}
+
+// 选择今天
+const handleSelectToday = () => {
+  currentDate.value = getCalendarDate(dayjs());
+};
+onMounted(() => {
+  if (props.current) {
+    handleChangeCurrent(dayjs(props.current, props.format));
+  } else {
+    dateList.value = getDateList();
+  }
+});
+</script>
+<style lang="scss" scoped>
+.calendar {
+  width: 355px;
+  user-select: none;
+  background-color: #ffffff;
+  color: #414f64;
+  .calendar-header {
+    display: flex;
+    align-items: center;
+    justify-content: space-between;
+    padding: 10px 12px;
+    font-size: 14px;
+    color: var(--el-text-color-primary);
+    border-bottom: 1px solid #f0f1f1;
+    .calendar-header__center {
+      font-weight: bold;
+    }
+  }
+  .calendar-main {
+    padding-bottom: 12px;
+    margin-top: 12px;
+  }
+  .calendar-table {
+    width: 100%;
+    th,
+    td {
+      // width: (100/7);
+      width: 14.285%;
+      min-width: 40px;
+      padding: 4px 0;
+      font-size: 14px;
+      font-weight: 400;
+      line-height: 24px;
+      color: var(--el-text-color-primary);
+      text-align: center;
+    }
+    th {
+      color: #bfbfbf;
+    }
+  }
+  .calendar-date {
+    position: relative;
+    width: 24px;
+    height: 24px;
+    margin: -1px auto;
+    border-radius: 2px;
+    &--grey {
+      color: #bfbfbf;
+    }
+    &:hover {
+      background: var(--el-color-primary-light-9);
+    }
+    &--today {
+      color: #ffffff !important;
+      background: var(--el-color-primary) !important;
+    }
+  }
+  .calendar-dot {
+    position: absolute;
+    top: -3px;
+    right: -3px;
+    width: 6px;
+    height: 6px;
+    background-color: #fc474c;
+    border-radius: 50%;
+  }
+  .calendar-footer {
+    width: 100%;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    padding: 10px 12px;
+    font-size: 14px;
+    color: #1d92ff;
+    border-top: 1px solid #f0f1f1;
+  }
+}
+.hand {
+  cursor: pointer;
+  width: 25px;
+  text-align: center;
+}
+</style>

+ 8 - 0
src/router/routes.ts

@@ -223,6 +223,14 @@ export const constantRoutes: Array<RouteRecordRaw> = [
     meta: {
       title: "信息发布"
     }
+  },
+  {
+    path: "/test",
+    name: "test",
+    component: () => import("@/views/test.vue"),
+    meta: {
+      title: "测试"
+    }
   }
 ];
 

+ 37 - 0
src/views/test.vue

@@ -0,0 +1,37 @@
+<template>
+<div class="container">
+  <div @click="show = true">
+    巡查日历
+  </div>
+  <Calendar v-show="show" ref="calendarRef" :current="current" :dotDays="data" @change="handleChange" />
+</div>
+</template>
+
+<script lang="ts" setup>
+import {reactive, ref} from "vue";
+import { onClickOutside } from "@vueuse/core";
+
+let show = ref(false);
+let calendarRef = ref();
+const current = ref('2024-10-02');
+const data = reactive([
+    { date: '2024-10-19', color: '#a2d7f1' },
+    { date: '2024-10-18', color: '#ffd5d8' },
+    { date: '2024-10-17', color: '#ffefcc' },
+    { date: '2024-10-16', color: '#dfdfdf' }
+])
+onClickOutside(calendarRef, event => {
+  show.value = false;
+});
+const handleChange = (date) => {
+  current.value = date;
+  show.value = false;
+  console.log(current.value)
+}
+</script>
+
+<style lang="scss" scoped>
+.container {
+  margin: 0 auto;
+}
+</style>