跳转至

第44期线上编程实战赛

1 竞赛管理系统

let newObj ={}
Object.assign(newObj,obj)

2 汽车私人定制

const element = document.querySelector('fieldset[data-uic~="color-compare"]');
const left = document.querySelector("#left");
const right = document.querySelector("#right");
const range = document.querySelector("#range");

// 修改该元素的CSS变量
const change=()=>{
    element.style.setProperty("--_c1", left.value);
    element.style.setProperty("--_c2", right.value);
    element.style.setProperty("--_x", range.value+'%');
}
change()
left.addEventListener('input', change)
right.addEventListener('input', change)
range.addEventListener('input', change)
  • 设置CSS变量:element.style.setProperty('--ca', 'value')

  • 获取CSS变量:element.style.setProperty('--ca')

  • :where()选择器

  • 渐变背景

background-image: linear-gradient(to right, var(--_c1) 0 var(--_x), var(--_c2) 0 var(--_x));

3 花与大海

<body>
    <!-- App根组件开始 -->
    <div id="app">
        <div class="box">
            <!-- TODO开始: 修改代码实现点击按钮切换组件 -->
            <Component :is="comp"></Component>
            <div class="btns">
                <button type="button" id="btn1" class="btn" @click="change(0)"></button>
                <button type="button" id="btn2" class="btn" @click="change(1)">大海</button>
            </div>
            <!-- TODO结束 -->
        </div>
    </div>
    <!-- App根组件结束 -->

    <!-- Flower组件开始 -->
    <template id="Flower">
        <div class="Flower">
            <img src="./images/flower.jpg">
        </div>
    </template>
    <!-- Flower组件结束 -->

    <!-- Ocean组件开始 -->
    <template id="Ocean">
        <div class="Ocean">
            <img src="./images/ocean.jpg">
        </div>
        </div>
    </template>
    <!-- Ocean组件结束 -->
</body>

<script type="module">
    const { createApp, ref, reactive, toRefs } = Vue
    const app = Vue.createApp({
        setup() {
            // TODO开始: 添加代码,实现动态组件切换效果
            let comp = ref('Flower')
            const change = (val) => {
                if (val) comp.value = 'Ocean'
                else comp.value = 'Flower'
                console.log(val);
            }
            return { change, comp }
            // TODO结束
        },
    });
    // Flower组件
    app.component("Flower", {
        template: "#Flower",
    })

    // Ocean组件
    app.component("Ocean", {
        template: "#Ocean",
    })
    app.mount('#app');

</script>

</html>
  • 动态组件<Component :is="comp"></Component>

  • is后面跟一个字符串就行,这个字符串是组件的名字

  • 注册组件

  • html <template id="Flower"> <div class="Flower"> <img src="./images/flower.jpg"> </div> </template>

  • js app.component("Flower", { template: "#Flower", })

4 网站换皮肤

const body = document.querySelector('#body')
const chooseimg = document.querySelector('.chooseimg')
if (localStorage.getItem('src')){
    body.style.backgroundImage = `url(${localStorage.getItem('src')})`
    box.classList.add('active')
    box.classList.remove('default')
}
chooseimg.addEventListener('click', (e) => {
    if (e.target.tagName != 'IMG') return
    let src = e.target.src
    localStorage.setItem('src', src)
    body.style.backgroundImage = `url(${src})`
    box.classList.add('active')
    box.classList.remove('default')
})

clearSkin.addEventListener('click', () => {
    body.style.backgroundImage = ''
    box.classList.remove('active')
    box.classList.add('default')
})

5 进制转换

HEX() {
    return parseInt(this.request, this.binary).toString(16)
},
DEC() {
    return parseInt(this.request, this.binary).toString(10)
},
OCT() {
    return parseInt(this.request, this.binary).toString(8)
},
BIN() {
    return parseInt(this.request, this.binary).toString(2)
}
  • parsetInt(string, radix),把string按照radix进制解析为数字
  • Number.prototype.toString(radix) 把数字转换为radix进制的字符串

6 学生数据请求

    else if (req.url === '/student' && req.method === 'GET') {
        let file = fs.readFileSync('./js/data.js', 'utf8')
        send(res, 0, '', file)
    } else if (req.url === '/student/add' && req.method === 'POST') {
        let body = "";
        req.on("readable", () => {
            let chunk = "";
            if (null !== (chunk = req.read())) {
                body += chunk;
            }
        });
        req.on("end", () => {
            if (body) {
                send(res, 0, '', body)
            }
        });
    }
async function init() {
    // 在这里需要获取学生数据并展示到页面中
    let res;
    // TODO 请求服务器拿到数据,并完成界面渲染,请求学生数据时需要调用上面提供的方法解析响应体中的data数据,调用渲染方法,注意render函数需要传入数组
    fetch('/student').then(r => r.json()).then(r => {
        console.log(r);
        res = parseRes(r.data)
        render(res)
        submitBtn.addEventListener('click', handleAddStudent)
    })
}

