控件主要功能,可以单行显示多选的tag,并且可以利用左右键移动tag在组件中的位置
直接上代码:
<el-select
v-model="tags"
ref="tagsSelect"
multiple
filterable
remote
clearable
popper-class="tag-input-options"
:remote-method="searchTags"
:loading="isSearching"
class="tag-input"
@keyup.native.left="navLeft"
@keyup.native.right="navRight">
<el-option
v-for="item in tagOptions"
:key="item.id"
:label="item.full_path.map(f => f.name).join(' > ')"
:value="item.id">
<span class="tag-option" v-html="highlightTagOption(item.name)"></span>
</el-option>
</el-select>
下面是处理逻辑,主要是navLeft以及navRight方法的实现(typescript):
主要思路就是改变left,控制tag在select中的位移
const TAGS_SELECTOR = '.tag-input .el-select__tags';
const TAG_SELECTOR = '.tag-input .el-select__tags .el-tag';
private navLeft() {
let $hitTagDoms = $(TAG_SELECTOR + '.is-hit');
if (!$hitTagDoms.length) {
$(TAG_SELECTOR).last().addClass('is-hit');
return;
} else if ($hitTagDoms.is($(TAG_SELECTOR).first())) {
$(TAG_SELECTOR).removeClass('is-hit');
$(TAG_SELECTOR).last().addClass('is-hit');
$(TAGS_SELECTOR).css('left', '11px');
return;
}
let $lastTagDom = $hitTagDoms.hasClass('is-hit') ? $hitTagDoms.prev() : $hitTagDoms;
$(TAG_SELECTOR).removeClass('is-hit');
$lastTagDom.addClass('is-hit');
let firstTagRect = $(TAG_SELECTOR).get(0).getBoundingClientRect() as DOMRect;
let containerTagRect = $('.tag-input').get(0).getBoundingClientRect() as DOMRect;
let left = Number.parseFloat($(TAGS_SELECTOR).css('left'));
left = Number.isNaN(left) ? 0 : left;
let totalLeft = left + ($lastTagDom.innerWidth() as number);
if (totalLeft >= containerTagRect.x - firstTagRect.x + 11) { // 11 is padding left of tags
totalLeft = left + containerTagRect.x - firstTagRect.x + 11;
}
$(TAGS_SELECTOR).css('left', totalLeft + 'px');
}
private navRight() {
let $lastHitTagDoms = $(TAG_SELECTOR + '.is-hit');
if (!$lastHitTagDoms.length || $lastHitTagDoms.is($(TAG_SELECTOR).last())) { return; }
$(TAG_SELECTOR).removeClass('is-hit');
let $hitTagDoms = $lastHitTagDoms.next();
$hitTagDoms.addClass('is-hit');
let hitTagRect = $hitTagDoms.get(0).getBoundingClientRect() as DOMRect;
let containerTagRect = $('.tag-input').get(0).getBoundingClientRect() as DOMRect;
if (hitTagRect.right + 56 > containerTagRect.right) { // 56 is width of search button
let left = Number.parseFloat($(TAGS_SELECTOR).css('left'));
left = Number.isNaN(left) ? 0 : left;
let totalLeft = left - ($hitTagDoms.innerWidth() as number);
if ($hitTagDoms.is($(TAG_SELECTOR).last())) { // assure input showing
totalLeft = 0;
}
$(TAGS_SELECTOR).css('left', totalLeft + 'px');
}
}
为了不让select控件多选换行,需要覆盖一下相关的css:
.tag-input {
width: 645px;
overflow-x:hidden;
.el-select__tags {
flex-wrap: nowrap;
justify-content: flex-end;
transition: left .5s;
}
}
上面这样实现方式还有几个问题:
1)左右键会影响select中的input光标位置
2)在下拉框显示的时候,删除tag,会造成下拉框的位置偏移
3)把tag滑到最开始的时候,从前往后删除tag,会造成空白,因为没有调整left值
可以提供解决的思路:
1)可以不用left,right键控制移动tag,换成其他键
2)在删除的时候,手工隐藏下拉框,在输入搜索的时候在显示出来
3)在删除的时候,调整left值
private renderingTag() {
let $firstTagDom = $(TAG_SELECTOR + ':first-child');
if ($firstTagDom.length) {
let firstTagRect = $firstTagDom.get(0).getBoundingClientRect() as DOMRect;
let containerTagRect = $('.tag-input').get(0).getBoundingClientRect() as DOMRect;
if (firstTagRect.left > containerTagRect.left + 11) { // 11 is margin left
let left = Number.parseFloat($(TAGS_SELECTOR).css('left'));
left = Number.isNaN(left) ? 0 : left;
left -= (firstTagRect.left - containerTagRect.left - 11);
$(TAGS_SELECTOR).css('left', left + 'px');
}
}
}
下面看下效果图: