mirror of
https://codeup.aliyun.com/67762337eccfc218f6110e0e/vue/fe-manage.git
synced 2025-12-19 07:46:46 +08:00
402 lines
11 KiB
Vue
402 lines
11 KiB
Vue
<script setup>
|
||
import dragCollapse from "./dragCollapse.vue";
|
||
import { ElButton, ElCheckbox, ElDialog } from "element-plus";
|
||
import { $message, $messageBox } from "@/utils/useMessage";
|
||
import dragTable from "./dragTable.vue";
|
||
import { ref, watch } from "vue";
|
||
import { getType } from "@/hooks/useCreateCourseMaps";
|
||
import { useCourseData } from "@/hooks/useCourseData";
|
||
import chooseFileList from "@/components/CreatedCourse/chooseFileList.vue";
|
||
import VideoComp from "@/components/CreatedCourse/preview/VideoComp.vue";
|
||
import AudioComp from "@/components/CreatedCourse/preview/AudioComp.vue";
|
||
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 HomeWorkComp from "@/components/CreatedCourse/preview/HomeWorkComp.vue";
|
||
import AccessComp from "@/components/CreatedCourse/preview/AccessComp.vue";
|
||
import SvgIcon from "@/components/SvgIcon.vue";
|
||
import { Plus } from "@element-plus/icons-vue";
|
||
defineOptions({
|
||
name: "CreateCourse",
|
||
});
|
||
// 组件映射
|
||
const mapComponents = [
|
||
VideoComp,
|
||
AudioComp,
|
||
EditorComp,
|
||
DocComp,
|
||
LinkComp,
|
||
ScormComp,
|
||
PaperComp,
|
||
HomeWorkComp,
|
||
AccessComp,
|
||
];
|
||
|
||
// 使用课程数据hook
|
||
const { courseMetadata, courseList, courseActionButtons, addChapter } =
|
||
useCourseData();
|
||
const chooseItemData = ref({});
|
||
const copyChooseItemData = ref({});
|
||
|
||
// 监听课程索引变化,更新classId
|
||
watch(
|
||
() => courseMetadata.chooseIndex,
|
||
(newVal) => {
|
||
if (courseList.value[newVal]) {
|
||
courseMetadata.classId = courseList.value[newVal].id || "";
|
||
}
|
||
}
|
||
);
|
||
|
||
// 课程操作映射
|
||
const courseOperations = {
|
||
addVideo: () => {
|
||
courseMetadata.resType = 10;
|
||
courseMetadata.showDialog = true;
|
||
},
|
||
addAudio: () => {
|
||
courseMetadata.resType = 20;
|
||
courseMetadata.showDialog = true;
|
||
},
|
||
addDocument: () => {
|
||
courseMetadata.resType = 40;
|
||
courseMetadata.showDialog = true;
|
||
courseMetadata.isNext = false;
|
||
},
|
||
addImageText: () => {
|
||
courseMetadata.resType = 41;
|
||
chooseItemData.value.resType = 41;
|
||
courseMetadata.showSettingDialog = true;
|
||
},
|
||
addExternalLink: () => {
|
||
courseMetadata.resType = 52;
|
||
chooseItemData.value.resType = 52;
|
||
courseMetadata.showSettingDialog = true;
|
||
},
|
||
addScorm: () => {
|
||
courseMetadata.resType = 50;
|
||
courseMetadata.showDialog = true;
|
||
courseMetadata.isNext = false;
|
||
},
|
||
addExam: () => {
|
||
courseMetadata.resType = 61;
|
||
courseMetadata.showDialog = true;
|
||
courseMetadata.isNext = false;
|
||
},
|
||
addHomework: () => {
|
||
courseMetadata.resType = 60;
|
||
chooseItemData.value.resType = 60;
|
||
courseMetadata.showSettingDialog = true;
|
||
},
|
||
addAssessment: () => {
|
||
courseMetadata.resType = 62;
|
||
chooseItemData.value.resType = 62;
|
||
courseMetadata.showSettingDialog = true;
|
||
},
|
||
};
|
||
|
||
// 执行课程操作
|
||
const executeCourseOperation = (operationName, data) => {
|
||
courseMetadata.chooseIndex = data;
|
||
courseMetadata.selectionIndex = null;
|
||
copyChooseItemData.value = {};
|
||
courseMetadata.isPreview = false;
|
||
courseMetadata.isNext = true;
|
||
chooseItemData.value = {};
|
||
|
||
if (courseOperations[operationName]) {
|
||
courseOperations[operationName](data);
|
||
} else {
|
||
console.warn(`未找到操作: ${operationName}`);
|
||
}
|
||
};
|
||
|
||
// 选择项目处理
|
||
const chooseItem = (data) => {
|
||
chooseItemData.value = data;
|
||
// 如果不需要下一步,则直接保存
|
||
if (!courseMetadata.isNext) {
|
||
saveContent();
|
||
return;
|
||
}
|
||
courseMetadata.showSettingDialog = true;
|
||
};
|
||
|
||
// 预览项目处理
|
||
const choosePreviewItem = (data) => {
|
||
chooseItemData.value = data;
|
||
courseMetadata.showSettingDialog = true;
|
||
courseMetadata.isPreview = true;
|
||
};
|
||
|
||
// 取消保存
|
||
const cancelSave = () => {
|
||
courseMetadata.showSettingDialog = false;
|
||
// 恢复原始数据
|
||
chooseItemData.value = copyChooseItemData.value;
|
||
if (
|
||
courseMetadata.chooseIndex !== null &&
|
||
courseMetadata.selectionIndex !== null &&
|
||
courseList.value[courseMetadata.chooseIndex] &&
|
||
courseList.value[courseMetadata.chooseIndex].data[
|
||
courseMetadata.selectionIndex
|
||
]
|
||
) {
|
||
courseList.value[courseMetadata.chooseIndex].data[
|
||
courseMetadata.selectionIndex
|
||
] = chooseItemData.value;
|
||
}
|
||
};
|
||
|
||
// 保存内容
|
||
const saveContent = () => {
|
||
if (courseMetadata.selectionIndex !== null) {
|
||
// 更新已有项
|
||
if (
|
||
courseList.value[courseMetadata.chooseIndex] &&
|
||
courseList.value[courseMetadata.chooseIndex].data[
|
||
courseMetadata.selectionIndex
|
||
]
|
||
) {
|
||
courseList.value[courseMetadata.chooseIndex].data[
|
||
courseMetadata.selectionIndex
|
||
] = {
|
||
...courseList.value[courseMetadata.chooseIndex].data[
|
||
courseMetadata.selectionIndex
|
||
],
|
||
...chooseItemData.value,
|
||
};
|
||
}
|
||
} else {
|
||
// 添加新项
|
||
if (courseList.value[courseMetadata.chooseIndex]) {
|
||
courseList.value[courseMetadata.chooseIndex].data.push({
|
||
resType: courseMetadata.resType,
|
||
...chooseItemData.value,
|
||
});
|
||
}
|
||
}
|
||
|
||
courseMetadata.showDialog = false;
|
||
courseMetadata.showSettingDialog = false;
|
||
};
|
||
|
||
// 删除行
|
||
const deleteRow = (data) => {
|
||
courseMetadata.chooseIndex = data.index;
|
||
courseMetadata.selectionIndex = data.selectionIndex;
|
||
|
||
$messageBox
|
||
.confirm(`确定删除【${data.record.name}】吗?`, "删除确认", {
|
||
confirmButtonText: "确认",
|
||
cancelButtonText: "取消",
|
||
type: "error",
|
||
})
|
||
.then(() => {
|
||
$message.success("删除成功");
|
||
if (
|
||
courseList.value[courseMetadata.chooseIndex] &&
|
||
courseList.value[courseMetadata.chooseIndex].data
|
||
) {
|
||
courseList.value[courseMetadata.chooseIndex].data.splice(
|
||
courseMetadata.selectionIndex,
|
||
1
|
||
);
|
||
}
|
||
})
|
||
.catch(() => {
|
||
// 用户取消删除
|
||
});
|
||
};
|
||
|
||
// 设置行
|
||
const settingRow = (data) => {
|
||
courseMetadata.chooseIndex = data.index;
|
||
courseMetadata.selectionIndex = data.selectionIndex;
|
||
chooseItemData.value = { ...data.record }; // 创建副本避免直接引用
|
||
copyChooseItemData.value = JSON.parse(JSON.stringify(data.record));
|
||
courseMetadata.isPreview = false;
|
||
courseMetadata.showSettingDialog = true;
|
||
};
|
||
|
||
// 预览行
|
||
const previewRow = (data) => {
|
||
courseMetadata.chooseIndex = data.index;
|
||
courseMetadata.selectionIndex = data.selectionIndex;
|
||
chooseItemData.value = { ...data.record }; // 创建副本避免直接引用
|
||
copyChooseItemData.value = JSON.parse(JSON.stringify(data.record));
|
||
courseMetadata.isPreview = true;
|
||
courseMetadata.showSettingDialog = true;
|
||
};
|
||
|
||
// 自定义考试
|
||
const chooseCusExam = (data) => {
|
||
chooseItemData.value = data;
|
||
courseMetadata.showSettingDialog = true;
|
||
};
|
||
|
||
// 下一步处理
|
||
const handleNext = () => {
|
||
$message.success("213");
|
||
};
|
||
</script>
|
||
|
||
<template>
|
||
<div class="create-course">
|
||
<div class="course-header">
|
||
<div class="title ml18">课程名称</div>
|
||
<span>创建时间:{{ courseMetadata.createTime }}</span>
|
||
</div>
|
||
<div class="course-content">
|
||
<div class="course-actions">
|
||
<el-button @click="addChapter" :icon="Plus">添加章</el-button>
|
||
<el-checkbox class="ml10">顺序学习</el-checkbox>
|
||
</div>
|
||
|
||
<div>
|
||
<dragCollapse
|
||
v-model:courseList="courseList"
|
||
titleKey="title"
|
||
:isEdit="true"
|
||
>
|
||
<!-- <template #title="{ course }">{{ course.title }}</template>-->
|
||
<template #desc="{ course }">
|
||
若课程只有一个章节,将不在学员端显示该章节名称
|
||
</template>
|
||
<template #default="{ course, index }">
|
||
<div class="drag-course-btn-content">
|
||
<el-button
|
||
v-for="btn in courseActionButtons"
|
||
:key="btn.fun"
|
||
type="primary"
|
||
class="btn-item"
|
||
plain
|
||
@click="executeCourseOperation(btn.fun, index)"
|
||
>
|
||
<svg-icon :icon-class="btn.icon" class="mr10"></svg-icon>
|
||
{{ btn.label }}
|
||
</el-button>
|
||
</div>
|
||
<div>
|
||
<!-- 添加 groupId 和 tableId 属性以支持跨表格拖拽 -->
|
||
<dragTable
|
||
:data="course.data"
|
||
group-id="course-chapters"
|
||
:table-id="'chapter-' + index"
|
||
:index="index"
|
||
@delete="deleteRow"
|
||
@setting="settingRow"
|
||
@preview="previewRow"
|
||
/>
|
||
</div>
|
||
</template>
|
||
</dragCollapse>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 选择文件列表 -->
|
||
<el-dialog
|
||
v-model="courseMetadata.showDialog"
|
||
:title="getType(courseMetadata.resType)"
|
||
>
|
||
<chooseFileList
|
||
v-if="courseMetadata.showDialog"
|
||
@chooseItem="chooseItem"
|
||
@choosePreviewItem="choosePreviewItem"
|
||
:resType="courseMetadata.resType"
|
||
:showTablePreview="courseMetadata.showTablePreview"
|
||
@chooseCusExam="chooseCusExam"
|
||
/>
|
||
</el-dialog>
|
||
|
||
<!-- 设置预览弹窗 -->
|
||
<!-- :fullscreen="chooseItemData.resType === 41"-->
|
||
<el-dialog
|
||
v-model="courseMetadata.showSettingDialog"
|
||
:title="
|
||
courseMetadata.isPreview ? '预览' : getType(chooseItemData.resType)
|
||
"
|
||
>
|
||
<div class="component-preview">
|
||
<template v-for="item in mapComponents" :key="item.name">
|
||
<component
|
||
v-if="
|
||
Number(chooseItemData.resType) === item.resType &&
|
||
courseMetadata.showSettingDialog
|
||
"
|
||
:is="item"
|
||
v-model:dialogVideoForm="chooseItemData"
|
||
:isPreview="courseMetadata.isPreview"
|
||
:classId="courseMetadata.classId"
|
||
/>
|
||
</template>
|
||
</div>
|
||
|
||
<template #footer>
|
||
<div class="dialog-footer">
|
||
<el-button @click="cancelSave()">取消</el-button>
|
||
<el-button
|
||
type="primary"
|
||
@click="saveContent()"
|
||
v-if="!courseMetadata.isPreview"
|
||
>
|
||
保存
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<div class="p10 pst-s bg-w bottom0">
|
||
<el-button>取消</el-button>
|
||
<el-button type="primary">存草稿</el-button>
|
||
<el-button @click="handleNext" type="primary">下一步</el-button>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped lang="scss">
|
||
.create-course {
|
||
width: 100%;
|
||
padding: 27px 20px;
|
||
.course-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
|
||
.title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
|
||
.course-actions {
|
||
padding: 10px;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.drag-course-btn-content {
|
||
padding: 0 10px;
|
||
|
||
.btn-item + .btn-item {
|
||
margin-left: 10px;
|
||
}
|
||
.btn-item {
|
||
background-color: #fff;
|
||
border-color: #fff;
|
||
color: #639afa;
|
||
&:hover {
|
||
background-color: #d9ecff;
|
||
border-color: #d9ecff;
|
||
}
|
||
}
|
||
}
|
||
|
||
.component-preview {
|
||
max-height: 600px;
|
||
overflow: auto;
|
||
}
|
||
}
|
||
</style>
|