Stories

Detail Return Return

記錄動態修改element中el-calendar日曆組件日期的高度 - Stories Detail

首先看下需求頁面的整體佈局。

頁面分為上下佈局,上邊模塊包含左側日曆和右側導入部分,下邊模塊是數據狀態部分。日曆和導入組件固定高度420px;日曆寬度500px;數據狀態寬度100%,高度自適應。

項目是由vue2+elementui開發,這裏主要説的是日曆的高度如何動態設置?

由於業務需求,日曆只展示當前月份數據,使用css將上個月份和下個月份數據進行隱藏,所以日曆有時是5行數據展示,有時是6行數據展示,但是總的高度(420px)不變,所以每個格子高度就需要根據行數動態進行設置。

要動態設置,必須做到兩點:

  • 要知道當前月份所佔行數
  • 如何計算高度

先來看看第一點,通過觀察發現,日曆組件是用table開發的,而不管是切換到哪個月份,table下固定的是展示6個tr,第一個tr下包括上個月和當前月分數據或者只有當前月數據,第二、三、四個tr都是當前月份數據,最後一行是當前月份和下個月數據或者下個月數據,所以我們可以循環遍歷所有的tr,統計所有的tr數量,如果tr下所有的td類名是next,則不統計當前tr,就可以得到當前月份的行數。

