vue3 实现一个简单的输入框

需求背景

需要一个输入框,可以输入文字,添加表情,一开始用了富文本编辑器,有点大材小用,所以自己封装一个输入框组件。支持输入文字,选择表情/插入表情,支持组合键换行,使用enter
进行提交

效果图

在这里插入图片描述

技术实现

  1. 通过原生textarea实现
  2. 通过 v-model 来实现 父子组件的数据传递,子组件监听数据的变化,向外emit('update:modelValue', inputValue),保证父组件能更新绑定的值
  3. 每次插入时,需要重新聚焦更新光标位置
  4. 通过向外 暴露的 (__insertText),来实现插入表情/文字
  5. 通过向外暴露的(_clear) 输入完毕发送后,需要clear 掉输入框的内容
  6. 通过向外暴露的(__isEmpty) 来判断是否有内容,如果没内容,做按钮的禁用状态(当然也可以直接用父组件绑定的值)
  7. 父组件通过ref 就可以调用以上方法,来做操作。比如发送完数据,调clear清空内容,如果输入框没有内容,则调用isEmpty,做按钮的一些状态

代码实现(子组件)

/*** 自定义文本输入框组件*/import { ref, watch } from 'vue'
import { ElInput } from 'element-plus'export default defineComponent({props: {modelValue: {type: String,default: ''}},emits: ['update:modelValue', 'chatSend'],setup(props, { emit, expose }) {const { t } = useI18n()const editorRef = ref()const disabled = ref(false)const valueHtml = ref(props.modelValue)const currentEvent = ref()watch(() => valueHtml.value,() => {emit('update:modelValue', valueHtml.value)})// 插入元素const _insertText = async (msg: string) => {const msgLength = msg.lengthconst editor = currentEvent.valueif (editor) {const startPos = editor.selectionStartvalueHtml.value = `${valueHtml.value.substring(0, startPos)}${'' + msg}${valueHtml.value.substring(startPos)}`_focus()nextTick(() => {// 此处是 根据元素的长度,来设置光标位置editor.setSelectionRange(startPos + msgLength, startPos + msgLength)})}}// focusconst _focus = () => {editorRef.value && editorRef.value.focus()}// 清空编辑器const _clear = () => {editorRef.value && editorRef.value.clear()}// 是否内容为空const _isEmpty = () => {const str = valueHtml.value.trim().replace(/\n/g, '')return str === '' || str.length == 0 || typeof str === 'undefined'}// 禁用编辑器const _disable = () => {disabled.value = true}// 解除禁用编辑器const _enable = () => {disabled.value = false}const handleKeyDown = (event: KeyboardEvent) => {currentEvent.value = event.target as HTMLInputElement// 定义组合键 Mapconst shortCutKeys: (keyof KeyboardEvent)[] = ['metaKey', 'altKey', 'ctrlKey', 'shiftKey']const isEnterKey = event.code === 'Enter'const isShortcutKeys = shortCutKeys.some((item) => event[item])if (isEnterKey && isShortcutKeys) {// 获取光标位置const cursorPosition = currentEvent.value.selectionStart// 拆分成两段文本const textBeforeCursor = valueHtml.value.slice(0, cursorPosition)const textAfterCursor = valueHtml.value.slice(cursorPosition)// 合并为带有换行符的新文本const newText = textBeforeCursor + '\n' + textAfterCursor// 更新输入框的值valueHtml.value = newText// 文本编辑器的高度发生变化后nextTick(() => {// 高度变化 自动滚动到底部const editor = editorRef.value.textareaeditorRef.value.textarea.scrollTop = editor.scrollHeight// 设置光标位置为: start 和 end 相同,光标会移动到换行符后面的新行首currentEvent.value.setSelectionRange(cursorPosition + 1, cursorPosition + 1)})} else if (event.code === 'Enter') {// 阻止掉 Enter 的默认换行行为event.preventDefault()emit('chatSend')}}// 向外暴露方法expose({_insertText,_clear,_disable,_isEmpty,_enable,_focus})return () => (<ElInputref={editorRef}v-model={valueHtml.value}type="textarea"disabled={disabled.value}onKeydown={handleKeyDown}/>)}
})

代码实现(父组件调用)

  • 输入框组件

<base-editorref="chatEditorRef"v-model="inputText"@chat-send="sendText"></base-editor>
  • 工具栏组件

<ChatTools :chat-tools-list="newChatToolsList" @get-emoji="getToolsMsg" />

当我们点击工具栏组件,就会获取到工具栏的文字/表情/或者插入的xxx
,此时根据引用,调用输入框的暴露出的_insertText方法,直接就插入进去

const getToolsMsg = async (msg: string) => {chatEditorRef.value._insertText(msg)
}

其他方法调用

  • _isEmpty(): 未输入,按钮是禁用状态
 <el-button :disabled="chatEditorRef?._isEmpty()" @click="sendText">

当然你也可以直接使用绑定的输入框的变量去判断

<el-button :disabled="!inputText" @click="sendText">
  • _clear(): 提交完请求,清空输入框
chatEditorRef.value._clear()

关于插入的问题

光标位置的获取,根据元素插入位置,光标换行,支持快捷键等。可以参考上一篇文章
https://blog.csdn.net/qq_38845858/article/details/135769222?spm=1001.2014.3001.5502

总结

  1. 原生其实也可以实现,没必要用一个很重的富文本编辑器
  2. 做表情插入,或者其他插入,都是工具栏,和输入框组件的关系是兄弟组件的关系,兄弟组件之间,怎么做数据传递,事件传递,怎么设计

(1) 父组件作为中介

(2)事件总线通过订阅/发布做消息通知

(3)仓库vuex/pina

事件总线还是能不用就不用,因为全局性的东西,用着爽,后面复杂了,就乱了。而且 事件总线是发布订阅,你还得注意销毁,存仓库 又不太合适,子组件自己暴露出方法,让其他组件调用,感觉目前是最简单的方式了

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

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

相关文章

模块化学习

CommonJS 全局变量污染依赖混乱 模块化的标准&#xff1a; CommonJS CMJ 社区标准 node环境ES Module ESM 官方标准 CMJ规范&#xff1a; 所有的 js 文件都是一个模块 运行的模块 入口模块所有的模块中的全局变量、函数&#xff0c;均不会产生污染当一个模块需要提供一些东西给…

Python Web详细教程

Python Web开发的详细教程可以分为以下几个步骤&#xff1a; 1. 环境搭建 安装Python&#xff1a;确保你的系统上安装了Python。你可以从Python官网下载并安装最新版本的Python。 安装虚拟环境&#xff1a;为了隔离项目依赖&#xff0c;建议使用虚拟环境。你可以使用venv模块创…

DPU:未来计算的三大支柱之一

随着计算领域的不断发展&#xff0c;数据处理单元&#xff08;DPU&#xff09;的出现正在重塑数据中心和计算架构的未来。在本文中&#xff0c;我们深入探讨了DPU在该领域的重要性和作用&#xff0c;并将其与传统的中央处理器&#xff08;CPU&#xff09;和图形处理单元&#x…

2024年小程序云开发CMS内容管理无法使用,无法同步内容模型到云开发数据库的解决方案,回退老版本CMS内容管理的最新方法

一&#xff0c;问题描述 最近越来越多的同学找石头哥&#xff0c;说cms用不了&#xff0c;其实是小程序官方最近又搞大动作了&#xff0c;偷偷的升级的云开发cms&#xff08;内容管理&#xff09;以下都称cms&#xff0c;不升级不要紧&#xff0c;这一升级&#xff0c;就导致我…

亚马逊云科技实时 AI 编程助手 Amazon CodeWhisperer,开发快人一步

​ 亚马逊云科技&#xff1a;https://mic.anruicloud.com/url/1024 ​ Amazon CodeWhisperer 是一款 AI 编码配套应用程序&#xff0c;可在 IDE 中生成 整行代码和完整的函数代码建议&#xff0c;以帮助您更快地完成更多工作。在本系列 文章中&#xff0c;我们将为您详细介绍 A…

学习阶段单片机买esp32还是stm32?

学习阶段单片机买esp32还是stm32? 在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「stm32的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“888”之后私信回复“888”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xf…

5G时代对于工业化场景应用有什么改善

5G 不仅仅是 4G 的技术升级&#xff0c;而是将平板电脑和智能手机的技术升级。除了更好的高清视频流和其他高带宽应用&#xff0c;消费者不会注意到很多性能差异。然而&#xff0c;在工业领域&#xff0c;5G 代表着巨大的飞跃。 在工厂和厂房内&#xff0c; 设备的Wi-Fi 网络经…

el-table实现转置表格

vue版本&#xff1a;vue2.6.10 elementui版本&#xff1a;2.15.14 实现效果&#xff1a;el-table实现行列互换 代码&#xff1a; <template><div class"app-container"><span>原始数据</span><el-table:data"datas"border>…

JAVA的方法

学习过C语言&#xff0c;我们可以知道&#xff0c;C语言是由各种各样的函数构成的&#xff0c;程序的入口是主函数&#xff0c;程序结束也是从主函数的最后一行代码结束的&#xff08;默认主函数中间没有return&#xff09;。JAVA是在C上编写的&#xff0c;而C是在C语言之上编写…

Groovy(第四节) Groovy 之循环

目录 循环 深入方法 Groovy 中的范围 设置范围 默认参数值

orm工具saveOrUpdate()操作设计思路

背景 当我们使用数据库向表中插入数据的时候&#xff0c;经常遇到这样的情况&#xff1a; 判断数据是否存在&#xff1b;如果不存在&#xff0c;则插入&#xff1b;如果存在&#xff0c;则更新。 如何设计saveOrUpdate&#xff08;&#xff09; 当我们手写一个orm工具库的时…

自动化信息抽取:提升物资仓库管理效率的实践案例

一、引言 在当今快节奏的供应链管理中&#xff0c;物资仓库的信息抽取和处理是确保物流效率的关键环节。我曾参与的一个项目&#xff0c;正是针对这一需求而设计。该项目的核心目标是优化收货与入库流程&#xff0c;通过先进的信息抽取技术&#xff0c;我们能够自动接收并处理来…

MYSQL--JDBC*

一.介绍: 1.JDBC是一种用于执行SQL于语句的JAVA API,JDBC是一种使用JAVA访问数据库的执行规范标准,能够为不同的数据库提供统一的访问!由一组使用JAVA语言编写的接口以及类组成的 2.JDBC核心的类以及相关的接口主要有: DriverManager 注册驱动 Connection 使用…

代码随想录刷题笔记-Day25

1. 分割回文串 131. 分割回文串https://leetcode.cn/problems/palindrome-partitioning/ 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都一样的字符串。 示例 1&#xf…

购房合同的注意事项是什么呢?房子备案需要多久?

房屋登记需要多长时间&#xff1f; 购房合同要注意什么&#xff1f; 2019/02/20 10:02:07 来源&#xff1a;方天下观点&#xff08;6993&#xff09; [摘要] 购买房屋后&#xff0c;需要向房管部门办理房屋登记。 这样可以证明房子是在业主名下的&#xff0c;所以需要了解房屋…

c++学习记录 deque容器—排序

算法&#xff1a; sort(iterator beg,iterator end); //对beg和end区间内元素进行排序 #include<iostream> using namespace std; #include<deque> #include<algorithm>//标准算法头文件void printDeque(const deque<int>& d) {for …

tar压缩分包,解决git上传对文件的大小限制

分包 比如压缩包abc.tar.gz大于github要求的30M, 可以通过分包命令&#xff1a; tar czf - abc.tar.gz | split -b 20m - abc.tar.gz 将abc.tar.gz分成不大于20M的多个包&#xff0c;再提交到github. 合并包 使用时通过命令将包合并回来&#xff1a; cat abc.tar.gz* | tar …

【知识分享】Echarts雷达图组件封装

封装 Echarts 的雷达图&#xff08;radar chart&#xff09;组件也可以让你在 Vue 项目中更方便地使用雷达图。以下是封装 Echarts 雷达图组件的思路和示例代码&#xff1a; &#xff08;1&#xff09;继续沿用之前安装的 Echarts 和 Vue-Echarts 插件。 &#xff08;2&#…

FPGA-时钟管理单元

时钟管理单元(Clock Management Tile, CMT) : 即时钟管理片,是FPGA器件中一个十分重要的时钟资源。能够对内部和外部的时钟去偏斜、去抖动,同时还支持频率合成、分倍频等功能。 举例&#xff0c;下面这个例子数据从FIFO输出时钟频率为125MHZ&#xff0c;这个时钟频率可以通过…

flink重温笔记(八):Flink 高级 API 开发——flink 四大基石之 Window(涉及Time)

Flink学习笔记 前言&#xff1a;今天是学习 flink 的第八天啦&#xff01;学习了 flink 高级 API 开发中四大基石之一&#xff1a; window&#xff08;窗口&#xff09;知识点&#xff0c;这一部分只要是解决数据窗口计算问题&#xff0c;其中时间窗口涉及时间&#xff0c;计数…