ソースを参照

合并maindropSearch分支
Squashed commit of the following:

commit c29feeaf12293c8dd6b3f35916952a3690fbbc7c
Author: liucf <liucf@zjlantone.com>
Date: Thu Sep 26 13:35:20 2019 +0800

先处理搜索选中逻辑

commit 3ad9384c82a0e3efba239191f5767076bc06f25f
Author: liucf <liucf@zjlantone.com>
Date: Thu Sep 26 10:26:33 2019 +0800

细节优化

commit a56800bf6bc81386e1e991c779c98f547e47046e
Author: liucf <liucf@zjlantone.com>
Date: Wed Sep 25 17:35:50 2019 +0800

增加字数限制,已经存值到localStorage

commit f6d39f7b4aef620fbc674e610903f805f2bd692a
Author: liucf <liucf@zjlantone.com>
Date: Wed Sep 25 15:15:26 2019 +0800

主诉-添加症状增加搜索功能-未完成

liucf 5 年 前
コミット
2f72ad43d8

BIN
src/common/components/SearchBox/imgs/clear.png


BIN
src/common/components/SearchBox/imgs/search.png


+ 162 - 0
src/common/components/SearchBox/index.jsx

@@ -0,0 +1,162 @@
+import React from 'react';
+import styles from './index.less';
+import clear from './imgs/clear.png';
+import search from './imgs/search.png';
+import config from '@config/index';
+import classNames from 'classnames';
+import SearchDrop from '@components/SearchDrop';
+import ScrollArea from 'react-scrollbar';
+/**
+ * 主诉“添加症状”下拉中的:搜索框(含结果下拉)
+ * 接收参数:
+ * show:上级下拉是否隐藏,用于componentWillReceiveProps中
+ * cliIndex:上级点击标签的index(如“添加症状”),用于选中数据插入的位置标识;
+ */
+class SearchBox extends React.Component {
+    constructor(props) {
+        super(props);
+        this.state = {
+          showBox:false, //显示搜索结果
+          border:'',
+          showClear:false, //显示清空icon
+          timer:null
+        }
+        this.textInput = React.createRef();
+        this.handleClearVal = this.handleClearVal.bind(this);
+        this.handleFocus = this.handleFocus.bind(this);
+        this.handleBlur = this.handleBlur.bind(this);
+        this.clickIcon = this.clickIcon.bind(this);
+        this.handleSearchSelect = this.handleSearchSelect.bind(this);
+        this.reset = this.reset.bind(this);
+    }
+    handleClearVal(){
+        const { clearSearch } = this.props;
+        this.textInput.current.value = '';
+        this.textInput.current.focus();
+        this.setState({
+          showClear:false
+        });
+        clearSearch&&clearSearch();
+    }
+    handleSearchSelect(e,item){
+      e.stopPropagation();
+      e.preventDefault();
+      const {cliIndex,chooseSearch,clearSearch,onSelect} = this.props;
+      chooseSearch&&chooseSearch({item:item,index:cliIndex});
+      onSelect();//上一级的“确定”事件
+      clearSearch&&clearSearch();
+      this.textInput.current.value = "";
+    }
+    handleInput(e){
+      e.stopPropagation();
+      e.preventDefault();
+      const { getSearchData ,mainIds} = this.props;
+        clearTimeout(this.state.timer);
+        let timer = setTimeout(()=>{
+            clearTimeout(this.state.timer);
+            if(e.target.value.trim() == ''){
+              this.setState({
+                showClear:false
+              })
+              return
+            }
+            this.setState({
+              showClear:true
+            })
+            getSearchData && getSearchData({inpStr:e.target.value.replace('<br>',''),boxMark:1,itemType:0,mainIds:mainIds});
+        },config.delayTime);
+        this.setState({
+          timer
+        });
+    }
+    handleFocus(e){//聚焦时边框变蓝色
+      e.stopPropagation();
+      e.preventDefault();
+      this.setState({border:true})
+    }
+    handleBlur(e){
+      e.stopPropagation();
+      e.preventDefault();
+      this.setState({border:false})
+    }
+    clickIcon(){
+      this.setState({showBox:true})
+      setTimeout(()=>{
+        this.textInput.current.focus();
+      },0)
+    }
+    // 重置事件
+    reset(){
+      const { clearSearch } = this.props;
+      clearSearch();
+      this.textInput.current.value = "";
+      this.setState({
+        showBox:false,
+        border:false,
+        showClear:false
+      })
+    }
+    componentDidMount(){
+      this.props.onRef(this);
+    }
+    componentWillReceiveProps(next){
+      // 隐藏时,清空搜索框内文字,清空搜索结果,隐藏搜索框
+      const { clearSearch } = this.props;
+      if(!next.show && next.show != this.props.show){
+        this.reset();
+      }
+    }
+    render() {
+        const { mainSearchData } = this.props;
+        const { showClear ,border, showBox} = this.state;
+        const showBd = showBox?styles['borderNone']:'';//显示边框
+        const borderCor = border?styles['border']:''; //蓝色边框
+        const isShow = showBox?styles['show']:styles['hide']; //是否显示输入框
+        let litext = '';
+        const contStyle={
+          opacity:'0.4',
+          right:'0',
+          top:'1px',
+          zIndex:'15',
+          width:'14px',
+          background:'#f1f1f1'};
+        const barStyle={background:'#777',width:'100%'};
+        return (
+            <div className={classNames(styles['search'])} onClick={(e)=>{e.stopPropagation();}} onBlur={(e)=>{e.stopPropagation();}}>
+                <img className={styles.searchVal} src={search} alt="搜索" onClick={this.clickIcon}/>
+                <img style={{display:showClear?'block':'none'}} className={styles.clearVal} src={clear} onClick={this.handleClearVal} alt="清空" />
+                <input
+                    className={classNames(isShow,borderCor,showBd)}
+                    type="text"
+                    maxLength="30"
+                    ref={this.textInput}
+                    onFocus={this.handleFocus}
+                    onBlur={this.handleBlur}
+                    onInput={(e) => { 
+                      this.handleInput(e)
+                    }}
+                    onPropertyChange={(e) => {  // 兼容ie
+                      this.handleInput(e)
+                    }}
+                />
+                {mainSearchData&&mainSearchData.length>0 ? <div className={styles.autoList}>
+                    <ScrollArea speed={0.8}
+                      horizontal={false}
+                      stopScrollPropagation={mainSearchData.length>6?true:false}
+                      style={{maxHeight:'225px'}}
+                      verticalContainerStyle={contStyle}
+                      verticalScrollbarStyle={barStyle}
+                      contentClassName="content">
+                      <ul>
+                        {mainSearchData&&mainSearchData.map((it)=>{
+                          litext = it.showType==1?it.name:it.name+'('+it.retrievalName+')';
+                          return <li key={it.conceptId} onClick={(e)=>this.handleSearchSelect(e,it)} title={litext}>{litext}</li>
+                        })}
+                      </ul>
+                    </ScrollArea>
+                  </div>:''}
+            </div>
+        )
+    }
+}
+export default SearchBox;

