時間選擇控件YearPicker(基於React,antd)


不知道為什么螞蟻金服團隊沒有在ant design的DatePicker中單獨給出選擇年份的組件,這給我們這種懶人造成了很大的痛苦,自己手造輪子是很麻煩的。畢竟只是一個伸手黨,emmmmm.....

然后就打算自己手擼了,首先去偷看了螞蟻自己組件的樣式,打算照着搬下來。后來發現下面的item是用的table布局,這種布局是我最厭惡的,還是換種方式吧,ul>li,嗯,我最喜歡的

然后開始。

 

 

代碼如下:

/** 
 * 使用方法
 * 引入:
 * import YearPicker from "@/common/widget/YearPicker";//路徑按照自己的來
 * // 獲取年份值
  filterByYear = value => {
    console.log(value);
  };
 * <YearPicker
        operand={12}//點擊左箭頭或右箭頭一次遞增的數值,可以不寫,默認為0
        callback={this.filterByYear}//用於獲取最后選擇到的值
    />
*/
import React, { Component } from "react";
import { Icon } from "antd";
import "../css/calendar.less";//這個文件自己選擇一個溫暖的地方放

class YearPicker extends Component {
  constructor(props) {
    super(props);
    this.state = {
      isShow: false,
      selectedyear: "",
      years: []
    };
  }

  componentWillMount() {
    let { defaultValue } = this.props;
    this.setState({ selectedyear: defaultValue });
  }
  componentDidMount() {
    let _this = this;
    document.addEventListener(
      "click",
      function(e) {
        const { isShow } = _this.state;
        let clsName = e.target.className;
        if (
          clsName.indexOf("calendar") === -1 &&
          e.target.tagName !== "BUTTON" &&
          isShow
        ) {
          _this.hide();
        }
      },
      false
    );
  }
  //初始化數據處理
  initData = (operand, defaultValue) => {
    operand = operand ? operand : 12;
    let year = defaultValue - 1970;
    let curr = year % operand;
    let start = defaultValue - curr;
    let end = defaultValue + operand - 1 - curr;
    this.getYearsArr(start, end);
  };
  //   獲取年份范圍數組
  getYearsArr = (start, end) => {
    let arr = [];
    for (let i = start; i <= end; i++) {
      arr.push(Number(i));
    }
    this.setState({
      years: arr
    });
  };
  // 顯示日歷年組件
  show = () => {
    const { selectedyear } = this.state;
    let { operand } = this.props;
    operand = operand ? operand : 12;
    this.initData(operand, selectedyear);
    this.setState({ isShow: true });
  };
  // 隱藏日期年組件
  hide = () => {
    this.setState({ isShow: false });
  };
  // 向前的年份
  prev = () => {
    const { years } = this.state;
    if (years[0] <= 1970) {
      return;
    }
    this.getNewYearRangestartAndEnd("prev");
  };
  // 向后的年份
  next = () => {
    this.getNewYearRangestartAndEnd("next");
  };

  //   獲取新的年份
  getNewYearRangestartAndEnd = type => {
    const { selectedyear, years } = this.state;
    let operand = Number(this.props.operand);
    operand = operand ? operand : 12;
    let start = Number(years[0]);
    let end = Number(years[years.length - 1]);
    let newstart;
    let newend;
    if (type == "prev") {
      newstart = parseInt(start - operand);
      newend = parseInt(end - operand);
    }
    if (type == "next") {
      newstart = parseInt(start + operand);
      newend = parseInt(end + operand);
    }
    this.getYearsArr(newstart, newend);
  };

  // 選中某一年
  selects = e => {
    let val = Number(e.target.value);
    this.hide();
    this.setState({ selectedyear: val });
    if (this.props.callback) {
      this.props.callback(val);
    }
  };

