NEZ-2511 fix:优化 asset 顶部搜索插件

This commit is contained in:
zhangyu
2023-01-18 09:49:02 +08:00
parent b7fc36743d
commit da0283bf36
5 changed files with 397 additions and 15 deletions

View File

@@ -2,6 +2,9 @@
transform: rotate(0);
transition: all linear .2s;
}
.nz-dropdown-icon{
color: $--color-text-disabled;
}
.arrow-down.arrow-up {
transform: rotate(180deg);
}

View File

@@ -0,0 +1,338 @@
<template>
<div :style="{'min-height': height + 'px',height:getHeight + 'px'}" class="search-box__container">
<div v-for="(data, type, index) in titleSearchListCopy" :key="index" v-if="!(data.hide)" ref="searchContentBox" :class="{'search-content-box-height': data.length == 0}" class="search-content-box">
<div class="search-title">{{data.label}}:</div>
<el-skeleton v-if="data.type === 'checkBox'" :class="skeletonClass(data)" :loading="!data.show" :rows="1" class="search-items" :ref="`search-items${type}`" :style="{height: data.height}">
<template>
<el-checkbox-group v-if="data.type === 'checkBox'" ref="searchContent" v-model="selectValue[data.key]" @change="selectValue.change === 0 && selectValue.change++">
<template v-for="(item, j) in data.children">
<el-checkbox :key="j" :label="item.value" :title="item.key || item.name || item.label">{{item.key || item.name || item.label}} <span class="search-content-num">({{item.num || 0}})</span></el-checkbox>
</template>
</el-checkbox-group>
</template>
<span
v-show="data.needMore"
:style="{top: `${moreBtnTop(data.type)}px`}"
class="search-more"
@click="changShowMore(type)">
{{$t('overall.more')}}<i :class="data.showMore ? 'arrow-up' : ''" class="el-icon-arrow-down arrow-down"/>
</span>
</el-skeleton>
<el-skeleton v-if="data.type === 'dropdownCheckBox'" :loading="!data.show" :rows="1" class="search-items" :ref="`search-items${type}`" :style="{height: data.height}">
<template>
<dropdown
v-for="(item, j) in data.children"
:key="j"
:selectValue="selectValue[data.key]"
:type="data.key"
:ref="`${type}_${item.id}_cascader`"
:collapse-tags="true"
:item="item"
@resize="(value) => resize(value, type, item, data.key)"
@blur="casFocus"
@change="(value) => casChange(value, type, item, data.key)"
@focus="casFocus"
></dropdown>
</template>
<span
v-show="data.needMore"
:style="{top: `${moreBtnTop(data.type)}px`}"
class="search-more"
@click="changShowMore(type)">
{{$t('overall.more')}}<i :class="data.showMore ? 'arrow-up' : ''" class="el-icon-arrow-down arrow-down"/>
</span>
</el-skeleton>
</div>
</div>
</template>
<script>
import dropdown from '@/components/common/labelFilter/dropdown'
export default {
name: 'clickSearch',
components: {
dropdown
},
props: {
titleSearchList: {
/*
* project: {
label: 'Project', // 显示的label
key: 'projectIds', // 搜索使用的key
type: 'checkBox', // 类型
children: [] // 需要展示的子集
showMore: false, // 是否需要显示更多
index: 最大宽度的个数, // 是否需要显示更多
},
* */
type: Object,
default () {
return {
project: {
label: this.$t('overall.project'), // 显示的label
key: 'projectIds', // 搜索使用的key
type: 'checkBox', // 类型
children: [], // 需要展示的子集
show: false,
showMore: false,
index: 10 // 是否需要显示更多
}
}
}
},
selectValue: {
type: Object
}
},
computed: {
moreBtnTop () {
return function (type) {
let top
switch (type) {
case 'checkBox': {
top = 8
break
}
case 'dropdownCheckBox': {
top = 8
break
}
default: break
}
return top
}
},
skeletonClass () {
return function (data) {
let className = ''
if (data.type === 'checkBox') {
className = 'search-items--checkbox'
} else if (data.type === 'dropdownCheckBox') {
className = 'search-items--dropdown'
}
if (data.showMore) {
className += 'more'
}
return className
}
},
height () {
const paddingHeight = 0
const checkBoxRowHeight = 40
const labelRowHeight = 40
let checkBoxRowCount = 0
let labelRowCount = 0
Object.keys(this.titleSearchListCopy).forEach(type => {
if (this.titleSearchListCopy[type].hide) {
return
}
if (this.titleSearchListCopy[type].type === 'checkBox') {
checkBoxRowCount++
} else if (this.titleSearchListCopy[type].type === 'dropdownCheckBox') {
labelRowCount++
}
})
return paddingHeight + checkBoxRowHeight * checkBoxRowCount + labelRowHeight * labelRowCount + checkBoxRowCount + labelRowCount + 2
},
getHeight () {
if (this.changeMoreNum) {
let height = 2
this.$refs.searchContentBox.forEach(item => {
height += item.offsetHeight
})
return height
} else {
return 0
}
}
},
watch: {
titleSearchList: {
immediate: true,
deep: true,
handler (n) {
console.log(JSON.parse(JSON.stringify(n)))
this.titleSearchListCopy = n
this.needMore()
}
},
selectValue: {
deep: true,
handler (n) {
this.$emit('reload', n)
}
}
},
data () {
return {
contentWidth: 0, // 搜索框内部区域的宽度
titleSearchListCopy: {},
widthConstant: {
checkBox: {
boxMargin: 30,
checkBox: 14,
tagPadding: 10,
tagBlankTotal: 24 // 以上2个空白占位的总和
},
dropdownCheckBox: {
boxMargin: 21,
labelPadding: 6,
tagMaxWidth: 150, // 定义tag标签最大宽度
// 先分解cascader输入框的结构如下
tagMargin: 6,
tagPadding: 10,
tagBorder: 2,
tagRemoveIcon: 11,
tagBlank: 2,
tagBlankTotal: 31, // 以上五个空白占位的总和
textMaxWidth: 119, // 纯文本最大宽度通过tag最大宽度减去total得到
inputOriginalWidth: 60, // 输入框初始宽度
moreNumberWidth: 42, // 选中选项时右侧的+1、+2...数字提示内容的宽度
arrowDownWidth: 30 // 输入框右端箭头区域的宽度
}
},
changeMoreNum: 0
}
},
mounted () {
},
methods: {
/* value: 选择器返回的值 */
casChange (value, type, item, key) {
this.$nextTick(() => {
// 计算change后新宽度
const oldInputWidth = item.inputWidth
this.setEachCascWidth(value, type, item)
/* 为实现动态宽度而改变输入框宽度因为cascader组件的宽度是由它决定的 */
if (item.inputWidth !== oldInputWidth) {
this.$refs[`${type}_${item.id}_cascader`][0].$el.querySelector('.el-input__inner').style.width = `${item.inputWidth}px`
} else if (!value.length) {
this.$refs[`${type}_${item.id}_cascader`][0].$el.querySelector('.el-input__inner').style.width = ''
}
/* 组织参数 */
const cascs = []
this.titleSearchListCopy[type].children.forEach(c => {
cascs.push(this.$refs[`${type}_${c.id}_cascader`][0])
})
if (type === 'assetLabel' || type === 'state') { // label特殊处理组织成{“id”:[“张三”,"lw"],"id":["李四"]}
const values = {}
cascs.forEach(c => {
const nodes = c.$refs.cascader ? c.$refs.cascader.getCheckedNodes() : []
nodes.forEach(n => {
const metaId = n.data.metaId
if (type === 'assetLabel') {
if (values[metaId]) {
values[metaId].push(n.data.name)
} else {
values[metaId] = [n.data.name]
}
} else {
if (values[metaId]) {
values[metaId].push(n.data.id)
} else {
values[metaId] = [n.data.id]
}
}
})
})
const valuesStr = JSON.stringify(values)
this.selectValue[key] = valuesStr === '{}' || !valuesStr ? null : valuesStr
} else {
const values = new Set()
cascs.forEach(c => {
const nodes = c.$refs.cascader ? c.$refs.cascader.getCheckedNodes() : []
nodes.forEach(n => values.add(n.data.id))
})
this.selectValue[key] = [...values]
}
if (!this.selectValue.change) {
this.selectValue.change = 1
} else {
this.selectValue.change++
}
})
},
computeCascWidth (textWidth, labelWidth) { // label + tag + margin
const tagWidth = textWidth > this.widthConstant.dropdownCheckBox.textMaxWidth ? this.widthConstant.dropdownCheckBox.textMaxWidth : textWidth + this.widthConstant.dropdownCheckBox.tagBlankTotal // 限制原始文字宽度不超过最大值得到实际tag宽度
const inputWidth = tagWidth + this.widthConstant.dropdownCheckBox.moreNumberWidth + this.widthConstant.dropdownCheckBox.arrowDownWidth
return { tagWidth, inputWidth, width: labelWidth + inputWidth + this.widthConstant.dropdownCheckBox.boxMargin }
},
resize (value, type, item) {
const oldInputWidth = item.inputWidth
if (type) {
if (value.length === 0) { // 回到初始宽度
item.width = item.originalWidth
item.inputWidth = this.widthConstant.dropdownCheckBox.inputOriginalWidth
} else {
const showTag = item.children.find(c => c.id == value[0]) // 展示的tag
const { tagWidth, inputWidth, width } = this.computeCascWidth(this.computeDistance(showTag.name, 16), item.labelWidth)
item.width = width
item.inputWidth = inputWidth
}
}
if (item.inputWidth !== oldInputWidth) {
this.$nextTick(() => {
this.$refs[`${type}_${item.id}_cascader`][0].$el.querySelector('.el-input__inner').style.width = `${item.inputWidth}px`
})
}
},
setEachCascWidth (value, type, item) {
if (type) {
if (value.length === 0) { // 回到初始宽度
item.width = item.originalWidth
item.inputWidth = this.widthConstant.dropdownCheckBox.inputOriginalWidth
} else {
const showTag = item.children.find(c => c.id == value[0]) // 展示的tag
const { tagWidth, inputWidth, width } = this.computeCascWidth(this.computeDistance(showTag.name, 16), item.labelWidth)
item.width = width
item.inputWidth = inputWidth
}
}
},
casFocus (item, isFocus, e) {
this.$set(item, 'isFocus', isFocus)
},
computeDistance (str, fontSize) {
let width = 0
const c = fontSize ? '.temp-dom--' + fontSize : '.temp-dom'
const html = document.querySelector(c)
html.innerText = str
width = html.offsetWidth
return width
},
changShowMore (type) {
this.titleSearchListCopy[type].showMore = !this.titleSearchListCopy[type].showMore
console.log(this.titleSearchListCopy[type].showMore)
if (this.titleSearchListCopy[type].showMore) {
this.titleSearchListCopy[type].height = 'auto'
} else {
this.titleSearchListCopy[type].height = '40px'
}
},
needMore () {
this.$nextTick(() => {
Object.keys(this.titleSearchListCopy).forEach(type => {
if (this.$refs[`search-items${type}`]) {
console.log(this.$refs[`search-items${type}`][0].$el.offsetHeight, type)
if (this.$refs[`search-items${type}`][0].$el.offsetHeight > 40 && !this.titleSearchListCopy[type].showMore) {
this.titleSearchListCopy[type].needMore = true
this.titleSearchListCopy[type].height = '40px'
}
}
})
})
}
},
destroyed () {
}
}
</script>
<style scoped lang="scss">
.search-items{
overflow: hidden;
}
</style>

