软件工程实践第4次作业-结对编程之实验室程序实现

链接
    作业链接:https://edu.cnblogs.com/campus/fzu/SE2020/homework/11277
    github项目地址:https://github.com/Limerence910/031802114-031802131.git
    结对同学(博客地址):031802114黄颜熠
具体分工
    031802114黄颜熠:数据处理,算法设计与实现,el-tree的设计,博客撰写
    031802131吴鹏辉:网页前端设计,单元测试,el-tree的设计,博客撰写

PSP表格

PSP2.1 Personal Software Process Stages 预估耗时(分钟) 实际耗时(分钟)
Planning 计划
Estimate 估计这个任务需要多少时间 70 75
Development 开发
Analysis 需求分析 (包括学习新技术) 90 130
Design Spec 生成设计文档 70 80
Design Review 设计复审 70 90
Coding Standard 代码规范 (为目前的开发制定合适的规范) 70 65
Design 具体设计 110 90
Coding 具体编码 650 750
Code Review 代码复审 100 110
Test 测试(自我测试,修改代码,提交修改) 110 130
Reporting 报告
Test Report 测试报告 70 60
Size Measurement 计算工作量 70 70
Postmortem & Process Improvement Plan 事后总结, 并提出过程改进计划 130 150
合计 1610 1760

解题思路描述与设计实现说明

代码实现思路

  • 我们讨论对比了多种树形框架,决定使用element-ui框架的tree树形控件来实现学术家族树的生成
  • 对输入的文本进行数据处理,将其转化为el-tree组件可用的数据格式(json格式)。
  • 动态增加节点生成树,再使用element-ui框架中的@on-click添加点击事件,对生成的树添加搜索、增加节点、删除节点、移动节点等功能
  • 在树关联时,先建立新树,之后在前树遍历导师的姓名,若找到则加入到前树的子节点中,否则作为一棵新树显示在网页上。
  • 使用CSS对网页进行美化,修改文字的大小和颜色,设计简洁、清新的美化风格。
  • 将整个HTML划分为HTML + CSS + JS文件,编写单元测试文件,形成项目的基本架构。

关键实现的流程图或数据流图

重要的/有价值的代码片段

<!-- 设置树形控件以及节点属性和操作功能 -->
    <el-tree
        :data="data"
        node-key="id"
        :props="defaultProps"
        :filter-node-method="filterNode"
        
        style="
                position: absolute;
                top: 110px;
                left: 560px;
                border-bottom-right-radius: 10px;
                border-bottom-left-radius: 10px;
                border-top-left-radius: 10px;
                border-top-right-radius: 10px;
                "
        draggable
        :allow-drop="allowDrop"
        :allow-drag="allowDrag"
        ref="tree">
        <span class="custom-tree-node" slot-scope="{ node, data }">
        <span>{{ node.label }}</span>
            <span>
                <!-- 点击"+"号添加成员 -->
                <el-button
                        type="text"
                        size="mini"
                        @click="() => append(node, data)"
                        class="el-icon-plus">
                </el-button>
                <!-- 点击"-"号删除成员 -->
                <el-button
                        type="text"
                        size="mini"
                        @click="() => remove(node, data)"
                        class="el-icon-delete">
                </el-button>
            </span>
        </span>
    </el-tree>
