import React,{Component} from 'react'; import style from './index.less'; import config from "@config/index"; import {filterArr,isIE,getPageCoordinate,filterDataArr,preventDefault,setFontColorSize,moveEnd} 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:标签内文字 * * ****/ let indexNum = ''; 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.handleClick = this.handleClick.bind(this); this.selectStart = this.selectStart.bind(this); this.selectEnd=this.selectEnd.bind(this); } handleFocus(e){ e.stopPropagation(); const that = this; let txt = ''; //黏贴时去掉html格式 $(this.$span.current).on("paste",function(e){ setTimeout(function(){ txt = that.$span.current.innerText||that.$span.current.innerHTML; that.$span.current.innerHTML = txt; moveEnd($(that.$span.current)[0]); //光标落到最后去 }); }) 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,noSearch,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.innerHTML = labelVal; //innerHTML兼容FF26 e.target.blur(); 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}); if(noSearch){ return; } //延迟搜索 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(config.regPreAndAft,''); if(!temp&&searchPre&&newText){ search = searchPre; } 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){//为了阻止冒泡事件 e.stopPropagation(); //解除绑定事件 $(this.$span.current).off("paste"); } 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&&range.anchorNode.length; let preObj = $(this.$span.current).prev(); if(ev.keyCode==37){//向左 let preObj = $(this.$span.current).prev(); let obj = preObj[0]&&preObj[0].nodeName=="DIV"?preObj.prev():preObj; if(textIndex == 0){ preventDefault(ev); if(obj){ obj[0]&&this.moveEnd(obj[0]); } } } if(ev.keyCode==39){//向右 let curObj = $(this.$span.current) let nextObj = $(this.$span.current).next(); let obj = nextObj[0]&&nextObj[0].nodeName=="DIV"?nextObj.next():nextObj; if(textIndex == textLength || textLength==undefined || (textIndex == 0 && textLength==1)){ preventDefault(ev); if(obj){ obj[0]&&obj.focus(); } } } } handleKeyup(e){ const {boxMark,handleKeydown,removeId,handleClear,removeSpan,handleChange,select_end} = 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(); // ||textIndex ==innerVal.length&&preObj[0].nodeName=="DIV" let range = window.getSelection(); let textIndex = range.focusOffset; let nextObj = $(this.$span.current); let nexObj = $(this.$span.current).next(); let nexObjN = nexObj.next(); let preObj = $(this.$span.current).prev(); let nexVal = nexObj[0]&&nexObj[0].innerText || nexObj[0]&&nexObj[0].innerHTML; 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(); this.moveEnd(nextObj[0],1) }else{ this.moveEnd(preObj[0]) } } //action里往后删除 if(innerVal == preVal){ let data = innerVal.trim(); if(nextObj && !config.punctuationReg.test(data) || data=='
'){ handleKeydown&&handleKeydown({boxMark,i:index,text:data,flag:'del'}); if(nextObj && nextObj[0] && nextObj[0].nodeName !=="DIV"){ // IE浏览器focus光标在最后,其他浏览器在最前 // nextObj.focus(); if(boxMark==2&&this.props.data[index+1]&&this.props.data[index+1].flag==3){ this.moveEnd(nexObjN[0],1); }else{ this.moveEnd(nextObj[0],1); } } /*this.setState({ index: null })*/ }else if(indexNum ==innerVal.length&&nexObj[0]&&nexObj[0].nodeName=="DIV"){//span最后一位后面为div标签 handleKeydown&&handleKeydown({boxMark,i:index+1,text:data,flag:'del'}); this.moveEnd(nextObj[0]); }else if(nexObj[0]&&nexObj[0].nodeName=="SPAN"){ if(nexVal.length <= 1){//为空或者一个字delete时候 removeId && removeId({boxMark,i:index+1,text:"",flag:'del'}); handleClear && handleClear({boxMark}); this.moveEnd(nexObj[0]) }else if(nexVal.length > 1){ let tmpVal = nexVal.substr(1,data.length) handleChange&&handleChange({text1:tmpVal,boxMark,i:index+1}); nexObj.html(tmpVal) this.moveEnd(nexObj[0],1) } } } } if(ev.keyCode==8&&!select_end){ // 主诉现病史去重:删除最后一个字的时候移除该数据(将name、id和value替换成空)并移除id // 前面是标签,内容为空时再删一次才移除标签;前面是文本,则直接移除; let preObj = $(this.$span.current).prev(); let curObj = $(this.$span.current); 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]); } } let range = window.getSelection(); let textIndex = range.focusOffset; 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)){ // 后半段是处理IE if(index!==0 && !config.punctuationReg.test(data) || index!==0 && data=='
'||indexNum == 0&&preObj[0]&&preObj[0].nodeName=="DIV"){ // 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 }) }else if(textIndex == 0&&preObj[0]&&preObj[0].nodeName=="SPAN"){ let data = preObj[0].innerText || preObj[0].innerHTML; if(data.length == 1){//span只有一个文本,删除后移除检索 removeId && removeId({boxMark,i:index-1,text:""}); handleClear && handleClear({boxMark}); if(preObj[0].nodeName !=="DIV"){ if(boxMark==2){ this.moveEnd(preObj[0],1); }else{ this.moveEnd(preObj[0]); } } }else{ let tmpVal = data.substr(0,data.length-1) handleChange&&handleChange({text1:tmpVal,boxMark,i:index}); preObj.html(tmpVal) if(preObj[0].nodeName !=="DIV"){ this.moveEnd(preObj[0]); } } } } // 主诉使用模板删除最后一个空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||''); } } handleClick(e){ $(this.$span.current).attr({"contentEditable":true}).focus() let range = window.getSelection(); let textIndex = range.focusOffset; indexNum = textIndex } componentDidMount(){ const {value} = this.props; if(value){ this.$span.current.innerText?(this.$span.current.innerText = value||''):(this.$span.current.innerHTML = value||''); } } selectStart(){ const {i,setSelectArea,boxMark}= this.props; setSelectArea({i,boxMark,dir:'start'}); } selectEnd(){ const {i,setSelectArea,boxMark}= this.props; setSelectArea({i,boxMark,dir:'end'}); } getClass(){ const {full,value,saveText,i,preIsExt,afterIsExt,mouseSelect} = this.props; const br = preIsExt&&!afterIsExt; //最后一个体征标签 const preSelected = saveText[i-1]; const isFull = full?' '+style['full']:''; //是否宽度设为整行宽度 //有标点符号之外的字符或者前一个标签有选中值时,显示为黑色,否则查体中,有体征标记显示蓝色,否则灰显 const ext = preIsExt?style['ext']:style['unselect']; const unselect = value.match(config.punctuationReg)||preSelected?'':ext; const hasBr = br?style['editable-br']:''; //最后一个体征标签换行 const selectedArea = mouseSelect?style['selected-area']:''; const $span = this.$span.current; if(br&&$span&&!$span.innerText){ this.$span.current.innerHTML=' '; } return classNames(style['editable-span'],isFull,unselect,hasBr,setFontColorSize(2,6),selectedArea); } render() { return ; } } export default EditableSpan;