index.jsx 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. import React,{Component} from 'react';
  2. import className from 'classnames';
  3. import {NumberPan,Notify} from '@commonComp';
  4. import style from './index.less';
  5. import $ from "jquery";
  6. import {handleEnter,getPageCoordinate} from '@utils/tools.js';
  7. /***
  8. * author:zn@2018-11-19
  9. * 接收参数:
  10. * value: 默认选中的值
  11. * placeholder:灰显文字
  12. * handleSelect: 选中事件
  13. * show: 是否显示下拉
  14. * allClick:是否前后缀也可唤出数字键盘
  15. *
  16. * ***/
  17. class NumberDrop extends Component{
  18. constructor(props){
  19. super(props);
  20. this.state={
  21. editable:false, //标签是否可输入
  22. timer:null,
  23. sltTimer:null,
  24. hasSelect:false, //是否点过下拉键盘
  25. boxLeft:0,
  26. boxTop:0,
  27. tmpTop:0,
  28. tmpScroll:0,
  29. placeholder:props.placeholder
  30. };
  31. this.$span = React.createRef();
  32. this.$pre = React.createRef();
  33. this.$suf = React.createRef();
  34. this.$cont = React.createRef();
  35. this.select = this.select.bind(this);
  36. this.numInpBlur = this.numInpBlur.bind(this);
  37. this.handleSpanInp = this.handleSpanInp.bind(this);
  38. this.handleNumClick = this.handleNumClick.bind(this);
  39. this.handleNumFocus = this.handleNumFocus.bind(this);
  40. this.handleBlur = this.handleBlur.bind(this);
  41. this.changeToEdit = this.changeToEdit.bind(this);
  42. this.handleKeyDowm = this.handleKeyDowm.bind(this);
  43. this.beyondArea = this.beyondArea.bind(this);
  44. }
  45. select(text){ //选中键盘上数字事件
  46. let timer = null;
  47. clearTimeout(this.state.sltTimer);
  48. const {handleSelect,ikey,suffix,prefix,mainSaveText,min,max} = this.props;
  49. const needCompare=min!=undefined&&max!=undefined;
  50. if(!text){
  51. this.setState({
  52. placeholder:this.props.placeholder
  53. });
  54. }else{
  55. //console.log(text,isNaN(+text),max<+text)
  56. if(needCompare){
  57. if(!isNaN(+text)&&max<+text){ //数值过大
  58. this.beyondArea();
  59. return;
  60. }
  61. const that = this;
  62. timer = setTimeout(function(){
  63. clearTimeout(that.state.sltTimer);
  64. if(!that.props.show&&!isNaN(+text)&&min>+text){
  65. that.beyondArea();
  66. return;
  67. }
  68. },1500);
  69. }
  70. this.setState({
  71. hasSelect:true,
  72. sltTimer:timer
  73. });
  74. }
  75. handleSelect&&handleSelect({ikey,text,suffix,prefix,mainSaveText});
  76. }
  77. beyondArea(){
  78. const {handleSelect,ikey,suffix,prefix,mainSaveText} = this.props;
  79. Notify.info("输入数值不在合理值范围内,请重新输入!");
  80. handleSelect&&handleSelect({ikey,text:'',suffix,prefix,mainSaveText});
  81. this.setState({
  82. placeholder:this.props.placeholder,
  83. hasSelect:false
  84. });
  85. }
  86. handleNumFocus(e){ //数字框可编辑状态下聚集事件,处理是否显示下拉等
  87. const {placeholder} = this.state;
  88. const val = e.target.innerText.trim();
  89. //console.log(33,e.target.innerText,placeholder,e.target.innerText.trim() == placeholder)
  90. if(val!=''&&val == placeholder){
  91. this.setState({
  92. placeholder:''
  93. });
  94. }
  95. e.stopPropagation();
  96. }
  97. handleNumClick(e){ //数字框不可编辑的状态时点击事件,点击将数字框变为可输入且下拉不再显示直到失焦后再次聚集
  98. const {show,handleShow,ikey,id,patId,handleHide,value} = this.props;
  99. if(show) {
  100. handleHide && handleHide();
  101. return;
  102. }else{
  103. const {editable} = this.state;
  104. if(editable){
  105. return;
  106. }
  107. const that = this;
  108. //双击时不显示下拉
  109. clearTimeout(that.state.timer);
  110. const timer = setTimeout(function(){
  111. //只有弹窗关闭则点击数字键盘会清空当前数据
  112. that.$span.current.focus();
  113. that.setState({
  114. hasSelect:false
  115. });
  116. handleShow&&handleShow({ikey,id:patId||id});
  117. },300);
  118. this.setState({
  119. timer,
  120. boxLeft:getPageCoordinate(e).boxLeft,
  121. boxTop:getPageCoordinate(e).boxTop,
  122. tmpScroll: $("#addScrollEvent")[0].scrollTop,
  123. tmpTop:getPageCoordinate(e).boxTop
  124. });
  125. $("#addScrollEvent").scroll(()=>{
  126. let scrollYs = $("#addScrollEvent")[0].scrollTop;
  127. this.setState({
  128. boxTop:this.state.tmpTop - scrollYs + this.state.tmpScroll
  129. })
  130. })
  131. }
  132. e.stopPropagation();
  133. }
  134. numInpBlur(e){ //数字框失焦,保存值到store中
  135. e.stopPropagation();
  136. const {handleSelect,ikey,suffix,prefix,mainSaveText,min,max,show} = this.props;
  137. if(show){ //修改清空后第一次点击键盘不触发click事件bug
  138. return;
  139. }
  140. //输入超出合理范围提示且清空
  141. const txt = e.target.innerText.replace(/^\s*/,'');
  142. if(max!=undefined&&!isNaN(+txt)&&(min>+txt||max<+txt)){
  143. this.beyondArea();
  144. return;
  145. }
  146. //输入为空时显示placeholder
  147. if(!e.target.innerText.trim()){
  148. this.setState({
  149. placeholder:this.props.placeholder
  150. });
  151. }
  152. /*this.setState({
  153. hasSelect:false
  154. });*/
  155. const val = e.target.innerText.replace(/^\s*/,'');
  156. const {placeholder} = this.state;
  157. let text = val===placeholder?'':val.replace(/[\u4e00-\u9fa5]/g,'');
  158. e.target.innerText = ''; //避免出现重复输入值
  159. handleSelect&&handleSelect({ikey,text,suffix,prefix,mainSaveText});
  160. }
  161. handleSpanInp(e){ //数字框输入事件
  162. e.stopPropagation();
  163. const {handleHide} = this.props;
  164. /*this.setState({ //再键盘点击数字要清空
  165. hasSelect:false
  166. });*/
  167. handleHide&&handleHide();
  168. }
  169. handleKeyDowm(e){
  170. handleEnter();
  171. //只能输入数字
  172. const key = e.key;
  173. const ctrlOn = e.ctrlKey;
  174. const isCopyPaste = ctrlOn&&(key=='v'||key=='c');
  175. if((!/[0-9|.|~|\/]/.test(key)&&key.length==1&&!isCopyPaste)){
  176. e.preventDefault();
  177. return false;
  178. }
  179. }
  180. getClasses(){ //整个标签是否有值的状态
  181. const {hideTag,placeholder,value} = this.props;
  182. const $span = this.$span.current;
  183. const val = value;//$span&&$span.innerText.trim()||value;
  184. const blueBorder = this.state.editable?style['blue-border']:'';
  185. const isSelected = val&&val!=placeholder?style['selected']:style['container'];
  186. const noTag = hideTag?style['no-tag']:'';
  187. return className(isSelected,noTag,blueBorder);
  188. }
  189. changeToEdit(e){ //整个标签双击编辑状态
  190. const {value,id,handleDbclick,patId,handleHide,show} = this.props;
  191. clearTimeout(this.state.timer);//取消延时的单击事件
  192. e.preventDefault();
  193. if(show){
  194. handleHide&&handleHide();
  195. }
  196. if(value&&value.trim()) {//有选中值的标签才能双击编辑
  197. this.setState({
  198. editable: true
  199. });
  200. setTimeout(()=>{
  201. this.$cont.current.focus();
  202. })
  203. //双击埋点记录
  204. handleDbclick && handleDbclick({id:patId||id});
  205. }
  206. }
  207. handleBlur(e){ //双击编辑blur
  208. const {handleLabelChange,ikey,boxMark,value} = this.props;
  209. //if(!this.state.editable) return;
  210. this.setState({
  211. editable: false
  212. });
  213. let totalVal = e.target.innerText;
  214. let changeVal = this.$span.current.innerText.replace(/^\s*/,'');//数字框值-修改后;去掉前空格避免多空格叠加
  215. let prefix = this.$pre.current.innerText.replace(/^\s*/,''); //前缀值-修改后
  216. let suffix = this.$suf.current.innerText.replace(/^\s*/,''); //后缀值-修改后
  217. //console.log('数字框:'+changeVal,";全部:"+totalVal,";前缀:"+prefix+";后缀:"+suffix);
  218. handleLabelChange && handleLabelChange({ikey,changeVal,type:boxMark,totalVal,prefix,suffix});
  219. }
  220. getSpanClass(){ //将被替换的文字选中状态显示
  221. //const {hasSelect} = this.state;
  222. const cls = this.props.show?style['blued']:'';
  223. return cls;
  224. }
  225. componentDidMount(){
  226. //设置最小宽度避免输入后宽度跳动
  227. const spanWidth = window.getComputedStyle(this.$span.current).width;
  228. this.$span.current.style.minWidth=spanWidth;
  229. }
  230. render(){
  231. const {prefix,suffix,show,value,handleHide,allClick} = this.props;
  232. const {numEditable,placeholder,editable,hasSelect,boxTop,boxLeft} = this.state;
  233. return <div className={this.getClasses()}
  234. ref={this.$cont}
  235. onDoubleClick={this.changeToEdit}
  236. onClick={allClick?this.handleNumClick:null}
  237. contentEditable={editable}
  238. onBlur={this.handleBlur}
  239. onkeydown={handleEnter}>
  240. <span ref = {this.$pre}>&nbsp;{prefix}</span>
  241. <span onFocus={this.handleNumFocus}
  242. onClick={allClick?null:this.handleNumClick}
  243. contentEditable={true}
  244. style={{minWidth:'10px',display:'inline-block',textAlign:'center'}}
  245. ref = {this.$span}
  246. onBlur={this.numInpBlur}
  247. onInput={this.handleSpanInp}
  248. className={this.getSpanClass()}
  249. onkeydown={this.handleKeyDowm}>&nbsp;{value||placeholder}</span>
  250. <span ref = {this.$suf}>&nbsp;{suffix}</span>
  251. <NumberPan handleSelect={(text)=>this.select(text)}
  252. onClose={handleHide}
  253. show={show}
  254. toClear={!hasSelect}
  255. left={boxLeft}
  256. top={boxTop}/>
  257. </div>
  258. }
  259. }
  260. export default NumberDrop;