我的实习踩坑记录

785 阅读1分钟

上传文件校验文件属性 + 必填的实现方案(Form包裹Upload组件)

Vue + iview 检验图片宽高+必填

<Form ref="formItem" :model="formItem" :rules="ruleValidate" :label-width="80">
    <FormItem label="图片" prop="img">
        <Upload 
            v-model="formItem.img"
            :action="UPLOAD_IMG_URL" 
            accept="image/*"
            :before-upload="beforeUpload"
            :on-success="handleUploadSuccess"
            :on-progress="handleProgress"
            :on-error="handleUploadError"
            :on-remove="handleUploadRemove"
            :disabled="!canUpload || formItem.img ? true : false"
        >
            <Button icon="ios-cloud-upload-outline" ></Button>
        </Upload>
    </FormItem>
</Form>
<Button type="primary" @click="handleSubmit('formItem')">{{$t('ok')}}</Button>

data(){
    return {
        formItem: {
            img: null,
        },
        canUpload: true, // 用于控制上传中时,Uplaod失效
        ruleValidate: {
                img: [
                    { required: true, validator: this.validateUpload,trigger: 'blur' },
                ],
            },
    }
}
methods: {
    // 准备上传
    beforeUpload(file) {
        const that =this;
        return this.checkImageWH(file, 1000, 100,function(){
            that.$Message.warning('图片格式要求为1000*100');
        });
    },
    
    // 封装的一个校验文件宽高的函数
    checkImageWH (file, width, height,fn) {
        let self = this;
        return new Promise(function (resolve, reject) {
            let filereader = new FileReader();
            filereader.onload = e => {
                let src = e.target.result;
                const image = new Image();
                image.onload = function () {
                    if (width && this.width !== width || this.height !== height) {
                        fn()
                        reject();
                    } else {
                        resolve();
                    }
                };
                image.onerror = reject;
                image.src = src;
            };
            filereader.readAsDataURL(file);
        });
    },
    
    // 上传中
    handleProgress(event,file){
        this.canUpload = false;
    },
    // 上传失败
    handleUploadError () {
        this.$Message.error('上传失败');
        this.canUpload = true;
    },
   // 上传成功
    handleUploadSuccess (res) {
        if (res.status_code != Constants.API_STATUS.OK) {
            this.$Message.error(res.extra.details);
            return;
        }
        this.formItem.img = res.data; 
        this.canUpload = true;
    },
    // 上传失败
    handleUploadRemove () {
        this.formItem.img = null; 
    }, 
    
    // Form校验:文件必须上传
    validateUpload = (rule, value, callback) => {
        if (this.formItem.img === null) {
            callback(new Error('请选择要上传的图片'));
        } else {
            callback();
        }
    };
    
    // 点击确定
    handleSubmit (name) {
        this.$refs[name].validate((valid) => {
            if (valid) {
               // 验证成功
            }
        })
    },
}

react + antd

<Form>
    <Form.Item label="上传营业执照">
        {this.props.form.getFieldDecorator('file', {
            rules: [{ required: true, message: '必填' }, {validator: this.validFunction}],
        })(
            <Upload {...props} 
                accept=".pdf,.png,.jpg,.jpeg" 
                beforeUpload={this.beforeUploadfile}
                onRemove={this.removeFile}
            >
                <Button disabled={this.state.file}>
                上传(支持格式:pdf、png、jpg、jpeg)
                <Icon type="plus" />
                </Button>
            </Upload>
        )}
    </Form.Item>
</Form>
<div className="setting-modal-footer" >
    <Button type="primary" onClick={this.handleOk}>
        完成
    </Button>
</div>
class OrganizationModal extends Component {

    state = {
        file: null, //用于存储上传的文件,控制单文件上传
    } 
    
    // 点击完成按钮,触发表单校验逻辑
    handleOk = () => {
        this.props.form.validateFields((err, value) => {
            if (!err) {
                // 校验成功后,做你想做的事        
            }
        });
    };
    
    // 上传前的回调
    beforeUploadfile = (file) => {
        this.setState({ file });
    };
   
