From 40f5e10da7ec40dc62091e3cd4b2ae272f256d54 Mon Sep 17 00:00:00 2001 From: axlrose2333 <690927457@qq.com> Date: Sat, 18 Feb 2023 21:59:58 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8C=81=E7=BB=AD=E5=BC=80=E5=8F=91=E4=B9=A0?= =?UTF-8?q?=E9=A2=98=E7=AE=A1=E7=90=86=E6=A8=A1=E5=9D=97=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9Eckeditor5=E5=AF=8C=E6=96=87=E6=9C=AC=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 3 + src/api/exercises.js | 12 + src/components/richText/ckEditor4.vue | 180 + src/components/richText/ckEditor5.vue | 53 + .../richText/customUploadAdapter.js | 146 + src/main.js | 2 + src/utils/custom.js | 50 + src/view/course/addCourse/index.vue | 34 +- src/view/course/components/chapter.vue | 33 +- src/view/course/components/exercisesPool.vue | 103 + src/view/course/courseCategory/index.vue | 2 +- src/view/exercisesManage/index.vue | 332 + yarn-error.log | 9497 +++++++++++++++++ 13 files changed, 10423 insertions(+), 24 deletions(-) create mode 100644 src/api/exercises.js create mode 100644 src/components/richText/ckEditor4.vue create mode 100644 src/components/richText/ckEditor5.vue create mode 100644 src/components/richText/customUploadAdapter.js create mode 100644 src/view/course/components/exercisesPool.vue create mode 100644 src/view/exercisesManage/index.vue create mode 100644 yarn-error.log diff --git a/package.json b/package.json index d33434d..0e90494 100644 --- a/package.json +++ b/package.json @@ -10,9 +10,12 @@ "fix-memory-limit": "cross-env LIMIT=4096 increase-memory-limit" }, "dependencies": { + "@ckeditor/ckeditor5-build-classic": "^36.0.1", + "@ckeditor/ckeditor5-vue": "^4.0.1", "@element-plus/icons-vue": "^0.2.7", "ali-oss": "^6.17.1", "axios": "^0.19.2", + "axl-editor": "^1.0.1", "core-js": "^3.6.5", "echarts": "5.3.2", "element-plus": "2.2.9", diff --git a/src/api/exercises.js b/src/api/exercises.js new file mode 100644 index 0000000..8d940e2 --- /dev/null +++ b/src/api/exercises.js @@ -0,0 +1,12 @@ +import service from '@/utils/request' +const api = { + // 课程api + getExercisesList : data => { + return service({ + url: '/question', + method: 'get', + params:data + }) + } +} +export default api diff --git a/src/components/richText/ckEditor4.vue b/src/components/richText/ckEditor4.vue new file mode 100644 index 0000000..49b6f32 --- /dev/null +++ b/src/components/richText/ckEditor4.vue @@ -0,0 +1,180 @@ + + + + + + diff --git a/src/components/richText/ckEditor5.vue b/src/components/richText/ckEditor5.vue new file mode 100644 index 0000000..73f5cfd --- /dev/null +++ b/src/components/richText/ckEditor5.vue @@ -0,0 +1,53 @@ + + + diff --git a/src/components/richText/customUploadAdapter.js b/src/components/richText/customUploadAdapter.js new file mode 100644 index 0000000..4dcc110 --- /dev/null +++ b/src/components/richText/customUploadAdapter.js @@ -0,0 +1,146 @@ +/** + * 图片压缩处理,转换为等比的高800px的图像 + * @params file File类型的图片文件 + * @return Promise 返回一个promise,值为一个压缩后的图片文件 + */ +import { getToken } from '@/utils/auth' +function imgCutdown(file) { + return new Promise((resolve) => { + const render = new FileReader(); + render.onload = function(progress) { + const target = progress.target; + if (!target) return; + + const reuslt = target.result; + if (typeof reuslt === "string") { + const image = new Image(); + image.src = reuslt; + image.onload = function() { + const h = 800; + const rate = h / image.height; + const canvas = document.createElement("canvas"); + const context = canvas.getContext("2d"); + if (!context) return; + canvas.width = image.width * rate; + canvas.height = h; + context.drawImage( + image, + 0, + 0, + image.width, + image.height, + 0, + 0, + canvas.width, + canvas.height + ); + canvas.toBlob( + function(b) { + const file = new File([b], "pic", { + type: "image/jpeg", + }); + resolve(file); + }, + "image/jpeg", + 0.5 + ); + }; + } + }; + render.readAsDataURL(file); + }); +} + +// 上传图片的地址,我这里使用的是vue的环境变量,也可以直接写死 +// const uploadUrl = `${process.env.VUE_APP_HOST}image/upload.do`; +// const uploadUrl = `https://192.168.1.23:8000/api/v1/public/uploadFile` +const uploadUrl = `https://apiwx.twzxjy.com/api/v1/public/uploadFile` + +// 自定义适配器类 +class MyUploadAdapter { + constructor(loader) { + this.loader = loader + } + + upload() { + return this.loader.file.then( + (file) => + new Promise((resolve, reject) => { + this._initRequest(); + this._initListeners(resolve, reject, file); + this._sendRequest(file); + }) + ) + } + + abort() { + if (this.xhr) { + this.xhr.abort(); + } + } + + _initRequest() { + const xhr = (this.xhr = new XMLHttpRequest()) + + xhr.open('POST', uploadUrl, true) + xhr.setRequestHeader('Authorization', 'Bearer ' + getToken()) + xhr.responseType = 'json' + } + + _initListeners(resolve, reject, file) { + const xhr = this.xhr + const loader = this.loader + const genericErrorText = `Couldn't upload file: ${file.name}.` + + xhr.addEventListener('error', () => reject(genericErrorText)) + xhr.addEventListener('abort', () => reject()) + xhr.addEventListener('load', () => { + const response = xhr.response + + if (!response || response.error) { + return reject( + response && response.error ? response.error.message : genericErrorText + ) + } + console.log(response) + + resolve({ + default: response.data.path, + }) + }) + + if (xhr.upload) { + xhr.upload.addEventListener('progress', (evt) => { + if (evt.lengthComputable) { + loader.uploadTotal = evt.total + loader.uploaded = evt.loaded + } + }) + } + } + + async _sendRequest(file) { + const data = new FormData(); + // 判断如果上传图片大于1M,则进行压缩处理 + // if (file.size > 1000 * 1024) { + // file = await imgCutdown(file); + // } + + // 上传参数就根据后端的处理而设置了 + // data.append("file", file); + // data.append("name", file.name); + // data.append("group", "pic"); + data.append( 'file', file ); + this.xhr.send(data); + } +} + +function MyCustomUploadAdapterPlugin(editor) { + editor.plugins.get("FileRepository").createUploadAdapter = (loader) => { + return new MyUploadAdapter(loader); + }; +} +export { + MyUploadAdapter, + MyCustomUploadAdapterPlugin +} diff --git a/src/main.js b/src/main.js index a4a02a8..6e83ef7 100644 --- a/src/main.js +++ b/src/main.js @@ -13,6 +13,7 @@ import auth from '@/directive/auth' import { store } from '@/pinia' import App from './App.vue' import { initDom } from './utils/positionToCode' +import CKEditor from '@ckeditor/ckeditor5-vue' initDom() /** * @description 导入加载进度条,防止首屏加载时间过长,用户等待 @@ -31,6 +32,7 @@ const app = createApp(App) app.config.productionTip = false app + .use(CKEditor) .use(run) .use(store) .use(auth) diff --git a/src/utils/custom.js b/src/utils/custom.js index 019e1d0..9743a23 100644 --- a/src/utils/custom.js +++ b/src/utils/custom.js @@ -11,6 +11,56 @@ const custom = { } } return arr + }, + getExercisesTypeList() { + return [ + { + label: '单选题', + value2:'single', + value:1 + }, + { + label: '多选题', + value2:'mutil', + value:2 + }, + { + label: '判断题', + value2:'judge', + value:3 + }, + { + label: '填空题', + value2:'application', + value:4 + }, + { + label: '选填题', + value2:'application', + value:5 + } + ] + }, + getExercisesTypeName(type) { + let name = '' + switch (type){ + case 1: + name = '单选题'; + break; + case 2: + name = '多选题'; + break; + case 3: + name = '判断题'; + break; + case 4: + name = '填空题'; + break; + case 5: + name = '选填题'; + break; + } + return name } } export default custom diff --git a/src/view/course/addCourse/index.vue b/src/view/course/addCourse/index.vue index 11d698c..6fe0592 100644 --- a/src/view/course/addCourse/index.vue +++ b/src/view/course/addCourse/index.vue @@ -1,5 +1,6 @@