添加课程模块和章节;
This commit is contained in:
		| @@ -2,7 +2,7 @@ ENV = 'development' | |||||||
| VITE_CLI_PORT = 8088 | VITE_CLI_PORT = 8088 | ||||||
| VITE_SERVER_PORT = 8888 | VITE_SERVER_PORT = 8888 | ||||||
| VITE_BASE_API = /api | VITE_BASE_API = /api | ||||||
| VITE_BASE_PATH = http://192.168.1.208 | VITE_BASE_PATH = http://192.168.1.40 | ||||||
| VITE_EDITOR = vscode | VITE_EDITOR = vscode | ||||||
| // VITE_EDITOR = webstorm 如果使用webstorm开发且要使用dom定位到代码行功能 请先自定添加 webstorm到环境变量 再将VITE_EDITOR值修改为webstorm | // VITE_EDITOR = webstorm 如果使用webstorm开发且要使用dom定位到代码行功能 请先自定添加 webstorm到环境变量 再将VITE_EDITOR值修改为webstorm | ||||||
| // 如果使用docker-compose开发模式,设置为下面的地址或本机主机IP | // 如果使用docker-compose开发模式,设置为下面的地址或本机主机IP | ||||||
|   | |||||||
| @@ -11,11 +11,13 @@ | |||||||
|   }, |   }, | ||||||
|   "dependencies": { |   "dependencies": { | ||||||
|     "@element-plus/icons-vue": "^0.2.7", |     "@element-plus/icons-vue": "^0.2.7", | ||||||
|  |     "ali-oss": "^6.17.1", | ||||||
|     "axios": "^0.19.2", |     "axios": "^0.19.2", | ||||||
|     "core-js": "^3.6.5", |     "core-js": "^3.6.5", | ||||||
|     "echarts": "5.3.2", |     "echarts": "5.3.2", | ||||||
|     "element-plus": "2.2.9", |     "element-plus": "2.2.9", | ||||||
|     "highlight.js": "^10.6.0", |     "highlight.js": "^10.6.0", | ||||||
|  |     "js-cookie": "^3.0.1", | ||||||
|     "marked": "^2.0.0", |     "marked": "^2.0.0", | ||||||
|     "mitt": "^3.0.0", |     "mitt": "^3.0.0", | ||||||
|     "nprogress": "^0.2.0", |     "nprogress": "^0.2.0", | ||||||
|   | |||||||
							
								
								
									
										20
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/App.vue
									
									
									
									
									
								
							| @@ -24,4 +24,24 @@ export default { | |||||||
| .el-button{ | .el-button{ | ||||||
|   font-weight: 400 !important; |   font-weight: 400 !important; | ||||||
| } | } | ||||||
|  | .avatar-uploader .el-upload { | ||||||
|  |   border: 1px dashed var(--el-border-color); | ||||||
|  |   border-radius: 6px; | ||||||
|  |   cursor: pointer; | ||||||
|  |   position: relative; | ||||||
|  |   overflow: hidden; | ||||||
|  |   transition: var(--el-transition-duration-fast); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .avatar-uploader .el-upload:hover { | ||||||
|  |   border-color: var(--el-color-primary); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | .el-icon.avatar-uploader-icon { | ||||||
|  |   font-size: 28px; | ||||||
|  |   color: #8c939d; | ||||||
|  |   width: 178px; | ||||||
|  |   height: 178px; | ||||||
|  |   text-align: center; | ||||||
|  | } | ||||||
| </style> | </style> | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								src/api/common.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/api/common.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | import service from '@/utils/request' | ||||||
|  | const headers = { | ||||||
|  |     "Content-Type": "multipart/form-data;binary", | ||||||
|  | }; | ||||||
|  | const api = { | ||||||
|  |   upload:data=>{//文件上传 | ||||||
|  |         return service({ | ||||||
|  |             data, | ||||||
|  |             url: '/public/uploadFile', | ||||||
|  |             method: 'POST', | ||||||
|  |             headers | ||||||
|  |         }) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | export default api | ||||||
| @@ -8,6 +8,33 @@ const api = { | |||||||
|       params:data |       params:data | ||||||
|     }) |     }) | ||||||
|   }, |   }, | ||||||
|  |   getCourse : data => { // 获取课程详情 | ||||||
|  |     return service({ | ||||||
|  |       url: '/sys-course/'+data.id, | ||||||
|  |       method: 'get' | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|  |   addCourse : data => { // 添加课程 | ||||||
|  |     return service({ | ||||||
|  |       url: '/sys-course', | ||||||
|  |       method: 'post', | ||||||
|  |       data | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|  |   editCourse : data => { // 编辑课程 | ||||||
|  |     return service({ | ||||||
|  |       url: '/sys-course', | ||||||
|  |       method: 'put', | ||||||
|  |       data | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|  |   delCourse : data => { // 删除课程 | ||||||
|  |     return service({ | ||||||
|  |       url: '/sys-course', | ||||||
|  |       method: 'delete', | ||||||
|  |       data | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|   // 课程分类api |   // 课程分类api | ||||||
|   getSubjectList : data => { |   getSubjectList : data => { | ||||||
|     return service({ |     return service({ | ||||||
| @@ -37,5 +64,33 @@ const api = { | |||||||
|       data |       data | ||||||
|     }) |     }) | ||||||
|   }, |   }, | ||||||
|  |   // 章节api | ||||||
|  |   getChapter:data => { | ||||||
|  |     return service({ | ||||||
|  |       url: '/sys-course-ware/'+data.id, | ||||||
|  |       method: 'get' | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|  |   addChapter : data => { | ||||||
|  |     return service({ | ||||||
|  |       url: '/sys-course-ware', | ||||||
|  |       method: 'post', | ||||||
|  |       data | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|  |   editChapter : data => { | ||||||
|  |     return service({ | ||||||
|  |       url: '/sys-course-ware/update', | ||||||
|  |       method: 'put', | ||||||
|  |       data | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
|  |   delChapter : data => { | ||||||
|  |     return service({ | ||||||
|  |       url: '/sys-course-ware', | ||||||
|  |       method: 'delete', | ||||||
|  |       data | ||||||
|  |     }) | ||||||
|  |   }, | ||||||
| } | } | ||||||
| export default api | export default api | ||||||
|   | |||||||
							
								
								
									
										15
									
								
								src/utils/auth.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/utils/auth.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | |||||||
|  | import Cookies from 'js-cookie' | ||||||
|  |  | ||||||
|  | const TokenKey = 'Admin-Token' | ||||||
|  |  | ||||||
|  | export function getToken() { | ||||||
|  |   return Cookies.get(TokenKey) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function setToken(token) { | ||||||
|  |   return Cookies.set(TokenKey, token) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function removeToken() { | ||||||
|  |   return Cookies.remove(TokenKey) | ||||||
|  | } | ||||||
							
								
								
									
										431
									
								
								src/utils/index.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										431
									
								
								src/utils/index.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,431 @@ | |||||||
|  | /** | ||||||
|  |  * Created by PanJiaChen on 16/11/18. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | import OSS from 'ali-oss' | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Parse the time to string | ||||||
|  |  * @param {(Object|string|number)} time | ||||||
|  |  * @param {string} cFormat | ||||||
|  |  * @returns {string | null} | ||||||
|  |  */ | ||||||
|  | export function parseTime(time, cFormat) { | ||||||
|  |   if (arguments.length === 0) { | ||||||
|  |     return null | ||||||
|  |   } | ||||||
|  |   if (time_str.indexOf('01-01-01') > -1) { | ||||||
|  |     return '-' | ||||||
|  |   } | ||||||
|  |   const format = cFormat || '{y}-{m}-{d} {h}:{i}:{s}' | ||||||
|  |   let date | ||||||
|  |   if (typeof time === 'object') { | ||||||
|  |     date = time | ||||||
|  |   } else { | ||||||
|  |     if ((typeof time === 'string') && (/^[0-9]+$/.test(time))) { | ||||||
|  |       time = parseInt(time) | ||||||
|  |     } | ||||||
|  |     if ((typeof time === 'number') && (time.toString().length === 10)) { | ||||||
|  |       time = time * 1000 | ||||||
|  |     } | ||||||
|  |     date = new Date(time) | ||||||
|  |   } | ||||||
|  |   const formatObj = { | ||||||
|  |     y: date.getFullYear(), | ||||||
|  |     m: date.getMonth() + 1, | ||||||
|  |     d: date.getDate(), | ||||||
|  |     h: date.getHours(), | ||||||
|  |     i: date.getMinutes(), | ||||||
|  |     s: date.getSeconds(), | ||||||
|  |     a: date.getDay() | ||||||
|  |   } | ||||||
|  |   const time_str = format.replace(/{([ymdhisa])+}/g, (result, key) => { | ||||||
|  |     const value = formatObj[key] | ||||||
|  |     // Note: getDay() returns 0 on Sunday | ||||||
|  |     if (key === 'a') { return ['日', '一', '二', '三', '四', '五', '六'][value] } | ||||||
|  |     return value.toString().padStart(2, '0') | ||||||
|  |   }) | ||||||
|  |  | ||||||
|  |   return time_str | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {number} time | ||||||
|  |  * @param {string} option | ||||||
|  |  * @returns {string} | ||||||
|  |  */ | ||||||
|  | export function formatTime(time, option) { | ||||||
|  |   if (('' + time).length === 10) { | ||||||
|  |     time = parseInt(time) * 1000 | ||||||
|  |   } else { | ||||||
|  |     time = +time | ||||||
|  |   } | ||||||
|  |   const d = new Date(time) | ||||||
|  |   const now = Date.now() | ||||||
|  |  | ||||||
|  |   const diff = (now - d) / 1000 | ||||||
|  |  | ||||||
|  |   if (diff < 30) { | ||||||
|  |     return '刚刚' | ||||||
|  |   } else if (diff < 3600) { | ||||||
|  |     // less 1 hour | ||||||
|  |     return Math.ceil(diff / 60) + '分钟前' | ||||||
|  |   } else if (diff < 3600 * 24) { | ||||||
|  |     return Math.ceil(diff / 3600) + '小时前' | ||||||
|  |   } else if (diff < 3600 * 24 * 2) { | ||||||
|  |     return '1天前' | ||||||
|  |   } | ||||||
|  |   if (option) { | ||||||
|  |     return parseTime(time, option) | ||||||
|  |   } else { | ||||||
|  |     return ( | ||||||
|  |       d.getMonth() + | ||||||
|  |       1 + | ||||||
|  |       '月' + | ||||||
|  |       d.getDate() + | ||||||
|  |       '日' + | ||||||
|  |       d.getHours() + | ||||||
|  |       '时' + | ||||||
|  |       d.getMinutes() + | ||||||
|  |       '分' | ||||||
|  |     ) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {string} url | ||||||
|  |  * @returns {Object} | ||||||
|  |  */ | ||||||
|  | export function getQueryObject(url) { | ||||||
|  |   url = url == null ? window.location.href : url | ||||||
|  |   const search = url.substring(url.lastIndexOf('?') + 1) | ||||||
|  |   const obj = {} | ||||||
|  |   const reg = /([^?&=]+)=([^?&=]*)/g | ||||||
|  |   search.replace(reg, (rs, $1, $2) => { | ||||||
|  |     const name = decodeURIComponent($1) | ||||||
|  |     let val = decodeURIComponent($2) | ||||||
|  |     val = String(val) | ||||||
|  |     obj[name] = val | ||||||
|  |     return rs | ||||||
|  |   }) | ||||||
|  |   return obj | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {string} input value | ||||||
|  |  * @returns {number} output value | ||||||
|  |  */ | ||||||
|  | export function byteLength(str) { | ||||||
|  |   // returns the byte length of an utf8 string | ||||||
|  |   let s = str.length | ||||||
|  |   for (var i = str.length - 1; i >= 0; i--) { | ||||||
|  |     const code = str.charCodeAt(i) | ||||||
|  |     if (code > 0x7f && code <= 0x7ff) s++ | ||||||
|  |     else if (code > 0x7ff && code <= 0xffff) s += 2 | ||||||
|  |     if (code >= 0xDC00 && code <= 0xDFFF) i-- | ||||||
|  |   } | ||||||
|  |   return s | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {Array} actual | ||||||
|  |  * @returns {Array} | ||||||
|  |  */ | ||||||
|  | export function cleanArray(actual) { | ||||||
|  |   const newArray = [] | ||||||
|  |   for (let i = 0; i < actual.length; i++) { | ||||||
|  |     if (actual[i]) { | ||||||
|  |       newArray.push(actual[i]) | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   return newArray | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {Object} json | ||||||
|  |  * @returns {Array} | ||||||
|  |  */ | ||||||
|  | export function param(json) { | ||||||
|  |   if (!json) return '' | ||||||
|  |   return cleanArray( | ||||||
|  |     Object.keys(json).map(key => { | ||||||
|  |       if (json[key] === undefined) return '' | ||||||
|  |       return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]) | ||||||
|  |     }) | ||||||
|  |   ).join('&') | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {string} url | ||||||
|  |  * @returns {Object} | ||||||
|  |  */ | ||||||
|  | export function param2Obj(url) { | ||||||
|  |   const search = url.split('?')[1] | ||||||
|  |   if (!search) { | ||||||
|  |     return {} | ||||||
|  |   } | ||||||
|  |   return JSON.parse( | ||||||
|  |     '{"' + | ||||||
|  |     decodeURIComponent(search) | ||||||
|  |       .replace(/"/g, '\\"') | ||||||
|  |       .replace(/&/g, '","') | ||||||
|  |       .replace(/=/g, '":"') | ||||||
|  |       .replace(/\+/g, ' ') + | ||||||
|  |     '"}' | ||||||
|  |   ) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {string} val | ||||||
|  |  * @returns {string} | ||||||
|  |  */ | ||||||
|  | export function html2Text(val) { | ||||||
|  |   const div = document.createElement('div') | ||||||
|  |   div.innerHTML = val | ||||||
|  |   return div.textContent || div.innerText | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Merges two objects, giving the last one precedence | ||||||
|  |  * @param {Object} target | ||||||
|  |  * @param {(Object|Array)} source | ||||||
|  |  * @returns {Object} | ||||||
|  |  */ | ||||||
|  | export function objectMerge(target, source) { | ||||||
|  |   if (typeof target !== 'object') { | ||||||
|  |     target = {} | ||||||
|  |   } | ||||||
|  |   if (Array.isArray(source)) { | ||||||
|  |     return source.slice() | ||||||
|  |   } | ||||||
|  |   Object.keys(source).forEach(property => { | ||||||
|  |     const sourceProperty = source[property] | ||||||
|  |     if (typeof sourceProperty === 'object') { | ||||||
|  |       target[property] = objectMerge(target[property], sourceProperty) | ||||||
|  |     } else { | ||||||
|  |       target[property] = sourceProperty | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  |   return target | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {HTMLElement} element | ||||||
|  |  * @param {string} className | ||||||
|  |  */ | ||||||
|  | export function toggleClass(element, className) { | ||||||
|  |   if (!element || !className) { | ||||||
|  |     return | ||||||
|  |   } | ||||||
|  |   let classString = element.className | ||||||
|  |   const nameIndex = classString.indexOf(className) | ||||||
|  |   if (nameIndex === -1) { | ||||||
|  |     classString += '' + className | ||||||
|  |   } else { | ||||||
|  |     classString = | ||||||
|  |       classString.substr(0, nameIndex) + | ||||||
|  |       classString.substr(nameIndex + className.length) | ||||||
|  |   } | ||||||
|  |   element.className = classString | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {string} type | ||||||
|  |  * @returns {Date} | ||||||
|  |  */ | ||||||
|  | export function getTime(type) { | ||||||
|  |   if (type === 'start') { | ||||||
|  |     return new Date().getTime() - 3600 * 1000 * 24 * 90 | ||||||
|  |   } else { | ||||||
|  |     return new Date(new Date().toDateString()) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {Function} func | ||||||
|  |  * @param {number} wait | ||||||
|  |  * @param {boolean} immediate | ||||||
|  |  * @return {*} | ||||||
|  |  */ | ||||||
|  | export function debounce(func, wait, immediate) { | ||||||
|  |   let timeout, args, context, timestamp, result | ||||||
|  |  | ||||||
|  |   const later = function() { | ||||||
|  |     // 据上一次触发时间间隔 | ||||||
|  |     const last = +new Date() - timestamp | ||||||
|  |  | ||||||
|  |     // 上次被包装函数被调用时间间隔 last 小于设定时间间隔 wait | ||||||
|  |     if (last < wait && last > 0) { | ||||||
|  |       timeout = setTimeout(later, wait - last) | ||||||
|  |     } else { | ||||||
|  |       timeout = null | ||||||
|  |       // 如果设定为immediate===true,因为开始边界已经调用过了此处无需调用 | ||||||
|  |       if (!immediate) { | ||||||
|  |         result = func.apply(context, args) | ||||||
|  |         if (!timeout) context = args = null | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return function(...args) { | ||||||
|  |     context = this | ||||||
|  |     timestamp = +new Date() | ||||||
|  |     const callNow = immediate && !timeout | ||||||
|  |     // 如果延时不存在,重新设定延时 | ||||||
|  |     if (!timeout) timeout = setTimeout(later, wait) | ||||||
|  |     if (callNow) { | ||||||
|  |       result = func.apply(context, args) | ||||||
|  |       context = args = null | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |     return result | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * This is just a simple version of deep copy | ||||||
|  |  * Has a lot of edge cases bug | ||||||
|  |  * If you want to use a perfect deep copy, use lodash's _.cloneDeep | ||||||
|  |  * @param {Object} source | ||||||
|  |  * @returns {Object} | ||||||
|  |  */ | ||||||
|  | export function deepClone(source) { | ||||||
|  |   if (!source && typeof source !== 'object') { | ||||||
|  |     throw new Error('error arguments', 'deepClone') | ||||||
|  |   } | ||||||
|  |   const targetObj = source.constructor === Array ? [] : {} | ||||||
|  |   Object.keys(source).forEach(keys => { | ||||||
|  |     if (source[keys] && typeof source[keys] === 'object') { | ||||||
|  |       targetObj[keys] = deepClone(source[keys]) | ||||||
|  |     } else { | ||||||
|  |       targetObj[keys] = source[keys] | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  |   return targetObj | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {Array} arr | ||||||
|  |  * @returns {Array} | ||||||
|  |  */ | ||||||
|  | export function uniqueArr(arr) { | ||||||
|  |   return Array.from(new Set(arr)) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @returns {string} | ||||||
|  |  */ | ||||||
|  | export function createUniqueString() { | ||||||
|  |   const timestamp = +new Date() + '' | ||||||
|  |   const randomNum = parseInt((1 + Math.random()) * 65536) + '' | ||||||
|  |   return (+(randomNum + timestamp)).toString(32) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Check if an element has a class | ||||||
|  |  * @param {HTMLElement} elm | ||||||
|  |  * @param {string} cls | ||||||
|  |  * @returns {boolean} | ||||||
|  |  */ | ||||||
|  | export function hasClass(ele, cls) { | ||||||
|  |   return !!ele.className.match(new RegExp('(\\s|^)' + cls + '(\\s|$)')) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Add class to element | ||||||
|  |  * @param {HTMLElement} elm | ||||||
|  |  * @param {string} cls | ||||||
|  |  */ | ||||||
|  | export function addClass(ele, cls) { | ||||||
|  |   if (!hasClass(ele, cls)) ele.className += ' ' + cls | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * Remove class from element | ||||||
|  |  * @param {HTMLElement} elm | ||||||
|  |  * @param {string} cls | ||||||
|  |  */ | ||||||
|  | export function removeClass(ele, cls) { | ||||||
|  |   if (hasClass(ele, cls)) { | ||||||
|  |     const reg = new RegExp('(\\s|^)' + cls + '(\\s|$)') | ||||||
|  |     ele.className = ele.className.replace(reg, ' ') | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | /** | ||||||
|  |  * @param {Array} filterVal | ||||||
|  |  * @param {Object} jsonData | ||||||
|  |  * @returns {string} | ||||||
|  |  */ | ||||||
|  | export function formatJson(filterVal, jsonData) { | ||||||
|  |   return jsonData.map(v => filterVal.map(j => { | ||||||
|  |     if (j === 'timestamp') { | ||||||
|  |       return parseTime(v[j]) | ||||||
|  |     } else { | ||||||
|  |       return v[j] | ||||||
|  |     } | ||||||
|  |   })) | ||||||
|  | } | ||||||
|  |  | ||||||
|  | // 格式化价格 | ||||||
|  | export function prices(n, type = 'except', per = 100) { | ||||||
|  |   if (!n) return '0.00' | ||||||
|  |   return type === 'multiply' ? (n * per) : `${n / per}`.includes('.') ? (n / per) : `${n / per}.00` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function beforeUpload(file) { | ||||||
|  |   const isLt05M = file.size / 1024 / 1024 < 0.5 | ||||||
|  |   const isJPG = file.type.indexOf('image/') === -1 | ||||||
|  |   if (isJPG) { | ||||||
|  |     this.$message.error('文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件') | ||||||
|  |   } | ||||||
|  |   if (!isLt05M) { | ||||||
|  |     this.$message.error('上传头像图片大小不能超过 500k!') | ||||||
|  |   } | ||||||
|  |   return !isJPG && isLt05M | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function formateCardNumber(str) { | ||||||
|  |   if (!str) return '-' | ||||||
|  |   if (str.length < 8) { | ||||||
|  |     return str | ||||||
|  |   } | ||||||
|  |   const leftString = str.substring(0, 4) | ||||||
|  |   const rightString = str.substring(str.length - 4) | ||||||
|  |   return `${leftString}***${rightString}` | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export const formatNumber = (str, decimals = 2) => `${parseFloat(str.toFixed(decimals)).toLocaleString()}` | ||||||
|  |  | ||||||
|  | // 拍平数组 | ||||||
|  | export function flatten(arr) { | ||||||
|  |   if (!arr) return [] | ||||||
|  |   let arr1 = [] | ||||||
|  |   arr.forEach((val) => { | ||||||
|  |     arr1.push(val) | ||||||
|  |     if (val?.children) { | ||||||
|  |       arr1 = arr1.concat(flatten(val.children)) | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  |   return arr1 | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function clients() { | ||||||
|  |   let client = {} | ||||||
|  |   client = new OSS({ | ||||||
|  |     endpoint: 'oss-cn-chengdu.aliyuncs.com', | ||||||
|  |     accessKeyId: 'LTAI5t7K52jxxBCvYtS3CCm3', | ||||||
|  |     accessKeySecret: 'VFLO6JDorP8fFmjZuxsSCxL5zzHMgW', | ||||||
|  |     bucket: 'gwjxb', | ||||||
|  |     timeout: 10 * 60 * 1000 | ||||||
|  |   }) | ||||||
|  |   return client | ||||||
|  | } | ||||||
|  |  | ||||||
|  | export function getNowDate() { | ||||||
|  |   const date = new Date() | ||||||
|  |   const year = date.getFullYear() // 年 | ||||||
|  |   const month = date.getMonth() + 1 // 月 | ||||||
|  |   const day = date.getDate() | ||||||
|  |   return `${year}/${month}/${day}` | ||||||
|  | } | ||||||
							
								
								
									
										241
									
								
								src/view/course/addCourse/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										241
									
								
								src/view/course/addCourse/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,241 @@ | |||||||
|  | <script setup> | ||||||
|  | // 引入依赖 | ||||||
|  | import api from '@/api/course' | ||||||
|  | import com_api from '@/api/common' | ||||||
|  | import custom from '@/utils/custom' | ||||||
|  | // import WarningBar from '@/components/warningBar/warningBar.vue' | ||||||
|  | import {ref,onMounted,provide  } from 'vue' | ||||||
|  | import { ElMessage, ElMessageBox } from 'element-plus' | ||||||
|  | import { useRouter, useRoute } from 'vue-router' | ||||||
|  | const router = useRouter() | ||||||
|  | const route = useRoute() | ||||||
|  | import { useUserStore } from '@/pinia/modules/user' | ||||||
|  | const userStore = useUserStore() | ||||||
|  | import chapterCom from '../components/chapter.vue' | ||||||
|  | // 变量 | ||||||
|  | const path = ref(import.meta.env.VITE_BASE_API) | ||||||
|  | const course_id = ref(0) | ||||||
|  | const active = ref(0) | ||||||
|  | const form = ref({status:1}) | ||||||
|  | const subjectParams = ref({ | ||||||
|  |     pageIndex:1, | ||||||
|  |     pageSize:100, | ||||||
|  | }) | ||||||
|  | const subjectList = ref([]) | ||||||
|  | const rules = ref({ | ||||||
|  |   name: [{ required: true, message: '请输入课程名称', trigger: 'blur' }], | ||||||
|  |   subject: [{ required: true, message: '请选择课程科目', trigger: 'change' }], | ||||||
|  |   price: [{ required: true, message: '请输入课程价格', trigger: 'blur' }] | ||||||
|  | }) | ||||||
|  | const ruleFormRef = ref(null) | ||||||
|  | const chapter_info = ref([]) | ||||||
|  | const current_subject = ref('') | ||||||
|  | // 生命周期 | ||||||
|  | onMounted(() => { | ||||||
|  |   course_id.value = route.params.course_id || 0 | ||||||
|  |   if(course_id.value) { | ||||||
|  |     getCourseInfo() | ||||||
|  |   } | ||||||
|  |   getSubject() | ||||||
|  | }) | ||||||
|  | provide('subjectList', subjectList) | ||||||
|  | provide('current_subject', current_subject) | ||||||
|  | async function getCourseInfo() { // 获取课程信息 | ||||||
|  |   const res = await api.getCourse({id:course_id.value}) | ||||||
|  |   if(res.code === 0) { | ||||||
|  |     chapter_info.value = res.data.course_ware_json | ||||||
|  |     form.value = res.data | ||||||
|  |     console.log(form.value) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | // 方法 | ||||||
|  | function stepChangeFunc(type) { | ||||||
|  |   switch (type) { | ||||||
|  |     case 1: // 上一步 | ||||||
|  |       active.value -=1 | ||||||
|  |       break; | ||||||
|  |     case 2: // 下一步 | ||||||
|  |         if(active.value == 0) { // 保存第一步的信息 | ||||||
|  |           submitForm(ruleFormRef.value) | ||||||
|  |         } | ||||||
|  |         else{ | ||||||
|  |           active.value +=1 | ||||||
|  |         } | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | const submitForm = async (formEl) => { | ||||||
|  |   if (!formEl) return | ||||||
|  |   await formEl.validate((valid, fields) => { | ||||||
|  |     if (valid) { | ||||||
|  |       console.log('submit!') | ||||||
|  |       saveStep1() | ||||||
|  |     } else { | ||||||
|  |       // console.log('error submit!', fields) | ||||||
|  |     } | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | async function saveStep1() { | ||||||
|  |   let params = { | ||||||
|  |     step1:{ | ||||||
|  |       ...form.value | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   params.step1.price = parseInt(params.step1.price) | ||||||
|  |   let func_name ='' | ||||||
|  |   if(course_id.value) { // 编辑 | ||||||
|  |     params.course_id = parseInt(course_id.value) | ||||||
|  |     func_name = 'editCourse' | ||||||
|  |   } | ||||||
|  |   else{ // 新增 | ||||||
|  |     func_name = 'addCourse' | ||||||
|  |   } | ||||||
|  |   const res = await api[func_name](params) | ||||||
|  |   if(res.code === 0) { | ||||||
|  |     active.value += 1 | ||||||
|  |     course_id.value = res.data | ||||||
|  |     current_subject.value = form.value.subject | ||||||
|  |     ElMessage({ | ||||||
|  |       type: 'success', | ||||||
|  |       message: '操作成功!' | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | async function getSubject(){ // 获取课程分类 | ||||||
|  |   const res = await api.getSubjectList(subjectParams.value) | ||||||
|  |   if(res.code === 0) { | ||||||
|  |     subjectList.value = custom.getStdSubject(res.data.records) | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | async function uploadAction(file){//图片上传 非oss | ||||||
|  | 		    file.status = 'uploading'; | ||||||
|  |         file.message = '上传中...'; | ||||||
|  |         //开始上传 | ||||||
|  |         const content = new FormData(); | ||||||
|  |         //上传图片需要转换二进制这里要用到FormData | ||||||
|  |         content.append("file", file.file); | ||||||
|  |         content.append("type", '1'); | ||||||
|  |         const res=await com_api.upload(content); | ||||||
|  |         if(res.code==200){ | ||||||
|  |           file.status = 'done'; | ||||||
|  |           file.message = '上传成功'; | ||||||
|  |           file.url = res.data.full_path; | ||||||
|  |           this.courseForm.cover = file.url | ||||||
|  |         } | ||||||
|  |         else{ | ||||||
|  |           file.status = 'failed'; | ||||||
|  |           file.message = '上传失败'; | ||||||
|  |         } | ||||||
|  |         //重新赋值  img/视频 | ||||||
|  |  | ||||||
|  |         // var val_arr = [] | ||||||
|  |         // for(let item of topicInfo.value[topic_index.value].answer_img_arr){ | ||||||
|  |         //   if(item.uid==file.file.uid){ | ||||||
|  |         //     item.url=file.url | ||||||
|  |         //   } | ||||||
|  |         //   val_arr.push(item.url) | ||||||
|  |         // } | ||||||
|  |         // topicInfo.value[topic_index.value].answer_img = val_arr.join(",") | ||||||
|  |         // const exercises_info = { | ||||||
|  |         //   ...topicInfo.value[topic_index.value], | ||||||
|  |         //   answer_img: topicInfo.value[topic_index.value].answer_img | ||||||
|  |         // } | ||||||
|  |         // card_submit_data(exercises_info) | ||||||
|  |     } | ||||||
|  | function handleAvatarSuccess(res) { | ||||||
|  |   form.value.cover = res.data.file.url | ||||||
|  | } | ||||||
|  | function beforeAvatarUpload(file) { | ||||||
|  |       const isLt05M = file.size / 1024 / 1024 < 5 | ||||||
|  |       const isJPG = file.type.indexOf('image/') === -1 | ||||||
|  |       if (isJPG) { | ||||||
|  |         ElMessage.error('文件格式错误,请上传图片类型,如:JPG,PNG后缀的文件') | ||||||
|  |       } | ||||||
|  |       if (!isLt05M) { | ||||||
|  |         ElMessage.error('上传头像图片大小不能超过 2M!') | ||||||
|  |       } | ||||||
|  |       return !isJPG && isLt05M | ||||||
|  |     } | ||||||
|  | function onSubmit() { | ||||||
|  |   router.push({name:'courseManage'}) | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <!--    步骤条--> | ||||||
|  |     <div class="gva-search-box"> | ||||||
|  |         <el-steps :active="active" finish-status="success" simple style="margin-bottom: 20px"> | ||||||
|  |           <el-step title="课程信息" /> | ||||||
|  |           <el-step title="添加章节" /> | ||||||
|  |         </el-steps> | ||||||
|  |     </div> | ||||||
|  |     <!--    表单区域--> | ||||||
|  |     <div class="gva-table-box"> | ||||||
|  |       <!--      step1--> | ||||||
|  |       <el-form ref="ruleFormRef" v-if="active == 0" :model="form" :rules="rules" label-width="120px" style="width: 50%"> | ||||||
|  |         <el-form-item label="课程名称" prop="name"> | ||||||
|  |           <el-input placeholder="请输入课程名称" v-model="form.name" /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="课程分类" prop="subject"> | ||||||
|  |           <el-select v-model="form.subject" placeholder="请选择"> | ||||||
|  |             <el-option | ||||||
|  |               v-for="item in subjectList" | ||||||
|  |               :key="item.id" | ||||||
|  |               :label="item.name" | ||||||
|  |               :value="item.name" | ||||||
|  |             /> | ||||||
|  |           </el-select> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="封面"> | ||||||
|  |           <el-upload | ||||||
|  |             class="avatar-uploader" | ||||||
|  |             :action="`${path}/fileUploadAndDownload/upload`" | ||||||
|  |             :headers="{ 'x-token': userStore.token }" | ||||||
|  |             :show-file-list="false" | ||||||
|  |             :on-success="handleAvatarSuccess" | ||||||
|  |             :before-upload="beforeAvatarUpload" | ||||||
|  |           > | ||||||
|  |             <img v-if="form.cover" :src="form.cover" class="avatar" /> | ||||||
|  |             <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon> | ||||||
|  |           </el-upload> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="价格" prop="price"> | ||||||
|  |           <el-input placeholder="请输入课程价格" type="number" v-model="form.price" /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="课程简介"> | ||||||
|  |           <el-input v-model="form.introduction" type="textarea" /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="包含试听"> | ||||||
|  |           <el-switch v-model="form.is_audition" active-text="是" inactive-text="否" :active-value="1" :inactive-value="-1"  /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="设置精品"> | ||||||
|  |           <el-switch v-model="form.is_boutique" active-text="是" inactive-text="否" :active-value="1" :inactive-value="-1"  /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="课程状态"> | ||||||
|  |           <el-switch v-model="form.status" active-text="上架" inactive-text="下架" :active-value="1" :inactive-value="-1"  /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item style="display: none"> | ||||||
|  |           <el-button type="primary" @click="submitForm(ruleFormRef)"> | ||||||
|  |             Create | ||||||
|  |           </el-button> | ||||||
|  |         </el-form-item> | ||||||
|  |       </el-form> | ||||||
|  |       <!--      step2--> | ||||||
|  |       <div class="chapter-part" v-if="active == 1"> | ||||||
|  |         <chapter-com  @reload="getCourseInfo" :chapter_info="chapter_info" :course_id="course_id"/> | ||||||
|  |       </div> | ||||||
|  |       <div class="btn-box" style="text-align: right"> | ||||||
|  |         <el-button v-if="active>0" @click="stepChangeFunc(1)">上一步</el-button> | ||||||
|  |         <el-button v-if="active<1" type="primary" @click="stepChangeFunc(2)">下一步</el-button> | ||||||
|  |         <el-button v-else type="primary" @click="onSubmit">完成</el-button> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <style scoped> | ||||||
|  |   .avatar-uploader .avatar { | ||||||
|  |     width: 178px; | ||||||
|  |     height: 178px; | ||||||
|  |     display: block; | ||||||
|  |   } | ||||||
|  | </style> | ||||||
							
								
								
									
										315
									
								
								src/view/course/components/chapter.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										315
									
								
								src/view/course/components/chapter.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,315 @@ | |||||||
|  | <script> | ||||||
|  | export default { | ||||||
|  |   name: 'chapterCom', | ||||||
|  |   methods: { | ||||||
|  |  | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <script setup> | ||||||
|  | // 引入依赖 | ||||||
|  | import api from '@/api/course' | ||||||
|  | // import com_api from '@/api/common' | ||||||
|  | // import custom from '@/utils/custom' | ||||||
|  | // import WarningBar from '@/components/warningBar/warningBar.vue' | ||||||
|  | import {ref,onMounted,watch } from 'vue' | ||||||
|  | import { ElMessage, ElMessageBox } from 'element-plus' | ||||||
|  | import { useRouter, useRoute } from 'vue-router' | ||||||
|  | const router = useRouter() | ||||||
|  | const route = useRoute() | ||||||
|  | import { useUserStore } from '@/pinia/modules/user' | ||||||
|  | const userStore = useUserStore() | ||||||
|  | import exercisesCom from '../components/mediaPool.vue' | ||||||
|  | const props = defineProps(['course_id','chapter_info']) | ||||||
|  | const emit = defineEmits(['reload']) | ||||||
|  | import UploadCommon from '@/components/upload/common.vue' | ||||||
|  | // 变量 | ||||||
|  | const dialogChapterVisible = ref(false) | ||||||
|  | // const dialogChapterTitle = ref('') | ||||||
|  | const chapterForm = ref({pid:0}) | ||||||
|  | const chapterRules = ref({ | ||||||
|  |   name: [{ required: true, message: '请输入章节名称', trigger: 'blur' }] | ||||||
|  | }) | ||||||
|  | const chapter_data = ref([]) | ||||||
|  | const dialogChapterChildVisible = ref(false) | ||||||
|  | const chapterChildForm = ref({}) | ||||||
|  | const chapterChildRules = ref({ | ||||||
|  |   name: [{ required: true, message: '请输入章节名称', trigger: 'blur' }] | ||||||
|  | }) | ||||||
|  | const dialogChapterTitle = ref('') | ||||||
|  | const dialogChapterChildTitle = ref('') | ||||||
|  | // const path = ref(import.meta.env.VITE_BASE_API) | ||||||
|  | const imageCommon = ref('') | ||||||
|  | // const current_chapter = ref({}) // 当前操作的大章节 | ||||||
|  | // const current_chapter_child = ref({}) // 当前操作的子章节 | ||||||
|  | // const chapter_index = ref(0) | ||||||
|  | // 生命周期 | ||||||
|  | onMounted(() => { | ||||||
|  |   chapter_data.value = props.chapter_info | ||||||
|  | }) | ||||||
|  | watch(props,(old, newProps) => { | ||||||
|  |   chapter_data.value = props.chapter_info | ||||||
|  | }) | ||||||
|  | // 方法 | ||||||
|  | function addChapterFunc() { // 添加大章节 | ||||||
|  |   chapterForm.value = {pid:0} | ||||||
|  |   dialogChapterVisible.value = true | ||||||
|  |   dialogChapterTitle.value = '添加大章节' | ||||||
|  | } | ||||||
|  | async function deleteChapterFunc(item) { //删除大章节 | ||||||
|  |   delFunc(item.id) | ||||||
|  | } | ||||||
|  | function delFunc(id) { | ||||||
|  |   ElMessageBox.confirm('此操作将永久删除该数据, 是否继续?', '提示', { | ||||||
|  |     confirmButtonText: '确定', | ||||||
|  |     cancelButtonText: '取消', | ||||||
|  |     type: 'warning' | ||||||
|  |   }) | ||||||
|  |   .then(async() => { | ||||||
|  |     const res = await api.delChapter({ | ||||||
|  |       ids:[id] | ||||||
|  |     }) | ||||||
|  |     if (res.code === 0) { | ||||||
|  |       ElMessage({ | ||||||
|  |         type: 'success', | ||||||
|  |         message: '删除成功!' | ||||||
|  |       }) | ||||||
|  |       emit('reload') | ||||||
|  |     } | ||||||
|  |   },() => { | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | function editChapterFunc(item) { //编辑大章节 | ||||||
|  |   chapterForm.value = item | ||||||
|  |   dialogChapterVisible.value = true | ||||||
|  |   dialogChapterTitle.value = '编辑大章节' | ||||||
|  | } | ||||||
|  | function closeChapterDialog() { | ||||||
|  |   dialogChapterVisible.value = false | ||||||
|  | } | ||||||
|  | function closeChapterChildDialog() { | ||||||
|  |   dialogChapterChildVisible.value = false | ||||||
|  | } | ||||||
|  | async function enterChapterDialog() { // 提交大章节 | ||||||
|  |   // console.log(chapterForm.value) | ||||||
|  |   // return | ||||||
|  |   chapterForm.value.sort = parseInt(chapterForm.value.sort) | ||||||
|  |   const params = { | ||||||
|  |     ...chapterForm.value | ||||||
|  |   } | ||||||
|  |   let func_name = '' | ||||||
|  |   if(chapterForm.value.id) { // 编辑 | ||||||
|  |     func_name = 'editChapter' | ||||||
|  |   } | ||||||
|  |   else{ // 新增 | ||||||
|  |     func_name = 'addChapter' | ||||||
|  |     params.course_id = props.course_id | ||||||
|  |   } | ||||||
|  |   const res = await api[func_name](params) | ||||||
|  |   if(res.code === 0) { | ||||||
|  |     ElMessage({ | ||||||
|  |       type: 'success', | ||||||
|  |       message: '操作成功!' | ||||||
|  |     }) | ||||||
|  |     closeChapterDialog() | ||||||
|  |     emit('reload') | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | function addChapterchildFunc(item) { // 打开新增子章节窗口 | ||||||
|  |   dialogChapterChildTitle.value = '新增子章节' | ||||||
|  |   // current_chapter.value = item | ||||||
|  |   chapterChildForm.value = {pid:item.id} | ||||||
|  |   dialogChapterChildVisible.value = true | ||||||
|  | } | ||||||
|  | function editChapterChildFunc(item,main_index) { // 打开编辑子章节窗口 | ||||||
|  |   dialogChapterChildTitle.value = '编辑子章节' | ||||||
|  |   // current_chapter.value = chapter_data.value[main_index] | ||||||
|  |   chapterChildForm.value = item | ||||||
|  |   dialogChapterChildVisible.value = true | ||||||
|  | } | ||||||
|  | function deleteChapterChildFunc(item) { // 删除子章节 | ||||||
|  |   delFunc(item.id) | ||||||
|  | } | ||||||
|  | async function enterChapterChildDialog() { // 提交小章节 | ||||||
|  |   chapterChildForm.value.sort = parseInt(chapterChildForm.value.sort) | ||||||
|  |   const params = { | ||||||
|  |     ...chapterChildForm.value | ||||||
|  |   } | ||||||
|  |   let func_name = '' | ||||||
|  |   // console.log(params) | ||||||
|  |   // return | ||||||
|  |   if(chapterChildForm.value.id){ // 编辑 | ||||||
|  |     func_name = 'editChapter' | ||||||
|  |   } | ||||||
|  |   else{ // 新增 | ||||||
|  |     func_name = 'addChapter' | ||||||
|  |     params.course_id = props.course_id | ||||||
|  |   } | ||||||
|  |   const res = await api[func_name](params) | ||||||
|  |   if(res.code === 0) { | ||||||
|  |     ElMessage({ | ||||||
|  |       type: 'success', | ||||||
|  |       message: '操作成功!' | ||||||
|  |     }) | ||||||
|  |     closeChapterChildDialog() | ||||||
|  |     emit('reload') | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | function getFilePath(file) { // 获取上传文件之后的地址 | ||||||
|  |  | ||||||
|  | } | ||||||
|  | function chooseChapterChildExercises() { // 打开选择习题窗口 | ||||||
|  |  | ||||||
|  | } | ||||||
|  | function openExercisesWinFunc() { // 打开媒体库 | ||||||
|  |  | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <div class="btn-box" style="text-align: right;margin: 10px 0 20px;"> | ||||||
|  |       <el-button type="primary" icon="plus" @click="addChapterFunc">添加章节</el-button> | ||||||
|  |     </div> | ||||||
|  |     <div class="list-box"> | ||||||
|  |       <div class="lb-card" v-for="(item,i) in chapter_data"> | ||||||
|  |         <!--大章节标题--> | ||||||
|  |         <div class="lb-title-part"> | ||||||
|  |           <div class="lbt-title">{{item.name}}</div> | ||||||
|  |           <div class="lbt-btn-box"> | ||||||
|  |             <el-button | ||||||
|  |               icon="edit" | ||||||
|  |               size="small" | ||||||
|  |               type="primary" | ||||||
|  |               link | ||||||
|  |               @click="editChapterFunc(item)" | ||||||
|  |             /> | ||||||
|  |             <el-button | ||||||
|  |               icon="plus" | ||||||
|  |               size="small" | ||||||
|  |               type="primary" | ||||||
|  |               link | ||||||
|  |               @click="addChapterchildFunc(item)" | ||||||
|  |             /> | ||||||
|  |             <el-button | ||||||
|  |               icon="delete" | ||||||
|  |               size="small" | ||||||
|  |               type="primary" | ||||||
|  |               link | ||||||
|  |               @click="deleteChapterFunc(item)" | ||||||
|  |             /> | ||||||
|  |           </div> | ||||||
|  |         </div> | ||||||
|  |         <!--表格数据--> | ||||||
|  |         <div class="lb-table-part" style="margin-bottom: 20px"> | ||||||
|  |           <el-table :data="item.children" > | ||||||
|  |             <el-table-column align="left" label="id" width="60" prop="id" /> | ||||||
|  |             <el-table-column align="left" label="子章节名称" min-width="60" prop="name" /> | ||||||
|  |             <el-table-column align="left" label="课件名称" min-width="60" prop="url_name" /> | ||||||
|  |             <el-table-column align="left" label="章节习题" min-width="60" prop="is_contain_exercise"> | ||||||
|  |               <template #default="scope"> | ||||||
|  |                 {{scope.row.is_contain_exercise === 0?'未添加习题':scope.row.is_contain_exercise}} | ||||||
|  |               </template> | ||||||
|  |             </el-table-column> | ||||||
|  |             <el-table-column align="left" label="是否免费" min-width="60"> | ||||||
|  |               <template #default="scope"> | ||||||
|  |                 {{scope.row.is_free === 1? '是':'否'}} | ||||||
|  |               </template> | ||||||
|  |             </el-table-column> | ||||||
|  |             <el-table-column align="left" label="是否展示" min-width="60"> | ||||||
|  |               <template #default="scope"> | ||||||
|  |                 {{scope.row.is_show === 1? '是':'否'}} | ||||||
|  |               </template> | ||||||
|  |             </el-table-column> | ||||||
|  |             <el-table-column align="left" label="操作" min-width="60"> | ||||||
|  |               <template #default="scope"> | ||||||
|  |                 <el-button | ||||||
|  |                   icon="edit" | ||||||
|  |                   size="small" | ||||||
|  |                   type="primary" | ||||||
|  |                   link | ||||||
|  |                   @click="editChapterChildFunc(scope.row)" | ||||||
|  |                 >编辑</el-button> | ||||||
|  |                 <el-button | ||||||
|  |                   icon="delete" | ||||||
|  |                   size="small" | ||||||
|  |                   type="primary" | ||||||
|  |                   link | ||||||
|  |                   @click="deleteChapterChildFunc(scope.row)" | ||||||
|  |                 >删除</el-button> | ||||||
|  |               </template> | ||||||
|  |             </el-table-column> | ||||||
|  |           </el-table> | ||||||
|  |  | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <!-- 添加/编辑 大章节窗口--> | ||||||
|  |     <el-dialog v-model="dialogChapterVisible" :before-close="closeChapterDialog" :title="dialogChapterTitle"> | ||||||
|  |       <el-form ref="apiForm" :model="chapterForm" :rules="chapterRules" label-width="80px"> | ||||||
|  |         <el-form-item label="章节名称" prop="name"> | ||||||
|  |           <el-input v-model="chapterForm.name" autocomplete="off" /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="章节排序" prop="sort"> | ||||||
|  |           <el-input v-model="chapterForm.sort" placeholder="输入整数,排序越小越靠前展示" autocomplete="off" /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="是否免费" prop="is_free"> | ||||||
|  |           <el-switch v-model="chapterForm.is_free" active-text="是" inactive-text="否" :active-value="1" :inactive-value="-1"  /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="是否展示" prop="is_show"> | ||||||
|  |           <el-switch v-model="chapterForm.is_show" active-text="是" inactive-text="否" :active-value="1" :inactive-value="-1"  /> | ||||||
|  |         </el-form-item> | ||||||
|  |       </el-form> | ||||||
|  |       <template #footer> | ||||||
|  |         <div class="dialog-footer"> | ||||||
|  |           <el-button size="small" @click="closeChapterDialog">取 消</el-button> | ||||||
|  |           <el-button size="small" type="primary" @click="enterChapterDialog">确 定</el-button> | ||||||
|  |         </div> | ||||||
|  |       </template> | ||||||
|  |     </el-dialog> | ||||||
|  |     <!-- 添加小章节窗口--> | ||||||
|  |     <el-dialog v-model="dialogChapterChildVisible" :before-close="closeChapterChildDialog" :title="dialogChapterChildTitle"> | ||||||
|  |       <el-form ref="apiForm" :model="chapterChildForm" :rules="chapterChildRules" label-width="80px"> | ||||||
|  |         <el-form-item label="章节名称" prop="name"> | ||||||
|  |           <el-input v-model="chapterChildForm.name" autocomplete="off" /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="上传课件"> | ||||||
|  |           <!--          <upload-common--> | ||||||
|  |           <!--            v-model:imageCommon="imageCommon"--> | ||||||
|  |           <!--            class="upload-btn"--> | ||||||
|  |           <!--            @on-success="getFilePath"--> | ||||||
|  |           <!--          />--> | ||||||
|  |           <exercises-com @on-success="getFilePath" /> | ||||||
|  |           <!--          <el-button size="small" type="primary" @click="openExercisesWinFunc">点击上传</el-button>--> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="习题管理"> | ||||||
|  |           <el-button size="small" @click="chooseChapterChildExercises">添加习题</el-button> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="章节排序" prop="sort"> | ||||||
|  |           <el-input v-model="chapterChildForm.sort" placeholder="输入整数,排序越小越靠前展示" autocomplete="off" /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="是否免费" prop="is_free"> | ||||||
|  |           <el-switch v-model="chapterChildForm.is_free" active-text="是" inactive-text="否" :active-value="1" :inactive-value="-1"  /> | ||||||
|  |         </el-form-item> | ||||||
|  |         <el-form-item label="是否展示" prop="is_show"> | ||||||
|  |           <el-switch v-model="chapterChildForm.is_show" active-text="是" inactive-text="否" :active-value="1" :inactive-value="-1"  /> | ||||||
|  |         </el-form-item> | ||||||
|  |       </el-form> | ||||||
|  |       <template #footer> | ||||||
|  |         <div class="dialog-footer"> | ||||||
|  |           <el-button size="small" @click="closeChapterChildDialog">取 消</el-button> | ||||||
|  |           <el-button size="small" type="primary" @click="enterChapterChildDialog">确 定</el-button> | ||||||
|  |         </div> | ||||||
|  |       </template> | ||||||
|  |     </el-dialog> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <style scoped> | ||||||
|  |   .lbt-title{ | ||||||
|  |     margin-right: 20px; | ||||||
|  |     font-size: 18px; | ||||||
|  |   } | ||||||
|  |   .lb-title-part{ | ||||||
|  |     display: flex; | ||||||
|  |     align-items: center; | ||||||
|  |   } | ||||||
|  | </style> | ||||||
							
								
								
									
										141
									
								
								src/view/course/components/mediaPool.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/view/course/components/mediaPool.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,141 @@ | |||||||
|  | <script setup> | ||||||
|  | // 引入依赖 | ||||||
|  | import api from '@/api/course' | ||||||
|  | // import com_api from '@/api/common' | ||||||
|  | // import custom from '@/utils/custom' | ||||||
|  | // import WarningBar from '@/components/warningBar/warningBar.vue' | ||||||
|  | import {ref,onMounted,watch,inject  } from 'vue' | ||||||
|  | import { ElMessage, ElMessageBox } from 'element-plus' | ||||||
|  | import { useRouter, useRoute } from 'vue-router' | ||||||
|  | const router = useRouter() | ||||||
|  | const route = useRoute() | ||||||
|  | import { useUserStore } from '@/pinia/modules/user' | ||||||
|  | const userStore = useUserStore() | ||||||
|  | // import chapterCom from '../components/chapter.vue' | ||||||
|  | const props = defineProps(['course_id','chapter_info']) | ||||||
|  | const emit = defineEmits(['on-success']) | ||||||
|  | import { clients, getNowDate } from '@/utils' | ||||||
|  | import { getToken } from '@/utils/auth' | ||||||
|  | // 变量 | ||||||
|  | const headers = ref({ Authorization: 'Bearer ' + getToken() }) | ||||||
|  | const drawer = ref(false) | ||||||
|  | const queryParams =ref({ | ||||||
|  |   pageIndex:1, | ||||||
|  |   pageSize:10, | ||||||
|  |   name:'' | ||||||
|  | }) | ||||||
|  | // 生命周期 | ||||||
|  | const subjectList = inject('subjectList') | ||||||
|  | const current_subject = inject('current_subject') | ||||||
|  | onMounted(() => { | ||||||
|  |   // console.log(headers.value) | ||||||
|  | }) | ||||||
|  |  | ||||||
|  | // 方法 | ||||||
|  | function openExercisesWinFunc() { | ||||||
|  |   drawer.value = true | ||||||
|  | } | ||||||
|  | function onSubmit() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  | function onReset() { | ||||||
|  |   queryParams.value = { | ||||||
|  |     pageIndex:1, | ||||||
|  |     pageSize:10, | ||||||
|  |     name:'' | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | async function httpUpload(file) { // 上传oss | ||||||
|  |   console.log(file) | ||||||
|  |       // this.$set(this.chapterTable.sel, 'percentage', 0) | ||||||
|  |       // this.$set(this.chapterTable.sel, 'loading', true) | ||||||
|  |       const client = clients() | ||||||
|  |       // 判断扩展名 | ||||||
|  |       const tmpcnt = file.file.name.lastIndexOf('.') | ||||||
|  |       const exname = file.file.name.substring(tmpcnt + 1) | ||||||
|  |       const imgUrl = 'http://gwjxb.oss-cn-chengdu.aliyuncs.com' | ||||||
|  |       const fileName = `static/uploadfile/${getNowDate()}/${file.file.uid}.${exname}` | ||||||
|  |       await client.multipartUpload(fileName, file.file, { | ||||||
|  |         progress: (p, cpt, res) => { | ||||||
|  |           // 获取分片上传进度、断点和返回值。 | ||||||
|  |           // this.$set( | ||||||
|  |           //   this.chapterTable.sel, | ||||||
|  |           //   'percentage', | ||||||
|  |           //   (Math.floor(p * 100) / 100) * 100 | ||||||
|  |           // ) | ||||||
|  |           // if (p === 1) { | ||||||
|  |           //   this.$set(this.chapterTable.sel, 'loading', false) | ||||||
|  |           // } | ||||||
|  |         }, | ||||||
|  |         parallel: 5, // 并发上传的分片数量 | ||||||
|  |         partSize: 1024 * 1024 * 20, | ||||||
|  |         // headers, | ||||||
|  |         // 指定meta参数,自定义Object的元信息。通过head接口可以获取到Object的meta数据。 | ||||||
|  |         meta: { | ||||||
|  |           year: 2020, | ||||||
|  |           people: 'test' | ||||||
|  |         }, | ||||||
|  |         mime: file.type // 上传文件类型 | ||||||
|  |       }) | ||||||
|  |       // 填写Object完整路径,例如exampledir/exampleobject.txt。Object完整路径中不能包含Bucket名称。 | ||||||
|  |       const head = await client.head(fileName) | ||||||
|  |       if (head.status === 200) { | ||||||
|  |         // 上传成功之后添加媒体库 | ||||||
|  |         // this.addDepotFun(`${imgUrl}/${fileName}`, file.file.name) | ||||||
|  |         // 数据赋值 | ||||||
|  |         // this.$set(this.chapterTable.sel, 'url', `${imgUrl}/${fileName}`) | ||||||
|  |         // this.$set(this.chapterTable.sel, 'url_name', file.file.name) | ||||||
|  |       } | ||||||
|  |     } | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <el-button size="small" type="primary" @click="openExercisesWinFunc">点击上传</el-button> | ||||||
|  |     <el-drawer v-model="drawer" title="媒体库" size="50%"> | ||||||
|  |       <div class="drawer-section"> | ||||||
|  |         <!--      搜索框--> | ||||||
|  |         <div class="search-box"> | ||||||
|  |           <el-form ref="searchForm" :inline="true" :model="queryParams"> | ||||||
|  |             <el-form-item label="课程名称"> | ||||||
|  |               <el-input v-model="queryParams.name" placeholder="根据课程名称进行查询" /> | ||||||
|  |             </el-form-item> | ||||||
|  |             <el-form-item label="课程分类"> | ||||||
|  |               <el-select v-model="queryParams.subject" clearable placeholder="请选择"> | ||||||
|  |                 <el-option | ||||||
|  |                   v-for="item in subjectList" | ||||||
|  |                   :key="item.id" | ||||||
|  |                   :label="item.name" | ||||||
|  |                   :value="item.id" | ||||||
|  |                 /> | ||||||
|  |               </el-select> | ||||||
|  |             </el-form-item> | ||||||
|  |             <el-form-item> | ||||||
|  |               <el-button size="small" type="primary" icon="search" @click="onSubmit">查询</el-button> | ||||||
|  |               <el-button size="small" icon="refresh" @click="onReset">重置</el-button> | ||||||
|  |             </el-form-item> | ||||||
|  |             <el-form-item> | ||||||
|  |               <el-upload | ||||||
|  |                 class="upload-demo" | ||||||
|  |                 action | ||||||
|  |                 :http-request="httpUpload" | ||||||
|  |                 :show-file-list="false" | ||||||
|  |               > | ||||||
|  |                 <el-button | ||||||
|  |                   size="small" | ||||||
|  |                   type="text" | ||||||
|  |                 >普通上传</el-button> | ||||||
|  |               </el-upload> | ||||||
|  |             </el-form-item> | ||||||
|  |           </el-form> | ||||||
|  |         </div> | ||||||
|  |         <!--      数据列表--> | ||||||
|  |         <div class="list-box"></div> | ||||||
|  |       </div> | ||||||
|  |     </el-drawer> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <style scoped> | ||||||
|  |   .drawer-section{ | ||||||
|  |     padding: 10px; | ||||||
|  |   } | ||||||
|  | </style> | ||||||
| @@ -1,9 +1,9 @@ | |||||||
| <script setup> | <script setup> | ||||||
| // 引入依赖 | // 引入依赖 | ||||||
| import api from '@/api/course' | import api from '@/api/course' | ||||||
| import custom from '@/utils/custom' | // import custom from '@/utils/custom' | ||||||
| import { toSQLLine } from '@/utils/stringFun' | // import { toSQLLine } from '@/utils/stringFun' | ||||||
| import WarningBar from '@/components/warningBar/warningBar.vue' | // import WarningBar from '@/components/warningBar/warningBar.vue' | ||||||
| import {ref,onMounted } from 'vue' | import {ref,onMounted } from 'vue' | ||||||
| import { ElMessage, ElMessageBox } from 'element-plus' | import { ElMessage, ElMessageBox } from 'element-plus' | ||||||
| // 变量 | // 变量 | ||||||
| @@ -12,9 +12,9 @@ const queryParams = ref({ | |||||||
|   pageSize:10, |   pageSize:10, | ||||||
|   keyword:'' |   keyword:'' | ||||||
| }) | }) | ||||||
|  | const total = ref(0) | ||||||
| // const dataList = ref([]) | // const dataList = ref([]) | ||||||
| const subjectList = ref([]) | const subjectList = ref([]) | ||||||
| const deleteVisible = ref(false) |  | ||||||
| const dialogFormVisible = ref(false) | const dialogFormVisible = ref(false) | ||||||
| const tableData =ref([]) | const tableData =ref([]) | ||||||
| const dialogTitle = ref('') | const dialogTitle = ref('') | ||||||
| @@ -43,6 +43,7 @@ async function getSubjectList() { | |||||||
|   subjectList.value = res.data.records |   subjectList.value = res.data.records | ||||||
|   if(res.code == 0) { |   if(res.code == 0) { | ||||||
|     subjectList.value = res.data.records |     subjectList.value = res.data.records | ||||||
|  |     total.value = res.data.total | ||||||
|     subjectList.value = addTreeFormatSubject(subjectList.value) |     subjectList.value = addTreeFormatSubject(subjectList.value) | ||||||
|     console.log(subjectList.value) |     console.log(subjectList.value) | ||||||
|   } |   } | ||||||
| @@ -64,16 +65,16 @@ function onSubmit() { | |||||||
|   getSubjectList() |   getSubjectList() | ||||||
| } | } | ||||||
| // 排序 | // 排序 | ||||||
| const sortChange = ({ prop, order }) => { | // const sortChange = ({ prop, order }) => { | ||||||
|   if (prop) { | //   if (prop) { | ||||||
|     if (prop === 'ID') { | //     if (prop === 'ID') { | ||||||
|       prop = 'id' | //       prop = 'id' | ||||||
|     } | //     } | ||||||
|     queryParams.value.orderKey = toSQLLine(prop) | //     queryParams.value.orderKey = toSQLLine(prop) | ||||||
|     queryParams.value.desc = order === 'descending' | //     queryParams.value.desc = order === 'descending' | ||||||
|   } | //   } | ||||||
|   getSubjectList() | //   getSubjectList() | ||||||
| } | // } | ||||||
| // 批量操作 | // 批量操作 | ||||||
| const handleSelectionChange = (val) => { | const handleSelectionChange = (val) => { | ||||||
|   apis.value = val |   apis.value = val | ||||||
| @@ -188,6 +189,14 @@ const onReset = () => { | |||||||
|     keyword:'' |     keyword:'' | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | function handleCurrentChange(val) { | ||||||
|  |   queryParams.value.pageIndex = val | ||||||
|  |   getSubjectList() | ||||||
|  | } | ||||||
|  | function handleSizeChange(val) { | ||||||
|  |   queryParams.value.pageSize = val | ||||||
|  |   getSubjectList() | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
| <template> | <template> | ||||||
|   <div> |   <div> | ||||||
| @@ -242,8 +251,19 @@ const onReset = () => { | |||||||
|           </template> |           </template> | ||||||
|         </el-table-column> |         </el-table-column> | ||||||
|       </el-table> |       </el-table> | ||||||
|  |       <div class="gva-pagination"> | ||||||
|  |         <el-pagination | ||||||
|  |           :current-page="queryParams.pageIndex" | ||||||
|  |           :page-size="queryParams.pageSize" | ||||||
|  |           :page-sizes="[10, 30, 50, 100]" | ||||||
|  |           :total="total" | ||||||
|  |           layout="total, sizes, prev, pager, next, jumper" | ||||||
|  |           @current-change="handleCurrentChange" | ||||||
|  |           @size-change="handleSizeChange" | ||||||
|  |         /> | ||||||
|       </div> |       </div> | ||||||
|     <!--新增、编辑--> |     </div> | ||||||
|  |     <!--新增、编辑 窗口--> | ||||||
|     <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" :title="dialogTitle"> |     <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" :title="dialogTitle"> | ||||||
|       <!-- <warning-bar title="新增API,需要在角色管理内配置权限才可使用" />--> |       <!-- <warning-bar title="新增API,需要在角色管理内配置权限才可使用" />--> | ||||||
|       <el-form ref="categoryForm" :model="form" :rules="rules" label-width="80px"> |       <el-form ref="categoryForm" :model="form" :rules="rules" label-width="80px"> | ||||||
|   | |||||||
| @@ -1,10 +1,14 @@ | |||||||
| <script setup> | <script setup> | ||||||
| // 引入依赖 | // 引入依赖 | ||||||
| import api from '@/api/course' | import api from '@/api/course' | ||||||
|  | import custom from '@/utils/custom' | ||||||
| // import { toSQLLine } from '@/utils/stringFun' | // import { toSQLLine } from '@/utils/stringFun' | ||||||
| import WarningBar from '@/components/warningBar/warningBar.vue' | import WarningBar from '@/components/warningBar/warningBar.vue' | ||||||
| import {ref,onMounted } from 'vue' | import {ref,onMounted } from 'vue' | ||||||
| import { ElMessage, ElMessageBox } from 'element-plus' | import { ElMessage, ElMessageBox } from 'element-plus' | ||||||
|  | import { useRouter, useRoute } from 'vue-router' | ||||||
|  | const router = useRouter() | ||||||
|  | const route = useRoute() | ||||||
| // 变量 | // 变量 | ||||||
| const queryParams = ref({ | const queryParams = ref({ | ||||||
|   pageIndex:1, |   pageIndex:1, | ||||||
| @@ -17,42 +21,140 @@ const subjectParams = ref({ | |||||||
|     pageIndex:1, |     pageIndex:1, | ||||||
|     pageSize:100, |     pageSize:100, | ||||||
| }) | }) | ||||||
| const dataList = ref([]) | const tableData = ref([]) | ||||||
| const subjectList = ref([]) | const subjectList = ref([]) | ||||||
|  | const deleteVisible = ref(false) | ||||||
|  | const course_ids = ref([]) | ||||||
|  | const total = ref(0) | ||||||
|  | const dialogFormVisible = ref(false) | ||||||
|  | const dialogTitle = ref('') | ||||||
|  | const form =ref({}) | ||||||
|  | const rules = ref({ | ||||||
|  |   name: [{ required: true, message: '请输入课程名称', trigger: 'blur' }] | ||||||
|  | }) | ||||||
|  | const course_id = ref(0) | ||||||
| // 生命周期 | // 生命周期 | ||||||
| getCourseList() | onMounted(() => { | ||||||
| // getSubject() |   getCourseList() | ||||||
|  |   getSubject() | ||||||
|  | }) | ||||||
| // 方法 | // 方法 | ||||||
| async function getCourseList() { | async function getCourseList() { | ||||||
|   const res = await api.getCourseList(queryParams.value) |   const res = await api.getCourseList(queryParams.value) | ||||||
|   if(res.code === 200) { |   if(res.code === 0) { | ||||||
|     dataList.value = res.data.list |     tableData.value = res.data.records | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | async function getSubject(){ // 获取课程分类 | ||||||
|  |   const res = await api.getSubjectList(subjectParams.value) | ||||||
|  |   if(res.code === 0) { | ||||||
|  |     subjectList.value = custom.getStdSubject(res.data.records) | ||||||
|  |     // console.log(subjectList.value) | ||||||
|   } |   } | ||||||
| } | } | ||||||
| function onSubmit() { | function onSubmit() { | ||||||
|   getCourseList() |   getCourseList() | ||||||
| } | } | ||||||
| async function getSubject(){ | const onReset = () => { | ||||||
|   const res = await api.getSubject() |   queryParams.value = { | ||||||
|   if(res.code == 200) { |     pageIndex:1, | ||||||
|     subjectList.value = res.data.list |     pageSize:10, | ||||||
|  |     name:'', | ||||||
|  |     status:'', | ||||||
|  |     subject:'' | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | function openDialog(type) { | ||||||
|  |   let params = {} | ||||||
|  |   switch (type){ | ||||||
|  |     case 'add': | ||||||
|  |       dialogTitle.value = '新增课程' | ||||||
|  |       break; | ||||||
|  |     case 'edit': | ||||||
|  |       params.course_id = course_id.value | ||||||
|  |       dialogTitle.value = '编辑课程' | ||||||
|  |       break; | ||||||
|  |   } | ||||||
|  |   router.push({name:'addCourse',params}) | ||||||
|  | } | ||||||
|  | async function onDelete() { | ||||||
|  |   console.log(course_ids.value) | ||||||
|  |   const ids = course_ids.value.map(item => item.course_id) | ||||||
|  |   const res = await api.delCourse({ ids }) | ||||||
|  |   if (res.code === 0) { | ||||||
|  |     ElMessage({ | ||||||
|  |       type: 'success', | ||||||
|  |       message: res.msg | ||||||
|  |     }) | ||||||
|  |     // if (tableData.value.length === ids.length && page.value > 1) { | ||||||
|  |     //   page.value-- | ||||||
|  |     // } | ||||||
|  |     deleteVisible.value = false | ||||||
|  |     getCourseList() | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | const handleSelectionChange = (val) => { | ||||||
|  |   // console.log(val) | ||||||
|  |   course_ids.value = val | ||||||
|  | } | ||||||
|  | function editCourseFunc(row) { | ||||||
|  |   course_id.value = row.course_id | ||||||
|  |   openDialog('edit') | ||||||
|  | } | ||||||
|  | function deleteCourseFunc(row) { | ||||||
|  |   ElMessageBox.confirm('此操作将永久删除该数据, 是否继续?', '提示', { | ||||||
|  |     confirmButtonText: '确定', | ||||||
|  |     cancelButtonText: '取消', | ||||||
|  |     type: 'warning' | ||||||
|  |   }) | ||||||
|  |   .then(async() => { | ||||||
|  |     const res = await api.delCourse({ | ||||||
|  |           ids:[row.course_id] | ||||||
|  |     }) | ||||||
|  |     if (res.code === 0) { | ||||||
|  |       ElMessage({ | ||||||
|  |         type: 'success', | ||||||
|  |         message: '删除成功!' | ||||||
|  |       }) | ||||||
|  |       // if (tableData.value.length === 1 && page.value > 1) { | ||||||
|  |       //   page.value-- | ||||||
|  |       // } | ||||||
|  |       getCourseList() | ||||||
|  |     } | ||||||
|  |   },() => { | ||||||
|  |   }) | ||||||
|  | } | ||||||
|  | function handleCurrentChange() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  | function handleSizeChange() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  | function closeDialog(){ | ||||||
|  |   dialogFormVisible.value = false | ||||||
|  | } | ||||||
|  | function enterDialog() { | ||||||
|  |  | ||||||
|  | } | ||||||
|  | function viewCourseFunc(row) { // 查看课程 | ||||||
|  |   router.push({name:'viewCourse',params:{course_id:row.course_id}}) | ||||||
|  | } | ||||||
| </script> | </script> | ||||||
| <template> | <template> | ||||||
|   <div> |   <div> | ||||||
|  |     <!--    搜索框--> | ||||||
|     <div class="gva-search-box"> |     <div class="gva-search-box"> | ||||||
|       <el-form ref="searchForm" :inline="true" :model="queryParams"> |       <el-form ref="searchForm" :inline="true" :model="queryParams"> | ||||||
|         <el-form-item label="课程名称"> |         <el-form-item label="课程名称"> | ||||||
|           <el-input v-model="queryParams.name" placeholder="课程名称" /> |           <el-input v-model="queryParams.name" placeholder="根据课程名称进行查询" /> | ||||||
|         </el-form-item> |         </el-form-item> | ||||||
|         <el-form-item label="课程状态"> |         <el-form-item label="课程分类"> | ||||||
|           <el-select v-model="queryParams.method" clearable placeholder="请选择"> |           <el-select v-model="queryParams.subject" clearable placeholder="请选择"> | ||||||
|             <el-option |             <el-option | ||||||
|               v-for="item in subjectList" |               v-for="item in subjectList" | ||||||
|               :key="item.value" |               :key="item.id" | ||||||
|               :label="`${item.label}(${item.value})`" |               :label="item.name" | ||||||
|               :value="item.value" |               :value="item.id" | ||||||
|             /> |             /> | ||||||
|           </el-select> |           </el-select> | ||||||
|         </el-form-item> |         </el-form-item> | ||||||
| @@ -62,9 +164,11 @@ async function getSubject(){ | |||||||
|         </el-form-item> |         </el-form-item> | ||||||
|       </el-form> |       </el-form> | ||||||
|     </div> |     </div> | ||||||
|  |     <!--    表格数据--> | ||||||
|     <div class="gva-table-box"> |     <div class="gva-table-box"> | ||||||
|  |       <!--      批量操作--> | ||||||
|       <div class="gva-btn-list"> |       <div class="gva-btn-list"> | ||||||
|         <el-button size="small" type="primary" icon="plus" @click="openDialog('addApi')">新增</el-button> |         <el-button size="small" type="primary" icon="plus" @click="openDialog('add')">新增</el-button> | ||||||
|         <el-popover v-model="deleteVisible" placement="top" width="160"> |         <el-popover v-model="deleteVisible" placement="top" width="160"> | ||||||
|           <p>确定要删除吗?</p> |           <p>确定要删除吗?</p> | ||||||
|           <div style="text-align: right; margin-top: 8px;"> |           <div style="text-align: right; margin-top: 8px;"> | ||||||
| @@ -72,50 +176,52 @@ async function getSubject(){ | |||||||
|             <el-button size="small" type="primary" @click="onDelete">确定</el-button> |             <el-button size="small" type="primary" @click="onDelete">确定</el-button> | ||||||
|           </div> |           </div> | ||||||
|           <template #reference> |           <template #reference> | ||||||
|             <el-button icon="delete" size="small" :disabled="!apis.length" style="margin-left: 10px;" @click="deleteVisible = true">删除</el-button> |             <el-button icon="delete" size="small" type="danger" :disabled="!course_ids.length" style="margin-left: 10px;" @click="deleteVisible = true">删除</el-button> | ||||||
|           </template> |           </template> | ||||||
|         </el-popover> |         </el-popover> | ||||||
|       </div> |       </div> | ||||||
|       <el-table :data="tableData" @sort-change="sortChange" @selection-change="handleSelectionChange"> |       <!--      数据列表--> | ||||||
|  |       <el-table :data="tableData" @selection-change="handleSelectionChange"> | ||||||
|         <el-table-column |         <el-table-column | ||||||
|           type="selection" |           type="selection" | ||||||
|           width="55" |           width="55" | ||||||
|         /> |         /> | ||||||
|         <el-table-column align="left" label="id" min-width="60" prop="ID" sortable="custom" /> |         <el-table-column align="left" label="id" min-width="60" prop="id" sortable="custom" /> | ||||||
|         <el-table-column align="left" label="API路径" min-width="150" prop="path" sortable="custom" /> |         <el-table-column align="left" label="名称" min-width="150" prop="name" /> | ||||||
|         <el-table-column align="left" label="API分组" min-width="150" prop="apiGroup" sortable="custom" /> |         <el-table-column align="left" label="科目" min-width="150" prop="subject" /> | ||||||
|         <el-table-column align="left" label="API简介" min-width="150" prop="description" sortable="custom" /> |         <el-table-column align="left" label="价格" min-width="150" prop="price" /> | ||||||
|         <el-table-column align="left" label="请求" min-width="150" prop="method" sortable="custom"> |         <el-table-column align="left" label="状态" min-width="150" prop="status" /> | ||||||
|           <template #default="scope"> |         <el-table-column align="left" label="是否精品" min-width="150" prop="is_boutique" /> | ||||||
|             <div> |  | ||||||
|               {{ scope.row.method }} / {{ methodFilter(scope.row.method) }} |  | ||||||
|             </div> |  | ||||||
|           </template> |  | ||||||
|         </el-table-column> |  | ||||||
|  |  | ||||||
|         <el-table-column align="left" fixed="right" label="操作" width="200"> |         <el-table-column align="left" fixed="right" label="操作" width="200"> | ||||||
|           <template #default="scope"> |           <template #default="scope"> | ||||||
|  |             <el-button | ||||||
|  |               icon="view" | ||||||
|  |               size="small" | ||||||
|  |               type="primary" | ||||||
|  |               link | ||||||
|  |               @click="viewCourseFunc(scope.row)" | ||||||
|  |             >查看</el-button> | ||||||
|             <el-button |             <el-button | ||||||
|               icon="edit" |               icon="edit" | ||||||
|               size="small" |               size="small" | ||||||
|               type="primary" |               type="primary" | ||||||
|               link |               link | ||||||
|               @click="editApiFunc(scope.row)" |               @click="editCourseFunc(scope.row)" | ||||||
|             >编辑</el-button> |             >编辑</el-button> | ||||||
|             <el-button |             <el-button | ||||||
|               icon="delete" |               icon="delete" | ||||||
|               size="small" |               size="small" | ||||||
|               type="primary" |               type="danger" | ||||||
|               link |               link | ||||||
|               @click="deleteApiFunc(scope.row)" |               @click="deleteCourseFunc(scope.row)" | ||||||
|             >删除</el-button> |             >删除</el-button> | ||||||
|           </template> |           </template> | ||||||
|         </el-table-column> |         </el-table-column> | ||||||
|       </el-table> |       </el-table> | ||||||
|       <div class="gva-pagination"> |       <div class="gva-pagination"> | ||||||
|         <el-pagination |         <el-pagination | ||||||
|           :current-page="page" |           :current-page="queryParams.pageIndex" | ||||||
|           :page-size="pageSize" |           :page-size="queryParams.pageSize" | ||||||
|           :page-sizes="[10, 30, 50, 100]" |           :page-sizes="[10, 30, 50, 100]" | ||||||
|           :total="total" |           :total="total" | ||||||
|           layout="total, sizes, prev, pager, next, jumper" |           layout="total, sizes, prev, pager, next, jumper" | ||||||
| @@ -127,26 +233,12 @@ async function getSubject(){ | |||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" :title="dialogTitle"> |     <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" :title="dialogTitle"> | ||||||
|       <warning-bar title="新增API,需要在角色管理内配置权限才可使用" /> |  | ||||||
|       <el-form ref="apiForm" :model="form" :rules="rules" label-width="80px"> |       <el-form ref="apiForm" :model="form" :rules="rules" label-width="80px"> | ||||||
|         <el-form-item label="路径" prop="path"> |         <el-form-item label="课程名称" prop="name"> | ||||||
|           <el-input v-model="form.path" autocomplete="off" /> |           <el-input v-model="form.name" autocomplete="off" /> | ||||||
|         </el-form-item> |         </el-form-item> | ||||||
|         <el-form-item label="请求" prop="method"> |         <el-form-item label="课程简介" prop="introduction"> | ||||||
|           <el-select v-model="form.method" placeholder="请选择" style="width:100%"> |           <el-input v-model="form.introduction" autocomplete="off" /> | ||||||
|             <el-option |  | ||||||
|               v-for="item in methodOptions" |  | ||||||
|               :key="item.value" |  | ||||||
|               :label="`${item.label}(${item.value})`" |  | ||||||
|               :value="item.value" |  | ||||||
|             /> |  | ||||||
|           </el-select> |  | ||||||
|         </el-form-item> |  | ||||||
|         <el-form-item label="api分组" prop="apiGroup"> |  | ||||||
|           <el-input v-model="form.apiGroup" autocomplete="off" /> |  | ||||||
|         </el-form-item> |  | ||||||
|         <el-form-item label="api简介" prop="description"> |  | ||||||
|           <el-input v-model="form.description" autocomplete="off" /> |  | ||||||
|         </el-form-item> |         </el-form-item> | ||||||
|       </el-form> |       </el-form> | ||||||
|       <template #footer> |       <template #footer> | ||||||
|   | |||||||
							
								
								
									
										79
									
								
								src/view/course/viewCourse/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/view/course/viewCourse/index.vue
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,79 @@ | |||||||
|  | <script setup> | ||||||
|  | // 引入依赖 | ||||||
|  | import api from '@/api/course' | ||||||
|  | import {ref,onMounted } from 'vue' | ||||||
|  | import { ElMessage, ElMessageBox } from 'element-plus' | ||||||
|  | import { useRouter, useRoute } from 'vue-router' | ||||||
|  | const router = useRouter() | ||||||
|  | const route = useRoute() | ||||||
|  | // 变量 | ||||||
|  | const course_id = ref(0) | ||||||
|  | const courseData = ref({}) | ||||||
|  | // 生命周期 | ||||||
|  | onMounted(() => { | ||||||
|  |   course_id.value = route.params.course_id | ||||||
|  |   getCourse() | ||||||
|  | }) | ||||||
|  | //方法 | ||||||
|  | async function getCourse() { // 获取课程信息 | ||||||
|  |   const res = await api.getCourse({id:course_id.value}) | ||||||
|  |   if(res.code === 0) { | ||||||
|  |     courseData.value = res.data | ||||||
|  |   } | ||||||
|  | } | ||||||
|  | </script> | ||||||
|  | <template> | ||||||
|  |   <div> | ||||||
|  |     <!--    基本信息--> | ||||||
|  |     <div class="gva-search-box"> | ||||||
|  |       <div class="baseinfoBox"> | ||||||
|  |         <div class="base-card course-name"> | ||||||
|  |           <div class="bc-left">课程名称:</div> | ||||||
|  |           <div class="bc-right">{{courseData.step1.name}}</div> | ||||||
|  |         </div> | ||||||
|  |         <div class="base-card"> | ||||||
|  |           <div class="bc-left">课程名称:</div> | ||||||
|  |           <div class="bc-right">{{courseData.step1.name}}</div> | ||||||
|  |         </div> | ||||||
|  |         <div class="base-card"> | ||||||
|  |           <div class="bc-left">课程科目:</div> | ||||||
|  |           <div class="bc-right">{{courseData.step1.subject}}</div> | ||||||
|  |         </div> | ||||||
|  |         <div class="base-card"> | ||||||
|  |           <div class="bc-left">课程状态:</div> | ||||||
|  |           <div class="bc-right">{{courseData.step1.status === 1?'上架':'下架'}}</div> | ||||||
|  |         </div> | ||||||
|  |         <div class="base-card"> | ||||||
|  |           <div class="bc-left">课程价格:</div> | ||||||
|  |           <div class="bc-right">{{courseData.step1.price}}</div> | ||||||
|  |         </div> | ||||||
|  |         <div class="base-card"> | ||||||
|  |           <div class="bc-left">教师姓名:</div> | ||||||
|  |           <div class="bc-right"></div> | ||||||
|  |         </div> | ||||||
|  |         <div class="base-card"> | ||||||
|  |           <div class="bc-left">创建时间:</div> | ||||||
|  |           <div class="bc-right"></div> | ||||||
|  |         </div> | ||||||
|  |         <div class="base-card"> | ||||||
|  |           <div class="bc-left">课程简介:</div> | ||||||
|  |           <div class="bc-right">{{courseData.step1.introduction}}</div> | ||||||
|  |         </div> | ||||||
|  |       </div> | ||||||
|  |     </div> | ||||||
|  |     <!--    章节信息--> | ||||||
|  |     <div class="gva-table-box"> | ||||||
|  |  | ||||||
|  |     </div> | ||||||
|  |   </div> | ||||||
|  | </template> | ||||||
|  | <style scoped> | ||||||
|  | .course-name{ | ||||||
|  |   font-weight: 900; | ||||||
|  |   font-size: 18px; | ||||||
|  |   width: 100%; | ||||||
|  | } | ||||||
|  |   .baseinfoBox{ | ||||||
|  |     overflow: hidden; | ||||||
|  |   } | ||||||
|  | </style> | ||||||
		Reference in New Issue
	
	Block a user