async function handleAddStudent(e) {
    e.preventDefault();
    // TODO 你需要将表单中所有的文本框的值获取到,然后向服务器请求,拿到服务器数据调用渲染方法,注意render函数需要传入数组
    let data;
    fetch('/student/add', {
        method: 'POST',
        body: JSON.stringify({
            name: nameDom.value,
            age: ageDom.value,
            phone: phoneDom.value,
        })
    }).then(res => res.json()).then(res => {
        console.log(res);
        res=parseRes(res.data)
        console.log(res);
        render([res])
    })
}

7 Markdown编辑器

function parseMarkdownToHTML(markdown) {
    // TODO: 请完成这个函数
    const res = []
    const list = markdown.split('\n')

    const title = /^#{1,6} (.+)/
    const img = /^!\[([^\]]+)\]\(([^\)]+)\)/
    const enter = /\n/
    const li = /^- (.+)/
    const isTable = /^\|/

    for (let index = 0; index < list.length; index++) {
        let line = list[index]
        if (title.test(line)) {
            let level=line.split(' ')[0].length
            res.push(line.replace(title, `<h${level}>$1</h${level}>`))
        } else if (img.test(line)) {
            res.push(line.replace(img, '<img src="$2" alt="$1">'))
        } else if (line === '') {
            res.push('<br>')
        } else if (li.test(line)) {
            let html = '<ul>'
            while (li.test(line)) {
                html += line.replace(li, '<li>$1</li>')
                line = list[++index]
            }
            index--
            html += '</ul>'
            res.push(html)
        } else if (isTable.test(line)) {
            let html = '<table><tbody>'
            while (isTable.test(line)) {
                // debugger
                let tr = line.split('|').slice(1,-1).map(e => `<td>${e.trim()}</td>`).join('')
                tr = `<tr>${tr}</tr>`
                html += tr
                line = list[++index]
            }
            index--
            html += '</tbody></table>'
            res.push(html)
        } else {
            res.push(line)
        }
    }
    return res.join('<br>');
}
  • 换行到底怎么解析?str.replace('\n', '<br>')

8 中国十大名花

index.htm

let useFlower = useFlowerStore();
onMounted(() => {
  axios("./js/flowers.json").then((res) => {
    useFlower.flowers = res.data;
    let item=localStorage.getItem('item')
    if(item){
        useFlower.select(JSON.parse(item))
    }
  });
});

store.js

const useFlowerStore = defineStore("useFlowerStore", {
  state: () => ({
    flowers: [], // 所有花卉信息
    selectedFlower: null, // 当前选中的花
  }),
  // TODO:补全代码实现目标效果
  getters: {},
  actions: {
    select(item){
      this.selectedFlower=item
      localStorage.setItem('item', JSON.stringify(item))
    },
    clear(){
      this.selectedFlower=null
      localStorage.clear()
    }
  },
  // TODOEnd
});

FlowerDetails.js

// TODO:补全代码实现目标效果
const FlowerDetails = {
  template: `
    <div class="flower" v-if='flowerStore.selectedFlower'>
        <h2 class="flower-name">{{flowerStore.selectedFlower.name}}</h2>
        <img :src="flowerStore.selectedFlower.image">
        <p class="flower-description">花语:{{flowerStore.selectedFlower.language}}</p>
        <button type="info" @click='flowerStore.clear'>返回</button>
    </div>
  `,
  setup() {
    let flowerStore=useFlowerStore()
    return {flowerStore}
  },
};
// TODOEnd

FlowerList.js

// TODO:补全代码实现目标效果
const FlowerList = {
    template: `
  <div class="flower-list" v-if='flowerStore.flowers.length && !flowerStore.selectedFlower'>
    <div class="flower-card" v-for='item in flowerStore.flowers' :key='item.id'>
      <h2 class="name">{{item.name}}</h2>
      <p class="scientific-name">{{item.scientificName}}</p>
      <div class="description">{{item.description}}</div>
      <button type="primary" @click=flowerStore.select(item)>查看详情</button>
    </div>
  </div>
  `,
    setup() {
        let flowerStore = useFlowerStore()
        // const select=(item)=>{}
        return {
            flowerStore
        }
    },
};
// TODOEnd
  • .value 通常用于处理 ref 对象。Pinia 的 state 属性默认不是 ref,而是使用 reactive 创建的响应式对象。所以你可以直接赋值,而不需要 .value

9 熟能生巧 -- 记忆函数

function memorize(fn) {
    let map = new Map()
    return function (...arg) {
        let str_arg=JSON.stringify(arg)
        if (map.has(str_arg)) return map.get(str_arg)
        else {
            let ans = fn(...arg)
            map.set(str_arg, ans)
            return ans
        }
    }
}
  • 如何记忆数组?JSON.stringify(arr)

