vue-quill-editor的使用

最近做项目有个富文本需求,选择用vue-quill-editor,有些个性化设置,在这里记录一下:

功能描述:点击内容,展示富文本,保存后展示文本(如下图):

编辑时候:

 

 

 提交后变成查看状态:

 

技术点有一下几个:

1.toolbar个性化设置(字体大小设置item为自定义字体、自定义了一个工作汇报标签,实现点击展示弹窗效果)

 

实现方法主要是toolbar设置和样式得处理:具体代码入下:

toolbar设置:

//toolbarOptions配置 
export const toolbarOptions = [
  ['bold', 'italic', 'underline', 'strike'], // 加粗,倾斜,下划线,删除线
  ['blockquote', 'code-block'], // 引号,代码

  [{ list: 'ordered' }, { list: 'bullet' }], // 有序列表,无序列表

  [{ indent: '-1' }, { indent: '+1' }], // 左移,右移
  [{ direction: 'rtl' }], // 左右对齐

  [{ size: [false, '10px', '14px', '16px', '18px', '20px', '24px'] }], // 字体大小
  [{ header: [1, 2, 3, 4, 5, false] }], // 标题大小

  [{ color: [] }, { background: [] }], // 文字颜色,文字背景
  [{ font: ['SimSun', 'SimHei', 'Microsoft-YaHei', 'KaiTi', 'FangSong', 'Arial', 'Times-New-Roman', 'sans-serif'] }], // 字体风格
  [{ align: [] }], // 对齐方式

  ['image', 'link', 'clean', 'report']// 图片,链接,清除,自定义行为
]
//在quill中使用
toolbar: {
            container: toolbarOptions,
            handlers: {
              report: this.openWorkReport
            }
          }

  

  css配合(全部样式,可以直接复制使用):

/* 字体风格 */
.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: '宋体';
  font-family: 'SimSun';
}

.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: '黑体';
  font-family: 'SimHei';
}

.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: '微软雅黑';
  font-family: 'Microsoft YaHei';
}

.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: '楷体';
  font-family: 'KaiTi';
}

.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: '仿宋';
  font-family: 'FangSong';
}

.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';
  font-family: 'Arial';
}

.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: 'Times New Roman';
  font-family: 'Times New Roman';
}

.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';
  font-family: 'sans-serif';
}

.ql-font-SimSun {
  font-family: 'SimSun';
}

.ql-font-SimHei {
  font-family: 'SimHei';
}

.ql-font-Microsoft-YaHei {
  font-family: 'Microsoft YaHei';
}

.ql-font-KaiTi {
  font-family: 'KaiTi';
}

.ql-font-FangSong {
  font-family: 'FangSong';
}

.ql-font-Arial {
  font-family: 'Arial';
}

.ql-font-Times-New-Roman {
  font-family: 'Times New Roman';
}

.ql-font-sans-serif {
  font-family: 'sans-serif';
}
/* 字体大小 */
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="false"]::before{
    content: 'Normal';
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="10px"]::before{
    content: '10px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="10px"]::before {
    content: '10px';
    font-size: 10px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="14px"]::before{
    content: '14px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="14px"]::before {
    content: '14px';
    font-size: 14px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="16px"]::before{
    content: '16px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="16px"]::before {
    content: '16px';
    font-size: 16px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="18px"]::before {
    content: '18px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="18px"]::before {
    content: '18px';
    font-size: 18px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="20px"]::before{
    content: '20px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="20px"]::before {
    content: '20px';
    font-size: 20px;
}
.ql-snow .ql-picker.ql-size .ql-picker-label[data-value="24px"]::before{
    content: '24px';
}
.ql-snow .ql-picker.ql-size .ql-picker-item[data-value="24px"]::before {
    content: '24px';
    font-size: 24px;
}
/* 段落大小 */
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="1"]::before {
    content: '标题1';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="2"]::before {
    content: '标题2';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="3"]::before {
    content: '标题3';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="4"]::before {
    content: '标题4';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="5"]::before {
    content: '标题5';
}
.ql-snow .ql-picker.ql-header .ql-picker-item[data-value="false"]::before {
    content: '正常';
}
/* 默认设置 */
.ql-snow .ql-editor{
    font-size: 14px;
}
/* 查看样式 */
.view-editor .ql-toolbar{
 display: none;
}
.view-editor .ql-container.ql-snow{
    border: 0;
}
.view-editor .ql-container.ql-snow .ql-editor{
    padding: 0;
}
/* 编辑样式 */
.edit-editor .ql-toolbar{
    display: block;
}
.edit-editor .ql-container.ql-snow{
    border: 1px solid #ccc;
    min-height: inherit;
}
/* 自定义toobar样式设计 */
/* 工作汇报弹窗 */
.ql-snow.ql-toolbar .ql-formats .ql-report{
    background: url("../images/meeting/report.png") no-repeat;
    background-size: contain;
    display: inline-block;
    height: 18px;
    margin: 3px 5px;
    width: 28px;
}

 2.实现@或者#用户功能,js实现代码入下:

