From 651c888579b8b37aafe02b608ac1a4bb4a5a36de Mon Sep 17 00:00:00 2001
From: user1 <10901603+no-distractions1@user.noreply.gitee.com>
Date: 星期二, 25 六月 2024 16:06:43 +0800
Subject: [PATCH] Merge branch 'master' of http://182.92.203.7:2001/r/testbookLayout

---
 src/books/sportsAndHealth/css/default.less           |  118 +---
 .env.product                                         |    4 
 src/components/dragQuestion/index.vue                |  527 ++++++++++++++++++++++
 src/components/pdfview/index.vue                     |    4 
 src/books/sportsAndHealth/view/components/testPp.vue |   82 +++
 src/books/mathBook/view/components/index.vue         |    6 
 src/books/sportsAndHealth/view/components/index.vue  |   42 
 src/books/mathBook/view/components/chapter001.vue    |   83 +++
 src/components/graffiti/components/toolBtns.vue      |   74 +++
 src/components/graffiti/index.vue                    |  234 ++++++++++
 src/components/graffiti/components/brushSize.vue     |   83 +++
 src/components/examinations/index.vue                |   37 
 src/components/graffiti/components/colorPicker.vue   |   61 ++
 13 files changed, 1,216 insertions(+), 139 deletions(-)

diff --git a/.env.product b/.env.product
index 76a6d12..c9c1245 100644
--- a/.env.product
+++ b/.env.product
@@ -1,6 +1,6 @@
 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'
\ No newline at end of file
+VUE_APP_BOOK_ID = 'sportsAndHealth'
\ No newline at end of file
diff --git a/src/books/mathBook/view/components/chapter001.vue b/src/books/mathBook/view/components/chapter001.vue
index 9cf0e20..adbe2e5 100644
--- a/src/books/mathBook/view/components/chapter001.vue
+++ b/src/books/mathBook/view/components/chapter001.vue
@@ -575,7 +575,10 @@
         </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"
@@ -820,9 +823,11 @@
 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,
@@ -886,6 +891,80 @@
           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: {
diff --git a/src/books/mathBook/view/components/index.vue b/src/books/mathBook/view/components/index.vue
index 44910de..35bafa6 100644
--- a/src/books/mathBook/view/components/index.vue
+++ b/src/books/mathBook/view/components/index.vue
@@ -162,8 +162,8 @@
     }, 500);
 
     // 娴嬭瘯椤甸潰璺宠浆
-    // setTimeout(() => {
-    //   this.gotoPage(1, 10);
+    setTimeout(() => {
+      this.gotoPage(2, 10);
     //   setTimeout(() => {
     //     this.renderSign("Highlight", {
     //       id: "2ACA9359",
@@ -191,7 +191,7 @@
     //   txt: " 杩愬姩绯荤粺鏄敱楠ㄣ�侀杩炵粨鍜岄楠艰倢涓夐儴鍒嗙粍鎴愮殑銆傚叏韬殑楠ㄩ�氳繃楠ㄨ繛缁撶粍鎴愪汉浣撻楠硷紙瑙佸浘1-1锛夈�傞楠兼槸浜轰綋鐨勬敮鏋讹紝鍏锋湁淇濇姢鍐呰剰鍣ㄥ畼銆佷緵鑲岃倝闄勭潃鍜屼綔涓鸿倢鑲夎繍鍔ㄧ殑鏉犳潌绛変綔鐢ㄣ�傚湪绁炵粡绯荤粺鐨勬敮閰嶄笅锛岃倢鑲夋敹缂╃壍鍔ㄦ墍闄勭潃鐨勯缁曠潃鍏宠妭杞姩锛屼娇韬綋浜х敓鍚勭鍔ㄤ綔銆傛墍浠ワ紝杩愬姩绯荤粺鍏锋湁杩愬姩銆佹敮鎸佸拰淇濇姢绛夊姛鑳斤紝骞煎勾鏃舵湡鐨勯楠艰繕鍏锋湁閫犺鍔熻兘銆� ",
     //   txtIndex: 57
     // });
-    // }, 5000);
+    }, 500);
 
     // 鑾峰彇棰樼洰id鍒楄〃
     this.getQuestionId();
diff --git a/src/books/sportsAndHealth/css/default.less b/src/books/sportsAndHealth/css/default.less
index 61eedbb..000358d 100644
--- a/src/books/sportsAndHealth/css/default.less
+++ b/src/books/sportsAndHealth/css/default.less
@@ -20,7 +20,7 @@
       .chapter {
         textarea {
           width: 100%;
-          font-family: 'FZLTXIHJW';
+          font-family: "FZLTXIHJW";
           padding: 2px 5px;
           box-sizing: border-box;
         }
@@ -86,7 +86,7 @@
 
           span {
             margin-right: 28px;
-            font-family: 'FZLTXIHJW';
+            font-family: "FZLTXIHJW";
             font-size: 18px;
           }
           span:nth-child(3) {
@@ -116,7 +116,7 @@
           width: 1px;
           height: 100%;
           border-left: 2px dotted #eee;
-          margin:0 14px;
+          margin: 0 14px;
         }
 
         .header-even {
@@ -127,7 +127,7 @@
           align-items: flex-end;
           span {
             margin-left: 28px;
-            font-family: 'FZLTXIHJW';
+            font-family: "FZLTXIHJW";
             font-size: 18px;
           }
 
@@ -140,7 +140,7 @@
             font-weight: 700;
           }
 
-          span:nth-child(3){
+          span:nth-child(3) {
             margin-left: 0;
           }
         }
@@ -172,7 +172,7 @@
               span {
                 font-size: 14px;
                 text-align: center;
-                font-family: 'FZLTXIHJW';
+                font-family: "FZLTXIHJW";
                 display: flex;
                 align-items: center;
                 justify-content: flex-start;
@@ -240,7 +240,7 @@
               justify-content: center;
               font-size: 14px;
               text-align: center;
-              font-family: 'FZLTXIHJW';
+              font-family: "FZLTXIHJW";
             }
           }
         }
@@ -264,7 +264,7 @@
           justify-content: center;
           align-items: center;
           padding-top: 20px;
-          font-family: 'FZLTXIHJW';
+          font-family: "FZLTXIHJW";
 
           .table-bottom {
             width: 100%;
@@ -303,7 +303,7 @@
 
               span:hover {
                 background-color: #89a0d0;
-                color: #FFF;
+                color: #fff;
               }
 
               span:nth-child(1) {
@@ -326,9 +326,9 @@
               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 {
@@ -337,7 +337,7 @@
               padding: 3px 10px;
               font-size: 14px;
               line-height: 25px;
-              font-family: 'FZLTXIHJW';
+              font-family: "FZLTXIHJW";
 
               input {
                 cursor: pointer;
@@ -356,7 +356,7 @@
           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;
@@ -384,7 +384,7 @@
           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;
@@ -398,7 +398,7 @@
           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;
@@ -419,9 +419,8 @@
 
           p {
             font-size: 16px;
-            font-family: 'FZLTXIHJW';
+            font-family: "FZLTXIHJW";
           }
-
         }
 
         .bk-xyx {
@@ -568,7 +567,7 @@
               span {
                 font-size: 14px;
                 text-align: center;
-                font-family: 'FZLTXIHJW';
+                font-family: "FZLTXIHJW";
                 display: flex;
                 align-items: center;
                 justify-content: flex-start;
@@ -612,7 +611,6 @@
           text-indent: 2em;
         }
 
-
         .xyx-title {
           font-family: "FZLTXIHJW";
           text-align: center;
@@ -624,7 +622,7 @@
         .xyx-text {
           p {
             font-size: 16px;
-            font-family: 'FZLTXIHJW';
+            font-family: "FZLTXIHJW";
             padding: 0 10px;
             margin: 0;
           }
@@ -715,7 +713,7 @@
           background-color: transparent;
           border-bottom: 1px solid #000;
           font-size: 18px;
-          font-family: '瀹嬩綋';
+          font-family: "瀹嬩綋";
         }
 
         .assess:focus {
@@ -1551,7 +1549,7 @@
               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;
@@ -1690,7 +1688,7 @@
         }
 
         .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%;
@@ -1713,8 +1711,6 @@
       }
 
       @media screen and (max-width: 800px) {
-
-
         .chapter {
           .coverImg {
             height: 100%;
@@ -1759,7 +1755,7 @@
                 span {
                   font-size: 14px;
                   text-align: center;
-                  font-family: 'FZLTXIHJW';
+                  font-family: "FZLTXIHJW";
                   line-height: 20px;
                 }
 
@@ -1786,7 +1782,6 @@
             justify-content: center;
             flex-wrap: wrap;
           }
-
 
           .bj-img {
             display: flex;
@@ -1820,7 +1815,7 @@
             box-sizing: border-box;
           }
 
-          .tablePublic table tr th{
+          .tablePublic table tr th {
             padding: 0;
             width: auto !important;
           }
@@ -1831,10 +1826,8 @@
         }
       }
 
-      @media screen and (max-width:700px) {
-
+      @media screen and (max-width: 700px) {
         .chapter {
-
           .coverImg {
             margin: 0;
           }
@@ -1855,13 +1848,11 @@
               position: relative;
             }
           }
-
         }
       }
 
-      @media screen and (max-width:600px) {
+      @media screen and (max-width: 600px) {
         .chapter {
-
           .coverImg {
             margin: 0;
             height: 100%;
@@ -1914,7 +1905,6 @@
             }
           }
 
-
           .bj-empyt-chapter {
             height: 30%;
           }
@@ -1931,60 +1921,8 @@
   }
 }
 
-.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;
   /* 閫変腑鏃剁殑鑳屾櫙鑹� */
 }
@@ -2014,8 +1952,6 @@
   user-select: none;
 }
 
-
-
 body {
   background-color: #e6e6e6;
-}
\ No newline at end of file
+}
diff --git a/src/books/sportsAndHealth/view/components/index.vue b/src/books/sportsAndHealth/view/components/index.vue
index 155b932..0e773bb 100644
--- a/src/books/sportsAndHealth/view/components/index.vue
+++ b/src/books/sportsAndHealth/view/components/index.vue
@@ -26,6 +26,7 @@
       <ChapterThree
         v-if="showCatalogList.indexOf(4) > -1"
         :showPageList="loadPageList"
+        @openPDFChange="swdtChange"
       />
       <!-- <ChapterFour
         v-if="showCatalogList.indexOf(5) > -1"
@@ -281,11 +282,18 @@
   },
   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("瀛愬簲鐢╬df", 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() {
@@ -724,23 +732,19 @@
           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(
@@ -763,12 +767,8 @@
           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,
@@ -787,8 +787,8 @@
               var paginationInfoEl = dom.querySelector(".pageBox");
               if (paginationInfoEl)
                 paginationInfoEl.textContent = currentPage + "/" + totalPages;
-            }
-          }
+            },
+          },
         });
       }
     },
diff --git a/src/books/sportsAndHealth/view/components/testPp.vue b/src/books/sportsAndHealth/view/components/testPp.vue
index d95e2f5..16f5d1d 100644
--- a/src/books/sportsAndHealth/view/components/testPp.vue
+++ b/src/books/sportsAndHealth/view/components/testPp.vue
@@ -1461,7 +1461,6 @@
         :visible.sync="dialogVisible"
         width="60vw"
         top="2vh"
-        append-to-body
         lock-scroll
         :show-close="false"
         class="custom-dialog"
@@ -1693,9 +1692,18 @@
     },
     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) {
@@ -1730,3 +1738,69 @@
   },
 };
 </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>
