[ 蓝桥杯Web真题 ]-组课神器

news2024/11/19 16:22:54

目录

介绍

准备

目标

规定

思路

参考解法


介绍

在很多教育网站的平台上,课程的章节目录会使用树型组件呈现,为了方便调整菜单,前端工程师会为其赋予拖拽功能。本题需要在已提供的基础项目中,完成可拖拽树型组件的功能。

准备

├── effect.gif
├── css
│   └── index.css
├── index.html
└── js
    ├── data.json
    ├── axios.min.js
    └── index.js

其中:

  • index.html 是主页面。
  • js/index.js 是待完善的 js 文件。
  • js/data.json 是存放数据的 json 文件。
  • js/axios.min.js 是 axios 文件。
  • css/index.css 是 css 样式文件。
  • effect.gif 是完成的效果图。

注意:打开环境后发现缺少项目代码,请复制下述命令至命令行进行下载。

cd /home/project
wget -q https://labfile.oss.aliyuncs.com/courses/18213/test8.zip
unzip test8.zip && rm test8.zip

在浏览器中预览 index.html 页面,显示如下所示:

目标

请在 js/index.js 文件中补全代码。

最终效果可参考文件夹下面的 gif 图,图片名称为 effect.gif (提示:可以通过 VS Code 或者浏览器预览 gif 图片)。

具体需求如下:

1.补全 js/index.js 文件中 ajax 函数,功能为根据请求方式 method 不同,拿到树型组件的数据并返回。具体如下:

  • 当 method === "get" 时,判断 localStorage 中是否存在 key 为 data 的数据,若存在,则从 localStorage 中直接获取后处理为 json 格式并返回;若不存在则从 ./js/data.json(必须使用该路径请求,否则可能会请求不到数据)中使用 ajax 获取并返回。

  • 当 method === "post" 时,将通过参数 data 传递过来的数据转化为 json 格式的字符串,并保存到 localStorage 中,key 命名为 data

最终返回的数据格式如下:

[
    {
        "id": 1001,
        "label": "第一章 Vue 初体验",
        "children": [ ... ]
    },
    {
      "id": 1006,
      "label": "第二章 Vue 核心概念",
      "children": [
          {
              "id": 1007,
              "label": "2.1 概念理解",
              "children": [
                  {
                      "id": 1008,
                      "label": "聊一聊虚拟 DOM",
                      "tag":"文档 1"
                  },
                  ...
              ]
          },
          {
              "id": 1012,
              "label": "2.2 Vue 基础入门",
              "children": [
                  {
                      "id": 1013,
                      "label": "Vue 的基本语法",
                      "tag":"实验 6"
                  },
                  ...
              ]
          }
      ]
  }
]

2.补全 js/index.js 文件中的 treeMenusRender 函数,使用所传参数 data 生成指定 DOM 结构的模板字符串(完整的模板字符串的 HTML 样例结构可以在 index.html 中查看),并在包含 .tree-node 的元素节点上加上指定属性如下:

属性名属性值描述
data-grade${grade}表示菜单的层级,整数,由 treeMenusRender 函数的 grade 参数值计算获得,章节是 1,小节是 2,实验文档是 3。
data-index${id}表示菜单的唯一 id,使用每层菜单数据的 id 字段值。

3.补全 js/index.js 文件中的 treeDataRefresh 函数,功能为:根据参数列表 { dragGrade, dragElementId }, { dropGrade, dropElementId } 重新生成拖拽后的树型组件数据 treeDatatreeData 为全局变量,直接访问并根据参数处理后重新赋值即可)。

方便规则描述,现将 data.json 中的数据扁平化处理,得到的数据顺序如下:

