vue-textarea光标位置插入指定元素

vue-textarea光标位置插入指定元素

需求

点击插入关键字的时候把内容插入到光标所在的位置

效果图

img

实现

html

      <div class="temlate-container"><div class="template-content"><el-inputref="modelContent"v-model="mould.modelContent"autocomplete="off":rows="4"placeholder="请输入关键字"type="textarea"style="width:100%":readonly="readonly"@input="listenKeyword"/><div v-show="!readonly" class="keyword-content"><span class="keyword-title">插入关键字</span><div class="keyword-select"><div class="mould-key"><list-code-selectorv-model="mouldKeyName"type="mouldKey":readonly="readonly"@change="handleKeyword"/><el-button round :loading="loading" type="primary" @click="select">插入关键字</el-button></div></div></div></div><div class="example">示例:{{ mould.example }}</div></div>

js方法

  select() {if (isBlank(this.mouldKeyName)) return this.$message.warning('请选择关键字')const dom = this.$refs.modelContent.$refs.textareaconsole.log('this.mould.modelContent', this.mould.modelContent)let pos = 0if (dom.selectionStart || dom.selectionStart === 0) {// dom.selectionStart第一个被选中的字符的序号(index),从0开始pos = dom.selectionStartconsole.log(' dom.selectionStart', dom.selectionStart)} else if (document.selection) {// IE Supportconst selectRange = document.selection.createRange()selectRange.moveStart('character', -dom.value.length)pos = selectRange.text.length}console.log('pos', pos)const before = this.mould.modelContent.substr(0, pos)const after = this.mould.modelContent.substr(pos)console.log(pos, 'before', before, 'after===', after)this.mould.modelContent = before + '{' + this.mouldKeyName + '}' + aftersetTimeout(() => {if (dom.setSelectionRange) {// 当前元素内的文本设置备选中范围dom.setSelectionRange(pos, pos + this.mouldKeyName.length + 2)console.log('当前元素内的文本设置备选中范围====', pos, pos + this.mouldKeyName.length + 2)} else if (dom.createTextRange) {console.log('聚焦控件后把光标放到最后', dom.createTextRange)// IE Supportconst range = dom.createTextRange()range.collapse(true)range.moveEnd('character', pos)range.moveStart('character', pos)range.select()}}, 0)this.listenKeyword()//监听输入或选中内容是都括号对应及把选中的name改成example放入对应的示例下},
// 监听模块内容输入框listenKeyword() {const modelContent = this.mould.modelContentconst getLocalexample = JSON.parse(localStorage.getItem('example'))const haveVal = /\{(.+?)\}/g // {123}|{} 花括号,大括号const result = modelContent.match(haveVal)if (modelContent.length === 0) {this.mould.example = ''}this.mould.example = modelContentif (isNotBlank(result) && this.isBracketBalance(modelContent)) {this.isSymmetry = falsethis.mould.example = modelContentresult.forEach(code => {getLocalexample.forEach(key => {if (key.example) {if (code === key.code) {this.mould.example = this.mould.example.replace(key.code, key.example)// this.mould.example = this.mould.example.replace(key.code, '{' + key.example + '}')}} else {this.mould.example = this.mould.example.replace(key.code, '{' + this.mouldKeyName + '}')}})})} else if (!this.isBracketBalance(modelContent)) {this.isSymmetry = truethis.$message.warning('缺少完整括号字符')}},// 判断括号是否对称isBracketBalance(str) {var leftBracketNum = 0var strLength = str.lengthfor (var i = 0; i < strLength; i++) {var temp = str.charAt(i)if (temp === '{') {leftBracketNum++}if (temp === '}') {leftBracketNum--}}// 最后判断leftBracketNum,如果为0表示平衡否则不平衡if (leftBracketNum === 0) {return true} else {return false}},

来源:

  • https://blog.csdn.net/qq_40190624/article/details/109217790
  • https://blog.csdn.net/xuhang139/article/details/123050596

el-input textarea插入文字标签

  1. 字符串模板以“[“开始 以“]”结束 是一个元素

  2. 按下backspace退格键或者delete删除键删除字符时,如果删的是字符串模板,会删除整个字符串模板元素

  3. 选择文字元素后,会选择整个的字符串模板

  4. 字符串模板得到焦点后会将焦点移动到字符串模板后

img

<template><div><el-form label-width="80px"><el-form-item label="内容"><el-inputref="smsInput"type="textarea"v-model="smsContent"placeholder="请输入内容":rows="4"@input.native="smsInput"@blur="inputBlur"@focus="focusHandler"@click.native="focusHandler"@keydown.up.down.left.right.native="focusHandler"@select.native="selectHandler"/></el-form-item></el-form><el-popover style="margin-left: 10px; float: right;" placement="right-start" width="200" v-model="visible" trigger="manual"><div class="insert-list"><p class="i-title">元素列表</p><div v-for="(item,index) in btns" :key="index">{{ item }}<el-button @click="insertStr('['+item+']')" size="mini" type="primary">插入</el-button></div></div><el-button slot="reference" size="small" @click="visible = !visible" type="primary">插入元素</el-button></el-popover></div>
</template><script>
export default {data() {return {smsContent: "",inputFocus: null,visible: false,btns: ['姓名', '费用', '日期', '电话号码', '发件人']};},methods: {// 插入元素insertStr(str) {let before = this.smsContent.slice(0, this.inputFocus);let after = this.smsContent.slice(this.inputFocus,this.smsContent.length);this.inputFocus = this.inputFocus + str.length;this.smsContent = before + str + after;this.$emit("smsText", this.smsContent);},// 保存光标位置inputBlur(e) {this.inputFocus = e.target.selectionStart;this.visible = false;},// 删除元素剩余部分smsInput(e) {//deleteContentBackward==退格键 deleteContentForward==del键if (e.inputType === "deleteContentBackward" || e.inputType === "deleteContentForward") {let beforeIndex = 0;let afterIndex = 0;// 光标位置往前for (let i = e.target.selectionStart - 1; i >= 0; i--) {if (this.smsContent[i] === "[") {beforeIndex = i;afterIndex = e.target.selectionStart;break;}if (this.smsContent[i] === "]") {break;}}// 光标位置往后for (let i = e.target.selectionStart; i < this.smsContent.length; i++) {if (this.smsContent[i] === "]") {afterIndex = i + 1;beforeIndex = e.target.selectionStart;break;}if (this.smsContent[i] === "[") {break;}}if (beforeIndex === 0 && afterIndex === 0) {return}let beforeStr = this.smsContent.slice(0, beforeIndex)let afterStr = this.smsContent.slice(afterIndex)this.smsContent = beforeStr + afterStrthis.inputFocus = beforeStr.lengththis.$nextTick(() => {this.changeFocus(e.target, this.inputFocus, this.inputFocus);});}this.$emit("smsText", this.smsContent);},// 选择元素剩余部分selectHandler(e) {// 光标开始位置往前for (let i = e.target.selectionStart - 1; i >= 0; i--) {if (this.smsContent[i] === "[") {this.changeFocus(e.target, i, e.target.selectionEnd);break;}if (this.smsContent[i] === "]") {break;}}// 光标结束位置往后for (let i = e.target.selectionEnd; i < this.smsContent.length; i++) {if (this.smsContent[i] === "]") {this.changeFocus(e.target, e.target.selectionStart, i + 1);break;}if (this.smsContent[i] === "[") {break;}}},// 焦点跳出元素内focusHandler(e) {setTimeout(() => {let selStart = e.target.selectionStartlet beforeArrLength = this.smsContent.slice(0, selStart).split("[").length;let afterArrLength = this.smsContent.slice(0, selStart).split("]").length;//根据'['和']'生成两个数组 判断数组长度 是否相等 不相等就不成对就移动光标if (beforeArrLength !== afterArrLength) {let pos = this.smsContent.indexOf("]", selStart) + 1if (beforeArrLength > afterArrLength && e.code === 'ArrowLeft') {//按下按键左箭头pos = this.smsContent.lastIndexOf("[", selStart)}this.changeFocus(e.target, pos, pos);}}, 100);},// 修改光标位置changeFocus(target, start, end) {let range, el = target;if (el.setSelectionRange) {el.setSelectionRange(start, end);} else {range = el.createTextRange();range.collapse(false);range.select();}},},
};
</script><style scoped>
.insert-list p {text-align: center;
}.insert-list div {margin: 10px 0;display: flex;justify-content: space-between;
}
</style>

来源:https://blog.csdn.net/xuhang139/article/details/123050596

自定义实现方案

效果

img

代码

customTemplateVariable.vue

<template><div><el-form ref="dataForm" :rules="rules" :model="dataForm" label-width="80px"><el-form-item label="模板内容:" prop="templateContext"><div ref="promptContentDiv" v-model="dataForm.templateContext" :contenteditable="true" v-html="contentHtml" style="padding: 6px;border:1px solid #c0c4cc;width:100%; height: 100px;display: inline-block;"/><!--添加变量下拉菜单--><el-dropdown @command="addVariable"><span class="el-dropdown-link"><div class="el-button el-button--text" style="cursor: default;"><i style="font-size: 16px; color: #8c8a8c;" class="el-icon-circle-plus-outline cursor_pointer">添加变量</i></div></span><el-dropdown-menu class="dropdown-max" slot="dropdown"><el-dropdown-itemv-for="(variable, index) in variableNames":key="index+'var'":command="variable.dictLabel"><button type="button" class="el-button el-button--text">{{ variable.dictLabel }}</button></el-dropdown-item></el-dropdown-menu></el-dropdown><el-button :loading="submitLoading" type="primary" @click="templateSubmit">提交</el-button></el-form-item></el-form></div>
</template><script>
import {parsePromptVariable, parsePromptTags} from '@/api/graph_extend'export default {/** 自定义模板变量组件 */name: 'customTemplateVariable',components: {},data() {return {// 数据表单dataForm: {},// 提交按钮禁用状态submitLoading: false,// Html模板内容contentHtml: '',// 变量名称列表variableNames: [],// 表单验证规则rules: {templateContext: [{required: true, message: '模板内容不能为空', trigger: 'blur'}]}}},created() {// 初始选择项this.initOptions()},/** 渲染完成后执行 */mounted() {// 添加自定义文本域监听事件this.addCustomTextareaListener();},methods: {initOptions() {// 模拟添加变量数据this.variableNames.unshift({dictLabel: '名称', dictValue: 'name'})this.variableNames.unshift({dictLabel: '年龄', dictValue: 'age'})this.variableNames.unshift({dictLabel: '性别', dictValue: 'gender'})},/*** 添加变量* @param command 命令*/addVariable: function(command) {let sel = window.getSelection()let node_prompt_variable_prefix = '<span contenteditable="false" disabled="disabled" class="el-tag el-tag--info el-tag--small">'let node_prompt_variable_suffix = '</span>'// 只在当前输入框中插入console.log("addVariable", command);console.log("addVariable1", sel.focusNode);console.log("addVariable2", sel.focusNode ? sel.focusNode.parentNode : null);console.log("addVariable3", this.$refs.promptContentDiv);if (!sel.focusNode || (sel.focusNode.parentNode !== this.$refs.promptContentDiv && sel.focusNode !== this.$refs.promptContentDiv)) {return}if (sel.getRangeAt && sel.rangeCount) {let range = sel.getRangeAt(0)range.deleteContents()let el = document.createElement('div')el.innerHTML = node_prompt_variable_prefix + command + node_prompt_variable_suffixlet node, lastNode, frag = document.createDocumentFragment()while ((node = el.firstChild)) {lastNode = frag.appendChild(node)}range.insertNode(frag)if (lastNode) {range = range.cloneRange()range.setStartAfter(lastNode)range.collapse(true)sel.removeAllRanges()sel.addRange(range)}}},/*** 添加自定义文本域监听事件*/addCustomTextareaListener() {let self = thislet el = this.$refs.promptContentDivif (this.dataForm.templateContext) {// 模板内容替换为显示变量let tempContext = this.replaceShowVariables(this.dataForm.templateContext);this.$set(this.dataForm, 'templateContext', tempContext);// 加载后,将${}变量变为html标签this.contentHtml = parsePromptVariable(tempContext)el.innerHTML = this.contentHtml}// 添加监听事件,动态获取编辑框内容el.addEventListener('DOMSubtreeModified', function () {if (el.innerHTML !== null && el.innerHTML !== undefined) {self.dataForm.templateContext = parsePromptTags(el.innerHTML)}})},/*** 模板内容替换为数据变量* @param templateContext 模板内容* @return {*} 替换后的模板内容*/replaceDataVariables(templateContext){if (!templateContext) {return "";}for (let i in this.variableNames) {templateContext = templateContext.replaceAll("{" + this.variableNames[i].dictLabel + "}", "{" + this.variableNames[i].dictValue + "}");}return templateContext;},/*** 模板内容替换为显示变量* @param templateContext 模板内容* @return {*} 替换后的模板内容*/replaceShowVariables(templateContext){if (!templateContext) {return "";}for (let i in this.variableNames) {templateContext = templateContext.replaceAll("{" + this.variableNames[i].dictValue + "}", "{" + this.variableNames[i].dictLabel + "}");}return templateContext;},templateSubmit(){this.$refs['dataForm'].validate(valid => {if (valid) {// 提交按钮禁用状态this.submitLoading = true// 模板内容替换为数据变量let tempContext = this.replaceDataVariables(this.dataForm.templateContext);console.log("templateContext", this.dataForm.templateContext);console.log("tempContext", tempContext);// 提交按钮非禁用状态this.submitLoading = false}})}}
}
</script>

graph_extend.js

// 播报语变量样式前缀,后缀
const node_prompt_variable_prefix = '<span contenteditable="false" disabled="disabled" class="el-tag el-tag--info el-tag--small">';
const node_prompt_variable_suffix = '</span>';// 解析播报语变量,将 {} 标识的变量用 <span></span> 标签包裹
export function parsePromptVariable(str) {let reg = /\{(.*?)}/g;return str.replace(reg, function (result, group) {return node_prompt_variable_prefix + group + node_prompt_variable_suffix;});
}// 获取播报语中变量,将 <span></span> 标签标识的变量用 {} 标识
export function parsePromptTags(str) {let reg = new RegExp(node_prompt_variable_prefix + '(.*?)' + node_prompt_variable_suffix, 'g');return str.replace(reg, function (result, group) {return "{" + group + "}";});
}

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

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

相关文章

嵌入式学习56-ARM5(linux驱动启动程序)

知识零碎&#xff1a; bootm&#xff1a; 启动内核同时给内核传参 …

怎么给一个字典进行按值或key来排序?

字典是具有指定数字或键的特定数据集或组。在 Python 以外的编程语言中&#xff0c;它们也被称为哈希映射或关联数组。 一般来说&#xff0c;它是键值对的形式&#xff0c;就像现实世界的字典一样。 要创建字典&#xff0c;请从左括号开始&#xff0c;添加键并键入一个冒号。…

Leetcode86_分隔链表

1.leetcode原题链接&#xff1a;. - 力扣&#xff08;LeetCode&#xff09; 2.题目描述 给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的…

回文链表leecode

回文链表 偶数情况奇数情况 回文链表leecode 偶数情况 public boolean isPalindrome(ListNode head) {if (head null) {return true;}ListNode fast head;ListNode slow head;while (fast ! null && fast.next ! null) {fast fast.next.next;slow slow.next;}//反…

【spring】@Resource注解学习

Resource介绍 在Spring框架中&#xff0c;Resource 注解是一个JSR-250标准注解&#xff0c;用于自动装配&#xff08;autowiring&#xff09;Spring容器中的bean。Resource 注解可以用于字段、方法和方法参数上&#xff0c;以声明依赖注入。 Resource源码 Target({TYPE, FIE…

Python相关性分析

分析连续变量之间线性相关程度的强弱&#xff0c;并用适当的统计指标表示出来的过程称为相关分析。 可以直接绘制散点图&#xff0c;或者绘制散点图矩阵&#xff0c;或者计算相关系数来进行相关分析。 相关系数的计算如下所示&#xff1a; 示例数据&#xff1a; 计算百合酱蒸…

HarmonyOS Next 视频弹幕功能

视频弹幕功能 介绍 本示例介绍如何使用ohos.danmakuflamemaster和ohos.gsyvideoplayer开发支持视频弹幕的播放器。可以自定义弹幕样式、占据屏幕宽度&#xff0c;发送弹幕&#xff0c;开关弹幕视图。 效果图预览 使用说明 点击播放按钮&#xff0c;进行视频播放&#xff0c…

【C语言】qsort()函数排序及其模拟实现,万物皆可排!

&#x1f525;博客主页&#x1f525;&#xff1a;【 坊钰_CSDN博客 】 欢迎各位点赞&#x1f44d;评论✍收藏⭐ 目录 1. 函数介绍 2. qsort举例排列整型变量 3. qsort举例排列结构型变量 3.1 按名字排序 3.1.1 srtcmp函数 3.2 按年龄排序 4. qsort函数模拟实现(采用冒泡的…

[Python]可视化地展示出表格中某一列同一数据的出现次数

前言 Matplotlib画图工具的官网地址是 http://matplotlib.org/ Python环境下实现Matlab制图功能的第三方库&#xff0c;需要numpy库的支持&#xff0c;支持用户方便设计出二维、三维数据的图形显示&#xff0c;制作的图形达到出版级的标准。 pandas 是 Python 编程语言中一个…

PHP 使用 PHPMailer 发送电子邮件

1. PHPMailer 介绍 phpMailer 是一个非常强大的 php 发送邮件扩展包&#xff0c;可以设定发送邮件地址、回复地址、邮件主题、html邮件内容和上传附件等&#xff0c;使用起来非常方便。它目前有着有近 4 千万的下载量&#xff0c;是 PHP 开发者实现邮件发送功能的首选扩展包 它…

12.基础乐理-半音、全音

音是有高有底的&#xff0c;音的震动频率越高、音的赫兹越高&#xff0c;我们就说这个音越高&#xff0c;钢琴从左到右&#xff0c;音是逐渐变高的&#xff0c;因变高&#xff0c;它的频率&#xff0c;Hz数是在增加的&#xff0c;如下图&#xff1a; 但是赫兹它动不动就是几百几…

NPL预训练模型-GPT-3

简介及特点 GPT-3是一个由OpenAI开发的自然语言处理&#xff08;NLP&#xff09;预训练模型&#xff0c;它是生成式预训练变换器&#xff08;Generative Pretrained Transformer&#xff09;系列的第三代模型。GPT-3以其巨大的规模和强大的语言处理能力而闻名&#xff0c;具有…

爬虫 | 基于 Python 实现有道翻译工具

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本项目旨在利用 Python 语言实现一个简单的有道翻译工具。有道翻译是一款常用的在线翻译服务&#xff0c;能够实现多种语言的互译&#xff0c;提供高质量的翻译结果。 目录 一、项目功能 二、注意事项 三、代码解析 1. 导入…

DC-9渗透测试复现

DC-9渗透测试复现 目的&#xff1a; 获取最高权限以及flag 过程&#xff1a; 信息打点--sql注入- 文件包含漏洞-Knockd开门开启ssh连接-hyjra爆破-sudo提权(文件追加) 环境&#xff1a; 攻击机&#xff1a;kali(192.168.85.137) 靶机&#xff1a;DC_3(192.168.85.141) …

javaswing + mysql通讯录

javaswingaddressbook 需要代码私聊

结构体及应用;结构体指针及应用;union、enum、typedef三个关键字

结构体及应用 参考文章链接&#xff1a;https://blog.csdn.net/zw1996/article/details/53844585结构体的声明 结构体的初始化 注意如果在定义结构体变量的时候没有初始化&#xff0c;那么后面就不能全部一起初始化了。 /这样是可以的&#xff0c;在定义变量的时候就初始化了…

(十四)C++自制植物大战僵尸游戏windows平台视频播放实现

植物大战僵尸游戏开发教程专栏地址http://t.csdnimg.cn/8UFMs VLC库 在Cocos2d-x游戏开发框架中&#xff0c;没有实现windows平台视频播放的功能&#xff0c;需要自定义实现。在本项目中使用vlc库实现windows平台的视频播放功能。 vlc官网&#xff1a;网址 下载完成后&#x…

虚幻引擎源码版安装下载,点击GenerateProjectFiles.bat报错 error NU1101NuGet包问题解决参考方案

开发环境配置与源码安装使用 安装VS2022 按照官方文档安装需要的vs配置 虚幻引擎源代码下载 Epic里面下载的引擎与源代码引擎区别&#xff1a;Epic里面下载的引擎是已经编译过的它的源代码访问权限不完整&#xff0c;源代码版本提供比较完整引擎代码&#xff0c;并且可以修…

MDK stm32怎么生成bin文件

第一种 D:\Keil_v5\ARM\ac5.6\bin\fromelf.exe --bin -o ../../Output/atk_f407.bin ../../Output/atk_f407.axf 空格解析 D:\Keil_v5\ARM\ac5.6\bin\fromelf.exe一个空格--bin一个空格-o两个空格../../Output/atk_f407.bin ../../Output/atk_f407.axf &#xff08;注意后…

ELK,ELFK日志收集分析系统

ELK简介 ELK是一套完整的日志集中处理解决方案&#xff0c;将ElasticSearch&#xff0c;Logstash和Kibana三个开源工具配合使用&#xff0c;实现用户对日志的查询、排序、统计需求。 ELK工作原理 在所有需要收集日志的服务器上部署Logstash&#xff0c;或者先将日志进行集中…