+
@@ -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: [],
diff --git a/src/views/charts/PanelChart.vue b/src/views/charts/PanelChart.vue
new file mode 100644
index 00000000..0e5c0dc0
--- /dev/null
+++ b/src/views/charts/PanelChart.vue
@@ -0,0 +1,309 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/charts/PanelChartList.vue b/src/views/charts/PanelChartList.vue
new file mode 100644
index 00000000..ce0c05af
--- /dev/null
+++ b/src/views/charts/PanelChartList.vue
@@ -0,0 +1,343 @@
+
+
+
+
+
diff --git a/src/views/charts/charts/ChartMap.vue b/src/views/charts/charts/ChartMap.vue
new file mode 100644
index 00000000..ee742b59
--- /dev/null
+++ b/src/views/charts/charts/ChartMap.vue
@@ -0,0 +1,263 @@
+
+ < back
+
+
+
+
diff --git a/src/views/charts/charts/ChartNoData.vue b/src/views/charts/charts/ChartNoData.vue
new file mode 100644
index 00000000..1396f5f0
--- /dev/null
+++ b/src/views/charts/charts/ChartNoData.vue
@@ -0,0 +1,9 @@
+
+ No data
+
+
+
diff --git a/src/views/charts/charts/ChartTabs.vue b/src/views/charts/charts/ChartTabs.vue
new file mode 100644
index 00000000..f7b2dcf9
--- /dev/null
+++ b/src/views/charts/charts/ChartTabs.vue
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/views/charts/charts/chart-options.js b/src/views/charts/charts/chart-options.js
new file mode 100644
index 00000000..460c4e6d
--- /dev/null
+++ b/src/views/charts/charts/chart-options.js
@@ -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 = '
'
+ params.forEach((item, i) => {
+ const tData = item.data[0]
+ if (i === 0) {
+ str += '
'
+ str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
+ str += '
'
+ }
+ str += '
'
+ str += item.marker
+ str += `
+ ${item.seriesName}
+ `
+ str += `
+ ${unitConvert(item.data[1], item.data[2]).join(' ')}
+ `
+ str += '
'
+ })
+ str += '
'
+ return str
+}
+
+export function timeVerticalFormatter (params) {
+ let str = '
'
+ params.forEach((item, i) => {
+ const tData = item.data[0]
+ if (i === 0) {
+ str += '
'
+ str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
+ str += '
'
+ }
+ str += '
'
+ str += item.marker
+ str += `
+ ${item.seriesName}
+ `
+ str += `
+ ${unitConvert(item.data[1], item.data[2]).join(' ')}
+ `
+ str += '
'
+ })
+ str += '
'
+ return str
+}
+
+export function timeHorizontalFormatter (params) {
+ let str = '
'
+ params.forEach((item, i) => {
+ const tData = item.data[1]
+ if (i === 0) {
+ str += '
'
+ str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
+ str += '
'
+ }
+ str += '
'
+ str += item.marker
+ str += `
+ ${item.seriesName}
+ `
+ str += `
+ ${unitConvert(item.data[0], item.data[2]).join(' ')}
+ `
+ str += '
'
+ })
+ str += '
'
+ return str
+}
+export function categoryHorizontalFormatter (params) {
+ let str = '
'
+ params.forEach((item, i) => {
+ str += '
'
+ str += item.data[1] + ': ' + item.data[0]
+ str += '
'
+ })
+ str += '
'
+ return str
+}
+export function categoryVerticalFormatter (params) {
+ let str = '
'
+ params.forEach((item, i) => {
+ str += '
'
+ str += item.data[0] + ': ' + item.data[1]
+ str += '
'
+ })
+ str += '
'
+ return str
+}
diff --git a/src/views/charts/charts/options/bar.js b/src/views/charts/charts/options/bar.js
new file mode 100644
index 00000000..0f9198ea
--- /dev/null
+++ b/src/views/charts/charts/options/bar.js
@@ -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])
+ }
+ }
+ }]
+}
diff --git a/src/views/charts/charts/options/graph.js b/src/views/charts/charts/options/graph.js
new file mode 100644
index 00000000..43eddd9e
--- /dev/null
+++ b/src/views/charts/charts/options/graph.js
@@ -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: []
+ }
+ ]
+}
diff --git a/src/views/charts/charts/options/line.js b/src/views/charts/charts/options/line.js
new file mode 100644
index 00000000..300cea2c
--- /dev/null
+++ b/src/views/charts/charts/options/line.js
@@ -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
+ }
+ ]
+}
diff --git a/src/views/charts/charts/options/pie.js b/src/views/charts/charts/options/pie.js
new file mode 100644
index 00000000..b2648335
--- /dev/null
+++ b/src/views/charts/charts/options/pie.js
@@ -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)'
+ }
+ }
+ }
+ ]
+}
diff --git a/src/views/charts/charts/options/sankey.js b/src/views/charts/charts/options/sankey.js
new file mode 100644
index 00000000..e4856ffe
--- /dev/null
+++ b/src/views/charts/charts/options/sankey.js
@@ -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'
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/src/views/charts/charts/tools.js b/src/views/charts/charts/tools.js
new file mode 100644
index 00000000..3eb4c239
--- /dev/null
+++ b/src/views/charts/charts/tools.js
@@ -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 = '
'
+ params.forEach((item, i) => {
+ const tData = item.data[0]
+ if (i === 0) {
+ str += '
'
+ str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
+ str += '
'
+ }
+ str += '
'
+ str += item.marker
+ str += `
+ ${item.seriesName}
+ `
+ str += `
+ ${unitConvert(item.data[1], item.data[2]).join(' ')}
+ `
+ str += '
'
+ })
+ str += '
'
+ return str
+}
+export function tooLongFormatter (name) {
+ return format.truncateText(name, 110, '12')
+}
+export function timeHorizontalFormatter (params) {
+ let str = '
'
+ params.forEach((item, i) => {
+ const tData = item.data[1]
+ if (i === 0) {
+ str += '
'
+ str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
+ str += '
'
+ }
+ str += '
'
+ str += item.marker
+ str += `
+ ${item.seriesName}
+ `
+ str += `
+ ${unitConvert(item.data[0], item.data[2]).join(' ')}
+ `
+ str += '
'
+ })
+ str += '
'
+ return str
+}
+export function categoryHorizontalFormatter (params) {
+ let str = '
'
+ params.forEach((item, i) => {
+ str += '
'
+ str += item.data[1] + ': ' + item.data[0]
+ str += '
'
+ })
+ str += '
'
+ return str
+}
+export function categoryVerticalFormatter (params) {
+ let str = '
'
+ params.forEach((item, i) => {
+ str += '
'
+ str += item.data[0] + ': ' + item.data[1]
+ str += '
'
+ })
+ str += '
'
+ return str
+}
+export function timeVerticalFormatter (params) {
+ let str = '
'
+ params.forEach((item, i) => {
+ const tData = item.data[0]
+ if (i === 0) {
+ str += '
'
+ str += window.$dayJs.tz(tData).format('YYYY-MM-DD HH:mm:ss')
+ str += '
'
+ }
+ str += '
'
+ str += item.marker
+ str += `
+ ${item.seriesName}
+ `
+ str += `
+ ${unitConvert(item.data[1], item.data[2]).join(' ')}
+ `
+ str += '
'
+ })
+ str += '
'
+ return str
+}
diff --git a/src/views/settings/Chart.vue b/src/views/settings/Chart.vue
index bc9c6aa5..df0afb7e 100644
--- a/src/views/settings/Chart.vue
+++ b/src/views/settings/Chart.vue
@@ -53,7 +53,7 @@