美文网首页
2019-10-09课件管理功能部分注意事项

2019-10-09课件管理功能部分注意事项

作者: 菩灵 | 来源:发表于2019-10-10 20:01 被阅读0次
    • 删除操作的时候需要考虑到查询列表,要在删除后,根据查询列表的数据发起请求,才能正确显示


      删除后错误显示方式
    • 新建课件部分,存在文件重复问题,刷新后改善,需要解决;快速点击保存文件,可同时重复上传多份同一文件,需要节流,保存后先销毁再返回

    • 文件格式

      • 图片:"image/*"
      • PDF:"application/pdf"
      • 视频:"video/*"
    • 在react中模板字符串需要用{}包起来:action={`${config.domain}/file`}

    • 点击课件名称显示课件信息包含预览功能,点击编辑的时候无预览功能,而只有文件展示与替换功能

    • 预览PDF使用<embed type="application/pdf" src={pdfUrl} width="100%" height="800px" />

    • 预览视频使用import Player from 'griffith';

    • 培训管理新增培训按钮分两个业务逻辑处理,类似于:指挥中心---任务中心

    • 课件管理的查看也类似于:公众举报与评价---公众举报

    • 修改请求和接收的字段:

    POST发送的newValue:
    {
      coursewareFileName: "PDF测试文件4.pdf"
      coursewareFileUid: "rc-upload-1570667615043-8"
      coursewareName: "哈法"
      coursewareSourceUrl: "http://www.yuntsoft.com:7389/group1/M00/01/94/rBDZEl2efb6AAvQuAACwHeXRbq0429.pdf"
      coursewareType: 0
    }
    GET得到的后端数据response.records[i]:
    {
      coursewareFileName: null
      coursewareFileUid: null
      coursewareId: 64
      coursewareName: "噶"
      coursewareSourceUrl:         "https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png"
      coursewareType: 0
      coursewareTypeName: "文件"
      createUserId: null
      createUserName: "市场监管"
      dele: 0
      edit: null
      gmtCreate: "2019-10-09 15:07:12"
    }
    
    • 新增课件、编辑课件、查看课件区别处理
      1、编辑课件和查看课件都是modal,需要传入handleModalVisible方法,并且最外层标签改成modal
      2、编辑课件的初始值来自record,接收之后需要进一步处理
      3、经测试发现QueueAnim动画效果在模态框中无效,故注释掉
      4、去掉返回按钮,把返回按钮的功能给取消,去掉保存按钮,把保存按钮的功能给确定
      5、父组件中对于编辑功能的支持{updateVisible && <UpdateCourseware modalVisible={updateVisible} {...updateHandle} />}
      6、从record传过来的数据在componentDidMount中进行初始化
      7、别忘了表单上的initialValue值要重新设置;
      8、modal标签上的属性设置注意:点击遮罩层不关闭maskClosable="false"和显示隐藏由父组件的状态控制,子组件默认一直显示visible="true"
      9、注意:fileIds上的值也要初始化否则会报错,此时的fileIds全局定义不合理
      10、执行PUT方法的时候需要url传递coursewareId,否则无法成功请求
      11、给模态框设置destroyOnClose的话会影响文件上传
      12、重要:一个完整的请求配置:
    1、@connect与model建立连接
    // 引入dva中的connect函数
    import { connect } from 'dva';
    @connect(({ courseware, loading }) => ({
      courseware,
      loading,
    }))
    2、在业务处理中发送dispatch请求,注意请求的是命名空间下的update方法
      /**
       * 更新
       * @param params
       */
      handleUpdate = params => {
        const { dispatch } = this.props;
        console.log('调用主页面更新方法')
        dispatch({
          type: 'courseware/update',
          payload: params,
          callback: this.handleUpdateCall,
        });
      };
    3、model中的update是一个异步generator函数,注意请求了一个updateCourseware的函数
        /**
         * 更新模板
         * @param payload
         * @param callback
         * @param call
         * @param put
         * @returns {IterableIterator<*>}
         */ *update({ payload, callback }, { call }) {
          const response = yield call(updateCourseware, payload);
          if (response.resultCode === 0) {
            notification.success({ message: '更新成功!' });
          } else {
            notification.error({ message: '更新失败!', description: response.resultMsg });
          }
          if (callback && typeof callback === 'function') {
            callback(response);
          }
        },
    4、在model的文件开头从services中引入updateCourseware
    import {
      queryCoursewareList,
      addCourseware,
      deleteCourseware,
      updateCourseware
    } from '../services/courseware'
    5、在services文件中暴露出异步请求方法
    export async function updateCourseware(params) {
      return request(`${config.domain}/courseware`, {
        method: 'PUT',
        body: params,
      });
    }
    

    至此,一个完整的请求动作可以正常进行
    注意主页面的dispatch动作命名空间下的异步方法名要和model一致,model执行请求的函数名要和service文件一致,model文件必须引入service文件暴露的函数。
    只要任何一个步骤不对,轻则无法发送请求,重则dispatch(...).then is not a function
    13、为防止Radio单选框值改变,文件类型不变导致错误提交的bug,需要在handleRadioClick函数中将state中的fileList和全局的fileIds置空
    14、日期选择框

    import moment from 'moment'; // 引入时间处理模块
    import { DatePicker } from 'antd'
    const { RangePicker } = DatePicker;
    
                    <RangePicker
                      style={{ width: '80%' }}
                      ranges={{
                        "今天":[moment(),moment()]
                      }}
                      disabledDate={(current) => {
                        console.log(current)
                        return current && current < moment().subtract(1, "days")
                      }}
                      showTime={{
                        hideDisabledOptions: true,
                        defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('11:59:59', 'HH:mm:ss')],
                      }}
                    />
    disableDate属性禁用日期:
    handleData(time){
            if(!time){
                return false
            }else{
            // 大于当前日期不能选 time > moment()
            // 小于当前日期不能选 time < moment().subtract(1, "days")
            // 只能选前7后7 time < moment().subtract(7, "days") || time > moment().add(7, 'd')
                return time < moment().subtract(7, "days") || time > moment().add(7, 'd')
            }
        }
     
    <RangePicker  disabledDate={this.handleData}/>
    

    15、点击增加一行的改写


    image.png

    或者:


    image.png

    16、modal以及Table上的属性,如果是布尔值,需要用{}而不是用"",比如bordered={false}

    17、当无法通过表单双向绑定验证数据的时候可以用if判断代替,用notifaction.warning提示

          if(fieldsValue.templateName === undefined || fieldsValue.templateName === null || fieldsValue.templateName === ""){
            notification.warning({
              message: "模板名称不能为空",
            });
            return;
          }
    

    18、在<Row>标签的包裹下,用<Col>标签将<FormItem>包裹起来即可实现一行显示,响应式属性: md: 8, lg: 24, xl: 48 ,sm:24(响应式栅格有24份)

    19、多重判断:initialValue:deptCanteenSelectList && deptCanteenSelectList.length > 0 ? deptCanteenSelectList[0].deptId : undefined使用逻辑运算符+三元表达式

                    当前审核状态:
                    {auditStateNum === '0' || auditStateNum === null
                      ? '待提交'
                      : auditStateNum === '1'
                      ? '待审核'
                      : auditStateNum === '2'
                      ? '已通过'
                      : auditStateNum === '3'
                      ? '未通过'
                      : '-'}
    

    如果前两项条件都为真才返回第三项:

    {(auditStateNum === '0' || auditStateNum === null || auditStateNum === '3') && getUserInfo('perms').includes('cookbook:add') && (
         <Button type="primary" onClick={this.handleOnClick}>
        提交审核
        </Button>
    )}
    

    多重逻辑运算符,flag最后取到一个布尔值
    const flag = highRiskFood[index] !== undefined && highRiskFood[index].length > 0

    20、整行删除技巧:

        // 行删除
        const {foodList} = this.state;
        const index = foodList.findIndex(item => row.key === item.key);
        foodList.splice(index,1);
    

    21、新增培训--->培训内容部分思路:Table中的数据保存到state中,点击保存的时候进行校验,如果菜单太长可以notification.info整个界面提示

        form.validateFields((err,fieldsValue) => {
            if (err) return;
            if(foodList.length ===0){
              notification.warning({
                message: "菜谱目录不能为空",
              });
              return;
            }
            handleAdd(this.handleAddData(foodList,fieldsValue,nowAddData));
          },
        );
    

    提交的时候可以对不需要的数据直接使用delete方法:

        foodList.forEach(item =>{
          const food = {...item};
          food.dishId = fieldsValue[`dishId${item.key}`].substring(fieldsValue[`dishId${item.key}`].indexOf('id:')+3); // 菜品id
          food.employeeId = fieldsValue[`employeeId${item.key}`].substring(fieldsValue[`employeeId${item.key}`].indexOf('id:')+3); // 负责厨师id
          delete food.dishName;
          dishFoodDTOList.push(food);
        });
    

    点击添加课件按需弹出相应模态框,通过子组件传值保存到父组件的courseList中,父组件校验courseList不需要form方法,可以直接校验
    子组件向父组件传值,需要父组件先传递一个方法过去,子组件触发方法将值作为参数传入,父组件调用该方法将值保存到自己state中,比如子组件handleSubmit直接调用this.handleAddCourseware,到父组件中接收传入的值this.setState

    当新建课件完成之后,新建可见被课件名称取代,可以考虑使用components 覆盖默认的 table 元素

    针对Radio.Group组件,form.getFileDecorator方法定义的名称不允许相同,否则radio的值会互相影响大

    新增课程Table区域的DataSource生成主要依靠新增课程这个按钮的add事件,在事件每点击一次DataSource

    • 双向绑定的方法名称form.getFieldDecorator而不是form.getFileDecorator

    删除按钮针对的也是dataSource上的操作,每点击一下删除一行

    • 点击新建课件之后,从子组件获取到课件的id保存到父组件state中的ids数组;此时新建课件变成了课件名称;当再次点击的时候,子组件再向父组件传值,父组件可以用新值替代旧值,杜绝实际课件Id不一致的bug
    • 如果点击了之后不作修改,保存的时候就没有数据添加进去,所以需要在点击的时候把现有的ids传过去,哪怕为空值,但是为空值的话,校验就会发生问题。。。
    • 只要关心子组件返回来的数据即可,可以加入逻辑判断,如果有返回数据,就覆盖,没有返回数据不覆盖,最后校验的时候仍然是有值可选的

    22、总结:新增一个模态框组件的完整流程

    • 新增模态框子组件的流程
      1、当前pages文件夹下新建子组件文件:CreateCourseByWare.js
      2、主页面中引入import CreateCourseByWare from '/CreateCourseByWare'
      3、主页面render的return的最后一个标签前加入可视判断{createByWareVisible && ( <CreateCourseByWare modalVisible={createByWareVisible} {...createByWareHandle} /> )}
      4、主页面的state中保存可视判断createByWareVisible :false
      5、主页面的render方法中const { createByWareVisible } = this.state
      6、主页面的render方法中定义传递给modal的对象(包含方法,父组件的数据等)
        /**
         * 定义传递给子组件的对象
         * */
        const createByWareHandle = {
          handleAdd: this.handleCreateByWare,
          handleModalVisible: () => {
            this.setState({ createByWareVisible: false });
          },
        };
    

    7、主页面组件创建之后(和render平级)定义处理子组件的方法:

      /**
       * 根据现有课件增加
       * @param params
       */
      handleCreateByWare = params => {
        const { dispatch } = this.props;
        dispatch({
          type: 'courseware/add',
          payload: params,
          callback: this.handleCreateByWareCall,
        });
      };
    
      /**
       * 根据现有课件增加之后的回调函数
       * @param res
       */
      handleCreateByWareCall = res => {
        if (res.resultCode === 0) {
          const { dispatch } = this.props;
          const { formValues,nowSelectCanteen } = this.state;
          dispatch({
            type: 'cookBookConfig/fetchCookBookList',
            payload: { ...formValues,deptId:nowSelectCanteen },
            callback: this.handleCookBookListCallBack,
          });
          this.setState({ createVisible: false });
        }
      };
    

    8、子组件开始书写,首先头部引入modal组件import { Modal } from 'antd'

    • 个别纯查看信息的modal,确定和取消按钮都绑定handleModalVisible事件

    9、在主页面给控制子组件可视的元素绑定事件

      /**
       * 点击按需添加课件
       * */
      addCoureWare = () => {
        const { trainType, createByWareVisible, createBySelfVisible } = this.state
        if(trainType === 0){ // 从现有课件添加
          this.setState({
            createByWareVisible:true,
          })
        }else{
          this.setState({
            createBySelfVisible:true,
          })
        }
      }
    

    10、子组件制作页面

        <Modal
          width={800}
          maskClosable={false}
          // destroyOnClose
          title="添加课件"
          visible={modalVisible}
          onCancel={() => handleModalVisible()}
          // footer={footerButton}
        >
          写点内容
        </Modal>
    

    11、子组件render函数中从父组件接收所需属性和方法

        const {
          modalVisible,
          handleModalVisible,
          form,
        } = this.props;
    

    12、注意:modal上面的onOK和onCancel属性接收的是一个回调函数,必须加括号表示执行了,否则只传入一个方法是不会自动执行的

            onCancel={() => handleModalVisible()}
            onOk={() => this.okHandle()}
    

    13、上传课件增加培训数据流程:

    • 课程列表courseList,课程对象courseObj
      1、每次点击增加课程的时候组织出一个课程对象,赋值key,其余置空,push到courseList中
        const courseObj = {
          key : indexNum, // 每一门课程的key
          courseName : "", // 每一门课程的名称
          coursewareName : "", // 课程对应的课件名称啊
          coursewareId : "" // 课程对应的课件的Id
        };
    

    2、每次点击删除的时候传入row,根据row.key找到所在对象的index值,splice掉整个对象

        const index = courseList.findIndex(item => row.key === item.key);
        courseList.splice(index,1);
    

    3、对特定项点击添加课件的时候,传入row,根据row得到当前项的key值,key需要将后台传回来的coursewareName和coursewareId以及当前页面的courseName组合在一起生成当前想的courseObj,并且替换掉courseList光有key的那一项,splice(index,Obj),只要用map方法遍历courseList数组,找到item.key === current.key的一项,进行相应项的改写即可

    4、获取课程名称部分,通过form.getFileDecorator绑定courseName$(record.key),在handleSubmit的时候通过foreach遍历赋值

    5、提交校验
    自定义校验:

    绑定的时候规则置空
                  {form.getFieldDecorator('content', {})(
                    <BraftEditor
                      controls={controls}
                      extendControls={extendControls}
                      onChange={this.handleChange}
                      style={{ border: '1px solid #bfbfbf' }}
                    />
                )}
    校验的时候取值做判断
          if (fieldsValue.content.isEmpty()) {
            message.error('资讯内容不能为空');
            return;
          }
          if (tags.length === 0) {
            message.error('标签不能为空');
            return;
          }
          if (fieldsValue.newsTitle.length > 30) {
            message.error('资讯标题过长,30字符以内');
            return;
          }
    

    6、添加培训学员部分可参考:检测管理——样品管理——采样食堂
    使用treeSelect标签,值需要用递归获取,

    7、 时间数据的传递:不能把moment对象直接POST,而是要先格式化
    trainStartTime:values.trainTime[0].format('YYYY-MM-DD HH:mm:ss'), // .d trainEndTime:values.trainTime[1].format('YYYY-MM-DD HH:mm:ss'), // .d

    相关文章

      网友评论

          本文标题:2019-10-09课件管理功能部分注意事项

          本文链接:https://www.haomeiwen.com/subject/fubwpctx.html