  render() {
    const { isShow, years, selectedyear } = this.state;

    return (
      <div className="calendar-wrap">
        <div className="calendar-input">
          <input
            className="calendar-value"
            placeholder="請選擇年份"
            onFocus={this.show}
            value={selectedyear}
            readOnly
          />
          <Icon type="calendar" className="calendar-icon" />
        </div>
        {isShow ? (
          <List
            data={years}
            value={selectedyear}
            prev={this.prev}
            next={this.next}
            cback={this.selects}
          />
        ) : (
          ""
        )}
      </div>
    );
  }
}
const List = props => {
  const { data, value, prev, next, cback } = props;
  return (
    <div className="calendar-container">
      <div className="calendar-head-year">
        <Icon
          type="double-left"
          className="calendar-btn prev-btn"
          title=""
          onClick={prev}
        />
        <span className="calendar-year-range">{value}</span>
        <Icon
          type="double-right"
          className="calendar-btn next-btn"
          title=""
          onClick={next}
        />
      </div>
      <div className="calendar-body-year">
        <ul className="calendar-year-ul">
          {data.map((item, index) => (
            <li
              key={index}
              title={item}
              className={
                item == value
                  ? "calendar-year-li calendar-year-selected"
                  : "calendar-year-li"
              }
            >
              <button onClick={cback} value={item}>
                {item}
              </button>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
};

export default YearPicker;

less代碼:

@focuscolor: #108ee9;
@bordercolor: #d9d9d9;
/*這部分根據你自己的容器樣式,我這個地方是因為公用組件的原因需要設置*/ #wrapper .toolbar
{ overflow: inherit !important; } #wrapper .toolbar > div:after { content: ""; display: block; visibility: hidden; width: 0; clear: both; } /*---以下為必備樣式----*/ .calendar-wrap { position: relative; .calendar-input { width: 151px; position: relative; cursor: pointer; .calendar-icon { position: absolute; right: 10px; top: 10px; color: #919191; } input { width: 151px; height: 31px; border: 1px solid @bordercolor; border-radius: 4px; font-size: 12px; outline: none; display: block; padding: 6px 7px; transition: all 0.3s; &:hover, &:active { border-color: @focuscolor; } } } .calendar-container { width: 232px; outline: none; border-radius: 4px; box-shadow: 0 1px 6px rgba(0, 0, 0, 0.2); border: 1px solid @bordercolor; position: absolute; top: 0; left: 0; z-index: 999; background-color: #fff; line-height: 1.5; } .calendar-head-year { height: 34px; line-height: 34px; text-align: center; width: 100%; position: relative; border-bottom: 1px solid #e9e9e9; .calendar-year-range { padding: 0 2px; font-weight: bold; display: inline-block; color: rgba(0, 0, 0, 0.65); line-height: 34px; } .calendar-btn { position: absolute; top: 0; color: rgba(0, 0, 0, 0.43); padding: 0 5px; font-size: 12px; display: inline-block; line-height: 34px; cursor: pointer; &:hover { color: @focuscolor; } } .prev-btn { left: 7px; } .next-btn { right: 7px; } } .calendar-body-year { width: 100%; height: 218px; .calendar-year-ul { list-style: none; .calendar-year-li { float: left; text-align: center; width: 76px; cursor: pointer; > button { cursor: pointer; outline: none; border: 0; display: inline-block; margin: 0 auto; color: rgba(0, 0, 0, 0.65); background: transparent; text-align: center; height: 24px; line-height: 24px; padding: 0 6px; border-radius: 4px; transition: background 0.3s ease; margin: 14px 0; &:hover { color: @focuscolor; } } } .calendar-year-selected { > button { background: #108ee9; color: #fff; &:hover { color: #fff; } } } } } }

以上代碼在IE10,11、FireFox, Chrome, Safari下親測兼容良好

接下來說說,這個過程中遇到的問題:

1、點擊組件外部任意地方需要關閉控件

以為顯示的屬性設置在組件內部的state中,因此當時有點懵逼,一開始想用redux store來控制,然后覺得這個強行依賴太惡心了,放棄

然后,想到在document上綁定click事件,雖然有點違背react不直接操作dom的原則。但是這是我這種智商能想到的比較好的方式了

(偷偷看了螞蟻的方式,將整個選擇的組件與input框隔離成獨立的部分,采用 類似全屏遮罩層的方式,檢測input在窗口中的位置來設置展示組件的位置,這樣確實對於控制點擊外部任意處關閉控件。但是這樣屬於一個控件的東西被拆分不是我這種一根筋的人能接受的)

so, 我還是倔強的使用了自己的方式

componentDidMount() {
    let _this = this;
    document.addEventListener(
      "click",
      function(e) {
        const { isShow } = _this.state;
        let clsName = e.target.className;
        if (
          clsName.indexOf("calendar") === -1 &&
          e.target.tagName !== "BUTTON" &&
          isShow
        ) {
          _this.hide();
        }
      },
      false
    );
  }

2. 當外部容器設置了overflow:hidden 的情況時,控件會被遮擋

這個問題的話,額, 我就是去掉overflow:hidden, 然后給容器清除浮動或者設置固定高度

(一般需要設置overflow:hidden也是因為使用了浮動)

好了,我廢話就這么多了,哈哈哈


免責聲明!

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



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