index.jsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. import React,{Component} from 'react';
  2. import classNames from 'classnames';
  3. import config from '@config/index.js';
  4. import style from './index.less';
  5. import {setPosition,deepClone,handleEnter,isIE,windowEventHandler,filterDataArr,getIds,getPageCoordinate,setFontColorSize,handleMouseUp} from '@utils/tools.js';
  6. import {Notify} from '@commonComp';
  7. import ListItems from '@components/ListItems';
  8. import CommonSymptom from '@components/CommonSymptom';
  9. import $ from 'jquery';
  10. /****
  11. * 标签组合下拉,选中的项目展开
  12. * author:zn@2018-11-21
  13. * 接收参数:
  14. * data:下拉内容
  15. * value:选中成文
  16. * placeholder:灰显文字
  17. * ikey:当前标签的index
  18. * copyType:选中后是否复制本身(仅展开类型使用)
  19. * data.selecteds:选中选项(仅展开类型使用)
  20. * show:是否显示下拉
  21. * order:成文顺序,0点选顺序,1从上到下从左到右
  22. * isImports:是否高亮显示(仅查体中使用)
  23. * type:所在模块:现病史、查体等
  24. * tagType:标签类型
  25. * id:id
  26. * flag:仅主诉中使用
  27. * isExtBlue:是否为查体生命体征标签蓝色显示
  28. *
  29. * ***/
  30. class SpreadDrop extends Component{
  31. constructor(props){
  32. super(props);
  33. const {nones,noneOn,noneIds,withOn,exists,nowOn,withs,exclusion,excluName} = deepClone(props.selecteds||[]);
  34. const initEx = this.getInitExclu();
  35. this.state = {
  36. nones:nones||'', //无,字符串拼接
  37. exists:exists||[], //主症状id
  38. excluName:excluName||initEx.excName,
  39. withs:withs||[], //伴随 id
  40. noneIds:noneIds||[],
  41. noneOn:noneOn||false, //无是否选中
  42. withOn:withOn||false, //伴是否选中
  43. nowOn:nowOn||'', //最近选中“无”还是“伴”
  44. exclusion:exclusion||initEx.excluId, //选中互斥项id
  45. timer:null, //延时,区分单击双击
  46. ban:{}, //放'伴'字段
  47. editable:false, //双击编辑
  48. labelVal:'', //存放标签原有的值--主诉字数限制用
  49. left:'auto',
  50. tmpDom:null
  51. };
  52. this.$div = React.createRef();
  53. this.$list = React.createRef();
  54. this.handleSelect = this.handleSelect.bind(this);
  55. this.clearState = this.clearState.bind(this);
  56. this.handleClear = this.handleClear.bind(this);
  57. this.handleShow = this.handleShow.bind(this);
  58. this.handleConfirm = this.handleConfirm.bind(this);
  59. this.changeToEdit = this.changeToEdit.bind(this);
  60. this.handleBlur = this.handleBlur.bind(this);
  61. this.onChange = this.onChange.bind(this);
  62. /*是否点击确定按钮标记,处理点击其他等同于确定操作bug,
  63. 如不区分确定按钮提交和点击其他提交,确定按钮提交后会多更新一次状态导致数据重复*/
  64. this.btnClickFlag = false;
  65. }
  66. handleShow(e){//单击
  67. e&&e.stopPropagation();
  68. const {ikey,handleShow,placeholder,flag,id,value,tagType,type,data,windowWidth,setHighter,CommonSymptoms} = this.props;
  69. let num = 0;//判断为五类切超出页面
  70. data && data.map((item)=>{
  71. if(item.formPosition != 1){
  72. ++num
  73. }
  74. });
  75. const listWidth = 30+$(this.$list.current).width();
  76. const wleft = getPageCoordinate(e).boxLeft;
  77. if(num >= 4 && windowWidth-wleft < listWidth){
  78. this.setState({
  79. left:windowWidth-wleft-listWidth-50
  80. })
  81. }else{
  82. this.setState({
  83. left:'auto'
  84. })
  85. }
  86. //高度超出时,增加左侧大容器padding
  87. setPosition(e,this.$list.current,setHighter);
  88. // window.event? window.event.cancelBubble = true : e.stopPropagation();
  89. this.setStateInit(); //恢复初始选中状态
  90. const that = this;
  91. this.btnClickFlag = false;
  92. clearTimeout(this.state.timer);
  93. this.state.timer = setTimeout(()=>{
  94. if (that.state.editable) {//如果处于编辑状态点击不显示下拉框
  95. return
  96. }else{
  97. document.activeElement.blur()//chrome41有效,但是失去焦点的span仍能编辑
  98. $(e.target).parent().prev().attr({"contentEditable":false})
  99. this.setState({
  100. tmpDom:e.target
  101. });
  102. handleShow&&handleShow({ikey,placeholder,flag,id,value,tagType,type,hasCommon:CommonSymptoms.length>0});
  103. }
  104. },300)
  105. }
  106. changeToEdit(e){//双击
  107. window.getSelection ? window.getSelection().removeAllRanges() : document.selection.empty();
  108. const {value,id,placeholder,handleDbclick,handleHide} = this.props;
  109. let text = e.target.innerText || e.target.innerHTML;
  110. handleHide&&handleHide(); //展开情况下双击收起
  111. // clearTimeout(this.state.timer);//取消延时的单击事件
  112. e.stopPropagation();
  113. // e.preventDefault();
  114. if(value&&value.trim()){//有选中值的标签才能双击编辑
  115. this.setState({
  116. labelVal:text,
  117. editable:true
  118. });
  119. //失焦关闭编辑状态
  120. setTimeout(()=>{
  121. e.target.focus();
  122. })
  123. handleDbclick && handleDbclick({value,id,placeholder});
  124. }
  125. }
  126. onChange(e){
  127. const {mainSaveText,ikey,type,handleLabelChange} = this.props;
  128. const {labelVal,editable} = this.state;
  129. let mainText = filterDataArr(mainSaveText);//主诉字数
  130. if(editable){//避免IE中点击标签也会触发
  131. let val = e.target.innerText || e.target.innerHTML;
  132. if(+type==1){// 主诉字数达到上限时不允许输入
  133. if(mainText.length >= config.limited){
  134. if(val.length > labelVal.length){
  135. e.target.innerText?(e.target.innerText = labelVal):(e.target.innerHTML = labelVal);
  136. Notify.info(config.limitText);
  137. return
  138. }else if(val.length == labelVal.length){
  139. this.setState({
  140. labelVal:val
  141. });
  142. }else{
  143. handleLabelChange && handleLabelChange({ikey,changeVal:val,type});
  144. }
  145. }
  146. }
  147. }
  148. }
  149. handleBlur(e){
  150. e.stopPropagation();
  151. const {ikey,type,handleLabelChange} = this.props;
  152. const {editable} = this.state;
  153. const ev = e || window.event;
  154. if(editable){
  155. // 更改标签的value值
  156. let changeVal = ev.target.innerText || ev.target.innerHTML;
  157. if(!isIE()){
  158. e.target.innerText?(e.target.innerText = ''):(e.target.innerHTML = ''); //避免出现重复输入值
  159. }
  160. handleLabelChange && handleLabelChange({ikey,changeVal,type});
  161. }
  162. this.setState({
  163. editable:false
  164. });
  165. }
  166. getInitExclu(){
  167. const {defaulted,data,value} = this.props;
  168. let excluId = '',excName='';
  169. let showDefaulted = this.ifDefault();
  170. if(showDefaulted&&defaulted&&value===undefined){
  171. const it = data[0];
  172. excluId = it.exclusionType===1?it.questionDetailList[0].id:'';
  173. excName = it.exclusionType===1?it.questionDetailList[0].name:'';
  174. }
  175. return {
  176. excluId,
  177. excName
  178. }
  179. }
  180. setStateInit(){
  181. const {nones,noneOn,noneIds,withOn,exists,nowOn,withs,exclusion,excluName,ban} = deepClone(this.props.selecteds||[]);
  182. const initEx = this.getInitExclu();
  183. this.setState({
  184. nones:nones||'',
  185. exists:exists||[],
  186. excluName:excluName||initEx.excName,
  187. withs:withs||[],
  188. noneIds:noneIds||[],
  189. noneOn:noneOn||false,
  190. withOn:withOn||false,
  191. nowOn:nowOn||'',
  192. exclusion:exclusion||initEx.excluId,
  193. ban:ban||{},
  194. });
  195. }
  196. clearState(){
  197. this.setState({
  198. nones:'',
  199. exists:[],
  200. withs:[],
  201. noneIds:[],
  202. noneOn:false,
  203. withOn:false,
  204. nowOn:'',
  205. exclusion:'',
  206. excluName:'',
  207. ban:{}
  208. });
  209. this.btnClickFlag = false;
  210. }
  211. handleClear(e){
  212. e.stopPropagation();
  213. this.clearState();
  214. }
  215. handleConfirm(e){
  216. // e.stopPropagation();
  217. const {handleConfirm,ikey,type,tagType,order,mainSaveText,copyType,value,mainData} = this.props;
  218. const params = Object.assign({},this.state,{ikey,type,tagType,order,mainSaveText,copyType,value,mainData});
  219. delete params.tmpDom; //避免上面deepClone selecteds报错
  220. handleConfirm&&handleConfirm(params);
  221. this.btnClickFlag = true;
  222. //点确定后隐藏弹窗
  223. this.props.handleHide();
  224. }
  225. handleSelect(item,isExclu,joint,listIndex,selected){
  226. let {withOn,withs,noneOn,exclusion,exists,nowOn,nones,noneIds,ban} = this.state;
  227. const id = item.id;
  228. const linkStr = joint||'';
  229. const name = item.name+linkStr;
  230. if(isExclu){ //操作“互斥项”
  231. if([...noneIds,...exists,...withs].length>0){ //已选非互斥项,清空已选项,选中该互斥项
  232. this.clearState();
  233. this.setState({
  234. exclusion:id,
  235. excluName:name
  236. });
  237. return;
  238. }
  239. //未选中互斥项,直接选中该互斥项
  240. console.log(id,isExclu,joint,listIndex,selected,exclusion,name)
  241. let temp = '';
  242. if(exclusion==''){
  243. temp = id;
  244. }else if(exclusion!=''&&exclusion!==id){
  245. temp = exclusion;
  246. }
  247. this.setState({
  248. exclusion:temp,
  249. excluName:temp ===''?'':name
  250. });
  251. return;
  252. }
  253. //操作单选项
  254. if(selected){
  255. const tIndex= exists.findIndex((it)=>it.questionId===item.questionId);
  256. const bIndex= withs.findIndex((it)=>it.questionId===item.questionId);
  257. if(tIndex!=-1){
  258. exists.splice(tIndex,1,Object.assign({},item,{name})); //修改单选列连接字符不显示bug
  259. this.setState({
  260. exists,
  261. })
  262. }
  263. if(bIndex!=-1){
  264. withs.splice(tIndex,1,item);
  265. this.setState({
  266. withs,
  267. })
  268. }
  269. return;
  270. }
  271. if(exclusion!==''){ //互斥项已选中,清空互斥项
  272. this.setState({
  273. exclusion:'',
  274. excluName:''
  275. });
  276. }
  277. if(+item.code===1){ //操作“伴”类型
  278. this.setState({
  279. withOn:!withOn,
  280. withs:withOn?[]:withs,
  281. ban:withOn?{}:{id:id,name:name,value:name},
  282. nowOn:withOn?(noneOn?'none':''):'with'
  283. });
  284. return;
  285. }
  286. if(+item.code===2){ //操作“无”类型
  287. this.setState({
  288. noneOn:!noneOn,
  289. noneIds:noneOn?[]:[...noneIds,id],
  290. nones:noneOn?'':name,
  291. nowOn:noneOn?(withOn?'with':''):'none'
  292. });
  293. return;
  294. }
  295. //操作普通项
  296. let existsIds = exists.length>0? getIds(exists):[];
  297. let withsIds = withs.length>0? getIds(withs):[];
  298. if(existsIds.includes(id)){
  299. let existsData = exists;
  300. exists.forEach((it,i)=>{
  301. if(it.id==id){
  302. existsData.splice(i,1);
  303. }
  304. })
  305. exists = existsData;
  306. }else if(noneIds.includes(id)){
  307. nones = nones.replace(name+'、','');
  308. noneIds.splice(noneIds.indexOf(id),1);
  309. }else if(withsIds.includes(id)){
  310. let withsData = withs;
  311. withs.forEach((it,i)=>{
  312. if(it.id==id){
  313. withsData.splice(i,1);
  314. }
  315. })
  316. withs = withsData;
  317. }else{ //选中普通项
  318. if(nowOn=='none'){
  319. nones += name+'、';
  320. noneIds.push(id);
  321. }else if(nowOn=='with'){
  322. withs.push({id:id,name:name,questionId:item.questionId,conceptId:item.conceptId});
  323. }else{
  324. exists.push({id:id,name:name,listIndex,questionId:item.questionId,conceptId:item.conceptId});
  325. }
  326. }
  327. this.setState({
  328. nones,
  329. noneIds,
  330. exists,
  331. withs,
  332. ban
  333. //existsName,
  334. //withsName,
  335. });
  336. }
  337. handleCommonSelect(obj){
  338. const {handleConfirm,tagType,ikey,mainData,mainSaveText,type} = this.props;
  339. const param = {tagType,ikey,exists:obj.select,type,mainData,mainSaveText,withs:[],nones:'',ban:{},noneIds:[],isCommon:true};
  340. handleConfirm&&handleConfirm(param);
  341. this.btnClickFlag = true;
  342. //点确定后隐藏弹窗
  343. this.props.handleHide();
  344. }
  345. getClass(){
  346. const {isImports,show,value,isExtBlue,defaulted,mouseSelect} = this.props;
  347. const blueBorder = this.state.editable?style['blue-border']:'';
  348. const orgBorder = isImports&&!value?style['orange-border']:'';
  349. const selectedArea = mouseSelect?style['selected-area']:'';
  350. const ext = isExtBlue?style['ext']:'';
  351. let showDefaulted = this.ifDefault();
  352. if(show){
  353. $(this.$div.current).addClass(style['borderd']);
  354. }else{
  355. $(this.$div.current).removeClass(style['borderd']);
  356. }
  357. if(value||(showDefaulted&&value===undefined&&defaulted)){
  358. return classNames(style['selected-tag'],blueBorder,setFontColorSize(),selectedArea);
  359. }
  360. if(!value){
  361. return classNames(style['tag'],orgBorder,ext,setFontColorSize(2,6),selectedArea);
  362. }
  363. return classNames(style['tag'],orgBorder,ext,setFontColorSize(1),selectedArea);
  364. }
  365. ifDefault(){
  366. const {type,otherDefault,curDefault,showVal} = this.props;
  367. const showDefaulted = ((type=='2'&&curDefault)||(type=='3'&&otherDefault))&&showVal;
  368. return showDefaulted;
  369. }
  370. handleMouseDown(){
  371. const {i,setSelectArea,boxMark}= this.props;
  372. !this.state.editable&&setSelectArea({i,boxMark,dir:'start'});
  373. }
  374. componentDidMount(){
  375. if(isIE()){
  376. $(this.$div.current).onIe8Input(function(e){
  377. this.onChange(e)
  378. },this);
  379. }
  380. }
  381. render(){
  382. const {placeholder,value,show,data,order,type,tagType,pos,defaulted,showVal,CommonSymptoms,select_start,i,boxMark} = this.props;
  383. const {tmpDom,left} = this.state;
  384. let showDefaulted = this.ifDefault();
  385. let showV = showDefaulted&&value===undefined?showVal:value; //未选中过值时展示默认选中
  386. const noPushData = (+tagType===11)&&(!data[1]||!data[1].questionDetailList||data[1].questionDetailList.length===0); //无推送数据
  387. const showCommonData = (+type===2)&&(+tagType===11)&&noPushData&&CommonSymptoms.length>0?true:false;
  388. if(!show&&tmpDom){
  389. $(tmpDom).parent().prev().attr({"contentEditable":true})
  390. }
  391. const {editable} = this.state;
  392. return <div className={style['container']}
  393. onBlur={(e)=>e.stopPropagation()}
  394. onInput={(e)=>e.stopPropagation()}>
  395. <div
  396. ref={this.$div}
  397. onClick={this.handleShow}
  398. className={this.getClass()}
  399. contentEditable={editable}
  400. onDoubleClick={this.changeToEdit}
  401. onBlur={this.handleBlur}
  402. onInput={this.onChange}
  403. onkeydown={handleEnter}
  404. onMouseUp={()=>handleMouseUp({select_start,i,boxMark})}
  405. onMouseDown={this.handleMouseDown.bind(this)}
  406. >{showV||placeholder}</div>
  407. {showCommonData&&show?<CommonSymptom data={CommonSymptoms} show={true} onSelect={this.handleCommonSelect.bind(this)} isCurrent={true} />:
  408. <ListItems parDiv={this.$list} defaulted={showDefaulted&&defaulted} pos={pos} data={data} order={order} left={left} boxMark={type} tagType={tagType}
  409. show={show} handleSelect={this.handleSelect} handleConfirm={this.handleConfirm} handleClear={this.handleClear} {...this.state}></ListItems>}
  410. </div>
  411. }
  412. }
  413. export default SpreadDrop;