上代码。
组件的Js文件。
import React, { Component } from "react"; import Style from './myYearSelect.less' function getOffsetTop(el){ return el.offsetParent ? el.offsetTop + getOffsetTop(el.offsetParent) : el.offsetTop } function getOffsetLeft(el){ return el.offsetParent ? el.offsetLeft + getOffsetLeft(el.offsetParent) : el.offsetLeft } class YearPicker extends Component { constructor(props) { super(props); this.state = { isShow: false, selectedyear:"", boxPos:{}, years: [] }; } componentWillMount() { let { defaultValue } = this.props; if (defaultValue) { this.setState({ selectedyear: defaultValue }); } } componentDidMount() { let _this = this; document.addEventListener( "click", _this.clickFunc, false ); } componentWillUnmount(){ let _this = this document.removeEventListener( "click", _this.clickFunc, false ); } clickFunc = e => { const { isShow } = this.state; let clsName = e.target.className; // console.log(e.target.dataset) if (!e.target.dataset || !e.target.dataset.tag || e.target.dataset.tag !== "year") { this.onBlurFunc() } } onFocusFunc = e => { const { selectedyear } = this.state; const operand = 12; const d = document.documentElement || document.body || null let top, left this.initData(operand, selectedyear); if (d) { top = d.scrollTop left = d.scrollLeft } this.setState({ isShow: true, boxPos:{ x: getOffsetTop(e.currentTarget) - (top ? top : 0), y: getOffsetLeft(e.currentTarget) - (left ? left : 0), } }) } onBlurFunc = e => { 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); this.getYearsArr(newstart, newend); } if (type == "next") { newstart = parseInt(start + operand); newend = parseInt(end + operand); this.getYearsArr(newstart, newend); } } initData = (operand, defaultValue) => { operand = operand ? operand : 12; let year = defaultValue - 1970; let curr = year % operand; let start = defaultValue - curr; let end = parseInt(defaultValue) + parseInt(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 }); }; // 选中某一年 selects = e => { let val = Number(e.target.value); this.setState({ selectedyear: val }); if (this.props.callback) { this.props.callback(val); } }; render() { const { width } = this.props const { isShow, boxPos, selectedyear, years } = this.state; const w = width ? width : 200 return ( <div className={Style.main} data-tag="year"> <input className={Style.mainInput} data-tag="year" value={selectedyear} readOnly onFocus={this.onFocusFunc} /> { isShow && boxPos.x ? <div data-tag="year" tabIndex="0" className={Style.box} style={{ top:boxPos.x, left: boxPos.y, w }}> <List data={years} value={selectedyear} prev={this.prev} next={this.next} cback={this.selects} /> </div> : '' } </div> ); } } const List = props => { const { data, value, prev, next, cback } = props; return ( <div className={Style.list} tabIndex="-1" data-tag="year"> <div className={Style.header} data-tag="year"> <a type="double-left" role="button" className={Style.prevBtn} onClick={prev} data-tag="year" ></a> <span data-tag="year">{value}</span> <a type="double-left" role="button" className={Style.nextBtn} onClick={next} data-tag="year" ></a> </div> <div className={Style.body} data-tag="year"> <ul className={Style.yearUl} data-tag="year"> {data.map((item, index) => ( <li key={index} title={item} data-tag="year" className={ item == value ? Style.calendarYearSelected : "" } > <button onClick={cback} value={item}> {item} </button> </li> ))} </ul> </div> </div> ); }; export default YearPicker;
组件的 Less 文件。
.main{ width: 100%; } .mainInput{ 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: 32px; 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; } .box{ // width: 100%; height: 260px; max-width: 400px; // max-height: 400px; background-color: #fff; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); position: fixed; } .list{ width: 100%; height: 100%; } .header{ position: relative; height: 34px; line-height: 34px; text-align: center; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; border-bottom: 1px solid #e9e9e9; background-color: #fff; color:#000; } .prevBtn, .nextBtn{ position: absolute; top: 0; color: #000; font-family: Arial, "Hiragino Sans GB", "Microsoft Yahei", "Microsoft Sans Serif", sans-serif; padding: 0 5px; font-size: 16px; display: inline-block; line-height: 34px; height: 100%; &:hover{ &::before, &::after{ border-color: rgba(0, 0, 0, 0.65); } } &::before, &::after{ position: relative; top: -1px; display: inline-block; width: 8px; height: 8px; vertical-align: middle; border: 0 solid #aaa; border-width: 1.5px 0 0 1.5px; border-radius: 1px; transform: rotate(-45deg) scale(0.8); transition: all 0.3s; content: ''; } &::after{ left:-3px } } .nextBtn{ &::before,&::after{ transform: rotate(135deg) scale(0.8); } } .prevBtn{ left:7%; } .nextBtn{ right:7%; } .body{ height: calc(100% - 34px); background-color: #fff; } .yearUl{ padding: 0; margin: 0; overflow: hidden; list-style: none; li{ float: left; width: 33.33%; max-width: 76px; text-align: center; 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; } } } li.calendarYearSelected{ button{ background-color: #108ee9; color: #fff; } }
使用组件的页面 jsx 文件。
import React from 'react' import moment from 'moment' import YearPicker from './yearPicker/myYearSelect' class yaerSelectPageNew extends React.Component { constructor(props){ super(props) this.state = { mockData:{ year:2020 }, YPWidth:231 } } filterByYear = (e) => { const { mockData } = this.state this.setState({mockData: { ...mockData, year: e.toString() }}); } render(){ const { YPWidth, mockData } = this.state return ( <div> <h1>Page </h1> <div style={{ '200px' }}> <YearPicker defaultValue={moment().format('YYYY')} callback={this.filterByYear} width={YPWidth} /> </div> </div> ); } } export default yaerSelectPageNew