vue-quill-editor富文本编辑器 中文翻译组件,编辑与展示
vue项目中用到了富文本编辑器,网上找了一些,觉得vue-quill-editor最好用,
ui简洁,功能也好配,够用了,文档不好读,有些小细节需要自己注意,我懒得分析,就封装成了组件
大家用的时候直接cope组件,但是不要cope文章呀~~~
需要注意的是编辑器保存的格式,我们在用div展示的时候,有些格式无效,所以我也同样封装了供页面展示的组件
首先安装包
npm 安装 vue-quill-editor
这是编辑器效果图:
这是编辑器的组件代码:
代码比较多,我折叠起来了
1 <template> 2 <div class="editor_wrap"> 3 <!-- 图片上传组件辅助--> 4 <el-upload 5 class="avatar-uploader" 6 :action="serverUrl" 7 name="image" 8 :headers="header" 9 multiple 10 :show-file-list="false" 11 :on-success="uploadSuccess" 12 :on-error="uploadError" 13 :before-upload="beforeUpload" 14 ></el-upload> 15 <quill-editor 16 v-loading="quillUpdateImg" 17 class="editor" 18 v-model="content" 19 ref="myQuillEditor" 20 :options="editorOption" 21 @blur="onEditorBlur($event)" 22 @focus="onEditorFocus($event)" 23 @change="onEditorChange($event)" 24 ></quill-editor> 25 </div> 26 </template> 27 <script> 28 import { quillEditor } from "vue-quill-editor"; 29 import "quill/dist/quill.core.css"; 30 import "quill/dist/quill.snow.css"; 31 import "quill/dist/quill.bubble.css"; 32 // 工具栏配置 33 const toolbarOptions = [ 34 ["bold", "italic", "underline", "strike"], // 加粗 斜体 下划线 删除线 35 ["blockquote", "code-block"], // 引用 代码块 36 // [{ header: 1 }, { header: 2 }], // 1、2 级标题 37 [{ list: "ordered" }, { list: "bullet" }], // 有序、无序列表 38 [{ script: "sub" }, { script: "super" }], // 上标/下标 39 [{ indent: "-1" }, { indent: "+1" }], // 缩进 2 40 [{ color: [ 41 '#ffffff', '#ffd7d5', '#ffdaa9', '#fffed5', '#d4fa00', '#73fcd6', '#a5c8ff', '#ffacd5', '#ff7faa', 42 '#d6d6d6', '#ffacaa', '#ffb995', '#fffb00', '#73fa79', '#00fcff', '#78acfe', '#d84fa9', '#ff4f79', 43 '#b2b2b2', '#d7aba9', '#ff6827', '#ffda51', '#00d100', '#00d5ff', '#0080ff', '#ac39ff', '#ff2941', 44 '#888888', '#7a4442', '#ff4c00', '#ffa900', '#3da742', '#3daad6', '#0052ff', '#7a4fd6', '#d92142', 45 '#000000', '#7b0c00', '#ff0000', '#d6a841', '#407600', '#007aaa', '#021eaa', '#797baa', '#ab1942' 46 ] }, { background: [ 47 '#ffffff', '#ffd7d5', '#ffdaa9', '#fffed5', '#d4fa00', '#73fcd6', '#a5c8ff', '#ffacd5', '#ff7faa', 48 '#d6d6d6', '#ffacaa', '#ffb995', '#fffb00', '#73fa79', '#00fcff', '#78acfe', '#d84fa9', '#ff4f79', 49 '#b2b2b2', '#d7aba9', '#ff6827', '#ffda51', '#00d100', '#00d5ff', '#0080ff', '#ac39ff', '#ff2941', 50 '#888888', '#7a4442', '#ff4c00', '#ffa900', '#3da742', '#3daad6', '#0052ff', '#7a4fd6', '#d92142', 51 '#000000', '#7b0c00', '#ff0000', '#d6a841', '#407600', '#007aaa', '#021eaa', '#797baa', '#ab1942' 52 ] }], // 字体颜色、字体背景颜色 53 [{ size: ["small", false, "large", "huge"] }], // 字体大小 2 54 [{ header: [1, 2, 3, 4, 5, 6, false] }], // 标题 55 [{ font: [] }], // 字体种类 2 56 [{'direction': 'rtl'}], // 文本方向 2 57 [{ align: [] }], // 对齐方式 2 58 ["clean"], // 清除文本格式 59 ["link", "image", "video"] // 链接、图片、视频 60 ]; 61 export default { 62 props: { 63 /*编辑器的内容*/ 64 value: null, 65 /*图片大小*/ 66 maxSize: { 67 type: Number, 68 default: 4000 //kb 69 } 70 }, 71 72 components: { 73 quillEditor 74 }, 75 watch: { 76 value(val) { 77 this.content = this.value 78 } 79 }, 80 81 data() { 82 return { 83 content: this.value, 84 quillUpdateImg: false, // 根据图片上传状态来确定是否显示loading动画,刚开始是false,不显示 85 editorOption: { 86 theme: "snow", // or 'bubble' 87 placeholder: "您想说点什么?", 88 modules: { 89 toolbar: { 90 container: toolbarOptions, 91 handlers: { 92 image: function(value) { 93 if (value) { 94 // 触发input框选择图片文件 95 document.querySelector(".avatar-uploader input").click(); 96 } else { 97 this.quill.format("image", false); 98 } 99 }, 100 // link: function(value) { 101 // if (value) { 102 // var href = prompt('请输入url'); 103 // this.quill.format("link", href); 104 // } else { 105 // this.quill.format("link", false); 106 // } 107 // }, 108 } 109 } 110 } 111 }, 112 serverUrl: process.env.VUE_APP_API_URL + "config/upload", // 这里写你要上传的图片服务器地址 113 header: { 114 token: localStorage.getItem("token") 115 } 116 }; 117 }, 118 119 methods: { 120 onEditorBlur() { 121 //失去焦点事件 122 }, 123 onEditorFocus() { 124 //获得焦点事件 125 }, 126 onEditorChange({ editor, html, text }) { 127 this.content = html; 128 //内容改变事件 129 this.$emit("textareaData", this.content); 130 }, 131 132 // 富文本图片上传前 133 beforeUpload() { 134 // 显示loading动画 135 this.quillUpdateImg = true; 136 }, 137 138 uploadSuccess(res, file) { 139 // res为图片服务器返回的数据 140 // 获取富文本组件实例 141 let quill = this.$refs.myQuillEditor.quill; 142 // 如果上传成功 143 if (res.error == 0) { 144 // 获取光标所在位置 145 let length = quill.getSelection().index; 146 // 插入图片 res.url为服务器返回的图片地址 147 quill.insertEmbed(length, "image", res.info.img_url); 148 // 调整光标到最后 149 quill.setSelection(length + 1); 150 } else { 151 this.$message.error("图片插入失败"); 152 } 153 // loading动画消失 154 this.quillUpdateImg = false; 155 }, 156 // 富文本图片上传失败 157 uploadError() { 158 // loading动画消失 159 this.quillUpdateImg = false; 160 this.$message.error("图片插入失败"); 161 } 162 } 163 }; 164 </script> 165 166 <style scoped> 167 .editor_wrap /deep/ .avatar-uploader { 168 display: none; 169 } 170 .editor_wrap /deep/ .editor { 171 line-height: normal !important; 172 height: 270px; 173 margin-bottom: 60px; 174 } 175 .editor_wrap /deep/ .editor .ql-bubble .ql-editor a { 176 color: #136ec2; 177 } 178 .editor_wrap /deep/ .editor img { 179 max-width: 720px; 180 margin:10px; 181 } 182 .editor_wrap /deep/ .ql-snow .ql-color-picker .ql-picker-options { 183 padding: 3px 5px; 184 width: 192px; 185 } 186 .editor_wrap /deep/ .ql-snow .ql-tooltip[data-mode="link"]::before { 187 content: "请输入链接地址:"; 188 } 189 .editor_wrap /deep/ .ql-snow .ql-tooltip.ql-editing a.ql-action::after { 190 border-right: 0px; 191 content: "保存"; 192 padding-right: 0px; 193 } 194 195 .editor_wrap /deep/ .ql-snow .ql-tooltip[data-mode="video"]::before { 196 content: "请输入视频地址:"; 197 } 198 199 .editor_wrap /deep/ .ql-snow .ql-picker.ql-size .ql-picker-label::before, 200 .editor_wrap /deep/ .ql-snow .ql-picker.ql-size .ql-picker-item::before { 201 content: "14px"; 202 } 203 .editor_wrap /deep/ .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="small"]::before, 204 .editor_wrap /deep/ .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="small"]::before { 205 content: "10px"; 206 } 207 .editor_wrap /deep/ .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="large"]::before, 208 .editor_wrap /deep/ .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="large"]::before { 209 content: "18px"; 210 } 211 .editor_wrap /deep/ .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="huge"]::before, 212 .editor_wrap /deep/ .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="huge"]::before { 213 content: "32px"; 214 } 215 216 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label::before, 217 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item::before { 218 content: "文本"; 219 } 220 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before, 221 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before { 222 content: "标题1"; 223 } 224 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before, 225 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before { 226 content: "标题2"; 227 } 228 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before, 229 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before { 230 content: "标题3"; 231 } 232 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before, 233 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before { 234 content: "标题4"; 235 } 236 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before, 237 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before { 238 content: "标题5"; 239 } 240 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before, 241 .editor_wrap /deep/ .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before { 242 content: "标题6"; 243 } 244 245 .editor_wrap /deep/ .ql-snow .ql-picker.ql-font .ql-picker-label::before, 246 .editor_wrap /deep/ .ql-snow .ql-picker.ql-font .ql-picker-item::before { 247 content: "标准字体"; 248 } 249 .editor_wrap /deep/ .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="serif"]::before, 250 .editor_wrap /deep/ .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="serif"]::before { 251 content: "衬线字体"; 252 } 253 .editor_wrap /deep/ .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="monospace"]::before, 254 .editor_wrap /deep/ .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="monospace"]::before { 255 content: "等宽字体"; 256 } 257 </style>
这是供页面展示的效果图:
这是供页面展示的组件代码:
代码比较多,同样我折叠起来了
1 <template> 2 <div class="editor_wrap"> 3 <quill-editor 4 class="editor" 5 v-model="content" 6 ref="myQuillEditor" 7 :options="editorOption" 8 @focus="onEditorFocus($event)" 9 ></quill-editor> 10 </div> 11 </template> 12 <script> 13 // 工具栏配置 14 const toolbarOptions = []; 15 16 import { quillEditor } from "vue-quill-editor"; 17 import "quill/dist/quill.core.css"; 18 import "quill/dist/quill.snow.css"; 19 import "quill/dist/quill.bubble.css"; 20 21 export default { 22 props: { 23 /*编辑器的内容*/ 24 value: null 25 }, 26 components: { 27 quillEditor 28 }, 29 watch: { 30 value(val) { 31 this.content = this.value; 32 } 33 }, 34 computed: { 35 editor() { 36 return this.$refs.myQuillEditor.quill; 37 } 38 }, 39 data() { 40 return { 41 content: this.value, 42 editorOption: { 43 theme: "bubble", // or 'bubble' 44 placeholder: "您想说点什么?", 45 modules: { 46 toolbar: { 47 container: toolbarOptions, 48 handlers: {} 49 } 50 } 51 } 52 }; 53 }, 54 methods: { 55 onEditorFocus(editor) { 56 // 富文本获得焦点时的事件 57 editor.enable(false); // 在获取焦点的时候禁用 58 } 59 } 60 }; 61 </script> 62 63 <style scoped> 64 .editor_wrap /deep/ .editor img { 65 max-width: 720px; 66 margin:10px; 67 } 68 .editor_wrap /deep/ .editor .ql-bubble .ql-editor a { 69 color: #136ec2; 70 } 71 </style>
值得注意的是页面展示的时候,会有一个鼠标光标出现,目前我没有处理,欢迎大神们留言指导
如果我的文章对您有帮助,欢迎您点击推荐以及评论 QAQ