10 找回密码

index.html

const changeStatus = (val) => {
    if(typeof val !=='string') return
    data.status = val
}

account.vue

<template>
  <div class="account container">
    <section class="content bgcolor-6">
      <span class="input input--juro">
        <input
          :ref="input"
          class="input__field input__field--juro"
          type="text"
          id="userName"
        />
        <label class="input__label input__label--juro" for="input-28">
          <span
            id="userNameVerify"
            class="input__label-content input__label-content--juro"
            >用户名</span
          >
        </label>
      </span>
      <span class="input input--juro">
        <input
          :ref="input"
          class="input__field input__field--juro"
          type="text"
          id="idNum"
        />
        <label class="input__label input__label--juro" for="input-29">
          <span
            id="idNumVerify"
            class="input__label-content input__label-content--juro"
            >身份证号</span
          >
        </label>
      </span>
    </section>
    <el-button type="primary" id="nextStep" @click="next">下一步</el-button>
  </div>
</template>

<script>
const { ref, onMounted, defineEmits } = Vue;
module.exports = {
  name: "account",
  setup(props,{emit}) {
    const next = () => {
      let flag=true;
      if (/^[\d\w\_]{3,5}$/.test(inputs.value[0].value)) {
        document.querySelector("#userNameVerify").classList.add("right");
        document.querySelector("#userNameVerify").classList.remove("wrong");
        document.querySelector("#userNameVerify").innerText = "用户名";
      } else {
        document.querySelector("#userNameVerify").classList.remove("right");
        document.querySelector("#userNameVerify").classList.add("wrong");
        document.querySelector("#userNameVerify").innerText = "用户名不合法";
        // context 是 setup 函数的第二个参数。它包含了一些组件相关的上下文信息,包括 attrs、slots 和 emit。
        emit("change", "error");
        flag=false
      }

      let shenfen = inputs.value[1].value;
      let region = shenfen.slice(0, 6);
      if (
        shenfen.length !== 18 ||
        !/^[1-9][\d]{5}$/.test(region) ||
        !/^(19|20|21)[\d]{2}$/.test(shenfen.slice(6, 10)) ||
        !/^\d{7}$/.test(shenfen.slice(10, 17)) ||
        !/^[\dX]$/
      ) {
        document.querySelector("#idNumVerify").classList.remove("right");
        document.querySelector("#idNumVerify").classList.add("wrong");
        document.querySelector("#idNumVerify").innerText = "身份证号不合法";
        emit("change", "error");
        flag=false
      } else {
        document.querySelector("#idNumVerify").classList.add("right");
        document.querySelector("#idNumVerify").classList.remove("wrong");
        document.querySelector("#idNumVerify").innerText = "身份证号";
      }
      if(flag){
        emit("change", "success");
        emit("show", "check");
        emit("next", 1);
        emit("change", "process");
      }
    };

    // ***此部分代码不用修改***
    //#region
    // ref 被设置为一个函数 input,这意味着 Vue 会在组件渲染时调用这个函数
    // 并将相应的 DOM 元素作为参数传递给它。
    const inputs = ref([]);
    const input = (el) => {
      inputs.value.push(el);
    };
    onMounted(() => {
      inputs.value.map((el) => {
        if (el.value.trim() != "") {
          el.parentNode.classList.add("input--filled");
        }
        el.onfocus = function () {
          el.parentNode.classList.add("input--filled");
        };
        el.onblur = function () {
          if (this.value.trim() != "") {
            el.parentNode.classList.add("input--filled");
          } else {
            el.parentNode.classList.remove("input--filled");
          }
        };
      });
    });
    //#endregion
    // **********************
    return {
      input,
      next,
    };
  },
};
</script>

<style scoped></style>

steps.vue

<template>
  <div class="steps">
    <el-steps
      align-center
      :active="props.active"
      :process-status="props.status"
      finish-status="success"
    >
      <el-step title="账号核实" description="输入用户名和身份证号"></el-step>
      <el-step title="验证身份" description="请确认您是真人"></el-step>
      <el-step title="修改密码" description="输入要修改的密码"></el-step>
      <el-step title="成功" description="成功找回密码"></el-step>
    </el-steps>
  </div>
</template>

<script>
module.exports = {
  // TODO:补全代码实现目标效果
  name:'steps',
  props:['active', 'status'],
  setup(props, { emit }) {
    return { props };
  },
};
</script>

<style scoped>
</style>

check.vue

<template>
  <div class="check" v-loading="loading" element-loading-text="验证中...">
    <div class="robot" @click="inputCheck">
      <input type="checkbox" />
      <span>确认您是真人</span>
    </div>
  </div>
