|
|
@@ -1,171 +1,146 @@
|
|
|
<template>
|
|
|
-<div class="quill-editor">
|
|
|
- <el-upload class="avatar-uploaderEdi" :action="url" :show-file-list="false" :on-success="uploadSuccess" :before-upload="beforeUpload">
|
|
|
- </el-upload>
|
|
|
- <quill-editor v-model="editorContent" class="ql-container sicepot" @change="onEditorChange($event)" :content="editorContent" :options="editorOption" ref="QuillEditor">
|
|
|
- </quill-editor>
|
|
|
+<div class="wangeditor-wrapper">
|
|
|
+ <div ref="toolbarContainer" class="toolbar-container"></div>
|
|
|
+ <div ref="editorContainer" class="editor-container"></div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import Cookies from "js-cookie";
|
|
|
-import 'quill/dist/quill.core.css'
|
|
|
-import 'quill/dist/quill.snow.css'
|
|
|
-import 'quill/dist/quill.bubble.css'
|
|
|
-import {
|
|
|
- quillEditor,
|
|
|
- Quill
|
|
|
-} from 'vue-quill-editor'
|
|
|
-// window.Quill 已在 main.js 入口处挂载,避免 webpack import 提升导致 resize 模块执行时 window.Quill 仍为 undefined。
|
|
|
-import ImageResize from 'quill-image-resize-module'
|
|
|
-Quill.register('modules/imageResize', ImageResize)
|
|
|
-// Quill 1.3.6 的 ui/icons.js 不含 undo/redo,需手动注入到全局 icons 映射,
|
|
|
-// 否则 snow 主题的 buildButtons 会因 icons[name] 为 null 跳过按钮 innerHTML 的渲染。
|
|
|
-const icons = Quill.import('ui/icons')
|
|
|
-icons['undo'] = '<svg viewbox="0 0 18 18"><polygon class="ql-fill ql-stroke" points="6 10 4 12 2 10 2 14 6 14 4 12 6 10 8 7 14 7 14 11 16 11 16 4 8 4 6 7"></polygon></svg>'
|
|
|
-icons['redo'] = '<svg viewbox="0 0 18 18"><polygon class="ql-fill ql-stroke" points="12 10 14 12 16 10 16 14 12 14 14 12 12 10 10 7 4 7 4 11 2 11 2 4 10 4 12 7"></polygon></svg>'
|
|
|
-const toolbarOptions = [
|
|
|
- ['undo', 'redo'], // 撤销,重做
|
|
|
- ['bold', 'italic', 'underline', 'strike'], // 加粗,斜体,下划线,删除线
|
|
|
- ['blockquote', 'code-block'], //引用,代码块
|
|
|
- [{
|
|
|
- 'header': 1
|
|
|
- }, {
|
|
|
- 'header': 2
|
|
|
- }], // 几级标题
|
|
|
- [{
|
|
|
- 'list': 'ordered'
|
|
|
- }, {
|
|
|
- 'list': 'bullet'
|
|
|
- }], // 有序列表,无序列表
|
|
|
- [{
|
|
|
- 'script': 'sub'
|
|
|
- }, {
|
|
|
- 'script': 'super'
|
|
|
- }], // 下角标,上角标
|
|
|
- [{
|
|
|
- 'indent': '-1'
|
|
|
- }, {
|
|
|
- 'indent': '+1'
|
|
|
- }], // 缩进
|
|
|
- [{
|
|
|
- 'direction': 'rtl'
|
|
|
- }], // 文字输入方向
|
|
|
- [{
|
|
|
- 'size': ['small', false, 'large', 'huge']
|
|
|
- }], // 字体大小
|
|
|
- [{
|
|
|
- 'header': [1, 2, 3, 4, 5, 6, false]
|
|
|
- }], // 标题
|
|
|
- [{
|
|
|
- 'color': []
|
|
|
- }, {
|
|
|
- 'background': []
|
|
|
- }], // 颜色选择
|
|
|
- [{
|
|
|
- 'font': ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial']
|
|
|
- }], // 字体
|
|
|
- [{
|
|
|
- 'align': []
|
|
|
- }], // 居中
|
|
|
- ['clean'], // 清除样式,
|
|
|
- ['link', 'image'], // 上传图片、上传视频
|
|
|
-]
|
|
|
+import '@wangeditor/editor/dist/css/style.css'
|
|
|
+import { createEditor, createToolbar } from '@wangeditor/editor'
|
|
|
+import Cookies from 'js-cookie'
|
|
|
+
|
|
|
export default {
|
|
|
+ name: 'EditorVue',
|
|
|
+ props: {
|
|
|
+ content: {
|
|
|
+ type: String,
|
|
|
+ default: ''
|
|
|
+ }
|
|
|
+ },
|
|
|
data() {
|
|
|
return {
|
|
|
- editorContent: this.content || '',
|
|
|
- editorOption: {
|
|
|
- placeholder: '请在这里输入',
|
|
|
- theme: 'snow', //主题 snow/bubble
|
|
|
- modules: {
|
|
|
- history: {
|
|
|
- delay: 1000,
|
|
|
- maxStack: 50,
|
|
|
- userOnly: false
|
|
|
- },
|
|
|
- imageResize: {
|
|
|
- displayStyles: {
|
|
|
- backgroundColor: 'black',
|
|
|
- border: 'none',
|
|
|
- color: 'white'
|
|
|
- },
|
|
|
- modules: ['Resize', 'DisplaySize', 'Toolbar']
|
|
|
- },
|
|
|
- toolbar: {
|
|
|
- container: toolbarOptions,
|
|
|
- handlers: {
|
|
|
- undo: function () {
|
|
|
- this.quill.history.undo()
|
|
|
- },
|
|
|
- redo: function () {
|
|
|
- this.quill.history.redo()
|
|
|
- },
|
|
|
- image: function (value) {
|
|
|
- if (value) {
|
|
|
- // 调用element的图片上传组件
|
|
|
- document.querySelector('.avatar-uploaderEdi input').click()
|
|
|
- } else {
|
|
|
- this.quill.format('image', false)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
- url: `${
|
|
|
- window.SITE_CONFIG["apiURL"]
|
|
|
- }/sys/oss/upload?token=${Cookies.get("token")}`,
|
|
|
+ editor: null,
|
|
|
+ toolbar: null
|
|
|
}
|
|
|
},
|
|
|
- props: ['content'],
|
|
|
- components: {
|
|
|
- quillEditor
|
|
|
- },
|
|
|
watch: {
|
|
|
- content(newValue) {
|
|
|
- // 检查新值是否与当前编辑器内容不同,避免不必要的更新和光标跳动
|
|
|
- if (newValue !== this.editorContent) {
|
|
|
- this.editorContent = newValue;
|
|
|
+ content(newVal) {
|
|
|
+ // 外部 prop 变化(异步加载详情)才回填,避免输入过程中光标跳动与循环触发
|
|
|
+ if (this.editor && newVal !== this.editor.getHtml()) {
|
|
|
+ this.editor.setHtml(newVal || '')
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
+ mounted() {
|
|
|
+ this._createEditor()
|
|
|
+ },
|
|
|
+ beforeDestroy() {
|
|
|
+ this._destroyEditor()
|
|
|
+ },
|
|
|
methods: {
|
|
|
+ // 父组件 ref 调用的初始化入口(对齐老 Quill 实现的 init() 行为)
|
|
|
init() {
|
|
|
- this.editorContent = this.content || '';
|
|
|
- },
|
|
|
- beforeUpload(file) {},
|
|
|
- // 值发生变化
|
|
|
- onEditorChange(editor) {
|
|
|
- this.$emit('EditorChange', this.editorContent);
|
|
|
+ this._destroyEditor()
|
|
|
+ this._createEditor()
|
|
|
},
|
|
|
- uploadSuccess(res) {
|
|
|
- // 获取富文本组件实例
|
|
|
- let quill = this.$refs.QuillEditor.quill
|
|
|
- // 如果上传成功
|
|
|
- if (res) {
|
|
|
- // 获取光标所在位置
|
|
|
- let length = quill.getSelection().index;
|
|
|
- // 插入图片,res为服务器返回的图片链接地址
|
|
|
- quill.insertEmbed(length, 'image', res.data)
|
|
|
- // 调整光标到最后
|
|
|
- quill.setSelection(length + 1)
|
|
|
- } else {
|
|
|
- // 提示信息,需引入Message
|
|
|
- this.$message.error('图片插入失败!')
|
|
|
+ _destroyEditor() {
|
|
|
+ if (this.editor) {
|
|
|
+ this.editor.destroy()
|
|
|
+ this.editor = null
|
|
|
+ }
|
|
|
+ if (this.toolbar) {
|
|
|
+ this.toolbar.destroy()
|
|
|
+ this.toolbar = null
|
|
|
}
|
|
|
},
|
|
|
+ _createEditor() {
|
|
|
+ const editorConfig = {
|
|
|
+ html: this.content || '',
|
|
|
+ placeholder: '请在这里输入',
|
|
|
+ readOnly: false,
|
|
|
+ autoFocus: false,
|
|
|
+ MENU_CONF: {
|
|
|
+ uploadImage: {
|
|
|
+ // 沿用项目原有的 OSS 上传端点,token 走 URL 参数
|
|
|
+ server: `${window.SITE_CONFIG['apiURL']}/sys/oss/upload?token=${Cookies.get('token') || ''}`,
|
|
|
+ fieldName: 'file',
|
|
|
+ // 后端响应是项目老格式 { code: 0, data: 'url', msg: '...' },
|
|
|
+ // wangEditor 默认期望 { errno, data: [{ url, alt, href }] },这里桥接一下
|
|
|
+ customInsert(res, insertFn) {
|
|
|
+ if (res && res.code === 0 && res.data) {
|
|
|
+ insertFn(res.data, '', res.data)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ fontFamily: {
|
|
|
+ fontFamilyList: [
|
|
|
+ { name: '宋体', value: 'SimSun' },
|
|
|
+ { name: '黑体', value: 'SimHei' },
|
|
|
+ { name: '微软雅黑', value: 'Microsoft YaHei' },
|
|
|
+ { name: '楷体', value: 'KaiTi' },
|
|
|
+ { name: '仿宋', value: 'FangSong' },
|
|
|
+ { name: 'Arial', value: 'Arial' }
|
|
|
+ ]
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onChange: (editor) => {
|
|
|
+ this.$emit('EditorChange', editor.getHtml())
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.editor = createEditor({
|
|
|
+ selector: this.$refs.editorContainer,
|
|
|
+ config: editorConfig,
|
|
|
+ mode: 'default'
|
|
|
+ })
|
|
|
|
|
|
+ const toolbarConfig = {
|
|
|
+ toolbarKeys: [
|
|
|
+ 'undo', 'redo',
|
|
|
+ '|',
|
|
|
+ 'headerSelect',
|
|
|
+ 'bold', 'italic', 'underline', 'strikeThrough',
|
|
|
+ '|',
|
|
|
+ 'color', 'bgColor',
|
|
|
+ '|',
|
|
|
+ 'fontSize', 'fontFamily',
|
|
|
+ 'lineHeight',
|
|
|
+ '|',
|
|
|
+ 'justifyLeft', 'justifyCenter', 'justifyRight',
|
|
|
+ '|',
|
|
|
+ 'bulletedList', 'numberedList',
|
|
|
+ 'indent',
|
|
|
+ '|',
|
|
|
+ 'insertLink', 'uploadImage',
|
|
|
+ 'blockquote', 'codeBlock',
|
|
|
+ '|',
|
|
|
+ 'divider', 'splitLine'
|
|
|
+ ]
|
|
|
+ }
|
|
|
+
|
|
|
+ this.toolbar = createToolbar({
|
|
|
+ editor: this.editor,
|
|
|
+ selector: this.$refs.toolbarContainer,
|
|
|
+ config: toolbarConfig,
|
|
|
+ mode: 'default'
|
|
|
+ })
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
</script>
|
|
|
|
|
|
<style>
|
|
|
-.sicepot {
|
|
|
- height: auto !important;
|
|
|
+.wangeditor-wrapper {
|
|
|
+ border: 1px solid #dcdfe6;
|
|
|
+ border-radius: 4px;
|
|
|
+ background: #fff;
|
|
|
}
|
|
|
-
|
|
|
-.sicepot .ql-container.ql-snow {
|
|
|
- height: 600px !important;
|
|
|
+.wangeditor-wrapper .toolbar-container {
|
|
|
+ border-bottom: 1px solid #dcdfe6;
|
|
|
+}
|
|
|
+.wangeditor-wrapper .editor-container {
|
|
|
+ min-height: 600px;
|
|
|
+ height: auto !important;
|
|
|
}
|
|
|
-</style>
|
|
|
+</style>
|