使用react做管理后台项目遇到的问题和难点

使用react做管理后台项目, 一点点感悟与问题
1.全选翻页问题
场景描述: 对一个table列表数据进行操作, 将选中的数据加到另一个列表中, 并可以对源列表进行全选和查询操作.
问题描述:当使用全选功能, 对数据进行翻页, 会导致原来选中的数据被清空, 原因是antd的全选按钮, 只选择到了当前页的数据, 当切换之后又是另一页数据了.
解决方案一: 将列表数据变成二维数组, 实现多页操作, 并将数据扁平化之后加到需要添加进去的列表中, 关键代码如下:

在这里插入图片描述
说明: 在state中有以下值:
propsParameter.selectedRowKeys , //存放被选中项列表(二维数组)
propsParameter.onItem, // 扁平化后的列表数据, 用于在后一个列表展示数据
list:[], 源数据列表
在后一个列表删除数据的关键代码如下:
在这里插入图片描述
基本实现需求, 但仍然存在问题, 查询之后会删除被选择的值, 原因是翻页问题导致.查询时当前一般只有一页,选中之后按页码来增删数据,当前查询数据会覆盖原来的第一页数据.
解决方案二: 穿梭框方案
在这里插入图片描述
在这里插入图片描述

总结: 使用穿梭框基本上就实现了业务需求, 但由于没有分页功能, 数据加载可能会相对有影响,如果之后有更好的方式,再补上.

  1. 路由跳转问题
    在菜单内部实现跳转时,会出现数据未加载问题.原因是内部切换菜单时没有触发model层的入口函数, 导致触发请求来获取数据.
    解决方案: 写一个有生命周期的子组件, 通过子组件的componentDidMount来触发父组件, 从而触发请求数据的接口. 因为子组件的componentDidMount函数会在每一次进入时触发.
    总结: 这个解决方式有些投机取巧, 借助有生命周期的子组件来实现接口请求触发,不是一个很明智的选择.

  2. 名称过长导致的样式问题
    解决方式: word-break:break-all; white-space:normal

  3. 调用浏览器的打印问题
    调用浏览器的打印: window.print(),
    设置打印样式: @media print{}

5.关于树形结构的问题
场景需求: 在选择战区省份城市时,需要动态加载城市下的经销商数据, 并且编辑时需要回显被选中的经销商。
解决方案: antd的tree树形选择组件
具体实现方式请查看这篇文章 antd的tree树形组件异步加载数据小案例

6.关于前端数据处理问题
场景需求:点击新增按钮之后新增一个问题, 其中包含题目、类型和选项等,需要对问题进行删减,对选项进行删减上下移动操作,并且选项中可以包含图片。
view.jsx
//页面方法

    //状态值变化
  function updateModel(value, name, modHierarchy) {
    if(name==='attendType'){
      updateModel(0, 'attendBenefit', 'attendInfo')
    }
    let obj = modelObj;
    if (modHierarchy) {
      modHierarchy = modHierarchy.split(".");
      if(modHierarchy&&modHierarchy.length > 0){
        modHierarchy.map(e => {
          obj = obj[e];
        });
      }
    }
    obj[name] = value;

    // 分发到model
    dispatch({
      type: `${namespace}/updateModel`,
      payload: obj
    });
  };
 //更换questions问题集合专用方法
  const changeQuestion = (value, ind, wrapLabel, label, deepLabel)=> {
    let ques = cloneDeep(modelObj[wrapLabel]);
    ques.map((item,index)=> {
      if(index === ind){
        item[label] = value;
        if(value == 3 && label == 'questionType'){
          item.options.length = 1;
        }
      }  
    })
    updateModel(ques, wrapLabel);
  }
  // 修改指定问题的选项
  const changeOptions = (value, quIndex, opIndex, label) => {
    let ques = cloneDeep(modelObj['questions']);
    
    ques[quIndex].options[opIndex][label] = value;

    updateModel(ques, 'questions');

  }
  //问卷中的图片上传处理
  const changeOptionImg =(info, quIndex, opIndex, label) => {
    let fileUrl = '';
    if (info.file.status === 'done') {
      message.success('上传成功');
      fileUrl = info.file.response.data;
    }
    changeOptions(fileUrl, quIndex,opIndex, label);
  }
  //添加问题
  const addQuestion = () => {
    dispatch({
      type: `${namespace}/addQuestion`,
      payload: {
      }
    });
  }

  //删除问题
  const removeQuestion = (index) => {
    dispatch({
      type: `${namespace}/removeQuestion`,
      payload: {
        index
      }
    });
  }
  //添加奖项
  const addPrizes = () => {
    dispatch({
      type: `${namespace}/addPrizes`,
      payload: {
      }
    });
  }
 //删除奖项
  const removePrizes = (index) => {
    if(modelObj.prizes.length>2){
      dispatch({
        type: `${namespace}/removePrizes`,
        payload:{
          index
        }
      });
    }else{
      message.error('奖项配置不能少于两个!')
    }
  }

