简介:TagsInput是一种可通过键入检索值回车生成检索条件标签,回退键删除上一个标签。效果如下:
- 基础样式
<template>
<div class='custom_input'>
<div style="width:100%;"
ref="searchP">
<div class="ipt_style">
<div class="ipt_left">
<div class="ipt_container">
<span class='btn'
v-for='(tag, index) in tags'
:key='index'>
<i class="search_val"
:style="{'maxWidth': Number(poverWidth) - (tag.length * 14 + 16 + 75) + 'px'}">{{tag.value}}</i>
<i class="el-icon-error search_close"
@click="closeTag(index)"></i>
</span>
<input type="text"
ref='input'
class="el-select__input"
@focus="showOptions"
:placeholder="placeholder"
@blur="closeOptions"
@keyup.delete="remove"
style="flex-grow: 1; width: 0.0961538%;height:32px;lineHeight:32px;"
@keyup.enter="add"
v-model='current'>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CustomInput',
data () {
return {
current: '',
placeholder: '请输入检索内容!'
}
}
}
</script>
<style lang="less">
.custom_input {
.ipt_style {
border: 1px solid #dcdfe6;
background-color: #ffffff;
border-radius: 5px;
width: 100%;
position: relative;
.ipt_left {
display: inline-block;
width: 100%;
}
.operation {
display: inline-block;
width: 60px;
padding-left: 6px;
position: absolute;
right: 0px;
top: 50%;
margin-top: -10px;
}
.operation i {
font-size: 20px;
margin: 0 2px;
cursor: pointer;
}
}
.ipt_container {
z-index: 1;
display: flex;
align-items: center;
flex-wrap: wrap;
line-height: normal;
white-space: normal;
.el-select__input {
border: none;
outline: none;
padding: 0;
margin-left: 15px;
color: #666;
padding-right: 10px;
font-size: 14px;
appearance: none;
height: 28px;
background-color: transparent;
}
}
}
</style>
- 事件
<input type="text"
ref='input'
class="el-select__input"
@focus="showOptions"
:placeholder="placeholder"
@blur="closeOptions"
@keyup.delete="remove"
style="flex-grow: 1; width: 0.0961538%;height:32px;lineHeight:32px;"
@keyup.enter="add"
v-model='current'>
// 聚焦点事件
showOptions () {
this.removeFlg = 2 // 删除标签
}
// 失焦事件
closeOptions () {
this.visible = false // 隐藏下拉选择区域
}
// 退格事件
remove (e) {
// 退格时判断input中是否有值,若有值正常删除,若没值则判断tags,将最后一个tags删除一个字符并放入input
const val = e.target.value
if (val === '' && this.tags.length > 0) {
this.removeFlg++
}
if (this.removeFlg > 1 && this.tags.length > 0) {
const length = this.tags.length - 1
this.current = this.tags[length].value
this.tags.splice(length, 1)
this.removeFlg = 0
}
}
// 回车事件
add (e) {
// 回车获取输入值,若输入值中有空格则代表“或”条件
const val = e.target.value
if (!val) return
const arr = val.split(' ')
let tempStr = ''
if (val.length > 1) {
arr.forEach((element, index) => {
if (arr.length - 1 === index) {
tempStr += element
} else {
tempStr += element + '或'
}
})
} else {
tempStr = arr[0]
}
const temp = {
value: tempStr,
keyValue: arr,
op: arr.length > 1 ? 'or' : 'and'
}
this.tags.push(temp)
this.current = ''
}
3.删除标签事件及标签内容超长处理
/* 遍历tags数据,展示生成的查询tag */
<span class='btn' v-for='(tag, index) in tags' :key='index'>
<i>{{tag.label}}</i>
/* 此处处理输入值超长,超出input框。poverWidth为下拉选择框的整体宽度减去tag字段名的宽度减去图标、padding距离等,剩余宽度则为实际输入内容区宽度,超出此宽度则...省略 */
<i class="search_val" :style="{'maxWidth': Number(poverWidth) - (16 + 75) + 'px'}">{{tag.value}}</i>
<i class="el-icon-error search_close" @click="closeTag(index)"></i>
</span>
// tag悬浮展示删除按钮,点击时删除当前tag
closeTag (index) {
this.tags.splice(index, 1)
}
- 完整代码
<template>
<div class='custom_input'>
<div style="width:100%;"
ref="searchP">
<div class="ipt_style">
<div class="ipt_left">
<div class="ipt_container">
<span class='btn'
v-for='(tag, index) in tags'
:key='index'>
<i class="search_val"
:style="{'maxWidth': Number(poverWidth) - (tag.length * 14 + 16 + 75) + 'px'}">{{tag.value}}</i>
<i class="el-icon-error search_close"
@click="closeTag(index)"></i>
</span>
<input type="text"
ref='input'
class="el-select__input"
@focus="showOptions"
:placeholder="placeholder"
@blur="closeOptions"
@keyup.delete="remove"
style="flex-grow: 1; width: 0.0961538%;height:32px;lineHeight:32px;"
@keyup.enter="add"
v-model='current'>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
name: 'CustomInput',
data () {
return {
current: '',
placeholder: '',
tags: [],
poverWidth: 0,
removeFlg: 0
}
},
props: {
},
watch: {
tags: {
handler (val) {
if (val.length) {
this.placeholder = ''
} else {
this.placeholder = '请输入检索内容!'
}
},
deep: true,
immediate: true
}
},
mounted () {
this.poverWidth = this.$refs.searchP.offsetWidth
},
methods: {
closeTag (index) {
this.tags.splice(index, 1)
},
showOptions () {
this.removeFlg = 2
},
closeOptions () {
this.visible = false
},
addHandle (key, label, val) {
const temp = {
key: key,
label: label,
value: val
}
this.tags.push(temp)
this.current = ''
this.visible = false
},
add (e) {
const val = e.target.value
if (!val) return
const arr = val.split(' ')
let tempStr = ''
if (val.length > 1) {
arr.forEach((element, index) => {
if (arr.length - 1 === index) {
tempStr += element
} else {
tempStr += element + '或'
}
})
} else {
tempStr = arr[0]
}
const temp = {
value: tempStr,
keyValue: arr,
op: arr.length > 1 ? 'or' : 'and'
}
this.tags.push(temp)
this.current = ''
this.visible = false
},
remove (e) {
const val = e.target.value
if (val === '' && this.tags.length > 0) {
this.removeFlg++
}
if (this.removeFlg > 1 && this.tags.length > 0) {
const length = this.tags.length - 1
this.current = this.tags[length].value
this.tags.splice(length, 1)
this.removeFlg = 0
}
}
}
}
</script>
<style lang="less">
.custom_input {
.elPopover {
.tips {
color: #8c8e91;
}
}
.ipt_style {
border: 1px solid #dcdfe6;
background-color: #ffffff;
border-radius: 5px;
width: 100%;
position: relative;
.ipt_left {
display: inline-block;
width: 100%;
}
.operation {
display: inline-block;
width: 60px;
padding-left: 6px;
position: absolute;
right: 0px;
top: 50%;
margin-top: -10px;
}
.operation i {
font-size: 20px;
margin: 0 2px;
cursor: pointer;
}
}
.option_value {
width: 100%;
white-space: nowrap;
overflow: hidden; //文本超出隐藏
text-overflow: ellipsis; //文本超出省略号替代
}
.hoverSty {
background-color: #f5f7fa;
}
.ipt_container {
z-index: 1;
display: flex;
align-items: center;
flex-wrap: wrap;
line-height: normal;
white-space: normal;
.btn {
display: inline-block;
border: 1px solid #4c7dff;
border-radius: 2px;
padding: 0;
margin: 4px 0px 4px 15px;
position: relative;
i {
display: inline-block;
padding: 0 4px;
font-style: normal;
font-size: 14px;
}
i:first-child {
background-color: #4c7dff;
color: white;
}
.search_val {
line-height: 20px;
color: #4c7dff;
white-space: nowrap;
vertical-align: top;
overflow: hidden; //文本超出隐藏
text-overflow: ellipsis; //文
}
.search_close {
font-size: 12px;
position: absolute;
right: -6px;
top: -6px;
padding: 1px;
background-color: white;
border-radius: 50%;
display: none;
}
}
.btn:hover .search_close {
display: inline-block;
}
.el-select__input {
border: none;
outline: none;
padding: 0;
margin-left: 15px;
color: #666;
padding-right: 10px;
font-size: 14px;
appearance: none;
height: 28px;
background-color: transparent;
}
}
}
</style>
5.引入(组件查询值的传入传出等暂未做处理,自己可根据实际情况做传参、事件等)
// 使用
<cus-input></cus-input>
// 引入
import CusInput from './customInput'
// 注册
components: {
CusInput
}