当前位置: 首页 > news >正文

【开发实践】在线考试系统(一) 生成错题知识点的思维导图

一、需求分析设计

        笔者开发了一个在线考试系统,导师提出一个需求:添加对考试错题相关知识点的总结。

        在question表中关联知识点的编号,题目可能关联多个知识点。这里笔者的设计是,只关联一个知识点,便于维护。

        下面是知识点表格的设计:

 masterID关联父级知识点,顶级知识点则为空。

二、思维导图组件

1、d3.js 插件准备

【包管理工具引入d3】:

yarn add d3 / npm install d3推荐使用yarn)

【js方式引入d3】

d3.js 入学者文档连接

2、vue 思维导图组件

这个模块笔者也是从网上获得的样式,原作者写的不太标准,不过也感谢作者大大啦!!

<template><div :id="id"></div>
</template>
<script>import * as d3 from 'd3';export default {props: {data: Object,nodeWidth:{type: Number,default: 160},nodeHeight:{type: Number,default: 40},active:{type: String,default: ''}},watch:{data(curVal, oldVal) {this.$nextTick(() => {this.drawMap()})}},data() {return {id: 'TreeMap' + randomString(4),deep: 0,treeData: null,show: true,demoData: {"label": "中国",link: "demo",url: 'https://baike.baidu.com/item/%E4%B8%AD%E5%9B%BD/1122445?fr=aladdin',"children":[{"label": "浙江",disabled: true,"children":[{"label": "杭州"},{"label": "宁波"},{"label": "温州"},{"label": "绍兴"}]},{"label": "广西","children":[{"label": "桂林","children":[{"label": "秀峰区"},{"label": "叠彩区"},{"label": "象山区"},{"label": "七星区"}]},{"label": "南宁"},{"label": "柳州"},{"label": "防城港"}]},]}}},mounted() {this.$nextTick(() => {this.drawMap()})},methods: {drawMap() {let that = this// 源数据let data = {}// 判断data是否为空对象if (this.data && JSON.stringify(this.data) !== "{}") {data = this.data} else {data = this.demoData}if (!this.treeData) {this.treeData = data} else {// 清空画布d3.select('#' + this.id).selectAll("svg").remove();}let leafList = []getTreeLeaf(data, leafList)let leafNum = leafList.lengthlet TreeDeep = getDepth(data)// 左右内边距let mapPaddingLR = 10// 上下内边距let mapPaddingTB = 0let mapWidth = this.nodeWidth * TreeDeep + mapPaddingLR * 2;let mapHeight = (this.nodeHeight - 4) * leafNum + mapPaddingTB * 2;// 定义画布—— 外边距 10pxlet svgMap = d3.select('#' + this.id).append('svg').attr("width", mapWidth).attr("height", mapHeight).style("margin", "0px")// 定义树状图画布let treeMap = svgMap.append("g").attr("transform", "translate(" + mapPaddingLR + "," + (mapHeight / 2 - mapPaddingTB) + ")");// 将源数据转换为可以生成树状图的数据(有节点 nodes 和连线 links )let treeData = d3.tree()// 设置每个节点的尺寸.nodeSize(// 节点包含后方的连接线 [节点高度,节点宽度][this.nodeHeight, this.nodeWidth])// 设置树状图节点之间的垂直间隔.separation(function (a, b) {// 样式一:节点间等间距// return (a.parent == b.parent ? 1: 2) / a.depth;// 样式二:根据节点子节点的数量,动态调整节点间的间距let rate = (a.parent == b.parent ? (b.children ? b.children.length / 2 : 1) : 2) / a.depth// 间距比例不能小于0.7,避免间距太小而重叠if (rate < 0.7) {rate = 0.7}return rate;})(// 创建层级布局,对源数据进行数据转换d3.hierarchy(data).sum(function (node) {// 函数执行的次数,为树节点的总数,node为每个节点return node.value;}))// 贝塞尔曲线生成器let Bézier_curve_generator = d3.linkHorizontal().x(function (d) {return d.y;}).y(function (d) {return d.x;});//绘制边treeMap.selectAll("path")// 节点的关系 links.data(treeData.links()).enter().append("path").attr("d", function (d) {// 根据name值的长度调整连线的起点var start = {x: d.source.x,// 连线起点的x坐标// 第1个10为与红圆圈的间距,第2个10为link内文字与边框的间距,第3个10为标签文字与连线起点的间距y: d.source.y + 10 + (d.source.data.link ? (getPXwidth(d.source.data.link) + 10) : 0) + getPXwidth(d.source.data.label) + 10};var end = {x: d.target.x, y: d.target.y};return Bézier_curve_generator({source: start, target: end});}).attr("fill", "none").attr("stroke", "#c3c3c3")// 虚线// .attr("stroke-dasharray", "8").attr("stroke-width", 1);// 创建分组——节点+文字let groups = treeMap.selectAll("g")// 节点 nodes.data(treeData.descendants()).enter().append("g").attr("transform", function (d) {var cx = d.x;var cy = d.y;return "translate(" + cy + "," + cx + ")";});//绘制节点(节点前的圆圈)groups.append("circle")// 树的展开折叠.on("click", function (event, node) {let data = node.dataif (data.children) {data.childrenTemp = data.childrendata.children = null} else {data.children = data.childrenTempdata.childrenTemp = null}that.drawMap()}).attr("cursor", 'pointer').attr("r", 4).attr("fill", function (d) {if (d.data.childrenTemp) {return 'red'} else {return 'white'}}).attr("stroke", "red").attr("stroke-width", 1);//绘制标注(节点前的矩形)groups.append("rect").attr("x", 8).attr("y", -10).attr("width",function (d) {return d.data.link ? (getPXwidth(d.data.link) + 10) : 0}).attr("height", 22).attr("fill", "grey")// 添加圆角.attr("rx", 4)//绘制链接方式groups.append("text").attr("x", 12).attr("y", -5).attr("dy", 10).attr("fill", 'white').attr("font-size", 12).text(function (d) {return d.data.link;})//绘制文字groups.append("text").on("click", function (event, node) {let data = node.data// 被禁用的节点,点击无效if (data.disabled) {return}// 有外链的节点,打开新窗口后恢复到思维导图页面if (data.url) {window.open(data.url)that.$emit('activeChange', 'map')return}// 标准节点—— 传出 propif (data.dicType) {that.$emit('dicTypeChange', data.dicType)}// 标准节点—— 传出 propif (data.prop) {that.$emit('activeChange', data.prop)}}).attr("x", function (d) {return 12 + (d.data.link ? (getPXwidth(d.data.link) + 10) : 0)}).attr("fill",function (d) {if (d.data.prop === that.active) {return '#409EFF'}}).attr("font-weight",function (d) {if (d.data.prop === that.active) {return 'bold'}}).attr("font-size", 14).attr("cursor",function (d) {if (d.data.disabled) {return 'not-allowed'} else {return 'pointer'}}).attr("y", -5).attr("dy", 10).attr("slot", function (d) {return d.data.prop;}).text(function (d) {return d.data.label;})},},}// 获取树的深度function getDepth(json) {var arr = [];arr.push(json);var depth = 0;while (arr.length > 0) {var temp = [];for (var i = 0; i < arr.length; i++) {temp.push(arr[i]);}arr = [];for (var i = 0; i < temp.length; i++) {if (temp[i].children && temp[i].children.length > 0) {for (var j = 0; j < temp[i].children.length; j++) {arr.push(temp[i].children[j]);}}}if (arr.length >= 0) {depth++;}}return depth;}// 提取树的子节点,最终所有树的子节点都会存入传入的leafList数组中function getTreeLeaf(treeData, leafList) {// 判断是否为数组if (Array.isArray(treeData)) {treeData.forEach(item => {if (item.children && item.children.length > 0) {getTreeLeaf(item.children, leafList)} else {leafList.push(item)}})} else {if (treeData.children && treeData.children.length > 0) {getTreeLeaf(treeData.children, leafList)} else {leafList.push(treeData)}}}// 获取包含汉字的字符串的长度function getStringSizeLength(string) {//先把中文替换成两个字节的英文,再计算长度return string.replace(/[\u0391-\uFFE5]/g, "aa").length;}// 生成随机的字符串function randomString(strLength) {strLength = strLength || 32;let strLib = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz"let n = "";for (let i = 0; i < strLength; i++) {n += strLib.charAt(Math.floor(Math.random() * strLib.length));}return n}// 获取字符串的像素宽度function getPXwidth(str, fontSize = "12px", fontFamily = "Microsoft YaHei") {var span = document.createElement("span");var result = {};result.width = span.offsetWidth;result.height = span.offsetHeight;span.style.visibility = "hidden";span.style.fontSize = fontSize;span.style.fontFamily = fontFamily;span.style.display = "inline-block";document.body.appendChild(span);if (typeof span.textContent != "undefined") {span.textContent = str;} else {span.innerText = str;}result.width = parseFloat(window.getComputedStyle(span).width) - result.width;// 字符串的显示高度// result.height = parseFloat(window.getComputedStyle(span).height) - result.height;return result.width;}
</script>

这里主要说明一下数据格式:

"label": "知识点", //模块名称
"link": "知识", //标注信息
"url"://连接,
"children":[{"label": "分支1",//以此类推 …………
}]

其他vue模块使用该组件

<superMindmap v-if="showMindMap" :active='active' :data="mapData" @activeChange="activeChange"/>// 点击思维导图节点后,触发变量更新
activeChange(newLabel) {this.active = newLabelthis.reloadMindMap()
},// 重载思维导图
reloadMindMap() {this.showMindMap = falsethis.$nextTick(() => {this.showMindMap = true})
},

三、效果展示

查看对应的考试,即可获取错题知识点的思维导图


完结撒花!!!如果你也觉得文章不错的话,点赞支持一下吧!!!

相关文章:

【开发实践】在线考试系统(一) 生成错题知识点的思维导图

一、需求分析设计 笔者开发了一个在线考试系统&#xff0c;导师提出一个需求&#xff1a;添加对考试错题相关知识点的总结。 在question表中关联知识点的编号&#xff0c;题目可能关联多个知识点。这里笔者的设计是&#xff0c;只关联一个知识点&#xff0c;便于维护。 下面是知…...

Java Web 实战 17 - 计算机网络之传输层协议(2)

大家好 , 这篇文章继续给大家讲解 TCP 协议当中的一些操作 , 比如 : 滑动窗口、流量控制、拥塞控制、延时应答、捎带应答、面向字节流这几个提升 TCP 效率的操作 . 我们还会给大家分析 TCP 连接出现异常的时候 , 该如何处理 . 最后会将 TCP 和 UDP 进行比较 上一篇文章的链接也…...

MyBatis<3>:动态SQL的使用<if><trim><where><set><foreach>

动态SQL是MyBatis的强大特性之一&#xff0c;能够完成不同条件下不同的sql拼接。参考官方文档&#xff1a;https://mybatis.org/mybatis-3/zh/dynamic-sql.html<if>标签看这个场景&#xff0c;有必填字段 和 非必填字段 &#xff0c;当字段不确定是否传入的时候&#xff…...

【超好懂的比赛题解】暨南大学2023东软教育杯ACM校赛个人题解

title : 暨南大学2023东软教育杯ACM校赛 题解 tags : ACM,练习记录 date : 2023-3-26 author : Linno 文章目录暨南大学2023东软教育杯ACM校赛 题解A-小王的魔法B-苏神的遗憾C-神父的碟D-基站建设E-小王的数字F-Uziの真身G-电子围棋H-二分大法I-丁真的小马朋友们J-单车运营K-超…...

go-zero学习及使用中遇到的问题

go-zero学习及使用中遇到的问题1 go-zero入门--单体服务demo1.1 单体服务【官方示例】1.1.1 创建greet服务1.1.2 目录结构1.1.3 编写逻辑1.1.4 启动并访问服务1.2 修改GET入参1.2.1 去除options限制的入参值1.2.2 重启并访问服务1.3 添加post请求【新增方法】1.3.1 修改 greet/…...

CCF-CSP认证 202303 500分题解

202303-1 田地丈量&#xff08;矩阵面积交&#xff09; 矩阵面积交x轴线段交长度*y轴线段交长度 线段交长度&#xff0c;相交的时候是min右端点-max左端点&#xff0c;不相交的时候是0 #include<bits/stdc.h> using namespace std; int n,a,b,ans,x,y,x2,y2; int f(in…...

板内盘中孔设计狂飙,细密间距线路中招

一博高速先生成员&#xff1a;王辉东大风起兮云飞扬&#xff0c;投板兮人心舒畅。赵理工打了哈欠&#xff0c;伸了个懒腰&#xff0c;看了看窗外&#xff0c;对林如烟说道&#xff1a;“春天虽美&#xff0c;但是容易让人沉醉。如烟&#xff0c;快女神节了&#xff0c;要不今晚…...

面试热点题:回溯算法 递增子序列与全排列 II

前言&#xff1a; 如果你一点也不了解什么叫做回溯算法&#xff0c;那么推荐你看看这一篇回溯入门&#xff0c;让你快速了解回溯算法的基本原理及框架 递增子序列 给你一个整数数组 nums &#xff0c;找出并返回所有该数组中不同的递增子序列&#xff0c;递增子序列中 至少有两…...

怎么找回回收站删除的文件

我们都知道&#xff0c;电脑文件都是放在桌面上的&#xff0c;单独存放或者一起存放在文件夹里。但总会有已用完或者是没用的文件&#xff0c;这让我们不得不对其进行清理。而清空回收站也是不可避免的。如果出现了清空文件中还有我们需要的文件&#xff0c;怎么找回回收站删除…...

dp-打家劫舍

你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。给定一个代表每个房屋存放金额的非…...

C++预处理连接

目录定义常量字符串前缀定义枚举类型Boost C库中常常使用预处理连接来定义宏和模板类Google开源的C单元测试框架gtest&#xff0c;使用预处理连接技术创建测试用例和测试方法C预处理连接&#xff08;Preprocessor Concatenation&#xff09;是一种宏定义技巧&#xff0c;用于将…...

3、DRF实战总结:基于类的视图APIView, GenericAPIView和GenericViewSet视图集(附源码)

前面介绍了什么是符合RESTful规范的API接口&#xff0c;以及使用了基于函数的视图(FBV)编写了对文章进行增删查改的API。在本篇文章将使用基于类的视图(Class-based View, CBV)重写之前的接口。 参考&#xff1a; 1、Django开发总结&#xff1a;Django MVT与MVC设计模式&…...

AutoSAR PduR -AutoSAR PDU常用的使用方式【发送,接收,网关】

总目录链接==>> AutoSAR入门和实战系列总目录 @学前问答: AutoSAR PDU在哪里全局定义的? AutoSAR PDU涉及到哪些模块? AutoSAR PDU网关怎么使用? 文章目录 1 AutoSAR PDU发送2 AutoSAR PDU接收3 AutoSAR PDU网关转发4 答疑解析AutoSAR PDU 怎么样通过PduR 实现与其…...

瑟瑟发抖吧~OpenAI刚刚推出王炸——引入ChatGPT插件,开启AI新生态

5分钟学会使用ChatGPT 插件&#xff08;ChatGPT plugins&#xff09;——ChatGPT生态建设的开端ChatGPT插件是什么OpenAI最新官方blog资料表示&#xff0c;已经在ChatGPT中实现了对插件的初步支持。插件是专门为以安全为核心原则的语言模型设计的工具&#xff0c;可帮助ChatGPT…...

脉诊(切脉、诊脉、按脉、持脉)之法——入门篇

认识脉诊何谓脉诊&#xff1f;脉诊的渊源脉诊重要吗&#xff1f;脉诊确有其事&#xff0c;还是故弄玄虚&#xff1f;中医科学吗&#xff1f;如何脉诊&#xff1f;寸口脉诊法何谓脉诊&#xff1f; 所谓脉诊&#xff0c;就是通过把脉来诊断身体健康状况的一种必要手段。 …...

【十二天学java】day09常用api介绍

1.API 1.1API概述 什么是API API (Application Programming Interface) &#xff1a;应用程序编程接口 java中的API 指的就是 JDK 中提供的各种功能的 Java类&#xff0c;这些类将底层的实现封装了起来&#xff0c;我们不需要关心这些类是如何实现的&#xff0c;只需要学习这…...

软件测试 - 测试用例常见面试题

1.测试用例的要素测试用例是为了实施测试而向被测试的系统提供的一组集合, 这组集合包含 : 测试环境, 操作步骤, 测试数据, 预期结果等要素.例如 : 在 B 站输入框输入一个空格, 检查结果测试用例标题 : 输入框输入空格测试环境 : Windows 系统, 谷歌浏览器-版本 111.0.5563.65&…...

几种常见的API接口分页方案

文章目录1 概述2 分页方案2.1 基于偏移量2.2 基于游标3 重复数据处理3.1 基于时间3.2 基于热度3.3 基于推荐1 概述 列表是互联网产品中很常见的一种内容排列形式&#xff0c;而且列表的数据集往往成千上万&#xff0c;一次性返回全量数据集的场景几乎不存在&#xff0c;所以出…...

【Object 类的方法】

在 Java 中&#xff0c;所有类都继承了 Object 类&#xff0c;因此 Object 类中的方法可以在所有 Java 对象中使用。下面是 Object 类中的一些常用方法介绍&#xff1a; equals(Object obj): 用于判断两个对象是否相等。默认情况下&#xff0c;该方法比较的是两个对象的地址是…...

留用户、补内容,在线音乐暗战不停

在线音乐在人们的日常生活中扮演着愈发重要的角色&#xff0c;尤其是在面临巨大压力时&#xff0c;人们往往更倾向于通过倾听一段音乐来缓解内心的紧张与焦虑。而随着在线音乐用户数量的增长以及付费意愿的增强&#xff0c;在线音乐行业也实现了稳步发展。 经过多年的发展&…...

python--exec

在Python中&#xff0c;eval和exec都是用来执行动态代码的内置函数&#xff0c;但它们的作用和使用方式有所不同。 eval(): 将字符串作为Python表达式进行求值&#xff0c;并返回结果。 exec(): 将字符串作为Python语句进行执行&#xff0c;没有返回值。 eval()的使用范围通常限…...

干货分享!这6个高效率办公软件,总有一个值得你收藏!

分享6款高效办公软件&#xff0c;可以解决你很多需求&#xff0c;职场人一定要知道。每一款都是精挑细的&#xff0c;可能有的已经很大众了&#xff0c;但肯定还有小伙伴不知道&#xff0c;废话不多说&#xff0c;直接看&#xff01;&#xff01; 1、Flomo笔记&#xff1a;记录…...

代码随想录刷题-链表总结篇

文章目录链表理论基础单链表双链表循环链表其余知识点链表理论基础单链表双链表循环链表其余知识点移除链表元素习题我的解法虚拟头结点解法设计链表习题我的解法代码随想录代码反转链表习题双指针递归两两交换链表中的节点习题我的解法代码随想录解法删除链表的倒数第N个节点习…...

C++:指针:什么是野指针

野指针目录1&#xff1a;定义2&#xff1a;野指针常见情形2.1 &#xff1a;未初始化的野指针2.2 所指的对象已经消亡2.3 指针释放之后未置空3&#xff1a;避免野指针1&#xff1a;定义 指向非法的内存地址的指针叫做野指针&#xff08;Wild Pointer&#xff09;&#xff0c;也…...

一线大厂高并发Redis缓存架构

文章目录高并发缓存架构设计架构设计思路完整代码开发规范与优化建议键值设计命令使用客户端的使用扩展布隆过滤器redis的过期键的清除策略高并发缓存架构设计 架构设计思路 首先是一个基础的缓存架构&#xff0c;对于新增、修改操作set会对缓存更新&#xff0c;对于查询操作…...

剑指offer-二维数组中的查找

文章目录题目描述题解一 无脑暴力循环题解二 初始二分法&#x1f315;博客x主页&#xff1a;己不由心王道长&#x1f315;! &#x1f30e;文章说明&#xff1a;剑指offer-二维数组中的查找&#x1f30e; ✅系列专栏&#xff1a;剑指offer &#x1f334;本篇内容&#xff1a;对剑…...

怎么设计一个秒杀系统

1、系统部署 秒杀系统部署要单独区别开其他系统单独部署&#xff0c;这个系统的流量肯定很大&#xff0c;单独部署。数据库也要单独用一个部署的数据库或者集群&#xff0c;防止高并发导致整个网站不可用。 2、防止超卖 100个库存&#xff0c;1000个人买&#xff0c;要保证不…...

程序参数解析C/C++库 The Lean Mean C++ Option Parser

开发中我们经常使用程序参数&#xff0c;根据参数的不同来实现不同的功能。POSIX和GNU组织对此都制定了一些标准&#xff0c;为了我们程序更为通用标准&#xff0c;建议遵循这些行业内的规范&#xff0c;本文介绍的开源库The Lean Mean C Option Parser就可以很好满足我们的需求…...

Java中的深拷贝和浅拷贝

目录 &#x1f34e;引出拷贝 &#x1f34e;浅拷贝 &#x1f34e;深拷贝 &#x1f34e;总结 引出拷贝 现在有一个学生类和书包类&#xff0c;在学生类中有引用类型的书包变量&#xff1a; class SchoolBag {private String brand; //书包的品牌private int size; //书…...

大文件上传

上图就是大致的流程一、标题图片上传课程的标题图片Ajax发送请求到后端后端接收到图片使用IO流去保存图片&#xff0c;返回图片的信息对象JS回调函数接收对象通过$("元素id").val(值)&#xff0c;方式给页面form表达img标签src属性值&#xff0c;达到上传图片并回显二…...

电气网站设计/搜索引擎是什么意思啊

目前职场中使用流程图或者思维导图梳理工作思路、制定工作规范已经成为了大家普遍认可的高效率工作方式。那去哪找制作流程图的工具呢&#xff1f;别找了&#xff0c;找不到了&#xff01;最赞的流程图工具都在U妹这&#xff01;一、ProcessOnProcessOn是一款网页版作图工具&am…...

广州制作网站公司/宁波seo外包费用

【回复“1024”&#xff0c;送你一个特别推送】在 Google I/O 2018 开发者大会上&#xff0c;谷歌官方推出了 Android Jetpack&#xff0c;其中包含的 Android 开发架构组件能够帮助我们简化开发流程&#xff0c;从而轻松打造出优质应用。开发者能够利用 Jetpack 组件学习最佳实…...

icp备案证书号查询/沈阳网站制作优化推广

&#xfeff;&#xfeff;Spring实现定时任务方法 标签&#xff1a; springquartztask 2017-03-11 02:02 190人阅读 评论(0) 收藏 举报 分类&#xff1a; spring&#xff08;3&#xff09; 作者同类文章Xtask quartz 版权声明&#xff1a;本文为博主原创文章&#xff0…...

wordpress 建站赚钱/百度云搜索引擎官方入口

说明&#xff1a; next(n)表示&#xff1a;命令是next&#xff0c;n是缩写&#xff1b;b表示break;i b表示info break;cond表示condition;r表示run;s表示step;c表示continue&#xff1b;p表示print&#xff1b;bt表示backtrack命令与参数之间有一个空格 调试运行环境相关命令 …...

织梦系统网站地图模板下载/怎么做网站广告

文章目录官网链接连接性能消耗问题分析数据库连接池的作用市面常见连接池产品和对比国货之光druid连接池使用导入druid依赖硬编码方式&#xff08;了解&#xff09;软编码方式druid配置(了解)官网链接 http://www.apache-druid.cn/GettingStarted/chapter-1.html 连接性能消耗…...

微信企业网站源码下载/不需要验证码的广告平台

//Retry机制public static class Retry{/// <summary>/// 重试零个参数无返回值的方法/// </summary>/// <param name"action">执行方法方法</param>/// <param name"retryInterval">重试间隔</param>/// <param n…...