NEZ-1514 feat: 全局搜索功能开发 (30%)

This commit is contained in:
zhangyu
2022-01-13 18:35:05 +08:00
parent 622b39c96d
commit ef8b8701a5
3 changed files with 335 additions and 19 deletions

View File

@@ -41,6 +41,7 @@
"vue-countupjs": "^1.0.0", "vue-countupjs": "^1.0.0",
"vue-draggable-resizable": "^2.3.0", "vue-draggable-resizable": "^2.3.0",
"vue-grid-layout": "^2.3.12", "vue-grid-layout": "^2.3.12",
"vue-highlight-text": "^1.6.2",
"vue-i18n": "^8.15.1", "vue-i18n": "^8.15.1",
"vue-quill-editor": "^3.0.6", "vue-quill-editor": "^3.0.6",
"vue-resource": "^1.5.1", "vue-resource": "^1.5.1",

View File

@@ -59,13 +59,118 @@
border-radius: 6px 6px 0 0; border-radius: 6px 6px 0 0;
} }
.global-search-content{ .global-search-content{
flex: 1; height: calc(100% - 72px);
display: flex;
flex-direction: column;
background: $--background-color-empty; background: $--background-color-empty;
border-top: 1px solid $--border-color-light; border-top: 1px solid $--border-color-light;
.table-no-data{
text-align: center;
}
.global-search-content-content{
flex: 1;
height: calc(100% - 72px);
display: flex;
> div{
width: 50%;
box-sizing: border-box;
height: 100%;
}
.list{
height: 100%;
width: 100%;
box-sizing: border-box;
overflow-y: auto;
}
.list-item{
height: 64px;
display: flex;
align-items: center;
flex-direction: column;
justify-content: center;
outline: none;
border-bottom: 1px solid $--border-color-light;
> div {
width: 100%;
box-sizing: border-box;
padding-left: 20px;
padding-right: 20px;
font-size: 16px;
color: $--color-text-primary;
.nz-icon{
color: $--color-monitor !important;
font-size: 16px;
vertical-align: middle
}
}
.list-item-content{
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.list-item-sub{
ont-size: 14px;
color: $--color-info;
font-weight: 400;
margin-top: 2px;
padding-left: 40px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.lise-item-active {
background: $--table-row-hover-background-color;
}
}
.global-search-footer { .global-search-footer {
height: 72px; height: 72px;
border-radius: 0 0 6px 6px; border-radius: 0 0 6px 6px;
background: $--background-color-empty; background: $--background-color-empty;
display: flex;
>div{
width: 50%;
box-sizing: border-box;
padding-left: 30px;
}
.global-search-footer-left{
display: flex;
align-items: center;
.keyboard{
background: $--background-color-2;
border: 1px solid $--border-color-light;
border-radius: 5px;
width: 32px;
height: 32px;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: center;
margin: 0 8px 0 0;
.nz-icon{
color: $--color-text-regular;
font-size: 16px;
}
}
}
.global-search-footer-right {
display: flex;
align-items: center;
justify-content: space-around;
.scope-box {
color: $--background-color-disabled;
.nz-icon {
margin-right: 5px;
color: $--background-color-disabled ;
}
}
.scope-box.is-select{
color: $--color-monitor;
.nz-icon {
color: $--color-monitor;
}
}
}
} }
} }
} }

View File

@@ -3,17 +3,77 @@
<div class="global-search-box" :class="firstShow? '' : 'search-after'" @click.self="close"> <div class="global-search-box" :class="firstShow? '' : 'search-after'" @click.self="close">
<div class="global-search-input" :class="firstShow? '' : 'search-after'"> <div class="global-search-input" :class="firstShow? '' : 'search-after'">
<i class="nz-icon nz-icon-search" v-loading="loading"></i> <i class="nz-icon nz-icon-search" v-loading="loading"></i>
<el-input v-model="searchStr" @keydown.native.enter="searchAll" ref="searchStr"></el-input> <el-input v-model="searchStr" @input="searchAll" ref="searchStr" :placeholder="$t('globalSearch.placeholder')" clearable></el-input>
<div @click="close" class="global-search-cancel"> <div @click="close" class="global-search-cancel">
Cancel Cancel
</div> </div>
</div> </div>
<div v-loading="loading" class="global-search-content" v-if="!firstShow"> <div v-loading="loading" class="global-search-content" v-if="!firstShow">
<div class="global-search-content-content" > <div class="global-search-content-content" v-if="tableData.length">
content <div class="global-search-content-left">
<ul
class="list"
ref="list"
v-infinite-scroll="load"
infinite-scroll-disabled="disabled">
<!-- 添加 tabindex 实现focus的效果 -1 - -4 已被使用 所以从-5 开始 -->
<li
v-for="(item,index) in tableData"
class="list-item"
:class="index === selectIndex ? 'lise-item-active' : ''"
:key="index"
:ref="'item'+ index"
@mouseenter="changeSelectIndex(index)"
:tabindex="(selectIndex+5) * -1"
>
<div class="list-item-content">
<i class="nz-icon" :class="selectIcon(item)" />
<HighlightText :keyword="searchStr" :overWriteStyle="{color: '#FA901C'}" style="vertical-align: middle" :title="item.name">{{item.name}}</HighlightText>
</div>
<div class="list-item-sub" v-if="item.sub">
{{ item.sub }}
</div>
</li>
<li class="list-item" v-if="nextLoading" v-loading="nextLoading"></li>
<!-- <li class="list-item" v-if="noMore&&!nextLoading">没有更多了</li>-->
</ul>
</div>
<div class="global-search-content-right">
</div>
</div>
<div v-else class="global-search-content" style="justify-content: center">
<div class="table-no-data">
<svg class="icon" aria-hidden="true">
<use xlink:href="#nz-icon-no-data-list"></use>
</svg>
<div class="table-no-data__title">No results found</div>
</div>
</div> </div>
<div class="global-search-footer"> <div class="global-search-footer">
footer <div class="global-search-footer-left">
<div class="keyboard">
<i class="nz-icon nz-icon-huiche" />
</div>
{{$t('globalSearch.toSelect')}}
<div class="keyboard" style="margin-left: 20px">
<i class="nz-icon nz-icon-xiangshang" />
</div>
<div class="keyboard">
<i class="nz-icon nz-icon-xiangxia" />
</div>
{{$t('globalSearch.toNavigate')}}
<div class="keyboard" style="margin-left: 20px">
<i class="nz-icon nz-icon-esc" style="font-size: 12px;"/>
</div>
{{$t('globalSearch.toEsc')}}
</div>
<div class="global-search-footer-right">
<div class="scope-box" v-for="item in scope" :class="item.isSelect ? 'is-select' : ''" :key="item.type">
<i class="nz-icon" :class="selectIcon(item)" />
{{stat[item.type] || 0}}
</div>
</div>
</div> </div>
</div> </div>
</div> </div>
@@ -21,11 +81,23 @@
</template> </template>
<script> <script>
import HighlightText from 'vue-highlight-text'
export default { export default {
name: 'globalSearch', name: 'globalSearch',
components: {
HighlightText
},
computed: { computed: {
globalShow () { globalShow () {
return this.$store.getters.getGlobalShow return this.$store.getters.getGlobalShow
},
noMore () {
console.log((this.pageObj.pageNo * this.pageObj.pageSize) >= this.pageObj.total)
return (this.pageObj.pageNo * this.pageObj.pageSize) >= this.pageObj.total
},
disabled () {
return this.loading || this.noMore
} }
}, },
data () { data () {
@@ -33,7 +105,43 @@ export default {
firstShow: true, firstShow: true,
searchStr: '', searchStr: '',
loading: false, loading: false,
selectIndex: '' nextLoading: false,
selectIndex: '',
tableData: [],
pageObj: {
pageNo: 1,
pageSize: 50,
total: 50
},
obj: {},
// scope: ['asset', 'datacenter', 'project', 'module', 'endpoint', 'alertrule'],
scope: [
{
type: 'asseet',
isSelect: true
}, {
type: 'datacenter',
isSelect: true
}, {
type: 'project',
isSelect: true
}, {
type: 'module',
isSelect: true
}, {
type: 'endpoint',
isSelect: true
}, {
type: 'alertrule',
isSelect: true
}
],
timer: null,
searchTimer: null,
isKeyDown: false,
stat: {
}
} }
}, },
watch: { watch: {
@@ -51,9 +159,24 @@ export default {
close () { close () {
this.searchStr = '' this.searchStr = ''
this.firstShow = true this.firstShow = true
this.selectIndex = 0
this.pageObj.pageNo = 1
this.tableData = []
this.$store.commit('setGlobalShow', false) this.$store.commit('setGlobalShow', false)
this.unbindEvent() this.unbindEvent()
}, },
selectIcon (item) {
// console.log(item)
switch (item.type) {
case 'asset' : return 'nz-icon-overview-project'
case 'datacenter' : return 'nz-icon-model'
case 'project' : return 'nz-icon-project'
case 'module' : return 'nz-icon-overview-module'
case 'endpoint' : return 'nz-icon-overview-endpoint'
case 'alertrule' : return 'nz-icon-Alertrule'
}
return 'nz-icon-module5'
},
bindEvent () { bindEvent () {
window.addEventListener('keydown', this.keyDown) window.addEventListener('keydown', this.keyDown)
}, },
@@ -61,31 +184,118 @@ export default {
this.selectIndex = '' this.selectIndex = ''
window.removeEventListener('keydown', this.keyDown) window.removeEventListener('keydown', this.keyDown)
}, },
keyDown (e) { scroll () {
console.log(e, e.target, e.keyCode) // const ulBox = this.$refs.list
if (e.target._prevClass === 'el-input__inner') { const liBox = this.$refs['item' + this.selectIndex][0]
console.log('blur') // const liHeight = liBox.clientHeight
this.$refs.searchStr.blur() // const height = ulBox.clientHeight - liHeight
// const offsetTop = liBox.offsetTop
liBox.focus()
// if ((this.selectIndex + 1) * liHeight > height) {
// ulBox.scrollTop = (this.selectIndex + 1) * liHeight - height
// } else {
// ulBox.scrollTop = 0
// }
// console.log(height, liHeight, offsetTop, ulBox.scrollTop)
},
changeSelectIndex (index) {
console.log('changeSelectIndex', this.isKeyDown)
if (this.isKeyDown) {
this.getItemInfo()
return return
} }
if (e.keyCode === '13') { this.selectIndex = index
this.getItemInfo()
},
keyDown (e) {
console.log('keyDown')
// console.log(e, e.target, e.keyCode)
if (e.keyCode === 13) {
this.jumpTo() this.jumpTo()
} }
if (e.keyCode === '56') { this.isKeyDown = true
if (e.keyCode === 40) {
if (this.selectIndex === this.tableData.length - 1) {
this.isKeyDown = false
return
}
this.selectIndex++ this.selectIndex++
this.scroll()
this.changeSelectIndex(this.selectIndex)
}
if (e.keyCode === 38) {
if (this.selectIndex === 0) {
this.isKeyDown = false
return
} }
if (e.keyCode === '78') {
this.selectIndex-- this.selectIndex--
this.scroll()
this.changeSelectIndex(this.selectIndex)
} }
if (e.keyCode === 27) {
this.close()
}
if (this.timer) {
clearInterval(this.timer)
this.timer = null
}
this.timer = setTimeout(() => {
this.isKeyDown = false
}, 200)
},
jumpTo () {
},
getItemInfo () {
this.obj = {}
}, },
searchAll () { searchAll () {
this.selectIndex = 0 if (this.searchTimer) {
this.firstShow = false clearInterval(this.searchTimer)
this.loading = true this.searchTimer = null
// this.$get().then(res=>{
// this.loading = false
// })
} }
this.loading = true
this.searchTimer = setTimeout(() => {
this.selectIndex = 0
this.pageObj.pageNo = 1
this.tableData = []
this.getData()
}, 500)
},
load () {
this.pageObj.pageNo++
this.nextLoading = true
this.getData()
},
getData () {
const param = {
pageNo: this.pageObj.pageNo,
pageSize: this.pageObj.pageSize,
scope: this.scope.filter(item => item.isSelect).map(item => item.type).join(','),
q: this.searchStr
}
if (!this.searchStr) {
this.firstShow = true
this.loading = false
this.nextLoading = false
return
}
this.$get('/stat/search', param).then(res => {
this.firstShow = false
this.nextLoading = false
if (res.code === 200) {
this.tableData = this.tableData.concat(res.data.list)
this.pageObj.total = res.data.total
this.stat = res.stat
} else {
this.$message.error(res.msg)
}
this.loading = false
})
}
},
destroyed () {
window.removeEventListener('keydown', this.keyDown)
} }
} }
</script> </script>