vue3封装富文本组件

246 阅读6分钟

vue3 引入@vueup/vue-quill 封装富文本组件

  1. 终端下载

npm install @vueup/vue-quill@alpha --save 或者 npm install @vueup/vue-quill@latest --save

  1. 封装子组件QuillEditor
<template>
  <QuillEditor
    theme="snow"
    :options="editorOption"
    contentType="html"
  />
</template>

<script lang="js" setup>
import { QuillEditor, Quill } from '@vueup/vue-quill'
import '@vueup/vue-quill/dist/vue-quill.snow.css';
import '@vueup/vue-quill/dist/vue-quill.bubble.css'

// 富文本配置
const fontSize = Quill.import('attributors/style/size') // 引入这个后会把样式写在style上
fontSize.whitelist = [false, '12px', '14px', '16px', '18px', '20px', '24px', '28px', '32px']
Quill.register(fontSize, true)
// 自定义字体类型
const fonts = [false, 'SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial', 'Times-New-Roman', 'sans-serif', '宋体', '黑体']
const Font = Quill.import('attributors/style/font')
Font.whitelist = fonts
Quill.register(Font, true);
const editorOption = {
  modules: {
    toolbar: [
      // ['bold', 'italic', 'underline', 'strike'], // 加粗,斜体,下划线,删除线
      ['bold', 'italic'], // 下划线,删除线无效
      // [{ header: 1 }, { header: 2 }], // 标题,键值对的形式;1、2表示字体大小
      [{ align: [] }], // 对齐方式
      [{ list: 'ordered' }, { list: 'bullet' }], // 列表
      [{ indent: '-1' }, { indent: '+1' }], // 缩进
      // [{ direction: 'ltl' }, { direction: 'rtl' }], // 文本方向
      [{ size: fontSize.whitelist }], // 字体大小
      [{ font: Font.whitelist }], // 字体
      [{ header: [1, 2, 3, 4, 5, 6, false] }], // 几级标题
      [{ script: 'sub' }, { script: 'super' }], // 上下标
      [{ color: [] }, { background: [] }], // 字体颜色,字体背景颜色
      ['clean'], // 清除字体样式
    ]
  },
  placeholder: '(如果选是,具体列出职责事项)',
}
// 鼠标悬停设置无效
// const addQuillTitle = () => {
//   const titleConfig = {
//     'ql-bold': '加粗',
//     'ql-color': '颜色',
//     'ql-font': '字体',
//     'ql-code': '插入代码',
//     'ql-italic': '斜体',
//     'ql-link': '添加链接',
//     'ql-background': '背景颜色',
//     'ql-size': '字体大小',
//     'ql-strike': '删除线',
//     'ql-script': '上标/下标',
//     'ql-underline': '下划线',
//     'ql-blockquote': '引用',
//     'ql-header': '标题',
//     'ql-indent': '缩进',
//     'ql-list': '列表',
//     'ql-align': '文本对齐',
//     'ql-direction': '文本方向',
//     'ql-code-block': '代码块',
//     'ql-formula': '公式',
//     'ql-image': '图片',
//     'ql-video': '视频',
//     'ql-clean': '清除字体样式'
//   }
//   const oToolBar = document.querySelector('.ql-toolbar')
//   if (!oToolBar) {
//     return false
//   }
//   const aButton = oToolBar.querySelectorAll('button')
//   const aSelect = oToolBar.querySelectorAll('select')
//   aButton.forEach(function (item) {
//     if (item.className === 'ql-script') {
//       item.value === 'sub' ? item.title = '下标' : item.title = '上标'
//     } else if (item.className === 'ql-indent') {
//       item.value === '+1' ? item.title = '向右缩进' : item.title = '向左缩进'
//     } else {
//       item.title = titleConfig[item.classList[0]]
//     }
//   })
//   aSelect.forEach(function (item) {
//     item.parentNode.title = titleConfig[item.classList[0]]
//   })
// }
// const hhh = () => {
//   showDialog.value = true
//   addQuillTitle()
// }
</script>

<script lang="js">
export default {
  name: 'QuillEditor'
}
</script>

<style scoped lang="scss">
// 汉化 父组件中不生效,需要在父组件中重新设置
:deep{
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="12px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="12px"]::before {
    content: '12px';
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before {
    content: "14px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before {
    content: "16px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before {
    content: "18px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before {
    content: "20px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="24px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="24px"]::before {
    content: "24px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="28px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="28px"]::before {
    content: "28px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="32px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="32px"]::before {content: "32px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item::before {
    content: "字号";
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item::before {
    content: "正文" !important;
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
    content: "标题1" !important;
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
    content: "标题2" !important;
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
    content: "标题3" !important;
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
    content: "标题4" !important;
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
    content: "标题5" !important;}
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
    content: "标题6" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item::before {
    content: "字体" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="Arial"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="Arial"]::before {
    content: "Arial" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="SimSun"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="SimSun"]::before {
    content: "SimSun" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="SimHei"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="SimHei"]::before {
    content: "SimHei" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="Microsoft-YaHei"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="Microsoft-YaHei"]::before {
    content: "微软雅黑" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="Arial"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="Arial"]::before {
    content: "Arial" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="Times-New-Roman"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="Times-New-Roman"]::before {
    content: "罗马" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="KaiTi"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="KaiTi"]::before {
    content: "楷体" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="sans-serif"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="sans-serif"]::before {
    content: "sans-serif" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="宋体"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="宋体"]::before {
    content: "宋体" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="黑体"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="黑体"]::before {
    content: "黑体" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="FangSong"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="FangSong"]::before {
    content: "仿宋" !important;
  }
}
</style>
  1. 父组件引入子组件QuillEditor
<template>
    <div>
        // 其他代码...
       <QuillEditor v-model:content="duty_content" />
    </div>
<template/>

<script lang="js" setup>
import { ref } from 'vue'
import QuillEditor from '@/components/QuillEditor.vue'

// 绑定变量值
const duty_content = ref('')
</script>

<style scoped lang="scss">
// 汉化 子组件设置不生效
:deep{
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="12px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="12px"]::before {
    content: '12px';
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before {
    content: "14px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before {
    content: "16px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before {
    content: "18px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before {
    content: "20px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="24px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="24px"]::before {
    content: "24px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="28px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="28px"]::before {
    content: "28px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label[data-value="32px"]::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item[data-value="32px"]::before {content: "32px";
  }
  .ql-snow .ql-picker.ql-size .ql-picker-label::before,
  .ql-snow .ql-picker.ql-size .ql-picker-item::before {
    content: "字号";
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item::before {
    content: "正文" !important;
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="1"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
    content: "标题1" !important;
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="2"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
    content: "标题2" !important;
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="3"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
    content: "标题3" !important;
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="4"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
    content: "标题4" !important;
  }
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="5"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
    content: "标题5" !important;}
  .ql-snow .ql-picker.ql-header .ql-picker-label[data-value="6"]::before,
  .ql-snow .ql-picker.ql-header .ql-picker-item[data-value="6"]::before {
    content: "标题6" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item::before {
    content: "字体" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="Arial"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="Arial"]::before {
    content: "Arial" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="SimSun"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="SimSun"]::before {
    content: "SimSun" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="SimHei"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="SimHei"]::before {
    content: "SimHei" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="Microsoft-YaHei"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="Microsoft-YaHei"]::before {
    content: "微软雅黑" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="Arial"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="Arial"]::before {
    content: "Arial" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="Times-New-Roman"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="Times-New-Roman"]::before {
    content: "罗马" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="KaiTi"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="KaiTi"]::before {
    content: "楷体" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="sans-serif"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="sans-serif"]::before {
    content: "sans-serif" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="宋体"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="宋体"]::before {
    content: "宋体" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="黑体"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="黑体"]::before {
    content: "黑体" !important;
  }
  .ql-snow .ql-picker.ql-font .ql-picker-label[data-value="FangSong"]::before,
  .ql-snow .ql-picker.ql-font .ql-picker-item[data-value="FangSong"]::before {
    content: "仿宋" !important;
  }
}
</style>

控制台有警告,不知道后面会不会出现兼容问题

[Deprecation] Listener added for a synchronous 'DOMNodeInserted' DOM Mutation Event. This event type is deprecated (w3c.github.io/uievents/#l…) and work is underway to remove it from this browser. Usage of this event listener will cause performance issues today, and represents a risk of future incompatibility. Consider using MutationObserver instead.