getRows() {
      this.$nextTick(() => {
        // 獲取<tbody>元素
        const tbody = document.querySelector("tbody");

        // 初始化計數器
        let trCount = 0;

        // 遍歷所有<tr>
        tbody.querySelectorAll("tr").forEach((tr) => {
          // 檢查所有<td>是否都有next類
          const allNext = Array.from(tr.querySelectorAll("td")).every((td) =>
            td.classList.contains("next")
          );

          // 如果沒有allNext(即至少有一個<td>不是next類),則計數器加一
          if (!allNext) {
            trCount++;
          }
        });
        document.documentElement.style.setProperty("--tr-rows-number", trCount);
      });

拿到了行數,現在需要去動態計算了,我們可以使用

document.documentElement.style.setProperty("--tr-rows-number", trCount);

設置了一個變量,將計算的行數進行賦值。然後再css中使用當前變量即可。

// 設置日曆
::v-deep .el-calendar-table .el-calendar-day {
 /* 此處的288是用總高度 - 頂部一個時間選擇器高度 - 星期幾的高度 - tr中的border */
  height: calc(288px / var(--tr-rows-number));
  padding: 0;
  color: #fff;
}

看下全部代碼:

<!--
 * @Author: rk
 * @Description: 日曆組件
 * @Date: 2024-05-13 09:27:24
 * @LastEditors: rk
 * @LastEditTime: 2024-05-29 09:45:10
-->
<template>
  <el-card v-loading="loading" shadow="never">
    <div class="calendar-box">
      <div>
        <el-date-picker
          v-model="currentDate"
          type="month"
          size="small"
          style="width: 120px"
          placeholder="選中年月"
          value-format="yyyy-MM"
          :clearable="false"
          @change="handleMonthChange"
        />
      </div>
      <el-button v-if="isSameDay()" size="small" plain @click="goBackToday">
        返回今天
      </el-button>
    </div>
    <el-calendar ref="calendar" v-model="calendarDate">
      <template slot="dateCell" slot-scope="{ data }">
        <div
          class="calendar-day"
          v-for="(item, index) in statusData(data.day)"
          :key="index"
          :class="['red', 'green'][item.status]"
          @click="handleDateChange(data)"
        >
          <i
            v-if="data.isSelected"
            class="icon-select el-icon-circle-check"
          ></i>
          {{ data.day.split("-").slice(2).join("-") }}
        </div>
      </template>
    </el-calendar>
  </el-card>
</template>

<script>
// utils
import { timeFormate } from "js-fastcode";

export default {
  name: "",
  props: {
    // 接口
    apiName: {
      type: Function,
      default: () => {},
    },
  },
  data() {
    return {
      loading: false,
      // 當前日期
      currentDate: timeFormate(1),
      // 日曆時間
      calendarDate: new Date(),
      // 日曆數據
      calendarData: [],
    };
  },
  created() {
    // 獲取日曆數據佔據行數
    this.getRows();
    this.$emit("get-date-result", timeFormate(2));
  },

  methods: {
    // 獲取日曆數據
    getCalendarData() {
      this.loading = true;
      let params = { yearMonth: this.currentDate };
      this.apiName(params).then((res) => {
        this.loading = false;
        this.calendarData = [
        { date: "2024-05-01", status: 1 },
        { date: "2024-05-02", status: 1 },
        { date: "2024-05-03", status: 1 },
        { date: "2024-05-04", status: 1 },
        { date: "2024-05-05", status: 1 },
        { date: "2024-05-06", status: 1 },
        { date: "2024-05-07", status: 1 },
        { date: "2024-05-08", status: 1 },
        { date: "2024-05-09", status: 1 },
        { date: "2024-05-10", status: 1 },
        { date: "2024-05-11", status: 1 },
        { date: "2024-05-12", status: 1 },
        { date: "2024-05-13", status: 1 },
        { date: "2024-05-14", status: 1 },
        { date: "2024-05-15", status: 1 },
        { date: "2024-05-16", status: 1 },
        { date: "2024-05-17", status: 1 },
        { date: "2024-05-18", status: 1 },
        { date: "2024-05-19", status: 1 },
        { date: "2024-05-20", status: 1 },
        { date: "2024-05-21", status: 1 },
        { date: "2024-05-22", status: 1 },
        { date: "2024-05-23", status: 1 },
        { date: "2024-05-24", status: 1 },
        { date: "2024-05-25", status: 1 },
        { date: "2024-05-26", status: 1 },
        { date: "2024-05-27", status: 0 },
        { date: "2024-05-28", status: 0 },
        { date: "2024-05-29", status: 0 },
        { date: "2024-05-30", status: 1 },
        { date: "2024-05-31", status: 1 },
      ];
      });
    },
    // 切換年月
    handleMonthChange(val) {
      // 獲取當前時間月份
      let month = timeFormate(1);
      // 如果時間一致,則默認當前日期
      if (val === month) {
        this.calendarDate = new Date();
        this.$emit("get-date-result", timeFormate(2));
      } else {
        // 否則默認每個月第一天
        this.calendarDate = new Date(val);
        this.$emit("get-date-result", val + "-01");
      }
      this.getRows();
      this.getCalendarData();
    },
    // 返回今天
    goBackToday() {
      this.currentDate = timeFormate(1);
      this.calendarDate = new Date();
      this.getRows();
      this.getCalendarData();
      this.$emit("get-date-result", timeFormate(2));
    },
    // 判斷是否是當前日期
    isSameDay() {
      // 日曆時間
      const date1 = timeFormate(2, new Date(this.calendarDate));
      // 當前時間
      const date2 = timeFormate(2, new Date());
      return date1 !== date2;
    },
    // 過濾出當前數據的狀態
    statusData(date) {
      return this.calendarData.filter((item) => {
        return date === item.date;
      });
    },
    // 切換日期
    handleDateChange(data) {
      this.calendarDate = new Date(data.day);
      this.$emit("get-date-result", data.day);
    },
    /**
     * 因為每個月日曆的行數不一樣,所以需要動態計算出日曆中需要顯示的行數
     */
    getRows() {
      this.$nextTick(() => {
        // 獲取<tbody>元素
        const tbody = document.querySelector("tbody");

        // 初始化計數器
        let trCount = 0;

        // 遍歷所有<tr>
        tbody.querySelectorAll("tr").forEach((tr) => {
          // 檢查所有<td>是否都有next類
          const allNext = Array.from(tr.querySelectorAll("td")).every((td) =>
            td.classList.contains("next")
          );

          // 如果沒有allNext(即至少有一個<td>不是next類),則計數器加一
          if (!allNext) {
            trCount++;
          }
        });
        document.documentElement.style.setProperty("--tr-rows-number", trCount);
      });
    },
  },
};
</script>

<style scoped lang="scss">
::v-deep .el-calendar__body {
  padding: 0 !important;
}
// 隱藏日曆中上個月的數據
::v-deep .el-calendar-table:not(.is-range) td.next {
  display: none;
}
// 隱藏日曆中下個月的數據
::v-deep .el-calendar-table:not(.is-range) td.prev {
  visibility: hidden;
}
// 設置日曆頭部樣式
::v-deep .el-calendar-table thead th {
  text-align: center;
  font-size: 14px;
}
::v-deep .el-calendar__header {
  padding: 0;
  display: none;
}
// 設置日曆
::v-deep .el-calendar-table .el-calendar-day {
  height: calc(288px / var(--tr-rows-number));
  padding: 0;
  color: #fff;
}
// card樣式設置
::v-deep .el-card__body {
  padding: 6px 20px 20px;
}
// 日曆頭部樣式自定義
.calendar-box {
  height: 55px;
  line-height: 55px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid $border-color-card;
}
.calendar-day {
  width: 100%;
  height: 100%;
  font-size: 12px;
  text-align: center;
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  .icon-select {
    right: 0;
    top: 0;
    position: absolute;
    font-size: 20px;
    color: #fff;
  }
}
// 有數據狀態
.green {
  background: $color-success;
}
// 無數據狀態
.red {
  background: $color-danger;
}
</style>

效果圖:

user avatar linlinma Avatar anchen_5c17815319fb5 Avatar populus Avatar nqbefgvs Avatar aresn Avatar hyfhao Avatar nznznz Avatar fushengruomengweihuanjihe Avatar 54r9rxzy Avatar heath_learning Avatar tizuqiudehongcha Avatar hu_qi Avatar
Favorites 58 users favorite the story!
Favorites

Add a new Comments

Some HTML is okay.