wangEditor5实现@评论功能

需求描述:在输入框输入@后显示用户列表,实现@人功能

请添加图片描述

当前环境:vue3+vite+elementPlus+wangEditor@5

需要插件:@wangeditor/plugin-mention

安装插件:npm i @wangeditor/plugin-mention

输入框组件分两部分:1. wangEditor富文本编辑器部分,2. 用户列表对话框部分

1. 富文本编辑器组件代码:AutoComplete.vue文件

<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 v-html="valueHtml"></div> --></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: ''
})
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()// const valueHtml = ref('<p>你好<span data-w-e-type="mention" data-w-e-is-void data-w-e-is-inline data-value="A张三" data-info="%7B%22id%22%3A%22a%22%7D">@A张三</span></p>')
const valueHtml = ref('')
const isShowModal = ref(false)watch(() => props.content, (val: string) => {nextTick(() => {valueHtml.value = val})
})// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {const editor = editorRef.valueif (editor == null) returneditor.destroy()
})
const position = ref({left: '15px',top: '40px'
})
const handleCreated = (editor: any) => {editorRef.value = editor // 记录 editor 实例,重要!position.value = editor.getSelectionPosition()
}const showMentionModal = () => {// 对话框的定位是根据富文本框的光标位置来确定的nextTick(() => {const editor = editorRef.valueconsole.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', // 必须是 'mention'value: username,info: { id },children: [{ text: '' }], // 必须有一个空 text 作为 children}const editor = editorRef.valueif (editor) {editor.restoreSelection() // 恢复选区editor.deleteBackward('character') // 删除 '@'editor.insertNode(mentionNode) // 插入 mentioneditor.move(1) // 移动光标}
}
const keyDown = (e: any) => {// 执行一些逻辑方法const editor = editorRef.valueconsole.log(editor.children[0].children.filter((item: any) => item.type === 'mention').map((item: any) => item.info.id), 'key === 发song')// this.sendBut()//发送信息的方法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. 用户列表对话框 MentionModal.vue文件

<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]) - 180return right < 0 ? 0 : (right + 'px')}return ''
})
// list 信息
const searchVal = ref('')
const tempList = Array.from({ length: 20 }).map((_, index) => {return {id: index,username: '张三' + index,account: 'wp'}
})
const list = ref(tempList)
// 根据 <input> value 筛选 list
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) => {// esc - 隐藏 modalif (event.key === 'Escape') {emit('hideMentionModal')}// enter - 插入 mention nodeif (event.key === 'Enter') {// 插入第一个const firstOne = searchedList.value[0]if (firstOne) {const { id, username } = firstOneinsertMentionHandler(id, username)}}
}
const insertMentionHandler = (id: any, username: any) => {emit('insertMention', id, username)emit('hideMentionModal') // 隐藏 modal
}
const input = ref()
onMounted(() => {// 获取光标位置// const domSelection = document.getSelection()// const domRange = domSelection?.getRangeAt(0)// if (domRange == null) return// const rect = domRange.getBoundingClientRect()// 定位 modal// top.value = props.position.top// left.value = props.position.left// focus inputnextTick(() => {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就会影响到用户对话框的定位,所以根据富文本编辑器的光标来定位最好。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/34277.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

谈谈对Spring MVC的理解

问题分析&#xff1a; SpringMVC 是一种基于 Java 语言开发&#xff0c;实现了 Web MVC 设计模式&#xff0c;请求驱动类型 的轻量级 Web 框架。 SpringMVC采用了 MVC 架构模式的思想&#xff0c;通过把 Model&#xff0c;View&#xff0c;Controller 分离&#xff0c;将 Web 层…

基于vue3+webpack5+qiankun实现微前端

一 主应用改造&#xff08;又称基座改造&#xff09; 1 在主应用中安装qiankun(npm i qiankun -S) 2 在src下新建micro-app.js文件&#xff0c;用于存放所有子应用。 const microApps [// 当匹配到activeRule 的时候&#xff0c;请求获取entry资源&#xff0c;渲染到containe…

软件测试基础篇——MySQL

MySQL 1、数据库技术概述 数据库database&#xff1a;存放和管理各种数据的仓库&#xff0c;操作的对象主要是【数据data】&#xff0c;科学的组织和存储数据&#xff0c;高效的获取和处理数据SQL&#xff1a;结构化查询语言&#xff0c;专为**关系型数据库而建立的操作语言&…

JavaScript类

JavaScript 类(class) 类是用于创建对象的模板。 我们使用 class 关键字来创建一个类&#xff0c;类体在一对大括号 {} 中&#xff0c;我们可以在大括号 {} 中定义类成员的位置&#xff0c;如方法或构造函数。 每个类中包含了一个特殊的方法 constructor()&#xff0c;它是类…

vue3项目中引入dialog插件,支持最大最小化、还原、拖拽

效果图&#xff1a; 上图是layui-vue组件库中的layer插件&#xff0c;我的项目使用的是element-plus组件库&#xff0c;在用不上layui组件库的情况下&#xff0c;就单独引入layui/layer-vue这个弹层插件就可以了 npm地址&#xff1a;layui/layer-vue - npm layui-vue组件库地址…

UnrealEngine - 网络同步之连接篇

1 连接过程 - 握手 传统的 C/S 架构下&#xff0c;Client 和 Server 通常会建立一条抽象的 Connection&#xff0c;用来进行两端的通信。 UE 的官方文档中提供了 Client 连接到 Server 的示例 &#xff0c;简单来说分为如下几步&#xff1a; 打包构建好 Client 和 Server 进程…

11-数据结构-栈和队列的应用(C语言)

栈和队列的应用 目录 栈和队列的应用 一、括号匹配&#xff08;栈&#xff09; 二、表达式的各种转换 (1)中缀转后缀(手工) (2)后缀转中缀表达式(手工) (3)中缀转后缀(栈) (4)中缀转后缀&#xff08;树&#xff09; (5)后缀表达式求值 (6)中缀表达式求值&#xff08;栈…

EasyExcel多次写入数据多个EasyExcel文件导出到zip压缩文件

笔者最近需要导出一百多万条数据到Excel&#xff0c;已经超出单张工作表的最大容量&#xff08;2^201048576&#xff09;&#xff0c;需要导出到多个工作表或多个Excel文件。 海量数据导出面临的问题有以下几个&#xff1a; 如果一次性查出所有数据&#xff0c;很可能内存溢出…

jackson库收发json格式数据和ajax发送json格式的数据

一、jackson库收发json格式数据 jackson库是maven仓库中用来实现组织json数据功能的库。 json格式  json格式一个组织数据的字符文本格式&#xff0c;它用键值对的方式存贮数据&#xff0c;json数据都是有一对对键值对组成的&#xff0c;键只能是字符串&#xff0c;用双引号包…

ArcGIS API for JavaScript 4.x 教程(一) 显示一张地图

了解如何创建和显示带有基本地图图层的地图。 地图包含地理数据层。地图包含一个基本地图层&#xff0c;以及一个或多个数据层&#xff08;可选&#xff09;。可以使用地图视图显示地图的特定区域&#xff0c;并设置位置和缩放级别。 本教程将向您展示如何使用地形底图层创建和…

时序预测 | Matlab实现基于GRNN广义回归神经网络的电力负荷预测模型

文章目录 效果一览文章概述源码设计参考资料效果一览 文章概述 时序预测 | Matlab实现基于GRNN广义回归神经网络的电力负荷预测模型 1.Matlab实现基于GRNN广义回归神经网络的电力负荷预测模型 2.单变量时间序列预测; 3.多指标评价,评价指标包括:R2、MAE、MBE等,代码质量极高…

XML学习基础知识归纳(一)

一、XML基本概述 &#xff08;1&#xff09;概念&#xff1a;XML是可扩展的标记语言&#xff0c;xml文档的后缀名为 .xml &#xff08;2&#xff09;作用&#xff1a;用来用来传输和存储数据&#xff0c;不用于表现和展示数据&#xff0c;这点呢相比于HTML来说是不同的&#…

vue2中实现响应式的原理object.defineproperty+发布定于模式的缺点

缺点&#xff1a; 1 不能监测到新增属性或者删除属性 2 不能检测到根据数组索引替换或新增的值。也无法监测数组索引的变化。 由于object.defineproperty&#xff08;对象&#xff0c;描述&#xff0c;对象的某个属性&#xff09;其实是对对象的某个属性进行修改&#xff0c;因…

python函数、运算符等简单介绍3(无顺序)

set&#xff08;集合&#xff09; 集合(set) -> 负责存储【不重复的数据】&#xff0c;并且是【无序存储】 的容器&#xff0c;主要用来去重和逻辑比较 set1 {1,2,3,4,58,7,4,1,2,3,5} print(set1) print(type(set1)) # 输出结果&#xff1a; {1, 2, 3, 4, 5, 7, 58} <…

svn文章四:版本控制策略 - 穿越时光机:SVN版本控制进阶技巧

文章四&#xff1a;版本控制策略 - “穿越时光机&#xff1a;SVN版本控制进阶技巧” 概述&#xff1a;版本控制是SVN的核心功能。本文将深入研究SVN版本控制的进阶技巧&#xff0c;包括标签管理、历史查看、版本回退等&#xff0c;让您成为版本控制的高手。 1. 引言 版本控制…

excel 之 VBA

1、excel和VBA 高效办公&#xff0c;把重复性的工作写成VBA代码&#xff08;VB代码的衍生物&#xff0c;语法和VBA相同&#xff09;。 首先打开开发工具模式&#xff0c;如果没有选显卡&#xff0c;需要手动打开 打开程序编辑界面 快捷键 altF11一般操作 程序调试&#xf…

概率论与数理统计:第二、三章:一维~n维随机变量及其分布

文章目录 Ch2. 一维随机变量及其分布1.一维随机变量1.随机变量2.分布函数 F ( x ) F(x) F(x)(1)定义(2)分布函数的性质 (充要条件)(3)分布函数的应用——求概率3.最大最小值函数 2.一维离散型随机变量及其概率分布(分布律)3.一维连续型随机变量及其概率分布(概率密度)4.一般类型…

【Nginx15】Nginx学习:HTTP核心模块(十二)内嵌变量

Nginx学习&#xff1a;HTTP核心模块&#xff08;十二&#xff09;内嵌变量 关于内嵌变量&#xff0c;其实就是 Nginx 开放给我们的在配置文件中可以使用的变量。源码中无非就是替换成真实的代码变量进行操作。这些变量可以帮助我们做很多事情。之前的文章中其实也有不少地方用到…

深度学习关键要素:数据集汇总与分享

引言 在深度学习的应用中&#xff0c;数据被认为是最重要的因素之一。因此&#xff0c;选择一个好的数据集对于深度学习的成功至关重要。在选择数据集时&#xff0c;不仅需要关注数据量的大小、多样性以及质量&#xff0c;还要考虑数据集是否代表了所研究问题的真实情况。本文…

2023国赛数学建模B题思路分析

文章目录 0 赛题思路1 竞赛信息2 竞赛时间3 建模常见问题类型3.1 分类问题3.2 优化问题3.3 预测问题3.4 评价问题 4 建模资料 0 赛题思路 &#xff08;赛题出来以后第一时间在CSDN分享&#xff09; https://blog.csdn.net/dc_sinor?typeblog 1 竞赛信息 全国大学生数学建模…