模仿 Antd 写一个季度的时间选择器 V1.0
说明:
版本:V1.0
这篇文章复制不可用。
功能介绍:
1.可以设置 可选择的时间范围,范围超出部分置灰不可选择
2.点击空白处默认收起弹窗,也可以在外部通过 open 属性控制
3.按确定按钮后过 onOk 属性返回 一个函数,第一个参数为选择的时间
改进方向:
若有时间,我希望弹窗中的确定按钮可以由用户选择,是否需要使用。
当用户决定不使用确定按钮时,选择时间后弹窗自动收起,并且把选择的时间返出去。
大家若有什么好的建议,我也会采纳,主要是得有时间,😊
效果展示:
更多版本:
ts 版 (网友 Ever-Lose 改进提供的)
js部份:
import React from "react"; import "./QuarterlyPicker.less" import moment from "moment"; import { connect } from "react-redux"; import PropTypes from "prop-types"; const quarterData = ["第一季度", "第二季度", "第三季度", "第四季度"]; class QuarterlyPicker extends React.Component { constructor(props) { super(props) this.state = { open: false, // 是否展示弹窗 year: "", // "2020" selectTime: "", // 选中的时间, "2020-1", "-1" 代表第一季度 selectionTime: "", // 确定的时间 oneDisplay: "block", twoDisplay: "block" } } componentDidMount() { const { value, open } = this.props; this.setState({ selectTime: value, selectionTime: value, year: value.split("-")[0] }) if (open === undefined) { document.addEventListener("click", this.eventFn) } } componentWillUnmount() { document.removeEventListener("click", this.eventFn) } eventFn = (ev) => { const ary = []; ev.path.map(item => { ary.push(item.className) }) if (!ary.includes("QuarterlyPicker")) { this.setState({ open: false }) } } onclick = (ev) => { ev.stopPropagation(); this.setState({ open: true }) } ulliclick = (index) => { const { year } = this.state; this.setState({ selectTime: `${year}-${index}` }) } iconLeftClick = () => { let { year } = this.state; const { startValue } = this.props; if (startValue) { if (year > startValue.split("-")[0]) { year--; this.setState({ year: `${year}` }) } } else { year--; this.setState({ year: `${year}` }) } this.idBlock(year) } iconRightClick = () => { let { year } = this.state; const { endValue } = this.props; if (endValue) { if (year < endValue.split("-")[0]) { year++; this.setState({ year: `${year}` }) } } else { year++; this.setState({ year: `${year}` }) } this.idBlock(year) } idBlock = (year) => { const { startValue, endValue } = this.props; if (startValue) { const startYear = startValue.split("-")[0]; if(+year > +startYear) { this.setState({ oneDisplay: "block" }) } else { this.setState({ oneDisplay: "none" }) } } else { this.setState({ oneDisplay: "block" }) } if (endValue) { const endYear = endValue.split("-")[0]; if(+year < +endYear) { this.setState({ twoDisplay: "block" }) } else { this.setState({ twoDisplay: "none" }) } } else { this.setState({ twoDisplay: "block" }) } } okBut = (ev) => { ev.stopPropagation(); const { selectTime, selectionTime } = this.state; this.setState({ selectionTime: selectTime, open: false }, () => { this.props.onOk(selectionTime) }) } quarterDataBefore = () => { // 。。。 } render() { const { oneDisplay, twoDisplay, selectTime, year, selectionTime } = this.state; console.log(oneDisplay, twoDisplay) let open = false; if (typeof (this.props.open) === "boolean") { open = this.props.open; } else { open = this.state.open; } return ( <div className="QuarterlyPicker" onClick={ev => { this.onclick(ev) }}> <div className="begin"> <input className="zjl-text" defaultValue={selectionTime}/> <i className="img" ></i> </div> <div className="child" style={{ display: open ? "block" : "none" }}> <header className="zjl-timehear"> <span>{selectTime}</span> </header> <div className="con"> <ul className="content-one"> <li className="lefticon" onClick={this.iconLeftClick} style={{ display: oneDisplay }}>{"<<"}</li> <li className="righticon" onClick={this.iconRightClick} style={{ display: twoDisplay }}>{">>"}</li> <li>{year}</li> </ul> </div> <div className="TimerXhlleft"> <ul className="quaterleft"> {this.quarterDataBefore()} </ul> </div> <div className="zjl-but"> <span onClick={this.okBut}> 确 定 </span> </div> </div> </div> ) } } QuarterlyPicker.propTypes = { value: PropTypes.string, startValue: PropTypes.string, endValue: PropTypes.string, open: PropTypes.bool, onOk: PropTypes.func } QuarterlyPicker.defaultProps = { value: `${moment().format("YYYY")}-${moment().quarter()}}`, startValue: "1970-1", endValue: `${moment().format("YYYY")}-${moment().quarter()}}`, open: undefined, onOk: () => { console.log(1) } } export default connect(state => state, {})(QuarterlyPicker)
less部份:
.QuarterlyPicker{ width: 100%; 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-text{ 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: 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; &: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); } } .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; } } .child{ 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); .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; } } } .zjl-timehear{ height: 34px; padding: 6px 10px; border-bottom: 1px solid #e8e8e8; span{ display: inline-block; width: 100%; margin: 0; cursor: default; } } .TimerXhlleft{ width: 100%; padding: 20px; .quaterleft{ display: flex; width: 100%; flex-direction: row; flex-wrap: wrap; justify-content: space-between; .quaterleftli{ width: 43%; margin-right: 0 !important; text-align: center; line-height: 50px; height: 50px; color: #333; .normalcolor{ display: inline-block; line-height: 30px; height: 30px; padding: 0 20px; cursor: pointer; &:hover{ background: #e6f7ff; cursor: pointer; } &.nonormaocolor{ background: #bae7ff; border-radius: 1px; // color: #fff; } &.warnnodata{ background: #F5f5f5; color: rgba(0, 0, 0, 0.25); cursor: not-allowed; } } } } } .con{ height: 40px; line-height: 40px; text-align: center; border-bottom: 1px solid #e8e8e8; user-select: none; .content-one{ white-space: nowrap; overflow: hidden; position: relative; .lefticon{ position: absolute; z-index: 100; top: 0; left: 0; font-size: 18px; cursor: pointer; width: 30px; margin-left: 20px; &:hover{ color: #40a9ff; } } .righticon{ position: absolute; z-index: 100; top: 0; right: 0; font-size: 18px; cursor: pointer; width: 30px; margin-right: 20px; &:hover{ color: #40a9ff; } } li { display: inline-block; text-align: center; cursor: default; } } } } }