用 element ui 实现季度选择器

由于在数据项目中经常以各种时间条件查询数据,所以时间选择器(DatePicker)组件是很常用的组件。但是在我使用的 Element UI 中,缺少了季度选择器的功能。

简易实现

一开始我根据时间范围使用 select 去遍历,如 2024-Q1、2023-Q4、2023-Q3 如此类推。

image

element 并无季度选择器

其实也算是快速解决了 element ui 无法选择季度的问题。但总感觉特别的 low,后来有时间了就去隔壁 ant design 看了看。

image

发现在新版的 ant design 都支持季度和季度范围选择器了……

查了查新的 element plus 也只是只支持了 'year' | 'years' |'month' | 'date' | 'dates' | 'datetime' | 'week' | 'datetimerange' | 'daterange' | 'monthrange' 这些个类型。

工具不给力,又不想用其他库的情况下只能手搓了。

手搓季度选择器

季度面板

参考 ant design 做了一个类似的面板。

image

<template>
  <div class="quarter-panel">
    <div class="quarter-panel-header">
      <i
        class="quarter-panel-header-icon el-icon-arrow-left"
        @click="currentYear--"
      />
      <div class="quarter-panel-header-title">{{ currentYear }} 年</div>
      <i
        class="quarter-panel-header-icon el-icon-arrow-right"
        @click="currentYear++"
      />
    </div>
    <div class="quarter-panel-content">
      <div
        v-for="option in quarterOptions"
        class="quarter-panel-item-btn"
        :class="getComputedClass(option.value)"
        :key="option.value"
        @click="emitClick(option.value)"
      >
        {{ option.label }}
      </div>
    </div>
  </div>
</template>

<script>
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat'

dayjs.extend(customParseFormat)

export default {
  name: 'QuarterPanel',
  props: {
    value: String,
    dice: Number,
    min: String,
    max: String,
    todayDisabled: Boolean,
    featureDisabled: Boolean,
  },
  data() {
    return {
      currentYear: 2023,
    }
  },
  computed: {
    day() {
      if (this.value) {
        return dayjs(this.value, 'YYYY-MM-DD')
      }
      return dayjs()
    },
    computedDate() {
      return this.day.startOf('quarter').format('YYYY-MM-DD')
    },
    quarterOptions() {
      return [
        { label: 'Q1', value: `${this.currentYear}-01-01` },
        { label: 'Q2', value: `${this.currentYear}-04-01` },
        { label: 'Q3', value: `${this.currentYear}-07-01` },
        { label: 'Q4', value: `${this.currentYear}-10-01` },
      ]
    },
  },
  mounted() {
    this.currentYear = dayjs().year()
  },
  methods: {
    getDisabled(value) {
      let isFeature = false

      if (this.todayDisabled) {
        isFeature = dayjs()
          .subtract(1, 'day')
          .startOf('quarter')
          .isBefore(dayjs(value))
      } else if (this.featureDisabled) {
        isFeature = dayjs().startOf('quarter').isBefore(dayjs(value))
      }

      const isMin = this.min
        ? dayjs(this.min, 'YYYY-MM-DD')
            .startOf('quarter')
            .isAfter(dayjs(value, 'YYYY-MM-DD'))
        : false

      const isMax = this.max
        ? dayjs(this.max, 'YYYY-MM-DD')
            .startOf('quarter')
            .isBefore(dayjs(value, 'YYYY-MM-DD'))
        : false

      return isFeature || isMin || isMax
    },
    getComputedClass(value) {
      if (this.computedDate === value) {
        return 'quarter-panel-item-btn-active'
      }
      if (this.getDisabled(value)) {
        return 'quarter-panel-item-btn-disabled'
      }
      return ''
    },
    emitClick(value) {
      if (this.getDisabled(value)) {
        return
      }
      this.$emit('input', value)
    },
  },
  watch: {
    dice() {
      this.currentYear = this.day.year()
    },
  },
}
</script>

<style lang="scss" scoped>
$--gw-primary-color: #f6674f;

.quarter-panel {
  width: 200px;
  color: #303133;

  .quarter-panel-header {
    height: 30px;
    padding: 12px;
    display: flex;
    align-items: center;

    .quarter-panel-header-icon {
      font-size: 12;
      margin: 5px;
      cursor: pointer;

      &:hover {
        color: $--gw-primary-color;
      }
    }

    .quarter-panel-header-title {
      flex: 1;
      text-align: center;
      font-size: 16;
    }
  }

  .quarter-panel-content {
    display: flex;
    align-items: center;

    .quarter-panel-item-btn {
      flex: 1;
      font-size: 14;
      height: 30px;
      line-height: 30px;
      text-align: center;
      cursor: pointer;
      border: solid 1px transparent;
      border-radius: 5px;

      &:hover {
        color: $--gw-primary-color;
        border: solid 1px $--gw-primary-color;
      }
    }

    .quarter-panel-item-btn-active {
      background: $--gw-primary-color;
      color: #ffffff;

      &:hover {
        color: #ffffff;
      }
    }

    .quarter-panel-item-btn-disabled {
      color: #909399;
      background: #f2f6fc;
      cursor: not-allowed;

      &:hover {
        color: #909399;
        background: #f2f6fc;
      }
    }
  }
}
</style>

季度选择器

将面板放到 el-popover 中实现类似 DatePicker 的效果。并且提供了像清空数据、最大值、最小值等常用功能。

image

<template>
  <div class="quarter-picker" :class="{ 'quarter-picker-disabled': disabled }">
    <div class="quarter-picker-date-button">
      <i class="iconfont icon-date-select-icon quarter-picker-time-icon" />
      <el-popover
        placement="bottom-start"
        width="200"
        trigger="click"
        ref="datePopover"
        :disabled="disabled"
        @show="initPopover"
      >
        <div
          class="quarter-picker-date-button-item quarter-picker-date-button-item-long"
          slot="reference"
        >
          <span v-if="form.date" class="button-item-span">
            {{ dateQuarterStr }}
          </span>
          <span v-else class="button-item-span">选择时间</span>
          <div class="bottom-line" />
        </div>
        <quarterPanel
          v-model="form.date"
          :dice="dice"
          :min="min"
          :max="max"
          :featureDisabled="featureDisabled"
          :todayDisabled="todayDisabled"
          @input="emitDateChange()"
        />
      </el-popover>
      <i
        v-show="form.date && clearable"
        class="el-icon-close quarter-picker-clear-icon"
        @click.stop="clearCurrentDate"
      />
    </div>
  </div>
</template>

<script>
import dayjs from 'dayjs'
import customParseFormat from 'dayjs/plugin/customParseFormat'
import quarterOfYear from 'dayjs/plugin/quarterOfYear'

import quarterPanel from './quarterPanel.vue'

dayjs.extend(customParseFormat)
dayjs.extend(quarterOfYear)

/**
 * date 日期
 */
export default {
  name: 'QuarterPicker',
  components: {
    quarterPanel,
  },
  props: {
    date: String,
    min: String,
    max: String,
    featureDisabled: Boolean,
    todayDisabled: Boolean,
    disabled: Boolean,
    clearable: Boolean,
  },
  data() {
    return {
      form: {
        date: '',
      },
      dice: 0,
    }
  },
  mounted() {
    this.syncData()
  },
  computed: {
    dateQuarterStr() {
      if (!this.form.date) return '选择季'

      const dj = dayjs(this.form.date).startOf('quarter')
      return `${dj.year()}-Q${dj.quarter()}`
    },
  },
  methods: {
    initPopover() {
      this.dice++
    },
    syncData() {
      this.form.date = this.date
    },
    clearCurrentDate() {
      if (this.disabled) return
      this.form.date = ''

      this.emitDateChange()
    },
    emitDateChange() {
      this.$emit('change', this.form)

      this.closePopovers()
    },
    closePopovers() {
      this.$refs.datePopover.doClose()
    },
  },
  watch: {
    date() {
      if (this.form.date !== this.date) {
        this.syncData()
      }
    },
  },
}
</script>

<style scoped lang="scss">
$--gw-primary-color: #f6674f;

.quarter-picker {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: flex-end;

  .quarter-picker-date-button {
    display: flex;
    position: relative;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    user-select: none;
    margin-left: 5px;
    width: 250px;
    padding-left: 10px;
    height: 28px;
    background: #ffffff;
    border: 1px solid #dcdfe6;
    font-size: 14px;
    font-family: Microsoft YaHei;
    font-weight: 400;
    color: #282c32;
    border-radius: 4px;

    .quarter-picker-time-icon {
      position: absolute;
      left: 12px;
    }

    .quarter-picker-date-button-item {
      position: relative;
      height: 28px;
      line-height: 28px;
      text-align: center;
      width: 70px;
      cursor: pointer;

      .bottom-line {
        position: absolute;
        bottom: 0;
        left: 0;
        right: 0;
        height: 2px;
        border-radius: 1px;
        background: transparent;
      }

      &:hover {
        .bottom-line {
          background: $--gw-primary-color;
        }
      }
    }

    .quarter-picker-date-button-item-long {
      width: 200px;

      .button-item-span {
        display: inline-block;
        width: 90px;
        text-align: center;
      }

      .button-item-span-active {
        color: $--gw-primary-color;
      }
    }

    .quarter-picker-clear-icon {
      position: absolute;
      right: 12px;
      font-size: 14;
      cursor: pointer;

      &:hover {
        color: $--gw-primary-color;
      }
    }
  }
}

