模仿 Antd 寫一個年份的時間選擇器 V2.0


說明:

版本:V2.0

此文章代碼復制不可用,有需要可以留言,或者聯系我。

功能介紹:

1.可以設置 可選擇的時間范圍,范圍超出部分置灰不可選擇。

2.點擊空白處默認收起彈窗,也可以在外部通過 open 屬性控制。

3.按確定按鈕后過 onOk 屬性返回 一個函數,第一個參數為選擇的時間。

4.*新增:用戶可以選擇是否使用確定按鈕。

5.*新增: disabled (禁用狀態)、className (設置選擇器)、defaultValue(時間為空時的默認顯示) 。

6.寬高自動獲取父級,最小高度22px,最下寬度90px。

改進方向:

此版本基本滿足開發時的各種需要,及突發情況。目前暫無改進計划。

大家若有什么好的建議,我也會采納,主要是得有時間。😊

吐槽一下:

這是多變態的產品,非得要一套“年份”的時間選擇器

效果展示:

js部份:

import React from "react";
import "./YearPicker.less"
import moment from "moment";
import { connect } from "react-redux";
import PropTypes from "prop-types";

class YearPicker extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      stateOpen: false, // 是否展示彈窗
      year: "", // "2020" 返給用戶的時間
      inputValue: "", // input 框顯示的內容
      selectYear: moment().format("YYYY"), // 選中的時間
      yearListData: [], // 可選中的年份時間列表
      basePage: 0 // 基准頁面
    }
  }

  componentDidMount() {
    const { value, open } = this.props;
    const { basePage, stateOpen } = this.state;
    const data = this.renderList(basePage)
    this.setState({
      selectYear: value ? value : moment().format("YYYY"),
      year: value ? value : "",
      yearListData: data,
      stateOpen: open === undefined ? stateOpen : open
    })
    if (open === undefined) {
      document.addEventListener("click", this.eventFn, true)
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.value !== this.props.value) {
      this.setState({
        year: nextProps.value
      })
    }
  }

  componentWillUnmount() {
    document.removeEventListener("click", this.eventFn, true)
  }

  eventFn = (ev) => {
     // ...
  }

  renderList = (basePage) => {
     // ...
  }

  onclick = (ev) => {
     // ...
  }

  yearClick = (ev, item, showOk) => {
     // ...
  }

  iconLeftClick = () => {
     // ...
  }

  iconRightClick = () => {
     // ...
  }

  okBut = (ev) => {
     // ...
  }

  textChange = () => {
     // ...
  }

  render() {
    const { year, stateOpen, selectYear, yearListData } = this.state;
    const { startValue, endValue, className, defaultValue, disabled, showOk } = this.props;
    let oneDisplay = "block", twoDisplay = "block";
    if (startValue && yearListData.length && yearListData.includes(startValue)) {
      oneDisplay = "none";
    }
    if (endValue && yearListData.length && yearListData.includes(endValue)) {
      twoDisplay = "none";
    }
    return (
      <div className={`YearPicker ${className} ${disabled ? "disabled-picker" : ""}`} id="YearPicker" onClick={(ev) => { disabled ? console.log("禁用") : this.onclick(ev) }}>
        <div className="begin">
          <input className={year ? "zjl-input" : "zjl-input default_input"} value={year ? year : defaultValue} onChange={() => { this.textChange() }} disabled={disabled} />
          <i className="img" ></i>
        </div>
        <div className="yearChild" style={{ display: stateOpen ? "block" : "none" }}>
          <div className="hearer">
            <span className="left_icon" onClick={this.iconLeftClick} style={{ display: oneDisplay }}>{"<<"}</span>
            <span className="zjl-selectText">{selectYear}</span>
            <span className="right_icon" onClick={this.iconRightClick} style={{ display: twoDisplay }}>{">>"}</span>
          </div>
          <div className="con">
            <ul className="yearBefore">
              {
                yearListData.length > 0 &&
                yearListData.map((item, index) => {
                  return <li key={index}
                    onClick={(+startValue && +startValue <= +item) && (+endValue && +item <= +endValue) ? (ev) => { this.yearClick(ev, item, showOk) } : null}>
                    <span className={(+startValue && +startValue <= +item) && (+endValue && +item <= +endValue) ? selectYear === item ? "beforeli brackgroundYear" : "beforeli" : "beforeli warnnodata"}>
                      {item}
                    </span>
                  </li>
                })
              }
            </ul>
          </div>
          {
            showOk ?
              <div className="zjl-but">
                <span onClick={ev => { this.okBut(ev) }}>確 定</span>
              </div> : null
          }
        </div>
      </div>
    )
  }
}

YearPicker.propTypes = {
  value: PropTypes.string,
  startValue: PropTypes.string,
  endValue: PropTypes.string,
  open: PropTypes.bool,
  disabled: PropTypes.bool,
  onOk: PropTypes.func,
  className: PropTypes.string,
}

YearPicker.defaultProps = {
  showOk: false, // 是否使用確定按鈕,默認不使用
  disabled: false, // 組件是否禁用,默認組件可以使用
  defaultValue: "請選擇時間", // 默認日期 or 沒有日期時的提示語
  value: "", // 日期
  startValue: "1970", // 默認可選擇時間范圍的起始時間
  endValue: moment().format("YYYY"), // 默認可選擇時間范圍的結束時間
  open: undefined, // 默認彈窗展示與否不受控
  onOk: () => { console.log(1) }, // 時間切換后的回調
  className: ""
}

