软件工程实践2019第五次作业
博客链接
余泓:https://www.cnblogs.com/fishred/
杨文燕:https://www.cnblogs.com/address2019/
作业博客:https://edu.cnblogs.com/campus/fzu/2019FZUSEZ/homework/8736
GitHub项目地址
https://github.com/fishred2941214/031702409-031702411
具体分工
余泓:代码编写、ui设计
杨文燕:jstree使用、测试
PSP表格
PSP2.1 | Personal Software Process Stages | 预计耗时(分钟) | 实际耗时(分钟) |
Planning | 计划(估计这个任务需要多少时间) | 60 | 60 |
Development | 开发 | 1505 | 1295 |
Analysis | 需求分析(包括学习新技术) | 240 | 360 |
Design Spec | 生成设计文档 | 60 | 30 |
Design Review | 设计复审 | 30 | 10 |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 5 | 5 |
Designt | 具体设计 | 60 | 40 |
Coding | 具体编码 | 1080 | 960 |
Code Review | 代码复审 | 30 | 10 |
Test | 测试(自我测试、修改代码,提交修改) | 200 | 260 |
Reporting | 报告 | 100 | 130 |
Test Repor | 测试报告 | 30 | 30 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem&Process Improvement Plan | 事后总结并提出过程改进计划 | 60 | 90 |
合计 | 1765 | 1735 |
树的结构设计:从导师名下划分出三个节点代表三个不同学位(前提是该导师有三种学位的学生,若只有两种则划分出两个节点,以此类推),学位节点下有代表入学年份的节点,点开该节点就是该年份入学的该学位的学生。
设计实现
JsTree插件
Jstree插件的使用是为了实现树的展开和收起,除此之外还为树加入了修改,增加,删除节点的功能。
$(function(){$("#tree_"+k+num+"").jstree({
plugins : ["types","contextmenu"],
'core' : {
//允许callback,为了后面进行创建、重命名、删除、移动或复制等操作
"check_callback" : true,},
"types": {
"default" : {
"icon" :false, // 关闭默认图标
},
},
'contextmenu':{
'items' : {
'add':{
'label':'新增分类',
'action':function(obj){
//reference获取当前选中节点的引用
var inst = jQuery.jstree.reference(obj.reference);
//通过get_node方法获取节点的信息,类似于实例对象
var clickedNode = inst.get_node(obj.reference);
/*
inst.create_node 参数1:父节点 参数2:新节点的数据
参数3: 1)first:当前节点下的头部新增节点
2)last:当前节点下的尾部新增节点
3)before:当前节点同级的上部新增节点
4)after:当前节点同级的下部新增节点
参数4:回调函数
参数5:Boolean类型,内部参数,指示父节点是否成功加载
*/
var newNode = inst.create_node(clickedNode,
{ //'id': 'ajson20',
//'parent' : 'ajson2',
'icon' : 'jstree-file',
'text':'新节点'},'last',function(node){
//回调返回创建后点节点,给新节点改名
inst.edit(node,node.val);
},'');
}
},
'rename':{
'label':'修改分类',
'action':function(obj){
var inst = jQuery.jstree.reference(obj.reference);
var clickedNode = inst.get_node(obj.reference);
inst.edit(obj.reference,clickedNode.val);
}
},
'delete':{
"label": "删除分类",
'action':function(obj){
var inst = jQuery.jstree.reference(obj.reference);
var clickedNode = inst.get_node(obj.reference);
inst.delete_node(obj.reference);
}
}
}
}
});});
JsTree是一个jquery的插件,使用首先需要引用jquery
<script src="https://apps.bdimg.com/libs/jquery/2.1.4/jquery.min.js"> </script>//通过 CDN引用jquery
<script src="./vakata-jstree-c9d7c14/dist/jstree.min.js"></script> // 引入jstress定义的样式
<link rel="stylesheet" href="./vakata-jstree-c9d7c14/dist/themes/default/style.min.css"/>
更多详细的使用,可以查看官网或是搜索教程。官网地址:https://www.jstree.com
关键代码——对文本的分解以及对应标签的创建
//用正则表达式提取输入内容中的导师和不同届学生的信息
var patt1 = /导师\:[\u4e00-\u9fa5]{2,5}/g;
var patt2 = /\d{4}级博士生\:.*/g;
var patt3 = /\d{4}级硕士生\:.*/g;
var patt4 = /\d{4}级本科生\:.*/g;
var patt5 = /\d{4}/;
var k=0;//k值用来记录一共进行了几次输入
function empty(){
document.getElementById("original_tree").value = "";//清空输入框
}
function analysis(content,num){
var teacher = content.match(patt1);//提取出导师信息
if(teacher == null)
{
alert("您输入的格式不正确!");//输入为空或输入无关信息或没有导师信息时,提示输入不正确
return;
}
var t_div = document.createElement('div');
t_div.setAttribute("id","tree_"+k+num);
document.getElementById("tree").appendChild(t_div);
var r_tree = document.createElement('ul');//创建根节点
r_tree.setAttribute("id","root"+k+num);
document.getElementById("tree_"+k+num).appendChild(r_tree);
teacher = teacher.join("").split(":");//所有的join和split操作均为了去除输入中的多余符号,提取所需信息,此处为获取导师名
var ele = document.createElement('li');
ele.setAttribute("id","teacher"+k+num);//添加一个名为导师的节点
ele.innerHTML = teacher[1];//内容为导师姓名
document.getElementById("root"+k+num).appendChild(ele);
var doctor = content.match(patt2);
var master = content.match(patt3);
var undergraduate = content.match(patt4);
//若该导师名下有至少一名学生,则创建代表学位的节点
if(doctor!=null||master!=null||undergraduate!=null)
{
var degree = document.createElement("ul");
degree.setAttribute("id","degree"+k+num);
document.getElementById("teacher"+k+num).appendChild(degree);
}
//该导师有博士学生,则为该导师添加博士类节点,并按照入学年份对学生进行分类,以下的硕士类和本科生类相同
if(doctor!=null)
{
var doctor_degree = document.createElement("li");
doctor_degree.setAttribute("id","doctor_degree"+k+num);
doctor_degree.innerHTML = "博士生";
document.getElementById("degree"+k+num).appendChild(doctor_degree);
var doctor_grade = document.createElement("ul");
doctor_grade.setAttribute("id","doctor_grade"+k+num);
document.getElementById("doctor_degree"+k+num).appendChild(doctor_grade);
for(var i = 0;i < doctor.length;i++)
{
var str = doctor[i];
str = str.split(":");//将入学年份和学位和所有博士学生的姓名分离
var ele1 = document.createElement('li');
ele1.innerHTML = str[0].match(patt5);//得到入学年份
ele1.setAttribute("id","grade"+k+num+i);
document.getElementById("doctor_grade"+k+num).appendChild(ele1);
var doctor_name = document.createElement("ul");
doctor_name.setAttribute("id","doctor_name"+k+num+i);
document.getElementById("grade"+k+num+i).appendChild(doctor_name);
var name = str[1].split("、");//得到所有学生姓名
for(var j = 0;j < name.length;j++)
{
var ele11 = document.createElement('li');
ele11.innerHTML = name[j];
ele11.setAttribute("id","name"+k+num+i+j);
document.getElementById("doctor_name"+k+num+i).appendChild(ele11);
}
}
}
if(master!=null)
{
var master_degree = document.createElement("li");
master_degree.setAttribute("id","master_degree"+k+num);
master_degree.innerHTML = "硕士生";
document.getElementById("degree"+k+num).appendChild(master_degree);
var master_grade = document.createElement("ul");
master_grade.setAttribute("id","master_grade"+k+num);
document.getElementById("master_degree"+k+num).appendChild(master_grade);
for(var i = 0;i < master.length;i++)
{
var str1 = master[i];
str1 = str1.split(":");
var ele2 = document.createElement('li');
ele2.innerHTML = str1[0].match(patt5);
ele2.setAttribute("id","grade1"+k+num+i);
document.getElementById("master_grade"+k+num).appendChild(ele2);
var master_name = document.createElement("ul");
master_name.setAttribute("id","master_name"+k+num+i);
document.getElementById("grade1"+k+num+i).appendChild(master_name);
var name = str1[1].split("、");
for(var j = 0;j < name.length;j++)
{
var ele22 = document.createElement('li');
ele22.innerHTML = name[j];
ele22.setAttribute("id","name1"+k+num+i+j);
document.getElementById("master_name"+k+num+i).appendChild(ele22);
}
}
}
if(undergraduate!=null)
{
var undergraduate_degree = document.createElement("li");
undergraduate_degree.setAttribute("id","undergraduate_degree"+k+num);
undergraduate_degree.innerHTML = "本科生";
document.getElementById("degree"+k+num).appendChild(undergraduate_degree);
var undergraduate_grade = document.createElement("ul");
undergraduate_grade.setAttribute("id","undergraduate_grade"+k+num);
document.getElementById("undergraduate_degree"+k+num).appendChild(undergraduate_grade);
for(var i = 0;i < undergraduate.length;i++)
{
var str2 = undergraduate[i];
str2 = str2.split(":");
var ele3 = document.createElement('li');
ele3.innerHTML = str2[0].match(patt5);
ele3.setAttribute("id","grade2"+k+num+i);
document.getElementById("undergraduate_grade"+k+num).appendChild(ele3);
var undergraduate_name = document.createElement("ul");
undergraduate_name.setAttribute("id","undergraduate_name"+k+num+i);
document.getElementById("grade2"+k+num+i).appendChild(undergraduate_name);
var name2 = str2[1].split("、");
for(var j = 0;j < name2.length;j++)
{
var ele33 = document.createElement('li');
ele33.innerHTML = name2[j];
ele33.setAttribute("id","name2"+k+num+i+j);
document.getElementById("undergraduate_name"+k+num+i).appendChild(ele33);
}
}
}
k++;
}
创建的标签实例
<div id = "tree">
<!---
产生的数据格式如下:
<ul>
<li>张三
<ul>
<li>博士生
<ul>
<li>2016
<ul>
<li>
王五
</li>
<li>
李四
</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
-->
</div>
特点设计与展示
网页支持多组输入、多棵树并存,并且提供在节点处进行修改、删除和增加节点的操作。
重置:考虑到创建树时输入的数据会比较多,如果输入错误的数据或要清空上一次的输入时可以直接点击重置按钮直接清空输入框
增加、删除、修改节点:考虑到有可能导师会随时增加新的学生,有的学生会更换导师,有的学生名输入时打错,这时候如果再新建一棵树就需要重新输入大段数据,比较麻烦,所以增加了这三个功能
界面展示
初始界面
支持多组输入、多棵树并存
在节点处进行修改、删除和增加节点
目录说明与使用说明
目录说明
- 家族树
- vakata-jstree-c9d7c14:Jstree插件文件
- TREE.png:插入的图片
- tree.css:网页的css文件
- tree.html:网页的html文件
- 森林.png:插入的图片
- REAMDE:目录说明与使用说明
使用说明
- 请将家族树文件夹下载或是将上述的文件统一下载并放入同一文件夹中,以防无法完整显示。
- 使用浏览器打开tree.html文件,即可在浏览器中看到页面。
- 在左侧的文本框输入数据,点击确定按钮,将会在右侧生成一棵以导师为根节点的树。
- 点击小三角或是双击节点内容如“张三”可展开或收起节点。
- 在节点处单击右键,可进行修改、删除、添加节点的操作。
- 修改节点:对节点进行重命名。
- 删除节点:删除该节点及其下级节点。
- 添加节点:在该节点处新增一个下级节点。
单元测试
选用工具:Mocha
学习方式:通过学习测试框架Mocha实例教程,自己上手跟着教程操作一次就能大概了解框架的使用
对输入文本进行数据处理函数的测试:
// analysis.test.js
var analysis = require('./analysis.js');
var expect = require('chai').expect;//引入断言库“chai”的 expect 断言风格
var message1="李二";
var message2="琪七";
var message3="司四";
var message4="王五";
var message5="刘六";
var message6="许六";
var message7="张三";
var message8="刘一";
var message9="天一";
var message10="吴五";
describe('文本分割测试', function() {
it('学生节点测试', function() {//测试实例
expect(analysis()).to.include(message1);//expect断言:analysis函数返回的数组中包含学生姓名“李二”
});
it('学生节点测试', function() {
expect(analysis()).to.include(message2);
});
it('学生节点测试', function() {
expect(analysis()).to.include(message3);
});
it('学生节点测试', function() {
expect(analysis()).to.include(message4);
});
it('学生节点测试', function() {
expect(analysis()).to.include(message5);
});
it('学生节点测试', function() {
expect(analysis()).to.include(message6);
});
it('导师节点测试', function() {//expect断言:analysis函数返回的数组中包含导师姓名“张三”
expect(analysis()).to.include(message7);
});
it('学生节点测试', function() {
expect(analysis()).to.include(message8);
});
it('学生节点测试', function() {
expect(analysis()).to.include(message9);
});
it('学生节点测试', function() {
expect(analysis()).to.include(message10);
});
});
界面测试
生成的树默认为收起状态
无输入(显示提示语)
单组输入(显示一棵树)
单组乱序输入(显示一棵树)
多组输入(多棵树并存)
2组输入
2组乱序输入
4组输入
无导师节点(显示提示语)
一组有导师节点,一组无导师节点的输入(显示一棵树及提示语)
多次输入,一次一组输入(每一次多显示一棵树)
多次输入,一次多组输入(每一次多显示二棵树)
签入记录
代码模块异常
问题描述:如何使用动态的id号生成jstree
尝试:搜索、浏览有关jstree使用的相关内容
是否解决:已解决
问题描述:无法支持多组数据的输入以及创建多棵id不同的树
尝试:使用多层循环的嵌套,用split函数划分数据,通过标记位来识别这是第几棵树的第几个节点
是否解决:已解决
问题描述:数据无法放在正确的ul节点下
尝试:使用标记位经历循环的自加生成不同的id,id可识别这是第几棵树的第几个节点
是否解决:已解决
问题描述:做不到将不同的树关联到一起(如这棵树的导师是另一个导师的学生,将两棵树合并)
尝试:搜索、浏览了bootstrap、vue、element等框架和插件
是否解决:未解决
评价队友
杨文燕:我的搭档是一个行动力很强的人,经常会发现她在默默地编程。她对html、css以及JavaScript比较熟悉,所以这次代码的编写主要是她负责。她真的是一个很可靠的人。非常高兴我能够和她搭档完成这两次的结对作业。希望我们的交流能够更加得及时呀。
余泓:我的队友是一个很愿意去学习新知识,尝试新方法的人,这次的jstree框架和单元测试都是由她来负责,对于任务也很积极负责,从不拖沓。希望未来有机会我们再次合作的时候分工能够更加明确。