本文系原创,转载请注明出处:
https://blog.youkuaiyun.com/chengbao315/article/details/84171511
最近在做一个数据大屏的web项目,页面中经常会用到一些报表、图表之类的功能,对于图表我选择了 echart 插件,因为 echart 做图表非常方便,效果也比较满足要求,但是对于图表,我则用的是 html 的 table 标签,每次做都要敲一大堆 html 代码,还要重复的编写和修改 css 样式。都面向对象编程这么多年了,这种效率的开发方式怎么能符合我的气质?!!何况同样是做图表的 echart 的怎么使用那么方便? 于是我决定自己写一个插件库。
好了,进入主题,今天我要跟大家分享一个table 插件的制作思路,今后大家可以根据这个思路完善自己的插件库,当然如果我做好了,也会跟大家分享。话不多说,先看效果:
这是一个非常简单的 table 报表,但是如果你想实现这样的效果的话,我相信你也需要敲不少代码。而我的设计却可以在 html 里几乎不用写任何代码,并可以在其他场景无限复用。不信你看,我的 html 代码如下:
<head>
<title>Document</title>
<script src="cb_table.js"></script>
</head>
<body>
<div id="mytable" style="width:300px; height:200px; background: black"></div>
<script>
var mytable = cb_tables.init('mytable');
var option = {
title: {
text: '员工信息表',
},
dataSet: {
data: [['姓名', '性别', '年龄', '部门']],
default: {
columns: 4,
rows: 6,
}
},
};
mytable.setOption(option);
</script>
</body>
可以看到,我这里用于显示报表的是一个 div 块,那么首先要做的是引入我设计的 cb_table.js 插件,然后初始化对象传入 div 标签的 id 属性,最后根据需要设置自己喜欢的 option 属性就可以实现上述的报表功能。怎么样?用过 echart 的伙伴是不是对于这种操作似曾相识啊?接下来看我怎么实现的吧:
首先,我们新建一个 .js 文件,插件叫什么名大家自己定吧,然后我需要定义一个对象,里面定义一个 init() 方法,在 init() 方法中我们可以创建一个 table 对象,并进行简单的样式设置,代码如下:
var cb_tables = {
init: function (id) {
var div_tab = document.getElementById(id);
var table = document.createElement("table");
div_tab.appendChild(table);
table.style.width = '100%';
table.style.height = '100%';
table.style.borderCollapse = 'collapse';
return new Mytable(table);
},
}
完成这步,就实现了将你的 div 中添加了一个 table 标签,并且返回一个 Mytable 类型的对象。为什么要这么写,因为这是一种很好的封装设计,至始至终用户都不知道我生成的 table 到底长什么样的,拥有哪些属性和样式,你能做的只能是按照我提供的接口调用我的方法。哈哈哈,私有化属性就是这么牛。
接下来看我的 Mytable 构造方法如何写的吧,这里需要强调一下,构造函数和普通的函数没有什么区别,但是构造函数首字母一定要大写!
function Mytable(table) {
this.table = table;
this.setOption = function (option) {
// 复制全部设置属性
option = copyOption(options, option);
this.table.style.border = option.table.border;
if(!!option.title.text){
var th = document.createElement("caption");
table.appendChild(th);
th.innerHTML = option.title.text;
th.style.color = option.title.textStyle.color ;
th.style.textAlign = option.title.x ;
th.style.width = '100%';
th.style.height = '15%';
th.style.fontFamily = option.title.textStyle.fontFamily;
th.style.fontSize = option.title.textStyle.fontSize;
table.style.height = '87%';
}
var columns = option.dataSet.default.columns,
rows = option.dataSet.default.rows;
for (var i = 0; i < rows; i++) {
var tr = document.createElement("tr");
table.appendChild(tr);
tr.style.height = 100 / rows + '%';
tr.style.backgroundColor = i % 2 ? option.table.dColor : option.table.sColor;
for (var j = 0; j < columns; j++) {
var td = document.createElement("td");
tr.appendChild(td);
td.style.width = 100 / columns + '%';
td.style.border = option.table.border;
td.style.color = i == 0 ? option.table.header.textStyle.color : option.table.td.textStyle.color;
td.style.fontFamily = i == 0 ? option.table.header.textStyle.fontFamily : option.table.td.textStyle.fontFamily;
td.style.fontSize = i == 0 ? option.table.header.textStyle.fontSize : option.table.td.textStyle.fontSize;
td.style.textAlign = 'center';
td.innerHTML = !option.dataSet.data[i] ? '' : !option.dataSet.data[i][j] ? '' : option.dataSet.data[i][j];
}
}
}
}
可以看到,在构造函数中,首先将传入的 table 对象 存入到自己的 table 对象中,方便后面使用,如果不想 table 公开,可以声明为私有对象。 然后添加一个 setOption() 的公有方法,方法有一个形参 option ,用于设置 tbale 的样式。接下来的代码就好理解了,根据 option 设置 table 的样式,根据行数和列数动态给 table 添加行和列,显示表格,并设置样式。
一切看起来似乎非常的顺利,不过你有没有发现,我给出的 html 代码中好像并没有那么多的设置啊?这个 setOption() 方法中的那么多设置哪来的?看到这里就是本篇文章的关键之处了。关键!关键!关键!重要的事情说三遍,我们的插件中需要规定一些默认的配置和配置对象,这个配置可以根据项目需要写成通用的样式,这样当用户不需要特殊修改时,报表也会呈现出你想要达到的样子。好了,继续看代码:
var textStyle = {
color: 'cyan',
fontFamily: '楷体',
fontSize: '12px',
}
var options = {
title: {
text: '',
x: 'center',
y: 'middle',
textStyle: textStyle,
},
table: {
border: '1px solid rgb(0,0,0)',
sColor: 'rgba(0,0,0,0.1)',
dColor: 'rgba(21,22,88,0.5)',
header: {
textStyle: textStyle,
},
td: {
textStyle: textStyle,
}
},
dataSet: {
data: '',
default: {
columns: 4,
rows: 6,
}
},
};
我这里只列举了几个简单的样式对象,当你的需求要求越来越高时,这个默认的样式可以完善的越来越详细,并且抽象成更具体的样式对象,就像 echart 那样。那么问题来了,当用户设置了某些样式,而其他样式缺省时,我们的 table 是如何获取到全部的样式属性的呢?这时要编写一个方法,我姑且叫做 copyOption() 方法,代码如下:
var copyOption = function (source, option) {
if (!option) {
option = source;
return option;
};
for (var prop in source) {
if (typeof (source[prop]) == 'object') {
if(!!option[prop]){
option[prop] = copyOption(source[prop], option[prop]);
}
else{
option[prop] = source[prop];
}
} else {
if(!option[prop]){
option[prop] = source[prop];
}
}
}
return option;
}
方法有两个形参,需要复制的源option,用户提供的设置option,最后返回合并完整的option 对象。这个方法中使用到了递归的编程思想,需要注意的是,我们的对象类型是引用类型,不可以直接复制,因为直接复制的是引用地址,这时需要将对象递归调用该函数进行进一步深入复制,这块不理解的小伙伴可以留言,或者搜索一下 JavaScript 深复制,学习一下相关的知识回头再理解这段代码。
以上,就实现了文章开头我所提到的所有功能,虽然功能还不太完善,但是用起来确实方便多了,以后我也会进一步完善。对于这篇文章感兴趣的小伙伴可以跟我一起探讨,多提宝贵意见,感谢大家支持!!!