分析
我们自己封装input输入标签需要注意一下几点:
- 样式实现,span实现标签效果、input隐藏边框(Element-UI可以直接使用tag)。
- 事件监听,确定生成标签的操作,可以是回车,并且需要监听离开焦点的情况。
- 标签限制,最多几个,以及输入验证
- 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:
- 首先我们需要在点击最外层的div时,让input聚焦
- 在css样式上,我们让input和那些tag使用flex布局放在一行,并加上换行属性,同时设置input最小宽度和flex:1,这样就可以让input一直更在tag后面,宽度小于定义的最小宽度时也会自动换行
- 需要监听空格和回车,以及失焦,将input的值放到tag数组里面,然后清空tag.
- 在添加tag的时候加一些限制,这样可以更好的用户操作
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%; }
- 使用:
<template> <tagInput v-model="from.tag" :width="302" placeholder="请输入标签" /> </template> <script> import tagInput from ./tagInput' export default { components: { tagInput }, data(){ return{ from:{ tag:[] } } } } </script>