diff --git a/src/api/modules/courseFile.js b/src/api/modules/courseFile.js new file mode 100644 index 00000000..18b5bdef --- /dev/null +++ b/src/api/modules/courseFile.js @@ -0,0 +1,92 @@ +/** + * 课件文件内容的相关处理,当前只是业务管理员和教师功能使用 + * 下载课件,一期是直接在新的窗口打开文件的地址就可以了。不采用流的方式下载 + */ +import ajax from "./xajax.js"; + +/** + * 分页查询,课件的管理列表 + * @param {Object} data + * { + name:查询的名称的关键字 + self:true/false,默认是true,是否只查询自己上传的课件 + resOwner1:资源归属一级的id + resOwner2:资源归属二级的id + resOwner3:资源归属三级的id + resType: 文件类型,10视频,20音频,30图片, 40 文档,41表图文,50表scrom包,90表其它 + } + */ +const pageList = function (data) { + return ajax.post("/systemapi/xboe/m/course/file/pagelist", data); +}; + +/** + * 选择课件的查询,这里也是分页查询,只是返回的内容,字段会很少,用于课件制作那选择已有课件内容。 + * + * @param {Object} data + * 查询参数如上面pageList方法 + */ +const findList = function (data) { + //一期返回内容就对象,之后优化只是返回需要的几字段,不是全部 + return ajax.post("/systemapi/xboe/m/course/file/pagelist", data); +}; + +/** + * 上传保存,上传课件成功后,再调用此接口,保存上传的文件信息。 + * 接口返回课件记录(json对象),直接追加到列表中。 + * @param {Object} data + * { + fileName:上传文件的名称 + fileType: 文件的后缀名,比如:.mp4 + filePath: 文件的保存路径 + resOwner1:资源归属一级的id + resOwner2:资源归属二级的id + resOwner3:资源归属三级的id + resType: 文件类型,10视频,20音频,30图片, 40 文档,50表图文,60表scrom包,90表其它, + remark: 备注说明,可以为空,不填 + } + */ +const saveUpload = function (data) { + return ajax.post("/systemapi/xboe/m/course/file/upload/save", data, { + timeout: 60000, + }); +}; + +/** + * 批量更新,上传时,一个一个上传,下面的列表的保存,使用此方法 + * @param {Object} data list集合 + *saveUpload方法返回的对象 + */ + +const batchUpdate = function (data) { + return ajax.postJson("/systemapi/xboe/m/course/file/batch/update", data); +}; + +/** + * 删除一个课件文件 + * @param {Object} data + * { + id:删除记录的id, + filePath: 文件的路径 + flag: true/false, 是否是物理删除,在上传那是物理删除,在列表中是逻辑删除 + } + */ +const delFile = function (data) { + return ajax.post("/systemapi/xboe/m/course/file/delete", data); +}; + +/** + * 根据id获取详细信息,用于显示课件内容时,播放时 + * @param {Object} id + */ +const detail = function (id) { + return ajax.get("/systemapi/xboe/m/course/file/detail?id=" + id); +}; + +export default { + pageList, + saveUpload, + batchUpdate, + detail, + delFile, +}; diff --git a/src/assets/scss/common.scss b/src/assets/scss/common.scss index 15ad4ecc..67b46243 100644 --- a/src/assets/scss/common.scss +++ b/src/assets/scss/common.scss @@ -766,3 +766,53 @@ textarea { // } // } //} + +//循环 生成 mt mb 从 5 10 20 30 +@for $i from 1 through 100 { + .mt#{$i} { + margin-top: $i * 1px; + } + .mb#{$i} { + margin-bottom: $i * 1px; + } + .ml#{$i} { + margin-left: $i * 1px; + } + .mr#{$i} { + margin-right: $i * 1px; + } + .mv#{$i} { + margin-top: $i * 1px; + margin-bottom: $i * 1px; + } + .mh#{$i} { + margin-left: $i * 1px; + margin-right: $i * 1px; + } + .fs#{$i} { + font-size: $i * 1px; + } + .pt#{$i} { + padding-top: $i * 1px; + } + .pb#{$i} { + padding-bottom: $i * 1px; + } + .pl#{$i} { + padding-left: $i * 1px; + } + .pr#{$i} { + padding-right: $i * 1px; + } + .pv#{$i} { + padding-top: $i * 1px; + padding-bottom: $i * 1px; + } + .ph#{$i} { + padding-left: $i * 1px; + padding-right: $i * 1px; + } + .w#{$i} { + width: $i * 1px; + } +} diff --git a/src/components/BasicElTable/BasicTable.vue b/src/components/BasicElTable/BasicTable.vue new file mode 100644 index 00000000..e5916288 --- /dev/null +++ b/src/components/BasicElTable/BasicTable.vue @@ -0,0 +1,87 @@ + + + + + + + + + + {{ record[column.key] }} + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/CreatedCourse/AddVideo.vue b/src/components/CreatedCourse/AddVideo.vue new file mode 100644 index 00000000..cbdf23d9 --- /dev/null +++ b/src/components/CreatedCourse/AddVideo.vue @@ -0,0 +1,225 @@ + + + + + + + 上传新视频 + 文件大小限制:1G,支持的文件类型:mp4 + + + + 查询 + + + + + + + + + + + + + + + 您的浏览器不支持video + + + + 是 + 否 + + + + + 默认(系统自动控制) + 按进度 + + % + + + + + + + + + + + diff --git a/src/hooks/useCourseData.js b/src/hooks/useCourseData.js index 3fa62b58..adef5480 100644 --- a/src/hooks/useCourseData.js +++ b/src/hooks/useCourseData.js @@ -1,53 +1,271 @@ import { ref, reactive } from "vue"; -import { message } from "ant-design-vue"; - +// import { message } from "ant-design-vue"; +import { + SettingOutlined, + EyeOutlined, + DeleteOutlined, + MenuOutlined, + VideoCameraOutlined, + AudioOutlined, + FileTextOutlined, + PictureOutlined, + LinkOutlined, + FolderOpenOutlined, + BankOutlined, + EditOutlined, +} from "@ant-design/icons-vue"; +import { createVNode, h } from "vue"; +import { getType } from "@/hooks/useCreateCourseMaps"; /** * 课程数据管理hook - * @returns + * @returns */ export function useCourseData() { // 课程元数据 const courseMetadata = reactive({ courseName: "", createTime: "", + chooseIndex: "", + sectionIndex: "", }); + const tableColumns = [ + { + title: "序号", + key: "index", + width: 100, + customRender: ({ index }) => { + return h( + "span", + { + style: { + display: "flex", + justifyContent: "space-between", + }, + }, + [ + h("span", { class: "drag-handle" }, [ + createVNode(MenuOutlined, { + style: { fontSize: "14px", color: "#666" }, + }), + ]), + h("span", {}, index + 1), + ] + ); + }, + }, + { + title: "节名称", + key: "name", + dataIndex: "name", + customRender: ({ record }) => { + // 检查当前行是否处于编辑状态 + if (record._value.isEdit) { + return h( + "span", + { style: { display: "flex", alignItems: "center", gap: "8px" } }, + [ + h("input", { + value: record._value.copyName, + onInput: (e) => { + record._value.copyName = e.target.value; + }, + style: { + border: "1px solid #d9d9d9", + borderRadius: "4px", + padding: "4px 11px", + width: "200px", + }, + }), + h( + "a", + { + href: "javascript:void(0)", + style: { fontSize: "16px", color: "#52c41a" }, + onClick: () => { + record._value.name = record._value.copyName; + record._value.copyName = null; + record._value.isEdit = false; + }, + }, + "✓" + ), + ] + ); + } + + // 否则显示正常文本和编辑图标 + const getIconComponent = (type) => { + switch (type) { + case "视频": + return VideoCameraOutlined; + case "音频": + return AudioOutlined; + case "文档": + return FileTextOutlined; + case "图文": + return PictureOutlined; + case "链接": + return LinkOutlined; + case "SCORM": + return FolderOpenOutlined; + case "考试": + return BankOutlined; + case "作业": + return EditOutlined; + case "评估": + return BankOutlined; + default: + return FileTextOutlined; + } + }; + + const Icon = getIconComponent(getType(record._value.resType)); + return h( + "span", + { style: { display: "flex", alignItems: "center", gap: "8px" } }, + [ + createVNode(Icon, { style: { color: "#1890ff" } }), + h("span", {}, record._value.name), + h( + "a", + { + href: "javascript:void(0)", + style: { marginLeft: "4px", fontSize: "12px" }, + onClick: () => { + record._value.copyName = record._value.name; + record._value.isEdit = true; + }, + }, + "✎" + ), + ] + ); + }, + }, + { + title: "类型", + key: "resType", + dataIndex: "resType", + align: "center", + customRender: ({ record }) => { + return h( + "span", + { + style: { + display: "block", + textAlign: "center", + }, + }, + getType(record._value.resType) + ); + }, + }, + { + title: "操作", + key: "action", + width: 220, + align: "center", + customRender: ({ record, index, data }) => { + return h( + "span", + { + style: { display: "flex", justifyContent: "center", gap: "12px" }, + }, + [ + // 设置 + h( + "a", + { + href: "javascript:void(0)", + onClick: () => { + console.log("设置:", record); + // 在这里添加设置功能,比如打开设置弹窗 + }, + }, + [ + createVNode(SettingOutlined, { + style: { + fontSize: "14px", + color: "#1890ff", + paddingRight: "10px", + }, + }), + h( + "span", + { style: { marginLeft: "4px", fontSize: "12px" } }, + "设置" + ), + ] + ), + // 预览 + h( + "a", + { + href: "javascript:void(0)", + onClick: () => { + console.log("预览:", record); + // 在这里添加预览功能 + }, + }, + [ + createVNode(EyeOutlined, { + style: { fontSize: "14px", color: "#1890ff" }, + }), + h( + "span", + { style: { marginLeft: "4px", fontSize: "12px" } }, + "预览" + ), + ] + ), + // 删除 + h( + "a", + { + href: "javascript:void(0)", + onClick: () => { + console.log("删除:", record, "索引:", index); + // 执行删除操作 + data.splice(index, 1); + }, + }, + [ + createVNode(DeleteOutlined, { + style: { fontSize: "14px", color: "red" }, + }), + h( + "span", + { style: { marginLeft: "4px", fontSize: "12px" } }, + "删除" + ), + ] + ), + ] + ); + }, + }, + ]; // 课程列表数据 const courseList = ref([ { title: "课程1", - data: [ - { key: "1-1", name: "视频课件名称", type: "视频" }, - { key: "1-2", name: "音频课件名称", type: "音频" }, - { key: "1-3", name: "文档课件名称", type: "文档" }, - ], + data: [], }, { title: "课程2", - data: [ - { key: "2-1", name: "图文课件名称", type: "图文" }, - { key: "2-2", name: "外部链接", type: "链接" }, - ], + data: [], }, { title: "课程3", - data: [ - { key: "3-1", name: "SCORM", type: "SCORM" }, - { key: "3-2", name: "考试名称", type: "考试" }, - { key: "3-3", name: "自定义考试名称", type: "考试" }, - { key: "3-4", name: "作业名称", type: "作业" }, - ], + data: [], }, ]); // 课程操作映射 const courseOperations = { addVideo: (index) => { - message.error("功能开发中"); - console.log("添加视频功能调用,索引:", index); - if (index !== undefined && courseList.value[index]) { - courseList.value[index].title = "课程4"; - } + courseMetadata.chooseIndex = index; }, addAudio: () => { console.log("添加音频功能调用"); @@ -137,6 +355,7 @@ export function useCourseData() { courseMetadata, courseList, courseActionButtons, - executeCourseOperation + executeCourseOperation, + tableColumns, }; -} \ No newline at end of file +} diff --git a/src/hooks/useCreateCourseMaps.js b/src/hooks/useCreateCourseMaps.js new file mode 100644 index 00000000..c45b0c37 --- /dev/null +++ b/src/hooks/useCreateCourseMaps.js @@ -0,0 +1,36 @@ +import apiCourseFile from "@/api/modules/courseFile"; + +const contentTypeMaps = { + 10: "视频", + 20: "音频", + 30: "图片", + 40: "文档", + 41: "图文", + 50: "scorm", + 52: "外链", + 60: "作业", + 61: "考试", + 62: "评估", + 90: "其它", +}; +export function getType(type) { + return contentTypeMaps[type]; +} + +// 根据不同type 类型查询 媒体 视频等列表 +export function getPageListByType(params) { + return new Promise((resolve, reject) => { + apiCourseFile + .pageList(params) + .then((res) => { + if (res.status === 200) { + resolve(res); + } else { + reject(res.message); + } + }) + .catch((err) => { + reject(err); + }); + }); +} diff --git a/src/views/courselibrary/components/createCourse.vue b/src/views/courselibrary/components/createCourse.vue index 0ec5aa05..f075728f 100644 --- a/src/views/courselibrary/components/createCourse.vue +++ b/src/views/courselibrary/components/createCourse.vue @@ -1,44 +1,94 @@ @@ -49,7 +99,7 @@ const tableColumns = [ - 添加章 + 添加章 顺序学习 @@ -74,15 +124,23 @@ const tableColumns = [ + + + + + @@ -93,6 +151,10 @@ const tableColumns = [ .course-header { display: flex; justify-content: space-between; + .title { + font-size: 16px; + font-weight: 600; + } } .drag-course-btn-content { diff --git a/src/views/courselibrary/components/dragTable.vue b/src/views/courselibrary/components/dragTable.vue index d901d690..401768db 100644 --- a/src/views/courselibrary/components/dragTable.vue +++ b/src/views/courselibrary/components/dragTable.vue @@ -29,7 +29,7 @@ import { EyeOutlined, SettingOutlined, } from "@ant-design/icons-vue"; - +import { getType } from "@/hooks/useCreateCourseMaps"; // 定义 props const props = defineProps({ data: { @@ -60,8 +60,14 @@ const props = defineProps({ type: Boolean, default: true, }, + index: { + type: Number, + default: 0, + }, }); +// 设置 预览 删除 三个按钮emit 事件 +const emit = defineEmits(["setting", "preview", "delete"]); // 根据类型返回对应图标组件 const getIconComponent = (type) => { switch (type) { @@ -95,24 +101,191 @@ const editValue = ref(""); // 开始编辑 const startEdit = (record) => { - editingKey.value = record.key; - editValue.value = record.name; + record._value.copyName = record._value.name; + record._value.isEdit = true; }; // 保存编辑 const saveEdit = (record) => { - record.name = editValue.value; - editingKey.value = ""; - editValue.value = ""; + record._value.name = record._value.copyName; + record._value.copyName = null; + record._value.isEdit = false; }; // 删除处理函数 -const handleDelete = (key) => { - const index = props.data.findIndex((item) => item.key === key); - if (index > -1) { - props.data.splice(index, 1); - console.log("删除了:", key); - } +const handleDelete = (index) => { + emit("delete", { index: props.index, selectionIndex: index }); +}; +const handleSetting = (index) => { + emit("setting", { index: props.index, selectionIndex: index }); +}; +const handlePreview = (index) => { + emit("preview", { index: props.index, selectionIndex: index }); +}; + +// 渲染序号列 +const renderIndexColumn = () => { + return ({ index }) => { + return h( + "span", + { + style: { + display: "flex", + justifyContent: "space-between", + }, + }, + [ + h("span", { class: "drag-handle" }, [ + createVNode(MenuOutlined, { + style: { fontSize: "14px", color: "#666" }, + }), + ]), + h("span", {}, index + 1), + ] + ); + }; +}; + +// 渲染名称列 +const renderNameColumn = () => { + return ({ record }) => { + // 如果处于编辑状态,显示输入框和确认按钮 + if (record._value.isEdit) { + return h( + "span", + { style: { display: "flex", alignItems: "center", gap: "8px" } }, + [ + h("input", { + value: record._value.copyName, + onInput: (e) => { + record._value.copyName = e.target.value; + }, + style: { + border: "1px solid #d9d9d9", + borderRadius: "4px", + padding: "4px 11px", + width: "200px", + }, + }), + h( + "a", + { + href: "javascript:void(0)", + style: { fontSize: "16px", color: "#52c41a" }, + onClick: () => { + saveEdit(record); + }, + }, + "✓" + ), + ] + ); + } + + // 否则显示正常文本和编辑图标 + const Icon = getIconComponent(getType(record._value.resType)); + return h( + "span", + { style: { display: "flex", alignItems: "center", gap: "8px" } }, + [ + createVNode(Icon, { style: { color: "#1890ff" } }), + h("span", {}, record._value.name), + h( + "a", + { + href: "javascript:void(0)", + style: { marginLeft: "4px", fontSize: "12px" }, + onClick: () => { + startEdit(record); + }, + }, + "✎" + ), + ] + ); + }; +}; + +// 渲染资源类型列 +const renderResTypeColumn = () => { + return ({ record }) => { + return h( + "span", + { + style: { + display: "block", + textAlign: "center", + }, + }, + getType(record._value.resType) + ); + }; +}; + +// 渲染操作列 +const renderActionColumn = () => { + return ({ record, index }) => { + return h( + "span", + { + style: { display: "flex", justifyContent: "center", gap: "12px" }, + }, + [ + // 设置 + h( + "a", + { href: "javascript:void(0)", onClick: () => handleSetting(index) }, + [ + createVNode(SettingOutlined, { + style: { + fontSize: "14px", + color: "#1890ff", + paddingRight: "10px", + }, + }), + h( + "span", + { style: { marginLeft: "4px", fontSize: "12px" } }, + "设置" + ), + ] + ), + // 预览 + h( + "a", + { href: "javascript:void(0)", onClick: () => handlePreview(index) }, + [ + createVNode(EyeOutlined, { + style: { fontSize: "14px", color: "#1890ff" }, + }), + h( + "span", + { style: { marginLeft: "4px", fontSize: "12px" } }, + "预览" + ), + ] + ), + // 删除 + h( + "a", + { + href: "javascript:void(0)", + onClick: () => handleDelete(index), + }, + [ + createVNode(DeleteOutlined, { + style: { fontSize: "14px", color: "red" }, + }), + h( + "span", + { style: { marginLeft: "4px", fontSize: "12px" } }, + "删除" + ), + ] + ), + ] + ); + }; }; // 处理列定义,添加自定义渲染逻辑 @@ -125,142 +298,21 @@ const processedColumns = computed(() => { // 为序号列添加自定义渲染 if (col.key === "index") { - processedCol.customRender = ({ index }) => { - return h( - "span", - { - style: { - display: "flex", - justifyContent: "space-between", - }, - }, - [ - h("span", { class: "drag-handle" }, [ - createVNode(MenuOutlined, { - style: { fontSize: "14px", color: "#666" }, - }), - ]), - h("span", {}, index + 1), - ] - ); - }; + processedCol.customRender = renderIndexColumn(); } // 为节名称列添加自定义渲染 if (col.key === "name") { - processedCol.customRender = ({ record }) => { - // 检查当前行是否处于编辑状态 - const isEditing = record.key === editingKey.value; + processedCol.customRender = renderNameColumn(); + } - // 如果处于编辑状态,显示输入框和确认按钮 - if (isEditing) { - return h( - "span", - { style: { display: "flex", alignItems: "center", gap: "8px" } }, - [ - h("input", { - value: editValue.value, - onInput: (e) => { - editValue.value = e.target.value; - }, - style: { - border: "1px solid #d9d9d9", - borderRadius: "4px", - padding: "4px 11px", - width: "200px", - }, - }), - h( - "a", - { - href: "javascript:void(0)", - style: { fontSize: "16px", color: "#52c41a" }, - onClick: () => saveEdit(record), - }, - "✓" - ), - ] - ); - } - - // 否则显示正常文本和编辑图标 - const Icon = getIconComponent(record.type); - return h( - "span", - { style: { display: "flex", alignItems: "center", gap: "8px" } }, - [ - createVNode(Icon, { style: { color: "#1890ff" } }), - h("span", {}, record.name), - h( - "a", - { - href: "javascript:void(0)", - style: { marginLeft: "4px", fontSize: "12px" }, - onClick: () => startEdit(record), - }, - "✎" - ), - ] - ); - }; + if (col.key === "resType") { + processedCol.customRender = renderResTypeColumn(); } // 为操作列添加自定义渲染 if (col.key === "action") { - processedCol.customRender = ({ record }) => { - return h( - "span", - { - style: { display: "flex", justifyContent: "center", gap: "12px" }, - }, - [ - // 设置 - h("a", { href: "javascript:void(0)" }, [ - createVNode(SettingOutlined, { - style: { - fontSize: "14px", - color: "#1890ff", - paddingRight: "10px", - }, - }), - h( - "span", - { style: { marginLeft: "4px", fontSize: "12px" } }, - "设置" - ), - ]), - // 预览 - h("a", { href: "javascript:void(0)" }, [ - createVNode(EyeOutlined, { - style: { fontSize: "14px", color: "#1890ff" }, - }), - h( - "span", - { style: { marginLeft: "4px", fontSize: "12px" } }, - "预览" - ), - ]), - // 删除 - h( - "a", - { - href: "javascript:void(0)", - onClick: () => handleDelete(record.key), - }, - [ - createVNode(DeleteOutlined, { - style: { fontSize: "14px", color: "red" }, - }), - h( - "span", - { style: { marginLeft: "4px", fontSize: "12px" } }, - "删除" - ), - ] - ), - ] - ); - }; + processedCol.customRender = renderActionColumn(); } return processedCol; @@ -274,85 +326,13 @@ const processedColumns = computed(() => { key: "index", width: 80, align: "center", - customRender: ({ index }) => { - return h( - "span", - { - style: { - display: "flex", - justifyContent: "space-between", - }, - }, - [ - h("span", { class: "drag-handle" }, [ - createVNode(MenuOutlined, { - style: { fontSize: "14px", color: "#666" }, - }), - ]), - h("span", {}, index + 1), - ] - ); - }, + customRender: renderIndexColumn(), }, { title: "节名称", key: "name", dataIndex: "name", - customRender: ({ record }) => { - // 检查当前行是否处于编辑状态 - const isEditing = record.key === editingKey.value; - - // 如果处于编辑状态,显示输入框和确认按钮 - if (isEditing) { - return h( - "span", - { style: { display: "flex", alignItems: "center", gap: "8px" } }, - [ - h("input", { - value: editValue.value, - onInput: (e) => { - editValue.value = e.target.value; - }, - style: { - border: "1px solid #d9d9d9", - borderRadius: "4px", - padding: "4px 11px", - width: "200px", - }, - }), - h( - "a", - { - href: "javascript:void(0)", - style: { fontSize: "16px", color: "#52c41a" }, - onClick: () => saveEdit(record), - }, - "✓" - ), - ] - ); - } - - // 否则显示正常文本和编辑图标 - const Icon = getIconComponent(record.type); - return h( - "span", - { style: { display: "flex", alignItems: "center", gap: "8px" } }, - [ - createVNode(Icon, { style: { color: "#1890ff" } }), - h("span", {}, record.name), - h( - "a", - { - href: "javascript:void(0)", - style: { marginLeft: "4px", fontSize: "12px" }, - onClick: () => startEdit(record), - }, - "✎" - ), - ] - ); - }, + customRender: renderNameColumn(), }, { title: "类型", @@ -365,58 +345,7 @@ const processedColumns = computed(() => { key: "action", width: 220, align: "center", - customRender: ({ record }) => { - return h( - "span", - { style: { display: "flex", justifyContent: "center", gap: "12px" } }, - [ - // 设置 - h("a", { href: "javascript:void(0)" }, [ - createVNode(SettingOutlined, { - style: { - fontSize: "14px", - color: "#1890ff", - paddingRight: "10px", - }, - }), - h( - "span", - { style: { marginLeft: "4px", fontSize: "12px" } }, - "设置" - ), - ]), - // 预览 - h("a", { href: "javascript:void(0)" }, [ - createVNode(EyeOutlined, { - style: { fontSize: "14px", color: "#1890ff" }, - }), - h( - "span", - { style: { marginLeft: "4px", fontSize: "12px" } }, - "预览" - ), - ]), - // 删除 - h( - "a", - { - href: "javascript:void(0)", - onClick: () => handleDelete(record.key), - }, - [ - createVNode(DeleteOutlined, { - style: { fontSize: "14px", color: "red" }, - }), - h( - "span", - { style: { marginLeft: "4px", fontSize: "12px" } }, - "删除" - ), - ] - ), - ] - ); - }, + customRender: renderActionColumn(), }, ]; }); @@ -440,10 +369,6 @@ const components = { pull: props.allowDragOut, put: props.allowDragIn, }, - onStart: () => {}, - onEnd: (evt) => { - console.log("拖拽结束,新顺序:", props.data); - }, }, { item: ({ element, index }) =>