View File

@@ -4,9 +4,10 @@
class="nz-label-search"
>
<div class="nz-label-search__label">
<span>{{item.name}}:</span>
<span>{{item.name}}: </span>
</div>
<el-cascader
v-if="item.isInit"
ref="cascader"
v-bind="$attrs"
v-model="cascaderData"
@@ -25,6 +26,9 @@
<span class="search-content-num">({{data.num || 0 }})</span>
</template>
</el-cascader>
<div v-else style="padding: 0 5px 0 32px" @click="initCascader(item)" class="nz-dropdown-icon">
<i class="el-input__icon el-icon-arrow-down"/>
</div>
</div>
</template>
@@ -75,12 +79,19 @@ export default {
},
change (value) {
this.$emit('change', value)
},
initCascader (item) {
item.isInit = true
this.$nextTick(() => {
this.$refs.cascader.dropDownVisible = true
})
}
},
computed: {
isHover () {
if (this.ready) {
return this.$refs.cascader.inputHover
// return this.$refs.cascader.inputHover
return false
} else {
return false
}

View File

@@ -204,7 +204,7 @@ import dataListMixin from '@/components/common/mixin/dataList'
import detailViewMixin from '@/components/common/mixin/detailViewMixin'
import assetTable from '@/components/common/table/asset/assetTable'
import assetDetail from '@/components/common/detailView/list/asset/assetDetail'
import clickSearch from '@/components/common/labelFilter/clickSearch'
import clickSearch from '@/components/common/labelFilter/clickSearchNew'
import topToolMoreOptions from '@/components/common/popBox/topToolMoreOptions'
import alertSilenceBox from '@/components/common/rightBox/alertSilenceBox'
import detailViewTopSearch from '@/components/common/detailView/detailViewTopSearch'
@@ -345,21 +345,27 @@ export default {
label: this.$t('overall.dc'),
key: 'dcIds',
type: 'checkBox',
height: 'auto',
children: [],
show: false,
showMore: false,
needMore: false,
width: 0,
index: -1
index: -1,
hide: false
},
type: {
label: this.$t('overall.type'),
key: 'typeIds',
type: 'checkBox',
children: [],
height: 'auto',
show: false,
showMore: false,
needMore: false,
width: 0,
index: -1
index: -1,
hide: false
},
ping: {
label: 'Ping',
@@ -369,30 +375,39 @@ export default {
{ key: 'Down', value: 0, name: 'Down' },
{ key: 'Up', value: 1, name: 'Up' }
],
height: 'auto',
show: false,
showMore: false,
needMore: false,
width: 0,
index: -1
index: -1,
hide: false
},
model: {
label: this.$t('asset.model'),
key: 'modelIds',
type: 'checkBox',
height: 'auto',
children: [],
show: false,
showMore: false,
needMore: false,
width: 0,
index: -1
index: -1,
hide: false
},
assetLabel: {
label: this.$t('overall.more'),
key: 'fields',
type: 'dropdownCheckBox',
children: [],
height: 'auto',
show: false,
showMore: false,
needMore: false,
width: 0,
index: -1
index: -1,
hide: false
}
},
selectValue: {
@@ -414,7 +429,8 @@ export default {
showMore: false,
dropShow: false,
width: 0,
index: -1
index: -1,
hide: false
},
type: {
label: this.$t('overall.type'),
@@ -425,7 +441,8 @@ export default {
showMore: false,
dropShow: false,
width: 0,
index: -1
index: -1,
hide: false
},
ping: {
label: 'Ping',
@@ -439,7 +456,8 @@ export default {
showMore: false,
dropShow: false,
width: 0,
index: -1
index: -1,
hide: false
},
model: {
label: this.$t('asset.model'),
@@ -450,7 +468,8 @@ export default {
showMore: false,
dropShow: false,
width: 0,
index: -1
index: -1,
hide: false
},
assetLabel: {
label: this.$t('overall.more'),
@@ -461,7 +480,8 @@ export default {
showMore: false,
dropShow: false,
width: 0,
index: -1
index: -1,
hide: false
}
},
timer: '',
@@ -624,13 +644,17 @@ export default {
const showData = response.data.list.filter(d => d.display === 1).map(item => {
return { label: item.name, prop: item.name, show: false, allowed: true, type: 'label' }
})
let fields = []
if (this.$route.query && this.$route.query.fields) {
fields = Object.keys(JSON.parse(this.$route.query.fields))
}
searchableData.forEach(m => {
if (m.param) {
const param = JSON.parse(m.param)
if (param.items) {
// param.items.map(p => { return { ...p, id: p.name, metaId: m.id, num: metaData.find(meta => meta.id === m.id && meta.option === p.name).num } })
data.push({
...m,
isInit: fields.indexOf(m.id + '') !== -1,
children: param.items.map(p => {
const metaFind = metaData.find(meta => meta.id === m.id && meta.option === p.name)
return {
@@ -810,6 +834,8 @@ export default {
this.detailSearchList.assetLabel.children = res
this.titleSearchList.assetLabel.show = true
this.detailSearchList.assetLabel.show = true
this.titleSearchList[key].hide = !res.length
this.detailSearchList[key].hide = !res.length
})
} else if (key === 'model') {
this.getModelData(statistics[keys]).then(res => {
@@ -817,10 +843,14 @@ export default {
this.detailSearchList.model.children = res
this.titleSearchList.model.show = true
this.detailSearchList.model.show = true
this.titleSearchList[key].hide = !res.length
this.detailSearchList[key].hide = !res.length
})
} else {
this.titleSearchList[key].children = statistics[keys].map(d => { return { ...d, value: d.id } })
this.detailSearchList[key].children = statistics[keys].map(d => { return { ...d, value: d.id } })
this.titleSearchList[key].hide = !this.titleSearchList[key].children.length
this.detailSearchList[key].hide = !this.detailSearchList[key].children.length
}
if (this.titleSearchList[key].children.length === 0) {
// delete this.titleSearchList[key]

View File

@@ -257,7 +257,7 @@ import endpointTable from '@/components/common/table/settings/endpointTable'
import EditEndpointBoxNew from '@/components/common/rightBox/editEndpointBoxNew'
import batchModifyEndpoint from '@/components/common/rightBox/batchModifyEndpoint'
import batchAddEndpoint from '@/components/common/rightBox/batchAddEndpoint'
import clickSearch from '@/components/common/labelFilter/clickSearch'
import clickSearch from '@/components/common/labelFilter/clickSearchNew'
import topToolMoreOptions from '@/components/common/popBox/topToolMoreOptions'
import alertSilenceBox from '@/components/common/rightBox/alertSilenceBox'
import nzDetailView from '@/components/common/detailView/nzDetailView'