写点什么

「35 分钟」开发刷题功能

作者:小鑫同学
  • 2022 年 8 月 30 日
    北京
  • 本文字数:2843 字

    阅读完需:约 9 分钟


1. 前言

大家好,我是小鑫同学。一位从事过 Android 开发混合开发,现在长期从事前端开发的编程爱好者,我觉得在编程之路上最重要的是知识的分享,所谓三人行必有我师。所以我开始在社区持续输出我所了解到、学习到、工作中遇到的各种编程知识,欢迎有想法、有同感的伙伴加我fe-xiaoxin微信交流~


最近有老朋友想做个类似驾考的 App 来练练他们考试的 1k 道竞赛理论题,干看 Excel 和打印的纸质版总是没有刷手机容器提神,这种场景的实践最好就是使用 uniapp 这种全套搞定,H5 页面、数据库、部署统统有~

2. 数据清洗

2.1 题目分析:

这个是我拿到的题目数据,通过简单的筛选分析得到以下几个特点:


  1. 数据类型:题干、选项、答案、难度、题型、试题解析(内容均为空就不做考虑了);

  2. 题干、难度:仅做展示即可;

  3. 题型:分为单选题、多选题、判断题,单选可多选统一使用radio-group实现选项选择,多选题使用checkbox-group实现选项选择;

  4. 选项

  5. 单选选项使用 “#” 分割,radio-group绑定时需要通过 “、” 拆分为选项值和选项显示内容;

  6. 多选选项使用同单选选项拆分数据;

  7. 判断题答案选项仅使用 “#” 分割开即可。

  8. 答案:通过"".split("")直接拆分到数组里面,方便后续跟选项后的进行匹配。

2.2 数据清洗:

使用 NodeJs 相关读取 excel 的模块加载文件并转换为 JSON 文件再次输出,这里我们输出的 JSON 以每一条数据为一个 JSON 对象的字符串并占一行的格式输出到 output.json 文件,unicloud 云数据库在导入数据的时候需要的就是这种格式。


读取 excel 模块: read-excel-file


我们采用fs.appendFileSync函数对数据逐行读取并逐行追加到 output.json 文件中~


const readXlsxFile = require('read-excel-file/node')const fs = require('fs');
readXlsxFile(fs.createReadStream('./车工竞赛理论题.xlsx')).then((rows) => { rows.forEach((row => { const line = JSON.stringify({ subject: row[0], options: row[1].split("#"), answer: row[2].split(""), level: row[3], type: row[4], }) fs.appendFileSync('output.json', `${line}\n`) }))})
复制代码

2.3 数据导入:

下图来自 dcloud 官网,主要注意第二张图的最后一点,数据格式的问题,我们在上一小节的数据清洗时已按要求做了处理~



3. 编写 H5 页面


我在 uniapp 市场找到的 swiper 组件,卡片的切换效果还是挺不错的,我们通过下面的 【再来 10 道题】 的按钮来切换下一页的题目,项目开发和插件安装建议直接使用 HBuilderX 使用~


因为选项再切割的时候存在“.”和“、”两种形式,我们统一转换为“、”,在答对题目后显示正确结果~


this.list = res.result.data.map(v => {  if (v.type === "单选题" || v.type === "多选题") {    return {      ...v,      options: v.options.map(i => i.replace('.', '、')),    }  } else {    return v;  }}) || [];
复制代码


选中题目后的事件处理,单选和判断通过是否在答案数组中包含即可,多选题可以通过对答案和选择的结果(均为数组)sort 排序并 toString 后比较两个字符串是否一致来判断,前提可以判断两个数组的数量一致~


radioChange(evt, item) {  const result = item.answer.find(v => v === evt.detail.value);  if (result) item.show = true;}
checkboxChange(evt, item) { console.log(evt, item) if (evt.detail.value.length === item.answer.length && evt.detail.value.sort().toString() === item.answer.sort().toString()) { item.show = true; }}
复制代码

3.1 单选题选项:

<view class="uni-list" v-if="item.type === '单选题'">  <radio-group @change="radioChange($event, item)">    <label class="uni-list-cell uni-list-cell-pd" v-for="(option, index) in item.options"           :key="index">      <view class="opt">        <radio color="#747474" :disabled="item.show" :value="option.split('、')[0]" />        {{option}}      </view>    </label>  </radio-group></view>
复制代码

3.2 多选题选项:

<view class="uni-list" v-if="item.type === '多选题'">  <checkbox-group @change="checkboxChange($event, item)">    <label class="uni-list-cell uni-list-cell-pd" v-for="(option, index) in item.options"           :key="index">      <view class="opt">        <checkbox color="#747474" :disabled="item.show" :value="option.split('、')[0]" />        {{option}}      </view>    </label>  </checkbox-group></view>
复制代码

3.3 判断题选项:

<view class="uni-list" v-if="item.type === '判断题'">  <radio-group @change="radioChange($event, item)">    <label class="uni-list-cell uni-list-cell-pd" v-for="(option, index) in item.options"           :key="index">      <view class="opt">        <radio color="#747474" :disabled="item.show" :value="option" />        {{option}}      </view>    </label>  </radio-group></view>
复制代码

4. 分页查询

4.1 初始化 db 对象:

const db = uniCloud.database({  provider: 'aliyun',  spaceId: '',  clientSecret: ''});
复制代码

4.2 使用 skip 函数来实现分页查询:

getList() {  this.loading = true;  const offset = this.index === 0 ? 0 : this.index * this.size;  this.list = [];  db.collection('questions')    // .where('type == "多选题"')    .skip(offset)    .limit(this.size)    .get()    .then((res) => {    this.index++;    this.list = res.result.data.map(v => {      if (v.type === "单选题" || v.type === "多选题") {        return {          ...v,          options: v.options.map(i => i.replace('.', '、')),        }      } else {        return v;      }    }) || [];    if (this.list.length === 0) {      this.index = 0;    } else {      this.resetSwiper();    }    this.loading = false;  }).catch((err) => {    this.loading = false;    console.log(err.code); // 打印错误码    console.log(err.message); // 打印错误内容  })}
复制代码


每次分页查询后需要将 z-swiper 手动切回第一张卡片~


resetSwiper() {  setTimeout(() => {    this.$refs.zSwiper.swiper && this.$refs.zSwiper.swiper.slideTo(0);  }, 0)}
复制代码

5. 总结

独立开发一些简易 H5 来说还是 uni 更占一些优势,尤其是有一些数据库需求的项目,可以直接借助 uni 提供的各种服务来完成整个生命周期的开发,后期可以直接将 H5 托管到该平台,还可以生成统一发布页面,今天就先介绍这么多,项目还在本地,需要源码的+我~




如果看完觉得有收获,欢迎点赞、评论、分享支持一下。你的支持和肯定,是我坚持写作的动力~


最后可以关注我 @小鑫同学。欢迎点此扫码加我微信fe-xiaoxin交流,共同进步(还可以帮你 fix🐛)~

发布于: 刚刚阅读数: 5
用户头像

小鑫同学

关注

公众号:前端小鑫同学 2018.12.10 加入

👨🏻‍💻InfoQ签约作者/“乘风者计划”签约博主,掘金/51CTO活跃作者~

评论

发布
暂无评论
「35分钟」开发刷题功能_前端_小鑫同学_InfoQ写作社区