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

222100129梅明胜 2024-03-26 22:30:48
这个作业属于哪个课程<2302软件工程社区>
这个作业要求在哪里<结对第二次作业--编程实现>
结对学号<222100128黄煦陶,222100129梅明胜>
这个作业的目标<完成编码撰写;完成博客撰写>
其他参考文献vue 官网教程, Element-Plus 教程, Git 官网

目录

  • 1.项目链接
  • 1.1 Gitcode 仓库网址
  • 1.2 云服务器项目网址
  • 1.3 代码规范地址
  • 2.PSP 表格和效能分析
  • 2.1 PSP 表格
  • 2.2 效能分析
  • 3.成品展示
  • 3.1 首页
  • 3.2 选手信息
  • 3.3 每日赛况
  • 3.4 详细赛况
  • 3.5 奖牌榜
  • 3.6 服务器部署
  • 4.结对讨论过程描述
  • 5.设计实现过程
  • 5.1 功能结构图
  • 5.2 设计概述
  • 5.3 遇到的问题和解方式
  • 6.代码说明
  • 7.心路历程和收获
  • 8.评价队友

1.项目链接

1.1 Gitcode 仓库网址

2.PSP 表格和效能分析

2.1 PSP 表格

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

2.2 效能分析

  • 需求分析部分:在本次学习中,我们对于前端的技术不太了解,实际花了很长时间在学习新技术上,包括作业布置前我们学习 vue 技术花了一些时间,但是在编写的过程中总是有一些小状况出现,这说明我们对技术的掌握还是不够熟练。在后续的课程作业中,应当先掌握技术,在编写过程中就能减少去学习新知识的时间。
  • 具体编码部分:编码超出预期时间是在我们的意料之内,一开始我们对vue整体的编写并不熟悉,后期在为了让组件呈现更好的效果上花了较多时间。

    3.成品展示

  • 本次编写的网站偏向于信息展示类网站,网站的元素应简洁明了,给予用户直观明确的信息;契合游泳比赛的主题,我们本次采用了蓝+白的主题配色;页面采用导航栏、内容、页脚的结构,方便用户在各个模块之间的切换。

    3.1 首页

  • 首页设计:上方设计了一个轮播图,用来展示关于比赛的主题图片

    在这里插入图片描述

  • 我们把了解更多模块直接放在首页的轮播图下方,采用图文交加的形式,介绍游泳世锦赛的举办背景等等。

    请添加图片描述

3.2 选手信息

  • 选手排名设计:采用表格的方式展示选手的信息。相同国籍的选手放在一起陈列。

    在这里插入图片描述

  • 排序查看信息:点击标题,可以根据此标题对数据进行排序

    在这里插入图片描述

3.3 每日赛况

  • 每日赛况设计:以日期组织展示每天的比赛。显示有每场比赛的详细举办时间与比赛性质(初/决赛)

    在这里插入图片描述

  • 每场比赛右侧详细信息按钮,点击可跳转至详细赛况页面对应的比赛栏目,展示该比赛的结果。

    在这里插入图片描述

3.4 详细赛况

  • 详细赛况设计:展示有每场比赛初赛与决赛的比赛结果。决赛金银铜牌有特殊标志标识

    请添加图片描述

3.5 奖牌榜

  • 奖牌统计:统计各国家队奖牌数,表格展示准确信息,饼状图直观展示各国获奖情况。

    在这里插入图片描述

  • 折线图:直观展示单场比赛各国家队获奖情况

    在这里插入图片描述

    3.6 服务器部署

  • 使用GitHub Pages进行服务器部署,免费且方便,实时开发实时查看

    在这里插入图片描述

4.结对讨论过程描述

本次作业我们基本上是采用线下方式(同寝室)进行探讨和交流,线上辅助讨论一些问题和 bug。

在这里插入图片描述


在这里插入图片描述


请添加图片描述


请添加图片描述


请添加图片描述


请添加图片描述


