第十四届蓝桥Web国赛题解

49 阅读5分钟

大学组

一、植物灌溉——CSS:Grid

注意 grid-area 属性值,grid-row-end 和 grid-column-end 是结束行/列,所以需要加 1

style.css

/* TODO 待补充代码 */
.treatment {
  grid-area: 1 / 2 / 4 / 6;
}

二、萌宠小玩家——JS:操作DOM

注意 vail_name 元素是 span

index.js

verifyName() {
  // TODO:  待补充代码
  if (nickname.value) {
    this.name = nickname.value;
    vail_name.style.display = "none";
  } else {
    vail_name.style.display = "inline";
  }
}

showLog(record) {
  // TODO:  待补充代码 
  this.logList.unshift(record);
  list.innerHTML = '';
  this.logList.forEach(item => {
    list.innerHTML += `
      <div>${item}</div>
    `
  });
}

三、element ui 轮播图二次封装——Vue2 + element ui

my-carousel.vue

el-carousel indicator-position="none" @change="onCarouselChange" ref="carousel" arrow="never" height="85vh">
  <el-carousel-item v-for="image in images" :key="image">
    <img :src="image" />
  </el-carousel-item>
</el-carousel>
<ul class="points">
  <!-- TODO: 请修改以下代码实现指示点的正确渲染 -->
  <li class="point" @click="changePoint(index)" :class="{ 'active': activeIndex === index }" v-for="(item, index) in images" :key="index"></li>
</ul>

// TODO: 请在下面补充代码实现指示点的正确渲染以及点击指示点切换图片的功能
data() {
  return {
    activeIndex: 0,
  }
},
methods: {
  onCarouselChange(index) {
    this.activeIndex = index
  },
  changePoint(index) {
    this.activeIndex = index
    this.$refs.carousel.setActiveItem(index)
  }
}

四、抢红包啦——JS

注意点:

  1. 随机金额范围我开始是自己取的10,因为题目貌似没有提示,但是错了,后来想了一下应该得与剩下的金额与个数相关,就过了,仔细想貌似有点道理但不多
  2. 最小金额为 0.01

randomAllocation.js

function randomAllocation(total, n) {
  //  TODO: 待补充代码
  let arr = [];
  while (n--) {
    if (n === 0) {
      arr.push(Number(total.toFixed(2)));
      break;
    }
    let newRed = Number((Math.random() * parseInt(total / n)).toFixed(2));
    if (newRed < 0.01) newRed = 0.01
    arr.push(newRed);
    total -= newRed;
  }
  return arr;
}

五、讨论区——CSS

这种题碰到了直接跳过,很ex

六、github contribution——Echarts

这种题不熟的话需要多尝试,就是觉得可能的情况下各个写法多试,比如这题第二问的 calendar 对象中的 itemStyle 对象的 borderWidth 属性值,题目说的是 border 宽为 1px,我一开始写 '1px' 不对,然后想到前面的 cellSize 值是 number 型,于是改成 1。第三问提示说参数为数组或对象,我们可直接用的数据就只有第一问的 data 数组,于是作为参数打印看一下,再根据打印结果调整

index.js

axios.get('./data.json')
  .then(res => {
    data = res.data
    render(data);
  })

calendar: {
  range: "2022",
  top: 80,
  left: "center",
  cellSize: 15,
  itemStyle: {
    color: '#ebedf0',
    borderWidth: 1
  },
  splitLine: {
    show: false
  },
  yearLabel: {
    show: false
  }
},
...
series: {
  type: "heatmap",
  coordinateSystem: "calendar",
  data: data.map(item => Object.values(item))
},
//TODO:待补充自定义 tooltip 代码
tooltip: {
  formatter: (data) => {
    return `
      <div id="tooltip">
        当前日期:${data.data[0]}
        提交次数:${data.data[1]}
      </div>
    `
  }
}

七、本文查重小能手——JS

第三问我感觉题目没有说清楚,标点有很多但是没有具体说有哪些,我看很多题解都是自己写了几个标点过了,我觉得不够严谨,用汉字正则表达式除去非汉字的字符我认为更好,但是一般题目会给的,所以我得把汉字正则给记下来最好

index.js

// TODO:待补充代码
// 请求文章数据并在右侧文本框 id = compareText 中元素默认显示第一篇文章数据
fetch('data.json')
  .then(res => res.json())
  .then(data => {
    articles = data.data
    compareText.value = articles.articleOne
  })
// TODO:待补充代码
// id = articleSelect 下拉框改变时显示对应的文章
articleSelect.addEventListener('change', (e) => {
  compareText.value = articles[e.target.value]
})

checkPlagiarism.js

function wordSegmentation(words) {
  // TODO:待补充代码
  words = words.replaceAll(/[^\u4e00-\u9fa5]/g, ' ')
  stopWords.forEach(item => {
    words = words.replaceAll(item, ' ')
  })
  return words.split(' ').filter(item => item !== '')
}

八、找到未引用的图片——NodeJS:fs

注意点:

  1. 通过 searchImage() 方法返回的图片路径数组需要处理为文件名,方便后续筛选

index.js

const findUnlinkImages = async function () {
  let unlinkImages = []; // 未被任何 md 文件引用的图片的数组
  // TODO 请通过 Node.js 在此处继续完成代码编写
  const articles = await traversalDir(articlesPath)
  const images = await traversalDir(imagesPath)
  let arr = []
  for (const item of articles) {
    const data = fs.readFileSync(`${articlesPath}/${item}`)
    arr.push(...searchImage(data).map(i => i.split('/')[2]).filter(item => !arr.includes(item)))
  }
  unlinkImages = images.filter(item => !arr.includes(item))
  return unlinkImages; // 此处应返回一个数组,如不明白,请仔细阅读题目
};