/* 获取输入框信息以及文本处理 */
        CInput() {
            var str = "";
            /* 以换行符分隔 */
            lines = this.mytext.split('\n');
            for (var i = 0; i < lines.length; i++){
                var parts = lines[i].split(":");
                if(parts[0] != ""){
                    if(parts[0] === "导师"){
                        str = parts[1];
                        var newChild = { id: this.id2++, label: lines[0], children: [] };

                        this.data3.push(newChild);
                    }
                    else{
                        /* 正则表达式判断关键词 */
                        if(parts[0].search(/博士生|硕士生|本科生/) != -1){
                            parts2 = parts[0].split("级");
                            if(this.Grade.indexOf(parts2[0]) >= 0){
                                number = this.Grade.indexOf(parts2[0]);
                            }
                            else{
                                this.number2 = 0;
                                newChild = {id: this.id2++, label: parts2[0] + "级", children: []};
                                this.data3[this.teacher].children.push(newChild);
                                this.Grade.push(parts2[0]);
                                number = this.Grade.indexOf(parts2[0]);
                            }

                            newChild = {id: this.id2++, label: parts2[1],children: []};
                            this.data3[this.teacher].children[number].children.push(newChild);

                            students = parts[1].split('、');
                            for (var j = 0; j < students.length; j++){
                                newChild = {id: this.id2++, label:students[j], children: []};
                                this.data3[this.teacher].children[number].children[this.number2].children.push(newChild);
                                /* 存储节点名,节点位置的信息以便查询插入 */
                                this.Name.push(students[j]);
                                this.Location.push([this.teacher,number,this.number2,this.id]);
                                this.id++;
                            }
                            this.id = 0;
                            this.number2++;
                        }
                        else{
                            experience = parts[1].split('、');
                            for(var k = 0; k < this.Name.length; k++){
                                if(this.Name[k] === parts[0]){
                                    for(var l = 0; l < experience.length; l++){
                                        newChild = {id: this.id2++, label: experience[l] };
                                        this.data3[this.Location[k][0]].children[this.Location[k][1]].children[this.Location[k][2]].children[this.Location[k][3]].children.push(newChild);
                                    }

                                }
                            }

                        }
                    }
                }
            }
            /* 关联树判断与建立 */
            this.related(str);

            /* 清空中间变量与数组 */
            this.Grade = [];
            this.Location = [];
            this.students = [];
            this.data3 = [];
            this.Name = [];
            this.number = 0;
            this.number2 = 0;
            
        },
related(str){
            var flag = false;
            /* 查找关联节点 */
            for(var i = 0;i < this.data.length; i++){   
                for(var j = 0;j < this.data[i].children.length; j++){
                    for(var k = 0; k < this.data[i].children[j].children.length; k++){
                        for(var l = 0; l < this.data[i].children[j].children[k].children.length; l++){
                            if(this.data[i].children[j].children[k].children[l].label === str){
                                flag = true;
                                for(var m = 0; m < this.data3[0].children.length; m++)
                                    this.data[i].children[j].children[k].children[l].children.push(this.data3[0].children[m]);
                            }
                        }
                    }
                }
            }       
            /* 若无关联节点添加为新树 */     
            if(flag === false){
                this.data.push(this.data3[0]);
            }
        },

附加特点设计与展示

设计的创意独到之处,这个设计的意义

    使用了element树形控件,以文件夹形式展示更加简明可观,便于用户阅览。我们为家族树添加了增加节点、删除节点、移动节点和搜索节点四个功能,当有新的输入数据或者现在数据出现错误时,使用者能在网页端非常简便的对树的结构进行修改,并直观的看到修改后的效果。添加清空按钮,可以清空所有学术家族树,方便进行大规模修改。

实现思路

    利用element-ui中的icon组件,构造点击事件,编写append、remove、check函数跟随在节点之后,实现节点的增加、删除、查找功能,使用el-tree的封装组件实现节点的移动功能(拖拽组件)。

贴出你认为重要的/有价值的代码片段,并解释

