结对第二次作业——编程实现

222200102王浩然 2024-09-30 23:20:50
这个作业属于哪个课程 FZU_SE_teacherW_4
这个作业要求在哪里结对第二次作业——编程实现
结对学号222200101,222200102
这个作业的目标编程实现结对第一次作业的网页原型
其他参考文献《构建之法》

目录

  • 1. CodeArt项目地址
  • 2. PSP表格
  • 3功能展示
  • 3.1 首页和公有头尾导航栏展示
  • 3.1.1首页展示
  • 3.1.2公有顶部导航栏展示
  • 3.1.3底部导航栏展示
  • 3.2 奖牌榜
  • 3.3 每日赛程
  • 3.3.1 日期切换与概览
  • 3.3.2打包的赛事浏览
  • 3.3.3点击跳转详细赛况
  • 3.4 详细赛程
  • 3.5 对阵图
  • 3.6了解更多
  • 3.6.1概览和顶部定位
  • 3.6.2外部链接跳转
  • 4结对过程
  • 4.1结对交流
  • 4.2 仓库commit信息截图如下
  • 5设计实现过程
  • 5.1功能结构图
  • 5.2设计实现过程
  • 6代码演示
  • 6.1奖牌榜关键代码展示
  • 6.2每日赛程关键代码展示
  • 6.2.1 训练赛和开幕式的输出
  • 6.2.2 打包项目的判断
  • 6.2.3 奖牌项目的判断
  • 6.2.4 没有奖牌的详细赛况
  • 6.3 详细赛事
  • 6.4 对阵表
  • 7心路历程与评价
  • 7.1心路历程
  • 7.2互相评价


1. CodeArt项目地址

CodeArt仓库地址
项目地址:114.55.130.95


2. PSP表格

222200101:

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划2020
• Estimate• 估计这个任务需要多少时间2020
Development开发18002380
• Analysis• 需求分析 (包括学习新技术)120120
• Design Spec• 生成设计文档3030
• Design Review• 设计复审60120
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)1010
• Design• 具体设计30800
• Coding• 具体编码1200880
• Code Review• 代码复审340120
• Test• 测试(自我测试,修改代码,提交修改)20300
Reporting报告100100
• Test Report• 测试报告6060
• Size Measurement• 计算工作量1010
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划3030
合计19202500

222200102:

PSPPersonal Software Process Stages预估耗时(分钟)实际耗时(分钟)
Planning计划3060
• Estimate• 估计这个任务需要多少时间3060
Development开发9002220
• Analysis• 需求分析 (包括学习新技术)200400
• Design Spec• 生成设计文档2520
• Design Review• 设计复审6020
• Coding Standard• 代码规范 (为目前的开发制定合适的规范)3020
• Design• 具体设计300600
• Coding• 具体编码6001200
• Code Review• 代码复审20120
• Test• 测试(自我测试,修改代码,提交修改)180240
Reporting报告90110
• Test Report• 测试报告5060
• Size Measurement• 计算工作量1010
• Postmortem & Process Improvement Plan• 事后总结, 并提出过程改进计划3040
合计10202390


3功能展示

3.1 首页和公有头尾导航栏展示

首页集成了其他网站的入口与介绍

3.1.1首页展示

演示如下

在这里插入图片描述

3.1.2公有顶部导航栏展示

每个页面可以通过左上角的小球呼出导航栏,点击对应网页标题即可完成跳转
演示如下

在这里插入图片描述

3.1.3底部导航栏展示

底部导航可以直接跳转到对应网页,并引入了一些外部链接
演示如下

在这里插入图片描述

3.2 奖牌榜

奖牌榜演示如下

在这里插入图片描述

3.3 每日赛程

设计参考了官网,除了赛事筛选器,基本复刻了官网的交互.

3.3.1 日期切换与概览

点击日期可以呼出日历,点击其他日期可以切换到该日期对应的赛事列表

在这里插入图片描述

3.3.2打包的赛事浏览

点击打包项目预览的'+'按钮即可呼出每一个项目的结果信息,点击减号即可收齐

在这里插入图片描述

3.3.3点击跳转详细赛况

点击图标可以跳转到对应详细赛况界面,打包项目需要点开赛事列表,在点击跳转.
演示如下
普通情况:

在这里插入图片描述

打包情况:

在这里插入图片描述

3.4 详细赛程

通过点击每日赛程跳转,或者通过导航跳转(此方法将默认展示足球界面)
可以切换不同赛事阶段并且点击赛事显示详细信息演示如下

在这里插入图片描述

3.5 对阵图

通过点击详细赛程的对阵图按钮跳转,或者通过导航跳转(此方法将默认展示足球界面)
演示如下

在这里插入图片描述

3.6了解更多

页面设计参考了官网,旨在完善补充除了比赛信息以外的别的奥运资讯

3.6.1概览和顶部定位

页面具有一些外部链接按钮,以及顶部可以直接定位页面内模块,具体演示如下

在这里插入图片描述

3.6.2外部链接跳转

该页面的每个按钮都可以点击跳转到对应的网页,演示如下

在这里插入图片描述


4结对过程

4.1结对交流

由于我们两个是舍友,结对过程讨论都是面对面的,线上聊天记录部分截图如下

在这里插入图片描述


在这里插入图片描述

4.2 仓库commit信息截图如下

在这里插入图片描述


5设计实现过程

5.1功能结构图

附图如下

在这里插入图片描述

