2020软件工程结对编程之实验室程序实现
目录
结对成员
这个作业属于哪个课程 | |
---|---|
这个作业要求在哪里 | |
这个作业的目标 | 在网页页面上呈现树形结构形式的师门树,树的节点,鼠标点击后是可以缩放的。同时,支持呈现多棵树并存、两棵关联树共存等形式。 |
学号 | 031802339、031802323 |
Github项目地址:
Github项目地址:https://github.com/zhengguorong339/031802323-031802339
结对同学博客链接
学号 | 姓名 | 博客链接 |
---|---|---|
031802323 | 彭宇泽 | https://www.cnblogs.com/AshCeimpeng/p/13799978.html |
031802339 | 郑国荣 | https://www.cnblogs.com/double-points/p/13776022.html |
具体分工
031802339郑国荣:主要负责UI设计、素材收集、归纳总结
031802323彭宇泽:主要负责编写代码、归纳总结、测试
PSP表格
PSP2.1 | Personal Software Process Stages | 预估耗时(小时) | 实际耗时(小时) |
---|---|---|---|
Planning | 计划 | 1 | 0.5 |
Estimate | 估计这个任务需要多少时间 | 0.5 | 0.5 |
Develop | 开发 | 5 | 8 |
Analysis | 需求分析 (包括学习新技术) | 30 | 35 |
Design Spec | 生成设计文档 | 2 | 2 |
Design Review | 设计复审 | 2 | 2 |
Coding Standard | 代码规范(为目前的开发制定合适的规范) | 1 | 2 |
Design | 具体设计 | 3 | 3 |
Coding | 具体编码 | 20 | 25 |
Code Review | 代码复审 | 1 | 1 |
Test | 测试(自我测试、修改代码、提交修改) | 5 | 5 |
Reporting | 报告 | 2 | 3 |
Test Repor | 测试报告 | 1.5 | 2 |
Size Measurement | 计算工作量 | 0.5 | 0.5 |
Postmortem & Process Improvement Plan | 事后总结, 并提出过程改进计划 | 2 | 2 |
合计 | 77.5 | 91.5 |
解题思路描述
需求分析:首先我们需要在web页面提供一个文本框,然后在文本框中输入给定的师生信息,接着把师生信息以树形结构的形式展现出来。因此信息的读入输出流程如下图所示:
关键算法及其代码实现
主要代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>T R E E</title>
<script src="https://d3js.org/d3.v5.min.js"></script>
<style>
.button {
background-color: #4CAF50; /* Green */
border: none;
color: white;
padding: 10px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
font-size: 12px;
margin: 4px 2px;
-webkit-transition-duration: 0.4s; /* Safari */
transition-duration: 0.4s;
cursor: pointer;
}
.button1 {
background-color: white;
color: black;
border: 2px solid #4CAF50;
}
.button1:hover {
background-color: #4CAF50;
color: white;
}
.text {
border-style: solid;
border-color: #98bf21;
}
</style>
</head>
<body>
<div id="txt" align="center">
<h1>Create A Tree</h1>
<textarea id="input" class="text" cols="60" rows="10" placeholder="请输入内容"></textarea>
</div>
<div id="but" align="center" style="vertical-align: middle;">
<input type=button value=刷新 onclick="location.reload()" class="button button1" style="margin-right:150px;">
<button class="button button1" onclick="jiexi()" style="margin-left:150px;">生成</button>
</div>
<div id="main">
<div id="Tree"></div>
</div>
<script>
function jiexi() {
var text = document.getElementById("input").value;
var top = text.slice(text.indexOf(":") + 1, text.indexOf("2"))
var cnt = text.match(/2/g);
var line = text.match(/:/g).length;
var lines = text.split(/[(\r\n)\r\n]+/)
var start = 0, branch = 0, end = 0;
for (var i = 0; i < line; i++) {
if (lines[i].indexOf("导师") != -1) {
start = i;
i = i + 1;
branch = 0;
for (var j = i; j < line; j++) {
if (lines[j].indexOf("生:") != -1) {
branch = branch + 1;
}
if (lines[j].indexOf("导师") != -1) {
end = j - 1;
showAtree(start, branch, end, lines);
break;
}
if (j == line - 1) {
end = j;
showAtree(start, branch, end, lines);
break;
}
}
}
}
}
function showAtree(start = 0, branch = 4, end = 5, lines) {
var flag = true;
if (flag == 1) {
let data = {
"name": "",
"children":
[
{
"name": "",
"children":
[
{
"name": "",
"children":
[
]
}
]
}
]
};
data.name = lines[start].slice(lines[start].indexOf(":") + 1, lines[start + 1].indexOf(":"));
var j = 0;
for (var i = start; i < start + branch; i++) {
data.children[j] = {
"name": "",
"children":
[
{
"name": "",
"children":
[
]
}
]
};
data.children[j].name = lines[i + 1].slice(0, lines[i + 1].indexOf(":"));
for (var k = 0; k < lines[i + 1].slice(lines[i + 1].indexOf(":") + 1, 50).split('、').length; k++) {
var nam = lines[i + 1].slice(lines[i + 1].indexOf(":") + 1, 50).split('、')[k];
data.children[j].children[k] = {
"name": "",
"children":
[
]
};
data.children[j].children[k].name = nam;
var x = -1;
var sea = nam + ":"
for (var l = start; l < end; l++) {
if (lines[l].indexOf(sea) != -1) {
x = 1;
break;
}
}
if (x == 1) {
for (var r = 0; r < lines[l].slice(lines[l].indexOf(":") + 1, 50).split('、').length; r++) {
var skill = lines[l].slice(lines[l].indexOf(":") + 1, 50).split('、')[r];
data.children[j].children[k].children[r] = [{ "name": "" }];
data.children[j].children[k].children[r].name = skill;
}
}
}
j = j + 1;
}
const root = d3.hierarchy(data);
root.x0 = dy / 2;
root.y0 = 0;
root.descendants().forEach((d, i) => {
d.id = i;
d._children = d.children;
});
var svg = d3.select("#Tree")
.append("svg")
.attr("width", width)
.attr("viewBox", [-margin.left, -margin.top, width, dx])
.style("font", "15px sans-serif")
.style("user-select", "none");
const gLink = svg.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5);
const gNode = svg.append("g")
.attr("cursor", "pointer")
.attr("pointer-events", "all");
function update(source) {
const duration = d3.event && d3.event.altKey ? 2500 : 250;
const nodes = root.descendants().reverse();
const links = root.links();
tree(root);
var left = root;
var right = root;
root.eachBefore(node => {
if (node.x < left.x) left = node;
if (node.x > right.x) right = node;
});
const height = right.x - left.x + margin.top + margin.bottom;
const transition = svg.transition()
.duration(duration)
.attr("viewBox", [-margin.left, left.x - margin.top, width, height])
.tween("resize", window.ResizeObserver ? null : () => () => svg.dispatch("toggle"));
const node = gNode.selectAll("g")
.data(nodes, d => d.id);
const nodeEnter = node.enter().append("g")
.attr("transform", d => `translate(${source.y0},${source.x0})`)
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0)
.on("click", d => {
d.children = d.children ? null : d._children;
update(d);
});
nodeEnter.append("circle")
.attr("r", 6)
.attr("fill", d => d._children ? "#ccc" : "#fff")
.attr("stroke", 'steelblue')
.attr("stroke-width", 2);
nodeEnter.append("text")
.attr("dy", "0.31em")
.attr("x", d => d._children ? -10 : 10)
.attr("text-anchor", d => d._children ? "end" : "start")
.text(d => d.data.name)
.clone(true).lower()
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3)
.attr("stroke", "white");
const nodeUpdate = node.merge(nodeEnter).transition(transition)
.attr("transform", d => `translate(${d.y},${d.x})`)
.attr("fill-opacity", 1)
.attr("stroke-opacity", 1);
const nodeExit = node.exit().transition(transition).remove()
.attr("transform", d => `translate(${source.y},${source.x})`)
.attr("fill-opacity", 0)
.attr("stroke-opacity", 0);
const link = gLink.selectAll("path")
.data(links, d => d.target.id);
const linkEnter = link.enter().append("path")
.attr("d", d => {
const o = { x: source.x0, y: source.y0 };
return diagonal({ source: o, target: o });
});
link.merge(linkEnter).transition(transition)
.attr("d", diagonal);
link.exit().transition(transition).remove()
.attr("d", d => {
const o = { x: source.x, y: source.y };
return diagonal({ source: o, target: o });
});
root.eachBefore(d => {
d.x0 = d.x;
d.y0 = d.y;
});
flag = false;
}
update(root);
return svg.node();
}
}
margin = ({ top: 100, right: 120, bottom: 10, left: 500 });
var width = 1800;
dy = 300;
dx = 30;
tree = d3.tree().nodeSize([dx, dy]);
diagonal = d3.linkHorizontal().x(d => d.y).y(d => d.x);
</script>
</body>
</html>
实现成果展示
初始界面:
生成单棵师门树:
生成多棵师门树
在博客中给出目录说明和使用说明
目录结构:
目录说明:
- 家族树
- 学术家族树:html文件
- README:目录说明与使用说明文件
- 使用说明:
- 点击github上的“Clone or download”按钮后,将上述文件打包下载,解压后即可使用,需保证上述所有文件在同一个文件夹下。
使用时直接用Chrome打开学术家族树.html文件即可,输入格式按照作业要求即可。 - 在出现的文本框输入数据,点击生成家族树按钮,将会在下方生成一棵以导师为根节点的树(暂不支持关联树的实现...),然后点击刷新按钮可以刷新页面重新输入。
- 点击github上的“Clone or download”按钮后,将上述文件打包下载,解压后即可使用,需保证上述所有文件在同一个文件夹下。
Github的代码签入记录
遇到的代码模块异常或结对困难及解决方法
(1)问题描述:对于一组师生信息的输入,不知道以什么为关键字为标志信息,一直无法正确进行切分,提取出每组的师生信息
做过哪些尝试:上网百度、询问同学
是否解决:已经解决
收获:提升了自己分析问题和解决问题的能力,并且对JavaScript的认识有了进一步的提高。
(2)问题描述:针对多组师生信息的输入,一直无法正确进行切分,提取出每组的师生信息
做过哪些尝试:上网百度
是否解决:未经解决
队友评价
031802323:我的队友对于作业要求理解很快,思路清晰。同时态度认真,学习新技术也很快,积极的讨论与研究细节让本次作业能够顺利完成,是一个不折不扣的好队友。
031802339:我的队友是一个努力上进、自学能力强的人,这次多亏了他的努力和对我的帮助,才使得我们的结对项目顺利完成。个人觉得我们两个在结对配合和交流上相对比较顺畅,总能适时地交流意见和建议。通过这次结对,从队友身上学到了很多。