【后台管理系统】—— Ant Design Pro组件使用(一)

一、搜索Search

    

  • 搜索框
    <Search placeholder="请输入关键字" 
            defaultValue={kw && kw != 'null' ? kw : ''}
            className={styles.search} 
            onChange={() => this.handleKwChange()} 
            onSearch={(e) => this.handleSearch(e)}
    />
  • 引入工具方法:去掉收尾空格

    import {trimStr} from '@/utils/utils';
    
    // utils.js
    export function trimStr(str){
      return str.replace(/(^s*)|(s*$)/g,"");
    }
  • 搜索按钮触发搜索方法,输入内容改变自动搜索

    handleSearch = e => {
        const { dispatch } = this.props;
        const { currentPage } = this.state;
        let kw = trimStr(e);
        this.setState({ keyword : kw });
        dispatch({
          type: 'newMallOrder/fetch',
          payload: {
            currentPage,
            e: {
              keyword: kw
            },
            showCount: 10
          },
        });
      };
    
      handleKwChange = () => {
        const { dispatch } = this.props;
        const { currentPage } = this.state;
        if(event && event.target && event.target.value){
          let value = event.target.value;
          this.handleSearch(value)
        }else{
          dispatch({
            type: 'newMallOrder/fetch',
            payload: {
              currentPage,
              e: {
                keyword: null
              },
              showCount: 10
            },
          });
        }
      }

二、选择器Select & TreeSelect

        

  • 表单中嵌入Select选择器
    <FormItem>
         {getFieldDecorator('tempTypeId',{
              initialValue: 0
         })(
          <Select placeholder="请选择" style={{  '100%' }} 
                  onChange={this.handleTempType}>
              <Option value={0}>H5在线编辑</Option>
              <Option value={1}>贺卡</Option>
              <Option value={2}>海报</Option>
              <Option value={3}>壁纸</Option>
              <Option value={4}>全部</Option>
          </Select>
         )}
    </FormItem>

    选择方法:

    handleTempType = value => {
        const { dispatch } = this.props;
        const { keyword } = this.state;
    
        this.setState({
          tempType: tempTypeMap[value]
        })
        dispatch({
          type: 'temp/fetch',
          payload: {
            currentPage: 1,
            e: {
              keyword: keyword,
              subjectClass: tempTypeMap[value]
            },
            showCount: 2
          }
        });
        dispatch({
          type: 'temp/fetchType',
          payload: {
            ofClass: tempTypeMap[value]
          },
          callback: (res) => {
            if(res.code == 200){
               let typeList = res.data;  // 获取联动选择框的数据
      
               typeList.forEach((typeItem, index) => {
                  dispatch({
                    type: 'temp/fetchThirdType',
                    payload: typeItem.id,
                    callback: (res) => {
                        if(res.code == 200 && res.data.length){
                           typeList[index].list = res.data;
                        }
                    }
                  })
               })
               setTimeout(() => this.setState({ typeList }), 0)
            }
          }
        });
      }
  • 联动选择的第一个选择框的父级数据
    let ParentTypeData = [
        {
          title: 'H5在线编辑',
          value: 0,
          key: 0,
        },
        {
          title: '贺卡',
          value: 1,
          key: 1,
        },
        {
          title: '海报',
          value: 2,
          key: 2,
        },
        {
          title: '壁纸',
          value: 3,
          key: 3,
        },
        {
          title: '全部',
          value: 4,
          key: 4,
        },
      ];
  • 处理获取到的联动选择第二个选择框的数据为TreeSelect需要的数据格式
    let typeData = [];
    const typeTree = (typeList, typeData) => {
          if(typeList.length) {
            for(let i=0; i<typeList.length; i++){
              typeData[i] = {
                title: typeList[i].kind,
                value: typeList[i].id,
                key: typeList[i].id
              }
              //二级分类
              if(typeList[i].list){
                typeData[i].children = [];
                typeTree(typeList[i].list, typeData[i].children)
              }
            }
          }
      }
    typeTree(typeList, typeData);
  • 表单中嵌入TreeSelect选择器

     <FormItem label="主题类别" {...formLayout}>
             <TreeSelect
                   defaultValue={tempType == null ? 4 : tempTypeMap.indexOf(tempType)}
                   value={parentTypeId}
                   style={{display: `${editDisable ? 'none' : 'inline-block'}`,  '47%', marginRight: '6%'}}
                   dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                   treeData={ParentTypeData}
                   placeholder="请选择"
                   onChange={handleParentType}
               />
              {form.getFieldDecorator('typeIds', {
                   rules: [{ type:"array", required: true, message: '请选择主题类别'}],
                   initialValue: detail.types && detail.types.length
                                ? detail.types.map((type) => type.id)
                                : []
              })(
              <TreeSelect
                   multiple   // 多选
                   style={{ `${editDisable ? '100%' : '47%'}`}}
                   dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
                   treeData={typeData}
                   placeholder="请选择"
                   disabled={editDisable}
                   onChange={handleTypeChange}
              />
           )}
    </FormItem>

