wanmeihuaxu 0664e021d0 bug fix:showSelectList options的closeOnSelect设置为false时,选项只能点一次
修改:添加每个选项独立的事件注册
2025-01-16 16:02:55 +08:00

239 lines
6.4 KiB
Vue

<template>
<q-card @mousewheel="mouseHandler">
<q-virtual-scroll
ref="scrollBar"
@scroll="scrollHandler"
:style="{ maxHeight: listMaxHeight + 'px', height: '100vh' }"
:virtual-scroll-slice-size="lazyItemSize"
:virtual-scroll-item-size="itemHeight"
:items="matchedItems"
>
<template v-slot="{ item, index }">
<div
:key="index"
v-ripple
@mousemove="currentIndex = index"
@click="clickOK"
:class="{
'item-selected': index === currentIndex,
}"
:style="{
height: itemHeight + 'px',
paddingRight: shortCutWidth - 16 + 'px',
cursor: 'pointer',
}"
>
<!-- 纯文本 -->
<q-item v-if="is.text">
<q-item-section>
<q-item-label lines="1">{{ item }}</q-item-label>
</q-item-section>
</q-item>
<!-- json -->
<q-item v-else-if="is.json">
<q-item-section avatar v-if="item.icon">
<q-avatar size="34px">
<q-img :src="item.icon" />
</q-avatar>
</q-item-section>
<q-item-section>
<q-item-label lines="1">{{ item.title }}</q-item-label>
<q-item-label lines="1" caption>{{
item.description
}}</q-item-label>
</q-item-section>
</q-item>
<!-- html -->
<q-item v-else-if="is.html">
<q-item-section>
<div v-html="item" class="full-width"></div>
</q-item-section>
</q-item>
</div>
</template>
</q-virtual-scroll>
<div
:style="{ top: 0, bottom: 0, width: shortCutWidth + 'px' }"
class="fixed-right shortcut"
>
<div
:style="{ height: itemHeight + 'px' }"
class="flex content-center justify-start q-pa-xs"
v-for="count in countsPerPage"
:key="count"
>
{{ $q.platform.is.mac ? "⌘" : "Alt" }}+{{ count % countsPerPage }}
</div>
</div>
<q-btn
v-if="options.options.showCancelButton"
class="absolute-bottom-right q-ma-xs"
round
color="primary"
icon="close"
@click="hide"
/>
</q-card>
</template>
<script>
import pinyinMatch from "pinyin-match";
export default {
data() {
return {
items: this.options.initItems,
listMaxHeight: 500,
currentIndex: 0,
itemHeight: 50,
lazyItemSize: 100,
shortCutWidth: 50,
searchWords: "",
topIndex: 0,
timeoutId: null,
};
},
mounted() {
this.options.options.enableSearch && this.setSubInput();
this.addListeners();
this.setUtoolsHeight(this.itemHeight * this.matchedItemsSize);
},
unmounted() {
this.clear();
},
computed: {
matchedItems() {
let matchedItems = this.searchWords
? this.items.filter((x) =>
pinyinMatch.match(x.title ? x.title : x, this.searchWords)
)
: this.items;
this.setUtoolsHeight(this.itemHeight * matchedItems.length);
return matchedItems;
},
matchedItemsSize() {
return this.matchedItems.length;
},
countsPerPage() {
return this.listMaxHeight / this.itemHeight;
},
bottomIndex() {
return this.topIndex + this.countsPerPage - 1;
},
is() {
return {
json: this.options.options.optionType === "json",
html: this.options.options.optionType === "html",
text: this.options.options.optionType === "plaintext",
};
},
},
props: {
options: Object,
},
methods: {
hide() {
this.clear();
this.$emit("hide");
},
clickOK() {
let selected = this.is.json
? this.matchedItems[this.currentIndex]
: {
id: this.currentIndex,
text: this.matchedItems[this.currentIndex],
};
this.is.json && selected.clickFn ? selected.clickFn(this.matchedItems[this.currentIndex].id) : this.$emit("clickOK", selected);
this.options.options.closeOnSelect && this.hide();
},
keyHandler(e) {
e.preventDefault();
if (this.$q.platform.is.mac ? e.metaKey : e.altKey && !isNaN(e.key)) {
let index = e.key === "0" ? 10 : parseInt(e.key);
this.currentIndex = this.topIndex + index - 1;
return this.clickOK();
}
switch (e.keyCode) {
case 38: // 上
this.currentIndex = Math.max(0, this.currentIndex - 1);
break;
case 40: // 下
this.currentIndex = Math.min(
this.matchedItemsSize - 1,
this.currentIndex + 1
);
break;
case 13: // 回车
return this.clickOK();
}
this.$nextTick(() => {
document.querySelector(".item-selected").scrollIntoViewIfNeeded(0);
});
},
scrollHandler(e) {
this.topIndex = parseInt(e.target.scrollTop / this.itemHeight);
clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => {
if (this.currentIndex < this.topIndex)
this.currentIndex = this.topIndex;
if (this.currentIndex > this.bottomIndex)
this.currentIndex = this.bottomIndex;
this.timeoutId = null;
}, 200);
},
mouseHandler(e) {
e.preventDefault();
this.$refs.scrollBar.$el.scrollBy(
0,
(e.deltaY / this.itemHeight).toFixed() * this.itemHeight
);
},
setSubInput() {
utools.setSubInput(({ text }) => {
this.searchWords = text;
if (this.matchedItems.length < this.currentIndex + 1) this.currentIndex = 0
}, this.options.options.placeholder);
},
setUtoolsHeight(height) {
utools.setExpendHeight(Math.min(height, this.listMaxHeight));
},
clear() {
utools.removeSubInput();
document.removeEventListener("keydown", this.keyHandler, true);
this.setUtoolsHeight(this.listMaxHeight);
},
addListeners() {
// 如果之前启用了子输入框,先移除监听
if (this.$root.subInputEvent)
document.removeEventListener(...this.$root.subInputEvent);
document.addEventListener("keydown", this.keyHandler, true);
},
},
};
</script>
<style scoped>
.q-card--dark {
background: var(--q-dark-page);
}
.q-item,
.shortcut {
user-select: none;
transition: 0s;
}
.item-selected {
background: #dfe2e6;
}
.body--dark .item-selected {
background: #515151;
}
</style>