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';
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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">
|
||||
<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>
|
||||
<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,88 +26,50 @@ 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()
|
||||
this.myChart = chart
|
||||
const polygonSeries = chart.series.push(new am4Maps.MapPolygonSeries())
|
||||
polygonSeries.useGeodata = true
|
||||
polygonSeries.exclude = ['AQ'] // 排除南极洲
|
||||
polygonSeries.tooltip.getFillFromObject = false
|
||||
polygonSeries.tooltip.background.fill = am4Core.color('#FFFFFF')
|
||||
this.polygonSeries = polygonSeries
|
||||
const polygonTemplate = polygonSeries.mapPolygons.template
|
||||
polygonTemplate.tooltipHTML = this.generateTooltipHTML()
|
||||
polygonTemplate.nonScalingStroke = true
|
||||
polygonTemplate.strokeWidth = 0.5
|
||||
polygonTemplate.fill = am4Core.color('rgba(176,196,222,.5)')
|
||||
this.loadAm4ChartMap(this.polygonSeries)
|
||||
// 地图点击事件
|
||||
polygonTemplate.events.on('hit', async ev => {
|
||||
const countryId = ev.target.dataItem.dataContext.id
|
||||
if (countryId) {
|
||||
ev.target.series.chart.zoomToMapObject(ev.target)
|
||||
ev.target.isHover = false
|
||||
this.countrySeries = chart.series.push(new am4Maps.MapPolygonSeries())
|
||||
this.countrySeries.tooltip.getFillFromObject = false
|
||||
this.countrySeries.tooltip.background.fill = am4Core.color('#FFFFFF')
|
||||
const countryTemplate = this.countrySeries.mapPolygons.template
|
||||
countryTemplate.tooltipHTML = this.generateTooltipHTML()
|
||||
countryTemplate.nonScalingStroke = true
|
||||
countryTemplate.strokeWidth = 0.5
|
||||
countryTemplate.fill = am4Core.color('rgba(176,196,222,.5)')
|
||||
const geoData = getGeoData(countryId)
|
||||
if (geoData) {
|
||||
this.countrySeries.geodata = geoData
|
||||
this.polygonSeries.hide()
|
||||
const country = ev.target.dataItem.dataContext.serverCountry
|
||||
const queryParams = { ...this.queryParams, country }
|
||||
const chartData = await getData(replaceUrlPlaceholder(this.chartInfo.params.url, queryParams))
|
||||
this.loadAm4ChartMap(this.countrySeries, ev.target.dataItem.dataContext.serverCountry, chartData)
|
||||
}
|
||||
const chart = am4Core.create(id, am4Maps.MapChart)
|
||||
chart.geodata = getGeoData(storageKey.iso36112WorldLow)
|
||||
chart.projection = new am4Maps.projections.Miller()
|
||||
this.myChart = chart
|
||||
const polygonSeries = chart.series.push(new am4Maps.MapPolygonSeries())
|
||||
polygonSeries.useGeodata = true
|
||||
polygonSeries.exclude = ['AQ'] // 排除南极洲
|
||||
polygonSeries.tooltip.getFillFromObject = false
|
||||
polygonSeries.tooltip.background.fill = am4Core.color('#FFFFFF')
|
||||
this.polygonSeries = polygonSeries
|
||||
const polygonTemplate = polygonSeries.mapPolygons.template
|
||||
polygonTemplate.tooltipHTML = this.generateTooltipHTML()
|
||||
polygonTemplate.nonScalingStroke = true
|
||||
polygonTemplate.strokeWidth = 0.5
|
||||
polygonTemplate.fill = am4Core.color('rgba(176,196,222,.5)')
|
||||
this.loadAm4ChartMap(this.polygonSeries)
|
||||
// 地图点击事件
|
||||
polygonTemplate.events.on('hit', async ev => {
|
||||
const countryId = ev.target.dataItem.dataContext.id
|
||||
if (countryId) {
|
||||
ev.target.series.chart.zoomToMapObject(ev.target)
|
||||
ev.target.isHover = false
|
||||
this.countrySeries = chart.series.push(new am4Maps.MapPolygonSeries())
|
||||
this.countrySeries.tooltip.getFillFromObject = false
|
||||
this.countrySeries.tooltip.background.fill = am4Core.color('#FFFFFF')
|
||||
const countryTemplate = this.countrySeries.mapPolygons.template
|
||||
countryTemplate.tooltipHTML = this.generateTooltipHTML()
|
||||
countryTemplate.nonScalingStroke = true
|
||||
countryTemplate.strokeWidth = 0.5
|
||||
countryTemplate.fill = am4Core.color('rgba(176,196,222,.5)')
|
||||
const geoData = getGeoData(countryId)
|
||||
if (geoData) {
|
||||
this.countrySeries.geodata = geoData
|
||||
this.polygonSeries.hide()
|
||||
const country = ev.target.dataItem.dataContext.serverCountry
|
||||
const queryParams = { ...this.queryParams, country }
|
||||
const chartData = await getData(replaceUrlPlaceholder(this.chartInfo.params.url, queryParams))
|
||||
this.loadAm4ChartMap(this.countrySeries, ev.target.dataItem.dataContext.serverCountry, chartData)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
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