第十四届蓝桥杯大赛Web应用与开发省赛职业院校组题解

第十四届蓝桥杯Web应用与开发省赛题解

01 电影院排座位(5 分)

.seat-area {
  margin-top: 50px;
  display: grid;
  grid-template-columns: repeat(2, 45px 65px 45px 45px);
  gap: 10px;
}

02 图⽚⽔印⽣成(5 分)

function createWatermark(text, color, deg, opacity, count) {
  // 创建水印容器
  const container = document.createElement('div')
  container.className = 'watermark'

  // TODO: 根据输入参数创建文字水印
  for (let i = 0; i < count; i++) {
    const span = document.createElement('span')
    span.innerText = text
    span.style.color = color
    span.style.transform = `rotate(${deg}deg)`
    span.style.opacity = opacity
    container.appendChild(span)
  }
  return container
}

03 收集帛书碎⽚(10 分)

function collectPuzzle(...puzzles) {
  // TODO: 在这里写入具体的实现逻辑
  return Array.from(new Set(puzzles.flat()))
}

04 ⾃适应⻚⾯(10 分)

@media (max-width: 800px) {
  .menu {
    height: 54px;
    line-height: 54px;
    margin-bottom: 25px;
  }

  .icon-menu {
    color: #a0a0a0;
    margin-left: 20px;
    display: inline-block !important;
  }

  .icon-menu:hover {
    color: white;
    cursor: pointer;
  }

  .collapse {
    display: none;
  }

  input[type='checkbox']:checked ~ .collapse {
    display: flex;
    flex-direction: column;
    background-color: #252525;
  }

  .dropdown:hover ul {
    display: flex;
    flex-direction: column;
  }

  .row {
    margin-top: 20px;
    display: flex;
    flex-wrap: wrap;
  }

  .box {
    margin-bottom: 15px;
  }

  #tutorials img {
    margin: 0;
  }
}

05 外卖给好评(15 分)

<template>
  <div class="block">
    <span class="demonstration">请为外卖评分:</span>
    <ul class="rate-list">
      <li>
        <!-- TODO: 补全 el-rate 属性 -->
        送餐速度:
        <el-rate @change="changeScore" show-score="true" v-model="speed"></el-rate>
      </li>
      <li>
        <!-- TODO: 补全 el-rate 属性 -->
        外卖口味:
        <el-rate @change="changeScore" show-score="true" v-model="flavour"></el-rate>
      </li>
      <li>
        <!-- TODO: 补全 el-rate 属性 -->
        外卖包装:
        <el-rate @change="changeScore" show-score="true" v-model="pack"></el-rate>
      </li>
    </ul>
  </div>
</template>
<script>
module.exports = {
  data() {
    return {
      speed: 0, // 送餐速度
      flavour: 0, // 外卖口味
      pack: 0, // 外卖包装
    }
  },
  /* TODO:待补充代码 */
  methods: {
    changeScore() {
      if (this.speed && this.flavour && this.pack) {
        this.$emit('change', {
          speed: this.speed,
          flavour: this.flavour,
          pack: this.pack,
        })
      }
    },
  },
}
</script>

06 视频弹幕(15 分)

发送按钮点击事件

document.querySelector('#sendBulletBtn').addEventListener('click', () => {
  // TODO:点击发送按钮,输入框中的文字出现在弹幕中
  bulletConfig.value = document.querySelector('#bulletContent').value
  renderBullet(bulletConfig, videoEle, true)
})

renderBullet()

/**
 * @description 根据 bulletConfig 配置在 videoEle 元素最右边生成弹幕,并移动到最左边,弹幕最后消失
 * @param {Object} bulletConfig 弹幕配置
 * @param {Element} videoEle 视频元素
 * @param {boolean} isCreate 是否为新增发送的弹幕,为 true 表示为新增的弹幕
 *
 */
function renderBullet(bulletConfig, videoEle, isCreate = false) {
  const spanEle = document.createElement('SPAN')
  spanEle.classList.add(`bullet${index}`)
  if (isCreate) {
    spanEle.classList.add('create-bullet')
  }
  // TODO:控制弹幕的显示颜色和移动,每隔 bulletConfig.time 时间,弹幕移动的距离  bulletConfig.speed
  // 解构配置对象
  const { isHide, speed, time, value } = bulletConfig
  // 获取视频容器的宽度和高度
  const { width, height } = getEleStyle(videoEle)
  // 给元素添加样式
  spanEle.innerText = value
  spanEle.style.color = `#${getRandomNum(999999)}`
  spanEle.style.position = 'absolute'
  spanEle.style.top = `${getRandomNum(height)}px`
  spanEle.style.left = `${width}px`
  spanEle.style.display = isHide ? 'none' : 'block'
  // 将配置好的SPAN标签追加到视频容器中
  videoEle.appendChild(spanEle)
  // 控制弹幕移动
  let left = width
  const timer = setInterval(() => {
    left -= speed
    spanEle.style.left = `${left}px`
    if (left <= -spanEle.clientWidth) {
      videoEle.removeChild(spanEle)
      clearInterval(timer)
    }
  }, time)
}

07 ISBN 转换与⽣成(20 分)

// 将用户输入的带分隔符的 isbn 字符串转换只有纯数字和大写 X 字母的字符串
// 入参 str 为转换为包含任意字符的字符串
function getNumbers(str) {
  // TODO: 待补充代码
  return str == '' ? '' : str.match(/\d|X/g).join('')
}

// 验证当前 ISBN10 字符串是否有效
// 入参 str 为待判断的只有纯数字和大写 X 字母的字符串
function validISBN10(str) {
  // TODO: 待补充代码
  const reg = new RegExp(/\d{10}|(\d{9}X{1})/)
  if (!reg.test(str)) return false
  const num = str.split('')
  if (num.length != 10) return false // 上面那个正则有问题,这里加个判断
  const sum = num.slice(0, 9).reduce((prev, cur, index) => prev + Number(cur) * ++index, 0)
  const remainder = sum % 11
  const contrast = num[9] == 'X' ? 10 : num[9]
  return contrast == remainder
}

// 将用户输入的 ISBN-10 字符串转化为 ISBN-13 字符串
// 入参 isbn 为有效的 ISBN-10 字符串
function ISBN10To13(isbn) {
  // TODO: 待补充代码
  const str = '978' + isbn.substring(0, 9)
  const sum = str.split('').reduce((prev, cur) => {
    cur = Number(cur) // 转数字格式
    return (prev += cur % 2 == 0 ? cur * 3 : cur)
  }, 0)
  const remainder = sum % 10
  const verify = remainder == 0 ? 0 : 10 - remainder
  return str + verify
}

08 全球新冠疫情数据统计(20 分)

<div id="app">
    <header>
      <div>全球新冠疫情数据统计</div>
    </header>
    <main>
      <!-- TODO: 请修改以下代码实现不同国家的数据展示功能 -->
      <div class="title">
        <h2>{{curCountry}}</h2>
      </div>
      <div class="boxes">
        <div class="box1">
          <h3>确诊</h3>
          <div class="number">
            <span class="font-bold">新增:{{newConfirmed}}</span>
          </div>
          <div class="number">
            <span class="font-bold">总计:{{totalConfirmed}}</span>
          </div>
        </div>
        <div class="box2">
          <h3>死亡</h3>
          <div class="number">
            <span class="font-bold">新增:{{newDeaths}}</span>
          </div>
          <div class="number">
            <span class="font-bold">总计:{{totalDeaths}}</span>
          </div>
        </div>
      </div>
      <select v-model="selectCountry">
        <option :value="0">Select Country</option>
        <option v-for="item in data" :value="item">{{item.Country}}</option>
        <!-- 请在此渲染所有国家选项 -->
      </select>
      <div id="chart" style="width: 100%; height: 50vh"></div>
    </main>