+ 54 - 0
src/common/components/SearchBox/index.less

@@ -0,0 +1,54 @@
+@import "~@less/variables.less";
+
+.search {
+    .contentZIndex1;
+    width: 316px;
+    padding: 8px;
+    box-sizing: border-box;
+    position: relative;
+    background-color: #fff;
+    .autoList {
+      position: absolute;
+      width: 300px;
+      background: #fff;
+      border: 1px solid #ccc;
+       ul{
+        li:hover{
+          border: 1px solid #3B9ED0;
+        }
+      } 
+    }
+    input {
+        width: 100%;
+        height: 34px;
+        line-height: 34px;
+        padding: 0 32px;
+        box-sizing: border-box;
+    }
+    input::ms-clear{
+        display: none;
+    }
+    .border {
+        border: 1px solid @blue !important;
+    }
+    .borderNone {
+        border: 1px solid #979797;
+    }
+    img {
+        position: absolute;
+        top: 15px;
+    }
+    .searchVal {
+        left: 18px;
+    }
+    .clearVal{
+        cursor: pointer;
+        right: 18px;
+    }
+}
+.show {
+    display:block;
+}
+.hide {
+    display: none;
+}

+ 3 - 1
src/common/components/index.js

@@ -24,6 +24,7 @@ import DelToast from "./DelToast";
 import TailInlineTag from "./TailInlineTag";
 import Footer from "./Footer";
 import WrapModalContainer from "./WrapModalContainer";
