Requirement description: Display the user list after entering @ in the input box to realize the @ person function
Current environment: vue3+vite+elementPlus+wangEditor@5
Requires plugins: @wangeditor/plugin-mention
Install plugin: npm i @wangeditor/plugin-mention
The input box component is divided into two parts: 1. wangEditor rich text editor part, 2. User list dialog part
1. Rich text editor component code: AutoComplete.vue
file
< template>
< div style = " border : 1px solid #ccc; position : relative; " >
< Editor style = " height : 100px" :defaultConfig = " editorConfig" v-model = " valueHtml" @onCreated = " handleCreated"
@onChange = " onChange" @keydown.enter.native = " keyDown" />
< mention-modal v-if = " isShowModal" @hideMentionModal = " hideMentionModal" @insertMention = " insertMention"
:position = " position" > </ mention-modal>
</ div>
</ template>
< script setup lang = " ts" >
import {
ref, shallowRef, onBeforeUnmount, nextTick, watch } from 'vue'
import {
Boot } from '@wangeditor/editor'
import {
Editor } from '@wangeditor/editor-for-vue'
import mentionModule from '@wangeditor/plugin-mention'
import MentionModal from './MentionModal.vue'
Boot. registerModule ( mentionModule)
const props = withDefaults ( defineProps< {
content? : string
} > ( ) , {
content : ''
} )
const editorRef = shallowRef ( )
const valueHtml = ref ( '' )
const isShowModal = ref ( false )
watch ( ( ) => props. content, ( val : string) => {
nextTick ( ( ) => {
valueHtml. value = val
} )
} )
onBeforeUnmount ( ( ) => {
const editor = editorRef. value
if ( editor == null ) return
editor. destroy ( )
} )
const position = ref ( {
left : '15px' ,
top : '40px'
} )
const handleCreated = ( editor : any) => {
editorRef. value = editor
position. value = editor. getSelectionPosition ( )
}
const showMentionModal = ( ) => {
nextTick ( ( ) => {
const editor = editorRef. value
console. log ( editor. getSelectionPosition ( ) ) ;
position. value = editor. getSelectionPosition ( )
} )
isShowModal. value = true
}
const hideMentionModal = ( ) => {
isShowModal. value = false
}
const editorConfig = {
placeholder : '请输入内容...' ,
EXTEND_CONF : {
mentionConfig : {
showModal : showMentionModal,
hideModal : hideMentionModal,
} ,
} ,
}
const onChange = ( editor : any) => {
console. log ( 'changed html' , editor. getHtml ( ) )
console. log ( 'changed content' , editor. children)
}
const insertMention = ( id : any, username : any) => {
const mentionNode = {
type : 'mention' ,
value : username,
info : {
id } ,
children : [ {
text : '' } ] ,
}
const editor = editorRef. value
if ( editor) {
editor. restoreSelection ( )
editor. deleteBackward ( 'character' )
editor. insertNode ( mentionNode)
editor. move ( 1 )
}
}
const keyDown = ( e : any) => {
const editor = editorRef. value
console. log ( editor. children[ 0 ] . children. filter ( ( item : any) => item. type === 'mention' ) . map ( ( item : any) => item. info. id) , 'key === 发song' )
if ( e != undefined ) {
e. preventDefault ( ) ;
}
}
</ script>
< style src = " @wangeditor/editor/dist/css/style.css" > </ style>
< style scoped >
.w-e-scroll {
max-height : 100px;
}
</ style>
2. User list dialog MentionModal.vue
file
< template>
< div id = " mention-modal" :style = " { top, left, right, bottom }" >
< el-input id = " mention-input" v-model = " searchVal" ref = " input" @keyup = " inputKeyupHandler" onkeypress = " if(event.keyCode === 13) return false" placeholder = " 请输入用户名搜索" />
< el-scrollbar height = " 200px" >
< ul id = " mention-list" >
< li v-for = " item in searchedList" :key = " item.id" @click = " insertMentionHandler(item.id, item.username)" > {
{
item.username }}({
{ item.account }})
</ li>
</ ul>
</ el-scrollbar>
</ div>
</ template>
< script setup lang = " ts" >
import {
ref, computed, onMounted, nextTick } from 'vue'
const props = defineProps< {
position : any
} > ( )
const emit = defineEmits ( [ 'hideMentionModal' , 'insertMention' ] )
const top = computed ( ( ) => {
return props. position. top
} )
const bottom = computed ( ( ) => {
return props. position. bottom
} )
const left = computed ( ( ) => {
return props. position. left
} )
const right = computed ( ( ) => {
if ( props. position. right) {
const right = + ( props. position. right. split ( 'px' ) [ 0 ] ) - 180
return right < 0 ? 0 : ( right + 'px' )
}
return ''
} )
const searchVal = ref ( '' )
const tempList = Array. from ( {
length : 20 } ) . map ( ( _, index ) => {
return {
id : index,
username : '张三' + index,
account : 'wp'
}
} )
const list = ref ( tempList)
const searchedList = computed ( ( ) => {
const searchValue = searchVal. value. trim ( ) . toLowerCase ( )
return list. value. filter ( item => {
const username = item. username. toLowerCase ( )
if ( username. indexOf ( searchValue) >= 0 ) {
return true
}
return false
} )
} )
const inputKeyupHandler = ( event : any) => {
if ( event. key === 'Escape' ) {
emit ( 'hideMentionModal' )
}
if ( event. key === 'Enter' ) {
const firstOne = searchedList. value[ 0 ]
if ( firstOne) {
const {
id, username } = firstOne
insertMentionHandler ( id, username)
}
}
}
const insertMentionHandler = ( id : any, username : any) => {
emit ( 'insertMention' , id, username)
emit ( 'hideMentionModal' )
}
const input = ref ( )
onMounted ( ( ) => {
nextTick ( ( ) => {
input. value?. focus ( )
} )
} )
</ script>
< style>
#mention-modal {
position : absolute;
border : 1px solid #ccc;
background-color : #fff;
padding : 5px;
transition : all .3s;
}
#mention-modal input {
width : 150px;
outline : none;
}
#mention-modal ul {
padding : 0;
margin : 5px 0 0;
}
#mention-modal ul li {
list-style : none;
cursor : pointer;
padding : 5px 2px 5px 10px;
text-align : left;
}
#mention-modal ul li:hover {
background-color : #f1f1f1;
}
</ style>
editor.getSelectionPosition()
Note: The positioning of the dialog box is determined based on the editor because I found that getting the cursor positioning based on the page is not very accurate when scrolling occurs.
Also, if your page components are nested in multiple layers, setting one of them relative
will affect the positioning of the user dialog box, so it is best to position it according to the cursor of the rich text editor.