模仿 Antd 写一个季度的时间选择器 V2.0
说明:
版本:V2.0
此文章代码复制不可用,有需要可以留言,或者联系我。
功能介绍:
1.可以设置 可选择的时间范围,范围超出部分置灰不可选择。
2.点击空白处默认收起弹窗,也可以在外部通过 open 属性控制。
3.按确定按钮后过 onOk 属性返回 一个函数,第一个参数为选择的时间。
4.*新增:用户可以选择是否使用确定按钮。
5.*新增: disabled (禁用状态)、className (设置选择器)、defaultValue(时间为空时的默认显示) 。
6.宽高自动获取父级,最小高度22px,最下宽度90px。
改进方向:
此版本基本满足开发时的各种需要,及突发情况。目前暂无改进计划。
观众老爷们若有什么好的建议,我也会采纳,主要是得有时间。😊
效果展示:
更多版本:
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 = { stateOpen: false, // 是否展示弹窗 year: "", // "2020" selectTime: `${moment().format("YYYY")}-${moment().quarter()}`, // 选中的时间, "2020-1", "-1" 代表第一季度 selectionTime: "", // 点确定后需要返回的时间 oneDisplay: "block", twoDisplay: "block" } } componentDidMount() { const { value, open } = this.props; let { year, selectTime } = this.state; year = value ? value.split("-")[0] : selectTime.split("-")[0] this.setState({ selectTime: value ? value : selectTime, selectionTime: value ? value : "", year }) this.idBlock(year) if (open === undefined) { document.addEventListener("click", this.eventFn, true) } } componentWillUnmount() { document.removeEventListener("click", this.eventFn, true) } eventFn = (ev) => { // ... } onclick = (ev) => { // ... } ulliclick = (index) => { // ... } iconLeftClick = () => { // ... } iconRightClick = () => { // ... } idBlock = (year) => { // ... } okBut = (ev) => { // ... } textChange = () => { // ... } quarterDataBefore = () => { // ... } render() { const { oneDisplay, twoDisplay, selectTime, year, selectionTime, stateOpen } = this.state; const { className, defaultValue, disabled, showOk, open } = this.props; let openOnOff = false; if (typeof (this.props.open) === "boolean") { openOnOff = open; } else { openOnOff = stateOpen; } return ( <div className={`QuarterlyPicker ${className}`} id="QuarterlyPicker" onClick={disabled ? console.log("禁用") : ev => { this.onclick(ev) }}> <div className="begin"> <input className={selectionTime ? "zjl-input" : "zjl-input default_input"} value={selectionTime ? selectionTime : defaultValue} disabled={disabled} onChange={() => { this.textChange() }} /> <i className="img" ></i> </div> <div className="child" style={{ display: openOnOff ? "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> { showOk ? <div className="zjl-but"> <span onClick={this.okBut}>确 定</span> </div> : null } </div> </div> ) } } QuarterlyPicker.propTypes = { value: PropTypes.string, startValue: PropTypes.string, endValue: PropTypes.string, open: PropTypes.bool, onOk: PropTypes.func, disabled: PropTypes.bool } QuarterlyPicker.defaultProps = { showOk: false, // 是否使用确定按钮,默认不使用 disabled: false, // 组件是否禁用,默认组件可以使用 defaultValue: "请选择时间", // 默认日期 or 没有日期时的提示语 value: "", startValue: "1970-1", endValue: `${moment().format("YYYY")}-${moment().quarter()}`, open: undefined, onOk: () => { console.log(1) }, className: "" } export default connect(state => state, {})(QuarterlyPicker)
less部份:
.QuarterlyPicker{ width: 100%; height: 100%; min-height: 22px; min-width: 90px; 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-input{ 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: 100%; 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); } } .zjl-input[disabled] { color: rgba(0, 0, 0, 0.25); background-color: #f5f5f5; cursor: not-allowed; opacity: 1; } .default_input{ color: rgba(0, 0, 0, 0.25); } .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; } } } } }