@wangeditor/plugin-mentionnpm i @wangeditor/plugin-mentionAutoComplete.vue文件< template> < divstyle = " border :  1px solid #ccc;  position :  relative; " > < Editorstyle = " height :  100px" :defaultConfig = " editorConfig" v-model = " valueHtml" @onCreated = " handleCreated" @onChange = " onChange" @keydown.enter.native = " keyDown" /> < mention-modalv-if = " isShowModal" @hideMentionModal = " hideMentionModal" @insertMention = " insertMention" :position = " position" > </ mention-modal> </ div> </ template> < scriptsetup  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> < stylesrc = " @wangeditor/editor/dist/css/style.css" > </ style> < stylescoped > 
.w-e-scroll  { 
  max-height :  100px; 
} 
 </ style> MentionModal.vue文件< template> < divid = " mention-modal" :style = " { top, left, right, bottom }" > < el-inputid = " mention-input" v-model = " searchVal" ref = " input" @keyup = " inputKeyupHandler" onkeypress = " if(event.keyCode === 13) return false" placeholder = " 请输入用户名搜索" /> < el-scrollbarheight = " 200px" > < ulid = " mention-list" > < liv-for = " item in searchedList" :key = " item.id" @click = " insertMentionHandler(item.id, item.username)" > </ li> </ ul> </ el-scrollbar> </ div> </ template> < scriptsetup  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()来确定的,因为我发现,当页面出现滚动时,根据页面获取光标定位不是很准确。 还有,如果你页面组件嵌套多层的话,其中有一个设置了relative就会影响到用户对话框的定位,所以根据富文本编辑器的光标来定位最好。