688
社区成员
发帖
与我相关
我的任务
分享| 这个作业属于哪个课程 | 2023年福大-软件工程实践-W班 |
|---|---|
| 这个作业要求在哪里 | 结对第二次作业--编程实现 |
| 结对学号 | 222000322 & 222000323 |
| 这个作业的目标 | 学习前后端开发框架,编码实现原型设计,部署网站到云服务器上。 |
| 其他参考文献 | CSDN |
322同学的PSP表格
| PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 30 | 40 |
| • Estimate | • 估计这个任务需要多少时间 | 30 | 40 |
| Development | 开发 | 1550 | 1960 |
| • Comunicate | • 讨论开发流程与使用框架 | 30 | 30 |
| • Analysis | • 技术学习 | 500 | 600 |
| • Coding Standard | • 代码规范 | 30 | 30 |
| • Coding | • 具体编码 | 900 | 1200 |
| • Code Review | • 代码复审 | 30 | 40 |
| • Test | • 测试 | 60 | 60 |
| Reporting | 报告 | 290 | 340 |
| • Test Repor | • 博客编写 | 240 | 300 |
| • Size Measurement | • 计算工作量 | 10 | 10 |
| • Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 30 |
| 合计 | 1870 | 2340 |
323同学的PSP表格
| PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
|---|---|---|---|
| Planning | 计划 | 30 | 40 |
| • Estimate | • 估计这个任务需要多少时间 | 30 | 40 |
| Development | 开发 | 1950 | 2160 |
| • Comunicate | • 讨论开发流程与使用框架 | 30 | 30 |
| • Analysis | • 技术学习 | 600 | 600 |
| • Coding Standard | • 代码规范 | 30 | 30 |
| • Coding | • 具体编码 | 1200 | 1400 |
| • Code Review | • 代码复审 | 30 | 40 |
| • Test | • 测试 | 60 | 60 |
| Reporting | 报告 | 290 | 340 |
| • Test Repor | • 博客编写 | 240 | 300 |
| • Size Measurement | • 计算工作量 | 10 | 10 |
| • Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 30 |
| 合计 | 2270 | 2540 |
技术还是第一生产力,没有技术真的是寸步难行Header包含了导航栏以及登录、注销按钮


复现了原型中设计的首页,点击按钮可以进入了解更多页面

通过点击按钮能够切换路由,同时每个按钮都设有hover样式

展示男女单打选手排名

每日赛况展现当天的比赛信息,可以通过日期滚动栏选择显示相应日期比赛

可以通过点击按钮查看每轮轮对应的对阵信息,对每轮比赛的胜者做了标红,每轮比赛悬停时有高光样式

点击比赛标签可以查看详细赛况。
注:只做了Day1的第一场比赛的详细赛况

了解更多页面显示了澳网的介绍,以轮播图的形式展现了历年比赛精彩瞬间

对错误信息进行alert显示,用户可以通过提示重新尝试登录

提供了再次输入密码框对两次输入的密码进行比对,避免用户输错密码。同样对错误信息进行alert显示


