jQuery火箭图标返回顶部代码

这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/SE2020
这个作业要求在哪里 https://edu.cnblogs.com/campus/fzu/SE2020/homework/11277
这个作业的目标 合作分工,前端与页面制作
学号 031802506



一、结对信息

  • 分工信息
学号 姓名 分工
031802504 陈新平 编写功能代码,UI 设计
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



三、基础功能编写与展示

基于 WEB 端和新的需求,我们重新设计了自己的方案:

网页功能分析

  • 通过 textareadu 读取一串固定格式的师生信息
  • 用正则将字符串解析为树数据对象,四层结构(导师-年级-学历-学生姓名-学生详细信息)
  • 在页面上渲染出树状图,默认不展开
  • 缩放:点击相应按钮,修改对应节点的 expand 属性,实现节点的展开与收回
  • 多树并存:用数组( treeData )存储所有生成的树对象
  • 多树关联:对于新输入的树,调用find方法在节点对象( nodeData )(记录了每个师生对应的节点对象)中搜寻每个叶节点的名称,若有则将其合并到这棵树上

成果展示

图 1 输入
图 2 节点展开显示
图 3 个人信息展示框
图 4 支持节点缩放
图 5 多树共存
图 6 合并树 1
图 7 合并树 2
图 8 双树关联 1
图 9 双树关联 2

关键代码

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 将脱离递归
    }



四、流程图

图 10 流程图



五、附加特点设计与展示

特点设计: 实现思路

  • 页面美观大方,正所谓 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()
    }

成果展示

图 11 支持横纵向显示切换
图 12 支持节点拖拽



六、目录结构

目录说明

图 13 目录说明

使用说明

  • 安装 nodejs
    注:本仓库使用的是 v14.13.1

  • 安装 vue
    npm install -g @vue/cli

  • clone该仓库
    $ git clone https://github.com/Stareven233/031802504-031802506

  • 安装依赖
    cd 031802504-031802506
    npm install

  • 编译 & 运行
    npm run serve

  • 编译 & 运行(生产环境)
    npm run build

详见:README.md


七、单元测试

测试工具

选择

用的是与 vue 集成良好的 jest ,再准确一点是 vue 的插件 cli-plugin-unit-jest ,它开箱即用不需配置

学习

先去查了博客,很杂,很多都是 jest 而非 cli-plugin-unit-jest ,不大一样,而且年代有点久,内容还少。
后来照着官方文档学,中英文混杂

简易教程

队友亲笔手写的价值 $999 的 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)
  })

构造思路

分成静态与动态两部分,静态指已知数据测试渲染是否正确,动态指给出原始文本能否通过页面输入、点击正确解析出数据并生成树
从组件的渲染、特定节点信息正误与内部变量三个方面综合考虑,重点放于树的生成合并与树渲染测试
站在测试员的角度,站在一般用户的角度思考,比如输入文本可能会是特殊结构,不同编码,或是大量数据造成处理过久溢出等错误

测试与覆盖率结果

图 14 单元测试



八、Github Commit 记录

图 15 Github Commit



九、总结

遇到的问题

  1. 问:久闻 vue 大名,但从没用过,时间紧任务重
    解:现卖现学
    学:nodejs 、vue 、less 、jest 等前端开发知识
  2. 问:vue 难以调试,配合上 eslint 体验更糟,代码规范严格,定义语法,分号换行都非常严,仿佛在打 python ,最要命的是不允许暴露内部变量,渲染出错也不能查看数据,绝望。
    试:vue-devtools
    解:习惯它
    学:vue-devtools (但没想象的好用,编译半天还出错)、感受到了 vue 的两面性,也或许只是自己学的还不够...
  3. 问:想为节点添加 popover 弹出框,然而 element 的这个组件要求将按钮包裹在内共同渲染,但是树是动态生成的,页面初始化时无法确定。
    试:选用了 ref 引用方式绑定,然而还是没有响应,能弹出却不能自动定位。反复看文档也没用相关 api 提供
    解:最后手写定位,但一开始没搞清相对定位跟绝对定位差别,为此浪费了大量时间...
    学:组件 props 及原生 js 操作
  4. 问:jest 文档不全,东拼西凑到处学...一开始由于用了 element 库一直报错说没注册组件
    试:查看文档、各论坛、不断调整代码
    解:在测试代码中组件要跟 main.js 一样用 Vue.use 注册,这些文档可都没说啊...
    学:jest 单元测试

队友评价

  1. 对工具的学习和掌握进度明显比我快啦,队友非常滴优秀;
  2. 因为两个人都是第一次学习前端框架,所以学起来比较抓狂,但是他的项目经验比我丰富,也指导了我很多,赞一个;
  3. 非常好心地不让我熬夜,一熬夜就坐在我背后开启极限编程模式;
  4. 最直接的感受:这次被带飞了。

不足的地方

  1. 对页面的美观程度还不够满意,因为考虑到多树共存的情况所以给页面布局的设计带来了一些困难,最终采用了简单化设计,但又过于简单了一些,后续还可以再对页面进行美化工作;
  2. 在 Vue 框架的学习和不大重要的功能上花费的时间占比较大,虽然 learning by doing,但是学得不够全面,就有点难过;
  3. 没做搜索功能,不难,但没时间,有些遗憾;
  4. 设想中还应该用 element-ui 的 tree 组件做一个侧边目录并集成搜索便于浏览;
  5. 没考虑师生同名的情况,如果有这种数据会因为相互递归而栈溢出。而且动态合并考虑的情况还不够充足。


第四次作业已完成
原文地址:https://www.cnblogs.com/fzu2018-clf-bky-blog/p/13786871.html