使用插件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>