请添加图片描述

5.设计实现过程

5.1 功能结构图

请添加图片描述

5.2 设计概述

请添加图片描述

5.3 遇到的问题和解方式

在我们的项目开发过程中,我们遇到了多个挑战和相应的解决方案,这些体验为我们的技术成长提供了宝贵的实践机会。

  1. CSS布局挑战:项目初期构建网页结构的时候,我们发现自己调整的CSS样式无法如预期生效,预计组件会布局在页面的上方,但实际上布局在页面的正中央,令我们百思不得其解。

解决方式:后来意识到是由于未删除Vue 3项目的默认CSS文件所致。这个问题一度让我们在页面布局上浪费了大量时间,但也教会了我们在新建项目时要注意检查和清理默认文件。

img

  1. UI组件选择:我们对待每一份作业都一丝不苟,原型设计也是,所以原先的原型设计没想到给我们埋下了这么大的坑(保证不是夸自己原型设计多么好)。对我们自己能力和时间来说,完成这么多精美的组件是不可能的事情。

解决方式:面对自身CSS技能的不足,我们决定采用==Element Plus==,这个决策大幅提高了开发效率,让我们能够快速实现漂亮且功能丰富的组件,减少了调整样式的时间 ,只需要按照接口文档说明,填写相应数据,简单布局排版即可。

  1. 数据处理: 由于我们项目是复刻一个赛事官网,特点就是需要呈现的比赛数据特别多,对于我们这种新手来说,把数据手工抄到代码上无疑是一种选择。但可想而知这种做法的工作量巨大。

解决方式:权衡之下我们愿意把时间用在学习高级一点的技术代替我们搬砖的活。项目中需要处理大量数据,我们选择了结合==TypeScript==和==Pinia==来解析JSON文件,实现数据的批量处理。这种方法不仅提高了效率,还使得数据在多个Vue页面中得到复用 ,所以我们的选择是正确的。

  1. 页面跳转Bug1:在实现从“赛程” (Schedule)页面到“比赛结果” (Result)页面的跳转时, 发现路由网址变化了,但导航栏的激活状态还没有变化,导航栏还显示当前页面是“赛程” (Schedule)页面。

解决方式:把路由信息与导航栏的激活状态绑定,让这两个状态同步更改。即可轻易解决。

img

  1. 页面跳转Bug2:在实现从“赛程” (Schedule)页面到“比赛结果” (Result)页面的跳转时, 确实可以精确打开指定比赛的指定阶段,如初赛或者决赛。但跳转之后再点击其他的比赛结果进行查看,可能会发生数据无法加载的问题。例如我们跳转查看Women 1m Springboard的preliminaries,然后我们查看Men 10m Synchronised,就无法查看任何数据。

解决方式:原因就是在跳转的时候,路由中包含props参数,指定我们要看preliminaries,使得可以精确跳转,但点击其他比赛结果的时候,props参数仍然奏效,导致数据加载异常。上面例子中的问题就是因为Men 10m Synchronised没有preliminaries,导致查看不了任何数据。我们只需要设置当用户点击其他组件的时候,令props中相关参数失效即可。

  1. 部署问题:部署项目时,我们遇到了npm run build失败的问题,导致dist文件夹中只有一个ico图标文件。

解决方式:原因是TypeScript的强类型机制,我们项目可以顺利运行,但typescript代码会有警告,就无法npm run build成功。通过选择不检查语法的方式强行构建,我们解决了这个问题(尝试解决类型转换问题,但费了很多功夫还是没能解决,不过不影响项目运行)。此外,我们还调整了生产环境下的资源路径,以适应生产环境的需求,得我们带来了很多项目构建部署上的经验。

