import React,{Component} from 'react'; import style from './index.less'; import config from "@config/index"; import {filterArr,isIE,getPageCoordinate,filterDataArr} from '@utils/tools.js'; import Notify from '../Notify/index.js'; import classNames from 'classnames'; import $ from 'jquery'; /***** * author:zn@2018-12-10 * 自由文本输入组件 * 接收参数: * value:默认显示文字 * handleChange:输入变化时触发函数,接收以下参数: * * boxMark:病例项标识 * * i:标签index * * text:标签内文字 * * ****/ class EditableSpan extends Component{ constructor(props){ super(props); this.state={ timer:null, clearTimer:null, oldText:props.value, labelVal:'', //存放标签原有的值--主诉字数限制用 preVal:'', index:null, searchPre:'', }; this.$span = React.createRef(); this.handleFocus = this.handleFocus.bind(this); this.onChange = this.onChange.bind(this); this.handleBlur = this.handleBlur.bind(this); this.handleKeydown = this.handleKeydown.bind(this); this.handleKeyup = this.handleKeyup.bind(this); this.moveEnd = this.moveEnd.bind(this); } handleFocus(e){ e.stopPropagation(); const {mainSaveText,full,setFocusIndex,i,boxMark,value}= this.props; let mainText = filterDataArr(mainSaveText);//主诉字数 if(+boxMark==3||+boxMark==4){ //主诉为空,且第一次聚焦其他史查体时提示且不可输入 if(!mainText&&full&&(value===''||value===undefined)){ Notify.error("无法操作,请先输入主诉"); e.target.blur(); return ; } } let text = e.target.innerText || e.target.innerHTML; setFocusIndex&&setFocusIndex({i,boxMark,dom:this.$span}); this.setState({ labelVal:text, index:i, searchPre:text }); } onChange(e){ e.stopPropagation(); const {handleChange,boxMark,i,handleSearch,value,mainSaveText,mainIds,handleClear} = this.props; const {labelVal,searchPre} = this.state; const text1 = e.target.innerText || e.target.innerHTML; // e.newValue IE浏览器DOMCharacterDataModified监听 // const text1 = e.target.innerText || e.target.innerHTML || e.newValue; let mainText = filterDataArr(mainSaveText);//主诉字数 if(+boxMark==1){ if(mainText.length >= config.limited){ if(text1.length > labelVal.length){ e.target.innerText = labelVal; Notify.info(config.limitText); return }else if(text1.length == labelVal.length){ this.setState({ labelVal:text1 }); }else{ handleChange&&handleChange({text1,boxMark,i}); } return } } this.setState({ labelVal:text1 }); const that = this; handleChange&&handleChange({text1,boxMark,i}); //延迟搜索 clearTimeout(this.state.timer); const timer = setTimeout(function(){ let newText = e.target.innerText || e.target.innerHTML; let temp = '',isEnd=false; let search=''; clearTimeout(that.state.timer); temp = newText.replace(searchPre,''); isEnd = !(newText.indexOf(searchPre)>0); search = temp.replace(/[(^\s*)|(\s*$)|(^\,*)|(\,*$)]/g,''); // if(!search&&searchPre){ if(!temp&&searchPre&&newText){ search = searchPre; } //console.log(labelVal,'旧:',searchPre,'新:',newText,'搜索:',search); if(config.punctuationReg.test(search)){ //只有标点符号时不搜索 handleSearch&&handleSearch({text:search,isEnd,boxMark,mainIds}); }else{//只有标点符号时要清空搜索结果 handleClear && handleClear({boxMark}) } //搜索后保持现在的值,继续输入时要用于对比 /*that.setState({ searchPre:newText });*/ },config.delayTime); this.setState({ timer }); } handleBlur(e){//为了阻止冒泡事件 const {boxMark,handleClear,handleChange,i} = this.props; e.stopPropagation(); // 延时清空搜索结果,不延时会影响选中 clearTimeout(this.state.clearTimer); const clearTimer = setTimeout(function(){ handleClear && handleClear({boxMark}) },config.delayTime); this.setState({ clearTimer }); } moveEnd(obj) { if(window.getSelection){//ie11 10 9 ff safari obj.focus(); //解决ff不获取焦点无法定位问题 var range = window.getSelection();//创建range range.selectAllChildren(obj);//range 选择obj下所有子内容 range.collapseToEnd();//光标移至最后 } else if (document.selection) {//ie10 9 8 7 6 5 var range = document.selection.createRange();//创建选择对象 range.moveToElementText(obj);//range定位到obj range.collapse(false);//光标移至最后 range.select(); } } handleKeydown(e){ const ev = e||window.event; const {i} = this.props; const target = ev.target||ev.srcElement; let innerVal = target.innerText || target.innerHTML,ele,boxTop; //禁止回车事件 if(ev.keyCode==13){return false;} //backspace事件 和delete if(ev.keyCode==8 || ev.keyCode==46){ //用于对比backspace前后的值 this.setState({ preVal:innerVal }) } let range = window.getSelection(); let textIndex = range.focusOffset; let textLength = range.anchorNode.length; if(ev.keyCode==37&& i!=0){//向左 let preObj = $(this.$span.current).prev(); let obj = preObj[0]&&preObj[0].nodeName=="DIV"?preObj.prev():preObj; if(textIndex == 0){ if(ev.preventDefault){//阻止默认事件 ev.preventDefault(); }else{ ev.returnValue=false; } this.moveEnd(obj[0]); } } if(ev.keyCode==39){//向右 let nextObj = $(this.$span.current).next(); let obj = nextObj[0]&&nextObj[0].nodeName=="DIV"?nextObj.next():nextObj; if(textIndex == textLength || textLength==undefined){ if(ev.preventDefault){//阻止默认事件 ev.preventDefault(); }else{ ev.returnValue=false; } obj.focus(); } } } handleKeyup(e){ const {boxMark,handleKeydown,removeId,handleClear,removeSpan} = this.props; const {preVal,index} = this.state; const ev = e||window.event; const target = ev.target||ev.srcElement; let innerVal = target.innerText || target.innerHTML,ele,boxTop; // 可编辑div不支持oninput事件,用此事件替代 /*if(isIE() && innerVal != preVal){ this.onChange(ev); }*/ if(ev.keyCode==46){//delete //判断nexObj // let nextObj = $(this.$span.current).next(); let nextObj = $(this.$span.current); if(preVal.trim().length==1&& !innerVal){ removeId && removeId({boxMark,i:index,text:"",flag:'del'}); handleClear && handleClear({boxMark});//删除最后一个字时清空搜索结果,避免现病史搜索框不立即消失的情况 //如果后一个不是标签,则光标移到最前 if(nextObj && nextObj[0].nodeName !=="DIV"){ nextObj.focus(); } } //action里往后删除 if(innerVal == preVal){ let data = innerVal.trim(); if(nextObj && !config.punctuationReg.test(data)){ handleKeydown&&handleKeydown({boxMark,i:index,text:data,flag:'del'}); // nextObj.focus(); if(nextObj && nextObj[0] && nextObj[0].nodeName !=="DIV"){ nextObj.focus(); } /*this.setState({ index: null })*/ } } } if(ev.keyCode==8){ // 主诉现病史去重:删除最后一个字的时候移除该数据(将name、id和value替换成空)并移除id // 前面是标签,内容为空时再删一次才移除标签;前面是文本,则直接移除; let preObj = $(this.$span.current).prev(); if(index!==0&&preVal.trim().length==1&& !innerVal){ removeId && removeId({boxMark,i:index,text:""}); handleClear && handleClear({boxMark});//删除最后一个字时清空搜索结果,避免现病史搜索框不立即消失的情况 if(preObj[0].nodeName !=="DIV"){ this.moveEnd(preObj[0]); } } if(innerVal !== preVal){ } else{ // 中英文数字和下划线--单独删除标签 /*const reg = new RegExp("([\u4E00-\uFA29]|[\uE7C7-\uE7F3]|[a-zA-Z0-9_])"); if(index!==0 && reg.test(innerVal)){ let obj = preObj[0].nodeName=="DIV"?preObj.prev():preObj; delSingleLable && delSingleLable({boxMark,i:index}); this.moveEnd(obj[0]); this.setState({ index: null }) }*/ let data = innerVal.trim(); //判断是否为空、中英文:, 。、;,且不是第一位 // let pattern = new RegExp(/^\,?$|^\,?$|^\.?$|^\。?$|^\、?$|^\;?$|^\;?$|^\:?$|^\:?$|\s/); // if(index!==0 && pattern.test(data)){ if(index!==0 && !config.punctuationReg.test(data)){ // let preObj = $(this.$span.current).prev(); let obj = preObj[0].nodeName=="DIV"?preObj.prev():preObj; handleKeydown&&handleKeydown({boxMark,i:index,text:data,flag:'backsp'}); this.moveEnd(obj[0]); this.setState({ index: null }) } } // 主诉使用模板删除最后一个空span时移除 if(boxMark==1 && index==0 && !innerVal){ removeSpan(); } } } componentWillReceiveProps(next){ const isRead = this.props.isRead; if(next.isRead != isRead){ this.$span.current.innerText?(this.$span.current.innerText = next.value||''):(this.$span.current.innerHTML = next.value||''); } } componentDidMount(){ const {value} = this.props; const that = this; if(value){ this.$span.current.innerText?(this.$span.current.innerText = value||''):(this.$span.current.innerHTML = value||''); } if(isIE()){ // 左右移动没问题,但是输入过程中会失焦,并且有时光标在但是无法输入 // $(this.$span.current)[0].addEventListener('DOMCharacterDataModified', function(e){that.onChange(e);}, false); // 此方法会影响左右切换时光标消失 /*$(this.$span.current).onIe8Input(function(e){ this.onChange(e) },this);*/ } } /*cancelSelect(e){//双击不选中 if(document.selection&&document.selection.empty){ document.selection.empty(); }else if (window.getSelection) { var sel=window.getSelection(); sel.removeAllRanges(); } }*/ getClass(){ const {full,value,saveText,i} = this.props; const preSelected = saveText[i-1]; const isFull = full?' '+style['full']:''; //是否宽度设为整行宽度 //有标点符号之外的字符或者前一个标签有选中值时,显示为黑色,否则灰显 const unselect = value.match(config.punctuationReg)||preSelected?'':style['unselect']; return classNames(style['editable-span'],isFull,unselect); } render() { return ; } } export default EditableSpan;