index.jsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. blurTimer:null,
  25. hasSelect:false, //是否点过下拉键盘
  26. boxLeft:0,
  27. boxTop:0,
  28. tmpTop:0,
  29. tmpScroll:0,
  30. placeholder:props.placeholder
  31. };
  32. this.$span = React.createRef();
  33. this.$pre = React.createRef();
  34. this.$suf = React.createRef();
  35. this.$cont = React.createRef();
  36. //this.select = this.select.bind(this);
  37. this.numInpBlur = this.numInpBlur.bind(this);
  38. this.handleSpanInp = this.handleSpanInp.bind(this);
  39. this.handleNumClick = this.handleNumClick.bind(this);
  40. this.handleNumFocus = this.handleNumFocus.bind(this);
  41. this.handleBlur = this.handleBlur.bind(this);
  42. this.changeToEdit = this.changeToEdit.bind(this);
  43. this.handleKeyDowm = this.handleKeyDowm.bind(this);
  44. this.beyondArea = this.beyondArea.bind(this);
  45. }
  46. select(text){ //选中键盘上数字事件
  47. //placeholder修改后,第一次点击键盘触发blur后onClick不触发,原因未知,改为onMouseup可触发
  48. let timer = null;
  49. clearTimeout(this.state.sltTimer);
  50. clearTimeout(this.state.blurTimer);
  51. const {handleSelect,ikey,suffix,prefix,mainSaveText,min,max} = this.props;
  52. const needCompare=min!=undefined&&max!=undefined;
  53. if(!text){
  54. this.setState({
  55. placeholder:this.props.placeholder
  56. });
  57. }else{
  58. if(needCompare){
  59. const that = this;
  60. const isFine = this.validSymbols(text,min,max); //有~或/时是否合理
  61. const hasSymbol = /[\/|\~]/g.test(text); //是否有~或/
  62. const singleFine = !isNaN(+text)&&parseFloat(min)<=parseFloat(text)&&parseFloat(text)<=parseFloat(max); //无~或/时是否合理
  63. timer = setTimeout(function(){
  64. clearTimeout(that.state.sltTimer);
  65. if(text!=''&&(!hasSymbol&&!singleFine)||(hasSymbol&&!isFine)){
  66. that.beyondArea();
  67. return;
  68. }
  69. },1500);
  70. }
  71. this.setState({
  72. hasSelect:true,
  73. sltTimer:timer
  74. });
  75. }
  76. handleSelect&&handleSelect({ikey,text,suffix,prefix,mainSaveText});
  77. }
  78. beyondArea(){
  79. const {handleSelect,ikey,suffix,prefix,mainSaveText} = this.props;
  80. Notify.info("输入数值不符合规范,请重新输入!");
  81. handleSelect&&handleSelect({ikey,text:'',suffix,prefix,mainSaveText});
  82. this.setState({
  83. placeholder:this.props.placeholder,
  84. hasSelect:false
  85. });
  86. }
  87. handleNumFocus(e){ //数字框可编辑状态下聚集事件,处理是否显示下拉等
  88. const {placeholder} = this.state;
  89. const val = e.target.innerText.trim();
  90. //console.log(33,e.target.innerText,placeholder,e.target.innerText.trim() == placeholder)
  91. if(val!=''&&val == placeholder){
  92. this.setState({
  93. placeholder:''
  94. });
  95. }
  96. e.stopPropagation();
  97. }
  98. handleKeyDowm(e){
  99. if(e.keyCode==13){
  100. const {reFocus,num,handleHide} = this.props;
  101. reFocus&&reFocus(num);
  102. handleHide && handleHide();
  103. }
  104. }
  105. handleNumClick(e){ //数字框不可编辑的状态时点击事件,点击将数字框变为可输入且下拉不再显示直到失焦后再次聚集
  106. const {show,handleShow,ikey,id,patId,handleHide,value} = this.props;
  107. if(show) {
  108. handleHide && handleHide();
  109. return;
  110. }else{
  111. const {editable} = this.state;
  112. if(editable){
  113. return;
  114. }
  115. const that = this;
  116. //双击时不显示下拉
  117. clearTimeout(that.state.timer);
  118. const timer = setTimeout(function(){
  119. //只有弹窗关闭则点击数字键盘会清空当前数据
  120. that.$span.current.focus();
  121. that.setState({
  122. hasSelect:false
  123. });
  124. handleShow&&handleShow({ikey,id:patId||id});
  125. },300);
  126. this.setState({
  127. timer,
  128. boxLeft:getPageCoordinate(e).boxLeft,
  129. boxTop:getPageCoordinate(e).boxTop,
  130. tmpScroll: $("#addScrollEvent")[0].scrollTop,
  131. tmpTop:getPageCoordinate(e).boxTop
  132. });
  133. $("#addScrollEvent").scroll(()=>{
  134. let scrollYs = $("#addScrollEvent")[0].scrollTop;
  135. this.setState({
  136. boxTop:this.state.tmpTop - scrollYs + this.state.tmpScroll
  137. })
  138. })
  139. }
  140. e.stopPropagation();
  141. }
  142. validSymbols(txt,min,max){
  143. //输入只有一个~或/时判断两边是否为合理数字,有多个为不合理
  144. const index1 = txt.indexOf('~');
  145. const index2 = txt.indexOf('/');
  146. const needCompare = min!=undefined&&max!=undefined;
  147. let arr1=[],arr2=[];
  148. if(index1!=-1&&index1==txt.lastIndexOf('~')&&index1!=txt.length-1){ //有且只有一个~,且不在最后
  149. arr1 = txt.split('~');
  150. //~的范围在合理范围内为合理值
  151. if(!isNaN(+arr1[0])&&!isNaN(+arr1[1])&&((!needCompare)||(needCompare&&parseFloat(min)<=parseFloat(arr1[0])&&parseFloat(arr1[0])<=parseFloat(max)&&parseFloat(min)<=parseFloat(arr1[1])&&parseFloat(arr1[1])<=parseFloat(max)))){
  152. return true
  153. }
  154. return false;
  155. }
  156. if(index2!=-1&&index2==txt.lastIndexOf('/')&&index2!=txt.length-1){ //有且只有一个~,且不在最后
  157. arr2 = txt.split('/');
  158. // /两边的数字分别在合理范围内为合理值
  159. if(!isNaN(+arr2[0])&&!isNaN(+arr2[1])&&((!needCompare)||(needCompare&&parseFloat(min)<=parseFloat(arr2[0])&&parseFloat(arr2[0])<=parseFloat(max)&&parseFloat(min)<=parseFloat(arr2[1])&&parseFloat(arr2[1])<=parseFloat(max)))){
  160. return true
  161. }
  162. return false;
  163. }
  164. return false;
  165. }
  166. numInpBlur(e){ //数字框失焦,保存值到store中
  167. e.stopPropagation();
  168. const {handleSelect,ikey,suffix,prefix,mainSaveText,min,max,show} = this.props;
  169. /*if(show){ //修改清空后第一次点击键盘不触发click事件bug--失焦placehoder消失,弃用
  170. return;
  171. }*/
  172. //输入超出合理范围或输入不是数字提示且清空
  173. const needCompare=min!=undefined&&max!=undefined;
  174. const txt = e.target.innerText.trim();
  175. const isFine = this.validSymbols(txt,min,max); //有~或/时是否合理
  176. const hasSymbol = /[\/|\~]/g.test(txt); //是否有~或/
  177. const singleFine = (!isNaN(+txt)&&!needCompare)||(!isNaN(+txt)&&needCompare&&parseFloat(min)<=parseFloat(txt)&&parseFloat(txt)<=parseFloat(max)); //无~或/时是否合理
  178. if(txt!=''&&(!hasSymbol&&!singleFine)||(hasSymbol&&!isFine)){
  179. this.beyondArea();
  180. return;
  181. }
  182. //输入为空时显示placeholder
  183. const timer = setTimeout(()=>{
  184. if(!e.target.innerText.trim()){
  185. this.setState({
  186. placeholder:this.props.placeholder
  187. });
  188. }
  189. },200);
  190. this.setState({
  191. blurTimer:timer
  192. });
  193. const val = e.target.innerText.trim();
  194. const {placeholder} = this.state;
  195. let text = val===placeholder?'':val;
  196. //e.target.innerText = ''; //避免出现重复输入值
  197. handleSelect&&handleSelect({ikey,text,suffix,prefix,mainSaveText});
  198. }
  199. handleSpanInp(e){ //数字框输入事件
  200. e.stopPropagation();
  201. const {handleHide} = this.props;
  202. handleHide&&handleHide();
  203. }
  204. getClasses(){ //整个标签是否有值的状态
  205. const {hideTag,placeholder,value,isImports} = this.props;
  206. const val = value;
  207. const blueBorder = this.state.editable?style['blue-border']:'';
  208. const isSelected = val&&val!=placeholder?style['selected']:style['container'];
  209. const orgBorder = isImports&&!(val&&val!=placeholder)?style['orange-border']:'';
  210. const noTag = hideTag?style['no-tag']:'';
  211. return className(isSelected,noTag,blueBorder,orgBorder);
  212. }
  213. changeToEdit(e){ //整个标签双击编辑状态
  214. window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
  215. const {value,id,handleDbclick,patId,handleHide,show} = this.props;
  216. clearTimeout(this.state.timer);//取消延时的单击事件
  217. e.preventDefault();
  218. if(show){
  219. handleHide&&handleHide();
  220. }
  221. if(value&&value.trim()) {//有选中值的标签才能双击编辑
  222. this.setState({
  223. editable: true
  224. });
  225. setTimeout(()=>{
  226. this.$cont.current.focus();
  227. })
  228. //双击埋点记录
  229. handleDbclick && handleDbclick({id:patId||id});
  230. }
  231. }
  232. handleBlur(e){ //双击编辑blur
  233. const {handleLabelChange,ikey,boxMark,value} = this.props;
  234. this.setState({
  235. editable: false
  236. });
  237. let totalVal = e.target.innerText.trim();
  238. let changeVal = this.$span.current.innerText.trim();//数字框值-修改后;去掉前空格避免多空格叠加
  239. let prefix = this.$pre.current.innerText.trim(); //前缀值-修改后
  240. let suffix = this.$suf.current.innerText.trim(); //后缀值-修改后
  241. // console.log('数字框:'+changeVal,";全部:"+totalVal,";前缀:"+prefix+";后缀:"+suffix);
  242. handleLabelChange && handleLabelChange({ikey,changeVal,type:boxMark,totalVal,prefix,suffix});
  243. }
  244. getSpanClass(){ //将被替换的文字选中状态显示
  245. const cls = this.props.show?style['blued']:'';
  246. return cls;
  247. }
  248. stopBubble(e){
  249. e.stopPropagation();
  250. }
  251. componentDidMount(){
  252. //设置最小宽度避免输入后宽度跳动
  253. const spanWidth = window.getComputedStyle(this.$span.current).width;
  254. this.$span.current.style.minWidth=spanWidth;
  255. //保存输入框dom以便聚焦
  256. const {saveDoms} = this.props;
  257. saveDoms&&saveDoms(this.$span);
  258. }
  259. render(){
  260. const {prefix,suffix,show,value,handleHide,allClick} = this.props;
  261. const {placeholder,editable,hasSelect,boxTop,boxLeft} = this.state;
  262. //console.log(prefix,value,placeholder+'1',this.props.placeholder)
  263. return <div className={this.getClasses()}
  264. ref={this.$cont}
  265. onDblClick ={this.changeToEdit}
  266. onClick={allClick?this.handleNumClick:null}
  267. contentEditable={editable}
  268. onBlur={this.handleBlur}
  269. onKeyDown={handleEnter}
  270. onFocus={this.stopBubble.bind(this)}>
  271. <span ref = {this.$pre}>&nbsp;{prefix}</span>
  272. <span onFocus={this.handleNumFocus}
  273. onClick={allClick?null:this.handleNumClick}
  274. contentEditable={true}
  275. style={{minWidth:'10px',display:'inline-block',textAlign:'center'}}
  276. ref = {this.$span}
  277. onKeyDown={this.handleKeyDowm}
  278. onBlur={this.numInpBlur}
  279. onInput={this.handleSpanInp}
  280. className={this.getSpanClass()}
  281. >&nbsp;{value||placeholder}</span>
  282. <span ref = {this.$suf}>&nbsp;{suffix}</span>
  283. <NumberPan handleSelect={this.select.bind(this)}
  284. onClose={handleHide}
  285. show={show}
  286. toClear={!hasSelect}
  287. left={boxLeft}
  288. top={boxTop}/>
  289. </div>
  290. }
  291. }
  292. export default NumberDrop;