Merge branch 'master' of http://182.92.203.7:2001/r/testbookLayout
| | |
| | | VUE_APP_ENV = 'product' |
| | | VUE_APP_API_URL = "https://jsek.bnuic.com" |
| | | VUE_APP_RESOURCE_CTX = 'https://jsek.bnuic.com/books/resource/' |
| | | VUE_APP_PUBLIC_PATH = 'https://jsek.bnuic.com/books/book/mathBook' |
| | | VUE_APP_PUBLIC_PATH = 'https://jsek.bnuic.com/books/book/sportsAndHealth' |
| | | VUE_APP_BOOK_LIST = "childHealth/lifeCare/sportsAndHealth/embedded/english/artAndDance/artAndDrama/mathBook" |
| | | VUE_APP_BOOK_ID = 'mathBook' |
| | | VUE_APP_BOOK_ID = 'sportsAndHealth' |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <div class="page-box padding-116" page="10"> |
| | | <!-- <drag :question="dragQuestion" :page="10"/> --> |
| | | <graffiti style="width:100%" /> |
| | | </div> |
| | | <!-- 函数控件弹窗 --> |
| | | <el-dialog |
| | | :visible.sync="dialogVisible" |
| | |
| | | import examinations from "@/components/examinations/index.vue"; |
| | | import { getResourcePath } from "@/assets/methods/resources"; |
| | | import { getCollectResource,setCollectResource } from "@/assets/methods/resources"; |
| | | import drag from '@/components/dragQuestion/index.vue' |
| | | import graffiti from "@/components/graffiti/index.vue" |
| | | export default { |
| | | name: "chapter-one", |
| | | components: { examinations }, |
| | | components: { examinations,drag,graffiti }, |
| | | props: { |
| | | showPageList: { |
| | | type: Array, |
| | |
| | | isShow: false, |
| | | }, |
| | | ], |
| | | dragQuestion:[ |
| | | { |
| | | analysisCon: null, |
| | | answer: ['A','B','C'], |
| | | difficulty: 0, |
| | | id: "7BC7B760", |
| | | isCollect: false, |
| | | isComplete: false, |
| | | isRight: null, |
| | | isUnfold: "", |
| | | isUserAnswer: false, |
| | | number: 1, |
| | | option: [ |
| | | { |
| | | img: "", |
| | | index: "010311", |
| | | txt: "胆小的", |
| | | value: "A", |
| | | isShow:true |
| | | }, |
| | | { |
| | | img: "", |
| | | index: "010312", |
| | | txt: "善良的", |
| | | value: "B", |
| | | isShow:true |
| | | }, |
| | | { |
| | | img: "", |
| | | index: "010313", |
| | | txt: "沉稳的", |
| | | value: "C", |
| | | isShow:true |
| | | }, |
| | | ], |
| | | optionStyle: "Txt", |
| | | questionType: "drag", |
| | | score: 2, |
| | | stem: { |
| | | 0: "蚂蚁队长走路昂首挺胸、步伐坚定,它是一只(", |
| | | 1:{ |
| | | data: "span", |
| | | num: 0 |
| | | }, |
| | | 2: ")蚂蚁;小蚂蚁走起路来小心翼翼,眼神飘忽不定,它是一只(", |
| | | 3:{ |
| | | data:"span", |
| | | num:1 |
| | | }, |
| | | 4:")蚂蚁;蚂蚁小妹面带微笑,时刻愿意帮助大家,它是一只(", |
| | | 5:{ |
| | | data:"span", |
| | | num:2, |
| | | }, |
| | | 6:" )蚂蚁" |
| | | }, |
| | | stemStyle: "RichTxt", |
| | | type: "拖拽题", |
| | | userAnswer:[ |
| | | { |
| | | vlaue:'', |
| | | txt:'' |
| | | }, |
| | | { |
| | | vlaue:'', |
| | | txt:'' |
| | | }, |
| | | { |
| | | vlaue:'', |
| | | txt:'' |
| | | }, |
| | | ] |
| | | }, |
| | | ] |
| | | }; |
| | | }, |
| | | methods: { |
| | |
| | | }, 500); |
| | | |
| | | // 测试页面跳转 |
| | | // setTimeout(() => { |
| | | // this.gotoPage(1, 10); |
| | | setTimeout(() => { |
| | | this.gotoPage(2, 10); |
| | | // setTimeout(() => { |
| | | // this.renderSign("Highlight", { |
| | | // id: "2ACA9359", |
| | |
| | | // txt: " 运动系统是由骨、骨连结和骨骼肌三部分组成的。全身的骨通过骨连结组成人体骨骼(见图1-1)。骨骼是人体的支架,具有保护内脏器官、供肌肉附着和作为肌肉运动的杠杆等作用。在神经系统的支配下,肌肉收缩牵动所附着的骨绕着关节转动,使身体产生各种动作。所以,运动系统具有运动、支持和保护等功能,幼年时期的骨骼还具有造血功能。 ", |
| | | // txtIndex: 57 |
| | | // }); |
| | | // }, 5000); |
| | | }, 500); |
| | | |
| | | // 获取题目id列表 |
| | | this.getQuestionId(); |
| | |
| | | .chapter { |
| | | textarea { |
| | | width: 100%; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | padding: 2px 5px; |
| | | box-sizing: border-box; |
| | | } |
| | |
| | | |
| | | span { |
| | | margin-right: 28px; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | font-size: 18px; |
| | | } |
| | | span:nth-child(3) { |
| | |
| | | width: 1px; |
| | | height: 100%; |
| | | border-left: 2px dotted #eee; |
| | | margin:0 14px; |
| | | margin: 0 14px; |
| | | } |
| | | |
| | | .header-even { |
| | |
| | | align-items: flex-end; |
| | | span { |
| | | margin-left: 28px; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | font-size: 18px; |
| | | } |
| | | |
| | |
| | | font-weight: 700; |
| | | } |
| | | |
| | | span:nth-child(3){ |
| | | span:nth-child(3) { |
| | | margin-left: 0; |
| | | } |
| | | } |
| | |
| | | span { |
| | | font-size: 14px; |
| | | text-align: center; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: flex-start; |
| | |
| | | justify-content: center; |
| | | font-size: 14px; |
| | | text-align: center; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | } |
| | | } |
| | | } |
| | |
| | | justify-content: center; |
| | | align-items: center; |
| | | padding-top: 20px; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | |
| | | .table-bottom { |
| | | width: 100%; |
| | |
| | | |
| | | span:hover { |
| | | background-color: #89a0d0; |
| | | color: #FFF; |
| | | color: #fff; |
| | | } |
| | | |
| | | span:nth-child(1) { |
| | |
| | | text-align: center; |
| | | padding: 5px 10px; |
| | | font-size: 14px; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | font-weight: 900; |
| | | min-width: 35px |
| | | min-width: 35px; |
| | | } |
| | | |
| | | tr td { |
| | |
| | | padding: 3px 10px; |
| | | font-size: 14px; |
| | | line-height: 25px; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | |
| | | input { |
| | | cursor: pointer; |
| | |
| | | top: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | background: url('../image/dy1.jpg'); |
| | | background: url("../image/dy1.jpg"); |
| | | background-position: top; |
| | | background-size: 100% 50%; |
| | | background-repeat: no-repeat; |
| | |
| | | top: 0; |
| | | width: 100%; |
| | | height: 100%; |
| | | background: url('../image/dy2.jpg'); |
| | | background: url("../image/dy2.jpg"); |
| | | background-position: top; |
| | | background-size: 100% 50%; |
| | | background-repeat: no-repeat; |
| | |
| | | top: 0; |
| | | width: 100%; |
| | | height: 98%; |
| | | background: url('../image/dy3.jpg'); |
| | | background: url("../image/dy3.jpg"); |
| | | background-position: top; |
| | | background-size: 100% 45%; |
| | | background-repeat: no-repeat; |
| | |
| | | |
| | | p { |
| | | font-size: 16px; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | } |
| | | |
| | | } |
| | | |
| | | .bk-xyx { |
| | |
| | | span { |
| | | font-size: 14px; |
| | | text-align: center; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: flex-start; |
| | |
| | | text-indent: 2em; |
| | | } |
| | | |
| | | |
| | | .xyx-title { |
| | | font-family: "FZLTXIHJW"; |
| | | text-align: center; |
| | |
| | | .xyx-text { |
| | | p { |
| | | font-size: 16px; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | padding: 0 10px; |
| | | margin: 0; |
| | | } |
| | |
| | | background-color: transparent; |
| | | border-bottom: 1px solid #000; |
| | | font-size: 18px; |
| | | font-family: '宋体'; |
| | | font-family: "宋体"; |
| | | } |
| | | |
| | | .assess:focus { |
| | |
| | | border: 1px solid #89a0d0; |
| | | border-radius: 5px; |
| | | font-size: 14px; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | text-indent: 2em; |
| | | line-height: 22px; |
| | | padding: 2px 5px; |
| | |
| | | } |
| | | |
| | | .bj1-mk { |
| | | background-image: url('../image/mk.png'); |
| | | background-image: url("../image/mk.png"); |
| | | background-position: center; |
| | | background-repeat: no-repeat; |
| | | background-size: 100% 100%; |
| | |
| | | } |
| | | |
| | | @media screen and (max-width: 800px) { |
| | | |
| | | |
| | | .chapter { |
| | | .coverImg { |
| | | height: 100%; |
| | |
| | | span { |
| | | font-size: 14px; |
| | | text-align: center; |
| | | font-family: 'FZLTXIHJW'; |
| | | font-family: "FZLTXIHJW"; |
| | | line-height: 20px; |
| | | } |
| | | |
| | |
| | | justify-content: center; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | |
| | | .bj-img { |
| | | display: flex; |
| | |
| | | box-sizing: border-box; |
| | | } |
| | | |
| | | .tablePublic table tr th{ |
| | | .tablePublic table tr th { |
| | | padding: 0; |
| | | width: auto !important; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | @media screen and (max-width:700px) { |
| | | |
| | | @media screen and (max-width: 700px) { |
| | | .chapter { |
| | | |
| | | .coverImg { |
| | | margin: 0; |
| | | } |
| | |
| | | position: relative; |
| | | } |
| | | } |
| | | |
| | | } |
| | | } |
| | | |
| | | @media screen and (max-width:600px) { |
| | | @media screen and (max-width: 600px) { |
| | | .chapter { |
| | | |
| | | .coverImg { |
| | | margin: 0; |
| | | height: 100%; |
| | |
| | | } |
| | | } |
| | | |
| | | |
| | | .bj-empyt-chapter { |
| | | height: 30%; |
| | | } |
| | |
| | | } |
| | | } |
| | | |
| | | .pdfModal { |
| | | width: 100%; |
| | | height: 90vh; |
| | | } |
| | | |
| | | @media screen and (max-width:800px) { |
| | | .pdfModal { |
| | | width: 100%; |
| | | height: 60vh; |
| | | } |
| | | |
| | | .custom-dialog { |
| | | .el-dialog { |
| | | width: 90vw !important; |
| | | } |
| | | } |
| | | } |
| | | |
| | | |
| | | .custom-dialog { |
| | | overflow: hidden !important; |
| | | |
| | | .el-dialog__body { |
| | | padding: 0; |
| | | } |
| | | |
| | | .el-dialog__header { |
| | | background-color: rgba(0, 0, 0, 0.8); |
| | | |
| | | .header_title { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | color: #fff; |
| | | font-weight: 900; |
| | | font-size: 16px; |
| | | font-family: 'FZLTXIHJW'; |
| | | |
| | | span:nth-child(2):hover { |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | |
| | | .el-dialog__title, |
| | | .el-dialog__headerbtn .el-dialog__close { |
| | | color: #fff; |
| | | font-weight: 900; |
| | | font-size: 16px; |
| | | font-family: 'FZLTXIHJW'; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .tablePublic input[type="checkbox"]:checked+.checkmark { |
| | | .tablePublic input[type="checkbox"]:checked + .checkmark { |
| | | background: #000 !important; |
| | | /* 选中时的背景色 */ |
| | | } |
| | |
| | | user-select: none; |
| | | } |
| | | |
| | | |
| | | |
| | | body { |
| | | background-color: #e6e6e6; |
| | | } |
| | | } |
| | |
| | | <ChapterThree |
| | | v-if="showCatalogList.indexOf(4) > -1" |
| | | :showPageList="loadPageList" |
| | | @openPDFChange="swdtChange" |
| | | /> |
| | | <!-- <ChapterFour |
| | | v-if="showCatalogList.indexOf(5) > -1" |
| | |
| | | }, |
| | | methods: { |
| | | swdtChange(data) { |
| | | if (this.$store.state.qiankun && this.$store.state.qiankun.chooseWords) { |
| | | this.$store.state.qiankun.chooseWords({ |
| | | type: data.type, |
| | | data: data.data, |
| | | }); |
| | | console.log("子应用pdf", data); |
| | | if (this.$store.state.qiankun && this.$store.state.qiankun.openPDF) { |
| | | if (data.type == "pdf") { |
| | | this.$store.state.qiankun.openPDF({ |
| | | data: data.data, |
| | | }); |
| | | } else { |
| | | this.$store.state.qiankun.chooseWords({ |
| | | type: data.type, |
| | | data: data.data, |
| | | }); |
| | | } |
| | | } |
| | | }, |
| | | changeDomViewer() { |
| | |
| | | autoplay: { |
| | | //自动开始 |
| | | delay: 3000, //时间间隔 |
| | | disableOnInteraction: false //*手动操作轮播图后不会暂停* |
| | | disableOnInteraction: false, //*手动操作轮播图后不会暂停* |
| | | }, |
| | | paginationClickable: true, |
| | | slidesPerView: 1, // 一组三个 |
| | | spaceBetween: 30, // 间隔 |
| | | // 如果需要前进后退按钮 |
| | | navigation: { |
| | | nextEl: dom.querySelector( |
| | | ".swiper-button-next" |
| | | ), |
| | | prevEl: dom.querySelector( |
| | | ".swiper-button-prev" |
| | | ) |
| | | nextEl: dom.querySelector(".swiper-button-next"), |
| | | prevEl: dom.querySelector(".swiper-button-prev"), |
| | | }, |
| | | // 窗口变化,重新init,针对F11全屏和放大缩小,必须加 |
| | | observer: true, |
| | | observeParents: true |
| | | observeParents: true, |
| | | // // 如果需要分页器 |
| | | // pagination: { |
| | | // el: (this.container ? this.container : document).querySelector( |
| | |
| | | spaceBetween: 30, // 间隔 |
| | | // 如果需要前进后退按钮 |
| | | navigation: { |
| | | nextEl: dom.querySelector( |
| | | ".swiper-button-next" |
| | | ), |
| | | prevEl: dom.querySelector( |
| | | ".swiper-button-prev" |
| | | ) |
| | | nextEl: dom.querySelector(".swiper-button-next"), |
| | | prevEl: dom.querySelector(".swiper-button-prev"), |
| | | }, |
| | | // 窗口变化,重新init,针对F11全屏和放大缩小,必须加 |
| | | observer: true, |
| | |
| | | var paginationInfoEl = dom.querySelector(".pageBox"); |
| | | if (paginationInfoEl) |
| | | paginationInfoEl.textContent = currentPage + "/" + totalPages; |
| | | } |
| | | } |
| | | }, |
| | | }, |
| | | }); |
| | | } |
| | | }, |
| | |
| | | :visible.sync="dialogVisible" |
| | | width="60vw" |
| | | top="2vh" |
| | | append-to-body |
| | | lock-scroll |
| | | :show-close="false" |
| | | class="custom-dialog" |
| | |
| | | }, |
| | | toUrl(val) { |
| | | if (val) { |
| | | this.dialogVisible = true; |
| | | this.p_md5 = this.chapter002.pdfMd5[val].md5; |
| | | this.pdfTitle = this.chapter002.pdfMd5[val].name; |
| | | const obj = { |
| | | type: "pdf", |
| | | data: { |
| | | md5: this.chapter002.pdfMd5[val].md5, |
| | | title: this.chapter002.pdfMd5[val].name, |
| | | }, |
| | | }; |
| | | this.$emit("openPDFChange", obj); |
| | | |
| | | // this.dialogVisible = true; |
| | | // this.p_md5 = this.chapter002.pdfMd5[val].md5; |
| | | // this.pdfTitle = this.chapter002.pdfMd5[val].name; |
| | | } |
| | | }, |
| | | async getVideo(md5, val) { |
| | |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="less"> |
| | | .pdfModal { |
| | | width: 100%; |
| | | height: 90vh; |
| | | } |
| | | .custom-dialog { |
| | | overflow: hidden !important; |
| | | |
| | | /deep/ .el-dialog__body { |
| | | padding: 0; |
| | | } |
| | | |
| | | /deep/ .el-dialog__header { |
| | | background-color: rgba(0, 0, 0, 0.8); |
| | | |
| | | .header_title { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | color: #fff; |
| | | font-weight: 900; |
| | | font-size: 16px; |
| | | font-family: "FZLTXIHJW"; |
| | | |
| | | span:nth-child(2):hover { |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | |
| | | /deep/.el-dialog__title, |
| | | /deep/ .el-dialog__headerbtn .el-dialog__close { |
| | | color: #fff; |
| | | font-weight: 900; |
| | | font-size: 16px; |
| | | font-family: "FZLTXIHJW"; |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media screen and (max-width: 1024px) { |
| | | .pdfModal { |
| | | width: 100%; |
| | | height: 80vh; |
| | | } |
| | | |
| | | .custom-dialog { |
| | | /deep/ .el-dialog { |
| | | width: 90vw !important; |
| | | } |
| | | } |
| | | } |
| | | |
| | | @media screen and (max-width: 800px) { |
| | | .pdfModal { |
| | | width: 100%; |
| | | height: 60vh; |
| | | } |
| | | |
| | | .custom-dialog { |
| | | /deep/ .el-dialog { |
| | | width: 90vw !important; |
| | | } |
| | | } |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="drag-box"> |
| | | <div |
| | | v-for="(item, index) in drageQuestion" |
| | | :key="item.id" |
| | | @mousemove="mousemove" |
| | | @mouseup="mouseup" |
| | | :num="index" |
| | | class="question-box" |
| | | > |
| | | <!-- 题干 --> |
| | | <div class="stem"> |
| | | <span v-for="(ditem, dindex) in item.stem" :key="dindex"> |
| | | <span v-if="typeof ditem == 'string'"> |
| | | {{ ditem }} |
| | | </span> |
| | | <span class="space" v-else>{{ item.userAnswer[ditem.num].txt }}</span> |
| | | </span> |
| | | </div> |
| | | |
| | | <!-- 选项 --> |
| | | <ul class="option-box"> |
| | | <li v-for="(citem, cindex) in item.option" :key="cindex"> |
| | | <span class="drag-option" @mousedown="mousedown" v-show="item.userAnswer.findIndex(eitem => eitem.value == citem.value) == -1" |
| | | >{{ citem.value }}、{{ citem.txt }}</span |
| | | > |
| | | </li> |
| | | </ul> |
| | | <!-- 提交后解析 --> |
| | | <div class="analysis" v-if="item.isComplete"> |
| | | <el-collapse |
| | | v-model="item.isUnfold" |
| | | @change="handleChange(item)" |
| | | accordion |
| | | v-if="item.questionType != 'shortAnswer'" |
| | | > |
| | | <el-collapse-item :name="item.id" class="objective"> |
| | | <template #title> |
| | | <div class="headerBox"> |
| | | <div class="headerConent"> |
| | | <p class="correct" :title="arrayToString(item.answer)"> |
| | | <span>正确答案:</span> |
| | | <span |
| | | v-html="arrayToString(item.answer)" |
| | | class="correctBox" |
| | | ></span> |
| | | </p> |
| | | <p |
| | | :class=" |
| | | item.isRight |
| | | ? 'correct textOverFlow' |
| | | : 'error textOverFlow' |
| | | " |
| | | :title="arrayToString(item.userAnswer)" |
| | | style="margin-left: 20px" |
| | | > |
| | | <span>您的答案:</span> |
| | | <span |
| | | class="errorBox" |
| | | v-if="item.userAnswer.length" |
| | | > |
| | | <span v-for="(answerItem,answerIdnex) in item.userAnswer" :key="answerIdnex">{{answerItem.value}}<span v-if="answerIdnex !== item.userAnswer.length - 1 && answerItem.value">,</span></span> |
| | | |
| | | </span |
| | | > |
| | | </p> |
| | | </div> |
| | | <p class="difficulty" v-if="item.difficulty"> |
| | | <span style="color: #333">难度等级:</span> |
| | | <el-rate |
| | | v-model="item.difficulty" |
| | | :max="3" |
| | | size="large" |
| | | disabled |
| | | disabled-void-color="#949494" |
| | | /> |
| | | </p> |
| | | <!-- 正确错误图标 --> |
| | | <span class="mr-10 judge-icon"> |
| | | <svg |
| | | v-if="item.isRight" |
| | | t="1716986419862" |
| | | class="icon" |
| | | viewBox="0 0 1820 1024" |
| | | version="1.1" |
| | | xmlns="http://www.w3.org/2000/svg" |
| | | p-id="18767" |
| | | xmlns:xlink="http://www.w3.org/1999/xlink" |
| | | width="40" |
| | | height="20" |
| | | > |
| | | <path |
| | | d="M1439.374222 216.007111s-169.472 56.490667-367.179852 282.443852C888.604444 703.222519 846.241185 787.949037 775.632593 900.93037 768.568889 893.866667 662.651259 689.095111 380.207407 540.814222l148.290371-141.226666s134.162963 91.790222 225.953185 261.262222c0 0 233.016889-360.116148 684.923259-536.642371v91.799704z m0 0" |
| | | fill="#1AFA29" |
| | | p-id="18768" |
| | | ></path> |
| | | </svg> |
| | | <svg |
| | | v-if="item.isRight == false" |
| | | t="1716987085767" |
| | | class="icon" |
| | | viewBox="0 0 1024 1024" |
| | | version="1.1" |
| | | xmlns="http://www.w3.org/2000/svg" |
| | | p-id="25745" |
| | | xmlns:xlink="http://www.w3.org/1999/xlink" |
| | | width="20" |
| | | height="20" |
| | | > |
| | | <path |
| | | d="M116.579135 38.64349531L38.703935 103.74399781c138.82075969 102.96027281 268.24660875 221.31426938 381.68489719 339.96758156C246.29374906 618.40145938 109.95003031 790.19602344 38.10817906 859.25288281l148.35573469 123.62658094c52.61360812-108.17625656 167.23381594-272.86683656 320.56281844-445.01635875 153.50744156 173.21056312 268.36844625 338.43166313 321.38977781 447.49243969 0 0 144.5682225-152.96636906 157.47435281-129.29729625-55.80632344-62.49011156-191.37776625-244.16501625-374.17990593-430.27403438 104.68422375-107.1132975 222.15274031-213.10127719 347.60304468-306.24740437L925.17746562 56.03842156C782.85412063 126.51895625 647.69328031 231.09093594 526.07845437 342.39755 403.34886594 226.82662719 264.46095125 116.16373719 116.579135 38.64349531L116.579135 38.64349531zM116.579135 38.64349531" |
| | | fill="#d81e06" |
| | | p-id="25746" |
| | | ></path> |
| | | </svg> |
| | | </span> |
| | | |
| | | <div class="headerConent sitgBox"> |
| | | <p v-if="!item.isUnfold"> |
| | | <span class="analysisColor">展开解析</span><el-image /> |
| | | </p> |
| | | <p v-else> |
| | | <span class="analysisColor">收起解析</span><el-image /> |
| | | </p> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <!-- 解析 --> |
| | | <div v-if="item.analysisCon" v-html="item.analysisCon"></div> |
| | | <div v-else>暂无数据</div> |
| | | </el-collapse-item> |
| | | <!-- 主观题 --> |
| | | </el-collapse> |
| | | </div> |
| | | </div> |
| | | <div class="drag-bottom-btn"> |
| | | <el-button @click="saveData">保存</el-button> |
| | | <el-button @click="submitData">提交</el-button> |
| | | <el-button @click="redio">重做</el-button> |
| | | <el-button @click="seeAnswer">查看答案</el-button> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | name: "drag", |
| | | props: { |
| | | question: { |
| | | type: Array, |
| | | default: [], |
| | | }, |
| | | page: { |
| | | type: Number, |
| | | }, |
| | | }, |
| | | watch: { |
| | | question: { |
| | | handler(newValue, oldValue) { |
| | | console.log("监听传值", newValue); |
| | | let oldAnswer = localStorage.getItem( |
| | | this.config.activeBook.name + "-drag-" + this.page |
| | | ); |
| | | if (oldAnswer) { |
| | | oldAnswer = JSON.parse(oldAnswer); |
| | | for (let index = 0; index < newValue.length; index++) { |
| | | // 旧答案赋值 |
| | | const item = newValue[index]; |
| | | const oldItem = oldAnswer.find((citem) => citem.id == item.id); |
| | | item.userAnswer = oldItem.userAnswer; |
| | | // 隐藏旧答案里的选项 |
| | | // for (let cindex = 0; cindex < item.option.length; cindex++) { |
| | | // const citem = item.option[cindex]; |
| | | // if(item.userAnswer.findIndex(ditem => ditem.value == citem.value) > -1) { |
| | | // citem.isShow = false |
| | | // } |
| | | // } |
| | | } |
| | | } |
| | | this.drageQuestion = newValue; |
| | | }, |
| | | immediate: true, |
| | | }, |
| | | }, |
| | | data() { |
| | | return { |
| | | questionIndex: null, |
| | | drageQuestion: [], |
| | | moveDom: null, |
| | | parentDom: null, |
| | | parentX: null, // 父盒子在页面的位置 |
| | | parentY: null, |
| | | maxX: null, |
| | | maxY: null, |
| | | spaceList: [], |
| | | }; |
| | | }, |
| | | mounted() { |
| | | console.log("传值", this.question); |
| | | }, |
| | | methods: { |
| | | // 1.选项拖拽 |
| | | // 2.限制拖拽移动只能在题范围内 |
| | | // 3.限制鼠标松开要在空格的范围内 |
| | | mousedown(e) { |
| | | e.preventDefault(); |
| | | console.log("鼠标按下", e); |
| | | this.moveDom = e.toElement; |
| | | this.parentDom = this.getParentWithClass(e.toElement, "question-box"); |
| | | this.questionIndex = this.parentDom.getAttribute("num"); |
| | | this.getSpaceInfo(this.parentDom); |
| | | const parentInfo = this.parentDom.getBoundingClientRect(); |
| | | this.parentX = parentInfo.x; |
| | | this.parentY = parentInfo.y; |
| | | this.maxX = parentInfo.x + this.parentDom.clientWidth; |
| | | this.maxY = parentInfo.y + this.parentDom.clientHeight; |
| | | console.log("父盒子", parentInfo); |
| | | }, |
| | | // 鼠标移动 |
| | | mousemove(e) { |
| | | if (!this.moveDom) return false; |
| | | this.moveDom.style.position = "absolute"; |
| | | // 限制在此题范围内拖动 |
| | | if (e.x <= this.maxX && e.y < this.maxY) { |
| | | const moveX = e.x - this.parentX; |
| | | const moveY = e.y - this.parentY; |
| | | this.moveDom.style.left = moveX + "px"; |
| | | this.moveDom.style.top = moveY + "px"; |
| | | // 如果移动到空格范围内,将答案填入空格 |
| | | if (this.spaceList.length) { |
| | | for (let index = 0; index < this.spaceList.length; index++) { |
| | | const item = this.spaceList[index]; |
| | | if ( |
| | | e.x >= item.minX && |
| | | e.x <= item.maxX && |
| | | e.y >= item.minY && |
| | | e.y <= item.maxY |
| | | ) { |
| | | this.$set( |
| | | this.drageQuestion[this.questionIndex].userAnswer, |
| | | index, |
| | | { |
| | | value:this.moveDom.textContent.charAt(0), |
| | | txt:this.moveDom.textContent.slice(2) |
| | | } |
| | | ); |
| | | this.moveDom.style.display = "none"; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | }, |
| | | // 鼠标抬起 |
| | | mouseup(e) { |
| | | if (this.moveDom) { |
| | | this.moveDom = null; |
| | | } |
| | | }, |
| | | // 获取题目空格坐标 |
| | | getSpaceInfo(parentDom) { |
| | | let arr = []; |
| | | const list = parentDom.querySelectorAll(".space"); |
| | | for (let index = 0; index < list.length; index++) { |
| | | const itemInfo = list[index].getBoundingClientRect(); |
| | | arr.push({ |
| | | minX: itemInfo.x, |
| | | maxX: itemInfo.x + itemInfo.width, |
| | | minY: itemInfo.y, |
| | | maxY: itemInfo.y + itemInfo.height, |
| | | }); |
| | | } |
| | | this.spaceList = arr; |
| | | console.log("空格范围", arr); |
| | | }, |
| | | // 获取父元素 |
| | | getParentWithClass(element, className) { |
| | | while (element.parentElement) { |
| | | element = element.parentElement; |
| | | if (element.classList.contains(className)) { |
| | | return element; |
| | | } |
| | | } |
| | | }, |
| | | // 保存答案 |
| | | saveData(e) { |
| | | let answerArr = []; |
| | | for (let index = 0; index < this.drageQuestion.length; index++) { |
| | | const item = this.drageQuestion[index]; |
| | | answerArr.push({ |
| | | id: item.id, |
| | | userAnswer: item.userAnswer, |
| | | }); |
| | | } |
| | | localStorage.setItem( |
| | | this.config.activeBook.name + "-drag-" + this.page, |
| | | JSON.stringify(answerArr) |
| | | ); |
| | | }, |
| | | // 提交 |
| | | submitData() { |
| | | let arr = []; |
| | | const data = this.drageQuestion; |
| | | for (let index = 0; index < data.length; index++) { |
| | | const item = data[index]; |
| | | for (let cindex = 0; cindex < item.userAnswer.length; cindex++) { |
| | | let citem = item.userAnswer[cindex]; |
| | | arr[cindex] = citem.value |
| | | } |
| | | item.isRight = arr == item.answer |
| | | item.isComplete = true |
| | | } |
| | | |
| | | console.log(arr,this.drageQuestion[0]); |
| | | }, |
| | | redio() { |
| | | localStorage.removeItem( |
| | | this.config.activeBook.name + "-drag-" + this.page |
| | | ); |
| | | let arr = [] |
| | | // const data = [] |
| | | for (let index = 0; index < this.drageQuestion.length; index++) { |
| | | const item = this.drageQuestion[index]; |
| | | item.isComplete = false |
| | | for (let cindex = 0; cindex < item.option.length; cindex++) { |
| | | const citem = item.option[cindex]; |
| | | arr.push( |
| | | { |
| | | txt:'', |
| | | value:'' |
| | | } |
| | | ) |
| | | } |
| | | item.userAnswer = arr |
| | | } |
| | | console.log(this.drageQuestion); |
| | | const optionList = ( |
| | | this.container ? this.container : document |
| | | ).querySelectorAll(".drag-option"); |
| | | for (let index = 0; index < optionList.length; index++) { |
| | | const item = optionList[index]; |
| | | item.style.position = null |
| | | item.style.top = null |
| | | item.style.left = null |
| | | } |
| | | }, |
| | | seeAnswer() { |
| | | for (let index = 0; index < this.drageQuestion.length; index++) { |
| | | const item = this.drageQuestion[index]; |
| | | item.isComplete = true; |
| | | } |
| | | }, |
| | | // 数组转为字符串方法 |
| | | arrayToString(data) { |
| | | // 检查是否为数组 |
| | | if (Array.isArray(data)) { |
| | | // 使用 join 方法将数组转换为字符串,默认使用逗号分隔 |
| | | return data.join(",").replace(/<[^>]*>/g, ""); |
| | | } else { |
| | | // 如果不是数组,直接返回原始值 |
| | | return data.replace(/<[^>]*>/g, ""); |
| | | } |
| | | }, |
| | | isHaveAnswer(data) { |
| | | if (typeof data == "string") { |
| | | data = data |
| | | .replace(/<[^>]*>/g, "") |
| | | .replace(/ /g, "") |
| | | .trim(); |
| | | if (data.length) { |
| | | return true; |
| | | } else { |
| | | return false; |
| | | } |
| | | } else { |
| | | const answer = data.find((item) => item.length > 0); |
| | | if (answer) { |
| | | return true; |
| | | } else { |
| | | return false; |
| | | } |
| | | } |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style lang="less" scoped> |
| | | p { |
| | | text-indent: 0 !important; |
| | | } |
| | | .drag-box { |
| | | position: relative; |
| | | } |
| | | .space { |
| | | display: inline-block; |
| | | text-align: center; |
| | | min-width: 60px !important; |
| | | min-height: 15px !important; |
| | | } |
| | | .option-box { |
| | | display: flex; |
| | | justify-content: space-evenly; |
| | | } |
| | | .drag-option { |
| | | cursor: pointer; |
| | | } |
| | | .stem { |
| | | line-height: 2em; |
| | | } |
| | | .drag-bottom-btn { |
| | | width: 100%; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | justify-content: center; |
| | | .el-button { |
| | | height: 30px; |
| | | width: 78px; |
| | | padding: 4px 10px; |
| | | } |
| | | } |
| | | .analysis { |
| | | margin: 20px 0; |
| | | width: 94%; |
| | | // margin-left: 12px; |
| | | } |
| | | .judge-icon { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | .el-collapse { |
| | | width: 100%; |
| | | } |
| | | /** 解析 */ |
| | | .objective { |
| | | /deep/ .el-collapse-item__header { |
| | | min-height: 48px; |
| | | height: min-content; |
| | | padding: 0 20px; |
| | | font-size: 16px; |
| | | background-color: #f4f4f4; |
| | | .headerBox { |
| | | width: 100%; |
| | | height: 100%; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | .headerConent { |
| | | height: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | p { |
| | | margin-bottom: 0; |
| | | } |
| | | .el-image { |
| | | width: 9px; |
| | | height: 9px; |
| | | } |
| | | .correct { |
| | | color: #1fbc1f; |
| | | width: 180px; |
| | | text-align: left; |
| | | display: flex; |
| | | span:nth-child(1) { |
| | | display: inline-block; |
| | | text-indent: 0; |
| | | width: 82px; |
| | | } |
| | | } |
| | | .correctBox { |
| | | width: 100px; |
| | | height: min-content; |
| | | word-break: break-all; |
| | | text-overflow: ellipsis; |
| | | display: -webkit-box; |
| | | -webkit-box-orient: vertical; |
| | | -webkit-line-clamp: 1; /* 这里是超出几行省略 */ |
| | | overflow: hidden; |
| | | } |
| | | .error { |
| | | display: flex; |
| | | text-align: left; |
| | | width: 170px; |
| | | color: #ee1818; |
| | | span { |
| | | height: min-content; |
| | | width: 100px; |
| | | } |
| | | .errorBox { |
| | | width: 100px; |
| | | // height: 48px; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | white-space: nowrap; |
| | | ::v-deep { |
| | | img { |
| | | max-height: 48px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .checkAnalysis { |
| | | cursor: pointer; |
| | | height: 100%; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | } |
| | | } |
| | | /deep/ .el-collapse-item__header:focus:not(:hover) { |
| | | color: #333; |
| | | } |
| | | /deep/ .el-collapse-item__content { |
| | | width: 93%; |
| | | padding: 0 20px; |
| | | background-color: #f4f4f4; |
| | | font-size: 16px; |
| | | color: #333; |
| | | } |
| | | /deep/ .el-collapse-item__arrow { |
| | | display: none; |
| | | } |
| | | </style> |
| | |
| | | }, |
| | | mounted() { |
| | | this.getCollectIdList(); |
| | | this.getErrorList() |
| | | this.getErrorList(); |
| | | }, |
| | | methods: { |
| | | // 数组转为字符串方法 |
| | |
| | | }, |
| | | // 批改题目 (练习,我的做题,我的收藏模式下) |
| | | handleQuestion() { |
| | | let errorId = [] |
| | | let errorId = []; |
| | | const list = this.cardData; |
| | | for (let index = 0; index < list.length; index++) { |
| | | const item = list[index]; |
| | |
| | | } |
| | | } |
| | | } |
| | | if(citem.isRight == false) { |
| | | errorId.push(citem.id) |
| | | if (citem.isRight == false) { |
| | | errorId.push(citem.id); |
| | | } |
| | | } |
| | | } |
| | | this.cardData = list; |
| | | let oldErrodId = this.allError.find(item => item.type == this.sourceType).errorList |
| | | let oldErrodId = this.allError.find( |
| | | (item) => item.type == this.sourceType |
| | | ).errorList; |
| | | for (let index = 0; index < errorId.length; index++) { |
| | | const item = errorId[index]; |
| | | if(oldErrodId.indexOf(item == -1)) { |
| | | oldErrodId.push(item) |
| | | if (oldErrodId.indexOf(item == -1)) { |
| | | oldErrodId.push(item); |
| | | } |
| | | } |
| | | for (let cindex = 0; cindex < this.allError.length; cindex++) { |
| | | const citem = this.allError[cindex]; |
| | | if(citem.type == this.sourceType) citem.errorList = oldErrodId |
| | | if (citem.type == this.sourceType) citem.errorList = oldErrodId; |
| | | } |
| | | this.MG.identity |
| | | .setUserKey({ |
| | | setKeyRequests: [ |
| | | { |
| | | domain: 'errorData', |
| | | domain: "errorData", |
| | | key: this.config.activeBook.bookId, |
| | | value: JSON.stringify(this.allError) |
| | | } |
| | | ] |
| | | value: JSON.stringify(this.allError), |
| | | }, |
| | | ], |
| | | }) |
| | | .then((res) => { |
| | | console.log('错题已保存',this.allError) |
| | | }) |
| | | console.log("错题已保存", this.allError); |
| | | }); |
| | | }, |
| | | getParentWithClass(element, className) { |
| | | while (element.parentElement) { |
| | |
| | | }, |
| | | // 获取收藏id列表 |
| | | getCollectIdList() { |
| | | const token = localStorage.getItem("token"); |
| | | if (!token) return false; |
| | | this.MG.identity |
| | | .getUserKey({ |
| | | domain: "collectData", |
| | |
| | | }, |
| | | // 获取错题id列表 |
| | | getErrorList() { |
| | | const token = localStorage.getItem("token"); |
| | | if (!token) return false; |
| | | this.MG.identity |
| | | .getUserKey({ |
| | | domain: "errorData", |
| | |
| | | ).errorList; |
| | | } |
| | | } catch (error) {} |
| | | }); |
| | | }) |
| | | .catch(() => {}); |
| | | }, |
| | | }, |
| | | }; |
New file |
| | |
| | | <template> |
| | | <div class="wrap-range"> |
| | | <!-- 为了不在子组件中变更值,不用v-model --> |
| | | <input |
| | | type="range" |
| | | :value="brushSize" |
| | | min="1" |
| | | max="30" |
| | | title="调整笔刷粗细" |
| | | @change="(event) => $emit('change-size', +event.target.value)" |
| | | /> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | props: { |
| | | size: { |
| | | type: Number, |
| | | default: 5, |
| | | }, |
| | | }, |
| | | computed:{ |
| | | brushSize() { |
| | | return this.size |
| | | } |
| | | } |
| | | }; |
| | | |
| | | // const brushSize = computed(() => props.size); |
| | | </script> |
| | | <style scoped> |
| | | .wrap-range input { |
| | | width: 150px; |
| | | height: 20px; |
| | | margin: 0; |
| | | transform-origin: 75px 75px; |
| | | border-radius: 15px; |
| | | -webkit-appearance: none; |
| | | appearance: none; |
| | | outline: none; |
| | | position: relative; |
| | | } |
| | | |
| | | .wrap-range input::after { |
| | | display: block; |
| | | content: ""; |
| | | width: 0; |
| | | height: 0; |
| | | border: 5px solid transparent; |
| | | border-right: 150px solid #00ccff; |
| | | border-left-width: 0; |
| | | position: absolute; |
| | | left: 0; |
| | | top: 5px; |
| | | border-radius: 15px; |
| | | z-index: 0; |
| | | } |
| | | |
| | | .wrap-range input[type="range"]::-webkit-slider-thumb, |
| | | .wrap-range input[type="range"]::-moz-range-thumb { |
| | | -webkit-appearance: none; |
| | | } |
| | | |
| | | .wrap-range input[type="range"]::-webkit-slider-runnable-track, |
| | | .wrap-range input[type="range"]::-moz-range-track { |
| | | height: 10px; |
| | | border-radius: 10px; |
| | | box-shadow: none; |
| | | } |
| | | |
| | | .wrap-range input[type="range"]::-webkit-slider-thumb { |
| | | -webkit-appearance: none; |
| | | height: 20px; |
| | | width: 20px; |
| | | margin-top: -1px; |
| | | background: #ffffff; |
| | | border-radius: 50%; |
| | | box-shadow: 0 0 8px #00ccff; |
| | | position: relative; |
| | | z-index: 999; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div> |
| | | <span |
| | | v-for="(color, index) of colorList" |
| | | class="color-item" |
| | | :class="{ active: colorSelected === color }" |
| | | :style="{ backgroundColor: color }" |
| | | :key="index" |
| | | @click="onChangeColor(color)" |
| | | ></span> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | props: { |
| | | color: { |
| | | type: String, |
| | | }, |
| | | }, |
| | | computed:{ |
| | | colorSelected() { |
| | | return this.color |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | colorList: [ |
| | | "#000000", |
| | | "#808080", |
| | | "#FF3333", |
| | | "#0066FF", |
| | | "#FFFF33", |
| | | "#33CC66", |
| | | ], |
| | | }; |
| | | }, |
| | | methods: { |
| | | onChangeColor(color) { |
| | | this.$emit("change-color", color); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .color-item { |
| | | display: inline-block; |
| | | width: 32px; |
| | | height: 32px; |
| | | margin: 0 4px; |
| | | box-sizing: border-box; |
| | | border: 4px solid white; |
| | | box-shadow: 0 0 8px rgba(0, 0, 0, 0.2); |
| | | cursor: pointer; |
| | | transition: 0.3s; |
| | | } |
| | | .color-item.active { |
| | | box-shadow: 0 0 15px #00ccff; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="tools"> |
| | | <button |
| | | v-for="(item, index) of toolList" |
| | | :key="index" |
| | | :class="{ active: toolSelected === item.name }" |
| | | :title="item.title" |
| | | @click="onChangeTool(item.name)" |
| | | > |
| | | <i :class="['iconfont', item.icon]"></i> |
| | | </button> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | export default { |
| | | props: { |
| | | tool: { |
| | | type: String, |
| | | default: "brush", |
| | | }, |
| | | }, |
| | | computed:{ |
| | | toolSelected() { |
| | | return this.tool |
| | | } |
| | | }, |
| | | data() { |
| | | return { |
| | | toolList: [ |
| | | { name: "brush", title: "画笔", icon: "icon-qianbi" }, |
| | | { name: "eraser", title: "橡皮擦", icon: "icon-xiangpi" }, |
| | | { name: "clear", title: "清空", icon: "icon-qingchu" }, |
| | | { name: "undo", title: "撤销", icon: "icon-chexiao" }, |
| | | { name: "save", title: "保存", icon: "icon-fuzhi" }, |
| | | ], |
| | | }; |
| | | }, |
| | | methods: { |
| | | onChangeTool(tool) { |
| | | this.$emit("change-tool", tool); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .tools button { |
| | | /* border-radius: 50%; */ |
| | | width: 32px; |
| | | height: 32px; |
| | | background-color: rgba(255, 255, 255, 0.7); |
| | | border: 1px solid #eee; |
| | | outline: none; |
| | | cursor: pointer; |
| | | box-sizing: border-box; |
| | | margin: 0 8px; |
| | | padding: 0; |
| | | text-align: center; |
| | | color: #ccc; |
| | | box-shadow: 0 0 8px rgba(0, 0, 0, 0.1); |
| | | transition: 0.3s; |
| | | } |
| | | |
| | | .tools button.active, |
| | | .tools button:active { |
| | | /* box-shadow: 0 0 15px #00CCFF; */ |
| | | color: #00ccff; |
| | | } |
| | | |
| | | .tools button i { |
| | | font-size: 20px; |
| | | } |
| | | </style> |
New file |
| | |
| | | <template> |
| | | <div class="page"> |
| | | <div class="main"> |
| | | <div id="canvas_panel"> |
| | | <canvas |
| | | id="canvas" |
| | | :style="{ |
| | | backgroundImage: `url(${backgroundImage})`, |
| | | backgroundSize: 'cover', |
| | | backgroundPosition: 'center', |
| | | }" |
| | | >当前浏览器不支持canvas。</canvas |
| | | > |
| | | </div> |
| | | </div> |
| | | <div class="footer"> |
| | | <BrushSize :size="brushSize" @change-size="onChangeSize" /> |
| | | <ColorPicker :color="brushColor" @change-color="onChangeColor" /> |
| | | <ToolBtns :tool="brushTool" @change-tool="onChangeTool" /> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | |
| | | <script> |
| | | import BrushSize from "./components/brushSize.vue"; |
| | | import ColorPicker from "./components/colorPicker.vue"; |
| | | import ToolBtns from "./components/toolBtns.vue"; |
| | | export default { |
| | | name: "graffiti", |
| | | components: { BrushSize, ColorPicker, ToolBtns }, |
| | | data() { |
| | | return { |
| | | canvas: null, |
| | | context: null, |
| | | painting: false, |
| | | historyData: [], // 存储历史数据,用于撤销 |
| | | brushSize: 5, // 笔刷大小 |
| | | brushColor: "#000000", // 笔刷颜色 |
| | | brushTool: "brush", |
| | | canvasOffset: { |
| | | left: 0, |
| | | top: 0, |
| | | }, |
| | | backgroundImage: |
| | | "https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF", |
| | | }; |
| | | }, |
| | | mounted() { |
| | | this.canvas = document.getElementById("canvas"); |
| | | if (this.canvas.getContext) { |
| | | this.context = this.canvas.getContext("2d", { willReadFrequently: true }); |
| | | this.initCanvas(); |
| | | // window.addEventListener('resize', updateCanvasPosition); |
| | | window.addEventListener("scroll", this.updateCanvasOffset); // 添加滚动条滚动事件监听器 |
| | | this.getCanvasOffset(); |
| | | this.context.lineGap = "round"; |
| | | this.context.lineJoin = "round"; |
| | | this.canvas.addEventListener("mousedown", this.downCallback); |
| | | this.canvas.addEventListener("mousemove", this.moveCallback); |
| | | this.canvas.addEventListener("mouseup", this.closePaint); |
| | | this.canvas.addEventListener("mouseleave", this.closePaint); |
| | | } |
| | | this.toolClear(); |
| | | }, |
| | | methods: { |
| | | changeBackground(imgUrl) { |
| | | this.backgroundImage = imgUrl; |
| | | }, |
| | | initCanvas() { |
| | | const that = this |
| | | function resetCanvas() { |
| | | const elPanel = document.getElementById("canvas_panel"); |
| | | that.canvas.width = elPanel.clientWidth; |
| | | that.canvas.height = elPanel.clientHeight; |
| | | that.context = that.canvas.getContext("2d", { |
| | | willReadFrequently: true, |
| | | }); // 添加这一行 |
| | | that.context.fillStyle = "white"; |
| | | that.context.fillRect(0, 0, that.canvas.width, that.canvas.height); |
| | | that.context.fillStyle = "black"; |
| | | that.getCanvasOffset(); // 更新画布位置 |
| | | } |
| | | |
| | | resetCanvas(); |
| | | window.addEventListener("resize", resetCanvas); |
| | | }, |
| | | |
| | | // 获取canvas的偏移值 |
| | | getCanvasOffset() { |
| | | const rect = this.canvas.getBoundingClientRect(); |
| | | this.canvasOffset.left = rect.left * (this.canvas.width / rect.width); // 兼容缩放场景 |
| | | this.canvasOffset.top = rect.top * (this.canvas.height / rect.height); |
| | | }, |
| | | |
| | | // 计算当前鼠标相对于canvas的坐标 |
| | | calcRelativeCoordinate(x, y) { |
| | | return { |
| | | x: x - this.canvasOffset.left, |
| | | y: y - this.canvasOffset.top, |
| | | }; |
| | | }, |
| | | |
| | | downCallback(event) { |
| | | // 先保存之前的数据,用于撤销时恢复(绘制前保存,不是绘制后再保存) |
| | | const data = this.context.getImageData( |
| | | 0, |
| | | 0, |
| | | this.canvas.width, |
| | | this.canvas.height |
| | | ); |
| | | this.saveData(data); |
| | | const { clientX, clientY } = event; |
| | | const { x, y } = this.calcRelativeCoordinate(clientX, clientY); |
| | | this.context.beginPath(); |
| | | this.context.moveTo(x, y); |
| | | this.context.lineWidth = this.brushSize; |
| | | this.context.strokeStyle = |
| | | this.brushTool === "eraser" ? "#FFFFFF" : this.brushColor; |
| | | this.painting = true; |
| | | }, |
| | | |
| | | moveCallback(event) { |
| | | if (!this.painting) { |
| | | return; |
| | | } |
| | | const { clientX, clientY } = event; |
| | | const { x, y } = this.calcRelativeCoordinate(clientX, clientY); |
| | | this.context.lineTo(x, y); |
| | | this.context.stroke(); |
| | | }, |
| | | closePaint() { |
| | | this.painting = false; |
| | | }, |
| | | updateCanvasOffset() { |
| | | this.getCanvasOffset(); // 重新计算画布的偏移值 |
| | | }, |
| | | |
| | | onChangeSize(size) { |
| | | this.brushSize = size; |
| | | }, |
| | | onChangeColor(color) { |
| | | this.brushColor = color; |
| | | }, |
| | | onChangeTool(tool) { |
| | | this.brushTool = tool; |
| | | switch (tool) { |
| | | case "clear": |
| | | this.toolClear(); |
| | | break; |
| | | case "undo": |
| | | this.toolUndo(); |
| | | break; |
| | | case "save": |
| | | this.toolSave(); |
| | | break; |
| | | } |
| | | }, |
| | | toolClear() { |
| | | this.context.clearRect(0, 0, this.canvas.width, this.canvas.height); |
| | | this.resetToolActive(); |
| | | }, |
| | | toolSave() { |
| | | const imageDataUrl = this.canvas.toDataURL("image/png"); |
| | | console.log(imageDataUrl); |
| | | // const imgUrl = canvas.toDataURL('image/png'); |
| | | // const el = document.createElement('a'); |
| | | // el.setAttribute('href', imgUrl); |
| | | // el.setAttribute('target', '_blank'); |
| | | // el.setAttribute('download', `graffiti-${Date.now()}`); |
| | | // document.body.appendChild(el); |
| | | // el.click(); |
| | | // document.body.removeChild(el); |
| | | // resetToolActive(); |
| | | }, |
| | | toolUndo() { |
| | | if (this.historyData.length <= 0) { |
| | | this.resetToolActive(); |
| | | return; |
| | | } |
| | | const lastIndex = this.historyData.length - 1; |
| | | this.context.putImageData(this.historyData[lastIndex], 0, 0); |
| | | this.historyData.pop(); |
| | | |
| | | this.resetToolActive(); |
| | | }, |
| | | // 存储数据 |
| | | saveData(data) { |
| | | this.historyData.length >= 50 && this.historyData.shift(); // 设置储存上限为50步 |
| | | this.historyData.push(data); |
| | | }, |
| | | // 清除、撤销、保存状态不需要保持,操作完后恢复笔刷状态 |
| | | resetToolActive() { |
| | | setTimeout(() => { |
| | | this.brushTool = "brush"; |
| | | }, 1000); |
| | | }, |
| | | }, |
| | | }; |
| | | </script> |
| | | |
| | | <style scoped> |
| | | .page { |
| | | display: flex; |
| | | flex-direction: column; |
| | | width: 1038px; |
| | | height: 866px; |
| | | } |
| | | |
| | | .main { |
| | | flex: 1; |
| | | } |
| | | |
| | | .footer { |
| | | display: flex; |
| | | justify-content: space-around; |
| | | align-items: center; |
| | | height: 88px; |
| | | background-color: #fff; |
| | | } |
| | | |
| | | #canvas_panel { |
| | | margin: 12px; |
| | | height: calc(100% - 24px); |
| | | /* 消除空格影响 */ |
| | | font-size: 0; |
| | | background-color: #fff; |
| | | box-shadow: 0px 0px 12px rgba(0, 0, 0, 0.12); |
| | | } |
| | | |
| | | #canvas { |
| | | cursor: crosshair; |
| | | /* background: url('https://t7.baidu.com/it/u=1819248061,230866778&fm=193&f=GIF') no-repeat !important; */ |
| | | } |
| | | </style> |
| | |
| | | window.open(await getResourcePath(this.preViewMd5)); |
| | | }, |
| | | domViewer() { |
| | | let ele = document.getElementById("imageParent"); |
| | | let ele = (this.container ? this.container : document).getElementById( |
| | | "imageParent" |
| | | ); |
| | | this.viewerCon = new Viewer(ele, { |
| | | inline: false, |
| | | container: this.container |