.quarter-picker-disabled {
  .quarter-picker-date-button {
    color: #c0c4cc;
    background-color: #f2f6fc;

    .quarter-picker-date-button-item {
      cursor: not-allowed;
    }
  }

  .quarter-picker-date-button-item {
    &:hover {
      .bottom-line {
        background: transparent !important;
      }
    }
  }

  .quarter-picker-clear-icon {
    cursor: not-allowed !important;

    &:hover {
      color: #c0c4cc !important;
    }
  }
}
</style>

组件的使用

最后就是组件的使用了:

  <QuarterPicker
    type="quarter"
    :date="quarter.date"
    :min="minDate"
    :max="maxDate"
    :featureDisabled="options.featureDisabled"
    :todayDisabled="options.todayDisabled"
    :disabled="options.disabled"
    :clearable="options.clearable"
    @change="handleQuarterPickerChange"
  />
handleQuarterPickerChange({ date }) {
  this.quarter.date = date

  this.$message({
    message: '触发查询请求',
    type: 'success',
  })
},

最后

另外,季度范围选择器也可以用类似的思路来实现。以上就是个人解决季度选择器的方式。希望能对有类似需求的同学一些帮助。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/559251.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

win/mac达芬奇19下载:DaVinci Resolve Studio 19

DaVinci Resolve Studio 19 是一款功能强大的视频编辑和调色软件&#xff0c;广泛应用于电影、电视和网络节目的后期制作。这款软件不仅提供了专业的剪辑、调色和音频处理工具&#xff0c;还引入了全新的DaVinci Neural Engine AI工具&#xff0c;对100多项功能进行了大规模升级…

美化博客文章(持续更新)

&#x1f381;个人主页&#xff1a;我们的五年 &#x1f50d;系列专栏&#xff1a;游戏实现&#xff1a;贪吃蛇​​​​​​ &#x1f337;追光的人&#xff0c;终会万丈光芒 前言&#xff1a; 该文提供我的一些文章设计的一些方法 目录 1.应用超链接 1.应用超链接

差速机器人模型LQR 控制仿真——路径模拟

LQR路径跟踪要求路径中带角度&#xff0c;即坐标&#xff08;x,y,yaw&#xff09;&#xff0c;而一般我们的规划出来的路径不带角度。这里通过总结相关方法&#xff0c;并提供一个案例。 将点路径拟合成一条完整的线路径算法 将点路径拟合成一条完整的线路径是一个常见的问题…

【Java开发指南 | 第十五篇】Java Character 类、String 类

读者可订阅专栏&#xff1a;Java开发指南 |【CSDN秋说】 文章目录 Java Character 类转义序列 Java String 类连接字符串 Java Character 类 Character 类是 Java 中用来表示字符的包装类&#xff0c;它提供了一系列静态方法用于对字符进行操作&#xff0c;其主要分为静态方法…

06 JavaScript学习:语句

JavaScript 语句是用来执行特定任务或操作的一组指令。它可以包括变量声明、条件语句、循环语句、函数调用等。JavaScript 语句以分号结尾&#xff0c;每个语句都会被解释器执行。 分号 ; 在JavaScript中&#xff0c;分号&#xff08;;&#xff09;用于表示语句的结束。尽管在…

python爬虫-----深入了解 requests 库(第二十五天)

&#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; &#x1f388;&#x1f388;所属专栏&#xff1a;python爬虫学习&#x1f388;&#x1f388; ✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天…

【汇编语言】初识汇编

【汇编语言】初识汇编 文章目录 【汇编语言】初识汇编前言由机器语言到汇编语言机器语言与机器指令汇编语言与汇编指令汇编语言程序示例 计算机组成指令和数据的表示计算机的存储单元计算机的总线 内存读写与地址空间CPU对存储器的读写内存地址空间 总结 前言 为什么要学习汇编…

Numpy重修系列(一) --- 初识Numpy

一、为什么使用Numpy&#xff1f; 1.1、简介 Python科学计算基础包&#xff0c;提供 多维数组对象 、派生对象&#xff08;掩码数组、矩阵&#xff09; 数组的快速操作&#xff08;数学计算、逻辑、形状变化、排序、选择、输入输出、离散傅里叶变换、基本线性代数、基本统计运…

