diff --git a/src/assets/css/theme.scss b/src/assets/css/theme.scss index b5ac78fd..b1dbd0d1 100644 --- a/src/assets/css/theme.scss +++ b/src/assets/css/theme.scss @@ -13,16 +13,32 @@ $--menu-hover-background-color: #000C18; // menu背景色 $--menu-item-font-color: #BEBEBE; // menu字色 $--menu-item-hover-fill: $--color-primary; // menu鼠标悬浮、激活时背景色 +$--collapse-header-height: 42px; +$--collapse-border-color: #EFF2F5; + /** 自定义变量 **/ :root { + /* 自适应变量 */ + @media only screen and (min-width : 10px) { + --chart-height-unit: 25px; // chart的单元高度 + --entity-width: calc(50% - 5px); // entity列表每个entity框的宽度 + --entity-height: 190px; // entity列表每个entity框的高度 + } @media only screen and (min-width : 1224px) { --chart-height-unit: 30px; } + @media only screen and (min-width : 1560px) { + --entity-width: calc(33.3% - 7px); + } @media only screen and (min-width : 1824px) { --chart-height-unit: 40px; + --entity-width: calc(33.3% - 7px); + --entity-height: 210px; } @media only screen and (min-width : 2424px) { --chart-height-unit: 55px; + --entity-width: calc(25% - 8px); + --entity-height: 240px; } } @@ -32,7 +48,7 @@ $--border-radius-primary: 2px; $--right-box-border-color: #E7EAED; /* 按钮 */ -$--button-border-radius: $--border-color-primary; // 按钮圆角 +$--button-border-radius: $--border-radius-primary; // 按钮圆角 $--button-primary-color: #FFF; // 普通按钮字色 $--button-primary-background-color: $--color-primary; // 普通按钮背景色 diff --git a/src/components/charts/EchartsFrame.vue b/src/components/charts/EchartsFrame.vue index 58865b4c..d4b06d26 100644 --- a/src/components/charts/EchartsFrame.vue +++ b/src/components/charts/EchartsFrame.vue @@ -1,5 +1,5 @@ - diff --git a/src/components/charts/panel.scss b/src/components/charts/panel.scss index 5e1b04a7..2c0583bc 100644 --- a/src/components/charts/panel.scss +++ b/src/components/charts/panel.scss @@ -25,6 +25,10 @@ height: 100%; width: 100%; } + &>.cn-chart.cn-chart__echarts--statistics { + border: none; + box-shadow: none; + } &>.cn-chart__echarts, &>.cn-chart__table, &>.cn-chart__map { display: flex; flex-direction: column; diff --git a/src/components/entities/EntityList.vue b/src/components/entities/EntityList.vue new file mode 100644 index 00000000..6af9f128 --- /dev/null +++ b/src/components/entities/EntityList.vue @@ -0,0 +1,83 @@ + + + + + diff --git a/src/components/entities/LeftFilter.vue b/src/components/entities/LeftFilter.vue index 50d9bcf5..92be8f58 100644 --- a/src/components/entities/LeftFilter.vue +++ b/src/components/entities/LeftFilter.vue @@ -2,19 +2,28 @@
{{$t('entities.filter')}}
- + @@ -34,8 +43,10 @@ export default { active: ['1'] } }, - mounted () { - console.info(this.filterData) + methods: { + nodeClick (data, node) { + this.$emit('select', data) + } } } @@ -47,6 +58,7 @@ export default { width: 100%; height: 100%; border: 1px solid $--right-box-border-color; + overflow: auto; .filter__header { background-color: #FAFAFA; @@ -59,12 +71,28 @@ export default { .filter__body { display: flex; flex-direction: column; + height: 100%; + overflow: auto; .filter__collapse { - max-height: 50%; - .el-collapse-item__header { padding-left: 10px; + padding-top: 8px; + font-weight: bold; + + &.is-active { + border-bottom: 1px solid $--right-box-border-color; + } + } + .el-collapse-item__wrap { + padding-top: 6px !important; + + .filter-item { + display: flex; + justify-content: space-between; + padding-right: 6px; + width: 100%; + } } } } diff --git a/src/components/entities/entities.scss b/src/components/entities/entities.scss index adcb8d33..e383b92a 100644 --- a/src/components/entities/entities.scss +++ b/src/components/entities/entities.scss @@ -7,4 +7,83 @@ height: 100%; width: 100%; background-color: #fff; -} \ No newline at end of file + + .entity-list { + height: 100%; + width: 100%; + + .entity-list__content { + display: grid; + grid-template-columns: repeat(auto-fill, var(--entity-width)); + grid-auto-flow: row; + grid-auto-rows: var(--entity-height); + grid-gap: 10px; + height: calc(100% - 67px); + width: 100%; + overflow: auto; + + .cn-entity { + display: grid; + grid-template-rows: 44% 56%; + border: 1px solid $--right-box-border-color; + + .cn-entity__header { + display: flex; + align-items: center; + padding: 20px; + + .header__icon { + display: flex; + justify-content: center; + justify-items: center; + align-items: center; + width: 52px; + height: 52px; + border-radius: 50%; + + i { + font-size: 20px; + } + } + .header__content { + display: flex; + flex-direction: column; + justify-content: center; + padding-left: 10px; + + .content__title { + font-size: 22px; + color: #333333; + font-weight: bold; + } + .content__desc { + font-size: 14px; + color: #666666; + padding-top: 3px; + + .desc__label { + color: #aaa; + padding-right: 10px; + } + } + } + } + .cn-entity__body { + background-color: lightgoldenrodyellow; + } + } + } + .entity-list__pagination { + height: 67px; + } + } +} +.ip-green { + color: #23BF9A; +} +.domain-blue { + color: #0290FF; +} +.app-orange { + color: #FFA200; +} diff --git a/src/utils/api.js b/src/utils/api.js index 82be99c9..3bea93df 100644 --- a/src/utils/api.js +++ b/src/utils/api.js @@ -16,7 +16,10 @@ export const api = { chart: '/visual/chart', entityIpFilter: '/interface/entity/ip/filter', entityDomainFilter: '/interface/entity/domain/filter', - entityAppFilter: '/interface/entity/app/filter' + entityAppFilter: '/interface/entity/app/filter', + entityIpList: '/interface/entity/ip/list', + entityDomainList: '/interface/entity/domain/list', + entityAppList: '/interface/entity/app/list' } /* panel */ export async function getPanelList (params) { @@ -44,6 +47,18 @@ export async function getEntityDomainFilterList (params) { export async function getEntityAppFilterList (params) { return await getData(api.entityAppFilter, params, true) } +/* ip类型entity列表 */ +export async function getEntityIpList (params) { + return await getData(api.entityIpList, params, true) +} +/* domain类型entity列表 */ +export async function getEntityDomainList (params) { + return await getData(api.entityDomainList, params, true) +} +/* app类型entity列表 */ +export async function getEntityAppList (params) { + return await getData(api.entityAppList, params, true) +} /* 字典 */ export async function getDictList (params) { return await getData(api.dict, params, true) diff --git a/src/views/entities/EntityExplorer.vue b/src/views/entities/EntityExplorer.vue index bfd1a0f9..957ad18d 100644 --- a/src/views/entities/EntityExplorer.vue +++ b/src/views/entities/EntityExplorer.vue @@ -21,9 +21,13 @@ -
+
@@ -32,17 +36,32 @@ import { entityType } from '@/utils/constants' import { ref } from 'vue' import LeftFilter from '@/components/entities/LeftFilter' -import { getEntityIpFilterList, getEntityDomainFilterList, getEntityAppFilterList } from '@/utils/api' +import EntityList from '@/components/entities/EntityList' +import { getEntityIpFilterList, getEntityDomainFilterList, getEntityAppFilterList, getEntityIpList, getEntityDomainList, getEntityAppList } from '@/utils/api' export default { name: 'EntityExplorer', data () { return { searchContent: '', - filterData: [] + filterData: [], + pageObjLeftTop: { + pageNo: 1, + pageSize: 10 + }, + pageObjLeftBottom: { + pageNo: 1, + pageSize: 10 + }, + pageObjRight: { + pageNo: 1, + pageSize: 50 + }, + listData: [] } }, components: { - LeftFilter + LeftFilter, + EntityList }, methods: { async loadData (filterType, conditionGroup) { @@ -63,32 +82,60 @@ export default { default: break } return data + }, + select (data) { + + }, + getEntityData (param) { + } }, watch: { filterType (n) { + this.pageObjLeftTop = { + pageNo: 1, + pageSize: 10 + } + this.pageObjLeftBottom = { + pageNo: 1, + pageSize: 10 + } + this.pageObjRight = { + pageNo: 1, + pageSize: 50 + } + this.listData = [] const requests = [] const data = [] switch (n) { case 'ip': { requests.push(this.loadData(n, 'country')) requests.push(this.loadData(n, 'asn')) - data.push({ title: this.$t('entities.countryOrRegion'), key: 'country' }) - data.push({ title: this.$t('entities.systemNumber'), key: 'asn' }) + data.push({ title: this.$t('entities.countryOrRegion'), key: 'country', childrenKey: 'region', load }) + data.push({ title: this.$t('entities.systemNumber'), key: 'asn', load }) + getEntityIpList({ ...this.pageObjRight }).then(res => { + this.listData = res + }) break } case 'domain': { requests.push(this.loadData(n, 'group')) requests.push(this.loadData(n, 'level')) - data.push({ title: this.$t('entities.group'), key: 'group' }) - data.push({ title: this.$t('entities.level'), key: 'level' }) + data.push({ title: this.$t('entities.group'), key: 'group', childrenKey: 'name', load }) + data.push({ title: this.$t('entities.level'), key: 'level', load }) + getEntityDomainList({ ...this.pageObjRight }).then(res => { + this.listData = res + }) break } case 'app': { requests.push(this.loadData(n, 'category')) requests.push(this.loadData(n, 'risk')) - data.push({ title: this.$t('entities.category'), key: 'category' }) - data.push({ title: this.$t('entities.risk'), key: 'risk' }) + data.push({ title: this.$t('entities.category'), key: 'category', childrenKey: 'subcategory', load }) + data.push({ title: this.$t('entities.risk'), key: 'risk', load }) + getEntityAppList({ ...this.pageObjRight }).then(res => { + this.listData = res + }) break } default: break @@ -104,15 +151,21 @@ export default { async mounted () { const country = await this.loadData(this.filterType, 'country') const asn = await this.loadData(this.filterType, 'asn') + this.listData = await getEntityIpList({ ...this.pageObjRight }) this.filterData.push({ title: this.$t('entities.countryOrRegion'), key: 'country', - data: country + childrenKey: 'region', + data: country, + filterType: this.filterType, + load }) this.filterData.push({ title: this.$t('entities.systemNumber'), key: 'asn', - data: asn + data: asn, + filterType: this.filterType, + load }) }, setup () { @@ -123,6 +176,36 @@ export default { } } } +const load = (node, resolve, filterType, key) => { + if (node.level === 0) { + resolve(node.data) + } else { + let req = null + switch (filterType) { + case 'ip': { + req = getEntityIpFilterList({ type: key }) + break + } + case 'domain': { + req = getEntityDomainFilterList({ type: key }) + break + } + case 'app': { + req = getEntityAppFilterList({ type: key }) + break + } + default: break + } + if (req !== null) { + req.then(res => { + res = res.map(r => { + return { ...r, leaf: true } + }) + resolve(res) + }) + } + } +}