九、表单生成器——Vue2

这题我做了很久,感觉是个错题,因为我连效果图的基本数据都没正常渲染就过了,就不放代码了(虽然是我在题解区看了很久)

十、恶龙与公主——JS

  1. 第一问:二维数组按顺时针遍历,就是来回→↓←↑,我采用的方法是定义上下左右四个边界初始值,每走完一次方向就更新对应边界值(即缩短)并且判断是否走完
  2. 第二问:貌似走到最后越界报错了的情况不用管

index.js

mazePath(arr) {
    // TODO:得到起点到终点经过的每个 box 元素的 data-index 的值并依次保存在数组中
    let newArr = []
    let up = 0
    let down = arr.length - 1
    let left = 0
    let right = arr.length - 1
    let total = arr.length * arr.length
    while (newArr.length < total) {
        for (let j = left; j <= right; j++) {
            newArr.push(arr[up][j])
        }
        up++
        if (newArr.length === total) break
        for (let i = up; i <= down; i++) {
            newArr.push(arr[i][right])
        }
        right--
        if (newArr.length === total) break
        for (let j = right; j >= left; j--) {
            newArr.push(arr[down][j])
        }
        down--
        if (newArr.length === total) break
        for (let i = down; i >= up; i--) {
            newArr.push(arr[i][left])
        }
        left++
        if (newArr.length === total) break
    }
    return newArr
},
moveHandler() {
    let step = this.element.gameStep.value = this.getStep(this.gameData.step);
    // TODO:根据点击营救后获得的点数,正确到达指定位置调用bloodCalculator函数计算当前血量,到达公主处调用 tipRender 函数,每步的时间间隔在 200ms内(大于此时间会导致判题失败)。
    const boxs = Array.from(this.element.boxs)
    const timer = setInterval(() => {
        step--
        this.gameData.curPos++
        const curPos = this.gameData.pathArr[this.gameData.curPos]
        const box = boxs.find(box => box.dataset.index == curPos);
        document.querySelector('.active').classList.remove('active')
        box.classList.add('active')
        if (step === 0) {
            this.bloodCalculator(box)
            if (box.dataset.index === 'end') {
                this.tipRender('success')
            }
            clearInterval(timer)
        }
    }, 200)
},

职业组

四、版本比较器——JS

  1. 分割转为数字型数组好比较大小,不过判断格式有点麻烦了些。
  2. map方法传入Number可以将字符型转为数字型,这样更简便

index.js

function compareVersion(version1, version2) {
    //TODO:待补充代码
    let v1 = version1.split('.').map(Number)
    let v2 = version2.split('.').map(Number)
    if (v1.length !== 3 || v1.includes(NaN) || v2.length !== 3 || v2.includes(NaN)) return 'error'
    if (v1.some(item => item < 0) || v2.some(item => item < 0)) return 'error'
    for (let i = 0; i < 3; i++) {
        if (v1[i] < v2[i]) return -1
        if (v1[i] > v2[i]) return 1
        if (i === 2) return 0
    }
}

优化

用正则判断格式,另外相等的情况也无需判断

function compareVersion(version1, version2) {
    //TODO:待补充代码
    const reg = /^\d+\.\d+\.\d+$/
    if (!reg.test(version1) || !reg.test(version2)) return 'error'
    let v1 = version1.split('.').map(Number)
    let v2 = version2.split('.').map(Number)
    for (let i = 0; i < 3; i++) {
        if (v1[i] < v2[i]) return -1
        if (v1[i] > v2[i]) return 1
    }
    return 0
}

七、图片验证码——Vue2

注意点

  1. 第一问 比较好的做法是给图片数组添加一个新的属性标志是否选择,我直接添加用不了,然后用一个新的数组在 原数组的基础上添加一个属性

index.html

<div class="box" v-if="!success">
  <div class="checkbox" @click="startCheck"></div>
  <span>我是人类</span>
</div>
<button id="login" type="submit" v-else>登录</button>

<div class="image-container" @click="image.isSelected = !image.isSelected" :class="{ 'selected': image.isSelected }" v-for="(image, index) in realImages">
  <img :src="image.path">
</div>

<button id="check-btn" @click="check">{{ selectCount === 0 ? '跳过' : '提交' }}</button>

data: {
  success: false,
  realImages: [],
  ...
},
computed: {
  ...
  selectCount() {
    return this.realImages.filter(item => item.isSelected).length
  }
},
methods: {
  // 展示验证图像模态框,随机选择一个问题类型和九张图片
  startCheck() {
    ...
    this.realImages = []
    this.images.forEach(item => {
      this.realImages.push({ ...item, isSelected: false })
    })
  },
  // TODO: 请修改以下代码实现用户选择正确与否的判断逻辑,可以添加新的方法
  check() {
    let result = true;
    this.realImages.forEach(item => {
      if (item.type && !item.isSelected || !item.type && item.isSelected) result = false
    })
    // 根据用户选择的正确与否展示相应的toast
    if (result) {
      this.toastContent = '验证成功'
      this.showModal = false
      this.success = true
    } else {
      this.toastContent = '验证失败,请重试'
    }
  },
},