这里用el的组件来实现,一般标签组件又要带输入效果,所以是需要el-tag和el-input组件进行组合
截图中红色为组件封装的区域,蓝色是输入框所在的区域
左边是下拉选择框已封装好的组件,右边则是一个按钮,修改了样式,并且是绝对定位,所以是中间容器宽度需要考虑的因素之一
新增实现效果
1. 需要标签和输入框所在区域可滚动,可以查看到所有的标签
2. 需要选择标签和输入标签时时,可以完整地看到新增的标签
3. 需要取消勾选和打叉掉不要的标签时,可以出现回滚的效果
实现效果如下
当标签增加就左移,减少就右移
实现
下面代码实际是vue2+vue3组合式api的项目
1. 定义所有要用到且会影响实现效果的数据
const scrollContainer = ref() //滚动容器元素
const hasScrolled = ref(false) //是否可以滚动
const tagsRef = ref() //目前存在的标签
const deleteTagWidth = ref() // 删除标签的宽度
const inputWidth = 270 // 输入框设置的宽度
const buttonWidth = 36 // 按钮遮挡的宽度
const isInputFocusHiddenTag = ref(false) //点击空白区域会聚焦输入框,此时标签被遮挡的参数
2. 检测滚动容器是否可以滚动
const checkScroll = () => {
context.root.$nextTick(() => {
hasScrolled.value = isError.value
? scrollContainer.value.scrollWidth > (scrollContainer.value.clientWidth - buttonWidth)
: (scrollContainer.value.scrollWidth - inputWidth) > (scrollContainer.value.clientWidth - buttonWidth)
})
}
这边有对于标签数量做显示,如果标签数量超过时会报错,并且输入框会隐藏,所以对于是否出错做了一下处理,没有出错就是(容器滚动宽度 > 容器宽度 - 右侧按钮宽度),出错则还需要去除一下输入框的宽度
3. 标签增加时调用函数
const leftScroll = () => {
context.root.$nextTick(() => {
if (hasScrolled.value) {
// 根据要求,当focus遮挡前面的标签时,再添加标签时,需要滚动到最前面
if (isInputFocusHiddenTag.value) initPlaceScroll()
else {
const offset = tagsRef.value[tagsRef.value.length - 1].$el.offsetWidth
scrollContainer.value.scrollTo({
left: scrollContainer.value.scrollLeft + offset,
top: 0,
behavior: 'smooth'
})
}
}
})
}
拿到标签列表最后一个标签的宽度,之后左移+该宽度
4. 标签删除时调用函数
const rightScroll = () => {
context.root.$nextTick(() => {
if (hasScrolled.value) {
scrollContainer.value.scrollTo({
left: scrollContainer.value.scrollLeft - deleteTagWidth.value,
top: 0,
behavior: 'smooth'
})
} else {
initPlaceScroll()
}
})
}
删除标签列表的那个标签的宽度会被记录到deleteTagWidth上,去除掉就可以了
5. 无法滚动则重置到正常的滚动状态
const initPlaceScroll = () => {
scrollContainer.value.scrollTo({
top: 0,
left: 0,
behavior: 'smooth'
})
isInputFocusHiddenTag.value = false
}
6. 监视标签增加和减少的列表
watch(() => checkedList.value.length, (nv, ov) => {
checkScroll()
if (nv > ov) {
leftScroll()
} else {
rightScroll()
}
})
7. 隐藏容器滚动条,改成滚轮横向滚动
需要添加@wheel事件到滚动容器上
const handleWheel = (e) => {
const offset = e.deltaY
scrollContainer.value.scrollTo(scrollContainer.value.scrollLeft + offset, 0)
}