实现一个可以实时提示的textarea组件
该组件输入、换行、变换光标可以实时给出提示
效果:
textarea.vue
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<template> <div> <el-input id="user-input" type="textarea" placeholder="请换行输入不同的通知用户" :autosize="{minRows: 2, maxRows: 10}" v-model="inputValue" @blur="closeHint" @input="settingHint" @click.native="settingHint" @keyup.native="disposeKey"> </el-input> <input-hint :all-items="hintItems" :position = 'hintPosition' @select = "replaceStr" ></input-hint> </div> </template> <script lang="ts"> import { Vue, Component, Prop } from "vue-property-decorator"; import InputHint from "./inputHint.vue"; import $ from "jquery"; @Component({ components: { InputHint } }) export default class AdvancedTextarea extends Vue { inputValue: string = ''; Seprator = "\n"; allUsers: string[] = []; hintItems: string[] = []; //传入提示框的项,可以是html字符串;为空则表示不显示提示框 initPosition = { //输入框的信息,用于计算提示框位置 left: 15, top: 5, rowHeight: 20, //一行的高度 fontSize: 7 //一个字的宽度 } hintPosition = { left: this.initPosition.left, top: this.initPosition.top } //按上下左右键时,重置提示框 disposeKey(e) { if (e.keyCode === 37 || e.keyCode === 38 || e.keyCode === 39 || e.keyCode === 40) { this.settingHint(); } } settingHint(val?) { let cursorLocation = $('#user-input').caret(); //光标位置 let newStr = this.inputValue.slice(0, cursorLocation); //输入框光标前的字符 let newArr = newStr.split(this.Seprator); let searchKey = newArr.length === 0 ? "" : newArr[newArr.length - 1]; let regExp = new RegExp(searchKey, 'ig'); this.hintItems = searchKey ? this.allUsers.filter(item => item.indexOf(searchKey) !== -1).map(item => item.replace(regExp, `<strong>${searchKey}</strong>`)) : this.allUsers; this.hintPosition.left = this.initPosition.left + this.initPosition.fontSize * (searchKey.length > 0 ? searchKey.length - 1 : 0); this.hintPosition.top = this.initPosition.top + this.initPosition.rowHeight * (newArr.length > 10 ? 10 : newArr.length); } closeHint() { //延后关闭是因为立即关闭的话,点击提示框内容就无法触发点击事件 window.setTimeout(() => { this.hintItems = null; window.clearTimeout(); }, 200); } //将光标当前值替换为选中值 replaceStr(val) { let cursorLocation = $('#user-input').caret(); //光标位置 let newStr = this.inputValue.slice(0, cursorLocation); //输入框光标前的字符 let row = newStr.split(this.Seprator).length - 1; //光标所在行 let oriArr = this.inputValue.split(this.Seprator); oriArr[row] = val; this.inputValue = oriArr.join(this.Seprator); $('#user-input').focus(); } getAllUsers() { this.allUsers = [ 'xiaoming@qq.com', 'daming@163.com', 'liuxioawei@gridsum.com', '432454354@qq.com', 'zhangzheng@qq.com', 'mostlove@163.com', 'wangweihao@gridsum.com', '67900332@qq.com', 'xiaosi@qq.com', 'loveshuang@163.com', 'liuxioawei@gridsum.com', '87456563@qq.com', 'yaru@qq.com', 'wuyuetian@163.com', 'junjun@gridsum.com', '67576889@qq.com', 'shuanger@qq.com', 'she@163.com', 'ruiji@gridsum.com', '45454334@qq.com', ] } mounted() { if (this.allUsers.length === 0) { this.getAllUsers(); } } } </script>
inputHint.vue
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<template> <div v-show="allItems&&allItems.length!==0"> <ul class="el-dropdown-menu el-popper max-height new-scoll-bar" :style="{left: position.left+'px', top: position.top+'px'}"> <li class="el-dropdown-menu__item" v-for="(item,index) in allItems" :key="index" v-html="item" @click="selectItem(item)"></li> </ul> </div> </template> <style lang="postcss" scoped> .max-height { max-height: 250px; overflow-y: auto; } </style> <script lang="ts"> import { Vue, Component } from "vue-property-decorator"; @Component({ props: { allItems: { type: Array, default: [] }, position: { type: Object, default: { left: 0, top: 0 } } } }) export default class InputHint extends Vue { allItems = this.allItems; selectItem(item: string) { let regExp = /<strong>|<\/strong>/g; let str = item.replace(regExp, ''); this.$emit('select', str) } } </script>