6.代码说明

  1. App.vue:
  • <el-header class="header">: 代表页面的头部区域。在这个标签内,有一个自定义组件,这可能是一个包含网站logo、导航菜单或其他信息的组件。class="header"用于给头部添加特定的样式。

  • <el-main class="main">: 表示页面的主体内容区域。内部使用了组件,这是Vue Router的一部分,用于根据当前路由动态显示不同的页面内容。这意味着,用户在导航网站时,主体内容会根据用户的操作显示不同的组件,而头部和尾部保持不变。class="main"可能用于设置主体区域的样式,比如间距、宽度或背景色。

  • <el-footer class="footer">: 代表页面的底部区域。里面有一个自定义组件,可能包含版权信息、联系方式或其他一些网站的基本信息。class="footer"用于为页脚区域添加样式。

  • 这段代码通过==Element Plus==的布局组件实现了一个响应式的页面布局,分为头部、主体和尾部三个区域。头部和尾部是静态的,对于网站的所有页面来说是共通的,而主体部分则是动态的,可以根据用户的不同操作展示不同的内容。这种布局方式很适合像我们一样需要有统一头部和尾部,但中间内容频繁变动的网站或应用。

    <template>
    <div class="content">
      <div class="common-layout">
        <el-container class="container">
          <el-header class="header">
            <Head></Head>
          </el-header>
          <el-main class="main">
            <RouterView />
          </el-main>
          <el-footer class="footer">
            <Foot/>
          </el-footer>
        </el-container>
      </div>
    </div>
    </template>
    

  1. router/index.ts:
  • history: createWebHistory()指定了Vue Router使用HTML5的历史模式。这种模式利用history.pushState API来改变URL而不重新加载页面。这使得用户在浏览单页面应用时可以享受到像浏览传统页面一样的体验,包括前进和后退导航。
  • routes数组定义了所有路由的数组。每个路由都是一个对象,包含path、name和component等属性:
    path: URL路径,当用户访问这个路径时,路由会被激活。
    name: 路由的名称,可以用于编程式导航。
    component: 当路由被激活时,应该渲染的组件。
  • 代码中第一个路由定义了当用户访问/home时,应该渲染HomeView组件。其余页面同理,代码中就不重复给出了。
  • 代码中第二个路由定义了应用的默认路径(/)应该重定向到/home。这意味着当用户访问应用的根路径时,会自动被导航到/home
  • 代码中第三个路由捕获所有不匹配上述定义的路径,使用正则表达式/:pathMatch(.*)*来匹配,并将这些访问重定向到/home。这通常用于处理404页面,确保所有未识别的路径都会重定向到首页。
const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/home',
      name: 'home',
      component: HomeView
    },
    {
      path: '/',
      redirect: '/home'
    },
    {
      path: '/:pathMatch(.*)*',
      redirect: '/home'
    },
    ......
  ]
})

  1. store\athletes.ts:
  • 这段代码的主要思路是在应用启动时读取和处理JSON文件中的数据,然后使用==Pinia==存储处理后的数据。通过定义类型和使用flatMapmap进行数据处理,代码既保证了类型安全,又提供了一个清晰、易于管理的方法来处理和访问运动员数据。这种模式特别适合于需要预加载和全局访问大量静态数据的应用场景。这里处理的数据,具体的使用将在下一点中演示
  • 定义接口Athlete:这个接口用于定义单个运动员对象应该具有的属性。这有助于在处理运动员数据时提供类型安全,确保数据的一致性和可预测性。
  • 处理JSON数据:AthletesData.flatMap(...)用于将原始的JSON数据转换为Athlete接口定义的形式。flatMap方法首先对AthletesData数组的每个元素(每个国家)执行一个映射操作,然后将结果扁平化为一个数组。这意味着无论原始数据中有多少个国家,最终都会得到一个单层级的运动员数组。
  • 数据映射:对于每个国家对象,通过country.Participations.map(...)遍历其Participations数组(参与的运动员),并为每个参与者创建一个新的对象,这个对象符合Athlete接口的结构。
  • gettersgetters是用于从存储中派生状态的函数。它简单地返回状态中的athletes数组。通过这种方式,组件可以直接访问经过处理的运动员数据。