</div>
<script>
  var vm = new Vue({
    el: "#app",
    methods: {
      // TODO: 请修改该函数代码实现题目要求
      initChart () {
        // 初始化图表
        this.chart = echarts.init(document.getElementById("chart"));
        this.chartOptions = {
          title: {
            text: "全球感染人数前30国家累计确诊人数统计",
            x: "center",
          },
          tooltip: {
            trigger: "axis",
            axisPointer: {
              type: "shadow",
              label: { show: true },
            },
          },
          // 设置x轴数据
          xAxis: {
            // TODO:这里需要显示国家名称缩写,因为有些国家的全称太长,会导致界面不美观
            data: this.countryCodeList,
            axisLabel: {
              show: true,
              interval: 0,
            },
          },
          yAxis: {
            type: "value",
            name: "确诊数量",
          },
          // 设置y轴数据
          series: [
            {
              // TODO:设置图表中的总确证数
              data: this.totalConfirmedList,
              type: "bar",
              itemStyle: {
                normal: { color: "#a90000" },
              },
            },
          ],
        };
        // 调用此方法设置 echarts 数据
        this.chart.setOption(this.chartOptions);
      },
    },
    // TODO: 请在此添加代码实现组件加载时数据请求的功能
    async mounted () {
      this.data = (await axios.get('./js/covid-data.json')).data
      this.initChart();
    },
    computed: {
      countryCodeList () {
        return this.data.map(e => e.CountryCode)
      },
      totalConfirmedList () {
        return this.data.map(e => e.TotalConfirmed)
      }
    },
    data () {
      return {
        data: [],
        selectCountry: 0,
        newConfirmed: 0,
        totalConfirmed: 0,
        newDeaths: 0,
        totalDeaths: 0,
        curCountry: '请选择国家'
      }
    },
    watch: {
      // 监听下拉列表值,动态切换数据
      selectCountry (val) {
        this.curCountry = val.CountryCode || this.curC
        this.newConfirmed = val.NewConfirmed || 0
        this.totalConfirmed = val.TotalConfirmed || 0
        this.newDeaths = val.NewDeaths || 0
        this.totalDeaths = val.TotalDeaths || 0
      }
    }
  });
</script>

09 ⻣架屏(25 分)

实现思路: 此题主要难点在于 styleclass 的处理,将当前渲染对象传入对应方法,判断返回对象具有的属性

/*
 * 骨架屏渲染组件
 */
let ItemTemplate = ``
// TODO: 请补充完整Template,完成组件代码编写
ItemTemplate += `
<div :class="'ske-'+paragraph.type+'-container'">
  <div v-for="item in arrIs(paragraph)" :class="classIs(item)" :style="styleIs(item)" >
    <item :paragraph="item" :active="active"></item>
  </div>
</div>
`

Vue.component('item', {
  name: 'item',
  template: ItemTemplate,
  props: ['paragraph', 'active'],
  data() {
    return { typeList: ['rect', 'circle'], classPrefix: 'ske ske-', activeClass: ' ske-ani' }
  },
  watch: {},
  methods: {
    // 判断是 rows or cols
    arrIs(obj) {
      if (obj?.rows) return obj.rows
      else if (obj?.cols) return obj.cols
      else return []
    },
    // 判断class类
    classIs(obj) {
      if (this.typeList.includes(obj.type)) {
        return this.classPrefix + obj.type + (this.active ? this.activeClass : '')
      } else {
        return this.classPrefix + obj.type
      }
    },
    // 判断样式
    styleIs(obj) {
      if (obj?.style && obj?.rowStyle) return { ...obj.style, ...obj.rowStyle }
      else if (obj?.style) return obj.style
      else if (obj?.rowStyle) return obj.rowStyle
      else if (obj?.colStyle) return obj.colStyle
      else return {}
    },
  },
})

