更新记录

1.2.5(2024-03-29) 下载此版本

  1. 稍作优化

1.2.4(2024-02-21) 下载此版本

  1. 更新示例工程
  2. 更新文档

1.2.3(2024-02-06) 下载此版本

  1. 更新文档
  2. 兼容vue2的预更新进行中...
查看更多

平台兼容性

Vue2 Vue3
×
App 快应用 微信小程序 支付宝小程序 百度小程序 字节小程序 QQ小程序
HBuilderX 3.1.0 app-vue × × × × ×
钉钉小程序 快手小程序 飞书小程序 京东小程序
× × × ×
H5-Safari Android Browser 微信浏览器(Android) QQ浏览器(Android) Chrome IE Edge Firefox PC-Safari

sv-exam

前言

  1. 这是一款未使用任何第三方库的刷题考试系统插件,包含三种模式:背题模式、刷题模式、模拟考试
  2. 目前经实测,已兼容微信小程序、安卓App、H5三端,其他平台未实测
  3. 如需商用,还请加群私聊联系作者本人,加群方式见本文结尾

重要公告!!!

  1. 在2024/02/21日v1.2.4版本后,除了部分重大bug或问题外,此插件将不会再频繁更新,以后将会在sv-exam-plus插件中不断更新迭代
  2. sv-exam-plus是基于本sv-exam的基础上,对vue2/3进行了适配兼容,详情请点击sv-exam-plus

简介

  1. 背题模式:用户可以查看题目和答案,但不能进行答题操作
  2. 刷题模式:用户可以进行答题操作,并记录答题情况
  3. 模拟考试:用户可以进行答题操作,并记录答题情况,系统会根据答题情况给出模拟考试的成绩
  4. 题目、选项、解析部分均支持富文本内容,可以添加图片等较为丰富的内容

功能

除上述三种模式外,还开放以下功能事件:

  1. 题目收藏
  2. 答题记录(根据做题情况添加记录或错题本)
  3. 纠错反馈
  4. 退出确认事件(考试模式下会弹出退出提示框,确认返回后方可离开考试)

使用方式

建议一键导入示例工程,参照示例工程使用

<template>
  <view class="exam">
    <sv-exam 
      :type="examOptions.type" 
      :data="examData" 
      :lib="examOptions.lib"
      :favs="myFavs" 
      @changePaper="changePaper" 
      @quit="onQuit">
    </sv-exam>
  </view>
</template>

<script setup>
import { ref } from 'vue';
import { onLoad, onUnload } from '@dcloudio/uni-app'
import data from '/assets/json/temp-data.js'
import { cloneDeep } from 'lodash'

const tempdata = cloneDeep(data) // 深拷贝原数据,防止数据污染

const examOptions = ref({})
const examData = ref([]) // 题目列表
const myFavs = ref([]) // 收藏夹

onLoad((options) => {
  examOptions.value = options // type:三种考试类型exam/practice/recite, lib:题库id

  handleData(options)

  todoContinue()

  // 开启相关事件监听
  examWatcher()
})

// 数据处理
function handleData(options) {
  setTimeout(() => {
    // 模拟从接口异步获取题目数据
    examData.value = findPaperFromData(options.lib)

    myFavs.value = uni.getStorageSync('sv-exam-fav') || []
  }, 1000)
}

// 继续进度刷题
function todoContinue() {
  if (examOptions.value.type !== 'practice') return
  let dataCache = uni.getStorageSync('sv-exam-data-cache')
  if (dataCache[examOptions.value.lib]?.length > 0) {
    uni.showModal({
      title: '系统提示',
      content: '是否继续上次答题?',
      showCancel: true,
      success: ({ confirm }) => {
        if (confirm) {
          examData.value = dataCache[examOptions.value.lib]
        } else {
          // 重新开始答题
          uni.$emit('sv-exam-start', 0)
        }
      }
    })
  }
}

// 获取指定题库的题目列表
function findPaperFromData(lib) {
  let temp = tempdata.filter(item => item.from_lib == lib)
  if (examOptions.value.type == 'exam') {
    // 打乱顺序取随机100道
    temp = shuffleAndPickTen(temp, 100)
  }
  return temp
}

// 考试模式需随机打乱题库并取其中num道题目,num默认100
function shuffleAndPickTen(arr, num = 100) {
  // 打乱数组
  function shuffle(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
  }
  // 打乱后的数组取前num个(或数组长度不足num时全部)
  const shuffledArr = shuffle(arr)
  return shuffledArr.slice(0, Math.min(shuffledArr.length, num))
}

/**
 * 切换题目
 * @param {Object} e 当前题目
 */
function changePaper(e) {
  console.log('==== 切换题目 :', e)
}

/**
 * 退出考试
 * @param {Object} e 做题记录,可缓存或上传云端,用作下一次打开后继续进度
 */
function onQuit(e) {
  if (examOptions.value.type == 'practice') {
    const storage = uni.getStorageSync('sv-exam-data-cache') || {}
    storage[examOptions.value.lib] = e
    uni.setStorageSync('sv-exam-data-cache', storage)
  }
  console.log('==== 退出考试 :', e)
}

// exam相关事件监听
function examWatcher() {
  /**
   * 监听题目提交
   * @param e 题目记录: 可根据返回的题目记录,自定义选择存入做题记录或错题本
   */
  uni.$on('e-exam-paper-item-submit', (e) => {
    console.log('==== 监听题目提交 e-exam-paper-item-submit :', e)
  })

  /**
   * 监听考完交卷
   * @param e 答题记录列表: 可根据返回的答题记录列表,缓存或上传云端,提示下一次打开是否继续上次答题
   */
  uni.$on('e-exam-paper-complete', (e) => {
    console.log('==== 监听考完交卷 e-exam-paper-complete :', e)
  })

  /**
   * 监听题目收藏
   * @param e 收藏题目数据
   */
  uni.$on('e-exam-paper-item-fav', (e) => {
    const favStorage = uni.getStorageSync('sv-exam-fav') || []
    if (e.isfav) {
      // 添加收藏
      favStorage.push(e.paper.data)
    } else {
      // 移除收藏
      favStorage.splice(favStorage.findIndex(item => item.exam_id === e.paper.data.exam_id), 1)
    }
    uni.setStorageSync('sv-exam-fav', favStorage)
    // 将新的收藏列表更新至myFavs
    myFavs.value = favStorage
    console.log('==== 监听题目收藏 e-exam-paper-item-fav :', e)
  })

  /**
   * 监听纠错反馈
   * @param e 纠错反馈的题目
   */
  uni.$on('e-exam-paper-item-feedback', (e) => {
    console.log('==== 监听纠错反馈 e-exam-paper-item-feedback :', e)
  })
}

onUnload(() => {
  // 统一做exam所有事件监听移除操作
  uni.$off('e-exam-paper-item-submit')
  uni.$off('e-exam-paper-complete')
  uni.$off('e-exam-paper-item-fav')
  uni.$off('e-exam-paper-item-feedback')
})
</script>

<style>
.exam {
  width: 100%;
  height: calc(100vh - var(--window-top));
}
</style>

其他场景使用方式

例如在部分其他场景,比如:收藏本,错题本中,开启刷题时需要跳转至刷题页面,但是可能不需要答题卡等部分功能,为此提高代码复用性,在1.1.8版本提供简约形式使用,案例如下:

// simple-exam.vue页面

<template>
  <view class="exam">
    <view class="exam-container">
      <sv-exam 
        type="practice"  // --- 默认为刷题模式
        :data="examData"  
        :showCard="false" // --- 关闭答题卡
        @changePaper="changePaper"
        @quit="onQuit">
      </sv-exam>
    </view>
  </view>
</template>

<script setup>
import { computed, ref, getCurrentInstance, nextTick } from 'vue'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import { useSysStore } from '@/store/sys'

const { proxy } = getCurrentInstance();

