index.jsx 15 KB

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