109
社区成员
这个作业属于哪个课程 | https://bbs.csdn.net/forums/2401_CS_SE_FZU |
---|---|
这个作业要求在哪里 | https://bbs.csdn.net/topics/619333839 |
结对学号 | 222200410_222200411 |
这个作业的目标 | 使用web技术编程实现上次作业的原型设计,包括基础功能和附加功能的实现并进行部署,同时在截止之前提交一份博客 |
其他参考文献 | 《构建之法》 |
@
222200410第二次结对作业仓库地址
222200411第二次结对作业仓库地址
Vue 3: 官方的 Vue 3 代码规范提供了详细的指南,以帮助开发者编写清晰、一致的代码。官方风格指南
HTML/CSS/JavaScript: 编码规范 by @mdo 提供了一套实用的 HTML 和 CSS 代码规范,以及 JavaScript 编写指南。编码规范
JavaScript: MDN Web 文档项目提供了 JavaScript 代码示例编写指南,包括现代 JavaScript 特性的使用和注释规范。JavaScript 代码示例编写指南
活动 | 预估时间 | 实际时间 |
---|---|---|
计划阶段 | 290分钟 | 300分钟 |
理解项目需求 | 58分钟 | 60分钟 |
制定项目计划 | 59分钟 | 60分钟 |
学习新技术/工具 | 120分钟 | 120分钟 |
定义代码规范 | 58分钟 | 60分钟 |
定义分支策略 | 55分钟 | 60分钟 |
开发阶段 | 820分钟 | 840分钟 |
基础功能实现 | 355分钟 | 360分钟 |
奖牌榜排名 | 118分钟 | 120分钟 |
每日赛程&赛程详细信息 | 119分钟 | 120分钟 |
对阵表 | 118分钟 | 120分钟 |
附加功能实现 | 234分钟 | 240分钟 |
首页推荐 | 117分钟 | 120分钟 |
了解更多 | 117分钟 | 120分钟 |
部署阶段 | 170分钟 | 180分钟 |
准备部署环境 | 58分钟 | 60分钟 |
部署应用到服务器 | 58分钟 | 60分钟 |
测试部署环境 | 54分钟 | 60分钟 |
发布阶段 | 180分钟 | 180分钟 |
发起Pull Request | 59分钟 | 60分钟 |
合并代码 | 60分钟 | 60分钟 |
发布Release版本 | 61分钟 | 60分钟 |
文档阶段 | 290分钟 | 300分钟 |
撰写博客草稿 | 118分钟 | 120分钟 |
编辑和校对博客 | 118分钟 | 120分钟 |
发布博客 | 54分钟 | 60分钟 |
总计 | 2330分钟 | 2460分钟 |
以下将进行此次编码实现的结果展示,具体代码实现介绍详见第六部分。
首页展示
作为基础功能要求之外的附加页面,可以说首页就是一个项目的门面。而在原型设计中,我们将其设计得图文并茂,来提高用户的体验感和兴趣。在此次编码实现中,我们针对原型设计进行微调,以展现更好的呈现效果和内容丰富度。以下将一一进行详细介绍。首页封面
这是首页第一幕,首先就是经过特殊处理契合整体色调的埃菲尔铁塔。
以“2024 Paris”和奥运五环共同构成了网页的logo。而导航栏我们则将其分为四个板块,分别为首页、奖牌榜、赛程以及了解更多。我们为导航栏添加了流畅的动态效果,当鼠标移入时,将会呈现出下划线的效果,提示用户已选中此块;当用户单击时,则会跳转到相应界面;而当鼠标移出相应区域时下划线效果就会消失。
当用户想要了解奥运会更多信息时,如下所示,点击页面中的按钮“了解更多”,同样也可以跳转到了解更多的页面。
首页轮播图
当页面下滑,映入眼帘的就是奥运精彩瞬间的轮播图,轮播图将不停轮播选取的几张奥运健儿们比赛过程中的图片。选择这种表现形式是想通过奥运健儿的高光时刻来表现奥运精神,体现网页主旨,丰富网页内容。
首页栏目
再下滑就是为首页制作的三个展示栏目,分别展示了部分奖牌榜、比赛以及奥运会相关信息,同时一一对应相应的页面。为了使页面的呈现的效果更丰富,我们同样为此添加了动态效果。当鼠标移入展示框时,将会呈现出变灰的效果,移出时则恢复原来的样式。而按钮“complete”则是鼠标移入后变白,移除后恢复,当单击此按钮时则会跳转到对应的页面,为用户展示完整信息。
奖牌榜展示
奖牌榜则采用了较为简洁的展示效果,通过“卡片式”呈现此次奥运会的奖牌总榜,包括排序、国家名称、金牌数、银牌数、铜牌数以及奖牌总数等属性。用户通过鼠标的滚动可以查看完整的奖牌榜。而关于此页面的导航栏,所实现的效果与首页一致,关于导航栏的呈现后续就不一一赘述。
每日赛程展示
关于赛程,同样通过"卡片“的形式来呈现比赛信息(时间、大项、比赛、对战双方),同时我用户可通过日期的下拉列表选择想要查询的日期,即可刷新赛程数据。
详细赛况展示(非足球)
点击相应比赛的卡片,则会跳转到该比赛的详细赛况页面,用户点击不同分组的按钮就会切换到相应分组的详细赛况。
详细赛况展示(足球)详细赛况
对于足球项目,详细赛况页面呈现将更为丰富,增加展示了出赛名单(对战双方各自的上场队员及替补队员的号数、姓名和位置)和比赛详情(对阵双方的得分点)。同时还增设了对战表的选项卡供用户切换页面。而页面左上方的Back按钮则是供用户返回上一页。
Back按钮供用户返回上一页
对阵表
对阵表展现了足球每一轮次的比赛双方和最终得分,并通过线条的连接展示了比赛晋级关系。同时当鼠标移动到某支队伍的比赛信息上时,其他轮次中的该队伍将会做高亮展示。同样对阵表页面也可以通过点击详细赛况跳转回赛况页面。
高亮展示
了解更多展示
为了使网站内容更加丰富,我们增设了了解更多的板块,主要向用户提供此次奥运会的其他信息。
理解原型设计
我们首先对原型设计的每个页面进行了详细分析,包括布局、颜色方案、字体和图标等。我们讨论了用户如何与界面交互,以及这些交互如何转化为实际的功能需求。
详细讨论内容:
任务分配
我们根据原型设计将任务分解为前端和数据来源两大块,并进一步细分为具体的功能模块。
详细讨论内容:
技术栈选择
我们讨论了不同技术栈的优缺点,最终选择了Vue3、Node.js。
详细讨论内容:
数据获取策略
我们讨论了如何使用Python来爬取网页数据,并确定了数据爬取的合法性和合规性(仅用于教学内容)。
详细讨论内容:
界面实现讨论
我们之前的原型设计使用了墨刀设计了用户界面原型,本次作业我们讨论了如何将原型设计转化为实际的UI组件。
详细讨论内容:
功能实现细节
我们讨论了每个功能的实现细节,包括数据处理、用户交互和界面展示。
详细讨论内容:
问题解决策略
我们针对开发中可能遇到的问题,讨论了解决策略和备选方案。
详细讨论内容:
部署策略
我们讨论了如何将应用部署到CodeArt华为云服务器,并确定了部署流程和回滚策略。
详细讨论内容:
文档和博客撰写
我们讨论了项目文档的结构和内容,并确定了博客撰写的分工和发布计划。
详细讨论内容:
沟通和协作工具
我们确定了使用Git和CodeArt进行代码版本控制。
详细讨论内容:
通过这些详细的讨论和协作,我们确保了项目的顺利进行,并在规定的时间内完成了功能的开发和部署。
基础功能分析
功能1:奖牌榜排名
功能2:每日赛程
功能3:对阵表
附加功能分析
附加功能1
:首页
附加功能2
:了解更多
附加功能3
:详细赛况
其他说明
数据来源
部署
通过以上功能分析,我们可以清晰地理解每个功能的目的、需求和用户交互方式,为后续的开发工作提供指导。
系统设计
**技术栈
**:
**开发环境
**:
**构建工具
**:
**部署
**:
功能架构图
以下是功能架构图:
注
:仅列出了主要的路由跳转,具体的成果中的跳转将会更加丰富。
系统创新性
**数据动态加载
**:
**组件化架构
**:
**响应式设计
**:
**交互式用户界面
**:
合理性
**前端路由
**:
**代码分割
**:
**性能优化
**:
**用户体验
**:
**可访问性
**:
**准确性
**:
通过以上设计,我们可以确保项目的顺利进行,并在规定的时间内完成所有功能的开发和部署。
**页面
**:
**组件化
**:
**数据与工具函数
**:
**样式管理
**:
HTML结构设计
CSS样式设计
响应式设计:
样式重用:
scoped
属性来实现样式的局部作用域。JavaScript交互设计
v-if
、v-show
)来实现条件性渲染,动态改变页面布局和样式。:class
和:style
绑定来动态应用CSS类和样式。**代码规范
**:
采用ESLint进行代码质量和风格的检查。
**本地JSON文件
**:
**动态数据加载
**:
<template>
、<script>
和<style>
进行组件封装。**数据模拟
**:
**数据更新策略
**:
对于需要更新数据的页面,使用Vue的watch
或computed
属性实现。
**响应式布局
**:
**交互设计
**:
v-on
指令绑定事件处理器。**视觉反馈
**:
对用户的操作给予即时反馈,如按钮点击效果、鼠标绑定事件等。
**代码分割
**:
**资源懒加载
**:
**文档编写
**:
**模块化
**:
通过以上设计思路,我们可以确保项目按照预期完成,并在未来能够方便地进行维护和扩展。
**问题
**:随着项目页面的增加和复杂度的提升,文件和目录变得难以管理,不管是不同功能模块的页面的划分还是子父组件之间的调用。
**解决方式
**:
组件划分:根据功能和路由将页面划分为不同的Vue组件。
目录结构:创建一个清晰的目录结构,包括components
、views
、router
、assets
等。
Vue CLI:使用Vue CLI的vue create
命令来初始化项目,它提供了一个合理的默认项目结构。
**问题
**:数据的状态管理在多组件间变得复杂。
**解决方式
**:
组件的data
属性:对于局部状态,使用组件的data
属性。
Prop Drilling:通过props将数据传递给子组件,但注意不要过度使用。
**问题
**:在不同设备上提供良好的用户体验。自适应一直是前端实现页面样式时需要特别注意的问题。在不同设备上,页面的呈现效果可能大不相同,如果没有自适应,在一些设备上页面的呈现效果会不理想。而在实现自适应的过程中我们也遇到了困难,花费了一定时间进行“测试”和调整。
**解决方式
**:
**问题
**:从本地JSON文件动态加载数据。在每日赛程和详细赛况那个模块是需要动态加载数据的。用户通过切换日期查看不同日期的赛程信息,这个过程是需要数据的动态加载的。而在爬取下来的json数据中,由于比赛项目的不同和特殊性,不同比赛项目的数据结构是不一样的,在对于数据的处理和加载上遇到了一定的挑战。
**解决方式
**:
created
或mounted
钩子中加载数据。**问题
**:实现复杂和流畅的用户交互。在这个作业中,功能模块和页面是相对较多的,如何设计和实现页面的跳转逻辑正确也是一个值得思考的问题。
**解决方式
**:
**Vue的v-on
**:使用Vue的v-on
指令来监听用户事件。
计算属性和观察者:使用计算属性和Vue的watch
选项来响应数据变化。
Vue Router的导航守卫:使用导航守卫来处理路由跳转逻辑。
**问题
**:保持代码风格一致。由于这次作业不同于个人实战那样可以有自己的代码风格,这是一次结对完成的任务。而不同个体之间的代码风格可能大不相同。为了解决保持项目代码规范和风格的相对一致性这个问题,我们也投入了一些时间来确定规则
**解决方式
**:
**问题
**:将项目相关的环境配置再次移植部署到服务器,window和服务器的Linux环境与依赖存在冲突。
**解决方式
**:
docker-compose.yml
文件中,可以为每个服务指定网络配置,以避免网络冲突。关键功能1
:实现每日赛程中的某场比赛跳转到对应赛程详情,赛程详情展示该比赛属于的大项下所有比赛,需分具体比赛项目(A组B组等)查看
具体实现思路
:实现点击每日赛程中的某场比赛跳转到对应比赛详情背后的传值与接收值
push
方法和路由的 query
参数来导航和传递数据 goToMatchDetails(matchData) {
const routeParams = {
name: 'MatchDetail',
query: {
disciplineName: matchData.disciplineName,
specificEvent: matchData.specificEvent,
matchCode: matchData.matchCode,
}
};
// 检查是否存在 competitors 属性,并且它不为空
if (matchData.competitors && matchData.competitors.length >= 2) {
// 如果存在,添加 competitors 的 name 到路由参数
routeParams.query.team1Name = matchData.competitors[0].name;
routeParams.query.team2Name = matchData.competitors[1].name;
}
// 无论如何,都要推送这三个基本属性
this.$router.push(routeParams);
},
useRoute
钩子获取当前路由的引用,允许访问路由的参数,再从路由查询参数中获取数据,并更新响应式引用的值,获得传过来并存储的值import { ref, onMounted, computed } from 'vue';
import { useRoute } from 'vue-router';
export default {
setup() {
const route = useRoute();
const disciplineName = ref('');
const specificEvent = ref('');
const matchCode = ref('');
const team1Name = ref('');
const team2Name = ref('');
onMounted(async () => {
const { disciplineName: dn, specificEvent: se, matchCode: mc, team1Name: t1, team2Name: t2 } = route.query;
disciplineName.value = dn;
specificEvent.value = se;
matchCode.value = mc;
team1Name.value = t1;
team2Name.value = t2;
使用python脚本重新组织数据形式为更适合详细赛程的json文件,从以日期为文件索引修改为以大项为文件索引
import json
import os
from collections import defaultdict
#获取当前目录下的所有json文件
json_files = [f for f in os.listdir('.') if f.endswith('.json') and os.path.isfile(f)]
#创建一个字典来存储归类后的数据
grouped_data = defaultdict(lambda: defaultdict(list))
#遍历所有json文件
for file_name in json_files:
# 读取JSON数据
with open(file_name, 'r', encoding='utf-8') as file:
json_data = json.load(file)
#遍历数据
for item in json_data:
#创建result文件夹(如果不存在)
result_folder = 'result'
if not os.path.exists(result_folder):
os.makedirs(result_folder)
#遍历归类后的字典,写入文件
for discipline_name, events in grouped_data.items():
#创建一个列表来存储归类后的比赛数据
events_list = []
for specific_event, items in events.items():
event_data = {"specificEvent": specific_event, "items": items}
events_list.append(event_data)
#写入新的JSON文件到result文件夹
output_file_name = os.path.join(result_folder, discipline_name + '.json')
with open(output_file_name, 'w', encoding='utf-8') as f:
json.dump(events_list, f, indent=4, ensure_ascii=False)
print(f"处理后的数据已写入到 {output_file_name}")
将加载的数据解析成结构化格式,并存储在响应式变量中:matchList
存储所有比赛的详细信息。
stages
存储所有不重复的比赛阶段。activeStage
存储当前选中的比赛阶段。
const jsonModule = await import(`@/assets/match/result/result/${disciplineName.value}.json`);
const data = jsonModule.default || jsonModule;
// 解析 JSON 数据并填充到 matchList
data.forEach(group => {
group.items.forEach(event => {
const match = {
id: event.matchCode,
group: event.specificEvent,
team1: event.competitors[0].name,
code1: event.competitors[0].noc.replace('./country_images/', ''),
score1: event.competitors[0].mark,
team2: event.competitors[1].name,
code2: event.competitors[1].noc.replace('./country_images/', ''),
score2: event.competitors[1].mark,
time: `${event.date.month}月${event.date.day}日 ${event.startDate}`,
status: event.competitors[0].winnerLoserTie === 'W' ? '已结束' : event.competitors[1].winnerLoserTie === 'W' ? '已结束' : '进行中'
};
matchList.value.push(match);
if (!stages.value.includes(event.specificEvent)) {
stages.value.push(event.specificEvent);
}
});
});
根据路由参数specificEvent初始化当前选中的比赛阶段,确保高亮显示当前具体比赛项目和具体该场比赛。
if (specificEvent.value && stages.value.includes(specificEvent.value)) {
activeStage.value = specificEvent.value;
} else if (stages.value.length > 0) {
activeStage.value = stages.value[0];
}
使用计算属性 paginatedMatches 实现分页逻辑,根据当前选中的比赛阶段和页码计算当前页应显示的比赛列表。
const paginatedMatches = computed(() => {
if (!activeStage.value) return [];
const start = (currentPage.value - 1) * pageSize.value;
return matchList.value.filter(m => m.group === activeStage.value).slice(start, start + pageSize.value);
});
使用计算属性 totalPages 计算并存储总页数,基于当前阶段下的比赛列表总数和每页显示的比赛数。
computed: {
totalPages() {
return Math.ceil(
this.matchList.filter((m) => (this.activeStage ? m.group === this.activeStage : true)).length / this.pageSize
);
},
},
提供 filterMatchesByStage 等方法,允许用户选择不同的比赛阶段,并在切换阶段时重置分页到第一页。
methods: {
goBack() {
window.history.back();
},
prevPage() {
if (this.currentPage > 1) {
this.currentPage--;
}
},
nextPage() {
if (this.currentPage < this.totalPages) {
this.currentPage++;
}
},
getFlagUrl(code) {
return require(`@/assets/country_images/${code}`); // 调整路径
},
filterMatchesByStage(stage) {
this.activeStage = stage;
this.currentPage = 1; // 重置到第一页
},
},
关键功能2:
实现了一个比赛信息展示和管理的功能,可以根据通过选择日期加载对应日期的所有赛事,并展示该场赛事基本信息
具体实现思路:
日期范围处理:组件提供了一个getDates
函数,用于生成两个日期之间的所有日期数组。这通常用于创建日期选择器,允许用户选择一个特定的日期。
// 生成日期数组
const getDates = (startDate, endDate) => {
const date = new Date(startDate);
const dates = [];
while (date <= new Date(endDate)) {
dates.push(
`${date.getFullYear()}-${(date.getMonth() + 1)
.toString()
.padStart(2, '0')}-${date
.getDate()
.toString()
.padStart(2, '0')}`
);
date.setDate(date.getDate() + 1);
}
return dates;
};
动态数据加载:提供了loadMatches方法,用于根据选择的日期动态加载对应的JSON文件,该文件包含当日的比赛数据。
// 动态加载对应日期的 JSON 文件
async loadMatches() {
// 清空上次加载的比赛数据
this.matches = []; // 清空 matches 数组
const dateKey = this.selectedDate.slice(5).replace('-', '');
console.log(`Loading matches for date: ${this.selectedDate}`); // 日志1:打印正在加载的日期
try {
// 根据选择的日期加载对应的 JSON 文件
const matchData = await import(`@/assets/match/result/${dateKey}.json`);
console.log('Match data loaded:', matchData.default || matchData); // 日志2:打印加载的数据
this.processMatches(matchData.default || matchData);
console.log('Matches processed:', this.matches); // 日志3:打印处理后存储在matches数组中的数据
} catch (error) {
console.error('Error loading match data:', error);
}
},
数据处理:提供了processMatches方法,用于处理加载的比赛数据,包括映射每个比赛数据和为每个参赛者加载国旗图片路径。
// 处理匹配数据
processMatches(matches) {
console.log('Processing matches...'); // 日志4:开始处理数据
this.matches = matches.map(match => {
match.competitors = match.competitors.map(competitor => {
if (competitor.noc) {
// 加载国旗图片路径
competitor.noc = require(`@/assets/match/country_images/${competitor.noc.split('/').pop()}`);
console.log(`Flag loaded for ${competitor.name}: ${competitor.noc}`); // 日志5:打印国旗图片路径
} else {
console.warn(`No NOC for competitor ${competitor.name}`); // 日志6:警告没有NOC的情况
}
return competitor;
});
return match;
});
},
以下是针对所使用的语言(HTML+CSS+JavaScript、Vue 3)的一份简要的代码规范,且在项目中严格遵守:
链接: codestyle.md
在这次结对作业中,我深刻体会到了合作的力量。我们从项目启动时的规划讨论,到技术选型,再到功能实现,每一步都凝聚了我们共同的努力。我负责的部分是奖牌榜、了解更多完整页面的实现以及首页、对阵表、详细赛况的部分页面样式设计以及博客的主要撰写,这对我来说是一个不小的挑战,因为我需要深入理解Vue 3的响应式系统和组件通信机制。在开发过程中,我们遇到了数据绑定和状态管理的问题,但通过查阅文档、搜索在线资源和相互讨论,我们逐渐找到了解决方案。代码审查环节让我们的代码更加健壮,也让我学会了如何编写更规范、更易读的代码。在部署阶段,我体验了将应用部署到服务器的过程。这次经历不仅提升了我的技术能力,也锻炼了我的沟通和协作能力。
这次结对作业是一次宝贵的学习经历。我们从项目的需求分析开始,一步步走到了最终的部署。在这个过程中,我负责了每日赛程和详细赛况的功能实现,这让我有机会深入探索Vue 3的强大功能,尤其是它的响应式数据绑定和组件系统。我们在开发中遇到了一些难题,比如如何优化数据加载的性能,以及如何实现复杂的用户交互。通过不断的尝试和结对队友之间的协作,我们最终克服了这些挑战。代码审查帮助我们保持了代码的质量,也让我学会了如何从别人的代码中学习。在部署过程中,我学习了服务器的基本知识和部署策略。这次合作不仅让我的技术有了显著的提升,也让我认识到了团队合作的重要性。
222200410对222200411的评价:
我的队友在整个项目中表现出了极高的专业素养和团队精神。他对前端技术有较清晰的认识,尤其是在Vue 3和JavaScript方面展现出了很强的能力。在面对复杂的功能实现时,他总是能够耐心地研究问题,并提出新的解决方案。同时他非常可靠,他总是愿意伸出援手,无论是在代码审查还是在解决技术难题时。他的团队合作精神和积极的态度为结对创造了一个非常积极的工作环境。他的沟通非常有效,总是能够及时地提供反馈和建议,帮助团队保持在预期的轨道上。
222200411对222200410的评价:
我的队友在本项目中展现了卓越的专业技能和团队协作能力。她对前端开发有着深刻的理解,特别是在html和css样式方面表现突出。面对项目中的各式页面,她都能很快的找到实现思路快速编写好页面。面对页面组件之间错综复杂相互影响的css样式,她总是能细心耐心的一点点调整,使页面样式达到最完美的状态。她的积极态度和团队精神为团队营造了一个高速前进的工作环境。她的沟通技巧非常出色,总是能够提供及时、有价值的反馈和建议,确保项目按计划顺利推进。
222200410的commit记录:
222200411的commit记录: