时间选择控件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也是因为使用了浮动)

好了,我废话就这么多了,哈哈哈

posted @ 2018-06-04 15:24  一江西流  阅读(5997)  评论(13编辑  收藏