项目实现基于第一次原型设计结对作业,功能结构图见上,经过讨论我们前端采用vue后端采用Springboot实现相应功能,实现流程展示如下:
轮播图主要依靠组件来实现,但如element组件中的轮播图是最基本的轮播效果,要实现原型设计中轮播图展现中间图片时放大的效果有些困难,所以另外下载了swiper组件来实现这一功能,通过查询资料,发现了有人实现了这一效果,但是经过试验发现效果还是不尽人意,同时我们在其他回答下面获得了启发,通过调整css代码,经过试验最终达到了想要的效果,后期分析造成这个问题的原因还是由于对于vue语句和组件属性的不了解。
具体代码在后面展示
原型中实现这个效果还是比较容易,但是要编码实现还是有一些困难,我们最初也是想在网上查看是否有人做过类似功能,但是不知道是问题描述得不清楚还是其他原因,我们并没有找到相关案例,但是轮播图的制作还是给了我们一些启发,是否可以通过轮播图来实现滚动栏?我们将按钮替换图片,通过调整swiper一次性展现图片的个数和相关css代码,我们实现了这一效果,但是还是存在一个问题就是滚动栏的样式会受页面缩放的影响,可能对于这一问题还有其他更好的解法。
在设计晋级图时我沿用了制作原型时的组件的思想,将晋级图分为了3个组件来实现,这需要组件间进行数据的传输,这就导致了数据不同步或者说渲染和数据改变异步的问题出现,最后是通过将数据源声明为computed对象,监听按钮属性的变化,以此来实现数据源的切换。
解决了数据源切换的问题,但是出现了新的问题,由于我晋级图按照原型的设计选择一次展示两轮比赛,这也就是说每个数据源都是一个2层二叉树数组,数组的对象数量不同。但在渲染时看见的现象是先前以及渲染过的二叉树组件并没有渲染为新的二叉树(假如说第一次展示只有一个二叉树,当切换数据源变为两个二叉树时,先前的一个二叉树中的数据并没有变为新的数据),最后通过在PromoteItem的onUpdated方法中,对数据进行赋值,成功解决问题。
一开始打算通过已有组件实现晋级图,但找了半天找到的不是不能插槽就是晋级图的方向不对,最后决定自己划线。由于只需要实现一层二叉树,不需要考虑嵌套递归的问题,这使得实现变得不那么复杂。最后了解到可以通过伪类来实现,在大佬的帮助下成功实现想要的效果。
轮播图实现
主要是通过CSS代码和swiper属性实现,思路见上问题描述
<div class="swiper-container">
<swiper :modules="modules"
:effect="'coverflow'"
:loop="true"
:slidesPerView= "'auto'"
:centeredSlides="true"
:space-between="100"
:autoplay="{ delay: 800, disableOnInteraction: false }"
:observer = true
:observeParents = "false"
class="swiper-wrapper">
<swiper-slide><img src="src\assets\rowplay\6.jpeg" alt="" /></swiper-slide>
<swiper-slide><img src="src\assets\rowplay\1.jpeg" alt="" /></swiper-slide>
<swiper-slide><img src="src\assets\rowplay\2.jpeg" alt="" /></swiper-slide>
<swiper-slide><img src="src\assets\rowplay\3.jpeg" alt="" /></swiper-slide>
<swiper-slide><img src="src\assets\rowplay\4.jpeg" alt="" /></swiper-slide>
<swiper-slide><img src="src\assets\rowplay\5.jpeg" alt="" /></swiper-slide>
</swiper>
</div>
.swiper-container {
margin: auto;
margin-top: 30px;
width: 1300px;
height: 500px;
margin-bottom: 53px;
overflow: visible
}
.swiper-container .swiper-wrapper .swiper-slide{ width: 620px; border-radius: 20px;}
.swiper-container .swiper-wrapper .swiper-slide img{width: 100%; height: 420px; border-radius: 20px;}
.swiper-container .swiper-wrapper .swiper-slide-prev{ margin-top: 60px; height: 300px;}
.swiper-container .swiper-wrapper .swiper-slide-prev img{ height: 300px;}
.swiper-container .swiper-wrapper .swiper-slide-next{ margin-top: 60px; height: 300px;}
.swiper-container .swiper-wrapper .swiper-slide-next img{ height: 300px;}
.swiper-container .swiper-wrapper .swiper-slide-active{ width: 620px;}
日期滚动栏
通过设置CSS将原来轮播图箭头移至两边,同时为了解决swiper中element按钮悬停和按下时效果出现异常的bug,另外编写了相关代码,思路见上问题描述
<script setup lang="ts">
const change = (index: number) => {
currentButton.value = index;
getMatch(index+1).then(res => {
CommunityList.value = res.data.data;
})
}
</script>
<div class="wrap" style="margin: 0 100px;">
<div class="swiper-no-swiping" style="position: relative;padding:0px 0px;margin: 10px;">
<swiper
ref="{swiperRef}"
:slidesPerView="12"
:centeredSlides="false"
:spaceBetween="0"
:navigation="true"
:modules="modules"
class="mySwiper"
style="height: 100px;margin-top: 0px; position: initial;"
>
<swiper-slide><el-button @click="change(0)" :class="{ newStyle:0===currentButton}">Day 1</el-button></swiper-slide>
<swiper-slide><el-button @click="change(1)" :class="{ newStyle:1===currentButton}">Day 2</el-button></swiper-slide>
···
</swiper>
</div>
</div>
晋级图组件
封装了三个组件如下