+import SearchBox from "./SearchBox";
 
 module.exports = {
     Banner,
@@ -52,5 +53,6 @@ module.exports = {
     MiniToast,
     TailInlineTag,
     Footer,
-    WrapModalContainer
+    WrapModalContainer,
+    SearchBox
 };

+ 1 - 1
src/components/MainSuit/index.jsx

@@ -163,7 +163,7 @@ class MainSuit extends Component{
       }
       ev.target.blur();
       ev.target.innerText?(ev.target.innerText = data.substr(0,config.limited)):(ev.target.innerHTML = data.substr(0,config.limited));  //输入法内输入多个字再按enter的情况
-      console.log(333,data,ev)
+      //console.log(333,data,ev)
       // ev.target.blur();
       this.setState({
         inpText:data.substr(0,config.limited),

+ 37 - 9
src/components/SpreadDrop/index.jsx

@@ -5,6 +5,7 @@ import style from './index.less';
 import {setPosition,deepClone,filterArr,handleEnter,isIE,windowEventHandler,filterDataArr,getIds,getPageCoordinate} from '@utils/tools.js';
 import {Notify} from '@commonComp';
 import ScrollArea from 'react-scrollbar';
+import SearchBox from '@containers/SearchBox'
 import $ from 'jquery';
 /****
  * 标签组合下拉,选中的项目展开
@@ -200,7 +201,7 @@ class SpreadDrop extends Component{
     this.clearState();
   }
   handleConfirm(e){
-    e.stopPropagation();
+    // e.stopPropagation();
     const {handleConfirm,ikey,type,tagType,order,mainSaveText,copyType,value,mainData} = this.props;
     const params = Object.assign({},this.state,{ikey,type,tagType,order,mainSaveText,copyType,value,mainData});
     delete params.tmpDom;       //避免上面deepClone selecteds报错
@@ -359,8 +360,9 @@ class SpreadDrop extends Component{
     }
   }*/
   render(){
-    const {placeholder,value,show,data,order} = this.props;
+    const {placeholder,value,show,data,order,type,tagType,ikey} = this.props;
     const {tmpDom,left} = this.state
+    const clickIndx = ikey.split('-')[1];//展开下拉的index
     if(!show&&tmpDom){
       $(tmpDom).parent().prev().attr({"contentEditable":true})
     }
@@ -379,8 +381,8 @@ class SpreadDrop extends Component{
         onInput={this.onChange}
         onkeydown={handleEnter}
         >{value||placeholder}</div>
-          <ListItems parDiv={this.$list} data={data} order={order} left={left}
-             show={show} handleSelect={this.handleSelect} handleConfirm={this.handleConfirm} handleClear={this.handleClear} {...this.state}></ListItems>
+          <ListItems parDiv={this.$list} data={data} order={order} left={left} boxMark={type} tagType={tagType}
+             show={show} cliIndex={clickIndx} handleSelect={this.handleSelect} handleConfirm={this.handleConfirm} handleClear={this.handleClear} {...this.state}></ListItems>
       </div>
   }
 }
@@ -389,6 +391,7 @@ class ListItems extends Component{
   constructor(props){
     super(props);
     this.$cont = React.createRef();
+    this.clickConfirm = this.clickConfirm.bind(this);
   }
   getLabels(){
     const {data,handleSelect} = this.props;
@@ -416,6 +419,11 @@ class ListItems extends Component{
     });
     return list;
   }
+  clickConfirm(){
+    const {handleConfirm} = this.props;
+    this.child&&this.child.reset();//重置搜索框中的数据
+    handleConfirm();   
+  }
   getStyle(){
     const {show,left} = this.props;
     return {
@@ -424,14 +432,19 @@ class ListItems extends Component{
     }
   }
   render (){
-    const {handleClear,handleConfirm,order,parDiv} = this.props;
+    const {handleClear,handleConfirm,order,parDiv,boxMark,tagType,show,cliIndex} = this.props;
     return <div className={style["drop-list"]} ref={parDiv} style={this.getStyle()} contentEditable="false" onClick={(e)=>{e.stopPropagation();}}>
         <p className={style['orderTips']}>按{order?'从左到右从上到下':'点击'}顺序成文</p>
         {this.getLabels()}
         <div className="oper clearfix">
           <span className={style['clear']} onClick={handleClear}>清空选项</span>
-          <span className={style['confirm']} onClick={handleConfirm}>确定</span>
+          <span className={style['confirm']} onClick={this.clickConfirm}>确定</span>
         </div>
+        
+        {boxMark==1 && tagType==11 && <div className="search">
+                  <div className={style["line"]}></div>
+                  <SearchBox show={show} cliIndex={cliIndex} onSelect={handleConfirm} onRef={(child)=>{this.child = child;}}/>
+                </div>}
       </div>
   }
 }
@@ -526,11 +539,26 @@ class ListItem extends Component{
       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>
     });
   }
-  render(){
+
+  getMainData(){//主诉添加症状-带搜索框
     const {datas,isSpecialPos} = this.props;
+    return datas&&datas.map((it,i)=>{
+      return <li onClick={(e)=>this.handleClick(e,it,i)} 
+            className={this.getClass(it.id)} 
+            title={it.name.length>4?it.name:''}
+            style={{'width':'55px','display':'inline-block'}}>
+            {it.name&&it.name.length>4?it.name.slice(0,4)+'...':it.name}
+          </li>
+    });
+  }
+
+  render(){
+    const {datas,isSpecialPos,boxMark,tagType,listIndex} = this.props;
     const pos = isSpecialPos?style['independent']:'';
-    return  <ul className={classNames(style['row'],pos)} onBlur={(e)=>e.stopPropagation()}>
-      {this.getData()}
+    const ifMainSear = boxMark==1 && tagType==11?true:false;
+    const main = ifMainSear&&listIndex==1?style['mainUl']:'';//伴字ul不设置宽度
+    return  <ul className={classNames(style['row'],pos,main)} onBlur={(e)=>e.stopPropagation()}>
+      {ifMainSear?this.getMainData():this.getData()}
     </ul>
   }
 }

+ 13 - 0
src/components/SpreadDrop/index.less

@@ -20,6 +20,19 @@
     width: 100%;
     border-bottom: 1px @disable-border-color solid;
   }
