diff --git a/src/api/modules/filecloud.js b/src/api/modules/filecloud.js new file mode 100644 index 00000000..a6cae99f --- /dev/null +++ b/src/api/modules/filecloud.js @@ -0,0 +1,87 @@ +import ajax from "./xajax"; + +/** + * @param {文件夹} folder + */ +const list = function (folder) { + return ajax.post("/systemapi/api/m/xfile/base/all/list", { folder }); +}; + +const findByName = function (name) { + return ajax.post("/systemapi/api/m/xfile/base/all/find", { name }); +}; + +/** + * 文件夹树 + */ +const folderTree = function () { + return ajax.get("/systemapi/api/m/xfile/base/folder/tree"); +}; + +/** + * 创建文件夹 + * @param {*} data + */ +const folderCreate = function (data) { + return ajax.post("/systemapi/api/m/xfile/base/folder/create", data); +}; + +/** + * 重命名 + * @param {*} id + * @param {*} name + */ +const folderRename = function (id, name) { + return ajax.post("/systemapi/api/m/xfile/base/folder/rename", { id, name }); +}; + +/** + * 删除文件夹 + * @param {*} id + */ +const folderDelete = function (id) { + return ajax.del("/systemapi/api/m/xfile/base/folder/delete?id=" + id); +}; + +/** + * 文章分页查询 + * @param {*} data + */ +const filePageList = function (data) { + return ajax.post("/systemapi/api/m/xfile/base/file/pagelist", data); +}; + +const fileRename = function (id, name) { + return ajax.post("/systemapi/api/m/xfile/base/file/rename", { id, name }); +}; + +const fileDelete = function (id, path) { + return ajax.post("/systemapi/api/m/xfile/base/file/delete", { id, path }); +}; + +const fileMove = function (id, folderId) { + return ajax.post("/systemapi/api/m/xfile/base/file/rename", { id, folderId }); +}; + +const fileDetail = function (id) { + return ajax.get("/systemapi/api/m/xfile/base/file/detail?id=" + id); +}; + +const fileSetDelete = function (id) { + // return ajax.del("/systemapi/api/m/xfile/base/file/setdelete?id=" + id); +}; + +export default { + list, + folderTree, + folderCreate, + folderRename, + folderDelete, + filePageList, + fileRename, + fileDelete, + fileMove, + fileDetail, + fileSetDelete, + findByName, +}; diff --git a/src/api/modules/newApi.js b/src/api/modules/newApi.js new file mode 100644 index 00000000..65e8c19e --- /dev/null +++ b/src/api/modules/newApi.js @@ -0,0 +1,8 @@ +import http from "./newConfig"; + +// 课程内容 +export const getClassTree = function (sysResType) { + return http.get( + `/systemapi/xboe/type/tree-list?sysResType=${sysResType}&status=1` + ); +}; diff --git a/src/api/modules/newConfig.js b/src/api/modules/newConfig.js new file mode 100644 index 00000000..71f99b7c --- /dev/null +++ b/src/api/modules/newConfig.js @@ -0,0 +1,107 @@ +/* + * @Author: lixg lixg@dongwu-inc.com + * @Date: 2022-11-21 14:32:52 + * @LastEditors: lixg lixg@dongwu-inc.com + * @LastEditTime: 2023-01-04 13:49:54 + * @FilePath: /fe-manage/src/api/config.js + * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE + */ +import { message } from "ant-design-vue"; +import axios from "axios"; +import router from "@/router"; +import { REFRESH_TOKEN_API } from "@/api/ThirdApi"; +import { boeRequest } from "@/api/request"; +// import { getCookie } from '../api/method' +// const Qs = require("qs"); + +// axios.defaults.headers.post["Content-Type"] = +// "application/x-www-form-urlencoded"; +export const FILE_UPLOAD_URL = process.env.VUE_APP_BASE_API + "/file/upload"; +export const BATCH_IMPORT_SCORE = + process.env.VUE_APP_BASE_API + "/admin/offcourse/batchImportScore"; +axios.defaults.withCredentials = true; +const http = axios.create({ + // baseURL: process.env.VUE_APP_BASE_API, + timeout: 1000 * 15, + // headers: { "Content-Type": "multipart/form-data" }, + headers: { "Content-Type": "application/json" }, +}); + +http.interceptors.request.use( + (config) => { + // console.log("config", config); + // const token = localStorage.getItem("token"); + // // const token = getCookie('token') + // // console.log('token', token) + // if (token) { + // config.headers.token = token; //测试1111 + // } else { + // console.log("当前请求页面无token,请执行操作!!!"); + + // // 此处测试默认配置token + // config.headers.token = + // "eyJ0eXBlIjoidG9rZW4iLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwOlwvXC91LmJvZS5jb20iLCJpYXQiOjE2NzAxNTMxMDMsImV4cCI6MTY3MDE2MDMwMywiR2l2ZW5OYW1lIjoiYm9ldSIsInVzZXJJZCI6IjZCMDQ5RkFGLUMzMTQtN0NDRi0wRDI4LTBEMjNGNEM0MjUzMSIsInVJZCI6Ijk2NTM0MjAyNzQ5NzYwNzE2OCIsInBlcm1pc3Npb24iOiIifQ==.c937b2d3a59cbab2136fdde55fd38f06bdff041212aab0fa6741bc4be41e28a7"; + // // } + return config; + }, + (err) => { + console.log("登陆前拦截", err); + return Promise.reject(err); + } +); + +http.interceptors.response.use( + (response) => { + // console.log('response', response) + let { + data: { code, msg, show, status }, + } = response; + + if (!code && status) { + code = status; + } + if (code === 0 || code === 200) { + return response.data; + } + if (code === 1000) { + window.location.href = + process.env.VUE_APP_LOGIN_URL + + encodeURIComponent( + window.location.protocol + + process.env.VUE_APP_BOE_API_URL + + process.env.VUE_APP_BASE + + router.currentRoute.value.fullPath + ); + // TODO token过期后退出登录 清空当前用户标记 - 为了刷新页面使用 + localStorage.removeItem("refreshPage"); + return Promise.reject(response); + } + if (code === 1001) { + window.location.href = + process.env.VUE_APP_LOGIN_URL + + encodeURIComponent( + window.location.protocol + + process.env.VUE_APP_BOE_API_URL + + process.env.VUE_APP_BASE + + router.currentRoute.value.fullPath + ); + return Promise.reject(response); + } + show ? message.error(msg) : message.error("系统接口数据异常,请联系管理员"); + console.log("api %o", msg); + return Promise.reject(response); + }, + function (error) { + if (error.message == "timeout of 1ms exceeded") { + message.destroy(); + message.error("请求超时"); + } + console.log("api error %o", error); + return Promise.reject(error); + } +); + +export default http; +export function setHttpTimeout(newTimeout) { + http.defaults.timeout = newTimeout; +} diff --git a/src/api/modules/xajax.js b/src/api/modules/xajax.js new file mode 100644 index 00000000..1eb3777d --- /dev/null +++ b/src/api/modules/xajax.js @@ -0,0 +1,187 @@ +import axios from "axios"; +import qs from "qs"; +import { notification, Modal, message } from "ant-design-vue"; + +// 登录重定向 URL(可根据环境变量配置) +const ReLoginUrl = process.env.VUE_APP_LOGIN_URL || "/login"; +const TokenName = "XBOE-Access-Token"; + +// JSON 请求实例(Content-Type: application/json) +const jsonRequest = axios.create({ + headers: { "Content-Type": "application/json;charset=utf-8" }, + // baseURL: process.env.VUE_APP_BASE_API, + timeout: 60000, +}); + +// 请求拦截器 - 不再携带 token(你要求移除 getToken) +jsonRequest.interceptors.request.use( + (config) => { + // ⚠️ 已移除 getToken 相关逻辑 + // 如果后续需要手动加 token,可在此处添加: + // config.headers[TokenName] = 'your-token'; + return config; + }, + (error) => { + console.error("Request error:", error); + return Promise.reject(error); + } +); + +// 响应拦截器 +jsonRequest.interceptors.response.use( + (res) => { + const code = res.data.status || 200; + + if (code === 200) { + return res.data; + } else { + if (code === 6001 || code === 401 || code === 402) { + Modal.warning({ + title: "登录失效", + content: "您已被登出,可以取消继续留在该页面,或者重新登录", + okText: "重新登录", + onOk() { + window.location.href = ReLoginUrl; + }, + }); + } else if (code === 403) { + message.error("当前操作没有权限"); + } else if (code === 302) { + window.location.href = ReLoginUrl; + } else { + // 其他业务错误,直接返回数据供调用方处理 + return res.data; // 不 throw 错误,方便前端自定义处理 + } + } + }, + (error) => { + console.error("Response error:", error); + let msg = "未知错误,请稍后重试"; + + if (error.message === "Network Error") { + msg = "网络异常,请检查网络连接"; + } else if (error.message.includes("timeout")) { + msg = "系统接口请求超时"; + } else if (error.message.includes("Request failed with status code")) { + const statusCode = error.message.substr(-3); + msg = `系统接口 ${statusCode} 异常`; + if (statusCode === "500") { + notification.error({ + message: "服务错误", + description: "服务器内部错误,请联系管理员。", + duration: 5, + }); + } + } + + message.error(msg, 5); + return Promise.reject(error); + } +); + +// Form 请求实例(x-www-form-urlencoded) +const formRequest = axios.create({ + headers: { "Content-Type": "application/x-www-form-urlencoded" }, + // baseURL: process.env.VUE_APP_BASE_API, + timeout: 10000, +}); + +// 请求拦截器(form) +formRequest.interceptors.request.use( + (config) => { + // 同样不带 token + return config; + }, + (error) => { + console.error("Form request error:", error); + return Promise.reject(error); + } +); + +// 响应拦截器(form) +formRequest.interceptors.response.use( + (res) => { + const code = res.data.status || 200; + + if (code === 200) { + return res.data; + } else { + if (code === 6001 || code === 401 || code === 402) { + Modal.warning({ + title: "登录已过期", + content: "登录状态无效,即将跳转至登录页", + okText: "确认", + onOk() { + window.location.href = ReLoginUrl; + }, + }); + } else if (code === 403) { + message.error("暂无权限执行此操作"); + } else if (code === 302) { + window.location.href = ReLoginUrl; + } else { + return res.data; // 返回原始数据供业务判断 + } + } + }, + (error) => { + console.error("Form response error:", error); + let msg = "请求失败"; + + if (error.message === "Network Error") { + msg = "网络连接失败"; + } else if (error.message.includes("timeout")) { + msg = "请求超时"; + } else if (error.message.includes("Request failed with status code")) { + msg = `服务端异常 (${error.message.slice(-3)})`; + } + + message.error(msg, 5); + return Promise.reject(error); + } +); + +// ================== API 方法封装 ================== + +const requestJson = jsonRequest.request; + +const get = (url, params, config) => { + const finalConfig = { ...config, params }; + return formRequest.get(url, finalConfig); +}; + +const post = (url, data, config) => { + return formRequest.post(url, qs.stringify(data), config); +}; + +const postForm = (url, data, config) => { + return formRequest.post(url, data, config); // 不 stringify,用于上传文件等 +}; + +const postJson = jsonRequest.post; +const put = (url, data, config) => { + return formRequest.put(url, qs.stringify(data), config); +}; +const putJson = jsonRequest.put; +const patch = (url, data, config) => { + return formRequest.patch(url, qs.stringify(data), config); +}; +const patchJson = jsonRequest.patch; +const del = (url, config) => { + return formRequest.delete(url, config); +}; + +// 导出统一接口 +export default { + request: jsonRequest.request, // 通用 request 方法 + requestJson, + get, + post, + postForm, + postJson, + put, + putJson, + patch, + patchJson, + del, +}; diff --git a/src/assets/images/filetypes.png b/src/assets/images/filetypes.png new file mode 100644 index 00000000..ce6c79b5 Binary files /dev/null and b/src/assets/images/filetypes.png differ diff --git a/src/assets/scss/common.scss b/src/assets/scss/common.scss index 9fe41ec3..15ad4ecc 100644 --- a/src/assets/scss/common.scss +++ b/src/assets/scss/common.scss @@ -299,8 +299,8 @@ textarea { display: inline-block; .ant-select:not(.ant-select-customize-input) - .ant-select-selector - .ant-select-selection-search-input { + .ant-select-selector + .ant-select-selection-search-input { background-color: rgba(255, 255, 255, 0); border: none; } @@ -317,7 +317,7 @@ textarea { .ant-select-focused:not(.ant-select-disabled).ant-select:not( .ant-select-customize-input ) - .ant-select-selector { + .ant-select-selector { box-shadow: none; } @@ -561,8 +561,8 @@ textarea { } .ant-table-tbody - > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) - > td { + > tr:hover:not(.ant-table-expanded-row):not(.ant-table-row-selected) + > td { background: #f6f9fd; } @@ -678,9 +678,9 @@ textarea { width: 100%; height: 68px; background: linear-gradient( - 0deg, - rgba(78, 166, 255, 0) 0%, - rgba(78, 166, 255, 0.2) 100% + 0deg, + rgba(78, 166, 255, 0) 0%, + rgba(78, 166, 255, 0.2) 100% ); } @@ -697,25 +697,25 @@ textarea { .ant-modal { .modalHeader { background: linear-gradient( - 180deg, - rgba(103, 64, 255, 0.2) 0%, - rgba(166, 168, 255, 0) 100% + 180deg, + rgba(103, 64, 255, 0.2) 0%, + rgba(166, 168, 255, 0) 100% ) !important; } .top { background: linear-gradient( - 180deg, - rgba(103, 64, 255, 0.2) 0%, - rgba(166, 168, 255, 0) 100% + 180deg, + rgba(103, 64, 255, 0.2) 0%, + rgba(166, 168, 255, 0) 100% ) !important; } .del_header { background: linear-gradient( - 180deg, - rgba(103, 64, 255, 0.2) 0%, - rgba(166, 168, 255, 0) 100% + 180deg, + rgba(103, 64, 255, 0.2) 0%, + rgba(166, 168, 255, 0) 100% ) !important; } } @@ -736,4 +736,33 @@ textarea { z-index: 100; } -//loading-------------------------------------------------------- +.el-select, +.el-cascader { + width: 100%; +} +////loading-------------------------------------------------------- +// +//.default-form { +// .ant-form-item { +// .ant-form-item-label { +// line-height: 40px; +// } +// .ant-form-item-control { +// .ant-input, +// .ant-select-selector { +// width: 100%; +// line-height: 40px; +// +// height: 40px; +// //border-radius: 8px; +// } +// .ant-form-item-control-input-content { +// line-height: 40px; +// } +// .ant-checkbox-group, +// .ant-radio-group { +// line-height: 40px; +// } +// } +// } +//} diff --git a/src/assets/scss/filetypes.css b/src/assets/scss/filetypes.css new file mode 100644 index 00000000..b850f76a --- /dev/null +++ b/src/assets/scss/filetypes.css @@ -0,0 +1,280 @@ +.ft { + display: block; + width: 64px; + height: 64px; + background: url(../images/filetypes.png) no-repeat 0 0; +} +.ft_small { + display: block; + width: 16px; + height: 16px; + float: left; + background: url(../images/filetypes.png) no-repeat 0 0; + margin-right: 3px; +} + +.ft_null { + background-position: 0 0px; +} +.ft_small_null { + background-position: -164px -48px; +} +.ft_folder { + background-position: 0 -124px; +} +.ft_small_folder { + background-position: -164px -172px; +} +.ft_ai { + background-position: 0 -248px; +} +.ft_small_ai { + background-position: -164px -296px; +} +.ft_aif { + background-position: 0 -372px; +} +.ft_small_aif { + background-position: -164px -420px; +} +.ft_aiff { + background-position: 0 -496px; +} +.ft_small_aiff { + background-position: -164px -544px; +} +.ft_asp { + background-position: 0 -620px; +} +.ft_small_asp { + background-position: -164px -668px; +} +.ft_apk { + background-position: 0 -744px; +} +.ft_small_apk { + background-position: -164px -792px; +} +.ft_avi { + background-position: 0 -868px; +} +.ft_small_avi { + background-position: -164px -916px; +} +.ft_bmp { + background-position: 0 -992px; +} +.ft_small_bmp { + background-position: -164px -1040px; +} +.ft_doc { + background-position: 0 -1116px; +} +.ft_small_doc { + background-position: -164px -1164px; +} +.ft_docx { + background-position: 0 -1116px; +} +.ft_small_docx { + background-position: -164px -1164px; +} +.ft_dvd { + background-position: 0 -1240px; +} +.ft_small_dvd { + background-position: -164px -1288px; +} +.ft_dwf { + background-position: 0 -1364px; +} +.ft_small_dwf { + background-position: -164px -1412px; +} +.ft_exe { + background-position: 0 -1488px; +} +.ft_small_exe { + background-position: -164px -1536px; +} +.ft_fla { + background-position: 0 -1612px; +} +.ft_small_fla { + background-position: -164px -1660px; +} +.ft_gif { + background-position: 0 -1736px; +} +.ft_small_gif { + background-position: -164px -1784px; +} +.ft_htc { + background-position: 0 -1860px; +} +.ft_small_htc { + background-position: -164px -1908px; +} +.ft_html { + background-position: 0 -1984px; +} +.ft_small_html { + background-position: -164px -2032px; +} +.ft_htm { + background-position: 0 -1984px; +} +.ft_small_htm { + background-position: -164px -2032px; +} + +.ft_ics { + background-position: 0 -2108px; +} +.ft_small_ics { + background-position: -164px -2156px; +} +.ft_ico { + background-position: 0 -2232px; +} +.ft_small_ico { + background-position: -164px -2280px; +} +.ft_java { + background-position: 0 -2357px; +} +.ft_small_java { + background-position: -164px -2404px; +} +.ft_jpg { + background-position: 0 -2480px; +} +.ft_small_jpg { + background-position: -164px -2528px; +} +.ft_png { + background-position: 0 -2480px; +} +.ft_small_png { + background-position: -164px -2528px; +} +.ft_jsp { + background-position: 0 -2604px; +} +.ft_small_jsp { + background-position: -164px -2652px; +} +.ft_mov { + background-position: 0 -2728px; +} +.ft_small_mov { + background-position: -164px -2776px; +} +.ft_mp3 { + background-position: 0 -2852px; +} +.ft_small_mp3 { + background-position: -164px -2900px; +} +.ft_mp4 { + background-position: 0 -2976px; +} +.ft_small_mp4 { + background-position: -164px -3024px; +} +.ft_pdf { + background-position: 0 -3100px; +} +.ft_small_pdf { + background-position: -164px -3148px; +} +.ft_ppt { + background-position: 0 -3224px; +} +.ft_small_ppt { + background-position: -164px -3272px; +} +.ft_pptx { + background-position: 0 -3224px; +} +.ft_small_pptx { + background-position: -164px -3272px; +} +.ft_psd { + background-position: 0 -3348px; +} +.ft_small_psd { + background-position: -164px -3396px; +} +.ft_rm { + background-position: 0 -3472px; +} +.ft_small_rm { + background-position: -164px -3520px; +} +.ft_rif { + background-position: 0 -3596px; +} +.ft_small_rif { + background-position: -164px -3644px; +} +.ft_rar { + background-position: 0 -3719px; +} +.ft_small_rar { + background-position: -164px -3768px; +} +.ft_swf { + background-position: 0 -3844px; +} +.ft_small_swf { + background-position: -164px -3892px; +} +.ft_tif { + background-position: 0 -3968px; +} +.ft_small_tif { + background-position: -164px -4016px; +} +.ft_txt { + background-position: 0 -4092px; +} +.ft_small_txt { + background-position: -164px -4140px; +} +.ft_wma { + background-position: 0 -4216px; +} +.ft_small_wma { + background-position: -164px -4264px; +} +.ft_wri { + background-position: 0 -4339px; +} +.ft_small_wri { + background-position: -164px -4387px; +} +.ft_xls { + background-position: 0 -4464px; +} +.ft_small_xls { + background-position: -164px -4512px; +} +.ft_xlsx { + background-position: 0 -4464px; +} +.ft_small_xlsx { + background-position: -164px -4512px; +} +.ft_xsl { + background-position: 0 -4587px; +} +.ft_small_xsl { + background-position: -164px -4635px; +} +.ft_zip { + background-position: 0 -4712px; +} +.ft_small_zip { + background-position: -164px -4760px; +} diff --git a/src/components/FileCloud/index.vue b/src/components/FileCloud/index.vue new file mode 100644 index 00000000..03bb3eb1 --- /dev/null +++ b/src/components/FileCloud/index.vue @@ -0,0 +1,301 @@ + + + + + + + diff --git a/src/hooks/useCourseData.js b/src/hooks/useCourseData.js new file mode 100644 index 00000000..3fa62b58 --- /dev/null +++ b/src/hooks/useCourseData.js @@ -0,0 +1,142 @@ +import { ref, reactive } from "vue"; +import { message } from "ant-design-vue"; + +/** + * 课程数据管理hook + * @returns + */ +export function useCourseData() { + // 课程元数据 + const courseMetadata = reactive({ + courseName: "", + createTime: "", + }); + + // 课程列表数据 + const courseList = ref([ + { + title: "课程1", + data: [ + { key: "1-1", name: "视频课件名称", type: "视频" }, + { key: "1-2", name: "音频课件名称", type: "音频" }, + { key: "1-3", name: "文档课件名称", type: "文档" }, + ], + }, + { + title: "课程2", + data: [ + { key: "2-1", name: "图文课件名称", type: "图文" }, + { key: "2-2", name: "外部链接", type: "链接" }, + ], + }, + { + 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: "作业" }, + ], + }, + ]); + + // 课程操作映射 + const courseOperations = { + addVideo: (index) => { + message.error("功能开发中"); + console.log("添加视频功能调用,索引:", index); + if (index !== undefined && courseList.value[index]) { + courseList.value[index].title = "课程4"; + } + }, + addAudio: () => { + console.log("添加音频功能调用"); + }, + addDocument: () => { + console.log("添加文档功能调用"); + }, + addImageText: () => { + console.log("添加图文功能调用"); + }, + addExternalLink: () => { + console.log("添加外部链接功能调用"); + }, + addScorm: () => { + console.log("添加SCORM功能调用"); + }, + addExam: () => { + console.log("添加考试功能调用"); + }, + addHomework: () => { + console.log("添加作业功能调用"); + }, + addAssessment: () => { + console.log("添加评估功能调用"); + }, + }; + + // 执行课程操作 + const executeCourseOperation = (operationName, data) => { + if (courseOperations[operationName]) { + courseOperations[operationName](data); + } else { + console.warn(`未找到操作: ${operationName}`); + } + }; + + // 课程操作按钮 + const courseActionButtons = [ + { + label: "添加视频", + icon: "", + fun: "addVideo", + }, + { + label: "添加音频", + icon: "", + fun: "addAudio", + }, + { + label: "添加文档", + icon: "", + fun: "addDocument", + }, + { + label: "添加图文", + icon: "", + fun: "addImageText", + }, + { + label: "外部链接", + icon: "", + fun: "addExternalLink", + }, + { + label: "SCORM", + icon: "", + fun: "addScorm", + }, + { + label: "添加考试", + icon: "", + fun: "addExam", + }, + { + label: "添加作业", + icon: "", + fun: "addHomework", + }, + { + label: "添加评估", + icon: "", + fun: "addAssessment", + }, + ]; + + return { + courseMetadata, + courseList, + courseActionButtons, + executeCourseOperation + }; +} \ No newline at end of file diff --git a/src/hooks/useCourseForm.js b/src/hooks/useCourseForm.js new file mode 100644 index 00000000..85f80bb8 --- /dev/null +++ b/src/hooks/useCourseForm.js @@ -0,0 +1,50 @@ +import { reactive, ref } from "vue"; + +/** + * 课程表单相关hook + * @returns + */ +export function useCourseForm() { + // 表单相关 + const formRef = ref(); + const formState = reactive({ + courseName: "", // 课程名称 + courseCategory: [], // 课程分类 + resourceBelong: undefined, // 资源归属 + lecturer: undefined, // 授课教师 + targetGroup: "", // 目标人群 + courseTags: [], // 课程标签 + audience: undefined, // 受众 + visibility: "Apple", // 可见性 + coverIntro: "", // 封面介绍 + courseValue: "", // 课程价值 + courseIntro: "", // 课程简介 + }); + + // 可见性选项 + const visibilityOptions = [ + { label: "PC端可见", value: "Apple" }, + { label: "移动端可见", value: "Pear" }, + { label: "多端可见", value: "Orange", disabled: false }, + ]; + + // 表单重置 + const resetForm = (courseCoverurl, fileList) => { + if (formRef.value) { + formRef.value.resetFields(); + } + if (courseCoverurl) { + courseCoverurl.value = ""; + } + if (fileList) { + fileList.value = []; + } + }; + + return { + formRef, + formState, + visibilityOptions, + resetForm + }; +} \ No newline at end of file diff --git a/src/hooks/useUpload.js b/src/hooks/useUpload.js new file mode 100644 index 00000000..555b8af7 --- /dev/null +++ b/src/hooks/useUpload.js @@ -0,0 +1,65 @@ +import { ref } from "vue"; +import { ElMessage } from "element-plus"; +import { Loading, Plus } from "@element-plus/icons-vue"; + +/** + * 文件上传相关hook + * @returns + */ +export function useUpload() { + // 上传相关 + const fileList = ref([]); + const loading = ref(false); + const courseCoverurl = ref(""); + + // 获取图片base64 + const getBase64 = (img, callback) => { + const reader = new FileReader(); + reader.addEventListener("load", () => callback(reader.result)); + reader.readAsDataURL(img); + }; + + // 上传状态变化处理 + const handleChange = (info) => { + if (info.file.status === "uploading") { + loading.value = true; + return; + } + if (info.file.status === "done") { + // Get this url from response in real world. + getBase64(info.file.originFileObj, (base64Url) => { + courseCoverurl.value = base64Url; + loading.value = false; + }); + } + if (info.file.status === "error") { + loading.value = false; + ElMessage.error("upload error"); + } + }; + + // 上传前检查 + const beforeUpload = (file) => { + const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png"; + if (!isJpgOrPng) { + ElMessage.error("You can only upload JPG file!"); + } + const isLt2M = file.size / 1024 / 1024 < 2; + if (!isLt2M) { + ElMessage.error("Image must smaller than 2MB!"); + } + return isJpgOrPng && isLt2M; + }; + + return { + // 数据 + fileList, + loading, + courseCoverurl, + + // 方法 + getBase64, + handleChange, + beforeUpload + }; +} \ No newline at end of file diff --git a/src/main.js b/src/main.js index 0ebeba8f..00db50f7 100644 --- a/src/main.js +++ b/src/main.js @@ -6,28 +6,33 @@ * @FilePath: /fe-manage/src/main.js * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE */ -import { createApp } from 'vue' -import App from './App.vue' -import router from './router' -import store from './store' +import { createApp } from "vue"; +import App from "./App.vue"; +import router from "./router"; +import store from "./store"; +import { message } from "ant-design-vue"; // import ElementPlus from 'element-plus' -import 'element-plus/dist/index.css' +import "element-plus/dist/index.css"; // import zhCn from 'element-plus/es/locale/lang/zh-cn' -import "@/assets/scss/common.scss" -import Antd from 'ant-design-vue'; -import 'ant-design-vue/dist/antd.css'; -import {request} from "@/api/request"; -import {USER_INFO, USER_PERMISSION, VALIDATE_TOKEN} from "@/api/apis"; +import "@/assets/scss/common.scss"; +import Antd from "ant-design-vue"; +import "ant-design-vue/dist/antd.css"; +import { request } from "@/api/request"; +import { USER_INFO, USER_PERMISSION, VALIDATE_TOKEN } from "@/api/apis"; import * as api1 from "@/api/index1"; -import {getCookieForName} from "@/api/method"; -import components from './components' -import axios from 'axios' +import { getCookieForName } from "@/api/method"; +import components from "./components"; +import axios from "axios"; import Cookies from "vue-cookies"; // axios.defaults.withCredentials = true; // import zhCN from 'ant-design-vue/es/locale/zh_CN'; -const app = createApp(App) +const app = createApp(App); //全局注册 -app.use(components) +app.use(components); +message.config({ + top: "250px", +}); + // 清理控制台warn信息 app.config.warnHandler = () => null; // app.use(ElementPlus, { @@ -35,102 +40,112 @@ app.config.warnHandler = () => null; // }) router.beforeEach(async (to, from, next) => { - if (!getCookieForName("token")) { - window.location.href = process.env.VUE_APP_LOGIN_URL + encodeURIComponent(window.location.protocol + process.env.VUE_APP_BOE_API_URL + process.env.VUE_APP_BASE + router.currentRoute.value.fullPath) - return + if (!getCookieForName("token")) { + window.location.href = + process.env.VUE_APP_LOGIN_URL + + encodeURIComponent( + window.location.protocol + + process.env.VUE_APP_BOE_API_URL + + process.env.VUE_APP_BASE + + router.currentRoute.value.fullPath + ); + return; + } + //第一次进入 没有用户信息 + if (!store.state.userInfo.userId) { + try { + await request(VALIDATE_TOKEN); + await getUserInfo(); + await getUserPermission(); + init(); + } catch (e) { + console.log("token失效 跳转到登录页"); } - //第一次进入 没有用户信息 - if(!store.state.userInfo.userId){ - try{ - await request(VALIDATE_TOKEN) - await getUserInfo() - await getUserPermission(); - init() - }catch (e){ - console.log('token失效 跳转到登录页') - } - } - next(); -}) + } + next(); +}); app.use(Antd); app.use(router); app.use(store); -app.mount('#app'); +app.mount("#app"); async function getUserPermission() { - return request(USER_PERMISSION, {permissionType: 'PAGE'}).then(res => { - store.commit("SET_PERMISSION", res.data?.map(s => s.url)); - }) + return request(USER_PERMISSION, { permissionType: "PAGE" }).then((res) => { + store.commit( + "SET_PERMISSION", + res.data?.map((s) => s.url) + ); + }); } async function getUserInfo() { - const userInfo = await request(USER_INFO); - store.commit("SET_USER", userInfo.data); - axios({ - method: "get", - url: "/userbasic/orgHrbp/reportOrgs", - params: { - workNum:userInfo.data.userNo - }, - headers: { - "XBOR-Access-token": Cookies.get("token"), - }, - }).then(res=>{ - store.commit("SET_USER_ORGS", res.data); - }) + const userInfo = await request(USER_INFO); + store.commit("SET_USER", userInfo.data); + axios({ + method: "get", + url: "/userbasic/orgHrbp/reportOrgs", + params: { + workNum: userInfo.data.userNo, + }, + headers: { + "XBOR-Access-token": Cookies.get("token"), + }, + }).then((res) => { + store.commit("SET_USER_ORGS", res.data); + }); } async function initDict(key) { - const list = await getDictList(key); - store.commit("SET_DICT", {key, data: list}); + const list = await getDictList(key); + store.commit("SET_DICT", { key, data: list }); } -const getDictList = (param) => api1.getDictTree({code: param,}).then((res) => res.data.data); +const getDictList = (param) => + api1.getDictTree({ code: param }).then((res) => res.data.data); const initDictTree = (key) => { - axios({ - method: "get", - url: "/systemapi/xboe/type/tree-list", - params: { - sysResType: "1", - status: "1", - }, - headers: { - "XBOR-Access-token": Cookies.get("token"), - }, - }).then( - (res) => { - console.log(res.data.result,'课程分类接口') - store.commit("SET_DICT", {key, data: res.data.result}); - //转化为map放到状态中 - let map=new Map(); - res.data.result.forEach(item=>{ - map.set(item.id, item.name); - if(item.children && item.children!=''){ - item.children.forEach(child=>{ - map.set(child.id, child.name); - if(child.children && child.children!=''){ - child.children.forEach(last=>{ - map.set(last.id, last.name); - }) - } - }) - } + axios({ + method: "get", + url: "/systemapi/xboe/type/tree-list", + params: { + sysResType: "1", + status: "1", + }, + headers: { + "XBOR-Access-token": Cookies.get("token"), + }, + }).then( + (res) => { + console.log(res.data.result, "课程分类接口"); + store.commit("SET_DICT", { key, data: res.data.result }); + //转化为map放到状态中 + let map = new Map(); + res.data.result.forEach((item) => { + map.set(item.id, item.name); + if (item.children && item.children != "") { + item.children.forEach((child) => { + map.set(child.id, child.name); + if (child.children && child.children != "") { + child.children.forEach((last) => { + map.set(last.id, last.name); + }); + } }); - store.commit("SET_SYSTYPEMAP", map); - }, - (err) => { - message.error(err); } - ); -} + }); + store.commit("SET_SYSTYPEMAP", map); + }, + (err) => { + message.error(err); + } + ); +}; async function init() { - - // initDict("content_type"); //内容分类 - initDictTree("content_type"); //内容分类,换成type/tree-list接口 - initDict("project_level"); //项目级别 - initDict("project_sys"); //培训分类 - initDict("project_pic"); //项目封面 - initDict("router_pic"); //路径图封面 - initDict("course_pic"); //课程封面 - initDict("job_type"); //岗位 - initDict("band"); //band - initDict("examine_cover") //讲师认证封面图 - initDict("project_number") //项目编号 -} \ No newline at end of file + // initDict("content_type"); //内容分类 + initDictTree("content_type"); //内容分类,换成type/tree-list接口 + initDict("project_level"); //项目级别 + initDict("project_sys"); //培训分类 + initDict("project_pic"); //项目封面 + initDict("router_pic"); //路径图封面 + initDict("course_pic"); //课程封面 + initDict("job_type"); //岗位 + initDict("band"); //band + initDict("examine_cover"); //讲师认证封面图 + initDict("project_number"); //项目编号 +} diff --git a/src/views/courselibrary/OnlineManage.vue b/src/views/courselibrary/OnlineManage.vue index 3d354710..0f90fa76 100644 --- a/src/views/courselibrary/OnlineManage.vue +++ b/src/views/courselibrary/OnlineManage.vue @@ -7,118 +7,122 @@
培训时间:
-
-
{{ showSecondFilter ? '收起' : '展开' }} ▼
+
+ {{ showSecondFilter ? "收起" : "展开" }} ▼ +
-
+
-
+
-
+
-
+
-
+
@@ -163,22 +167,28 @@
@@ -186,15 +196,15 @@
@@ -211,330 +221,330 @@ export default { // 控制第二排搜索项的显隐 showSecondFilter: false, searchParam: { - name: '', + name: "", category: undefined, - teacher: '', + teacher: "", valueDate: [], reviewStatus: undefined, publishStatus: undefined, enableStatus: undefined, // 新增:启用状态 - isPublic: undefined, // 新增:是否公开课 + isPublic: undefined, // 新增:是否公开课 resourceOwner: undefined, // 新增:资源归属 createSource: undefined, // 新增:创建来源 pageNo: 1, - pageSize: 10 + pageSize: 10, }, tableData: [ { - key: '1', - name: '《少走弯路的职场法则》-2021公开课', - teacher: '张三', + key: "1", + name: "《少走弯路的职场法则》-2021公开课", + teacher: "张三", courseDuration: 10, studyDuration: 10, studentCount: 1, rating: 5.0, - reviewStatus: '-', - publishStatus: '未发布' + reviewStatus: "-", + publishStatus: "未发布", }, { - key: '2', - name: 'PDCA循环工作法', - teacher: '张三', + key: "2", + name: "PDCA循环工作法", + teacher: "张三", courseDuration: 20, studyDuration: 20, studentCount: 2, rating: 4.0, - reviewStatus: '审核中', - publishStatus: '未发布' + reviewStatus: "审核中", + publishStatus: "未发布", }, { - key: '3', - name: 'BOE端到端的流程体系', - teacher: '张三', + key: "3", + name: "BOE端到端的流程体系", + teacher: "张三", courseDuration: 30, studyDuration: 30, studentCount: 3, rating: 3.0, - reviewStatus: '审核驳回', - publishStatus: '未发布' + reviewStatus: "审核驳回", + publishStatus: "未发布", }, { - key: '4', - name: '结构性思维与表达-2023年公开课', - teacher: '张三', + key: "4", + name: "结构性思维与表达-2023年公开课", + teacher: "张三", courseDuration: 40, studyDuration: 40, studentCount: 4, rating: 5.0, - reviewStatus: '审核通过', - publishStatus: '已发布' + reviewStatus: "审核通过", + publishStatus: "已发布", }, { - key: '5', - name: '标准化异常处理流程', - teacher: '张三', + key: "5", + name: "标准化异常处理流程", + teacher: "张三", courseDuration: 10, studyDuration: 10, studentCount: 6, rating: 4.0, - reviewStatus: '审核通过', - publishStatus: '已发布' + reviewStatus: "审核通过", + publishStatus: "已发布", }, { - key: '6', - name: '企业经营法则', - teacher: '张三', + key: "6", + name: "企业经营法则", + teacher: "张三", courseDuration: 20, studyDuration: 20, studentCount: 6, rating: 3.0, - reviewStatus: '审核通过', - publishStatus: '已发布' + reviewStatus: "审核通过", + publishStatus: "已发布", }, { - key: '7', - name: '京东方战略实践学习', - teacher: '张三', + key: "7", + name: "京东方战略实践学习", + teacher: "张三", courseDuration: 30, studyDuration: 30, studentCount: 7, rating: 5.0, - reviewStatus: '审核通过', - publishStatus: '已发布' + reviewStatus: "审核通过", + publishStatus: "已发布", }, { - key: '8', - name: '市场营销精要之《如何在新环境下做好新产品整合营销上市》', - teacher: '张三', + key: "8", + name: "市场营销精要之《如何在新环境下做好新产品整合营销上市》", + teacher: "张三", courseDuration: 40, studyDuration: 40, studentCount: 89, rating: 4.0, - reviewStatus: '审核中', - publishStatus: '已发布' + reviewStatus: "审核中", + publishStatus: "已发布", }, { - key: '9', - name: '2024热点论坛第三期:国际格局的基本结构及其相关问题', - teacher: '张三', + key: "9", + name: "2024热点论坛第三期:国际格局的基本结构及其相关问题", + teacher: "张三", courseDuration: 10, studyDuration: 10, studentCount: 1, rating: 3.0, - reviewStatus: '审核中', - publishStatus: '已发布' + reviewStatus: "审核中", + publishStatus: "已发布", }, { - key: '10', - name: '2024热点论坛2-《新“人机”时代的生存与发展》', - teacher: '张三', + key: "10", + name: "2024热点论坛2-《新“人机”时代的生存与发展》", + teacher: "张三", courseDuration: 20, studyDuration: 20, studentCount: 2, rating: 5.0, - reviewStatus: '审核驳回', - publishStatus: '已发布' - } + reviewStatus: "审核驳回", + publishStatus: "已发布", + }, ], tableDataTotal: 100, tableLoading: false, categoryList: [ - { value: 'all', label: '全部课程分类' }, - { value: 'tech', label: '技术类' }, - { value: 'manage', label: '管理类' }, - { value: 'sale', label: '销售类' } + { value: "all", label: "全部课程分类" }, + { value: "tech", label: "技术类" }, + { value: "manage", label: "管理类" }, + { value: "sale", label: "销售类" }, ], reviewStatusList: [ - { value: 'all', label: '全部审核状态' }, - { value: 'pending', label: '审核中' }, - { value: 'approved', label: '审核通过' }, - { value: 'rejected', label: '审核驳回' } + { value: "all", label: "全部审核状态" }, + { value: "pending", label: "审核中" }, + { value: "approved", label: "审核通过" }, + { value: "rejected", label: "审核驳回" }, ], publishStatusList: [ - { value: 'all', label: '全部发布状态' }, - { value: 'draft', label: '未发布' }, - { value: 'published', label: '已发布' } + { value: "all", label: "全部发布状态" }, + { value: "draft", label: "未发布" }, + { value: "published", label: "已发布" }, ], // 新增的下拉选项列表 enableStatusList: [ - { value: 'all', label: '全部启用状态' }, - { value: 'enabled', label: '已启用' }, - { value: 'disabled', label: '已禁用' } + { value: "all", label: "全部启用状态" }, + { value: "enabled", label: "已启用" }, + { value: "disabled", label: "已禁用" }, ], isPublicList: [ - { value: 'all', label: '是否公开课' }, - { value: 'yes', label: '是' }, - { value: 'no', label: '否' } + { value: "all", label: "是否公开课" }, + { value: "yes", label: "是" }, + { value: "no", label: "否" }, ], resourceOwnerList: [ - { value: 'all', label: '全部资源归属' }, - { value: 'self', label: '自有' }, - { value: 'third', label: '第三方' } + { value: "all", label: "全部资源归属" }, + { value: "self", label: "自有" }, + { value: "third", label: "第三方" }, ], createSourceList: [ - { value: 'all', label: '全部创建来源' }, - { value: 'manual', label: '手动创建' }, - { value: 'import', label: '批量导入' }, - { value: 'api', label: 'API创建' } + { value: "all", label: "全部创建来源" }, + { value: "manual", label: "手动创建" }, + { value: "import", label: "批量导入" }, + { value: "api", label: "API创建" }, ], columns: [ { - title: '课程名称', - dataIndex: 'name', - key: 'name', - className: 'h', + title: "课程名称", + dataIndex: "name", + key: "name", + className: "h", ellipsis: true, width: 200, sorter: true, - fixed: "left" + fixed: "left", }, { - title: '课程分类', - dataIndex: 'teacher', - className: 'h', - key: 'teacher', - align: 'center', + title: "课程分类", + dataIndex: "teacher", + className: "h", + key: "teacher", + align: "center", - sorter: true + sorter: true, }, { - title: '授课教师', - dataIndex: 'teacher', - className: 'h', - key: 'teacher', - align: 'center', + title: "授课教师", + dataIndex: "teacher", + className: "h", + key: "teacher", + align: "center", - sorter: true + sorter: true, }, { - title: '课程时长', - dataIndex: 'courseDuration', - className: 'h', - key: 'courseDuration', - align: 'center', + title: "课程时长", + dataIndex: "courseDuration", + className: "h", + key: "courseDuration", + align: "center", - sorter: true + sorter: true, }, { - title: '学习时长', - dataIndex: 'studyDuration', - className: 'h', - key: 'studyDuration', - align: 'center', + title: "学习时长", + dataIndex: "studyDuration", + className: "h", + key: "studyDuration", + align: "center", - sorter: true + sorter: true, }, { - title: '学习人数', - dataIndex: 'studentCount', - className: 'h', - key: 'studentCount', - align: 'center', + title: "学习人数", + dataIndex: "studentCount", + className: "h", + key: "studentCount", + align: "center", - sorter: true + sorter: true, }, { - title: '课程评分', - dataIndex: 'rating', - className: 'h', - key: 'rating', - align: 'center', + title: "课程评分", + dataIndex: "rating", + className: "h", + key: "rating", + align: "center", - sorter: true + sorter: true, }, { - title: '审核状态', - dataIndex: 'reviewStatus', - className: 'h', - key: 'reviewStatus', - align: 'center', + title: "审核状态", + dataIndex: "reviewStatus", + className: "h", + key: "reviewStatus", + align: "center", - sorter: true + sorter: true, }, { - title: '发布状态', - dataIndex: 'publishStatus', - className: 'h', - key: 'publishStatus', - align: 'center', + title: "发布状态", + dataIndex: "publishStatus", + className: "h", + key: "publishStatus", + align: "center", - sorter: true + sorter: true, }, { - title: '启停用状态', - dataIndex: 'publishStatus', - className: 'h', - key: 'publishStatus', - align: 'center', + title: "启停用状态", + dataIndex: "publishStatus", + className: "h", + key: "publishStatus", + align: "center", - sorter: true + sorter: true, }, { - title: '排序值', - dataIndex: 'publishStatus', - className: 'h', - key: 'publishStatus', - align: 'center', + title: "排序值", + dataIndex: "publishStatus", + className: "h", + key: "publishStatus", + align: "center", - sorter: true + sorter: true, }, { - title: '公开课', - dataIndex: 'publishStatus', - className: 'h', - key: 'publishStatus', - align: 'center', + title: "公开课", + dataIndex: "publishStatus", + className: "h", + key: "publishStatus", + align: "center", - sorter: true + sorter: true, }, { - title: '资源归属', - dataIndex: 'publishStatus', - className: 'h', - key: 'publishStatus', - align: 'center', + title: "资源归属", + dataIndex: "publishStatus", + className: "h", + key: "publishStatus", + align: "center", - sorter: true + sorter: true, }, { - title: '创建人', - dataIndex: 'publishStatus', - className: 'h', - key: 'publishStatus', - align: 'center', + title: "创建人", + dataIndex: "publishStatus", + className: "h", + key: "publishStatus", + align: "center", - sorter: true + sorter: true, }, { - title: '创建来源', - dataIndex: 'publishStatus', - className: 'h', - key: 'publishStatus', - align: 'center', + title: "创建来源", + dataIndex: "publishStatus", + className: "h", + key: "publishStatus", + align: "center", - sorter: true + sorter: true, }, { - title: '创建时间', - dataIndex: 'publishStatus', - className: 'h', - key: 'publishStatus', - align: 'center', + title: "创建时间", + dataIndex: "publishStatus", + className: "h", + key: "publishStatus", + align: "center", - sorter: true + sorter: true, }, { - title: '操作', - dataIndex: 'operation', - key: 'operation', - className: 'h', - align: 'right', - fixed: 'right', + title: "操作", + dataIndex: "operation", + key: "operation", + className: "h", + align: "right", + fixed: "right", width: 200, - scopedSlots: { customRender: 'action' }, - sorter: false + scopedSlots: { customRender: "action" }, + sorter: false, }, - ] + ], }; }, methods: { @@ -546,13 +556,17 @@ export default { searchSubmit() {}, searchReset() {}, showModal1() {}, - handleEdit() {}, + handleEdit() { + this.$router.push({ + path: "/ProfessionalMode", + }); + }, editMaterial() {}, grantPermission() {}, deleteRecord() {}, exportData() {}, - changePagination() {} - } + changePagination() {}, + }, }; diff --git a/src/views/courselibrary/components/createCourse.vue b/src/views/courselibrary/components/createCourse.vue new file mode 100644 index 00000000..0ec5aa05 --- /dev/null +++ b/src/views/courselibrary/components/createCourse.vue @@ -0,0 +1,105 @@ + + + + + diff --git a/src/views/courselibrary/components/dragCollapse.vue b/src/views/courselibrary/components/dragCollapse.vue new file mode 100644 index 00000000..33d8be05 --- /dev/null +++ b/src/views/courselibrary/components/dragCollapse.vue @@ -0,0 +1,127 @@ + + + + + diff --git a/src/views/courselibrary/components/dragTable.vue b/src/views/courselibrary/components/dragTable.vue new file mode 100644 index 00000000..d901d690 --- /dev/null +++ b/src/views/courselibrary/components/dragTable.vue @@ -0,0 +1,508 @@ + + + + + diff --git a/src/views/courselibrary/components/professionalmode.vue b/src/views/courselibrary/components/professionalmode.vue new file mode 100644 index 00000000..5e3bcfdc --- /dev/null +++ b/src/views/courselibrary/components/professionalmode.vue @@ -0,0 +1,410 @@ + + + + +