这个作业属于哪个课程 | https://edu.cnblogs.com/campus/fzu/SE2020 |
---|---|
这个作业要求在哪里 | https://edu.cnblogs.com/campus/fzu/SE2020/homework/11277 |
这个作业的目标 | 利用github合作开发,前端开发设计 |
学号 | 031802504 |
一、结对信息
分工信息
学号 | 姓名 | 分工 |
---|---|---|
031802504 | 陈新平 | 编写功能代码,UI 设计 |
031802506 | 程灵飞 | 单元测试代码,博客攥写 |
队友博客链接: https://www.cnblogs.com/fzu2018-clf-bky-blog/p/13786871.html
本次作业链接: https://edu.cnblogs.com/campus/fzu/SE2020/homework/11277
对应 github 链接: https://github.com/Stareven233/031802504-031802506
二、PSP 表格
PSP 2.1 | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 10 | 10 |
Estimate | 估计这个任务需要多少时间 | 10 | 10 |
Development | 开发 | 500 | 720 |
Analysis | 需求分析 (包括学习新技术) | 120 | 120 |
Design Spec | 生成设计文档 | 120 | 120 |
Design Review | 设计复审 | 20 | 20 |
Coding Standard | 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
Design | 具体设计 | 60 | 60 |
Coding | 具体编码 | 120 | 180 |
Code Review | 代码复审 | 30 | 30 |
Test | 测试(自我测试,修改代码,提交修改) | 120 | 300 |
Reporting | 报告 | 30 | 30 |
Test Report | 测试报告 | 10 | 10 |
Size Measurement | 计算工作量 | 10 | 10 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 60 | 60 |
合计 | 1230 | 1690 |
三、项目速览
- 项目链接
- 若打不开很有可能需要修改dns,例如233.5.5.5或233.6.6.6
- 或者在C:WindowsSystem32driversetchosts文件中某一行添加
185.199.110.153 stareven233.github.io
- ps:修改hosts要求管理员权限
四、解题思路描述与设计说明
代码实现思路
- 通过textareadu读取一串固定格式的师生信息
- 用正则将字符串解析为树数据对象,四层结构(导师-年级-学历-学生姓名-学生详细信息)
- 在页面上渲染出树状图,默认不展开
- 缩放:点击相应按钮,修改对应节点的expand属性,实现节点的展开与收回
- 多树并存:用数组(treeData)存储所有生成的树对象
- 多树关联:对于新输入的树,调用find方法在节点对象(nodeData)(记录了每个师生对应的节点对象)中搜寻每个叶节点的名称,若有则将其合并到这棵树上
流程图
关键代码
mergeTree函数用于合并树,但本质上可以合并某个对象到一个同类对象数组中,要求为 {label: xx, children: [{}, ]}
类的嵌套对象
仅当obj的属性label与arr中某个对象label相同时递归合并二者的children属性,children是数组
mergeTree (arr, obj) {
// 若其中存在一个元素其label属性与obj的label相同则执行合并,否则obj推入arr中
const tmp = arr.find(item => item.label === obj.label)
if (tmp === undefined) {
arr.push(obj)
return
}
for (const c of obj.children || []) {
this.mergeTree(tmp.children, c)
}
// 替换策略:不同label直接push,否则递归合并children;最终由于学生姓名处无children属性只比较label将脱离递归
}
五、附加特点设计与展示
特点设计: 实现思路
- 页面美观大方,正所谓
Simple is Better Than Complex
: Element-ui - 树可动态添加,动态合并,自适应节点展开收缩,丝滑顺畅: 以数组存储每个树对象,每次生成新树时更新数组
- 导师节点(包括动态合并后从叶节点升级的)高亮标出,容易区分: 为组件v-bind一个根据节点数据设置class的函数,生成时调用
- 每个树都可独立拖动,再也不用担心放不下了: 监听鼠标事件onmousemove,实时调整组件位置
- 方便易用,支持横纵切换,一键缩放: 为组件on-expand事件绑定函数,调整组件的expand属性
- 节点点击后在旁边浮现节点信息(即学生的详细经历): 监听节点on-node-click事件,手动调整位置
树支持放大缩小,简单易用,ctrl+鼠标滚轮: 无
代码片段
showPopover函数当节点被点击触发,仅当节点为师生时允许允许,效果是在节点旁边用一个tooltip框展示节点的附加信息
showPopover (e, data) {
e.stopPropagation()
// 避免同时触发document上的close
const node = e.target
if (node.className.indexOf('tree-teacher') + node.className.indexOf('tree-student') === -2) {
// 两个都找不到时返回值(每个为-1)加起来为-2
return
}
this.popContent = data.tag || '暂无tag'
const popNode = document.getElementById(this.$refs.popover.tooltipId)
const pos = this.getAbsolutePos(node)
// getAbsolutePos函数借助节点的offsetParent方法递归获取元素相对浏览器的绝对坐标
if (popNode === null) {
return
}
popNode.style.left = pos.left + 'px'
popNode.style.top = pos.top + node.getBoundingClientRect().height + 10 + 'px'
// 很坑,left与top必须用绝对坐标,而且popover show之前无法获取坐标等信息
this.$refs.popover.doShow()
}
成果展示
六、目录结构
目录说明
使用说明
-
安装nodejs: https://nodejs.org/
注:本仓库使用的是v14.13.1 -
安装vue-cli
npm install -g @vue/cli
-
clone该仓库
$ git clone git@github.com:Stareven233/031802504-031802506.git
-
安装依赖
cd 031802504-031802506
npm install
-
编译&运行
npm run serve
-
编译&运行(生产环境)
npm run build
七、单元测试
测试工具
选择
用的是与vue集成良好的jest,再准确一点是vue的插件cli-plugin-unit-jest,它开箱即用不需配置
学习
先去查了博客,很杂,很多都是jest而非cli-plugin-unit-jest,不大一样,而且年代有点久,内容还少。
后来照着官方文档学,中英文混杂
简易教程
https://www.cnblogs.com/Stareven233/p/13807613.html
代码
这3个测试用于测试generateTree函数(负责解析并生成树对象),通过wrapper.vm.treeData获取生成结果与已知正确的数据作比较来判断正误
wrapper.findAllComponents({ name: 'el-button' }).at(0).trigger('click') // 先打开侧边输入框
it('generateTree(expand)', () => {
const button = wrapper.findAllComponents({ name: 'el-button' }).at(2)
button.trigger('click')
wrapper.vm.expandChange()
// 先生成,再展开才有数据,而且还不能跟上方的侧边输入框打开放一起
})
it('generateTree(treeData)', () => {
const tData = [{ label: '张三', tag: '导师', isTeacher: true, children: [...]...]
expect(wrapper.vm.treeData).toEqual(tData)
})
it('generateTree(nodeData)', () => {
const nData = { 张三: { isTeacher: true, node: { label: '张三', tag: '导师', isTeacher: true, children: [...]}
expect(wrapper.vm.nodeData).toEqual(nData)
})
构造思路
分成静态与动态两部分,静态指已知数据测试渲染是否正确,动态指给出原始文本能否通过页面输入、点击正确解析出数据并生成树
从组件的渲染、特定节点信息正误与内部变量三个方面综合考虑,重点放于树的生成合并与树渲染测试
站在测试员的角度,站在一般用户的角度思考,比如输入文本可能会是特殊结构,不同编码,或是大量数据造成处理过久溢出等错误
测试与覆盖率结果
八、Github Commit 记录
九、总结
遇到的问题
- 问:久闻vue大名,但从没用过,时间紧任务重
解:现卖现学
学:nodejs、vue、less、jest等前端开发知识 - 问:vue难以调试,配合上eslint体验更糟,代码规范严格,定义语法,分号换行都非常严,仿佛在打python,最要命的是不允许暴露内部变量,渲染出错也不能查看数据,绝望。
试:vue-devtools
解:习惯它
学:vue-devtools(但没想象的好用,编译半天还出错)、感受到了vue的两面性,也或许只是自己学的还不够... - 问:想为节点添加popover弹出框,然而element的这个组件要求将按钮包裹在内共同渲染,但是树是动态生成的,页面初始化时无法确定。
试:选用了ref引用方式绑定,然而还是没有响应,能弹出却不能自动定位。反复看文档也没用相关api提供
解:最后手写定位,但一开始没搞清相对定位跟绝对定位差别,为此浪费了大量时间...
学:组件props及原生js操作 - 问:jest文档不全,东拼西凑到处学...一开始由于用了element库一直报错说没注册组件
试:查看文档、各论坛、不断调整代码
解:在测试代码中组件要跟main.js一样用Vue.use注册,这些文档可都没说啊...
学:jest单元测试 - 问:队友天天熬夜,然而我睡眠浅,因此一直没睡好,空耗了不少coding时间
试:多次沟通,他也确实多有注意,但醒着总会发出声音...
解:无 可能或者应该还有一些,但是忘记了
不足
- 时间没规划好,在不大重要的功能(popover)上花了太多时间
- 没做搜索功能,不难,但没时间,有些遗憾。
- 设想中还应该用element-ui的tree组件做一个侧边目录并集成搜索便于浏览...
- 没考虑师生同名的情况,如果有这种数据会因为相互递归而栈溢出。而且动态合并考虑的情况还不够充足。
- 页面效果还可以再好一些,奈何能力与时间都有限。
队友评价
- 大佬,稳,懂的都懂
- 喜欢熬夜,然而第二天又很晚起,我也得被迫晚睡,特别是这几天还要早起改bug
- 希望能一起早睡早起,同步合作的节奏