</template>

<script>
const { ref} = Vue;
module.exports = {
  name: "check",
  setup(props, { emit }) {
    // TODO:补全代码实现目标效果
    const loading = ref(false);
    console.log();
    const inputCheck = () => {
      loading.value = true;
      setTimeout(() => {
        loading.value = false;
        emit("change", "success");
        emit("show", "modify");
        emit("next", 2);
        emit("change", "process");
      }, 1500);
    };

    return {
      inputCheck,
      loading,
    };
  },
};
</script>
<style scoped></style>

modify.vue

<template>
  <div class="modify container">
    <section class="content bgcolor-6">
      <span class="input input--juro">
        <input
          :ref="input"
          class="input__field input__field--juro"
          type="text"
          id="passwordOne"
        />
        <label class="input__label input__label--juro" for="input-28">
          <span
            id="passwordOneVerify"
            class="input__label-content input__label-content--juro"
            :class="{
              wrong: rightPasswd === 2,
              right: rightPasswd === 1,
            }"
            >{{ rightPasswd === 2 ? "您输入的密码不合法" : "请输入密码" }}</span
          >
        </label>
      </span>
      <span class="input input--juro">
        <input
          :ref="input"
          class="input__field input__field--juro"
          type="text"
          id="passwordTwo"
        />
        <label class="input__label input__label--juro" for="input-29">
          <span
            id="passwordTwoVerify"
            class="input__label-content input__label-content--juro"
            :class="{ wrong: twoEqual === 2, right: twoEqual === 1 }"
            >{{
              twoEqual === 2 ? "两次输入的密码不一致" : "请再次输入密码"
            }}</span
          >
        </label>
      </span>
    </section>
    <el-button type="primary" id="alter" @click="checkModify">修改</el-button>
  </div>
</template>

<script>
const { ref, onMounted } = Vue;
module.exports = {
  name: "modify",
  setup(props, { emit }) {
    // TODO:补全代码实现目标效果
    console.log(emit);
    const checkPasswd = (password) => {
      // debugger
      if (password.length < 6 || password.length > 12) return false;
      if (!/[A-Z]/.test(password) || !/[a-z]/.test(password)) return false;
      if (!/\d/.test(password)|| !/[_@$!%*?&]/.test(password)) return false;
      return true;
    };

    // ***此部分代码不用修改***
    //#region
    const inputs = ref([]);
    const input = (el) => {
      inputs.value.push(el);
    };
    onMounted(() => {
      inputs.value.map((el) => {
        if (el.value.trim() != "") {
          el.parentNode.classList.add("input--filled");
        }
        el.onfocus = function () {
          el.parentNode.classList.add("input--filled");
        };
        el.onblur = function () {
          if (this.value.trim() != "") {
            el.parentNode.classList.add("input--filled");
          } else {
            el.parentNode.classList.remove("input--filled");
          }
        };
      });
    });
    //#endregion
    // **********************
    const rightPasswd = ref(0);
    const twoEqual = ref(0);
    const checkModify = () => {
      if (checkPasswd(inputs.value[0].value)) {
        rightPasswd.value = 1;
      } else {
        rightPasswd.value = 2;
        emit("change", "error");
      }

      if (inputs.value[0].value == inputs.value[1].value) {
        twoEqual.value = 1;
        if (rightPasswd.value === 1) {
          console.log(emit);
          emit("change", "success");
          emit("show", "success");
          emit("next", "3");
          emit("change", "success");
        }
      } else {
        twoEqual.value = 2;
      }
    };
    return {
      input,
      rightPasswd,
      twoEqual,
      checkModify,
    };
  },
};
</script>

<style scoped>
</style>
  • 在 Vue 3 中,setup 函数可以接收两个参数:

  • props:这是一个对象,包含传递给组件的属性。props 是响应式的,可以在 setup 函数中直接使用。

  • context:这是一个对象,包含了组件的上下文信息。它有以下三个属性:
    • attrs:包含传递给组件但未被声明为 props 的属性。这些属性通常会被应用到组件的根元素上。
    • slots:包含传递给组件的插槽内容。
    • emit:用于触发自定义事件的方法。

因此,setup 函数的完整签名如下:

<script>
module.exports = {
  // TODO:补全代码实现目标效果
  name:'steps',
  props:['active', 'status'],
  setup(props, { emit }) {
    return { props };
  },
};
</script>
  • 要使用props,必须声明props:['active', 'status'],不然不会触发响应式

  • 如果属性名包含特殊字符、空格、连字符(-)等,或者以数字开头,就需要加引号

<span
    id="passwordOneVerify"
    :class="{
      'input__label-content': true,
      'input__label-content--juro': true,
      wrong: rightPasswd === 2,
      right: rightPasswd === 1,
    }"
>请输入密码</span>