CN-268 feat: panel重构

This commit is contained in:
chenjinsong
2022-01-16 23:16:00 +08:00
parent 53cba023e7
commit d86d18a2ee
68 changed files with 3852 additions and 185 deletions

View File

@@ -22,10 +22,10 @@
<template v-for="chart in tab.children">
<chart
v-if="activeTab == tab.id"
:key="chart.id"
:chart="chart"
:key="Chart.id"
:chart="Chart"
:time-filter="timeFilter"
:ref="`chart-${chart.id}`"
:ref="`chart-${Chart.id}`"
:entity="entity"
@getCurrentTimeRange="getCurrentTimeRange"
></chart>
@@ -367,11 +367,11 @@
</div>
</div>
<div class="cn-chart__body">
<template v-for="chart in chartInfo.children" :key="chart.id">
<template v-for="chart in chartInfo.children" :key="Chart.id">
<chart
:chart="chart"
:chart="Chart"
:time-filter="timeFilter"
:ref="`chart-${chart.id}`"
:ref="`chart-${Chart.id}`"
:entity="entity"
:parent-data="groupData"
:from-block="fromBlock"
@@ -402,11 +402,11 @@
</div>
</div>
<div class="cn-chart__body">
<template v-for="chart in chartInfo.children" :key="chart.id">
<template v-for="chart in chartInfo.children" :key="Chart.id">
<chart
:chart="chart"
:chart="Chart"
:time-filter="chartTimeFilter"
:ref="`chart-${chart.id}`"
:ref="`chart-${Chart.id}`"
:entity="entity"
:parent-data="groupData"
:from-block="true"

132
src/views/charts/Chart2.vue Normal file
View File

@@ -0,0 +1,132 @@
<template>
<div class="cn-chart">
<loading :loading="loading"></loading>
<chart-no-data v-if="!loading && (isNoData || isError)"></chart-no-data>
<template v-else>
<chart-tabs
v-if="isTabs"
:chart-info="chartInfo"
:query-params="queryParams"
></chart-tabs>
<chart-map
v-else-if="isMap"
:chart-info="chartInfo"
:chart-data="chartData"
:query-params="queryParams"
@showLoading="showLoading"
></chart-map>
</template>
</div>
</template>
<script>
import Loading from '@/components/common/Loading'
import ChartNoData from './charts/ChartNoData'
import ChartTabs from './charts/ChartTabs'
import ChartMap from './charts/ChartMap'
import {
isEcharts,
isSingleValue,
isTable,
isActiveIpTable,
isTitle,
isMap,
getOption,
isEchartsWithTable,
isEchartsWithStatistics,
isEchartsTimeBar,
isEchartsCategoryBar,
isMapLine,
isMapBlock,
isSingleValueWithEcharts,
isSingleValueWithEchartsTemp,
isRelationShip,
isTabs,
isGroup,
isSankey,
isIpBasicInfo,
isIpOpenPort,
isIpHostedDomain,
isDomainWhois,
isDomainDnsRecord,
isCryptocurrencyEventList,
isAppBasicInfo,
isAppRelatedDomain,
isBlock
} from './charts/tools'
import _ from 'lodash'
export default {
name: 'chart',
components: {
Loading,
ChartNoData,
ChartTabs,
ChartMap
},
props: {
chartInfo: Object,
chartData: [Object, Array, String], // 数据在父组件查询后传入,本组件内不查询,只根据接传递的数据来渲染
queryParams: Object, // 接口请求参数
customChartOption: Object, // 需要自定义echarts的option时传入非必须传入该值时仍需传对应格式的chartData
isFullscreen: Boolean,
loading: Boolean,
panelLock: Boolean,
isError: Boolean
},
computed: {
isNoData () {
return _.isEmpty(this.chartData)
},
chartOption () {
if (this.customChartOption) {
return _.cloneDeep(this.customChartOption)
} else {
return getOption(this.chartInfo.type)
}
}
},
methods: {
resize () {
this.$refs['chart' + this.chartInfo.id] && this.$refs['chart' + this.chartInfo.id].resize()
},
showLoading (show) {
this.$emit('showLoading', show)
}
},
setup (props) {
return {
isEcharts: isEcharts(props.chartInfo.type),
isEchartsTimeBar: isEchartsTimeBar(props.chartInfo.type),
isEchartsCategoryBar: isEchartsCategoryBar(props.chartInfo.type),
isEchartsWithTable: isEchartsWithTable(props.chartInfo.type),
isEchartsWithStatistics: isEchartsWithStatistics(props.chartInfo.type),
isSingleValue: isSingleValue(props.chartInfo.type),
isSingleValueWithEcharts: isSingleValueWithEcharts(props.chartInfo.type),
isSingleValueWithEchartsTemp: isSingleValueWithEchartsTemp(props.chartInfo.type),
isRelationShip: isRelationShip(props.chartInfo.type),
isTable: isTable(props.chartInfo.type),
isActiveIpTable: isActiveIpTable(props.chartInfo.type),
isMap: isMap(props.chartInfo.type),
isTitle: isTitle(props.chartInfo.type),
isMapLine: isMapLine(props.chartInfo.type),
isMapBlock: isMapBlock(props.chartInfo.type),
isTabs: isTabs(props.chartInfo.type),
isGroup: isGroup(props.chartInfo.type),
isBlock: isBlock(props.chartInfo.type),
isSankey: isSankey(props.chartInfo.type),
isIpBasicInfo: isIpBasicInfo(props.chartInfo.type),
isIpHostedDomain: isIpHostedDomain(props.chartInfo.type),
isIpOpenPort: isIpOpenPort(props.chartInfo.type),
isDomainWhois: isDomainWhois(props.chartInfo.type),
isDomainDnsRecord: isDomainDnsRecord(props.chartInfo.type),
isCryptocurrencyEventList: isCryptocurrencyEventList(props.chartInfo.type),
isAppBasicInfo: isAppBasicInfo(props.chartInfo.type),
isAppRelatedDomain: isAppRelatedDomain(props.chartInfo.type)
}
}
}
</script>

View File

@@ -0,0 +1,52 @@
<template>
<div class="chart-header" :class="{'chart-header--title-chart': isTitle}">
<div class="chart-header__title">{{chartInfo.name}}</div>
<chart-error :isError="isError" :errorInfo="errorInfo"></chart-error>
<div class="chart-header__tools" v-if="!isTitle">
<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>
</div>
</template>
<script>
import { isTitle } from './charts/tools'
import ChartError from '@/components/charts/ChartError'
export default {
name: 'ChartHeader',
props: {
chartInfo: Object,
errorInfo: {
type: String,
default: ''
},
isError: {
type: Boolean,
default: false
}
},
components: {
ChartError
},
data () {
return {
dropdownMenuShow: false,
errorText: ''
}
},
methods: {
refresh () {
this.$emit('refresh')
}
},
setup (props) {
return {
isTitle: isTitle(props.chartInfo.type)
}
}
}
</script>

View File

