【后台管理系统】—— Ant Design Pro页面相关(二)

一、弹框Modal表单

 

  1. 使用Form.create()包装得到一个含有this.props.form属性的CreatForm自组件
  2. 从页面主(父)组件获得props数据和propsMethod方法
  3. return渲染一个Modal中嵌入Form包裹着多个FormItem的弹框表单
const CreateForm = Form.create()(props => {
  const { form, current, detail, imgList, introImgList, visible,
          previewVisible, previewImage, handleImgChange, handleImgRemove,
          handleImgPreview, handleImgCancel, beforeUpload, initImgList, handleFileThumb,
          introPreviewVisible, introPreviewImage, handleIntroImgChange, handleIntroImgRemove,
          handleIntroImgPreview, handleIntroImgCancel, beforeIntroUpload, handleIntroFileThumb,
          handleSubmit, handleCancel,
          handleContentChange, handleEditPreview } = props;

  const previewArr = previewImage.split('/');
  const previewType = previewArr[previewArr.length-2];
  const controls = [ 'undo', 'redo', 'separator',
    'font-size', 'line-height', 'letter-spacing', 'separator',
    'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
    'superscript', 'subscript', 'remove-styles', 'emoji',  'separator', 'text-indent', 'text-align', 'separator',
    'headings', 'list-ul', 'list-ol', 'separator',
    'link', 'separator', 'hr', 'separator',
    'media', 'separator',
    'clear'
  ];


  const { getFieldDecorator, getFieldValue, setFieldsValue } = form;
  const formLayout = {
    labelCol: { span: 5 },
    wrapperCol: { span: 16 },
  };
  const formLayoutWithOutLabel = {
    wrapperCol: { span: 16, offset: 5 }
  }

  const extendControls = [
    {
      key: 'custom-button',
      type: 'button',
      text: '预览',
      onClick: handleEditPreview
    }
  ];

  const initKeys = (detail) => {
    let defaultKeys = [];
    detail.param.forEach((val, index) => defaultKeys.push(index))
    return defaultKeys;
  }
  getFieldDecorator('keys', { initialValue: current && detail && detail.param.length ? initKeys(detail) : [0] });
  const keys = getFieldValue('keys');

  const ImgUpButton = (
    <div>
      <Icon type="plus" />
      <div className="ant-upload-text">Upload</div>
    </div>
  );

  const okHandle = () => {
    form.validateFields((err, fieldsValue) => {
      if (err) return;
      form.resetFields();
      handleSubmit(fieldsValue);
    });
  }

  const add = () => {
    const keys = getFieldValue('keys');

    if (keys.length === 5) {
      message.info('产品参数最多5个')
      return;
    }
    let nextKeys = keys;
    let nextKeyValue = keys[keys.length-1]+1;
    nextKeys = nextKeys.concat(nextKeyValue);

    setFieldsValue({
      keys: nextKeys,
    });
  };

  const remove = index => {
    const keys = getFieldValue('keys');
    let param = getFieldValue('param');
    if (keys.length === 1) {
      return;
    }
    if(param[index]){
      param.splice(index, 1)
    }
    setFieldsValue({
      keys: keys.filter((keyItem, i) => i !== index),
      param
    });
  };

  const handleUploadFn = (param) => {
    const { file } = param;

    handleImageUpload(file, 'tutorial').then(res => {
        param.success({
            url: `${setFileHost()+res}`,
            meta: {
              id: new Date().getTime(),
              loop: false,
              autoPlay: false,
              controls: true
            }
        })
    })
  }

  const handleValidateFn = (file) => {
    return file.size < 1024 * 1024 * 100
  }

  const defaultContent = (content) => {
    let contentObj = JSON.parse(BraftEditor.createEditorState(content).toRAW());
    let urlArr;
    Object.keys(contentObj.entityMap).forEach((key) => {
      if(contentObj.entityMap[key].data.url){
        urlArr = contentObj.entityMap[key].data.url.split('/')
        if(urlArr.length == 3){
          urlArr.splice(0,1);
          contentObj.entityMap[key].data.url = `${setFileHost()}`+ 'sys/' + urlArr.join('/');
        }
      }
    });
    let contentRaw = JSON.stringify(contentObj);
    return contentRaw;
}

  const initParamValue = (detail, index) => {
    let defaultParamValue = {};
    detail.param.forEach((p, i) => {
      if(i === index){
        defaultParamValue = p
      }
    })
    return defaultParamValue;
  }

  const handleParamChange = (newValue, index) => {
    let param = getFieldValue('param');
    setFieldsValue({
      param: param.map((p, i) => i == index ? newValue : p)
    });
  }

  const paramsFormItems = keys.map((k, index) => (
    <FormItem
      {...(index === 0 ? formLayout : formLayoutWithOutLabel)}
      label={index === 0 ? '参数信息' : ''}
      key={k}
    >
      {getFieldDecorator(`param[${index}]`, {
        validateTrigger: ['onChange', 'onBlur'],
        rules: [
          {
            type: 'object',
            required: true,
            validator: (_, value, callback) => {
              if (!value.key || !value.value || (value.key && value.key.length > 10) || (value.value && value.value.length > 20)) {
                callback('请输入1-10字参数及1-20参数信息或删除此输入框')
              } else {
                callback()
              }
            }
          },
        ],
        initialValue: current && detail && detail.param.length ? initParamValue(detail, index) : {}
      })(
          <ParamsInputArray keys={keys} index={index} add={add} remove={remove} onChange={handleParamChange}/>
        )}
    </FormItem>
  ));

  return (
    <Modal
      destroyOnClose
      width={1200}
      bodyStyle={{height: 750, overflow: 'auto'}}
      style={{ top: 0 }}
      title={`${Object.keys(current).length ? '编辑' : '添加'}产品`}
      visible={visible}
      keyboard={false}
      maskClosable={false}
      okText="确定"
      cancelText="取消"
      onOk={okHandle}
      onCancel={handleCancel}
    >
       <Form onSubmit={handleSubmit}>
          <FormItem label="产品名称" {...formLayout}>
            {getFieldDecorator('name', {
              rules: [{ required: true, message: '请输入5-20字产品名称', min: 5, max: 20}],
              initialValue: current && detail ? detail.name : ''
            })(<Input placeholder="请输入" />)}
          </FormItem>
          <FormItem label="产品价格" {...formLayout}>
            {getFieldDecorator('price', {
              rules: [
                { type: 'number', required: true, message: '请输入1-100000元整数产品价格'},
                { pattern: /^[0-9]*[1-9][0-9]*$/, message: '请输入1-100000元整数产品价格'}
              ],
              initialValue: current && detail ? detail.price/100 : ''
            })(<InputNumber
                min={1}
                max={100000}
                style={{width: 120}}
                placeholder="请输入"
            />)}
          </FormItem>
          <FormItem label="产品库存" {...formLayout}>
            {getFieldDecorator('stock', {
              rules: [{ type: 'number', required: true, message: '请输入5-100000产品库存', min:5, max:100000}],
              initialValue: current && detail ? detail.stock : ''
            })(<InputNumber
                min={5}
                max={100000}
                style={{width: 120}}
                placeholder="请输入"
                formatter={value => `${value}`.replace(/\B(?=(\d{3})+(?!\d))/g, ',')}
                parser={value => value.replace(/\$\s?|(,*)/g, '')}
            />)}
          </FormItem>
          <FormItem label="是否设为系统产品" {...formLayout}>
            {getFieldDecorator('fromSystem', {
              rules: [{ required: true, message: '请选择是否设为系统产品'}],
              initialValue: current && detail ? detail.fromSystem : false
            })(<Switch defaultChecked={current && detail ? detail.fromSystem : false} />)}
          </FormItem>
          <FormItem label="发布状态" {...formLayout}>
            {getFieldDecorator('publishStatus', {
              rules: [{ type: 'number', required: true, message: '请选择发布状态'}],
              initialValue: current && detail ? Number(detail.publishStatus) : ''
            })(<Select placeholder="请选择">
                <SelectOption value={0}>未发布</SelectOption>
                <SelectOption value={1}>已发布</SelectOption>
              </Select>)}
          </FormItem>
         <FormItem label="人物介绍图片" {...formLayout}>
           {getFieldDecorator('introPic', {
             initialValue: current && detail && detail.introPic
               ? [{
                 uid: '-1',
                 status: 'done',
                 name: detail.introPic,
                 url: `${setFileHost()+detail.introPic}`,
                 thumbUrl: `${setFileHost()+detail.introPic}`
               }] : ''
           })(
             <div>
               <Upload
                 accept="image/*"
                 // action={(file) => handleImageUpload(file, 'image').then(res => {
                 //    handleIntroFileThumb(res)
                 //  })}
                 listType="picture-card"
                 fileList={introImgList}
                 onPreview={handleIntroImgPreview}
                 onRemove={handleIntroImgRemove}
                 beforeUpload={beforeIntroUpload}
                 // onChange={handleIntroImgChange}
               >
                 {introImgList.length >= 1 ? null : ImgUpButton}
               </Upload>
               <Modal visible={introPreviewVisible} footer={null} onCancel={handleIntroImgCancel} style={{textAlign: 'center'}}>
                 <img alt="人物介绍图片" style={{ width: '100%' }} src={introPreviewImage} />
               </Modal>
             </div>
           )}
          </FormItem>
          <FormItem label="产品图片" {...formLayout}>
            {getFieldDecorator('rotationChart', {
              rules: [{ required: true, message: '请上传1-7张图片'}],
              initialValue: current && detail && detail.rotationChart && detail.rotationChart.length
                            ? initImgList(detail) : []
            })(
              <div>
                <Upload
                    accept="image/*"
                    // action={(file) => handleImageUpload(file, 'image').then(res => {
                    //    handleFileThumb(res, file, imgList)
                    //  })}
                    listType="picture-card"
                    fileList={imgList}
                    onPreview={handleImgPreview}
                    onRemove={handleImgRemove}
                    beforeUpload={beforeUpload}
                    // onChange={handleImgChange}
                >
                  {imgList.length >= 7 ? null : ImgUpButton}
                </Upload>
                <Modal visible={previewVisible} footer={null} onCancel={handleImgCancel} style={{textAlign: 'center'}}>
                  {previewType == 'liveWallPaper' ?
                   <video src={previewImage} style={{ width: '50%' }} controls="controls" autoPlay="autoplay">
                       您的浏览器不支持 video 标签。
                   </video>
                   : <img alt="产品图片" style={{ width: '100%' }} src={previewImage} />}
                </Modal>
              </div>
            )}
          </FormItem>
          {paramsFormItems}
          <FormItem label="产品详情" {...formLayout}>
            {getFieldDecorator('content', {
                validateTrigger: 'onBlur',
                rules: [{
                    required: true,
                    validator: (_, value, callback) => {
                      if (value.toHTML().length < 50 || value.toHTML().length > 15000000) {
                          callback('请输入50-15000000字产品详情')
                      } else {
                          callback()
                      }
                    }
                }],
                initialValue: current && detail ? BraftEditor.createEditorState(defaultContent(detail.content)) : ''
                })(
                    <BraftEditor       // 富文本插件组件
                        className="my-editor"
                        controls={controls}
                        extendControls={extendControls}
                        placeholder="请输入50-15000000字产品详情"
                        media={{
                            uploadFn: handleUploadFn,
                            validateFn: handleValidateFn,
                            accepts: {
                              image: 'image/png, image/jpeg, image/jpg, image/gif, image/webp, image/apng, image/svg',
                              video: 'video/mp4, video/ogg, video/webm',
                              audio: 'audio/mp3, audio/mp4, audio/ogg, audio/mpeg'
                            }
                        }}
                        onChange={handleContentChange}
                    />
            )}
          </FormItem>
       </Form>
    </Modal>
  );
});  

