feat(course): 添加SCORM文件预览功能

- 在chooseFileList组件中增加SCORM文件的预览按钮及逻辑处理
- 新增ScormComp.vue组件用于展示SCORM内容
- 更新createCourse.vue以支持SCORM类型的添加与预览操作
- 调整dragTable.vue中的显示控制逻辑,适配SCORM类型
- 修改useCreateCourseMaps.js中SCORM类型的名称为大写格式
- 扩展上传文件类型判断和相关参数传递逻辑
- 优化代码结构和可读性,确保SCORM资源正确加载和显示
This commit is contained in:
陈昱达
2025-11-24 19:16:19 +08:00
parent 6ac50b1fe9
commit 8d9775b77a
5 changed files with 145 additions and 28 deletions

View File

@@ -2,7 +2,11 @@
import { reactive, onMounted, ref, h } from "vue"; import { reactive, onMounted, ref, h } from "vue";
import { ElButton, ElInput, ElUpload, ElMessage } from "element-plus"; import { ElButton, ElInput, ElUpload, ElMessage } from "element-plus";
import BasicTable from "@/components/BasicElTable/BasicTable.vue"; import BasicTable from "@/components/BasicElTable/BasicTable.vue";
import { getPageListByType, getType, getMapsItem } from "@/hooks/useCreateCourseMaps"; import {
getPageListByType,
getType,
getMapsItem,
} from "@/hooks/useCreateCourseMaps";
import apiCourseFile from "@/api/modules/courseFile"; import apiCourseFile from "@/api/modules/courseFile";
import { useRoute } from "vue-router"; import { useRoute } from "vue-router";
import Cookies from "vue-cookies"; import Cookies from "vue-cookies";
@@ -83,6 +87,20 @@ const columns = [
width: 150, width: 150,
render: (params) => { render: (params) => {
return h("div", [ return h("div", [
h(
ElButton,
{
type: "primary",
size: "small",
style: {
display: [40, 50, 62].includes(props.resType) ? "" : "none",
},
onClick: () => {
handlePreviewItem(params.row);
},
},
"预览"
),
h( h(
ElButton, ElButton,
{ {
@@ -100,7 +118,7 @@ const columns = [
]; ];
// 事件发射 // 事件发射
const emit = defineEmits(["chooseItem"]); const emit = defineEmits(["chooseItem", "choosePreviewItem"]);
// 处理选择项目 // 处理选择项目
const handleChooseItem = (row) => { const handleChooseItem = (row) => {
@@ -110,9 +128,20 @@ const handleChooseItem = (row) => {
completeSetup: 0, completeSetup: 0,
setupTage: "", setupTage: "",
resType: props.resType, resType: props.resType,
dir: props.resType === 50 ? "scorm" : "course",
}); });
}; };
const handlePreviewItem = (row) => {
emit("choosePreviewItem", {
...row,
isDrag: false,
completeSetup: 0,
setupTage: "",
resType: props.resType,
dir: props.resType === 50 ? "scorm" : "course",
});
};
// 分页改变处理 // 分页改变处理
const changePagination = (PAGINATION) => { const changePagination = (PAGINATION) => {
Object.assign(pagination, PAGINATION); Object.assign(pagination, PAGINATION);
@@ -128,7 +157,7 @@ const getVideoList = () => {
pageIndex: pagination.current, pageIndex: pagination.current,
self: true, self: true,
}; };
getPageListByType(paramsData).then((res) => { getPageListByType(paramsData).then((res) => {
loading.value = false; loading.value = false;
tableData.value = res.result.list; tableData.value = res.result.list;
@@ -136,15 +165,6 @@ const getVideoList = () => {
}); });
}; };
// 选择项目处理
const chooseItem = () => {
showDialog.value = false;
emit("chooseItem", {
...dialogVideoForm,
type: props.resType,
});
};
// 上传成功处理 // 上传成功处理
const handleUploadSuccess = (res, file) => { const handleUploadSuccess = (res, file) => {
if (res.status === 200) { if (res.status === 200) {
@@ -160,7 +180,7 @@ const handleUploadSuccess = (res, file) => {
duration: routeQuery.duration, duration: routeQuery.duration,
remark: "课程中直接上传", remark: "课程中直接上传",
}; };
apiCourseFile.saveUpload(courseWare).then((rs) => { apiCourseFile.saveUpload(courseWare).then((rs) => {
if (rs.status === 200) { if (rs.status === 200) {
emit("chooseItem", { emit("chooseItem", {
@@ -183,39 +203,46 @@ const handleBeforeUpload = (file) => {
ElMessage({ message: `文件格式不正确!`, type: "error", offset: 100 }); ElMessage({ message: `文件格式不正确!`, type: "error", offset: 100 });
return false; return false;
} }
let fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1); let fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
fileExtension = fileExtension.toLowerCase(); fileExtension = fileExtension.toLowerCase();
// 校检文件类型 // 校检文件类型
if (getMapsItem(props.resType).fileType.join(",").indexOf(fileExtension) === -1) { if (
getMapsItem(props.resType).fileType.join(",").indexOf(fileExtension) === -1
) {
ElMessage({ ElMessage({
message: `文件格式不正确, 请上传正确格式的${getMapsItem(props.resType).name}文件!`, message: `文件格式不正确, 请上传正确格式的${
getMapsItem(props.resType).name
}文件!`,
type: "error", type: "error",
offset: 100, offset: 100,
}); });
return false; return false;
} }
// 校检文件大小 // 校检文件大小
if (getMapsItem(props.resType).uploadSize) { if (getMapsItem(props.resType).uploadSize) {
const isLt = file.size / 1024 / 1024 < getMapsItem(props.resType).uploadSize; const isLt =
file.size / 1024 / 1024 < getMapsItem(props.resType).uploadSize;
if (!isLt) { if (!isLt) {
ElMessage({ ElMessage({
message: `上传文件大小不能超过 ${getMapsItem(props.resType).uploadSize} !`, message: `上传文件大小不能超过 ${
getMapsItem(props.resType).uploadSize
} !`,
type: "error", type: "error",
offset: 100, offset: 100,
}); });
return false; return false;
} }
} }
if (props.resType === 50) { if (props.resType === 50) {
dialogVideoForm.dir = "scorm"; dialogVideoForm.dir = "scorm";
} else { } else {
dialogVideoForm.dir = "course"; dialogVideoForm.dir = "course";
} }
return true; return true;
}; };
@@ -237,7 +264,9 @@ onMounted(() => {
:file-list="fileList" :file-list="fileList"
:before-upload="handleBeforeUpload" :before-upload="handleBeforeUpload"
> >
<el-button v-if="[10, 20, 40].includes(props.resType)" type="primary" <el-button
v-if="[10, 20, 40, 50].includes(props.resType)"
type="primary"
>上传新{{ getType(props.resType) }}</el-button >上传新{{ getType(props.resType) }}</el-button
> >
</el-upload> </el-upload>
@@ -282,4 +311,4 @@ onMounted(() => {
} }
} }
} }
</style> </style>

View File

@@ -0,0 +1,70 @@
<script setup>
import { ElForm, ElFormItem, ElInput } from "element-plus";
import { onMounted, ref } from "vue";
import apiCourseFile from "@/api/modules/courseFile";
defineOptions({
resType: 50,
});
const props = defineProps({
dialogVideoForm: {
type: Object,
default: () => ({
name: "",
filePath: "",
isDrag: true,
completeSetup: 0,
setupTage: 0,
openType: "",
}),
},
isPreview: {
type: Boolean,
default: false,
},
});
const path = process.env.VUE_APP_BOE_API_URL;
const scormUrl = ref("");
import { useMediaComponent } from "@/hooks/useMediaComponent";
// Emit updates to parent component
const emit = defineEmits(["update:dialogVideoForm"]);
// 使用hook处理公共逻辑
const { localDialogVideoForm, updateFormValue, fileBaseUrl } =
useMediaComponent(props, emit);
const loadScormFile = () => {
console.log(localDialogVideoForm.value);
apiCourseFile.detail(localDialogVideoForm.value.id).then((rs) => {
if (rs.status === 200) {
localDialogVideoForm.value.dir = "scorm";
let urlPre = window.location.protocol;
let configUrl = `https:${path}/newscorm/scorm-player`;
configUrl = urlPre + configUrl.substring(configUrl.indexOf(":") + 1);
scormUrl.value = configUrl + "?r=1&mode=preview&scormId=" + rs.result.id;
}
});
};
onMounted(() => {
loadScormFile();
});
</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>
<iframe
style="width: 100%; max-height: 500px; overflow: auto"
:src="scormUrl"
frameborder="0"
></iframe>
</el-form>
</template>
<style scoped lang="scss"></style>

View File

@@ -67,7 +67,7 @@ const contentTypeMaps = [
}, },
{ {
type: 50, type: 50,
name: "scorm", name: "SCORM",
icon: "icon-file", icon: "icon-file",
// 1G B // 1G B
uploadSize: 1024 * 1024 * 1024, uploadSize: 1024 * 1024 * 1024,

View File

@@ -13,8 +13,16 @@ import AudioComp from "@/components/CreatedCourse/preview/AudioComp.vue";
import EditorComp from "@/components/CreatedCourse/preview/EditorComp.vue"; 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 { getType } from "@/hooks/useCreateCourseMaps"; import { getType } from "@/hooks/useCreateCourseMaps";
const mapComponents = [VideoComp, AudioComp, EditorComp, DocComp, LinkComp]; const mapComponents = [
VideoComp,
AudioComp,
EditorComp,
DocComp,
LinkComp,
ScormComp,
];
// 使用课程数据hook // 使用课程数据hook
const { courseMetadata, courseList, courseActionButtons, addChapter } = const { courseMetadata, courseList, courseActionButtons, addChapter } =
@@ -52,7 +60,9 @@ const courseOperations = {
showSettingDialog.value = true; showSettingDialog.value = true;
}, },
addScorm: () => { addScorm: () => {
console.log("添加SCORM功能调用"); courseMetadata.resType = 50;
showDialog.value = true;
isNext.value = false;
}, },
addExam: () => { addExam: () => {
console.log("添加考试功能调用"); console.log("添加考试功能调用");
@@ -69,6 +79,7 @@ const executeCourseOperation = (operationName, data) => {
courseMetadata.chooseIndex = data; courseMetadata.chooseIndex = data;
courseMetadata.selectionIndex = null; courseMetadata.selectionIndex = null;
isPreview.value = false; isPreview.value = false;
isNext.value = true;
chooseItemData.value = {}; chooseItemData.value = {};
if (courseOperations[operationName]) { if (courseOperations[operationName]) {
courseOperations[operationName](data); courseOperations[operationName](data);
@@ -77,6 +88,7 @@ const executeCourseOperation = (operationName, data) => {
} }
}; };
const chooseItem = (data) => { const chooseItem = (data) => {
console.log(data);
chooseItemData.value = data; chooseItemData.value = data;
if (!isNext.value) { if (!isNext.value) {
saveContent(); saveContent();
@@ -84,6 +96,11 @@ const chooseItem = (data) => {
} }
showSettingDialog.value = true; showSettingDialog.value = true;
}; };
const choosePreviewItem = (data) => {
chooseItemData.value = data;
showSettingDialog.value = true;
isPreview.value = true;
};
// 保存 // 保存
const saveContent = () => { const saveContent = () => {
if (courseMetadata.selectionIndex !== null) { if (courseMetadata.selectionIndex !== null) {
@@ -180,6 +197,7 @@ const previewRow = (data) => {
<chooseFileList <chooseFileList
v-if="showDialog" v-if="showDialog"
@chooseItem="chooseItem" @chooseItem="chooseItem"
@choosePreviewItem="choosePreviewItem"
:resType="courseMetadata.resType" :resType="courseMetadata.resType"
:showTablePreview="showTablePreview" :showTablePreview="showTablePreview"
></chooseFileList> ></chooseFileList>

View File

@@ -239,7 +239,7 @@ const renderActionColumn = () => {
href: "javascript:void(0)", href: "javascript:void(0)",
onClick: () => handleSetting(index, record), onClick: () => handleSetting(index, record),
style: { style: {
display: [40].includes(record.resType) ? "none" : "", display: [40, 50].includes(record.resType) ? "none" : "",
}, },
}, },
[ [