数据分析案例-中国黄金股票市场的EDA与价格预测

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

【数据结构】单链表经典算法题的巧妙解题思路

目录 题目 1.移除链表元素 2.反转链表 3.链表的中间节点 4.合并两个有序链表 5.环形链表的约瑟夫问题 解析 题目1&#xff1a;创建新链表 题目2&#xff1a;巧用三个指针 题目3&#xff1a;快慢指针 题目4&#xff1a;哨兵位节点 题目5&#xff1a;环形链表 介绍完了…

Activity——spring方式创建activiti所需数据表结构

文章目录 前言依赖引入编写数据库连接等配置配置日志文件编写java代码生成数据库表结构问题反馈与解决思路问题一&#xff1a;Cause: java.sql.SQLSyntaxErrorException: Table activiti_02.act_ge_property doesnt exist 为什么文件名必须写死&#xff1f; 前言 在之前创建ac…

循序渐进丨使用 Python 向 MogDB 数据库批量操作数据的方法

当我们有时候需要向数据库里批量插入数据&#xff0c;或者批量导出数据时&#xff0c;除了使用传统的gsql copy命令&#xff0c;也可以通过Python的驱动psycopg2进行批量操作。本文介绍了使用psycopg2里的executemany、copy_from、copy_to、copy_expert等方式来批量操作 MogDB …

js-pytorch:开启前端+AI新世界

嗨&#xff0c; 大家好&#xff0c; 我是 徐小夕。最近在 github 上发现一款非常有意思的框架—— js-pytorch。它可以让前端轻松使用 javascript 来运行深度学习框架。作为一名资深前端技术玩家&#xff0c; 今天就和大家分享一下这款框架。 往期精彩 Nocode/Doc&#xff0c;可…

python爬虫之爬取携程景点评价(5)

一、景点部分评价爬取 【携程攻略】携程旅游攻略,自助游,自驾游,出游,自由行攻略指南 (ctrip.com) import requests from bs4 import BeautifulSoupif __name__ __main__:url https://m.ctrip.com/webapp/you/commentWeb/commentList?seo0&businessId22176&busines…

“中医显示器”是人体健康监测器

随着科技的进步&#xff0c;现代医学设备已经深入到了人们的日常生活中。然而&#xff0c;在这个过程中&#xff0c;我们不应忘记我们的医学根源&#xff0c;中医。我们将中医的望、闻、问、切四诊与现代科技相结合&#xff0c;通过一系列的传感器和算法将人体的生理状态以数字…

3、MYSQL-一条sql如何在MYSQL中执行的

MySQL的内部组件结构 大体来说&#xff0c;MySQL 可以分为 Server 层和存储引擎层两部分。 Server层 主要包括连接器、查询缓存、分析器、优化器、执行器等&#xff0c;涵盖 MySQL 的大多数核心服务功能&#xff0c;以及所有的内置函数&#xff08;如日期、时间、数学和加密函…

[Algorithm][滑动窗口][无重复字符的最长字串][最大连续的一个数 Ⅲ][将x减到0的最小操作数]详细讲解

目录 1.无重复字符的最长字串1.题目链接2.算法原理详解3.代码实现 2.最大连续的一个数 Ⅲ1.题目链接2.算法原理详解3.代码实现 3.将x减到0的最小操作数1.题目链接2.算法原理详解3.代码实现 1.无重复字符的最长字串 1.题目链接 无重复字符的最长字串 2.算法原理详解 研究的对…

算法打卡day39

今日任务&#xff1a; 1&#xff09;卡码网57. 爬楼梯&#xff08;70. 爬楼梯进阶版&#xff09; 2&#xff09;322.零钱兑换 3&#xff09;279.完全平方数 4&#xff09;复习day14 卡码网57. 爬楼梯&#xff08;70. 爬楼梯进阶版&#xff09; 题目链接&#xff1a;57. 爬楼梯…

数据结构从入门到实战——顺序表的应用

目录 一、基于动态顺序表实现通讯录 二、代码实现 2.1 通讯录的初始化 2.2 通讯录的销毁 2.3 通讯录的展示 2.4 通讯录添加联系人信息 2.5 通讯录删除联系人信息 2.6 通讯录修改联系人信息 2.7 通讯录的查找联系人信息 2.8 将通讯录中联系人信息保存到文件中 2.9…

乡政府管理系统|基于Springboot的乡政府管理系统设计与实现(源码+数据库+文档)

乡政府管理系统目录 目录 基于Springboot的乡政府管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、用户信息管理 2、活动信息管理 3、新闻类型管理 4、新闻动态管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选题推…