• 【后台管理系统】—— 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={{ 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={{ 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={{  '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={{  '50%' }} controls="controls" autoPlay="autoplay">
                           您的浏览器不支持 video 标签。
                       </video>
                       : <img alt="产品图片" style={{  '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={{  '100%' }} src={previewImage} />
                          </Modal>
                        </div>
                      )}
                    </Form.Item>
                  </Col>
              </Row>
      </Card>
    • Table结合Modal表单

      // 表格展示一   ----   可添加/编辑 
      <Card title="轮播图商品管理" bordered={false} style=
                {{marginTop: 30}}>
            <Button
                  style={{  '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={{  '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表单

    转载请注明出处

  • 相关阅读:
    第二次冲刺 站立会议7
    第二次冲刺 站立会议6
    第二次冲刺 站立会议5
    第二次冲刺 站立会议4
    第二次冲刺 站立会议3
    第二次冲刺 站立会议2
    第二次冲刺 站立会议1
    第一次冲刺 站立会议9
    第一次冲刺 站立会议8
    第一次冲刺 站立会议7
  • 原文地址:https://www.cnblogs.com/ljq66/p/11918327.html
Copyright © 2020-2023  润新知