@@ -1,13 +1,17 @@
<template>
<div style="padding: 10px 0 20px 20px; overflow: auto" v-if="!isEntityDetail">
<div id="cn-panel"
:class="(isCryptocurrency)?'cn-panel cn-panel-crypto':'cn-panel'"
>
<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"/>
</div>
<chart
<panel-chart-list
:time-filter="timeFilter"
:data-list="chartList"
:panel-lock="panelLock"
>
</panel-chart-list>
<!-- <chart
v-for="chart in chartList"
:key="chart.id"
:chart="chart"
@@ -15,7 +19,7 @@
:ref="`chart-${chart.id}`"
:entity="entity"
@getCurrentTimeRange="getCurrentTimeRange"
></chart>
></chart>-->
<!-- <grid-layout v-model:layout="chartList"
:col-num="12"
:row-height="30"
@@ -40,12 +44,12 @@
<div class="entity-detail__body">
<div class="cn-panel" @scroll="scroll" id="cn-panel">
<template v-for="chart in chartList" :key="chart.id">
<chart
<!-- <chart
:chart="chart"
:ref="`chart-${chart.id}`"
:entity="entity"
@getCurrentTimeRange="getCurrentTimeRange"
></chart>
></chart>-->
</template>
</div>
</div>
@@ -58,7 +62,7 @@ import { ref } from 'vue'
import { panelTypeAndRouteMapping } from '@/utils/constants'
import { api, getPanelList, getChartList } from '@/utils/api'
import { getNowTime } from '@/utils/date-util'
import Chart from './Chart'
import PanelChartList from './PanelChartList'
import DateTimeRange from '@/components/common/TimeRange/DateTimeRange'
import TimeRefresh from '@/components/common/TimeRange/TimeRefresh'
@@ -70,13 +74,14 @@ export default {
typeName: String
},
components: {
Chart,
DateTimeRange,
TimeRefresh
TimeRefresh,
PanelChartList
},
data () {
return {
chartList: [], // 普通panel的chart
panelLock: true,
// entity详情的chart
detailTabs: [],
detailChartList: [],

View File

@@ -0,0 +1,309 @@
<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)">
<!-- title和工具栏支持浮动 -->
<chart-header
v-if="!isFullscreen && showHeader"
:is-error="isError"
:error-info="errorInfo"
:chart-data="chartData"
:chart-info="chartInfo"
@loadMore="loadMore"
@refresh="refresh"
@showFullscreen="showFullscreen"
></chart-header>
<!-- chart -->
<!-- 数据查询后传入chart组件chart组件内不查询只根据接传递的数据来渲染 -->
<chart
ref="chart"
v-if="(!isGroup || !chartInfo.param.collapse) && !isTitle"
:chart-data="chartData"
:chart-info="chartInfo"
:query-params="queryParams"
:panel-lock="panelLock"
:is-error="isError"
:loading="loading"
:is-fullscreen="isFullscreen"
@showLoading="showLoading"
></chart>
</div>
</template>
<script>
import ChartHeader from './ChartHeader'
import Chart from '@/views/charts/Chart2'
import {
isEcharts,
isSingleValue,
isTable,
isActiveIpTable,
isTitle,
isMap,
isEchartsWithTable,
isEchartsWithStatistics,
isEchartsTimeBar,
isEchartsCategoryBar,
isMapLine,
isMapBlock,
isSingleValueWithEcharts,
isSingleValueWithEchartsTemp,
isRelationShip,
isTabs,
isGroup,
isSankey,
isIpBasicInfo,
isIpOpenPort,
isIpHostedDomain,
isDomainWhois,
isDomainDnsRecord,
isCryptocurrencyEventList,
isAppBasicInfo,
isAppRelatedDomain,
isBlock
} from './charts/tools'
import { tableTitleMapping } from '@/components/charts/chart-table-title'
import { replaceUrlPlaceholder } from '@/utils/tools'
import { getNowTime, getSecond } from '@/utils/date-util'
import { chartPieTableTopOptions, chartTableDefaultPageSize, chartTableTopOptions } from '@/utils/constants'
import { get } from '@/utils/http'
import { ref } from 'vue'
export default {
name: 'PanelChart',
components: {
ChartHeader,
Chart
},
props: {
chartInfo: Object, // 其中的param json串已转化为对象
timeFilter: Object, // 时间范围
isFullscreen: Boolean,
panelLock: Boolean,
showHeader: {
type: Boolean,
default: true
}
},
data () {
return {
chartData: null, // 图表要渲染的数据请求接口得到传入chart组件chart组件内除特别情况(多级)外不做查询
loading: false,
isError: false, // 接口响应是否报错
errorInfo: '', // 接口具体错误信息
queryTimeRange: { // 实际查询接口时使用的时间
startTime: '',
endTime: ''
},
queryParams: {},
standaloneTimeRange: { // 单个图表刷新时,重新获取时间范围,且不影响到其他图表
use: false,
startTime: '',
endTime: ''
},
orderPieTable: chartPieTableTopOptions[0].value,
// 挖矿右下角图
activeIpTable: {
orderBy: 'machine',
tableData: [
{
name: '192.168.20.21',
num: 111
}, {
name: '192.168.20.22',
num: 345
}, {
name: '192.168.20.23',
num: 111
}, {
name: '192.168.20.24',
num: 345
}, {
name: '192.168.20.25',
num: 111
}, {
name: '192.168.20.26',
num: 345
}
] // table的所有数据
},
table: {
pageSize: chartTableDefaultPageSize,
limit: chartTableTopOptions[0], // top-n
orderBy: 'sessions'
}
}
},
computed: {
headerH () {
return this.$store.getters.getHeaderH
},
headerHPadding () {
return this.$store.getters.getHeaderHPadding
}
},
methods: {
/* 参数 extraParams 额外请求参数isRefresh 是否是刷新 */
getChartData (url, extraParams = {}, isRefresh) {
this.loading = true
this.standaloneTimeRange.use = !!isRefresh
try {
// 单个图表刷新时,使用单独的时间
if (this.standaloneTimeRange.use) {
this.queryTimeRange = { startTime: getSecond(this.standaloneTimeRange.startTime), endTime: getSecond(this.standaloneTimeRange.endTime) }
} else if (this.timeFilter) {
this.queryTimeRange = { startTime: getSecond(this.timeFilter.startTime), endTime: getSecond(this.timeFilter.endTime) }
} else {
this.queryTimeRange = { startTime: getSecond(this.chartTimeFilter.startTime), endTime: getSecond(this.chartTimeFilter.endTime) }
}
const chartParams = this.chartInfo.params
// 接口查询参数
this.queryParams = {
...this.handleQueryParams(),
...this.queryTimeRange,
...this.entity,
...extraParams
}
const requestUrl = url || chartParams.url
if (requestUrl) {
get(replaceUrlPlaceholder(requestUrl, this.queryParams)).then(response => {
if (response.code === 200) {
this.chartData = response.data.result
this.isError = false
} else {
this.isError = true
this.noData = true
this.errorInfo = response.msg || response.message || 'Unknown'
}
})
}
} catch (e) {
console.error(e)
} finally {
setTimeout(() => {
this.loading = false
}, 200)
}
},
handleQueryParams () {
const params = {}
if (this.isEchartsWithTable) {
params.limit = 10
params.order = this.orderPieTable
} else if (this.isRelationShip) {
params.limit = 5
} else if (this.isActiveIpTable) {
params.order = this.activeIpTable.orderBy
} else if (this.isTable) {
params.limit = this.table.limit
params.order = this.table.orderBy
}
return params
},
resize () {
this.$refs.chart.resize()
},
refresh () {
const myEndTime = window.$dayJs.tz().valueOf()
const myStartTime = myEndTime - this.chartTimeFilter.dateRangeValue * 60 * 1000
this.standaloneTimeRange.use = true
this.standaloneTimeRange.startTime = myStartTime
this.standaloneTimeRange.endTime = myEndTime
this.getChartData(null, {}, true)
},
showFullscreen (show) {
this.$emit('showFullscreen', show, this.chartInfo)
},
loadMore () {
this.$nextTick(() => {
this.$refs.chart && this.$refs.chart.$refs['chart' + this.chartInfo.id].initChart()
})
},
getDataKey (r) {
let key = ''
let labelText = ''
if (r.establishLatency || r.establishLatency === 0) {
key = 'establishLatency'
labelText = this.$t('networkAppPerformance.tripTime')
} else if (r.httpResponseLatency || r.httpResponseLatency === 0) {
key = 'httpResponseLatency'
labelText = this.$t('networkAppPerformance.httpResponse')
} else if (r.sslConLatency || r.sslConLatency === 0) {
key = 'sslConLatency'
labelText = this.$t('networkAppPerformance.sslResponse')
} else if (r.sequenceGapLossPercent || r.sequenceGapLossPercent === 0) {
key = 'sequenceGapLossPercent'
labelText = this.$t('networkAppPerformance.packetLossRate')
} else if (r.pktRetransPercent || r.pktRetransPercent === 0) {
key = 'pktRetransPercent'
labelText = this.$t('networkAppPerformance.retransmissionRate')
} else if (r.sessions || r.sessions === 0) {
key = 'sessions'
labelText = this.$t('overall.sessions')
}
return { key, labelText }
},
getTableTitle (data) {
if (data.length > 0) {
const dataColumns = Object.keys(data[0]) // 返回数据的字段
const columns = dataColumns.map(c => tableTitleMapping[c]) // 展示字段
const keys = ['clientIp', 'serverIp', 'ip', 'appId', 'app', 'appName', 'domain']
return columns.sort((a, b) => {
if (keys.indexOf(a.prop) > -1) {
return -1
} else if (keys.indexOf(b.prop) > -1) {
return 1
} else {
return 0
}
})
} else {
return []
}
},
showLoading (show) {
this.loading = show
}
},
mounted () {
this.getChartData()
},
setup (props) {
const dateRangeValue = 60
const { startTime, endTime } = getNowTime(dateRangeValue)
const chartTimeFilter = ref({ startTime, endTime, dateRangeValue })
return {
chartTimeFilter,
isEcharts: isEcharts(props.chartInfo.type),
isEchartsTimeBar: isEchartsTimeBar(props.chartInfo.type),
isEchartsCategoryBar: isEchartsCategoryBar(props.chartInfo.type),
isEchartsWithTable: isEchartsWithTable(props.chartInfo.type),
isEchartsWithStatistics: isEchartsWithStatistics(props.chartInfo.type),
isSingleValue: isSingleValue(props.chartInfo.type),
isSingleValueWithEcharts: isSingleValueWithEcharts(props.chartInfo.type),
isSingleValueWithEchartsTemp: isSingleValueWithEchartsTemp(props.chartInfo.type),
isRelationShip: isRelationShip(props.chartInfo.type),
isTable: isTable(props.chartInfo.type),
isActiveIpTable: isActiveIpTable(props.chartInfo.type),
isMap: isMap(props.chartInfo.type),
isTitle: isTitle(props.chartInfo.type),
isMapLine: isMapLine(props.chartInfo.type),
isMapBlock: isMapBlock(props.chartInfo.type),
isTabs: isTabs(props.chartInfo.type),
isGroup: isGroup(props.chartInfo.type),
isBlock: isBlock(props.chartInfo.type),
isSankey: isSankey(props.chartInfo.type),
isIpBasicInfo: isIpBasicInfo(props.chartInfo.type),
isIpHostedDomain: isIpHostedDomain(props.chartInfo.type),
isIpOpenPort: isIpOpenPort(props.chartInfo.type),
isDomainWhois: isDomainWhois(props.chartInfo.type),
isDomainDnsRecord: isDomainDnsRecord(props.chartInfo.type),
isCryptocurrencyEventList: isCryptocurrencyEventList(props.chartInfo.type),
isAppBasicInfo: isAppBasicInfo(props.chartInfo.type),
isAppRelatedDomain: isAppRelatedDomain(props.chartInfo.type)
}
}
}
</script>

View File

@@ -0,0 +1,343 @@
<template>
<div :id='`chartList${(isGroup ? "Group" : "") + timestamp}`' class="chart-list" ref="layoutBox">
<grid-layout
ref="layout"
v-if="gridLayoutShow"
v-model:layout="copyDataList"
:col-num="30"
:is-draggable="!panelLock"
:is-resizable="!panelLock"
:margin="[10, 10]"
:row-height="40"
:vertical-compact="true"
:use-css-transforms="false"
>
<grid-item
v-for="item in copyDataList"
:key="item.id"
:h="item.h"
:i="item.i"
:w="item.w"
:x="item.x"
:y="item.y"
:static="item.static"
:ref="'grid-item' + item.id"
:isResizable = "item.type === 'group' ? false: null"
dragAllowFrom=".chart-header"
dragIgnoreFrom=".chart-header__tools"
>
<panel-chart
:ref="'chart' + item.id"
:chart-info="item"
:show-header="true"
:time-filter="timeFilter"
@showFullscreen="showFullscreen"
></panel-chart>
</grid-item>
</grid-layout>
<!-- noData -->
<div v-if="noData" class="no-data">
<div class="no-data-div">No data</div>
</div>
<!-- 全屏查看 -->
<el-dialog
v-model="fullscreen.visible"
:show-close="false"
class="chart-fullscreen"
destroy-on-close
fullscreen
:modal-append-to-body="false"
>
<panel-chart
:ref="'chart-fullscreen' + fullscreen.chartInfo.id"
:chart-info="fullscreen.chartInfo"
:is-fullscreen="true"
:panelLock="panelLock"
:time-filter="timeFilter"
@showFullscreen="showFullscreen"
></panel-chart>
</el-dialog>
</div>
</template>
<script>
import PanelChart from '@/views/charts/PanelChart'
import VueGridLayout from 'vue-grid-layout'
import { getTypeCategory } from './charts/tools'
export default {
name: 'PanelChartList',
components: {
PanelChart,
GridLayout: VueGridLayout.GridLayout,
GridItem: VueGridLayout.GridItem
},
props: {
timeFilter: Object, // 时间范围
panelLock: { type: Boolean, default: true },
isGroup: Boolean,
dataList: Array // 看板中所有图表信息
},
data () {
return {
gridLayoutLoading: false,
gridLayoutShow: false,
copyDataList: [],
noData: false, // no data
tempDom: { height: '', width: '' },
stepWidth: null,
timer: null,
timestamp: new Date().getTime(),
fullscreen: {
visible: false,
chartData: [],
chartInfo: {}
},
scrollTop: 0,
scrollTopTimer: null
}
},
methods: {
init () {
const dom = document.getElementById(this.isGroup ? `chartListGroup${this.timestamp}` : `chartList${this.timestamp}`)
if (dom) {
this.stepWidth = Math.floor(dom.offsetWidth / 12)
}
},
showFullscreen (show, chartInfo) {
this.fullscreen.chartInfo = chartInfo
this.fullscreen.visible = show
}
},
watch: {
dataList: {
deep: true,
handler (n, o) {
this.gridLayoutShow = false
this.gridLayoutLoading = true
this.noData = !n || n.length < 1
/*const nn = JSON.parse(`
[
{
"id": 1,
"name": "Sessions",
"i18n": "overall.sessions",
"panelId": 1,
"pid": 0,
"type": 11,
"x": 20,
"y": 1,
"w": 10,
"h": 5,
"params": "{\\"url\\":\\"/interface/traffic/sessions?startTime={{startTime}}&endTime={{endTime}}\\",\\"unitType\\":\\"number\\"}",
"remark": "",
"state": 1,
"buildIn": 0,
"children": [],
"parent": null,
"panel": {
"id": 1
}
},
{
"id": 2,
"name": "Traffic summary",
"i18n": "trafficSummary.trafficSummary",
"panelId": 1,
"pid": 0,
"type": 93,
"x": 0,
"y": 0,
"w": 30,
"h": 1,
"params": "",
"remark": "",
"state": 1,
"buildIn": 0,
"children": [],
"parent": null,
"panel": {
"id": 1
}
},
{
"id": 3,
"name": "Active list",
"i18n": "trafficSummary.activeList",
"panelId": 1,
"pid": 0,
"type": 93,
"x": 0,
"y": 16,
"w": 30,
"h": 1,
"params": "",
"remark": "",
"state": 1,
"buildIn": 0,
"children": [],
"parent": null,
"panel": {
"id": 1
}
},
{
"id": 4,
"name": "Domain analysis statistics",
"i18n": "trafficSummary.domainStatistics",
"panelId": 1,
"pid": 0,
"type": 93,
"x": 0,
"y": 35,
"w": 30,
"h": 1,
"params": "",
"remark": "",
"state": 1,
"buildIn": 0,
"children": [],
"parent": null,
"panel": {
"id": 1
}
},
{
"id": 6,
"name": "Packets",
"i18n": "overall.packets",
"panelId": 1,
"pid": 0,
"type": 11,
"x": 20,
"y": 6,
"w": 10,
"h": 5,
"params": "{\\"url\\":\\"/interface/traffic/packets?startTime={{startTime}}&endTime={{endTime}}\\",\\"unitType\\":\\"number\\"}",
"remark": "",
"state": 1,
"buildIn": 0,
"children": [],
"parent": null,
"panel": {
"id": 1
}
},
{
"id": 9,
"name": "Throughput",
"i18n": "trafficSummary.throughput",
"panelId": 1,
"pid": 0,
"type": 11,
"x": 20,
"y": 11,
"w": 10,
"h": 5,
"params": "{\\"url\\":\\"/interface/traffic/throughput?startTime={{startTime}}&endTime={{endTime}}\\",\\"unitType\\":\\"byte\\"}",
"remark": "packets",
"state": 1,
"buildIn": 0,
"children": [],
"parent": null,
"panel": {
"id": 1
}
},
{
"id": 10,
"name": "Active client",
"i18n": "trafficSummary.activeClient",
"panelId": 1,
"pid": 0,
"type": 61,
"x": 0,
"y": 17,
"w": 10,
"h": 9,
"params": "{\\"url\\":\\"/interface/traffic/activeClientIP?startTime={{startTime}}&endTime={{endTime}}&order={{order}}&limit={{limit}}\\"}",
"remark": "",
"state": 1,
"buildIn": 0,
"children": [],
"parent": null,
"panel": {
"id": 1
}
},
{
"id": 11,
"name": "Domain category",
"i18n": "trafficSummary.domainCategory",
"panelId": 1,
"pid": 0,
"type": 31,
"x": 0,
"y": 36,
"w": 15,
"h": 13,
"params": "{\\"url\\":\\"/interface/traffic/topDomainCategories?startTime={{startTime}}&endTime={{endTime}}&limit={{limit}}\\",\\"nameColumn\\":\\"fqdnCategoryName\\",\\"valueColumn\\":\\"uniqDomains\\",\\"tableNameColumn\\":\\"domain\\",\\"urlTable\\":\\"/interface/traffic/activeDomainCategories?startTime={{startTime}}&endTime={{endTime}}&fqdnCategoryName={{fqdnCategoryName}}&limit={{limit}}&order={{order}}\\",\\"urlChildrenTable\\":\\"/interface/traffic/domainCategoryServerIpList?startTime={{startTime}}&endTime={{endTime}}&order={{order}}&limit={{limit}}&domain={{domain}}\\"}",
"remark": "",
"state": 1,
"buildIn": 0,
"panel": {
"id": 1
}
},
{
"id": 12,
"name": "Traffic map",
"i18n": "trafficSummary.trafficMap",
"panelId": 1,
"pid": 0,
"type": 2,
"x": 0,
"y": 1,
"w": 20,
"h": 15,
"params": "{\\"url\\":\\"/interface/traffic/map?startTime={{startTime}}&endTime={{endTime}}&country={{country}}&province={{province}}&city={{city}}\\",\\"unitType\\":\\"byte\\",\\"valueColumn\\":\\"bytes\\"}",
"remark": "traffic summaryyy",
"state": 1,
"buildIn": 0,
"children": [],
"parent": null,
"panel": {
"id": 1
}
}
]
`)*/
const tempList = n.map(item => {
let params = {}
const height = item.h
if (item.params) {
try {
params = JSON.parse(item.params)
} catch (e) {
console.error(e)
}
params.showHeader = true
}
return {
...item,
i: item.id,
w: item.w,
h: height,
x: item.x || 0,
y: item.y || 0,
params,
category: getTypeCategory(item.type) // 类别是echarts还是map等等
}
})
this.$nextTick(() => {
this.copyDataList = JSON.parse(JSON.stringify(tempList))
setTimeout(() => {
this.gridLayoutShow = true
})
this.gridLayoutLoading = false
})
}
}
}
}
</script>

View File

@@ -0,0 +1,263 @@
<template>
<div class="map-back" v-show="showMapBackButton" @click="mapBack">&lt; back</div>
<div class="chart-drawing" :id="`chart${chartInfo.id}`"></div>
</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 unitConvert, { valueToRangeValue } from '@/utils/unit-convert'
import { HeatLegend } from '@/components/amcharts/heatLegend'
import { getData } from '@/utils/api'
export default {
name: 'ChartMap',
data () {
return {
myChart: null,
mapPictureUrl: '/Tiles/{z}/{x}/{y}.png',
showMapBackButton: false,
polygonSeries: null, // 世界地图series
countrySeries: null // 下钻国家series
}
},
props: {
chartInfo: Object,
chartData: [Array, Object],
queryParams: Object
},
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)
}
}
})
}
},
loadAm4ChartMap (polygonSeries, country, chartData) {
this.$emit('showLoading', true)
try {
// 清除数据
polygonSeries.data.splice(0)
// 清除legend
this.myChart.children.each((s, i) => {
if (s && s.className !== 'Container') {
this.myChart.children.removeIndex(i)
}
})
this.showMapBackButton = !!country
const chartParams = this.chartInfo.params
const data = chartData || this.chartData
if (data && this.isMapBlock) {
const sumData = []
data.forEach(r => {
const hit = sumData.find(s => s.id === r.serverId)
const { key, labelText } = this.getDataKey(r)
const value = Number(r[key]) || 0
if (hit) {
hit.value += value
} else {
sumData.push({
id: r.serverId,
serverCountry: r.serverCountry,
key,
labelText,
value
})
}
})
const seriesData = sumData.map(r => ({
...r,
showValue: (r.value || r.value === 0) ? valueToRangeValue(r.value, chartParams.unitType).join(' ') : ''
}))
polygonSeries.data = [...seriesData]
const sorted = seriesData.sort((a, b) => b.value - a.value)
const allZero = this.$_.isEmpty(sorted) || Number(sorted[0].value) === 0 // 数据全为0的情况legend只显示1个颜色
polygonSeries.heatRules.push({
property: 'fill',
target: polygonSeries.mapPolygons.template,
min: this.myChart.colors.getIndex(1).brighten(1),
max: allZero ? this.myChart.colors.getIndex(1).brighten(1) : this.myChart.colors.getIndex(1).brighten(-0.3)
})
const heatLegend = this.myChart.createChild(HeatLegend)
heatLegend.markerContainer.height = 6
heatLegend.series = polygonSeries
heatLegend.align = 'left'
heatLegend.markerCount = allZero ? 1 : 3
heatLegend.minValue = 0
heatLegend.fontSize = 12
heatLegend.maxValue = allZero ? 1 : Number(sorted[0].value)
heatLegend.width = allZero ? am4Core.percent(10) : am4Core.percent(25)
heatLegend.marginLeft = 15
heatLegend.valign = 'bottom'
const minRange = heatLegend.valueAxis.axisRanges.create()
minRange.value = heatLegend.minValue
minRange.label.text = minRange.value === 0 ? 0 : unitConvert(heatLegend.minValue, chartParams.unitType).join(' ')
const maxRange = heatLegend.valueAxis.axisRanges.create()
maxRange.value = heatLegend.maxValue
maxRange.label.text = maxRange.value === 0 ? 0 : unitConvert(heatLegend.maxValue, chartParams.unitType).join(' ')
heatLegend.valueAxis.renderer.labels.template.adapter.add('text', function (labelText) {
return ''
})
}
} catch (e) {
console.error(e)
} finally {
setTimeout(() => {
this.$emit('showLoading', false)
}, 200)
}
},
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(() => {
this.showMapBackButton = false
this.polygonSeries.show()
this.myChart.goHome()
})
},
generateTooltipHTML () {
return `
<div class="map-tooltip" style="padding-bottom: 10px;">
<div class="map-tooltip__title">{name}</div>
<div class="map-tooltip__content">
<span>{labelText}</span>
<span>{showValue}</span>
</div>
</div>
`
},
getDataKey (r) {
let key = ''
let labelText = ''
if (r.establishLatency || r.establishLatency === 0) {
key = 'establishLatency'
labelText = this.$t('networkAppPerformance.tripTime')
} else if (r.httpResponseLatency || r.httpResponseLatency === 0) {
key = 'httpResponseLatency'
labelText = this.$t('networkAppPerformance.httpResponse')
} else if (r.sslConLatency || r.sslConLatency === 0) {
key = 'sslConLatency'
labelText = this.$t('networkAppPerformance.sslResponse')
} else if (r.sequenceGapLossPercent || r.sequenceGapLossPercent === 0) {
key = 'sequenceGapLossPercent'
labelText = this.$t('networkAppPerformance.packetLossRate')
} else if (r.pktRetransPercent || r.pktRetransPercent === 0) {
key = 'pktRetransPercent'
labelText = this.$t('networkAppPerformance.retransmissionRate')
} else if (r.sessions || r.sessions === 0) {
key = 'sessions'
labelText = this.$t('overall.sessions')
}
return { key, labelText }
}
},
mounted () {
this.initMap(`chart${this.chartInfo.id}`)
},
setup (props) {
return {
isIpBasicInfo: isIpBasicInfo(props.chartInfo.type),
isMapBlock: isMapBlock(props.chartInfo.type)
}
}
}
</script>