   // 移除文件的回调
    removeFile = () => {
        this.setState({file: null});
    };
    
    // 自定义表单校验函数
    validFunction = (rule, value, callback) => {
        // 此处需要注意:value.file会报错上次上传的文件,但是如果没有上传文件value.fileList为一个长度0的数组
        if (value && !value.fileList.length) {
            callback("必填");
            return;
        }
        callback(); // 校验通过
    }
}


  • react+antd(改进) 发现上个方法有缺陷,
<Form.Item label="营业执照">
    {getFieldDecorator('license', {
        rules: [{ required: true, message: '必填' }],
    })(
        <Upload 
            {...props} 
            fileList={licenseFile} 
            listType="picture-card"
            beforeUpload={this.beforeUpload}
            onPreview={this.handlePreview}
            onChange={this.licenseChange} 
            onRemove={this.removeFile}
            accept=".png,.jpg,.jpeg">
            <Input
                placeholder='像素要求不小于800*800'
                suffix={<Icon type="plus" style={{color: '#000000', opacity: 0.3}}/>}
                disabled
            />
        </Upload>
    )}
</Form.Item>

// 触发校验
<Button className="next-step" onClick={this.handleNextStep}>下一步</Button>

// 提供图片预览
<Modal visible={previewVisible} footer={null} onCancel={this.handleCancel}>
    <img alt="example" style={{ width: '100%' }} src={previewImage} />
</Modal>

state = {
    licenseFile: [], // 绑定Upload已经上传的文件列表(受控)
    previewImage: '', // 预览图片地址
    previewVisible: false, // 预览状态
};

// 移除回调
removeFile = () => {
    // 只是改变了受控组件初始值
    this.setState({
        licenseFile: [],
    });
    // 重要:为了检验,一定要操作form属性
    this.props.form.setFieldsValue({
        license: [],
    });
};

// 上传前回调
beforeUpload = (file) => {
    // checkImageWH同上
    return checkImageWH(file, 800, 800, function() {
        message.warning('像素要求小于800*800');
        this.removeFile(); // 记得清空~ 单文件上传,如果不符合条件,逻辑上应该没有已上传文件
    }.bind(this));
};

// 上传文件改变时的状态
licenseChange = (info) => {
    this.setState({
        licenseFile: info.fileList.slice(-1)
    });
    if (info.file.status === 'done') {
        message.success(`${info.file.name} 上传成功`);
    } else if (info.file.status === 'error') {
        message.error(`${info.file.name} 上传失败`);
    }
};

// 预览回调
handlePreview = async file => {
    if (!file.url && !file.preview) {
      file.preview = await this.getBase64(file.originFileObj);
    }

    this.setState({
      previewImage: file.url || file.preview,
      previewVisible: true,
    });
};

getBase64 = (file) => {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.readAsDataURL(file);
      reader.onload = () => resolve(reader.result);
      reader.onerror = error => reject(error);
    });
};

// 关闭预览弹窗
handleCancel = () => this.setState({ previewVisible: false });

修改antd中Modal的样式

这个Modal的样式是有作用域的,所以官方文档中写明,可以给Modal指定className

  • 踩坑: scss样式无论怎么修改都不生效,在浏览器中确可以生效
  • 正确方式:
// 1. 给Modal指定className
 <div className="container">
    <Modal visible={true} className="myModal">
        <div className="title">机构申请入驻</div>
    </Modal>
</div>

在写scss时要注意,modal的className: myModal不能嵌套在container里面,一定要自己是最外层,可以理解为Modal是独立的样式作用范围。

// 2. 写scss
.container {
    width: 100%;
    height: 810px;
}

.myModal {
    .title {
        height: 24px;
        font-family: PingFangSC;
        font-size: 18px;
        font-weight: 600;
        line-height: 1.33;
        color: #000000;
    }
} 

这样就可以生效啦~想怎么改怎么改

解决元素脱离文档流后事件无法触发

z-index: 1;提高层级,马上就能点击到了