Browse Source

优化富文本编辑器
Signed-off-by: Caner <5658514@qq.com>

Caner 3 years ago
parent
commit
a3402c664c

+ 1 - 0
.gitignore

@@ -29,3 +29,4 @@ build/Release
 node_modules
 .env.development
 dist
+*.lock

+ 5 - 3
package.json

@@ -13,12 +13,14 @@
     "@wangeditor/editor-for-vue": "^5.1.12",
     "axios": "^1.2.2",
     "echarts": "^5.4.1",
-    "pinia": "^2.0.18",
-    "socket.io-client": "^4.5.4",
-    "vue": "^3.2.37",
+    "html2canvas": "^1.4.1",
     "image-size": "^1.0.1",
+    "jspdf": "^2.5.1",
     "openseadragon": "^4.0.0",
+    "pinia": "^2.0.18",
+    "socket.io-client": "^4.5.4",
     "view-ui-plus": "^1.2.0",
+    "vue": "^3.2.37",
     "vue-router": "^4.1.3"
   },
   "devDependencies": {

+ 7 - 9
src/components/editeDemo.vue

@@ -10,7 +10,7 @@
       v-for="s in arrBr"
       :key="s"
       class="brs"
-      :style="`top:${s * 1228 + 30}px`"
+      :style="`top:${s * A4Height + 45}px`"
     >
       PDF分割线
     </div>
@@ -68,8 +68,7 @@ const config = {
 
 // 内容 HTML
 const value = (
-  `
-  <p style="text-indent: 42pt; text-align: left;"><span style="font-size: 19px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span style="font-size: 19px; font-family: 黑体;"> &nbsp;合同编号</span><span style="font-size: 19px; font-family: 黑体;"><u>xxx</u></span></p>
+  `<p style="text-indent: 42pt; text-align: left;"><span style="font-size: 19px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span style="font-size: 19px; font-family: 黑体;"> &nbsp;合同编号</span><span style="font-size: 19px; font-family: 黑体;"><u>xxx</u></span></p>
   <p style="text-indent: 42pt; text-align: left;"><span style="font-size: 19px;"> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; </span><span style="font-size: 19px; font-family: 黑体;"> &nbsp;报告编号</span><span style="font-size: 19px; font-family: 黑体;"><u>xxx</u></span></p>
   <p style="text-indent: 42pt;"><br></p><p style="text-indent: 42pt;"><br></p><p style="text-indent: 42pt;"><br></p><p style="text-indent: 42pt;"><br></p><p style="text-indent: 42pt;"><br></p>
   <p style="text-indent: 42pt;"><br></p><p style="text-align: center;"><span style="font-size: 22px;">XXXtest</span></p>
@@ -82,15 +81,12 @@ const value = (
   <p style="text-align: center;"><br></p><p style="text-align: center;"><br></p>
   <p style="text-align: center;"><br></p><p style="text-align: center;"><span style="color: rgb(0, 0, 0); font-size: 22px;">xxx单位</span></p>
   <p style="text-align: center;"><span style="color: rgb(0, 0, 0); font-size: 22px;">二零二二</span><span style="font-size: 22px;">年xx月xx</span><span style="color: rgb(0, 0, 0); font-size: 22px;">日</span></p>
-  <p style="text-align: center;"><br></p>
-  <p style="text-align: center;"><br></p>
-  <p style="text-align: center;"><br></p>
   `
 ).replace(/[\r\n]/g, '')
 const valueHtml = ref(value)
 
 // A4 默认1228高度
-const A4Height = 1228
+const A4Height = 1023
 // 分割线
 const arrBr = ref(0)
 
@@ -107,6 +103,9 @@ const changes = (editor: any) => {
     if (num !== arrBr.value) {
       arrBr.value = num
     }
+    // 滚动到最底部
+    const editContentDom = document.getElementsByClassName('EditorContent')[0] as HTMLElement
+    editContentDom.scrollTo(0, dom.offsetHeight)
   }
 }
 
@@ -145,7 +144,6 @@ onBeforeUnmount(() => {
 
   .resetStyle {
     width: 850px;
-    min-height: 7rem;
     margin: 30px auto;
     background-color: #fff;
     padding: 20px 50px 50px 50px;
@@ -165,7 +163,7 @@ onBeforeUnmount(() => {
 
   &::-webkit-scrollbar {
     display: block;
-    width: 15px !important; //对垂直方向滚动条
+    width: 8px !important; //对垂直方向滚动条
   }
 
   //滚动的滑块

+ 1 - 0
src/components/loading.vue

@@ -14,6 +14,7 @@
         align-items: center;
         justify-content: center;
         background: rgba(0, 0, 0, 0.5);
+        z-index: 999999;
     }
     .loader {
 

+ 10 - 1
src/pages/App.vue

@@ -9,7 +9,16 @@ import { computed } from 'vue'
 
 const store = useStore()
 const show = computed(() => store.loading)
-console.log('更多好玩的,请修改路径')
+console.log(
+  `More:
+1. '/' 多人画板
+2. '/chart' echart 码数表
+3. '/edit' 富文本编辑
+4. '/fly' 二维动态导向
+5. '/move' 移动组件
+6. '/news' 实时新闻
+7. '/plantool' 平面图缩放`
+)
 
 </script>
 <style>

+ 1 - 1
src/pages/views/edit/index.vue

@@ -16,7 +16,7 @@ onMounted(() => {
 </script>
 <style lang="less" scoped>
 .box {
-    width: 1200px;
+    width: 1000px;
     height: 100vh;
     margin: 0 auto;
 }

+ 143 - 0
src/utils/html2File.ts

@@ -0,0 +1,143 @@
+import html2canvas from "html2canvas";
+import JSPDF from "jspdf";
+import { getActivePinia } from "pinia";
+
+/**
+ * loading
+ * @param type 是否显示
+ */
+const resetLoaing = (type: boolean) => {
+    const store = getActivePinia()
+    if (!store) return
+    const obj = store.state.value
+    for (const key in obj) {
+        const el = obj[key]
+        if ('loading' in el) {
+            el.loading = type
+            break
+        }
+    }
+
+}
+
+/**
+ * 导出PDF
+ * @param domID 需要输出PDF的页面id
+ * @param fileName 文件名
+ * @param type  默认A4分页
+ * @returns 
+ */
+export const exp2pdf = async (domID: string, fileName: string, type = 'A4') => {
+    const dom = document.getElementById(domID)
+    if (!dom) return
+    resetLoaing(true)
+    const canvas = await html2canvas(dom, {
+        logging: false,
+        useCORS: true, // 允许图片跨域   
+        scale: 1.5
+    })
+
+    if (type === 'A4') {
+        // A4分页
+        const pdf = new JSPDF("p", "mm", "a4") // A4纸,纵向
+        const ctx = canvas.getContext("2d") as any
+        const a4w = 200;
+        const a4h = 277 // A4大小,210mm x 297mm,四边各保留20mm的边距
+        const imgHeight = Math.floor(a4h * canvas.width / a4w) // 按A4显示比例换算一页图像的像素高度
+        let renderedHeight = 0
+        while (renderedHeight < canvas.height) {
+            const page = document.createElement("canvas")
+            page.width = canvas.width
+            page.height = Math.min(imgHeight, canvas.height - renderedHeight) // 可能内容不足一页
+            // 用getImageData剪裁指定区域,并画到前面创建的canvas对象中
+            page.getContext("2d", { willReadFrequently: true })?.putImageData(ctx.getImageData(0, renderedHeight, canvas.width, Math.min(imgHeight, canvas.height - renderedHeight)), 0, 0)
+            pdf.addImage(page.toDataURL("image/jpeg", 1.0), "JPEG", 10, 10, a4w, Math.min(a4h, a4w * page.height / page.width)) // 添加图像到页面,保留10mm边距
+            renderedHeight += imgHeight
+            if (renderedHeight < canvas.height) { pdf.addPage() } // 如果后面还有内容,添加一个空页
+            // delete page;
+        }
+        pdf.save(fileName)
+
+    } else {
+        // 整张
+        const domHeight = dom.offsetHeight // 获取DOM高度
+        const domWidth = dom.offsetWidth // 获取DOM宽度        
+        const pdf = new JSPDF('p', 'px', [domWidth, domHeight])
+        pdf.addImage(canvas.toDataURL("image/jpeg", 1.0), "JPEG", 10, 10, domWidth, domHeight)
+        pdf.save(fileName)
+    }
+    resetLoaing(false)
+}
+
+/**
+ * 导出PNG
+ * @param domID 需要输出PNG的页面id
+ * @param fileName 文件名
+ * @param bkcolor 背景色
+ */
+export const exp2png = async (domID: string, fileName: string, bkcolor: string) => {
+    resetLoaing(true)
+    window.scroll(0, 0) // 首先先顶部
+    const design = document.getElementById(domID) as HTMLElement
+    if (!design) return
+    const imgHeight = design.offsetHeight // 获取DOM高度
+    const imgWidth = design.offsetWidth // 获取DOM宽度
+    const scale = window.devicePixelRatio <= 3 ? 3 : window.devicePixelRatio // 获取设备像素比
+    const canvas = await html2canvas(design, {
+        backgroundColor: bkcolor, // 设置背景颜色
+        useCORS: true, // 允许图片跨域
+        scale: scale, // 缩放3倍,使得图片更加清晰=>越清晰图片越大
+        width: imgWidth,
+        height: imgHeight,
+        imageTimeout: 5000 // 设置图片的超时,设置0为禁用
+    })
+    const download = (url: string, fileName: string) => {
+        let aLink = document.createElement('a')
+        aLink.href = url
+        aLink.download = fileName || '' // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效
+        let event
+        if (window.MouseEvent) event = new MouseEvent('click')
+        else {
+            event = document.createEvent('MouseEvents')
+            event.initMouseEvent(
+                'click',
+                true,
+                false,
+                window,
+                0,
+                0,
+                0,
+                0,
+                0,
+                false,
+                false,
+                false,
+                false,
+                0,
+                null
+            )
+        }
+        aLink.dispatchEvent(event)
+    }
+    // 两种下载方式url + blob
+    let imgURL = canvas.toDataURL('image/png') as any
+    if (typeof imgURL === 'object' && imgURL instanceof Blob) {
+        imgURL = URL.createObjectURL(imgURL) // 创建blob地址
+        download(imgURL, fileName)
+    } else {
+        // url  +  请求得到blob
+        let htmlrq = new XMLHttpRequest() as any
+        htmlrq.open('GET', imgURL, true)
+        htmlrq.responseType = 'blob'
+        htmlrq.onload = function (e: any) {
+            if (e.target.status === 200) {
+                imgURL = URL.createObjectURL(e.target.response) // 创建blob地址
+                download(imgURL, fileName)
+            } else {
+                console.error('下载错误')
+            }
+        }
+        htmlrq.send()
+    }
+    resetLoaing(false)
+}

+ 2 - 0
src/utils/registerMenu.js

@@ -1,4 +1,5 @@
 import { Boot } from '@wangeditor/editor'
+import { exp2pdf } from '@/utils/html2File'
 console.warn('此插件注册富文本自定义按钮')
 // 注册一个保存按钮
 class SaveBtn {
@@ -22,6 +23,7 @@ class SaveBtn {
     }
 
     exec() {
+        exp2pdf('exportPDF', '测试' + new Date().toLocaleDateString())
         console.log('导出');
     }
 }