|
|
@@ -0,0 +1,286 @@
|
|
|
+<!--
|
|
|
+ * @Descripttion: 季度选择器
|
|
|
+ * @version: 1.0
|
|
|
+ * @Author: https://www.lervor.com/
|
|
|
+ * @Date: 2021-12-06
|
|
|
+ * @LastEditTime: 2021-12-09
|
|
|
+-->
|
|
|
+<template>
|
|
|
+ <el-popover
|
|
|
+ trigger="focus"
|
|
|
+ v-model="pickerVisible"
|
|
|
+ popper-class="lervor-quarter-popover"
|
|
|
+ :disabled="disabled">
|
|
|
+ <el-input
|
|
|
+ ref="reference"
|
|
|
+ slot="reference"
|
|
|
+ class="el-date-editor"
|
|
|
+ readonly
|
|
|
+ :disabled="disabled"
|
|
|
+ :size="size"
|
|
|
+ :placeholder="placeholder"
|
|
|
+ :value="displayValue"
|
|
|
+ :validate-event="false"
|
|
|
+ :style="{ width }"
|
|
|
+ @mouseenter.native="handleMouseEnter"
|
|
|
+ @mouseleave.native="showClose = false">
|
|
|
+ <i slot="prefix"
|
|
|
+ class="el-input__icon"
|
|
|
+ :class="triggerClass">
|
|
|
+ </i>
|
|
|
+ <i slot="suffix"
|
|
|
+ class="el-input__icon"
|
|
|
+ :class="[showClose ? '' + clearIcon : '']"
|
|
|
+ @click="handleClickIcon"
|
|
|
+ @mousedown="handleMousedownIcon">
|
|
|
+ </i>
|
|
|
+ </el-input>
|
|
|
+ <div class="lervor-quarter-picker">
|
|
|
+ <div class="el-date-picker__header el-date-picker__header--bordered">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ aria-label="前一年"
|
|
|
+ class="el-picker-panel__icon-btn el-date-picker__prev-btn el-icon-d-arrow-left"
|
|
|
+ @click="prevYear">
|
|
|
+ </button>
|
|
|
+ <span
|
|
|
+ role="button"
|
|
|
+ class="el-date-picker__header-label">{{ yearLabel }}</span>
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ aria-label="后一年"
|
|
|
+ class="el-picker-panel__icon-btn el-date-picker__next-btn el-icon-d-arrow-right"
|
|
|
+ @click="nextYear">
|
|
|
+ </button>
|
|
|
+ </div>
|
|
|
+ <div class="el-picker-panel__content" style="width: 200px; margin: 10px 15px;">
|
|
|
+ <table class="lervor-quarter-table" @click="handleTableClick">
|
|
|
+ <tbody>
|
|
|
+ <tr>
|
|
|
+ <td class="available" :class="getCellStyle(0)">
|
|
|
+ <a class="cell">第一季度</a>
|
|
|
+ </td>
|
|
|
+ <td class="available" :class="getCellStyle(1)">
|
|
|
+ <a class="cell">第二季度</a>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ <tr>
|
|
|
+ <td class="available" :class="getCellStyle(2)">
|
|
|
+ <a class="cell">第三季度</a>
|
|
|
+ </td>
|
|
|
+ <td class="available" :class="getCellStyle(3)">
|
|
|
+ <a class="cell">第四季度</a>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </el-popover>
|
|
|
+ </template>
|
|
|
+
|
|
|
+ <script>
|
|
|
+ import { formatDate, prevYear, nextYear, range, nextDate, isDateObject, parseDate } from 'element-ui/src/utils/date-util'
|
|
|
+ import { hasClass } from 'element-ui/src/utils/dom'
|
|
|
+
|
|
|
+ // 获取指定年份和季度的所有日期
|
|
|
+ const datesInYearAndQuarter = (year, quarter) => {
|
|
|
+ const numOfDays = getDayCountOfQuarter(year, quarter)
|
|
|
+ const firstDay = new Date(year, quarter * 3, 1)
|
|
|
+ return range(numOfDays).map(n => nextDate(firstDay, n))
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取指定年份和季度总天数
|
|
|
+ const getDayCountOfQuarter = (year, quarter) => {
|
|
|
+ switch(quarter) {
|
|
|
+ case 0: // 第一季度包含二月,需要对是否闰年进行判断处理
|
|
|
+ if (year % 4 === 0 && year % 100 !== 0 || year % 400 === 0) {
|
|
|
+ return 91
|
|
|
+ } else {
|
|
|
+ return 90
|
|
|
+ }
|
|
|
+ case 1:
|
|
|
+ return 91
|
|
|
+ default:
|
|
|
+ return 92
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ export default {
|
|
|
+ name: 'QuarterPicker',
|
|
|
+ props: {
|
|
|
+ size: String,
|
|
|
+ format: String, // 显示在输入框中的格式,引入季度:q(阿拉伯数字)、Q(中文数字)
|
|
|
+ valueFormat: String,
|
|
|
+ placeholder: String,
|
|
|
+ prefixIcon: String,
|
|
|
+ clearIcon: {
|
|
|
+ type: String,
|
|
|
+ default: 'el-icon-circle-close'
|
|
|
+ },
|
|
|
+ disabled: Boolean,
|
|
|
+ clearable: {
|
|
|
+ type: Boolean,
|
|
|
+ default: true
|
|
|
+ },
|
|
|
+ width: { // 组件宽度
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ },
|
|
|
+ disabledDate: {}, // 不可用的日期
|
|
|
+ value: null
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ showClose: false,
|
|
|
+ pickerVisible: false,
|
|
|
+ date: new Date(),
|
|
|
+ quarterText: [ '一', '二', '三', '四' ]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ triggerClass() {
|
|
|
+ return this.prefixIcon || 'el-icon-date'
|
|
|
+ },
|
|
|
+ displayValue() {
|
|
|
+ if (!this.value) return null
|
|
|
+ // 季度,从0开始
|
|
|
+ const quarter = parseInt(this.parsedValue.getMonth() / 3)
|
|
|
+ let fDate = formatDate(this.parsedValue, this.format)
|
|
|
+ fDate = fDate.replace(/q/, quarter + 1).replace(/Q/, this.quarterText[quarter])
|
|
|
+ return fDate
|
|
|
+ },
|
|
|
+ year() {
|
|
|
+ return this.date.getFullYear()
|
|
|
+ },
|
|
|
+ yearLabel() {
|
|
|
+ return this.year + ' 年'
|
|
|
+ },
|
|
|
+ parsedValue() {
|
|
|
+ if (!this.value) {
|
|
|
+ return this.value
|
|
|
+ }
|
|
|
+ if (isDateObject(this.value)) {
|
|
|
+ return this.value
|
|
|
+ }
|
|
|
+ // 非时间格式且设置了valueFormat,进行时间转换
|
|
|
+ if (this.valueFormat) {
|
|
|
+ return parseDate(this.value, this.valueFormat)
|
|
|
+ }
|
|
|
+ // 非时间格式且未设置valueFormat,再尝试转换时间
|
|
|
+ return new Date(this.value)
|
|
|
+ }
|
|
|
+ },
|
|
|
+ watch: {
|
|
|
+ value(value) {
|
|
|
+ this.date = value ? this.parsedValue : new Date()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ handleMouseEnter() {
|
|
|
+ if (this.disabled) return
|
|
|
+ if (this.value && this.clearable) {
|
|
|
+ this.showClose = true
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleClickIcon(event) {
|
|
|
+ if (this.disabled) return
|
|
|
+ if (this.showClose) {
|
|
|
+ this.$emit('input', null)
|
|
|
+ this.$emit('change', null)
|
|
|
+ this.showClose = false
|
|
|
+ this.pickerVisible = false
|
|
|
+ this.$refs.reference.blur()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleMousedownIcon(event) {
|
|
|
+ // 阻止鼠标按下清空按钮,防止清空数据时季度选择面板闪现
|
|
|
+ event.preventDefault()
|
|
|
+ },
|
|
|
+ handleTableClick(event) {
|
|
|
+ let target = event.target
|
|
|
+ if (target.tagName === 'A') {
|
|
|
+ target = target.parentNode
|
|
|
+ }
|
|
|
+ if (target.tagName !== 'TD') return
|
|
|
+ if (hasClass(target, 'disabled')) return
|
|
|
+ const column = target.cellIndex
|
|
|
+ const row = target.parentNode.rowIndex
|
|
|
+ // 季度,从0开始
|
|
|
+ const quarter = row * 2 + column
|
|
|
+ // 季度开始月份,从0开始
|
|
|
+ const month = quarter * 3
|
|
|
+ let newDate = new Date(this.year, month, 1)
|
|
|
+ if (this.valueFormat) {
|
|
|
+ newDate = formatDate(newDate, this.valueFormat)
|
|
|
+ }
|
|
|
+ this.pickerVisible = false
|
|
|
+ this.$emit('input', newDate)
|
|
|
+ this.$emit('change', newDate)
|
|
|
+ },
|
|
|
+ prevYear() {
|
|
|
+ this.date = prevYear(this.date)
|
|
|
+ },
|
|
|
+ nextYear() {
|
|
|
+ this.date = nextYear(this.date)
|
|
|
+ },
|
|
|
+ getCellStyle(quarter) {
|
|
|
+ const style = {}
|
|
|
+ const today = new Date()
|
|
|
+ const date = this.parsedValue ? this.parsedValue : today
|
|
|
+ style.disabled = typeof this.disabledDate === 'function'
|
|
|
+ ? datesInYearAndQuarter(this.year, quarter).every(this.disabledDate) : false
|
|
|
+ // 当前选中的季度样式
|
|
|
+ style.current = date.getFullYear() === this.year && parseInt(date.getMonth() / 3) === quarter
|
|
|
+ // 今日所在季度样式
|
|
|
+ style.quarter = today.getFullYear() === this.year && parseInt(today.getMonth() / 3) === quarter
|
|
|
+ return style
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ </script>
|
|
|
+
|
|
|
+ <style>
|
|
|
+ .lervor-quarter-picker {
|
|
|
+ line-height: 30px;
|
|
|
+ }
|
|
|
+ .lervor-quarter-popover {
|
|
|
+ padding: 0;
|
|
|
+ }
|
|
|
+ .lervor-quarter-table {
|
|
|
+ font-size: 12px;
|
|
|
+ margin: -1px;
|
|
|
+ border-collapse: collapse;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+ .lervor-quarter-table td {
|
|
|
+ text-align: center;
|
|
|
+ padding: 10px 3px;
|
|
|
+ cursor: pointer;
|
|
|
+ }
|
|
|
+ .lervor-quarter-table td .cell {
|
|
|
+ height: 32px;
|
|
|
+ display: block;
|
|
|
+ line-height: 32px;
|
|
|
+ color: #606266;
|
|
|
+ margin: 0 auto;
|
|
|
+ }
|
|
|
+ .lervor-quarter-table td .cell:hover {
|
|
|
+ color: #1890ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .lervor-quarter-table td.current:not(.disabled) .cell {
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+
|
|
|
+ .lervor-quarter-table td.quarter .cell {
|
|
|
+ color: #409eff;
|
|
|
+ font-weight: 700;
|
|
|
+ }
|
|
|
+
|
|
|
+ .lervor-quarter-table td.disabled .cell {
|
|
|
+ background-color: #F5F7FA;
|
|
|
+ cursor: not-allowed;
|
|
|
+ color: #C0C4CC;
|
|
|
+ }
|
|
|
+ </style>
|