/* 以会话框的形式输入需要加入的节点信息 */
        append(node,data) {
            console.log(node,data,'增加')
            this.$prompt('节点名字', '增加节点', {
                confirmButtonText: '确定',
                cancelButtonText: '取消',
                }).then(({ value }) => {
                    this.dfs(node.id - 1 ,value);
            });
        },
        /* 查找需要加入信息的旧节点信息 */
        search(k,value){
            for(var m = 0; m < this.data.length; m++){
                    if(this.data[m].id === k){
                        newChild = {id: this.id2++ ,label: value, children:[]};

                        this.data[m].children.push(newChild);

                        return;
                    }
                    else{
                        for(var n = 0; n < this.data[m].children.length; n++){
                            // alert(this.data[m].children[n].id);
                            if(this.data[m].children[n].id === k){
                                newChild = {id: this.id2++ ,label: value, children:[]};
                                this.data[m].children[n].children.push(newChild);

                                return;
                            }
                            else{
                                for(var o = 0; o < this.data[m].children[n].children.length; o++){

                                    if(this.data[m].children[n].children[o].id === k){
                                        newChild = {id: this.id2++ ,label: value, children:[]};
                                        this.data[m].children[n].children[o].children.push(newChild);

                                        return;
                                    }
                                    else{
                                        for(var p = 0; p < this.data[m].children[n].children[o].children.length; p++){
                                            // alert(this.data[m].children[n].children[o].children[p].id);
                                            if(this.data[m].children[n].children[o].children[p].id === k){
                                                newChild = {id: this.id2++ ,label: value};
                                                this.data[m].children[n].children[o].children[p].children.push(newChild);
                                                return;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
            }

        },
/* 删除选中节点以及其所有子节点 */
        remove(node, data) {
            const parent = node.parent;
            const children = parent.data.children || parent.data;
            const index = children.findIndex(d => d.id === data.id);
            children.splice(index, 1);
        },

实现成果展示

目录说明和使用说明

目录

如何运行网页

    进入GitHub项目,下载整个项目的zip压缩包,解压后打开FamilyTree文件夹,双击打开index.html运行即可。(要保证FamilyTree中的文件没有丢失或移动到别的路径下。)

单元测试

测试工具

    通过作业博客里的几份教程,大致学习了JS单元测试的方法,最后选择使用mocha工具来进行单元测试。这里是我们写的一份mocha简易教程

单元测试代码

var Cinput_test = require('./tree.js');
var expect = require('chai').expect;

var data = [{"id":0,"label":"导师:张三","children":[{"id":1,"label":"2016级","children":[{"id":2,"label":"博士生","children":[{"id":3,"label":"天一","children":[]},{"id":4,"label":"王二","children":[]},{"id":5,"label":"吴五","children":[]}]},{"id":10,"label":"硕士生","children":[{"id":11,"label":"刘一","children":[]},{"id":12,"label":"李二","children":[{"id":21,"label":"字节跳动"},{"id":22,"label":"京东云"}]},{"id":13,"label":"李三","children":[]}]}]},{"id":6,"label":"2015级","children":[{"id":7,"label":"硕士生","children":[{"id":8,"label":"王五","children":[]},{"id":9,"label":"许六","children":[]}]}]},{"id":14,"label":"2017级","children":[{"id":15,"label":"本科生","children":[{"id":16,"label":"刘六","children":[{"id":19,"label":"JAVA"},{"id":20,"label":"数学建模"}]},{"id":17,"label":"琪七","children":[]},{"id":18,"label":"司四","children":[]}]}]}]}];
describe('测试一', function() {
  it('信息缺失', function() {
    expect(Cinput_test('导师:张三\n' +
        '2016级博士生:天一、王二、吴五\n' +
        '2015级硕士生:王五、许六\n' +
        '2016级硕士生:刘一、李二、李三\n' +
        '2017级本科生:刘六、琪七、司四\n' +
        '\n' +
        '刘六:JAVA、数学建模\n' +
        '\n' +
        '李二:字节跳动、京东云')).to.be.equal(data);
  });
});

    我们主要对读入文本内容并转化为可用的JSON格式的Cinput_test()函数进行了5次样例测试。结果如下:

测试数据的构造思路

    使用多组不同的数据进行样例测试,尽量包含各种可能出现的极端数据(比如只有一个导师节点、只有前缀没有名字等)。

Github的代码签入记录截图

遇到的代码模块异常及解决方法

  • 数据处理模块:在创建新的树节点的时候,会与HTML中的input和button组件产生冲突,输入框和按钮就不见了。后发现新节点的赋值需要使用“:”而不是“=”号
  • 关联树模块:每次均生成一棵新树,将其并入前树时由于是数组结构“[]”,无法加入新节点。需要使用this.data[0]来取出其中节点的属性字典
  • 单元测试模块:Vue库和mocha无法安装与识别。因为Vue全局安装,文件夹中无法识别,需要局部安装。其次需要在package.json文件中的devDependencies加入mocha,才可以成功安装进行单元测试

评价你的队友

    黄颜熠同学代码能力一流,性格活泼,为人阳光,易相处,积极上进,最令我惊讶的是在编程中不达目的不罢休(主要指不睡觉)的毅力,值得我学习。黄颜熠同学需要改进的地方是心态较差,编程遇到困难时容易急躁。

posted @ 2020-10-10 21:59  Fzu_吴鹏辉  阅读(138)  评论(0编辑  收藏  举报