From 00c6c38846f94687cfc4ce6d3c0506f8349a26e5 Mon Sep 17 00:00:00 2001 From: zhouwx <1175765986@qq.com> Date: 星期四, 04 七月 2024 16:49:18 +0800 Subject: [PATCH] 提交 --- src/layout/components/Sidebar/menu.js | 41 src/layout/components/Navbar.vue | 32 src/views/onlineEducation/courseManage/components/courseManageDialog.vue | 320 ++++++ src/views/onlineEducation/courseManage/courseResource/index.vue | 115 ++ src/views/onlineEducation/systemManage/banner/components/bannerDialog.vue | 2 src/api/onlineEducation/upload.js | 21 src/views/onlineEducation/courseManage/courseResource/componets/resourceDialog.vue | 345 ++++++ src/views/onlineEducation/people/index.vue | 140 ++ src/views/onlineEducation/systemManage/user/components/userDialog.vue | 5 public/spark-md5.min.js | 1 src/api/onlineEducation/courseManage.js | 65 + src/views/onlineEducation/systemManage/courseClassification/index.vue | 2 public/hash.js | 31 src/api/onlineEducation/chapters.js | 70 + src/api/onlineEducation/student.js | 67 + src/views/onlineEducation/people/components/stuDialog.vue | 311 ++++++ src/views/onlineEducation/courseManage/index.vue | 265 +++++ src/router/index.js | 29 src/views/onlineEducation/courseManage/courseChapters/components/chapterDialog.vue | 249 ++++ src/views/onlineEducation/courseManage/courseChapters/index.vue | 124 ++ src/main.js | 1 package.json | 3 src/views/onlineEducation/courseManage/courseChapters/components/chooseResource.vue | 123 ++ src/api/onlineEducation/courseResource.js | 49 src/views/components/upload.vue | 530 ++++++++++ src/views/onlineEducation/systemManage/courseClassification/components/courseClassDialog.vue | 13 26 files changed, 2,896 insertions(+), 58 deletions(-) diff --git a/package.json b/package.json index 41c27a2..ad42e19 100644 --- a/package.json +++ b/package.json @@ -31,9 +31,12 @@ "js-cookie": "3.0.1", "jsencrypt": "3.3.1", "nprogress": "0.2.0", + "p-limit": "^5.0.0", "pinia": "2.0.22", "quill": "^2.0.0-dev.3", + "spark-md5": "^3.0.2", "tinymce": "^5.10.2", + "video.js": "^8.12.0", "vue": "3.2.45", "vue-baidu-map-3x": "^1.0.35", "vue-cropper": "1.0.3", diff --git a/public/hash.js b/public/hash.js new file mode 100644 index 0000000..a5245bd --- /dev/null +++ b/public/hash.js @@ -0,0 +1,31 @@ +self.importScripts("/spark-md5.min.js"); // 导入脚本 + +// 生成文件 hash +self.onmessage = e => { + const { fileChunkList } = e.data; + const spark = new self.SparkMD5.ArrayBuffer(); + let percentage = 0; + let count = 0; + const loadNext = index => { + const reader = new FileReader(); + reader.readAsArrayBuffer(fileChunkList[index].file); + reader.onload = e => { + count++; + spark.append(e.target.result); + if (count === fileChunkList.length) { + self.postMessage({ + percentage: 100, + hash: spark.end() + }); + self.close(); + } else { + percentage += 100 / fileChunkList.length; + self.postMessage({ + percentage + }); + loadNext(count); + } + }; + }; + loadNext(0); +}; \ No newline at end of file diff --git a/public/spark-md5.min.js b/public/spark-md5.min.js new file mode 100644 index 0000000..5a22f70 --- /dev/null +++ b/public/spark-md5.min.js @@ -0,0 +1 @@ +(function(factory){if(typeof exports==="object"){module.exports=factory()}else if(typeof define==="function"&&define.amd){define(factory)}else{var glob;try{glob=window}catch(e){glob=self}glob.SparkMD5=factory()}})(function(undefined){"use strict";var add32=function(a,b){return a+b&4294967295},hex_chr=["0","1","2","3","4","5","6","7","8","9","a","b","c","d","e","f"];function cmn(q,a,b,x,s,t){a=add32(add32(a,q),add32(x,t));return add32(a<<s|a>>>32-s,b)}function md5cycle(x,k){var a=x[0],b=x[1],c=x[2],d=x[3];a+=(b&c|~b&d)+k[0]-680876936|0;a=(a<<7|a>>>25)+b|0;d+=(a&b|~a&c)+k[1]-389564586|0;d=(d<<12|d>>>20)+a|0;c+=(d&a|~d&b)+k[2]+606105819|0;c=(c<<17|c>>>15)+d|0;b+=(c&d|~c&a)+k[3]-1044525330|0;b=(b<<22|b>>>10)+c|0;a+=(b&c|~b&d)+k[4]-176418897|0;a=(a<<7|a>>>25)+b|0;d+=(a&b|~a&c)+k[5]+1200080426|0;d=(d<<12|d>>>20)+a|0;c+=(d&a|~d&b)+k[6]-1473231341|0;c=(c<<17|c>>>15)+d|0;b+=(c&d|~c&a)+k[7]-45705983|0;b=(b<<22|b>>>10)+c|0;a+=(b&c|~b&d)+k[8]+1770035416|0;a=(a<<7|a>>>25)+b|0;d+=(a&b|~a&c)+k[9]-1958414417|0;d=(d<<12|d>>>20)+a|0;c+=(d&a|~d&b)+k[10]-42063|0;c=(c<<17|c>>>15)+d|0;b+=(c&d|~c&a)+k[11]-1990404162|0;b=(b<<22|b>>>10)+c|0;a+=(b&c|~b&d)+k[12]+1804603682|0;a=(a<<7|a>>>25)+b|0;d+=(a&b|~a&c)+k[13]-40341101|0;d=(d<<12|d>>>20)+a|0;c+=(d&a|~d&b)+k[14]-1502002290|0;c=(c<<17|c>>>15)+d|0;b+=(c&d|~c&a)+k[15]+1236535329|0;b=(b<<22|b>>>10)+c|0;a+=(b&d|c&~d)+k[1]-165796510|0;a=(a<<5|a>>>27)+b|0;d+=(a&c|b&~c)+k[6]-1069501632|0;d=(d<<9|d>>>23)+a|0;c+=(d&b|a&~b)+k[11]+643717713|0;c=(c<<14|c>>>18)+d|0;b+=(c&a|d&~a)+k[0]-373897302|0;b=(b<<20|b>>>12)+c|0;a+=(b&d|c&~d)+k[5]-701558691|0;a=(a<<5|a>>>27)+b|0;d+=(a&c|b&~c)+k[10]+38016083|0;d=(d<<9|d>>>23)+a|0;c+=(d&b|a&~b)+k[15]-660478335|0;c=(c<<14|c>>>18)+d|0;b+=(c&a|d&~a)+k[4]-405537848|0;b=(b<<20|b>>>12)+c|0;a+=(b&d|c&~d)+k[9]+568446438|0;a=(a<<5|a>>>27)+b|0;d+=(a&c|b&~c)+k[14]-1019803690|0;d=(d<<9|d>>>23)+a|0;c+=(d&b|a&~b)+k[3]-187363961|0;c=(c<<14|c>>>18)+d|0;b+=(c&a|d&~a)+k[8]+1163531501|0;b=(b<<20|b>>>12)+c|0;a+=(b&d|c&~d)+k[13]-1444681467|0;a=(a<<5|a>>>27)+b|0;d+=(a&c|b&~c)+k[2]-51403784|0;d=(d<<9|d>>>23)+a|0;c+=(d&b|a&~b)+k[7]+1735328473|0;c=(c<<14|c>>>18)+d|0;b+=(c&a|d&~a)+k[12]-1926607734|0;b=(b<<20|b>>>12)+c|0;a+=(b^c^d)+k[5]-378558|0;a=(a<<4|a>>>28)+b|0;d+=(a^b^c)+k[8]-2022574463|0;d=(d<<11|d>>>21)+a|0;c+=(d^a^b)+k[11]+1839030562|0;c=(c<<16|c>>>16)+d|0;b+=(c^d^a)+k[14]-35309556|0;b=(b<<23|b>>>9)+c|0;a+=(b^c^d)+k[1]-1530992060|0;a=(a<<4|a>>>28)+b|0;d+=(a^b^c)+k[4]+1272893353|0;d=(d<<11|d>>>21)+a|0;c+=(d^a^b)+k[7]-155497632|0;c=(c<<16|c>>>16)+d|0;b+=(c^d^a)+k[10]-1094730640|0;b=(b<<23|b>>>9)+c|0;a+=(b^c^d)+k[13]+681279174|0;a=(a<<4|a>>>28)+b|0;d+=(a^b^c)+k[0]-358537222|0;d=(d<<11|d>>>21)+a|0;c+=(d^a^b)+k[3]-722521979|0;c=(c<<16|c>>>16)+d|0;b+=(c^d^a)+k[6]+76029189|0;b=(b<<23|b>>>9)+c|0;a+=(b^c^d)+k[9]-640364487|0;a=(a<<4|a>>>28)+b|0;d+=(a^b^c)+k[12]-421815835|0;d=(d<<11|d>>>21)+a|0;c+=(d^a^b)+k[15]+530742520|0;c=(c<<16|c>>>16)+d|0;b+=(c^d^a)+k[2]-995338651|0;b=(b<<23|b>>>9)+c|0;a+=(c^(b|~d))+k[0]-198630844|0;a=(a<<6|a>>>26)+b|0;d+=(b^(a|~c))+k[7]+1126891415|0;d=(d<<10|d>>>22)+a|0;c+=(a^(d|~b))+k[14]-1416354905|0;c=(c<<15|c>>>17)+d|0;b+=(d^(c|~a))+k[5]-57434055|0;b=(b<<21|b>>>11)+c|0;a+=(c^(b|~d))+k[12]+1700485571|0;a=(a<<6|a>>>26)+b|0;d+=(b^(a|~c))+k[3]-1894986606|0;d=(d<<10|d>>>22)+a|0;c+=(a^(d|~b))+k[10]-1051523|0;c=(c<<15|c>>>17)+d|0;b+=(d^(c|~a))+k[1]-2054922799|0;b=(b<<21|b>>>11)+c|0;a+=(c^(b|~d))+k[8]+1873313359|0;a=(a<<6|a>>>26)+b|0;d+=(b^(a|~c))+k[15]-30611744|0;d=(d<<10|d>>>22)+a|0;c+=(a^(d|~b))+k[6]-1560198380|0;c=(c<<15|c>>>17)+d|0;b+=(d^(c|~a))+k[13]+1309151649|0;b=(b<<21|b>>>11)+c|0;a+=(c^(b|~d))+k[4]-145523070|0;a=(a<<6|a>>>26)+b|0;d+=(b^(a|~c))+k[11]-1120210379|0;d=(d<<10|d>>>22)+a|0;c+=(a^(d|~b))+k[2]+718787259|0;c=(c<<15|c>>>17)+d|0;b+=(d^(c|~a))+k[9]-343485551|0;b=(b<<21|b>>>11)+c|0;x[0]=a+x[0]|0;x[1]=b+x[1]|0;x[2]=c+x[2]|0;x[3]=d+x[3]|0}function md5blk(s){var md5blks=[],i;for(i=0;i<64;i+=4){md5blks[i>>2]=s.charCodeAt(i)+(s.charCodeAt(i+1)<<8)+(s.charCodeAt(i+2)<<16)+(s.charCodeAt(i+3)<<24)}return md5blks}function md5blk_array(a){var md5blks=[],i;for(i=0;i<64;i+=4){md5blks[i>>2]=a[i]+(a[i+1]<<8)+(a[i+2]<<16)+(a[i+3]<<24)}return md5blks}function md51(s){var n=s.length,state=[1732584193,-271733879,-1732584194,271733878],i,length,tail,tmp,lo,hi;for(i=64;i<=n;i+=64){md5cycle(state,md5blk(s.substring(i-64,i)))}s=s.substring(i-64);length=s.length;tail=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(i=0;i<length;i+=1){tail[i>>2]|=s.charCodeAt(i)<<(i%4<<3)}tail[i>>2]|=128<<(i%4<<3);if(i>55){md5cycle(state,tail);for(i=0;i<16;i+=1){tail[i]=0}}tmp=n*8;tmp=tmp.toString(16).match(/(.*?)(.{0,8})$/);lo=parseInt(tmp[2],16);hi=parseInt(tmp[1],16)||0;tail[14]=lo;tail[15]=hi;md5cycle(state,tail);return state}function md51_array(a){var n=a.length,state=[1732584193,-271733879,-1732584194,271733878],i,length,tail,tmp,lo,hi;for(i=64;i<=n;i+=64){md5cycle(state,md5blk_array(a.subarray(i-64,i)))}a=i-64<n?a.subarray(i-64):new Uint8Array(0);length=a.length;tail=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];for(i=0;i<length;i+=1){tail[i>>2]|=a[i]<<(i%4<<3)}tail[i>>2]|=128<<(i%4<<3);if(i>55){md5cycle(state,tail);for(i=0;i<16;i+=1){tail[i]=0}}tmp=n*8;tmp=tmp.toString(16).match(/(.*?)(.{0,8})$/);lo=parseInt(tmp[2],16);hi=parseInt(tmp[1],16)||0;tail[14]=lo;tail[15]=hi;md5cycle(state,tail);return state}function rhex(n){var s="",j;for(j=0;j<4;j+=1){s+=hex_chr[n>>j*8+4&15]+hex_chr[n>>j*8&15]}return s}function hex(x){var i;for(i=0;i<x.length;i+=1){x[i]=rhex(x[i])}return x.join("")}if(hex(md51("hello"))!=="5d41402abc4b2a76b9719d911017c592"){add32=function(x,y){var lsw=(x&65535)+(y&65535),msw=(x>>16)+(y>>16)+(lsw>>16);return msw<<16|lsw&65535}}if(typeof ArrayBuffer!=="undefined"&&!ArrayBuffer.prototype.slice){(function(){function clamp(val,length){val=val|0||0;if(val<0){return Math.max(val+length,0)}return Math.min(val,length)}ArrayBuffer.prototype.slice=function(from,to){var length=this.byteLength,begin=clamp(from,length),end=length,num,target,targetArray,sourceArray;if(to!==undefined){end=clamp(to,length)}if(begin>end){return new ArrayBuffer(0)}num=end-begin;target=new ArrayBuffer(num);targetArray=new Uint8Array(target);sourceArray=new Uint8Array(this,begin,num);targetArray.set(sourceArray);return target}})()}function toUtf8(str){if(/[\u0080-\uFFFF]/.test(str)){str=unescape(encodeURIComponent(str))}return str}function utf8Str2ArrayBuffer(str,returnUInt8Array){var length=str.length,buff=new ArrayBuffer(length),arr=new Uint8Array(buff),i;for(i=0;i<length;i+=1){arr[i]=str.charCodeAt(i)}return returnUInt8Array?arr:buff}function arrayBuffer2Utf8Str(buff){return String.fromCharCode.apply(null,new Uint8Array(buff))}function concatenateArrayBuffers(first,second,returnUInt8Array){var result=new Uint8Array(first.byteLength+second.byteLength);result.set(new Uint8Array(first));result.set(new Uint8Array(second),first.byteLength);return returnUInt8Array?result:result.buffer}function hexToBinaryString(hex){var bytes=[],length=hex.length,x;for(x=0;x<length-1;x+=2){bytes.push(parseInt(hex.substr(x,2),16))}return String.fromCharCode.apply(String,bytes)}function SparkMD5(){this.reset()}SparkMD5.prototype.append=function(str){this.appendBinary(toUtf8(str));return this};SparkMD5.prototype.appendBinary=function(contents){this._buff+=contents;this._length+=contents.length;var length=this._buff.length,i;for(i=64;i<=length;i+=64){md5cycle(this._hash,md5blk(this._buff.substring(i-64,i)))}this._buff=this._buff.substring(i-64);return this};SparkMD5.prototype.end=function(raw){var buff=this._buff,length=buff.length,i,tail=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],ret;for(i=0;i<length;i+=1){tail[i>>2]|=buff.charCodeAt(i)<<(i%4<<3)}this._finish(tail,length);ret=hex(this._hash);if(raw){ret=hexToBinaryString(ret)}this.reset();return ret};SparkMD5.prototype.reset=function(){this._buff="";this._length=0;this._hash=[1732584193,-271733879,-1732584194,271733878];return this};SparkMD5.prototype.getState=function(){return{buff:this._buff,length:this._length,hash:this._hash}};SparkMD5.prototype.setState=function(state){this._buff=state.buff;this._length=state.length;this._hash=state.hash;return this};SparkMD5.prototype.destroy=function(){delete this._hash;delete this._buff;delete this._length};SparkMD5.prototype._finish=function(tail,length){var i=length,tmp,lo,hi;tail[i>>2]|=128<<(i%4<<3);if(i>55){md5cycle(this._hash,tail);for(i=0;i<16;i+=1){tail[i]=0}}tmp=this._length*8;tmp=tmp.toString(16).match(/(.*?)(.{0,8})$/);lo=parseInt(tmp[2],16);hi=parseInt(tmp[1],16)||0;tail[14]=lo;tail[15]=hi;md5cycle(this._hash,tail)};SparkMD5.hash=function(str,raw){return SparkMD5.hashBinary(toUtf8(str),raw)};SparkMD5.hashBinary=function(content,raw){var hash=md51(content),ret=hex(hash);return raw?hexToBinaryString(ret):ret};SparkMD5.ArrayBuffer=function(){this.reset()};SparkMD5.ArrayBuffer.prototype.append=function(arr){var buff=concatenateArrayBuffers(this._buff.buffer,arr,true),length=buff.length,i;this._length+=arr.byteLength;for(i=64;i<=length;i+=64){md5cycle(this._hash,md5blk_array(buff.subarray(i-64,i)))}this._buff=i-64<length?new Uint8Array(buff.buffer.slice(i-64)):new Uint8Array(0);return this};SparkMD5.ArrayBuffer.prototype.end=function(raw){var buff=this._buff,length=buff.length,tail=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],i,ret;for(i=0;i<length;i+=1){tail[i>>2]|=buff[i]<<(i%4<<3)}this._finish(tail,length);ret=hex(this._hash);if(raw){ret=hexToBinaryString(ret)}this.reset();return ret};SparkMD5.ArrayBuffer.prototype.reset=function(){this._buff=new Uint8Array(0);this._length=0;this._hash=[1732584193,-271733879,-1732584194,271733878];return this};SparkMD5.ArrayBuffer.prototype.getState=function(){var state=SparkMD5.prototype.getState.call(this);state.buff=arrayBuffer2Utf8Str(state.buff);return state};SparkMD5.ArrayBuffer.prototype.setState=function(state){state.buff=utf8Str2ArrayBuffer(state.buff,true);return SparkMD5.prototype.setState.call(this,state)};SparkMD5.ArrayBuffer.prototype.destroy=SparkMD5.prototype.destroy;SparkMD5.ArrayBuffer.prototype._finish=SparkMD5.prototype._finish;SparkMD5.ArrayBuffer.hash=function(arr,raw){var hash=md51_array(new Uint8Array(arr)),ret=hex(hash);return raw?hexToBinaryString(ret):ret};return SparkMD5}); diff --git a/src/api/onlineEducation/chapters.js b/src/api/onlineEducation/chapters.js new file mode 100644 index 0000000..26898bf --- /dev/null +++ b/src/api/onlineEducation/chapters.js @@ -0,0 +1,70 @@ +import request from "@/utils/request"; + + +export function checkChapterName(data) { + return request({ + url: '/course-chapter/checkNameUnique', + method: 'post', + data: data + }) +} +export function getChapters(params) { + return request({ + url: '/course-chapter/getCourseChapter', + method: 'get', + params: params + }) +} + +export function addChapter(data) { + return request({ + url: '/course-chapter', + method: 'post', + data: data + }) +} + +export function addChapterPeriod(data) { + return request({ + url: '/course-chapter-period', + method: 'post', + data: data + }) +} + +export function editChapter(params) { + return request({ + url: `/course-chapter`, + method: 'put', + data: params + }) +} +export function editChapterPeriod(params) { + return request({ + url: `/course-chapter-period`, + method: 'put', + data: params + }) +} + +export function getChapterById(params) { + return request({ + url: '/course-chapter/' +params , + method: 'get', + params: params + }) +} + + +export function delChapter(data) { + return request({ + url: `/course-chapter/` + data, + method: 'delete' + }) +} +export function delPeriod(data) { + return request({ + url: `/course-chapter-period/` + data, + method: 'delete' + }) +} diff --git a/src/api/onlineEducation/courseManage.js b/src/api/onlineEducation/courseManage.js new file mode 100644 index 0000000..0024c74 --- /dev/null +++ b/src/api/onlineEducation/courseManage.js @@ -0,0 +1,65 @@ +import request from "@/utils/request"; + +export function checkCourseName(data) { + return request({ + url: '/course/checkNameUnique', + method: 'post', + data: data + }) +} + +export function getCourse(params) { + return request({ + url: '/course/list', + method: 'get', + params: params + }) +} + +export function addCourse(data) { + return request({ + url: '/course', + method: 'post', + data: data + }) +} + +export function editCourse(params) { + return request({ + url: `/course`, + method: 'put', + data: params + }) +} + +export function getCourseById(params) { + return request({ + url: '/course/' +params , + method: 'get', + params: params + }) +} + + +export function delCourse(data) { + return request({ + url: `/course/` + data, + method: 'delete' + }) +} + +export function doCourse(data) { + return request({ + url: '/course/doApprove', + method: 'post', + data: data + }) +} + +export function changeCourseStatus(params) { + return request({ + url: '/course/changeStatus', + method: 'put', + data: params + }) +} diff --git a/src/api/onlineEducation/courseResource.js b/src/api/onlineEducation/courseResource.js new file mode 100644 index 0000000..46b7bc6 --- /dev/null +++ b/src/api/onlineEducation/courseResource.js @@ -0,0 +1,49 @@ +import request from "@/utils/request"; + +export function checkResourceName(data) { + return request({ + url: '/resource/checkNameUnique', + method: 'post', + data: data + }) +} + +export function getResource(params) { + return request({ + url: '/resource/list', + method: 'get', + params: params + }) +} + +export function addResource(data) { + return request({ + url: '/resource', + method: 'post', + data: data + }) +} + +export function editResource(params) { + return request({ + url: `/resource`, + method: 'put', + data: params + }) +} + +export function getResourceById(params) { + return request({ + url: '/resource/' +params , + method: 'get', + params: params + }) +} + + +export function delResource(data) { + return request({ + url: `/resource/` + data, + method: 'delete' + }) +} diff --git a/src/api/onlineEducation/student.js b/src/api/onlineEducation/student.js new file mode 100644 index 0000000..46a6d13 --- /dev/null +++ b/src/api/onlineEducation/student.js @@ -0,0 +1,67 @@ +import request from '@/utils/request' + +export function getStudent(param) { + return request({ + url: '/student/list', + method: 'get', + params: param + }) +} + +export function getStudentById(params) { + return request({ + url: '/system/user/' +params , + method: 'get', + params: params + }) +} + +export function addStudent(data) { + return request({ + url: '/student', + method: 'post', + data: data + }) +} + +export function checkStuIdNo(data) { + return request({ + url: '/student/checkIdNoUnique', + method: 'post', + data: data + }) +} + +export function checkStuPhone(data) { + return request({ + url: '/student/checkPhoneUnique', + method: 'post', + data: data + }) +} + + + +export function editStudent(params) { + return request({ + url: `/student`, + method: 'put', + data: params + }) +} + +export function resetPwd(params) { + return request({ + url: `/student/resetPwd`, + method: 'put', + data: params + }) +} + +export function delStudent(userId) { + return request({ + url: '/student/' + userId, + method: 'delete' + }) +} + diff --git a/src/api/onlineEducation/upload.js b/src/api/onlineEducation/upload.js new file mode 100644 index 0000000..a42f314 --- /dev/null +++ b/src/api/onlineEducation/upload.js @@ -0,0 +1,21 @@ +import request from '@/utils/request' + + +export function uploadFileRequest(data,uploadProgressHandle) { + return request({ + url: '/system/common/uploadSlice', + method: 'post', + headers:{'Content-Type':'multipart/form-data'}, + data: data, + onUploadProgress: uploadProgressHandle + }) +} + +export function mergeFileRequest(data) { + return request({ + url: '/system/common/uploadMerge', + headers:{'Content-Type':'multipart/form-data'}, + method: 'post', + data: data + }) +} diff --git a/src/layout/components/Navbar.vue b/src/layout/components/Navbar.vue index 68b1fd7..f75f237 100644 --- a/src/layout/components/Navbar.vue +++ b/src/layout/components/Navbar.vue @@ -48,7 +48,8 @@ </el-dropdown> </div> </div> -<!-- <review-dialog ref="reviewRef" ></review-dialog>--> + + <user-dialog ref="reviewRef" ></user-dialog> <!-- <register ref="regRef" @getList="getList" />--> <!-- <supervise-dialog ref="superRef"></supervise-dialog>--> </div> @@ -72,6 +73,7 @@ import Cookies from "js-cookie"; import {getUserById} from "@/api/sysUsers"; import menu from "@/layout/components/Sidebar/menu"; +import userDialog from '@/views/onlineEducation/systemManage/user/components/userDialog.vue' const appStore = useAppStore() const userStore = useUserStore() @@ -135,34 +137,10 @@ function getInfo() { console.log("getInfo",userInfo.value) - //机构用户 - if(userInfo.value.identity === 1){ - //审核驳回(可修改) - if(userInfo.value.state === 3){ - const obj = { - id: userInfo.value.id, - username: userInfo.value.username, - agencyId: userInfo.value.agentId - } - regRef.value.openDialog('reject', obj); - }else{ - //审核通过、未审核状态(不可修改) - const obj = { - agencyId: userInfo.value.agentId - } - reviewRef.value.openDialog(obj,'view') - } - } - //监管用户 - else if (userInfo.value.identity === 0) { - const obj = { - id: userInfo.value.id - } - superRef.value.openDialog('view', obj); - } + reviewRef.value.openDialog('view',userInfo.value) } function editPsd() { - superRef.value.openDialog('pwd', userInfo.value); + reviewRef.value.openDialog('pwd',userInfo.value) } const sidebarRouters = ref([]) diff --git a/src/layout/components/Sidebar/menu.js b/src/layout/components/Sidebar/menu.js index c319557..0e23c86 100644 --- a/src/layout/components/Sidebar/menu.js +++ b/src/layout/components/Sidebar/menu.js @@ -1,10 +1,27 @@ import Layout from '@/layout' const menu = { adminMenu: [ + // { + // path: '/course', + // name: 'Course', + // meta: { title: '课程管理',icon: 'documentation',affix: true } + // }, { - path: '/course', - name: 'Course', - meta: { title: '课程管理',icon: 'documentation',affix: true } + path: '/onlineEducation', + redirect: '/onlineEducation/courseManage', + meta: { title: '课程管理',icon: 'form'}, + children: [ + { + path: 'course', + name: 'course', + meta: { title: '课程列表',icon: 'list'} + } , + { + path: 'courseResource', + name: 'courseResource', + meta: { title: '课程资源',icon: 'education'} + } , + ] }, { path: '/question', @@ -66,9 +83,21 @@ ], companyMenu: [ { - path: '/course', - name: 'Course', - meta: { title: '课程管理',icon: 'documentation',affix: true } + path: '/onlineEducation', + redirect: '/onlineEducation/courseManage', + meta: { title: '课程管理',icon: 'form'}, + children: [ + { + path: 'course', + name: 'course', + meta: { title: '课程列表',icon: 'list'} + } , + { + path: 'courseResource', + name: 'courseResource', + meta: { title: '课程资源',icon: 'education'} + } , + ] }, { path: '/question', diff --git a/src/main.js b/src/main.js index d1504ce..ad736f7 100644 --- a/src/main.js +++ b/src/main.js @@ -51,6 +51,7 @@ import { Boot } from '@wangeditor/editor' import attachmentModule from '@wangeditor/plugin-upload-attachment' import loadMore from '@/utils/selectLoadMoreDirective' +import "video.js/dist/video-js.css" Boot.registerModule(attachmentModule) const app = createApp(App) diff --git a/src/router/index.js b/src/router/index.js index d650ebd..20f9d8f 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -53,6 +53,18 @@ hidden: true }, + { + path: '/chapters', + component: Layout, + redirect: '/chapters', + children: [ + { + path: '/chapters', + component: () => import('@/views/onlineEducation/courseManage/courseChapters/index.vue'), + name: 'Chapters', + } + ] + }, // { // path: '', // component: Layout, @@ -79,16 +91,23 @@ ] }, { - path: '/course', + path: '/onlineEducation', component: Layout, - redirect: '/course', + redirect: '/onlineEducation/courseManage', + meta: { title: '课程管理'}, children: [ { - path: '/course', + path: 'course', component: () => import('@/views/onlineEducation/courseManage/index.vue'), name: 'course', - meta: { title: '课程管理',icon: 'form', affix: true } - } + meta: { title: '课程列表',icon: 'form', affix: true } + }, + { + path: 'courseResource', + component: () => import('@/views/onlineEducation/courseManage/courseResource/index.vue'), + name: 'courseResource', + meta: { title: '课程资源',icon: 'form', affix: true } + }, ] }, { diff --git a/src/views/components/upload.vue b/src/views/components/upload.vue new file mode 100644 index 0000000..efb2c60 --- /dev/null +++ b/src/views/components/upload.vue @@ -0,0 +1,530 @@ + +<template> + <div class="greetings"> + <el-upload accept=".mp4, .mp3, .xls, .xlsx, .doc, .docx, .ppt, .pptx, .pdf" :on-change="handleFileChange" :on-preview="view" :auto-upload="false" ref="uploadfileComponent" :limit="1" :on-exceed="handleExceed" v-model:file-list="fileList"> + <template #trigger> + <el-button type="primary">选择文件</el-button> + </template> + <el-button :disabled="uploadDisabled" style="margin-left: 10px" type="success" @click="handlerUpload">上传</el-button> + <el-button class="ml-3" type="success" @click="resetData" >重置</el-button> + <template #tip> + <br /><br /> + <span>上传进度:{{ fakeUploadPercentage }}%</span> + <el-progress :text-inside="true" :stroke-width="26" :percentage="fakeUploadPercentage" /> + <div class="el-upload__tip text-red">限制一个文件, 新文件将会覆盖原文件</div> + </template> + </el-upload> + <br> + <div v-if="container.showVideo" style="width: 300px;height: 200px"> + <video ref="videoPlayer" class="video-js" style="margin: auto auto"></video> + </div> + + </div> +</template> + +<script setup> +import { ElMessage } from "element-plus"; +import videojs from "video.js" +import { computed, nextTick, onMounted, onUnmounted,ref,reactive,watch } from "vue"; +import SparkMD5 from "spark-md5"; +import {uploadFileRequest,mergeFileRequest} from "@/api/onlineEducation/upload" +import pLimit from 'p-limit' + +const videoPlayer = ref(null) +const myPlayer = ref(null) +const uploadDisabled=ref(false) +const chunkSize = ref(10 * 1024 * 1024) // 切片大小 +const uploadedCount=ref(0) //已上传的分配个数 +const fileChunkList=ref([]) +const fileList=ref([]) +const uploadfileComponent=ref(null) +const emit = defineEmits(["getFile"]); +const props = defineProps({ + responseType: { + type: Number, + default: 0 + } +}) +const container=reactive({ + file:{ + name:'', + percentage:0, + status:1, + size:0, + url:'', + raw:null, + uid:0 + }, + fileMd5:'', + worker:null, + showVideo:false +}) +// 生成文件hash的进度 +const hashPercentage = ref(0) +// 显示在页面上的文件上传进度 +const fakeUploadPercentage = ref(0) +const type = ref(); +onMounted(() => { + type.value = props.responseType + // getVideo(props.responseType) + +}) +const resourcePath = ref(); +const getVideo = (value) => { + type.value = value; + if(value == 1){ + // container.showVideo = true + nextTick(() => { + console.log("111111",videoPlayer.value) + myPlayer.value = videojs(videoPlayer.value, { + poster: "",//视频封面 + controls: true,//视频控件 + autoplay:true,//自动播放 + sources: [ + { + src: resourcePath.value ? "http://192.168.2.16:9000/trainexam/" + resourcePath.value : '', + // src:'', + type: 'application/x-mpegURL', + } + ], + controlBar: { + remainingTimeDisplay: { + displayNegative: false + } + }, + playbackRates: [0.5, 1, 1.5, 2]//设置播放速度 + }, onPlayerReady) + }); + } +} +// watch(() => props.responseType, value => getVideo(value)) +onUnmounted(() => { + if (myPlayer.value) { + myPlayer.value.dispose() + } +}) + +const dispose = () => { + // if (myPlayer.value) { + // myPlayer.value.dispose() + // resourcePath.value = '' + // } + container.showVideo = false; + resourcePath.value = '' + hashPercentage.value=0 + uploadPercentage.value=0 + fakeUploadPercentage.value=0 + uploadedCount.value=0 + fileChunkList.value=[] + fileList.value=[] +} + +const changeType = (val) => { + type.value = val + dispose() + if(val == 1){ + container.showVideo = true + nextTick(() => { + getVideo(val) + }) + } +} +const openValue = ref(); +const open = (val) => { + console.log("val",val) + openValue.value = val + fakeUploadPercentage.value = 100 + if(val.resourceType == 1){ + container.showVideo = true + resourcePath.value = val.resourcePath; + getVideo(val.resourceType) + }else { + container.showVideo = false + // if (myPlayer.value) { + // myPlayer.value.dispose() + // } + fileList.value.push({ + path: val.resourcePath, + name: val.originName + }) + } +} +const view = (file) => { + console.log('vlco',file) + // console.log("点击文件=>", file); + const url = 'http://192.168.2.16:9000/trainexam/' + file.path; + const link = document.createElement("a"); + link.href = url; + link.download = file.name; + // link.target = "_blank"; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); +} +// video初始化完成的回调函数 +const onPlayerReady = () => { + myPlayer.value.log("play.....") + bindVideoEvents() +} +// 绑定事件 +const bindVideoEvents = () => { + if (!myPlayer.value) return + myPlayer.value.on('play', onPlay) + myPlayer.value.on('pause', onPause) + myPlayer.value.on('ended', onEnded) + myPlayer.value.on('timeupdate', onTimeupdate) + myPlayer.value.on('loadedmetadata', onLoadedmetadata) + myPlayer.value.on('fullscreenchange', onFullscreenchange) + myPlayer.value.on('error', err => { + console.log('视频加载发生错误', err) + }) +} + +const onPlay = () => { + console.log('播放视频') +} +const onPause = () => { + console.log('暂停播放') +} +const onEnded = () => {} +const onTimeupdate = () => { + console.log('播放位置已更改时,播放时间更新') +} +// 全屏切换 +const onFullscreenchange = () => { + console.log('全屏状态改变') +} +// 元数据加载完成 +const onLoadedmetadata = () => { + console.log('元数据加载完成') + +} + + +//计算文件上传的进度 +const uploadPercentage= computed({ + get(){ + if(!container.file||!fileChunkList.value.length){ + return 0 + } + const loaded=fileChunkList.value.map(item => item.size * item.percentage).reduce((acc,cur) => { + return acc+cur + }) + console.log('loaded',uploadedCount.value,loaded) + return parseInt((loaded/container.file.size).toFixed(2)) + }, + set(value){ + return value + } +}) + +// watch uploadPercentage,得到fakeUploadPercentage +watch(uploadPercentage, (newValue) => { + if (newValue >= fakeUploadPercentage.value) { + fakeUploadPercentage.value = newValue + } +}) + +const resetData = () => { + container.showVideo = false + resourcePath.value = '' + hashPercentage.value=0 + uploadPercentage.value=0 + fakeUploadPercentage.value=0 + uploadedCount.value=0 + fileChunkList.value=[] + fileList.value=[] + if(container.worker){ + container.worker.onmessage=null + } + // if (myPlayer.value) { + // myPlayer.value.dispose() + // } + const file = { + resourceSize: null, + md5: '', + resourcePath: '', + mediaType: '', + docPage: 0, + resourceLength: '', + originName:'' + } + emit("getFile",file) +} + +//选择了文件 +const handleFileChange=(uploadFile,uploadFiles) =>{ + // resetData() + if(!uploadFile){ + return + } + container.file=uploadFile + fileList.value=uploadFiles +} + +const handleExceed= (files) => { + uploadfileComponent.value.clearFiles() + nextTick(() => { + uploadfileComponent.value.handleStart(files[0]) + }) +} + +//上传 +const handlerUpload= async() => { + + if(type.value == null){ + ElMessage({ + type: 'warning', + message: '请先选择资源类型' + }); + return false + } + + if(!container.file.raw){ + return + } + if(container.file.raw.size > 1024 * 1024 * 1000){ + ElMessage({ + type: 'warning', + message: '文件大小不能超过1G' + }); + return false + } + const filetype = container.file.raw.name.split(".").pop(); + const extension = (filetype === "mp4" || filetype ==="mp3" || filetype ==="xls" || filetype === "xlsx" || filetype ==="doc" || filetype ==="docx" || filetype === "ppt" || filetype ==="pptx" || filetype ==="pdf"); + if (!extension ) { + ElMessage({ + type: 'warning', + message: '暂不支持该格式上传' + }); + return false; + } + if((type.value == 1 && filetype != 'mp4') || (type.value == 2 && filetype != 'mp3')){ + ElMessage({ + type: 'warning', + message: '请上传所选资源类型的文件' + }); + return false; + } + if(type.value == 3){ + if( filetype == 'xls' || filetype == 'xlsx' || filetype == 'doc'|| filetype == 'docx'|| filetype == 'ppt'|| filetype == 'pptx'|| filetype == 'pdf' ){ + + }else { + ElMessage({ + type: 'warning', + message: '请上传所选资源类型的文件' + }); + return false; + } + } + + //文件分片 + const chunkList=createFileChunk(container.file.raw) + console.log('文件分了多少片:',chunkList.length) + //通过webworker计算出文件hash + container.fileMd5=await calculateMd5(chunkList) + console.log('文件hash1:',container.fileMd5) + // container.fileMd5=await getFileMD5(container.file.raw) + // console.log('文件hash2:',container.hash) + + fileChunkList.value=[] + fileChunkList.value=chunkList.map(({file},index) => ({ + fileMd5:container.fileMd5, + index, + chunkName: `${container.fileMd5}-${index}`, + chunk:file, + size:file.size, + // 如果已上传切片数组uploadedList中包含这个切片,则证明这个切片之前已经上传成功了,进度设为100。 + percentage:0 + })) + + uploadChunks(fileChunkList) +} + +//文件分片 +const createFileChunk = (file,size=chunkSize.value) => { + const chunkList=[] + let cur=0 + while(cur<file.size){ + chunkList.push({ + file:file.slice(cur,cur+size), + }) + cur+=size + } + return chunkList +} + +//计算文件md5 方法1 +const calculateMd5 = (chunkList) => { + return new Promise((resolve) => { + container.worker=new Worker('/hash.js') + container.worker.postMessage({fileChunkList:chunkList}) + container.worker.onmessage= (e) => { + const {percentage,hash} = e.data + hashPercentage.value=percentage.toFixed(2) + if(hash){ + resolve(hash) + } + } + }) +} + +//计算文件md5 方法2 +const getFileMD5 = (file) => { + return new Promise((resolve, reject) => { + const spark = new SparkMD5.ArrayBuffer() + const fileReader = new FileReader() + fileReader.onload = (e) => { + spark.append(e.target?.result) + resolve(spark.end()) + } + fileReader.onerror = () => { + reject('') + } + fileReader.readAsArrayBuffer(file) + }) +} + +//计算上传进度 +const createProgressHandler = (item) => { + console.log('createProgresshandler -> item', item); + return (p) => { + if(item.percentage>=100){ + item.percentage = 100 + }else{ + item.percentage=parseInt(String((p.loaded/p.total)*100)) + } + // 确保进度百分比不会超过100% + if (item.percentage > 100) item.percentage = 100 + } +} + + + +//上传切片 +const uploadChunks= async(uploadedList) => { + const limit = pLimit(10); // 控制并发数为10 + const requestList=uploadedList.value.map(({chunk,chunkName,index,fileMd5}) => { + const formdata=new FormData() + formdata.append('file',chunk) + formdata.append('chunkName',chunkName) + formdata.append('fileName',container.file.name) + formdata.append('fileMd5',fileMd5) + formdata.append('index',index) + return {formdata,index} + }).map(async ({formdata,index}) => { + return limit(() => doUploadChunk({data:formdata,onUploadProgress:createProgressHandler(fileChunkList.value[index])})) + }) + await Promise.all(requestList) + console.log("数组:",fileChunkList) + if(uploadedCount.value>=fileChunkList.value.length){ + mergeRequest() + } + +} + + +const doUploadChunk = ({data,onUploadProgress}) => { + return new Promise((resolve) => { + uploadFileRequest(data,onUploadProgress).then((result) => { + let resData=result.data + if(result&&result.code==200){ + uploadedCount.value=uploadedCount.value+1 + fileChunkList.value[data.get('index')].percentage=100 //手动更新进度 + console.log(uploadedCount.value,'result--------------') + } + resolve('done') + }) + }) +} + +const mergeRequest = async() => { + if(container.file.name.lastIndexOf(".") === -1){ + ElMessage.warning("请输入文件后缀名") + return + } + let data=await mergeFileRequest({fileMd5:container.fileMd5,fileName:container.file.name}) + console.log(data,"mege------------222") + if(data && data.code==200){ + const filetype = data.data.originName.split(".").pop(); + if(filetype == 'mp4' || filetype == 'MP4'){ + container.showVideo = true + await nextTick(() => { + + console.log("myPlayer.value",myPlayer.value) + myPlayer.value.src( + { + src:"http://192.168.2.16:9000/trainexam/"+data.data.path, + type: 'application/x-mpegURL', + }) + // myPlayer.value.load() + myPlayer.value.play().catch((error) => { + console.error('Error playing video:', error); + }); + }) + // myPlayer.value.pause() + //myPlayer.value.reset() + } + const file = { + resourceSize: data.data.size, + md5: data.data.md5, + resourcePath: data.data.path, + mediaType: filetype, + docPage: data.data.docPage, + resourceLength: data.data.resourceLength, + originName: data.data.originName + } + emit("getFile",file) + + + + ElMessage.success("上传成功") + }else{ + ElMessage.success("合并数据失败") + } +} + +defineExpose({ + dispose, + changeType, + open +}); +</script> + + +<style scoped> +.greetings{ + :deep(.video-js) { + width: 300px; + height: 200px; + } + :deep(.el-icon--close) { + display: none; + } +} + + + +h1 { + font-weight: 500; + font-size: 2.6rem; + position: relative; + top: -10px; +} + +h3 { + font-size: 1.2rem; +} + +.greetings h1, +.greetings h3 { + text-align: center; +} + +@media (min-width: 1024px) { + .greetings h1, + .greetings h3 { + text-align: left; + } +} +</style> diff --git a/src/views/onlineEducation/courseManage/components/courseManageDialog.vue b/src/views/onlineEducation/courseManage/components/courseManageDialog.vue new file mode 100644 index 0000000..9d397ac --- /dev/null +++ b/src/views/onlineEducation/courseManage/components/courseManageDialog.vue @@ -0,0 +1,320 @@ +<template> + <div class="notice"> + <el-dialog + v-model="dialogVisible" + :title="title" + width="500px" + :before-close="handleClose" + > + <el-form :model="state.form" size="default" ref="busRef" :rules="state.formRules" label-width="150px" > + <el-form-item label="课程名称:" prop="name"> + <el-input v-model.trim="state.form.name" placeholder="请输入课程名称"></el-input> + </el-form-item> + <el-form-item label="课程分类:" prop="categoryId" > +<!-- <el-select clearable v-model="state.form.categoryId" style="width: 100%" placeholder="请选择课程分类" @change="changeType">--> +<!-- <el-option v-for="item in state.classifyList" :key="item" :label="item.name" :value="item.id" />--> +<!-- </el-select>--> + <el-cascader + ref="classifyRef" + v-model="state.form.categoryId" + :options="state.classifyList" + :props="state.props" + clearable + :show-all-levels="false" + @change="handleChange" + /> + </el-form-item> + <el-form-item label="要求课时:" prop="period"> + <el-input v-model="state.form.period" placeholder="请输入要求课时"> + <template #append>分钟</template> + </el-input> + </el-form-item> + <el-form-item label="提交单位:" prop="companyName" > + <el-input v-model="state.form.companyName" disabled/> + </el-form-item> + <el-form-item label="封面:"> + <el-upload accept="image/*" :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.imgLimit' v-model:file-list="state.imgList" list-type="picture-card" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)" > + <el-icon><Plus /></el-icon> + <template #tip> + <div class="el-upload__tip">上传jpg/png图片尺寸小于5M,最多可上传1张</div> + </template> + </el-upload> + </el-form-item> + </el-form> + <template #footer> + <span class="dialog-footer"> + <el-button @click="handleClose" size="default">取 消</el-button> + <el-button type="primary" @click="onSubmit" size="default" v-preReClick>确认</el-button> + </span> + </template> + </el-dialog> + </div> +</template> +<script setup> +import {reactive, ref, toRefs} from 'vue' +import Editor from "@/components/Editor/index.vue"; +import {ElMessage} from "element-plus"; +import {addNotice} from "@/api/backManage/notice"; +import {addDict, editDict, getDictDetail} from "@/api/backManage/evaluate"; +import {addCompany, checkName, distributeCompany, editCompany} from "@/api/onlineEducation/company"; +import {verifyPhone} from "@/utils/validate"; +import { + addClassification, + checkClassName, + editClassification, + getClassification +} from "@/api/onlineEducation/courseClass"; +import {addCourse, checkCourseName, editCourse, getCourseById} from "@/api/onlineEducation/courseManage"; +import {getToken} from "@/utils/auth"; +import {delPic, getBannerById} from "@/api/onlineEducation/banner"; +import Cookies from "js-cookie"; + +const dialogVisible = ref(false); +const title = ref(""); +const busRef = ref(); +const length = ref() +const emit = defineEmits(["getList"]); +const startUsername = ref(''); +const classifyRef = ref(null) + +const validateName = (rule, value, callback)=>{ + if(value === ''){ + callback(new Error('请输入课程名称')) + }else if(title.value === '编辑' && value === startUsername.value){ + callback() + }else{ + let param = {} + if(title.value === '新增') { + param = { + name:value + } + }else if(title.value === '编辑'){ + param = { + name:value, + id: state.form.id + } + } + checkCourseName(param).then((res)=>{ + if(res.data == false){ + callback(new Error('课程名称已被占用,请更换其他名称')) + }else{ + callback() + } + }) + } +} +const state = reactive({ + form: { + id: '', + name: '', + categoryId: null, + period: null, + logo: '', + companyId: null + }, + formRules: { + name: [{required: true, trigger: "blur", validator: validateName}], + categoryId: [{required: true, message: '请选择课程分类', trigger: 'blur'}], + period: [{required: true, message: '请输入要求课时', trigger: 'blur'}], + }, + classifyList: [], + uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile', + header: { + Authorization: getToken() + }, + imgLimit: 1, + imgList: [], + isAdmin: false, + props: { + checkStrictly: true, + } +}) + +const openDialog = async (type, value) => { + await getClassifyList(); + const userInfo = JSON.parse(Cookies.get('userInfo')) + console.log("userInfo",userInfo) + if(userInfo.userType === 0){ + state.isAdmin = true; + state.form.companyName = '公开课' + state.form.companyId = null + }else { + state.isAdmin = false; + state.form.companyName = userInfo.companyName + state.form.companyId = userInfo.companyId + } + title.value = type === 'addFirst' || type === 'add' ? '新增' : type ==='edit' ? '编辑' : '' ; + if(type === 'edit') { + const res = await getCourseById(value.id); + if(res.code === 200){ + state.form = res.data + console.log("11",res.data) + if(res.data.logo) { + const obj = { + url: import.meta.env.VITE_APP_BASE_API + "/" + res.data.logo, + name: '' + } + state.imgList = [obj] + } + }else{ + ElMessage.warning(res.message) + } + startUsername.value = value.username; + }else if(type === 'add' && value ){ + state.form.parentId = value.id + } + dialogVisible.value = true; +} +const getClassifyList = async () => { + const res = await getClassification(); + if(res.code === 200){ + state.classifyList = recursion(res.data) + }else{ + ElMessage.warning(res.message) + } +} +const recursion = (data) => { + let tmp = [] + for (let i = 0; i < data.length; i++) { + let item = data[i] + // children为空 + if (item.children&& item.children.length==0) { + tmp.push({ + value: item.id, + label: item.name + }) + // 有children + } else { + tmp.push({ + value: item.id, + label: item.name, + children:recursion(item.children) + }) + } + } + return tmp; +} +const handleAvatarSuccess = (res, uploadFile) => { + if(res.code == 200){ + state.form.logo = res.data.path + }else{ + state.imgList = [] + ElMessage({ + type: 'warning', + message: '文件上传失败' + }) + } +} +const handleChange = ()=> { + console.log("label====",classifyRef.value.getCheckedNodes()[0].value) + state.form.categoryId = classifyRef.value.getCheckedNodes()[0].value + // 我这里只是打印了一下label的值哦,需要赋值的话自己去赋值哦 +} + + +const showTip =()=>{ + ElMessage({ + type: 'warning', + message: '超出文件上传数量' + }); +} +const picSize = async (rawFile) => { + if(rawFile.size / 1024 / 1024 > 5){ + ElMessage({ + type: 'warning', + message: '文件大小不能超过5M' + }); + return false + } +}; +const handleRemove = async (file, uploadFiles) => { + let path = state.form.logo; + await delPic({path: path}).then(res => { + if(res.code == 200){ + // ElMessage({ + // type: 'success', + // message: '文件已删除' + // }) + state.form.logo = '' + }else{ + ElMessage({ + type: 'warning', + message: res.message + }) + } + }).catch(() => { + state.form.imgUrl = '' + }); +} +const onSubmit = async () => { + const valid = await busRef.value.validate(); + if(valid){ + if(title.value === '新增'){ + const {id, ...data} = JSON.parse(JSON.stringify(state.form)) + const res = await addCourse(data) + if(res.code === 200){ + ElMessage({ + type: 'success', + message: '新增成功' + }); + }else{ + ElMessage.warning(res.message) + } + emit("getList") + busRef.value.clearValidate(); + reset(); + dialogVisible.value = false; + }else if(title.value === '编辑'){ + const {...data} = JSON.parse(JSON.stringify(state.form)) + const res = await editCourse(data) + if(res.code === 200){ + ElMessage({ + type: 'success', + message: '编辑成功' + }); + }else{ + ElMessage.warning(res.message) + } + emit("getList") + busRef.value.clearValidate(); + reset(); + dialogVisible.value = false; + } + } +} + +const handleClose = () => { + busRef.value.clearValidate(); + reset(); + dialogVisible.value = false; + emit("getList") + +} +const reset = () => { + state.form = { + id: '', + name: '', + categoryId: null, + period: null, + logo: '', + companyId: null + } +} +defineExpose({ + openDialog +}); + +</script> + +<style scoped lang="scss"> +.notice{ + :deep(.el-form .el-form-item__label) { + font-size: 15px; + } + .file { + display: flex; + flex-direction: column; + align-items: flex-start; + } +} +</style> diff --git a/src/views/onlineEducation/courseManage/courseChapters/components/chapterDialog.vue b/src/views/onlineEducation/courseManage/courseChapters/components/chapterDialog.vue new file mode 100644 index 0000000..3bc2552 --- /dev/null +++ b/src/views/onlineEducation/courseManage/courseChapters/components/chapterDialog.vue @@ -0,0 +1,249 @@ +<template> + <div class="notice"> + <el-dialog + v-model="dialogVisible" + :title="title" + width="550px" + :before-close="handleClose" + > + <el-form :model="state.form" size="default" ref="busRef" :rules="state.formRules" label-width="100px" > + <el-form-item label="章名称:" prop="name"> + <el-input v-model.trim="state.form.name" maxlength="100" show-word-limit :disabled="!state.isFirst"></el-input> + </el-form-item> + <el-form-item label="节名称:" prop="chapter.name" v-if="!state.isFirst"> + <el-input v-model.trim="state.chapter.name"></el-input> + </el-form-item> + <el-form-item label="资源:" v-if="!state.isFirst"> + <div style="display: flex;align-items: center; width: 100%;justify-content: space-between;"> + <el-input v-model.trim="state.chapter.resourceName" disabled style="width: 80%"></el-input> + <el-button type="primary" style="margin-left: 20px" plain size="default" @click="openResource">选择资源</el-button> + </div> + + </el-form-item> + + <el-form-item label="排序:" prop="sort" > + <el-input-number v-model="state.form.sort" /> + </el-form-item> + </el-form> + <template #footer> + <span class="dialog-footer"> + <el-button @click="handleClose" size="default">取 消</el-button> + <el-button type="primary" @click="onSubmit" size="default" v-preReClick>确认</el-button> + </span> + </template> + </el-dialog> + <courseDialog ref="courseRef" @choose-res="choosedResource" ></courseDialog> + </div> +</template> +<script setup> +import {reactive, ref, toRefs} from 'vue' +import Editor from "@/components/Editor/index.vue"; +import {ElMessage} from "element-plus"; +import {addNotice} from "@/api/backManage/notice"; +import {addDict, editDict, getDictDetail} from "@/api/backManage/evaluate"; +import {addCompany, checkName, distributeCompany, editCompany} from "@/api/onlineEducation/company"; +import {verifyPhone} from "@/utils/validate"; +import {addClassification, checkClassName, editClassification} from "@/api/onlineEducation/courseClass"; +import courseDialog from './chooseResource.vue' +import { + addChapter, + addChapterPeriod, + checkChapterName, + editChapter, + editChapterPeriod +} from "@/api/onlineEducation/chapters"; + +const dialogVisible = ref(false); +const title = ref(""); +const busRef = ref(); +const courseRef = ref() +const length = ref() +const emit = defineEmits(["getList"]); +const startUsername = ref(''); +const startUsernamePeriod = ref(''); + + +const validateName = (rule, value, callback)=>{ + if(value === ''){ + callback(new Error('请输入章名称')) + }else if(title.value === '编辑' && value === startUsername.value || title.value === '新增' && value === startUsername.value){ + callback() + }else{ + let param = {} + if(title.value === '新增') { + param = { + name:value, + courseId: state.form.courseId + } + }else if(title.value === '编辑'){ + param = { + name:value, + id: state.form.id, + courseId: state.form.courseId + } + } + checkChapterName(param).then((res)=>{ + if(res.data == false){ + callback(new Error('名称已被占用,请更换其他名称')) + }else{ + callback() + } + }) + } +} +const state = reactive({ + form: { + id: '', + name: '', + sort: 0, + courseId: null, + chapterPeriods: [] + }, + formRules:{ + name: [{ required: true, trigger: "blur", validator: validateName }], + }, + isFirst: true, + chapter: {} +}) + +const openDialog = async (type, value) => { + length.value = value.listLength + state.form.courseId = value.courseId + title.value = type === 'addFirst' || type === 'add' ? '新增' : type ==='edit' ? '编辑' : '' ; + if(type === 'edit') { + if(!value.chapterId){ + state.isFirst = true; + startUsername.value = value.name; + state.form = value; + state.form.sort = value.sort; + }else { + state.isFirst = false; + startUsernamePeriod.value = value.name; + state.chapter = value; + state.form.name = value.capterName; + startUsername.value = value.capterName; + state.chapter.resourceName = value.resource.name + } + + }else if(type === 'add' && value ){ + startUsername.value = value.name; + state.chapter.chapterId = value.id; + state.chapter.courseId = value.courseId; + state.form.name = value.name + state.isFirst = false; + }else { + state.isFirst = true; + } + dialogVisible.value = true; +} +const choosedResource = (val) => { + state.chapter.resourceName = val.name + state.chapter.resourceId = val.id +} + +const onSubmit = async () => { + const valid = await busRef.value.validate(); + if(valid){ + if(title.value === '新增'){ + if(state.chapter.chapterId){ + const {id, ...data} = JSON.parse(JSON.stringify(state.chapter)) + const res = await addChapterPeriod(data) + if(res.code === 200){ + ElMessage({ + type: 'success', + message: '新增成功' + }); + }else{ + ElMessage.warning(res.message) + } + }else{ + const {id, ...data} = JSON.parse(JSON.stringify(state.form)) + const res = await addChapter(data) + if(res.code === 200){ + ElMessage({ + type: 'success', + message: '新增成功' + }); + }else{ + ElMessage.warning(res.message) + } + } + + emit("getList") + busRef.value.clearValidate(); + reset(); + dialogVisible.value = false; + }else if(title.value === '编辑'){ + if(state.chapter.chapterId){ + const {...data} = JSON.parse(JSON.stringify(state.chapter)) + const res = await editChapterPeriod(data) + if(res.code === 200){ + ElMessage({ + type: 'success', + message: '编辑成功' + }); + }else{ + ElMessage.warning(res.message) + } + emit("getList") + busRef.value.clearValidate(); + reset(); + dialogVisible.value = false; + }else { + const {...data} = JSON.parse(JSON.stringify(state.form)) + const res = await editChapter(data) + if(res.code === 200){ + ElMessage({ + type: 'success', + message: '编辑成功' + }); + }else{ + ElMessage.warning(res.message) + } + emit("getList") + busRef.value.clearValidate(); + reset(); + dialogVisible.value = false; + } + + } + } +} + +const handleClose = () => { + busRef.value.clearValidate(); + reset(); + dialogVisible.value = false; + emit("getList") +} +const openResource = () => { + courseRef.value.openDialog() +} +const reset = () => { + state.form = { + id: '', + name: '', + sort: 0, + courseId: null, + chapterPeriods: [] + } + state.chapter={} +} +defineExpose({ + openDialog +}); + +</script> + +<style scoped lang="scss"> +.notice{ + :deep(.el-form .el-form-item__label) { + font-size: 15px; + } + .file { + display: flex; + flex-direction: column; + align-items: flex-start; + } +} +</style> diff --git a/src/views/onlineEducation/courseManage/courseChapters/components/chooseResource.vue b/src/views/onlineEducation/courseManage/courseChapters/components/chooseResource.vue new file mode 100644 index 0000000..b67ed2c --- /dev/null +++ b/src/views/onlineEducation/courseManage/courseChapters/components/chooseResource.vue @@ -0,0 +1,123 @@ +<template> + <div class="app-container"> + <el-dialog + v-model="dialogVisible" + title="选择资源" + width="600px" + :before-close="handleClose" + > + <div style="margin-bottom: 10px"> + <el-form> + <el-form-item label="资源名称"> + <el-input style="width: 20%" v-model="data.queryParams.name "></el-input> + <el-button type="primary" style="margin-left: 30px" @click="getList">查询</el-button> + <el-button plain @click="reset">重置</el-button> + </el-form-item> + + </el-form> + </div> + <!-- 表格数据 --> + <el-table v-loading="loading" :data="dataList" :border="true"> + <el-table-column label="序号" type="index" align="center" width="80" /> + <el-table-column label="资源名称" prop="name" align="center" /> + <el-table-column label="资源类型" prop="resourceType" align="center" > + <template #default="scope"> + <span>{{scope.row.resourceType == 1 ? '视频':scope.row.resourceType == 2 ? '音频':'文档'}}</span> + </template> + </el-table-column> + <el-table-column label="操作" align="center" class-name="small-padding fixed-width" > + <template #default="scope"> + <el-button link type="primary" @click="choose(scope.row)">选择</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + </el-dialog> + </div> +</template> + +<script setup> +import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue"; +import {ElMessage, ElMessageBox} from "element-plus"; +import {checkResourceName, delResource, getResource} from "@/api/onlineEducation/courseResource"; +import {checkName} from "@/api/onlineEducation/company"; +const { proxy } = getCurrentInstance(); +const loading = ref(false); +const emit = defineEmits(["chooseRes"]); +const dialogRef = ref(); +const data = reactive({ + queryParams: { + pageNum: 1, + pageSize: 10, + name: '' + }, + total: 0, + dataList: [] +}); + +const dialogVisible = ref(false); +const { queryParams, total, dataList } = toRefs(data); + +onMounted(()=>{ + +}) + +onUnmounted(()=>{ + +}) + +const openDialog = async () => { + await getList() + dialogVisible.value = true; +} + +const handleClose = () => { + dialogVisible.value = false; +} +const getList = async () => { + loading.value = true + const res = await getResource(data.queryParams) + if(res.code == 200){ + data.dataList = res.data.list.map(item => { + return{ + ...item, + sizeMB: Number((item.resourceSize /1024 /1024).toFixed(2))+'MB' + } + }) + console.log("ddd",data.dataList) + data.total = res.data.total + }else{ + ElMessage.warning(res.message) + } + loading.value = false +} + +const choose = (value) => { + console.log("co",value) + emit('chooseRes',value) + dialogVisible.value = false + // dialogRef.value.openDialog(type, value); +} +defineExpose({ + openDialog +}); + + +/** 重置新增的表单以及其他数据 */ +function reset() { + data.queryParams = { + pageNum: 1, + pageSize: 10, + name: '' + } + getList() +} + +</script> diff --git a/src/views/onlineEducation/courseManage/courseChapters/index.vue b/src/views/onlineEducation/courseManage/courseChapters/index.vue new file mode 100644 index 0000000..1ce31f6 --- /dev/null +++ b/src/views/onlineEducation/courseManage/courseChapters/index.vue @@ -0,0 +1,124 @@ +<template> + <div class="app-container"> + <div style="margin-bottom: 10px"> + <el-button type="success" plain @click="openDialog('addFirst',{courseId: data.courseId})">章添加</el-button> + + </div> + <!-- 表格数据 --> + <el-table v-loading="loading" :data="dataList" :border="true" row-key="id" :tree-props="{ children: 'chapterPeriods' }"> + <el-table-column label="序号" type="index" align="center" width="80" /> + <el-table-column label="章节名称" > + <template #default="scope"> + <span>{{scope.row.name}}</span> + </template> + </el-table-column> + <el-table-column label="排序" prop="sort" align="center" width="80" /> + <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="250" > + <template #default="scope"> + <el-button type="success" plain @click="openDialog('add',scope.row)" v-if="!scope.row.chapterId">节添加</el-button> + <el-button type="primary" plain @click="openDialog('edit',scope.row)">编辑</el-button> + <el-button type="danger" plain @click="handleDelete(scope.row)">删除</el-button> + </template> + </el-table-column> + </el-table> + <chapters-dialog ref="areaRef" @getList="getList"></chapters-dialog> + </div> +</template> + +<script setup> +import {getCurrentInstance, onMounted, reactive, ref, toRefs} from "vue"; +import {ElMessage, ElMessageBox} from "element-plus"; +import chaptersDialog from "./components/chapterDialog.vue" +import {delArea, getArea} from "@/api/backManage/area"; +import {getDictList} from "@/api/backManage/evaluate"; +import {delMonitor} from "@/api/sysUsers"; +import {useRoute} from 'vue-router' +import {delClassification, getClassification} from "@/api/onlineEducation/courseClass"; +import {delChapter, delPeriod, getChapters} from "@/api/onlineEducation/chapters"; +const { proxy } = getCurrentInstance(); +const route = useRoute() +const loading = ref(false); +const areaRef = ref(); +const cityList = ref([]) +const data = reactive({ + queryParams: { + name: '', + }, + total: 0, + dataList: [ + ], + courseId: '' +}); + +const { queryParams, total, dataList } = toRefs(data); + +//页面加载 +onMounted(() => { + + data.courseId = route.query.courseId + console.log("rou",data.courseId) + getList(); +}); +const getList = async () => { + loading.value = true; + const param = { + courseId: data.courseId + } + const res = await getChapters(param); + if(res.code === 200){ + dataList.value = res.data + }else{ + ElMessage.warning(res.message) + } + loading.value = false; + console.log('dataList.value',dataList.value) +} + +const openDialog = (type, value) => { + dataList.value.forEach(item => { + if(item.id == value.chapterId){ + value.capterName = item.name + } + }) + areaRef.value.openDialog(type, value); +} + +/** 重置新增的表单以及其他数据 */ +function reset() { + data.queryParams.name = ''; + data.queryParams.pageNum = 1; + getList(); +} +const handleDelete = (val) => { + ElMessageBox.confirm( + '确定删除此条数据?', + '提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }) + .then( async() => { + if(!val.chapterId){ + const res = await delChapter(val.id) + if(res.code == 200){ + ElMessage.success('数据删除成功') + await getList() + }else{ + ElMessage.warning(res.message) + } + }else { + const res = await delPeriod(val.id) + if(res.code == 200){ + ElMessage.success('数据删除成功') + await getList() + }else{ + ElMessage.warning(res.message) + } + } + + }) +} + + +</script> diff --git a/src/views/onlineEducation/courseManage/courseResource/componets/resourceDialog.vue b/src/views/onlineEducation/courseManage/courseResource/componets/resourceDialog.vue new file mode 100644 index 0000000..d1cbdd4 --- /dev/null +++ b/src/views/onlineEducation/courseManage/courseResource/componets/resourceDialog.vue @@ -0,0 +1,345 @@ +<template> + <div class="notice"> + <el-dialog + v-model="dialogVisible" + :title="title" + width="550px" + :before-close="handleClose" + > + <el-form :model="state.form" size="default" ref="busRef" :rules="state.formRules" label-width="130px" > + <el-form-item label="资源名称:" prop="name" > + <el-input v-model.trim="state.form.name" placeholder="请输入资源名称"></el-input> + </el-form-item> + <el-form-item label="资源类型:" prop="resourceType" > + <el-select + @change="changeType" + v-model="state.form.resourceType" + placeholder="请选择资源类型" + size="default" + style="width: 100%" + + > + <el-option + v-for="item in state.typeList" + :key="item.id" + :label="item.name" + :value="item.id" + /> + </el-select> + </el-form-item> + <el-form-item label="资源时长(秒):" prop="resourceLength" v-if="(state.form.resourceType == 1 || state.form.resourceType == 2)&& state.form.resourceLength"> + <el-input + :disabled="state.form.resourceLength!=0" + v-model="state.form.resourceLength" + style="width: 100%" + placeholder="请输入资源时长(秒)" + > + <template #append>秒</template> + </el-input> + </el-form-item> + <el-form-item label="文档页数:" prop="docPage" v-if="state.form.resourceType == 3 && state.form.docPage "> + <el-input + :disabled="state.form.docPage!=0" + v-model="state.form.docPage" + style="width: 100%" + placeholder="请输入文档页数:" + > + <template #append>页</template> + </el-input> + </el-form-item> + + <el-form-item label="资源上传:"> + <file-upload ref="fileRef" :responseType="state.form.resourceType" @getFile="getFile"></file-upload> + </el-form-item> + + </el-form> + <template #footer> + <span class="dialog-footer"> + <el-button @click="handleClose" size="default">取 消</el-button> + <el-button type="primary" @click="onSubmit" size="default" v-preReClick>确认</el-button> + </span> + </template> + </el-dialog> + </div> +</template> +<script setup> +import {nextTick, reactive, ref, toRefs} from 'vue' +import Editor from "@/components/Editor/index.vue"; +import {ElMessage} from "element-plus"; +import {addNotice} from "@/api/backManage/notice"; +import {addDict, editDict, getDictDetail} from "@/api/backManage/evaluate"; +import {addCompany, checkName, distributeCompany, editCompany} from "@/api/onlineEducation/company"; +import {verifyPhone} from "@/utils/validate"; +import {addBanner, delPic, editBanner, getBannerById} from "@/api/onlineEducation/banner"; +import {getToken} from "@/utils/auth"; +import {getUserById} from "@/api/onlineEducation/user"; +import {addResource, checkResourceName, editResource, getResourceById} from "@/api/onlineEducation/courseResource"; +import fileUpload from '@/views/components/upload.vue' +import Cookies from "js-cookie"; + +const dialogVisible = ref(false); +const title = ref(""); +const fileRef = ref(); +const busRef = ref(); +const length = ref() +const emit = defineEmits(["getList"]); +const startUsername = ref(''); +const startPhone = ref(''); + +const validateName = (rule, value, callback)=>{ + if(value === ''){ + callback(new Error('请输入资源名称')) + }else if(title.value === '编辑' && value === startUsername.value){ + callback() + }else{ + let param = {} + if(title.value === '新增') { + param = { + name:value, + } + }else if(title.value === '编辑'){ + param = { + name:value, + id: state.form.id + } + } + checkResourceName(param).then((res)=>{ + if(res.data == false){ + callback(new Error('资源名称已被占用,请更换其他名称')) + }else{ + callback() + } + }) + } +} + +const state = reactive({ + form: { + id: '', + name: '', + mediaType: '', + resourceType: null, + companyId: null, + resourcePath:'', + md5: '', + resourceSize: null, + resourceLength: '', + docPage: null, + originName: '' + }, + formRules:{ + name: [{ required: true, trigger: "blur", validator: validateName }], + resourceType:[{ required: true, message: '请选择资源类型', trigger: 'blur' }], + resourceLength: [{ required: true, trigger: "blur", message: '请输入资源时长(秒)' }], + }, + uploadUrl: import.meta.env.VITE_APP_BASE_API + '/system/common/uploadFile', + header: { + Authorization:getToken() + }, + imgLimit: 1, + imgList: [], + typeList: [ + { + id: 1, + name: '视频' + }, + { + id: 2, + name: '音频' + }, + { + id: 3, + name: '文档' + }, + ] +}) +const handleChange = (file, fileLists) => { + state.form.file = file.raw; + console.log(file); + // 这里把数组置空是为了每次只能上传一个文件,以防报错 + state.imgList = []; + state.form.mediaType = file.raw.type +} +const handleAvatarSuccess = (response, file, fileList) => { + if(response.code == 200){ + if(file && file.raw){ + state.form.mediaType = file.raw.type + } + state.form.file = file + }else{ + state.imgList = [] + ElMessage({ + type: 'warning', + message: '文件上传失败' + }) + } +} +const showTip =()=>{ + ElMessage({ + type: 'warning', + message: '超出文件上传数量' + }); +} +const picSize = async (rawFile) => { + if(rawFile.size / 1024 / 1024 > 5){ + ElMessage({ + type: 'warning', + message: '文件大小不能超过5M' + }); + return false + } +}; +const handleRemove = async (file, uploadFiles) => { + let path = state.form.imgUrl; + await delPic({path: path}).then(res => { + if(res.code == 200){ + // ElMessage({ + // type: 'success', + // message: '文件已删除' + // }) + state.form.imgUrl = '' + }else{ + ElMessage({ + type: 'warning', + message: res.message + }) + } + }).catch(() => { + state.form.imgUrl = '' + }); +} + +const openDialog = async (type, value) => { + length.value = value.listLength + title.value = type === 'add' ? '新增' : type ==='edit' ? '编辑' : '' ; + if(type === 'edit') { + const res = await getResourceById(value.id); + if(res.code === 200){ + state.form = res.data + }else{ + ElMessage.warning(res.message) + } + } + dialogVisible.value = true; + if(type === 'edit') { + await nextTick(() => { + fileRef.value.open(state.form); + }) + } +} +const getFile = (val) => { + + if(val.md5 != ''){ + state.form.md5 = val.md5 + state.form.resourcePath = val.resourcePath + state.form.mediaType = val.mediaType + state.form.resourceSize = val.resourceSize + state.form.docPage = val.docPage + state.form.resourceLength = val.resourceLength + state.form.originName = val.originName + } else if( val.md5 == ''){ + state.form.resourceType = '' + + } else { + ElMessage({ + type: 'warning', + message: '请上传题库资源' + }); + } +} + +const changeType = (val) => { + state.form.md5 = '' + state.form.resourcePath = '' + state.form.mediaType ='' + state.form.resourceSize = null + state.form.docPage = null + state.form.resourceLength = null + state.form.originName = '' + fileRef.value.changeType(val); +} +const onSubmit = async () => { + const valid = await busRef.value.validate(); + if(valid){ + if(title.value === '新增'){ + const {id,...data} = JSON.parse(JSON.stringify(state.form)) + const res = await addResource(data) + if(res.code === 200){ + ElMessage({ + type: 'success', + message: '新增成功' + }); + }else{ + ElMessage.warning(res.message) + } + dialogVisible.value = false; + emit("getList") + busRef.value.clearValidate(); + fileRef.value.dispose(); + reset(); + + + }else if(title.value === '编辑'){ + const {...data} = JSON.parse(JSON.stringify(state.form)) + const res = await editResource(data) + if(res.code === 200){ + ElMessage({ + type: 'success', + message: '编辑成功' + }); + }else{ + ElMessage.warning(res.message) + } + dialogVisible.value = false; + emit("getList") + busRef.value.clearValidate(); + fileRef.value.dispose(); + reset(); + + + } + } +} + +const handleClose = () => { + dialogVisible.value = false; + fileRef.value.dispose(); + busRef.value.clearValidate(); + reset(); + + emit("getList") + +} +const reset = () => { + state.form = { + id: '', + name: '', + mediaType: '', + resourceType: null, + companyId: null, + resourcePath:'', + md5: '', + resourceSize: null, + resourceLength: '', + docPage: null, + originName: '' + } +} +defineExpose({ + openDialog +}); + +</script> + +<style scoped lang="scss"> +.notice{ + :deep(.el-form .el-form-item__label) { + font-size: 15px; + } + .file { + display: flex; + flex-direction: column; + align-items: flex-start; + } +} +</style> diff --git a/src/views/onlineEducation/courseManage/courseResource/index.vue b/src/views/onlineEducation/courseManage/courseResource/index.vue new file mode 100644 index 0000000..e0b3274 --- /dev/null +++ b/src/views/onlineEducation/courseManage/courseResource/index.vue @@ -0,0 +1,115 @@ +<template> + <div class="app-container"> + <div style="margin-bottom: 10px"> + <el-button + type="primary" + plain + icon="Plus" + @click="openDialog('add',{})" + >新增</el-button> + </div> + <!-- 表格数据 --> + <el-table v-loading="loading" :data="dataList" :border="true"> + <el-table-column label="序号" type="index" align="center" width="80" /> + <el-table-column label="资源名称" prop="name" align="center" /> + <el-table-column label="资源大小" prop="sizeMB" align="center" > + </el-table-column> + <el-table-column label="资源类型" prop="resourceType" align="center" > + <template #default="scope"> + <span>{{scope.row.resourceType == 1 ? '视频':scope.row.resourceType == 2 ? '音频':'文档'}}</span> + </template> + </el-table-column> + <el-table-column label="操作" align="center" class-name="small-padding fixed-width" > + <template #default="scope"> + <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button> + <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> +<resource-dialog ref="dialogRef" @getList="getList"></resource-dialog> + </div> +</template> + +<script setup> +import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue"; +import {ElMessage, ElMessageBox} from "element-plus"; +import resourceDialog from './componets/resourceDialog.vue' +import {checkResourceName, delResource, getResource} from "@/api/onlineEducation/courseResource"; +import {checkName} from "@/api/onlineEducation/company"; +const { proxy } = getCurrentInstance(); +const loading = ref(false); +const dialogRef = ref(); +const data = reactive({ + queryParams: { + pageNum: 1, + pageSize: 10, + }, + total: 0, + dataList: [] +}); + +const { queryParams, total, dataList } = toRefs(data); + +onMounted(()=>{ + getList() +}) + +onUnmounted(()=>{ + +}) + +const getList = async () => { + loading.value = true + const res = await getResource(data.queryParams) + if(res.code == 200){ + data.dataList = res.data.list.map(item => { + return{ + ...item, + sizeMB: Number((item.resourceSize /1024 /1024).toFixed(2))+'MB' + } + }) + console.log("ddd",data.dataList) + data.total = res.data.total + }else{ + ElMessage.warning(res.message) + } + loading.value = false +} + +const openDialog = (type, value) => { + dialogRef.value.openDialog(type, value); +} + +/** 重置新增的表单以及其他数据 */ +function reset() { + proxy.resetForm("roleRef"); +} +const handleDelete = (val) => { + ElMessageBox.confirm( + '确定删除此条数据?', + '提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }) + .then( async() => { + const res = await delResource(val.id) + if(res.code == 200){ + ElMessage.success('数据删除成功') + await getList() + }else{ + ElMessage.warning(res.message) + } + }) +} + +</script> diff --git a/src/views/onlineEducation/courseManage/index.vue b/src/views/onlineEducation/courseManage/index.vue index 14b32de..08dc044 100644 --- a/src/views/onlineEducation/courseManage/index.vue +++ b/src/views/onlineEducation/courseManage/index.vue @@ -1,12 +1,263 @@ <template> -<div>课程管理</div> + <div class="app-container"> + <div style="margin-bottom: 10px"> + <el-button + type="primary" + plain + icon="Plus" + @click="openDialog('add',{})" + >新增</el-button> + </div> + <!-- 表格数据 --> + <el-table v-loading="loading" :data="dataList" :border="true"> + <el-table-column label="序号" type="index" align="center" width="80" /> + <el-table-column label="封面" prop="logo" align="center" > + <template #default="scope"> + <div class="demo-image__preview" v-if="scope.row.logo && scope.row.logo.length>0"> + <el-image + style="width: 100px; height: 100px" + :src= "scope.row.logo[0]" + :zoom-rate="1.2" + :max-scale="7" + :min-scale="0.2" + :preview-src-list="scope.row.logo" + :initial-index="0" + fit="cover" + :preview-teleported=true + /> + </div> + </template> + </el-table-column> + <el-table-column label="课程名称" prop="name" align="center" /> + <el-table-column label="课程分类" prop="categoryName" align="center" /> + <el-table-column label="要求课时" prop="period" align="center" /> + <el-table-column label="提交单位" prop="companyName" align="center" /> + <el-table-column label="审核状态" prop="state" align="center" > + <template #default="scope"> + <span>{{scope.row.state == 1?'待审核':scope.row.state == 2?'审批通过':'审批不通过'}}</span> + </template> + </el-table-column> + <el-table-column label="创建时间" prop="createTime" align="center" width="180" /> + <el-table-column label="状态" prop="status" align="center" > + + <template #default="scope" v-if="data.isAdmin"> + <el-switch + v-if="scope.row.state == 2" + v-model="scope.row.status" + :active-value="0" + :inactive-value="1" + inline-prompt + active-text="正常" + inactive-text="停用" + @change="switchStatus($event,scope.row)" + /> + <span v-else>--</span> + </template> + <template #default="scope" v-else> + <span v-if="scope.row.state == 2">{{scope.row.status == 1? '停用' : '正常'}}</span> + <span v-else>--</span> + </template> + </el-table-column> + <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180"> + <template #default="scope"> + <div v-if="scope.row.state == 2"> + <div v-if="data.isAdmin"> + <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button> + <el-button link type="primary" @click="toChapters(scope.row)">章节</el-button> + <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> + </div> + <div v-else>--</div> + </div> + <div v-else-if="scope.row.state == 1" > + <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button> + <el-button link type="primary" v-if="data.isAdmin" @click="openApprove(scope.row)">审核</el-button> + <el-button link type="primary" @click="toChapters(scope.row)">章节</el-button> + <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> + </div> + <div v-else-if="scope.row.state == 3" > + <div v-if="data.isAdmin">--</div> + <div v-else> + <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button> + <el-button link type="primary" @click="submitApprove(scope.row)">提交审核</el-button> + <el-button link type="primary" @click="toChapters(scope.row)">章节</el-button> + <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> + </div> + </div> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + <course-manage-dialog ref="dialogRef" @getList=getList></course-manage-dialog> + <el-dialog v-model="data.appDialog" title="审批课程" width="30%" center align-center> + <el-radio-group v-model="data.appoveForm.state" style="width: 100%"> + <el-radio :label="2" size="large" border>通过</el-radio> + <el-radio :label="3" size="large" border>驳回</el-radio> + </el-radio-group> + <template #footer> + <span class="dialog-footer"> + <el-button @click="data.appDialog = false">取消</el-button> + <el-button type="primary" @click="confirmApproval">确认</el-button> + </span> + </template> + </el-dialog> + </div> </template> + <script setup> +import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue"; +import {ElMessage, ElMessageBox} from "element-plus"; +import {delCompany, getCompany} from "@/api/onlineEducation/company"; +import courseManageDialog from './components/courseManageDialog.vue' +import {delBanner, getBanner} from "@/api/onlineEducation/banner"; +import {useRouter} from 'vue-router' +import Cookies from "js-cookie"; +import {changeCourseStatus, delCourse, doCourse, getCourse} from "@/api/onlineEducation/courseManage"; +const { proxy } = getCurrentInstance(); +const router = useRouter() +const loading = ref(false); +const dialogRef = ref(); +const data = reactive({ + queryParams: { + pageNum: 1, + pageSize: 10, + }, + total: 0, + dataList: [], + isAdmin: false, + appDialog: false, + appoveForm: { + id: null, + state: null + }, +}); + +const { queryParams, total, dataList } = toRefs(data); + +onMounted(async ()=>{ + const userInfo = JSON.parse(Cookies.get('userInfo')) + console.log("userInfo",userInfo) + if(userInfo.userType === 0){ + data.isAdmin = true; + }else { + data.isAdmin = false; + } + await getList() +}) + +onUnmounted(()=>{ + +}) + +const getList = async () => { + loading.value = true + const res = await getCourse(data.queryParams) + if(res.code == 200){ + data.dataList = res.data.list.map(item => { + return { + ...item, + logo: item.logo ?[import.meta.env.VITE_APP_BASE_API + "/" + item.logo] : [], + } + }) + console.log("ddd",data.dataList) + data.total = res.data.total + }else{ + ElMessage.warning(res.message) + } + loading.value = false +} + +const openDialog = (type, value) => { + dialogRef.value.openDialog(type, value); +} + +/** 重置新增的表单以及其他数据 */ +function reset() { + proxy.resetForm("roleRef"); +} +const handleDelete = (val) => { + ElMessageBox.confirm( + '确定删除此条数据?', + '提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }) + .then( async() => { + const res = await delCourse(val.id) + if(res.code == 200){ + ElMessage.success('数据删除成功') + await getList() + }else{ + ElMessage.warning(res.message) + } + }) +} +const switchStatus = (e,val) => { + ElMessageBox.confirm( + '确定修改该课程当前状态?', + '提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }) + .then( async() => { + const res = await changeCourseStatus({id: val.id,status: e}) + if(res.code == 200){ + ElMessage.success('状态修改成功') + await getList() + }else{ + ElMessage.warning(res.message) + } + }) + .catch(() => { + getList() + }) + +} +const openApprove = (val) => { + data.appoveForm = { + id: null, + state: null + } + data.appoveForm.id = val.id + data.appDialog = true +} +const confirmApproval = async () =>{ + if(data.appoveForm.state !== null){ + const res = await doCourse(data.appoveForm) + if(res.code == 200){ + ElMessage.success('审批成功') + await getList() + data.appDialog = false + }else{ + ElMessage.warning(res.message) + } + } +} +const submitApprove = async (val) => { + const param = { + id: val.id, + state: 1 + } + const res = await doCourse(param) + if(res.code == 200){ + ElMessage.success('提交成功') + await getList() + }else{ + ElMessage.warning(res.message) + } +} +const toChapters = (val) => { + router.push({ path: "/chapters", query: { courseId: val.id } }); +} </script> - - - -<style scoped lang="scss"> - -</style> diff --git a/src/views/onlineEducation/people/components/stuDialog.vue b/src/views/onlineEducation/people/components/stuDialog.vue new file mode 100644 index 0000000..3b92dd1 --- /dev/null +++ b/src/views/onlineEducation/people/components/stuDialog.vue @@ -0,0 +1,311 @@ +<template> + <div class="notice"> + <el-dialog + v-model="dialogVisible" + :title="state.title" + width="550px" + :before-close="handleClose" + > + <el-form :model="state.form" size="default" ref="superRef" :rules="state.formRules" label-width="180px" > + <el-form-item label="企业:" prop="companyName" v-if="state.title !== '修改密码'"> + <el-input v-model.trim="state.form.companyName" disabled></el-input> + </el-form-item> + <el-form-item label="姓名:" prop="name" v-if="state.title !== '修改密码'"> + <el-input v-model.trim="state.form.name" :disabled="disabled" placeholder="请输入姓名" ></el-input> + </el-form-item> + <el-form-item label="性别:" prop="sex" v-if="state.title !== '修改密码'"> + <el-radio-group v-model="state.form.sex" :disabled="disabled"> + <el-radio :label="0">男</el-radio> + <el-radio :label="1">女</el-radio> + </el-radio-group> + </el-form-item> + <el-form-item label="身份证号:" prop="idNo" v-if="state.title !== '修改密码'"> + <el-input v-model.trim="state.form.idNo" :disabled="disabled" placeholder="请输入身份证号" ></el-input> + </el-form-item> + <el-form-item label="手机号(登录用户名):" prop="phone" v-if="state.title !== '修改密码'" > + <el-input v-model.trim="state.form.phone" :maxlength="11" :disabled="disabled" placeholder="请输入手机号"></el-input> + </el-form-item> + <el-form-item label="密码:" prop="password" v-if="state.title == '新增' || state.title == '修改密码'"> + <el-input v-model.trim="state.form.password" type="password" show-password placeholder="请输入密码"></el-input> + </el-form-item> + <el-form-item label="重复密码:" prop="confirmPassword" v-if="state.title == '新增' || state.title == '修改密码'"> + <el-input v-model.trim="state.form.confirmPassword" type="password" show-password placeholder="请输入确认密码"></el-input> + </el-form-item> + <el-form-item label="工号:" prop="empno" v-if="state.title !== '修改密码'"> + <el-input v-model.trim="state.form.empno" :disabled="disabled" placeholder="请输入工号" ></el-input> + </el-form-item> + <el-form-item label="岗位:" prop="post" v-if="state.title !== '修改密码'"> + <el-input v-model.trim="state.form.post" :disabled="disabled" placeholder="请输入岗位" ></el-input> + </el-form-item> + <el-form-item label="职务:" prop="duty" v-if="state.title !== '修改密码'"> + <el-input v-model.trim="state.form.duty" :disabled="disabled" placeholder="请输入职务" ></el-input> + </el-form-item> + </el-form> + <template #footer v-if="state.title !='查看'"> + <span class="dialog-footer"> + <el-button @click="handleClose" size="default">取 消</el-button> + <el-button type="primary" @click="onSubmit" size="default" v-preReClick>确认</el-button> + </span> + </template> + </el-dialog> + </div> +</template> +<script setup> +import {reactive, ref, toRefs, defineEmits, nextTick, onMounted} from 'vue' +import { View } from "@element-plus/icons-vue"; +import scorllSelect from '@/components/scrollSelect/index.vue' +import {ElMessage, ElMessageBox} from "element-plus"; +import {verifyPhone, verifyPwd, verifyUsername} from "@/utils/validate"; +import { checkUserName, checkPhone } from "@/api/login" +import {resetPwd} from "@/api/onlineEducation/student" +import {Base64} from "js-base64" +import Cookies from "js-cookie"; +import {addStudent, checkStuIdNo, checkStuPhone, editStudent} from "@/api/onlineEducation/student"; + +const emit = defineEmits(["getList"]); +const dialogVisible = ref(false) +const superRef = ref(null) +const scrollRef = ref(null) + + +const equalToPassword = (rule, value, callback) => { + if (state.form.password !== value) { + callback(new Error("两次输入的密码不一致")); + } else { + callback(); + } +}; + +const validateUserPhone = (rule, value, callback)=>{ + if(value === ''){ + callback(new Error('请输入手机号')) + }else if(state.title === '编辑' && value === startPhone.value){ + callback() + } else if(!verifyPhone(value)){ + callback(new Error('手机号格式有误')) + }else{ + let param = {} + if(state.title === '新增') { + param = { + phone:value + } + }else if(state.title === '编辑'){ + param = { + phone:value, + id: state.form.id + } + } + checkStuPhone(param).then((res)=>{ + if(res.data == false){ + callback(new Error('手机号已被占用,请更换其他手机号')) + }else{ + callback() + } + }) + } +} + +let validatePwd = (rule, value, callback)=>{ + if(value === ''){ + callback(new Error('请输入密码')) + }else{ + if(!verifyPwd(value)){ + callback(new Error('密码须包含字母、数字、特殊字符,长度在6-16之间')) + }else{ + callback() + } + } +} +let validateIdNo = (rule, value, callback)=>{ + if(value === ''){ + callback(new Error('请输入身份证号')) + }else if(state.title === '编辑' && value === startIdNo.value){ + callback() + }else{ + let param = {} + if(state.title === '新增') { + param = { + idNo:value + } + }else if(state.title === '编辑'){ + param = { + idNo:value, + id: state.form.id + } + } + checkStuIdNo(param).then((res)=>{ + if(res.data == false){ + callback(new Error('身份证号已被占用,请更换其他身份证号')) + // ElMessageBox.confirm( + // `该人员${state.form.name}(身份证号:${state.form.idNo})与${state.form.idNo}已经绑定,确定将该人员的责任归属变更到贵公企业?`, + // '提示', + // { + // confirmButtonText: '确认', + // cancelButtonText: '取消', + // type: 'warning', + // icon: '' + // } + // ) + // .then(() => { + // ElMessage({ + // type: 'success', + // message: 'Delete completed', + // }) + // }) + // .catch(() => { + // callback(new Error('身份证号已被占用,请更换其他身份证号')) + // }) + }else{ + callback() + } + }) + } +} + + +const state = reactive({ + title: '', + form: { + id: null, + name: '', + phone: '', + password: '', + confirmPassword: '', + sex: 0, + companyId: null, + empno: '', + post: '', + duty: '', + idNo: '' + + }, + formRules:{ + name: [{ required: true, message: '请输入公司、部门或者车间岗位名称', trigger: 'blur' }], + password: [{ required: true, validator: validatePwd, trigger: 'blur' }], + confirmPassword: [{ required: true, validator: equalToPassword, trigger: 'blur' }], + phone: [{ required: true, validator: validateUserPhone, trigger: 'blur' }], + idNo: [{ required: true, validator: validateIdNo, trigger: 'blur' }], + }, + isAdmin: false + +}) +const startPhone = ref(''); +const startIdNo = ref(''); +const UisMounted = ref(false); +onMounted(() => { + UisMounted.value = true; + +}); + +const disabled = ref(false); +const openDialog = async (type, value) => { + const userInfo = JSON.parse(Cookies.get('userInfo')) + console.log("userInfo",userInfo) + if(userInfo.userType === 0){ + state.isAdmin = true; + state.form.userType = 0; + }else { + state.isAdmin = false; + state.form.companyId = userInfo.companyId; + state.form.companyName = userInfo.companyName; + state.form.userType = 1; + } + state.title = type === 'add' ? '新增' : type ==='edit' ? '编辑' : type ==='pwd' ? '修改密码' : '查看' ; + if(type === 'edit' || type === 'view') { + if( type === 'view'){ + disabled.value = true; + } + startPhone.value = value.phone + startIdNo.value = value.idNo + state.form = value + state.form.companyName = value.company.name; + console.log("ba",state.form) + } + if(type == 'pwd'){ + state.form.id = value.id + } + dialogVisible.value = true +} +const onSubmit = async () => { + const valid = await superRef.value.validate(); + if(valid){ + if(state.title == '新增'){ + const {confirmPassword,id,...data} = state.form + data.password = Base64.encode(data.password) + const res = await addStudent(data) + if(res.code == 200){ + ElMessage.success(res.message) + emit('getList') + handleClose() + dialogVisible.value = false; + }else{ + ElMessage.warning(res.message) + } + }else if(state.title == '编辑'){ + const {id, name, phone, sex, companyId, empno, post, duty, idNo} = state.form + const data = {id, name, phone, sex, companyId, empno, post, duty, idNo} + const res = await editStudent(data) + if(res.code == 200){ + ElMessage.success(res.message) + emit('getList') + handleClose() + }else{ + ElMessage.warning(res.message) + } + }else{ + const {id,password} = state.form + const data = {id,password} + data.password = Base64.encode(data.password) + const res = await resetPwd(data) + if(res.code == 200){ + ElMessage.success(res.message) + emit('getList') + handleClose() + }else{ + ElMessage.warning(res.message) + } + } + } +} +const handleClose = () => { + state.form = { + id: null, + name: '', + phone: '', + password: '', + confirmPassword: '', + sex: 0, + companyId: null, + empno: '', + post: '', + duty: '', + idNo: '' + } + superRef.value.clearValidate(); + superRef.value.resetFields() + dialogVisible.value = false; +} + + + + + + +defineExpose({ + openDialog +}); + +</script> + +<style scoped lang="scss"> +.notice{ + :deep(.el-form .el-form-item__label) { + font-size: 15px; + } + .file { + display: flex; + flex-direction: column; + align-items: flex-start; + } +} +</style> diff --git a/src/views/onlineEducation/people/index.vue b/src/views/onlineEducation/people/index.vue index 4e19fe7..2141fa2 100644 --- a/src/views/onlineEducation/people/index.vue +++ b/src/views/onlineEducation/people/index.vue @@ -1,12 +1,138 @@ <template> -<div>人员管理</div> + <div class="app-container"> + <div style="margin-bottom: 10px"> + <el-button + type="primary" + plain + icon="Plus" + @click="openDialog('add',{})" + >新增</el-button> + </div> + <!-- 表格数据 --> + <el-table v-loading="loading" :data="dataList" :border="true"> + <el-table-column label="序号" type="index" align="center" width="80" /> + <el-table-column label="工号" prop="empno" align="center" /> + <el-table-column label="姓名" prop="name" align="center" /> + <el-table-column label="性别" prop="sex" align="center" > + <template #default="scope"> + <span>{{scope.row.sex == 0 ? '男':'女'}}</span> + </template> + </el-table-column> + <el-table-column label="手机号" prop="phone" align="center" width="130"/> + <el-table-column label="身份证" prop="idNo" align="center" width="200" :show-overflow-tooltip="true"/> + <el-table-column label="创建人" prop="createBy" align="center"/> + <el-table-column label="工作岗位" prop="post" align="center"/> + <el-table-column label="职务" prop="duty" align="center"/> + <el-table-column label="一人一档" prop="duty" align="center" width="120"> + <template #default="scope"> + <el-button link type="primary">培训考试记录</el-button> + </template> + </el-table-column> + <el-table-column label="操作" align="center" class-name="small-padding fixed-width" width="180"> + <template #default="scope"> + <el-button link type="primary" @click="openDialog('edit',scope.row)">编辑</el-button> + <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button> + <el-button link type="primary" @click="openDialog('pwd',scope.row)">修改密码</el-button> + </template> + </el-table-column> + </el-table> + + <pagination + v-show="total > 0" + :total="total" + v-model:page="queryParams.pageNum" + v-model:limit="queryParams.pageSize" + @pagination="getList" + /> + + <stu-dialog ref="dialogRef" @getList=getList></stu-dialog> + </div> </template> + <script setup> +import {getCurrentInstance, onMounted, onUnmounted, reactive, ref, toRefs} from "vue"; +import {ElMessage, ElMessageBox} from "element-plus"; +import {delCompany, getCompany} from "@/api/onlineEducation/company"; +import stuDialog from "./components/stuDialog.vue" +import {delUser, getUser} from "@/api/onlineEducation/user"; +import Cookies from "js-cookie"; +import {delStudent, getStudent} from "@/api/onlineEducation/student"; + + +const { proxy } = getCurrentInstance(); +const loading = ref(false); +const dialogRef = ref(); +const data = reactive({ + queryParams: { + pageNum: 1, + pageSize: 10, + }, + total: 0, + dataList: [], + isAdmin: false + +}); + +const { queryParams, total, dataList } = toRefs(data); + +onMounted(async ()=>{ + const userInfo = JSON.parse(Cookies.get('userInfo')) + console.log("userInfo",userInfo) + if(userInfo.userType === 0){ + data.isAdmin = true; + }else { + data.isAdmin = false; + } + await getList() +}) +onUnmounted(()=>{ + +}) + +const getList = async () => { + loading.value = true + const res = await getStudent(data.queryParams) + if(res.code == 200){ + data.dataList = res.data.list + data.total = res.data.total + }else{ + ElMessage.warning(res.message) + } + loading.value = false +} + +const openDialog = (type, value) => { + if(type == 'add' && data.isAdmin){ + ElMessage.warning('监管部门请联系企业创建企业学员') + }else{ + dialogRef.value.openDialog(type, value); + } + + +} + +/** 重置新增的表单以及其他数据 */ +function reset() { + proxy.resetForm("roleRef"); +} +const handleDelete = (val) => { + ElMessageBox.confirm( + '确定删除此条数据?', + '提示', + { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + }) + .then( async() => { + const res = await delStudent(val.id) + if(res.code == 200){ + ElMessage.success('数据删除成功') + await getList() + }else{ + ElMessage.warning(res.message) + } + }) +} </script> - - - -<style scoped lang="scss"> - -</style> diff --git a/src/views/onlineEducation/systemManage/banner/components/bannerDialog.vue b/src/views/onlineEducation/systemManage/banner/components/bannerDialog.vue index b95c4cf..bd7f546 100644 --- a/src/views/onlineEducation/systemManage/banner/components/bannerDialog.vue +++ b/src/views/onlineEducation/systemManage/banner/components/bannerDialog.vue @@ -11,7 +11,7 @@ <el-input v-model.trim="state.form.title"></el-input> </el-form-item> <el-form-item prop="imgUrl" label="图片:"> - <el-upload accept="image/*" :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.imgLimit' v-model:file-list="state.imgList" list-type="picture-card" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles,'证书')" > + <el-upload accept="image/*" :action="state.uploadUrl" :headers="state.header" method="post" :on-success="(res, uploadFile)=>handleAvatarSuccess(res, uploadFile)" :on-exceed="showTip" :limit='state.imgLimit' v-model:file-list="state.imgList" list-type="picture-card" :before-upload="picSize" :on-remove="(file, uploadFiles)=>handleRemove(file, uploadFiles)" > <el-icon><Plus /></el-icon> <template #tip> <div class="el-upload__tip">上传jpg/png图片尺寸小于5M,最多可上传1张</div> diff --git a/src/views/onlineEducation/systemManage/courseClassification/components/courseClassDialog.vue b/src/views/onlineEducation/systemManage/courseClassification/components/courseClassDialog.vue index 51901ac..7b29ada 100644 --- a/src/views/onlineEducation/systemManage/courseClassification/components/courseClassDialog.vue +++ b/src/views/onlineEducation/systemManage/courseClassification/components/courseClassDialog.vue @@ -7,6 +7,9 @@ :before-close="handleClose" > <el-form :model="state.form" size="default" ref="busRef" :rules="state.formRules" label-width="150px" > + <el-form-item label="上级分类:" prop="name" v-if="!state.isFirst"> + <el-input v-model.trim="state.form.parentName" disabled></el-input> + </el-form-item> <el-form-item label="名称:" prop="name"> <el-input v-model.trim="state.form.name"></el-input> </el-form-item> @@ -76,6 +79,7 @@ form: { id: '', name: '', + parentName: '', sort: 0, parentId: null, status: true @@ -83,8 +87,8 @@ formRules:{ name: [{ required: true, trigger: "blur", validator: validateName }], }, + isFirst: true }) - const openDialog = async (type, value) => { length.value = value.listLength @@ -95,7 +99,11 @@ state.form.sort = value.sort; startUsername.value = value.username; }else if(type === 'add' && value ){ - state.form.parentId = value.id + state.isFirst = false; + state.form.parentId = value.id; + state.form.parentName = value.name; + }else { + state.isFirst = true; } dialogVisible.value = true; } @@ -150,6 +158,7 @@ state.form = { id: '', name: '', + parentName: '', sort: 0, parentId: null, status: true diff --git a/src/views/onlineEducation/systemManage/courseClassification/index.vue b/src/views/onlineEducation/systemManage/courseClassification/index.vue index 3f76fc7..49436e4 100644 --- a/src/views/onlineEducation/systemManage/courseClassification/index.vue +++ b/src/views/onlineEducation/systemManage/courseClassification/index.vue @@ -52,8 +52,6 @@ const data = reactive({ queryParams: { name: '', - pageNum: 1, - pageSize: 10, }, total: 0, dataList: [ diff --git a/src/views/onlineEducation/systemManage/user/components/userDialog.vue b/src/views/onlineEducation/systemManage/user/components/userDialog.vue index 71dd1e2..7ef7401 100644 --- a/src/views/onlineEducation/systemManage/user/components/userDialog.vue +++ b/src/views/onlineEducation/systemManage/user/components/userDialog.vue @@ -57,6 +57,7 @@ </el-select> <el-input v-else disabled style="width: 45%" v-model="state.form.companyName"></el-input> <scorllSelect + :disabled="disabled" ref="scrollRef" v-if="UisMounted && (state.form.userType === 2 || state.form.userType === 3)" v-model="state.form.parentName" @@ -190,7 +191,9 @@ state.form.userType = 1; } - await getCompanyList('open') + if(type !== 'view' && type !== 'pwd'){ + await getCompanyList('open') + } state.title = type === 'add' ? '新增' : type ==='edit' ? '编辑' : type ==='pwd' ? '修改密码' : '查看' ; if(type === 'edit' || type === 'view') { if( type === 'view'){ -- Gitblit v1.9.2