export default connect(state => state, {})(YearPicker)

less部份:

.YearPicker{
  width: 100%;
  height: 100%;
  min-height: 22px;
  min-width: 90px;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  color: rgba(0, 0, 0, 0.65);
  font-size: 14px;
  font-variant: tabular-nums;
  line-height: 1.5;
  list-style: none;
  font-feature-settings: 'tnum';
  position: relative;
  display: inline-block;
  outline: none;
  cursor: text;
  transition: opacity 0.3s;
  .begin{
    position: relative;
    height: 100%;
    .zjl-input{
      text-overflow: ellipsis;
      touch-action: manipulation;
      box-sizing: border-box;
      margin: 0;
      padding: 0;
      font-variant: tabular-nums;
      list-style: none;
      font-feature-settings: 'tnum';
      position: relative;
      display: inline-block;
      width: 100%;
      height: 100%;
      padding: 4px 11px;
      color: rgba(0, 0, 0, 0.65);
      font-size: 14px;
      line-height: 1.5;
      background-color: #fff;
      background-image: none;
      border: 1px solid #d9d9d9;
      border-radius: 4px;
      transition: all 0.3s;
      &:hover{
        border-color: #40a9ff;
        border-right-width: 1px !important;
      }
      &:focus {
        border-color: #40a9ff;
        border-right-width: 1px !important;
        outline: 0;
        box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
      }
    }
    .zjl-input[disabled] {
      color: rgba(0, 0, 0, 0.25);
      background-color: #f5f5f5;
      cursor: not-allowed;
      opacity: 1;
    }
    .default_input{
      color: rgba(0, 0, 0, 0.25);
    }
    .img{
      display: inline-block;
      position: absolute;
      top: 50%;
      right: 12px;
      height: 14px;
      width: 14px;
      margin-top: -7px;
      background: url("../../assets/imgs/日歷1.png") no-repeat center;
      background-size: 100% 100%;
      color: rgba(0, 0, 0, 0.25);
      font-size: 14px;
      line-height: 1;
      z-index: 1;
      transition: all 0.3s;
      user-select: none;
    }
  }
  .yearChild{
    box-sizing: border-box;
    margin: 0;
    padding: 0;
    color: rgba(0, 0, 0, 0.65);
    font-variant: tabular-nums;
    line-height: 1.5;
    list-style: none;
    font-feature-settings: 'tnum';
    position: absolute;
    z-index: 1050;
    width: 280px;
    font-size: 14px;
    line-height: 1.5;
    text-align: left;
    list-style: none;
    background-color: #fff;
    background-clip: padding-box;
    border: 1px solid #fff;
    border-radius: 4px;
    outline: none;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
    .hearer{
      height: 40px;
      line-height: 40px;
      text-align: center;
      border-bottom: 1px solid #e8e8e8;
      user-select: none;
      white-space: nowrap;
      overflow: hidden;
      position: relative;
      span{
        display: inline-block;
      }
      .left_icon{
        position: absolute;
        z-index: 100;
        top: 0;
        left: 0;
        cursor: pointer;
        width: 30px;
        margin-left: 20px;
        &:hover{
          color: #40a9ff;
        }
      }
      .zjl-selectText{
        text-align: center;
      }
      .right_icon{
        position: absolute;
        z-index: 100;
        top: 0;
        right: 0;
        cursor: pointer;
        width: 30px;
        margin-right: 20px;
        &:hover{
          color: #40a9ff;
        }
      }
    }
    .con{
      width: 100%;
      .yearBefore{
        width: 100%;
        padding: 10px 20px;
        overflow: hidden;
        display: grid;
        grid-gap: 20px;
        grid-template-columns: repeat(3, 1fr);
        // grid-auto-rows: 20px 20px 20px;
        grid-template-areas:
          "h h h"
          "h h h"
          "h h h"
          "h h h";
        li {
          text-align: center;
          font-size: 14px;
          .beforeli{
            display: inline-block;
            cursor: pointer;
            padding: 0 10px;
            height: 24px;
            line-height: 23px;
            &:hover{
              background: #e6f7ff;
            }
          }
          .brackgroundYear{
            background: #bae7ff;
            border-radius: 1px;
            &:hover{
              background: #bae7ff;
            }
          }
          .warnnodata{
            background-color: #f5f5f5;
            color: rgba(0, 0, 0, 0.25);
            cursor: not-allowed;
          }
        }
      }
    }
    .zjl-but{
      position: relative;
      height: auto;
      text-align: right;
      padding: 0 12px;
      line-height: 38px;
      border-top: 1px solid #e8e8e8;
      span{
        position: relative;
        display: inline-block;
        font-weight: 400;
        white-space: nowrap;
        text-align: center;
        background-image: none;
        border: 1px solid transparent;
        box-shadow: 0 2px 0 rgba(0, 0, 0, 0.015);
        cursor: pointer;
        transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
        user-select: none;
        touch-action: manipulation;
        height: 32px;
        padding: 0 15px;
        color: #fff;
        background-color: #1890ff;
        border-color: #1890ff;
        text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.12);
        -webkit-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
        box-shadow: 0 2px 0 rgba(0, 0, 0, 0.045);
        height: 24px;
        padding: 0 7px;
        font-size: 14px;
        border-radius: 4px;
        line-height: 22px;
        &:hover{
          color: #fff;
          background-color: #40a9ff;
          border-color: #40a9ff;
        }
      }
    }
  }
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM