From 00c6c38846f94687cfc4ce6d3c0506f8349a26e5 Mon Sep 17 00:00:00 2001
From: zhouwx <1175765986@qq.com>
Date: Thu, 04 Jul 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