mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/fe-manage.git
synced 2025-12-06 17:36:44 +08:00
feat(course): 添加自定义考试功能并优化试卷组件
- 新增自定义考试试卷类型支持 - 实现试卷预览和编辑功能 - 添加试题管理组件,支持单选、多选、判断题 - 集成雪花ID生成器用于试题唯一标识 - 优化课程创建流程,支持考试内容配置 - 扩展SCSS样式库,增加flex布局和间距工具类 - 新增课程API模块,完善考试相关接口 - 实现试卷内容动态加载和保存逻辑
This commit is contained in:
@@ -35,6 +35,7 @@
|
|||||||
"quill-blot-formatter": "^1.0.5",
|
"quill-blot-formatter": "^1.0.5",
|
||||||
"quill-image-drop-module": "^1.0.3",
|
"quill-image-drop-module": "^1.0.3",
|
||||||
"quill-image-resize-module": "^3.0.0",
|
"quill-image-resize-module": "^3.0.0",
|
||||||
|
"snowflake-id-js": "^1.0.1",
|
||||||
"sortablejs": "^1.15.0",
|
"sortablejs": "^1.15.0",
|
||||||
"vue": "^3.2.13",
|
"vue": "^3.2.13",
|
||||||
"vue-cookies": "^1.8.2",
|
"vue-cookies": "^1.8.2",
|
||||||
|
|||||||
503
src/api/modules/course.js
Normal file
503
src/api/modules/course.js
Normal file
@@ -0,0 +1,503 @@
|
|||||||
|
/**
|
||||||
|
* 课程的操作,课程的添加,修改,列表查询,课程的审核发布等操作。
|
||||||
|
* 针对于管理员,教师的功能
|
||||||
|
*
|
||||||
|
**/
|
||||||
|
import ajax from "./xajax.js";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存课程基本信息,新增和更新都是此方式
|
||||||
|
* @param {Object} data
|
||||||
|
*{
|
||||||
|
course:{
|
||||||
|
课程的基本信息,具体字段内容另外提供
|
||||||
|
name:课程名称
|
||||||
|
type:课程类型,10微课,21在线课(直播);20:在线课( 录播);30:面授课;40:混合式,
|
||||||
|
summary:摘人
|
||||||
|
overview: 课程简介
|
||||||
|
coverImg:封面图
|
||||||
|
sysType:系统分类,只存储最后一级,
|
||||||
|
resOwner1:资源归属一级的id
|
||||||
|
resOwner2:资源归属二级的id
|
||||||
|
resOwner3:资源归属三级的id
|
||||||
|
forUsers: 目标人群
|
||||||
|
forScene:应用场景
|
||||||
|
openObject: 开放权限
|
||||||
|
value:课程价值
|
||||||
|
tags:标签,多个使用-分隔
|
||||||
|
keywords:关键字
|
||||||
|
device: 1:PC端可见;2:移动端可见;3:多端可见
|
||||||
|
status: 1:未提交(草稿);2:已提交;
|
||||||
|
published:true/false 是否发布
|
||||||
|
enabled:true/false 启用、停用
|
||||||
|
isTop: true/false 是否置顶
|
||||||
|
source:整数,课程来源,1:内部;2;外部
|
||||||
|
|
||||||
|
},
|
||||||
|
teachers:[
|
||||||
|
{
|
||||||
|
teacherId:教师的id,
|
||||||
|
teacherName:教师的名称
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const saveBase = function (data) {
|
||||||
|
return ajax.postJson("/systemapi/xboe/m/course/manage/save", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 仅仅是保存课程信息,不包括教师信息
|
||||||
|
* @param {Object} data
|
||||||
|
*/
|
||||||
|
const saveOnlyCourse = function (data) {
|
||||||
|
return ajax.postJson(
|
||||||
|
"/systemapi/xboe/m/course/manage/save-only-course",
|
||||||
|
data
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**提交课程*/
|
||||||
|
const submitCourse = function (data) {
|
||||||
|
return ajax.postJson("/systemapi/xboe/m/course/manage/submit", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**撤销已提交审核的课程*/
|
||||||
|
const revokeSubmit = function (id) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/revoke", { id });
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制课程
|
||||||
|
* @param {Object}
|
||||||
|
*/
|
||||||
|
const copyCourse = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/copy", data);
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
查询课程是否有重复名称
|
||||||
|
*/
|
||||||
|
const isRedoName = function () {
|
||||||
|
return ajax.get("/systemapi/xboe/m/course/manage/isRedoName");
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
查询当前添加课程是否已有
|
||||||
|
courseName 要添加的课程姓名
|
||||||
|
*/
|
||||||
|
const isCourseName = function (courseName, courseId) {
|
||||||
|
return ajax.get(
|
||||||
|
`/systemapi/xboe/m/course/manage/isCourseName?courseName=${courseName}&courseId=${courseId}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 查询修改日志,列表,不分页
|
||||||
|
* @param {Object} params
|
||||||
|
* {
|
||||||
|
num:数量,可以不传,默认是10条,最新的10条
|
||||||
|
courseId:课程的id
|
||||||
|
name: 修改人
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const findUpdateLogs = function (params) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/upldate-logs", params);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据id获取修改的详细信息
|
||||||
|
* @param {Object} id
|
||||||
|
*/
|
||||||
|
const getUpdateLog = function (id) {
|
||||||
|
return ajax.get(
|
||||||
|
"/systemapi/xboe/m/course/manage/upldate-log-detail?id=" + id
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存课程的一条学习内容信息,新增和更新都是此方式
|
||||||
|
* @param {Object} data
|
||||||
|
* {
|
||||||
|
content:内容 {
|
||||||
|
courseId,csectionId章节id(微课为空),sortIndex排序顺序(微课为空),contentType 内容类型(图文41,连接52,作业60,考试61,评估62)
|
||||||
|
contentName,contentRefId无关联内容时为空(此字段内容后台会控制),content具体的内容
|
||||||
|
}
|
||||||
|
|
||||||
|
homework:作业信息 单个对象{
|
||||||
|
courseId,contentId上面对象的id(后台会控制),name,content,file,deadTime,
|
||||||
|
submitMode:1表提交附件,2直接填写,3表两者都可以
|
||||||
|
}
|
||||||
|
exam:考试信息 单个对象
|
||||||
|
{
|
||||||
|
courseId,contentId上面对象的id(后台会控制),testName,testDuration考试时长,
|
||||||
|
paperType试卷类型 1自定义,2使用已有试卷
|
||||||
|
paperId试题的id,使用已有试卷时保存选择试卷的id
|
||||||
|
showAnalysis是否显示解析,showAnswer否显示答案,times尝试次数
|
||||||
|
arrange试题排序 0表不乱序,1试题乱序,2选项乱序,3全部乱序
|
||||||
|
scoringType评分方式 1最高一次,2最后一次
|
||||||
|
passLine及格线整数
|
||||||
|
randomMode是否随机模式true/false
|
||||||
|
paperContent试卷的内容,json存储的对象{items:[]}
|
||||||
|
|
||||||
|
}
|
||||||
|
assess:评估信息,list 多条记录.
|
||||||
|
[
|
||||||
|
{ courseId,contentId,assessId评估id(一期为空),question问题,qType问题类型}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const saveContent = function (data) {
|
||||||
|
return ajax.postJson("/systemapi/xboe/m/course/content/save", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新课程内容的顺序
|
||||||
|
* @param {String} cid //课程的id
|
||||||
|
* @param {Array} items
|
||||||
|
* [
|
||||||
|
* {
|
||||||
|
* id:章的id
|
||||||
|
* index:整数,顺序值
|
||||||
|
* items:[
|
||||||
|
* {id:内容的id,index:顺序值}
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
const updateContentOrders = function (cid, items) {
|
||||||
|
return ajax.postJson(
|
||||||
|
"/systemapi/xboe/m/course/content/update-orders/" + cid,
|
||||||
|
items
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 课程的详细信息
|
||||||
|
* @param {String} id
|
||||||
|
*/
|
||||||
|
const detail = function (id) {
|
||||||
|
return ajax.get("/systemapi/xboe/m/course/manage/detail?id=" + id);
|
||||||
|
};
|
||||||
|
const getDictIds = function (pid, type) {
|
||||||
|
return ajax.get(
|
||||||
|
`/systemapi/xboe/m/course/manage/getDictIds?pid=${pid}&type=${type}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* 更新内容的名称
|
||||||
|
* @param {Object} data
|
||||||
|
* {
|
||||||
|
id:'',
|
||||||
|
name:''
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const updateContentName = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/content/update-name", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除一条学习内容
|
||||||
|
* @param {Object} data
|
||||||
|
* {
|
||||||
|
id: 内容的id,
|
||||||
|
ctype:对应内容的类型contentType
|
||||||
|
erasable:是否物理删除,此值是课程信息中系统带过来的字段,直接使用它就可以了
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
const delContent = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/content/delete", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存课程的章信息,新增和修改保存都是一个
|
||||||
|
* @param {Object} data
|
||||||
|
* courseId:课程的id
|
||||||
|
* name:章节名称
|
||||||
|
description:章节说明
|
||||||
|
parentId: 上级id。如果没有可以填“-1”字符串
|
||||||
|
orderIndex:显示顺序,顺序索引,整数
|
||||||
|
*/
|
||||||
|
const saveSection = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/content/save-section", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 删除章节目录,注意只有目录下没有学习内容时才允许删除
|
||||||
|
* @param {Object} data
|
||||||
|
*/
|
||||||
|
const delSection = function (id) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/content/delete-section?id=" + id);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据课程学习内容的id。获取作业信息,只有学习内容是作业时才会有信息
|
||||||
|
* @param {Object} ccid
|
||||||
|
*/
|
||||||
|
const getHomework = function (ccid) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/content/homework?ccid=" + ccid);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据课程学习内容的id。获取考试信息,只有学习内容是考试时才会有信息
|
||||||
|
* @param {Object} ccid
|
||||||
|
*/
|
||||||
|
const getExam = function (ccid) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/content/exam?ccid=" + ccid);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据课程学习内容的id。获取评估信息,评估内容可以获取
|
||||||
|
* @param {Object} ccid
|
||||||
|
*/
|
||||||
|
const getAssess = function (ccid) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/content/assess?ccid=" + ccid);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理列表查询
|
||||||
|
* @param {Object} query
|
||||||
|
* pageIndex:第几页
|
||||||
|
* pageSize:每页多少条
|
||||||
|
* resOwner1:资源归属一级的id
|
||||||
|
resOwner2:资源归属二级的id
|
||||||
|
resOwner3:资源归属三级的id
|
||||||
|
types:授课方式,多个使用 - 分隔
|
||||||
|
scenes:应用场景,多个使用 - 分隔
|
||||||
|
publish:true/false 是否发布,空值或不传就是全部
|
||||||
|
aid:创建人 aid
|
||||||
|
sysCreateUser: 创建人姓名
|
||||||
|
keyword:查询关键词
|
||||||
|
sysTypes:系统的分类,多级使用 - 分隔,注一期功能是分类的最后一级值,不支持多个的查询
|
||||||
|
orderField:排序字段 id s
|
||||||
|
orderAsc:true/false 是否是正序,从小到大
|
||||||
|
status:状态,多个使用 - 分隔 1代表待审核 5代表已审核 1 未提交 2 已提交 5 审核完成
|
||||||
|
type:课程类型,10微课,21在线课(直播);20:在线课( 录播);30:面授课;40:混合式,
|
||||||
|
name 课程名称
|
||||||
|
|
||||||
|
*/
|
||||||
|
const pageList = function (query) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/pagelist", query);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**计算待审核课程*/
|
||||||
|
const countWaitAudit = function () {
|
||||||
|
return ajax.get("/systemapi/xboe/m/course/manage/wait-audit-num");
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* [已用courseAudit中的hrbpAuditList替换]
|
||||||
|
* 当前用户需要审核的课程列表
|
||||||
|
* @param {Object} query 同pageList
|
||||||
|
*/
|
||||||
|
const auditList = function (query) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/audit-pagelist", query);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 【已移到courseAudit中】
|
||||||
|
* 教师需要审核的课程列表
|
||||||
|
*/
|
||||||
|
const teacherAuditList = function (query) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/audit/teacher-course", query);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定审核人,转审核人
|
||||||
|
* 点击“转审” 弹出教师查询窗口,查询教师,填写备注,提交,调用此接口
|
||||||
|
* @param {Object} data
|
||||||
|
* {courseId:课程id,teacherId:指定的审核人教师的id,teacherName:教师名称,remark:备注}
|
||||||
|
*/
|
||||||
|
const auditAppoint = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/audit/appoint", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取审核信息,上面教师点击审核课程时,用于查询,上面“转审”时,用户填写的备注信息
|
||||||
|
* @param {courseId:'课程id',teacherId:'可以不填写,系统会查询当前人'} data
|
||||||
|
*/
|
||||||
|
const getAuditInfo = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/audit/infos", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员的课程审核处理
|
||||||
|
* @param {Object} query {id:课程id,title:课程的名称, Boolean pass 是否通过,remark 备注}
|
||||||
|
*/
|
||||||
|
const audit = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/audit", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核记录列表,分页查询
|
||||||
|
*/
|
||||||
|
const auditPageRecords = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/audit/page-records", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审核记录列表,要卖课程id,查询出审核列记录信息
|
||||||
|
* { courseId:必须}
|
||||||
|
*/
|
||||||
|
const auditCourseRecords = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/audit/course-records", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员的课程发布,当前已经不再使用了
|
||||||
|
* @param {Object} query {ids:课程id,多个使用逗号分隔,title:课程的名称, Boolean pass 是否发布}
|
||||||
|
*/
|
||||||
|
const publish = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/publish", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const auditAndPublish = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/audit-publish", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置top
|
||||||
|
* @param {Object} query {ids:课程id,多个使用逗号分隔,title:课程的名称,Boolean top 是否置顶}
|
||||||
|
*/
|
||||||
|
const setTop = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/top", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员的设置启用停用
|
||||||
|
* @param {Object} query {ids:课程id,多个使用逗号分隔,title:课程的名称, Boolean enabled 是否启用}
|
||||||
|
*/
|
||||||
|
const setEnabled = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/enabled", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 管理员的删除课程
|
||||||
|
* erasable 此值是课程信息带过来的,直接传就可以
|
||||||
|
* @param {Object} query {id:课程id,多个使用逗号分隔,Boolean erasable 是否物理删除,title:课程的名称, remark 备注}
|
||||||
|
*/
|
||||||
|
const del = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/delete", data);
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
详情
|
||||||
|
*/
|
||||||
|
const detailFew = function (id) {
|
||||||
|
return ajax.get("/systemapi/xboe/m/course/portal/detail-few?id=" + id);
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
直接审核,教师提交审核
|
||||||
|
*/
|
||||||
|
const sumbits = function (data) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/sumbits", data);
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
教师授课记录
|
||||||
|
*/
|
||||||
|
const teacherCourse = function (teacherId) {
|
||||||
|
return ajax.get(
|
||||||
|
"/systemapi/xboe/m/course/manage/teacher-course?teacherId=" + teacherId
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
教师授课记录导出
|
||||||
|
@param teacherId 教师id
|
||||||
|
*/
|
||||||
|
const exportTeacherCourse = function (teacherId) {
|
||||||
|
return ajax.post(
|
||||||
|
"/systemapi/xboe/m/course/manage/export-teacher-course?teacherId=" +
|
||||||
|
teacherId
|
||||||
|
);
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
*待审核课程记录导出
|
||||||
|
* resOwner1:资源归属一级的id
|
||||||
|
resOwner2:资源归属二级的id
|
||||||
|
resOwner3:资源归属三级的id
|
||||||
|
types:授课方式,多个使用 - 分隔
|
||||||
|
scenes:应用场景,多个使用 - 分隔
|
||||||
|
publish:true/false 是否发布,空值或不传就是全部
|
||||||
|
aid:创建人 aid
|
||||||
|
sysCreateUser: 创建人姓名
|
||||||
|
keyword:查询关键词
|
||||||
|
sysTypes:系统的分类,多级使用 - 分隔,注一期功能是分类的最后一级值,不支持多个的查询
|
||||||
|
orderField:排序字段 id s
|
||||||
|
orderAsc:true/false 是否是正序,从小到大
|
||||||
|
status:状态,多个使用 - 分隔 1代表待审核 5代表已审核 1 未提交 2 已提交 5 审核完成
|
||||||
|
type:课程类型,10微课,21在线课(直播);20:在线课( 录播);30:面授课;40:混合式,
|
||||||
|
name 课程名称
|
||||||
|
*/
|
||||||
|
const exportCourseAudit = function (query) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/exportCourseAudit", query);
|
||||||
|
};
|
||||||
|
/*
|
||||||
|
参数同上待审核课程记录导出
|
||||||
|
课程的导出和已审核的课程导出
|
||||||
|
*/
|
||||||
|
const exportCourse = function (query) {
|
||||||
|
return ajax.post("/systemapi/xboe/m/course/manage/exportCourse", query);
|
||||||
|
};
|
||||||
|
|
||||||
|
//判断受众id是否有关联
|
||||||
|
const queryCrowd = function (query) {
|
||||||
|
return ajax.postJson("/systemapi/xboe/m/course/manage/queryCrowd", query);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 二次查询
|
||||||
|
* @param{
|
||||||
|
* ids
|
||||||
|
* }
|
||||||
|
* */
|
||||||
|
const ids = function (data) {
|
||||||
|
return ajax.postJson("/systemapi/xboe/m/course/manage/ids", data);
|
||||||
|
};
|
||||||
|
|
||||||
|
const saveTip = function () {
|
||||||
|
return ajax.postJson("/systemapi/xboe/m/course/manage/saveTip");
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
saveBase,
|
||||||
|
submitCourse,
|
||||||
|
revokeSubmit,
|
||||||
|
copyCourse,
|
||||||
|
findUpdateLogs,
|
||||||
|
getUpdateLog,
|
||||||
|
detail,
|
||||||
|
getDictIds,
|
||||||
|
saveContent,
|
||||||
|
pageList,
|
||||||
|
setEnabled,
|
||||||
|
del,
|
||||||
|
publish,
|
||||||
|
saveSection,
|
||||||
|
getHomework,
|
||||||
|
countWaitAudit,
|
||||||
|
auditList,
|
||||||
|
teacherAuditList,
|
||||||
|
auditAppoint,
|
||||||
|
getAuditInfo,
|
||||||
|
audit,
|
||||||
|
auditPageRecords,
|
||||||
|
auditCourseRecords,
|
||||||
|
auditAndPublish,
|
||||||
|
getAssess,
|
||||||
|
setTop,
|
||||||
|
delSection,
|
||||||
|
getExam,
|
||||||
|
delContent,
|
||||||
|
updateContentName,
|
||||||
|
updateContentOrders,
|
||||||
|
saveOnlyCourse,
|
||||||
|
isRedoName,
|
||||||
|
isCourseName,
|
||||||
|
detailFew,
|
||||||
|
sumbits,
|
||||||
|
teacherCourse,
|
||||||
|
exportTeacherCourse,
|
||||||
|
exportCourseAudit,
|
||||||
|
exportCourse,
|
||||||
|
queryCrowd,
|
||||||
|
ids,
|
||||||
|
saveTip,
|
||||||
|
};
|
||||||
@@ -789,6 +789,9 @@ textarea {
|
|||||||
margin-left: $i * 1px;
|
margin-left: $i * 1px;
|
||||||
margin-right: $i * 1px;
|
margin-right: $i * 1px;
|
||||||
}
|
}
|
||||||
|
.m#{$i} {
|
||||||
|
margin: $i * 1px;
|
||||||
|
}
|
||||||
.fs#{$i} {
|
.fs#{$i} {
|
||||||
font-size: $i * 1px;
|
font-size: $i * 1px;
|
||||||
}
|
}
|
||||||
@@ -812,7 +815,28 @@ textarea {
|
|||||||
padding-left: $i * 1px;
|
padding-left: $i * 1px;
|
||||||
padding-right: $i * 1px;
|
padding-right: $i * 1px;
|
||||||
}
|
}
|
||||||
|
.p#{$i} {
|
||||||
|
padding: $i * 1px;
|
||||||
|
}
|
||||||
.w#{$i} {
|
.w#{$i} {
|
||||||
width: $i * 1px;
|
width: $i * 1px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
.align-center {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.justify-content-c {
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.justify-content-b {
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.justify-content-e {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
.justify-content-s {
|
||||||
|
justify-content: flex-start;
|
||||||
|
}
|
||||||
|
|||||||
@@ -118,18 +118,41 @@ const columns = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
// 事件发射
|
// 事件发射
|
||||||
const emit = defineEmits(["chooseItem", "choosePreviewItem"]);
|
const emit = defineEmits(["chooseItem", "choosePreviewItem", "chooseCusExam"]);
|
||||||
|
|
||||||
// 处理选择项目
|
// 处理选择项目
|
||||||
const handleChooseItem = (row) => {
|
const handleChooseItem = (row) => {
|
||||||
emit("chooseItem", {
|
switch (props.resType) {
|
||||||
|
case 61:
|
||||||
|
console.log(row);
|
||||||
|
if (row.counts === 0) {
|
||||||
|
ElMessage.error("此试卷无试题内容,请重新选择");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// emit("chooseItem", {
|
||||||
|
// ...row,
|
||||||
|
// isDrag: false,
|
||||||
|
// completeSetup: 0,
|
||||||
|
// setupTage: "",
|
||||||
|
// resType: props.resType,
|
||||||
|
// dir: props.resType === 50 ? "scorm" : "course",
|
||||||
|
// });
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let obj = {
|
||||||
...row,
|
...row,
|
||||||
isDrag: false,
|
isDrag: false,
|
||||||
completeSetup: 0,
|
completeSetup: 0,
|
||||||
setupTage: "",
|
setupTage: "",
|
||||||
resType: props.resType,
|
resType: props.resType,
|
||||||
dir: props.resType === 50 ? "scorm" : "course",
|
dir: props.resType === 50 ? "scorm" : "course",
|
||||||
});
|
};
|
||||||
|
if (props.resType === 61) {
|
||||||
|
obj.paperType = 2;
|
||||||
|
}
|
||||||
|
emit("chooseItem", obj);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handlePreviewItem = (row) => {
|
const handlePreviewItem = (row) => {
|
||||||
@@ -267,6 +290,14 @@ const handleBeforeUpload = (file) => {
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
const openCusExam = () => {
|
||||||
|
emit("chooseCusExam", {
|
||||||
|
resType: props.resType,
|
||||||
|
dir: props.resType === 50 ? "scorm" : "course",
|
||||||
|
paperType: 1,
|
||||||
|
name: "自定义考试",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// 生命周期
|
// 生命周期
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -292,7 +323,10 @@ onMounted(() => {
|
|||||||
>上传新{{ getType(props.resType) }}</el-button
|
>上传新{{ getType(props.resType) }}</el-button
|
||||||
>
|
>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
<el-button v-if="[61].includes(props.resType)" type="primary"
|
<el-button
|
||||||
|
v-if="[61].includes(props.resType)"
|
||||||
|
type="primary"
|
||||||
|
@click="openCusExam"
|
||||||
>自定义考试</el-button
|
>自定义考试</el-button
|
||||||
>
|
>
|
||||||
<span class="desc ml10" v-if="![61].includes(props.resType)"
|
<span class="desc ml10" v-if="![61].includes(props.resType)"
|
||||||
|
|||||||
@@ -0,0 +1,417 @@
|
|||||||
|
<template>
|
||||||
|
<div class="simple-paper">
|
||||||
|
<!-- 顶部操作栏 -->
|
||||||
|
<div class="toolbar">
|
||||||
|
<div class="toolbar-buttons">
|
||||||
|
<el-button-group>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="addQuestion(101)"
|
||||||
|
:icon="Plus"
|
||||||
|
size="small"
|
||||||
|
:disabled="disabled"
|
||||||
|
>单选</el-button
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="addQuestion(102)"
|
||||||
|
:icon="Plus"
|
||||||
|
size="small"
|
||||||
|
:disabled="disabled"
|
||||||
|
>多选</el-button
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="addQuestion(103)"
|
||||||
|
:icon="Plus"
|
||||||
|
size="small"
|
||||||
|
:disabled="disabled"
|
||||||
|
>判断</el-button
|
||||||
|
>
|
||||||
|
</el-button-group>
|
||||||
|
</div>
|
||||||
|
<div class="toolbar-tip">点题干编辑</div>
|
||||||
|
<div class="toolbar-info">
|
||||||
|
<el-checkbox v-model="optShow" :disabled="disabled"
|
||||||
|
>显示选项</el-checkbox
|
||||||
|
>
|
||||||
|
<span class="toolbar-stats">
|
||||||
|
共<span class="bigred"> {{ data.items.length }} </span>题,
|
||||||
|
<span class="bigred">{{ total }}</span> 分
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 试卷内容 -->
|
||||||
|
<div class="paper-container">
|
||||||
|
<div
|
||||||
|
v-for="(item, idx) in data.items"
|
||||||
|
:key="idx"
|
||||||
|
class="paper-item"
|
||||||
|
@mouseenter="showOptions(item)"
|
||||||
|
@mouseleave="hideOptions(item)"
|
||||||
|
>
|
||||||
|
<div class="paper-item-content">
|
||||||
|
<div class="paper-item-main">
|
||||||
|
<!-- 显示模式 -->
|
||||||
|
<div
|
||||||
|
v-if="editIndex !== idx"
|
||||||
|
class="paper-item-question"
|
||||||
|
@click="handleEditItem(item, idx)"
|
||||||
|
>
|
||||||
|
{{ idx + 1 }}.【
|
||||||
|
<span v-if="item.type === 101">单选题</span>
|
||||||
|
<span v-if="item.type === 102">多选题</span>
|
||||||
|
<span v-if="item.type === 103">判断题</span>
|
||||||
|
】{{ item.content }}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 选项显示(悬停或全局显示) -->
|
||||||
|
<div v-if="optShow || item.optShow" class="paper-options">
|
||||||
|
<div
|
||||||
|
v-for="(opt, optIdx) in item.options"
|
||||||
|
:key="optIdx"
|
||||||
|
class="paper-option"
|
||||||
|
:class="{ 'paper-option-selected': opt.answer }"
|
||||||
|
@click="setOptAnswer(item, opt)"
|
||||||
|
>
|
||||||
|
{{ String.fromCharCode(65 + optIdx) }}. {{ opt.content }}
|
||||||
|
<i v-if="opt.answer" class="el-icon-check option-checkmark"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 编辑模式 -->
|
||||||
|
<div v-if="editIndex === idx" class="paper-item-editor">
|
||||||
|
<el-input v-model="item.content" placeholder="试题的内容" />
|
||||||
|
<div class="editor-tip">注:一行一个选项</div>
|
||||||
|
<el-input
|
||||||
|
v-model="curTextOptions"
|
||||||
|
type="textarea"
|
||||||
|
:rows="5"
|
||||||
|
placeholder="每行一个选项内容"
|
||||||
|
class="editor-textarea"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
@click="handleSaveItem(item)"
|
||||||
|
type="warning"
|
||||||
|
size="small"
|
||||||
|
class="editor-save-btn"
|
||||||
|
>
|
||||||
|
编辑完成
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 分数与删除 -->
|
||||||
|
<div class="paper-item-actions">
|
||||||
|
<el-input
|
||||||
|
:disabled="disabled"
|
||||||
|
v-model="item.score"
|
||||||
|
class="score-input"
|
||||||
|
size="small"
|
||||||
|
placeholder="分数"
|
||||||
|
/>
|
||||||
|
<el-button
|
||||||
|
:disabled="disabled"
|
||||||
|
@click="removeQuestion(idx)"
|
||||||
|
type="danger"
|
||||||
|
size="small"
|
||||||
|
:icon="Delete"
|
||||||
|
class="remove-btn"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted, watch } from "vue";
|
||||||
|
import { Plus, Delete } from "@element-plus/icons-vue";
|
||||||
|
import { snowflakeGenerator } from "snowflake-id-js";
|
||||||
|
import { ElButtonGroup, ElButton, ElInput, ElCheckbox } from "element-plus";
|
||||||
|
|
||||||
|
// Props
|
||||||
|
const props = defineProps({
|
||||||
|
data: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
items: [],
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
disabled: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const emit = defineEmits(["update:data"]);
|
||||||
|
|
||||||
|
// 监听 data 变化并触发 update 事件
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
(newVal) => {
|
||||||
|
emit("update:data", newVal);
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
);
|
||||||
|
|
||||||
|
// 响应式状态
|
||||||
|
const editIndex = ref(-1);
|
||||||
|
const curTextOptions = ref("");
|
||||||
|
const optShow = ref(true);
|
||||||
|
const generator = ref(null);
|
||||||
|
|
||||||
|
// 默认测试题
|
||||||
|
const qdata = [
|
||||||
|
{
|
||||||
|
id: "1",
|
||||||
|
type: 101,
|
||||||
|
score: 5,
|
||||||
|
checked: false,
|
||||||
|
optShow: false,
|
||||||
|
content: "点击编辑试题内容",
|
||||||
|
options: [
|
||||||
|
{ id: "11", content: "选项", answer: false },
|
||||||
|
{ id: "12", content: "选项", answer: true },
|
||||||
|
{ id: "13", content: "选项", answer: false },
|
||||||
|
{ id: "14", content: "选项", answer: false },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
const seed = Math.floor(Math.random() * 50) + 1;
|
||||||
|
generator.value = snowflakeGenerator(seed);
|
||||||
|
initItem();
|
||||||
|
});
|
||||||
|
|
||||||
|
// 总分计算
|
||||||
|
const total = computed(() => {
|
||||||
|
return props.data.items.reduce((sum, item) => {
|
||||||
|
return sum + (parseFloat(item.score) || 0);
|
||||||
|
}, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 初始化测试数据(仅当 items 为空)
|
||||||
|
function initItem() {
|
||||||
|
if (props.data.items.length === 0) {
|
||||||
|
qdata.forEach((item) => {
|
||||||
|
props.data.items.push({ ...item });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 显示/隐藏选项
|
||||||
|
function showOptions(item) {
|
||||||
|
item.optShow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function hideOptions(item) {
|
||||||
|
item.optShow = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置答案(单选/多选逻辑)
|
||||||
|
function setOptAnswer(item, opt) {
|
||||||
|
if (props.disabled) return;
|
||||||
|
if (item.type !== 102) {
|
||||||
|
// 单选或判断:清空其他选项
|
||||||
|
item.options.forEach((o) => (o.answer = false));
|
||||||
|
}
|
||||||
|
// 切换当前选项
|
||||||
|
opt.answer = !opt.answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 进入编辑
|
||||||
|
function handleEditItem(item, idx) {
|
||||||
|
if (props.disabled) return;
|
||||||
|
editIndex.value = idx;
|
||||||
|
// 拼接选项为文本(每行一个)
|
||||||
|
const text = item.options.map((opt) => opt.content).join("\n");
|
||||||
|
curTextOptions.value = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存编辑
|
||||||
|
function handleSaveItem(item) {
|
||||||
|
if (props.disabled) return;
|
||||||
|
const lines = curTextOptions.value
|
||||||
|
.trim()
|
||||||
|
.split("\n")
|
||||||
|
.filter((line) => line.trim() !== "");
|
||||||
|
if (lines.length === 0) return;
|
||||||
|
|
||||||
|
// 保存当前选中的索引
|
||||||
|
const oldAnswers = item.options
|
||||||
|
.map((opt, i) => (opt.answer ? i : -1))
|
||||||
|
.filter((i) => i !== -1);
|
||||||
|
|
||||||
|
// 重建 options
|
||||||
|
const newOptions = lines.map((content, i) => {
|
||||||
|
const id = generator.value.next().value;
|
||||||
|
const isAnswer = oldAnswers.includes(i);
|
||||||
|
return { id, content, answer: isAnswer };
|
||||||
|
});
|
||||||
|
|
||||||
|
item.options = newOptions;
|
||||||
|
editIndex.value = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加题目
|
||||||
|
function addQuestion(type) {
|
||||||
|
const id = generator.value.next().value;
|
||||||
|
let options = [];
|
||||||
|
|
||||||
|
if (type === 101 || type === 102) {
|
||||||
|
// 单选/多选:3个默认选项
|
||||||
|
options = Array.from({ length: 3 }, (_, i) => ({
|
||||||
|
id: generator.value.next().value,
|
||||||
|
content: "选项",
|
||||||
|
answer: false,
|
||||||
|
}));
|
||||||
|
} else if (type === 103) {
|
||||||
|
// 判断题
|
||||||
|
options = [
|
||||||
|
{ id: generator.value.next().value, content: "正确", answer: false },
|
||||||
|
{ id: generator.value.next().value, content: "错误", answer: false },
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
const newItem = {
|
||||||
|
id,
|
||||||
|
type,
|
||||||
|
score: 5,
|
||||||
|
optShow: false,
|
||||||
|
content: "点击编辑试题内容",
|
||||||
|
options,
|
||||||
|
};
|
||||||
|
|
||||||
|
props.data.items.push(newItem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除题目
|
||||||
|
function removeQuestion(index) {
|
||||||
|
props.data.items.splice(index, 1);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.simple-paper {
|
||||||
|
.toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 5px;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.toolbar-buttons {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-tip {
|
||||||
|
flex: 1;
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 10px;
|
||||||
|
color: #919191;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toolbar-info {
|
||||||
|
flex: 1;
|
||||||
|
line-height: 40px;
|
||||||
|
text-align: right;
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
.toolbar-stats {
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.paper-container {
|
||||||
|
border: 1px solid #dfdfdf;
|
||||||
|
padding: 5px 0;
|
||||||
|
//height: 500px;
|
||||||
|
//overflow: auto;
|
||||||
|
|
||||||
|
.paper-item {
|
||||||
|
padding: 5px 10px;
|
||||||
|
border-bottom: 1px solid #cccccc;
|
||||||
|
|
||||||
|
.paper-item-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 5px;
|
||||||
|
|
||||||
|
.paper-item-main {
|
||||||
|
flex: 1;
|
||||||
|
padding-right: 5px;
|
||||||
|
|
||||||
|
.paper-item-question {
|
||||||
|
cursor: pointer;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.paper-options {
|
||||||
|
padding: 5px 0;
|
||||||
|
|
||||||
|
.paper-option {
|
||||||
|
line-height: 25px;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 2px 5px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.paper-option-selected {
|
||||||
|
color: green;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.option-checkmark {
|
||||||
|
float: right;
|
||||||
|
color: green;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.paper-item-editor {
|
||||||
|
.editor-tip {
|
||||||
|
color: red;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-textarea {
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.editor-save-btn {
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.paper-item-actions {
|
||||||
|
width: 110px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.score-input {
|
||||||
|
width: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-btn {
|
||||||
|
margin-left: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.bigred {
|
||||||
|
color: red;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
214
src/components/CreatedCourse/preview/PaperComp.vue
Normal file
214
src/components/CreatedCourse/preview/PaperComp.vue
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
<script setup>
|
||||||
|
import {
|
||||||
|
ElForm,
|
||||||
|
ElFormItem,
|
||||||
|
ElInput,
|
||||||
|
ElRadioGroup,
|
||||||
|
ElRadio,
|
||||||
|
ElCheckbox,
|
||||||
|
ElInputNumber,
|
||||||
|
} from "element-plus";
|
||||||
|
import { onMounted, ref } from "vue";
|
||||||
|
import apiCourse from "@/api/modules/course";
|
||||||
|
import apiExamPaper from "@/api/modules/paper";
|
||||||
|
import SimplePaper from "./ChildrenComponent/SimplePaper.vue";
|
||||||
|
defineOptions({
|
||||||
|
resType: 61,
|
||||||
|
});
|
||||||
|
const props = defineProps({
|
||||||
|
dialogVideoForm: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
name: "",
|
||||||
|
filePath: "",
|
||||||
|
isDrag: true,
|
||||||
|
completeSetup: 0,
|
||||||
|
setupTage: 0,
|
||||||
|
openType: "",
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
isPreview: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
|
classId: {
|
||||||
|
type: String,
|
||||||
|
default: "",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
import { useMediaComponent } from "@/hooks/useMediaComponent";
|
||||||
|
// Emit updates to parent component
|
||||||
|
const emit = defineEmits(["update:dialogVideoForm"]);
|
||||||
|
|
||||||
|
// 使用hook处理公共逻辑
|
||||||
|
const { localDialogVideoForm, updateFormValue, fileBaseUrl } =
|
||||||
|
useMediaComponent(props, emit);
|
||||||
|
|
||||||
|
const loadExamFile = () => {
|
||||||
|
// 查询课程详情,在查询课程考试内
|
||||||
|
// console.log(props, "props");
|
||||||
|
// apiCourse.getExam(props.classId).then((rs) => {
|
||||||
|
// console.log(rs);
|
||||||
|
// });
|
||||||
|
// apiExamPaper.detail(localDialogVideoForm.value.id).then((res) => {
|
||||||
|
// if (res.status === 200) {
|
||||||
|
// console.log(res);
|
||||||
|
// localDialogVideoForm.value.counts = res.result.counts;
|
||||||
|
// localDialogVideoForm.value.name = res.result.testName;
|
||||||
|
// localDialogVideoForm.value.paperId = res.result.id;
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
};
|
||||||
|
onMounted(() => {
|
||||||
|
console.log(localDialogVideoForm.value, 123);
|
||||||
|
if (localDialogVideoForm.value.paperType === 1) {
|
||||||
|
if (!localDialogVideoForm.value.id) {
|
||||||
|
localDialogVideoForm.value = Object.assign(localDialogVideoForm.value, {
|
||||||
|
testDuration: localDialogVideoForm.value.testDuration || 30,
|
||||||
|
passLine: localDialogVideoForm.value.passLine || 60,
|
||||||
|
scoringType: localDialogVideoForm.value.scoringType || 1,
|
||||||
|
percentScore: localDialogVideoForm.value.percentScore || false,
|
||||||
|
randomMode: localDialogVideoForm.value.randomMode || false,
|
||||||
|
qnum: localDialogVideoForm.value.qnum || 1,
|
||||||
|
counts: localDialogVideoForm.value.counts || 1,
|
||||||
|
paperJson: localDialogVideoForm.value.paperJson || {
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 选择的试卷 还没做
|
||||||
|
loadExamFile();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<el-form label-position="right" label-width="100px">
|
||||||
|
<el-form-item label="考试名称">
|
||||||
|
<el-input
|
||||||
|
v-model="localDialogVideoForm.name"
|
||||||
|
:disabled="isPreview"
|
||||||
|
@update:modelValue="(val) => updateFormValue('name', val)"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="考试时长">
|
||||||
|
<el-input
|
||||||
|
v-model="localDialogVideoForm.testDuration"
|
||||||
|
:disabled="isPreview"
|
||||||
|
@update:modelValue="(val) => updateFormValue('testDuration', val)"
|
||||||
|
>
|
||||||
|
<template #append>分钟</template>
|
||||||
|
</el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="及格线">
|
||||||
|
<el-input
|
||||||
|
v-model="localDialogVideoForm.passLine"
|
||||||
|
:disabled="isPreview"
|
||||||
|
@update:modelValue="(val) => updateFormValue('passLine', val)"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="打开方式">
|
||||||
|
<el-radio-group
|
||||||
|
v-model="localDialogVideoForm.scoringType"
|
||||||
|
@update:modelValue="(val) => updateFormValue('scoringType', val)"
|
||||||
|
:disabled="isPreview"
|
||||||
|
>
|
||||||
|
<el-radio :value="1" :label="1">最高一次</el-radio>
|
||||||
|
<el-radio :value="2" :label="2">最后一次</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="百分制">
|
||||||
|
<el-checkbox
|
||||||
|
:label="true"
|
||||||
|
:value="true"
|
||||||
|
v-model="localDialogVideoForm.percentScore"
|
||||||
|
:disabled="isPreview"
|
||||||
|
@update:modelValue="(val) => updateFormValue('percentScore', val)"
|
||||||
|
>实际成绩*100/实际总分</el-checkbox
|
||||||
|
>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="考试说明">
|
||||||
|
<el-input
|
||||||
|
v-model="localDialogVideoForm.info"
|
||||||
|
type="textarea"
|
||||||
|
:disabled="isPreview"
|
||||||
|
@update:modelValue="(val) => updateFormValue('info', val)"
|
||||||
|
></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="随机模式">
|
||||||
|
<el-radio-group
|
||||||
|
:disabled="isPreview"
|
||||||
|
v-model="localDialogVideoForm.randomMode"
|
||||||
|
@update:modelValue="(val) => updateFormValue('randomMode', val)"
|
||||||
|
>
|
||||||
|
<el-radio :label="true">是</el-radio>
|
||||||
|
<el-radio :label="false">否</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item
|
||||||
|
label="数量"
|
||||||
|
v-if="
|
||||||
|
localDialogVideoForm.randomMode && localDialogVideoForm.paperType === 1
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<div class="flex align-center">
|
||||||
|
<el-input-number
|
||||||
|
:disabled="isPreview"
|
||||||
|
v-model="localDialogVideoForm.qnum"
|
||||||
|
@update:modelValue="(val) => updateFormValue('qnum', val)"
|
||||||
|
:min="1"
|
||||||
|
:max="
|
||||||
|
localDialogVideoForm.paperType === 1
|
||||||
|
? localDialogVideoForm.paperJson.items.length
|
||||||
|
: localDialogVideoForm.counts
|
||||||
|
"
|
||||||
|
>
|
||||||
|
</el-input-number>
|
||||||
|
<span
|
||||||
|
style="margin-left: 10px"
|
||||||
|
v-if="
|
||||||
|
localDialogVideoForm.paperType === 1
|
||||||
|
? localDialogVideoForm.paperJson.items.length <= 0
|
||||||
|
: localDialogVideoForm.counts <= 0
|
||||||
|
"
|
||||||
|
>先{{
|
||||||
|
localDialogVideoForm.paperType === 1 ? "添加试题" : "选择试卷"
|
||||||
|
}}</span
|
||||||
|
>
|
||||||
|
<span
|
||||||
|
style="margin-left: 10px"
|
||||||
|
v-if="
|
||||||
|
localDialogVideoForm.paperType === 1
|
||||||
|
? localDialogVideoForm.paperJson.items.length > 0
|
||||||
|
: localDialogVideoForm.counts > 0
|
||||||
|
"
|
||||||
|
>试卷有
|
||||||
|
{{
|
||||||
|
localDialogVideoForm.paperType === 1
|
||||||
|
? localDialogVideoForm.paperJson.items.length
|
||||||
|
: localDialogVideoForm.counts
|
||||||
|
}}
|
||||||
|
道试题</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<div class="parer-comp" v-if="localDialogVideoForm.paperType === 1">
|
||||||
|
<SimplePaper
|
||||||
|
v-model:data="localDialogVideoForm.paperJson"
|
||||||
|
:disabled="isPreview"
|
||||||
|
@update:data="(val) => updateFormValue('paperJson', val)"
|
||||||
|
></SimplePaper>
|
||||||
|
</div>
|
||||||
|
<!-- <div v-if="localDialogVideoForm.paperType === 2">-->
|
||||||
|
<!-- <div class="flex align-center">-->
|
||||||
|
<!-- <span style="padding-right: 12px; width: 100px; text-align: right"-->
|
||||||
|
<!-- >试卷</span-->
|
||||||
|
<!-- >-->
|
||||||
|
<!-- {{ localDialogVideoForm.name }}-->
|
||||||
|
<!-- </div>-->
|
||||||
|
<!-- </div>-->
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -251,6 +251,7 @@ export function useCourseData() {
|
|||||||
const courseList = ref([
|
const courseList = ref([
|
||||||
{
|
{
|
||||||
title: "课程1",
|
title: "课程1",
|
||||||
|
id: "1441803797035380736",
|
||||||
data: [],
|
data: [],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
import dragCollapse from "./dragCollapse.vue";
|
import dragCollapse from "./dragCollapse.vue";
|
||||||
import { ElButton, ElCheckbox, ElDialog, ElMessageBox } from "element-plus";
|
import { ElButton, ElCheckbox, ElDialog, ElMessageBox } from "element-plus";
|
||||||
import dragTable from "./dragTable.vue";
|
import dragTable from "./dragTable.vue";
|
||||||
import { ref, reactive } from "vue";
|
import { ref, watch } from "vue";
|
||||||
defineOptions({
|
defineOptions({
|
||||||
name: "CreateCourse",
|
name: "CreateCourse",
|
||||||
});
|
});
|
||||||
@@ -14,6 +14,7 @@ import EditorComp from "@/components/CreatedCourse/preview/EditorComp.vue";
|
|||||||
import DocComp from "@/components/CreatedCourse/preview/DocComp.vue";
|
import DocComp from "@/components/CreatedCourse/preview/DocComp.vue";
|
||||||
import LinkComp from "@/components/CreatedCourse/preview/LinkComp.vue";
|
import LinkComp from "@/components/CreatedCourse/preview/LinkComp.vue";
|
||||||
import ScormComp from "@/components/CreatedCourse/preview/ScormComp.vue";
|
import ScormComp from "@/components/CreatedCourse/preview/ScormComp.vue";
|
||||||
|
import PaperComp from "@/components/CreatedCourse/preview/PaperComp.vue";
|
||||||
import { getType } from "@/hooks/useCreateCourseMaps";
|
import { getType } from "@/hooks/useCreateCourseMaps";
|
||||||
const mapComponents = [
|
const mapComponents = [
|
||||||
VideoComp,
|
VideoComp,
|
||||||
@@ -22,6 +23,7 @@ const mapComponents = [
|
|||||||
DocComp,
|
DocComp,
|
||||||
LinkComp,
|
LinkComp,
|
||||||
ScormComp,
|
ScormComp,
|
||||||
|
PaperComp,
|
||||||
];
|
];
|
||||||
|
|
||||||
// 使用课程数据hook
|
// 使用课程数据hook
|
||||||
@@ -30,10 +32,24 @@ const { courseMetadata, courseList, courseActionButtons, addChapter } =
|
|||||||
const isPreview = ref(false);
|
const isPreview = ref(false);
|
||||||
const chooseItemData = ref({});
|
const chooseItemData = ref({});
|
||||||
const showSettingDialog = ref(false);
|
const showSettingDialog = ref(false);
|
||||||
|
// 添加操作的时候 弹窗是否弹出对应类型表单
|
||||||
const isNext = ref(true);
|
const isNext = ref(true);
|
||||||
const showTablePreview = ref(false);
|
const showTablePreview = ref(false);
|
||||||
// 定义表格列
|
// 定义表格列
|
||||||
const showDialog = ref(false);
|
const showDialog = ref(false);
|
||||||
|
|
||||||
|
const classId = ref("");
|
||||||
|
watch(
|
||||||
|
() => courseMetadata.chooseIndex,
|
||||||
|
(newVal) => {
|
||||||
|
console.log(newVal);
|
||||||
|
console.log(courseList.value[newVal]);
|
||||||
|
classId.value = courseList.value[newVal].id;
|
||||||
|
|
||||||
|
console.log(classId.value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 课程操作映射
|
// 课程操作映射
|
||||||
const courseOperations = {
|
const courseOperations = {
|
||||||
addVideo: () => {
|
addVideo: () => {
|
||||||
@@ -67,6 +83,7 @@ const courseOperations = {
|
|||||||
addExam: () => {
|
addExam: () => {
|
||||||
courseMetadata.resType = 61;
|
courseMetadata.resType = 61;
|
||||||
showDialog.value = true;
|
showDialog.value = true;
|
||||||
|
isNext.value = false;
|
||||||
},
|
},
|
||||||
addHomework: () => {
|
addHomework: () => {
|
||||||
console.log("添加作业功能调用");
|
console.log("添加作业功能调用");
|
||||||
@@ -104,6 +121,8 @@ const choosePreviewItem = (data) => {
|
|||||||
};
|
};
|
||||||
// 保存
|
// 保存
|
||||||
const saveContent = () => {
|
const saveContent = () => {
|
||||||
|
console.log(chooseItemData.value);
|
||||||
|
|
||||||
if (courseMetadata.selectionIndex !== null) {
|
if (courseMetadata.selectionIndex !== null) {
|
||||||
courseList.value[courseMetadata.chooseIndex].data[
|
courseList.value[courseMetadata.chooseIndex].data[
|
||||||
courseMetadata.selectionIndex
|
courseMetadata.selectionIndex
|
||||||
@@ -116,6 +135,8 @@ const saveContent = () => {
|
|||||||
}
|
}
|
||||||
showDialog.value = false;
|
showDialog.value = false;
|
||||||
showSettingDialog.value = false;
|
showSettingDialog.value = false;
|
||||||
|
|
||||||
|
// 可以调用保存方法 保存考试
|
||||||
};
|
};
|
||||||
const deleteRow = (data) => {
|
const deleteRow = (data) => {
|
||||||
courseMetadata.chooseIndex = data.index;
|
courseMetadata.chooseIndex = data.index;
|
||||||
@@ -145,6 +166,12 @@ const previewRow = (data) => {
|
|||||||
isPreview.value = true;
|
isPreview.value = true;
|
||||||
showSettingDialog.value = true;
|
showSettingDialog.value = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 自定义考试
|
||||||
|
const chooseCusExam = (data) => {
|
||||||
|
chooseItemData.value = data;
|
||||||
|
showSettingDialog.value = true;
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -201,6 +228,7 @@ const previewRow = (data) => {
|
|||||||
@choosePreviewItem="choosePreviewItem"
|
@choosePreviewItem="choosePreviewItem"
|
||||||
:resType="courseMetadata.resType"
|
:resType="courseMetadata.resType"
|
||||||
:showTablePreview="showTablePreview"
|
:showTablePreview="showTablePreview"
|
||||||
|
@chooseCusExam="chooseCusExam"
|
||||||
></chooseFileList>
|
></chooseFileList>
|
||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
@@ -209,15 +237,19 @@ const previewRow = (data) => {
|
|||||||
v-model="showSettingDialog"
|
v-model="showSettingDialog"
|
||||||
:title="isPreview ? '预览' : getType(chooseItemData.resType)"
|
:title="isPreview ? '预览' : getType(chooseItemData.resType)"
|
||||||
>
|
>
|
||||||
<div v-for="item in mapComponents">
|
<div style="max-height: 600px; overflow: auto">
|
||||||
<component
|
<template v-for="item in mapComponents">
|
||||||
v-if="
|
<component
|
||||||
Number(chooseItemData.resType) === item.resType && showSettingDialog
|
v-if="
|
||||||
"
|
Number(chooseItemData.resType) === item.resType &&
|
||||||
:is="item"
|
showSettingDialog
|
||||||
v-model:dialogVideoForm="chooseItemData"
|
"
|
||||||
:isPreview="isPreview"
|
:is="item"
|
||||||
></component>
|
v-model:dialogVideoForm="chooseItemData"
|
||||||
|
:isPreview="isPreview"
|
||||||
|
:classId="classId"
|
||||||
|
></component>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
|
|||||||
Reference in New Issue
Block a user