主(父)组件中<CreatForm />的使用,props数据和propsMethods方法的传递

const parentMethods = {
      initImgList: this.initImgList,
      handleFileThumb: this.handleFileThumb,
      handleImgChange: this.handleImgChange,
      handleImgRemove: this.handleImgRemove,
      handleImgPreview: this.handleImgPreview,
      handleImgCancel: this.handleImgCancel,
      handleContentChange: this.handleContentChange,
      handleEditPreview: this.handleEditPreview,
      beforeUpload: this.beforeUpload,
      handleIntroFileThumb: this.handleIntroFileThumb,
      handleIntroImgChange: this.handleIntroImgChange,
      handleIntroImgRemove: this.handleIntroImgRemove,
      handleIntroImgPreview: this.handleIntroImgPreview,
      handleIntroImgCancel: this.handleIntroImgCancel,
      beforeIntroUpload: this.beforeIntroUpload,
      handleSubmit: this.handleSubmit,
      handleCancel: this.handleCancel
}

// state数据、model层的props数据
const parentProps = {
      current,
      detail,
      imgList,
      introImgList,
      visible,
      previewVisible,
      previewImage,
      introPreviewVisible,
      introPreviewImage,
      contentVisible
}

return (
      <PageHeaderWrapper title="产品列表">
        <div className={styles.standardList}>
             //  页面展示的列表 、表格
        </div>
        <CreateForm {...parentMethods} {...parentProps}></CreateForm>
        // 其它简单Modal 直接在此处使用<Modal />
      </PageHeaderWrapper>
    );
  }
}

  

