npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

hep-mistake

v1.2.31

Published

习题课

Readme

hep-mistake 使用文档

hep-mistake 是基于 Vue 3、Element Plus 的习题练习组件包,提供答题页、自由练习、错题本、AI 助手与拖拽容器等组件。

安装与注册

npm install hep-mistake
import { createApp } from 'vue'
import ElementPlus from 'element-plus'
import HepMistake from 'hep-mistake'
import App from './App.vue'

const app = createApp(App)
app.use(ElementPlus)
app.use(HepMistake)
app.mount('#app')

插件会全局注册以下组件:

| 组件名 | 说明 | | --- | --- | | ExerciseToPay | 答题、判分、交卷主组件 | | Practice | 练习入口组件 | | freePractice | 自由练习配置组件 | | notebook | 错题本组件 | | Intelligent | AI 助手组件 | | DraggingSlot | 可拖拽插槽容器 |

ExerciseToPay

答题页主组件,负责题目渲染、上一题/下一题、检查答案、AI 判分、错题本、提交答题结果。

<template>
  <ExerciseToPay
    :optionsStyle="optionsStyle"
    :questionUuidList="questionUuids"
    :getQuestionInfo="getQuestionInfo"
    :getStatic="getStatic"
    :historyHandler="historyHandler"
    :fitbAiScoreFn="fitbAiScoreFn"
    :upLoadFileSuccess="upLoadFileSuccess"
    :submitCorrectionFn="submitCorrectionFn"
    :feedbackType="feedbackType"
    @questionAddFn="questionAddFn"
    @questionAllAddFn="questionAllAddFn"
    @addMistakeBook="addMistakeBook"
  />
</template>

Props

| 参数 | 类型 | 必填 | 说明 | | --- | --- | --- | --- | | optionsStyle | ExerciseToPayStyle | 否 | 页面样式配置 | | questionUuidList | Array<string \| Question> | 是 | 题目 UUID 列表;答题过程中会被替换为题目对象 | | getQuestionInfo | (questionUuid: string) => Promise<Question> | 是 | 根据题目 UUID 获取题目详情 | | getStatic | (questionUuid: string) => Promise<{ data: QuestionStatic }> | 是 | 获取单题统计 | | historyHandler | (questionUuid: string) => Promise<HistoryAnswer> | 是 | 回显历史答题记录 | | fitbAiScoreFn | (question: Question, onMessage?: (text: string) => void) => Promise<AiScoreResponse> | 是 | 填空题/问答题 AI 判分;问答题支持流式回调 | | upLoadFileSuccess | (file: File) => Promise<string> | 是 | 编辑器图片/媒体上传 | | submitCorrectionFn | (data: CorrectionForm) => Promise<any> | 是 | 提交纠错 | | feedbackType | FeedbackType | 是 | 练习反馈模式、标题、倒计时等 |

Events

| 事件 | 参数 | 触发时机 | | --- | --- | --- | | questionAddFn | (data: QuestionSubmit, cardUpload?: Record<string, string>) | 单题检查答案后 | | questionAllAddFn | (data: any, cardUpload?: Record<string, string>) | 全卷提交后 | | addMistakeBook | (questionUuids: string[]) | 点击加入错题本 |

optionsStyle

interface ExerciseToPayStyle {
  bodyBgColor?: string
  width?: string | number
  titleFontSize?: string | number
  titleColor?: string
  cardColor?: string
  boxShadow?: string
  titleFontWeight?: string | number
}

feedbackType

interface FeedbackType {
  feedbackWay: 1 | 2 | 3 | 4
  paperTime?: number | null
  workName: string
  questionCard?: Record<string, string>
}

feedbackWay 说明:

| 值 | 说明 | | --- | --- | | 1 | 单题模式,检查当前题后显示下一题 | | 2 | 全题模式,可整卷提交 | | 3 | 背题/只看答案模式 | | 4 | 带答题卡回显模式 |

getQuestionInfo 返回结构

组件内部依赖题目对象中的以下字段。不同题型可以包含额外字段。

interface Question {
  id: number | string
  questionUuid: string
  questionDbId: string
  questionTypeBase:
    | 'dan_xuan'
    | 'duo_xuan'
    | 'pan_duan'
    | 'tian_kong'
    | 'wen_da'
    | 'lian_xian'
    | 'pai_xu'
    | 'wan_xing_tian_kong'
    | 'zong_he'
    | 'ting_li'
  questionTypeName?: string
  subjectEditorValue: string
  answerEditorValue: string
  explainEditorValue?: string
  knowledge?: Array<{ knowledgeId: string; knowledgeName: string }>
  options?: any[]
  answers: QuestionAnswers
  subQuestionList?: Question[]
  attachObject?: any
  questionStatic?: QuestionStatic
}

interface QuestionAnswers {
  questionUuid?: string
  answer: any
  answerObj?: any
  isAnswered?: boolean
  isRight?: 'true' | 'false' | 'trueFalse' | 'none'
  isRemake?: boolean
  noIsMark?: boolean
  useTime?: number
  qAnswer?: any
  upFileList?: Array<{ url: string; type: 'img' | 'media' | string; name?: string }>
  student_answer_image?: string[]
  AIMessage?: {
    content: Array<{
      text: {
        value: string | object
      }
    }>
  }
}

getStatic 返回结构

interface QuestionStaticResponse {
  data: {
    allnums: number
    allrights: number
  }
}

historyHandler 返回结构

返回历史答题记录。组件会将其合并到题目 answers 中,常见字段如下:

interface HistoryAnswer {
  isRight?: 'true' | 'false' | 'trueFalse' | 'none'
  isAnswered?: boolean
  answer?: any
  answerObj?: any
  userAnswer?: string
  useTime?: number
  smallQuestionList?: any[]
}

fitbAiScoreFn 接口

用于填空题和问答题自动判分。组件会在 questionTypeBasetian_kongwen_da 时调用。

后端调用填空题 AI 判分服务时,接口地址、请求结构、响应解包、结果校验和异常处理详见 AI 填空题判分接入文档

type FitbAiScoreFn = (
  question: Question,
  onMessage?: (text: string) => void
) => Promise<AiScoreResponse>

问答题流式输出时,onMessage 接收接口累计文本。页面展示时只读取 JSON 中的 reasoning 字段。

返回示例:

interface AiScoreResponse {
  data?: {
    contentList?: string[]
    message?: {
      content?: Array<{
        text: {
          value: string | AiScoreValue
        }
      }>
    }
  }
}

interface AiScoreValue {
  score?: number
  full_score?: number
  grade?: string
  reasoning?: string
  results?: Array<{
    score?: number
    reasoning?: string
  }>
}

问答题判定规则:

| 条件 | answers.isRight | | --- | --- | | score 存在且等于 full_score | true | | 未返回 score | false | | score === 0 | false | | 其它有分未满分 | trueFalse |

问答题流式 SSE 封装示例:

import { fetchEventSource } from '@microsoft/fetch-event-source'

export async function getImgAnswerScore(
  data: {
    prompt: string
    questionDbId: string
    questionUuid: string
  },
  onMessage?: (text: string) => void
) {
  let fullText = ''

  await fetchEventSource('/api/api/public/qa/getImgAnswerScore', {
    method: 'POST',
    headers: {
      Accept: 'text/event-stream',
      'Content-Type': 'application/json',
      Authorization: 'Bearer <token>'
    },
    body: JSON.stringify(data),
    onmessage(event) {
      const eventData = JSON.parse(event.data)
      const chunk =
        eventData?.choices?.[0]?.delta?.content ??
        eventData?.content ??
        ''

      if (!chunk) return
      fullText += chunk
      onMessage?.(fullText)
    }
  })

  return {
    data: {
      contentList: [fullText],
      message: {
        content: [
          {
            text: {
              value: fullText
            }
          }
        ]
      }
    }
  }
}

开发环境中如果 Vite 代理配置会把浏览器侧 /api 前缀 rewrite 掉,浏览器请求 /api/api/public/qa/getImgAnswerScore 后,后端实际收到的是 /api/public/qa/getImgAnswerScore

upLoadFileSuccess 接口

type UpLoadFileSuccess = (file: File) => Promise<string>

返回上传后的文件 URL 或路径。编辑器会将该地址插入答题内容,并记录到 answers.upFileList

submitCorrectionFn 接口

type SubmitCorrectionFn = (data: CorrectionForm) => Promise<any>

type CorrectionType = '题干有误' | '答案有误' | '解析有误' | '其他' | '选项有误'

interface CorrectionForm {
  questionUuid: string
  questionDbId: string
  checkedLabelList: CorrectionType[]
  remarks?: string
}

freePractice

自由练习配置组件。组件根据题库知识点、难度、题型数量生成练习,并打开答题页。

<freePractice
  :getList="getKnowledgeList"
  :loadNode="loadKnowledgeNode"
  :getDifficult="getDifficult"
  :Guaher="getQuestionTypes"
  :paotNum="getQuestionTypeCount"
  :submit="createPracticeQuestions"
  :practiceList="savePractice"
  @paramFormNum="onParamForm"
  @practiceForm="onPracticeForm"
/>

Props 接口

| 参数 | 类型 | 说明 | | --- | --- | --- | | getList | () => Promise<{ data: Knowledge[] }> | 获取知识点树根节点 | | loadNode | (node: any, resolve: Function) => void \| Promise<void> | 懒加载知识点子节点 | | getDifficult | () => Promise<{ data: Difficult[] }> | 获取难度列表 | | Guaher | () => Promise<{ data: QuestionType[] }> | 获取题型列表;当前代码中保留但未启用 | | paotNum | () => Promise<{ data: QuestionTypeCount[] }> | 获取所选条件下各题型可用数量 | | submit | () => Promise<{ data: { questionUuids: string[] } }> | 按当前条件抽题 | | practiceList | (form: PracticeCreateForm) => Promise<{ data: { testId: string } }> | 保存练习记录并返回 testId |

Events

| 事件 | 参数 | 说明 | | --- | --- | --- | | paramFormNum | PracticeParamForm | 查询题型数量、开始练习前抛出当前条件 | | practiceForm | PracticeCreateForm | 保存练习前抛出最终练习表单 |

数据结构

interface Knowledge {
  id: string | number
  parentId?: string | number
  name: string
  children?: Knowledge[]
}

interface Difficult {
  id: string | number
  name: string
}

interface QuestionTypeCount {
  questionTypeBase?: string
  questionTypeName?: string
  questionTypeNum: number
  questionNum?: number | ''
}

interface PracticeParamForm {
  workName: string
  practiceType: number
  feedbackWay: number
  questionDbId: string
  questionRange: number
  knowledgeIds: Array<string | number>
  basicDifficult: Array<string | number>
  questions?: QuestionTypeCount[]
  paperTime?: number
  isTiming?: 0 | 1 | boolean
}

interface PracticeCreateForm extends PracticeParamForm {
  questionUuids: string
  client: 'pc' | string
}

notebook

错题本组件。用于展示常错知识点、错题列表、按条件查询、删除错题、从错题生成练习。

<notebook
  :getList="getWrongKnowledge"
  :noteList="getWrongQuestionList"
  :mistakesList="deleteWrongQuestions"
  :mistakesType="getWrongQuestionTypes"
  :practiceList="createWrongPractice"
  :practionList="createKnowledgePractice"
  @noteForm="onNoteQuery"
  @practForm="onKnowledgePracticeForm"
  @practiceForm="onPracticeForm"
  @mistakesForm="onDeleteWrongQuestions"
/>

Props 接口

| 参数 | 类型 | 说明 | | --- | --- | --- | | getList | () => Promise<{ data: WrongKnowledge[] }> | 获取常错知识点 | | noteList | (query: WrongQuestionQuery) => Promise<{ data: WrongQuestion[]; total: number }> | 查询错题列表 | | mistakesList | (questionUuids: string[]) => Promise<any> | 删除错题 | | mistakesType | () => Promise<{ data: QuestionType[] }> | 获取错题题型 | | practiceList | (form: WrongPracticeForm) => Promise<{ data: { testId: string } }> | 从选中错题创建练习/背题 | | practionList | (form: KnowledgePracticeForm) => Promise<{ data: { testId: string } }> | 从常错知识点创建单题练习 |

Events

| 事件 | 参数 | 说明 | | --- | --- | --- | | noteForm | WrongQuestionQuery | 查询错题列表前抛出查询条件 | | practForm | KnowledgePracticeForm | 从知识点生成练习前抛出表单 | | practiceForm | WrongPracticeForm | 从错题生成练习前抛出表单 | | mistakesForm | string[] | 删除错题前抛出题目 UUID 列表 |

数据结构

interface WrongKnowledge {
  knowledgeId: string
  name: string
  count?: number
}

interface WrongQuestionQuery {
  questionTypeName?: string
  inquire?: string
  basicDifficultName?: string
  timeOrder?: string
  startTime?: string
  endTime?: string
  pageNum: number
  pageSize: number
  questionDbId: string
}

interface WrongQuestion {
  questionUuid: string
  questionJsonContent: string
  questionTypeName?: string
  basicDifficultName?: string
  createTime?: string
  checked?: boolean
}

interface WrongPracticeForm {
  practiceType: 6
  questionNum: number
  questionUuids: string[]
  questionDbId: string
  exerciseTpye: '1' | '2' | string
  feedbackWay: 1 | 3
  client: 'pc'
}

interface KnowledgePracticeForm {
  knowledgeId: string
  workName: string
  questionDbId?: string
}

Intelligent

AI 助手组件。当前组件通过 props 接收外部接口函数,不直接绑定后端。

建议按以下形式接入:

<Intelligent
  :currenOptions="{ questionDbId, questionUuid }"
  :set_ai_answer_add="saveAiAnswer"
  :get_img2latex="img2latex"
  :get_has_ai_times="getHasAiTimes"
  :set_model_dialog_add="saveModelDialog"
  :eve_State="0"
  :imgUser="imgUser"
  @close="visible = false"
/>

常用接口:

| 参数 | 类型 | 说明 | | --- | --- | --- | | get_has_ai_times | () => Promise<any> | 获取当前题 AI 可用次数 | | set_model_dialog_add | (data: any) => Promise<any> | 保存模型对话 | | set_ai_answer_add | (data: any) => Promise<any> | 保存 AI 回答 | | get_img2latex | (data: any) => Promise<any> | 图片转 LaTeX |

DraggingSlot

可拖拽容器,需要通过 dragHeader 插槽绑定拖拽事件。

<DraggingSlot :windowSize="{ width: 80, height: 80 }">
  <template #dragHeader="{ startDrag, onDragging }">
    <div @mousedown="startDrag" @mousemove="onDragging">
      拖拽我
    </div>
  </template>
</DraggingSlot>

| 参数 | 类型 | 说明 | | --- | --- | --- | | windowSize | { width: number; height: number } | 拖拽元素尺寸 |

注意事项

  1. 组件依赖 Vue 3、Element Plus、Vue Router、MathJax/TinyMCE 相关运行环境。
  2. 题目详情字段需要与组件内部题型渲染结构一致,尤其是 questionTypeBaseanswersoptions
  3. 问答题 AI 判分结果页面只展示 reasoning 字段;最终判分依赖 scorefull_score
  4. 开发环境使用 Vite 代理时,注意浏览器请求路径和后端真实路径可能不同。
  5. 组件内部部分页面会通过 window.open 跳转到 /paperOnline/#/hepMistake?testId=...,宿主项目需要提供对应路由。