添加课程模块和章节;
This commit is contained in:
		| @@ -2,7 +2,7 @@ ENV = 'development' | ||||
| VITE_CLI_PORT = 8088 | ||||
| VITE_SERVER_PORT = 8888 | ||||
| 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 = webstorm 如果使用webstorm开发且要使用dom定位到代码行功能 请先自定添加 webstorm到环境变量 再将VITE_EDITOR值修改为webstorm | ||||
| // 如果使用docker-compose开发模式,设置为下面的地址或本机主机IP | ||||
|   | ||||
							
								
								
									
										102
									
								
								package.json
									
									
									
									
									
								
							
							
						
						
									
										102
									
								
								package.json
									
									
									
									
									
								
							| @@ -1,52 +1,54 @@ | ||||
| { | ||||
|     "name": "gin-vue-admin", | ||||
|     "version": "2.5.5", | ||||
|     "private": true, | ||||
|     "scripts": { | ||||
|         "serve": "vite --host --mode development", | ||||
|         "build": "vite build --mode production", | ||||
|         "limit-build": "npm install increase-memory-limit-fixbug cross-env -g && npm run fix-memory-limit && node ./limit && npm run build", | ||||
|         "preview": "vite preview", | ||||
|         "fix-memory-limit": "cross-env LIMIT=4096 increase-memory-limit" | ||||
|     }, | ||||
|     "dependencies": { | ||||
|         "@element-plus/icons-vue": "^0.2.7", | ||||
|         "axios": "^0.19.2", | ||||
|         "core-js": "^3.6.5", | ||||
|         "echarts": "5.3.2", | ||||
|         "element-plus": "2.2.9", | ||||
|         "highlight.js": "^10.6.0", | ||||
|         "marked": "^2.0.0", | ||||
|         "mitt": "^3.0.0", | ||||
|         "nprogress": "^0.2.0", | ||||
|         "path": "^0.12.7", | ||||
|         "pinia": "^2.0.9", | ||||
|         "qs": "^6.8.0", | ||||
|         "quill": "^1.3.7", | ||||
|         "screenfull": "^5.0.2", | ||||
|         "spark-md5": "^3.0.1", | ||||
|         "vue": "^3.2.25", | ||||
|         "vue-router": "^4.0.0-0" | ||||
|     }, | ||||
|     "devDependencies": { | ||||
|         "@vitejs/plugin-legacy": "^2.0.0", | ||||
|         "@vitejs/plugin-vue": "^3.0.1", | ||||
|         "@vue/cli-plugin-babel": "~4.5.0", | ||||
|         "@vue/cli-plugin-eslint": "~4.5.0", | ||||
|         "@vue/cli-plugin-router": "~4.5.0", | ||||
|         "@vue/cli-plugin-vuex": "~4.5.0", | ||||
|         "@vue/cli-service": "~4.5.0", | ||||
|         "@vue/compiler-sfc": "^3.1.5", | ||||
|         "babel-eslint": "^10.1.0", | ||||
|         "babel-plugin-import": "^1.13.3", | ||||
|         "chalk": "^4.1.2", | ||||
|         "dotenv": "^10.0.0", | ||||
|         "eslint": "^6.7.2", | ||||
|         "eslint-plugin-vue": "^7.0.0", | ||||
|         "sass": "^1.54.0", | ||||
|         "terser": "^5.4.0", | ||||
|         "vite": "^3.0.1", | ||||
|         "vite-plugin-banner": "^0.1.3", | ||||
|         "vite-plugin-importer": "^0.2.5" | ||||
|     } | ||||
|   "name": "gin-vue-admin", | ||||
|   "version": "2.5.5", | ||||
|   "private": true, | ||||
|   "scripts": { | ||||
|     "serve": "vite --host --mode development", | ||||
|     "build": "vite build --mode production", | ||||
|     "limit-build": "npm install increase-memory-limit-fixbug cross-env -g && npm run fix-memory-limit && node ./limit && npm run build", | ||||
|     "preview": "vite preview", | ||||
|     "fix-memory-limit": "cross-env LIMIT=4096 increase-memory-limit" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@element-plus/icons-vue": "^0.2.7", | ||||
|     "ali-oss": "^6.17.1", | ||||
|     "axios": "^0.19.2", | ||||
|     "core-js": "^3.6.5", | ||||
|     "echarts": "5.3.2", | ||||
|     "element-plus": "2.2.9", | ||||
|     "highlight.js": "^10.6.0", | ||||
|     "js-cookie": "^3.0.1", | ||||
|     "marked": "^2.0.0", | ||||
|     "mitt": "^3.0.0", | ||||
|     "nprogress": "^0.2.0", | ||||
|     "path": "^0.12.7", | ||||
|     "pinia": "^2.0.9", | ||||
|     "qs": "^6.8.0", | ||||
|     "quill": "^1.3.7", | ||||
|     "screenfull": "^5.0.2", | ||||
|     "spark-md5": "^3.0.1", | ||||
|     "vue": "^3.2.25", | ||||
|     "vue-router": "^4.0.0-0" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@vitejs/plugin-legacy": "^2.0.0", | ||||
|     "@vitejs/plugin-vue": "^3.0.1", | ||||
|     "@vue/cli-plugin-babel": "~4.5.0", | ||||
|     "@vue/cli-plugin-eslint": "~4.5.0", | ||||
|     "@vue/cli-plugin-router": "~4.5.0", | ||||
|     "@vue/cli-plugin-vuex": "~4.5.0", | ||||
|     "@vue/cli-service": "~4.5.0", | ||||
|     "@vue/compiler-sfc": "^3.1.5", | ||||
|     "babel-eslint": "^10.1.0", | ||||
|     "babel-plugin-import": "^1.13.3", | ||||
|     "chalk": "^4.1.2", | ||||
|     "dotenv": "^10.0.0", | ||||
|     "eslint": "^6.7.2", | ||||
|     "eslint-plugin-vue": "^7.0.0", | ||||
|     "sass": "^1.54.0", | ||||
|     "terser": "^5.4.0", | ||||
|     "vite": "^3.0.1", | ||||
|     "vite-plugin-banner": "^0.1.3", | ||||
|     "vite-plugin-importer": "^0.2.5" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										20
									
								
								src/App.vue
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								src/App.vue
									
									
									
									
									
								
							| @@ -24,4 +24,24 @@ export default { | ||||
| .el-button{ | ||||
|   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> | ||||
|   | ||||
							
								
								
									
										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 | ||||
|     }) | ||||
|   }, | ||||
|   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 | ||||
|   getSubjectList : data => { | ||||
|     return service({ | ||||
| @@ -37,5 +64,33 @@ const api = { | ||||
|       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 | ||||
|   | ||||
							
								
								
									
										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> | ||||
| // 引入依赖 | ||||
| import api from '@/api/course' | ||||
| import custom from '@/utils/custom' | ||||
| import { toSQLLine } from '@/utils/stringFun' | ||||
| import WarningBar from '@/components/warningBar/warningBar.vue' | ||||
| // import custom from '@/utils/custom' | ||||
| // import { toSQLLine } from '@/utils/stringFun' | ||||
| // import WarningBar from '@/components/warningBar/warningBar.vue' | ||||
| import {ref,onMounted } from 'vue' | ||||
| import { ElMessage, ElMessageBox } from 'element-plus' | ||||
| // 变量 | ||||
| @@ -12,9 +12,9 @@ const queryParams = ref({ | ||||
|   pageSize:10, | ||||
|   keyword:'' | ||||
| }) | ||||
| const total = ref(0) | ||||
| // const dataList = ref([]) | ||||
| const subjectList = ref([]) | ||||
| const deleteVisible = ref(false) | ||||
| const dialogFormVisible = ref(false) | ||||
| const tableData =ref([]) | ||||
| const dialogTitle = ref('') | ||||
| @@ -43,6 +43,7 @@ async function getSubjectList() { | ||||
|   subjectList.value = res.data.records | ||||
|   if(res.code == 0) { | ||||
|     subjectList.value = res.data.records | ||||
|     total.value = res.data.total | ||||
|     subjectList.value = addTreeFormatSubject(subjectList.value) | ||||
|     console.log(subjectList.value) | ||||
|   } | ||||
| @@ -64,16 +65,16 @@ function onSubmit() { | ||||
|   getSubjectList() | ||||
| } | ||||
| // 排序 | ||||
| const sortChange = ({ prop, order }) => { | ||||
|   if (prop) { | ||||
|     if (prop === 'ID') { | ||||
|       prop = 'id' | ||||
|     } | ||||
|     queryParams.value.orderKey = toSQLLine(prop) | ||||
|     queryParams.value.desc = order === 'descending' | ||||
|   } | ||||
|   getSubjectList() | ||||
| } | ||||
| // const sortChange = ({ prop, order }) => { | ||||
| //   if (prop) { | ||||
| //     if (prop === 'ID') { | ||||
| //       prop = 'id' | ||||
| //     } | ||||
| //     queryParams.value.orderKey = toSQLLine(prop) | ||||
| //     queryParams.value.desc = order === 'descending' | ||||
| //   } | ||||
| //   getSubjectList() | ||||
| // } | ||||
| // 批量操作 | ||||
| const handleSelectionChange = (val) => { | ||||
|   apis.value = val | ||||
| @@ -188,6 +189,14 @@ const onReset = () => { | ||||
|     keyword:'' | ||||
|   } | ||||
| } | ||||
| function handleCurrentChange(val) { | ||||
|   queryParams.value.pageIndex = val | ||||
|   getSubjectList() | ||||
| } | ||||
| function handleSizeChange(val) { | ||||
|   queryParams.value.pageSize = val | ||||
|   getSubjectList() | ||||
| } | ||||
| </script> | ||||
| <template> | ||||
|   <div> | ||||
| @@ -242,8 +251,19 @@ const onReset = () => { | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </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> | ||||
|     <!--新增、编辑--> | ||||
|     <!--新增、编辑 窗口--> | ||||
|     <el-dialog v-model="dialogFormVisible" :before-close="closeDialog" :title="dialogTitle"> | ||||
|       <!-- <warning-bar title="新增API,需要在角色管理内配置权限才可使用" />--> | ||||
|       <el-form ref="categoryForm" :model="form" :rules="rules" label-width="80px"> | ||||
|   | ||||
| @@ -1,10 +1,14 @@ | ||||
| <script setup> | ||||
| // 引入依赖 | ||||
| import api from '@/api/course' | ||||
| import custom from '@/utils/custom' | ||||
| // import { toSQLLine } from '@/utils/stringFun' | ||||
| import WarningBar from '@/components/warningBar/warningBar.vue' | ||||
| import {ref,onMounted } from 'vue' | ||||
| import { ElMessage, ElMessageBox } from 'element-plus' | ||||
| import { useRouter, useRoute } from 'vue-router' | ||||
| const router = useRouter() | ||||
| const route = useRoute() | ||||
| // 变量 | ||||
| const queryParams = ref({ | ||||
|   pageIndex:1, | ||||
| @@ -17,42 +21,140 @@ const subjectParams = ref({ | ||||
|     pageIndex:1, | ||||
|     pageSize:100, | ||||
| }) | ||||
| const dataList = ref([]) | ||||
| const tableData = 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() | ||||
| // getSubject() | ||||
| onMounted(() => { | ||||
|   getCourseList() | ||||
|   getSubject() | ||||
| }) | ||||
| // 方法 | ||||
| async function getCourseList() { | ||||
|   const res = await api.getCourseList(queryParams.value) | ||||
|   if(res.code === 200) { | ||||
|     dataList.value = res.data.list | ||||
|   if(res.code === 0) { | ||||
|     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() { | ||||
|   getCourseList() | ||||
| } | ||||
| async function getSubject(){ | ||||
|   const res = await api.getSubject() | ||||
|   if(res.code == 200) { | ||||
|     subjectList.value = res.data.list | ||||
| const onReset = () => { | ||||
|   queryParams.value = { | ||||
|     pageIndex:1, | ||||
|     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> | ||||
| <template> | ||||
|   <div> | ||||
|     <!--    搜索框--> | ||||
|     <div class="gva-search-box"> | ||||
|       <el-form ref="searchForm" :inline="true" :model="queryParams"> | ||||
|         <el-form-item label="课程名称"> | ||||
|           <el-input v-model="queryParams.name" placeholder="课程名称" /> | ||||
|           <el-input v-model="queryParams.name" placeholder="根据课程名称进行查询" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="课程状态"> | ||||
|           <el-select v-model="queryParams.method" clearable placeholder="请选择"> | ||||
|         <el-form-item label="课程分类"> | ||||
|           <el-select v-model="queryParams.subject" clearable placeholder="请选择"> | ||||
|             <el-option | ||||
|               v-for="item in subjectList" | ||||
|               :key="item.value" | ||||
|               :label="`${item.label}(${item.value})`" | ||||
|               :value="item.value" | ||||
|               :key="item.id" | ||||
|               :label="item.name" | ||||
|               :value="item.id" | ||||
|             /> | ||||
|           </el-select> | ||||
|         </el-form-item> | ||||
| @@ -62,9 +164,11 @@ async function getSubject(){ | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|     </div> | ||||
|     <!--    表格数据--> | ||||
|     <div class="gva-table-box"> | ||||
|       <!--      批量操作--> | ||||
|       <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"> | ||||
|           <p>确定要删除吗?</p> | ||||
|           <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> | ||||
|           </div> | ||||
|           <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> | ||||
|         </el-popover> | ||||
|       </div> | ||||
|       <el-table :data="tableData" @sort-change="sortChange" @selection-change="handleSelectionChange"> | ||||
|       <!--      数据列表--> | ||||
|       <el-table :data="tableData" @selection-change="handleSelectionChange"> | ||||
|         <el-table-column | ||||
|           type="selection" | ||||
|           width="55" | ||||
|         /> | ||||
|         <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="API分组" min-width="150" prop="apiGroup" sortable="custom" /> | ||||
|         <el-table-column align="left" label="API简介" min-width="150" prop="description" sortable="custom" /> | ||||
|         <el-table-column align="left" label="请求" min-width="150" prop="method" sortable="custom"> | ||||
|           <template #default="scope"> | ||||
|             <div> | ||||
|               {{ scope.row.method }} / {{ methodFilter(scope.row.method) }} | ||||
|             </div> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|  | ||||
|         <el-table-column align="left" label="id" min-width="60" prop="id" sortable="custom" /> | ||||
|         <el-table-column align="left" label="名称" min-width="150" prop="name" /> | ||||
|         <el-table-column align="left" label="科目" min-width="150" prop="subject" /> | ||||
|         <el-table-column align="left" label="价格" min-width="150" prop="price" /> | ||||
|         <el-table-column align="left" label="状态" min-width="150" prop="status" /> | ||||
|         <el-table-column align="left" label="是否精品" min-width="150" prop="is_boutique" /> | ||||
|         <el-table-column align="left" fixed="right" label="操作" width="200"> | ||||
|           <template #default="scope"> | ||||
|             <el-button | ||||
|               icon="view" | ||||
|               size="small" | ||||
|               type="primary" | ||||
|               link | ||||
|               @click="viewCourseFunc(scope.row)" | ||||
|             >查看</el-button> | ||||
|             <el-button | ||||
|               icon="edit" | ||||
|               size="small" | ||||
|               type="primary" | ||||
|               link | ||||
|               @click="editApiFunc(scope.row)" | ||||
|               @click="editCourseFunc(scope.row)" | ||||
|             >编辑</el-button> | ||||
|             <el-button | ||||
|               icon="delete" | ||||
|               size="small" | ||||
|               type="primary" | ||||
|               type="danger" | ||||
|               link | ||||
|               @click="deleteApiFunc(scope.row)" | ||||
|               @click="deleteCourseFunc(scope.row)" | ||||
|             >删除</el-button> | ||||
|           </template> | ||||
|         </el-table-column> | ||||
|       </el-table> | ||||
|       <div class="gva-pagination"> | ||||
|         <el-pagination | ||||
|           :current-page="page" | ||||
|           :page-size="pageSize" | ||||
|           :current-page="queryParams.pageIndex" | ||||
|           :page-size="queryParams.pageSize" | ||||
|           :page-sizes="[10, 30, 50, 100]" | ||||
|           :total="total" | ||||
|           layout="total, sizes, prev, pager, next, jumper" | ||||
| @@ -127,26 +233,12 @@ async function getSubject(){ | ||||
|     </div> | ||||
|  | ||||
|     <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-item label="路径" prop="path"> | ||||
|           <el-input v-model="form.path" autocomplete="off" /> | ||||
|         <el-form-item label="课程名称" prop="name"> | ||||
|           <el-input v-model="form.name" autocomplete="off" /> | ||||
|         </el-form-item> | ||||
|         <el-form-item label="请求" prop="method"> | ||||
|           <el-select v-model="form.method" placeholder="请选择" style="width:100%"> | ||||
|             <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 label="课程简介" prop="introduction"> | ||||
|           <el-input v-model="form.introduction" autocomplete="off" /> | ||||
|         </el-form-item> | ||||
|       </el-form> | ||||
|       <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