import { defineStore } from 'pinia';
import AthletesData from '@/assets/data/athletes.json';
interface Athlete {
    CountryName: string;
    CountryCode: string;
    Gender: number;
    PreferredLastName: string;
    PreferredFirstName: string;
    ScoreboardPhotoId: string;
    DOB: string;
  }
  export const useAthletesData = defineStore('athletesData', {
    state: () => ({
      // 在状态初始化时立即处理数据
      athletes: AthletesData.flatMap(country => 
        country.Participations.map(participation => ({
          CountryName: country.CountryName,
          CountryCode: country.CountryCode,
          Gender: participation.Gender,
          PreferredLastName: participation.PreferredLastName,
          PreferredFirstName: participation.PreferredFirstName,
          ScoreboardPhotoId: participation.ScoreboardPhotoId,
          DOB: participation.DOB,
        }))
      ),
    }),
    getters: {
      allAthletes: (state) => state.athletes,
    },
  });

  1. AthletesView.vue:
  • 数据使用和展示逻辑:这段代码的关键在于如何将从Pinia存储中获取的数据以一种用户友好的方式展示出来。通过el-tableel-table-column,我们可以自定义每列的展示逻辑,包括通过插槽自定义复杂的数据显示(如姓名的全名显示、性别的图标显示以及日期的格式化)。这样的数据展示方法既灵活又强大,能够应对各种数据展示需求。
  • el-table: 使用==Element Plus==的<el-table>组件来展示数据。这个组件接受一个数组作为数据源,并为每个数据项渲染一行。
  • el-table-column: 每个<el-table-column>定义了表格中的一列。prop属性用于指定该列要显示数据对象中的哪个属性。label属性定义了列头的标题。如果列需要特殊格式化或包含复杂内容,则不使用prop属性,而是通过具名插槽(template)来自定义列的内容。
  • 导入Pinia存储: 通过import { useAthletesData } from '@/store/athletes'导入之前定义的Pinia存储。
  • 使用存储: 通过调用useAthletesData()来获取存储实例,然后通过store.allAthletes访问存储中的全部运动员数据。这里的athletes变量将被用作<el-table>的数据源。
  • 数据处理函数: formatDate函数用于将日期字符串格式化为更易读的格式(例如:"YYYY-MM-DD")。这在显示日期时非常有用,因为原始数据可能不是以最终希望展示给用户的格式存储的
    <template>
      <el-table>
        <el-table-column prop="CountryName" label="Country" sortable> </el-table-column>
        <el-table-column label=" "> </el-table-column>
        <el-table-column label="Athlete Name">
          <template #default="{ row }">
            <span class="AthleteName">{{ row.PreferredFirstName + ' ' + row.PreferredLastName }}</span>
          </template>
        </el-table-column>
        <el-table-column prop="Gender" label="Gender">
          <template #default="{ row }">
            {{ row.Gender === 0 ? 'Male' : 'Female' }}
            <el-icon v-if="row.Gender === 0">
              <male />
            </el-icon>
            <el-icon v-else>
              <female />
            </el-icon>
          </template>
        </el-table-column>
        <el-table-column label="DOB" sortable>
          <template #default="{ row }">
            <span class="DOB">{{ formatDate(row.DOB) }}</span>
          </template>
        </el-table-column>
        <el-table-column>
          <img src="@/assets/image/div.png" alt="div" />
        </el-table-column>
      </el-table>
    </template>
    <script setup>
    import { useAthletesData } from '@/store/athletes'
    // 使用 Pinia store
    const store = useAthletesData()
    // 从 Pinia store 获取运动员数据
    const athletes = store.allAthletes
    // 定义一个简单的日期格式化函数
    const formatDate = (dateString) => {
    const date = new Date(dateString)
    const year = date.getFullYear()
    const month = date.getMonth() + 1
    const day = date.getDate()
    return `${year}-${month.toString().padStart(2, '0')}-${day.toString().padStart(2, '0')}`
    }
    </script>
    

  1. store\results.ts:
  • 这两个操作展示了如何根据输入参数从复杂的数据结构中提取和处理信息,使其更适合在用户界面上显示。通过这种方式,可以灵活地根据页面需要显示相应的比赛细节和分数信息,同时也保持了代码的清晰性和可维护性。这种模式在处理具有复杂数据结构的应用中非常有用,特别是在需要根据用户输入动态提取数据时。
  • fetchMatchDetails(matchType: string)*
  • 数据验证:首先检查获取的数据是否有效。这包括验证对应matchType的数据是否存在以及数据中的Heats属性是否为一个数组。如果这些验证失败,会在控制台输出错误信息,并返回一个空数组。
  • 数据映射:如果数据有效,接下来对Heats数组进行映射,为每个heat返回一个包含EndUtcDateTime(结束时间)和PhaseName(阶段名称)的对象。这个操作的目的是为了简化数据,使其更适合显示比赛的基本信息。
  • fetchMatchScore(matchType: string, PhaseName: string)*
  • 数据验证:类似于fetchMatchDetails,首先验证是否能根据matchType找到对应的数据。如果找不到,输出错误信息并返回空数组。
  • 查找特定阶段:使用find方法在Heats数组中查找名称匹配PhaseName的比赛。如果找不到对应的热身赛或该热身赛没有结果数据(Results),同样输出错误信息并返回空数组。
  • 提取和映射分数信息:对找到的targetHeat.Results数组进行映射,为每个结果提取出Rank(排名)、NAT(国家代码)、FullName(全名)、AthleteResultAge(运动员参赛年龄)、TotalPoints(总分)以及PointsBehind(落后分数)。对于PointsBehind,如果不存在,则默认为null
    actions: {
      fetchMatchDetails(matchType: string) {
        const data = this.matchDataMap[matchType];
        if (!data || !Array.isArray(data.Heats)) {
          console.error("Heats is not an array or undefined in", matchType);
          return [];
        }
        return data.Heats.map(heat => ({
          EndUtcDateTime: heat.EndUtcDateTime,
          PhaseName: heat.PhaseName,
        }));
      },
      fetchMatchScore(matchType: string, PhaseName: string) {
        const data = this.matchDataMap[matchType];
        if (!data) {
          console.error("Match data not found for:", matchType);
          return [];
        }
        const targetHeat = data.Heats.find(heat => heat.PhaseName === PhaseName);
        if (!targetHeat || !targetHeat.Results) {
          console.error("No matching heat or results found for phase:", PhaseName);
          return [];
        }
        return targetHeat.Results.map(result => ({
          Rank: result.Rank,
          NAT: result.NAT,
          FullName: result.FullName,
          AthleteResultAge: result.AthleteResultAge,
          TotalPoints: result.TotalPoints,
          PointsBehind: result.PointsBehind || null,
        }));
      }
    }
    

  1. ResultsView.vue:
  • 这个代码段展示了Vue中父子组件之间数据传递的基本模式:父组件通过属性(props)向子组件传递数据。在这个例子中,matchTypephaseName是从父组件传递到resultsTabresultsDetails子组件的数据。这种数据传递机制是单向的,从父组件流向子组件,这有助于保持组件间数据流的清晰和可预测。通过这种方式,可以在不同组件之间共享数据,同时保持它们的独立性和可重用性。这部分传递的数据将在下一点演示子组件如何接受来自父组件传递的数据。
  • resultsTab子组件传递matchType:在<el-collapse-item>的标题插槽中,matchType通过属性绑定(:matchType="matchType")传递给resultsTab组件。这允许resultsTab组件根据传入的matchType显示相应的标题信息。
  • resultsDetails子组件传递matchTypephaseName:在<el-collapse-item>内部,matchTypephaseName通过属性绑定(:matchType="matchType":phaseName="phaseName")传递给resultsDetails组件。这使得resultsDetails组件能根据这些属性显示详细的比赛结果。
    <template>
    <div class="content">
      <el-collapse accordion class="collapse" v-model="activeName">
        <el-card shadow="hover">
          <el-collapse-item v-for="(matchType, index) in matchTypes" :key="index" :name="matchType" >
            <template #title>
              <!-- 传递 matchType 给 resultsTab 组件 -->
              <resultsTab :matchType="matchType"></resultsTab>
            </template>
            <!-- 传递 matchType 和 phaseName 给 resultsDetails 组件 -->
            <resultsDetails :matchType="matchType" :phaseName="phaseName"></resultsDetails>
          </el-collapse-item>
        </el-card>
      </el-collapse>
    </div>
    </template>
    

  1. components\results\resultsDetails.vue:
  • 这段代码演示了在Vue 3组件中接收和使用来自父组件的props,如何利用Vue的组合式API进行数据的异步加载和响应式管理。通过将业务逻辑和数据加载操作定义在组件内部,resultsDetails组件能够根据传入的matchTypephaseName从全局状态管理(如Pinia store)中检索特定比赛的详细信息和分数,进而在组件内部以期望的方式展示这些数据。这种模式大大增强了组件的可重用性和灵活性,同时也保持了代码的清晰和易于维护。
  • 接收来自父组件的Props:通过defineProps方法定义接收的props。这里定义了matchTypephaseName两个属性,其中matchType是必需的,而phaseName则不是。
  • onMounted生命周期钩子中,使用await关键字异步调用store的方法(fetchMatchDetailsfetchMatchScore),以根据props.matchTypeprops.phaseName获取匹配的详细信息和分数。
  • 将获取到的数据赋值给响应式变量,如matchDetails.valuescores.value,使得数据变更能够触发组件的重新渲染。
  • 使用watch函数来监视props.phaseName的变化。一旦phaseName发生变化,便会更新activeTabName.value。通过设置{ immediate: true }watch会在组件初次渲染时立即执行,确保即使在phaseName初始值存在时也能正确设置activeTabName
    <script setup name="resultsDetails">
    import { onMounted, ref ,watch } from 'vue';
    import useResultsData from '@/store/results';
    const store = useResultsData();
    const props = defineProps({
    matchType: {
      type: String,
      required: true,
    },
    phaseName: {
      type: String,
      required: false,
    },
    });
    const matchDetails = ref([]);
    const scores = ref([]);
    const activeTabName = ref('');
    watch(() => props.phaseName, (newPhaseName) => {
    if (newPhaseName) {
      activeTabName.value = newPhaseName;
    }
    }, { immediate: true });
    onMounted(async () => {
    const details = await store.fetchMatchDetails(props.matchType);
    // 输出details
    //console.log(details)
    matchDetails.value = details;
    // Initialize activeTabName with props.phaseName or fallback to the first detail's PhaseName
    if (!props.phaseName && details.length > 0) {
      activeTabName.value = details[0].PhaseName;
    }
    for (const detail of details) {
      const score = await store.fetchMatchScore(props.matchType, detail.PhaseName);
      scores.value.push(score);
    }
    });
    </script>
    

  1. StatisticsView.vue:
  • 这段代码展现了如何利用Vue 3ECharts来创建动态的数据可视化组件。通过从Pinia store中获取数据,并根据这些数据动态生成图表,可以提供丰富的用户交互和数据展示。此外,ECharts的灵活性和强大的配置选项使得定制化高质量的图表变得简单快捷。
  • setPieChartOptions函数:定义了如何设置饼图的配置项。这个函数接收四个参数:图表实例、图表标题、数据数组、以及要展示的奖牌类型(金、银、铜)。
    图表标题和布局:title对象配置了图表的标题和其在图表中的位置。
    提示框组件:tooltip配置了鼠标悬浮时显示的信息。
    图例组件:legend配置了图例的布局和位置。
    系列列表:series数组定义了图表的类型为pie(饼图),包括饼图的半径、数据项(每个国家的奖牌数和国家名),以及饼图扇区被高亮时的样式。