View File

@@ -0,0 +1,9 @@
<template>
<div class="panel-chart__no-data">No data</div>
</template>
<script>
export default {
name: 'ChartNoData'
}
</script>

View File

@@ -0,0 +1,68 @@
<template>
<el-tabs
class="cn-chart cn-chart__tabs"
v-model="activeTab"
@tab-click="changeTab"
:style="computePosition"
:ref="`chart-${chart.id}`"
>
<el-tab-pane
v-for="tab in chartInfo.children"
:label="tab.i18n ? $t(tab.i18n) : tab.name" :name="`${tab.id}`"
:key="tab.id"
:ref="`chart-${chart.id}`"
>
<template v-for="chart in tab.children" :key="chart.id">
<panel-chart
:ref="'chart' + chart.id"
:chart-info="chart"
:show-header="true"
:time-filter="timeFilter"
@showFullscreen="showFullscreen"
></panel-chart>
</template>
</el-tab-pane>
</el-tabs>
</template>
<script>
import _ from 'lodash'
import PanelChart from '@/views/charts/PanelChart'
export default {
name: 'ChartTabs',
props: {
chartInfo: Object,
chartData: [Object, Array, String], // 数据在父组件查询后传入,本组件内不查询,只根据接传递的数据来渲染
queryParams: Object // 接口请求参数
},
components: {
PanelChart
},
computed: {
timeFilter () {
return {
startTime: this.queryParams.startTime,
endTime: this.queryParams.endTime
}
}
},
data () {
return {
}
},
methods: {
showFullscreen () {
}
},
setup (props) {
let activeTab = ''
if (!_.isEmpty(props.chartInfo.children)) {
activeTab = `${props.chartInfo.children[0].id}`
}
return {
activeTab
}
}
}
</script>

View File

@@ -0,0 +1,828 @@
/**
* @author 陈劲松
* @date 2021/6/16
* @description chart option和一些工具
*/
import { format } from 'echarts'
import { unitTypes } from '@/utils/constants'
import unitConvert from '@/utils/unit-convert'
import _ from 'lodash'
export const chartColor = ['#5370C6', '#90CC74', '#FAC858', '#EE6666',
'#73BFDE', '#3BA172', '#FC8452', '#9960B4',
'#E97CCC', '#FEA69E', '#0F8AB2', '#57CBAC',
'#5888BC', '#63B6AC', '#EDC6B2', '#D5746B']
export const chartBarColor = ['#0F8AB2', '#57CBAC']
export function getChartColor (index) {
return chartColor[index % chartColor.length]
}
export function getCharBartColor (index) {
return chartBarColor[index % chartBarColor.length]
}
const line = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: axiosFormatter,
show: true,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'time'
},
yAxis: {
type: 'value',
axisLabel: {
formatter: function (value, index, a, b) {
return unitConvert(value, unitTypes.number).join(' ')
}
},
minInterval: 1
},
animation: false,
grid: {
left: 55,
bottom: 30,
top: 100,
right: 25
},
color: chartColor,
legend: {
tooltip: {
show: true,
formatter: '{a}'
},
show: true,
right: 23,
top: 8,
padding: 2,
orient: 'horizontal',
icon: 'circle',
itemGap: 10,
itemWidth: 10,
textStyle: {
padding: [0, 0, 0, 2],
fontSize: 14
},
formatter: tooLongFormatter
},
axisLabel: {
fontSize: 14
},
series: [
{
name: '',
type: 'line',
smooth: false,
symbol: 'none',
data: []
}
]
}
const lineWithStatistics = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: axiosFormatter,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'time'
},
animation: false,
yAxis: {
type: 'value',
axisLabel: {
formatter: function (value, index) {
return unitConvert(value, unitTypes.number).join(' ')
}
},
minInterval: 1
},
color: chartColor,
grid: {
left: 55,
bottom: 30,
top: 20,
right: 20
},
legend: {
show: false
},
axisLabel: {
fontSize: 14
},
series: [
{
name: '',
type: 'line',
smooth: false,
symbol: 'none',
data: []
}
]
}
const lineStack = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: axiosFormatter,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'time'
},
color: chartColor,
yAxis: {
type: 'value',
axisLabel: {
formatter: function (value, index) {
return unitConvert(value, unitTypes.number).join(' ')
}
},
minInterval: 1
},
grid: {
left: 55,
bottom: 45,
top: 10,
right: 180
},
legend: {
show: true,
right: 30,
top: 'middle',
orient: 'vertical',
icon: 'circle',
itemGap: 20,
itemWidth: 10,
formatter: tooLongFormatter,
textStyle: {
padding: [0, 0, 0, 5],
fontSize: 14
}
},
axisLabel: {
fontSize: 14
},
series: [
{
name: '',
type: 'line',
stack: 'value',
areaStyle: {},
symbol: 'none',
data: []
}
]
}
const pieWithTable = {
tooltip: {
appendToBody: true
},
color: chartColor,
animation: false,
legend: {
orient: 'vertical',
type: 'plain',
left: '60%',
top: 'middle',
icon: 'circle',
itemWidth: 10, // 设置宽度
itemHeight: 10, // 设置高度
itemGap: 20,
formatter: tooLongFormatter,
tooltip: {
show: true
}
},
series: [
{
type: 'pie',
selectedMode: 'single',
radius: ['42%', '65%'],
center: ['30%', '50%'],
data: [],
label: {
formatter: '{d}%'
},
tooltip: {
formatter: function (param, index, callback) {
return `${param.name}: ${unitConvert(param.value, param.data.unitType).join(' ')}`
}
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
const ipHostedDomain = {
color: chartColor,
animation: false,
tooltip: {
show: true
},
legend: {
orient: 'vertical',
type: 'plain',
right: '8%',
top: 'middle',
icon: 'circle',
itemWidth: 10, // 设置宽度
itemHeight: 10, // 设置高度
itemGap: 20,
tooltip: {
show: true
}
},
series: [
{
type: 'pie',
selectedMode: 'single',
radius: ['42%', '65%'],
center: ['36%', '50%'],
data: [],
label: {
formatter: '{d}%'
},
tooltip: {
formatter: function (param, index, callback) {
return `${param.name}: ${unitConvert(param.value, param.data.unitType).join(' ')}`
}
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
const singleValueLine = {
tooltip: {
show: true,
enterable: true,
showContent: true,
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
}
},
xAxis: {
type: 'time',
show: false
},
yAxis: {
type: 'value',
show: false
},
animation: false,
grid: {
left: 0,
bottom: 2,
top: 5,
right: 0
},
color: chartColor,
legend: {
show: false
},
series: [
{
type: 'line',
legendHoverLink: false,
itemStyle: {
normal: {
color: '#81C9FF',
lineStyle: {
width: 2
}
}
},
data: [],
showSymbol: false,
areaStyle: { color: '#C9EAFF' }
}
]
}
export const entityListLineOption = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: axiosFormatter,
show: true,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'time',
show: false
},
yAxis: {
type: 'value',
show: false
},
animation: false,
grid: {
left: 0,
bottom: 2,
top: 5,
right: 0
},
color: chartColor,
legend: {
show: false
},
series: [
{
type: 'line',
legendHoverLink: false,
itemStyle: {
normal: {
lineStyle: {
width: 2
}
}
},
data: [],
showSymbol: false
}
]
}
const relationShip = {
grid: {
left: 0,
bottom: 50,
top: 80,
right: 0
},
series: [
{
type: 'graph',
layout: 'force',
symbolSize: 40,
roam: true,
force: {
repulsion: 350
},
draggable: true,
label: { show: true },
edgeSymbol: ['none', 'arrow'],
edgeSymbolSize: 7,
data: [],
links: []
}
]
}
const sankey = {
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
},
series: [
{
type: 'sankey',
data: [],
links: [],
right: '5%',
top: 50,
bottom: 100,
levels: [
{
depth: 0,
itemStyle: {
color: '#47D49C'
},
lineStyle: {
color: '#999'
}
}, {
depth: 1,
itemStyle: {
color: '#A69BF5'
},
lineStyle: {
color: '#999'
}
}, {
depth: 2,
itemStyle: {
color: '#73A0FA'
},
lineStyle: {
color: '#999'
}
}
]
}
]
}
const ipOpenPortBar = {
xAxis: {
type: 'category',
axisTick: { show: false },
axisLine: { show: false }
},
grid: {
top: 30,
left: 60,
right: 50,
bottom: 50
},
yAxis: {
type: 'value',
show: false
},
series: [{
barWidth: 38,
data: [],
type: 'bar',
label: { show: true, position: 'top' },
barCategoryGap: '10%'
}]
}
const categoryBar = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: categoryVerticalFormatter,
show: true,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'category',
axisTick: { show: false },
axisLine: { show: false }
},
grid: {
top: 20,
left: 10,
right: 25,
bottom: 20,
containLabel: true
},
yAxis: {
type: 'value',
axisTick: { show: false },
axisLine: { show: false }
},
color: chartColor,
series: [{
barWidth: 15,
data: [],
type: 'bar',
label: { show: false },
barCategoryGap: '10%',
itemStyle: {
color: function (params) {
return getCharBartColor([params.dataIndex])
}
}
}]
}
const timeBar = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: timeVerticalFormatter,
show: true,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'time',
axisTick: { show: false },
axisLine: { show: false },
axisLabel: {
interval: 0,
// rotate: -40, //设置日期显示样式(倾斜度)
formatter: function (value) { // 在这里写你需要的时间格式
const t_date = new Date(value)
return [t_date.getMonth() + 1, t_date.getDate()].join('/') + ' ' + [t_date.getHours(), t_date.getMinutes()].join(':')
}
}
},
grid: {
top: 20,
left: 25,
right: 25,
bottom: 20,
containLabel: true
},
yAxis: {
type: 'value',
axisTick: { show: false },
axisLine: { show: false },
axisLabel: {
formatter: function (value, index, a, b) {
return unitConvert(value, unitTypes.number).join(' ')
}
},
minInterval: 1
},
color: chartColor,
series: [{
barWidth: 15,
data: [],
type: 'bar',
label: { show: false },
barCategoryGap: '10%',
itemStyle: {
color: function (params) {
return getCharBartColor([params.dataIndex])
}
}
}]
}
const typeOptionMappings = [
{ value: 11, option: line }, // 常规折线图
{ value: 12, option: lineWithStatistics }, // 带统计表格的折线图
{ value: 13, option: lineStack }, // 折线堆叠图
{ value: 22, option: ipOpenPortBar }, // ip详情--开放端口的柱状图
{ value: 23, option: timeBar }, // 矿机所属单位
{ value: 24, option: categoryBar }, // 挖矿事件统计
{ value: 31, option: pieWithTable }, // 常规折线图
{ value: 33, option: ipHostedDomain }, // ip详情--托管域名
{ value: 34, option: ipHostedDomain }, // app详情--相关域名
{ value: 42, option: relationShip }, // 关系图
{ value: 43, option: sankey }, // 桑基图
{ value: 52, option: singleValueLine }
]
const typeCategory = {
MAP: 'map',
TABLE: 'table',
ECHARTS: 'echarts',
TITLE: 'title',
SINGLE: 'singleValue',
TABS: 'tabs'
}
export function getTypeCategory (type) {
if (isMap(type)) {
return typeCategory.MAP
} else if (isEcharts(type)) {
return typeCategory.ECHARTS
} else if (isTable(type)) {
return typeCategory.TABLE
} else if (isSingleValue(type)) {
return typeCategory.SINGLE
} else if (isTitle(type)) {
return typeCategory.TITLE
} else if (isTabs(type)) {
return typeCategory.TABS
}
}
/* 柱状图:挖矿事件统计(time类型柱状图) */
export function isEchartsTimeBar (type) {
return type == 23
}
/* 柱状图:矿机所属单位(category类型柱状图) */
export function isEchartsCategoryBar (type) {
return type == 24
}
/* 饼图柱状图等 */
export function isEcharts (type) {
return type >= 11 && type <= 50
}
/* 地图 */
export function isMap (type) {
return type >= 1 && type <= 10
}
/* 连线地图 */
export function isMapLine (type) {
return type === 1
}
/* 色块地图 */
export function isMapBlock (type) {
return type === 2
}
/* 带统计的折线图 */
export function isEchartsWithStatistics (type) {
return type === 12
}
/* 关系图 */
export function isRelationShip (type) {
return type === 42
}
/* 桑基图 */
export function isSankey (type) {
return type === 43
}
/* 单值 */
export function isSingleValue (type) {
return type >= 51 && type <= 60
}
/* 带折线图的单值 */
export function isSingleValueWithEcharts (type) {
return type === 52
}
/* 带折线图的单值 */
export function isSingleValueWithEchartsTemp (type) {
return type === 55
}
/* 带Table的饼图 */
export function isEchartsWithTable (type) {
return type === 31
}
/* table */
export function isTable (type) {
return type >= 61 && type <= 70
}
/* table */
export function isActiveIpTable (type) {
return type == 63
}
/* title */
export function isTitle (type) {
return type === 93
}
/* tabs */
export function isTabs (type) {
return type === 91
}
/* IP实体基本信息 */
export function isIpBasicInfo (type) {
return type === 4
}
/* IP实体开放端口 */
export function isIpOpenPort (type) {
return type === 22
}
/* IP实体托管域名 */
export function isIpHostedDomain (type) {
return type === 33
}
/* APP实体相关域名 */
export function isAppRelatedDomain (type) {
return type === 34
}
/* APP实体基本信息 */
export function isAppBasicInfo (type) {
return type === 82
}
/* DOMAIN实体Whois */
export function isDomainWhois (type) {
return type === 83
}
/* DOMAIN实体DNS记录 */
export function isDomainDnsRecord (type) {
return type === 84
}
/* 近期挖矿事件 */
export function isCryptocurrencyEventList (type) {
return type === 85
}
/* 组 */
export function isGroup (type) {
return type === 94
}
/* 实体详情块 */
export function isBlock (type) {
return type === 95
}
export function getOption (type) {
const mapping = typeOptionMappings.find(m => m.value === type)
return mapping && mapping.option ? _.cloneDeep(mapping.option) : null
}
export const layoutConstant = {
HEADER: 'header',
FOOTER: 'footer'
}
export function getLayout (type) {
const layout = []
if (!isSingleValue(type) && !isTitle(type)) {
layout.push(layoutConstant.HEADER)
}
if (type === 12 || type === 31) {
layout.push(layoutConstant.FOOTER)
}
return layout
}
function tooLongFormatter (name) {
return format.truncateText(name, 110, '12')
}
function axiosFormatter (params) {
let str = '<div>'
params.forEach((item, i) => {
const tData = item.data[0]
if (i === 0) {
str += '<div style="margin-bottom: 5px">'
str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
str += '</div>'
}
str += '<div class="cn-chart-tooltip-box">'
str += item.marker
str += `<span class="cn-chart-tooltip-content">
${item.seriesName}
</span>`
str += `<span class="cn-chart-tooltip-value">
${unitConvert(item.data[1], item.data[2]).join(' ')}
</span>`
str += '</div>'
})
str += '</div>'
return str
}
export function timeVerticalFormatter (params) {
let str = '<div>'
params.forEach((item, i) => {
const tData = item.data[0]
if (i === 0) {
str += '<div style="margin-bottom: 5px">'
str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
str += '</div>'
}
str += '<div class="cn-chart-tooltip-box">'
str += item.marker
str += `<span class="cn-chart-tooltip-content">
${item.seriesName}
</span>`
str += `<span class="cn-chart-tooltip-value">
${unitConvert(item.data[1], item.data[2]).join(' ')}
</span>`
str += '</div>'
})
str += '</div>'
return str
}
export function timeHorizontalFormatter (params) {
let str = '<div>'
params.forEach((item, i) => {
const tData = item.data[1]
if (i === 0) {
str += '<div style="margin-bottom: 5px">'
str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
str += '</div>'
}
str += '<div class="cn-chart-tooltip-box">'
str += item.marker
str += `<span class="cn-chart-tooltip-content">
${item.seriesName}
</span>`
str += `<span class="cn-chart-tooltip-value">
${unitConvert(item.data[0], item.data[2]).join(' ')}
</span>`
str += '</div>'
})
str += '</div>'
return str
}
export function categoryHorizontalFormatter (params) {
let str = '<div>'
params.forEach((item, i) => {
str += '<div class="cn-chart-tooltip-box">'
str += item.data[1] + ': ' + item.data[0]
str += '</div>'
})
str += '</div>'
return str
}
export function categoryVerticalFormatter (params) {
let str = '<div>'
params.forEach((item, i) => {
str += '<div class="cn-chart-tooltip-box">'
str += item.data[0] + ': ' + item.data[1]
str += '</div>'
})
str += '</div>'
return str
}

