位置: IT常识 - 正文

【蓝桥杯Web】第十四届蓝桥杯Web模拟赛 3 期 | 精品题解(下)(蓝桥杯2021出结果)

编辑:rootadmin
【蓝桥杯Web】第十四届蓝桥杯Web模拟赛 3 期 | 精品题解(下)

推荐整理分享【蓝桥杯Web】第十四届蓝桥杯Web模拟赛 3 期 | 精品题解(下)(蓝桥杯2021出结果),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:蓝桥杯b组2021,蓝桥杯答疑,蓝桥杯2022,蓝桥杯官方,蓝桥杯a组2021,蓝桥杯2022,蓝桥杯资料,蓝桥杯2022,内容如对您有帮助,希望把文章链接给更多的朋友!

🧑‍💼 个人简介:一个不甘平庸的平凡人🍬 🖥️ 蓝桥杯专栏:蓝桥杯题解/感悟 🖥️ TS知识总结:十万字TS知识点总结 👉 你的一键三连是我更新的最大动力❤️! 📢 欢迎私信博主加入前端交流群🌹

📑 目录🔽 前言9️⃣ 趣购🔟 分页组件🔷 虚拟滚动列表(职业院校组)🔼 结语🔽 前言

昨天更新了第十四届蓝桥杯Web模拟赛 3 期的一些基础题的解析,今天抽时间把剩余的压轴题的解析肝出来了,本科组最后的两个题加上职业院校组中与本科组不同的一个题,总共三题,这三题还是有一定难度的,各位小伙伴们加油!

9️⃣ 趣购

这一题挺有趣的,考的拖放Api在平时开发中不是很常见,但考的Vue计算属性还是挺有用的,我们先从事件下手:

<div class="good-list"> <div v-for="good in goods" :key="good.name" class="good" draggable="true" @dragstart="dragstart($event,good)"> <img :src="good.cover" /> <span>{{ good.name }}</span> <span>¥{{ good.price }}</span> </div></div>

上面先为每个商品绑定draggable="true"使其变成可拖放元素,再为其绑定dragstart事件,其对应的dragstart事件处理程序如下:

dragstart(ev,good){ // 向dataTransfer属性中添加拖拽数据 ev.dataTransfer.setData("name", good.name); ev.dataTransfer.setData("price", good.price);}

根据题目信息,我们很容易知道可以在dataTransfer 属性中保存事件的数据。

draggable:这个属性是枚举类型 (en-US),而不是布尔类型。这意味着必须显式指定值为 true 或者 false,像 <img draggable> 这样的简写是不允许的。正确的用法是 <img draggable="false">。 dragstart事件:当用户开始拖拽一个元素或选中的文本时触发

之后需要为购物车图标绑定放置事件drop:

<div id="trolley" class="trolley" @dragover.prevent @drop="drop" > <span id="bought" class="bought" v-if="bought.length !== 0">{{ bought.length }}</span> <img src="./images/trolley.jpeg" /></div>

根据题目信息可以得知,可以通过drop事件来获取可拖放元素的数据,而要想触发drop事件需要先清除dragover事件的默认行为,在Vue中可以通过.prevent修饰符来清除事件的默认行为,所以在购物车图标上还需要绑定一个@dragover.prevent。drop事件对应的事件处理程序如下:

drop(ev){ // 先获取dataTransfer上保存的可拖放元素的数据 const name = ev.dataTransfer.getData("name"); const price = ev.dataTransfer.getData("price"); // 向bought数组中添加该商品的信息(向购物车中添加商品) this.bought.push({name,price:Number(price)}) // 这里将price转换成number类型,方便之后的计算},

观察题目代码很容易推断出data中的bought是用来存放购物车的数据的

通过上面的步骤后题目的要求我们已经实现了一半了,下面需要解决的问题就是将购物车(bought)内的数据渲染到页面上,观察发现页面中使用到了两个计算属性来显示购物车(bought)数据:

<div class="result"> <div> 购物车商品:<span id="goods">{{ goodsDetail }}</span> </div> <div> 购物车商品总计:<span id="total">{{ totalPrice }}</span> </div></div>

所以接下来我们只需要补全goodsDetail和totalPrice这两个计算属性就ok了:

totalPrice() { // 通过数组的reduce求和函数来获取购物车商品总计 return this.bought.reduce((a, b) => { return a + b.price }, 0);},goodsDetail() { /** * 这里用了两次reduce * 第一次是为了将bought中相同的商品合并为同一个对象,并为其添加一个amount字段表示其数量 * 第二次是为了将数据转换成符合题目要求的字符串格式 */ return this.bought.reduce((a, b) => { const good = a.find(item => item.name === b.name) // 先查询a中与b相同的商品 if (good) { // 如果a中有与b相同的商品,则将其amount加1即可 good.amount++ } else { // 如果a中没有与b相同的商品,则向其push b商品的信息并初始化一个amount字段 a.push({ name: b.name, price: Number(b.price), amount: 1 }) } return a }, []).reduce((a, b) => { return a + b.name + '*' + b.amount + ' ' }, '');},

上面代码中使用了数组的reduce方法,对该方法不熟悉的小伙伴可查阅:MDN reduce

🔟 分页组件

到了本科组压轴的题了,这一题的要求还是挺多的,任务量比较大,但好在题中是根据任务数来给分的,所以遇到这种题不要慌,一步一步的向下走就好。

目标一:

/** * @description ajax 请求,通过传递的 currentPage, pageSize 获取到当前页和总页数的数据 * @param {string} url 请求地址,必填 * @param {string} method 请求方式,可选参数,默认为 get * @param {string} data 请求体数据,可选参数 * @param {number} currentPage 当前页数,必填 * @param {number} pageSize 每页显示条目个数,必填 * @return {object} {data,total} data为data.json中data数组的部分数据,total为data.json中total的值 * */async function ajax({ url, method = "get", data, query: { currentPage, pageSize },}) { // TODO:根据函数参数 `query` 对象 `currentPage, pageSize` 获得当前页的数据 let result = { data: [], total: 0, }; let res = await axios[method](url, data); // 获取请求结果 let resData = res.data.data; result.total = resData.length; result.data = resData.splice((currentPage - 1) * pageSize, pageSize); // 通过splice方法将当前页的数据截取出来 return result;}

目标二:

/** * @description 事件绑定,改变 this.currentPage 的值,值在 1 到 this.totalPages 之间 **/initEvents() { this.root.querySelector("#btn-prev").addEventListener("click", () => { // TODO:"<" 按钮的点击事件, 点击时 this.currentPage - 1 if (this.currentPage > 1) { this.currentPage--; this.initPagination(); } }); this.root.querySelector("#btn-next").addEventListener("click", () => { // TODO:">" 按钮的点击事件, 点击时 this.currentPage + 1 if (this.currentPage < this.totalPages) { this.currentPage++; this.initPagination(); } }); this.root.querySelector(".pager").addEventListener("click", (e) => { if (e.target.nodeName.toLowerCase() === "li") { if (this.currentPage === e.target.innerText) return; if (e.target.classList.contains("more")) return; this.currentPage = Number(e.target.innerText); } this.initPagination(); });}

补全initEvents函数并不能,根据事件控制currentPage的值即可,需要注意的就是每当currentPage的值改变都要调用一下initPagination事件(这一点题目代码中initPagination事件上的注释里明确说到了)。

将initEvents函数补全之后提交测试我们还不能通过目标二,这是因为负责渲染分页按钮的renderPagination函数还没有补全(目标四的要求),导致页面上还不能正确显示分页组件,所以系统才会不让通过,目标三提交不通过也是这个原因。

【蓝桥杯Web】第十四届蓝桥杯Web模拟赛 3 期 | 精品题解(下)(蓝桥杯2021出结果)

也就是说不先通过目标四,目标二和目标三完成了也不会通过,那这题分目标给分的意义在哪呢🙄)

目标三:

个人认为目标三是最为复杂的一个任务,主要是你要能想到这种逻辑,下面代码中我分了两大类情况:

当totalPages<=pagerCount时直接遍历totalPages向数组中添加页码就行,这没什么好说的。当totalPages>pagerCount时就比较复杂了,需要再考虑三种情况,也就是题目中给的例子[1,2,3,4,10],[1,3,4,5,10],[1,7,8,9,10]这三种情况,观察这三个数组很容易发现:[1,2,3,4,10]是靠左显示,向右扩散的;也就说从左向右读是连续的,从右向左读会出现断层(4和10)[1,3,4,5,10]是中间显示,向两边扩散;也就说不管是从左向右读还是从右向左读都会出现断层(1和3,5和10)[1,7,8,9,10]是靠右显示,向左扩散的;也就说从右向左读是连续的,从左向右读会出现断层(1和7)

上面关于第二类的三种情况的说明可能不是很准确,大家明白这个意思就行。

了解了情况后,直接看代码:

/** * @description 得到分页数组 indexArr,如[1,2,3,4,10],[1,3,4,5,10],[1,7,8,9,10] * @param {number} currentPage 当前页数,默认为第一页 * @param {number} totalPages 总的页码数 * @param {number} pagerCount 设置最大页码按钮数。 页码按钮的数量,当总页数超过该值时会折叠 * @return {Array} 分页数组 indexArr */const createPaginationIndexArr = (currentPage, totalPages, pagerCount) => { let indexArr = []; // TODO:根据传参生成分页数组 indexArr indexArr[0] = 1; // 第一项肯定是1 if (!currentPage) { // currentPage不存在时默认为1 currentPage = 1; } if (!totalPages) { // totalPages不存在时默认为currentPage totalPages = currentPage; } if (!pagerCount) { // pagerCount不存在时默认为5 pagerCount = 5; } // 上面三个判断可以不要 let medial = Math.floor(pagerCount / 2); // 中间位置 if (totalPages <= pagerCount) { for (let i = 1; i < totalPages; i++) { indexArr[i] = i + 1; } } else { indexArr[pagerCount - 1] = totalPages; // 最后一项为totalPages // 当前页数靠左边,则从左向右扩散添加 // 例如当前页数是2,3,4,总页数为10,页码按钮数是5时:[1,2,3,4,10] if (currentPage <= medial && totalPages - currentPage > medial) { for (let i = 1; i < pagerCount - 1; i++) { indexArr[i] = i + 1; } } // 当前页数在中间,则从中间向两边扩散 // 例如当前页数是4,总页数为10,页面按钮数是5时:[1,3,4,5,10] if (currentPage > medial && totalPages - currentPage > medial) { indexArr[medial] = currentPage; // 中间位置设置为当夜页数 for (let i = medial - 1, c = 1; i > 0; i--, c++) { indexArr[i] = currentPage - c; // 向左扩散添加 if (medial + c < pagerCount - 1) { indexArr[medial + c] = currentPage + c; // 向右扩散添加 } } } // 当前页数在右边,则从右向左扩散添加 // 例如当前页数是7,8,9,总页数为10,页面按钮数是5时:[1,7,8,9,10] if (currentPage > medial && totalPages - currentPage <= medial) { for (let i = pagerCount - 2, c = 1; i > 0; i--, c++) { indexArr[i] = totalPages - c; } } } return indexArr;};module.exports = { createPaginationIndexArr,};

在第二类情况下indexArr数组的第一项一定是1,最后一项一定是totalPages,所以我们只需要再根据那三种情况填充indexArr中剩余位置的空间即可。

目标四:

/** * @description 根据序号数组生成分页组件的字符串模板通过 innerHTML 挂载在 root 元素内 * @param {Array} indexArr 分页数组 indexArr * @return {String} 分页组件的字符串模板 */renderPagination(indexArr) { let template = ""; // TODO:根据 indexArr 数组生成分页组件的字符串模板 template template = indexArr .map((item, index) => { let more = `<li class="number more">...</li>`; let str = `<li class="number ${item === this.currentPage ? "active" : ""}">${item}</li>`; if (index > 0 && item - indexArr[index - 1] > 1) { // 如果当前item与上一个item(index[index-1])的差值大于1,则需要在当前分页按钮的前面添加... return more + str; } return str; }) .join(""); this.root.innerHTML = ` <div class="pagination"> <div class="btn btn-left" id="btn-prev">&lt;</div> <ul class="pager">${template} </ul> <div class="btn btn-right" id="btn-next">&gt;</div> </div>`;}🔷 虚拟滚动列表(职业院校组)

这一题考察日常开发中常见的列表优化方式:虚拟滚动列表。

先通过axios获得全部的数据:

mounted() { // TODO: 完成数据请求 axios.get("./data.json").then((res) => { this.list = res.data; // 存放总数据 this.totalHeight = res.data.length * this.itemHeight; // 总高度 });},

然后对容器绑定滚动事件scroll:

<div id="virtual-list" class="virtual-list" @scroll="scroll">methods: { // TODO: 完成事件处理 scroll(e) { this.start = Math.floor(e.target.scrollTop / this.itemHeight); },},

在scroll事件中,我们通过滚动条已经滚动的高度/每一项的高度来获取可视区域内的第一项的下标(this.start)。

例如:已经滚动300,每一项高度为100,则已经滚动了三项了,目前可视区域内的第一项应为第四项,其对应的下标就为3

计算出需要渲染到页面上的列表:

computed: { showingList() { let sliceStart = this.start > this.buffer ? this.start - this.buffer : 0; let sliceEnd = this.start + this.length + this.buffer + 1; return this.list.slice(sliceStart, sliceEnd); },},

题目中给了buffer这个字段,其目的是为了防止出现白屏,在我们每次计算需要渲染到页面上的列表时向前面多计算buffer个,向后面也多计算buffer个。

在showingList方法中我们主要通过数组的slice方法来截取到需要渲染到页面上的部分,其中:sliceStart表示开始截取的下标,sliceEnd表示结束截取的下标(因为是截取不到sliceEnd位置的,所以需要提前+1)。

当this.start改变时,showingList计算属性会重新执行,于是就能获取到每次滚动时需要渲染到页面中的数据。

只是获取到数据还不行,当我们进行滚动时需要将列表项容器也进行位移,这样才能保证数据一直在可视区域内:

<ul id="list" class="list" :style="{ transform: 'translateY(' + (start > buffer ? (start - buffer) * itemHeight : 0) + 'px)', }">

在start小于buffer时,由于列表可视区域下方还有buffer个元素,当我们滚动时列表项能自然进行滚动,所以不需要设置translateY,只有当start>buffer时才需要设置translateY。

实现虚拟列表的代码量并不多,主要还是在于逻辑。

🔼 结语

距离第十四届蓝桥杯的正式比赛还有不到一个月的时间,好好复习,祝大家都能在正式比赛中取得满意的成绩!

如果本篇文章对你有所帮助,还请客官一件四连!❤️

本文链接地址:https://www.jiuchutong.com/zhishi/298681.html 转载请保留说明!

上一篇:【OpenCV-Python】:查找物体轮廓+计算轮廓面积、长度、重心(python的opencv)

下一篇:现代信号处理——阵列信号处理(空域滤波原理及其算法)(现代信号处理张贤达pdf)

  • 微博营销不是所有人都能做的(微博营销属于什么营销方式)

    微博营销不是所有人都能做的(微博营销属于什么营销方式)

  • 交通部 花小猪(交通部再次点名花小猪)(花小猪 车辆)

    交通部 花小猪(交通部再次点名花小猪)(花小猪 车辆)

  • 华为荣耀v20在哪里插卡(华为荣耀v20在哪设置黑屏下能看到时间)

    华为荣耀v20在哪里插卡(华为荣耀v20在哪设置黑屏下能看到时间)

  • 滴滴出行怎么看历史订单(滴滴出行怎么看剩余骑行次数)

    滴滴出行怎么看历史订单(滴滴出行怎么看剩余骑行次数)

  • 怎么保存腾讯视频到手机相册(怎么保存腾讯视频里的精彩片段?)

    怎么保存腾讯视频到手机相册(怎么保存腾讯视频里的精彩片段?)

  • 发起群聊对方有提示吗(发起群聊对方有提醒吗)

    发起群聊对方有提示吗(发起群聊对方有提醒吗)

  • 针式打印机一进纸就退(针式打印机一进纸就卡纸了怎么办)

    针式打印机一进纸就退(针式打印机一进纸就卡纸了怎么办)

  • 格式工厂在哪里打开(手机格式工厂在哪里)

    格式工厂在哪里打开(手机格式工厂在哪里)

  • 手机有声音但是黑屏怎么办(手机有声音但是黑屏是内屏坏了吗)

    手机有声音但是黑屏怎么办(手机有声音但是黑屏是内屏坏了吗)

  • 换手机信息如何转移到另外手机上(换手机之后信息怎么转移)

    换手机信息如何转移到另外手机上(换手机之后信息怎么转移)

  • 天猫精灵cc怎么监控(天猫精灵CC怎么拆)

    天猫精灵cc怎么监控(天猫精灵CC怎么拆)

  • vivo手电筒在哪(vivo手电筒在哪儿)

    vivo手电筒在哪(vivo手电筒在哪儿)

  • 苹果11屏幕黄怎么调白(苹果屏幕黄怎么关掉)

    苹果11屏幕黄怎么调白(苹果屏幕黄怎么关掉)

  • 苹果x相机声音怎么关(苹果x相机声音在哪里打开)

    苹果x相机声音怎么关(苹果x相机声音在哪里打开)

  • 小米6x支持多少w快充(小米6x支持33w快充吗)

    小米6x支持多少w快充(小米6x支持33w快充吗)

  • 手机恢复出厂设置后数据还能恢复吗(手机恢复出厂设置是不是彻底干净)

    手机恢复出厂设置后数据还能恢复吗(手机恢复出厂设置是不是彻底干净)

  • 相册里的私密照片怎么找(相册里的私密照片怎么找?)

    相册里的私密照片怎么找(相册里的私密照片怎么找?)

  • opsson是什么牌子手机(opsson是什么牌子手机多少钱)

    opsson是什么牌子手机(opsson是什么牌子手机多少钱)

  • oppo手机怎么开启短信提示音(oppo手机怎么开机不了机)

    oppo手机怎么开启短信提示音(oppo手机怎么开机不了机)

  • 苹果手机自带输入法怎么换行(苹果手机自带输入法)

    苹果手机自带输入法怎么换行(苹果手机自带输入法)

  • apple watch series4如何微信支付

    apple watch series4如何微信支付

  • 苹果电话怎么拉黑(苹果电话怎么拉出来)

    苹果电话怎么拉黑(苹果电话怎么拉出来)

  • uniapp小程序自定义顶部导航栏,输入框软键盘把顶部顶上去的解决方法(uniapp小程序自定义tabbar在iOS手机上太高了)

    uniapp小程序自定义顶部导航栏,输入框软键盘把顶部顶上去的解决方法(uniapp小程序自定义tabbar在iOS手机上太高了)

  • manpath命令  查看man手册页的查询路径(man-s命令)

    manpath命令 查看man手册页的查询路径(man-s命令)

  • Linux下载安装RabbitMQ(linux lab安装)

    Linux下载安装RabbitMQ(linux lab安装)

  • phpcms如何配置数据库(php cms)

    phpcms如何配置数据库(php cms)

  • 个税应纳税所得额是要上交的钱吗
  • 增值税销项进项什么意思
  • 所得税费按年缴纳?
  • 先开发票后发货
  • 发票抬头类型怎么选 个人不能报销吗
  • 代建制规定
  • 商贸企业购进商品怎么做成本
  • 失业社保补助金领取条件
  • 金税盘减免税额
  • 开完发票业务没收入需要确认收入入账吗?
  • 增值税发票备注栏怎么填写
  • 代收代付给个人
  • 增值税发票的基数是什么
  • 生产成本属于什么会计要素
  • 银行余额不平怎么快速找原因
  • 需要自己到税务局自行申报个人所得税的纳税人有哪些?
  • 基本户是否可以冻结
  • 职工食堂的费用,会计上怎么做账?
  • win7浏览器显示证书错误怎么解决
  • unity默认存储路径
  • CodeIgniter与PHP5.6的兼容问题
  • 怎么租一个月
  • react img onerror
  • 销售方怎么申请红字信息表
  • 债转股税收优惠政策
  • 房地产销售未完工产品收入是含税的吗
  • 水表同步
  • vue批量上传图片至oss
  • svc语法
  • 外出培训餐费要进差旅费吗
  • vue.js简介
  • php如何防止sql注入攻击
  • 应收账款与主营业务收入的比率
  • 金融机构贷款准备金
  • 企业投资计入什么科目
  • 个人所得税汇算清缴时间
  • mongodb查询工具
  • 记 vue-cli-plugin-dll 使用,优化vue-cli项目构建打包速度
  • 科目余额表怎么填
  • 销售公司扣款制度合法吗
  • 交易性金融资产的账务处理
  • 公司销售一批物品怎么做
  • 对供应商的罚款需要开票吗
  • 物业公司支出费用
  • 固定资产的计提折旧方法有哪些
  • 坏账损失记入
  • 对公账户给私人账户转账,几天到账
  • 票据到期无力支付怎么办
  • 开票操作流程是什么
  • 明细账的登记方向与总账的登记方向是一致的
  • 未分配利润太大了怎么处理
  • sql2008设置单用户模式
  • centos7 syslog
  • windowsserver2008r2版本区别
  • 怎么在bios关闭软驱
  • 用u盘装系统怎么操作步骤
  • secbizsrv.exe是什么程序
  • win8管理工具在哪里
  • win8系统win10有什么区别
  • winxp系统介绍
  • win7 64位系统只有搜狗浏览器可以打开网页其他浏览器打不开的故障原因及解决方法
  • 在windowsxp的应用程序中,经常有一些菜单选项呈暗灰色
  • windows8关机键在哪里
  • win8飞行模式在哪
  • linux命令eof
  • WIN7系统如何设置开机密码和屏保密码
  • extend列表
  • cocos2dx4.0入门
  • 微信小程序基于spingboot
  • unity调用c++封装的dll
  • jquery-validation
  • 安卓手机管家删除的照片怎么恢复
  • django admin form
  • javascript的常用例子
  • 全年上缴个人所得税多少
  • 汽车购置税退税需要多长时间
  • 购买服务合同模版
  • 苏州税务系统
  • 朝阳区地方税务局电话
  • 应交税金包含企业所得税吗
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设