index.jsx 13 KB

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