懒羊羊
2024-01-31 e57a8990ae56f657a59c435a0613c5f7a8728003
提交 | 用户 | 时间
e57a89 1 <template>
2   <div>
3     <el-upload
4       :action="uploadUrl"
5       :before-upload="handleBeforeUpload"
6       :on-success="handleUploadSuccess"
7       :on-error="handleUploadError"
8       name="file"
9       :show-file-list="false"
10       :headers="headers"
11       style="display: none"
12       ref="upload"
13       v-if="this.type == 'url'"
14     >
15     </el-upload>
16     <div class="editor" ref="editor" :style="styles"></div>
17   </div>
18 </template>
19
20 <script>
21 import Quill from "quill";
22 import "quill/dist/quill.core.css";
23 import "quill/dist/quill.snow.css";
24 import "quill/dist/quill.bubble.css";
25 import { getToken } from "@/utils/auth";
26
27 export default {
28   name: "Editor",
29   props: {
30     /* 编辑器的内容 */
31     value: {
32       type: String,
33       default: "",
34     },
35     /* 高度 */
36     height: {
37       type: Number,
38       default: null,
39     },
40     /* 最小高度 */
41     minHeight: {
42       type: Number,
43       default: null,
44     },
45     /* 只读 */
46     readOnly: {
47       type: Boolean,
48       default: false,
49     },
50     /* 上传文件大小限制(MB) */
51     fileSize: {
52       type: Number,
53       default: 5,
54     },
55     /* 类型(base64格式、url格式) */
56     type: {
57       type: String,
58       default: "url",
59     }
60   },
61   data() {
62     return {
63       uploadUrl: process.env.VUE_APP_BASE_API + "/common/upload", // 上传的图片服务器地址
64       headers: {
65         Authorization: "Bearer " + getToken()
66       },
67       Quill: null,
68       currentValue: "",
69       options: {
70         theme: "snow",
71         bounds: document.body,
72         debug: "warn",
73         modules: {
74           // 工具栏配置
75           toolbar: [
76             ["bold", "italic", "underline", "strike"],       // 加粗 斜体 下划线 删除线
77             ["blockquote", "code-block"],                    // 引用  代码块
78             [{ list: "ordered" }, { list: "bullet" }],       // 有序、无序列表
79             [{ indent: "-1" }, { indent: "+1" }],            // 缩进
80             [{ size: ["small", false, "large", "huge"] }],   // 字体大小
81             [{ header: [1, 2, 3, 4, 5, 6, false] }],         // 标题
82             [{ color: [] }, { background: [] }],             // 字体颜色、字体背景颜色
83             [{ align: [] }],                                 // 对齐方式
84             ["clean"],                                       // 清除文本格式
85             ["link", "image", "video"]                       // 链接、图片、视频
86           ],
87         },
88         placeholder: "请输入内容",
89         readOnly: this.readOnly,
90       },
91     };
92   },
93   computed: {
94     styles() {
95       let style = {};
96       if (this.minHeight) {
97         style.minHeight = `${this.minHeight}px`;
98       }
99       if (this.height) {
100         style.height = `${this.height}px`;
101       }
102       return style;
103     },
104   },
105   watch: {
106     value: {
107       handler(val) {
108         if (val !== this.currentValue) {
109           this.currentValue = val === null ? "" : val;
110           if (this.Quill) {
111             this.Quill.pasteHTML(this.currentValue);
112           }
113         }
114       },
115       immediate: true,
116     },
117   },
118   mounted() {
119     this.init();
120   },
121   beforeDestroy() {
122     this.Quill = null;
123   },
124   methods: {
125     init() {
126       const editor = this.$refs.editor;
127       this.Quill = new Quill(editor, this.options);
128       // 如果设置了上传地址则自定义图片上传事件
129       if (this.type == 'url') {
130         let toolbar = this.Quill.getModule("toolbar");
131         toolbar.addHandler("image", (value) => {
132           if (value) {
133             this.$refs.upload.$children[0].$refs.input.click();
134           } else {
135             this.quill.format("image", false);
136           }
137         });
138       }
139       this.Quill.pasteHTML(this.currentValue);
140       this.Quill.on("text-change", (delta, oldDelta, source) => {
141         const html = this.$refs.editor.children[0].innerHTML;
142         const text = this.Quill.getText();
143         const quill = this.Quill;
144         this.currentValue = html;
145         this.$emit("input", html);
146         this.$emit("on-change", { html, text, quill });
147       });
148       this.Quill.on("text-change", (delta, oldDelta, source) => {
149         this.$emit("on-text-change", delta, oldDelta, source);
150       });
151       this.Quill.on("selection-change", (range, oldRange, source) => {
152         this.$emit("on-selection-change", range, oldRange, source);
153       });
154       this.Quill.on("editor-change", (eventName, ...args) => {
155         this.$emit("on-editor-change", eventName, ...args);
156       });
157     },
158     // 上传前校检格式和大小
159     handleBeforeUpload(file) {
160       const type = ["image/jpeg", "image/jpg", "image/png", "image/svg"];
161       const isJPG = type.includes(file.type);
162       // 检验文件格式
163       if (!isJPG) {
164         this.$message.error(`图片格式错误!`);
165         return false;
166       }
167       // 校检文件大小
168       if (this.fileSize) {
169         const isLt = file.size / 1024 / 1024 < this.fileSize;
170         if (!isLt) {
171           this.$message.error(`上传文件大小不能超过 ${this.fileSize} MB!`);
172           return false;
173         }
174       }
175       return true;
176     },
177     handleUploadSuccess(res, file) {
178       // 如果上传成功
179       if (res.code == 200) {
180         // 获取富文本组件实例
181         let quill = this.Quill;
182         // 获取光标所在位置
183         let length = quill.getSelection().index;
184         // 插入图片  res.url为服务器返回的图片地址
185         quill.insertEmbed(length, "image", process.env.VUE_APP_BASE_API + res.fileName);
186         // 调整光标到最后
187         quill.setSelection(length + 1);
188       } else {
189         this.$message.error("图片插入失败");
190       }
191     },
192     handleUploadError() {
193       this.$message.error("图片插入失败");
194     },
195   },
196 };
197 </script>
198
199 <style>
200 .editor, .ql-toolbar {
201   white-space: pre-wrap !important;
202   line-height: normal !important;
203 }
204 .quill-img {
205   display: none;
206 }
207 .ql-snow .ql-tooltip[data-mode="link"]::before {
208   content: "请输入链接地址:";
209 }
210 .ql-snow .ql-tooltip.ql-editing a.ql-action::after {
211   border-right: 0px;
212   content: "保存";
213   padding-right: 0px;
214 }
215 .ql-snow .ql-tooltip[data-mode="video"]::before {
216   content: "请输入视频地址:";
217 }
218 .ql-snow .ql-picker.ql-size .ql-picker-label::before,
219 .ql-snow .ql-picker.ql-size .ql-picker-item::before {
220   content: "14px";
221 }
222 .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before,
223 .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before {
224   content: "10px";
225 }
226 .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before,
227 .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before {
228   content: "18px";
229 }
230 .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before,
231 .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before {
232   content: "32px";
233 }
234 .ql-snow .ql-picker.ql-header .ql-picker-label::before,
235 .ql-snow .ql-picker.ql-header .ql-picker-item::before {
236   content: "文本";
237 }
238 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
239 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
240   content: "标题1";
241 }
242 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
243 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
244   content: "标题2";
245 }
246 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
247 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
248   content: "标题3";
249 }
250 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
251 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
252   content: "标题4";
253 }
254 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
255 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
256   content: "标题5";
257 }
258 .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
259 .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
260   content: "标题6";
261 }
262 .ql-snow .ql-picker.ql-font .ql-picker-label::before,
263 .ql-snow .ql-picker.ql-font .ql-picker-item::before {
264   content: "标准字体";
265 }
266 .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before,
267 .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before {
268   content: "衬线字体";
269 }
270 .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before,
271 .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before {
272   content: "等宽字体";
273 }
274 </style>