581
社区成员
这个作业属于哪个课程 | 软件工程-23年春季学期软件工程 |
---|---|
这个作业要求在哪里 | 结对第二次作业--编程实现 |
结对学号 | 222000133、222000134 |
这个作业的目标 | 1、编程实现所需功能 2、撰写博客 |
其他参考文献 | CSDN、《构建之法》、哔哩哔哩 |
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 15 | 10 |
• Estimate | • 估计这个任务需要多少时间 | 15 | 10 |
Development | 开发 | 2475 | 2980 |
• Analysis | • 需求分析 (包括学习新技术) | 130 | 150 |
• Design Spec | • 生成设计文档 | 30 | 30 |
• Design Review | • 设计复审 | 20 | 15 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 15 | 15 |
• Design | • 具体设计 | 25 | 20 |
• Coding | • 具体编码 | 2100 | 2600 |
• Code Review | • 代码复审 | 35 | 30 |
• Test | • 测试(自我测试,修改代码,提交修改) | 120 | 120 |
Reporting | 报告 | 80 | 70 |
• Test Repor | • 测试报告 | 30 | 30 |
• Size Measurement | • 计算工作量 | 20 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 20 |
合计 | 2570 | 3060 |
该成果主要基于澳大利亚网球公开赛网站设计实现,❗❗所爬取的数据均用于教学使用❗❗。
图片失效点这
设置一级导航栏和了解更多下的二级导航栏,实现页面切换。
一级导航栏下:首页、选手排名、每日赛程、晋级图、冠军地图
二级导航栏(了解更多)下:举办历史、冠军地图
上半部分放置澳网宣传视频。
下半部分采用轮播图放置宣传照片,可通过点击图片、"<"或">"切换按钮、下方横条进行图片切换。
相比于原型设计,增加每2000ms自动切换图片功能,更加合理。
采用下拉框选项切换不同类型的选手排名。
采用表格形式展示选手排名,表格设置成斑马纹,鼠标移到某一行会改变底色。
采用下拉框选项切换不同类型的比赛。
采用轮播按钮实现按钮组。
切换日期查看不同比赛日的赛程。选择比赛类型以及日期,下方出现对应赛程信息,显示比赛类型、比赛场地及轮次、参与选手、比赛时间和比分。实现获胜者排名加粗显示及对应“✔”图标的显示。(全部界面都已实现)
采用下拉框切换比赛场次。
从每日赛程中点击某一场比赛进入详细赛况界面,上方显示该场比赛的两位选手及赛程信息,下方选择不同的下拉选项切换比赛场次。
采用下拉框选项切换不同类型的选手排名。
采用切换按钮实现晋级图排列方式(横排或竖排)和是否展开(全部折叠或全部展开),也可通过晋级图上的“㊉”或“㊀”实现部分展开与折叠。
可以通过晋级图看到选手的晋升路线,显示选手和姓名和比分,实现获胜者排名加粗显示及对应“✔”图标的显示。鼠标移动到某一场比赛时会蓝色边框提示。
左侧采用事件线从创立之初、不断发展、公开赛时段讲述澳网的举办背景。
右侧放置澳网相关图片。
以世界地图的形式展示各个国家的冠军数及排名,颜色深浅表示该国家冠军数所处的层级。鼠标移动到对应国家时,该国家绿色高亮显示并显示该国家的rank和gold。左下角显示不同层次,鼠标移到上面时处于该层次的所有国家都绿色高亮显示。
图片失效点这
❗❗若下方“图片失效点这”进去为“403 Forbidden”,只要复制网址再打开即可。❗❗
因为是舍友,而且借用了活动室,所以线上的交流比较少,大部分遇到的问题和困难都是线下一起交流解决的。
前端设计:
Vue2
、axios
、Echarts可视化技术
后端设计:
Servlet
、Tomcat
HttpServlet
的类,重写doGet
方法以便前后端的交互。对于每日赛程,通过req.getParameter
方法获取前端传入的条件数据,解析数据后以court
为key
值,Match
类为value
形成HashMap
,外面再套一层Map
转为json对象进行数据回传方便前端操作,自此形成API
接口。
- 问题一:每日赛程走马灯式的按钮组实现问题
- 解决方式: 因为在制作首页轮播图时直接采用了
vue2
的element-ui
组件,所以我们以为走马灯式的按钮组应该也差不多,但是把组件放上去的时候,我们意识到不对,按钮组和轮播图不太像,因此我们继续查找资料,我们先尝试了vue-slick
组件,但是效果很不好,后来又找到了实现slick-carousel的js代码,可惜我们不会把代码嵌到vue中,遂放弃😭。最后,我们终于找到了vue-slick-carousel
,当时有预感可以实现,我们配了一堆属性之后,终于实现了和官网一样的走马灯式按钮!(可以说是喜极而泣了,毕竟搞了一个晚上,而且属性全是英文的😭)然而,在我们实现之后,突然发现了vue-slick-carousel
官网上有配好的属性!(发现的时候,人是傻的,毕竟官网全是英文,扑面而来的英文字母让我们没想到可以去扒一扒官网😰)
- 问题二:每日赛程每场比赛div布局实现问题
- 解决方式: 因为澳网官网上每场比赛的div是一行多个的,为了实现和官网一样的效果,我们通过查找资料之后,发现大部分用
flex
布局实现,但是当我们实施之后,发现并没有好的效果,最后实在没有办法了,咨询了其他同学,发现用float
就可以了。怎么能这么简单啊啊啊
- 问题三:晋级图树状形式实现问题
- 解决方式: 由于晋级图中的相关比赛之间存在晋级线连接,一开始不知道如何实现这个晋级线,包括比赛的排版要实现成官网那样似乎也很难实现。在网上搜索
vue2
实现晋级图也是几乎没有。后来转换了思维方向,将晋级图换个方向,决赛在左边,这样看似乎是一颗树状图,除根节点和叶节点外都有两个子节点。再次搜索发现了vue2-org-tree
,设置对应的属性及事件最终实现晋级图。最后相较于官网实现的晋级图增加了横排竖排展示方式以及是否展开。
- 问题四:冠军地图实现问题
- 解决方式: 通过查找资料,我们知道要引入
echarts库
,引入echarts5
后(请记住这个echarts5),插入了中国地图进行测试(网上绝大部分的资料都是使用中国地图作为示例的),测试之后,成功了!(喜极而泣+1)但是当我们换成世界地图的world.js文件时,报错了,查看js文件后发现,两个文件的json结构不一样,不能直接替换。后面改了代码,因为echarts5
淘汰了地图,所以我们在网上找到了map
包,引入之后,发现运行起来,可惜的是,不知道为什么,运行之后有两个世界地图,我们复盘了一下代码,一致认为代码是没有问题的,之后修改height
属性,让两张地图完美重叠,但是后来发现在不同的电脑或者不同的浏览器上,地图依然是错开的。最后,我们突然想到,会不会是echarts
版本的问题,毕竟echarts5把map淘汰了,于是我们死马当活马医,把echarts5
替换成echarts4
,终于,成功了!
- 问题一:数据文件路径正确却无法找到问题
- 解决方式: 首先对于数据文件路径进行了多次检查,确定路径确实没有写错。后续通过查找发现,Servlet在编译后,读取文件的位置并不在我们所存放数据的src下,而是从web文件夹下读取,最后采用
this.getServletContext().getRealPath("/WEB-INF/classes/data/"+day+".json");
得到正确的文件路径,最终成功读取数据文件。
- 问题一:前端跨域访问后端问题
- 解决方式: 一开始前端存在与后端数据交互的界面一直接收不到数据,在确保代码正确性的情况下,上网搜索,可能是跨域访问原因,由于后端所设置的端口号为
9090
,而前端的默认端口号为8080
,通过f12
开发者工具查看确实得到错误已拦截跨源请求:同源策略禁止读取位于...的远程资源。
具体解决方法为,打开config/index.js
文件,找到proxyTable :{}
,在下面添加如下代码后,问题得以解决。
index.js
在/srcrouter/index.js为所有页面配置router路由,实现路由跳转。
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home.vue'
...
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
...
]
})
App.vue
采用element ui组件。
顶部:使用<el-menu>
实现导航栏,通过mode
属性设置为"horizontal"
设置为水平方向。<el-menu-item>
放置菜单项,通过设置index
路径实现界面切换。了解更多二级菜单采用<el-submenu>
设置index
为2(2个子菜单项)
主体部分:通过<router-view/>
实现各界面内容的切换。
<template>
...
<el-menu :default-active="this.$route.path" class="el-menu-dmeo" router mode="horizontal" @select="handelSelect">
<img src="@/assets/logo.png">
<el-menu-item index="/home">首页</el-menu-item>
...
<el-submenu index="2">
<template slot="title">了解更多</template>
...
</el-submenu>
...
<router-view/>
...
</template>
各界面代码
所有的界面(.vue文件)放在components下。
主要展示和说明<template>
和<script>
,<css>
部分不做过多说明。
1、首页 Home.vue
通过<iframe>
插入澳网宣传视频,src
属性设置视频地址。
通过el-carousel
实现轮播图并能自动切换。设置interval
为2000,实现2s切换一张图片。
<template>
...
<iframe src="//player.bilibili.com/player.html?aid=865635314&bvid=BV1Q54y1T7HT&cid=1043214210&page=1"
scrolling="no" border="0" frameborder="no" framespacing="0" allowfullscreen="true">
</iframe>
...
<el-carousel :interval="2000" type="card" height="600px">
<el-carousel-item v-for="item in imagebox" :key="item">
<img :src="item.idView" class="image" />
</el-carousel-item>
</el-carousel>
...
</template>
<script>
export default {
name: 'Home',
data () {
return {
homeTitle: require('@/assets/homeTitle.png'),
bshelton: require('@/assets/bshelton.png'),
erybakina: require('@/assets/erybakina.png'),
bg: { ... },
imagebox: [
{ id: 0, idView: require('../assets/imagebox/1.png') },
...
]
...
</script>
2、选手排名 Ranking.vue
通过<el-select>
设置下拉框,v-model
设置默认选项。<el-option>
设置排名类型下拉选项,v-for
从options
循环得到所有选项,通过@click.native
设置选项切换时的事件变化。
通过<el-table>
实现表格存放选手排名数据。stripe
增加表格斑马纹样式。<el-table-column>
设置每一列列名。
<template>
...
<el-select ref="selectValue1" v-model="value1" @change="choiceOn">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
</el-select>
...
<el-table :data="menAces" :header-cell-style="{
color: '#fff',
background: '#0091d2',
fontWeight: '700',
}" stripe style="width:100%" :default-sort="{ prop: 'rank', order: 'ascending' }">
<el-table-column prop="name" label="Name" width="200"> </el-table-column>
<el-table-column prop="rank" label="Rank" width="100" align="center"> </el-table-column>
<el-table-column prop="matches" label="Matches" width="120" align="center"> </el-table-column>
<el-table-column prop="aces" label="Aces" align="center"> </el-table-column>
</el-table>
...
</template>
由于选手排名数据量较少,所以直接内嵌在vue中。
<script>
export default {
name: 'Ranking',
data () {
return {
menAces: [ ... ],
womenAces: [ ... ],
options: [ ... ],
value1: '选项1'
}
},
...
</script>
3、每日赛程 Schedule.vue
通过<el-select>
设置下拉框,v-model
设置默认选项。<el-option>
设置比赛类型下拉选项,v-for
从options
循环得到所有选项,通过@click.native
设置选项切换时的事件变化。
通过<VueSlickCarousel>
实现按钮轮播,<el-button>
组件采用<v-for>
实现按钮设置,实现按钮组(同时最多只能有一个被选中)。
通过v-for
实现每个比赛的赛况展示,通过v-if
实现获胜方的加粗显示以及对应“✔”的显示。
<template>
...
<el-select ref="selectValue1" v-model="value1">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"
@click.native="choiceEventOn(item)" />
</el-select>
<div class="slick">
<VueSlickCarousel :centerMode="true" :centerPadding=20 :infinite="true" :slidesToShow=10 :speed:=500>
<el-button round size="medium" v-for="item in buttons" v-model="value2" :key="item.value"
@click.prevent="choiceOn(item)" autofocus="true">
{{ item.label }}
</el-button>
</VueSlickCarousel>
</div>
<div class="main" v-for="item in matchData.data" :key="item.court">
...
<div class="rowAnchor" v-for="match in item.match" :key="match.duration" v-on:click='testFunc()'>
...
<p class="scoreHeaderTitle">{{ match.event }}</p>
<p class="scoreHeaderSubtitle">{{ match.round }} • {{ match.court }}</p>
...
<p class="scoreHeaderStatus">{{ match.status }}</p>
<p class="scoreHeaderDuratio">{{ match.duration }}</p>
...
<p class="playerRowTeam1" v-if="match.winner == 0"><strong>{{ match.players[0] }}</strong></p>
<p class="playerRowTeam1" v-if="match.winner == 1">{{ match.players[0] }}</p>
...
<span class="playerRowStatus" v-if="match.winner == 0">√</span>
<span class="playerRowScore1">{{ match.score[0] }}</span>
...
</div>
...
</div>
</template>
通过axios
实现与后端的交互,在watch
中监听日期和比赛类型的切换,实时展示。
<script>
...
export default {
...
data () {
return {
day: '0116',
events: 'Event',
matchData: null,
buttons: [ ... ],
options: [ ... ],
value1: '选项1',
value2: '0116'
}
},
...
methods: {
...
testFunc () {
this.$router.push('/results')
}
},
mounted () {
axios.get('/matchData', {
params: {
day: this.day,
event: this.events
}
}).then(response => {
this.matchData = response.data
console.log(this.matchData)
})
},
watch: {
day (Day) { ... },
events (Event) { ... }
}
}
</script>
4、详细赛况 Results.vue(附加功能1)
通过<el-select>
设置下拉框,v-model
设置默认选项。<el-option>
设置比赛场次下拉选项,v-for
从options
循环得到所有选项,通过@click.native
设置选项切换时的事件变化。
<template>
...
<el-select ref="selectValue1" v-model="value1" @change="choiceOn">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"
@click.native="selectThing(item)" />
</el-select>
...
</template>
<script>
export default {
name: 'Results',
props: {
imgsrc1: { type: String, default: '../static/img/day1_result1_set2_1.png' },
...
},
data () {
return {
options: [ ... ],
value1: '选项1'
}
},
...
}
</script>
5、晋级图 Draws.vue
通过<el-select>
设置下拉框,v-model
设置默认选项。<el-option>
设置比赛类型下拉选项,v-for
从options
循环得到所有选项,通过@click.native
设置选项切换时的事件变化。
通过<el-switch>
设置晋级图横排或竖排两种不同展示方式,v-model
设置默认选项横排。
通过<el-switch>
设置晋级图是否全部展开,v-model
设置默认全部展开。
通过<vue2-org-tree>
树实现晋级图,通过:render-content
设置结点渲染样式,@on-expand
设置展开。
<template>
...
<el-select ref="selectValue1" v-model="value1">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"
@click.native="choiceTypeOn(item)" />
</el-select>
...
<el-switch v-model="horizontal" :width="50" active-text="横排" inactive-text="竖排" style="margin-top: 8px" />
...
<el-switch v-model="expandAll" :width="50" active-text="全部展开" inactive-text="全部折叠" style="margin: 8px"
@change="expandChange" />
...
<el-scrollbar :style="scrollTreeStyle" class="el-org-tree">
<vue2-org-tree :data="treeData.data" :horizontal="horizontal" :collapsable="collapsable"
:label-class-name="labelClassName" :render-content="renderContent" name="organ" @on-expand="onExpand"
@on-node-click="onNodeClick" />
</el-scrollbar>
...
</template>
<script>
import VueSlickCarousel from 'vue-slick-carousel'
export default {
....
data () {
return {
...
treeData: {
labelClassName: 'bg-color-orange',
basicInfo: { id: null, label: '---null' },
basicSwitch: false,
data: { ... }
},
...
buttons: [ ... ],
options: [ ... ],
value1: 1
}
},
...
methods: {
renderContent (h, data) { return ( ... ) },
onMouseout (e, data) { ... },
onMouseover (e, data) { ... },
NodeClick (e, data) { ... },
toggleExpand (data, val) { ... },
collapse (list) { ... },
onExpand (e, data) { ... },
expandChange () { ... },
...
}
}
}
</script>
6、了解更多
举办背景 More1.vue(附加功能2)
通过<el-timeline>
实现澳网举办历史时间线,<el-timeline-item>
实现每一项的内容设置,timestamp
属性设置时间点。
<!-- eslint-disable vue/valid-template-root -->
<template>
...
<el-timeline>
<el-timeline-item class="point" v-for="(activity, index) in activities" :key="index" :color="activity.color"
:size="activity.size" :timestamp="activity.timestamp">
<span>一、创立之初</span>
</el-timeline-item>
<el-timeline-item timestamp="1904年" placement="top">
<el-card>
<p>澳大利亚和新西兰的网球机构官员决定成立澳大拉西亚草地网球协会负责筹办每年一届的澳大拉西亚锦标赛并共同组队参加戴维斯杯的比赛。</p>
</el-card>
</el-timeline-item>
...
</el-timeline>
...
</template>
<script>
export default {
name: 'More1',
data () {
return {
moreTitle: require('@/assets/moreBackground.png'),
activities: [{ size: 'large', color: '#0091d2' }]
}
}
}
</script>
冠军地图 More2.vue(扩展功能)
采用ref
设置冠军地图。
<template>
...
<div class="worldmap" :style="{ height: height, width: width }" ref="myEchart"></div>
...
</template>
mounted ()
表示在一开始就初始化展示地图。
<script>
import * as echarts from 'echarts'
import '../../node_modules/echarts/map/js/world.js'
export default {
name: 'echarts',
props: {
width: { ... },
height: { ... }
},
...
mounted () {
this.initChart()
},
methods: {
initChart () {
this.chart = echarts.init(this.$refs.myEchart)
window.onresize = echarts.init(this.$refs.myEchart).resize
this.chart.setOption({
backgroundColor: '#fff',
title: { ... },
visualMap: {
type: 'piecewise',
pieces: [ ... ]
},
tooltip: {
trigger: 'item',
formatter: function (val) {
return val.data.name + ': ' + '<br/> rank💡:' + val.data.rank + '<br/>gold🥇:' + val.data.gold
}
},
geo: {
map: 'world',
label: {
emphasis: {
show: false
}
},
roam: false,
silent: true
},
series: [{
type: 'map',
mapType: 'world',
mapLocation: {
y: 100
},
data: [ ... ],
nameMap: { ... },
symbolSize: 12,
label: {
normal: {
show: false
},
emphasis: {
show: false
}
},
itemStyle: { ... } // 样式
}]
})
}
}
}
</script>
通过Servlet实现后端。
MatchServlet.java
重写doGet
方法,获得从前端传入的日期和比赛类型,传入Lib
中进行相应的解析。采用HashMap<String, ArrayList<Match>>
存储比赛数据,以场地court
为key
,比赛信息为value
。由于前端只接受json数据,所以最后通过将Map
转为JSONObject
传回前端。
最后在HashMap
外再套一层Map
再回传是为了方便前端对数据的处理。
@WebServlet("/matchData")
public class MatchServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
resp.setContentType("application/json");
String day = req.getParameter("day");
String event = req.getParameter("event");
String filePath = this.getServletContext().getRealPath("/WEB-INF/classes/data/"+day+".json");
HashMap<String, ArrayList<Match>> matchData = Lib.outputSchedule(filePath,day,event);
ArrayList<Data> data=new ArrayList<>();
for(String key:matchData.keySet()){
Data data1=new Data();
data1.setCourt(key);
data1.setMatch(matchData.get(key));
data.add(data1);
}
HashMap<String,ArrayList<Data>> map=new HashMap<>();
map.put("data",data);
JSONObject jsonObject = new JSONObject(map);
resp.getWriter().print(jsonObject);
resp.getWriter().flush();
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
Lib.java
采用fastjson2
进行json数据的解析。返回HashMap<String, ArrayList<Match>>
。具体解析方法与个人作业二类似,故不作代码的展示。
vue-slick-carousel
、vue2-org-tree
和element-ui
的走马灯、时间线、下拉框、按钮等,我对vue框架有了进一步的认识,也学会了怎么上传代码到gitcode分支。