CN-1388 fix: 调整实体列表等待交互和接口请求顺序等

1.调整接口请求顺序为list > 左侧筛选 > 顶部统计;
2.优化左侧筛选loading交互;
3.将默认每页条数改为10,可选条数由20,30,50改为10,20,50
This commit is contained in:
chenjinsong
2023-10-14 15:22:38 +08:00
parent b44a99f354
commit cdd3557bbe
7 changed files with 178 additions and 105 deletions

View File

@@ -32,6 +32,7 @@
width: calc(100% - 30px);
margin: 0 10px 0 20px;
max-height: 265px;
min-height: 40px;
overflow-y: scroll;
overflow-x: hidden;

View File

@@ -21,7 +21,7 @@
.entity-list--list {
display: flex;
flex-direction: column;
height: 100%;
height: auto;
/*overflow: visible;/*overflow: auto;*/
.cn-entity__shadow {

View File

@@ -58,13 +58,24 @@ export default {
*/
setup (props) {
const { query } = useRoute()
const pageSize = ref(defaultPageSize)
// pageSize取值顺序1.url2.缓存3.pageObj4.默认值
const urlPageSize = parseInt(query.pageSize)
const cachePageSize = parseInt(localStorage.getItem(storageKey.pageSize + '-' + localStorage.getItem(storageKey.username) + '-' + props.tableId))
const pageObjPageSize = props.pageObj.pageSize
const pageSize = ref(urlPageSize || cachePageSize || pageObjPageSize || defaultPageSize)
const currentPageNo = ref(props.storePageNoOnUrl ? (query.pageNo || (props.pageObj.pageNo || 1)) : (props.pageObj.pageNo || 1))
return {
pageSize,
currentPageNo
}
},
mounted () {
if (this.postPageSizes && this.postPageSizes.length > 0) {
this.resetPageSizes()
}
this.currentPageNo = parseInt(this.currentPageNo)
this.current(this.currentPageNo)
},
data () {
return {
// pageSize: defaultPageSize,
@@ -147,6 +158,7 @@ export default {
size (val) {
// eslint-disable-next-line vue/no-mutating-props
// this.pageObj.pageNo = 1
this.$emit('scrollbarToTop')
this.$emit('pageSize', val)
this.backgroundColor()
@@ -163,11 +175,12 @@ export default {
wrap.scrollTop = 0
}
})
this.$emit('scrollbarToTop')
})
},
resetPageSizes: function () {
if (this.postPageSizes) {
this.pageSizes = this.postPageSizes.map((item) => {
this.pageSizes = this.postPageSizes.map(item => {
return {
label: item + this.$t('pageSize'),
value: item
@@ -183,20 +196,6 @@ export default {
// }
}
},
mounted () {
if (this.postPageSizes && this.postPageSizes.length > 0) {
this.pageSize = this.postPageSizes[0]
this.resetPageSizes()
} else {
const pageSize = localStorage.getItem(storageKey.pageSize + '-' + localStorage.getItem(storageKey.username) + '-' + this.tableId)
if (pageSize != 'undefined' && pageSize != null) {
this.pageSize = parseInt(pageSize)
}
}
this.currentPageNo = parseInt(this.currentPageNo)
this.current(this.currentPageNo)
},
watch: {
postPageSizes: {
immediate: true,

View File

@@ -804,10 +804,10 @@ export default {
}
})
}
if (route === this.route) {
/* if (route === this.route) {
this.refresh()
return
}
} */
if (route) {
this.$router.push({
path: route,

View File

@@ -185,7 +185,6 @@ export default {
},
data () {
return {
showList: false,
listMode: 'list', // entity列表的模式list|block
entityAppTotal: '-',
@@ -213,28 +212,36 @@ export default {
title: this.$t('entity.topCountries'),
topColumn: 'ip.country',
totalCount: 0,
data: []
data: [],
loading: false,
firstLoad: true
},
{
icon: 'cn-icon cn-icon-city',
title: this.$t('entity.topCities'),
topColumn: 'ip.city',
totalCount: 0,
data: []
data: [],
loading: false,
firstLoad: true
},
{
icon: 'cn-icon cn-icon-as',
title: this.$t('entity.topASNs'),
topColumn: 'ip.asn',
totalCount: 0,
data: []
data: [],
loading: false,
firstLoad: true
},
{
icon: 'cn-icon cn-icon-operator',
title: this.$t('entity.topISPs'),
topColumn: 'ip.isp',
totalCount: 0,
data: []
data: [],
loading: false,
firstLoad: true
},
{
icon: 'cn-icon cn-icon-open-port',
@@ -242,28 +249,36 @@ export default {
topColumn: 'ip.port',
topColumn1: 'ip.protocol',
totalCount: 0,
data: []
data: [],
loading: false,
firstLoad: true
},
{
icon: 'cn-icon cn-icon-FQDN',
title: this.$t('entity.topFQDNCategories'),
topColumn: 'domain.category',
totalCount: 0,
data: []
data: [],
loading: false,
firstLoad: true
},
{
icon: 'cn-icon cn-icon-category2',
title: this.$t('entity.topAppCategories'),
topColumn: 'app.category',
totalCount: 0,
data: []
data: [],
loading: false,
firstLoad: true
},
{
icon: 'cn-icon cn-icon-tag1',
title: this.$t('entity.topTags'),
topColumn: 'tag',
totalCount: 0,
data: []
data: [],
loading: false,
firstLoad: true
}
],
listData: [],
@@ -396,16 +411,17 @@ export default {
mode: mode,
range: this.timeFilter.dateRangeValue,
pageNo: this.pageObj.pageNo,
pageSize: this.pageObj.pageSize
pageSize: this.pageObj.pageSize,
showList: true
}
})
this.showList = true
// this.showList = true
// 跳转页面,则不执行搜索功能
return true
}
this.queryFilterNew({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryList({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryFilterNew({ q: this.q, ...this.pageObj, ...this.timeFilter })
this.queryCount({ q: this.q, ...this.pageObj, ...this.timeFilter })
// 延时一秒避免初始化时pageSize为20pageNo为1也会调用“搜索”的情况
@@ -489,8 +505,8 @@ export default {
/** 新版查询filter数据 */
queryFilterNew (params) {
const queryParams = {
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
startTime: getSecond(params.startTime),
endTime: getSecond(params.endTime),
resource: params.q || ''
}
this.loadingLeft = true
@@ -502,8 +518,48 @@ export default {
const aggDomain = axios.get(api.entity.entityList.aggDomain, { params: queryParams })
const aggAppCategory = axios.get(api.entity.entityList.aggAppCategory, { params: queryParams })
const aggTag = axios.get(api.entity.entityList.aggTag, { params: queryParams })
Promise.all([aggCountry, aggCity, aggIPAsn, aggIPIsp, aggPort, aggDomain, aggAppCategory, aggTag]).then(response => {
const requests = [aggCountry, aggCity, aggIPAsn, aggIPIsp, aggPort, aggDomain, aggAppCategory, aggTag]
requests.forEach((req, index) => {
this.newFilterData[index].loading = true
req.then(response => {
if (response.status === 200 && response.data.data.list) {
if (response.data.data.list.length >= 5) {
this.newFilterData[index].showNum = 5
} else {
this.newFilterData[index].showNum = response.data.data.list.length
}
this.newFilterData[index].data = []
response.data.data.list.forEach((item, i) => {
let obj = {
label: item.value,
topColumn: this.newFilterData[index].topColumn,
value: item.uniqueEntities,
showNum: 5
}
if (index === 0) {
obj.flag = item.value // 接口字段名称为'China'svg名称为'CN'通过countryNameIdMapping进行转换
}
if (index === 4) {
obj = {
topColumn: this.newFilterData[index].topColumn,
topColumn1: this.newFilterData[index].topColumn1,
port: item.port,
l7Protocol: item.l7Protocol,
value: item.uniqueEntities,
showNum: 5
}
}
this.newFilterData[index].data.push(obj)
})
}
}).catch(e => {
this.$message.error(e.response.data.message)
}).finally(() => {
this.newFilterData[index].loading = false
this.newFilterData[index].firstLoad = false
})
})
/*Promise.all([aggCountry, aggCity, aggIPAsn, aggIPIsp, aggPort, aggDomain, aggAppCategory, aggTag]).then(response => {
response.forEach((item1, index) => {
if (item1.status === 200 && item1.data.data.list) {
if (item1.data.data.list.length >= 5) {
@@ -540,7 +596,7 @@ export default {
this.$message.error(e.response.data.message)
}).finally(() => {
this.loadingLeft = false
})
})*/
},
/** 实体列表查询 */
queryList (params) {
@@ -548,8 +604,8 @@ export default {
const queryParams = {
pageSize: params.pageSize,
pageNo: params.pageNo,
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
startTime: getSecond(params.startTime),
endTime: getSecond(params.endTime),
resource: params.q || ''
}
axios.get(api.entity.entityList.list, { params: queryParams }).then(response => {
@@ -570,8 +626,8 @@ export default {
queryCount (params) {
this.loadingCount = true
const queryParams = {
// startTime: getSecond(params.startTime),
// endTime: getSecond(params.endTime),
startTime: getSecond(params.startTime),
endTime: getSecond(params.endTime),
resource: params.q || ''
}
axios.get(api.entity.entityList.summaryCount, { params: queryParams }).then(response => {
@@ -692,23 +748,25 @@ export default {
}
},
mounted () {
this.getEntityIndexData()
let { q, listMode } = this.$route.query
if (!this.showList) {
this.getEntityIndexData()
} else {
let { q, listMode } = this.$route.query
// 如果地址栏有listMode即列表页并非首页则开始搜索
if (listMode) {
this.showList = true
// %位置为0是输入中文时能解码%2025%分别是空格和%的情况
if (q && (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1)) {
q = decodeURI(q)
// 如果地址栏有listMode即列表页并非首页则开始搜索
if (listMode) {
// %位置为0是输入中文时能解码%2025%分别是空格和%的情况
if (q && (q.indexOf('%') === 0 || q.indexOf('%20') > -1 || q.indexOf('%25') > -1)) {
q = decodeURI(q)
}
// %位置不为0即内容包含非英文时
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
if (q && q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
q = decodeURI(q)
}
this.initSearch(q)
this.listMode = listMode
}
// %位置不为0即内容包含非英文时
const str1 = q.substring(q.indexOf('%'), q.indexOf('%') + 3)
if (q && q.indexOf('%') > 0 && (str1 !== '%20' || str1 === '%25')) {
q = decodeURI(q)
}
this.initSearch(q)
this.listMode = listMode
}
},
watch: {
@@ -722,11 +780,12 @@ export default {
const rangeParam = query.range
const startTimeParam = query.startTime
const endTimeParam = query.endTime
const showList = ref(Boolean(query.showList))
// 若url携带了使用携带的值否则使用默认值。
const dateRangeValue = rangeParam ? parseInt(query.range) : 60
const dateRangeValue = rangeParam ? parseInt(query.range) : 60 * 24
const timeFilter = ref({ dateRangeValue })
if (!startTimeParam || !endTimeParam) {
const { startTime, endTime } = getNowTime(60)
const { startTime, endTime } = getNowTime(60 * 24)
timeFilter.value.startTime = startTime
timeFilter.value.endTime = endTime
} else {
@@ -737,12 +796,13 @@ export default {
pageNo: query.pageNo ? parseInt(query.pageNo) : 1,
// 是否重置pageNo在执行新搜索时是true
resetPageNo: true,
pageSize: query.pageSize ? parseInt(query.pageSize) : defaultPageSize,
pageSize: query.pageSize ? parseInt(query.pageSize) : 10, // TODO 23-10-14 默认暂时改为10
total: 0
})
return {
timeFilter,
pageObj
pageObj,
showList
}
},
beforeUnmount () {

View File

@@ -2,51 +2,49 @@
<div class="entity-filter-case" style="position: relative">
<div class="filter-case__header">{{ $t('entities.filter1') }}</div>
<div v-if="filterDataLength>0">
<div v-if="filterDataLength > 0">
<div class="entity-filter" v-for="(item, index) in myFilterData" :key="index">
<div v-if="item.data.length>0">
<div class="filter__header">
<i :class="item.icon"></i>
{{ item.title }}
</div>
<div class="filter__body" style="position: relative">
<loading :loading="loadingLeft" style="top: -5px;"></loading>
<div class="filter__body-item"
v-for="(data, i) in item.data.slice(0, item.showNum)"
:key="i"
@click="filter(data.label, data)">
<div class="filter__body-item-left">
<div v-if="data.flag">
<img v-if="data.flag===countryNameIdMapping.Unknown || !countryNameIdMapping[data.flag]" src="../../../public/images/flag/Unknown.svg" class="filter-country-flag">
<img v-else :src="require(`../../../public/images/flag/${countryNameIdMapping[data.flag]}.png`)" class="filter-country-flag"/>
</div>
<div v-else class="filter__body-item-left-index">{{ i+1 }}</div>
<div class="filter__body-item-left-label">
<el-tooltip :content="data.label" placement="top" effect="light" :disabled="disabledLabel">
<span @mouseenter="handleMouse(`filter${index}${i}`)" :id="`filter${index}${i}`">
<span v-if="item.topColumn==='ip.port'">
{{ data.port }}/{{ data.l7Protocol }}
</span>
<span v-else>{{ data.label }}</span>
</span>
</el-tooltip>
</div>
</div>
<div class="filter__body-item-right">{{ data.value }}</div>
</div>
</div>
<div @click="showMoreFilter(item, index)"
:class="item.showNum === item.data.length ? 'filter-no-show-more' : 'filter-show-more'">
{{ $t('entity.showMore') }}
</div>
<div class="filter-hr"></div>
<div class="filter__header">
<i :class="item.icon"></i>
{{ item.title }}
</div>
<div class="filter__body" style="position: relative">
<loading :loading="item.loading" style="top: -5px;"></loading>
<div class="filter__body-item"
v-for="(data, i) in item.data.slice(0, item.showNum)"
:key="i"
@click="filter(data.label, data)">
<div class="filter__body-item-left">
<div v-if="data.flag">
<img v-if="data.flag===countryNameIdMapping.Unknown || !countryNameIdMapping[data.flag]" src="../../../public/images/flag/Unknown.svg" class="filter-country-flag">
<img v-else :src="require(`../../../public/images/flag/${countryNameIdMapping[data.flag]}.png`)" class="filter-country-flag"/>
</div>
<div v-else class="filter__body-item-left-index">{{ i+1 }}</div>
<div class="filter__body-item-left-label">
<el-tooltip :content="data.label" placement="top" effect="light" :disabled="disabledLabel">
<span @mouseenter="handleMouse(`filter${index}${i}`)" :id="`filter${index}${i}`">
<span v-if="item.topColumn==='ip.port'">
{{ data.port }}/{{ data.l7Protocol }}
</span>
<span v-else>{{ data.label }}</span>
</span>
</el-tooltip>
</div>
</div>
<div class="filter__body-item-right">{{ data.value }}</div>
</div>
</div>
<div @click="showMoreFilter(item, index)"
:class="item.showNum >= item.data.length || item.data.length <= 5 ? 'filter-no-show-more' : 'filter-show-more'">
{{ $t('entity.showMore') }}
</div>
<div class="filter-hr"></div>
</div>
</div>
<loading v-else-if="isFirstLoad" :loading="isFirstLoad"></loading>
<chart-no-data v-else style="padding-top: 40px"></chart-no-data>
</div>
</template>
@@ -54,6 +52,7 @@
import Loading from '@/components/common/Loading'
import ChartNoData from '@/views/charts/charts/ChartNoData'
import { countryNameIdMapping } from '@/utils/constants'
import _ from 'lodash'
export default {
name: 'EntityFilter',
components: { ChartNoData, Loading },
@@ -73,6 +72,9 @@ export default {
})
return length
},
isFirstLoad () {
return this.myFilterData.some(d => d.firstLoad)
}
},
data () {
@@ -85,6 +87,14 @@ export default {
mounted () {
this.myFilterData = this.filterData
},
watch: {
filterData: {
deep: true,
handler (n) {
this.myFilterData = _.cloneDeep(n)
}
}
},
methods: {
/**
* 判断文字是否溢出超出则鼠标移入tooltip显示否则鼠标移入不显示
@@ -103,17 +113,14 @@ export default {
this.$emit('filter', name, data)
},
showMoreFilter (item, index) {
if ((item.data.length - item.showNum) >= 5) {
this.myFilterData[index].showNum = item.data.length
/*if ((item.data.length - item.showNum) >= 5) {
item.shouNum += 5
this.myFilterData[index].showNum += 5
} else {
this.myFilterData[index].showNum += (item.data.length - item.showNum)
}
}*/
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@@ -38,11 +38,13 @@
<Pagination
ref="pagination"
:page-obj="pageObj"
:post-page-sizes="[10, 20, 50]"
@pageNo='pageNo'
@pageSize='pageSize'
@size-change="pageSize"
@prev-click="prev"
@next-click="next"
@scrollbarToTop="scrollbarToTop"
>
</Pagination>
</div>
@@ -124,6 +126,10 @@ export default {
const container = document.getElementById('cnContainer')
container.scrollTop += e.deltaY / 2
}
},
scrollbarToTop () {
const container = document.getElementById('cnContainer')
container.scrollTop = 0
}
},
mounted () {