import { computed, onMounted, ref } from 'vue';
import * as echarts from 'echarts';
import useMedalCount from '@/store/statistic';
const medalCountStore = useMedalCount();
const goldChartRef = ref<echarts.ECharts | null>(null);
const silverChartRef = ref<echarts.ECharts | null>(null);
const bronzeChartRef = ref<echarts.ECharts | null>(null);
onMounted(async () => {
    await medalCountStore.calculateMedalTally();
    const goldChartDom = document.getElementById('gold-chart');
    const silverChartDom = document.getElementById('silver-chart');
    const bronzeChartDom = document.getElementById('bronze-chart');
    if (goldChartDom) goldChartRef.value = echarts.init(goldChartDom);
    if (silverChartDom) silverChartRef.value = echarts.init(silverChartDom);
    if (bronzeChartDom) bronzeChartRef.value = echarts.init(bronzeChartDom);
    const sortedData = Object.entries(medalCountStore.medalTally).map(([country, medals]) => ({
        country,
        gold: medals.Gold,
        silver: medals.Silver,
        bronze: medals.Bronze,
    }));
    if (goldChartRef.value) {
        setPieChartOptions(goldChartRef.value, 'Gold Medals Distribution', sortedData, 'gold');
    }
    if (silverChartRef.value) {
        setPieChartOptions(silverChartRef.value, 'Silver Medals Distribution', sortedData, 'silver');
    }
    if (bronzeChartRef.value) {
        setPieChartOptions(bronzeChartRef.value, 'Bronze Medals Distribution', sortedData, 'bronze');
    }
});
function setPieChartOptions(chart: echarts.ECharts, title: string, data: Array<{ country: string; gold: number; silver: number; bronze: number; }>, type: 'gold' | 'silver' | 'bronze') {
    const option = {
        title: {
            text: title,
            left: 'center'
        },
        tooltip: {
            trigger: 'item'
        },
        legend: {
            orient: 'vertical',
            left: 'left',
        },
        series: [
            {
                name: type,
                type: 'pie',
                radius: '50%',
                data: data.map(item => ({
                    value: item[type],
                    name: item.country
                })),
                emphasis: {
                    itemStyle: {
                        shadowBlur: 10,
                        shadowOffsetX: 0,
                        shadowColor: 'rgba(0, 0, 0, 0.5)'
                    }
                }
            }
        ]
    };
    if (chart) {
        chart.setOption(option);
    }
}

