110
社区成员
这个作业属于哪个课程 | FZU_SE_teacherW_4 |
---|---|
这个作业要求在哪里 | 结对第二次作业——编程实现 |
结对学号 | 222200101,222200102 |
这个作业的目标 | 编程实现结对第一次作业的网页原型 |
其他参考文献 | 《构建之法》 |
222200101:
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 20 | 20 |
• Estimate | • 估计这个任务需要多少时间 | 20 | 20 |
Development | 开发 | 1800 | 2380 |
• Analysis | • 需求分析 (包括学习新技术) | 120 | 120 |
• Design Spec | • 生成设计文档 | 30 | 30 |
• Design Review | • 设计复审 | 60 | 120 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 10 | 10 |
• Design | • 具体设计 | 30 | 800 |
• Coding | • 具体编码 | 1200 | 880 |
• Code Review | • 代码复审 | 340 | 120 |
• Test | • 测试(自我测试,修改代码,提交修改) | 20 | 300 |
Reporting | 报告 | 100 | 100 |
• Test Report | • 测试报告 | 60 | 60 |
• Size Measurement | • 计算工作量 | 10 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 30 |
合计 | 1920 | 2500 |
222200102:
PSP | Personal Software Process Stages | 预估耗时(分钟) | 实际耗时(分钟) |
---|---|---|---|
Planning | 计划 | 30 | 60 |
• Estimate | • 估计这个任务需要多少时间 | 30 | 60 |
Development | 开发 | 900 | 2220 |
• Analysis | • 需求分析 (包括学习新技术) | 200 | 400 |
• Design Spec | • 生成设计文档 | 25 | 20 |
• Design Review | • 设计复审 | 60 | 20 |
• Coding Standard | • 代码规范 (为目前的开发制定合适的规范) | 30 | 20 |
• Design | • 具体设计 | 300 | 600 |
• Coding | • 具体编码 | 600 | 1200 |
• Code Review | • 代码复审 | 20 | 120 |
• Test | • 测试(自我测试,修改代码,提交修改) | 180 | 240 |
Reporting | 报告 | 90 | 110 |
• Test Report | • 测试报告 | 50 | 60 |
• Size Measurement | • 计算工作量 | 10 | 10 |
• Postmortem & Process Improvement Plan | • 事后总结, 并提出过程改进计划 | 30 | 40 |
合计 | 1020 | 2390 |
首页集成了其他网站的入口与介绍
演示如下
每个页面可以通过左上角的小球呼出导航栏,点击对应网页标题即可完成跳转
演示如下
底部导航可以直接跳转到对应网页,并引入了一些外部链接
演示如下
奖牌榜演示如下
设计参考了官网,除了赛事筛选器,基本复刻了官网的交互.
点击日期可以呼出日历,点击其他日期可以切换到该日期对应的赛事列表
点击打包项目预览的'+'按钮即可呼出每一个项目的结果信息,点击减号即可收齐
点击图标可以跳转到对应详细赛况界面,打包项目需要点开赛事列表,在点击跳转.
演示如下
普通情况:
打包情况:
通过点击每日赛程跳转,或者通过导航跳转(此方法将默认展示足球界面)
可以切换不同赛事阶段并且点击赛事显示详细信息演示如下
通过点击详细赛程的对阵图按钮跳转,或者通过导航跳转(此方法将默认展示足球界面)
演示如下
页面设计参考了官网,旨在完善补充除了比赛信息以外的别的奥运资讯
页面具有一些外部链接按钮,以及顶部可以直接定位页面内模块,具体演示如下
该页面的每个按钮都可以点击跳转到对应的网页,演示如下
由于我们两个是舍友,结对过程讨论都是面对面的,线上聊天记录部分截图如下
附图如下
主要设计思路是根据页面划分模块,以页面为单位进行划分职责并且维护对应的js以及css文件.
首页,奖牌榜,和了解更多的设计较为简单.奖牌榜利用之前爬取的数据,通过设置元素与布局已经写一些简单的就完成了,首页和了解更多则是通过图片和布局设计添加外部链接完成.
每日赛程由于文件量较大,选择根据日期显示不同赛事(也符合JSON文件根据日期保存的格式),由于赛事数组的属性有不同,固使用了if,else判断这个数组元素的属性格式,进行判断选择那种输出方式,打包赛事由于具体赛事可能时间不同固处于文件的不同位置,采取将JSON文件数组读入程序数组中,遍历到有goupid的元素时,以其为起点遍历文件添加并且将数组内对所有groupip相同的元素移动到其后面,之后如上过程遍历数组.
详细赛程包括显示每个项目的不同阶段,如D组、C组、B组、半决赛、决赛等,以及选择不同比赛项目查看比赛的详细信息,详细信息显示默认为足球,由每日赛况点击进入时,向网页传入包含比赛信息的参数,依次跳转到相应的比赛界面。还包括跳转到对应项目的对阵表功能,如果该项目有对阵表可以选择跳转到相应链接。每个项目具有不同的阶段,对 json 文件进行读取时,可以判断 phase 类型来确定是否是一个阶段,使用循环添加元素,以适应不同的比赛项目需求。选择不同阶段的各比赛场次时,会显示该项目的详细信息。
对阵表的实现基于每个比赛项目的数据实现,由于比赛项目不一定具有对阵表,通过判断比赛参与者进行简单判断,由于赛事项目过多,项目的对阵表或是没有或是具有各种差异,因此可能存在显示问题,对于明确没有的项目会提示不存在对阵表。对阵表实现鼠标悬浮在某个国家时,可以高亮显示该国家在对阵表中的所有位置。通过比赛详细信息查看对阵表时,会向页面传入对应的项目参数,以获取相关数据。
通过fetch获取JSON文件之后,利用信息按照指定格式构建组件,然后添加到容器里
具体代码如下
fetch('./data/countrymedals.json')
.then(response => response.json())
.then(data =>
{
const itemsContainer = document.getElementById('medal-list-items');
data.forEach(item =>
{
const leaderboardItem = document.createElement('div');
leaderboardItem.className = 'medal-list-item';
leaderboardItem.innerHTML =
`
<span class="item_rank">${item.rank}</span>
<img src="./img/flag/${item.code}.png" class="item_flag">
<span class="item_name">${item.countryName}</span>
<span class="item_gold">${item.gold}</span>
<span class="item_silver">${item.silver}</span>
<span class="item_bronze">${item.bronze}</span>
<span class="item_total">${item.total}</span>
`;
itemsContainer.appendChild(leaderboardItem);
});
})
.catch(error => console.error('Error fetching JSON:', error));
由于本人水平较低,写的代码很冗余,固根据划分的情况进行关键代码展示
以下代码均在如下循环中进行
console.log(date);
let day = String(date.getDate()).padStart(2, '0'); // 获取日期并补零
let month = String(date.getMonth()).padStart(2, '0'); // 获取月份并补零
let jsonFilePath = `./data/2024-${month}-${day}.json`;
fetch(jsonFilePath) // 获取对应日期的JSON 文件(文件名为YYYY-MM-DD.json)
.then(response => response.json()) // 解析 JSON 文件
.then(data =>
{
//代码部分
}
每日赛程展示中训练赛和开幕式是很特殊的两种格式,没有对应的项目跳转所以放在if判断中的第一个判断
if (data[index].phaseCode=="TRNO") //训练赛赛事阶段为TRNO不设置跳转
{
const detailText = document.createElement('p');
detailText.className = 'day_game_previev_detailnametxt';
detailText.innerText = data[index].eventUnitName;
dayGamePreviev.appendChild(detailText);
container.appendChild(dayGamePreviev);
continue;
}
打包项目需要特殊的输出格式,单独赛事还要判断有无奖牌进行进一步判断输出格式,固将其放在,第二个判断if里
一下展示进入if后的遍历数组进行项目打包的代码
var group_game_name = [];//详细赛程名数组
group_game_name.push(event.eventName);
for (let i = index; i < data.length; i++)
{
if (data[i].groupId == event.groupId)
{
/*
进行对应元素的添加与打包具体过程略
*/
//数组内的元素移动避免重复打包
if (!group_game_name.includes(data[i].eventName)) group_game_name.push(data[i].eventName);
let temp = data.splice(i, 1)[0];
data.splice(index++, 0, temp);
group_num++;
}
if(!data[i].competitors||data[i].competitors.length==0||!data[i].competitors[0].results||!(data[i].competitors[0].results.winnerLoserTie))
{
day_game_result_group.classList.add("pre");
}//如果小项赛程不符合详细展示格式,则将展现的打包赛事仅输出赛事预览
}
index--;//抵消下次循环的自增使得可以成功指向下一组
container.appendChild(dayGamePreviev);//比赛项目栏处理完毕
container.appendChild(day_game_result_group_continer);
continue;//该打包项目结束
符合输出具体信息格式后,判断胜负方进行加粗等
else
{
const day_game_result_group_name1 = document.createElement('label');//名字1添加
day_game_result_group_name1.className='day_game_result_group_name1';
const day_game_result_group_name2 = document.createElement('label');
day_game_result_group_name2.className='day_game_result_group_name2';
console.log(data[i].id)
if(data[i].competitors[0].results.winnerLoserTie=="W")
{
day_game_result_group_name1.innerHTML = data[i].competitors[0].name + " Winner";
day_game_result_group_name1.style.fontWeight=700;
day_game_result_group_name2.innerText = data[i].competitors[1].name;
}
else
{
day_game_result_group_name1.innerText = data[i].competitors[0].name;
day_game_result_group_name2.innerHTML = data[i].competitors[1].name+ " Winner";
day_game_result_group_name2.style.fontWeight=700;
}
判断是否需要输出比分信息,如果是则添加对应比分样式
if(data[i].competitors[0].results.detailedMark)
{
day_game_result_group_point1.className='day_game_result_group_detailpoint1';
day_game_result_group_point2.className='day_game_result_group_detailpoint2';
for(let j=0;j<data[i].competitors[0].results.detailedMark.length;j++)
{
day_game_result_group_point1.innerHTML+= data[i].competitors[0].results.detailedMark[j][0].padStart(2, '0')+" ";
}
day_game_result_group_point1.innerHTML+=data[i].competitors[0].results.mark;
for(let j=0;j<data[i].competitors[1].results.detailedMark.length;j++)
{
day_game_result_group_point2.innerHTML+= data[i].competitors[1].results.detailedMark[j][0].padStart(2, '0')+" ";
}
day_game_result_group_point2.innerHTML+=data[i].competitors[1].results.mark;
}
else
{
day_game_result_group_point1.className='day_game_result_group_point1';
day_game_result_group_point1.innerText=data[i].competitors[0].results.mark;
day_game_result_group_point2.className='day_game_result_group_point2';
day_game_result_group_point2.innerText=data[i].competitors[1].results.mark;
}
如果有奖品将进行如下代码,确保奖牌样式被正确添加
if(data[index].medalFlag!=0)//有奖牌的情况
{
/*
进行对应元素的添加与打包具体过程略
*/
continue;
}
else ....
如果没有奖牌将判断是否可以输出具体比分的赛事信息
else if((data[index].eventUnitType=="HATH"||data[index].eventUnitType=="HTEAM")&&data[index].phaseType!=1)// 个人/团体 比赛
{
/*
进行对应元素的添加与打包具体过程略
*/
}
else//不符合输出具体信息的情况则只添加预览部分
{
dayGamePreviev.style.cursor='pointer';
dayGamePreviev.addEventListener('click', function()
{
const url = 'game_detail.html?phaseId=' + encodeURIComponent(data[index].phaseId);
window.open(url, "",);
});
container.appendChild(dayGamePreviev);//比赛项目栏处理完毕
}
continue;//一次json数组元素的判断结束
使用 fetch 获取 json 数据,页面默认读取足球运动的信息,从每日赛程选择项目进入时会传入项目的参数以读取项目的 json 文件:
var url = decodeURI(window.location.href);
var argsIndex = url.split("?phaseId=");
console.log(argsIndex)
var arg;
if (argsIndex.length === 1)
arg = "FBLWTEAM11------------";
else
arg = argsIndex[1].substring(0, 22);
let defaultGroup;
fetch("./data/detailgame/" + arg + ".json")
.then(response => response.json())
.then(data => {
let title = document.getElementById("game-detail-title");
let sportName = data.event.description;
title.textContent = sportName + "-详细赛况";
let container = document.querySelector(".tab-panel-container");
container.innerHTML = '';
container.appendChild(createTabContainer(data.event.phases));
container.appendChild(createPanelSwiperContainer(data.event.phases))
addBracketSelectEvent();
addPanelClickEvent();
})
.catch(err => {
console.error(err);
});
不同的项目有不同的比赛阶段,使用 js 动态创建内容,以适应不同的项目:
function createTabContainer(phases) {
let tabContanier = document.createElement("div");
tabContanier.classList.add("tab-container");
phases.sort((a, b) => b.order - a.order).forEach(phase => {
if (phase.type === "3" && phase.units != undefined) {
if (defaultGroup === undefined)
defaultGroup = phase.shortDescription;
let button = document.createElement("button");
button.setAttribute("group", phase.shortDescription);
button.type = "button";
button.textContent = phase.shortDescription;
button.addEventListener("click", () => {
let buttons = document.querySelectorAll(".tab-container button");
buttons.forEach(button => {
button.classList.remove("selected");
})
button.classList.add("selected");
switchGroup(button.textContent, phases);
addPanelClickEvent();
});
tabContanier.appendChild(button);
}
});
return tabContanier;
}
点击不同的项目时,显示项目详细信息:
function updateDetailPrev(panel) {
let detailPrevContainer = document.querySelector(".detail-prev-container");
let nocImgs = panel.querySelectorAll(".noc-flag");
let scores = panel.querySelectorAll(".score");
let leftScore, rightScore;
if (scores[0].textContent > scores[1].textContent) {
leftScore = `<p class="left-score detail-winner">${scores[0].textContent}</p>`;
rightScore = `<p class="right-score">${scores[1].textContent}</p>`;
} else {
leftScore = `<p class="left-score">${scores[0].textContent}</p>`;
rightScore = `<p class="right-score detail-winner">${scores[1].textContent}</p>`;
}
......
对阵表需要对数据进行一定的排序,使连接时可以对应到不同的场次,读取数据后找到不同的比赛阶段,排序后加入同一个列表中:
function processData(data) {
let flag = true;
data.event.phases.forEach(bracketPhase => {
if (bracketPhase.shortDescription === "1/4决赛") {
bracketPhase.units.sort((a, b) => a.order - b.order).forEach(bracketItem => { // 比赛信息
let liItem = document.createElement("li");
liItem.classList.add("bracket-match-container")
// 比赛的具体数据添加在下方
liItem.appendChild(createMatchCard(bracketItem));
QFNL_ul.appendChild(liItem);
});
} else if (bracketPhase.shortDescription === "半决赛") {
bracketPhase.units.sort((a, b) => a.order - b.order).forEach(bracketItem => {
let liItem = document.createElement("li");
liItem.classList.add("bracket-match-container")
liItem.appendChild(createMatchCard(bracketItem));
SFNL_ul.appendChild(liItem);
});
} else if (bracketPhase.shortDescription === "决赛") {// 决赛
if (bracketPhase.units[0].schedule.start === undefined ||
bracketPhase.units[0].schedule.start.length != 2
) {
flag = false;
}
// 空白
let liItemBlank = document.createElement("li");
liItemBlank.classList.add("bracket-match-container", "blank")
liItemBlank.appendChild(createMatchCard(0));
FFNL_ul.appendChild(liItemBlank);
bracketPhase.units.sort((a, b) => a.order - b.order).forEach(bracketItem => {
let liItem = document.createElement("li");
liItem.classList.add("bracket-match-container")
liItem.appendChild(createMatchCard(bracketItem));
FFNL_ul.appendChild(liItem);
});
}
});
bracketBody.appendChild(QFNL_ul);
bracketBody.appendChild(SFNL_ul);
bracketBody.appendChild(FFNL_ul);
return flag;
}
要实现鼠标悬浮高亮显示相同国家的信息,可以为每个需要高亮显示的元素添加一个表示国家的属性,鼠标悬浮时首先获取当前的属性,使用循环找到所有相同属性并使用css添加高亮效果:
function addMouseHoverEvent() {
let scoreLines = document.querySelectorAll(".score-line");
scoreLines.forEach(scoreLine => {
scoreLine.addEventListener("mouseenter", () => {
// 获取选择的国家名
let hoverNoc = scoreLine.getAttribute("noc");
let sameNocs = document.querySelectorAll(`[noc=${hoverNoc}]`);
sameNocs.forEach(noc => {
noc.classList.add("currentIndicator")
})
})
scoreLine.addEventListener("mouseleave", () => {
// 获取选择的国家名
let hoverNoc = scoreLine.getAttribute("noc");
let sameNocs = document.querySelectorAll(`[noc=${hoverNoc}]`);
sameNocs.forEach(noc => {
noc.classList.remove("currentIndicator")
})
})
});
}
222200101:首先是不熟悉html,css,JavaScript,花了两个小时入门了一些基础语法,之后边做边学,写了一些很丑陋的码orz,首页和奖牌榜的添加逻辑相对较简单,每日赛程每个输出项目需要详细的判断,加上没什么json文件的读取经验,导致了写JavaScript的时候很痛苦,后面上手了后面还是很快乐的,尤其是我们两个都是初学者,逐渐开始入门,掌握一些效果实现的时候都会互相鼓励,极大的提升了开发的热情,后面写起来虽然每天基本都在写,也挺累的但是精神上比较快乐.
222200102: 一开始对 web 编程完全没有了解,在开始的两三天对html、css、JavaScript 进行了快速的了解和学习,开始做项目后,发现 web 编程十分耗费时间,尤其是对像我这样刚开始学习而完全不熟悉的人来说,往往需要花费很长的时间完成一个功能或是一个页面效果。在经过一周以来的高强度作业后,感到很幸运有一位很好的结对伙伴来完成项目,两个人共同的开发使学习和项目开发的进展都更为顺利。能够顺利完成这个作业收获很多
222200101 to 222200102:很好的队友,学习能力很强,技术很过硬,做事情认真也不敷衍,意见沟通都很顺利,而且还会帮助我解决问题hh,和有热情的人一起做事情是很快乐的,这样的结对编程很愉快.
222200102 to 222200101:我的结对队友是十分优秀的,在完成项目时给了我很多帮助,我们都对web编程不熟悉,有一位这样的伙伴使得长时间的学习和项目的实现都更加顺利,十分好的结对队友。