[VUE3 + Element Plus]使用css隐藏年月选择实现每月的日时分秒选择器

218 阅读2分钟

1.业务背景

在工作场景中,面临这样一个业务需求:构建一个工作量填报系统,该系统的核心功能之一,是能够在每月的既定时间推送工作量填报待办卡片给用户。
管理员可通过系统灵活配置推送时间,精确到每月的具体日期、小时、分钟以及秒。
为达成上述功能,系统需实现一个每月的日时分秒选择器。

2.实现效果

87a9b723b25a04ffc37138c8b81e55b.png

3.实现思路

查阅 Element Plus官方文档 后,Element Plus的 Date Picker 没有直接可实现该业务场景时间格式的类型。

Date picker 可选的type类型: dada3f5cde86495655d976250a23523.png

可以使用type='datetime'的 el-date-picker,使用CSS隐藏掉年月选择栏和星期行,将格式format设为'DD日 HH:mm:ss'。这样用户直接观感上只选择了日时分秒。最后按后端参数格式要求取其中的日时分秒请求接口就可以了。

4.源码及细节

el-date-picker:

<template>
  <el-form :model="formData">
    <el-row :gutter="20">
      <el-col :span="4">
        <el-form-item prop="start" label="开始时间">
          <el-date-picker
            v-model="formData.start"
            type="datetime"
            placeholder="请选择每月开始时间"
            popper-class="only-date-picker-popper"
            date-format="DD日"
            time-format="HH:mm:ss"
            format="DD日 HH:mm:ss"
            :default-value="formatDate()"
            @change="(val) => onOnlyDateTimeChange(val, 'start')"
          />
        </el-form-item>
      </el-col>
      <el-col :span="4">
        <el-form-item prop="end" label="结束时间">
          <el-date-picker
            v-model="formData.end"
            type="datetime"
            placeholder="请选择每月结束时间"
            popper-class="only-date-picker-popper"
            date-format="DD日"
            time-format="HH:mm:ss"
            format="DD日 HH:mm:ss"
            :default-value="formatDate()"
            @change="(val) => onOnlyDateTimeChange(val, 'end')"
          />
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>

</template>
<script setup>
import { ref } from 'vue'

const formData = ref({ start: "", end: "" })
</script>

css:
需隐藏的部分:

5939227bf63cb631c739870fa32eab7.png

.only-date-picker-poper{
    .el-date-picker__header{
        display:none !important;
    }
    .el-picker-panel__content{
        tr:first-child{
            display:none;
        }
        .next-month{
            visibility:hidden;
        }
        .prev-month{
            visibility:hidden;
        }
    }
}

细节完善: 为了使在外观上看起来整齐,选择的固定年月2024年1月。该月的1号在星期一,并且日选择器需要保证该月必须有31天。该月也符合此条件。

<script setup>
//...

const DEFAULT_YEAR = '2024'
const DEFAULT_MONTH = '01'

const formatDate = (day = 1, time = "00:00:00") => {
  if (!day || !time) {
    return null
  }
  const dayString = typeof day === "number" ? day.toString() : day;
  return new Date(
    `${DEFAULT_YEAR}-${DEFAULT_MONTH}-${dayString.padStart(2, "0")} ${time}`
  )
}
</script>

虽然在css上隐藏掉了年月选择器,但是通过键盘方向键操作仍可跳到其余年月。这样在使用时并不会有什么问题,因为后端只需要日的值。只是用户下次打开的1号不在顶格,样式不好看。因此加上一层保险:监听el-date-picker的change时间,将年月赋值回默认值。

<script setup>
//...

const onOnlyDateTimeChange = (val, prop) => {
  const hour = val.getHours().toString().padStart(2, "0")
  const minute = val.getMinutes().toString().padStart(2, "0")
  const second = val.getSeconds().toString().padStart(2, "0")
  const day = val.getDate()
  formData.value[prop] = formatDate(day, `${hour}:${minute}:${second}`)
}
</script>

完整VUE代码:

<template>
  <el-form :model="formData">
    <el-row :gutter="20">
      <el-col :span="4">
        <el-form-item prop="start" label="开始时间">
          <el-date-picker
            v-model="formData.start"
            type="datetime"
            placeholder="请选择每月开始时间"
            popper-class="only-date-picker-popper"
            date-format="DD日"
            time-format="HH:mm:ss"
            format="DD日 HH:mm:ss"
            :default-value="formatDate()"
            @change="(val) => onOnlyDateTimeChange(val, 'start')"
          />
        </el-form-item>
      </el-col>
      <el-col :span="4">
        <el-form-item prop="end" label="结束时间">
          <el-date-picker
            v-model="formData.end"
            type="datetime"
            placeholder="请选择每月结束时间"
            popper-class="only-date-picker-popper"
            date-format="DD日"
            time-format="HH:mm:ss"
            format="DD日 HH:mm:ss"
            :default-value="formatDate()"
            @change="(val) => onOnlyDateTimeChange(val, 'end')"
          />
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>
<script setup>
import { ref } from "vue";
const formData = ref({ start: "", end: "" });

const DEFAULT_YEAR = "2024";
const DEFAULT_MONTH = "01";

const formatDate = (day = 1, time = "00:00:00") => {
  if (!day || !time) {
    return null
  }
  const dayString = typeof day === "number" ? day.toString() : day;
  return new Date(
    `${DEFAULT_YEAR}-${DEFAULT_MONTH}-${dayString.padStart(2, "0")} ${time}`
  )
}

const onOnlyDateTimeChange = (val, prop) => {
  const hour = val.getHours().toString().padStart(2, "0")
  const minute = val.getMinutes().toString().padStart(2, "0")
  const second = val.getSeconds().toString().padStart(2, "0")
  const day = val.getDate()
  formData.value[prop] = formatDate(day, `${hour}:${minute}:${second}`)
}
</script>
<style lang="scss">
.only-date-picker-popper {
  .el-date-picker__header {
    display: none !important;
  }

  .el-picker-panel__content {
    tr:first-child {
      display: none;
    }

    .next-month {
      visibility: hidden;
    }

    .prev-month {
      visibility: hidden;
    }
  }
}
</style>