[
  { grade: "1", label: "第一章 Vue 初体验", id: "1001" },
  { grade: "2", label: "1.1 Vue 简介", id: "1002" },
  { grade: "3", label: "Vue 的发展历程", id: "1003" },
  { grade: "3", label: "Vue 特点", id: "1004" },
  { grade: "3", label: "一分钟上手 Vue", id: "1005" },
  { grade: "1", label: "第二章 Vue 核心概念", id: "1006" },
  { grade: "2", label: "2.1 概念理解", id: "1007" },
  { grade: "3", label: "聊一聊虚拟 DOM", id: "1008" },
  { grade: "3", label: "感受一下虚拟 DOM", id: "1009" },
  { grade: "3", label: "聊一聊 MVVM 设计模式", id: "1010" },
  { grade: "3", label: "Vue 中的 MVVM 设计模式", id: "1011" }, // 即将被拖拽的元素节点
  { grade: "2", label: "2.2 Vue 基础入门", id: "1012" },
  { grade: "3", label: "Vue 的基本语法", id: "1013" },
  { grade: "3", label: "第一步,创建 Vue 应用实例", id: "1014" },
]

拖拽前后的规则说明如下:

情况一:若拖拽的节点和放置的节点为同级,即 treeDataRefresh 函数参数列表中 dragGrade == dropGrade,则将 id == dragElementId(例如:1011)的节点移动到 id==dropElementId(例如:1008)的节点后,作为其后第一个邻近的兄弟节点。最终生成的数据顺序如下:

[
  { grade: "1", label: "第一章 Vue 初体验", id: "1001" },
  { grade: "2", label: "1.1 Vue 简介", id: "1002" },
  { grade: "3", label: "Vue 的发展历程", id: "1003" },
  { grade: "3", label: "Vue 特点", id: "1004" },
  { grade: "3", label: "一分钟上手 Vue", id: "1005" },
  { grade: "1", label: "第二章 Vue 核心概念", id: "1006" },
  { grade: "2", label: "2.1 概念理解", id: "1007" },
  { grade: "3", label: "聊一聊虚拟 DOM", id: "1008" },
  // 在目标元素节点下方插入
  { grade: "3", label: "Vue 中的 MVVM 设计模式", id: "1011" },
  { grade: "3", label: "感受一下虚拟 DOM", id: "1009" },
  { grade: "3", label: "聊一聊 MVVM 设计模式", id: "1010" },
  // 移除被拖拽的元素节点
  { grade: "2", label: "2.2 Vue 基础入门", id: "1012" },
  { grade: "3", label: "Vue 的基本语法", id: "1013" },
  { grade: "3", label: "第一步,创建 Vue 应用实例", id: "1014" }
]

情况二:若拖拽的节点和放置的节点为上下级,即 treeDataRefresh 函数参数列表中 dragGrade - dropGrade == 1,则将 id == dragElementId(例如:1011)的节点移动到 id==dropElementId(例如:1002)的节点下,并作为其第一个子节点。最终生成的数据顺序如下:

[
  { grade: "1", label: "第一章 Vue 初体验", id: "1001" },
  { grade: "2", label: "1.1 Vue 简介", id: "1002" },
  // 在目标元素节点下方插入
  { grade: "3", label: "Vue 中的 MVVM 设计模式", id: "1011" },
  { grade: "3", label: "Vue 的发展历程", id: "1003" },
  { grade: "3", label: "Vue 特点", id: "1004" },
  { grade: "3", label: "一分钟上手 Vue", id: "1005" },
  { grade: "1", label: "第二章 Vue 核心概念", id: "1006" },
  { grade: "2", label: "2.1 概念理解", id: "1007" },
  { grade: "3", label: "聊一聊虚拟 DOM", id: "1008" },
  { grade: "3", label: "感受一下虚拟 DOM", id: "1009" },
  { grade: "3", label: "聊一聊 MVVM 设计模式", id: "1010" },
  // 移除被拖拽的元素节点
  { grade: "2", label: "2.2 Vue 基础入门", id: "1012" },
  { grade: "3", label: "Vue 的基本语法", id: "1013" },
  { grade: "3", label: "第一步,创建 Vue 应用实例", id: "1014" }
];

