这个作业属于哪个课程 | 软件工程实践2023年春季学期 |
---|---|
这个作业要求在哪里 | 结对第二次作业–编程实现 |
结对学号 | 222000114_222000116 |
这个作业的目标 | 使用web技术实现原型中的功能 |
其他参考文献 | CSDN、Vue文档、Element-ui文档 |
目录
一、git仓库链接和代码规范链接
1.1 GitCode仓库地址
1.2代码规范
1.3云服务器
二、PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 50 |
Estimate | • 估计这个任务需要多少时间 | 20 | 35 |
Development | 开发 | 2110 | 2095 |
Analysis | • 需求分析 (包括学习新技术) | 150 | 120 |
Design Spec | • 生成设计文档 | 10 | 25 |
Code | • 具体编码 | 1800 | 1620 |
Test | • 测试 | 120 | 240 |
Code Review | • 代码复审 | 30 | 90 |
Reporting | 报告 | 60 | 210 |
Blog | • 撰写博客 | 40 | 180 |
Postmortem & Process Improvement Plan | • 事后总结,并提出过程改进计划 | 20 | 30 |
合计 | 2185 | 2355 |
三、成品展示
3.1 主页展示
3.2 选手排名展示
3.3 每日赛况展示
3.4 晋级图展示
3.5 了解更多展示
3.6 详细赛况展示
四、结对讨论过程描述
4.1 项目开始前学习
本次结对作业涉及编程,且不限制库和框架的使用,如何实现是首先要讨论的问题,在了解过现在市场上的主流框架后,结合项目时间紧迫,我们最终选择了上手快、功能强大的Vue2框架,学习了如何安装配置Vue2,以及最基础的使用方法。两人各自学习了Vue文档中的快速上手部分的全部内容。
- 聊天记录
4.2 项目开始
项目过程中多次对于Git的分支使用产生疑问并讨论思考如何解决,以及就如何实现等方面多沟通多交流多对接(此处仅展示部分聊天记录)
- 聊天记录
五、设计实现过程
5.1 设计实现思路
- 首先,创建一个 Vue 项目,并安装所需的依赖。
- 在项目的 App.vue 组件中,添加一个顶部导航栏和一个主要内容区域。
- 在项目中使用 Vue Router 来实现页面导航。在 router/index.js 文件中定义不同页面的路由路径,并在 App.vue 中引入 router-link 组件来生成页面导航链接。
- 对于每个页面,创建一个单独的组件,并在路由配置中将其与相应的路径关联起来。
- 在每个页面组件中,使用 Element UI 提供的组件来展示不同的内容。例如,主页展示一些代表运动员风姿,选手排名页面展示选手列表和排名信息,详细赛况页面可以展示每场比赛的具体结果等等。
- 数据获取部分,在 < script > 中使用一个个list来存放所需的数据,再使用 v-for来遍历读取。
- 最后,为一些页面添加一些交互功能,例如点击某场比赛时展示该场比赛的详细信息,使用 Vue 提供的事件处理机制来实现这些交互功能。
5.2 功能结构图
六、代码说明
6.1 导航栏
Header.vue:
<template>
<nav>
<ul>
<li>
<router-link to="/" exact>主页</router-link>
<router-link to="/2" exact>选手排名</router-link>
<router-link to="/3" exact>每日赛况</router-link>
<router-link to="/4" exact>晋级图</router-link>
<router-link to="/5" exact>了解更多</router-link>
</li>
</ul>
</nav>
</template>
<script>
export default {
name: 'pageheader',
}
</script>
App.vue:
<template>
<div id="app">
<pageheader></pageheader>
<router-view/>
</div>
</template>
<script>
import Header from './components/Header.vue'
import Page1 from './components/Page1.vue'
import Page2 from './components/Page2.vue'
import Page3 from './components/Page3.vue'
import Page4 from './components/Page4.vue'
import Page5 from './components/Page5.vue'
import Page6 from './components/Page6.vue'
export default {
name: 'App',
components: {
"pageheader":Header,
'page1':Page1,
'page2':Page2,
'page3':Page3,
'page4':Page4,
'page5':Page5,
'page6':Page6,
}
}
</script>
<style>
#app {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 0px;
}
</style>
6.2 选手排名
Page2.vue: 排名通过vue中的***{{}}***双花括号法用一个变量循环读取:
<div id="ranks">
<div id="manrank">
<table id="tbl" border=1 width="80%" frame=void>
<tr>
<th></th>
<th>Rank</th>
<th>Matches</th>
<th>Aces</th>
</tr>
<tr v-for="man in men" :key="man.id">
<td>{{ man.name }}</td>
<td>{{ man.rank }}</td>
<td>{{ man.matches }}</td>
<td>{{ man.aces }}</td>
</tr>
</table>
</div>
<div id="womanrank">
<table id="tb2" border=1 width="80%" frame=void>
<tr>
<th></th>
<th>Rank</th>
<th>Matches</th>
<th>Aces</th>
</tr>
<tr v-for="woman in women" :key="woman.id">
<td>{{ woman.name }}</td>
<td>{{ woman.rank }}</td>
<td>{{ woman.matches }}</td>
<td>{{ woman.aces }}</td>
</tr>
</table>
</div>
</div>
< script > 部分代码:
<script>
let id1 = 0
let id2 = 0
export default {
data() {
return {
manscore: '85',
manname: 'ben-shelton',
womanscore: '54',
womanname: 'elena-rybakina',
men: [
{ id: id1++, name: 'Ben Shelton', rank: id1, matches: '5', aces: '85' },
{ id: id1++, name: 'Stefanos Tsitsipas', rank: id1, matches: '7', aces: '84' },
{ id: id1++, name: 'Karen Khachanov', rank: id1, matches: '6', aces: '83' },
{ id: id1++, name: 'Hubert Hurkacz', rank: id1, matches: '4', aces: '68' },
{ id: id1++, name: 'Tommy Paul', rank: id1, matches: '6', aces: '67' },
{ id: id1++, name: 'Novak Djokovic', rank: id1, matches: '7', aces: '66' },
{ id: id1++, name: 'Felix Auger-Aliassime', rank: id1, matches: '4', aces: '65' },
{ id: id1++, name: 'Alexei Popyrin', rank: id1, matches: '3', aces: '64' },
{ id: id1++, name: 'J.J. Wolf', rank: id1, matches: '4', aces: '62' },
{ id: id1++, name: 'Andrey Rublev', rank: id1, matches: '5', aces: '61' },
{ id: id1++, name: 'Frances Tiafoe', rank: id1, matches: '3', aces: '59' },
{ id: id1++, name: 'Taylor Fritz', rank: id1, matches: '2', aces: '53' },
{ id: id1++, name: 'Thanasi Kokkinakis', rank: id1, matches: '2', aces: '51' },
{ id: id1++, name: 'Christopher Eubanks', rank: id1, matches: '2', aces: '50' },
{ id: id1++, name: 'Denis Shapovalov', rank: id1, matches: '3', aces: '45' },
{ id: id1++, name: 'Maxime Cressy', rank: id1, matches: '2', aces: '44' },
{ id: id1++, name: 'Sebastian Korda', rank: id1, matches: '5', aces: '41' },
{ id: id1++, name: 'Ugo Humbert', rank: id1, matches: '3', aces: '40' },
{ id: id1++, name: 'Grigor Dimitrov', rank: id1, matches: '3', aces: '39' },
{ id: id1++, name: 'Benjamin Bonzi', rank: id1, matches: '3', aces: '36' },
],
women: [
{ id: id2++, name: 'Elena Rybakina', rank: id1, matches: '7', aces: '54' },
{ id: id2++, name: 'Aryna Sabalenka', rank: id1, matches: '7', aces: '46' },
{ id: id2++, name: 'Karolina Pliskova', rank: id1, matches: '5', aces: '31' },
{ id: id2++, name: 'Caroline Garcia', rank: id1, matches: '4', aces: '25' },
{ id: id2++, name: 'Donna Vekic', rank: id1, matches: '5', aces: '24' },
{ id: id2++, name: 'Magda Linette', rank: id1, matches: '6', aces: '21' },
{ id: id2++, name: 'Linda Fruhvirtova', rank: id1, matches: '4', aces: '20' },
{ id: id2++, name: 'Coco Gauff', rank: id1, matches: '4', aces: '17' },
{ id: id2++, name: 'Maria Sakkari', rank: id1, matches: '3', aces: '16' },
{ id: id2++, name: 'Elise Mertens', rank: id1, matches: '3', aces: '16' },
{ id: id2++, name: 'Petra Martic', rank: id1, matches: '2', aces: '15' },
{ id: id2++, name: 'Anna Bondar', rank: id1, matches: '2', aces: '15' },
{ id: id2++, name: 'Belinda Bencic', rank: id1, matches: '4', aces: '15' },
{ id: id2++, name: 'Jelena Ostapenko', rank: id1, matches: '5', aces: '15' },
{ id: id2++, name: 'Victoria Azarenka', rank: id1, matches: '6', aces: '15' },
{ id: id2++, name: 'Diana Shnaider', rank: id1, matches: '2', aces: '14' },
{ id: id2++, name: 'Shelby Rogers', rank: id1, matches: '2', aces: '13' },
{ id: id2++, name: 'Taylor Townsend', rank: id1, matches: '2', aces: '12' },
{ id: id2++, name: 'Marta Kostyuk', rank: id1, matches: '3', aces: '12' },
{ id: id2++, name: 'Petra Kvitova', rank: id1, matches: '2', aces: '11' },
],
}
},
}
</script>
6.3 每日赛况
每日赛况主要使用了 Element-UI 的一个插件:Tabs标签页
Page2.vue:
<el-tabs v-model="activeName" @tab-click="handleClick" stretch element-loading-background="black">
<el-tab-pane label="Q1" name="q1"></el-tab-pane>
<el-tab-pane label="Q2" name="q2"></el-tab-pane>
<el-tab-pane label="Q3" name="q3"></el-tab-pane>
<el-tab-pane label="Q4" name="q4"></el-tab-pane>
<el-tab-pane label="Day1" name="day1"></el-tab-pane>
.......
< script > 部分代码:(由于
写不完了老师大发慈悲,降低了要求,所以只选择了每日赛况中的几天来实现)
<script>
import Q1 from './Q1.vue'
import Q2 from './Q2.vue'
import Q3 from './Q3.vue'
import Q4 from './Q4.vue'
import Day1 from './Day1.vue'
export default {
data() {
return {
activeName: 'day1',
curTabcompnents: {
'q1': 'Q1',
'q2': 'Q2',
'q3': 'Q3',
'q4': 'Q4',
'day1': 'Day1',
}
};
},
components: { Q1, Q2, Q3, Q4, Day1 },
methods: {
handleClick(tab, event) {
console.log(tab, event);
}
}
};
</script>
6.4 晋级图
<div class="box" id="b1">
<div class="P1name">{{P1name1}}</div>
<div class="P1score">{{P1score1}}</div>
<div class='line'></div>
<div class="P2name" id="winner">{{P2name1}}</div>
<div class="P2score">{{P2score1}}</div>
</div>
<div class="box" id="b2">
<div class="P1name">{{P1name2}}</div>
<div class="P1score">{{P1score2}}</div>
<div class='line'></div>
<div class="P2name" id="winner">{{P2name2}}</div>
<div class="P2score">{{P2score2}}</div>
</div>
.......
< style > 部分代码:
.box {
width: 230px;
height: 80px;
background-color: rgba(226, 234, 234, 0.7);
border-radius: 10px;
position: absolute;
z-index: 1;
}
.box:hover {
background-color: rgba(146, 152, 152, 0.8);
}
.P1name {
position: absolute;
text-align: left;
top: 10%;
z-index: 2;
}
.P1score {
position: absolute;
top: 10%;
right: 10%;
z-index: 2;
}
.P2name {
position: absolute;
bottom: 10%;
text-align: left;
z-index: 2;
}
.P2score {
position: absolute;
bottom: 10%;
right: 10%;
z-index: 2;
}
#winner {
text-decoration: underline;
color: firebrick;
font-weight: bold
}
.line {
position: relative;
width: auto;
height: 3px;
top: 50%;
background-color: #7e797953;
z-index: 1;
}
#b1 {
top: 9.7%;
left: 3%;
}
#b2 {
top: 28%;
left: 3%;
}
......
6.5 了解更多
轮播图部分:仍然使用了Element-UI中的插件carousel
<el-carousel :interval="4000" type="card" height="350px">
<el-carousel-item v-for="item in items" :key="item.id">
<img :src="item.src" alt="" height="350px" width="800px">
</el-carousel-item>
</el-carousel>
< style > 部分代码:
<script>
export default {
data() {
return {
items: [
{ id: 1, src: require('../picture/learnmore_png/1.png') },
{ id: 2, src: require('../picture/learnmore_png/2.png') },
{ id: 3, src: require('../picture/learnmore_png/3.png') },
{ id: 4, src: require('../picture/learnmore_png/4.png') },
{ id: 5, src: require('../picture/learnmore_png/5.png') },
{ id: 6, src: require('../picture/learnmore_png/6.png') },
]
}
}
}
</script>
6.6 详细赛况
详细赛况部分:使用选择器来实现同一场比赛的不同set切换
<select id="select1" v-model="selectClassEnd" @change="selectClass($event)">
<option value="NONE">未选择</option>
<option v-for="(options, id) in selectClassData" :key="id" :value="options.id">
{{ options.title }}
</option>
</select>
.....
< style > 部分代码:
export default {
data() {
return {
selectClassData: [
{
id: 1, title: "Set1",
infor: 'B. Krejcikova/K. Siniakova win the set',
totalscore: '6-4',
infor1_1_1: 'Point to B. Krejcikova/K. Siniakova • 0:48',
infor1_1_2: 'Aoyama/Shibahara lose the point with a Forehand Forced Error',
infor1_1_3: 'Game',
infor1_2_1: 'Point to B. Krejcikova/K. Siniakova • 0:46',
infor1_2_2: 'Krejcikova/Siniakova win the point with a Forehand Winner',
......
},
{
id: 2, title: "Set2",
infor: 'B. Krejcikova/K. Siniakova win the match',
totalscore: '6-4, 6-3',
infor1_1_1: 'Point to B. Krejcikova/K. Siniakova • 1:29',
infor1_1_2: 'Aoyama/Shibahara lose the point with a Backhand Unforced Error',
infor1_1_3: 'Game',
infor1_2_1: 'Point to B. Krejcikova/K. Siniakova • 1:28',
infor1_2_2: 'Krejcikova/Siniakova win the point with a Backhand Winner',
......
},
],
selectClassEnd: "NONE",//类别默认选择
select_class_id: "1",//类别id提交报名需要
}
},
methods: {
//类别选中
selectClass(event) {
this.select_class_id = event.target.value; //获取option对应的value值 select_class_id是后台约定的提交数据的名称
},
},
七、心路历程和收获
- 222000114JCY●心路历程和收获
学习 Vue 框架的基本语法和组件化开发思想,包括 Vue 实例、组件等等。学习使用 Element UI 组件库来构建页面,了解组件的使用方法和参数配置,例如 el-menu、el-table、el-carousel等等。设计网页的页面结构和布局,确定各个页面之间的导航关系和交互功能,并根据需求选择合适的组件和布局方式进行开发。在获取数据部分花了颇多心思,并将数据渲染到页面上,以展示比赛信息、选手排名等内容。实现了页面的交互功能,例如点击比赛后展示比赛的详细信息,悬停比赛高亮显示等等。完成网页开发后进行测试和debug,尽力解决存在的 bug 和性能问题。
收获主要有以下几点:
- 学会了使用 Vue 框架和 Element UI 组件库来进行前端开发,了解了组件化开发的思想和优势。
- 加深了对页面布局、交互设计和用户体验的认识,了解了如何构建一个可用性和易用性较高的网站。
- 学会了解决问题的思路和方法,通过查找文档、搜索问题、debug 调试等方式来解决可能遇到的问题。
- 222000116LJW●心路历程和收获
一开始,我对前端开发一窍不通,只知道一些 HTML 、 CSS 和 JavaScript 的基础知识。然后,我开始学习 Vue 框架,理解组件化开发的思想和语法,了解 Vue 实例、组件、生命周期等基本概念。接下来,我学习如何使用 Element UI 组件库来快速构建页面,研究组件的使用方法和参数配置,例如 el-menu、el-table、el-carousel 等等。在开发过程中,我不断查找文档和教程,解决遇到的问题和 bug,调整页面布局和样式,不断优化页面性能和用户体验。最后,我和队友一起完成了一个完整的网页项目,从中学到了很多前端开发的知识和经验,同时也提升了自己的编程能力和解决问题的能力。
收获主要有以下几点:
- 学会了使用 Vue 框架和 Element UI 组件库来进行前端开发。
- 对 JavaScript 语言、HTML 和 CSS 等前端技术有了更深入的了解。
- 学会了如何通过查找文档和搜索问题来解决遇到的问题。
- 提高了自己的编程能力和解决问题的能力。
八、评价结对队友
- 222000114====>222000116的评价:
在本次结对作业中,LJW同志十分细心、负责,对于页面的组件的调整,常常为了一个细微的位置调节,进行一次又一次的修改、比对。且拥有扎实的HTML+CSS+JavaScript基础,能一个人独立完成复杂的功能模块。在分配任务方面也不斤斤计较,宽容大度,在提前完成他的任务后也会热心的来帮助我完成我的任务,对我的模块提出优化、改进建议。学习能力很强,能快速掌握一项新的技术。但有时容易钻牛角尖,希望以后可以改进。
- 222000116====>222000114的评价:
本次结对作业,JCY同志学习能力很强,能很快学会一些插件、库的使用方法,责任心强,分配的任务保质保量完成,主动承担一些任务,能够及时解决一些紧急情况,整体规划清晰,知道每个阶段应该完成的任务,在设计过程中有许多有趣的想法和创意,能够积极主动地和其他队友沟通协作,解决问题。但有时急于求成,耐心不足,希望以后改进。