文 / 景朝霞
来源公号 / 朝霞的光影笔记
ID / zhaoxiajingjing
图 / 自己画
❥❥❥❥点个赞,让我知道你来过~❥❥❥❥
【前情提要】
【iview】
0 / 读 iview 源码
上个月,用iview的Table组件做合并行的效果,认真的读了下那块的源码,收获颇多。
△14.1iview的Table组件的行/列合并
https://github.com/view-desig...
△14.2在table.vue的props对象里面定义属性
它使用在这:
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
in
11
===
全等号10
&&
逻辑与6
||
逻辑或5
(2)A||B
A为真,则返回A;A不为真,则返回B
A&&B
A为真,则返回B;A不为真,则返回A
(3)prop in obj
in判断对象中是否有属性prop
(4)==
等号:只需要判断值是否相等,如果两边数据类型不一致,先转换数据类型,再进行判断
===
全等号:严格判断,既判断数据类型也判断值,都相等才为TRUE
(5)为FALSE的5种情况:''
空字符串、null
、undefined
、0
、NaN
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又如何对于点选的搜索条件进行获取,需要在原有的基础上进行扩展并兼容原有代码。
△14.3 需求:在搜索条件展示2+棵树
2 / 实践
1、原有支持1棵树
搜索条件的数据是封装在CustomSearch这个组件里面,但是Tree的数据需要单独请求,再放进去渲染的。
△14.4原有的组件只支持一棵树的展示
2、现在需求是展示2棵树
现在需要改成支持2+棵树,同时此种方法也支持1棵树的渲染,兼容之前的写法,这个在其他地方用的很多,不能影响别人的使用。
△14.5业务的CustomSearch高级搜索组件里面
MOCK模拟的接口地址:
form表单动态数据的接口[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]
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。