规定

  • 请勿修改 js/index.js 文件外的任何内容。
  • 请严格按照考试步骤操作,切勿修改考试默认提供项目中的文件名称、文件夹路径、class 名、id 名、图片名等,以免造成无法判题通过。先自己做一下吧:传送门

思路

这道题目时第14届蓝桥杯省赛真题的最后一题。难度还是挺大的。第一问主要是进行数据的读取,使用到了axios来进行数据的请求,同时使用到了LocalStorage的数据读取已经数据设置。第二个功能主要是进行数据的模板渲染。最后主要是考察对DOM的操作

参考解法

第一个功能相对来说比较简单,主要考察axios发送请求以及LocalStorage本地数据的读取以及存储的知识点。获取数据主要是使用到localStorage中的getItem方法,存储数据主要是用到了了其setItem的方法。进行本地数据的存储可以减少不必要的ajax请求,因此可以进行性能的优化。同时还需要知道JSON.parse以及JSON.stringify的使用方法,第一个是用于将 JSON 字符串解析成对应的 JavaScript 对象,而后者是则是相反。

async function ajax({ url, method = "get", data }) {
    let result;
    // TODO:根据请求方式 method 不同,拿到树型组件的数据
    // 当method === "get" 时,localStorage 存在数据从 localStorage 中获取,不存在则从 /js/data.json 中获取
    // 当method === "post" 时,将数据保存到localStorage 中,key 命名为 data
    if(method === "get"){
      const localData = localStorage.getItem("data")
      if(localData){
          result = JSON.parse(localData)
      }else{
        const res = await axios.get(url)
        result = res.data.data
      }
    }
    if(method === "post"){
        localStorage.setItem("data",JSON.stringify(data))
    }
    return result;
  }

第二个功能的实现使用到了递归的方式处理了树形数据的多层级结构,确保了生成的 HTML 结构符合树型组件的预期。

function treeMenusRender(data, grade = 0) {
  let treeTemplate = "";
  // TODO:根据传入的 treeData 的数据生成树型组件的模板字符串
  function generateTree(nodes, depth = 0) {
    let template = '';
    nodes.forEach(node => {
      template += `<div class="tree-node" data-index="${node.id}" data-grade="${depth + 1}">
        <div class="tree-node-content" style="margin-left: ${depth * 15}px">
          <div class="tree-node-content-left">
            <img src="./images/dragger.svg" alt="" class="point-svg" />
            <span class="tree-node-tag">${node.tag || ''}</span>
            <span class="tree-node-label">${node.label}</span>
            <img class="config-svg" src="./images/config.svg" alt="" />
          </div>
          <div class="tree-node-content-right">
            <div class="students-count">
              <span class="number"> 0人完成</span>
              <span class="line">|</span>
              <span class="number">0人提交报告</span>
            </div>
            <div class="config">
              <img class="config-svg" src="./images/config.svg" alt="" />
              <button class="doc-link">编辑文档</button>
            </div>
          </div>
        </div>`;

      if (node.children && node.children.length > 0) {
        template += `<div class="tree-node-children">${generateTree(node.children, depth + 1)}</div>`;
      }

      template += `</div>`;
    });
    return template;
  }

  // 生成树型组件的模板字符串
  treeTemplate = generateTree(data, grade);
  
  return treeTemplate;
}

treeMenusRender 函数:这个函数接收两个参数:data 是树形数据的数组,grade 是可选的用来表示树的层级。在函数内部定义了 generateTree 函数,用来递归生成树的结构。generateTree 函数接收一个节点数组和深度参数,然后对每个节点进行处理。

generateTree 函数:这个函数是用来构建树结构的核心部分。它使用了递归的方式处理树形结构的不同层级。对于传入的节点数组,它遍历每个节点,并根据节点的数据生成相应的 HTML 结构。对于每个节点,它创建一个包含节点信息的 <div class="tree-node"> 元素。使用 data-index 和 data-grade 属性存储节点的索引和等级信息。对于每个节点的子节点,递归调用 generateTree 函数,直到遍历完所有层级的节点。