// 题目列表
const examData = ref([])

onLoad((options) => {
  // 通信通道传值 - 只监听一次
  const eventChannel = proxy.getOpenerEventChannel()
  eventChannel.once('e-transmit-exams', (res) => {
    examData.value = res.data // 从错题本或试题收藏夹页面传过来试题列表
    nextTick(() => {
      // 默认试题索引需要在页面渲染后赋予
      uni.$emit('sv-exam-start', res.index)  // 从错题本或试题收藏夹页面传过来试题索引
    })
  })

  // 开启监听者
  examWatcher()
})

function examWatcher() {
  /**
   * 监听题目提交
   * @param e 题目记录: 可根据返回的题目记录,自定义选择存入做题记录或错题本
   */
  uni.$on('e-exam-paper-item-submit', async (e) => {
    console.log('==== 监听题目提交 e-exam-paper-item-submit :', e)
  })
  /**
   * 监听纠错反馈
   * @param e 纠错反馈的题目
   */
  uni.$on('e-exam-paper-item-feedback', (e) => {
    console.log('==== 监听纠错反馈 e-exam-paper-item-feedback :', e)
  })
}

/**
 * 切换题目
 * @param {Object} e 当前题目
 */
function changePaper(e) {
  console.log('==== 切换题目 :', e)
}

/**
 * 退出考试
 * @param {Object} e 做题记录,可缓存或上传云端,用作下一次打开后继续进度
 */
function onQuit(e) {
  console.log('==== 退出考试 :', e)
}

onUnload(() => {
  // 统一做exam所有事件监听移除操作 (尽管该页面可能未开启相关监听)
  uni.$off('e-exam-paper-item-submit')
  uni.$off('e-exam-paper-complete')
  uni.$off('e-exam-paper-item-fav')
  uni.$off('e-exam-paper-item-feedback')
})
</script>

配置中心

// 在文件目录 @/uni_modules/sv-exam/components/sv-exam/config.js 下进行相关配置

/**
 * 三种答题模式 - 字段值可改,字段循序勿改,请保持 0:背题,1:刷题,2:考试 的顺序
 * recite 背题模式 题库中顺序出题,带有答案,只需看题背答案,无需手动写题,可继续背题
 * practice 刷题模式 题库中顺序出题,答题完自动批改对错并给出答案,可继续或重新答题
 * exam 模拟考试 题库中随机抽取指定数量道题并打乱顺序,提交后自动批改,退出考试时弹出提示,时间到自动交卷
 */
examTypeDict: {
  0: 'recite',
  1: 'practice',
  2: 'exam'
},
paperTypeDict: { // 题型字典
  0: '单选题',
  1: '多选题',
  2: '判断题' // 判断题本质上和单选一样
},
swiperDuration: 200, // 上/下一题切换动画速度 单位ms
favThrottle: 1000, // 收藏节流时间 单位ms
favThrottleText: '收藏太频繁啦~', // 收藏节流字样
feedbackText: '答案有误?点击反馈', // 反馈提示字样
toCompleteText: '在最后一页交卷', // 去交卷提示字样
quitText: '正在考试中,确定离开吗?', // 返回提示内容
countdown: 60, // 考试倒计时 单位分钟

自定义图标

  1. 本插件采用阿里巴巴矢量库图标,在插件目录 @/uni_modules/sv-exam/components/sv-exam/icons 文件夹下
  2. 需要自定义图标时,请替换该icons文件夹iconfont.ttf文件,并修改iconfont.css中的Unicode编码(建议保留原icon-名称,例如icon-correct、icon-wrong等,只修改Unicode编码即可)

写在最后

若对插件有任何疑问或者优化建议,欢迎在评论区留言,在插件市场中的私信消息本人可能不经常留意,导致没能及时回复, 可以加入本人的插件问答QQ交流群: 852637893,欢迎进群交流。

交流群:852637893

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率:

许可协议

MIT协议

使用中有什么不明白的地方,就向插件作者提问吧~ 我要提问