diff --git a/src/components/dragQuestion/index.vue b/src/components/dragQuestion/index.vue
new file mode 100644
index 0000000..f8e81da
--- /dev/null
+++ b/src/components/dragQuestion/index.vue
@@ -0,0 +1,527 @@
+<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>姝g‘绛旀锛�</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>
+                <!-- 姝g‘閿欒鍥炬爣 -->
+                <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">灞曞紑瑙f瀽</span><el-image />
+                  </p>
+                  <p v-else>
+                    <span class="analysisColor">鏀惰捣瑙f瀽</span><el-image />
+                  </p>
+                </div>
+              </div>
+            </template>
+            <!-- 瑙f瀽 -->
+            <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(/&nbsp;/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%;
+}
+/** 瑙f瀽 */
+.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>
diff --git a/src/components/examinations/index.vue b/src/components/examinations/index.vue
index 98120c7..eddf0e0 100644
--- a/src/components/examinations/index.vue
+++ b/src/components/examinations/index.vue
@@ -561,7 +561,7 @@
   },
   mounted() {
     this.getCollectIdList();
-    this.getErrorList()
+    this.getErrorList();
   },
   methods: {
     // 鏁扮粍杞负瀛楃涓叉柟娉�
@@ -621,7 +621,7 @@
     },
     // 鎵规敼棰樼洰  (缁冧範,鎴戠殑鍋氶,鎴戠殑鏀惰棌妯″紡涓�)
     handleQuestion() {
-      let errorId = []
+      let errorId = [];
       const list = this.cardData;
       for (let index = 0; index < list.length; index++) {
         const item = list[index];
@@ -665,36 +665,38 @@
               }
             }
           }
-          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) {
@@ -792,6 +794,8 @@
     },
     // 鑾峰彇鏀惰棌id鍒楄〃
     getCollectIdList() {
+      const token = localStorage.getItem("token");
+      if (!token) return false;
       this.MG.identity
         .getUserKey({
           domain: "collectData",
@@ -821,6 +825,8 @@
     },
     // 鑾峰彇閿欓id鍒楄〃
     getErrorList() {
+      const token = localStorage.getItem("token");
+      if (!token) return false;
       this.MG.identity
         .getUserKey({
           domain: "errorData",
@@ -841,7 +847,8 @@
               ).errorList;
             }
           } catch (error) {}
-        });
+        })
+        .catch(() => {});
     },
   },
 };
diff --git a/src/components/graffiti/components/brushSize.vue b/src/components/graffiti/components/brushSize.vue
new file mode 100644
index 0000000..9c47a7c
--- /dev/null
+++ b/src/components/graffiti/components/brushSize.vue
@@ -0,0 +1,83 @@
+<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>
diff --git a/src/components/graffiti/components/colorPicker.vue b/src/components/graffiti/components/colorPicker.vue
new file mode 100644
index 0000000..4cde0de
--- /dev/null
+++ b/src/components/graffiti/components/colorPicker.vue
@@ -0,0 +1,61 @@
+<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>
diff --git a/src/components/graffiti/components/toolBtns.vue b/src/components/graffiti/components/toolBtns.vue
new file mode 100644
index 0000000..aad1a3f
--- /dev/null
+++ b/src/components/graffiti/components/toolBtns.vue
@@ -0,0 +1,74 @@
+<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>
diff --git a/src/components/graffiti/index.vue b/src/components/graffiti/index.vue
new file mode 100644
index 0000000..8980a01
--- /dev/null
+++ b/src/components/graffiti/index.vue
@@ -0,0 +1,234 @@
+<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);
+    },
+
+    // 璁$畻褰撳墠榧犳爣鐩稿浜巆anvas鐨勫潗鏍�
+    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>
diff --git a/src/components/pdfview/index.vue b/src/components/pdfview/index.vue
index a7405cb..b69c934 100644
--- a/src/components/pdfview/index.vue
+++ b/src/components/pdfview/index.vue
@@ -78,7 +78,9 @@
       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

--
Gitblit v1.9.1