生成的 HTML 结构包含了节点的内容,例如标签、标签属性、图标等。每个节点都包含一个 <div class="tree-node">,里面嵌套着节点的内容和可能存在的子节点。生成的 HTML 结构存储在 treeTemplate 变量中,最终作为函数的返回值。

最后一个功能的实现,先找到需要移动的数据并保存到变量当中,将需要移动的数据删除,寻找需要移动要的位置,最后插入数据。

function treeDataRefresh(
  { dragGrade, dragElementId },
  { dropGrade, dropElementId }
) {
  if (dragElementId === dropElementId) return;
  // TODO:根据 `dragElementId, dropElementId` 重新生成拖拽完成后的树型组件的数据 `treeData`
  if( dragGrade == dropGrade || dragGrade - dropGrade == 1){
    let drapItem = {}
    for(let i = 0;i < treeData.length;i++){
      if(treeData[i].id === Number(dragElementId)){
        drapItem = treeData[i]
        treeData.splice(i,1)
        break
      }
      for(let j = 0;j < treeData[i].children.length;j++){
        if(treeData[i].children[j].id === Number(dragElementId)){
          drapItem = treeData[i].children[j]
          treeData[i].children.splice(j,1)
          break
        }
        for(let k = 0;k < treeData[i].children[j].children.length;k++){
          if(treeData[i].children[j].children[k].id === Number(dragElementId)){
            drapItem = treeData[i].children[j].children[k]
            treeData[i].children[j].children.splice(k,1)
            break
          }
        }
      }
    }
    if(dragGrade == dropGrade){
      for(let i = 0;i < treeData.length;i++){
        if(treeData[i].id === Number(dropElementId)){
          treeData.splice(i+1,0,drapItem)
          return
        }
        for(let j = 0;j < treeData[i].children.length;j++){
          if(treeData[i].children[j].id === Number(dropElementId)){
            treeData[i].children.splice(j+1,0,drapItem)
            return
          }
          for(let k = 0;k < treeData[i].children[j].children.length;k++){
            if(treeData[i].children[j].children[k].id === Number(dropElementId)){
              treeData[i].children[j].children.splice(k+1,0,drapItem)
              return
            }
          }
        }
      }
    }else if(dragGrade - dropGrade == 1){
      for(let i = 0;i < treeData.length;i++){
        if(treeData[i].id === Number(dropElementId)){
          console.log( treeData[i])
          treeData[i].children.unshift(drapItem)
          return
        }
        for(let j = 0;j < treeData[i].children.length;j++){
          if(treeData[i].children[j].id === Number(dropElementId)){
            treeData[i].children[j].children.unshift(drapItem)
            return
          }
        }
      }
    }
  }
}

首先,我们找到拖拽的节点(根据 dragElementId),然后将其从原始位置中移除,并将移除的节点保存在 drapItem。它首先遍历整个 treeData 数组,查找具有匹配 id 的节点。第一个循环:遍历主节点(第一级)。第二个循环:遍历每个主节点下的子节点(第二级)。第三个循环:遍历每个子节点下的子节点(第三级)。

如果找到匹配的 id,则会将该节点保存在 drapItem 中,并从数组中移除。这个过程确保了我们可以正确找到拖拽的节点,并将其从原始位置中取出,以备后续的插入或添加到新位置。

拖拽到同一级别时,查找被拖拽的节点: 首先在树形数据 treeData 中查找被拖拽的节点(根据 dragElementId)。移除被拖拽的节点: 找到后,从原来的位置中移除该节点,并保存在 drapItem 中。插入到目标节点后: 接着,在 treeData 中找到放置节点的位置(根据 dropElementId),然后将被拖拽的节点 drapItem 插入到放置节点后面。