View File

@@ -0,0 +1,135 @@
import unitConvert from '@/utils/unit-convert'
import { unitTypes } from '@/utils/constants'
import {
categoryVerticalFormatter,
chartColor,
getCharBartColor,
timeVerticalFormatter
} from '@/views/charts/charts/tools'
export const ipOpenPortBar = {
xAxis: {
type: 'category',
axisTick: { show: false },
axisLine: { show: false }
},
grid: {
top: 30,
left: 60,
right: 50,
bottom: 50
},
yAxis: {
type: 'value',
show: false
},
series: [{
barWidth: 38,
data: [],
type: 'bar',
label: { show: true, position: 'top' },
barCategoryGap: '10%'
}]
}
export const categoryBar = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: categoryVerticalFormatter,
show: true,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'category',
axisTick: { show: false },
axisLine: { show: false }
},
grid: {
top: 20,
left: 10,
right: 25,
bottom: 20,
containLabel: true
},
yAxis: {
type: 'value',
axisTick: { show: false },
axisLine: { show: false }
},
color: chartColor,
series: [{
barWidth: 15,
data: [],
type: 'bar',
label: { show: false },
barCategoryGap: '10%',
itemStyle: {
color: function (params) {
return getCharBartColor([params.dataIndex])
}
}
}]
}
export const timeBar = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: timeVerticalFormatter,
show: true,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'time',
axisTick: { show: false },
axisLine: { show: false },
axisLabel: {
interval: 0,
// rotate: -40, //设置日期显示样式(倾斜度)
formatter: function (value) { // 在这里写你需要的时间格式
const tDate = new Date(value)
return [tDate.getMonth() + 1, tDate.getDate()].join('/') + ' ' + [tDate.getHours(), tDate.getMinutes()].join(':')
}
}
},
grid: {
top: 20,
left: 25,
right: 25,
bottom: 20,
containLabel: true
},
yAxis: {
type: 'value',
axisTick: { show: false },
axisLine: { show: false },
axisLabel: {
formatter: function (value, index, a, b) {
return unitConvert(value, unitTypes.number).join(' ')
}
},
minInterval: 1
},
color: chartColor,
series: [{
barWidth: 15,
data: [],
type: 'bar',
label: { show: false },
barCategoryGap: '10%',
itemStyle: {
color: function (params) {
return getCharBartColor([params.dataIndex])
}
}
}]
}

