index.jsx 16 KB

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