10 组课神器(25 分)

ajax()

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 dataList = localStorage.getItem('data')
    result = dataList ? JSON.parse(dataList) : (await axios({ url, method })).data.data
  }

  if (method === 'post') {
    // result = (await axios({ url, method, data })).data
    localStorage.setItem('data', JSON.stringify(data))
  }
  return result
}

treeMenusRender()

实现思路: 递归生成DOM树

function treeMenusRender(data, grade = 0) {
  let treeTemplate = ''
  // TODO:根据传入的 treeData 的数据生成树型组件的模板字符串
  grade++
  for (obj of data) {
    treeTemplate +=
      grade === 3
        ? `<div class="tree-node" data-index="${obj.id}" data-grade="${grade}">
            <div class="tree-node-content" style="margin-left: 30px">
              <div class="tree-node-content-left">
                <img src="./images/dragger.svg" alt="" class="point-svg" />
                <span class="tree-node-tag">${obj.tag}</span>
                <span class="tree-node-label">${obj.label}</span>
              </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>`
        : `<div class="tree-node" data-index="${obj.id}" data-grade="${grade}">
            <div class="tree-node-content" style="margin-left: ${grade === 2 && 15}px">
              <div class="tree-node-content-left">
                <img src="./images/dragger.svg" alt="" class="point-svg" />
                <span class="tree-node-label">${obj.label}</span>
                <img class="config-svg" src="./images/config.svg" alt="" />
              </div>
            </div>`

    if (obj?.children) treeTemplate += `<div class="tree-node-children">${treeMenusRender(obj.children, grade)}</div>`
    treeTemplate += `</div>`
  }
  return treeTemplate
}

treeDataRefresh()

实现思路: 利用字符串替换的方式,将拖动的元素插入到对应节点,最后将替换完成的字符串转JSON对象重新赋值给 treeData

  • 首先将 treeData 转字符串
  • 调用 getDragElement() 方法,将被拖拽的元素对象和放入元素对象转字符串
  • 根据题目要求对 treeDataStr 进行替换
  • treeDataStr 转JSON对象并赋值给 treeData
function treeDataRefresh({ dragGrade, dragElementId }, { dropGrade, dropElementId }) {
  if (dragElementId === dropElementId) return
  // TODO:根据 `dragElementId, dropElementId` 重新生成拖拽完成后的树型组件的数据 `treeData`
  let dragStr = JSON.stringify(getDragElement(treeData, dragElementId))
  let dropStr = JSON.stringify(getDragElement(treeData, dropElementId))
  let treeDataStr = JSON.stringify(treeData)
  if (dragGrade === dropGrade) {
    treeDataStr = treeDataStr.replace(dragStr, '')
    treeDataStr = treeDataStr.replace(dropStr, dropStr + ',' + dragStr)
  }
  if (dragGrade - dropGrade == 1) {
    if (dropStr.includes(dragStr)) dropStr = dropStr.replace(dragStr, '')
    const newDragStr = `${dragStr},`
    const newDropStr = dropStr.replace('[', '[' + newDragStr)
    treeDataStr = treeDataStr.replace(dragStr, '')
    treeDataStr = treeDataStr.replace(dropStr, newDropStr)
  }
  // 处理多余字符
  treeDataStr = treeDataStr.replace(',,', ',').replace('[,', '[').replace(',]', ']')
  treeData = JSON.parse(treeDataStr)
}
getDragElement()

通过传入的 id 获取被拖拽的节点对象

function getDragElement(data, id) {
  for (const obj of flatObj(data)) {
    if (obj.id == id) return obj
  }
}
flatObj()

此方法将 treeData 扁平化

function flatObj(obj) {
  return obj.reduce((prev, cur) => {
    prev = [...prev, cur]
    if (cur?.children) prev = [...prev, ...flatObj(cur.children)]
    return prev
  }, [])
}
  • 6
    点赞
  • 36
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值