记一次开发过程中,iview遇到的一些坑以及解决办法

时间:2024-03-13 14:32:39

写在开头:本次项目采用的是vue2.0+iview3.0,最近公司没啥事,来总结一下开发过程中遇到的问题。

 

1、Modal关闭问题

需求背景:modal框里面是个form表单,点击确定之后,先验证form表单,验证通过则关闭modal框,验证不成功则提示用户,不关闭。

问题描述:本来刚开始想通过modal框v-model绑定的值(true或false)来进行控制,手动改之后,报错。

解决办法:

       官方iview的modal组件的api里面有个loading属性,可通过控制loading的值来进行控制modal的显示。

      记一次开发过程中,iview遇到的一些坑以及解决办法

       举例说明:

      记一次开发过程中,iview遇到的一些坑以及解决办法

      记一次开发过程中,iview遇到的一些坑以及解决办法

     注意: refuseLoading刚开始一定要设置为true。

    记一次开发过程中,iview遇到的一些坑以及解决办法

   这样的话就可以解决问题了。

   衍生出来的问题:当关闭模态框之后,再次打开时,表单数据没有重置,还是上一次的数据。

   解决办法:this.$refs[name].resetFields();    重置表单数据,在关闭模态框的时候调用这个方法可解决。

 

2、同时验证多个表单问题

需求背景:一个页面有多个表单,提交的时候需要验证多个表单,都验证成功才能进行下一步操作

解决办法:用一个数组来存放每个表单的验证结果(true验证通过,false验证不通过),最后循环这个数组如果值都为true,说明验证通过。

               举例说明:页面有3个表单,需要同时验证,主要代码如下:

<template>
    <div class="hello">
        <Form ref="formValidate1" :model="formValidate1" :rules="ruleValidate">
            <FormItem label="Name" prop="name">
                <Input v-model="formValidate1.name" placeholder="Enter your name"></Input>
            </FormItem>
        </Form>
        <Form ref="formValidate2" :model="formValidate2" :rules="ruleValidate">
            <FormItem label="Name" prop="name">
                <Input v-model="formValidate2.name" placeholder="Enter your name"></Input>
            </FormItem>
        </Form>
        <Form ref="formValidate3" :model="formValidate3" :rules="ruleValidate">
            <FormItem label="Name" prop="name">
                <Input v-model="formValidate3.name" placeholder="Enter your name"></Input>
            </FormItem>
        </Form>
        <Button @click="submit">提交</Button>
    </div>
</template>

<script>
    export default {
        name: 'HelloWorld',
        data() {
            return {
                formValidate1: {
                    name: ''
                },
                formValidate2: {
                    name: ''
                },
                formValidate3: {
                    name: ''
                },
                ruleValidate: {
                    name: [
                        { required: true, message: 'The name cannot be empty', trigger: 'blur' }
                    ]
                },
                arr: []
            }
        },
        methods: {
            check(name){  // 验证表单是否通过
                this.$refs[name].validate((valid) => {
                    if(valid) {
                        this.arr.push(true);  // arr 这个数组是用来存放单个表单的验证状态
                    } else {
                        this.arr.push(false);
                    }
                })
            },
            submit(){  // 提交
                this.arr = [];  // 重置数组
                // 同时验证多个表单
                this.check('formValidate1');
                this.check('formValidate2');
                this.check('formValidate3');
                var flag = null;
                for(var i = 0; i < this.arr.length; i++) {
                    if(this.arr[i]) { // 单个表单验证通过,继续验证下个表单
                        flag = true;
                        continue;
                    } else { // 单个表单验证不通过,结束
                        flag = false;
                        break;
                    }
                }
                if(flag){  // 验证表单成功
                    alert("验证成功");
                }else{
                    alert("验证失败")
                }
            }
        }
    }
</script>

<style scoped></style>

 

3、Select 内的 Option 动态改变时,有时选中值未更新的问题

       需求背景:Select的下拉数组是由后台返回的,选中的值也是后台返回的。正确赋值之后,select选中的值未更新。

       解决办法:刚开始一直在不停的调试,有时候可能正确显示,有时候又不行。这个随机事件真的。。。。最后查阅官方文档,好吧,这是官方的坑,更新到iview最新版本后,问题得以解决。

                         这也给我以后很好的警示,有时候一些异常情况,可以先看哈官方的更新日志,因为我们刚开始做项目的时候,版本只是当时的最新版,一些问题可能官方后面已经修复了,所以应及时更新版本。

                        记一次开发过程中,iview遇到的一些坑以及解决办法

 

 4、Table相关问题

       (1)render函数的运用

                记一次开发过程中,iview遇到的一些坑以及解决办法          

                 参数解读:

                  h:  vue  Render函数的别名(全名 createElement)即 Render函数

                  params: table 该行内容的对象

                  props:设置创建的标签对象的属性

                  style:设置创建的标签对象的样式

                  on:为创建的标签绑定事件

                  scopedSlots:显示作用域插槽

 

                   a、Switch 开关

                    {
                        title: "可控开关",
                        key: "isOpen",
                        align: "center",
                        width: 100,
                        render:(h, params) => {
                            return h('i-switch', {
                                props: {
                                    value: params.row.isOpen ? params.row.isOpen : false,  // 指定当前是否选中  Boolean类型  (isOpen后端返回字段,根据自己接口返回数据,自行修改)
                                },
                                scopedSlots:{
                                    open: () => h('span', 'on'),  // 自定义显示打开时的内容
                                    close: () => h('span', 'off')  // 自定义显示关闭时的内容
                                },
                                on: {
                                    /*
                                     * 触发事件是on-change
                                     * 参数value是回调值  Boolean类型                                    
                                    */ 
                                    'on-change': (value) => { 
                                        this.data[params.index].isOpen = value;  // 赋值  data:表格数据
                                    }
                                }
                            })
                        }
                    }

       

              b、Button按钮

                    {
                        title: '操作',
                        key: 'action',
                        width: 150,
                        align: 'center',
                        render: (h, params) => {  // 按钮操作
                            return h('div', [
                                h('Button', {
                                    props: {
                                        type: 'primary',
                                        size: 'small'
                                    },
                                    style: {  // 自定义样式
                                        marginRight: '5px'
                                    },
                                    on: {  // 自定义事件
                                        click: () => {
                                            this.show(params.index)  // params.index是拿到table的行序列,可以取到对应的表格值
                                        }
                                    }
                                }, '查看'),
                                h('Button', {
                                    props: {
                                        type: 'error',
                                        size: 'small'
                                    },
                                    on: {
                                        click: () => {
                                            this.remove(params.index)
                                        }
                                    }
                                }, '删除')
                            ]);
                        }
                    }

 

                 c、Input 输入框

                    {
                        title: "input输入框",
                        key: "inputText",
                        align: "center",
                        render:(h, params) => {
                            return h('Input', {
                                props: {
                                    value: params.row.inputText ? params.row.inputText : '',
                                    size: 'small'
                                },
                                on: {
                                    'on-blur': (event) => {  // 输入框失去焦点时触发
                                        this.data[params.index].inputText = event.target.value;  // 赋值   data:表格数据
                                    }
                                }
                            });
                        }
                    }

              

                       d、Select 下拉框

                    {
                        title: 'select下拉框',
                        key: 'selectText',
                        align: 'center',
                        render: (h, params) => {
                            return h('Select',{
                                    props:{
                                        value: params.row.selectText ? params.row.selectText : '',
                                        size: 'small'
                                    },
                                    on: {
                                        'on-change':(value) => {  //  下拉框选定的值
                                            this.data[params.index].selectText = value;
                                        }
                                    }
                                },
                                /**
                                 * this.selectAction   下拉框Option数组
                                 * selectAction:[
                                        {
                                            value: '01',
                                            name:'select_1'
                                        },
                                        {
                                            value: '02',
                                            name:'select_2'
                                        }
                                    ]
                                 */
                                this.selectAction.map((item) =>{  // 下拉选项
                                    return h('Option', {
                                        props: {
                                            value: item.value,
                                            label: item.name
                                        }
                                    })
                                })
                            )
                        }
                    }

            

                    e、Rate评分

                    {
                        title: "评分",
                        key: "rate",
                        align: "center",
                        render:(h, params) => {
                            return h('Rate', {
                                props: {
                                    value: Number(params.row.rate),  // 当前 star 数   Number类型
                                    'allow-half': true,  // 可以选中半星
                                    disabled: false  // 是否只读
                                },
                                on: {
                                    'on-change': (value) => {   // 评分改变时触发
                                        this.data[params.index].rate = value;  // 赋值  data:表格数据
                                    }
                                }
                            })
                        }
                    }

 

                   f、Img图片

                  {
                        title: "头像",
                        key: "avatar",
                        align: "center",
                        width: 100,
                        render:(h, params) => {
                            return h('Avatar', {  // 也可用原生img标签代替
                                style: {  
                                    width: '30px',
                                    height: '30px',
                                    'border-radius': '50%'
                                },
                                attrs: {
                                    src: 'https://i.loli.net/2017/08/21/599a521472424.jpg'
                                }
                            })
                        }
                    }

 

                  g、DatePicker时间选择器

                    {
                        title: "时间选择器",
                        key: "date",
                        align: "center",
                        render:(h, params) => {
                            return h('DatePicker', {
                                props: {
                                    value: params.row.date,
                                    size: 'small',
                                    type: 'datetime'
                                },
                                on: {
                                    'on-change': (value) => {  // 输入框失去焦点时触发
                                        this.data[params.index].date = value;  // 赋值   data:表格数据
                                    }
                                }
                            });
                        }
                    }

 

                     h、对数据进行处理

                            比如,后端返回时间是时间戳格式,展示给用户看的肯定不能是时间戳,这时候就需要我们对数据进行处理              

                   {
                        title: "申请年份",
                        align: "center",
                        key: "applyDate",
                        render: (h, params) => {
                            return h('span', {
                                    
                            }, new Date(params.row.applyDate).getFullYear())  // 对后端返回的时间戳进行处理,返回页面需要展示的格式
                        }
                    }

 

                        差不多就总结了这几个,写多了就发现,是一样的模板,直接套到render函数里面就是了。想要更多的学习render函数相关的,可以自己前往官网学习。

                        完整代码:

