說明:
版本:V2.0
此文章代碼復制不可用,有需要可以留言,或者聯系我。
功能介紹:
1.可以設置 可選擇的時間范圍,范圍超出部分置灰不可選擇。
2.點擊空白處默認收起彈窗,也可以在外部通過 open 屬性控制。
3.按確定按鈕后過 onOk 屬性返回 一個函數,第一個參數為選擇的時間。
4.*新增:用戶可以選擇是否使用確定按鈕。
5.*新增: disabled (禁用狀態)、className (設置選擇器)、defaultValue(時間為空時的默認顯示) 。
6.寬高自動獲取父級,最小高度22px,最下寬度90px。
改進方向:
此版本基本滿足開發時的各種需要,及突發情況。目前暫無改進計划。
大家若有什么好的建議,我也會采納,主要是得有時間。😊
吐槽一下:
這是多變態的產品,非得要一套“年份”的時間選擇器
效果展示:
js部份:
import React from "react"; import "./YearPicker.less" import moment from "moment"; import { connect } from "react-redux"; import PropTypes from "prop-types"; class YearPicker extends React.Component { constructor(props) { super(props) this.state = { stateOpen: false, // 是否展示彈窗 year: "", // "2020" 返給用戶的時間 inputValue: "", // input 框顯示的內容 selectYear: moment().format("YYYY"), // 選中的時間 yearListData: [], // 可選中的年份時間列表 basePage: 0 // 基准頁面 } } componentDidMount() { const { value, open } = this.props; const { basePage, stateOpen } = this.state; const data = this.renderList(basePage) this.setState({ selectYear: value ? value : moment().format("YYYY"), year: value ? value : "", yearListData: data, stateOpen: open === undefined ? stateOpen : open }) if (open === undefined) { document.addEventListener("click", this.eventFn, true) } } componentWillReceiveProps(nextProps) { if (nextProps.value !== this.props.value) { this.setState({ year: nextProps.value }) } } componentWillUnmount() { document.removeEventListener("click", this.eventFn, true) } eventFn = (ev) => { // ... } renderList = (basePage) => { // ... } onclick = (ev) => { // ... } yearClick = (ev, item, showOk) => { // ... } iconLeftClick = () => { // ... } iconRightClick = () => { // ... } okBut = (ev) => { // ... } textChange = () => { // ... } render() { const { year, stateOpen, selectYear, yearListData } = this.state; const { startValue, endValue, className, defaultValue, disabled, showOk } = this.props; let oneDisplay = "block", twoDisplay = "block"; if (startValue && yearListData.length && yearListData.includes(startValue)) { oneDisplay = "none"; } if (endValue && yearListData.length && yearListData.includes(endValue)) { twoDisplay = "none"; } return ( <div className={`YearPicker ${className} ${disabled ? "disabled-picker" : ""}`} id="YearPicker" onClick={(ev) => { disabled ? console.log("禁用") : this.onclick(ev) }}> <div className="begin"> <input className={year ? "zjl-input" : "zjl-input default_input"} value={year ? year : defaultValue} onChange={() => { this.textChange() }} disabled={disabled} /> <i className="img" ></i> </div> <div className="yearChild" style={{ display: stateOpen ? "block" : "none" }}> <div className="hearer"> <span className="left_icon" onClick={this.iconLeftClick} style={{ display: oneDisplay }}>{"<<"}</span> <span className="zjl-selectText">{selectYear}</span> <span className="right_icon" onClick={this.iconRightClick} style={{ display: twoDisplay }}>{">>"}</span> </div> <div className="con"> <ul className="yearBefore"> { yearListData.length > 0 && yearListData.map((item, index) => { return <li key={index} onClick={(+startValue && +startValue <= +item) && (+endValue && +item <= +endValue) ? (ev) => { this.yearClick(ev, item, showOk) } : null}> <span className={(+startValue && +startValue <= +item) && (+endValue && +item <= +endValue) ? selectYear === item ? "beforeli brackgroundYear" : "beforeli" : "beforeli warnnodata"}> {item} </span> </li> }) } </ul> </div> { showOk ? <div className="zjl-but"> <span onClick={ev => { this.okBut(ev) }}>確 定</span> </div> : null } </div> </div> ) } } YearPicker.propTypes = { value: PropTypes.string, startValue: PropTypes.string, endValue: PropTypes.string, open: PropTypes.bool, disabled: PropTypes.bool, onOk: PropTypes.func, className: PropTypes.string, } YearPicker.defaultProps = { showOk: false, // 是否使用確定按鈕,默認不使用 disabled: false, // 組件是否禁用,默認組件可以使用 defaultValue: "請選擇時間", // 默認日期 or 沒有日期時的提示語 value: "", // 日期 startValue: "1970", // 默認可選擇時間范圍的起始時間 endValue: moment().format("YYYY"), // 默認可選擇時間范圍的結束時間 open: undefined, // 默認彈窗展示與否不受控 onOk: () => { console.log(1) }, // 時間切換后的回調 className: "" } export default connect(state => state, {})(YearPicker)
less部份:
.YearPicker{ 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; } } .yearChild{ 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); .hearer{ height: 40px; line-height: 40px; text-align: center; border-bottom: 1px solid #e8e8e8; user-select: none; white-space: nowrap; overflow: hidden; position: relative; span{ display: inline-block; } .left_icon{ position: absolute; z-index: 100; top: 0; left: 0; cursor: pointer; width: 30px; margin-left: 20px; &:hover{ color: #40a9ff; } } .zjl-selectText{ text-align: center; } .right_icon{ position: absolute; z-index: 100; top: 0; right: 0; cursor: pointer; width: 30px; margin-right: 20px; &:hover{ color: #40a9ff; } } } .con{ width: 100%; .yearBefore{ width: 100%; padding: 10px 20px; overflow: hidden; display: grid; grid-gap: 20px; grid-template-columns: repeat(3, 1fr); // grid-auto-rows: 20px 20px 20px; grid-template-areas: "h h h" "h h h" "h h h" "h h h"; li { text-align: center; font-size: 14px; .beforeli{ display: inline-block; cursor: pointer; padding: 0 10px; height: 24px; line-height: 23px; &:hover{ background: #e6f7ff; } } .brackgroundYear{ background: #bae7ff; border-radius: 1px; &:hover{ background: #bae7ff; } } .warnnodata{ background-color: #f5f5f5; color: rgba(0, 0, 0, 0.25); cursor: not-allowed; } } } } .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; } } } } }
。