拖拽到下一级时,查找被拖拽的节点: 同样地,在树形数据 treeData 中查找被拖拽的节点(根据 dragElementId)。移除被拖拽的节点: 找到后,从原来的位置中移除该节点,并保存在 drapItem 中。添加为子节点: 接着,在 treeData 中找到放置节点的位置(根据 dropElementId),将被拖拽的节点 drapItem 添加为放置节点的子节点的第一个节点。 

(第三部分的代码可以考虑更加简化的方法,因此试了一直弄不出来,累了,就直接暴力解决了。优化考虑就交给各位了!)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1298192.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【数据结构】——排序篇(上)

前言&#xff1a;前面我们已经学过了许许多多的排序方法&#xff0c;如冒泡排序&#xff0c;选择排序&#xff0c;堆排序等等&#xff0c;那么我们就来将排序的方法总结一下。 我们的排序方法包括以下几种&#xff0c;而快速排序和归并排序我们后面进行详细的讲解。 直接插入…

【SpringBoot】解析Springboot事件机制,事件发布和监听

解析Springboot事件机制&#xff0c;事件发布和监听 一、Spring的事件是什么二、使用步骤2.1 依赖处理2.2 定义事件实体类2.3 定义事件监听类2.4 事件发布 三、异步调用3.1 启用异步调用3.2 监听器方法上添加 Async 注解 一、Spring的事件是什么 Spring的事件监听&#xff08;…

Android 从assets读取文件装载成Bitmap,Kotlin

Android 从assets读取文件装载成Bitmap&#xff0c;Kotlin /*** fileName assets里面图片文件名*/fun readBitmapFromAssets(ctx: Context, fileName: String): Bitmap? {val assetManager: AssetManager ctx.assetsvar bitmap: Bitmap? nulltry {val inputStream assetMa…

自行编写一个简单的shell!

本文旨在编写一个简单的shell外壳程序&#xff01;功能类似于shell的一些基本操作&#xff01;虽然不能全部实现shell的一些功能&#xff01;但是通过此文章&#xff0c;自己写一个简单的shell程序也是不成问题&#xff01;并且通过此文章&#xff0c;可以让读者对linux中一些环…

实现一个简单的外卖系统

在这个技术飞速发展的时代&#xff0c;外卖系统已经成为人们生活中的一部分。通过一些简单的技术代码&#xff0c;我们可以搭建一个基本的外卖系统&#xff0c;让用户方便地选择、下单和支付。本文将使用Python和Flask框架为基础&#xff0c;演示一个简单的外卖系统的实现。 …

基于ssm海鲜自助餐厅系统论文

摘 要 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计算机运行的软件更是受到各界的关注。加上现在人们已经步入信息时代&#xff0c;所以对于信息的宣传和管理就很关键。因此海鲜餐厅信息的…

C/C++之输入输出

文章目录 一.C语言的输入输出1.printfi. 输出整数ii. 浮点数iii.字符 & 字符串 2.scanfi.整数ii.浮点数iii. 字符 & 字符串 3.特殊用法i. * 的应用ii. %n 的应用iii. %[] 的应用 二.C中的输入输出1.couti. 缓冲区&#xff08;buffer&#xff09;ii. cout之格式化输出 2…

鸿蒙开发组件之Text

一、文本组件Text加载主要有两种方式&#xff1a; 1、直接写的文本 Text(你好&#xff0c;世界) 2、从本地资源读取的文本 Text($r(app.string.xxxx)) 二、文本国际化 其中&#xff0c;文本设置可以支持国际化。可以通过对本地文本读取支持国际化。在需要设置国际化的文本…

在AWS Lambda上部署EC2编译的FFmpeg工具——自定义层的方案

大纲 1 确定Lambda运行时环境1.1 Lambda系统、镜像、内核版本1.2 运行时1.2.1 Python1.2.2 Java 2 环境准备2.1 创建EC2实例 3 编译FFmpeg3.1 连接EC2 4 编译5 上传S3存储桶5.1 创建S3桶5.2 创建IAM策略5.3 创建IAM角色5.4 EC2关联角色5.5 修改桶策略5.6 打包并上传 6 创建Lamb…