通过组件内定义方法,生成Modal弹框内容Content,直接使用<Modal />

const getModalContent = () => {
     const controls = [ 'undo', 'redo', 'separator',
        'font-size', 'line-height', 'letter-spacing', 'separator',
        'text-color', 'bold', 'italic', 'underline', 'strike-through', 'separator',
        'superscript', 'subscript', 'remove-styles', 'emoji',  'separator', 'text-indent', 'text-align', 'separator',
        'headings', 'list-ul', 'list-ol', 'separator',
        'link', 'separator', 'hr', 'separator',
        'media', 'separator',
        'clear'
     ];
     const formLayout = {
        labelCol: { span: 5 },
        wrapperCol: { span: 16 },
     };

     const handleUploadFn = (param) => {
        const { file } = param;

        const fileTypeArr = file.type.split('/');
        const fileType = fileTypeArr[0];

        if(fileType == 'video'){
          handleImageUpload(file, 'tutorialVideo').then(res => {
            param.success({
              url: `${setFileHost()+res}`,
              meta: {
                id: new Date().getTime(),
                loop: false,
                autoPlay: false,
                controls: true
              }
            })
          })
        }else{
          handleImageUpload(file, 'tutorial').then(res => {
            param.success({
              url: `${setFileHost()+res}`,
              meta: {
                id: new Date().getTime(),
                loop: false,
                autoPlay: false,
                controls: true
              }
            })
          })
        }
      }

      const handleValidateFn = (file) => {
        return file.size < 1024 * 1024 * 100
      }

      const defaultContent = (content) => {
          let contentObj = JSON.parse(BraftEditor.createEditorState(content).toRAW());
          let urlArr;
          Object.keys(contentObj.entityMap).forEach((key) => {
            if(contentObj.entityMap[key].data.url){
              urlArr = contentObj.entityMap[key].data.url.split('/')
              console.log('默认内容', urlArr);
              if(urlArr.length == 2){  //ios视频前缀yihezo
                urlArr.splice(0,1);
                contentObj.entityMap[key].data.url = `${setFileHost()}`+ 'yihezo/' + urlArr.join('/');
              }

              if(urlArr.length == 3){  //其它媒体文件前缀sys/tutorail
                urlArr.splice(0,1);
                contentObj.entityMap[key].data.url = `${setFileHost()}`+ 'sys/' + urlArr.join('/');
              }
            }
          });
          let contentRaw = JSON.stringify(contentObj);
          return contentRaw;
      }

      const extendControls = [
        {
          key: 'custom-button',
          type: 'button',
          text: '预览',
          onClick: this.handleEditPreview
        }
      ];

      return (
        <Form onSubmit={this.handleSubmit}>
          <FormItem label="教程标题" {...formLayout}>
            {getFieldDecorator('title', {
              rules: [{ required: true, message: '请输入至多10字标题', max: 10 }],
              initialValue: current && detail ? detail.title : '',
            })(<Input placeholder="请输入" />)}
          </FormItem>
          <FormItem label="教程类型" {...formLayout}>
            {getFieldDecorator('kindId', {
              rules: [{ required: true, message: '请选择类型' }],
              initialValue: current && detail ? detail.kindId : undefined
            })(
              <TreeSelect
                  dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                  treeData={typeData}
                  placeholder="请选择"
                  onChange={this.handleTypeChange}
              />
            )}
          </FormItem>
          <FormItem label="教程正文" {...formLayout}>
            {getFieldDecorator('content', {
                validateTrigger: 'onBlur',
                rules: [{
                    required: true,
                    validator: (_, value, callback) => {
                    if (value.isEmpty()) {
                        callback('请输入正文内容')
                    } else {
                        callback()
                    }
                    }
                }],
                initialValue: current && detail ? BraftEditor.createEditorState(defaultContent(detail.content)) : ''
                })(
                    <BraftEditor
                        className="my-editor"
                        controls={controls}
                        extendControls={extendControls}
                        placeholder="请输入正文内容"
                        media={{
                            uploadFn: handleUploadFn,
                            validateFn: handleValidateFn,
                            accepts: {
                              image: 'image/png, image/jpeg, image/jpg, image/gif, image/webp, image/apng, image/svg',
                              video: 'video/mp4',
                              audio: 'audio/mp3, audio/mp4, audio/ogg, audio/mpeg'
                            }
                        }}
                        onChange={this.handleContentChange}
                    />
                )}
          </FormItem>
        </Form>
      );
};
const modalFooter = done
      ? { footer: null, onCancel: this.handleDone }
      : { okText: '保存', onOk: this.handleSubmit, onCancel: this.handleCancel };


