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.hqwc.cn/news/637335.html

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

相关文章

HarmonyOS NEXT 使用XComponent + Vsync 实现自定义动画

介绍 XComponent 提供了应用在 native 侧调用 OpenGLES 图形接口的能力&#xff0c;本文主要介绍如何配合 Vsync 事件&#xff0c;完成自定义动画。在这种实现方式下&#xff0c;自定义动画的绘制不在 UI 主线程中完成&#xff0c;即使主线程卡顿&#xff0c;动画效果也不会受…

Android 获取手机整体流量使用情况以及某个应用的流量的统计

源代码下载地址&#xff1a; 链接&#xff1a;https://pan.quark.cn/s/b6ab9000c0bd

java高校办公室行政事务管理系统设计与实现(springboot+mysql源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的闲一品交易平台。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 项目简介&#xff1a; 基于mvc的高校办公室行政…

嵌入式Linux开发实操(十七):Linux Media Infrastructure userspace API

视频和无线电流媒体设备使用的Linux内核到用户空间API,包括摄像机、模拟和数字电视接收卡、AM/FM接收卡、软件定义无线电(SDR)、流捕获和输出设备、编解码器设备和遥控器。典型的媒体设备硬件如下: 媒体基础设施API就是用于控制此类设备的,分五个部分。 第一部分V4L2 API…

【编程Tool】VS code安装与使用配置保姆级教程

目录 1.软件介绍 2.软件下载&#xff1a; 3.安装 3.1. 双击可执行文件 3.2. 同意协议 3.3. 选择安装路径&#xff0c;默认在C盘 3.4. 点击下一步 3.5. 可选择所有附加任务 3.6. 点击安装 3.7. 等待安装 3.8. 点击完成 3.9. 安装成功 4.下载MinGW64 4.1. MinGW-64下载地址 &…

labview中循环停止事件的深入研究

1.错误用法 第一次值事件运行的时候空白按钮给的F值&#xff0c;第二次值事件运行的时候空白按钮给的T值&#xff0c;这时循环才真正结束。 2.正确用法之一 赋值和值改变事件从同时进行变成按顺序执行。 3.正确用法之二 值事件发生以后超时事件将T值赋值给结束条件&#xff…

堆的概念、堆的向下调整算法、堆的向上调整算法、堆的基本功能实现

目录 堆的介绍 堆的概念 堆的性质 堆的结构 堆的向下调整算法 基本思想&#xff08;以建小堆为例&#xff09; 代码 堆的向上调整算法 基本思想&#xff08;以建小堆为例&#xff09; 代码 堆功能的实现 堆的初始化 HeapInit 销毁堆 HeapDestroy 打印堆 HeapPrint …

【ZYNQ】Zynq 芯片介绍

Zynq 是 Xilinx 公司提出的全可编程 SoC 架构&#xff0c;集成了单核或多核 ARM 处理器与 Xilinx 16nm 或 28nm 可编程逻辑&#xff0c;包括 Zynq 7000 Soc&#xff0c;Zynq UltraScale MPSoC 和 Zync UltraScale RFSoC 等系列。本文主要介绍 Xilinx Zynq 7000 系列芯片架构、功…

[阅读笔记20][BTX]Branch-Train-MiX: Mixing Expert LLMs into a Mixture-of-Experts LLM

这篇论文是meta在24年3月发表的&#xff0c;它提出的BTX结构融合了BTM和MoE的优点&#xff0c;既能保证各专家模型训练时的高度并行&#xff0c;又是一个统一的单个模型&#xff0c;可以进一步微调。 这篇论文研究了以高效方法训练LLM使其获得各领域专家的能力&#xff0c;例如…

欧科云链:香港虚拟资产OTC合规在即,技术监管成市场规范关键

4月12日香港OTC发牌制度公众咨询结束后&#xff0c;欧科云链研究院在星岛日报发表专栏文章&#xff0c;分享对香港OTC市场的调研情况&#xff0c;并提出“技术监管是香港OTC及Web3生态走向规范的关键”。欧科云链研究院认为&#xff0c;随着OTC监管及虚拟资产现货ETF等事件向前…

【Yolov系列】Yolov5学习(一)补充1.1:自适应锚框计算

1、Yolov5的网络结构 Yolov5中使用的Coco数据集输入图片的尺寸为640*640&#xff0c;但是训练过程的输入尺寸并不唯一&#xff0c;Yolov5可以采用Mosaic增强技术把4张图片的部分组成了一张尺寸一定的输入图片。如果需要使用预训练权重&#xff0c;最好将输入图片尺寸调整到与作…

【剪映专业版】13快速为视频配好音:清晰、无噪声、对齐

视频课程&#xff1a;B站有知公开课【剪映电脑版教程】 使用场景&#xff1a;视频无声音或者视频有声音但是需要更改声音 时间指示器在哪里&#xff0c;就从哪里开始 红色按钮&#xff1a;开始录音 声音波纹&#xff1a;蓝色最佳&#xff0c;黄色或红色声音太大&#xff0c;…