控件主要功能,可以單行顯示多選的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');
}
}
}
下面看下效果圖: