2020软工实践第二次结对作业
2020软工实践第二次结对作业
这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/SE2020 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/SE2020/homework/11277 |
这个作业的目标 | 锻炼协作能力,学习js、html知识,熟练github使用 |
学号 | 031802443 |
结对同学的博客链接: | https://www.cnblogs.com/alangakong/p/13786718.html |
你们队创建的仓库的GitHub项目地址 | https://github.com/huipai/031802443-031802406 |
一、psp表格
PSP2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 10 |
Estimate | 估计这个任务需要多少时间 | ||
Development | 开发 | 60 | 500 |
Analysis | 需求分析 (包括学习新技术) | 800 | 1200 |
Design Spec | 生成设计文档 | 30 | 30 |
Design Review | 设计复审 | 60 | 60 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 40 | 40 |
Design | 具体设计 | 60 | 60 |
Coding | 具体编码 | 300 | 500 |
Code Review | 代码复审 | 180 | 180 |
Test | 测试(自我测试,修改代码,提交修改) | 180 | 180 |
Reporting | 报告 | 60 | 120 |
Test Report | 测试报告 | 60 | 120 |
Size Measurement | 计算工作量 | 30 | 20 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 60 | 80 |
合计 | 1930 | 3100 |
二、成员及分工
成员 | 分工 |
---|---|
胡烨艳 | UI设计、后期美化 |
郑民浩 | 生成树js的编码实现 |
三、解题思路描述与设计实现说明
代码实现思路
本次编程使用了jstree插件,因此,关键在于如何对输入的数据进行转换,使其成为插件所能使用的对象。首先将输入数据按组分解,对每位导师独立的生成一组树,接着按关键字划分,从导师名下划分出代表不同学位的节点,不同学位节点中划分出不同年级的节点,年级节点内包含该级学生。
关键实现的流程图或数据流图
重要的/有价值的代码片段
function buildTree(str,num,submitNum)
//submitNum为用户的输入次数,num为在该次输入中,这是第num组数据
{
var supervisorPatt=/导师:.*/g; //利用正则表达式提取导师与学生信息
var doctorPatt=/\d{1,}级博士生:.*/g;
var postgraduatePatt=/\d{1,}级硕士生:.*/g;
var undergraduatePatt=/\d{1,}级本科生:.*/g;
var numberPatt=/\d{1,}/g; //利用正则表达式提取入学年份
var supervisor=str.match(supervisorPatt); //得到导师信息
if(supervisor==null) //导师信息为空
{
alert("请按照格式输入导师名");
throw new Error(result.error_code);
}
else if(supervisor.length>1) //一组数据中读取到多位导师信息
{
alert("每位导师之间空行数为2");
throw new Error(result.error_code);
}
var treeDiv=document.createElement('div'); //建立该树的div块
treeDiv.setAttribute("id","tree"+submitNum+num);
document.getElementById("studyTree").appendChild(treeDiv);
var treeRoot=document.createElement('ul'); //根据jstree格式,建立根节点
treeRoot.setAttribute("id","root"+submitNum+num);
document.getElementById("tree"+submitNum+num).appendChild(treeRoot);
supervisor=supervisor.join("").split(":"); //从提取出的导师信息中进行拆分得到导师名
var ele = document.createElement('li'); //建立导师节点
ele.setAttribute("id","supervisor"+submitNum+num);
ele.innerHTML=supervisor[1];
document.getElementById("root"+submitNum+num).appendChild(ele);
var doctor=str.match(doctorPatt); //在输入数据中寻找硕士,博士,本科三种学位
var postgraduate=str.match(postgraduatePatt);
var undergraduate=str.match(undergraduatePatt);
if(doctor||postgraduate||undergraduate) //如果有学生数据,则建立学位根节点
{
var degree = document.createElement('ul');
degree.setAttribute("id","degree"+submitNum+num);
document.getElementById("supervisor"+submitNum+num).appendChild(degree);
}
else
{
alert("注意:输入的数据中导师"+supervisor[1]+"没有检测到学生数据,如果数据无误,请忽略该提示")
}
if(doctor) //如果有博士数据,则按照插件格式建立节点
{
var doctorDegree=document.createElement('li'); //建立博士节点
doctorDegree.setAttribute("id","doctorDegree"+submitNum+num);
doctorDegree.innerHTML="博士生";
document.getElementById("degree"+submitNum+num).appendChild(doctorDegree);
var doctorGradeUl=document.createElement('ul'); //建立博士年级根节点
doctorGradeUl.setAttribute("id","doctorGradeUl"+submitNum+num);
document.getElementById("doctorDegree"+submitNum+num).appendChild(doctorGradeUl);
for(var i=0;i<doctor.length;i++) //读取所有年级的博士数据并建立各个年级节点
{
var doctorStr=doctor[i];
doctorStr=doctorStr.split(":"); //将冒号前后区分开,前为年级,后为具体学生
var doctorGradeLi=document.createElement('li'); //建立年级节点
doctorGradeLi.innerHTML=doctorStr[0].match(numberPatt);
doctorGradeLi.setAttribute("id","doctorGradeLi"+submitNum+num+i);
document.getElementById("doctorGradeUl"+submitNum+num).appendChild(doctorGradeLi);
var doctorNameUl = document.createElement('ul'); //建立学生根节点
doctorNameUl.setAttribute("id","doctorNameUl"+submitNum+num+i);
document.getElementById("doctorGradeLi"+submitNum+num+i).appendChild(doctorNameUl);
var nameValue=doctorStr[1].split("、"); //将所有学生拆分开
for(var j=0;j<nameValue.length;j++) //建立各个学生的独有节点
{
var doctorNameLi=document.createElement('li');
doctorNameLi.setAttribute("id","暂无"+nameValue[j]+submitNum+num);
doctorNameLi.innerHTML=nameValue[j];
document.getElementById("doctorNameUl"+submitNum+num+i).appendChild(doctorNameLi);
}
}
}
//硕士生与本科生节点建立过程类似,不再重复展示
str=str.split("\n\n"); //拆分个人经历与技能
for(var i=1;i<str.length;i++) //遍历所有经历
{
//由于学生节点id根据姓名生成,因此直接提取经历中的姓名,并进行搜索,将该学生id改为姓名+技能与经历,方便生成点击事件
var abilityStr=str[i];
abilityStr=abilityStr.split(":");
if(!document.getElementById("暂无"+abilityStr[0]+submitNum+num))
{
alert("没有找到姓名为"+abilityStr[0]+"的学生,该条技能经历添加失败");
}
else
{
document.getElementById("暂无"+abilityStr[0]+submitNum+num).setAttribute("id",abilityStr[0]+"的技能及其经历:"+abilityStr[1]);
}
}
四、网页展示
五、附加特点设计与展示
设计的创意及设计的意义
考虑到多次输入数据可能会出现错误,提供了重置功能来清空树与文本框
考虑到可能会有新的数据,因此在已生成的树中提供了新增节点,删除节点与重命名节点功能
为了使修改方便,提供了拖动节点修改树的功能
实现思路
参考jstree api文档,与博客,对jstree进行配置
贴出你认为重要的/有价值的代码片段,并解释【2分】
$(function(){$("#tree"+submitNum+num+"").jstree({
plugins : ["types","contextmenu","state","dnd"],
'state': {
"opened":true //默认生成树展开
},
'core' : {
"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);
//建立新节点
var newNode = inst.create_node(clickedNode,
{
'icon' : false,//关闭默认图标
'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);
}
}
}
}
});});
支持多组输入、多次输入、多棵树并存,支持新增节点,删除节点,修改节点名,以及拖动节点来对树进行重新排列,以及重置功能
多组输入
多次输入
右键点击子节点即可打开菜单栏
拖动节点进行修改
六、目录及使用说明
目录说明
-
JS文件夹:myJs.js文件包含了生成学术树的主体js代码以及所用到的jQuery和jsTree文件。
-
dist文件夹:jstree插件目录
-
image文件夹:插入的图片
-
css文件夹:网页的css文件
-
index.html:网页的html文件
-
REAMDE.md:目录说明与使用说明
使用说明
-
请将上述的文件统一下载并放入同一文件夹中,以防出现异常。
-
使用浏览器打开index.html文件,即可在浏览器中看到页面。
-
在左侧的文本框输入数据,点击生成按钮,将会在右侧生成一棵以导师为根节点的树。
-
输入时每组数据之间空两行,个人技能之间空一行,数据中的符号使用中文半角字符。
-
点击重置按钮,将会清空文本框内容与已生成的树。
-
点击学生类型节点,将会出现个人技能及经历。
-
点击小三角或是双击节点内容如可展开或收起节点。
-
在节点处单击右键,可进行修改、删除、添加节点的操作。
-
修改节点:重新命名选中节点。
-
删除节点:删除选中节点及其子节点。
-
新增节点:在该节点处新增一个子节点。
-
支持拖动节点进行树的修改。
-
如何运行网页
下载完整代码后,保证其他文件夹也在其中,都是引用本地相对路径的文件
使用谷歌浏览器打开index.html 即可打开网页
在学术树生成器中,按照样例输入并生成即可获得对应的生成树
点击姓名可查看技能树以及经历
七、单元测试
测试工具
选用了mocha测试工具,根据教程http://www.ruanyifeng.com/blog/2015/12/a-mocha-tutorial-of-examples.html学习mocha的使用
简易教程
首先安装node.js,安装参照https://www.runoob.com/nodejs/nodejs-install-setup.html
接着打开cmd,输入npm install --global mocha
安装mocha
参照上文mocha教程编写测试代码
部分单元测试代码
利用describe块形成测试套件,调用node提供的assert断言完成测试
describe('异常情况', () => {
it('输入数据为空', () => {
assert.throws(() => {//希望抛出异常
test.spiltInput("")
},Error);
});
it('导师信息格式错误1', () => {
assert.throws(() => {
test.buildTree(`导师张三
2016级博士生:天一、王二、吴五
2015级硕士生:李四、王五、许六
2016级硕士生:刘一、李二、李三
2017级本科生:刘六、琪七
刘六:JAVA、数学建模
李二:字节跳动、京东云`)},Error);
});
});
- 运行结果
构造测试思路
将测试分为正常情况与异常情况两种,考虑用户可能对格式的不熟悉,在异常情况中构造了不按格式输入的数据,在正常情况中则构造了单颗树与多颗树的输入数据
八、Github的代码签入记录截图
九、遇到的代码模块异常或结对困难及解决方法
问题:css界面设计不熟悉,网页ui不够美观
解决:通过查询文档,观看视频,略微美化了网页
问题:技能树的添加
解决:将技能作为个人id,添加点击事件显示节点id
问题: mocha环境搭建
解决: 看了很多博客,每次运行都报错,之后清理了数据才安装好
问题: 测试用例的编写
解决: 因为从前没有做测试的习惯,因此没有基础,跟着教程从头学习
十、收获
- 在做中学习了js、html、css三种语言
- 熟悉了github的团队开发
- 熟悉了单元测试
十一、评价你的队友
值得学习的地方:做事认真仔细踏实,很好沟通,团队合作遇到问题可以很及时的解决
需要改进的地方:我们都是小白,还需提高代码能力