109
社区成员
这个作业属于哪个课程 | https://bbs.csdn.net/forums/2401_CS_SE_FZU |
---|---|
这个作业要求在哪里 | https://bbs.csdn.net/topics/619333839 |
结对学号 | 052207115 042201126 |
这个作业的目标 | 基于 Web 技术的原型实现、Git 协作、项目部署 |
其他参考文献 | 无 |
目录
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 30 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 30 |
Development | 开发 | 900 | 960 |
• Analysis | • 需求分析(包括学习新技术) | 60 | 80 |
• Design Spec | • 生成设计文档 | 60 | 70 |
• Design Review | • 设计复审 | 30 | 20 |
• Coding Standard | • 代码规范(为目前的开发制定合适的规范) | 60 | 80 |
• Design | • 具体设计 | 160 | 160 |
• Coding | • 具体编码 | 400 | 500 |
• Code Review | • 代码复审 | 30 | 30 |
• Test | • 测试(自我测试,修改代码,提交修改) | 140 | 140 |
Reporting | 报告 | 180 | 210 |
• Test Report | • 测试报告 | 60 | 70 |
• Size Measurement | • 计算工作量 | 30 | 40 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 60 | 100 |
合计 | 1120 | 1320 |
四个主要页面在父组件,以切换页面,首页展示主题,链接了解更多模块
展示国家、排名、奖牌数
选择日期部分可以选择奥运会时间之内的日期,下面是静态数据
提供了button可以选择男女的比赛对阵表,同时做好高亮显示
展示巴黎奥运会愿景
在为期三天的工作过程中,我们主要采取线上讨论的方式,由于时间紧迫,虽然我们组能做后端,但是我们刚刚接触vue的使用,所以我们花了大量时间在学习前端三件套基础和Vue代码框架上,
内容分配:
欧阳开源负责项目框架确立、基本的代码设计,首页、每日赛程、对阵表等页面的前端实现
杨世铭同学负责功能设计、项目部署、对阵表页面的前端实现、细节优化等
要求我们对在上次的原型设计进行实现和扩充,通过前端代码实现网页。与上一次结对任务相比,我们缺少了ui界面,必须通过代码,借助Go-Live不断调整代码。需要处理大量的数据的同时,还要尽可能考虑用户体验,保证用户能够在不同的平台浏览网页。在交互友好性方面,我们需要为每个页面设计足够多的跳转入口,防止用户对于网页功能的使用有迷惑的地方。我们决定使用vue-cli来构建代码框架,保证目录结构清晰,是我们的任务专注于逻辑开发和视图设计上。
本次项目采用Vue2框架,辅助的工具有element-ui,vue router3
<template>
<div class="background">
<div class="content">
<h1>2024 夏季巴黎奥运会</h1>
<router-link to="/GetMore">
<p>了解更多</p>
</router-link>
</div>
</div>
</template>
<style scoped>
/* 背景容器 */
.background {
background-image: url('../assets/more-background-pic.jpg');
background-size: cover;
background-position: center;
position: relative;
display: flex;
justify-content: center;
align-items: center;
background-repeat: no-repeat;
width: 100vw;
height: 100vh;
}
/* 页面内容的样式 */
.content {
background-color: rgba(255, 255, 255, 0.7);
padding: 20px;
border-radius: 10px;
text-align: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 36px;
color: #333;
}
p {
font-size: 18px;
color: #555;
text-decoration: none;
cursor: pointer;
}
/* 鼠标悬停时添加下划线 */
p:hover {
text-decoration: underline;
}
</style>
<script>
// import router from '@/router';
export default {
name: "OlympicPage",
};
</script>
读取json里的奖牌榜数据,获取数据
<template>
<div class="Total">
<div class="myDiv">
<div class="medaltext">
奖牌榜
</div>
<img src="../assets/olympicpic.png" alt="">
</div>
<!-- 将表格容器设为居中对齐 -->
<div class="table-container">
<el-table :data="tableData" style="width: 100%">
<el-table-column prop="rank" label="排名" width="180"></el-table-column>
<el-table-column prop="countryname" label="国家" width="180"></el-table-column>
<el-table-column prop="gold" label="金牌"></el-table-column>
<el-table-column prop="silver" label="银牌"></el-table-column>
<el-table-column prop="bronze" label="铜牌"></el-table-column>
<el-table-column prop="count" label="总牌数"></el-table-column>
</el-table>
</div>
</div>
</template>
<style>
.medaltext {
font-size: 50px;
font-weight: 1000;
color: white;
text-align: center;
/* 文本居中 */
}
.Total {
background-color: #cfcfd3;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
}
.myDiv {
background: linear-gradient(to bottom, #5BC2F5, #1F5895);
/* 设置背景颜色 */
width: 100%;
height: 300px;
min-height: calc(100% - 100px);
/* 父元素的高度至少为视窗高度减去100px */
position: relative;
}
/* 表格容器居中 */
.table-container {
display: flex;
justify-content: center;
width: 60%;
margin: 0 auto;
}
</style>
<script>
import totalData from '../data/total.json';
export default {
data() {
return {
tableData: []
};
},
mounted() {
this.loadTableData();
},
methods: {
loadTableData() {
// 直接使用引入的 JSON 数据
this.tableData = totalData.data.medalsList;
}
},
}
</script>
<template>
<div>
<div class="myDiv">
<div class="medaltext">每日赛程</div>
<img src="../assets/olympicpic.png" alt="">
<!-- 显示从 DateTimePicker 子组件中传递过来的 selectedDate -->
<h1>{{ selectedDate }}</h1>
</div>
<br><br>
<!-- 监听子组件传递的日期变化 -->
<DateTimePicker @date-changed="handleDateChange" />
<br><br>
<DayMatch></DayMatch>
</div>
</template>
<style scoped>
.myDiv {
background: linear-gradient(to bottom, #5BC2F5, #1F5895);
/* 设置背景颜色 */
width: 100%;
height: 300px;
min-height: calc(100% - 100px);
/* 父元素的高度至少为视窗高度减去100px */
position: relative;
}
</style>
<script>
import DateTimePicker from './DateTimePicker.vue';
import DayMatch from './DayMatch.vue';
export default {
components: {
DateTimePicker,
DayMatch
},
data() {
return {
selectedDate: '' // 用于存储从 DateTimePicker 接收的日期
};
},
methods: {
handleDateChange(date) {
// 将日期 "09-11" 拆解为月和日
const [month, day] = date.split('-').map(Number);
// 创建合法日期范围
const startDate = new Date(2024, 6, 24); // 7月24日
const endDate = new Date(2024, 7, 11); // 8月11日
// 当前选择的日期
const selectedDate = new Date(2024, month - 1, day); // 注意 month-1 因为 JavaScript 中的月份从0开始
// 检查选择的日期是否在有效范围内
if (selectedDate >= startDate && selectedDate <= endDate) {
this.selectedDate = date; // 更新 selectedDate 的值
} else {
alert("请输入正确时间"); // 提示用户输入正确时间
}
}
}
};
</script>
父组件
<template>
<div>
<div class="myDiv">
<div class="medaltext">
对阵表
</div>
<img src="../assets/olympicpic.png" alt="Olympic Image">
</div>
<br><br>
<!-- 选择性别的按钮 -->
<el-row>
<el-button type="warning" round @click="selectGender('male')"
:plain="selectedGender !== 'male'">男</el-button>
<el-button type="warning" round @click="selectGender('female')"
:plain="selectedGender !== 'female'">女</el-button>
</el-row>
<br><br>
<!-- 根据性别显示不同的组件 -->
<vs-list-component1 v-if="selectedGender === 'male'"></vs-list-component1>
<vs-list-component2 v-if="selectedGender === 'female'"></vs-list-component2>
</div>
</template>
<script>
import VsListComponent1 from './vsListComponent1.vue';
import VsListComponent2 from './vsListComponent2.vue';
export default {
components: {
VsListComponent1,
VsListComponent2
},
data() {
return {
selectedGender: 'male' // 默认显示男性的对阵表
};
},
methods: {
// 切换性别,显示不同的组件
selectGender(gender) {
this.selectedGender = gender;
}
}
};
</script>
<style scoped>
.myDiv {
background: linear-gradient(to bottom, #5BC2F5, #1F5895);
width: 100%;
height: 300px;
min-height: calc(100% - 100px);
position: relative;
}
.medaltext {
font-size: 50px;
color: white;
text-align: center;
}
</style>
子组件
<template>
<div class="container">
<div class="duizhenp">
<p>1/4决赛</p>
<p>半决赛</p>
<p>决赛</p>
</div>
<div class="bracket">
<div class="round quarterfinals">
<div class="match-container" v-for="(match, index) in quarterFinals" :key="'qf' + index">
<div class="match">
<div class="team" :class="{ highlighted: isHighlighted(match.team1) }"
@mouseover="highlightTeam(match.team1)" @mouseleave="clearHighlight">
<span>{{ match.team1 }}</span>
<span class="score">{{ match.score1 }}</span>
</div>
<div class="team" :class="{ highlighted: isHighlighted(match.team2) }"
@mouseover="highlightTeam(match.team2)" @mouseleave="clearHighlight">
<span>{{ match.team2 }}</span>
<span class="score">{{ match.score2 }}</span>
</div>
</div>
<img v-if="index % 2 === 0" class="connector quarter-connector" src="@/assets/线条.png" alt="线条" />
</div>
</div>
<div class="round semifinals">
<div class="match-container" v-for="(match, index) in semiFinals" :key="'sf' + index">
<div class="match">
<div class="team" :class="{ highlighted: isHighlighted(match.team1) }"
@mouseover="highlightTeam(match.team1)" @mouseleave="clearHighlight">
<span>{{ match.team1 }}</span>
<span class="score">{{ match.score1 }}</span>
</div>
<div class="team" :class="{ highlighted: isHighlighted(match.team2) }"
@mouseover="highlightTeam(match.team2)" @mouseleave="clearHighlight">
<span>{{ match.team2 }}</span>
<span class="score">{{ match.score2 }}</span>
</div>
</div>
<img v-if="index % 2 === 0" class="connector semi-connector" src="@/assets/线条.png" alt="线条" />
</div>
</div>
<div class="round finals">
<div class="match-container">
<div class="match">
<div class="team" :class="{ highlighted: isHighlighted(finalMatch.team1) }"
@mouseover="highlightTeam(finalMatch.team1)" @mouseleave="clearHighlight">
<span>{{ finalMatch.team1 }}</span>
<span class="score">{{ finalMatch.score1 }}</span>
</div>
<div class="team" :class="{ highlighted: isHighlighted(finalMatch.team2) }"
@mouseover="highlightTeam(finalMatch.team2)" @mouseleave="clearHighlight">
<span>{{ finalMatch.team2 }}</span>
<span class="score">{{ finalMatch.score2 }}</span>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
highlightedTeam: '', // 存储当前高亮的国家
// 对阵数据
quarterFinals: [
{ team1: '法国', score1: 1, team2: '阿根廷', score2: 0 },
{ team1: '埃及', score1: 1, team2: '巴拉圭', score2: 4 },
{ team1: '摩洛哥', score1: 3, team2: '美国', score2: 1 },
{ team1: '日本', score1: 0, team2: '西班牙', score2: 3 },
],
semiFinals: [
{ team1: '法国', score1: 3, team2: '巴拉圭', score2: 1 },
{ team1: '摩洛哥', score1: 1, team2: '西班牙', score2: 2 },
],
finalMatch: {
team1: '法国',
score1: 3,
team2: '西班牙',
score2: 5,
},
};
},
methods: {
// 判断是否高亮
isHighlighted(team) {
return this.highlightedTeam === team;
},
// 鼠标悬停时高亮指定队伍
highlightTeam(team) {
this.highlightedTeam = team;
},
// 清除高亮
clearHighlight() {
this.highlightedTeam = '';
},
},
};
</script>
<style scoped>
.container {
display: flex;
flex-direction: column;
align-items: center;
padding: 2vw;
background-color: #f2f2f2;
width: 100%;
box-sizing: border-box;
}
.duizhenp {
display: flex;
justify-content: space-around;
width: 100%;
background-color: #444;
color: white;
padding: 1vw;
margin-bottom: 2vw;
}
.bracket {
display: flex;
justify-content: space-between;
width: 100%;
position: relative;
}
.round {
display: flex;
flex-direction: column;
justify-content: space-around;
flex: 1;
}
.match-container {
display: flex;
flex-direction: column;
align-items: center;
margin: 1vw 0;
position: relative;
}
.match {
background-color: white;
border: 1px solid #444;
padding: 1vw;
width: 90%;
max-width: 200px;
z-index: 1;
}
.team {
display: flex;
justify-content: space-between;
padding: 0.5vw;
}
.score {
font-weight: bold;
}
.highlighted {
background-color: #f5d04c;
color: #000;
}
.connector {
position: absolute;
z-index: 0;
}
.quarter-connector {
width: 10%;
height: auto;
top: 125%;
left: 100%;
transform: translateY(-50%);
}
.semi-connector {
width: 10%;
height: auto;
top: 185%;
left: 100%;
transform: translateY(-50%);
}
@media (max-width: 768px) {
.bracket {
flex-direction: column;
}
.connector {
display: none;
}
.round {
width: 100%;
}
}
</style>
<template>
<div class="background">
<div class="content">
<h1>巴黎2024愿景</h1>
<p>2024年巴黎奥运会将会以一种独特的国际庆典精神展现奥林匹克主义的新愿景。</p>
<p>我们将会把世界上最具灵感城市之一的巴黎打造成对运动员而言的一个难忘舞台、一个真正的全球平台来宣传他们以及他们身上那些令人难以置信的故事。</p>
<p>我们将与整个奥林匹克大家庭合作,证明在经历了一个极具挑战的时期之后,体育比以往任何时候都更有助于创造一个更美好的世界。</p>
<p>我们的办赛规划中一大特色就是办赛场馆的95%为现有场馆和临时场馆。每一个场馆都有各自明确的传承意义,与城市的长期发展规划相一致。</p>
<p>运动庆典将会沿塞纳河延伸开来。让庆典氛围从距离巴黎市中心只需15分钟车程的全新奥运村蔓延至巴黎市中心地标,如埃菲尔铁塔以及大皇宫。</p>
<p>几百年来,巴黎一直对来自世界各地的人们敞开大门,其中包括包括奥林匹克运动的先驱们。双方相互合作、相互激励、塑造理念、开创未来。</p>
<p>2024年,我们将在皮埃尔·德·顾拜旦第一次设想体育团结世界可能性的城市举办一场盛大而有意义的体育盛会,这将在体育史上树立一个新的里程碑。</p>
</div>
</div>
</template>
<style scoped>
/* 背景容器 */
.background {
background-image: url('../assets/background-pic.png');
background-size: cover;
background-position: center;
position: relative;
display: flex;
justify-content: center;
align-items: center;
background-repeat: no-repeat;
width: 100vw;
height: 100vh;
}
/* 页面内容的样式 */
.content {
background-color: rgba(255, 255, 255, 0.7);
padding: 20px;
border-radius: 10px;
text-align: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 36px;
color: #333;
}
</style>
<script>
export default {
name: "OlympicPage",
};
</script>
Vue 的组件化开发
Vue 的组件化开发模式让我对代码的可维护性和复用性有了更深的理解。将页面拆分成独立的组件,不仅简化了开发流程,也使得团队协作更加高效。特别是当我在多个页面重复使用同一个组件时,能感受到组件复用的好处。
Element-UI 提供的便捷性
Element-UI 作为一个开源的 UI 组件库,极大地提高了开发效率。它提供了丰富的表单、表格、弹窗等常用组件,使得 UI 界面的开发更加简单直接。不需要从零开始设计 UI,大大减少了样式和交互上的工作量。
Vue-Router 的应用
在项目中,我使用 Vue-Router 实现了页面间的导航功能。通过路由跳转管理不同页面之间的逻辑,让整个应用的逻辑结构更加清晰。同时,利用路由守卫可以轻松实现权限控制,这在实际项目中非常实用。
跟杨世铭同学协作能和我很好地互补,例如我还不是很熟悉的项目部署,有java后端经验的世铭同学能够非常熟练地部署在服务器上,能够快速理解项目需求,并高效完成任务。在团队协作方面,他非常乐于沟通,善于分享自己的见解和经验。在代码开发中,他保持了很高的代码质量,注重细节,特别是在处理复杂功能时,他总能找到简洁有效的解决方案。
欧阳开源同学在此次合作中展现了出色的团队协作能力。他在项目中的细致和严谨确保了每个阶段的任务都能够高效、准时地完成。 尤其是在页面开发方面,他不仅完成了高质量的编码工作,还积极参与设计讨论,为项目提出了许多有建设性的优化建议。在结对作 业期间,他始终保持着开放的心态和积极的态度,持续推动项目向更高标准迈进。欧阳同学的出色表现与专业能力使他成为我们团 队中不可或缺的重要成员。
052207115欧阳开源:50%
042201126杨世铭:50%