NEZ-1514 feat: 全局搜索功能开发 (30%)
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user