记一次开发过程中,iview遇到的一些坑以及解决办法记一次开发过程中,iview遇到的一些坑以及解决办法
<template>
    <div class="hello">
        <Table border :columns="columns" :data="data"></Table>
    </div>
</template>

<script>
    export default {
        name: 'HelloWorld',
        data() {
            return {
                columns: [  // 表格列的配置描述
                    {
                        title: "头像",
                        key: "avatar",
                        align: "center",
                        width: 100,
                        render:(h, params) => {
                            return h('Avatar', {  // 也可用原生img标签代替
                                style: {  
                                    width: '30px',
                                    height: '30px',
                                    'border-radius': '50%'
                                },
                                attrs: {
                                    src: 'https://i.loli.net/2017/08/21/599a521472424.jpg'
                                }
                            })
                        }
                    },
                    {
                        title: "时间选择器",
                        key: "date",
                        align: "center",
                        render:(h, params) => {
                            return h('DatePicker', {
                                props: {
                                    value: params.row.date,
                                    size: 'small',
                                    type: 'datetime'
                                },
                                on: {
                                    'on-change': (value) => {  // 输入框失去焦点时触发
                                        this.data[params.index].date = value;  // 赋值   data:表格数据
                                    }
                                }
                            });
                        }
                    },
                    {
                        title: "input输入框",
                        key: "inputText",
                        align: "center",
                        render:(h, params) => {
                            return h('Input', {
                                props: {
                                    value: params.row.inputText ? params.row.inputText : '',
                                    size: 'small'
                                },
                                on: {
                                    'on-blur': (event) => {  // 输入框失去焦点时触发
                                        this.data[params.index].inputText = event.target.value;  // 赋值   data:表格数据
                                    }
                                }
                            });
                        }
                    },
                    {
                        title: 'select下拉框',
                        key: 'selectText',
                        align: 'center',
                        render: (h, params) => {
                            return h('Select',{
                                    props:{
                                        value: params.row.selectText ? params.row.selectText : '',
                                        size: 'small'
                                    },
                                    on: {
                                        'on-change':(value) => {  //  下拉框选定的值
                                            this.data[params.index].selectText = value;
                                        }
                                    }
                                },
                                /**
                                 * this.selectAction   下拉框Option数组
                                 * selectAction:[
                                        {
                                            value: '01',
                                            name:'select_1'
                                        },
                                        {
                                            value: '02',
                                            name:'select_2'
                                        }
                                    ]
                                 */
                                this.selectAction.map((item) =>{  // 下拉选项
                                    return h('Option', {
                                        props: {
                                            value: item.value,
                                            label: item.name
                                        }
                                    })
                                })
                            )
                        }
                    },
                    {
                        title: "申请年份",
                        align: "center",
                        key: "applyDate",
                        render: (h, params) => {
                            return h('span', {
                                    
                            }, new Date(params.row.applyDate).getFullYear())  // 对后端返回的时间戳进行处理,返回页面需要展示的格式
                        }
                    },
                    {
                        title: "可控开关",
                        key: "isOpen",
                        align: "center",
                        width: 100,
                        render:(h, params) => {
                            return h('i-switch', {
                                props: {
                                    value: params.row.isOpen ? params.row.isOpen : false,  // 指定当前是否选中  Boolean类型  (isOpen后端返回字段,根据自己接口返回数据,自行修改)
                                },
                                scopedSlots:{
                                    open: () => h('span', 'on'),  // 自定义显示打开时的内容
                                    close: () => h('span', 'off')  // 自定义显示关闭时的内容
                                },
                                on: {
                                    /*
                                     * 触发事件是on-change
                                     * 参数value是回调值  Boolean类型                                    
                                    */ 
                                    'on-change': (value) => { 
                                        this.data[params.index].isOpen = value;  // 赋值  data:表格数据
                                    }
                                }
                            })
                        }
                    },
                    {
                        title: "评分",
                        key: "rate",
                        align: "center",
                        render:(h, params) => {
                            return h('Rate', {
                                props: {
                                    value: Number(params.row.rate),  // 当前 star 数   Number类型
                                    'allow-half': true,  // 可以选中半星
                                    disabled: false  // 是否只读
                                },
                                on: {
                                    'on-change': (value) => {   // 评分改变时触发
                                        this.data[params.index].rate = value;  // 赋值  data:表格数据
                                    }
                                }
                            })
                        }
                    },
                    {
                        title: '操作',
                        key: 'action',
                        width: 150,
                        align: 'center',
                        render: (h, params) => {  // 按钮操作
                            return h('div', [
                                h('Button', {
                                    props: {
                                        type: 'primary',
                                        size: 'small'
                                    },
                                    style: {  // 自定义样式
                                        marginRight: '5px'
                                    },
                                    on: {  // 自定义事件
                                        click: () => {
                                            this.show(params.index)  // params.index是拿到table的行序列,可以取到对应的表格值
                                        }
                                    }
                                }, '查看'),
                                h('Button', {
                                    props: {
                                        type: 'error',
                                        size: 'small'
                                    },
                                    on: {
                                        click: () => {
                                            this.remove(params.index)
                                        }
                                    }
                                }, '删除')
                            ]);
                        }
                    }
                ],
                data: [  // 表格数据
                    {
                        inputText: '18',
                        isOpen: false,
                        selectText : '02',
                        rate: 4,
                        date: '2019-02-03 00:08:45',
                        applyDate: 1551835636920
                    },
                    {
                        inputText: '',
                        isOpen: true,
                        selectText : '01',
                        rate: 1.5,
                        date: '',
                        applyDate: 1506124800000
                    }
                ],
                selectAction:[
                    {
                        value: '01',
                        name:'select_1'
                    },
                    {
                        value: '02',
                        name:'select_2'
                    }
                ]
                
            }
        },
        methods: {
            show (index) {  // 查看
                this.$Modal.info({
                    title: '查看',
                    content: '查看详情'
                })
            },
            remove (index) {  // 删除
                this.data.splice(index, 1);
            }
        }
    }
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
    .hello{
        width: 90%;
        margin: 0 auto;
        padding: 20px 50px 0;
    }
</style>
View Code

                        

          (2)selection的多选来做单选:

                     通过给 columns 数据设置一项,指定 type: 'selection',即可自动开启多选功能,但是有些产品觉得iview的单选效果不好,非要用selection的来做单选。以下是解决方案:

 

                     {
                        title: '选择',
                        align:'center',
                        key: 'checkBox',
                        width: 80,
                        render:(h,params)=>{
                            return h('div',[
                                h('Checkbox',{
                                    props:{
                                        value: params.row.checkBox
                                    },
                                    on:{
                                        'on-change':(e)=>{
                                            this.data.forEach((item)=>{ // 先取消所有对象的勾选,checkBox设置为false
                                                this.$set(item, 'checkBox', false);
                                            });
                                            this.data[params.index].checkBox = e;  // 再将勾选的对象的checkBox设置为true
                                        }
                                    }
                                })
                            ])
                        }
                    }

     

             (3)结合Page分页组件一起使用

                   一般来讲,在表格数据比较多的情况下,会对表格进行分页展示

<Table border :columns="columns" :data="data"></Table>
<Page style="float: right;margin-top:20px" :total="page.total" :current="page.current" :page-size="page.size" @on-change="changePage"  @on-page-size-change="changePageSize" show-total show-elevator show-sizer />

               

                 分页一般有2种:前端分页、后端分页。前端分页就是前端一次性拿到所有数据,再对拿到的数据进行分页展示。后端分页就是前端一次只拿一页的数据展示,分页的时候再次请求后端。

        mounted(){
            this.changeTableData();
        },
        methods: {
            changePage(current){  // 页码改变的回调,返回改变后的页码
                this.page.current = current;
                this.changeTableData();
            },
            changePageSize(size){  // 切换每页条数时的回调,返回切换后的每页条数
                this.page.current = 1;
                this.page.size = size;
                this.changeTableData();
            },
            changeTableData(){
                /**
                 * page定义
                 * page: {
                        total: 0, 总数
                        current: 1, // 当前页码
                        size: 10  // 每页个数
                    }
                 *
                 *
                 * data: 表格展示数据
                 * tableData: 后端返回的所有数据
                 *  
                 */

                // 前端分页
                let _start, _end;
                let page = this.page;
                _start = (page.current - 1) * page.size;
                _end = page.current * page.size;
                this.data = this.tableData.slice(_start, _end);
                page.total = this.tableData.length;

                // 后端分页
                // ajax请求后端接口,重新获取数据
            }
        }

 

                使用了type=index,分页之后,索引还是从1开始,如何实现累加呢?

                使用render函数可解决:

                  {
                        title: '序号',
                        align:'center',
                        type: 'index2',
                        width: 80,
                        render: (h, params) => {
                            return h('span', params.index + (this.page.current - 1) * this.page.size + 1);
                        }
                    }

 

                在翻页之后,如何记住checkbox多选的选中和未选中状态?

                用一个数组checkData来装选中的数据,在每次表格数据改变时,通过比对数据来进行回填选中状态。

