Bläddra i källkod

canvas-editor

zhuliu 1 år sedan
förälder
incheckning
8c1670e1a4
71 ändrade filer med 5175 tillägg och 2 borttagningar
  1. 3 0
      package.json
  2. 1 0
      src/assets/images/alignment.svg
  3. 1 0
      src/assets/images/analysis.svg
  4. 1 0
      src/assets/images/arrow-left.svg
  5. 1 0
      src/assets/images/arrow-right.svg
  6. 1 0
      src/assets/images/block.svg
  7. 1 0
      src/assets/images/bold.svg
  8. 1 0
      src/assets/images/catalog.svg
  9. 1 0
      src/assets/images/center.svg
  10. 1 0
      src/assets/images/checkbox.svg
  11. 1 0
      src/assets/images/close.svg
  12. 1 0
      src/assets/images/codeblock.svg
  13. 1 0
      src/assets/images/color.svg
  14. 1 0
      src/assets/images/control.svg
  15. 1 0
      src/assets/images/date.svg
  16. 1 0
      src/assets/images/exit-fullscreen.svg
  17. 1 0
      src/assets/images/export_WORD.svg
  18. 1 0
      src/assets/images/format.svg
  19. 1 0
      src/assets/images/highlight.svg
  20. 1 0
      src/assets/images/hyperlink.svg
  21. 1 0
      src/assets/images/image.svg
  22. 1 0
      src/assets/images/italic.svg
  23. 1 0
      src/assets/images/latex.svg
  24. 1 0
      src/assets/images/left.svg
  25. 1 0
      src/assets/images/line-dash-dot-dot.svg
  26. 1 0
      src/assets/images/line-dash-dot.svg
  27. 1 0
      src/assets/images/line-dash-large-gap.svg
  28. 1 0
      src/assets/images/line-dash-small-gap.svg
  29. 1 0
      src/assets/images/line-dot.svg
  30. 1 0
      src/assets/images/line-single.svg
  31. 1 0
      src/assets/images/list.svg
  32. 1 0
      src/assets/images/page-break.svg
  33. 1 0
      src/assets/images/page-mode.svg
  34. 1 0
      src/assets/images/page-scale-add.svg
  35. 1 0
      src/assets/images/page-scale-minus.svg
  36. 1 0
      src/assets/images/painter.svg
  37. 1 0
      src/assets/images/paper-direction.svg
  38. 1 0
      src/assets/images/paper-margin.svg
  39. 1 0
      src/assets/images/paper-size.svg
  40. 1 0
      src/assets/images/print.svg
  41. 1 0
      src/assets/images/redo.svg
  42. 1 0
      src/assets/images/request-fullscreen.svg
  43. 1 0
      src/assets/images/right.svg
  44. 1 0
      src/assets/images/row-margin.svg
  45. 1 0
      src/assets/images/search.svg
  46. 1 0
      src/assets/images/separator.svg
  47. 1 0
      src/assets/images/signature-undo.svg
  48. 1 0
      src/assets/images/signature.svg
  49. 1 0
      src/assets/images/size-add.svg
  50. 1 0
      src/assets/images/size-minus.svg
  51. 1 0
      src/assets/images/strikeout.svg
  52. 1 0
      src/assets/images/subscript.svg
  53. 1 0
      src/assets/images/superscript.svg
  54. 1 0
      src/assets/images/table.svg
  55. 1 0
      src/assets/images/title.svg
  56. 1 0
      src/assets/images/trash.svg
  57. 1 0
      src/assets/images/underline.svg
  58. 1 0
      src/assets/images/undo.svg
  59. 1 0
      src/assets/images/watermark.svg
  60. 1 0
      src/assets/images/word-tool.svg
  61. 10 2
      src/router/index.js
  62. 830 0
      src/views/analyse/custom/index-benshen.vue
  63. 0 0
      src/views/analyse/custom/index-yuanshi.vue
  64. 63 0
      src/views/components/editor/components/analysis/index.vue
  65. 164 0
      src/views/components/editor/components/dialog/Dialog.js
  66. 131 0
      src/views/components/editor/components/dialog/dialog.css
  67. 309 0
      src/views/components/editor/components/signature/Signature.js
  68. 128 0
      src/views/components/editor/components/signature/signature.css
  69. 2431 0
      src/views/components/editor/editor.vue
  70. 31 0
      src/views/components/editor/index.vue
  71. 1016 0
      src/views/components/editor/style.css

+ 3 - 0
package.json

@@ -8,6 +8,7 @@
     "dev": "vue-cli-service serve"
   },
   "dependencies": {
+    "@hufe921/canvas-editor": "^0.9.86",
     "@riophae/vue-treeselect": "^0.4.0",
     "core-js": "^3.6.5",
     "driver.js": "^1.3.0",
@@ -44,6 +45,8 @@
     "compression-webpack-plugin": "^5.0.2",
     "eslint": "^6.7.2",
     "eslint-plugin-vue": "^6.2.2",
+    "file-saver": "^2.0.5",
+    "html-docx-js": "^0.3.1",
     "html-webpack-inline-source-plugin": "^0.0.10",
     "lodash": "^4.17.21",
     "pdfjs-dist": "^2.0.943",

+ 1 - 0
src/assets/images/alignment.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M2 13h12v1H2v-1zm0-3h12v1H2v-1zm0-3h12v1H2V7zm0-6h12v1H2V1zm0 3h12v1H2V4z" fill="#3D4757" fill-rule="evenodd"/></svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/analysis.svg


+ 1 - 0
src/assets/images/arrow-left.svg

@@ -0,0 +1 @@
+<svg width="4" height="7" xmlns="http://www.w3.org/2000/svg"><path fill="#6F6F6F" d="M0 3.5L4 0v7z" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/arrow-right.svg

@@ -0,0 +1 @@
+<svg width="4" height="7" xmlns="http://www.w3.org/2000/svg"><path fill="#6F6F6F" d="M4 3.5L0 0v7z" fill-rule="evenodd"/></svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/block.svg


+ 1 - 0
src/assets/images/bold.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8.131 6.9c2.035 0 2.569-.9 2.569-1.869 0-.968-.64-1.831-2.623-1.831H5.2v3.7h2.931zm.524 5.9c2.045 0 2.545-1.305 2.545-2.3 0-.985-.506-2.4-2.81-2.4H5.2v4.7h3.455zM4 2h4.71c2.367 0 3.19 1.583 3.19 3s-.325 1.852-1.1 2.5c1.2.5 1.569 1.379 1.6 3 .03 1.606-.586 3.5-3.769 3.5H4V2z" fill="#3D4757" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/catalog.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757"><path d="M13 0c.552 0 1 .48 1 1.071V13.93c0 .59-.448 1.07-1 1.07H3c-.552 0-1-.48-1-1.071V1.07C2 .48 2.448 0 3 0h10zm0 1H3v13h10V1z"/><path d="M5 10v1H4v-1h1zm6 0v1H6v-1h5zM5 7v1H4V7h1zm6 0v1H6V7h5zM5 4v1H4V4h1zm6 0v1H6V4h5z"/></g></svg>

+ 1 - 0
src/assets/images/center.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M2 13h12v1H2v-1zm2-3h8v1H4v-1zM2 7h12v1H2V7zm0-6h12v1H2V1zm2 3h8v1H4V4z" fill="#3D4757" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/checkbox.svg

@@ -0,0 +1 @@
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" x="0" y="0" viewBox="0 0 16 16" xml:space="preserve"><style>.st0{fill:#3d4757}</style><g id="_x30_1-文字_x2F_01开始_x2F_任务列表-16px"><path id="合并形状" class="st0" d="M10.1 2H2v11h11V8.7l1-1V13c0 .6-.4 1-1 1H2c-.6 0-1-.4-1-1V2c0-.6.4-1 1-1h9.1l-1 1z"/><path id="路径" class="st0" d="M7.7 8.5l5.7-5.8.9.8-6.1 5.9-.5.5-3.9-3.4.8-.7z"/></g></svg>

+ 1 - 0
src/assets/images/close.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M8.9 8.192l4.242 4.243-.707.707L8.192 8.9 3.95 13.142l-.707-.707 4.242-4.243L3.243 3.95l.707-.707 4.242 4.242 4.243-4.242.707.707L8.9 8.192z" fill="#6A6A6A" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/codeblock.svg

@@ -0,0 +1 @@
+<svg width="14" height="14" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path stroke="#525C6F" stroke-linejoin="round" d="M4 4.5L1.5 7 4 9.5M10 4.5L12.5 7 10 9.5"></path><rect fill="#525C6F" transform="scale(1 -1) rotate(70 16.711 0)" x="2.671" y="6.53" width="8" height="1" rx=".2"></rect></g></svg>

+ 1 - 0
src/assets/images/color.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M7.997 3.429L6.398 8h3.2L7.997 3.429zM8.497 2L12 12h-1L9.949 9h-3.9L5 12H4L7.496 2h1z" fill="#3D4757" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/control.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M9.793 1.5H3a.5.5 0 00-.5.5v12a.5.5 0 00.5.5h9a.5.5 0 00.5-.5V4.207L9.793 1.5z" stroke="#3D4757"/><g fill="#3D4757"><path d="M7 7h1v5H7z"/><path d="M5 7h5v1H5z"/></g><path fill="#3D4757" fill-rule="nonzero" d="M9 2h1v3H9z"/><path fill="#3D4757" fill-rule="nonzero" d="M9 4h3v1H9z"/></g></svg>

+ 1 - 0
src/assets/images/date.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path d="M7.5 14.154a5.654 5.654 0 100-11.308 5.654 5.654 0 000 11.308zm0 .846a6.5 6.5 0 110-13 6.5 6.5 0 010 13z" fill-rule="nonzero"/><path d="M8 8h4v1H7V4h1v4z"/></g></svg>

+ 1 - 0
src/assets/images/exit-fullscreen.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path d="M9 9h1v4H9z"/><path d="M9 9h4v1H9zM7 7H6V3h1z"/><path d="M7 7H3V6h4z"/></g></svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/export_WORD.svg


+ 1 - 0
src/assets/images/format.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path d="M8.213 13H6.8l6.636-6.636-4.243-4.243-7.07 7.071L5.928 13H4.515L1.06 9.546a.5.5 0 010-.707L8.839 1.06a.5.5 0 01.707 0l4.95 4.95a.5.5 0 010 .707L8.213 13z" fill-rule="nonzero"/><path d="M4.536 6.364l4.95 4.95-.707.707-4.95-4.95zM4.521 13h10.03v1H5.496z"/></g></svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/highlight.svg


+ 1 - 0
src/assets/images/hyperlink.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g transform="rotate(45 5.5 13.328)" fill="#3D4757" fill-rule="evenodd"><path d="M7 6H6V3.515a2.5 2.5 0 10-5 0V6H0V3.515a3.5 3.5 0 117 0V6zm0 4v2.5a3.5 3.5 0 01-7 0V10h1v2.5a2.5 2.5 0 105 0V10h1z" fill-rule="nonzero"/><rect x="3.062" y="5.209" width="1" height="5.5" rx=".5"/></g></svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/image.svg


+ 1 - 0
src/assets/images/italic.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M10.017 3L8.08 13H9v1H6v-1h1.182L9 3H8V2h3v1h-.983z" fill="#3D4757"/></svg>

+ 1 - 0
src/assets/images/latex.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path d="M9 13.5H2.982a.5.5 0 01-.404-.794l3.208-4.412a.5.5 0 000-.588L2.578 3.294a.5.5 0 01.404-.794H9" stroke="#3D4757"/><path d="M14.447 7l.809.588-2.139 2.942 2.139 2.942-.81.588-1.946-2.68-1.947 2.68-.809-.588 2.138-2.942-2.138-2.942.81-.588L12.5 9.679 14.447 7z" fill="#3D4757"/></g></svg>

+ 1 - 0
src/assets/images/left.svg

@@ -0,0 +1 @@
+<svg height="16" viewBox="0 0 16 16" width="16" xmlns="http://www.w3.org/2000/svg"><path d="M2 13h12v1H2zm0-3h8v1H2zm0-3h12v1H2zm0-6h12v1H2zm0 3h8v1H2z" fill="#3d4757" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/line-dash-dot-dot.svg

@@ -0,0 +1 @@
+<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M4 10h5v1H4zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2zm4 0h5v1h-5zm7 0h2v1h-2zm4 0h2v1h-2z" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/line-dash-dot.svg

@@ -0,0 +1 @@
+<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M4 10h6v1H4zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3zm6 0h6v1h-6zm9 0h3v1h-3z" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/line-dash-large-gap.svg

@@ -0,0 +1 @@
+<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M5 10h4v1H5zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4zm8 0h4v1h-4z" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/line-dash-small-gap.svg

@@ -0,0 +1 @@
+<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M4 10h4v1H4zm5 0h4v1H9zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h4v1h-4zm5 0h3v1h-3z" fill-rule="evenodd"/></svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/line-dot.svg


+ 1 - 0
src/assets/images/line-single.svg

@@ -0,0 +1 @@
+<svg width="126" height="20" xmlns="http://www.w3.org/2000/svg"><path d="M4 10h118v1H4z" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/list.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#3D4757" d="M7 12h7v1H7zm0-5h7v1H7zm0-5h7v1H7zM3 4V2H2V1h2v3h1v1H2V4h1z"/><path d="M2 6h3v1H2V6zm0 3h3v1H2V9z" fill="#4F4F4F"/><path fill="#3D4757" fill-rule="nonzero" d="M4.5 6L5 7l-2.5 3L2 9z"/><path d="M4 14l-1-2H2v-1h3v1H4l1 2v1H2v-1h2z" fill="#3D4757"/></g></svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/page-break.svg


+ 1 - 0
src/assets/images/page-mode.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g transform="translate(2 1)"><rect stroke="#3D4757" x=".5" y=".5" width="11" height="8" rx="1"/><path fill="#3D4757" fill-rule="nonzero" d="M2 3h4v1H2zm0 2h8v1H2z"/></g><g fill="#3D4757" fill-rule="nonzero"><path d="M14 15h-1v-5H3v5H2V9h12v6z"/><path d="M4 12h4v1H4zm0 2h8v1H4z"/></g></g></svg>

+ 1 - 0
src/assets/images/page-scale-add.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M7 7V3h2v4h4v2H9v4H7V9H3V7h4z" fill="#636363" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/page-scale-minus.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M3 7h10v2H3z" fill="#636363" fill-rule="evenodd"/></svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/painter.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/paper-direction.svg


+ 1 - 0
src/assets/images/paper-margin.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><rect stroke="#3D4757" x="2.5" y="1.5" width="11" height="13" rx="1"/><path fill="#3D4757" fill-rule="nonzero" d="M3 4h10v1H3zm0 7h10v1H3z"/><path fill="#3D4757" fill-rule="nonzero" d="M5 14V2h1v12zm5 0V2h1v12z"/></g></svg>

+ 1 - 0
src/assets/images/paper-size.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><g fill-rule="nonzero"><path d="M10.793 1.5L13.5 4.207V14L3 14.5 2.5 2l8.293-.5z" stroke="#3D4757"/><path fill="#3D4757" d="M10 2h1v3h-1z"/><path fill="#3D4757" d="M10 4h3v1h-3z"/></g><path d="M7 3v1H5v2H4V3h3z" fill="#3D4757"/><path fill="#3D4757" d="M5.169 3.43l6.62 8.784-.799.602-6.62-8.785z"/><path d="M9 13v-1h2v-2h1v3H9z" fill="#3D4757"/></g></svg>

+ 1 - 0
src/assets/images/print.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path d="M12 4h-1V2H5v2H4V2a1 1 0 011-1h6a1 1 0 011 1v2zm0 5v4a1 1 0 01-1 1H5a1 1 0 01-1-1V9h1v4h6V9h1z"/><path d="M12 12v-1h2V5H2v6h2v1H2a1 1 0 01-1-1V5a1 1 0 011-1h12a1 1 0 011 1v6a1 1 0 01-1 1h-2z"/><path d="M3 8h10v1H3zm8-2h2v1h-2z"/></g></svg>

+ 1 - 0
src/assets/images/redo.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M3 14v-3a4 4 0 014-4h3V6H7a5 5 0 00-5 5v3h1zm7.016-11.282v7.543l4.29-3.73z" fill="#3D4757"/></svg>

+ 1 - 0
src/assets/images/request-fullscreen.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path d="M3 3h1v4H3z"/><path d="M3 3h4v1H3zm10 10h-1V9h1z"/><path d="M13 13H9v-1h4z"/></g></svg>

+ 1 - 0
src/assets/images/right.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M2 13h12v1H2v-1zm4-3h8v1H6v-1zM2 7h12v1H2V7zm0-6h12v1H2V1zm4 3h8v1H6V4z" fill="#3D4757" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/row-margin.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M2 14h12v1H2v-1zm7-5h5v1H9V9zm0-3h5v1H9V6zM2 1h12v1H2V1zm2.5 3L7 7H2zm0 8L7 9H2z" fill="#3D4757" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/search.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><circle stroke="#3D4757" cx="6" cy="6" r="4.5"/><path d="M10.061 10.968L8.707 9.414l.707-.707 1.514 1.457.435-.404 2.632 2.462a1.154 1.154 0 01.05 1.635 1.184 1.184 0 01-1.655.064l-2.788-2.527.46-.426z" fill="#3D4757" fill-rule="nonzero"/></g></svg>

+ 1 - 0
src/assets/images/separator.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><path d="M3.9 2.8l2.01 2.506c.2.25.58.25.78 0L8.7 2.799l2.01 2.507c.2.25.58.25.78 0l2.4-2.993-.78-.626-2.01 2.507-2.01-2.507a.5.5 0 00-.78 0L6.3 4.194 4.29 1.687a.5.5 0 00-.78 0l-2.4 3 .78.625L3.9 2.8zM1 8h13v1H1zm0 4h3v1H1zm5 0h3v1H6zm5 0h3v1h-3z" fill="#3D4757"/></svg>

+ 1 - 0
src/assets/images/signature-undo.svg

@@ -0,0 +1 @@
+<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><path d="M8 6h5.5a7 7 0 010 14v-2a5 5 0 000-10H8v3L4 7l4-4v3z" fill="#3d4757" fill-rule="evenodd"/></svg>

+ 1 - 0
src/assets/images/signature.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><path fill="#3D4757" d="M2 13h12v1H2z"/><path fill="#4B5463" fill-rule="nonzero" d="M5.26 8.458l2.23 1.28L6 12H3z"/><path d="M5.666 6.149L4.31 8.168l4.045 2.48 1.503-2.159-4.192-2.34zm1.106-4.156l-1.986 3.44 6.3 3.648 2.037-2.943-6.351-4.145z" stroke="#4B5463"/></g></svg>

+ 1 - 0
src/assets/images/size-add.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path d="M6.215 3.29H7.64L11.855 14H10.52l-1.14-3H4.46l-1.14 3H2L6.215 3.29zM4.85 9.965h4.14L6.965 4.61h-.06L4.85 9.965z"/><path d="M12 4V2h1v2h2v1h-2v2h-1V5h-2V4h2z" fill-rule="nonzero"/></g></svg>

+ 1 - 0
src/assets/images/size-minus.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path fill-rule="nonzero" d="M11 4h4v1h-4z"/><path d="M6.215 3.29H7.64L11.855 14H10.52l-1.14-3H4.46l-1.14 3H2L6.215 3.29zM4.85 9.965h4.14L6.965 4.61h-.06L4.85 9.965z"/></g></svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/strikeout.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/subscript.svg


Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/superscript.svg


+ 1 - 0
src/assets/images/table.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="none" fill-rule="evenodd"><rect x=".5" y=".5" width="12" height="12" rx="1" transform="translate(1 1)" stroke="#3D4757"/><path fill="#3D4757" fill-rule="nonzero" d="M2 9h12v1H2zm0-4h12v1H2z"/><path fill="#3D4757" fill-rule="nonzero" d="M5 1h1v13H5zm4 0h1v13H9z"/></g></svg>

+ 1 - 0
src/assets/images/title.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path d="M14 1a1 1 0 011 1v12a1 1 0 01-1 1H2a1 1 0 01-1-1V2a1 1 0 011-1h12zm0 1H2v12h12V2z" fill-rule="nonzero"/><path fill-rule="nonzero" d="M4 11h8v1H4zm0-3h4v1H4zm0-3h2v1H4z"/><path d="M9 5h1v4H9z"/><path d="M7 5h5v1H7z"/></g></svg>

+ 1 - 0
src/assets/images/trash.svg

@@ -0,0 +1 @@
+<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg"><g fill="#3d4757" fill-rule="evenodd"><path d="M17 3v4h4v2h-2v13H6V9H4V7h4V3h9zm0 6H8v11h9V9zm-2-4h-5v2h5V5z" fill-rule="nonzero"/><path d="M10 10h2v7h-2zm3 0h2v7h-2z"/></g></svg>

+ 1 - 0
src/assets/images/underline.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M5 2v6a3 3 0 106 0V2h1v6a4 4 0 11-8 0V2h1zM4 13h8v1H4z" fill="#3D4757"/></svg>

+ 1 - 0
src/assets/images/undo.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M6 2.763v7.544l-4.29-3.73zM13 14v-3a4 4 0 00-4-4H6V6h3a5 5 0 015 5v3h-1z" fill="#3D4757"/></svg>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1 - 0
src/assets/images/watermark.svg


+ 1 - 0
src/assets/images/word-tool.svg

@@ -0,0 +1 @@
+<svg width="16" height="16" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path d="M1 0h1v15H1zm5 4h7v1H6zm-3 6h7v1H3z"/><path d="M1 0h4v1H1zm10 14h4v1h-4zM3 7h10v1H3z"/><path d="M14 0h1v15h-1zM8 0l3 3V0zm6 0h-3v1h3zM8 15l-3-3v3zm-6-1h3v1H2z"/></g></svg>

+ 10 - 2
src/router/index.js

@@ -18,7 +18,15 @@ const routes = [
     path: "/",
     component: () => import('@/views/index'),
   },
-
+  {
+    path:'/editor',
+    name:'editor',
+    meta: {
+      title: '编辑器',
+      notLogin:true
+    },
+    component: () => import("@/views/components/editor/index.vue")
+  },
   {
     path: '/login',
     name: 'Login',
@@ -587,7 +595,7 @@ router.beforeEach((to, from, next) => {
   if(from.path.indexOf('/patentDetails/')!=-1 && to.path.indexOf('/patentDetails/')==-1){
     sessionStorage.setItem('search', JSON.stringify({}))
   }
-  if (to.path === '/login' || to.path === '/' || to.path == '/agreeConceal') {
+  if (to.path === '/login' || to.path === '/' || to.path == '/agreeConceal'||to.meta.notLogin) {
     if (to.meta.title) {
       document.title = to.meta.title;
     }

+ 830 - 0
src/views/analyse/custom/index-benshen.vue

@@ -0,0 +1,830 @@
+<template>
+  <div class="height_100 custom-analyse" v-if="showPage">
+    <el-container>
+      <el-aside width="350px">
+        <TabItem :active-item="activeItem" :treeList="treeList" @edit="handleEdit" @delete="handleDelete" @select="handleSelect" @handleAdd3="handleAdd3"></TabItem>
+      </el-aside>
+      <el-main>
+        <el-header style="height:45px !important">
+          <template>
+            <div class="custom-analyse-options">
+                <div>
+                    <el-button type="text" size="small" @click="openDrawer(1)">分析栏位</el-button>
+                    <el-button type="text" size="small" @click="openDrawer(2)">图形配置</el-button>
+                </div>
+                <div>
+                    <span class="text">专利数量<span class="number">{{ patentNum }} 件</span></span>
+                    
+                    <el-dropdown size="small">
+                        <el-button size="small" type="text">
+                        更多菜单<i class="el-icon-arrow-down el-icon--right"></i>
+                        </el-button>
+                        <el-dropdown-menu slot="dropdown">
+                        <el-dropdown-item @click.native="handleScreenshot">截图</el-dropdown-item>
+                        <el-dropdown-item @click.native="handleAdd1(0)">保存修改</el-dropdown-item>
+                        <el-dropdown-item @click.native="handleAdd1(1)">另存为</el-dropdown-item>
+                        <el-dropdown-item @click.native="handleExport">导出数据</el-dropdown-item>
+                        </el-dropdown-menu>
+                    </el-dropdown>
+                </div>
+            </div>
+          </template>
+        </el-header>
+        <el-main class="custom-analyse-chart-img-box ">
+          <div class="height_100" v-if="showChart" v-loading="loading">
+            <chart ref="chartDom" :width="form.setting.width + form.setting.widthUnit" :height="form.setting.height + form.setting.heightUnit" />
+          </div>
+        </el-main>
+      </el-main>
+    </el-container>
+    <addAnalyseGroup ref="addAnalyseGroup" :projectId="projectId" :treeList="treeList" @save="getTreeList"></addAnalyseGroup>
+    <el-drawer
+        :title="title"
+        :visible.sync="drawer"
+        direction="rtl"
+        :before-close="handleClose"
+        size="500px">
+            <el-container>
+                <el-main>
+                    <component :type="this.form.setting.type" :is='components'></component> 
+                </el-main>
+                <el-footer class="footer-common">
+                    <el-button @click="handleClose">取 消</el-button>
+                    <el-button type="primary" @click="submit" :loading="loadingBtn">确 定</el-button>
+                </el-footer>
+            </el-container>
+            
+    </el-drawer>
+  </div>
+</template>
+
+<script>
+import { mapGetters } from "vuex";
+import html2canvas from "html2canvas";
+import { downLoad, findChildren, getTreeLastChildren, getTreeDataList, downLoadBase64 } from "@/utils";
+import { detectionChartType, getFormPermissions, getTreeNameByIds, getXAxisName, getSourceName } from "@/utils/chart";
+import { customPage } from "./mixins";
+import moment from "moment";
+import addAnalyseGroup from './components/dialog/addAnalyseGroup.vue'
+import Chart from "./components/Charts/index.vue";
+import TabItem from "./components/Tabs/Item";
+import TabData from "./components/Tabs/Data";
+import TabStyle from "./components/Tabs/Style";
+export default {
+  components: {
+    addAnalyseGroup,
+    Chart,
+    TabItem,
+    TabData,
+    TabStyle
+  },
+  mixins: [customPage],
+  props: {},
+  data() {
+    return {
+      showPage:false,
+        enable:[],
+        activeItem:[],
+        // treeList:[],
+        showChart:false,
+        title:'',
+        drawer:false,
+        components:'TabData',
+        saveType: 1,
+        loadingBtn:false,
+        id:null,
+        loading:false,
+        patentNum:0
+    };
+  },
+  watch: {},
+  computed: {
+    ...mapGetters(['userinfo']),
+    search(){
+        var a = this.$route.query.search
+        if(a){
+            a = JSON.parse(a)
+        }
+        return a
+    },
+    projectId(){
+        return this.$route.query.projectId
+    },
+    patentNums(){
+         return this.$route.query.patentNum || 0
+    },
+    searchPatentNum(){
+      return this.$route.query.searchPatentNum
+    }
+  },
+  created() {},
+  async mounted() {
+    await this.getPatentNum()
+    this.getData()
+    window.addEventListener('resize',this.refreshChart)
+  },
+  methods: {
+    async getPatentNum(){
+      if(!this.searchPatentNum){
+        this.patentNum = this.patentNums
+        return
+      }
+      let params1 = {
+        projectId: this.projectId,
+      }
+      await this.$api.getAllCountColumns(params1).then(res => {
+        if (res.code == 200) {
+          var data = []
+          res.data.data.forEach(item => {
+            if (item.filedKind == -1) {
+              item.value = item.field
+            }
+            if (item.filedKind == 0 && (item.type == 'tree')) {
+
+            } else {
+              data.push(item)
+            }
+          })
+          
+          this.$store.commit('SET_SYSTEM_FIELD',data)
+        }
+      })
+      let params = {
+        current: 1,
+        size: 1,
+        projectId: this.projectId,
+        searchQuery: '',//检索条件
+        customFields: [],
+        orderDTOList: [],//排序信息
+      }
+      this.$api.QueryPatent(params).then(res => {
+        if (res.code == 200) {
+          this.patentNum = res.data.total
+        }
+      }).catch(error=>{
+        this.patentNum = 0
+      })
+    },
+    openDrawer(type){
+        var components = {
+            1:'tabData',
+            2:'tabStyle'
+        }
+        this.title = type == 1?'数据':'样式配置'
+        this.components = components[type]
+        this.drawer = true
+    },
+    normalizer(node) {
+      if (node.children && !node.children.length) {
+        delete node.children;
+      }
+      return {
+        id: node.id,
+        label: node.name,
+        children: node.children
+      };
+    },
+
+
+    reset() {
+
+    },
+    // 获取???
+    async getData() {
+        await this.getTreeList()
+        if (this.treeList.length === 0 || this.treeList[0].children.length === 0) {
+          this.handleAdd()
+          this.showChart = false
+          this.saveType = 1
+          this.openDrawer(1)
+        } else {
+          this.activeItem = [this.treeList[0].id]
+          await this.handleSelect(this.treeList[0].children[0].id)
+        }
+        this.showPage = true
+
+    },
+    handleDelete(value) {
+      this.$confirm('确认删除本条数据吗?', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(async () => {
+        const response = await this.$api.delAnalyseGroup({id:value.id})
+        this.$message.success('删除成功')
+        await this.getTreeList()
+        if (this.form.id === value.id) {
+          const tree = this.treeList.find(item => item.id === value.parentId)
+          if (tree && tree.children.length) {
+            await this.handleSelect(tree.children[tree.children.length - 1].id)
+          } else {
+            this.handleAdd()
+          }
+        }
+      })
+    },
+    // 获取需要进行的每一个分析项
+    async getTreeList() {
+      this.loading = true
+      const response = await this.$api.queryAnalyseGroup({ projectId: this.projectId })
+      // 获取到的数据存
+      this.$store.commit('SET_CHART_TREE', response.data)
+      this.setChildren(this.treeList, 'isDisabled')
+      this.loading = false
+    },
+    handleClose() {
+      this.drawer = false
+    },
+    setChildren(arr, key) {
+      arr.forEach(item => {
+        if (item.children && item.children.length) {
+          this.setChildren(item.children, key)
+        } else {
+          this.$set(item, key, item.type === 1)
+        }
+      })
+    },
+    refreshChart() {
+      this.$nextTick(() => {
+        let chartDom = this.$refs.chartDom
+        if (chartDom) {
+          chartDom.refreshChart()
+        }
+      })
+    },
+    onChange() {
+      if (this.saveType || !this.form2.id) {
+        this.form2.name = getSourceName(this.form.schema.x.field, this.form.schema.x.expand, this.form.schema.x.num)
+        const tree = this.treeList.find(item => item.id === this.form2.parentId)
+        if (tree) {
+          this.form2.sort = tree.children.length + 1
+        }
+      } else {
+        this.form2.name = this.tempForm.name
+        this.form2.sort = this.tempForm.sort
+      }
+    },
+    handleAdd3() {
+      var form = {
+            type:1,
+            permissions: 2,
+            parentId: 0,
+            sort: this.treeList.length + 1,
+            name: '',
+        }
+        var title = '添加组'
+        this.$refs.addAnalyseGroup.open(title,form)
+    },
+    async handleScreenshot() {
+      const canvas = await html2canvas(this.$refs.chartDom.$el)
+      const base64 = canvas.toDataURL('image/jpg')
+      downLoadBase64(base64, '图片.png')
+    },
+    async handleAdd1(saveType) {
+      this.saveType = saveType
+    //   this.onChange()
+      var title = '保存自定义分析'
+      let data = {}
+        if(this.saveType == 1){
+
+        }else{
+            data.id = this.id
+        }
+      if (this.id && this.saveType == 0) {
+        const response = await this.$api.queryAnalyseDetail({id:this.id})
+        data = response.data
+      }
+      var form2 = {
+        ...data,
+        type:2,
+        // setting: this.form.setting,
+        // schema: this.form.schema,
+        // source: this.form.source,
+        projectId: this.projectId
+      }
+      form2.setting = this.form.setting
+      form2.schema = this.form.schema
+      form2.source = this.form.source
+      this.$refs.addAnalyseGroup.open(title,form2)
+    },
+    handleAdd() {
+      let table = []
+      for (let i = 0; i < 10; i++) {
+        table.push({
+          min: 0,
+          max: 0,
+          color: null
+        })
+      }
+      let form = {
+        search:this.search,
+        setting: {
+          type: 1,
+          showType: 0,
+          tableType: 0,
+          theme: "customed",
+          titleSize: 14,
+          dataSize: 14,
+          axisSize: 14,
+          dataLabel: false,
+          dataLabel2: false,
+          dataLabel3: false,
+          title1Dir: 0,
+          title1: "",
+          title2: "",
+          dataPosition: "top",
+          fontFamily: "sans-serif",
+          fontFamily2: "sans-serif",
+          fontFamily3: "sans-serif",
+          fontFamily4: "sans-serif",
+          fontWeight: false,
+          fontWeight2: false,
+          nameLocation: "end",
+          nameLocation2: "end",
+          width: "100",
+          height: "650",
+          widthUnit: "%",
+          heightUnit: "px",
+          splitLine: false,
+          splitLine2: false,
+          legend: true,
+          gridTop: "10",
+          gridLeft: "10",
+          gridBottom: "10",
+          gridRight: "10",
+          labelColor: "#000000",
+          legendColor: "#000000",
+          legendLocation: "top",
+          legendFontSize: "13",
+          fontSize: "13",
+          fontSize2: "13",
+          fontColor: "#000000",
+          fontColor2: "#000000",
+          show: true,
+          show2: true,
+          interval: true,
+          interval2: true,
+          rotate: "0",
+          rotate2: "0",
+          paddingTop: 10,
+          paddingBottom: 10,
+          paddingRight: 10,
+          paddingLeft: 10,
+          paddingTop2: 0,
+          paddingLeft2: 0,
+          paddingBottom2: 0,
+          paddingRight2: 0,
+          type2: "scroll",
+          barWidth: "",
+          scatterSize: "0",
+          config: {
+            color: [],
+            line: {
+              name: "",
+              first: "",
+              second: "",
+              operator: 0,
+              enable: false
+            },
+            table: table
+          }
+        },
+        schema: {
+          x: {
+            num: 10,
+            // field:'PA'
+          },
+          y: {
+            num: 10
+          },
+        },
+        source: {
+          x: [],
+          y: []
+        },
+      }
+      this.$store.commit('SET_CHART_FORM', form)
+    },
+    handleEdit(value,type) {
+      var title = '编辑组'
+      var form = { ...value }
+      form.type = type
+      this.$refs.addAnalyseGroup.open(title,form)
+    },
+    getDataCountParams() {
+      let source = {
+        x: [],
+        y: [],
+      }
+      let schema = {
+        x: {},
+        y: {}
+      }
+      let merge = {
+        x: 0,
+        y: 0
+      }
+      Object.keys(source).forEach(dimension => {
+        if (this.form.schema[dimension].type === 6) {
+          if (this.form.setting.type === 20) {
+            source[dimension] = this.getTreeAllNode(dimension)
+            merge[dimension] = 1
+          } else if (this.form.setting.type === 30) {
+            source[dimension] = this.getTreeLastChildren(dimension)
+          } else {
+            source[dimension] = this.getPatentTreeChildren(dimension)
+            merge[dimension] = 1
+          }
+        } else {
+          source[dimension] = this.form.source[dimension].filter(item => item.selected)
+        }
+      })
+      Object.keys(schema).forEach(dimension => {
+        schema[dimension] = {
+          expand: this.form.schema[dimension].expand || 0,
+          field: this.form.schema[dimension].field || 0,
+          num: this.form.schema[dimension].num,
+          type: this.form.schema[dimension].type,
+          ptype: this.form.schema[dimension].ptype,
+          merge: merge[dimension]
+        }
+      })
+      return { source, schema }
+    },
+    async getDataCount() {
+      if (this.reloadData) {
+        var data = {}
+        var sign = false
+        var count = []
+        var dimensions = []
+        if(this.form.schema.x.field && this.form.schema.y.field){
+            sign = true
+            dimensions = ['x','y']
+        }else{
+            sign = false
+            dimensions = ['x']
+            
+        }
+        if (!this.form.setting.type) {
+            if(sign){
+               this.form.setting.type = 2 
+            }else{
+                this.form.setting.type = 1
+            }
+        }else{
+            var chartType = this.$constants.chartType.find(item=>{
+                return item.value == this.form.setting.type
+            }).type
+            if(sign && chartType == 1){
+                this.form.setting.type = 2 
+            }else if(!sign && chartType == 2){
+                this.form.setting.type = 1
+            }
+        }
+        dimensions.forEach(dimension=>{
+            var form = this.form
+            var obj = form.schema[dimension]
+            this.loading = true
+            if(obj.fieldKind == 0){
+                count.push(
+                    {
+                        field:'field',
+                        fieldId:form.schema[dimension].field,
+                        topN: form.schema[dimension].num,
+                        values:this.selected[dimension]
+                    }
+                )
+            }else if(form.schema[dimension].type == 'DateTime'){
+                count.push(
+                    {
+                        field:form.schema[dimension].field,
+                        topN: form.schema[dimension].num,
+                        format:form.schema[dimension].expand,
+                        values:this.selected[dimension]
+                    }
+                )
+            }else{
+                if(['classify'].includes(form.schema[dimension].groupBy)){
+                    count.push(
+                    {
+                        field:form.schema[dimension].expand,
+                        topN: form.schema[dimension].num,
+                        values:this.selected[dimension]
+                    }
+                    )
+                }else{
+                    count.push(
+                    {
+                        field:form.schema[dimension].field,
+                        topN: form.schema[dimension].num,
+                        values:this.selected[dimension]
+                    }
+                    )
+                }
+                
+            }
+        })
+        
+        var params = {
+            ...this.form.search,
+            countVOS:count
+        }
+        try{
+          // var haveCustom = count.find(item=>{
+          //   return item.field == 'field' && item.fieldId
+          // })
+          // if(haveCustom){
+          //   let Custom = {
+          //     customFieldId: haveCustom.fieldId,
+          //   }
+          //  const customResponse = await this.$api.queryCustomOption(Custom)
+          //  if(customResponse.code == 200){
+          //   // var customData =this.$commonJS.treeToArray(customResponse.data.data,{children:child}) 
+          //   var customData =customResponse.data.data
+          //  }else{
+          //   var customData = []
+          //  }
+          // }
+            const response = await this.$api.esCountAnalysis(params)
+            if(sign){
+                var res = Object.keys(response.data.analyseMap)
+                for(var i = 0;i<res.length;i++){
+                    // var x = this.form.source.x[i]
+                    // data[x.name] = {}
+                    // for(var j = 0;j<this.form.source.y.length;j++){
+                    //     data[x.name][this.form.source.y[j].name] = this.form.source.y[j].number
+                    // }
+                    var analyseMapData = response.data.analyseMap[res[i]]
+                    var obj = {}
+                    for(var j =0 ;j<analyseMapData.length;j++){
+                      var name = analyseMapData[j].name
+                        obj[name] = analyseMapData[j].number
+                    }
+                    var key = res[i]
+                    // if(haveCustom){
+                    //   var index = customData.findIndex(item=>{
+                    //     return item.id == key
+                    //   })
+                    //   if(index!=-1){
+                    //     key = customData[index].name
+                    //     customData.splice(index,1)
+                    //   }
+                    // }
+                    data[key] = obj
+                }
+            }else{
+                for(var i = 0;i<response.data.detailDTOS.length;i++){
+                  var key = response.data.detailDTOS[i].name
+                  // if(haveCustom){
+                  //   var index = customData.findIndex(item=>{
+                  //     return item.id == key
+                  //   })
+                  //   if(index!=-1){
+                  //     key = customData[index].name
+                  //     customData.splice(index,1)
+                  //   }
+                  // }
+                    data[key] = response.data.detailDTOS[i].number
+                }
+            }
+            
+        } catch (e) {
+            data = []
+        }
+        this.$store.commit('SET_DATA_COUNT', data)
+        this.refreshChart()
+        this.$store.commit('SET_RELOAD_DATA', false)
+        this.loading = false
+        this.showChart = true
+      }
+    },
+    getTreeAllNode(dimension) {
+      const tree = getTreeDataList(this.form.source[dimension], [])
+      return tree.map(item => { return { id : item.id } })
+    },
+    getTreeLastChildren(dimension) {
+      const tree = getTreeDataList(this.form.source[dimension], [])
+      const data = tree.filter(item => this.treeKey[dimension].indexOf(item.id) !== -1)
+      let arr = data.filter(item =>  !item.children || !item.children.length)
+      data.map(item => arr.push(...getTreeLastChildren(item.children || [], [])))
+      return arr.map(item => { return { id : item.id } })
+    },
+    getPatentTreeChildren(dimension) {
+      const tree = getTreeDataList(this.form.source[dimension], [])
+      const selected = this.treeKey[dimension].map(item => item)
+      let arr = []
+      selected.map(item => {
+        const node = tree.find(t => t.id === item)
+        arr.push(node)
+        arr.push(...getTreeLastChildren(node.children || [], []))
+      })
+      return arr.map(item => { return { id : item.id } })
+    },
+    getTreeNodeName(node, tree) {
+      const path = node.path.split('/').map(p => parseInt(p, 0))
+      let name = []
+      path.map(item => {
+        const n = tree.find(x => x.id === item)
+        if (n) {
+          name.push(n.name)
+        }
+      })
+      return name.join('/')
+    },
+    getExportParams() { 
+      const xAxis = getXAxisName(this.patentField, this.form.schema.x.field, this.form.schema.x.expand)
+      let type = this.form.schema.y.field ? 2 : 1
+      type = this.form.setting.type === 21 ? 3 : type
+      let count = JSON.parse(JSON.stringify(this.count))
+      const xt = this.form.schema.x.type === 6
+      const yt = this.form.schema.y.type === 6
+      const xs = getTreeDataList(this.form.source.x, [])
+      const ys = getTreeDataList(this.form.source.y, [])
+      if (xt && !yt) {
+        for (let key in count) {
+          const xn = xs.find(item => item.id === parseInt(key))
+          const name = this.getTreeNodeName(xn, xs)
+          if (xn) {
+            count[name] = count[key]
+            delete count[key]
+          }
+        }
+      }
+      if (xt && yt) {
+        for (let xk in count) {
+          const xn = xs.find(item => item.id === parseInt(xk))
+          const xa = this.getTreeNodeName(xn, xs)
+          let data = {}
+          for (let yk in count[xk]) {
+            const yn = ys.find(item => item.id === parseInt(yk))
+            const ya = this.getTreeNodeName(yn, ys)
+            if (yn) {
+              data[ya] = count[xk][yk]
+            }
+          }
+          if (xn) {
+            count[xa] = data
+            delete count[xk]
+          }
+        }
+      }
+      if (!xt && yt) {
+        for (let key in count) {
+          let data = {}
+          for (let y in count[key]) {
+            const yn = ys.find(item => item.id === parseInt(y))
+            const ya = this.getTreeNodeName(yn, ys)
+            if (yn) {
+              data[ya] = count[key][y]
+              delete count[y]
+            }
+          }
+          count[key] = data
+        }
+      }
+      return { count, type, xAxis }
+    },
+    async handleExport() {
+        if(!this.form.schema.x.field && !this.form.schema.y.field){
+            this.$message.error('请先添加数据')
+            return false
+        }
+        var data = Object.keys(this.count)
+        var head = ''
+        var body = ''
+        if(this.form.schema.x.field && this.form.schema.y.field){
+            head = `<td>${this.form.schema.x.name}</td>`
+            for(var i = 0;i<this.selected.y.length;i++){
+                head = head + `<td>${this.selected.y[i]}</td>`
+            }
+            for(var i = 0;i<data.length;i++){
+                body = body + `<tr><td>${data[i]}</td>`
+                for(var j = 0;j<this.selected.y.length;j++){
+                    body = body + `<td>${this.count[data[i]][this.selected.y[j]]}</td>`
+                }
+                body = body + '</tr>'
+            }
+        }else{
+            head = `<td>${this.form.schema.x.name}</td><td>数量</td>`
+            for(var i = 0;i<data.length;i++){
+                body = body + `<tr><td>${data[i]}</td><td>${this.count[data[i]]}</td></tr>`
+            }
+        }
+        if(!head && !body){
+            this.$message.error('导出失败,请重试')
+            return false
+        }
+        this.$commonJS.exportExcel(head,body)
+    
+    },
+    resetChartType() {
+      this.form.setting.type = null
+      this.$store.dispatch('resetSettingColor')
+    },
+    async submit() {
+      if (!this.form.schema.x.field) {
+        this.$message.error('请选择一维数据')
+        return false
+      }else{
+        if(this.form.source.x.length == 0){
+          this.$message.error('一维数据为空')
+          return false
+        }
+      }
+      if(this.form.schema.y.field){
+        if(this.form.source.y.length == 0){
+          this.$message.error('二维数据为空')
+          return false
+        }
+      }
+      const chartType = this.$constants.chartType.find(c => c.value === this.form.setting.type)
+      const { op, cg } = detectionChartType(chartType || {}, this.form)
+      if (op && cg) {
+        this.resetChartType()
+      }
+      
+      this.getDataCount()
+      this.refreshChart()
+      this.showChart = true
+      this.handleClose()
+    },
+    async handleSelect(key) {
+        this.id = key
+      let source = {
+        x: [],
+        y: []
+      }
+      this.$store.commit('SET_RELOAD_DATA', true)
+      this.activeName = '1'
+      this.loading = true
+      const response = await this.$api.queryAnalyseDetail({id:key})
+      this.$store.commit('SET_CHART_FORM', JSON.parse(JSON.stringify(response.data)))
+      this.form.search = this.search
+      if (this.enable.indexOf(this.form.setting.type) === -1) {
+        this.form.setting.config.line.enable = false
+      }
+      for (let key in response.data.source) {
+        source[key] = response.data.source[key]
+        if (response.data.source[key].length === 0 || response.data.schema[key].type === 6) {
+          await this.$store.dispatch('getSourceDataList', key)
+          if (response.data.setting.title1) {
+            this.form.setting.title1 = response.data.setting.title1
+          }
+          if (response.data.setting.title2) {
+            this.form.setting.title2 = response.data.setting.title2
+          }
+        }
+      }
+      for (let key in response.data.schema) {
+        if (this.form.schema[key].type !== 6) {
+          this.$set(this.selected, key, this.form.source[key].slice(0, response.data.schema[key].num).map(item => item.name))
+          this.form.source[key].map(item => {
+            item.selected = this.selected[key].indexOf(item.name) !== -1
+          })
+        } else {
+          this.treeKey[key] = source[key].map(item => parseInt(item.name))
+          this.selected[key] = getTreeNameByIds(this.form.source[key], this.treeKey[key])
+        }
+      }
+      await this.$store.dispatch('getItemSettingColor', [])
+      await this.getDataCount()
+      this.$nextTick(() => {
+        this.loading = false
+      })
+    },
+
+  },
+  beforeDestroy(){
+    window.removeEventListener('resize',()=>{})
+  }
+};
+</script>
+<style lang="scss" scoped>
+.custom-analyse {
+  .custom-analyse-chart-img-box {
+    background: #ffffff;
+  }
+  .custom-analyse-chart-title {
+    padding: 20px;
+    span {
+      font-weight: bold;
+      font-size: 16px;
+    }
+  }
+  .custom-analyse-options {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    width: 100%;
+    padding: 20px;
+    .number {
+      padding-left: 10px;
+      color: #409EFF;
+    }
+    .text {
+      font-size: 13px;
+      padding-right: 20px;
+    }
+  }
+}
+</style>

src/views/analyse/custom/index copy.vue → src/views/analyse/custom/index-yuanshi.vue


+ 63 - 0
src/views/components/editor/components/analysis/index.vue

@@ -0,0 +1,63 @@
+<template>
+    <div>
+      <el-drawer class="custom-drawer-form" title="图" size="800px" append-to-body :visible.sync="visible"
+        direction="rtl" :before-close="close" destroy-on-close>
+        <el-container>
+          <el-main>
+            <el-input v-model="value"></el-input>
+          </el-main>
+          <el-footer class="footer-common">
+            <el-button @click="close">取 消</el-button>
+            <el-button type="primary" @click="edit" :loading="loading">确 定</el-button>
+          </el-footer>
+        </el-container>
+      </el-drawer>
+    </div>
+</template>
+  
+  <script>
+  export default {
+      name:"echarts",
+    components: {},
+    props: {},
+    data() {
+      return {
+          visible:false,
+          value:'',
+          loading:false,
+      };
+    },
+    watch: {},
+    computed: {},
+    created() {},
+    mounted() {
+      
+    },
+    methods: {
+      edit(){
+          console.log(1)
+          var message = {
+            src:'https://t7.baidu.com/it/u=1595072465,3644073269&fm=193&f=GIF',
+            data:{
+              value:this.value
+            }
+          }
+          this.$emit('data',message)
+          this.close()
+      },
+      open(data){
+        if(data){
+          this.$set(this,'value',data.value)
+        }else{
+          this.$set(this,'value','')
+        }
+        this.visible = true
+      },
+      close(){
+          this.visible = false
+      }
+    },
+  };
+  </script>
+  <style lang="scss" scoped>
+  </style>

+ 164 - 0
src/views/components/editor/components/dialog/Dialog.js

@@ -0,0 +1,164 @@
+import { EditorComponent, EDITOR_COMPONENT } from '@hufe921/canvas-editor'
+// import './dialog.css'
+
+export const IDialogData ={
+  type: '',
+  label: '',
+  name: '',
+  value: '',
+  options: [],
+  placeholder: '',
+  width: null,
+  height: null,
+  required: false
+}
+
+export const IDialogConfirm ={
+  name: '',
+  value: ''
+}
+
+export const IDialogOptions ={
+  onClose: () => {},
+  onCancel: () => {},
+  onConfirm: (payload) => {},
+  title: '',
+  data: [],
+}
+
+export class Dialog {
+   options
+   mask
+   container
+   inputList=[]
+
+  constructor(options) {
+    this.options = options
+    this.mask = null
+    this.container = null
+    this.inputList = []
+    this._render()
+  }
+
+   _render() {
+    const { title, data, onClose, onCancel, onConfirm } = this.options
+    // 渲染遮罩层
+    const mask = document.createElement('div')
+    mask.classList.add('dialog-mask')
+    mask.setAttribute(EDITOR_COMPONENT, EditorComponent.COMPONENT)
+    document.body.append(mask)
+    // 渲染容器
+    const container = document.createElement('div')
+    container.classList.add('dialog-container')
+    container.setAttribute(EDITOR_COMPONENT, EditorComponent.COMPONENT)
+    // 弹窗
+    const dialogContainer = document.createElement('div')
+    dialogContainer.classList.add('dialog')
+    container.append(dialogContainer)
+    // 标题容器
+    const titleContainer = document.createElement('div')
+    titleContainer.classList.add('dialog-title')
+    // 标题&关闭按钮
+    const titleSpan = document.createElement('span')
+    titleSpan.append(document.createTextNode(title))
+    const titleClose = document.createElement('i')
+    titleClose.onclick = () => {
+      if (onClose) {
+        onClose()
+      }
+      this._dispose()
+    }
+    titleContainer.append(titleSpan)
+    titleContainer.append(titleClose)
+    dialogContainer.append(titleContainer)
+    // 选项容器
+    const optionContainer = document.createElement('div')
+    optionContainer.classList.add('dialog-option')
+    // 选项
+    for (let i = 0; i < data.length; i++) {
+      const option = data[i]
+      const optionItemContainer = document.createElement('div')
+      optionItemContainer.classList.add('dialog-option__item')
+      // 选项名称
+      if (option.label) {
+        const optionName = document.createElement('span')
+        optionName.append(document.createTextNode(option.label))
+        optionItemContainer.append(optionName)
+        if (option.required) {
+          optionName.classList.add('dialog-option__item--require')
+        }
+      }
+      // 选项输入框
+      let optionInput
+      if (option.type === 'select') {
+        optionInput = document.createElement('select')
+        option.options?.forEach(item => {
+          const optionItem = document.createElement('option')
+          optionItem.value = item.value
+          optionItem.label = item.label
+          optionInput.append(optionItem)
+        })
+      } else if (option.type === 'textarea') {
+        optionInput = document.createElement('textarea')
+      } else {
+        optionInput = document.createElement('input')
+        optionInput.type = option.type
+      }
+      if (option.width) {
+        optionInput.style.width = `${option.width}px`
+      }
+      if (option.height) {
+        optionInput.style.height = `${option.height}px`
+      }
+      optionInput.name = option.name
+      optionInput.value = option.value || ''
+      if (!(optionInput instanceof HTMLSelectElement)) {
+        optionInput.placeholder = option.placeholder || ''
+      }
+      optionItemContainer.append(optionInput)
+      optionContainer.append(optionItemContainer)
+      this.inputList.push(optionInput)
+    }
+    dialogContainer.append(optionContainer)
+    // 按钮容器
+    const menuContainer = document.createElement('div')
+    menuContainer.classList.add('dialog-menu')
+    // 取消按钮
+    const cancelBtn = document.createElement('button')
+    cancelBtn.classList.add('dialog-menu__cancel')
+    cancelBtn.append(document.createTextNode('取消'))
+    cancelBtn.type = 'button'
+    cancelBtn.onclick = () => {
+      if (onCancel) {
+        onCancel()
+      }
+      this._dispose()
+    }
+    menuContainer.append(cancelBtn)
+    // 确认按钮
+    const confirmBtn = document.createElement('button')
+    confirmBtn.append(document.createTextNode('确定'))
+    confirmBtn.type = 'submit'
+    confirmBtn.onclick = () => {
+      if (onConfirm) {
+        const payload = this.inputList.map(input => ({
+          name: input.name,
+          value: input.value
+        }))
+        onConfirm(payload)
+      }
+      this._dispose()
+    }
+    menuContainer.append(confirmBtn)
+    dialogContainer.append(menuContainer)
+    // 渲染
+    document.body.append(container)
+    this.container = container
+    this.mask = mask
+  }
+
+   _dispose() {
+    this.mask?.remove()
+    this.container?.remove()
+  }
+}

+ 131 - 0
src/views/components/editor/components/dialog/dialog.css

@@ -0,0 +1,131 @@
+.dialog-mask {
+  position: fixed;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  opacity: .5;
+  background: #000000;
+  z-index: 99;
+}
+
+.dialog-container {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  overflow: auto;
+  z-index: 999;
+  margin: 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.dialog {
+  position: absolute;
+  padding: 0 30px 30px;
+  background: #ffffff;
+  box-shadow: 0 2px 12px 0 rgb(56 56 56 / 20%);
+  border: 1px solid #e2e6ed;
+  border-radius: 2px;
+}
+
+.dialog-title {
+  position: relative;
+  border-bottom: 1px solid #e2e6ed;
+  margin-bottom: 30px;
+  height: 60px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.dialog-title i {
+  width: 16px;
+  height: 16px;
+  cursor: pointer;
+  display: inline-block;
+  background: url('~@/assets/images/close.svg');
+}
+
+.dialog-option__item {
+  margin-bottom: 18px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.dialog-option__item span {
+  margin-right: 12px;
+  font-size: 14px;
+  color: #3d4757;
+  position: relative;
+}
+
+.dialog-option__item input,
+.dialog-option__item textarea,
+.dialog-option__item select {
+  width: 276px;
+  height: 30px;
+  border-radius: 2px;
+  border: 1px solid #d3d3d3;
+  min-height: 30px;
+  padding: 5px;
+  box-sizing: border-box;
+  outline: none;
+  appearance: none;
+  user-select: none;
+  font-family: inherit;
+}
+
+.dialog-option__item input:focus,
+.dialog-option__item textarea:focus {
+  border-color: #4991f2;
+}
+
+.dialog-option__item--require::before {
+  content: "*";
+  color: #f56c6c;
+  margin-right: 4px;
+  position: absolute;
+  left: -8px;
+}
+
+.dialog-menu {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+}
+
+.dialog-menu button {
+  position: relative;
+  display: inline-block;
+  border: 1px solid #e2e6ed;
+  border-radius: 2px;
+  background: #ffffff;
+  line-height: 22px;
+  padding: 0 16px;
+  white-space: nowrap;
+  cursor: pointer;
+}
+
+.dialog-menu button:hover {
+  background: rgba(25, 55, 88, .04);
+}
+
+.dialog-menu__cancel {
+  margin-right: 16px;
+}
+
+.dialog-menu button[type='submit'] {
+  color: #ffffff;
+  background: #4991f2;
+  border-color: #4991f2;
+}
+
+.dialog-menu button[type='submit']:hover {
+  background: #5b9cf3;
+  border-color: #5b9cf3;
+}

+ 309 - 0
src/views/components/editor/components/signature/Signature.js

@@ -0,0 +1,309 @@
+import { EditorComponent, EDITOR_COMPONENT } from '@hufe921/canvas-editor'
+// import './signature.css'
+
+export const ISignatureResult ={
+  value: '',
+  width: null,
+  height: null
+}
+
+export const ISignatureOptions ={
+  width: null,
+  height: null,
+  onClose: () => {},
+  onCancel: () => {},
+  onConfirm: (payload) => {}
+}
+
+export class Signature {
+   MAX_RECORD_COUNT = 1000
+   DEFAULT_WIDTH = 390
+   DEFAULT_HEIGHT = 180
+   undoStack= []
+   x = 0
+   y = 0
+   isDrawing = false
+   isDrawn = false
+   linePoints = []
+   canvasWidth
+   canvasHeight
+   options
+   mask
+   container
+   trashContainer
+   undoContainer
+   canvas
+   ctx
+   preTimeStamp
+   dpr
+
+  constructor(options) {
+    this.options = options
+    this.preTimeStamp = 0
+    this.dpr = window.devicePixelRatio
+    this.canvasWidth = (options.width || this.DEFAULT_WIDTH) * this.dpr
+    this.canvasHeight = (options.height || this.DEFAULT_HEIGHT) * this.dpr
+    const { mask, container, trashContainer, undoContainer, canvas } =
+      this._render()
+    this.mask = mask
+    this.container = container
+    this.trashContainer = trashContainer
+    this.undoContainer = undoContainer
+    this.canvas = canvas
+    this.ctx = canvas.getContext('2d')
+    this.ctx.scale(this.dpr, this.dpr)
+    this.ctx.lineCap = 'round'
+    this._bindEvent()
+    this._clearUndoFn()
+  }
+
+   _render() {
+    const { onClose, onCancel, onConfirm } = this.options
+    // 渲染遮罩层
+    const mask = document.createElement('div')
+    mask.classList.add('signature-mask')
+    mask.setAttribute(EDITOR_COMPONENT, EditorComponent.COMPONENT)
+    document.body.append(mask)
+    // 渲染容器
+    const container = document.createElement('div')
+    container.classList.add('signature-container')
+    container.setAttribute(EDITOR_COMPONENT, EditorComponent.COMPONENT)
+    // 弹窗
+    const signatureContainer = document.createElement('div')
+    signatureContainer.classList.add('signature')
+    container.append(signatureContainer)
+    // 标题容器
+    const titleContainer = document.createElement('div')
+    titleContainer.classList.add('signature-title')
+    // 标题&关闭按钮
+    const titleSpan = document.createElement('span')
+    titleSpan.append(document.createTextNode('插入签名'))
+    const titleClose = document.createElement('i')
+    titleClose.onclick = () => {
+      if (onClose) {
+        onClose()
+      }
+      this._dispose()
+    }
+    titleContainer.append(titleSpan)
+    titleContainer.append(titleClose)
+    signatureContainer.append(titleContainer)
+    // 操作区
+    const operationContainer = document.createElement('div')
+    operationContainer.classList.add('signature-operation')
+    // 撤销
+    const undoContainer = document.createElement('div')
+    undoContainer.classList.add('signature-operation__undo')
+    const undoIcon = document.createElement('i')
+    const undoLabel = document.createElement('span')
+    undoLabel.innerText = '撤销'
+    undoContainer.append(undoIcon)
+    undoContainer.append(undoLabel)
+    operationContainer.append(undoContainer)
+    // 清空画布
+    const trashContainer = document.createElement('div')
+    trashContainer.classList.add('signature-operation__trash')
+    const trashIcon = document.createElement('i')
+    const trashLabel = document.createElement('span')
+    trashLabel.innerText = '清空'
+    trashContainer.append(trashIcon)
+    trashContainer.append(trashLabel)
+    operationContainer.append(trashContainer)
+    signatureContainer.append(operationContainer)
+    // 绘图区
+    const canvasContainer = document.createElement('div')
+    canvasContainer.classList.add('signature-canvas')
+    const canvas = document.createElement('canvas')
+    canvas.width = this.canvasWidth
+    canvas.height = this.canvasHeight
+    canvas.style.width = `${this.canvasWidth / this.dpr}px`
+    canvas.style.height = `${this.canvasHeight / this.dpr}px`
+    canvasContainer.append(canvas)
+    signatureContainer.append(canvasContainer)
+    // 按钮容器
+    const menuContainer = document.createElement('div')
+    menuContainer.classList.add('signature-menu')
+    // 取消按钮
+    const cancelBtn = document.createElement('button')
+    cancelBtn.classList.add('signature-menu__cancel')
+    cancelBtn.append(document.createTextNode('取消'))
+    cancelBtn.type = 'button'
+    cancelBtn.onclick = () => {
+      if (onCancel) {
+        onCancel()
+      }
+      this._dispose()
+    }
+    menuContainer.append(cancelBtn)
+    // 确认按钮
+    const confirmBtn = document.createElement('button')
+    confirmBtn.append(document.createTextNode('确定'))
+    confirmBtn.type = 'submit'
+    confirmBtn.onclick = () => {
+      if (onConfirm) {
+        onConfirm(this._toData())
+      }
+      this._dispose()
+    }
+    menuContainer.append(confirmBtn)
+    signatureContainer.append(menuContainer)
+    // 渲染
+    document.body.append(container)
+    this.container = container
+    this.mask = mask
+    return {
+      mask,
+      canvas,
+      container,
+      trashContainer,
+      undoContainer
+    }
+  }
+
+   _bindEvent() {
+    this.trashContainer.onclick = this._clearCanvas.bind(this)
+    this.undoContainer.onclick = this._undo.bind(this)
+    this.canvas.onmousedown = this._startDraw.bind(this)
+    this.canvas.onmousemove = this._draw.bind(this)
+    this.container.onmouseup = this._stopDraw.bind(this)
+  }
+
+   _undo() {
+    if (this.undoStack.length > 1) {
+      this.undoStack.pop()
+      if (this.undoStack.length) {
+        this.undoStack[this.undoStack.length - 1]()
+      }
+    }
+  }
+
+   _saveUndoFn(fn) {
+    this.undoStack.push(fn)
+    while (this.undoStack.length > this.MAX_RECORD_COUNT) {
+      this.undoStack.shift()
+    }
+  }
+
+  _clearUndoFn() {
+    const clearFn = () => {
+      this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
+    }
+    this.undoStack = [clearFn]
+  }
+
+  _clearCanvas() {
+    this._clearUndoFn()
+    this.ctx.clearRect(0, 0, this.canvasWidth, this.canvasHeight)
+  }
+
+  _startDraw(evt) {
+    this.isDrawing = true
+    this.x = evt.offsetX
+    this.y = evt.offsetY
+    this.ctx.lineWidth = 1
+  }
+
+  _draw(evt) {
+    if (!this.isDrawing) return
+    // 计算鼠标移动速度
+    const curTimestamp = performance.now()
+    const distance = Math.sqrt(evt.movementX ** 2 + evt.movementY ** 2)
+    const speed = distance / (curTimestamp - this.preTimeStamp)
+    // 目标线宽:最小速度1,最大速度5,系数3
+    const SPEED_FACTOR = 3
+    const targetLineWidth = Math.min(5, Math.max(1, 5 - speed * SPEED_FACTOR))
+    // 平滑过渡算法(20%的变化比例)调整线条粗细:系数0.2
+    const SMOOTH_FACTOR = 0.2
+    this.ctx.lineWidth =
+      this.ctx.lineWidth * (1 - SMOOTH_FACTOR) + targetLineWidth * SMOOTH_FACTOR
+    // 绘制
+    const { offsetX, offsetY } = evt
+    this.ctx.beginPath()
+    this.ctx.moveTo(this.x, this.y)
+    this.ctx.lineTo(offsetX, offsetY)
+    this.ctx.stroke()
+    this.x = offsetX
+    this.y = offsetY
+    this.linePoints.push([offsetX, offsetY])
+    this.isDrawn = true
+    // 缓存之前时间戳
+    this.preTimeStamp = curTimestamp
+  }
+
+  _stopDraw() {
+    this.isDrawing = false
+    if (this.isDrawn) {
+      const imageData = this.ctx.getImageData(
+        0,
+        0,
+        this.canvasWidth,
+        this.canvasHeight
+      )
+      const self = this
+      this._saveUndoFn(function () {
+        self.ctx.clearRect(0, 0, self.canvasWidth, self.canvasHeight)
+        self.ctx.putImageData(imageData, 0, 0)
+      })
+      this.isDrawn = false
+    }
+  }
+
+   _toData() {
+    if (!this.linePoints.length) return null
+    // 查找矩形四角坐标
+    const startX = this.linePoints[0][0]
+    const startY = this.linePoints[0][1]
+    let minX = startX
+    let minY = startY
+    let maxX = startX
+    let maxY = startY
+    for (let p = 0; p < this.linePoints.length; p++) {
+      const point = this.linePoints[p]
+      if (minX > point[0]) {
+        minX = point[0]
+      }
+      if (maxX < point[0]) {
+        maxX = point[0]
+      }
+      if (minY > point[1]) {
+        minY = point[1]
+      }
+      if (maxY < point[1]) {
+        maxY = point[1]
+      }
+    }
+    // 增加边框宽度
+    const lineWidth = this.ctx.lineWidth
+    minX = minX < lineWidth ? 0 : minX - lineWidth
+    minY = minY < lineWidth ? 0 : minY - lineWidth
+    maxX = maxX + lineWidth
+    maxY = maxY + lineWidth
+    const sw = maxX - minX
+    const sh = maxY - minY
+    // 裁剪图像
+    const imageData = this.ctx.getImageData(
+      minX * this.dpr,
+      minY * this.dpr,
+      sw * this.dpr,
+      sh * this.dpr
+    )
+    const canvas = document.createElement('canvas')
+    canvas.style.width = `${sw}px`
+    canvas.style.height = `${sh}px`
+    canvas.width = sw * this.dpr
+    canvas.height = sh * this.dpr
+    const ctx = canvas.getContext('2d')
+    ctx.putImageData(imageData, 0, 0)
+    const value = canvas.toDataURL()
+    return {
+      value,
+      width: sw,
+      height: sh
+    }
+  }
+
+  _dispose() {
+    this.mask.remove()
+    this.container.remove()
+  }
+}

+ 128 - 0
src/views/components/editor/components/signature/signature.css

@@ -0,0 +1,128 @@
+.signature-mask {
+  position: fixed;
+  left: 0;
+  top: 0;
+  width: 100%;
+  height: 100%;
+  opacity: .5;
+  background: #000000;
+  z-index: 99;
+}
+
+.signature-container {
+  position: fixed;
+  top: 0;
+  right: 0;
+  bottom: 0;
+  left: 0;
+  overflow: auto;
+  z-index: 999;
+  margin: 0;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+}
+
+.signature {
+  position: absolute;
+  padding: 0 30px 30px;
+  background: #ffffff;
+  box-shadow: 0 2px 12px 0 rgb(56 56 56 / 20%);
+  border: 1px solid #e2e6ed;
+  border-radius: 2px;
+}
+
+.signature-title {
+  position: relative;
+  border-bottom: 1px solid #e2e6ed;
+  margin-bottom: 15px;
+  height: 60px;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+}
+
+.signature-title i {
+  width: 16px;
+  height: 16px;
+  cursor: pointer;
+  display: inline-block;
+  background: url('~@/assets/images/close.svg');
+}
+
+.signature-operation>div {
+  cursor: pointer;
+  display: inline-flex;
+  align-items: center;
+  color: #3d4757;
+  user-select: none;
+}
+
+.signature-operation>div:hover {
+  color: #6e7175;
+}
+
+.signature-operation>div i {
+  width: 24px;
+  height: 24px;
+  display: inline-block;
+}
+
+.signature-operation__undo {
+  background: url('~@/assets/images/signature-undo.svg') no-repeat;
+}
+
+.signature-operation__trash {
+  background: url('~@/assets/images/trash.svg') no-repeat;
+}
+
+.signature-operation>div span {
+  font-size: 12px;
+  margin: 0 5px;
+}
+
+.signature-canvas {
+  margin: 15px 0;
+  user-select: none;
+}
+
+.signature-canvas canvas {
+  background: #f3f5f7;
+}
+
+.signature-menu {
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+}
+
+.signature-menu button {
+  position: relative;
+  display: inline-block;
+  border: 1px solid #e2e6ed;
+  border-radius: 2px;
+  background: #ffffff;
+  line-height: 22px;
+  padding: 0 16px;
+  white-space: nowrap;
+  cursor: pointer;
+}
+
+.signature-menu button:hover {
+  background: rgba(25, 55, 88, .04);
+}
+
+.signature-menu__cancel {
+  margin-right: 16px;
+}
+
+.signature-menu button[type='submit'] {
+  color: #ffffff;
+  background: #4991f2;
+  border-color: #4991f2;
+}
+
+.signature-menu button[type='submit']:hover {
+  background: #5b9cf3;
+  border-color: #5b9cf3;
+}

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 2431 - 0
src/views/components/editor/editor.vue


+ 31 - 0
src/views/components/editor/index.vue

@@ -0,0 +1,31 @@
+<template>
+  <div style="height:100%">
+    <editor ref="wordEditor"  :htmlData="htmlData" :docJson="docJson" :key="keys" ></editor>
+    <!-- @save="save" @isSave="getSave" -->
+
+  </div>
+</template>
+
+<script>
+import editor from './editor.vue';
+export default {
+  components: {
+    editor
+  },
+  props: {},
+  data() {
+    return {
+      htmlData: '', // 编辑器html数据
+      docJson: 'ds', // 编辑器getValue数据
+      keys: new Date().getTime()
+    };
+  },
+  watch: {},
+  computed: {},
+  created() {},
+  mounted() {},
+  methods: {},
+};
+</script>
+<style lang="scss" scoped>
+</style>

Filskillnaden har hållts tillbaka eftersom den är för stor
+ 1016 - 0
src/views/components/editor/style.css