return (
      <PageHeaderWrapper title="产品列表">
        <div className={styles.standardList}>
             //  页面展示的列表 、表格
        </div>
        <Modal
            title={done ? null : `教程${current.id ? '编辑' : '添加'}`}
            className={styles.standardListForm}
            width={1200}
            style={{ top: 0 }}
            bodyStyle={done ? { padding: '56px 0' } : { padding: '28px 0 0' }}
            destroyOnClose
            visible={visible}
            keyboard={false}
            maskClosable={false}
            {...modalFooter}
        >
            {getModalContent()}
        </Modal>
</PageHeaderWrapper> ); } }

  

二、新页Card表单

                                               

 

1.布局就是简单的Card分栏,Form表单包裹FormItem表单项

2.需要注意的是提交方法handleSubmit,提交成功后router.push跳转返回到列表展示页

import router from 'umi/router';

router.push(`/newmall/goodsList?kw=${keyword}&&cp=${currentPage}`);

 

三、Card表单与表格Model表单结合

  • Card表单部分
     <Card title="基本信息" bordered={false}>
            <Row gutter={24} style={{marginTop: 5}}>
    
                 // Input输入框
                <Col xl={12} lg={12} md={24} sm={24} xs={24}>
                  <Form.Item label='分类名称'>
                    {getFieldDecorator('categoryName', {
                      rules: [
                        { required: true, message: '请输入1-5字分类名称' },
                        { min: 1, max: 5, message: '请输入1-5字分类名称' }
                      ],
                      initialValue: info && info.categoryName ? info.categoryName : ''
                    })(<Input placeholder="请输入分类名称" style={{maxWidth: 300}}  />)}
                  </Form.Item>
                </Col>
               
                // Upload上传图片
                <Col xl={12} lg={12} md={24} sm={24} xs={24}>
                  <Form.Item label='分类Logo'>
                    {getFieldDecorator('categoryLogo', {
                      rules: [{ required: true, message: '请选择分类Logo' }],
                      initialValue: info && info.categoryLogo ? [{
                        uid: '-1',
                        status: 'done',
                        name: info.categoryLogo,
                        url: `${setFileHost()+info.categoryLogo}`,
                        thumbUrl: `${setFileHost()+info.categoryLogo}`
                      }] : ''
                    })(
                      <div>
                        <Upload
                          accept="image/*"
                          action={(file) => handleImageUpload(file, 'img').then(res => {
                            this.handleFileThumb(res)
                          })}
                          listType="picture-card"
                          fileList={imgList}
                          beforeUpload={this.beforeImgUpload}
                          onRemove={this.handleImgRemove}
                          onPreview={this.handleImgPreview}
                          onChange={this.handleImgChange}
                        >
                          {imgList.length >= 1 ? null : ImgUpButton}
                        </Upload>
                        <Modal visible={previewImgVisible} footer={null} onCancel={this.handleImgCancel} style={{textAlign: 'center'}}>
                          <img alt="分类Logo" style={{ width: '100%' }} src={previewImage} />
                        </Modal>
                      </div>
                    )}
                  </Form.Item>
                </Col>
            </Row>
    </Card>
  • Table结合Modal表单

    // 表格展示一   ----   可添加/编辑 
    <Card title="轮播图商品管理" bordered={false} style=
              {{marginTop: 30}}>
          <Button
                style={{ width: '100%', marginBottom: 16 }}
                type="dashed"
                onClick={() => this.addRotation()}
                icon="plus"
          >
                新增轮播图商品
          </Button>
          <Table
                pagination={false}    //关闭分页功能
                loading={rotationLoading}
                rowKey={record => record.id}
                dataSource={rotation}
                columns={rotationColumns}
                onChange={this.handleRotationTableChange}
           />
     </Card>
    
     <RotationModal {...rotationModalMethods} {...rotationModalProps} />   // Modal表单
    
    // 表格展示二  ----  可添加/删除 
     <Card title="关联商品管理" bordered={false} style={{marginTop: 30}}>
              <Button
                style={{ width: '100%', marginBottom: 16 }}
                type="dashed"
                onClick={() => this.addProduct()}
                icon="plus"
              >
                关联商品
              </Button>
              <Table
                pagination={productPage}   //分页展示
                loading={productLoading}
                rowKey={record => record.productId}
                dataSource={productList}
                columns={productColumns}
                onChange={this.handleProductTableChange}
              />
     </Card>
    
     <ProductModal {...productModalMethods} {...productModalProps} />      // Modal表单

 


转载请注明出处

posted @ 2019-11-23 15:52  柳洁琼Elena  阅读(514)  评论(0编辑  收藏  举报