5.2设计实现过程

    主要设计思路是根据页面划分模块,以页面为单位进行划分职责并且维护对应的js以及css文件.
首页,奖牌榜,和了解更多的设计较为简单.奖牌榜利用之前爬取的数据,通过设置元素与布局已经写一些简单的就完成了,首页和了解更多则是通过图片和布局设计添加外部链接完成.

    每日赛程由于文件量较大,选择根据日期显示不同赛事(也符合JSON文件根据日期保存的格式),由于赛事数组的属性有不同,固使用了if,else判断这个数组元素的属性格式,进行判断选择那种输出方式,打包赛事由于具体赛事可能时间不同固处于文件的不同位置,采取将JSON文件数组读入程序数组中,遍历到有goupid的元素时,以其为起点遍历文件添加并且将数组内对所有groupip相同的元素移动到其后面,之后如上过程遍历数组.

    详细赛程包括显示每个项目的不同阶段,如D组、C组、B组、半决赛、决赛等,以及选择不同比赛项目查看比赛的详细信息,详细信息显示默认为足球,由每日赛况点击进入时,向网页传入包含比赛信息的参数,依次跳转到相应的比赛界面。还包括跳转到对应项目的对阵表功能,如果该项目有对阵表可以选择跳转到相应链接。每个项目具有不同的阶段,对 json 文件进行读取时,可以判断 phase 类型来确定是否是一个阶段,使用循环添加元素,以适应不同的比赛项目需求。选择不同阶段的各比赛场次时,会显示该项目的详细信息。

    对阵表的实现基于每个比赛项目的数据实现,由于比赛项目不一定具有对阵表,通过判断比赛参与者进行简单判断,由于赛事项目过多,项目的对阵表或是没有或是具有各种差异,因此可能存在显示问题,对于明确没有的项目会提示不存在对阵表。对阵表实现鼠标悬浮在某个国家时,可以高亮显示该国家在对阵表中的所有位置。通过比赛详细信息查看对阵表时,会向页面传入对应的项目参数,以获取相关数据。


6代码演示

6.1奖牌榜关键代码展示

通过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));

6.2每日赛程关键代码展示

由于本人水平较低,写的代码很冗余,固根据划分的情况进行关键代码展示
以下代码均在如下循环中进行

 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 => 
 {
  //代码部分
 }

6.2.1 训练赛和开幕式的输出

每日赛程展示中训练赛和开幕式是很特殊的两种格式,没有对应的项目跳转所以放在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;
   }
   

6.2.2 打包项目的判断

打包项目需要特殊的输出格式,单独赛事还要判断有无奖牌进行进一步判断输出格式,固将其放在,第二个判断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 + "&nbsp;&nbsp;&nbsp;&nbsp;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+ "&nbsp;&nbsp;&nbsp;&nbsp;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')+"&nbsp;&nbsp;&nbsp;&nbsp;";
        }
        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')+"&nbsp;&nbsp;&nbsp;&nbsp;";
        }
        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;
       }

6.2.3 奖牌项目的判断

如果有奖品将进行如下代码,确保奖牌样式被正确添加

  if(data[index].medalFlag!=0)//有奖牌的情况
    {
       /*
      进行对应元素的添加与打包具体过程略
      */
      continue;
    }
   else ....

6.2.4 没有奖牌的详细赛况

如果没有奖牌将判断是否可以输出具体比分的赛事信息

 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数组元素的判断结束

6.3 详细赛事

使用 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>`;
    }
......

6.4 对阵表

对阵表需要对数据进行一定的排序,使连接时可以对应到不同的场次,读取数据后找到不同的比赛阶段,排序后加入同一个列表中:

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")
            })
        })
    });
}

7心路历程与评价

7.1心路历程

222200101:首先是不熟悉html,css,JavaScript,花了两个小时入门了一些基础语法,之后边做边学,写了一些很丑陋的码orz,首页和奖牌榜的添加逻辑相对较简单,每日赛程每个输出项目需要详细的判断,加上没什么json文件的读取经验,导致了写JavaScript的时候很痛苦,后面上手了后面还是很快乐的,尤其是我们两个都是初学者,逐渐开始入门,掌握一些效果实现的时候都会互相鼓励,极大的提升了开发的热情,后面写起来虽然每天基本都在写,也挺累的但是精神上比较快乐.

222200102: 一开始对 web 编程完全没有了解,在开始的两三天对html、css、JavaScript 进行了快速的了解和学习,开始做项目后,发现 web 编程十分耗费时间,尤其是对像我这样刚开始学习而完全不熟悉的人来说,往往需要花费很长的时间完成一个功能或是一个页面效果。在经过一周以来的高强度作业后,感到很幸运有一位很好的结对伙伴来完成项目,两个人共同的开发使学习和项目开发的进展都更为顺利。能够顺利完成这个作业收获很多

7.2互相评价

222200101 to 222200102:很好的队友,学习能力很强,技术很过硬,做事情认真也不敷衍,意见沟通都很顺利,而且还会帮助我解决问题hh,和有热情的人一起做事情是很快乐的,这样的结对编程很愉快.

222200102 to 222200101:我的结对队友是十分优秀的,在完成项目时给了我很多帮助,我们都对web编程不熟悉,有一位这样的伙伴使得长时间的学习和项目的实现都更加顺利,十分好的结对队友。

...全文
35 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

239

社区成员

发帖
与我相关
我的任务
社区管理员
  • FZU_SE_teacherW
  • 助教赖晋松
  • D's Honey
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