// 1.引入mention 组件
import mention from 'quill-mention'
//2.在quill potion配置入下:
// mentionList需要格式化为{id, value}形式
modules: {
    mention: {
      allowedChars: /^[A-Za-z\sÅÄÖåäö]*$/,
      source: (searchTerm, renderList) => {
        renderList(this.mentionList, searchTerm)
      },
      onSelect: (data, insertItem) => {
        insertItem(data)
        this.onSelectd()
      },
      mentionDenotationChars: ['@', '#']
    }
  }

  最后给出完整得vue配置:

<template>
    <div @click="$emit('click')" :class="readonly?'view-editor':'edit-editor'" >
        <quill-editor v-model="content"
                  :disabled="readonly"
                  :options="editorOption"
                  @change="onEditorChange($event)"
                  :style="{minHeight:readonly?'0':minHeigh+'px'}"
                  >
    </quill-editor>
    </div>
</template>

<script>
import Quill from 'quill'
import { quillEditor } from 'vue-quill-editor'
import { ImageDrop } from 'quill-image-drop-module'
import ImageResize from 'quill-image-resize-module'
import mention from 'quill-mention' // 引入mention 组件
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
import { fontSize, fonts, toolbarOptions } from './config'
Quill.register('modules/imageDrop', ImageDrop)
Quill.register('modules/imageResize', ImageResize)
Quill.register('modules/mention', mention)
const Size = Quill.import('attributors/style/size')
Size.whitelist = fontSize
Quill.register(Size, true)
const Font = Quill.import('formats/font')
Font.whitelist = fonts
Quill.register(Font, true)
export default {
  name: 'index',
  components: {
    quillEditor
  },
  props: {
    readonly: {
      type: Boolean,
      default: false
    },
    value: {
      type: String,
      default: ''
    },
    minHeigh: {
      type: Number,
      default: 200
    },
    isPushContent: {
      type: Boolean,
      default: false
    },
    mentionList: { // @功能
      type: Array,
      default: () => {
        // 数据源(遍历成{id, value}形式)
        return []
      }
    }
  },
  data () {
    return {
      flag: true,
      content: '',
      editorOption: {
        placeholder: '请输入',
        theme: 'snow',
        modules: {
          mention: {
            allowedChars: /^[A-Za-z\sÅÄÖåäö]*$/,
            source: (searchTerm, renderList) => {
              renderList(this.mentionList, searchTerm)
            },
            onSelect: (data, insertItem) => {
              //   const item = { text: `@${data.value}`, name: data.value, id: data.id }
              insertItem(data)
              this.onSelectd()
            },
            mentionDenotationChars: ['@', '#']
          },
          imageDrop: true,
          imageResize: {},
          toolbar: {
            container: toolbarOptions,
            handlers: {
              report: this.openWorkReport
            }
          }
        }
      },
      replyTo: ''
    }
  },
  watch: {
    value: {
      handler (val) {
        this.content = val
      },
      immediate: true
    }
  },
  methods: {
    onSelectd () {
      this.replyTo = this.mentionList
        .reduce((acc, cur) => {
          if (this.content.includes(cur.value)) {
            acc.push(cur.id)
          }
          return acc
        }, [])
        .toString()
    },
    getEditorValue () {
      return this.content
    },
    openWorkReport () {
      this.$emit('openWorkReport')
    },
    setEditorValue (content = '') {
      this.content = content
    },
    onEditorChange ({ quill, html, text }) {
      this.content = html
      this.$emit('input', this.content)
      this.$emit('change', this.content)
    }
  }
}
</script>

<style>
@import '~@assets/css/font.css';
</style>

  希望对你有用!

 

posted @ 2022-01-04 18:54  鸡腿太小  阅读(3228)  评论(0编辑  收藏  举报