View File

@@ -0,0 +1,25 @@
export const relationShip = {
grid: {
left: 0,
bottom: 50,
top: 80,
right: 0
},
series: [
{
type: 'graph',
layout: 'force',
symbolSize: 40,
roam: true,
force: {
repulsion: 350
},
draggable: true,
label: { show: true },
edgeSymbol: ['none', 'arrow'],
edgeSymbolSize: 7,
data: [],
links: []
}
]
}

View File

@@ -0,0 +1,274 @@
import unitConvert from '@/utils/unit-convert'
import { unitTypes } from '@/utils/constants'
import { chartColor } from '@/views/charts/charts/chart-options'
import { axisFormatter, tooLongFormatter } from '../tools'
export const line = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: axisFormatter,
show: true,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'time'
},
yAxis: {
type: 'value',
axisLabel: {
formatter: function (value, index, a, b) {
return unitConvert(value, unitTypes.number).join(' ')
}
},
minInterval: 1
},
animation: false,
grid: {
left: 55,
bottom: 30,
top: 100,
right: 25
},
color: chartColor,
legend: {
tooltip: {
show: true,
formatter: '{a}'
},
show: true,
right: 23,
top: 8,
padding: 2,
orient: 'horizontal',
icon: 'circle',
itemGap: 10,
itemWidth: 10,
textStyle: {
padding: [0, 0, 0, 2],
fontSize: 14
},
formatter: tooLongFormatter
},
axisLabel: {
fontSize: 14
},
series: [
{
name: '',
type: 'line',
smooth: false,
symbol: 'none',
data: []
}
]
}
export const lineWithStatistics = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: axisFormatter,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'time'
},
animation: false,
yAxis: {
type: 'value',
axisLabel: {
formatter: function (value, index) {
return unitConvert(value, unitTypes.number).join(' ')
}
},
minInterval: 1
},
color: chartColor,
grid: {
left: 55,
bottom: 30,
top: 20,
right: 20
},
legend: {
show: false
},
axisLabel: {
fontSize: 14
},
series: [
{
name: '',
type: 'line',
smooth: false,
symbol: 'none',
data: []
}
]
}
export const lineStack = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: axisFormatter,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'time'
},
color: chartColor,
yAxis: {
type: 'value',
axisLabel: {
formatter: function (value, index) {
return unitConvert(value, unitTypes.number).join(' ')
}
},
minInterval: 1
},
grid: {
left: 55,
bottom: 45,
top: 10,
right: 180
},
legend: {
show: true,
right: 30,
top: 'middle',
orient: 'vertical',
icon: 'circle',
itemGap: 20,
itemWidth: 10,
formatter: tooLongFormatter,
textStyle: {
padding: [0, 0, 0, 5],
fontSize: 14
}
},
axisLabel: {
fontSize: 14
},
series: [
{
name: '',
type: 'line',
stack: 'value',
areaStyle: {},
symbol: 'none',
data: []
}
]
}
export const singleValueLine = {
tooltip: {
show: true,
enterable: true,
showContent: true,
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
}
},
xAxis: {
type: 'time',
show: false
},
yAxis: {
type: 'value',
show: false
},
animation: false,
grid: {
left: 0,
bottom: 2,
top: 5,
right: 0
},
color: chartColor,
legend: {
show: false
},
series: [
{
type: 'line',
legendHoverLink: false,
itemStyle: {
normal: {
color: '#81C9FF',
lineStyle: {
width: 2
}
}
},
data: [],
showSymbol: false,
areaStyle: { color: '#C9EAFF' }
}
]
}
export const entityListLine = {
tooltip: {
appendToBody: true,
trigger: 'axis',
textStyle: {
width: '20px',
overflow: 'truncate'
},
formatter: axisFormatter,
show: true,
className: 'nz-chart-tooltip',
extraCssText: 'box-shadow: 0 0 3px rgba(0, 0, 0, 0.3);max-width: 300px !important'
},
xAxis: {
type: 'time',
show: false
},
yAxis: {
type: 'value',
show: false
},
animation: false,
grid: {
left: 0,
bottom: 2,
top: 5,
right: 0
},
color: chartColor,
legend: {
show: false
},
series: [
{
type: 'line',
legendHoverLink: false,
itemStyle: {
normal: {
lineStyle: {
width: 2
}
}
},
data: [],
showSymbol: false
}
]
}