<Table border :columns="columns" :data="data" @on-select="changeSelect" @on-select-cancel="changeSelectCancel" @on-select-all="changeSelectAll" @on-select-all-cancel="changeSelectAllCancel"></Table>
         methods: {
            changeSelect(data, val){  // 选中某个数据
                /**
                 * checkData  多选框选中的数组
                 * id  每条数据的唯一标识
                 */
                for(let i in this.checkData){
                    if(val.id === this.checkData[i].id){
                        this.checkData.splice(i, 1);
                        break;
                    }
                }
                this.checkData.push(val);
            },
            changeSelectCancel(data, val){  // 取消某个数据
                for(let i in this.checkData){
                    if(val.id === this.checkData[i].id){
                        this.checkData.splice(i, 1);
                        break;
                    }
                }
            },
            changeSelectAll(data){  // 多选选中
                let arr = [];
                for(let i in data){
                    let flag = true;
                    for(let y in this.checkData){
                        if(data[i].id === this.checkData[y].id){ // 已有数据
                            flag = false;
                            break;
                        }else{
                            continue;
                        }
                    }
                    if(flag){  // 添加新数据
                        arr.push(data[i]); 
                    }
                }  
                this.checkData = this.checkData.concat(arr); 
            },
            changeSelectAllCancel(data){  // 多选取消
                for(let i in this.data){ // 取消当前页的内容
                    for(let y in this.checkData){
                        if(this.data[i].id === this.checkData[y].id){
                            this.checkData.splice(y, 1);
                            break;
                        }
                    }
                }
            },
        },
        watch:{
            'data':{
                handler:function(newValue, oldValue){
                    this.data = newValue;
                    for(let i in this.data){
                        let flag = false;
                        for(let y in this.checkData){
                            if(this.data[i].id === this.checkData[y].id){
                                flag = true;
                                break;
                            }else{
                                continue;
                            }
                        }
                        if(flag){  // 回填选中状态
                            this.data[i]._checked = true;
                        }else{
                            this.data[i]._checked = false;
                        }
                    }
                },
                deep:true
            }
        }

 

