2021-12-05 18:59:20 +08:00
|
|
|
|
Vue.component("traffic-map-box", {
|
|
|
|
|
|
props: ["v-stats", "v-is-attack"],
|
|
|
|
|
|
mounted: function () {
|
|
|
|
|
|
this.render()
|
|
|
|
|
|
},
|
|
|
|
|
|
data: function () {
|
|
|
|
|
|
let maxPercent = 0
|
2021-12-05 19:38:14 +08:00
|
|
|
|
let isAttack = this.vIsAttack
|
2021-12-05 18:59:20 +08:00
|
|
|
|
this.vStats.forEach(function (v) {
|
|
|
|
|
|
let percent = parseFloat(v.percent)
|
|
|
|
|
|
if (percent > maxPercent) {
|
|
|
|
|
|
maxPercent = percent
|
|
|
|
|
|
}
|
2021-12-05 19:38:14 +08:00
|
|
|
|
|
|
|
|
|
|
v.formattedCountRequests = teaweb.formatCount(v.countRequests) + "次"
|
|
|
|
|
|
v.formattedCountAttackRequests = teaweb.formatCount(v.countAttackRequests) + "次"
|
2021-12-05 18:59:20 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (maxPercent < 100) {
|
|
|
|
|
|
maxPercent *= 1.2 // 不要让某一项100%
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-12-06 08:55:47 +08:00
|
|
|
|
let screenIsNarrow = window.innerWidth < 512
|
|
|
|
|
|
|
2021-12-05 18:59:20 +08:00
|
|
|
|
return {
|
2021-12-05 19:38:14 +08:00
|
|
|
|
isAttack: isAttack,
|
2021-12-05 18:59:20 +08:00
|
|
|
|
stats: this.vStats,
|
|
|
|
|
|
chart: null,
|
|
|
|
|
|
minOpacity: 0.2,
|
2021-12-05 19:38:14 +08:00
|
|
|
|
maxPercent: maxPercent,
|
2021-12-06 08:55:47 +08:00
|
|
|
|
selectedCountryName: "",
|
|
|
|
|
|
screenIsNarrow: screenIsNarrow
|
2021-12-05 18:59:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
render: function () {
|
|
|
|
|
|
this.chart = teaweb.initChart(document.getElementById("traffic-map-box"));
|
|
|
|
|
|
let that = this
|
|
|
|
|
|
this.chart.setOption({
|
|
|
|
|
|
backgroundColor: "white",
|
2021-12-05 19:38:14 +08:00
|
|
|
|
grid: {
|
|
|
|
|
|
top: 0,
|
|
|
|
|
|
bottom: 0,
|
|
|
|
|
|
left: 0,
|
|
|
|
|
|
right: 0
|
|
|
|
|
|
},
|
2021-12-06 08:55:47 +08:00
|
|
|
|
roam: false,
|
2021-12-05 18:59:20 +08:00
|
|
|
|
tooltip: {
|
|
|
|
|
|
trigger: "item"
|
|
|
|
|
|
},
|
|
|
|
|
|
series: [{
|
|
|
|
|
|
type: "map",
|
|
|
|
|
|
map: "world",
|
2021-12-06 08:55:47 +08:00
|
|
|
|
zoom: 1.3,
|
2021-12-05 18:59:20 +08:00
|
|
|
|
selectedMode: false,
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
areaColor: "#E9F0F9",
|
2021-12-05 20:59:07 +08:00
|
|
|
|
borderColor: "#DDD"
|
2021-12-05 18:59:20 +08:00
|
|
|
|
},
|
|
|
|
|
|
emphasis: {
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
areaColor: "#8B9BD3",
|
|
|
|
|
|
opacity: 1.0
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
//select: {itemStyle:{ areaColor: "#8B9BD3", opacity: 0.8 }},
|
|
|
|
|
|
tooltip: {
|
|
|
|
|
|
formatter: function (args) {
|
|
|
|
|
|
let name = args.name
|
|
|
|
|
|
let stat = null
|
|
|
|
|
|
that.stats.forEach(function (v) {
|
|
|
|
|
|
if (v.name == name) {
|
|
|
|
|
|
stat = v
|
|
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
if (stat != null) {
|
2021-12-05 19:38:14 +08:00
|
|
|
|
return name + "<br/>流量:" + stat.formattedBytes + "<br/>流量占比:" + stat.percent + "%<br/>请求数:" + stat.formattedCountRequests + "<br/>攻击数:" + stat.formattedCountAttackRequests
|
2021-12-05 18:59:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
return name
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
data: this.stats.map(function (v) {
|
|
|
|
|
|
let opacity = parseFloat(v.percent) / that.maxPercent
|
|
|
|
|
|
if (opacity < that.minOpacity) {
|
|
|
|
|
|
opacity = that.minOpacity
|
|
|
|
|
|
}
|
|
|
|
|
|
let fullOpacity = opacity * 3
|
|
|
|
|
|
if (fullOpacity > 1) {
|
|
|
|
|
|
fullOpacity = 1
|
|
|
|
|
|
}
|
2021-12-05 19:38:14 +08:00
|
|
|
|
let isAttack = that.vIsAttack
|
2021-12-05 18:59:20 +08:00
|
|
|
|
let bgColor = "#276AC6"
|
|
|
|
|
|
if (isAttack) {
|
|
|
|
|
|
bgColor = "#B03A5B"
|
|
|
|
|
|
}
|
2021-12-05 19:38:14 +08:00
|
|
|
|
|
2021-12-05 18:59:20 +08:00
|
|
|
|
return {
|
|
|
|
|
|
name: v.name,
|
|
|
|
|
|
value: v.bytes,
|
|
|
|
|
|
percent: parseFloat(v.percent),
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
areaColor: bgColor,
|
|
|
|
|
|
opacity: opacity
|
|
|
|
|
|
},
|
|
|
|
|
|
emphasis: {
|
|
|
|
|
|
itemStyle: {
|
|
|
|
|
|
areaColor: bgColor,
|
|
|
|
|
|
opacity: fullOpacity
|
|
|
|
|
|
}
|
2021-12-05 19:38:14 +08:00
|
|
|
|
},
|
|
|
|
|
|
label: {
|
|
|
|
|
|
show: false,
|
|
|
|
|
|
formatter: function (args) {
|
|
|
|
|
|
if (args.name == that.selectedCountryName) {
|
|
|
|
|
|
return args.name
|
|
|
|
|
|
}
|
|
|
|
|
|
return ""
|
|
|
|
|
|
},
|
|
|
|
|
|
fontSize: "10px",
|
|
|
|
|
|
color: "#fff",
|
|
|
|
|
|
backgroundColor: "#8B9BD3",
|
|
|
|
|
|
padding: [2, 2, 2, 2]
|
2021-12-05 18:59:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}),
|
|
|
|
|
|
nameMap: window.WorldCountriesMap
|
|
|
|
|
|
}]
|
|
|
|
|
|
})
|
|
|
|
|
|
this.chart.resize()
|
|
|
|
|
|
},
|
2021-12-06 08:55:47 +08:00
|
|
|
|
selectCountry: function (countryName) {
|
2021-12-05 18:59:20 +08:00
|
|
|
|
if (this.chart == null) {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
let option = this.chart.getOption()
|
|
|
|
|
|
let that = this
|
|
|
|
|
|
option.series[0].data.forEach(function (v) {
|
|
|
|
|
|
let opacity = v.percent / that.maxPercent
|
|
|
|
|
|
if (opacity < that.minOpacity) {
|
|
|
|
|
|
opacity = that.minOpacity
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (v.name == countryName) {
|
|
|
|
|
|
if (v.isSelected) {
|
|
|
|
|
|
v.itemStyle.opacity = opacity
|
|
|
|
|
|
v.isSelected = false
|
2021-12-05 19:38:14 +08:00
|
|
|
|
v.label.show = false
|
|
|
|
|
|
that.selectedCountryName = ""
|
2021-12-05 18:59:20 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
v.isSelected = true
|
2021-12-05 19:38:14 +08:00
|
|
|
|
that.selectedCountryName = countryName
|
2021-12-05 18:59:20 +08:00
|
|
|
|
opacity *= 3
|
|
|
|
|
|
if (opacity > 1) {
|
|
|
|
|
|
opacity = 1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 至少是0.5,让用户能够看清
|
|
|
|
|
|
if (opacity < 0.5) {
|
|
|
|
|
|
opacity = 0.5
|
|
|
|
|
|
}
|
|
|
|
|
|
v.itemStyle.opacity = opacity
|
2021-12-05 19:38:14 +08:00
|
|
|
|
v.label.show = true
|
2021-12-05 18:59:20 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
v.itemStyle.opacity = opacity
|
|
|
|
|
|
v.isSelected = false
|
2021-12-05 19:38:14 +08:00
|
|
|
|
v.label.show = false
|
2021-12-05 18:59:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
})
|
|
|
|
|
|
this.chart.setOption(option)
|
2021-12-06 08:55:47 +08:00
|
|
|
|
},
|
|
|
|
|
|
select: function (args) {
|
|
|
|
|
|
this.selectCountry(args.countryName)
|
2021-12-05 18:59:20 +08:00
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
template: `<div>
|
2021-12-06 08:55:47 +08:00
|
|
|
|
<table style="width: 100%; border: 0; padding: 0; margin: 0">
|
|
|
|
|
|
<tbody>
|
|
|
|
|
|
<tr>
|
2021-12-05 18:59:20 +08:00
|
|
|
|
<td>
|
2021-12-06 08:55:47 +08:00
|
|
|
|
<div class="traffic-map-box" id="traffic-map-box"></div>
|
2021-12-05 18:59:20 +08:00
|
|
|
|
</td>
|
2021-12-06 08:55:47 +08:00
|
|
|
|
<td style="width: 14em" v-if="!screenIsNarrow">
|
|
|
|
|
|
<traffic-map-box-table :v-stats="stats" :v-is-attack="isAttack" @select="select"></traffic-map-box-table>
|
2021-12-05 18:59:20 +08:00
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
2021-12-06 08:55:47 +08:00
|
|
|
|
</tbody>
|
|
|
|
|
|
<tbody v-if="screenIsNarrow">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td colspan="2">
|
|
|
|
|
|
<traffic-map-box-table :v-stats="stats" :v-is-attack="isAttack" @select="select"></traffic-map-box-table>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</tbody>
|
2021-12-05 18:59:20 +08:00
|
|
|
|
</table>
|
|
|
|
|
|
</div>`
|
2021-12-06 08:55:47 +08:00
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
|
|
Vue.component("traffic-map-box-table", {
|
|
|
|
|
|
props: ["v-stats", "v-is-attack"],
|
|
|
|
|
|
data: function () {
|
|
|
|
|
|
return {
|
|
|
|
|
|
stats: this.vStats,
|
|
|
|
|
|
isAttack: this.vIsAttack
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
methods: {
|
|
|
|
|
|
select: function (countryName) {
|
|
|
|
|
|
this.$emit("select", {countryName: countryName})
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
template: `<div style="overflow-y: auto; max-height: 16em" class="narrow-scrollbar">
|
|
|
|
|
|
<table class="ui table selectable">
|
|
|
|
|
|
<thead>
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<th colspan="2">国家/地区排行 <tip-icon content="只有开启了统计的服务才会有记录。"></tip-icon></th>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</thead>
|
|
|
|
|
|
<tbody v-if="stats.length == 0">
|
|
|
|
|
|
<tr>
|
|
|
|
|
|
<td colspan="2">暂无数据</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
<tbody>
|
|
|
|
|
|
<tr v-for="(stat, index) in stats.slice(0, 10)">
|
|
|
|
|
|
<td @click.prevent="select(stat.name)" style="cursor: pointer" colspan="2">
|
|
|
|
|
|
<div class="ui progress bar" :class="{red: vIsAttack, blue:!vIsAttack}" style="margin-bottom: 0.3em">
|
|
|
|
|
|
<div class="bar" style="min-width: 0; height: 4px;" :style="{width: stat.percent + '%'}"></div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div>{{stat.name}}</div>
|
|
|
|
|
|
<div><span class="grey">{{stat.percent}}% </span>
|
|
|
|
|
|
<span class="small grey" v-if="isAttack">{{stat.formattedCountAttackRequests}}</span>
|
|
|
|
|
|
<span class="small grey" v-if="!isAttack">({{stat.formattedBytes}})</span></div>
|
|
|
|
|
|
</td>
|
|
|
|
|
|
</tr>
|
|
|
|
|
|
</tbody>
|
|
|
|
|
|
</table>
|
|
|
|
|
|
</div>`
|
2021-12-05 18:59:20 +08:00
|
|
|
|
})
|