index.vue 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. <template>
  2. <div class="cl-editor-quill">
  3. <div class="editor" :style="style"></div>
  4. <cl-upload-space
  5. ref="upload-space"
  6. detail-data
  7. :show-button="false"
  8. @confirm="onUploadSpaceConfirm"
  9. >
  10. </cl-upload-space>
  11. </div>
  12. </template>
  13. <script>
  14. import Quill from "quill";
  15. import "quill/dist/quill.snow.css";
  16. export default {
  17. name: "cl-editor-quill",
  18. props: {
  19. value: null,
  20. height: [String, Number],
  21. width: [String, Number],
  22. options: Object
  23. },
  24. data() {
  25. return {
  26. uploadURL: "",
  27. content: "",
  28. quill: null,
  29. cursorIndex: 0
  30. };
  31. },
  32. computed: {
  33. style() {
  34. let h = this.height;
  35. let w = this.width;
  36. if (!isNaN(Number(h))) {
  37. h += "px";
  38. }
  39. if (!isNaN(Number(w))) {
  40. h += "px";
  41. }
  42. return {
  43. height: h,
  44. width: w
  45. };
  46. }
  47. },
  48. watch: {
  49. value(val) {
  50. if (val) {
  51. if (val !== this.content) {
  52. this.setContent(val);
  53. }
  54. } else {
  55. this.setContent("");
  56. }
  57. },
  58. content(val) {
  59. this.$emit("input", val);
  60. }
  61. },
  62. mounted() {
  63. this.quill = new Quill(this.$el.querySelector(".editor"), {
  64. theme: "snow",
  65. placeholder: "输入内容",
  66. modules: {
  67. toolbar: [
  68. ["bold", "italic", "underline", "strike"],
  69. ["blockquote", "code-block"],
  70. [{ header: 1 }, { header: 2 }],
  71. [{ list: "ordered" }, { list: "bullet" }],
  72. [{ script: "sub" }, { script: "super" }],
  73. [{ indent: "-1" }, { indent: "+1" }],
  74. [{ direction: "rtl" }],
  75. [{ size: ["small", false, "large", "huge"] }],
  76. [{ header: [1, 2, 3, 4, 5, 6, false] }],
  77. [{ color: [] }, { background: [] }],
  78. [{ font: [] }],
  79. [{ align: [] }],
  80. ["clean"],
  81. ["link", "image"]
  82. ]
  83. },
  84. ...this.options
  85. });
  86. this.quill.getModule("toolbar").addHandler("image", this.uploadFileHandler);
  87. this.quill.on("text-change", () => {
  88. this.content = this.quill.root.innerHTML;
  89. });
  90. this.setContent(this.value);
  91. this.$emit("load", this.quill);
  92. },
  93. methods: {
  94. uploadFileHandler() {
  95. const selection = this.quill.getSelection();
  96. if (selection) {
  97. this.cursorIndex = selection.index;
  98. }
  99. this.$refs["upload-space"].open();
  100. },
  101. onUploadSpaceConfirm(files) {
  102. if (files.length > 0) {
  103. files.forEach((file, i) => {
  104. let [type] = file.type.split("/");
  105. this.quill.insertEmbed(
  106. this.cursorIndex + i,
  107. type,
  108. file.url,
  109. Quill.sources.USER
  110. );
  111. });
  112. }
  113. },
  114. setContent(val) {
  115. this.quill.root.innerHTML = val || "";
  116. }
  117. }
  118. };
  119. </script>
  120. <style lang="scss">
  121. .cl-editor-quill {
  122. background-color: #fff;
  123. .ql-snow {
  124. line-height: 22px !important;
  125. }
  126. .el-upload,
  127. #quill-upload-btn {
  128. display: none;
  129. }
  130. .ql-snow {
  131. border: 1px solid #dcdfe6;
  132. }
  133. .ql-snow .ql-tooltip[data-mode="link"]::before {
  134. content: "请输入链接地址:";
  135. }
  136. .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
  137. border-right: 0px;
  138. content: "保存";
  139. padding-right: 0px;
  140. }
  141. .ql-snow .ql-tooltip[data-mode="video"]::before {
  142. content: "请输入视频地址:";
  143. }
  144. .ql-snow .ql-picker.ql-size .ql-picker-label::before,
  145. .ql-snow .ql-picker.ql-size .ql-picker-item::before {
  146. content: "14px";
  147. }
  148. .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
  149. .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
  150. content: "10px";
  151. }
  152. .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
  153. .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
  154. content: "18px";
  155. }
  156. .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
  157. .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
  158. content: "32px";
  159. }
  160. .ql-snow .ql-picker.ql-header .ql-picker-label::before,
  161. .ql-snow .ql-picker.ql-header .ql-picker-item::before {
  162. content: "文本";
  163. }
  164. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
  165. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
  166. content: "标题1";
  167. }
  168. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
  169. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
  170. content: "标题2";
  171. }
  172. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
  173. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
  174. content: "标题3";
  175. }
  176. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
  177. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
  178. content: "标题4";
  179. }
  180. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
  181. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
  182. content: "标题5";
  183. }
  184. .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
  185. .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
  186. content: "标题6";
  187. }
  188. .ql-snow .ql-picker.ql-font .ql-picker-label::before,
  189. .ql-snow .ql-picker.ql-font .ql-picker-item::before {
  190. content: "标准字体";
  191. }
  192. .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
  193. .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
  194. content: "衬线字体";
  195. }
  196. .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
  197. .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
  198. content: "等宽字体";
  199. }
  200. }
  201. </style>