第十五届蓝桥杯(Web 应用开发)模拟赛 2 期-大学组(详细分析解答)

目录

1.相不相等

[1.1 题目要求](#1.1 题目要求)

[1.2 题目分析](#1.2 题目分析)

[1.3 源代码](#1.3 源代码)

2.三行情书

[2.1 题目要求](#2.1 题目要求)

[2.2 题目分析](#2.2 题目分析)

[2.3 源代码](#2.3 源代码)

3.电影院在线订票

[3.1 题目要求](#3.1 题目要求)

[3.2 题目分析](#3.2 题目分析)

[3.3 源代码](#3.3 源代码)

4.老虎坤(不然违规发不出来)

[4.1 题目要求](#4.1 题目要求)

[4.2 题目分析](#4.2 题目分析)

[4.3 源代码](#4.3 源代码)

5.星际通讯

[5.1 题目要求](#5.1 题目要求)

[5.2 题目分析](#5.2 题目分析)

[5.3 源代码](#5.3 源代码)

6.蓝桥杯排位

[6.1 题目要求](#6.1 题目要求)

[6.2 题目分析](#6.2 题目分析)

7.拼出一个未来

8.超能英雄联盟

9.实时展示权限日志

10.账户验证


刚做完第一期的模拟题目,第二期又开始发布了,花了点时间做完了,分享下自己的解题思路。

1.相不相等

1.1 题目要求

请你编写一个名为 expectFn 的函数,用于帮助开发人员测试他们的代码。它可以通过参数 val 接受任何值,并返回一个对象,该对象包含下面两个函数:

  • toBe(val):接受另一个值并在两个值相等( === )时返回 true 。如果它们不相等,则返回 "Not Equal" 。
  • notToBe(val):接受另一个值并在两个值不相等( !== )时返回 true 。如果它们相等,则返回 "Equal" 。

1.2 题目分析

题目要求写的很清楚了,按照其要求做判断条件即可

1.3 源代码

javascript 复制代码
var expectFn = function (val) {
  // TODO
  return {
    toBe: function (value) {
      if (val === value) {
        return true
      } else {
        return 'Not Equal'
      }
    },
    notToBe: function (value) {
      if (val !== value) {
        return true
      } else {
        return 'Equal'
      }
    }
  }
}

2.三行情书

2.1 题目要求

请完善 style.css 的 TODO 部分,具体要求如下:

  1. 让第一行标题即 .content span 标签中的文字单行显示,多余溢出显示省略号。
  2. 请使用 -webkit-box 语法使得下面的文字即 .content p 标签里的内容显示三行,多余溢出显示省略号。

2.2 题目分析

这里主要是如果写过案例的话,应该就知道怎么写,需要注意的是span元素是单行元素,要想达到效果,需要将其变为块级元素。

2.3 源代码

css 复制代码
span {
    font-size: 20px;
    color: #837362;
    /* TODO:补充下面的代码 */
    /* 单行显示,多余溢出省略号 */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    display: block;
    /* 使得溢出部分显示省略号 */
}

p {
    color: #837362;
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 3;
    /* 显示的行数 */
    overflow: hidden;
}

3.电影院在线订票

3.1 题目要求

  1. 实现异步数据读取和渲染功能,使用 axios 请求 ./data.json(必须使用该路径请求,否则可能会请求不到数据)的数据。
    • 将电影名字 name 渲染到 id 为 movie-name 的节点中。
    • 将电影售价 price 渲染到 id 为 movie-price 的节点中。
    • 将座位信息 seats 渲染到 id 为 seat-area 的节点中,二维数组中每一个子数组代表一行,0 代表没有被他人占位,1 代表已经被订购。
  2. 实现座位选择和总价计算的功能:
    • 被别人订过的座位不能再被选择。
    • 座位被选中后,状态更新,为相应的座位添加选中样式(即 selected),并更新已选择的座位数和总价。
    • 自己所选择的座位可以被取消,并更新已选择的座位数和总价。

3.2 题目分析

  • 第一点是使用axios发送请求获取到数据,这个不会的可以参考一下axios官网的案例学习一下
  • 第二个是根据拿到的数据渲染html的结构,这个的就是创建对应的html结构,最后添加到对应的节点。
  • 第三个是实现作为选择的功能,实现思路就是给所有的座位都绑定一个点击事件,通过事件对象身上是否被选中来替换类名,然后就是渲染数量。

3.3 源代码

javascript 复制代码
/* TODO: 
      1. 完成数据请求,生成电影名,价格以及座位情况
      2. 绑定点击事件,实现订票功能
 */

let data = {}
axios
  .get('../data.json')
  .then((res) => {
    console.log(res)
    data = res.data
    movieNameNode.innerHTML = data.name
    moviePriceNode.innerHTML = data.price
    //创建节点渲染数据
    data.seats.forEach((item) => {
      let row = document.createElement('div')
      row.className = 'row'
      item.forEach((item) => {
        let seat = document.createElement('div')
        seat.className = 'seat'
        row.appendChild(seat)
        if (item) {
          seat.classList.add('occupied')
        }
      })
      seatAreaNode.appendChild(row)
    })
  })
  .catch((err) => {
    console.log(err)
  })

// 获取座位区域节点
const seatAreaNode = document.getElementById('seat-area')
// 获取电影名节点
const movieNameNode = document.getElementById('movie-name')
// 获取电影票价节点
const moviePriceNode = document.getElementById('movie-price')
// 获取已订电影票数量节点
const count = document.getElementById('count')
// 获取已订电影票总价节点
const total = document.getElementById('total')

// 获取所有座位节点
const seatNodes = document.querySelectorAll('.seat')
// 初始化已选择座位数和总价
let selectedSeatsCount = 0
let totalPrice = 0

// 监听座位点击事件
seatAreaNode.addEventListener('click', (event) => {
  const clickedSeat = event.target

  // 检查是否点击的是座位
  if (clickedSeat.classList.contains('seat') && !clickedSeat.classList.contains('occupied')) {
    // 切换座位的选中状态
    clickedSeat.classList.toggle('selected')

    // 更新已选择座位数和总价
    if (clickedSeat.classList.contains('selected')) {
      selectedSeatsCount++
      totalPrice += data.price
    } else {
      selectedSeatsCount--
      totalPrice -= data.price
    }

    // 更新显示
    updateDisplay()
  }
})

// 更新显示函数
function updateDisplay() {
  count.textContent = selectedSeatsCount
  total.textContent = totalPrice 
}

4.老虎坤(不然违规发不出来)

4.1 题目要求

找到 js/index.js 中的 GetResult 函数,完成此函数,实现以下目标:

点击开始后,可以通过 GetResult的三个参数 r1r2r3 计算出滚动后每一列图片的停留位置。当最后停留的图片都相同时,意味着玩家中了大奖!文字框(class = textPanel)显示"恭喜你,中奖了",否则显示:"很遗憾,未中奖"。

参数介绍:r1r2r3 表示的是三列元素下的 li 的最后停留位置,分别对应第一列(id=sevenFirst)、第二列(id=sevenSecond)、第三列(id=sevenThird)。以第一列为例,最终显示的元素是 sevenFirst 下的第 rli 元素。请使用显示的 li 元素的 data-point 属性判断三张图片是否相同。当 data-point 属性对应的值相同时,说明这三张图片相同。

4.2 题目分析

这一题很明确的考察点就是获取元素身上的属性,需要用到getAttribute()方法,通过获取到的属性对比是否一样,如果三个相同则中奖。

4.3 源代码

javascript 复制代码
if (sevenFirst.children[r1 - 1].getAttribute('data-point') == sevenSecond.children[r2 - 1].getAttribute('data-point') 
&& sevenFirst.children[r1 - 1].getAttribute('data-point') == sevenThird.children[r3 - 1].getAttribute('data-point')) {
      textPanel.innerHTML = '恭喜你,中奖了'
    } else {
      textPanel.innerHTML = '很遗憾,未中奖'
    }

5.星际通讯

5.1 题目要求

完善 index.js 中的 translate 函数,完善其中的 TODO 部分:

translate 函数接收一个字符串参数 alienMessage,其中包含一系列外星人的密文。函数根据特定的翻译规则将密文翻译成人类语言,并返回翻译后的结果字符串。外星人密文翻译规则存放在 codonTable 变量中。

5.2 题目分析

这个题目就是js的切割 遍历

5.3 源代码

javascript 复制代码
// 密语规则
const codonTable = {
  IIX: '人类',
  VII: '哈喽',
  III: '你好',
  IXI: '赞',
  XVI: '嗨',
  CUV: '打击',
  XII: '夜晚',
  IVI: '我',
  XIC: '想',
  XIV: '交个朋友',
  VIX: '月亮',
  XCI: '代码',
  XIX: '祈福',
  XVI: '和',
  XXI: 'stop'
}

/**
 * @param {string} alienMessage 外星人的密文
 * @return {string}  翻译结果
 */
const translate = (alienMessage) => {
  // TODO:待补充代码
  // 检查密文是否为空
  if (!alienMessage) {
    return ''
  }

  // 切分密文
  const codons = []
  for (let i = 0; i < alienMessage.length; i += 3) {
    codons.push(alienMessage.slice(i, i + 3))
  }
  // 初始化翻译结果
  let translation = ''

  // 遍历密文
  for (const codon of codons) {
    // 检查是否为停止符号
    if (codon === 'XXI') {
      break
    }

    // 查找翻译表
    const translationResult = codonTable[codon]

    // 如果找到翻译结果,则添加到最终结果中
    if (translationResult) {
      translation += translationResult
    } else {
      // 未找到对应翻译结果,返回无效密语
      return '无效密语'
    }
  }
  return translation
}
// 请注意:以下代码用于检测,请勿删除。
try {
  module.exports = translate
} catch (e) {}

6.蓝桥杯排位

6.1 题目要求

  1. 根据请求的数据正确完成左侧热力地图。
  2. 右侧战力榜中柱形图中,根据 power 字段的值对所有学校进行排序,取出排在前 10 名的学校,从左到右降序排列。
  3. 完成数据请求(数据来源 ./mock/map.json),map.json 中存放的数据为省市对应的学校数据

6.2 题目分析

根据要求获取数据,然后替换options中的配置即可

javascript 复制代码
const { createApp, ref, onMounted } = Vue
const app = createApp({
  setup() {
    const chartsData = ref([])
    onMounted(() => {
      // TODO:待补充代码 请求数据,并正确渲染柱形图和地图
      axios
        .get('../mock/map.json')
        .then((res) => {
          chartsData.value = res.data
          showChartBar()
          showChinaMap()
        })
        .catch((err) => {
          console.log(err)
        })
    })
    // 展示柱状图
    const showChartBar = () => {
      const myChart = echarts.init(document.getElementById('chart'))

      let data = chartsData.value.map((item, index) => {
        return item.school_power
      })
      console.log(data)
      let result = data.flat(1).sort((a, z) => {
        return z.power - a.power
      })
      let arr = result.slice(0, 10)
      let school = arr.map((item) => {
        return item.name
      })
      let power = arr.map((item) => {
        return item.power
      })
      console.log(school)
      console.log(power)
      // 指定配置和数据
      const option = {
        xAxis: {
          type: 'category',
          axisLabel: { interval: 0, rotate: 40 },
          // TODO:待修改  柱状图 x 轴数据 -> 前 10 学校名称
          data: school
        },
        grid: {
          left: '3%',
          right: '4%',
          bottom: '3%',
          containLabel: true
        },
        yAxis: {
          type: 'value',
          boundaryGap: [0, 0.01]
        },
        series: [
          {
            // TODO:待修改   柱状图 y 轴数据->学校战力值
            data: power,
            type: 'bar',
            showBackground: true,
            backgroundStyle: {
              color: 'rgba(180, 180, 180, 0.2)'
            },
            itemStyle: {
              color: '#8c7ae6'
            }
          }
        ]
      }

      // 把配置给实例对象
      myChart.setOption(option)
      // 根据浏览器大小切换图表尺寸
      window.addEventListener('resize', function () {
        myChart.resize()
      })
    }

    // 展示地图
    const showChinaMap = () => {
      const chinaMap = echarts.init(document.getElementById('chinaMap'))

      // 进行相关配置
      const mapOption = {
        tooltip: [
          {
            backgroundColor: '#fff',
            subtext: 'aaa',
            borderColor: '#ccc',
            padding: 15,
            formatter: (params) => {
              return params.name + '热度值:' + params.value + '<br>' + params.data.school_count + '所学校已加入备赛'
            },
            textStyle: {
              fontSize: 18,
              fontWeight: 'bold',
              color: '#464646'
            },
            subtextStyle: {
              fontSize: 12,
              color: '#6E7079'
            }
          }
        ],
        geo: {
          // 这个是重点配置区
          map: 'china', // 表示中国地图
          label: {
            normal: {
              show: false // 是否显示对应地名
            }
          },
          itemStyle: {
            normal: {
              borderColor: 'rgb(38,63,168)',
              borderWidth: '0.4',
              areaColor: '#fff'
            },
            emphasis: {
              //鼠标移入的效果
              areaColor: 'rgb(255,158,0)',
              shadowOffsetX: 0,
              shadowOffsetY: 0,
              shadowBlur: 20,
              borderWidth: 0,
              shadowColor: 'rgba(0, 0, 0, 0.5)'
            }
          }
        },
        visualMap: {
          show: true,
          left: 'center',
          top: 'bottom',
          type: 'piecewise',
          align: 'bottom',
          orient: 'horizontal',
          pieces: [
            {
              gte: 80000,
              color: 'rgb(140,122,230)'
            },
            {
              min: 50000,
              max: 79999,
              color: 'rgba(140,122,230,.8)'
            },
            {
              min: 30000,
              max: 49999,
              color: 'rgba(140,122,230,.6)'
            },
            {
              min: 10000,
              max: 29999,
              color: 'rgba(140,122,230,.4)'
            },
            {
              min: 1,
              max: 9999,
              color: 'rgba(140,122,230,.2)'
            }
          ],
          textStyle: {
            color: '#000',
            fontSize: '11px'
          }
        },
        series: [
          {
            type: 'map',
            geoIndex: 0,
            // TODO:待修改 地图对应数据
            data: chartsData.value.map((item) => {
              return {
                name: item.name,
                school_count: item.school_count,
                value: item.value
              }
            })
          }
        ]
      }

      // 把配置给实例对象
      chinaMap.setOption(mapOption)
    }

    return {
      chartsData,
      showChartBar,
      showChinaMap
    }
  }
})

app.mount('#app')

7.拼出一个未来

题目要求和分析就不写了 ,代码中给了注释

javascript 复制代码
// 声明一个数组,包含了所有的拼图块数据
var puzzlePieces = [
  { src: './images/img1.png', id: 1 },
  { src: './images/img2.png', id: 2 },
  { src: './images/img3.png', id: 3 },
  { src: './images/img4.png', id: 4 },
  { src: './images/img5.png', id: 5 },
  { src: './images/img6.png', id: 6 },
  { src: './images/img7.png', id: 7 },
  { src: './images/img8.png', id: 8 },
  { src: './images/img9.png', id: 9 }
]

// 定义一个打乱数组的函数
function shuffleArray(array) {
  for (let i = array.length - 1; i > 0; i--) {
    const j = Math.floor(Math.random() * (i + 1))
    ;[array[i], array[j]] = [array[j], array[i]]
  }
  return array
}

// 使用定义的函数打乱拼图块数组
puzzlePieces = shuffleArray(puzzlePieces)

// 获取拼图容器元素
var container = document.getElementById('puzzle-container')

// 遍历拼图块数据数组
puzzlePieces.forEach(function (pieceData) {
  // 创建一个新的拼图块元素
  var piece = document.createElement('div')
  piece.classList.add('puzzle-piece')
  piece.setAttribute('draggable', 'true')

  // 创建一个新的图片元素
  var image = document.createElement('img')
  image.src = pieceData.src
  image.dataset.id = pieceData.id

  // 将图片元素添加到拼图块元素中
  piece.appendChild(image)

  // 将拼图块元素添加到父容器元素中
  container.appendChild(piece)
})

// 获取所有的拼图块元素,并转换为数组
const puzzleArray = Array.from(document.querySelectorAll('.puzzle-piece'))

// 获取成功消息元素
const successMessage = document.getElementById('success-message')

// 为每个拼图块元素添加拖拽事件监听器
puzzleArray.forEach((piece) => {
  piece.addEventListener('dragstart', dragStart)
  piece.addEventListener('dragover', dragOver)
  piece.addEventListener('drop', drop)
})

// 声明一个变量用来保存正在拖动的拼图块
let draggedPiece = null

// 定义开始拖动事件的处理函数
function dragStart(event) {
  draggedPiece = this
  event.dataTransfer.setData('text/plain', null)
}

// 定义在拖动过程中的处理函数,阻止默认行为
function dragOver(event) {
  event.preventDefault()
}

// 定义拖放事件的处理函数
function drop(event) {
  // 检查是否拖动的拼图块不是当前目标拼图块
  // draggedPiece 被拖动的拼图块元素。this 目标位置的拼图块元素。
  let num = 0
  if (draggedPiece !== this) {
    // TODO:待补充代码
    // 交换图片的 src 属性和 data-id 属性
    let tempSrc = draggedPiece.querySelector('img').src
    let tempDataId = draggedPiece.querySelector('img').dataset.id

    draggedPiece.querySelector('img').src = this.querySelector('img').src
    draggedPiece.querySelector('img').dataset.id = this.querySelector('img').dataset.id

    this.querySelector('img').src = tempSrc
    this.querySelector('img').dataset.id = tempDataId

    // 检查是否拼图成功
    puzzleArray.forEach((item, index) => {
      if (parseInt(item.children[0].getAttribute('data-id')) === index + 1) {
        num++
      }
    })
    if (num === 9) {
      successMessage.classList.remove('hide')
      successMessage.classList.add('show')
    } else {
      successMessage.classList.remove('show')
      successMessage.classList.add('hide')
    }
    // 重置正在拖动的拼图块
    draggedPiece = null
  }
}

8.超能英雄联盟

HeroList:

javascript 复制代码
// TODO:补全代码,实现目标效果
const HeroList = {
  template: `
  <div class="hero-list">
    <h2>可选英雄</h2>
    <ul>
      <li class="hero-item" v-for="(item,index) in store.heroes" :key="item.id">
        <span>{{item.name}}</span>
        <span>{{item.ability}}</span>
        <span>{{item.strength}}</span>
        <button @click=store.add(item.id) :disabled="item.btn">{{ item.btn ?  '已添加' : '添加至队伍' }}</button>
      </li>
    </ul>
  </div>
  `,
  setup() {
    //第一步获取数据
    const store = useHeroStore()
    axios
      .get('./js/heroes.json')
      .then((res) => {
        store.heroes = res.data
      })
      .catch((err) => {
        console.log(err)
      })
    return {
      store
    }
  }
}
// TODOEnd

TeamList:

javascript 复制代码
// TODO:补全代码,实现目标效果
const TeamList = {
  template: `
  <div class="team-list">
      <h2>我的队伍</h2>
      <ul>
        <li class="team-item" v-for="(item,index) in store.team" :key="item.id">
          <span>{{item.name}}</span>
          <span>{{item.strength}}</span>
          <button @click=store.removeHero(item.id)>移除</button>
        </li>
      </ul>
      <button class="sort-button" @click=store.sort>按实力排序</button>
      <p class="total-strength">当前队伍战斗力:{{store.totalStrength}} </p>
  </div>
  `,
  setup() {
    const store = useHeroStore()
    return {
      store
    }
  }
}
// TODOEnd

store.js:

javascript 复制代码
const { defineStore } = Pinia
const { ref } = Vue

const useHeroStore = defineStore('hero', {
  state: () => ({
    heroes: [], //英雄列表
    team: [] // 队伍列表
  }),
  // TODO:补全代码,实现目标效果
  getters: {
    //计算出战力总和strength
    totalStrength() {
      return this.team.reduce((total, hero) => {
        return total + hero.strength
      }, 0)
    }
  },
  actions: {
    add(id) {
      this.heroes[id - 1].btn = true
      this.team.push(this.heroes[id - 1])
    },
    removeHero(id) {
      this.heroes[id - 1].btn = false
      //移出team中的元素
      this.team = this.team.filter((hero) => hero.id !== id)
    },
    sort() {
      //按照实力排序strength
      this.team.sort((a, b) => {
        return b.strength - a.strength
      })
    }
  }
  // TODOEnd
})

9.实时展示权限日志

这个题目我不知道为啥,明明在线测试基本上满足题目要求了,但是就是不能完全通过

node.js:

javascript 复制代码
/**
 * 请完成下面的 TODO 部分,其他代码请勿改动
 */
const fs = require('fs')
const http = require('http')
const path = require('path')
const dataUrl = path.resolve(__dirname, '../data.json')
const loggerUrl = path.resolve(__dirname, '../logger.json')
// 获取唯一的id
function getLoggerId() {
  return Buffer.from(Date.now().toString()).toString('base64') + Math.random().toString(36).substring(2)
}

/**
 * 该方法统一了服务器返回的消息格式,并返回给客户端
 * @param {*} res 响应 response
 * @param {*} code 状态码,默认为 0 代表没有错误,如果有错误固定为 404
 * @param {*} msg 错误消息,固定为空字符串即可 ''
 * @param {*} data 响应体,为 js 对象,若 data 为 utf-8 编码时需要使用 eval(data) 处理
 */
function send(res, code, msg, data) {
  const responseObj = {
    code,
    msg,
    data
  }
  const da = JSON.stringify(responseObj)
  res.setHeader('Content-Type', 'application/json;charset=utf-8')
  res.write(da)
  res.end()
}

function handleStatic(res, pathName, part) {
  const content = fs.readFileSync(path.resolve(__dirname, pathName))
  let contentType = 'text/html'
  switch (part) {
    case 'css':
      contentType = 'text/css'
      break
    case 'js':
      contentType = 'text/js'
      break
  }
  res.writeHead(200, 'Content-Type', contentType)
  res.write(content)
  res.end()
}

const server = http.createServer((req, res) => {
  res.setHeader('Access-Control-Allow-Origin', '*')
  if (req.url === '/') {
    handleStatic(res, '../index.html', '')
  } else if (req.url === '/css/index.css') {
    handleStatic(res, `..${req.url}`, 'css')
  } else if (req.url === '/js/index.js') {
    handleStatic(res, `..${req.url}`, 'js')
  } else if (req.url === '/js/axios.min.js') {
    handleStatic(res, `..${req.url}`, 'js')
  } else if (req.url === '/js/vue3.global.min.js') {
    handleStatic(res, `..${req.url}`, 'js')
  }

  if (req.method === 'GET' && req.url === '/users') {
    // TODO 处理获取文件内容的操作
    //读取data.json中的数据
    let fileContent = fs.readFileSync(dataUrl, 'utf-8')
    let data = JSON.parse(fileContent)
    if (fileContent) {
      //将读取到的数据转化为json格式
      //将json格式的数据响应给客户端
      send(res, 0, '', data)
    }
  } else if (req.method === 'PUT' && req.url === '/editUser') {
    let fileContent = fs.readFileSync(dataUrl, 'utf-8')
    let data = JSON.parse(fileContent)
    let body = ''
    req.on('readable', () => {
      let chunk = ''
      if (null !== (chunk = req.read())) {
        body += chunk
      }
    })
    req.on('end', () => {
      if (body) {
        // TODO 处理更改文件数据并将最新的文件数据响应给客户端
        //处理put请求
        let bodyData = JSON.parse(body)
        //修改data.json中的数据
        data.forEach((item) => {
          if (item.id == bodyData.id) {
            item.power = bodyData.power
          }
        })
        //存储文件数据到data.json中
        fs.writeFileSync(dataUrl, JSON.stringify(data))
        send(res, 0, '', data)
      }
    })
  } else if (req.method === 'POST' && req.url === '/logger') {
    let body = ''
    req.on('readable', () => {
      let chunk = ''
      if (null !== (chunk = req.read())) {
        body += chunk
      }
    })
    req.on('end', () => {
      let fileContentLog = fs.readFileSync(loggerUrl, 'utf-8')
      //判断是否有日志
      let dataLog = []
      if (fileContentLog) {
        dataLog = JSON.parse(fileContentLog)
      }
      let fileContentUser = fs.readFileSync(dataUrl, 'utf-8')
      let dataUser = JSON.parse(fileContentUser)
      if (body) {
        // TODO 处理新增日志
        let bodyData = JSON.parse(body)

        let dataJson = {
          id: getLoggerId(),
          msg: bodyData.data,
          // 时间格式为:2023/6/6 上午8:10:35
          time: `${getTime()}`
        }
        //存储日志
        dataLog.unshift(dataJson)
        // 并在该对象转化成 JSON 格式的末尾添加换行符(如不添加换行符会导致检测不通过)
        fs.writeFileSync(loggerUrl, JSON.stringify(dataLog, null, 2) + '\n')
        send(res, 0, '', dataJson)
      }
    })
  }
})

function getTime() {
  // 获取当前时间
  const currentDate = new Date()
  // 获取年、月、日、时、分、秒
  const year = currentDate.getFullYear()
  const month = currentDate.getMonth() + 1 // 月份是从 0 开始的,所以要加 1
  const day = currentDate.getDate()
  const hours = currentDate.getHours()
  //获取是上午还是下午
  const amPm = hours >= 12 ? '下午' : '上午'
  const minutes = currentDate.getMinutes()
  const seconds = currentDate.getSeconds()
  // 格式化时间
  const formattedTime = `${year}/${month}/${day} ${amPm}${hours}:${minutes}:${seconds}`
  return formattedTime
}

server.listen(8080, () => {
  console.log('Server running on port 8080')
})

index.js:

javascript 复制代码
/**
 * 请完成下面的 TODO 部分,其他代码请勿改动
 */

// 对响应进行统一处理,如果不调用该函数,可能导致判题出错
// 参数为服务器的响应对象
function parseRes(res) {
  return (res.json && res.json()) || res.data
}

const App = {
  setup() {
    const { onMounted } = Vue
    const data = Vue.reactive({
      userList: [], //用户数组
      loggerList: [] //日志数组
    })
    const getPowerText = (power) => {
      return power ? '可以登录' : '禁止登录'
    }
    const handleChange = async (e) => {
      if (e.target.tagName !== 'INPUT') {
        return
      }
      // TODO 处理发送请求修改当前用户的权限并更新一条日志记录
      //处理put请求
      let res = await axios.put(`/editUser`, {
        id: e.target.dataset.id,
        power: e.target.checked
      })
      if (res.status == 200) {
        data.userList = parseRes(res.data)
      } else {
        console.log('修改失败')
      }
      //调用post请求,添加一条修改日志
      //用id找出用户名
      let userName = data.userList.find((item) => item.id == e.target.dataset.id).name
      let postRes = await axios.post('/logger', {
        data: `超级管理员将用户${userName}设置为${getPowerText(e.target.checked)}权限`
      })
      if (postRes.status == 200) {
        //将数据放在数组首
        let a = parseRes(postRes.data)
        data.loggerList.unshift(a)
      } else {
        console.log('添加日志失败')
      }
    }

    // TODO 在页面挂载之前请求用户数据并修改对应的响应数据
    //利用axios获取数据
    const getUserData = async () => {
      let res = await axios.get('/users')
      if (res.status == 200) {
        data.userList = res.data.data
      } else {
        getUserData()
      }
    }
    onMounted(() => {
      getUserData()
    })
    return {
      data,
      handleChange,
      getPowerText,
      getUserData
    }
  }
}
const app = Vue.createApp(App)
app.mount(document.querySelector('#app'))

10.账户验证

javascript 复制代码
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>账户验证</title>
  <link rel="stylesheet" type="text/css" href="./css/index.css" />
  <link rel="stylesheet" href="./css/element-plus@2.3.7/index.css">
  <script src="./js/vue3.global.js"></script>
  <script src="./css/element-plus@2.3.7/index.full.js"></script>
  <script type="importmap">
    {
      "imports": {
        "vue-demi": "./js/index.mjs",
        "vue": "./js/vue.esm-browser.prod.js",
        "pinia": "./js/pinia.esm-browser.js"
      }
    }
  </script>
  <script src="./js/pinia.esm-browser.js" type="module"></script>
</head>

<body>
  <!-- app根组件开始 -->
  <div id="app">
    <div class="header">
      <img class="back-btn" src="images/arrow.png" />
      <span id="main_title">使用手机号登录</span>
      <span class="blank"></span>
    </div>
    <component :is="showName"></component>
  </div>
  <!-- app根组件结束 -->

  <!-- phone组件开始 -->
  <template id="phone">
    <div>
      <ul class="phone">
        <span>输入手机号码</span>
        <li>
          <input v-model="phoneVal" type="text" autofocus id="numberInput" />
        </li>
        <li>
          <input v-model="isSure" type="checkbox" name="" id="checkbox" />
          <span>已阅读并同意
            <a href="javascript:;">服务协议</a>
            和
            <a href="javascript:;">隐私保护指引</a>
          </span>
        </li>
        <button id="btn" @click="nextStep">下一步</button>
      </ul>
    </div>
  </template>
  <!-- phone组件结束 -->

  <!-- check组件开始 -->
  <template id="check">
    <ul class="number">
      <span>输入短信验证码</span>
      <li class="hassend">已向
        <i>{{ handlePhoneVal }}</i>
        发送验证码
      </li>
      <li class="code-container">
        <input v-for="(item, index) in verificationCodeInput" :key="index" v-model="item" @input="handleInput(index)"
          @keydown="handleKeyDown(index)" class="code" type="number" min="0" max="9" ref="codeInput{{index}}"
          required />
      </li>
      <a href="javascript:;" id="resend" @click="resendCode">重新发送</a>
    </ul>
  </template>
  <!-- check组件结束 -->

  <!-- success组件开始 -->
  <template id="success">
    <div class="success">
      <ul>
        <div>验证成功!</div>
        <div>5s后将自动跳转</div>
      </ul>
    </div>
  </template>
  <!-- success组件结束 -->
</body>

<script type="module">
  import { createPinia } from 'pinia';
  import { createApp, ref, reactive, provide, inject, onBeforeMount } from 'vue';
  const { ElNotification } = ElementPlus;

  const app = createApp({
    setup() {
      const data = reactive({
        showName: 'phone',
      });

      const code = ref([]);
      const phoneVal = ref('');
      const createCode = function () {
        let res = '';
        function* _create() {
          let count = 0;
          while (++count <= 6) {
            yield Math.floor(Math.random() * 10);
          }
        }
        for (const iterator of _create()) {
          res += iterator;
        }
        return res;
      };

      const handlePhone = (num) => {
        let res = '';
        for (let idx in num) {
          if (idx > 2 && idx < num.length - 2) {
            res += '*';
          } else {
            res += num[idx];
          }
        }
        return res;
      };

      provide('code', code);
      provide('phoneVal', phoneVal);
      provide('createCode', createCode);
      provide('data', data);
      provide('handlePhone', handlePhone);

      return {
        ...data,
      };
    },
  });

  app.use(ElementPlus);
  app.use(createPinia());

  app.component('phone', {
    template: '#phone',
    setup() {
      const isSure = ref('');
      const phoneVal = inject('phoneVal');
      const code = inject('code');
      const createCode = inject('createCode');
      const data = inject('data');

      function verifyPhone(num) {
        if (num.length !== 11) return false;
        return num[0] === '1' && num[1] === '8';
      }

      return {
        isSure,
        phoneVal,
        nextStep() {
          if (!isSure.value)
            return ElNotification({
              title: '发送失败',
              message: '请先阅读并同意下方协议',
              type: 'error',
            });
          if (!verifyPhone(phoneVal.value))
            return ElNotification({
              title: '发送失败',
              message: '无效的手机号码',
              type: 'error',
            });
          code.value = createCode();
          ElNotification({
            title: '发送成功',
            message: '您在验证码为' + code.value,
            type: 'success',
          });
          data.showName = 'check';
        },
      };
    },
  });

  app.component('check', {
    template: '#check',
    setup() {
      const phoneVal = inject('phoneVal');
      const handlePhoneVal = inject('handlePhone')(phoneVal.value);
      const data = inject('data');
      const code = inject('code');
      const createCode = inject('createCode');
      const verificationCodeInput = Array(6).fill('');

      onBeforeMount(() => {
        setTimeout(() => {
          const oCodeIptList = [...document.getElementsByClassName('code')];

          oCodeIptList[0].focus();

          oCodeIptList.map((item) => {
            item.oninput = function () {
              if (item.value) {
                item?.nextElementSibling && item?.nextElementSibling.focus();
              } else {
                item?.previousElementSibling && item?.previousElementSibling.focus();
              }
              trackVal();
            };
          });

          function trackVal() {
            const val = verificationCodeInput.join('');
            if (val.length === 6) {
              if (val === code.value) {
                ElNotification({
                  title: '验证成功',
                  message: '欢迎回来',
                  type: 'success',
                });
                data.showName = 'success';
              } else {
                ElNotification({
                  title: '验证失败',
                  message: '您输入的验证码有误',
                  type: 'error',
                });
                verificationCodeInput.fill('');
                oCodeIptList[0].focus();
              }
            }
          }
        });
      });

      return {
        handlePhoneVal,
        verificationCodeInput,
        handleInput(index) {
          if (index < 5 && verificationCodeInput[index].length === 1) {
            this.$refs[`codeInput${index + 1}`]?.focus();
          } else if (index > 0 && verificationCodeInput[index].length === 0) {
            this.$refs[`codeInput${index - 1}`]?.focus();
          }
          trackVal();
        },
        handleKeyDown(index) {
          if (event.key === 'Backspace' && index > 0) {
            this.$refs[`codeInput${index - 1}`]?.focus();
          }
        },
        trackVal() {
          const val = verificationCodeInput.join('');
          if (val.length === 6) {
            if (val === code.value) {
              ElNotification({
                title: '验证成功',
                message: '欢迎回来',
                type: 'success',
              });
              data.showName = 'success';
            } else {
              ElNotification({
                title: '验证失败',
                message: '您输入的验证码有误',
                type: 'error',
              });
              verificationCodeInput.fill('');
              this.$refs['codeInput0']?.focus();
            }
          }
        },
        resendCode() {
          code.value = createCode();
          ElNotification({
            title: '发送成功',
            message: '您的验证码为' + code.value,
            type: 'success',
          });
        },
      };
    },
  });

  app.component('success', {
    template: '#success',
  });

  app.mount('#app');
</script>

</html>
相关推荐
吕彬-前端23 分钟前
使用vite+react+ts+Ant Design开发后台管理项目(五)
前端·javascript·react.js
学前端的小朱26 分钟前
Redux的简介及其在React中的应用
前端·javascript·react.js·redux·store
guai_guai_guai35 分钟前
uniapp
前端·javascript·vue.js·uni-app
bysking2 小时前
【前端-组件】定义行分组的表格表单实现-bysking
前端·react.js
王哲晓2 小时前
第三十章 章节练习商品列表组件封装
前端·javascript·vue.js
fg_4112 小时前
无网络安装ionic和运行
前端·npm
理想不理想v2 小时前
‌Vue 3相比Vue 2的主要改进‌?
前端·javascript·vue.js·面试
酷酷的阿云2 小时前
不用ECharts!从0到1徒手撸一个Vue3柱状图
前端·javascript·vue.js
微信:137971205872 小时前
web端手机录音
前端
齐 飞2 小时前
MongoDB笔记01-概念与安装
前端·数据库·笔记·后端·mongodb