7.心路历程和收获

2221000128 的心路历程和收获:本次的作业时间对我们来说相对有些紧迫,在作业发布前我们就已经开始学习新知识了(vue),但是在编程的过程中还是遇到很多问题,归根结底的原因还是技术掌握的不熟练。网页的布局也是真的很磨人,大量的时间都在搞 css,一开始怎么调整css样式都无法让页面按照自己代码所写的来呈现,最后发现是npm create vue@latest创建项目的时候有一个默认的全局css文件,影响了自己写的css样式,所以才css代码才不如自己所意,消耗了大量时间,人麻了(o(╥﹏╥)o)。在后续的学习中还需要积极动手实践,只有动手才能对一项技术更加熟练,我在此次作业是第一次接触了 vue,能够用 vue 写出一个简单的网页,学习与之相关的知识,对我后续做团队项目有较大的帮助,收获满满。

2221000129 的心路历程和收获:开始coding的时候,我没有马上就写第一个页面,而是像教程里面,先设计路由index.ts文件,创建简单的views。然后再开始写每一views的内容。期间由于我的经验不足,没有把vue3新项目的默认css文件删除,导致队友调整页面布局浪费了很多时间。
最省心的是,我没有马上写代码,而是多花一点时间学习element PLUS,导致我能够很快地利用已有的组件,轻轻松松地在页面上呈现各种好看的效果,省去我们自己调整样式的时间。
我也愿意尝试使用pinia进行状态管理,使得一份json数据文件可以只处理一次就被多个vue页面使用。与其花时间手工把数据写到代码中,还不如想想如何利用typescript解析json文件中的数据,一步到位的感觉太爽啦。
在实现从赛程Schedule页面跳转到比赛结果Result页面的时候,遇到两个bug。首先是跳转以后,导航栏的激活状态还停留在Schedule页面。这个bug比较简单,只需要把路由信息和导航栏激活选项绑定就好了,二者就能够同步。第二个bug是从Schedule跳转到Result页面的时候,路由中包含props参数,才允许精确跳转决赛还是初赛。但跳转以后查看其他比赛结果,可能数据就加载不出来。原因其实很简单,当要查看其他比赛的结果的时候,跳转过来的参数任然奏效,多余参数导致数据筛选失败。解决方法也很简单,适时让参数失效就好了。
最后部署的时候,也遇到不少问题。由于typescript强类型机制,项目能正常运行但代码会报错,且npm run build失败。查询资料,使用不检查语法强行build即可。后面也发现部署后,生产环境的资源路径和开发环境下不一样,也一一修改了,积累了不少经验。

