UNPKG

bee-input-number

Version:
766 lines (710 loc) 26.6 kB
import React, {Component} from 'react'; import classnames from 'classnames'; import InputGroup from 'bee-input-group'; import FormControl from 'bee-form-control'; import Message from 'bee-message'; import PropTypes from 'prop-types'; import i18n from './i18n'; import { getComponentLocale } from 'bee-locale/build/tool'; const propTypes = { max: PropTypes.number, min: PropTypes.number, step: PropTypes.number, autoWidth: PropTypes.bool, precision: PropTypes.number, format: PropTypes.func, delay: PropTypes.number, disabled:PropTypes.bool, toThousands:PropTypes.bool, locale:PropTypes.object, toNumber:PropTypes.bool, //回调函数内的值是否转换为数值类型 displayCheckPrompt:PropTypes.bool, //是否显示超出限制范围之后的检验提示 minusRight:PropTypes.bool,//负号是否在右边 handleBtnClick:PropTypes.func,//加减按钮点击回调 hideActionButton:PropTypes.bool,//隐藏加减按钮 }; const defaultProps = { value: "", step: 1, clsPrefix: 'u-input-number', iconStyle: 'double', autoWidth: false, delay: 300, toNumber:false, displayCheckPrompt:false, locale:{}, handleBtnClick:()=>{} }; //校验提示 function prompt (content) { Message.destroy(); Message.create({content: content, color: 'warninglight'}); } /** * 千分符 * @param {要转换的数据} num */ function toThousands(number) { if(number==='')return ''; if(number==='0')return '0'; let num = (number || 0).toString(); let integer = num.split('.')[0]; let decimal = num.split('.')[1]||''; let result = ''; while (integer.length > 3) { result = ',' + integer.slice(-3) + result; integer = integer.slice(0, integer.length - 3); } if (integer) { result = integer + result ; if(num=='.'||num.indexOf('.')==num.length-1){ result = result + '.'+decimal; }else if (decimal){ result = result + '.'+decimal; } } if(result[0]=='-'){ result = result.replace('-,','-') } return result; } function setCaretPosition(ctrl,pos,need) { if(ctrl&&need){ if(ctrl.setSelectionRange) { ctrl.focus(); ctrl.setSelectionRange(pos,pos); // IE8 and below } else if(ctrl.createTextRange) { var range = ctrl.createTextRange(); range.collapse(true); range.moveEnd('character', pos); range.moveStart('character', pos); range.select(); } } } class InputNumber extends Component { constructor(props) { super(props); // 初始化状态,加减按钮是否可用,根据当前值判断 let data = this.judgeValue(props); this.state = { value:data.value, max: Number.MAX_SAFE_INTEGER || Math.pow(2, 53) - 1, min: Number.MIN_SAFE_INTEGER || (Math.pow(2, 53) - 1) * -1, minusDisabled: data.minusDisabled, plusDisabled: data.plusDisabled, showValue:toThousands(data.value), placeholderShow:true } this.timer = null; this.focus = false; this.selectionStart = 0; } // unThousands = (number) =>{ // if(!number || number === "")return number; // number = number.toString(); // return number.replace(new RegExp(this.props.formatSymbol,'g'),''); // // return number.replace(/\,/g,''); // } /** * 校验value * @param {*} props * @param {原来的值} oldValue */ judgeValue = (props,oldValue)=> { let currentValue; let currentMinusDisabled = false; let currentPlusDisabled = false; let { value,min,max,precision,onChange,displayCheckPrompt } = props; if(!max && max !== 0 && this.state) max = this.state.max; if(!min && min !== 0 && this.state) min = this.state.min; if(props.minusRight){ value = value.toString(); if(value.indexOf('-')!=-1){//所有位置的负号转到前边 value = value.replace('-',''); value = '-'+value; } value = Number(value); } if ((value!=undefined)&&(value!=null)) { if(value===''){ currentValue=''; return { value: '', minusDisabled: false, plusDisabled: false } }else{ currentValue = Number(value) ||0; } } //lse if (min&&(value!='')) {//mdd中提出bug //currentValue = min; //} else if(value==='0'||value===0){ currentValue = 0; }else{//NaN if(oldValue||(oldValue===0)||(oldValue==='0')){ currentValue = oldValue; }else{//value为空 return { value: '', minusDisabled: false, plusDisabled: false } } } if(currentValue==-Infinity){ return { value: min, minusDisabled: true, plusDisabled: false } } if(currentValue==Infinity){ return { value: max, minusDisabled: false, plusDisabled: true } } const local = getComponentLocale(props, this.context, 'InputNumber', () => i18n); if (min&&currentValue <= min) { if(displayCheckPrompt)prompt(local['msgMin']); currentMinusDisabled = true; currentValue=min; } if (max&&currentValue >= max) { if(displayCheckPrompt)prompt(local['msgMax']); currentPlusDisabled = true; currentValue=max; } if(props.hasOwnProperty('precision')){ // currentValue = Number(currentValue).toFixed(precision); currentValue = this.getPrecision(currentValue); } if(props.minusRight){ currentValue = currentValue.toString(); if(currentValue.indexOf('-')!=-1){//负号转到后边 currentValue = currentValue.replace('-',''); currentValue = currentValue+'-'; } } return { value: currentValue, minusDisabled: currentMinusDisabled, plusDisabled: currentPlusDisabled } } componentDidMount(){ this.setState({ value: this.props.value, showValue:toThousands(this.props.value) }); } componentWillReceiveProps(nextProps){ if(this.focus){ if(nextProps.value==Infinity||nextProps.value==-Infinity){ }else{ let nextValue = nextProps.value; let isThousand = this.isThousandth(nextValue); // 是否是千分位 if (isThousand){ nextValue = this.delcommafy(nextValue); } this.setState({ value: nextValue, showValue:toThousands(nextValue), }); } }else{ let data = this.judgeValue(nextProps,this.state.value); this.setState({ value: data.value, showValue:toThousands(data.value), minusDisabled: data.minusDisabled, plusDisabled: data.plusDisabled }); } } ComponentWillUnMount() { this.clear(); } /** * @memberof InputNumber * type 是否要四舍五入(此参数无效,超长不让输入) */ numToFixed = (value,fixed,type) => { value = String(value); if(!value && value !== "0")return value; if(!fixed && String(fixed) !== "0")return value; let preIndex = value.indexOf("."); if(value.indexOf(".") === -1)return value; preIndex++; let endIndex = (preIndex+fixed); let precValue = value.substr(preIndex,endIndex)+"0000000000"; if(type){ return (Number(value) + 1e-14).toFixed(fixed); } return value.split(".")[0] +"."+ precValue.substr(0,fixed); } handleChange = (value) => { let selectionStart = this.input.selectionStart==undefined?this.input.input.selectionStart:this.input.selectionStart; this.selectionStart = selectionStart; const { onChange,toNumber,minusRight} = this.props; if(value===''){ onChange && onChange(value); this.setState({ value, showValue:'' }) return; } // value = this.unThousands(value); if(minusRight){ if(value.match(/-/g)&&value.match(/-/g).length>1)return } let thousandth = this.isThousandth(value); // 是否是千分位 if(isNaN(value)&&(value!=='.')&&(value!=='-')&&(!thousandth))return; if(value.indexOf(".") !== -1){//小数最大值处理 let prec = String(value.split(".")[1]).replace("-",""); if(this.props.precision === 0 && (prec ==="" || prec !=""))return; if(this.props.precision && prec.length > this.props.precision)return; // if(prec.length > 8)return; } this.setState({ value, showValue:toThousands(value), }); if(value==='-'){ onChange && onChange(value); }else if(value=='.'||value.indexOf('.')==value.length-1){//当输入小数点的时候 onChange && onChange(value); }else if(value[value.indexOf('.')+1]==0){//当输入 d.0 的时候,不转换Number onChange && onChange(value); }else{ toNumber?onChange && onChange(Number(value)):onChange && onChange(value); } if(this.props.toThousands){ let stateShowValue = toThousands(this.state.value); let showValue = toThousands(value) let addNumber = 0; let delNumber = 0; let reg = /[0-9]/ for(let i =0;i<selectionStart;i++){ if(!reg.test(showValue[i]))addNumber+=1; } for(let j= 0;j<selectionStart;j++){ if(stateShowValue[j]){ if(!reg.test(stateShowValue[j]))delNumber+=1; } } let position = selectionStart+addNumber-delNumber; setCaretPosition(this.input&&this.input.input,position,true) } } handleFocus = (value,e) => { this.focus = true; let {onFocus, min, max } = this.props; if (onFocus) { onFocus(this.getPrecision(this.state.value), e) } else { this.forceUpdate(); } } /** * 恢复科学技术法的问题 */ getFullNum = (num)=>{ //处理非数字 if(isNaN(num)){return num}; //处理不需要转换的数字 var str = ''+num; if(!/e/i.test(str)){return num;}; let _precision = this.props.precision?this.props.precision:18; return (Number(num)).toFixed(_precision).replace(/\.?0+$/, ""); } handleBlur = (v,e) => { this.focus = false; let {onBlur,precision,onChange,toNumber,max,min,displayCheckPrompt,minusRight,round } = this.props; if(!max && max !==0 ) max = this.state.max; if(!min && min !== 0) min = this.state.min; const local = getComponentLocale(this.props, this.context, 'InputNumber', () => i18n); v = this.state.value;//在onBlur的时候不需要活输入框的只,而是要获取state中的值,因为有format的时候就会有问题。 if(v==='' || !v){ this.setState({ value:v }) onChange && onChange(v); onBlur && onBlur(v,e); return; } // let value = this.unThousands(v); let value = this.numToFixed(v,precision,round); if(minusRight){ if(value.indexOf('-')!=-1){//所有位置的负号转到前边 value = value.replace('-',''); value = '-'+value; } } value = isNaN(Number(value)) ? 0 : Number(value); if((max || max===0) && value>max){ if(displayCheckPrompt)prompt(local['msgMax']); value = max; } if((min || min===0) && value<min){ if(displayCheckPrompt)prompt(local['msgMin']); value = min; } if(this.props.hasOwnProperty('precision')){ // value = value.toFixed(precision); value = this.getPrecision(value); } value = value.toString(); if(minusRight&&(value.indexOf('-')!=-1)){//负号转到后边 value = value.replace('-',''); value = value+'-'; } this.setState({ value, showValue:toThousands(value), placeholderShow:true }); this.detailDisable(value); if(toNumber&&(!minusRight)){ onChange && onChange(value); onBlur && onBlur(value,e); }else{ onChange && onChange(value); onBlur && onBlur(value,e); } } // 去掉千分位 delcommafy = (num) => {//去除千分位中的‘,’ if(num&&num!='undefined'&&num!='null'){ let numS = num; numS = numS.toString(); numS = numS.replace(/,/gi, ''); return numS; }else { return num; } } /** * 设置增加减少按钮是否可用 */ detailDisable = (value) => { let { max, min, step } = this.props; if(!max && max !== 0) max = this.state.max; if(!min && min !== 0) min = this.state.min; if(max&&(value >= max || Number(value) + Number(step) > max)){ this.setState({ plusDisabled: true }) }else{ this.setState({ plusDisabled: false }) } if(min&&(value <= min || value -step < min)){ this.setState({ minusDisabled: true }) }else{ this.setState({ minusDisabled: false }) } } /** * 减法 */ minus = (value) => { let {min, max, step, onChange, toNumber} = this.props; if(!max && max !== 0) max = this.state.max; if(!min && min !== 0) min = this.state.min; value = (value === '-') ? 0 : value; if(typeof min === "undefined"){ value = this.detail(value, step, 'reduce'); }else{ min = Number(min) if(value < min){ value = min; }else{ let reducedValue = this.detail(value, step, 'reduce'); if(reducedValue >= min){ value = reducedValue; } } } if(max){ max = Number(max) if(value > max){ value = max; } } this.setState({ value, showValue:toThousands(value) },()=>{ this.input.input.focus&&this.input.input.focus() }); toNumber?onChange && onChange(Number(value)):onChange && onChange(value); this.handleBtnClick('down',value); this.detailDisable(value); } /** * 加法 */ plus = (value) => { let {max, min, step, onChange, toNumber} = this.props; if(!max && max !== 0) max = this.state.max; if(!min && min !== 0) min = this.state.min; value = (value === '-') ? 0 : value; if(typeof max === "undefined"){ value = this.detail(value, step, 'add'); }else{ max = Number(max) if(max&&value > max){ value = max; }else{ let addedValue = this.detail(value, step, 'add'); if(max&&addedValue <= max){ value = addedValue; } } } if(min){ min = Number(min) if(value < min){ value = min; } } this.setState({ value, showValue:toThousands(value) },()=>{ this.input.input.focus&&this.input.input.focus() }); toNumber?onChange && onChange(Number(value)):onChange && onChange(value); this.handleBtnClick('up',value); this.detailDisable(value); } detail = (value, step, type) => { let {precision} = this.props; let valueFloat = this.separate(value); let stepFloat = this.separate(step); let ans; let stepFloatLength = stepFloat.toString().length; let valueFloatLength = valueFloat.toString().length; if (typeof precision === 'undefined') { precision = Math.max(stepFloatLength, valueFloatLength); } let coefficient = Math.pow(10, Math.abs(stepFloatLength - valueFloatLength)); if (type === 'add') { ans = (value * coefficient + step * coefficient) / coefficient; } else { ans = (value * coefficient - step * coefficient) / coefficient; } return ans.toFixed(precision); } // 是否是千分位 isThousandth = (value) => { let regex = /(?:^[-]?[1-9]\d{0,2}(?:$|(?:,\d{3})*(?:$|(\.\d{1,2}$))))|(?:(?:^[0](\.\d{1,2})?)|(?:^[-][0]\.\d{1,2}))$/; return regex.test(value); } /** * 分离小数和整数 * @param value * @returns {*} */ separate = (value) => { if(value==null||value==undefined){ return "" }else{ value = value.toString(); if(value.indexOf('.') > -1){ return value.split('.')[1]; }else{ return "" } } } clear = () => { if (this.timer) { clearTimeout(this.timer); } } handlePlusMouseDown = (e) => { e.preventDefault && e.preventDefault(); let {delay,disabled} = this.props; let {value} = this.state; if(disabled)return; this.plus(value); this.clear(); this.timer = setTimeout(() => { this.handlePlusMouseDown(e); }, delay); } handleReduceMouseDown = (e) => { e.preventDefault && e.preventDefault(); let {delay,disabled} = this.props; let {value} = this.state; if(disabled)return; this.minus(value); this.clear(); this.timer = setTimeout(() => { this.handleReduceMouseDown(e); }, delay); } getPrecision = (value)=>{ if(value==null||value==undefined)return value; if(!value && value === "")return value; value = String(value); value = value.indexOf("e") !== -1?this.getFullNum(value):value; const {precision} = this.props; if(precision === 0)return value; if (precision == undefined || (value.indexOf(".") !== -1 && String(value.split(".")[1]).length === precision)) { return value; } let before = value.substring(0,1),len = value.length, after = value.substring(len-1,len); before = before === "-"?before:""; after = after === "-"?after:""; //是科学计数法,不replace - if(before)value = value.substring(1,len); if(after)value = value.substring(0,len-1); // value = value.replace("-",''); let precV = "000000000000000000000000000000000000000000000000000000000000000000000000"; if(value.indexOf(".") === -1){ precV = precV.substr(0,precision); precV = precV?"."+precV:precV; if((!isNaN(value))&&(value.indexOf('-')!=-1||value.indexOf('+')!=-1)&&(value.indexOf('e')!=-1)){//是科学计数法,不拼接0000000 }else{ value = value + precV; } } return before+Number(value).toFixed(precision)+after; } handleBtnClick = (type,value)=>{ this.props.handleBtnClick(type,value) } isIE = ()=>{ if(window){ if (!!window.ActiveXObject || "ActiveXObject" in window)return true; } return false; } placeholderClick=()=>{ this.input.input.focus() this.setState({ placeholderShow:false }) } render() { const {toThousands,minusRight, step,disabled, clsPrefix, className, delay, onBlur, onFocus, iconStyle, autoWidth, onChange, format, precision,toNumber, hideActionButton, ...others} = this.props; let classes = { [`${clsPrefix}-auto`]: autoWidth, [`${clsPrefix}`]: true, [`${clsPrefix}-lg`]: others.size === "lg", [`${clsPrefix}-sm`]: others.size === "sm", }; let {value, minusDisabled, plusDisabled, showValue} = this.state; value = precision != null && !this.focus?this.getPrecision(value):value; value = format && !this.focus? format(value) : value; value = String(value).indexOf("e") !== -1?this.getFullNum(value):value; if(minusRight && String(value).indexOf('-')!=-1){ value = String(value).replace("-","")+"-"; } let disabledCursor = disabled? ' disabled-cursor':''; let disabledCon = disabled? ' disabled-con':''; return ( <div className={`${clsPrefix}-out`}> { iconStyle === 'double' ? ( <InputGroup className={classnames(className, classes,disabledCon)}> { this.isIE()&&(!value)?<div onClick={this.placeholderClick} style={{'display':this.state.placeholderShow?'block':'none'}} className={`${clsPrefix}-placeholder`}>{this.props.placeholder}</div>:'' } { hideActionButton ? '' : <InputGroup.Addon // onClick={()=>{minusDisabled?'':this.handleBtnClick('down')}} className={(minusDisabled && 'disabled' ) + disabledCursor} onMouseDown={ this.handleReduceMouseDown} onMouseLeave={ this.clear } onMouseUp={ this.clear }> - </InputGroup.Addon> } <FormControl {...others} value={toThousands?showValue:value} disabled={disabled} onBlur={ this.handleBlur } onFocus={this.handleFocus} onChange={ this.handleChange } ref={ref=>this.input = ref} /> { hideActionButton ? '' : <InputGroup.Addon // onClick={()=>{plusDisabled?'':this.handleBtnClick('up')}} className={(plusDisabled && 'disabled' ) + disabledCursor} onMouseDown={ this.handlePlusMouseDown} onMouseLeave={ this.clear } onMouseUp={ this.clear }> + </InputGroup.Addon> } </InputGroup> ) : ( <InputGroup className={classnames(className, classes,disabledCon)} simple > { this.isIE()&&(!value)?<div onClick={this.placeholderClick} style={{'display':this.state.placeholderShow?'block':'none'}} className={`${clsPrefix}-placeholder`}>{this.props.placeholder}</div>:'' } <FormControl {...others} value={toThousands?showValue:value} disabled={disabled} onBlur={ this.handleBlur } onFocus={this.handleFocus} onChange={ this.handleChange } ref={ref=>this.input = ref} /> { hideActionButton ? '' : <InputGroup.Button> <div className={classnames("icon-group")}> <span // onClick={()=>{plusDisabled?'':this.handleBtnClick('up')}} onMouseDown={ this.handlePlusMouseDown} onMouseLeave={ this.clear } onMouseUp={ this.clear } className={classnames('plus',{'disabled': plusDisabled,'disabled-cursor':disabledCursor})}> <span className="uf uf-arrow-up"/> </span> <span // onClick={()=> minusDisabled?'':this.handleBtnClick('down')} onMouseDown={ this.handleReduceMouseDown} onMouseLeave={ this.clear } onMouseUp={ this.clear } className={classnames("reduce",{'disabled': minusDisabled,'disabled-cursor':disabledCursor})}> <span className=" uf uf-arrow-down"/> </span> </div> </InputGroup.Button> } </InputGroup> ) } </div> ); } } ; InputNumber.defaultProps = defaultProps; InputNumber.propTypes = propTypes; InputNumber.contextTypes = { beeLocale: PropTypes.object }; export default InputNumber;