当前位置: 首页 > news >正文

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

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

转载于:https://www.cnblogs.com/zyl-Tara/p/9133524.html

相关文章:

  • MicroPython支持图形化编辑了:Python Editor带你轻松玩转MicroPython
  • 负载均衡【nginx反向代理】
  • python----文件读写
  • Tensorflow 学习笔记(一)TensorFlow入门
  • [Usaco2012 Dec]First! BZOJ3012
  • 前台jsp从session中拿值
  • 代替eval执行字符串表达式
  • 解决Android 7.0 App内切换语言不生效的问题
  • App测试方法总结
  • 一个SAP顾问在美国的这些年
  • Centos 7安装oracle 数据库
  • 关于easyui中datagrid分页问题--摘
  • 数据库学习(MySQL):JDBC的简单增删改查实现
  • MySQL IFNULL()函数的用法
  • 华三云ONEstor存储测试
  • 【译】理解JavaScript:new 关键字
  • 【跃迁之路】【477天】刻意练习系列236(2018.05.28)
  • Java小白进阶笔记(3)-初级面向对象
  • js中forEach回调同异步问题
  • Markdown 语法简单说明
  • NSTimer学习笔记
  • Rancher-k8s加速安装文档
  • SpringBoot 实战 (三) | 配置文件详解
  • TiDB 源码阅读系列文章(十)Chunk 和执行框架简介
  • 编写符合Python风格的对象
  • 创建一种深思熟虑的文化
  • 基于HAProxy的高性能缓存服务器nuster
  • 蓝海存储开关机注意事项总结
  • 力扣(LeetCode)965
  • 深度解析利用ES6进行Promise封装总结
  • 它承受着该等级不该有的简单, leetcode 564 寻找最近的回文数
  • 网络应用优化——时延与带宽
  • 小程序上传图片到七牛云(支持多张上传,预览,删除)
  • 延迟脚本的方式
  • 一天一个设计模式之JS实现——适配器模式
  • AI算硅基生命吗,为什么?
  • 没有任何编程基础可以直接学习python语言吗?学会后能够做什么? ...
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • ​【C语言】长篇详解,字符系列篇3-----strstr,strtok,strerror字符串函数的使用【图文详解​】
  • ​LeetCode解法汇总518. 零钱兑换 II
  • ​如何使用ArcGIS Pro制作渐变河流效果
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #我与Java虚拟机的故事#连载02:“小蓝”陪伴的日日夜夜
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (每日持续更新)信息系统项目管理(第四版)(高级项目管理)考试重点整理 第13章 项目资源管理(七)
  • (求助)用傲游上csdn博客时标签栏和网址栏一直显示袁萌 的头像
  • (四)Linux Shell编程——输入输出重定向
  • (一)Dubbo快速入门、介绍、使用
  • (转)从零实现3D图像引擎:(8)参数化直线与3D平面函数库
  • 、写入Shellcode到注册表上线
  • .net core 依赖注入的基本用发
  • .net core使用ef 6
  • .NET Framework 服务实现监控可观测性最佳实践
  • .Net Redis的秒杀Dome和异步执行