8.评价队友

222100128 的评价:我的队友还是一如既往的给力,前端组件设计和部署云服务器都是他一手操持,执行力和效率都很高,能够带领我这个小菜鸟在编写作业的过程中有条不紊地进行每一步,还能在帮我一起解决我这边解决不了的难题,是个大佬!!希望下次团队合作也能愉快进行!

222100129 的评价:我的队友自学能力很强,共同学习同一个vue和git视频教程的时候,他总能细心的学,所以一些技术上的细节了解得比较深,比如git相关操作,他能帮我解决很多问题,还有初始项目的默认css文件,也是他及时发现,免得我们浪费很多时间。我和他沟通json文件如何处理,需要什么样的数据进行展示和通信,他可以把复杂的问题用通俗易懂的方法解释给我,让我们沟通交流很顺畅,项目进展也较为顺利。

...全文
142 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复
内容概要:本文围绕基于NSGA-2算法的电-热-气综合需求响应(IDR)在综合能源系统中的多目标优化问题展开研究,提出了一种融合智能优化算法与能源系统协同调度的解决方案。研究系统性地构建了包含电力、热力与天然气多能耦合的综合能源系统模型,设计了以能效提升、运行成本降低和环境效益优化为目标的多目标优化框架,并采用NSGA-2算法求解该复杂非线性优化问题,获得Pareto最优解集。文中详细阐述了IDR机制建模方法、多目标优化问题的数学建模过程以及NSGA-2算法的关键实现步骤,包括编码方式、交叉变异策略与非支配排序机制,并通过Matlab代码实现了完整的仿真验证流程,展示了该方法在平衡多个冲突目标方面的优越性能。; 适合人群:具备电力系统、能源系统建模或优化算法基础的研究生、科研人员及工程技术人员,特别适用于从事综合能源系统规划、需求响应策略设计、多目标进化算法应用等相关领域的研究人员; 使用场景及目标:①应用于综合能源系统的运行优化与规划决策,支持多能互补场景下的协同调度分析;②为科研工作者提供NSGA-2算法在能源系统中实际应用的完整Matlab代码实现范例,助力学术论文复现、算法改进与模型拓展; 阅读建议:建议读者结合文中提供的Matlab代码,按照系统建模、目标函数设计、约束处理到算法实现的研究脉络逐步学习,重点关注Pareto前沿的生成过程、多目标权衡分析及算法参数敏感性,以深入掌握多目标进化算法在复杂能源系统优化中的工程化应用方法。

122

社区成员

发帖
与我相关
我的任务
社区描述
FZU-SE
软件工程 高校
社区管理员
  • LinQF39
  • 助教-吴可仪
  • 一杯时间
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

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