2020软件工程实践第二次结对编程作业

学术家庭树的前端实现

GitHub仓库地址
结对伙伴博客地址

这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/SE2020
这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/SE2020/homework/11277
这个作业的目标 <学习html,css,havascript前端设计语言,实现简单地页面布局>
学号 <041801520(林志炜)

结对分工

  • 031802403陈晓杰:前端页面设计以及实现,找到合适的框架并且引用
  • 041801520林志炜:算法设计以及实现,单元测试,代码debug,优化
  • 共同讨论完成代码的整合,互相指出各自实现的不足,列出PSP表格

PSP表格

PSP Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划 60 30
Estimate 估计这个任务需要多少时间 480 420
Development 开发 300 360
Analysis 需求分析 (包括学习新技术) 720 720
Design Spec 生成设计文档 30 30
Design Review 设计复审 30 30
Coding Standard 代码规范 (为目前的开发制定合适的规范) 20 20
Design 具体设计 60 50
Coding 具体编码 120 120
Code Review 代码复审 20 60
Test 测试(自我测试,修改代码,提交修改) 60 60
Reporting 报告 30 30
Test Report 测试报告 20 15
Size Measurement 计算工作量 15 15
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 30 25
合计 1995 1985

解题思路及设计实现

  • 在最开始的时候我们想的是简单使用创建树的算法,然后嵌入到框架树里面,并且添加一个节点缩放的函数。
  • 基于最开始的构想我们是创建了一个css外部文件,为了方便之后的页面布局的修改。然后再最后的html文件里面引用该css文件。
  • 实现最终学术家庭树最基本的思想是调用了javascript库,然后创建树数据接口,用算法实现数据的填充即节点扩充,并且在创建每一棵树后进行一次树的递归检测判断是否有的人既是导师又是学生,调整关联树。最后便是在每个页节点后面如果有必要的话添加一个子节点扩展成技能树。即tree.js文件包括检查函数,节点的缩放函数,树的建立函数。
  • 关键树算法流程图
  • 数据流图
  • 创建一个类似树的数组方便建树以及储存数据
 var treeData = [{
             "name": "",
             "parent": "",
             "experience": "",
             "children": [{
                 "name": "",
                 "parent": "",
                 "experience": "",
                 "children": [{
                     "name": "",
                     "parent": "",
                     "experience": ""
                 }]
  • 解析输入储存技能树数据
var count = 0; //定义儿子节点的编号
             var flag = 0; //定义标志是否为关联树值为1
             var all_data = document.getElementById("user").value;
             var sclice_data = [];
             var model_data = [];
             var skill_data = [];
             var sk_data = [];
             var sk_num = 0;
             var daoshi_num = 0;
             model_data = all_data.split("

");  //用split函数对输入文本进行分块
             for(var x = 0; x < model_data.length; x++)
             {
                sclice_data = model_data[x].split("
"); 
                if (sclice_data.length == 1 )
                {
                    var hb = sclice_data[0].split(":");
                    var head_tmp = hb[0];
                    var body_tmp = hb[1];
                    if(head_tmp != "导师")
                    {
                        sk_data = body_tmp.split("、");
                        var ct = 0;
                        skill_data[sk_num] = new Array();
                        skill_data[sk_num][ct++] = head_tmp;
                        for(var n = 0; n < sk_data.length; n++)
                        {
                            skill_data[sk_num][ct++] = sk_data[n];  //储存数据方便后续建树以及判断
                        }
                        sk_num += 1;                    }
                }
                else if(sclice_data.length > 1 || (sclice_data.length == 1 && sclice_data[0].split(":")[0] == "导师"))
                daoshi_num++;   //统计导师文本数据个数
             }

  • 简单解析带导师的文本,并进行存储,过程调用check函数检测是否为关联树,最后调用建树函数
for (var j = 0; j < daoshi_num; j++) 
             {
     //            document.write(j);
                 //初始化变量
                 count = 0;
                 //flag = 0;
                 quanju_flag = 0;
                 count_shu = 0
                     //treeData[j] = [{}];
                     //shuInit();
                 sclice_data = model_data[j].split("
");
     //            document.write(sclice_data);
                 for (var i = 0; i < sclice_data.length; i++) 
                 {
                     var head_tmp = "";
                     var body_tmp = "";
                     var kk = 0;
                     //fflag = 0;

                     var hb = sclice_data[i].split(":"); //从冒号分割一层字符串
                     head_tmp = hb[0];
                     body_tmp = hb[1];
                     //处理冒号前的部分
                     if (head_tmp == "导师") 
                     {
                         //document.write("<br /><br />"+body_tmp+"<br />");
                         var daoshi2 = {
                             "name": body_tmp,
                             "parent": "null",
                             "children": [{}]
                         }
                         treeData[j] = daoshi2; //将导师嵌入节点
                     } 
                     else
                     {

                         //document.write("<br />");
                         var children = {
                             "name": head_tmp,
                             "parent": "null",
                             "children": [{}]
                         }
                         treeData[j].children[count] = children; //将年级及职业嵌入节点
                         var bodies = body_tmp.split("、");
                         //document.write("姓名:");
                         for (var kk = 0; kk < bodies.length; ++kk) 
                         {
                             //document.write("|");
                     //        document.write(bodies[kk]+"|");
                             var op_flag = -1;
                             for(var op = 0; op < sk_num; op++) 
                             {
                        //         document.write(skill_data[op][0]);
                                 if(skill_data[op][0] == bodies[kk])
                                 {
                             //        document.write("557");
                                    var children = 
                                    {
                                        "name": bodies[kk],
                                        "parent": "null",
                                        "children": [{}]
                                    }
                                    op_flag = op;
                                    break;
                                 }
                        //         document.write("557");
                             }	
                             if(op_flag == -1)
                             {
                                    var children = 
                                    {
                                        "name": bodies[kk],
                                        "parent": "null",
                                        "children": []
                                    }
                             }
                                 //treeData.push(children);
                             treeData[j].children[count].children[kk] = children; //将姓名嵌入节点
                             //console.log(treeData);
                             //弹出学生名
                             //alert(treeData[j].children[count].children[kk].name);
                             if(op_flag != -1)
                             {
                                 for(var ye = 0; ye < skill_data[op_flag].length - 1; ye ++)
                                 {
                                     var children = 
                                     {
                                         "name": skill_data[op_flag][ye + 1],
                                         "parent": "null",
                                         "children": []
                                     }
                                     treeData[j].children[count].children[kk].children[ye] = children;
                                 }
                             }
                         }
                         count++; //第二子节点编号加一,生成下一个第二子节点
                     }
                 }
                 //一棵树型数据构造完成
                 //alert(treeData[j].length);
                 //和前面所有的树比较,判断是否为关联树
                 var tree_tmp = treeData[j];
                 var name_tmp = treeData[j].name;
                 for (num_tmp = 0; num_tmp < j; num_tmp++) {
                     check(treeData[num_tmp], name_tmp, tree_tmp, num_tmp);
                 }
                 if (!quanju_flag) count_shu++; //若有关联树则独立树的棵数不增加

                 //重新生成树
                 //shuInit(j);
                 //update(source);
             }

             //生成所有树
             // alert("shu: " + count_shu);
             for (var i = 0; i <= count_shu; i++) 
             {
                 shuInit(i)
             }
         }

特点设计及展示

  • 界面采用多个区域分块搭配颜色,并且引用了动漫图作为背景,较为酷炫
    实现思路为外部css文件调整各项文本框区域数值,html引用页面布局
    代码如下:
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8">
    <title>学术家族树</title>
</head>

<body>
    <script type="text/javascript" src="d3.v3.min.js"></script>
    <script type="text/javascript" src="tree.js"></script>
    <link href="style.css" type="text/css" rel="stylesheet" />

    <div class="bg-container"></div>
        <div class="flex-item1">
            <p id="titlename">INPUT</p>
        </div>
        <div class="flex-item2">
            <textarea style="background:none;border-style:none;" id="user"></textarea>
            </br>
            <button id="textbotton" onClick="chase()">生成家族树</button>
        </div>
        <div class="flex-item3">
            <p id="show-tree">学术家族树</p>
        </div>
    </div>
</body>

</html>
  • 成果展示
  • 创建树的过程借鉴了模板,最终生成比较生动的树,看起来更为美观,且能够进行点击缩放展开
    实现思路为建立缩放函数,节点扩充函数,以及最终的建树函数
  • 成果展示

目录及使用说明

  • tree1.rar
    • inde.html
    • style.css
    • README.md
    • tree.js
    • d3.v3.min.js
  • style.css是外部的界面布局文件,最终被inde.html文件引用
  • README.md是使用文档,类似于说明书
  • d3.v3.min.js是一个库
  • tree1.js是最新版核心算法生成树的文件,利用json数据生成树并且创立
  • inde.html是整体界面布局文件,引用了css以及js文件进行最后网页的生成,包含生成学术家族树按钮进行结果输出

测试人员使用说明

  • 测试人员使用时下载tree1.rar文件并解压到同一个文件夹下,最后点击html文件运行网页,因为背景是引用链接,因此联网才能有更好地体验,在右侧的空白文本框中输入文本数据,点击右下角的生成按钮,最终在左侧会生成所需要的学术家族树,并且鼠标点击可以缩放或者扩展。
  • 切记每组数据之间一定要空一行符合规范,最后一组数据最后一行不要换行,否则会导致结果出错。
以下是几组测试数据可供参考
导师:张三
2016级博士生:天一、王二、吴五
2015级硕士生:李四、王五、许六
2016级硕士生:刘一、李二、李三
2017级本科生:刘六、琪七、司四

刘六:JAVA、数学建模

李二:字节跳动、京东云
  • 输出结果
导师:张三
2016级博士生:天一、王二、吴五
2015级硕士生:李四、王五、许六
2016级硕士生:刘一、李二、李三
2017级本科生:刘六、琪七、司四

导师:吴五
2016级博士生:天二、王四、吴六
2015级硕士生:李一、王八、许七
2016级硕士生:刘三、李八、李二
2017级本科生:刘一、琪八、司四九

导师:刘2
2016级博士生:天四、王九
2015级硕士生:李四
2016级硕士生:刘一
2017级本科生:刘一
  • 输出结果

GitHub签入记录截图

单元测试

  • 首先是最简单的对树节点个数测试,截图如下

    可以大概看到对于树建立后节点个数的统计从单元测试来看是正确的。
  • 对子节点名字的查询验证,若是正确返回1,否则返回0,截图如下

    emm,节点的查询最后可以验证是正确的,保证了我们解析数据后以树的形式储存数据的正确性。

单元测试教程

  • 首先因为要配置mocha需要node环境,因此应该先下载node.js,安装教程
  • 下载好后在cmd窗口使用命令npm install mocha,但是之后在某个文件夹进行测试有时还需要局部配置chai等模块。
  • emmm,然后我只会照猫画虎写简单地测试程序,大概就像这里吧
  • 最后就是cmd窗口调试命令运行,上面链接都有,就不献丑了。

结对困难及解决

  • 问题:最开始对网页的制作一无所知,对于作业无从下手。
    解决:我们通过b站,菜鸟教程自学,达到了入门水平,看得懂框架,懂的引用。
  • 问题:尽管达到了入门水平,但是整体的代码编写页面布局还是较为困难。
    解决:调用了javascript自带的库,并且参考了一些页面布局代码,最终较为坎坷的实现了自己的网页界面,虽然看起来较为简约,但是实现过程并不是一帆风顺,所幸最后我们成功了,做出一个属于自己的网站界面。
  • 问题:我们找到了树的生成框架,但是困扰与如何进行数据的解析,储存,并且最后生成树。
    解决:最终借鉴了前辈们使用json数据类型生成树的方法,但是数据储存过程中出现了数据丢失,或者是数据格式导致了无法输出的情况,最后通过一行行的debug,中间使用了无数次的文本输出从注释中可以看到,最终实现了树的输出并且附带技能树。
  • 这次的网页制作经历收获了简单网页的布局,懂得使用框架,修改一些关键性的数据。同时学会了网页如何实现会更方便于后期的修改制作,以及在生成树算法方面有了深刻的理解。最后就是对于文本框输入数据的处理上,体会到了split函数的妙用,以及javascript数组的使用,最为关键的就是二维数组不能简单定义,否则会出现数据丢失的情况。

队友的评价

  • 值得我学习的地方是他对作业完成的积极性。以及在过程中提供较为关键的修改意见。
  • 需要改进的大概就是我们都需要提高知识面以及代码能力,不然每做一次作业都要死要活的。
原文地址:https://www.cnblogs.com/e557/p/13797001.html