CN-268 feat: panel重构--entity详情、ip基础信息等(部分)
This commit is contained in:
@@ -86,9 +86,6 @@
|
||||
font-weight:normal;
|
||||
}
|
||||
|
||||
.cn-panel-crypto {
|
||||
grid-template-columns: repeat(36, 1fr) !important;
|
||||
}
|
||||
.cn-chart:not(.cn-chart__group):not(.cn-chart__block) {
|
||||
&>.cn-chart__body {
|
||||
height: 100%;
|
||||
@@ -672,7 +669,7 @@
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
&>div {
|
||||
/*&>div {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(30, 1fr);
|
||||
grid-auto-flow: row;
|
||||
@@ -701,7 +698,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
.el-overlay {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
@import 'components/advancedSearch/advanced-search';
|
||||
@import './components/charts/panel';
|
||||
// @import './components/charts/panel';
|
||||
@import 'components/common/TimeRange/date-time-range';
|
||||
@import 'components/common/TimeRange/time-refresh';
|
||||
@import './components/common/pagination';
|
||||
|
||||
@@ -58,118 +58,36 @@
|
||||
color: #333;
|
||||
padding: 0;
|
||||
}
|
||||
/*&:hover {
|
||||
background-color: $--chart-title-hover-background-color;
|
||||
|
||||
.chart-header__tools {
|
||||
.chart-header__tool .tool__icon {
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
.chart-header__title {
|
||||
max-width: calc(100% - 100px);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
&.chart-header__title--block {
|
||||
color: #1890FF;
|
||||
font-weight: bold;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
.chart-header__tools {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.header__operation-btn {
|
||||
margin-left: 12px;
|
||||
cursor: pointer;
|
||||
color: #999;
|
||||
}
|
||||
.chart-header__tool {
|
||||
margin-left: 20px;
|
||||
cursor: pointer;
|
||||
|
||||
.tool__icon {
|
||||
// visibility: hidden;
|
||||
font-size: 14px;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
.nz-chart-dropdown {
|
||||
position: absolute;
|
||||
top: 44px;
|
||||
right: 0;
|
||||
left: unset;
|
||||
transform-origin: center top;
|
||||
z-index: 1000;
|
||||
width: 180px;
|
||||
li {
|
||||
padding-left: 15px !important;
|
||||
padding-right: 0 !important;
|
||||
width: calc(100% - 15px);
|
||||
text-align: left;
|
||||
i {
|
||||
margin-right: 10px;
|
||||
}
|
||||
&:hover i {
|
||||
color: $--color-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.chart-header-error{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -1px;
|
||||
}
|
||||
}
|
||||
.chart-screen-header.list-page{
|
||||
background: $--background-color-empty;
|
||||
}
|
||||
.chart-screen-header {
|
||||
display: flex;
|
||||
justify-content:space-between;
|
||||
align-items:center;
|
||||
padding: 0 20px 0 20px;
|
||||
height: 39px;
|
||||
font-size: 14px;
|
||||
line-height: 40px;
|
||||
color: $--color-text-primary;
|
||||
transition: all 0.2s;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
margin-top: 15px;
|
||||
&.chart-header--float {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
z-index: 100;
|
||||
box-sizing: border-box;
|
||||
height: 10px;
|
||||
opacity: 0;
|
||||
transition: all linear .2s;
|
||||
|
||||
&:hover {
|
||||
height: 39px;
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.chart-header__title {
|
||||
max-width: calc(100% - 100px);
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
font-size: 18px;
|
||||
color: #333;
|
||||
font-weight: 700;
|
||||
}
|
||||
.chart-header__tools {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.chart-header__tool {
|
||||
margin-left: 20px;
|
||||
cursor: pointer;
|
||||
|
||||
.tool__icon {
|
||||
visibility: hidden;
|
||||
font-size: 14px;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
.nz-chart-dropdown {
|
||||
.cn-chart-dropdown {
|
||||
position: absolute;
|
||||
top: 44px;
|
||||
right: 0;
|
||||
@@ -208,7 +126,8 @@
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
.cn-chart__single-value.cn-chart__single-value--detail-overview.cn-chart__single-value--icon-left {
|
||||
&>.cn-chart__single-value {
|
||||
&.cn-chart__single-value--detail-overview.cn-chart__single-value--icon-left {
|
||||
.single-value__icon {
|
||||
width: 38px;
|
||||
height: 38px;
|
||||
@@ -226,7 +145,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.cn-chart__single-value.cn-chart__single-value--icon-left {
|
||||
&.cn-chart__single-value--icon-left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
@@ -287,7 +206,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.cn-chart__single-value.cn-chart__single-value--icon-right {
|
||||
&.cn-chart__single-value--icon-right {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-around;
|
||||
@@ -337,7 +256,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.cn-chart__single-value.cn-chart__single-value--icon-right--color {
|
||||
&.cn-chart__single-value--icon-right--color {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
justify-content: space-around;
|
||||
@@ -406,7 +325,7 @@
|
||||
|
||||
}
|
||||
}
|
||||
.cn-chart__single-value.cn-chart__single-value--chart {
|
||||
&.cn-chart__single-value--chart {
|
||||
display: flex;
|
||||
padding: 13px 20px;
|
||||
height: 100%;
|
||||
@@ -438,43 +357,11 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-table-pagination.el-pagination {
|
||||
padding: 12px 0 9px 0;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.el-pagination__jump {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&>.cn-chart__whois>.cn-chart__body {
|
||||
&>.cn-chart__whois {
|
||||
overflow: auto;
|
||||
}
|
||||
&>.cn-chart__echarts, &>.cn-chart__table, &>.cn-chart__map, &>.cn-chart__group, &>.cn-chart__block, &>.cn-chart__whois, &>.cn-chart__dns-record, &>.cn-chart__app-basic {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.cn-chart__header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-shrink: 0;
|
||||
padding: 10px 20px 10px 18px;
|
||||
height: 47px;
|
||||
|
||||
.cn-chart__title {
|
||||
font-size: 16px;
|
||||
color: #333333;
|
||||
font-weight: bold;
|
||||
}
|
||||
.header__operations {
|
||||
color: #999;
|
||||
}
|
||||
}
|
||||
.cn-chart__body {
|
||||
flex: auto;
|
||||
&>.cn-chart__ip-basic {
|
||||
display: flex;
|
||||
.el-descriptions {
|
||||
padding-top: 30px;
|
||||
@@ -493,19 +380,8 @@
|
||||
color: #3976CB;
|
||||
}
|
||||
}
|
||||
}
|
||||
&>.cn-chart__block {
|
||||
&>.cn-chart__header {
|
||||
height: 60px;
|
||||
border-bottom: none !important;
|
||||
}
|
||||
&>.cn-chart__body {
|
||||
display: grid !important;
|
||||
grid-template-columns: repeat(30, 1fr);
|
||||
grid-auto-flow: row;
|
||||
grid-gap: 10px;
|
||||
padding: 0 20px;
|
||||
&>.cn-chart {
|
||||
.cn-chart {
|
||||
border: 1px solid #E7EAED;
|
||||
}
|
||||
/* detail页面block下的五连图的标题样式改变 */
|
||||
@@ -520,21 +396,6 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.cn-chart__group {
|
||||
.cn-chart__header {
|
||||
border-bottom: 1px solid $--content-right-background-color;
|
||||
}
|
||||
&>.cn-chart__body {
|
||||
display: grid !important;
|
||||
grid-gap: 10px;
|
||||
padding: 0 20px;
|
||||
.cn-chart {
|
||||
border: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
&>.cn-chart__title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -772,7 +633,7 @@
|
||||
padding: 10px 30px 30px;
|
||||
}
|
||||
}
|
||||
.pie-table {
|
||||
&>.pie-table {
|
||||
font-size: 14px;
|
||||
color: #333333;
|
||||
font-weight: 500;
|
||||
@@ -798,6 +659,18 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.chart-table-pagination.el-pagination {
|
||||
padding: 12px 0 9px 0;
|
||||
text-align: center;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
|
||||
.el-pagination__jump {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.chart__legend {
|
||||
width: calc(100% - 40px);
|
||||
border: 1px solid #E7EAED;
|
||||
@@ -875,3 +748,87 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.entity-detail-tool {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin: 10px 20px 10px 0;
|
||||
padding: 0 20px;
|
||||
height: 60px;
|
||||
background-color: #FFFFFF;
|
||||
box-shadow: 0 1px 2px 0 rgba(0,0,0,0.06);
|
||||
border-radius: 2px;
|
||||
|
||||
.cn-icon-arrow-left-circle {
|
||||
color: $--color-primary;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.chart-error-popper{
|
||||
word-wrap:break-word;
|
||||
word-break:break-word;
|
||||
border: 1px solid #e02f44;
|
||||
min-width: 180px !important;
|
||||
max-width: 280px !important;
|
||||
}
|
||||
|
||||
.chart-error-popper.el-popper.is-light {
|
||||
background: #e02f44;
|
||||
border: 1px solid #e02f44;
|
||||
}
|
||||
.chart-error-popper.el-popover.el-popper {
|
||||
color:white;
|
||||
}
|
||||
|
||||
.chart-error-popper.el-popper.is-light[data-popper-placement^='top'] .el-popper__arrow::before {
|
||||
border-color: #e02f44;
|
||||
background: #e02f44;
|
||||
bottom:0px;
|
||||
}
|
||||
|
||||
.chart-error-popper.el-popper.is-light[data-popper-placement^='bottom'] .el-popper__arrow::before {
|
||||
border-color: #e02f44;
|
||||
background: #e02f44;
|
||||
}
|
||||
|
||||
.chart-info-corner {
|
||||
color: #767980;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
display: none;
|
||||
left: 0;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
z-index: 2;
|
||||
top: 0;
|
||||
}
|
||||
.chart-info-corner--error {
|
||||
display: block;
|
||||
color: #fff;
|
||||
}
|
||||
.chart-info-corner--error .chart-info-corner-inner {
|
||||
border-left: 28px solid #e02f44;
|
||||
border-right: none;
|
||||
border-bottom: 28px solid rgba(0,0,0,0);
|
||||
}
|
||||
.chart-info-corner-inner {
|
||||
width: 0;
|
||||
height: 0;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.chart-info-corner .fa {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 6px;
|
||||
font-size: 65%;
|
||||
z-index: 3;
|
||||
font-style: normal;
|
||||
}
|
||||
.cn-chart-icon-warning:before {
|
||||
content: "!";
|
||||
font-weight:normal;
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
height: calc(100% - 28px);
|
||||
flex: 1;
|
||||
padding: 10px;
|
||||
overflow: auto;
|
||||
background-color: $--content-right-background-color;
|
||||
|
||||
&>.cn-entity-detail .entity-detail__body>.cn-panel {
|
||||
|
||||
@@ -16,7 +16,15 @@ const locale = require('element-plus/lib/locale')
|
||||
const debounce = require('lodash/debounce')
|
||||
const ElScrollbar = require('element-plus/lib/el-scrollbar')
|
||||
const union = require('lodash/union')
|
||||
|
||||
const utc = require('dayjs/plugin/utc')
|
||||
const timezone = require('dayjs/plugin/timezone')
|
||||
const advancedFormat = require('dayjs/plugin/advancedFormat')
|
||||
const weekday = require('dayjs/plugin/weekday')
|
||||
dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(advancedFormat)
|
||||
dayjs.extend(weekday)
|
||||
window.$dayJs = dayjs
|
||||
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { default: e } }
|
||||
|
||||
const dayjs__default = /* #__PURE__ */_interopDefaultLegacy(window.$dayJs)
|
||||
|
||||
@@ -19,15 +19,17 @@ import timezone from 'dayjs/plugin/timezone'
|
||||
import advancedFormat from 'dayjs/plugin/advancedFormat'
|
||||
import weekday from 'dayjs/plugin/weekday'
|
||||
|
||||
import DateTimeRange from '@/components/common/TimeRange/DateTimeRange'
|
||||
import TimeRefresh from '@/components/common/TimeRange/TimeRefresh'
|
||||
import PanelChartList from '@/views/charts/PanelChartList'
|
||||
|
||||
const _ = require('lodash') // lodash工具
|
||||
|
||||
dayjs.extend(utc)
|
||||
/*dayjs.extend(utc)
|
||||
dayjs.extend(timezone)
|
||||
dayjs.extend(advancedFormat)
|
||||
dayjs.extend(weekday)
|
||||
window.$dayJs = dayjs
|
||||
window.$dayJs = dayjs*/
|
||||
|
||||
const app = createApp(App)
|
||||
|
||||
@@ -47,6 +49,8 @@ app.config.globalProperties.$_ = _
|
||||
|
||||
app.mixin(commonMixin)
|
||||
|
||||
app.component('date-time-range', DateTimeRange)
|
||||
app.component('time-refresh', TimeRefresh)
|
||||
app.component('panel-chart-list', PanelChartList)
|
||||
|
||||
app.mount('#app')
|
||||
|
||||
@@ -8,7 +8,7 @@ import { storageKey } from '@/utils/constants'
|
||||
import { loadI18n } from '@/i18n'
|
||||
|
||||
const loginWhiteList = ['/login', '/'] // 免登陆白名单
|
||||
const permissionWhiteList = [...loginWhiteList] // 权限白名单
|
||||
const permissionWhiteList = [...loginWhiteList, '/entityDetail'] // 权限白名单
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
// 加载iso-3166-2资源
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div class="cn-chart">
|
||||
<loading :loading="loading && !isTabs"></loading>
|
||||
<loading :loading="loading && !isTabs && !isBlock"></loading>
|
||||
<chart-no-data v-if="isNoData"></chart-no-data>
|
||||
<template v-else>
|
||||
|
||||
@@ -8,13 +8,15 @@
|
||||
v-if="isTabs"
|
||||
:chart-info="chartInfo"
|
||||
:query-params="queryParams"
|
||||
:entity="entity"
|
||||
></chart-tabs>
|
||||
|
||||
<chart-map
|
||||
v-else-if="isMap"
|
||||
v-else-if="isMap && !isIpBasicInfo"
|
||||
:chart-info="chartInfo"
|
||||
:chart-data="chartData"
|
||||
:query-params="queryParams"
|
||||
:entity="entity"
|
||||
@showLoading="showLoading"
|
||||
></chart-map>
|
||||
|
||||
@@ -26,7 +28,19 @@
|
||||
@showLoading="showLoading"
|
||||
></chart-single-value>
|
||||
|
||||
<div v-else style="height: 100%; width: 100%; background-color: lightcyan;"></div>
|
||||
<chart-block
|
||||
v-else-if="isBlock"
|
||||
:chart-info="chartInfo"
|
||||
:chart-data="chartData"
|
||||
:entity="entity"
|
||||
></chart-block>
|
||||
|
||||
<ip-basic-info
|
||||
v-else-if="isIpBasicInfo"
|
||||
:chart-info="chartInfo"
|
||||
:chart-data="chartData"
|
||||
:entity="entity"
|
||||
></ip-basic-info>
|
||||
|
||||
<chart-echart-line
|
||||
v-else-if="isEchartsLine"
|
||||
@@ -47,6 +61,8 @@ import ChartNoData from './charts/ChartNoData'
|
||||
import ChartTabs from './charts/ChartTabs'
|
||||
import ChartMap from './charts/ChartMap'
|
||||
import ChartSingleValue from './charts/ChartSingleValue'
|
||||
import ChartBlock from './charts/ChartBlock'
|
||||
import IpBasicInfo from '@/views/charts/charts/IpBasicInfo'
|
||||
import ChartEchartLine from './charts/ChartEchartLine'
|
||||
import {
|
||||
isEcharts,
|
||||
@@ -84,12 +100,14 @@ import _ from 'lodash'
|
||||
export default {
|
||||
name: 'chart',
|
||||
components: {
|
||||
IpBasicInfo,
|
||||
ChartSingleValue,
|
||||
Loading,
|
||||
ChartNoData,
|
||||
ChartTabs,
|
||||
ChartMap,
|
||||
ChartEchartLine
|
||||
ChartEchartLine,
|
||||
ChartBlock
|
||||
},
|
||||
props: {
|
||||
chartInfo: Object,
|
||||
@@ -100,6 +118,7 @@ export default {
|
||||
isFullscreen: Boolean,
|
||||
loading: Boolean,
|
||||
panelLock: Boolean,
|
||||
entity: Object,
|
||||
isError: Boolean
|
||||
},
|
||||
computed: {
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
<template>
|
||||
<div class="chart-header" :class="{'chart-header--title-chart': isTitle}">
|
||||
<div class="chart-header__title">{{chartInfo.name}}</div>
|
||||
<div class="chart-header__title" :class="{'chart-header__title--block': isBlock}">{{chartInfo.name}}</div>
|
||||
<chart-error :isError="isError" :errorInfo="errorInfo"></chart-error>
|
||||
<div class="chart-header__tools" v-if="!isTitle && !isTabs">
|
||||
<div class="panel__time" v-if="chartInfo.params && chartInfo.params.showTimeTool">
|
||||
<date-time-range class="date-time-range" :start-time="chartTimeFilter.startTime" :end-time="chartTimeFilter.endTime" ref="dateTimeRange" @change="reload"/>
|
||||
<time-refresh class="date-time-range" @change="timeRefreshChange" :end-time="chartTimeFilter.endTime"/>
|
||||
</div>
|
||||
<template v-else-if="!isBlock">
|
||||
<el-popover trigger="click" placement="top" :content="chartInfo.remark" v-if="chartInfo.remark">
|
||||
<template #reference>
|
||||
<span class="header__operation-btn"><i class="cn-icon el-icon-info"></i></span>
|
||||
</template>
|
||||
</el-popover>
|
||||
<span class="header__operation-btn" @click="refresh"><i class="cn-icon cn-icon-refresh"></i></span>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { isTitle, isTabs } from './charts/tools'
|
||||
import { isTitle, isTabs, isBlock } from './charts/tools'
|
||||
import ChartError from '@/components/charts/ChartError'
|
||||
import { getNowTime } from '@/utils/date-util'
|
||||
import { ref } from 'vue'
|
||||
|
||||
export default {
|
||||
name: 'ChartHeader',
|
||||
props: {
|
||||
@@ -41,11 +50,29 @@ export default {
|
||||
methods: {
|
||||
refresh () {
|
||||
this.$emit('refresh')
|
||||
},
|
||||
timeRefreshChange () {
|
||||
if (!this.$refs.dateTimeRange.isCustom) {
|
||||
const value = this.chartTimeFilter.dateRangeValue
|
||||
this.$refs.dateTimeRange.quickChange(value)
|
||||
}
|
||||
},
|
||||
reload (s, e, v) {
|
||||
this.dateTimeRangeChange(s, e, v)
|
||||
},
|
||||
dateTimeRangeChange (s, e, v) {
|
||||
this.chartTimeFilter = { startTime: s, endTime: e, dateRangeValue: v }
|
||||
}
|
||||
},
|
||||
setup (props) {
|
||||
const dateRangeValue = 60
|
||||
const { startTime, endTime } = getNowTime(dateRangeValue)
|
||||
// entity详情内的chart时间工具不是公共的,需要单独定义
|
||||
const chartTimeFilter = ref({ startTime, endTime, dateRangeValue })
|
||||
return {
|
||||
chartTimeFilter,
|
||||
isTitle: isTitle(props.chartInfo.type),
|
||||
isBlock: isBlock(props.chartInfo.type),
|
||||
isTabs: isTabs(props.chartInfo.type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
<div style="padding: 10px 10px 20px 10px; overflow: auto" v-if="!isEntityDetail">
|
||||
<div id="cn-panel" class="cn-panel2">
|
||||
<div class="panel__time">
|
||||
<DateTimeRange class="date-time-range" :start-time="timeFilter.startTime" :end-time="timeFilter.endTime" ref="dateTimeRange" @change="reload"/>
|
||||
<TimeRefresh class="date-time-range" @change="timeRefreshChange" :end-time="timeFilter.endTime"/>
|
||||
<date-time-range class="date-time-range" :start-time="timeFilter.startTime" :end-time="timeFilter.endTime" ref="dateTimeRange" @change="reload"/>
|
||||
<time-refresh class="date-time-range" @change="timeRefreshChange" :end-time="timeFilter.endTime"/>
|
||||
</div>
|
||||
<panel-chart-list
|
||||
:time-filter="timeFilter"
|
||||
@@ -11,46 +11,26 @@
|
||||
:panel-lock="panelLock"
|
||||
>
|
||||
</panel-chart-list>
|
||||
<!-- <chart
|
||||
v-for="chart in chartList"
|
||||
:key="chart.id"
|
||||
:chart="chart"
|
||||
:time-filter="timeFilter"
|
||||
:ref="`chart-${chart.id}`"
|
||||
:entity="entity"
|
||||
@getCurrentTimeRange="getCurrentTimeRange"
|
||||
></chart>-->
|
||||
<!-- <grid-layout v-model:layout="chartList"
|
||||
:col-num="12"
|
||||
:row-height="30"
|
||||
:is-draggable="draggable"
|
||||
:is-resizable="resizable"
|
||||
:vertical-compact="compact"
|
||||
:use-css-transforms="true"
|
||||
>
|
||||
<grid-item v-for="item in chartList" :key="item.i"
|
||||
:x="item.x"
|
||||
:y="item.y"
|
||||
:w="item.w"
|
||||
:h="item.h"
|
||||
:i="item.i"
|
||||
>
|
||||
<span class="text">{{ item.i }}</span>
|
||||
</grid-item>
|
||||
</grid-layout>-->
|
||||
</div>
|
||||
</div>
|
||||
<div class="cn-entity-detail" id="cn-entity-detail" v-else>
|
||||
<div class="entity-detail__body">
|
||||
<div class="cn-panel" @scroll="scroll" id="cn-panel">
|
||||
<template v-for="chart in chartList" :key="chart.id">
|
||||
<!-- <chart
|
||||
<div class="cn-panel2" id="cn-panel">
|
||||
<panel-chart-list
|
||||
:time-filter="timeFilter"
|
||||
:data-list="chartList"
|
||||
:panel-lock="panelLock"
|
||||
:entity="entity"
|
||||
>
|
||||
</panel-chart-list>
|
||||
<!-- <template v-for="chart in chartList" :key="chart.id">
|
||||
<chart
|
||||
:chart="chart"
|
||||
:ref="`chart-${chart.id}`"
|
||||
:entity="entity"
|
||||
@getCurrentTimeRange="getCurrentTimeRange"
|
||||
></chart>-->
|
||||
</template>
|
||||
></chart>
|
||||
</template>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -62,8 +42,6 @@ import { ref } from 'vue'
|
||||
import { panelTypeAndRouteMapping } from '@/utils/constants'
|
||||
import { api, getPanelList, getChartList } from '@/utils/api'
|
||||
import { getNowTime } from '@/utils/date-util'
|
||||
import DateTimeRange from '@/components/common/TimeRange/DateTimeRange'
|
||||
import TimeRefresh from '@/components/common/TimeRange/TimeRefresh'
|
||||
|
||||
export default {
|
||||
name: 'Panel',
|
||||
@@ -72,10 +50,6 @@ export default {
|
||||
isEntityDetail: Boolean,
|
||||
typeName: String
|
||||
},
|
||||
components: {
|
||||
DateTimeRange,
|
||||
TimeRefresh
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
chartList: [], // 普通panel的chart
|
||||
@@ -125,16 +99,13 @@ export default {
|
||||
this.chartList = allCharts
|
||||
setTimeout(() => {
|
||||
this.$emit('chartLoaded', allCharts)
|
||||
})
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
changeTab ({ index }) {
|
||||
this.currentTab = this.detailTabs[index].id + ''
|
||||
this.detailChartList = this.detailTabs[index].children
|
||||
},
|
||||
scroll (e) {
|
||||
this.$emit('scroll', { top: e.target.scrollTop })
|
||||
},
|
||||
recursionParamsConvert (chart) {
|
||||
chart.params = chart.params ? JSON.parse(chart.params) : null
|
||||
if (!this.$_.isEmpty(chart.children)) {
|
||||
@@ -165,9 +136,6 @@ export default {
|
||||
this.chartList.forEach(chart => {
|
||||
this.$refs[`chart-${chart.id}`] && this.$refs[`chart-${chart.id}`].reloadChart()
|
||||
})
|
||||
},
|
||||
jumpToTop (top) {
|
||||
document.querySelector('#cn-panel').scrollTop = top
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
<template>
|
||||
<!-- chart外层箱子 -->
|
||||
<div :class="{'panel-chart--fullscreen': isFullscreen, 'panel-chart--title-chart': isTitle}" class="panel-chart" :id="isFullscreen ? ('chart-screen-' + chartInfo.id ) : ('chart-local-' + chartInfo.id)">
|
||||
<div
|
||||
:class="{'panel-chart--fullscreen': isFullscreen, 'panel-chart--title-chart': isTitle}"
|
||||
class="panel-chart"
|
||||
:id="isFullscreen ? ('chart-screen-' + chartInfo.id ) : ('chart-local-' + chartInfo.id)"
|
||||
>
|
||||
<!-- title和工具栏,支持浮动 -->
|
||||
<chart-header
|
||||
v-if="!isFullscreen && showHeader && !isSingleValue"
|
||||
v-if="!isFullscreen && showHeader && !isSingleValue && !isTabs"
|
||||
:is-error="isError"
|
||||
:error-info="errorInfo"
|
||||
:chart-data="chartData"
|
||||
@@ -16,7 +20,7 @@
|
||||
<!-- 数据查询后传入chart组件,chart组件内不查询,只根据接传递的数据来渲染 -->
|
||||
<chart
|
||||
ref="chart"
|
||||
v-if="(!isGroup || !chartInfo.param.collapse) && !isTitle"
|
||||
v-if="(!isGroup || !(chartInfo.params && chartInfo.params.collapse)) && !isTitle"
|
||||
:chart-data="chartData"
|
||||
:result-type="resultType"
|
||||
:chart-info="chartInfo"
|
||||
@@ -24,6 +28,7 @@
|
||||
:panel-lock="panelLock"
|
||||
:is-error="isError"
|
||||
:loading="loading"
|
||||
:entity="entity"
|
||||
:is-fullscreen="isFullscreen"
|
||||
@showLoading="showLoading"
|
||||
></chart>
|
||||
@@ -80,6 +85,7 @@ export default {
|
||||
timeFilter: Object, // 时间范围
|
||||
isFullscreen: Boolean,
|
||||
panelLock: Boolean,
|
||||
entity: Object,
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
|
||||
@@ -25,12 +25,14 @@
|
||||
:isResizable = "item.type === 'group' ? false: null"
|
||||
dragAllowFrom=".chart-header"
|
||||
dragIgnoreFrom=".chart-header__tools"
|
||||
v-bind="anchorPoint(item)"
|
||||
>
|
||||
<panel-chart
|
||||
:ref="'chart' + item.id"
|
||||
:chart-info="item"
|
||||
:show-header="true"
|
||||
:time-filter="timeFilter"
|
||||
:entity="entity"
|
||||
@showFullscreen="showFullscreen"
|
||||
></panel-chart>
|
||||
</grid-item>
|
||||
@@ -64,6 +66,7 @@
|
||||
import PanelChart from '@/views/charts/PanelChart'
|
||||
import VueGridLayout from 'vue-grid-layout'
|
||||
import { getTypeCategory } from './charts/tools'
|
||||
import _ from 'lodash'
|
||||
|
||||
export default {
|
||||
name: 'PanelChartList',
|
||||
@@ -76,6 +79,7 @@ export default {
|
||||
timeFilter: Object, // 时间范围
|
||||
panelLock: { type: Boolean, default: true },
|
||||
isGroup: Boolean,
|
||||
entity: Object,
|
||||
dataList: Array // 看板中所有图表信息
|
||||
},
|
||||
data () {
|
||||
@@ -109,9 +113,21 @@ export default {
|
||||
this.fullscreen.visible = show
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
anchorPoint () {
|
||||
return function (chart) {
|
||||
if (!_.isEmpty(chart.params && chart.params.anchorPoint)) {
|
||||
return { 'anchor-point': chart.params.anchorPoint }
|
||||
} else {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
dataList: {
|
||||
deep: true,
|
||||
immediate: true,
|
||||
handler (n, o) {
|
||||
this.gridLayoutShow = false
|
||||
this.gridLayoutLoading = true
|
||||
|
||||
23
src/views/charts/charts/ChartBlock.vue
Normal file
23
src/views/charts/charts/ChartBlock.vue
Normal file
@@ -0,0 +1,23 @@
|
||||
<template>
|
||||
<panel-chart-list
|
||||
:time-filter="timeFilter"
|
||||
:data-list="chartInfo.children"
|
||||
:panel-lock="true"
|
||||
:entity="entity"
|
||||
>
|
||||
</panel-chart-list>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chartMixin from '@/views/charts/charts/chart-mixin'
|
||||
|
||||
export default {
|
||||
name: 'ChartBlock',
|
||||
mixins: [chartMixin],
|
||||
props: {
|
||||
timeFilter: Object
|
||||
},
|
||||
mounted () {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -4,21 +4,16 @@
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as L from 'leaflet'
|
||||
import * as am4Core from '@amcharts/amcharts4/core'
|
||||
import * as am4Maps from '@amcharts/amcharts4/maps'
|
||||
import { getGeoData, replaceUrlPlaceholder } from '@/utils/tools'
|
||||
import { storageKey } from '@/utils/constants'
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
import icon from 'leaflet/dist/images/marker-icon.png'
|
||||
import iconShadow from 'leaflet/dist/images/marker-shadow.png'
|
||||
import {
|
||||
isIpBasicInfo,
|
||||
isMapBlock
|
||||
} from './tools'
|
||||
|
||||
import { isMapBlock } from './tools'
|
||||
import unitConvert, { valueToRangeValue } from '@/utils/unit-convert'
|
||||
import { HeatLegend } from '@/components/amcharts/heatLegend'
|
||||
import { getData } from '@/utils/api'
|
||||
import chartMixin from './chart-mixin'
|
||||
|
||||
export default {
|
||||
name: 'ChartMap',
|
||||
@@ -31,46 +26,9 @@ export default {
|
||||
countrySeries: null // 下钻国家series
|
||||
}
|
||||
},
|
||||
props: {
|
||||
chartInfo: Object,
|
||||
chartData: [Array, Object],
|
||||
queryParams: Object
|
||||
},
|
||||
mixins: [chartMixin],
|
||||
methods: {
|
||||
initMap (id) {
|
||||
if (this.isIpBasicInfo) {
|
||||
L.Marker.prototype.options.icon = L.icon({
|
||||
iconUrl: icon,
|
||||
shadowUrl: iconShadow
|
||||
})
|
||||
const map = L.map(`chart${this.chartInfo.id}`, {
|
||||
minZoom: 3,
|
||||
maxZoom: 7,
|
||||
zoom: 5,
|
||||
attributionControl: false,
|
||||
zoomControl: false,
|
||||
maxBounds: L.latLngBounds(L.latLng(-90, -180), L.latLng(90, 180))
|
||||
})
|
||||
L.tileLayer(
|
||||
this.mapPictureUrl,
|
||||
{ noWrap: true }
|
||||
).addTo(map)
|
||||
|
||||
const attribution = L.control.attribution({ position: 'bottomright', prefix: '' })
|
||||
attribution.addAttribution(' © OpenStreetMap contributors')
|
||||
attribution.addTo(map)
|
||||
|
||||
/* L.control.zoom({
|
||||
position: 'bottomright',
|
||||
zoomInText: '<i class="nz-icon nz-icon-enlarge"></i>',
|
||||
zoomOutText: '<i class="nz-icon nz-icon-narrow"></i>',
|
||||
zoomInTitle: '',
|
||||
zoomOutTitle: ''
|
||||
}).addTo(map) */
|
||||
|
||||
this.myChart = map
|
||||
this.loadLeafletMap()
|
||||
} else {
|
||||
const chart = am4Core.create(id, am4Maps.MapChart)
|
||||
chart.geodata = getGeoData(storageKey.iso36112WorldLow)
|
||||
chart.projection = new am4Maps.projections.Miller()
|
||||
@@ -112,7 +70,6 @@ export default {
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
loadAm4ChartMap (polygonSeries, country, chartData) {
|
||||
if (chartData) {
|
||||
@@ -196,20 +153,6 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
loadLeafletMap () {
|
||||
this.$emit('showLoading', true)
|
||||
try {
|
||||
this.myChart.setView([this.chartData.latitude, this.chartData.longitude], 5)
|
||||
const myIcon = L.divIcon({
|
||||
className: 'cn-icon cn-icon-position2 position-icon'
|
||||
})
|
||||
L.marker([this.chartData.latitude, this.chartData.longitude], { icon: myIcon }).addTo(this.myChart)
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
this.$emit('showLoading', false)
|
||||
}, 200)
|
||||
}
|
||||
},
|
||||
mapBack () {
|
||||
this.countrySeries.hide()
|
||||
this.$nextTick(() => {
|
||||
@@ -264,7 +207,6 @@ export default {
|
||||
},
|
||||
setup (props) {
|
||||
return {
|
||||
isIpBasicInfo: isIpBasicInfo(props.chartInfo.type),
|
||||
isMapBlock: isMapBlock(props.chartInfo.type)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
:time-filter="timeFilter"
|
||||
:data-list="tab.children"
|
||||
:panel-lock="true"
|
||||
:entity="entity"
|
||||
>
|
||||
</panel-chart-list>
|
||||
</el-tab-pane>
|
||||
@@ -23,14 +24,10 @@
|
||||
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import chartMixin from '@/views/charts/charts/chart-mixin'
|
||||
|
||||
export default {
|
||||
name: 'ChartTabs',
|
||||
props: {
|
||||
chartInfo: Object,
|
||||
chartData: [Object, Array, String], // 数据在父组件查询后传入,本组件内不查询,只根据接传递的数据来渲染
|
||||
queryParams: Object // 接口请求参数
|
||||
},
|
||||
computed: {
|
||||
timeFilter () {
|
||||
return {
|
||||
@@ -39,6 +36,7 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
mixins: [chartMixin],
|
||||
methods: {
|
||||
showFullscreen () {
|
||||
|
||||
|
||||
115
src/views/charts/charts/IpBasicInfo.vue
Normal file
115
src/views/charts/charts/IpBasicInfo.vue
Normal file
@@ -0,0 +1,115 @@
|
||||
<template>
|
||||
<div class="cn-chart__ip-basic">
|
||||
<el-descriptions :column="1">
|
||||
<el-descriptions-item label="ASN:">{{(chartData && chartData.asn) || '-'}}</el-descriptions-item>
|
||||
<el-descriptions-item label="AS Org:">{{(chartData && chartData.asOrganization) || '-'}}</el-descriptions-item>
|
||||
<el-descriptions-item :label="$t('entities.asSubnet') + ':'">{{(chartData && chartData.asSubnet) || '-'}}</el-descriptions-item>
|
||||
<el-descriptions-item label="ISP:">{{(chartData && chartData.isp) || '-'}}</el-descriptions-item>
|
||||
<el-descriptions-item label="DNS PTR:">{{(chartData && chartData.dnsPtr) || '-'}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="chart-location">
|
||||
<el-descriptions :column="1">
|
||||
<el-descriptions-item :label="$t('overall.location') + ':'">{{location}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="chart-drawing" style="padding: 0 36px 30px 0;" :id="`chart${chartInfo.id}`"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import chartMixin from '@/views/charts/charts/chart-mixin'
|
||||
import * as L from 'leaflet'
|
||||
import icon from 'leaflet/dist/images/marker-icon.png'
|
||||
import iconShadow from 'leaflet/dist/images/marker-shadow.png'
|
||||
import 'leaflet/dist/leaflet.css'
|
||||
|
||||
export default {
|
||||
name: 'IpBasicInfo',
|
||||
mixins: [chartMixin],
|
||||
data () {
|
||||
return {
|
||||
myChart: null,
|
||||
mapPictureUrl: '/Tiles/{z}/{x}/{y}.png'
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
location () {
|
||||
let location = ''
|
||||
if (this.chartInfo) {
|
||||
if (this.chartInfo.country) {
|
||||
location = this.chartInfo.country
|
||||
if (this.chartInfo.province) {
|
||||
location += ', '
|
||||
location += this.chartInfo.province
|
||||
if (this.chartInfo.city) {
|
||||
location += ', '
|
||||
location += this.chartInfo.city
|
||||
}
|
||||
}
|
||||
} else if (this.chartInfo.province) {
|
||||
location = this.chartInfo.province
|
||||
if (this.chartInfo.city) {
|
||||
location += ', '
|
||||
location += this.chartInfo.city
|
||||
}
|
||||
} else if (this.chartInfo.city) {
|
||||
location = this.chartInfo.city
|
||||
}
|
||||
}
|
||||
return location
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initMap (id) {
|
||||
L.Marker.prototype.options.icon = L.icon({
|
||||
iconUrl: icon,
|
||||
shadowUrl: iconShadow
|
||||
})
|
||||
const map = L.map(`chart${this.chartInfo.id}`, {
|
||||
minZoom: 3,
|
||||
maxZoom: 7,
|
||||
zoom: 5,
|
||||
attributionControl: false,
|
||||
zoomControl: false,
|
||||
maxBounds: L.latLngBounds(L.latLng(-90, -180), L.latLng(90, 180))
|
||||
})
|
||||
L.tileLayer(
|
||||
this.mapPictureUrl,
|
||||
{ noWrap: true }
|
||||
).addTo(map)
|
||||
|
||||
const attribution = L.control.attribution({ position: 'bottomright', prefix: '' })
|
||||
attribution.addAttribution(' © OpenStreetMap contributors')
|
||||
attribution.addTo(map)
|
||||
|
||||
/* L.control.zoom({
|
||||
position: 'bottomright',
|
||||
zoomInText: '<i class="nz-icon nz-icon-enlarge"></i>',
|
||||
zoomOutText: '<i class="nz-icon nz-icon-narrow"></i>',
|
||||
zoomInTitle: '',
|
||||
zoomOutTitle: ''
|
||||
}).addTo(map) */
|
||||
|
||||
this.myChart = map
|
||||
this.loadLeafletMap()
|
||||
},
|
||||
loadLeafletMap () {
|
||||
if (this.chartData.latitude && this.chartData.longitude) {
|
||||
this.myChart.setView([this.chartData.latitude, this.chartData.longitude], 5)
|
||||
const myIcon = L.divIcon({
|
||||
className: 'cn-icon cn-icon-position2 position-icon'
|
||||
})
|
||||
L.marker([this.chartData.latitude, this.chartData.longitude], { icon: myIcon }).addTo(this.myChart)
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
chartData: {
|
||||
deep: true,
|
||||
handler (n) {
|
||||
this.initMap(`chart${this.chartInfo.id}`)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
8
src/views/charts/charts/chart-mixin.js
Normal file
8
src/views/charts/charts/chart-mixin.js
Normal file
@@ -0,0 +1,8 @@
|
||||
export default {
|
||||
props: {
|
||||
chartInfo: Object,
|
||||
chartData: [Object, Array, String], // 数据在父组件查询后传入,本组件内不查询,只根据接传递的数据来渲染
|
||||
entity: Object,
|
||||
queryParams: Object // 接口请求参数
|
||||
}
|
||||
}
|
||||
@@ -12,13 +12,12 @@
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
<div class="entity-detail__content">
|
||||
<div class="entity-detail__content" @scroll="scroll" id="detailWrapper">
|
||||
<cn-panel
|
||||
ref="cnPanel"
|
||||
:entity="entityData"
|
||||
:is-entity-detail="true"
|
||||
@chartLoaded="chartLoaded"
|
||||
@scroll="scroll"
|
||||
></cn-panel>
|
||||
</div>
|
||||
</main>
|
||||
@@ -75,16 +74,16 @@ export default {
|
||||
chartLoaded (chartList) {
|
||||
this.anchorPoints = []
|
||||
let anchorPoints = []
|
||||
const panelDom = document.querySelector('#cn-panel')
|
||||
const panelDom = document.querySelector('#detailWrapper')
|
||||
this.scrollHeight = panelDom.scrollHeight
|
||||
this.clientHeight = panelDom.clientHeight
|
||||
chartList.forEach(chart => {
|
||||
if (chart.params.anchorPoint) {
|
||||
const dom = document.querySelector(`#${chart.params.anchorPoint}`)
|
||||
anchorPoints.push({
|
||||
const dom = document.querySelector(`[anchor-point='${chart.params.anchorPoint}']`)
|
||||
dom && anchorPoints.push({
|
||||
id: chart.params.anchorPoint,
|
||||
label: chart.i18n ? this.$t(chart.i18n) : chart.name,
|
||||
top: dom.offsetTop/* ,
|
||||
top: dom.offsetTop + 10/* ,
|
||||
height: document.querySelector(`#${chart.params.anchorPoint}}`).scrollHeight */
|
||||
})
|
||||
}
|
||||
@@ -98,12 +97,12 @@ export default {
|
||||
}
|
||||
this.anchorPoints = anchorPoints
|
||||
},
|
||||
scroll ({ top }) {
|
||||
this.top = top || 0
|
||||
scroll (e) {
|
||||
this.top = (e.target.scrollTop + 10) || 0
|
||||
},
|
||||
jumpToAnchor (anchor) {
|
||||
this.top = anchor.top
|
||||
this.$refs.cnPanel.jumpToTop(anchor.top)
|
||||
document.querySelector('#detailWrapper').scrollTop = this.top
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -133,7 +132,7 @@ export default {
|
||||
},
|
||||
currentAnchor () {
|
||||
let currentAnchor = null
|
||||
if (this.top + this.clientHeight === this.scrollHeight) {
|
||||
if (this.top + this.clientHeight - 10 === this.scrollHeight) {
|
||||
currentAnchor = this.anchorPoints[this.anchorPoints.length - 1]
|
||||
} else {
|
||||
this.anchorPoints.forEach(anchor => {
|
||||
|
||||
Reference in New Issue
Block a user