108
社区成员
这个作业属于哪个课程 | 2401_CS_SE_FZU |
---|---|
这个作业要求在哪里 | 结对第二次作业——编程实现 |
结对学号 | 222200234 222200404 |
这个作业的目标 | 基于Web技术的原型结对实现,项目部署 |
其他参考文献 | 《构建之法》 |
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时 (分钟) |
---|---|---|---|
Planning | 计划 | 60 | 40 |
• Estimate | • 估计这个任务需要多少时间 | 60 | 40 |
Development | 开发 | 2650 | 2940 |
• Analysis | • 需求分析(包括学习新技术) | 420 | 480 |
• Design Spec | • 生成设计文档 | 30 | 30 |
• Design Review | • 设计复审 | 60 | 40 |
• Coding Standard | • 代码规范(为目前的开发制定合适的规范) | 60 | 30 |
• Design | • 具体设计 | 400 | 420 |
• Coding | • 具体编码 | 1200 | 1400 |
• Code Review | • 代码复审 | 240 | 240 |
• Test | • 测试(自我测试,修改代码,提交修改) | 240 | 300 |
Reporting | 报告 | 240 | 300 |
• Test Report | • 测试报告 | 120 | 180 |
• Size Measurement | • 计算工作量 | 20 | 20 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 100 | 100 |
合计 | 2950 | 3280 |
首页采取自己实现的轮播图展示奥运会标志性图片。导航栏提供奖牌榜、每日赛程、了解更多的入口。
奖牌榜界面完成对奖牌的排名,展示各国所获金牌、银牌、铜牌的数量及总数。可滚动查看。
设有日期下拉框,滚动并点击日期,实现日期切换功能,查询该日期的赛程信息;
点击具体赛程信息,可查看具体比赛详情。(3.4)
可进行组别的切换来查看不同组别赛程
展示此场比赛双方国家出赛运动员名单,位置等信息
展示了具体比赛过程的情况,可通过点击分别查看上下半场的详情。
再次点击即可收起上下半场详细信息。
通过比赛详情中部导航栏,分别选择查看男子足球对阵图或女子足球对阵图。
向用户科普了一些奥运知识,如巴黎奥运会举办背景、吉祥物及会徽。
添加了相应的超链接以便用户了解更多。
拿到题目后,首先通过讨论,确定具体技术路线。我们采用Vue作为前端的主要框架,Python作为后端(爬取数据同时传输数据)进行开发。接着由刘嘉榕同学起型,开始搭建项目框架,以及配置好各种需要用到的环境和依赖和必要的函数,并配置好git仓库,便于后续协作。
再往后我们采用线上+线下的方式进行开发协作。分工协作部分主要体现在不同页面的内容和博客撰写,其中马思敏同学负责首页轮播、奖牌榜、对阵图部分界面设计和博客主要内容的撰写;刘嘉榕同学负责每日赛程,初赛名单,了解更多界面设计,后端,对接,部署和博客界面展示、思路过程等撰写。
主要遇到的问题为数据问题,我们爬取相应的数据,后端处理后返回给前端;这部分需要极大的耐心和细心进行分析。虽然过程比较艰辛,但是总体的合作讨论紧密高效,较高质量完成作业任务。
在项目的初始分析阶段,明确了系统需要满足的功能要求:主要展示奥运会相关的信息,包括奖牌榜、每日赛程和详细赛程。因此,首先需要获取相应的数据,然后需要处理大量的数据文件,并动态加载不同日期或赛事的详细信息。
项目的设计围绕Vue 框架展开,遵循组件化和模块化的设计思想。系统的每个功能模块(如奖牌榜、对阵图)都设计为独立的Vue组件,以便于维护和扩展。
关键设计点:
组件化设计:系统的功能模块,如奖牌榜、每日赛程、详细赛程和对阵图,均设计为独立的Vue组件。这种设计不仅提高了代码的组织性,也便于未来的维护和扩展。
组件复用:为了提高开发效率和减少代码冗余,我们对Nav导航栏和banner每日赛程展示框等在多个页面中重复使用的组件进行了提取和封装。这样的复用策略不仅简化了开发流程,也使得界面的一致性得到了保障。
动态数据加载:利用Vue的watch特性,我们实现了对日期变化的监听,从而动态加载相应日期的赛程信息。这种即时渲染机制确保了用户界面能够根据用户的选择快速响应,提升了用户体验。
Vuex状态管理:为了更好地管理全局状态,我们采用了Vuex。通过Vuex,我们能够集中管理和保存全局状态,如当前活跃页面(activePage)和奖牌榜(medals)。activePage有助于跟踪和管理用户的导航路径,而medals则在首次从后端获取数据后存储起来,避免了不必要的重复请求,提高了应用的性能。
为了满足用户对巴黎奥运会比赛结果的查询需求,我们计划开发一个高效、直观的前后端分离Web应用。以下是项目的详细规划:
技术栈:
功能特点:
问题:由于CSS样式冲突,导致布局错乱
解决方式:使用浏览器的开发者工具检查元素,找出导致布局问题的CSS规则,并进行相应的调整。使用媒体查询和灵活的布局模式(如Flexbox或Grid)来实现响应式设计。
问题:动态网页的数据加载通常依赖于JavaScript,这可能导致爬虫无法一次性获取所有数据,或者数据加载速度慢。
解决方式:使用像Selenium这样的工具来模拟浏览器行为,以便能够执行JavaScript并获取动态生成的内容。 对于数据加载慢的问题,可以在爬虫中设置适当的等待时间,或者使用异步加载技术来优化数据获取过程。
考虑使用API(如果可用)来获取数据,这通常比爬取网页更可靠、更高效。
问题:git无法将本地仓库的更改推送到远程仓库
解决方式:未得到解决,已使用git remote -v命令查看远程仓库的地址正确,且有足够的权限将更改推送到远程仓库。但在git push时出现错误。尝试了多种方法,都无法解决问题。
这个页面数据都是动态加载的,所以处理相当复杂
def scroll_down(driver, distance):
# 执行 JavaScript 滚动操作,滚动距离是 distance
driver.execute_script(f"window.scrollBy(0, {distance});")
# 等待几秒,确保内容加载
time.sleep(3)
# 创建一个空列表来存储格式化后的数据
formatted_data = []
# 获取当前页面的高度
last_height = driver.execute_script("return document.body.scrollHeight")
scroll_distance = last_height // 6 # 每次滚动页面的 1/6 高度
container = driver.find_element_by_class_name("ribbon-bg-container")
print(container)
items = driver.find_elements_by_tag_name("a")
cards = []
for item in items:
card = {}
head = item.find_element_by_class_name("score-header").text.strip()
countries_ele = item.find_elements_by_class_name("score-line d-flex flex-row ")
countries = []
for country in countries_ele:
countries.append( country.text.strip())
footer = item.find_element_by_class_name("card-footer").text.strip()
card["head"] = head
card["countries"] = countries
card['footer'] = footer
cards.append(card)
print(card)
使用正则表达式匹配
def get_player(country1, country2):
import re
# 初始化一个空列表来存储解析后的事件
events = []
up_events = []
down_events = []
# 读取文件
filename = "./data/" + country1 + "&" + country2 + ".txt"
with open(filename, 'r', encoding='utf-8') as file:
# 初始化变量
event = {}
time = None
scores = []
countries = []
players = []
situations = []
real_time = None
for line in file:
line = line.strip()
# 检查时间行
if line.startswith('上半场') or line.startswith('上半场结束'):
continue
if line.startswith('比赛开始'):
up_events = events
events = []
continue
if line.startswith('下半场') or line.startswith('比赛'):
continue
if line.startswith('下半场开始'):
down_events = events
events = []
if re.match(r"(\d+\'\s*\+\d*)|(\d+\')", line) or re.match(r"(\d+-\d+)", line):
if time is None:
# 保存上一个事件
if countries and players and situations:
event["time"] = real_time
event["scores"] = scores
event["countries"] = countries
event["players"] = players
event["situations"] = situations
events.append(event)
# 初始化新事件
event = {}
time = None
scores = []
countries = []
players = []
situations = []
time = line
else:
time += line
else:
if time:
real_time = time
time = None
if line.isdigit():
scores.append(int(line))
elif line:
player_match = re.match(r'(\w+\s\w+)', line)
if player_match:
players.append(player_match.group(1))
continue
situation_match = re.match(
r"(犯规|被犯规|射门|被守门员扑救|黄牌|换人|越位|点球射门|核查是否判罚点球|未犯规|有可能犯规|射门未中|角球|直接任意球(射门)|未中|助攻)",
line)
if situation_match:
situations.append(situation_match.group(1))
continue
country_match = re.match(r'(\w+)', line)
if country_match:
countries.append(country_match.group(1))
continue
for up in up_events:
print(up)
for down in down_events:
print(down)
return up_events, down_events
methods: {
showEvent(group){
this.activeGroup = group;
},
scrollLeft() {
if (this.scrollPosition > 0) {
console.log(this.scrollPosition)
this.scrollPosition -= 0.05; // 根据实际的间隙调整步长
}
},
scrollRight() {
if (this.scrollPosition < this.events[this.groups.indexOf(this.activeGroup)].length - 1) {
this.scrollPosition += 0.05;
}
},
}
<div class="pk-box" style="position: absolute; top: 50px; left: 125px;">
<div class="pk-item" v-bind:class="{ highlighted: highlightedCountry === '法国' }"
@mouseover="highlightCountry('法国')" @mouseleave="highlightCountry(null)">
<img src="..\assets\images\法国.png">
<span class="winner">法国</span>
<span>1</span>
</div>
</div>
data() {
return {
highlightedCountry: null
}
},
methods: {
highlightCountry(countryName) {
this.highlightedCountry = countryName;
}
},
.pk-item.highlighted {
background-color: #8EB1E3;
padding: 5px;
/* 可以添加其他样式,如背景色、阴影等 */
}