//页面渲染

 <Button type='primary' onClick={() => addQuestion()}>新增问题</Button>
              {
                modelObj.questions && modelObj.questions.map((item,index) => {
                    return (
                      <Card
                      extra={<Icon onClick={() => removeQuestion(index)} type="close-circle" theme="filled" style={{fontSize:'20px',color:'#fff',cursor:'pointer'}}/>}
                       title={`问题${index+1}`} key={"Index_" + index}>
                        <RadioGroup value = {item.questionType} name="radiogroup" onChange = {e=> changeQuestion(e.target.value, index, 'questions', 'questionType')}>
                          <Radio value = {1}>单选</Radio>
                          <Radio value = {2}>多选</Radio>
                          <Radio value = {3}>填空</Radio>
                        </RadioGroup>
                        <div>
                          <div className="ant-form-item-label">
                            <label>题目</label>
                          </div>
                          <Input style={{ width: 200 }} value= {item.questionTitle} onChange = {(e)=> changeQuestion(e.target.value, index, 'questions','questionTitle')}/>
                        </div>
                        <div>
                          <div className="ant-form-item-label">
                            <label>是否必填</label>
                          </div>
                          <Switch checked = {item.required === 1} onChange = {(checked) =>{let num = checked ? 1:0; changeQuestion(num, index, 'questions','required')} }/>
                          {item.questionType===2 &&
                            <div>
                              <div className="ant-form-item-label">
                                <label>最少选择数量</label>
                              </div>
                              <InputNumber min={1} style={{width:100}} value= {item.choiceMinNum}   onChange ={(value) => changeQuestion(value, index,'questions','choiceMinNum')}/>
                              <div className="ant-form-item-label" style={{marginLeft:20}}>
                                <label>最多选择数量</label>
                              </div>
                              <InputNumber min={1} style={{width:100}}  value= {item.choiceMaxNum} onChange ={(value) => changeQuestion(value,index, 'questions','choiceMaxNum')}/>
                            </div>
                          }
                        </div>
                        {
                          item.options.map((tem,dex) => {
                            return (
                              <div className = {styles.optionStyle} key={`index_${dex}`}>
                                {
                                  item.questionType!==3 && <div>
                                      <div className="ant-form-item-label">
                                      <label>{`选项${dex+1}`}</label>
                                      </div>
                                      <Input style={{ width: 200,marginRight:10 }} value= {tem.optionTitle} onChange ={(e) => changeOptions(e.target.value, index, dex, 'optionTitle')}/>
                                      <Upload
                                          className={styles.avatarUploader}
                                          showUploadList={false}
                                          listType="picture-card"
                                          headers = {{ token: Cookie.get(config.cookie.auth), acnt: Cookie.get(config.cookie.account) }}
                                          action= {`${config.baseURL}/changan/mgmt/upload/single/image`}
                                          beforeUpload={(file)=>beforeUpload(file,2)}
                                          onChange={(info)=>changeOptionImg(info,index, dex,'imageUrl')}
                                        >
                                        {
                                          tem.imageUrl?
                                          <img src={tem.imageUrl} alt="" className={styles.avator} /> :
                                          <div className={styles.avatarUploaderTrigger}>
                                            <Icon type="plus" />
                                            <div style={{"fontSize":12}}>上传</div>
                                          </div>
                                        }
                                      </Upload>
                                      <span className={styles.smallLabel}>
                                        <span><Icon type="plus-circle" theme="outlined" onClick = {()=> addOptions(index, item.options.length)}/></span>
                                        <span><Icon type="minus-circle" theme="outlined" onClick={()=> deleteOptions(index,dex,tem)}/></span>
                                        <span><Icon type="arrow-up" theme="outlined" onClick={()=>upOptions(index,dex)} /></span>
                                        <span ><Icon type="arrow-down" theme="outlined" onClick={()=>downOptions(index,dex)} /></span>
                                      </span> 
                                      <p style={{color:'#cbcbcb'}}>说明:图片建议尺寸:220x170px, 大小小于200kb, 仅支持jpg、png格式</p>
                                  </div>
                                }
                               {
                                  item.questionType === 3 && <div>
                                    <div>
                                      <div className="ant-form-item-label">
                                        <label>最少字数</label>
                                      </div>
                                      <InputNumber min={1} style={{ width: 200 }} value= {tem.leastLength} onChange = {(value) => changeOptions(value, index, dex, 'leastLength')}/>
                                      
                                      <div className="ant-form-item-label" style={{marginLeft:20}}>
                                        <label>最多字数</label>
                                      </div>
                                      <InputNumber min={1} style={{ width: 200 }} value= {tem.maxLength} onChange = {(value) => changeOptions(value, index, dex, 'maxLength')}/>
                                    </div>
                                  </div>
                                }
                              </div>
                            )
                          })
                        }
                         {
                            item.questionType!==3 && <div><Button icon='plus' onClick = {()=> addOptions(index, item.options.length)}>添加选项</Button></div>
                         }
                      </Card>
                    )
                  })
                }

//mod.js(model层)

//添加问题
    *addQuestion({ payload }, { put, call, select }) {
      let { questions } = yield select(e => e[tmpModule.namespace]);
      console.log('questions-->',questions.length+1);
      let tempObj = {
        questionSeq:questions.length+1, //问题排序
				questionType: 1, //问题类型:1.单选题,2.多选题.3.填空题
				questionTitle: "", //问题标题
				required: 0, //问题必填:0非必填,1必填
				choiceMinNum: '', //最少选择数量
        choiceMaxNum: '', //最多选择数量
				options: [//选项集合
					{
						optionTitle: "", //选项标题
            imageUrl: "",//图片
            optionSep:1,//排序
						localTitle:"选项1",//选项描述
						inputTips: "",//输入提示
						leastLength: '',//最少字数
						maxLength: '',//最多字数
					}
				]
			}
      questions.push(tempObj);
      yield put({type: 'store',payload: {questions} });  
    },
  //删除问题
    *removeQuestion({ payload }, { put, call, select }) {
      let { questions } = yield select(e => e[tmpModule.namespace]);
      questions.splice(payload.index,1);
      yield put({type: 'store',payload: {questions} });
    },
  //添加问卷中的选项
    *addOption({payload}, {put,call, select}) {
      let { questions, optionId} = yield select(e => e[tmpModule.namespace]);

      let {options} = questions[payload.index];
      console.log('options=--->', options);
      optionId++;

      payload.obj.optionId = optionId;

      // console.log('OptionId-->', optionId)
      
      options.push(payload.obj)
      yield put({
        type: 'store',
        payload: {
          questions,
          optionId
        }
      });     
    },
   //删除问卷中的选项
    *deleteOptions({payload}, {put,call, select}) {
      let { questions, optionId} = yield select(e => e[tmpModule.namespace]);
      let {queIndex,OpIndex,optionsItem} = payload;

      questions.filter((item,index)=>{
        if(index+''===queIndex+''){
          //return item.options[OpIndex].optionSep === optionsItem.optionSep
          if(item.options.length===1){
            return message.error('至少保留一个选项!');
          }else{
            return item.options.splice(OpIndex,1);
          }
        }
      })
      
      yield put({
        type: 'store',
        payload: {
          questions
        }
      });     
    },
    //上移问卷中的选项
    *upOptions({payload}, {put,call, select}) {
      let { questions} = yield select(e => e[tmpModule.namespace]);
      let {queIndex,OpIndex} = payload;

      questions.filter((item,index)=>{
        if(index+''===queIndex+''){
          item.options[OpIndex-1].optionSep = OpIndex+1;
          item.options[OpIndex].optionSep = OpIndex;

          let tmp = item.options[OpIndex];
          item.options[OpIndex] = item.options[OpIndex-1];
          item.options[OpIndex-1] = tmp;
          //return item.options.splice(OpIndex,1);
        }
      })

      yield put({
        type: 'store',
        payload: {
          questions
        }
      });  
    },
  //下移问卷中的选项
    *downOptions({payload}, {put,call, select}) {
      let { questions} = yield select(e => e[tmpModule.namespace]);
      let {queIndex,OpIndex} = payload;
      
      questions.filter((item,index)=>{
        if(index+''===queIndex+''){
          item.options[OpIndex].optionSep = OpIndex+2;
          item.options[OpIndex+1].optionSep = OpIndex+1;

          let tmp = item.options[OpIndex];
          item.options[OpIndex] = item.options[OpIndex+1];
          item.options[OpIndex+1] = tmp;
          //return item.options.splice(OpIndex,1);
        }
      })

      yield put({
        type: 'store',
        payload: {
          questions
        }
      });  
    },
    //新增奖项
    *addPrizes({ payload }, { put, call, select }) {
      let { prizes } = yield select(e => e[tmpModule.namespace]);
      let tempObj = {
        prizeId: "1", //奖品id
				prizeType: 2, //奖项类型:1.积分,2.礼品.3.抽奖次数,4谢谢参与
				prizeName: '', //奖品名称(赠送积分/礼品名称/赠送次数)
				prizeNumber: '', //奖品数量
				numerator: '',//中奖概率
				seq:1,//奖品排序
				prizeTips: '',//中奖提醒
				prizeImageUrl:'',//奖品图片
        prizeDelivery:1, //礼品发放方式:1验证码,2邮寄
        useBeginDatetime:'', //核销开始时间
        useEndDatetime:'', //核销结束时间
        attendIntegralName:'',//积分名称
        attendIntegralCode:'',//积分编码
			}
      prizes.push(tempObj);
      yield put({type: 'store',payload: {prizes} });     
    },
    //删除奖项
    *removePrizes({ payload }, { put, call, select }) {
      let { prizes } = yield select(e => e[tmpModule.namespace]);
      prizes.splice(payload.index,1);
      yield put({type: 'store',payload: {prizes} });
    },

猜你喜欢

转载自blog.csdn.net/qq_37210523/article/details/83418780