+  .mainUl{
+    width: 495px;
+    white-space: normal;
+    li{
+      width: 99px !important;
+    }
+  }
+  .line{
+    width: 100%;
+    height: 1px;
+    background: #EAEDF1;
+    margin: 20px 0 0 10px;
+  }
   li{
     padding-left: 20px;
     cursor: pointer;

+ 68 - 0
src/containers/SearchBox.js

@@ -0,0 +1,68 @@
+import {connect} from 'react-redux';
+import SearchBox from '@commonComp/SearchBox';
+import {getSearch} from '@store/async-actions/fetchModules.js';
+import {CLEAR_ADD_SEARCH,SET_ADD_SEARCH,CHOOSE_SEARCH} from '@store/types/mainSuit';
+import {ISREAD} from '@store/types/homePage.js';
+import {billing} from '@store/async-actions/pushMessage';
+import {Notify} from '@commonComp';
+import {filterDataArr} from '@utils/tools.js';
+import store from '@store';
+import config from '@config/index.js';
+
+function mapStateToProps(state){
+  return{
+    mainSearchData:state.mainSuit.addSearchData,//主诉-添加症状-搜索结果
+    mainIds:state.mainSuit.mainIds,  //搜索去重id
+  }
+}
+
+function mapDispatchToProps(dispatch){
+  return{
+    // 主诉-添加症状-搜索
+    getSearchData:(item)=>{
+      getSearch(item).then((res)=>{
+          let result = res.data;
+          if(+result.code == 0){
+            let data = result.data;
+            dispatch({
+              type:SET_ADD_SEARCH,
+              data:data
+            })
+          }else{
+            console.log(result.msg);
+          }
+      });
+    },
+    chooseSearch(obj){
+      // 主诉字数限制
+      let state = store.getState();
+      let mainSaveText = state.mainSuit.saveText;
+      let text = filterDataArr(mainSaveText);
+      let total = text.length + obj.item.name.length + 1; //+1是因为顿号
+      if(total >= config.limited){
+        Notify.info(config.limitText);
+        return
+      }
+      dispatch({
+        type: CHOOSE_SEARCH,
+        item:obj.item,
+        index:obj.index
+      })
+      dispatch({    //自由文本标签数据更新
+        type:ISREAD
+      });
+      //右侧推送
+      setTimeout(function(){ 
+        dispatch(billing());
+      },200);
+    },
+    clearSearch:()=>{
+      dispatch({
+        type: CLEAR_ADD_SEARCH
+      })
+    },
+  }
+}
+
+const SearchBoxContainer = connect(mapStateToProps,mapDispatchToProps)(SearchBox);
+export default SearchBoxContainer;

+ 49 - 0
src/store/actions/mainSuit.js

@@ -403,6 +403,55 @@ export const setSearch = (state,action)=>{
   res.isEnd = action.isEnd;
   return res;
 }
+// 主诉-添加症状-选中搜索结果
+export const chooseSearch = (state,action)=>{
+  const res = Object.assign({},state);
+  const index = parseInt(action.index);
+  const item = action.item;
+  let data = res.data;
+  let text = {id:item.questionId,name:item.name,value:item.name,tagType:config.tagType,conceptId:item.conceptId};
+  // 判断index前是saveText中是否有伴
+  let nText = {};
+  if(!data[index].pos){//第一病程
+    const preText = res.saveText.slice(0,index);
+    const ind = preText.indexOf("伴");
+    if(ind != -1){
+      nText = Object.assign({},text,{exist:2});
+    }else{
+      nText = Object.assign({},text,{exist:1});
+    }
+  }else{//第二及以上病程
+    nText = Object.assign({},text);
+  }
+
+  // 缓存到localStorage中
+  const mainSymp = storageLocal.get('mainSymp');
+  if(mainSymp){
+    let localArr = JSON.parse(mainSymp);
+    let sympArr = JSON.parse(JSON.stringify(localArr));
+    for(let k=0; k<localArr.length; k++){//判断是否已存在
+      if(localArr[k].conceptId==item.conceptId){
+        sympArr.splice(k,1);
+      }
+    }
+    sympArr.push(nText);
+    if(sympArr.length>5){
+      storageLocal.set('mainSymp',sympArr.slice(sympArr.length-5,));
+    }else{
+      storageLocal.set('mainSymp',sympArr);
+    }
+  }
+  // 插入data中
+  // res.data.splice(index,0,nText);
+  res.data.splice(index,0,Object.assign({},nText,{name:'、'+item.name,value:'、'+item.name}));
+  res.saveText.splice(index,0,'、'+item.name);
+  res.mainIds.push(item.conceptId);
+  if(item.questionId){
+    res.mainTailIds.push(item.questionId);
+  }
+  res.update=Math.random();
+  return res;
+}
 
 //将选中的搜索结果插入
 export const insertSearch = (state,action)=>{

+ 12 - 3
src/store/reducers/mainSuit.js

@@ -2,11 +2,11 @@ import {RECOVER_TAG_MAIN,COMM_SYMPTOMS,CLEAR_COMSYMPTOMS,SHOW_TAIL,INSERT_MAIN,
   SET_SEARCH,CLEAR_SEARCH,GET_BIGDATAPUSH,SET_MAINSUIT,MIX_CONFIRM,NUMBER_SELECT,
   RADIO_SELECT,COMM_CONFIRM,CHANGE_LABELVAL,SAVE_FREE,CLEAR_MAIN_SUIT,SET_DATA,
   INSERT_SEARCH,MAIN_FOCUS_INDEX,SETTEXTMODEVALUE,SETMAINTEXT,MAINADDLABELITEM,SETMAININPUT,DEL_MAIN,CHANGE_LABELVAL_NUMBER,
-  REMOVE_MAIN_ID,MAINSUIT_MUL,DEL_MAIN_LABLE,SET_FEATURE,SET_MS_RADIO_INPUT_VAL,SAVE_CHRONIC,MAIN_REMOVE_SPAN} from '../types/mainSuit'
+  REMOVE_MAIN_ID,MAINSUIT_MUL,DEL_MAIN_LABLE,SET_FEATURE,SET_MS_RADIO_INPUT_VAL,SAVE_CHRONIC,MAIN_REMOVE_SPAN,SET_ADD_SEARCH,CLEAR_ADD_SEARCH,CHOOSE_SEARCH} from '../types/mainSuit'
 import {recoveTag,getCommSymptoms,handleTailClick,insertMain,setSearch,getBigSymptom,setMainMoudle,confirm,
   setNumberValue,setRadioValue,commConfirm,changeLabelVal,saveFreeVal,clearMainSuit,insertSearch,setTextModeValue,setCheckText,
   addLabelItem,setInputLabel,backspaceText,changeNumLabelVal,removeId,multipleComfirn,delSingleLable,
-  getSymptomFeature,setRadioInputValue} from '../actions/mainSuit'
+  getSymptomFeature,setRadioInputValue,chooseSearch} from '../actions/mainSuit'
 
 
 const initState = {
@@ -29,7 +29,8 @@ const initState = {
   },
   chronicDesease:null, //慢病
   mainTailIds:[],  //获取症状尾巴用
-  mainReadSonM:[] //回读的子模板
+  mainReadSonM:[], //回读的子模板
+  addSearchData:[] //添加症状里的搜索
 }
 
 export default function(state=initState,action){
@@ -118,6 +119,14 @@ export default function(state=initState,action){
         res.editClear = true;
       }
       return res;
+    case SET_ADD_SEARCH: //添加症状-搜索
+      res.addSearchData = action.data;
+      return res;
+    case CHOOSE_SEARCH:
+      return chooseSearch(state,action);
+    case CLEAR_ADD_SEARCH://清空症状搜索结果
+      res.addSearchData = [];
+      return res;
     default:
       return state;
   }

+ 3 - 0
src/store/types/mainSuit.js

@@ -31,3 +31,6 @@ export const SET_MS_RADIO_INPUT_VAL = 'SET_MS_RADIO_INPUT_VAL';
 export const SAVE_CHRONIC = 'SAVE_CHRONIC'; //储存慢病信息
 export const RECOVER_TAG_MAIN = 'RECOVER_TAG_MAIN';   //恢复已删除标签
 export const MAIN_REMOVE_SPAN = 'MAIN_REMOVE_SPAN';   //删除最后一个空span
+export const SET_ADD_SEARCH = 'SET_ADD_SEARCH';   //添加症状-搜索
+export const CLEAR_ADD_SEARCH = 'CLEAR_ADD_SEARCH';   //添加症状-搜索
+export const CHOOSE_SEARCH = 'CHOOSE_SEARCH';   //添加症状-搜索