这个作业属于哪个课程 | 2302软件工程 |
---|---|
这个作业要求在哪里 | 结对第二次作业-编程实现 |
结对学号 | <222100302 222100101> |
这个作业的目标 | 采用web技术来实现上次作业中原型中的功能,实现一个界面友好的网站。 |
其他参考文献 | 《构建之法》 |
文章目录
1. Gitcode仓库链接和代码规范链接
1.1 Gitcode仓库链接
1.2 代码规范链接
2. PSP表格
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 1560 | 1590 |
• Analysis | • 需求分析 (包括学习新技术) | 300 | 500 |
• Design Spec | • 生成设计文档 | 60 | 90 |
• Design Review | • 设计复审 | 30 | 40 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 30 | 60 |
• Design | • 具体设计 | 30 | 60 |
• Coding | • 具体编码 | 1000 | 720 |
• Code Review | • 代码复审 | 10 | 30 |
• Test | • 测试(接口测试,修改代码,提交修改) | 100 | 90 |
Reporting | 报告 | 100 | 160 |
• Test and use Repor | • 接口、使用报告 | 60 | 120 |
• Size Measurement | • 计算工作量 | 20 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 20 | 20 |
合计 | 1690 | 1780 |
3. 成品展示
首页
页面头部包含一个带有Logo和标题的header,以及一个离开的导航栏nav,导航栏链接到不同页面。在header和nav之间有一个带有欢迎信息的div,通过背景色和字体样式突出显示。页面主体部分有一个容器div,内部包含一个轮播图slide,展示多个项目item,通过不同的背景图片和描述信息展示内容。页面采用响应式布局,使元素在不同屏幕大小下可以适应良好。使用了一些CSS动画效果,如文字跳动动画和内容显示动画,增强页面的吸引力和互动性。
选手信息
运动员信息展示页面,包括顶部的带有logo的标题栏、导航栏(默认隐藏)、运动员信息标题部分、以列表形式展示的运动员数据等。页面主题色调为蓝色和白色,整体布局简洁明了,使用了flex布局和斑马线效果优化视觉体验。每位运动员数据以国家、姓名、性别和出生日期等信息呈现,通过异步加载JSON数据实现动态展示运动员信息。整体设计使用户能方便查看运动员信息并跟随简单列表样式浏览。
每日赛况
每日赛况页面包括顶部的导航栏、赛程标题部分、日期选择按钮组、每日赛况内容展示区域等。页面主题色调为蓝色和白色,整体布局清晰,使用了阴影、边框和圆角等样式元素增加视觉效果。赛程内容分为不同日期的比赛项目信息,包含比赛类型、进行阶段、比赛时间等信息,还提供了查看详情按钮。整体设计使用户能方便浏览每日赛程信息和选择日期查看相应内容。
详细赛况
详细赛况页面,具有带有logo的标题栏和返回按钮。页面主题色调为蓝色和白色,整体布局简洁明了。运动员数据以表格形式呈现,包括排名、国家代码、姓名、年龄、总积分、距离领先积分等字段。页面中部显示了比赛类型和阶段的标题。用户可以通过返回按钮返回上一页,通过表格清楚地查看运动员数据。整体设计使用户可以快速浏览和了解运动员的相关信息。
奖牌榜
页面包含一个带有Logo和标题的header,以及一个水平导航栏nav,导航栏链接到不同页面。页面主体部分包含一个展示奖牌榜信息的div,其中包含一个标题"h2"和一个用来展示图表的div。使用了echarts库来创建奖牌榜图表,并从Medal.json文件中获取奖牌数据进行展示。图表包括了金牌、银牌、铜牌的数据展示,以柱状图形式呈现各个国家的奖牌情况。页面采用响应式设计,使得页面在不同屏幕大小下能够良好展示。页面整体UI设计简洁,颜色搭配清晰,使得用户可以清晰地获取到奖牌榜信息。
扩展功能:了解更多
了解更多页面是一个展示赛事信息和视频的页面。页面上方有包含logo的标题栏,包括赛事名称和一组导航链接。用户可以通过导航链接访问不同的页面,如首页、选手信息、每日赛况和奖牌榜等。在页面中间是一个3D旋转轮播图,展示不同图片以吸引用户注意。下方有一个视频部分,展示了不同的视频。页面整体设计现代且吸引人,用户可以方便地浏览赛事信息和观看相关视频。
4. 结对讨论过程描述
5. 设计实现过程
5.1 功能结构图
5.2 设计实现过程
开发过程中所使用的是node.js和vue。
- 页面整体结构: 每个页面都包含一个header用于展示Logo和标题,以及一个导航栏nav用于导航到不同页面,除此之外,每个页面还有独特的区块来展示特定内容。
- 页面动效: 通过JavaScript监听鼠标事件,实现在鼠标悬停时显示导航栏,鼠标移开时隐藏导航栏的效果。
- 数据展示: 页面涉及展示不同类型的信息,如赛程信息、运动员信息、奖牌榜等,数据通过JSON文件加载并展示在页面上。
- 页面布局: 根据不同页面的设计需求,使用不同的CSS样式对页面元素进行布局和美化,包括背景图、文字样式、按钮样式等。
- 页面交互: 在每日赛况页面中实现了按钮点击事件,根据不同按钮的点击跳转到对应的详细内容页面。
- 使用Echarts库: 奖牌榜页面使用了Echarts库创建图表来呈现数据,如奖牌榜页面展示各国家的奖牌数据。
- 3D翻转效果: 借助CSS3的3D效果,在了解更多页面展示了图片的3D翻转效果,增加页面的视觉吸引力。
- 总结:
- 整体设计过程通过HTML结构、JavaScript交互逻辑和CSS样式美化,展示了不同页面的内容和特点,提升了用户体验和页面的可读性。
- 前端主要负责展示页面的内容,包括页面布局、样式设计、交互效果等,使用Vue框架实现了动态数据绑定、路由导航等功能。
- 后端则主要负责数据的处理和提供接口供前端调用,包括提供运动员信息的JSON数据、奖牌榜信息的JSON数据、每日赛程数据等,使前端能够动态展示相关内容。
6. 代码说明
1.AthletesPage.vue
我们基于 Vue 3 的组件,使用了 Vue 3 的 Composition API,实现了一个页面组件,包括了页面的结构、交互逻辑和样式定义。通过 Vue 3 的 Composition API,实现了组件的数据响应和逻辑处理。。<template> 部分包含页面的 HTML 结构,包括页面的头部、导航、选手信息以及相关样式。<script setup> 部分使用了 Vue 3 的 <script setup> 语法糖,可以简化 Vue 组件的写法。在这里,它包含了组件的逻辑部分,包括数据处理和事件监听。首先,从 Vue 中引入了 ref 和 onMounted。ref 用于创建响应式数据,onMounted 用于在组件挂载后执行某个函数。创建了一个 athletes 的响应式数据,用于存储运动员信息。在 onMounted 钩子中,注册了一些事件监听,当鼠标移动到头部标题(ID 为 "tournament")上时,显示导航栏(ID 为 "nav"),移出时隐藏导航栏。导航栏的显示和隐藏是通过改变其 style.display 属性实现的。通过 fetch 方法获取了一个 JSON 文件 /athletes.json 中的数据,对数据进行处理后存储在 athletes 中。<style scoped> 部分定义了组件的样式,其中包括页面布局、字体样式、颜色等。部分样式是针对特定的元素或类进行定义的,以确保样式的局部性。
<template>
<header>
<img src="../assets/logo.png" alt="Logo" class="small-logo">
<h1 id="tournament">TOURNAMENT</h1>
</header>
<nav style="display: none;">
<a href="FirstPage">首页</a> <!-- 指向主页 -->
<a href="AthletesPage" style="text-shadow: 2px 2px 4px #9ec4ec;">选手信息</a> <!-- 指向运动员页面 -->
<a href="SchedualPage">每日赛况</a> <!-- 指向每日赛况页面 -->
<a href="MedalPage">奖牌榜</a> <!-- 指向奖牌榜页面 -->
<a href="MorePage">了解更多</a> <!-- 指向了解更多页面 -->
</nav>
<div id="Athletes">
<h2>Athletes</h2>
</div>
<div id="message">
<ul id="athletesList">
<!-- 添加标题行 -->
<li class="header">
<div>Country</div>
<div>Athlete</div>
<div>Gender</div>
<div>DOB</div>
</li>
<!-- 运动员列表项 -->
<li v-for="(athlete, index) in athletes" :key="index">
<div>{{ athlete.country }}</div>
<div>{{ athlete.fullName }}</div>
<div>{{ athlete.gender }}</div>
<div>{{ athlete.dob }}</div>
</li>
</ul>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
const images = ref(null);
let currentIndex = 0;
let intervalId;
onMounted(() => {
const tournament = document.getElementById("tournament");
const nav = document.querySelector("nav");
const slideshow = document.getElementById("slideshow");
tournament.addEventListener("mouseover", function() {
nav.style.display = "block";
nav.addEventListener("mouseover", function() {
nav.style.display = "block";
});
nav.addEventListener("mouseout", function() {
nav.style.display = "none";
});
});
tournament.addEventListener("mouseout", function() {
nav.style.display = "none";
});
});
const athletes = ref([]);
fetch('/athletes.json')
.then(response => response.json())
.then(data => {
data.forEach(user => {
user.Participations.forEach(participation => {
const fullName = participation.PreferredFirstName ? `${participation.PreferredFirstName} ${participation.PreferredLastName}` : participation.PreferredLastName;
const gender = participation.Gender === 0 ? 'Male' : 'Female';
const dob = new Date(participation.DOB).toLocaleDateString('en-US');
athletes.value.push({
country: user.CountryName,
fullName,
gender,
dob
});
});
});
})
.catch(error => {
console.error('Failed to fetch JSON:', error);
});
2.SchedualPage.vue
我们实现了一个赛程页面组件,包括了日期选择按钮和每日赛况的展示区域,以及相应的事件处理逻辑和页面跳转功能。,我们看到了 <template> 部分的内容,这部分主要描述了页面的结构,包括头部、导航栏、赛程信息展示区域等。其中:头部包括了一个图像标志和一个标题 "TOURNAMENT"。导航栏包含了几个链接,分别指向不同的页面,如主页、选手信息、每日赛况、奖牌榜和了解更多。<div id="Schedual"> 标签下是每日赛况的展示区域,其中包含了日期选择按钮和每日赛况的主要内容区域。日期选择按钮使用了 Element UI 的 <el-button> 组件,每个按钮上附带了一个日期,点击按钮可以显示对应日期的赛况信息。主要内容区域使用了 Element UI 的 <el-card> 组件,每个卡片展示了一场比赛的相关信息,包括比赛类型、是否结束、比赛时间等。在每个卡片的底部有一个按钮 "查看详情",点击该按钮可以跳转到详细赛况页面。接着,在 <script> 部分:使用了 onMounted 钩子来监听页面加载完成事件,当页面加载完成后,为标题 "TOURNAMENT" 添加了鼠标悬停事件的监听器,以控制导航栏的显示与隐藏。导出了一个对象,包含了组件的一些属性和方法:name 属性指定了组件的名称为 "SchedulePage"。components 属性为空,表示该组件不包含其他子组件。computed 属性包含了一个计算属性 content,根据 message 的值来决定返回哪个日期对应的赛程信息,以实现日期按钮的切换功能。methods 属性包含了两个方法:setMessage(value) 方法用于设置当前选中的日期,参数 value 为日期的序号。toDetails(panel) 方法用于跳转到详细赛况页面,它将选中比赛的类型和场次存储在 localStorage 中,并通过路由跳转到名为 "DetailsPage" 的页面。
<template>
<header>
<img src="../assets/logo.png" alt="Logo" class="small-logo">
<h1 id="tournament">TOURNAMENT</h1>
</header>
<nav>
<a href="FirstPage">首页</a> <!-- 指向主页 -->
<a href="AthletesPage">选手信息</a> <!-- 指向运动员页面 -->
<a href="SchedualPage" style="text-shadow: 2px 2px 4px #9ec4ec;">每日赛况</a> <!-- 指向每日赛况页面 -->
<a href="MedalPage">奖牌榜</a> <!-- 指向奖牌榜页面 -->
<a href="MorePage">了解更多</a> <!-- 指向了解更多页面 -->
</nav>
<div id="Schedual">
<h2>Schedual</h2>
</div>
<!-- -->
<div id="daily" style="text-align:center; margin-top: 40px;">
<el-row class="buttonGroup" style="height: 80px;margin: 20px auto">
<el-button round @click="setMessage(1)" :class="{ 'clickedButton': message === 1 }" class="dayButton">Thursday 18th January</el-button>
<el-button round @click="setMessage(2)" :class="{ 'clickedButton': message === 2 }" class="dayButton">Friday 19th January</el-button>
<el-button round @click="setMessage(3)" :class="{ 'clickedButton': message === 3 }" class="dayButton">Saturday 20th January</el-button>
<el-button round @click="setMessage(4)" :class="{ 'clickedButton': message === 4 }" class="dayButton">Sunday 21st January</el-button>
</el-row>
<!-- 每日赛况主要内容区域 -->
<div class="mainContent" style="margin: 0 auto;width: 900px;margin-top: 70px;">
<el-card class="box-card" v-for="(item, index) in content" :key="index"
style="margin: 10px 10px;display: inline-block;width: 400px; border-radius: 30px">
<el-row :gutter="10">
<el-col class="show" :span="14">
<!-- 比赛类型 -->
<strong>{{ item.type }}</strong>
<br>
</el-col>
<el-col class="show" :span="5" :offset="3">
<strong>Complete</strong>
<br>
<!-- 比赛时间 -->
<span class="text">{{ item.time }}</span>
</el-col>
</el-row>
<hr class="divide">
<el-row :gutter="10">
<!-- <el-col :span="2">
<img :src="item.playimg" class="playIcon" />
</el-col> -->
<!-- 比赛场次 -->
<!-- 动态绑定 -->
<el-col class="show" :span="7" :offset="2" :class="{ 'gold': item.play === 'Finals' }">
{{ item.play }}
</el-col>
</el-row>
<hr class="divide">
<!-- 跳转到详细赛况 -->
<el-row>
<el-button round class="more" @click="toDetails(item)">查看详情</el-button>
</el-row>
</el-card>
</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
onMounted(() => {
const tournament = document.getElementById("tournament");
const nav = document.querySelector("nav");
const slideshow = document.getElementById("slideshow");
tournament.addEventListener("mouseover", function() {
nav.style.display = "block";
nav.addEventListener("mouseover", function() {
nav.style.display = "block";
});
nav.addEventListener("mouseout", function() {
nav.style.display = "none";
});
});
tournament.addEventListener("mouseout", function() {
nav.style.display = "none";
});
});
export default {
name: "SchedulePage",
components: { },
computed: {
content() {
switch (this.message) {
case 1: return this.content1;
case 2: return this.content2;
case 3: return this.content3;
case 4: return this.content4;
default: return null; // 或者返回某个默认值或执行其他逻辑
}
}
},
methods: {
// 传递消息方法
setMessage(value) {
this.message = value
},
// 跳转到详细界面方法
toDetails(panel) {
localStorage.setItem('detailType', panel.type);
localStorage.setItem('detailPlay', panel.play);
this.$router.push({ name: 'DetailsPage' });
}
},
3.Router
我们配置了一个路由器,用于创建应用程序的路由。首先导入了 createRouter、createWebHistory 以及 RouteRecordRaw 三个函数或类型,这些都是从 Vue Router 中导入的。routes 是一个数组,包含了应用程序的所有路由信息。每个路由对象都描述了一个路径 (path),路由的名称 (name),以及路由对应的组件 (component)。每个路由对象的 component 属性都是一个函数调用,这个函数调用返回了一个动态导入的组件。这种方式可以实现懒加载,即在需要时再加载对应的组件,而不是一开始就加载所有组件。注释 webpackChunkName 指定了生成的 chunk 的名称。创建了路由对象 router,通过调用 createRouter 函数,传入了一个配置对象,其中包含了路由的模式 (history) 和定义的路由数组 (routes)。最后通过 export default router 导出了创建的路由对象,以便在应用程序的入口文件中使用。综上所述,这段代码配置了一个基于 Vue Router 的路由器,定义了应用程序中的所有路由信息,并创建了一个路由对象供应用程序使用。
import { createRouter, createWebHistory, RouteRecordRaw } from 'vue-router'
const routes: Array<RouteRecordRaw> = [
{
path: '/',
redirect: '/FirstPage'
},
{
path: '/FirstPage',
name: 'FirstPage',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/FirstPage.vue')
},
{
path: '/AthletesPage',
name: 'AthletesPage',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/AthletesPage.vue')
},
{
path: '/MorePage',
name: 'MorePage',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/MorePage.vue')
},
{
path: '/SchedualPage',
name: 'SchedualPage',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/SchedualPage.vue')
},
{
path: '/MedalPage',
name: 'MedalPage',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/MedalPage.vue')
},
{
path: '/DetailsPage',
name: 'DetailsPage',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/DetailsPage.vue')
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
7. 心路历程和收获
- Y同学: 在软件开发的道路上,每一次项目都是一次宝贵的经历。我们深有感触地体会到了框架的强大之处,它可以大大提高项目开发的效率,让我们见识到了自己还有许多需要学习和提升的地方,不过因为时间比较赶,对框架的理解和掌握能力还远远不够,需要继续提升自己的专业素养。其次,对git的使用还需要更为熟练地掌握,比如这次如何新建分支,共同在分支上合作写代码耗费了我们很长时间。
- L同学: 团队合作也是我们在这次实践中学到的重要一课。有效的沟通和合作可以帮助团队更好地解决问题,确保项目的成功。总的来说,软件工程实践需要不断学习和改进,每一次的实践都是一次宝贵的经验,让我们明白了自己的不足之处,并激励继续努力学习,不断提升自己的能力。在未来的路上,我会继续努力,不断前行,只有不断学习和实践,才能成为一名真正优秀的软件工程师。
8. 评价结对队友
L->Y:这次的作业我们深刻反思了上次团队作业中的不足,共同商讨并积极改进。在遇到困难时,大家都能够积极面对,没有互相指责,我觉得小杨同学是一个值得信赖的队友,她的专业素养和积极态度都让人印象深刻。
Y->L:针对上次作业中暴露出的问题,我们在这次作业中进行了深入的讨论和改进。面对挑战,我们都能够齐心协力,共同应对,我认为小L同学是一个非常出色的合作伙伴,她的创新思维和高效执行力让我受益匪浅。