1
文 / 景朝霞
来源公号 / 朝霞的光影笔记
ID / zhaoxiajingjing
图 / 自己画
❥❥❥❥点个赞,让我知道你来过~❥❥❥❥

【前情提要】

  1. 题目 | let和var的区别(一、二)
  2. 图解 | let和var的区别(一、二)
  3. 题目 | 带VAR和不带VAR的区别
  4. 图解 | 带VAR和不带VAR的区别
  5. 总结 | LET和VAR区别(三、四)
  6. 图解 | 作用域和作用域链
  7. 练习题 | 作用域和作用域链
  8. 图解 | 理解闭包
  9. 案例 | 闭包作用:保护和保存
  10. 图解 | 判断条件中的变量提升、私有变量、全局变量、arguments
  11. 理解 | 堆内存栈内存释放、null和{}、undefined的区别

【iview】

  1. iview低版本实现表格拖拽,滚动条列宽计算问题
  2. 案例 | iview中Table:拖拽适配列、自定义固定列、合并列
  3. 读源码 | 跟着大佬们学编程思想
  4. 上 | iview的Table组件合并列demo
  5. 下 | iview的Table组件合并列,升级代码

0 / 读 iview 源码

上个月,用iview的Table组件做合并行的效果,认真的读了下那块的源码,收获颇多。

公号ID:zhaoxiajingjing
△14.1iview的Table组件的行/列合并

https://github.com/view-desig...

公号:朝霞的光影笔记

△14.2在table.vue的props对象里面定义属性

它使用在这:

https://github.com/view-desig...

getChildNode (h, data, nodes) {
    // ...CODE
    if (data.children && data.children.length) {
        data.children.forEach((row, index) => {
            let $tds = [];

            this.columns.forEach((column, colIndex) => {
                // => 在循环每一列时,判断当前行/列是否需要展示"合并行"的属性
                // => this.showWithSpan() 方法返回一个布尔值
                // => 是:进入判断体
                if (this.showWithSpan(row, column, index, colIndex)) {
                    // ...CODE
                    const $td = h('td', {
                        class: this.alignCls(column, row),
                        // => 设置td的attrs属性,在vue里面写入DOM属性用 atrrs
                        // => this.getSpan() 方法返回rowspan、colspan的值
                        attrs: this.getSpan(row, column, index, colIndex)
                    }, [$tableCell]);
                    // ...CODE
                }
            });
        });
    }
    // ...CODE
}

△table-body.vue设置rowspan/colspan

/**
 * 获取span的值
 * @param {object} row 
 * @param {object} column 
 * @param {number} rowIndex 
 * @param {number} columnIndex 
 * 
 * @return {object} 返回一个对象
 */
getSpan (row, column, rowIndex, columnIndex) {
    // => 把父级的spanMethod函数的堆内存16进制地址赋值给fn
    // => fn就是传进来的那个函数了
    const fn = this.$parent.spanMethod;
    // => 判断fn是否为函数
    // => 是函数:一些操作
    // => 不是函数:则返回一个 {}对象
    if (typeof fn === 'function') {
        // => fn是函数,则调用该函数,传递实参集合,把函数的返回结果赋值给result
        const result = fn({
            row,
            column,
            rowIndex,
            columnIndex
        });
        // => 初始值rowspan/colspan都默认为1
        let rowspan = 1;
        let colspan = 1;
        // => 前面截图:spanMethod可以返回一个数组/对象
        // => 判断拿到的result是否为数组
        // => 分别获取到用户设置的rowspan/colspan的值
        if (Array.isArray(result)) {
            rowspan = result[0];
            colspan = result[1];
        } else if (typeof result === 'object') {
            rowspan = result.rowspan;
            colspan = result.colspan;
        }
        // => 把结果返回去
        return {
            rowspan,
            colspan
        };
    } else {
        return {};
    }
},

/**
 *  展示行/列合并
 * @param {object} row 当前行
 * @param {object} column 当前列
 * @param {number} rowIndex 当前行索引
 * @param {number} columnIndex 当前列索引
 * 
 * @return {boolean} 返回一个布尔值:有,true 没有 false
 */
showWithSpan (row, column, rowIndex, columnIndex) {
    // => 调用 this.getSpan() 方法,把返回结果赋值给常量 result
    const result = this.getSpan(row, column, rowIndex, columnIndex);

    // => 在result里面rowspan/colspan任意一个为0,则取反->true
    // => 最后的结果是返回一个boolean值
    return !(('rowspan' in result && result.rowspan === 0) || ('colspan' in result && result.colspan === 0));
};

△table-body.vue处理rowspan/colspan

!(('rowspan' in result && result.rowspan === 0) || ('colspan' in result && result.colspan === 0))

(1)运算符优先级(数字越大优先级越高):

()圆括号20

!逻辑非16

in11

===全等号10

&&逻辑与6

||逻辑或5

(2)A||BA为真,则返回A;A不为真,则返回B

A&&BA为真,则返回B;A不为真,则返回A

(3)prop in objin判断对象中是否有属性prop

(4)==等号:只需要判断值是否相等,如果两边数据类型不一致,先转换数据类型,再进行判断

===全等号:严格判断,既判断数据类型也判断值,都相等才为TRUE

(5)为FALSE的5种情况:''空字符串、nullundefined0NaN

1 / iview的编程思想

(1)行/列合并是在版本4.0.0加上的——保持原有代码的基础上升级:使用了spanMethod方法传参

(2)行/列合并控制table的td属性rowspan、colspan,需要指定哪行哪列:数组/对象都可以

(3)在收到指定行/列时,就需要判断是否为函数,是函数才可以被调用;函数的返回结果数组/对象拿到对应的值。

在开发中,遇到的问题:

简单一句:保证原有的代码没问题,还要同时能升级保证2+棵树的展示

啰嗦的解释,可以跳过:

基于iview的Form组件的动态增减表单项功能(https://www.iviewui.com/compo...)在实际应用中做了一个动态生成搜索条件的form表单的组件CustomSearch,包含表单项:Input输入框、DatePicker日期选择器 、Select 选择器、Checkbox多选框、Cascader级联选择、Tree树。

其中,Tree树组件是基于jQuery的zTree树插件进行封装。Tree组件所需要的数据是每个业务场景里自行传入的数据进行渲染的,以各个参数传参过去的,如::treeData=""渲染树的数组。

在之前的需求中,搜索条件的tree只有一棵树就满足了。最初设计CustomSearch组件与Tree组件渲染时,就很繁琐,只按照一棵树来渲染了。

在最新的需求里,需要两棵树的搜索条件了,动态请求回来的数据是可以把DOM渲染出来的。但是对于如何传参进去渲染不同的tree又如何对于点选的搜索条件进行获取,需要在原有的基础上进行扩展并兼容原有代码。

公号ID:zhaoxiajingjing
△14.3 需求:在搜索条件展示2+棵树

2 / 实践

1、原有支持1棵树

搜索条件的数据是封装在CustomSearch这个组件里面,但是Tree的数据需要单独请求,再放进去渲染的。
公号:朝霞的光影笔记
△14.4原有的组件只支持一棵树的展示

2、现在需求是展示2棵树

现在需要改成支持2+棵树,同时此种方法也支持1棵树的渲染,兼容之前的写法,这个在其他地方用的很多,不能影响别人的使用。

公号ID:zhaoxiajingjing
△14.5业务的CustomSearch高级搜索组件里面

MOCK模拟的接口地址:

form表单动态数据的接口[https://www.fastmock.site/moc...]

所属学院组织树[https://www.fastmock.site/moc...]

学科书籍树[https://www.fastmock.site/moc...]

/**
 * 设置tree的初始化数据的芳芳
 * @param [Object] treeJSON 展示tree的form项
 * @param [Array] treeKeys 展示tree的form项的key,
 * @returns {Object} 再把处理好的数据还回去
 */
treeMethod(treeJSON, treeKeys) {
    // 给每一个Tree添加初始化信息
    treeKeys.forEach((item, index) => {
        // treeParamsCustom 是一个对象,所以拿到它的堆内存地址即可
        let treeParams = treeJSON[item].formItemClass['treeParamsCustom'];
        // 初始化:treeData数据为空、默认不展示
        this.$set(treeParams, 'treeData', []);
        this.$set(treeParams, 'isShowTree', false);

        // 请求接口
        this.getTreeData([url], treeParams);
    });

    return treeJSON;
},
/**
 * 获取Tree的数据
 * @param url 请求接口
 * @param treeParams 需要设置的属性
 */
getTreeData(url, treeParams) {
    treeParams.isShowTree = false;

    _this.$http.get(url).then((response) => {
        let {data: {data: {treeData=[]}}} = response;
        if (response.processStatus) {
            treeParams.treeData = treeData;
            treeParams.isShowTree = true;
        }
    }, () => {
        treeParams.isShowTree = false;
        console.log('error');
    });
},

△应用:treeMethod方法,放在vue文件的methods对象里面

//获取isChecked属性为true的数据
getShowData(rows){
    let _this = this;
    _this.items=[];
    for(let i=0;i<rows.length;i++){
        let isChecked = rows[i].customClass.isChecked;
        let title = rows[i].title;
        if(isChecked){
            //当title为空时,一般都是跟前面的搜索内容相关联的,如果之前的不展示,则没有title的也不展示在搜索中
            if(!title && rows[i-1] && rows[i-1].customClass.isChecked){
                _this.items.push(rows[i]);
            }
            //如果是第一个则不与前面的搜索内容关联
            if(!title && !rows[i-1]){
                _this.items.push(rows[i]);
            }
            //正常的搜索内容,都会带title,
            if(title){
                _this.items.push(rows[i]);
            }
        }
    }
    /*
        以上代码主要是在把请求回来的数据处理成Form组件需要的格式
        https://www.iviewui.com/components/form#DTZJBDX
        在此时,把异步请求回来的Tree的数据放进来即可
    */
    if (typeof this.treeMethod === 'function') {
        /**
         * by jing_zhaoxia@sina.com
         * 把tree的信息放在form表单里面渲染
         * 初始化需要$set
         */
        let treeJSON = {};
        let treeKeys = [];
        // => 筛选出 treeJSON和treeKeys
        // => 设置一个对象作为命名空间,把用户操作的数据都放进去,这样在提交保存时候,可以统一删除掉即可
        _this.items.forEach((row, index) => {
            let {formItemClass: {formItemType}, key} = row;
            if (formItemType === 'Tree') {
                treeKeys.push(key);
                treeJSON[key] = row;
                // 补一个自定义的tree参数,后面提交时候可以删掉
                this.$set(treeJSON[key].formItemClass, 'treeParamsCustom', {});
            }
        });
        // => 把Tree的数据一一到对应的items里面
        this.getTreeParams(treeJSON, treeKeys)
            .then(result => {
                if (typeof result !== 'undefined' &&
                    ({}).toString.call(result) === '[object Object]') {
                    _this.items = _this.items.map((item, index) => {
                        if (treeKeys.includes(item.key)) {
                            item = result[item.key];
                        }
                        return item;
                    });
                }
            })
            .catch(err => {
                console.log(err);
            });
    }
},
// => 异步请求,这里使用 Promise来处理
getTreeParams(treeJSON = {}, treeKeys = []) {
    return new Promise((resolve, reject) => {
        const fn = this.treeMethod;
        let result = treeJSON;
        if (typeof fn === 'function') {
            result = fn(treeJSON, treeKeys);
            if (({}).toString.call(result) === '[object Object]') {
                resolve(result);
            }
        }
        resolve(result);
    });
},

△设置form的items项,放在vue文件的methods对象里面

公号:朝霞的光影笔记
△14.6兼容以前的代码

/**
 * 设置Tree组件需要的参数,兼容之前的写法
 * @param item 每一条form-item
 * @param constructor 当前值的数据类型
 * @param key 需要的key
 */
setTreeParams(item, constructor, key){
    if (item.formItemClass['treeParamsCustom'] !== undefined){
        let value = item.formItemClass['treeParamsCustom'][key];
        return ({}).toString.call(value) === `[object ${constructor}]` ? value : this[key];
    }
    return this[key];
},

△兼容以前的代码,放在vue文件的methods对象里面

3 / 预告

跟着jquery大佬学编程思想,还没有想好怎么写成文字描述的,先贴一个分享地址吧~

[https://github.com/jingzhaoxi...]

4 / 参考

运算符优先级[https://developer.mozilla.org...]

iviewui的Table组件[https://www.iviewui.com/compo...]

iviewui的Form组件[https://www.iviewui.com/compo...]

iviewui源码[https://github.com/view-desig...]

jQuery的zTree[http://www.treejs.cn/v3/api.php]

公号ID:zhaoxiajingjing


Pink
269 声望14 粉丝

没有什么是20遍解决不了的,如果有~那就再写20遍