5、定制iview主题

记一次开发过程中,iview遇到的一些坑以及解决办法

 

     按照官网,一步一步做的,最后却报错:

    记一次开发过程中,iview遇到的一些坑以及解决办法

   那是因为less的版本过高,重新卸载,安装3.0以下的版本(比如2.7.3版本),即可解决问题。

 

 

六、合并表格单元行

   官网已经提供了表头分组的api。可是我们想合并单元行列怎么办呢,虽然官网没提供api,但是好在我们可以用render函数自定义渲染行列。

   1、合并行

                   {
                        title: '合并行',
                        align:'center',
                        key: 'content',
                        render: (h, params) => {
                            let a = ['数学','语文']; 
                            return this.renderData(h, a);
                       }
                    }



            // 下面为渲染方法
            renderData(h, a){
                let b = [];
                a.map((val, index) => {
                    b.push(h("p", {
                        style: {
                            height: "40px",
                            lineHeight: "40px",
                            "width": "100%",
                            "display": "block",
                            "padding": "0 10px",
                            borderBottom: a.length !== index + 1 ? "1px solid #e8eaec" : "none"
                        }}, val)
                    )
                })
                return b;
            }

 

2、合并列

                  {
                        title: '姓名,昵称',
                        align:'center',
                        key: 'name',
                        renderHeader: (h, params) => {  // 自定义表头
                            let a = ['姓名', '昵称'];
                            return this.renderColumnData(h, a);
                        },
                        render: (h, params) => {
                            let a = ['张三', '李四'];
                            if(params.row.same){  // 名字姓名相同
                                a = ['王二麻子'];
                            }
                            return this.renderColumnData(h, a);
                       }
                    },

             // 下面为渲染的方法
             renderColumnData(h, a){  // 渲染合并列
                let b = [];
                let percent = a.length === 0 ? 100 : (Math.floor(100 / a.length * 100) / 100);
                a.map((val, index) => {
                    b.push(h("p", {
                        class: a.length !== index + 1 ? "has-border" : "",
                        style: {
                            width: `${percent}%`,
                            padding:'4px 8px',
                            'display': 'flex',
                            'align-items': 'center',
                            'justify-content': 'center'
                        }}, val)
                    )
                })
                let header = h('div',{
                    class: 'custom-header'
                }, b);
                return header;
            }

还需添加css样式

   .ivu-table-cell{
        padding: 0 !important;
        width: 100%;
    }
    .ivu-table-cell .has-border{
        position: relative;
    }
    .ivu-table-cell .has-border::after{
        content: '';
        position: absolute;
        width: 1px;
        background-color: #e8eaec;
        top: 0;
        bottom: 0;
        right: 0;
    }
    .custom-header{
        width: 100%;
        height: 100%;
        display: flex;
    }

需要注意的是  iview 里面的 ivu-table-cell  class样式  没添加height:100%。但是由于自定义列的时候,右边框的高度要100%,所以只能根据子元素手动改父节点 ivu-table-cell,添加样式。

在初始化,和改变表格数据的时候都需执行一次

           changeCustomHeight(){
                let customArr = document.getElementsByClassName('custom-header');  //custom-header   render函数里面自定义的class
                for(let i in customArr){
                    if(customArr[i].parentNode){
                        customArr[i].parentNode.style.height = "100%";
                    }
                }
            },

 

页面效果预览:

记一次开发过程中,iview遇到的一些坑以及解决办法

说明:此方法主要还是通过改变样式来实现效果,对于一些特殊的比较复杂的合并行列,可能无法实现。