quill 富文本编辑器 @提及(仿微信)【处理粘贴,光标异常等问题】

使用插件quill-mention,实现在quill 富文本编辑器使用@或#提及用户。

1. 安装

npm install quill-mention vue-quill-editor --save

"quill-mention": "3.1.0",
"vue-quill-editor": "3.0.6",

2.使用

<div class="quill-input">
            <mention-editor
              id="debotInput"
              ref="mentionEditorRef"
              :mention-list="atValues"
              :disabled="disableInput"
              :word-limit="wordLimit"
              :placeholder="placeholder"
              @enter="keyEnter"
              @focus="handleEditorFocus"
              @blur="handleEditorBlur"
              @change="handleEditorChange"
            />
          </div>
 // 用户手动输入的内容,排除选择的意图
    getInputMessage() {
      return this.$refs?.mentionEditorRef?.getInputText()
    },
    // 全部内容
    getMessage() {
      return this.$refs?.mentionEditorRef?.getText() || ''
    },

3.示例

mention-editor.vue

<template>
  <div class="mention-editor-wrap">
    <quill-editor
      ref="editor"
      v-model="value"
      :options="editorOptions"
      :disabled="disabled"
      @input="onHandleInput"
      @ready="onHandleReady"
      @change="handleEditorChange"
      @focus="handleEditorFocus"
      @blur="handleEditorBlur"
    />
  </div>
</template>
<script>
import { quillEditor } from 'vue-quill-editor' // 引入富文本
import 'quill-mention' // 引入mention 组件
import 'quill-mention/dist/quill.mention.min.css'
export default {
  components: {
    quillEditor
  },
  props: {
    inConversation: {
      type: Boolean,
      default: true
    },
    wordLimit: {
      type: Number,
      default: 0 // 字符长度限制  0为不限制
    },
    disabled: {
      type: Boolean,
      default: false
    },
    mentionList: {
      type: Array,
      default: () => []
    },
    placeholder: {
      type: String,
      default: '请输入内容哦'
    }
  },
  data() {
    return {
      disTips: false, // 插入数据的时候不需要执行提示,只有手动输入的时候才需要执行提示
      editorOptions: {
        placeholder: '请输入内容哦',
        modules: {
          toolbar: false,
          mention: {
            allowedChars: /^[0-9a-zA-Z_\u4e00-\u9fa5\s]*$/,
            mentionDenotationChars: ['@'],
            offsetLeft: 4,
            source: (searchTerm, renderList) => {

              const values = this.mentionList.filter((v) => v.value.startsWith(searchTerm))

              renderList(values, searchTerm) // 渲染函数(生成提醒框)
            },
            onSelect: (item, insertItem) => {
              this.disTips = true
              insertItem(item)
              this.disTips = false
            }
          },
          keyboard: {
            bindings: {
              shift_enter: {
                key: 13,
                shiftKey: true,
                handler: (range, ctx) => {
                  this.editor.insertText(range.index, '\n')
                }
              },
              enter: {
                key: 13,
                handler: () => {
                  this.$emit('enter')
                }
              }
            }
          }
        }
      },
      value: '',
      editor: null,
      hasContent: false,
      count: 0
    }
  },
  watch: {
    placeholder: {
      handler(val) {
        if (this.editor) this.editor.root.dataset.placeholder = val
      },
      immediate: true
    }
  },
  methods: {
    onHandleReady(quill) {
      this.editor = quill
      this.editor.root.dataset.placeholder = this.placeholder
      // 处理粘贴纯文本
      this.editor.clipboard.addMatcher(Node.ELEMENT_NODE, (node, delta) => {
        const ops = []
        delta.ops.forEach((op) => {
          if (op.insert && typeof op.insert === 'string') {
            ops.push({
              insert: op.insert
            })
          }
        })
        delta.ops = ops
        return delta
      })
    },
    onHandleInput() {
      if (this.wordLimit) {
        const len = this.editor.getLength() - 1
        this.count = len
        if (len > this.wordLimit) {
          this.editor.deleteText(this.wordLimit, 1)
        }
      }
    },
    clearEditor() {
      this.editor.setSelection(0)
      this.editor.setText('')
    },
    getText() {
      return this.editor.container.querySelector('.ql-editor').innerText.trim()
    },
    getInputText() {
      return this.editor.getText().trim()
    },
    handleEditorFocus() {
      this.$emit('focus')
    },
    handleEditorBlur() {
      this.$emit('blur')
    },
    handleEditorChange() {
      // 由于输入的带意图,所以提示语只能和用户输入的内容相关,排除意图
      this.$emit('change', { value: this.getText(), disTips: this.disTips || !this.getInputText() })
    },
    insertData(data) {
      this.disTips = true
      // 先清空数据
      this.clearEditor()
      // 再插入数据
      this.editor.insertText(
        0,
        data
      )
      // 解除限制
      this.disTips = false
    }
  }
}
</script>
<style lang="scss" scoped>
.mention-editor-wrap {
  height: 100%;
  ::v-deep {
    .ql-mention-list-container {
      width: 300px;
      border: 1px solid #e4e7ed;
      border-radius: 4px;
      background-color: #ffffff;
      box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
      z-index: 9001;
      overflow: auto;
      padding: 6px 0;
    }

    .quill-editor {
      height: 114px;
    }

    .ql-editor{
      padding: 16px 20px;
      span {
        background: white !important;
        color: #606266 !important;
        font-size: 14px !important;
      }

      span.mention {
        height: 24px;
        width: 65px;
        border-radius: 6px;
        user-select: all;
      }

      .mention > span {
        margin: 0 3px;
      }
      &::before {
        font-style: normal;
        font-size: 14px;
        left: 20px;
        color: #8C8C8C;
      }
    }

    .ql-toolbar {
      display: none;
    }

    .ql-container {
      height: 114px;
      border: none;
    }

    .ql-mention-loading {
      line-height: 44px;
      padding: 0 20px;
      vertical-align: middle;
      font-size: 16px;
    }

    .ql-mention-list {
      list-style: none;
      margin: 0;
      padding: 0;
      overflow: hidden;
    }

    .ql-mention-list-item {
      font-size: 14px;
      padding: 0 20px;
      line-height: 44px;
      cursor: pointer;
      color: #606266;
      display: flex;
      justify-content: space-between;
      align-items: center;
    }

    .ql-mention-list-item.disabled {
      cursor: auto;
    }

    .ql-mention-list-item.selected, ql-mention-list-item:hover {
      background-color:rgba(78, 114, 245, 0.08);
      text-decoration: none;
    }

  }
}
</style>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值