mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/fe-manage.git
synced 2025-12-06 09:26: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-image-drop-module": "^1.0.3",
|
||||
"quill-image-resize-module": "^3.0.0",
|
||||
"snowflake-id-js": "^1.0.1",
|
||||
"sortablejs": "^1.15.0",
|
||||
"vue": "^3.2.13",
|
||||
"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-right: $i * 1px;
|
||||
}
|
||||
.m#{$i} {
|
||||
margin: $i * 1px;
|
||||
}
|
||||
.fs#{$i} {
|
||||
font-size: $i * 1px;
|
||||
}
|
||||
@@ -812,7 +815,28 @@ textarea {
|
||||
padding-left: $i * 1px;
|
||||
padding-right: $i * 1px;
|
||||
}
|
||||
.p#{$i} {
|
||||
padding: $i * 1px;
|
||||
}
|
||||
.w#{$i} {
|
||||
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) => {
|
||||
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,
|
||||
isDrag: false,
|
||||
completeSetup: 0,
|
||||
setupTage: "",
|
||||
resType: props.resType,
|
||||
dir: props.resType === 50 ? "scorm" : "course",
|
||||
});
|
||||
};
|
||||
if (props.resType === 61) {
|
||||
obj.paperType = 2;
|
||||
}
|
||||
emit("chooseItem", obj);
|
||||
};
|
||||
|
||||
const handlePreviewItem = (row) => {
|
||||
@@ -267,6 +290,14 @@ const handleBeforeUpload = (file) => {
|
||||
|
||||
return true;
|
||||
};
|
||||
const openCusExam = () => {
|
||||
emit("chooseCusExam", {
|
||||
resType: props.resType,
|
||||
dir: props.resType === 50 ? "scorm" : "course",
|
||||
paperType: 1,
|
||||
name: "自定义考试",
|
||||
});
|
||||
};
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
@@ -292,7 +323,10 @@ onMounted(() => {
|
||||
>上传新{{ getType(props.resType) }}</el-button
|
||||
>
|
||||
</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
|
||||
>
|
||||
<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([
|
||||
{
|
||||
title: "课程1",
|
||||
id: "1441803797035380736",
|
||||
data: [],
|
||||
},
|
||||
{
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import dragCollapse from "./dragCollapse.vue";
|
||||
import { ElButton, ElCheckbox, ElDialog, ElMessageBox } from "element-plus";
|
||||
import dragTable from "./dragTable.vue";
|
||||
import { ref, reactive } from "vue";
|
||||
import { ref, watch } from "vue";
|
||||
defineOptions({
|
||||
name: "CreateCourse",
|
||||
});
|
||||
@@ -14,6 +14,7 @@ import EditorComp from "@/components/CreatedCourse/preview/EditorComp.vue";
|
||||
import DocComp from "@/components/CreatedCourse/preview/DocComp.vue";
|
||||
import LinkComp from "@/components/CreatedCourse/preview/LinkComp.vue";
|
||||
import ScormComp from "@/components/CreatedCourse/preview/ScormComp.vue";
|
||||
import PaperComp from "@/components/CreatedCourse/preview/PaperComp.vue";
|
||||
import { getType } from "@/hooks/useCreateCourseMaps";
|
||||
const mapComponents = [
|
||||
VideoComp,
|
||||
@@ -22,6 +23,7 @@ const mapComponents = [
|
||||
DocComp,
|
||||
LinkComp,
|
||||
ScormComp,
|
||||
PaperComp,
|
||||
];
|
||||
|
||||
// 使用课程数据hook
|
||||
@@ -30,10 +32,24 @@ const { courseMetadata, courseList, courseActionButtons, addChapter } =
|
||||
const isPreview = ref(false);
|
||||
const chooseItemData = ref({});
|
||||
const showSettingDialog = ref(false);
|
||||
// 添加操作的时候 弹窗是否弹出对应类型表单
|
||||
const isNext = ref(true);
|
||||
const showTablePreview = 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 = {
|
||||
addVideo: () => {
|
||||
@@ -67,6 +83,7 @@ const courseOperations = {
|
||||
addExam: () => {
|
||||
courseMetadata.resType = 61;
|
||||
showDialog.value = true;
|
||||
isNext.value = false;
|
||||
},
|
||||
addHomework: () => {
|
||||
console.log("添加作业功能调用");
|
||||
@@ -104,6 +121,8 @@ const choosePreviewItem = (data) => {
|
||||
};
|
||||
// 保存
|
||||
const saveContent = () => {
|
||||
console.log(chooseItemData.value);
|
||||
|
||||
if (courseMetadata.selectionIndex !== null) {
|
||||
courseList.value[courseMetadata.chooseIndex].data[
|
||||
courseMetadata.selectionIndex
|
||||
@@ -116,6 +135,8 @@ const saveContent = () => {
|
||||
}
|
||||
showDialog.value = false;
|
||||
showSettingDialog.value = false;
|
||||
|
||||
// 可以调用保存方法 保存考试
|
||||
};
|
||||
const deleteRow = (data) => {
|
||||
courseMetadata.chooseIndex = data.index;
|
||||
@@ -145,6 +166,12 @@ const previewRow = (data) => {
|
||||
isPreview.value = true;
|
||||
showSettingDialog.value = true;
|
||||
};
|
||||
|
||||
// 自定义考试
|
||||
const chooseCusExam = (data) => {
|
||||
chooseItemData.value = data;
|
||||
showSettingDialog.value = true;
|
||||
};
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -201,6 +228,7 @@ const previewRow = (data) => {
|
||||
@choosePreviewItem="choosePreviewItem"
|
||||
:resType="courseMetadata.resType"
|
||||
:showTablePreview="showTablePreview"
|
||||
@chooseCusExam="chooseCusExam"
|
||||
></chooseFileList>
|
||||
</el-dialog>
|
||||
|
||||
@@ -209,15 +237,19 @@ const previewRow = (data) => {
|
||||
v-model="showSettingDialog"
|
||||
:title="isPreview ? '预览' : getType(chooseItemData.resType)"
|
||||
>
|
||||
<div v-for="item in mapComponents">
|
||||
<component
|
||||
v-if="
|
||||
Number(chooseItemData.resType) === item.resType && showSettingDialog
|
||||
"
|
||||
:is="item"
|
||||
v-model:dialogVideoForm="chooseItemData"
|
||||
:isPreview="isPreview"
|
||||
></component>
|
||||
<div style="max-height: 600px; overflow: auto">
|
||||
<template v-for="item in mapComponents">
|
||||
<component
|
||||
v-if="
|
||||
Number(chooseItemData.resType) === item.resType &&
|
||||
showSettingDialog
|
||||
"
|
||||
:is="item"
|
||||
v-model:dialogVideoForm="chooseItemData"
|
||||
:isPreview="isPreview"
|
||||
:classId="classId"
|
||||
></component>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<template #footer>
|
||||
|
||||
Reference in New Issue
Block a user