【vue3】尚硅谷vue3学习笔记

Vue3快速上手 1.Vue3简介 2020年9月18日&#xff0c;Vue.js发布3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;海贼王&#xff09;耗时2年多、2600次提交、30个RFC、600次PR、99位贡献者github上的tags地址&#xff1a;https://github.com/vuejs/vue-next/release…

记录 | ubuntu监控cpu频率、温度等

ubuntu监控cpu频率、温度等 采用 i7z 进行监控&#xff0c;先安装&#xff1a; sudo apt install i7z -ysudo i7z

vue3 自己写一个月的日历

效果图 代码 <template><div class"monthPage"><div class"calendar" v-loading"loading"><!-- 星期 --><div class"weekBox"><div v-for"(item, index) in dayArr" :key"index&q…

5G+AI开花结果,助力智慧安检落地

“请带包的乘客过机安检&#xff01;”&#xff0c;深圳地铁、腾讯共同打造的5GAI智慧安检辅助系统亮相福田枢纽站&#xff0c;进一步解放了人力&#xff0c;提高安检效率&#xff0c;为交通安全保驾护航&#xff0c;让智慧出行成为现实。 传统的安检设备均为人工肉眼辨识&…

【Vue+Python】—— 基于Vue与Python的图书管理系统

文章目录 &#x1f356; 前言&#x1f3b6;一、项目描述✨二、项目展示&#x1f3c6;三、撒花 &#x1f356; 前言 【VuePython】—— 基于Vue与Python的图书管理系统 &#x1f3b6;一、项目描述 描述&#xff1a; 本项目为《基于Vue与Python的图书管理系统》&#xff0c;项目…

Java第21章网络通信

网络程序设计基础 网络程序设计编写的是与其他计算机进行通信的程序。Java 已经将网络程序所需要的元素封 装成不同的类&#xff0c;用户只要创建这些类的对象&#xff0c;使用相应的方法&#xff0c;即使不具备有关的网络支持&#xff0c;也可 以编写出高质量的网络…

手把手带你创建HAL版本MDK工程模板

手把手带你创建HAL版本MDK工程模板 如何快速开发 STM32 项目&#xff1f;我们总不能每次开发一个项目就搭建一次工程&#xff0c;这样效率太低了。 通常我们会使用一个模板工程&#xff0c;需要开发新项目的时候拿出来添加一些对应的模块及业务代码&#xff0c;一个项目就开发…

BearPi Std 板从入门到放弃 - 先天篇(1)(阶段 : 智慧城市 - 智慧路灯)

简介 对前面几篇整合, 做个小小汇总试验, 使用BearPi E53_SC1扩展板主芯片: STM32L431RCT6串口: Usart1扩展板与主板连接: I2C : I2C1 (光照强度传感器&#xff1a;BH1750)LED: PB9步骤 创建项目 参考 BearPi Std 板从入门到放弃 - 引气入体篇&#xff08;1&#xff09;(由零创…

LabelImg的使用及注意事项

LabelImg是一款开源的图像标注工具&#xff0c;它主要用于标注目标检测、语义分割和图像分类等深度学习中需要的数据集。通过使用LabelImg&#xff0c;用户可以快速、准确地为图片中的目标添加标注信息&#xff0c;从而建立数据集。 使用步骤&#xff1a; 下载LabelImg&#x…

word中,文本框如何跨页?

我们经常使用word编辑一些文档&#xff0c;文档中往往会有一些比较大的文本框&#xff0c;需要跨多页&#xff0c;我们可以使用本文章中的方法&#xff0c;将文本框连接在一起&#xff0c;是的内容自动跨页。 在文字中插入两个文本框如下图&#xff1a; 将内容放到第一个文本框…

基于SpringBoot的大学活动平台

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着互联网技术的不断…