<script setup lang="ts">
import { ref, onMounted, onUpdated, reactive } from 'vue';
const props = defineProps({
data: Object
})
const round = ref<any>({});
const teams = ref<any>([])
onMounted(() => {
round.value = props.data?.round;
teams.value = props.data?.teams;
console.log(props.data)
})
onUpdated(() => {
round.value = props.data?.round;
teams.value = props.data?.teams;
console.log(props.data)
})
</script>
<template>
<div class="container">
<div class="head">{{ round }}</div>
<div :class="team.win ? 'winner' : 'loser'" v-for="(team, index) in teams">
<div class="leftBox">
<img class="nation" :src="team.players[0].nationality">
<div class="name">{{ team.players[0].shortName }}</div>
</div>
<div class="sets">
<div class="game" v-for="(set, index) in team.scores">{{ set.game }}</div>
</div>
</div>
</div>
</template>
PromoteTree实现由三个PromoteItem对象构成的二叉树
<script setup lang="ts">
import { ref, onMounted, onUpdated, reactive } from 'vue';
import PromoteItem from '../components/PromoteItem.vue'
defineProps({
tree: Object
})
</script>
<template>
<div class="tree">
<div class="parent">
<PromoteItem :data="tree?.match"></PromoteItem>
</div>
<div class="line">
<div></div>
</div>
<div class="child">
<PromoteItem :data="node.match" v-for="(node, index) in tree?.child"></PromoteItem>
</div>
</div>
</template>
PromoteContainer用于封装整个二叉树列表
<script setup lang="ts">
import { ref, onMounted, onUpdated, reactive, watch } from 'vue';
import PromoteTree from '../components/PromoteTree.vue'
const props = defineProps({
list: Array<Object>
})
watch(() =>
props.list
, () => {
console.log(props.list?.length)
}, {
deep: true,
immediate: true
})
</script>
<template>
<div class="main">
<div class="tree" v-for="(t, index) in list" :key="index">
<PromoteTree :tree="t"></PromoteTree>
</div>
</div>
</template>
选手排名: 通过v-for循环显示数组中的对象
<tr v-for="(item, index) in mPlayers" :key="index">
<th style="text-align: left;">{{ item["fullName"] }}</th>
<td>{{ index + 1 }}</td>
<td>{{ item["matches"] }}</td>
<td>{{ item["value"] }}</td>
</tr>
将后端传来的完整二叉树分割为三组二叉树列表,finalsTree需要展示根节点,于是数组中传入完整的二叉树,semifinalsTree需要展示倒数第二轮比赛,因此在数组中传入root节点的两个子二叉树,以此类推,quarterFinalsTree也是同样的逻辑,存入semifinalsTree中的每个子二叉树。
onMounted(() => {
getPlayerPromote().then(res => {
promoteDataTree.value = res.data.data;
finalsTree.value.push(promoteDataTree.value);
promoteDataTree.value.child.forEach((semiNode: any) => {
semifinalsTree.value.push(semiNode);
});
semifinalsTree.value.forEach((node: any) => {
node.child.forEach((quarNode: any) => {
quarterFinalsTree.value.push(quarNode);
})
})
})
})
定义了一个computed对象,监听按钮的变化,给PromoteContainer提供对应的数据
const treeLists = computed(() => {
switch (currentButton.value) {
case 0:
return quarterFinalsTree.value
case 1:
return semifinalsTree.value
case 2:
return finalsTree.value
default:
return []
}
})
登录表单中去除输入的空格
onkeyup="value = value.replace(/[, ]/g,'')"
获取晋级图二叉树:首先根据round字段,从数据库中分别取出finals ,semifinals ,quarterfinals ,forthRound 轮的数据。选手的名字在一轮比赛中只会出现一次,那么当选手名字出现在下一轮中的比赛就说明他这场比赛是他晋级后参加的比赛,由此构建出父子关系。通过以上逻辑,从finals根节点开始,向下寻找,便成功构建出一个晋级二叉树。
public ResponseResult getPromoteTree() {
///获取每一轮的比赛信息
Map<String, List<MatchVo>> matchMap = new HashMap<>();
for (int i = 0; i < BusinessConstants.ROUND.length; i++) {
List<MatchTemp> teamTemps = matchMapper.getTeamListByRoundAndType(BusinessConstants.ROUND[i], BusinessConstants.REQUIRED_TYPE);
List<MatchVo> matchVos = getMatchVosByTempList(teamTemps);
matchMap.put(BusinessConstants.ROUND[i], matchVos);
}
//构建二叉树
MatchVo finals = matchMap.get(BusinessConstants.ROUND[0]).get(0);
List<MatchVo> semifinals = matchMap.get(BusinessConstants.ROUND[1]);
List<MatchVo> quarterfinals = matchMap.get(BusinessConstants.ROUND[2]);
List<MatchVo> forthRound = matchMap.get(BusinessConstants.ROUND[3]);
PromoteTreeNode root = new PromoteTreeNode(finals, new ArrayList<>());
for (int i = 0; i < semifinals.size(); i++) {
MatchVo semiMath = semifinals.get(i);
PromoteTreeNode semiNode = new PromoteTreeNode(semiMath, new ArrayList<>());
if (getPlayerShortName(finals).containsAll(getWinnerShortName(semiMath))) {
for (int j = 0; j < quarterfinals.size(); j++) {
MatchVo quarMath = quarterfinals.get(j);
PromoteTreeNode quarNode = new PromoteTreeNode(quarMath, new ArrayList<>());
if (getPlayerShortName(semiMath).containsAll(getWinnerShortName(quarMath))) {
for (int k = 0; k < forthRound.size(); k++) {
MatchVo forthMath = forthRound.get(k);
PromoteTreeNode forthNode = new PromoteTreeNode(forthMath, new ArrayList<>());
if (getPlayerShortName(quarMath).containsAll(getWinnerShortName(forthMath))){
quarNode.getChild().add(forthNode);
}
}
semiNode.getChild().add(quarNode);
}
}
root.getChild().add(semiNode);
}
}
return ResponseResult.okResult(root);
}




心路历程:我认为本次作业的难点主要在于前端的实现。一方面是不熟悉Vue3框架,另一方面是任务时间有限,可谓是时间紧任务重。在参考教程以及大佬的帮助下,初步搭建好Vue3项目框架后便是本着能跑就行的原则,猛猛的就写写,出bug了就去找,找不到就去学,学不明白就找大佬帮忙...在囫囵吞枣的学习以及编写了几个组件后,我对vue的语法以及常用方法有了基本的掌握,编写过程逐渐变得顺利起来,最后也是成功完成了任务。收获:通过这次作业,我加强了对html、JavaScript、css语法的掌握,尤其是CSS,在进行页面设计时给我好好的补了课,同时学习了TypeScript语法;对Vue3框架有了初步的认识,包括ajax的封装调用,Vue3中的监听对象等等;熟悉了网站前后端项目的部署。也算是成了一个小小的“全栈工程师”(bushi)。心路历程:收到本次作业要求时是比较有压力的,可以说我在技术栈方面是一个新手小白,对与各种框架也是两眼一抹黑,要在一周内完成技术的学习和需求实现,当时感觉十分头疼,不知道如何入手,还是我的队友在本次作业初期对前后端编码和使用框架同我讲解让我对整体编码流程有了初步的了解。收获:主要的收获是了解了vue框架和其中一些语句的使用,对于前后端工作内容和流程有了一定的了解,同时了解了前后端合作中需要注意的一些方面(如接口的对接),也了解了对于页面布局的一些知识,了解了swiper和element组件。博客内容条理清楚,网站也较为美观,完成度很高,赞!
1.晋级图涉及多种颜色,是否从醒目的角度出发?有找同学评价过吗?2.长列的日期轮播效果是否达到?为何不考虑借鉴日历的形式?
)。
②首先,我们本次日期轮播条的设计主要是以还原原型设计为主。其次,由于这次比赛只有14天,所以我们认为使用日期滚动也能达到不错的视觉效果和使用体验,至于老师您说的奥运会日期很多的情况下确实是使用日历的形式实现更为合理,也给了我们一个启发,在之后遇到日期很多的情况时会考虑优先使用日历来实现。