View File

@@ -0,0 +1,93 @@
import unitConvert from '@/utils/unit-convert'
import { chartColor } from '@/views/charts/charts/chart-options'
import { tooLongFormatter } from '../tools'
export const pieWithTable = {
tooltip: {
appendToBody: true
},
color: chartColor,
animation: false,
legend: {
orient: 'vertical',
type: 'plain',
left: '60%',
top: 'middle',
icon: 'circle',
itemWidth: 10, // 设置宽度
itemHeight: 10, // 设置高度
itemGap: 20,
formatter: tooLongFormatter,
tooltip: {
show: true
}
},
series: [
{
type: 'pie',
selectedMode: 'single',
radius: ['42%', '65%'],
center: ['30%', '50%'],
data: [],
label: {
formatter: '{d}%'
},
tooltip: {
formatter: function (param, index, callback) {
return `${param.name}: ${unitConvert(param.value, param.data.unitType).join(' ')}`
}
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}
export const ipHostedDomain = {
color: chartColor,
animation: false,
tooltip: {
show: true
},
legend: {
orient: 'vertical',
type: 'plain',
right: '8%',
top: 'middle',
icon: 'circle',
itemWidth: 10, // 设置宽度
itemHeight: 10, // 设置高度
itemGap: 20,
tooltip: {
show: true
}
},
series: [
{
type: 'pie',
selectedMode: 'single',
radius: ['42%', '65%'],
center: ['36%', '50%'],
data: [],
label: {
formatter: '{d}%'
},
tooltip: {
formatter: function (param, index, callback) {
return `${param.name}: ${unitConvert(param.value, param.data.unitType).join(' ')}`
}
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
}

View File

@@ -0,0 +1,43 @@
export const sankey = {
tooltip: {
trigger: 'item',
triggerOn: 'mousemove'
},
series: [
{
type: 'sankey',
data: [],
links: [],
right: '5%',
top: 50,
bottom: 100,
levels: [
{
depth: 0,
itemStyle: {
color: '#47D49C'
},
lineStyle: {
color: '#999'
}
}, {
depth: 1,
itemStyle: {
color: '#A69BF5'
},
lineStyle: {
color: '#999'
}
}, {
depth: 2,
itemStyle: {
color: '#73A0FA'
},
lineStyle: {
color: '#999'
}
}
]
}
]
}

View File

@@ -0,0 +1,337 @@
import unitConvert from '@/utils/unit-convert'
import { format } from 'echarts'
import _ from 'lodash'
import { line, lineWithStatistics, lineStack, singleValueLine } from './options/line'
import { ipOpenPortBar, timeBar, categoryBar } from './options/bar'
import { pieWithTable, ipHostedDomain } from './options/pie'
import { relationShip } from './options/graph'
import { sankey } from './options/sankey'
export const chartColor = ['#5370C6', '#90CC74', '#FAC858', '#EE6666',
'#73BFDE', '#3BA172', '#FC8452', '#9960B4',
'#E97CCC', '#FEA69E', '#0F8AB2', '#57CBAC',
'#5888BC', '#63B6AC', '#EDC6B2', '#D5746B']
export const chartBarColor = ['#0F8AB2', '#57CBAC']
export function getChartColor (index) {
return chartColor[index % chartColor.length]
}
export function getCharBartColor (index) {
return chartBarColor[index % chartBarColor.length]
}
const typeOptionMappings = [
{ value: 11, option: line }, // 常规折线图
{ value: 12, option: lineWithStatistics }, // 带统计表格的折线图
{ value: 13, option: lineStack }, // 折线堆叠图
{ value: 22, option: ipOpenPortBar }, // ip详情--开放端口的柱状图
{ value: 23, option: timeBar }, // 矿机所属单位
{ value: 24, option: categoryBar }, // 挖矿事件统计
{ value: 31, option: pieWithTable }, // 常规折线图
{ value: 33, option: ipHostedDomain }, // ip详情--托管域名
{ value: 34, option: ipHostedDomain }, // app详情--相关域名
{ value: 42, option: relationShip }, // 关系图
{ value: 43, option: sankey }, // 桑基图
{ value: 52, option: singleValueLine } // 单值图中的折线图
]
export function getOption (type) {
const mapping = typeOptionMappings.find(m => m.value === type)
return mapping && mapping.option ? _.cloneDeep(mapping.option) : null
}
/* 柱状图:挖矿事件统计(time类型柱状图) */
export function isEchartsTimeBar (type) {
return type === 23
}
/* 柱状图:矿机所属单位(category类型柱状图) */
export function isEchartsCategoryBar (type) {
return type === 24
}
/* 饼图柱状图等 */
export function isEcharts (type) {
return type >= 11 && type <= 50
}
/* 地图 */
export function isMap (type) {
return type >= 1 && type <= 10
}
/* 连线地图 */
export function isMapLine (type) {
return type === 1
}
/* 色块地图 */
export function isMapBlock (type) {
return type === 2
}
/* 带统计的折线图 */
export function isEchartsWithStatistics (type) {
return type === 12
}
/* 关系图 */
export function isRelationShip (type) {
return type === 42
}
/* 桑基图 */
export function isSankey (type) {
return type === 43
}
/* 单值 */
export function isSingleValue (type) {
return type >= 51 && type <= 60
}
/* 带折线图的单值 */
export function isSingleValueWithEcharts (type) {
return type === 52
}
/* 带折线图的单值 */
export function isSingleValueWithEchartsTemp (type) {
return type === 55
}
/* 带Table的饼图 */
export function isEchartsWithTable (type) {
return type === 31
}
/* table */
export function isTable (type) {
return type >= 61 && type <= 70
}
/* table */
export function isActiveIpTable (type) {
return type === 63
}
/* title */
export function isTitle (type) {
return type === 93
}
/* tabs */
export function isTabs (type) {
return type === 91
}
/* IP实体基本信息 */
export function isIpBasicInfo (type) {
return type === 4
}
/* IP实体开放端口 */
export function isIpOpenPort (type) {
return type === 22
}
/* IP实体托管域名 */
export function isIpHostedDomain (type) {
return type === 33
}
/* APP实体相关域名 */
export function isAppRelatedDomain (type) {
return type === 34
}
/* APP实体基本信息 */
export function isAppBasicInfo (type) {
return type === 82
}
/* DOMAIN实体Whois */
export function isDomainWhois (type) {
return type === 83
}
/* DOMAIN实体DNS记录 */
export function isDomainDnsRecord (type) {
return type === 84
}
/* 近期挖矿事件 */
export function isCryptocurrencyEventList (type) {
return type === 85
}
/* 组 */
export function isGroup (type) {
return type === 94
}
/* 实体详情块 */
export function isBlock (type) {
return type === 95
}
/* 根据type获取图表分类 */
const typeCategory = {
MAP: 'map',
TABLE: 'table',
ECHARTS: 'echarts',
TITLE: 'title',
SINGLE: 'singleValue',
TABS: 'tabs'
}
export function getTypeCategory (type) {
if (isMap(type)) {
return typeCategory.MAP
} else if (isEcharts(type)) {
return typeCategory.ECHARTS
} else if (isTable(type)) {
return typeCategory.TABLE
} else if (isSingleValue(type)) {
return typeCategory.SINGLE
} else if (isTitle(type)) {
return typeCategory.TITLE
} else if (isTabs(type)) {
return typeCategory.TABS
}
}
/* 根据type获取布局 */
export const layoutConstant = {
HEADER: 'header',
FOOTER: 'footer'
}
export function getLayout (type) {
const layout = []
if (!isSingleValue(type) && !isTitle(type)) {
layout.push(layoutConstant.HEADER)
}
if (type === 12 || type === 31) {
layout.push(layoutConstant.FOOTER)
}
return layout
}
export function getGroupHeight (arr) {
if (arr.length) {
let lastItem = []
let maxY = arr[0].y
arr.forEach((children, index) => {
if (maxY === children.y) {
lastItem.push(children)
} else if (maxY < children.y) {
maxY = children.y
lastItem = [children]
}
})
let maxHeight = 0
lastItem.forEach(last => {
if (maxHeight < last.height) {
maxHeight = last.height
}
})
if (maxY < 0) {
maxY = 0
}
return maxHeight + maxY
} else {
return 1
}
}
export function getLayoutPosition (arr) {
if (arr.length) {
let lastItem = []
let maxY = 0
arr.forEach((children, index) => {
if (maxY === children.y) {
lastItem.push(children)
} else if (maxY < children.y) {
maxY = children.y
lastItem = [children]
}
})
let maxX = 0
lastItem.forEach(last => {
if (maxX < last.x + last.span) {
maxX = last.x + last.span
}
})
return {
x: maxX,
y: maxY
}
} else {
return {
x: 0,
y: 0
}
}
}
export function axisFormatter (params) {
let str = '<div>'
params.forEach((item, i) => {
const tData = item.data[0]
if (i === 0) {
str += '<div style="margin-bottom: 5px">'
str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
str += '</div>'
}
str += '<div class="cn-chart-tooltip-box">'
str += item.marker
str += `<span class="cn-chart-tooltip-content">
${item.seriesName}
</span>`
str += `<span class="cn-chart-tooltip-value">
${unitConvert(item.data[1], item.data[2]).join(' ')}
</span>`
str += '</div>'
})
str += '</div>'
return str
}
export function tooLongFormatter (name) {
return format.truncateText(name, 110, '12')
}
export function timeHorizontalFormatter (params) {
let str = '<div>'
params.forEach((item, i) => {
const tData = item.data[1]
if (i === 0) {
str += '<div style="margin-bottom: 5px">'
str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
str += '</div>'
}
str += '<div class="cn-chart-tooltip-box">'
str += item.marker
str += `<span class="cn-chart-tooltip-content">
${item.seriesName}
</span>`
str += `<span class="cn-chart-tooltip-value">
${unitConvert(item.data[0], item.data[2]).join(' ')}
</span>`
str += '</div>'
})
str += '</div>'
return str
}
export function categoryHorizontalFormatter (params) {
let str = '<div>'
params.forEach((item, i) => {
str += '<div class="cn-chart-tooltip-box">'
str += item.data[1] + ': ' + item.data[0]
str += '</div>'
})
str += '</div>'
return str
}
export function categoryVerticalFormatter (params) {
let str = '<div>'
params.forEach((item, i) => {
str += '<div class="cn-chart-tooltip-box">'
str += item.data[0] + ': ' + item.data[1]
str += '</div>'
})
str += '</div>'
return str
}
export function timeVerticalFormatter (params) {
let str = '<div>'
params.forEach((item, i) => {
const tData = item.data[0]
if (i === 0) {
str += '<div style="margin-bottom: 5px">'
str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
str += '</div>'
}
str += '<div class="cn-chart-tooltip-box">'
str += item.marker
str += `<span class="cn-chart-tooltip-content">
${item.seriesName}
</span>`
str += `<span class="cn-chart-tooltip-value">
${unitConvert(item.data[1], item.data[2]).join(' ')}
</span>`
str += '</div>'
})
str += '</div>'
return str
}

View File

@@ -53,7 +53,7 @@
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/dataList'
import dataListMixin from '@/mixins/data-list'
import chartTable from '@/components/table/settings/ChartTable'
import chartBox from '@/components/rightBox/settings/ChartBox'
import { api } from '@/utils/api'

View File

@@ -78,7 +78,7 @@
import cnDataList from '@/components/table/CnDataList'
import galaxyProxyBox from '@/components/rightBox/settings/GalaxyProxyBox'
import galaxyProxyTable from '@/components/table/settings/GalaxyProxyTable'
import dataListMixin from '@/mixins/dataList'
import dataListMixin from '@/mixins/data-list'
import { api } from '@/utils/api'
import { get, put } from '@/utils/http'
import TopToolMoreOptions from '@/components/common/popBox/TopToolMoreOptions'

View File

@@ -55,7 +55,7 @@
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/dataList'
import dataListMixin from '@/mixins/data-list'
import i18nTable from '@/components/table/settings/I18nTable'
import i18nBox from '@/components/rightBox/settings/I18nBox'
import { put } from '@/utils/http'

View File

@@ -33,7 +33,7 @@
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/dataList'
import dataListMixin from '@/mixins/data-list'
import operationLogTable from '@/components/table/settings/OperationLogTable'
import { api } from '@/utils/api'

View File

@@ -48,7 +48,7 @@
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/dataList'
import dataListMixin from '@/mixins/data-list'
import rolesTable from '@/components/table/settings/RoleTable'
import roleBox from '@/components/rightBox/settings/RoleBox'
import { api } from '@/utils/api'

View File

@@ -53,7 +53,7 @@
</template>
<script>
import cnDataList from '@/components/table/CnDataList'
import dataListMixin from '@/mixins/dataList'
import dataListMixin from '@/mixins/data-list'
import userTable from '@/components/table/settings/UserTable'
import userBox from '@/components/rightBox/settings/UserBox'
import { api } from '@/utils/api'