analyze
We need to pay attention to the following points when encapsulating the input input tag by ourselves:
- Style realization, span realizes label effect, input hides border (Element-UI can directly use tag).
- Event monitoring, to determine the operation of generating labels, which can be carriage return, and needs to monitor the situation of leaving the focus.
- Tag limit, up to several, and input validation
- html:
<template> <!-- 外层div --> <div ref="arrbox" :style="{width:`${width}px`,'min-height':`${height}px`}" class="arrbox" @click="onclick"> <!-- 标签 --> <el-tag v-for="(item,index) in tagsArr" :key="index" type="info" closable @close.stop="removeTag(index)"><span :ref="`span_${index}`" :show="getTitle(index,item)" class="tagSPan"><span :ref="`spanRef_${index}`" @dblclick="selectDiv">{ { item }}</span></span></el-tag> <!-- 标签 自定义的标签样式 --> <!-- <div v-for="(item,index) in tagsArr" :key="index" class="spantab"> <span class="tagtext" :ref="`span_${index}`" :show="getTitle(index,item)" ><span :ref="`spanRef_${index}`" @dblclick="selectDiv">{ { item }}</span></span> <i class="tagclose" @click.stop="removeTag(index)" /> </div> --> <!-- 输入框 --> <input ref="inputTag" v-model="currentval" :placeholder="tagsArr.length==0?placeholder:''" class="inputTag" type="text" @keyup="keyupFn" @blur="addTags('bulr')" > </div> </template>
- js:
- First of all, we need to focus the input when clicking on the outermost div
- In the css style, we put the input and those tags in one line using flex layout, and add the newline attribute, and set the input minimum width and flex: 1, so that the input can always be behind the tag, and the width is smaller than the defined minimum Wrap automatically when width
- Need to monitor space and carriage return, and out of focus, put the input value into the tag array, and then clear the tag.
- Add some restrictions when adding tags, so that users can operate better
export default { name: 'inputTags', model: { prop: 'parentArr', event: 'change-parentArr' }, props: { width: { type: Number, default: 300 }, height: { type: Number, default: 40 }, parentArr: { type: Array, default() { return [] } }, limit: { // 最多生成标签数 type: Number, default: -1 }, placeholder: { type: String, default: '请输入' }, reg: { type: String, default: '' } }, data() { return { currentval: '', tagsArr: [], inputLength: '' } }, watch: { tagsArr(value) { this.$emit('change-parentArr', value) }, parentArr: { handler(value) { this.tagsArr = value }, deep: true, immediate: true }, currentval(val) { // 获取最后三个字符,如果是逗号或者分号就分割,主要是为了使用者复制整段话 const lastValue = val.substr(-3) // 输入逗号,生成标签 if (lastValue.indexOf(',') > -1 || lastValue.indexOf(',') > -1 || lastValue.indexOf(';') > -1 || lastValue.indexOf(';') > -1) { this.currentval = this.currentval.split(',')[0] this.currentval = this.currentval.split(',')[0] this.currentval = this.currentval.split(';')[0] this.currentval = this.currentval.split(';')[0] this.addTags() } } }, mounted() { }, methods: { getTitle(index, title) { this.$nextTick(() => { const spanDom = this.$refs[`span_${ index}`][0] const textDom = this.$refs[`spanRef_${ index}`][0] if (textDom.offsetWidth > spanDom.offsetWidth) { spanDom.setAttribute('title', title) } }) }, // 双击选中 selectDiv (e){ var range; var element=e.currentTarget if (document.body.createTextRange) { range = document.body.createTextRange() range.moveToElementText(element) range.select() } else if (window.getSelection) { var selection = window.getSelection() range = document.createRange() range.selectNodeContents(element) selection.removeAllRanges() selection.addRange(range) /* if(selection.setBaseAndExtent){ selection.setBaseAndExtent(text, 0, text, 1); }*/ } else { console.log('none') } }, removeTag(index) { this.tagsArr.splice(index, 1) }, // 监听按键,空格或者Enter keyupFn(e) { // if (e.code == 'Space' || e.keyCode == 32) { // this.currentval = this.currentval.slice(0, -1) // this.addTags() // } if (e.key == 'Enter' || e.keyCode == 13) { this.addTags() } }, addTags(type = 'click') { if (this.limit != -1 && this.tagsArr.length >= this.limit) { this.$message.warning(`只能输入${ this.limit}个`) return } if (this.tagsArr.indexOf(this.currentval) > -1) { if (type == 'click') { this.$message.warning(`已经输入了【${ this.currentval}】`) } else { this.currentval = '' } return } // 格式校验 if (this.reg != '') { if (!this.reg.test(this.currentval)) { this.$message.warning('格式不正确!') this.currentval = '' return } } this.tagsArr.push(this.currentval) this.tagsArr = this.tagsArr.filter(n => n != '') this.currentval = '' }, onclick() { this.$nextTick(() => { this.$refs.inputTag.focus() }) } } }
- css:
/* 外层div */ input::-webkit-input-placeholder { /* placeholder颜色 */ color: #c1c7cf; /* placeholder字体大小 */ font-size: 14px; } .arrbox { box-sizing:border-box; background-color: white; border: 1px solid #dcdee2; border-radius: 4px; font-size: 12px; text-align: left; word-wrap: break-word; overflow: hidden; display: inline-flex; flex-wrap: wrap; align-items: center; } /* input */ .inputTag { font-size: 12px; border: none; box-shadow: none; outline: none; background-color: transparent; min-width: 40%; color: #495060; flex: 1; height: 30px; margin-left: 15px; padding-left: 0; } .el-tag{ height: 25px; line-height: 25px; margin: 3px 3px; max-width: calc(100% - 5px) !important; } .tagSPan{ display: inline-block; max-width: calc(100% - 10px) !important; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } ::v-deep .el-tag__close { background-color: #C0C4CC; top:-10px; transform: scale(.8); &::before{ transform: translate(0,0.5px); } } /* 自定义的标签 */ .spantab { display: inline-block; font-size: 14px; margin: 3px; height: 20px; background-color: #f7f7f7; border: 1px solid #e8eaec; border-radius: 3px; max-width: calc(100% - 5px); position: relative; padding-right: 20px; } .tagtext { height: 20px; line-height: 20px; max-width: 100%; position: relative; display: inline-block; padding-left: 8px; color: #495060; font-size: 12px; cursor: pointer; opacity: 1; vertical-align: top; overflow: hidden; transition: 0.25s linear; max-width: 100% !important; text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } .tagclose { opacity: 1; -webkit-filter: none; filter: none; position: absolute; top: 0; right: 0; } .tagclose:after { content: "x"; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; line-height: 14px; vertical-align: top; text-align: center; margin: 2px 2px 2px 5px; cursor: pointer; width: 14px; height: 14px; display: inline-block; background-color: #C0C4CC; border-radius: 50%; }
- use:
<template> <tagInput v-model="from.tag" :width="302" placeholder="请输入标签" /> </template> <script> import tagInput from ./tagInput' export default { components: { tagInput }, data(){ return{ from:{ tag:[] } } } } </script>