三、图片视频音频上传Upload

  • 弹框表单中上传一张图片、一个音频

         

  1. 引入upload.js中封装的handImageUpload文件上传七牛云的方法
    import { handleImageUpload } from '@/utils/upload';
    
    // 预览文件时url前面需要加七牛云服务器前缀
    // eg:  'http://fileserver.liuliu123.cn/'
    import { setFileHost } from '@/utils/utils';

    upload.js

    var qiniu = require('qiniu-js')
    import axios from 'axios';
    import configs from '@/utils/env'
    import { message } from 'antd';
    
    //七牛云上传,input, onchange事件
    export function handleImageUpload(file, type, resName) {
        // console.log(file,'handleImageUpload')
        let suffix = file.type.split('/')[1];
    
        return new Promise(function(resolve, reject){
    
            if(!file) {
                reject('file is undefined')
            }
    
            function dataURItoBlob(base64Data) {
                var byteString;
                if(base64Data.split(',')[0].indexOf('base64') >= 0)
                    byteString = atob(base64Data.split(',')[1]);
                else
                    byteString = unescape(base64Data.split(',')[1]);
                var mimeString = base64Data.split(',')[0].split(':')[1].split(';')[0];
                var ia = new Uint8Array(byteString.length);
                for(var i = 0; i < byteString.length; i++) {
                    ia[i] = byteString.charCodeAt(i);
                }
                return new Blob([ia], {
                    type: mimeString
                });
            }
    
            function randomString(len) {
                len = len || 32;
                var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
                var maxPos = $chars.length;
                var pwd = '';
                for (let i = 0; i < len; i++) {
                    pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
                }
                return pwd;
            }
    
            var reader = new FileReader();
            reader.readAsDataURL(file);
            reader.onload = function(e) {
    
                var fileBlob =  dataURItoBlob(e.target.result);
                var key;
                switch(type){
                  case 'audio':
                    key = 'sys/file/music/' + resName + '.' + suffix;
                    break;
                  case 'video':
                    key = 'liveWallPaper/' + resName + '.' + suffix;
                    break;
                  case 'tutorial': //教程文件
                    key = 'sys/tutorial/' + new Date().getTime() + randomString(5) + '.' + suffix;
                    break;
                  case 'tutorialVideo': //针对IOS富文本视频显示问题单独处理教程视频
                    key = 'yihezo/' + new Date().getTime() +randomString(5) + '.' + suffix;
                    break;
                  default:
                    key = 'user/h5/' + new Date().getTime() +randomString(5) + '.' + suffix;
                }
    
    
                var putExtra = {
                    fname: file.name,
                    params: {},
                    mimeType: ["image/png", "image/jpeg", "image/jpg", "image/gif", "image/webp", "image/apng", "image/svg",
                               "audio/mp3", "audio/mp4", "audio/ogg", "audio/mpeg",
                               "video/mp4", "video/ogg", "video/webm"]
                };
    
                var config = {
                    useCdnDomain: true,
                };
    
                if(type == 'tutorialVideo' ){  //针对IOS富文本视频显示问题单独处理教程视频
                  axios.post(configs[process.env.API_ENV]['BG_SERVER']+'/file/qiniu/token/video', {
                    key: 'yihezo/' + new Date().getTime() +randomString(5)  //入参:教程视频的key
                  }, {
                    headers: {
                      AuthorizationToken: localStorage.getItem('login_token')
                    }
                  }).then(res =>{
                    let {data} = res;
                    if(data.code == 200) {
                      let token = data.data.token;
                      let observable = qiniu.upload(fileBlob, key, token, putExtra, config)
    
                      let subscription = observable.subscribe({next(res){
                          // console.log(res, 'loading')
                        }, error(res){
                          message.error('上传失败');
                        }, complete(res) {
                          resolve(res.key)
                        }})
                      //subscription.unsubscribe() // 上传取消
    
                    } else {
                      message.error('获取七牛云token失败');
                    }
                  }).catch(error => {
                    console.error(error)
                    reject(error)
                  })
    
                }else{
                  axios.post(configs[process.env.API_ENV]['BG_SERVER']+'/file/qiniu/token', {}, {
                    headers: {
                      AuthorizationToken: localStorage.getItem('login_token')
                    }
                  }).then(res =>{
                    let {data} = res;
                    if(data.code == 200) {
                      let token = data.data.token;
                      let observable = qiniu.upload(fileBlob, key, token, putExtra, config)
    
                      let subscription = observable.subscribe({next(res){
                          // console.log(res, 'loading')
                        }, error(res){
                          message.error('上传失败');
                        }, complete(res) {
                          resolve(res.key)
                        }})
                      //subscription.unsubscribe() // 上传取消
    
                    } else {
                      message.error('获取七牛云token失败');
                    }
                  }).catch(error => {
                    console.error(error)
                    reject(error)
                  })
                }
    
            }
        })
    
    }
    View Code
  2. state中定义初始值
    fileThumb: null,      // 存储上传七牛云后返回的图片url
    fileUri: null,        // 存储上传七牛云后返回的文件url
    fileVisible: false,   // 控制预览文件的弹框是否visible  
    previewVisible: false,// 控制预览图片的弹框是否visible
    previewImage: '',     //预览要上传的图片和上传后的图片的url
    previewFile: ''       //预览要上传的图片和上传后的图片的url
  3. 弹框表单中Upload组件

    // 上传按钮
    const ImgUpButton = (
         <div>
              <Icon type="plus" />
              <div className="ant-upload-text">Upload</div>
         </div>
    );
    
    const FileUpButton = (
          <Button>
              <Icon type="upload" /> Upload
          </Button>
     )
    <FormItem label="资源图片" {...this.formLayout}>
         {
              getFieldDecorator('thumb', {
                    rules: [{ required: true, message: '请上传图片' }],
                    initialValue: current.thumb ? [{     // 默认必须是数组
                           uid: '-1',
                           status: 'done',
                           name: current.resName,
                           url: `${setFileHost()+current.thumb}`,
                           thumbUrl: `${setFileHost()+current.thumb}`
                    }] : ""                              // 无值时必须是空串
              })(
              <div>
                    <Upload
                        accept="image/*"       // 限制上传的文件类型
                        action={(file) => handleImageUpload(file, 'image').then(res => {
                            const newFileThumb = [];
                            newFileThumb.push(res);
                            this.setState({
                              fileThumb: newFileThumb,
                            })
                         })}                             // 上传七牛云后存储url
                        listType="picture-card"
                        fileList={imgList}       //  显示的图片数组
                        onRemove={this.handleImgRemove}
                        onPreview={this.handleImgPreview}
                        onChange={this.handleImgChange}
                    >
                          {imgList.length >= 1 ? null : ImgUpButton}
                    </Upload>
                    <Modal visible={previewVisible} footer={null} onCancel={this.handleImgCancel}>
                      <img alt="资源图片" style={{  '100%' }} src={previewImage} />
                    </Modal>
                  </div>
                )}
              </FormItem>
              <FormItem label="资源文件" extra={resNameError || resName == null ? <span style={{color:'#1890FF'}}>请先输入资源名称</span> : ''} {...this.formLayout}>
                {getFieldDecorator('uri', {
                  rules: [{ required: true, message: '请上传文件' }],
                  initialValue: current.uri ? [{
                    uid: '-1',
                    status: 'done',
                    name: current.uri,
                    url: `${setFileHost()+current.uri}`
                  }] : ""
                })(
                  <div>
                    <Upload
                      accept="audio/mp3, audio/mp4, audio/ogg, audio/mpeg"
                      disabled={ resNameError || resName == null ? true : false }
                      action={(file) => handleImageUpload(file, 'audio', resName).then(res => {
                          const newFileUri = [];
                          newFileUri.push(res);
                          this.setState({
                            fileUri: newFileUri
                          })
                        })}
                      fileList={fileList}
                      onRemove={this.handleFileRemove}
                      onPreview={this.handleFilePreview}
                      onChange={this.handleFileChange}
                    >
                        {fileList.length >= 1 ? null : FileUpButton}
                    </Upload>
                    <Modal visible={fileVisible} footer={null} onCancel={this.handleFileCancel} style={{textAlign: 'center'}}>
                      <audio src={previewFile} style={{  '80%' }} controls="controls" autoPlay="autoplay">
                          您的浏览器不支持 audio 标签。
                      </audio>
                    </Modal>
                </div>
           )}
    </FormItem>
    // 上传图片 使用的方法
    // 删除、预览弹框关闭、预览图片url和预览弹框打开,存储改变的图片url
    handleImgRemove = () => {
        this.setState({
          imgList: [],
          fileThumb: [''],
        })
        return true
      }
    
    handleImgCancel = () => this.setState({ previewVisible: false })
    
    handleImgPreview = (file) => {
        this.setState({
          previewImage: file.url || file.thumbUrl,
          previewVisible: true,
        });
    }
    
    handleImgChange = ({ fileList }) => this.setState({ imgList: fileList })
    
    // 上传文件 使用的方法
    // 删除、预览弹框关闭、预览文件url和预览弹框打开,存储文件的图片url
    handleFileRemove = () => {
        this.setState({
          fileList: [],
          fileUri: [''],
        })
        return true
    }
    
    handleFileCancel = () => this.setState({ fileVisible: false })
    
    handleFilePreview = (file) => {
        file.url ?
        this.setState({
          previewFile: file.url,
          fileVisible: true,
        }) :
        message.error('请先保存');
    }
    
    handleFileChange = ({ fileList }) => this.setState({ fileList })
  • 弹框表单中上传多张图片

        

  1. 引入upload.js中封装的handImageUpload文件上传七牛云的方法

    import { handleImageUpload } from '@/utils/upload';
    import {setFileHost} from '@/utils/utils';
  2. state中定义初始数据
    // 上传多张轮播图(可上传视频)
    imgList: [],
    fileThumbs: [],
    previewVisible: false,
    previewImage: '',
        
    //  上传一张图片
    introImgList: [],
    introFileThumb: '',
    introPreviewVisible: false,
    introPreviewImage: '',
  3. showModal显示弹框的方法中: 处理获得的图片数组存入state

    showEditModal = item => {
        const { dispatch } = this.props;
    
        dispatch({
          type: 'project/fetchDetail',
          payload: {
              goodsId: item.id
          },
          callback: (res) => {
            if(res){
              this.setState({
                detail: res.data,
                imgList: res.data.rotationChart && res.data.rotationChart.length
                          ? this.initImgList(res.data) : "",    // Upload显示图片本地存储的图片url数组
                fileThumbs: res.data.rotationChart && res.data.rotationChart.length                            
                          ? this.initFileThumbs(res.data) : "", // 上传七牛云后存储的用于传给后端的图片url数组
                introImgList: res.data.introPic
                          ? [{
                            uid: '-1',
                            status: 'done',
                            name: res.data.introPic,
                            url: `${setFileHost()+res.data.introPic}`,
                            thumbUrl: `${setFileHost()+res.data.introPic}`
                          }] : '',
                introFileThumb: res.data.introPic ? res.data.introPic : '',
                current: item,
                addSubmit: false
              }, () => {
                this.setState({
                  visible: true
                })
              });
            }
          }
        })
    };
  4. 弹框表单中嵌入Upload组件

     const ImgUpButton = (       //  上传图片的按钮
        <div> 
          <Icon type="plus" />
          <div className="ant-upload-text">Upload</div>
        </div>
      );
    <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上传前的处理函数: 嵌套handleImageUpload方法  (上传一张图片或一个文件时,如果需要上传前判断文件类型、文件大小也是这么做)
                     // 1.包含handleFileThumb方法,代替action实现上传七牛云服务器后存储state;2.同时将新的图片数组imgArray存入本地imgList改变Upload组件显示的图片
                     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>
  5. 使用到的方法

    initImgList = (item) => {   // 处理Upload默认显示的数据
        let defaultImgList = [];
        item.rotationChart.forEach((imgListItem, index) => {
            defaultImgList.push ({
                uid: `${-1-index}`,
                status: 'done',
                name: item.name,
                url: imgListItem.img ? `${setFileHost()+imgListItem.img}` : '',
                thumbUrl: imgListItem.thumb ? `${setFileHost()+imgListItem.thumb}` : ''
            })
        })
        return defaultImgList
    }
    initFileThumbs = (item) => {  // 不更改不上传新的图片时默认向后端传的图片url数组
        let defaultFileThumbs = [];
        item.rotationChart.forEach((fileThumb, index) => {
          defaultFileThumbs[index] = fileThumb;
        })
        return defaultFileThumbs
    }
    handleFileThumb = (res, file, imgList) => {  // 更改fileThumbs数组
        let  { fileThumbs } = this.state;
        fileThumbs[imgList.length-1] = {
          img: res,
          index: imgList.length-1,
          type: file.type.split('/')[0],
          thumb: res
        };
        this.setState({
          fileThumbs
        })
    }
    // 关闭预览弹框
    handleImgCancel = () => this.setState({ previewVisible: false })
    
    // 显示预览弹框
    handleImgPreview = (file) => {
        this.setState({
          previewImage: file.url || file.thumbUrl,
          previewVisible: true,
        });
    }
    
    // 删除预览弹框
    handleImgRemove = (file)  => {
        const { fileThumbs, imgList } = this.state;
        let newList = [...imgList];
        let newFileThumbs = [...fileThumbs];
        newList.forEach((imgItem, index) => {
          if(imgItem.uid == file.uid){
            newList.splice(index, 1)
            newFileThumbs.splice(index, 1)
          }
        })
        this.setState({
          imgList: newList,
          fileThumbs: newFileThumbs
        }, () => {
          return true
        })
    }
    beforeUpload = (file) => {
          let type = file.type.split('/')[0];
          let name = file.name.split('.')[0];
          
          // 判断文件类型 -- 如果是视频url直接存入imgList,存入fileThumb
          if(type == 'video') {
            let imgArray = [...this.state.imgList];
            imgArray.push(file);
    
            handleImageUpload(file, 'video', name).then(res => {
              this.setState({
                imgList: imgArray
              })
              this.handleFileThumb(res, file, imgArray)
            })
          }else{
            // 如果是图片,使用react-cropper插件相关设置进行裁剪处理
            // 当打开同一张图片的时候清除上一次的缓存
            if (this.refs.cropper) {
              this.refs.cropper.reset();
            }
    
            var reader = new FileReader();
            const image = new Image();
            //因为读取文件需要时间,所以要在回调函数中使用读取的结果
            reader.readAsDataURL(file); //开始读取文件
    
            reader.onload = (e) => {
              image.src = reader.result;
              image.onload = () => {
              this.setState({
                  srcCropper: e.target.result, //cropper的图片路径
                  selectImgName: file.name, //文件名称
                  selectImgSize: (file.size / 1024 / 1024), //文件大小
                  selectImgSuffix: file.type.split("/")[1], //文件类型
                  editImageModalVisible: true, //打开控制裁剪弹窗的变量,为true即弹窗
             })
              if (this.refs.cropper) {
                  this.refs.cropper.replace(e.target.result);
              }
              }
            }
            return false;
          }
    }
  • 不需要裁剪的使用beforeUpload判断文件大小的上传一张图片

    <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>
    beforeIntroUpload = (file) => {
        const isLt3M = file.size / 1024 / 1024 < 3;
        if (!isLt3M) { //添加文件限制
          message.error('文件大小不能超过3M');
          return false;
        }
        // console.log('file', file)
    
        handleImageUpload(file, 'image').then(res => {
          this.setState({              // 存入introImgList
            introImgList: [{
              uid: file.uid,
              status: 'done',
              name: file.name,
              url: `${setFileHost()+res}`,
              thumbUrl: `${setFileHost()+res}`
            }]
          })
          this.handleIntroFileThumb(res)   // 存入introFileThumbs
        })
    
        return true
      }

转载请注明出处

原文地址:https://www.cnblogs.com/ljq66/p/11904760.html