Initial commit
This commit is contained in:
23
.gitignore
vendored
23
.gitignore
vendored
@@ -1,23 +0,0 @@
|
||||
.DS_Store
|
||||
node_modules
|
||||
/dist
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.*.local
|
||||
|
||||
# Log files
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
package-lock.json
|
||||
10988
package-lock.json
generated
Normal file
10988
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -2,13 +2,16 @@
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!-- 兼容旧版IE浏览器 -->
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<!-- 显示窗口的设置 -->
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1.0">
|
||||
<title>DNS</title>
|
||||
<script type="text/javascript">
|
||||
window.g = {
|
||||
// baseURL:'172.16.0.120:31540', //开发环境
|
||||
baseURL: '124.221.228.62:2525', //正式环境
|
||||
// baseURL: '124.221.228.62:2525', //正式环境
|
||||
baseURL: '124.221.228.62:12526', //正式环境
|
||||
// queryTags:[],
|
||||
// switchLanguage:"en",
|
||||
switchLanguage:"zh"
|
||||
|
||||
@@ -13,6 +13,7 @@ export default {
|
||||
//目标信息获取
|
||||
targetQueryList:'/target/',
|
||||
targetMap:'target/map',
|
||||
targetNodes: 'target/nodes',
|
||||
//目标信息筛选基础数据
|
||||
targetFilter:'/target/filter',
|
||||
//目标信息时延
|
||||
|
||||
1
src/api/world_v2.json
Normal file
1
src/api/world_v2.json
Normal file
File diff suppressed because one or more lines are too long
@@ -33,6 +33,7 @@ export default {
|
||||
if (!appRef) return
|
||||
// 当前宽高比
|
||||
const currentRate = parseFloat((window.innerWidth / window.innerHeight).toFixed(5))
|
||||
console.log("currentSize: ", window.innerWidth, "++++", window.innerHeight)
|
||||
if (appRef) {
|
||||
if (currentRate > baseProportion) {
|
||||
// 表示更宽
|
||||
|
||||
BIN
src/img/logo2.png
Normal file
BIN
src/img/logo2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 13 KiB |
BIN
src/img/logo_new.png
Normal file
BIN
src/img/logo_new.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 402 KiB |
BIN
src/img/logo_new_2.png
Normal file
BIN
src/img/logo_new_2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 395 KiB |
@@ -3,16 +3,17 @@
|
||||
<div class="headNew" >
|
||||
<div class="headNewLeft" >
|
||||
<div class="headNewLeftText">
|
||||
<!-- <span>YYDNS</span>-->
|
||||
<!-- <img src="../img/logyy.jpg">-->
|
||||
<img src="../img/logyy_美图抠图20240424.png" style="width: 180px">
|
||||
<!-- YYDNS图标 -->
|
||||
<!-- <img src="../img/logo2.png" style="width: 300px;margin-top:1%"> -->
|
||||
<!-- 信工所 + DNS 图标 -->
|
||||
<img src="../img/logo_new_2.png" style="width: 300px;margin-top:6%;margin-left:10%">
|
||||
</div>
|
||||
</div>
|
||||
<div class="headNewCenter" >
|
||||
<!-- <div class="headNewCenter" >
|
||||
<div class="headNewCenterText">
|
||||
<span style="color: rgb(0,0,0,0);">{{ title || 'Dashboard' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="headNewRight" >
|
||||
<div class="headNewRightAll" >
|
||||
<img src="../img/user.png" style="margin-top: 0.5%;height: 10%;width: 10%">
|
||||
@@ -32,9 +33,6 @@
|
||||
export default {
|
||||
name: "AppHeader",
|
||||
props:['title','fTag','fInput','fFrom','FIsConfigQuery'],
|
||||
// components:{
|
||||
// router
|
||||
// },
|
||||
data(){
|
||||
return{
|
||||
userName: localStorage.getItem('username'),
|
||||
@@ -81,12 +79,6 @@
|
||||
rules:{
|
||||
},
|
||||
};
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
watch: {
|
||||
|
||||
},
|
||||
created() {
|
||||
if (sessionStorage.getItem('switchLanguage') !== null) {
|
||||
@@ -109,7 +101,6 @@
|
||||
// this.getallLanguage()
|
||||
// this.getallTags()
|
||||
// this.getallWebs()
|
||||
|
||||
},
|
||||
methods:{
|
||||
logOut() {
|
||||
@@ -119,7 +110,6 @@
|
||||
this.$router.push({path:'/'})
|
||||
},
|
||||
ChangeLanguage(){
|
||||
|
||||
if(this.switchLanguage==='en'){
|
||||
// console.log("当前为英文页面 要换成中文页面")
|
||||
sessionStorage.setItem("switchLanguage","zh");
|
||||
@@ -338,7 +328,8 @@
|
||||
/*margin-top: 3.1%;*/
|
||||
margin-top: -8%;
|
||||
/*margin-left: 35%;*/
|
||||
margin-left: 10%;
|
||||
margin-left: 5%;
|
||||
float: left
|
||||
}
|
||||
}
|
||||
.headNewCenter{
|
||||
@@ -351,7 +342,6 @@
|
||||
font-size: 25px;
|
||||
margin-top: 1.6%;
|
||||
}
|
||||
|
||||
}
|
||||
.headNewRight{
|
||||
position: absolute;
|
||||
|
||||
@@ -3,11 +3,13 @@ import App from './App.vue'
|
||||
import router from './router'
|
||||
import store from './store'
|
||||
import './assets/css/iconfont/iconfont.js'
|
||||
import ElementUI from 'element-ui';
|
||||
import ElementUI from 'element-ui';
|
||||
import 'element-ui/lib/theme-chalk/index.css';
|
||||
import * as echarts from 'echarts';
|
||||
import { Message, Notification } from 'element-ui';
|
||||
|
||||
// // 引入 Echarts 和世界地图数据
|
||||
import 'echarts/map/js/world.js'
|
||||
|
||||
Vue.use(ElementUI);
|
||||
import './assets/css/iconfont/iconfont.css'
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
bing: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
console.log(val)
|
||||
console.log("val: " + val)
|
||||
this.bingfn(val)
|
||||
}
|
||||
},
|
||||
@@ -63,208 +63,209 @@
|
||||
myChart1.off('mouseover')
|
||||
|
||||
var option = {
|
||||
animation:false, //动态展示
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
bottom: '5%',
|
||||
containLabel: true
|
||||
},
|
||||
// title: {
|
||||
// text:"898989"
|
||||
// },
|
||||
baseOption: {
|
||||
timeline: {
|
||||
axisType: 'category', // 轴的类型
|
||||
show: true, // 显示组件
|
||||
autoPlay: true, // 自动播放
|
||||
playInterval: 5000, // 播放间隔
|
||||
data: ['IPv6', 'DNSSEC', 'DoT', 'DoH'],
|
||||
top:'3%', // timeline组件离容器上侧的距离
|
||||
left:'30%',
|
||||
right:'30%',
|
||||
symbol: 'none', // 设置标记的图形
|
||||
lineStyle: {
|
||||
show: false // 不显示轴线
|
||||
},
|
||||
controlStyle: {
|
||||
show: false
|
||||
},
|
||||
label: {
|
||||
fontSize: 15,
|
||||
fontWeight: 'bold',
|
||||
color: '#DCDCDC'
|
||||
},
|
||||
checkpointStyle: { // 『当前项』(checkpoint)的图形样式
|
||||
symbol: 'none',
|
||||
},
|
||||
progress: {
|
||||
label: {
|
||||
fontSize: 20,
|
||||
fontWeight: 'bolder',
|
||||
color: '#F5F5F5'
|
||||
}
|
||||
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data.xdata,
|
||||
// data: ["2024-1-1","2024-1-2","2024-1-3","2024-1-4","2024-1-5","2024-1-6","2024-1-7"],
|
||||
axisLine:{
|
||||
lineStyle:{
|
||||
color:'#f6fbfd'
|
||||
},
|
||||
emphasis: {
|
||||
label: {
|
||||
fontSize: 20,
|
||||
}
|
||||
}
|
||||
},
|
||||
axisLabel:{
|
||||
interval:0,
|
||||
show: true
|
||||
}
|
||||
|
||||
},
|
||||
// tooltip: {
|
||||
// trigger: 'axis',
|
||||
// // 添加样式
|
||||
// textStyle: {
|
||||
// textAlign: 'left' // 内容左对齐
|
||||
// }
|
||||
// },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
bottom: '5%',
|
||||
containLabel: true
|
||||
},
|
||||
// 使用 formatter 函数设置内容和样式
|
||||
formatter: function(params) {
|
||||
var result = '<div style="text-align:left;">'; // 左对齐样式
|
||||
result += params[0].name + '<br>'; // 添加横坐标名称
|
||||
|
||||
params.forEach(function(item) {
|
||||
result += item.marker + ' ' + item.seriesName + ': ' + item.value + '<br>'; // 添加数据项
|
||||
});
|
||||
|
||||
result += '</div>';
|
||||
return result;
|
||||
}
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '分类统计(个)', // 水量单位
|
||||
position: 'left', // 显示在左侧
|
||||
splitLine: {
|
||||
show: false,
|
||||
lineStyle: {
|
||||
color: "rgb(255,16,18)",
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: data.xdata,
|
||||
// data: ["2024-1-1","2024-1-2","2024-1-3","2024-1-4","2024-1-5","2024-1-6","2024-1-7"],
|
||||
axisLine:{
|
||||
lineStyle:{
|
||||
color:'#f6fbfd'
|
||||
}
|
||||
},
|
||||
axisLabel:{
|
||||
interval:0,
|
||||
show: true
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#f4f2fd'
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'value',
|
||||
name: '总量统计(个)', // 数量单位
|
||||
position: 'right', // 显示在右侧
|
||||
splitLine: {
|
||||
show: false,
|
||||
lineStyle: {
|
||||
color: "rgb(255,16,18)",
|
||||
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '数量统计(个)', // 水量单位
|
||||
position: 'left', // 显示在左侧
|
||||
splitLine: {
|
||||
show: false,
|
||||
lineStyle: {
|
||||
color: "rgb(255,16,18)",
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#f4f2fd'
|
||||
}
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#f4be6a'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
// yAxis: {
|
||||
// type: 'value',
|
||||
// splitLine: {//分割线配置
|
||||
// show:false,
|
||||
// lineStyle: {
|
||||
// color: "rgb(255,16,18)",
|
||||
// }
|
||||
// },
|
||||
// axisLine:{
|
||||
// lineStyle:{
|
||||
// color:'#fdc909'
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// yAxis:[
|
||||
// {
|
||||
// type:'value',
|
||||
// name:'分类数量',
|
||||
// interval: 5,
|
||||
// axisLabel: {
|
||||
// formatter:'{value}个'
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// type:'value',
|
||||
// name:'总量',
|
||||
// interval: 5,
|
||||
// axisLabel: {
|
||||
// formatter:'{value}个'
|
||||
// }
|
||||
// },
|
||||
//
|
||||
// ],
|
||||
series: [
|
||||
{
|
||||
name:'IPv6',
|
||||
barWidth: 20, //柱子宽度
|
||||
data:data.ydata1,
|
||||
// data:[45,89,56,58,66,12,96],
|
||||
color:["#2386bf"], //自定义颜色
|
||||
type: 'bar',
|
||||
label:{
|
||||
show:true,
|
||||
position:'top',
|
||||
formatter:function (params) {
|
||||
return params.value;
|
||||
}
|
||||
}
|
||||
// {
|
||||
// type: 'value',
|
||||
// name: '总量统计(个)', // 数量单位
|
||||
// position: 'right', // 显示在右侧
|
||||
// splitLine: {
|
||||
// show: false,
|
||||
// lineStyle: {
|
||||
// color: "rgb(255,16,18)",
|
||||
// }
|
||||
// },
|
||||
// axisLine: {
|
||||
// lineStyle: {
|
||||
// color: '#f4be6a'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
],
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
// 使用 formatter 函数设置内容和样式
|
||||
formatter: function(params) {
|
||||
var result = '<div style="text-align:left;">'; // 左对齐样式
|
||||
result += params[0].name + '<br>'; // 添加横坐标名称
|
||||
|
||||
params.forEach(function(item) {
|
||||
result += item.marker + ' ' + item.seriesName + ': ' + item.value + '<br>'; // 添加数据项
|
||||
});
|
||||
|
||||
result += '</div>';
|
||||
return result;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
options: [
|
||||
// IPv6
|
||||
{
|
||||
name:'DNS',
|
||||
barWidth: 20, //柱子宽度
|
||||
data:data.ydata2,
|
||||
// data:[450,890,560,580,660,155,85],
|
||||
color:["#434ff4"], //自定义颜色
|
||||
type: 'bar',
|
||||
label:{
|
||||
show:true,
|
||||
position:'top',
|
||||
formatter:function (params) {
|
||||
return params.value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
series: [
|
||||
{
|
||||
name:'IPv6',
|
||||
barWidth: 45, //柱子宽度
|
||||
data:data.ydata1,
|
||||
// data:[45,89,56,58,66,12,96],
|
||||
color:["#2386bf"], //自定义颜色
|
||||
type: 'bar',
|
||||
label:{
|
||||
show:true,
|
||||
position:'top',
|
||||
formatter:function (params) {
|
||||
return params.value;
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
},
|
||||
// DNS
|
||||
{
|
||||
name:'DoT',
|
||||
barWidth: 20, //柱子宽度
|
||||
data:data.ydata3,
|
||||
// data:[150,290,560,380,660,105,815],
|
||||
color:["rgba(58,76,222,0.89)"], //自定义颜色
|
||||
type: 'bar',
|
||||
label:{
|
||||
show:true,
|
||||
position:'top',
|
||||
formatter:function (params) {
|
||||
return params.value;
|
||||
}
|
||||
}
|
||||
|
||||
series: [
|
||||
{
|
||||
name:'DNS',
|
||||
barWidth: 45, //柱子宽度
|
||||
data:data.ydata2,
|
||||
// data:[450,890,560,580,660,155,85],
|
||||
color:["#434ff4"], //自定义颜色
|
||||
type: 'bar',
|
||||
label:{
|
||||
show:true,
|
||||
position:'top',
|
||||
formatter:function (params) {
|
||||
return params.value;
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
// DoT
|
||||
{
|
||||
name:'DoH',
|
||||
barWidth: 20, //柱子宽度
|
||||
data:data.ydata4,
|
||||
// data:[650,790,560,180,660,15,85],
|
||||
color:["#117cf4"], //自定义颜色
|
||||
type: 'bar',
|
||||
label:{
|
||||
show:true,
|
||||
position:'top',
|
||||
formatter:function (params) {
|
||||
return params.value;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
,
|
||||
series: [
|
||||
{
|
||||
name:'DoT',
|
||||
barWidth: 45, //柱子宽度
|
||||
data:data.ydata3,
|
||||
// data:[150,290,560,380,660,105,815],
|
||||
color:["rgba(58,76,222,0.89)"], //自定义颜色
|
||||
type: 'bar',
|
||||
label:{
|
||||
show:true,
|
||||
position:'top',
|
||||
formatter:function (params) {
|
||||
return params.value;
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
},
|
||||
// DoH
|
||||
{
|
||||
name:'总量',
|
||||
barWidth: 20, //柱子宽度
|
||||
data:data.ydata5,
|
||||
// data:[1120,7090,5660,1800,6060,105,805],
|
||||
color:["#f4be6a"], //自定义颜色
|
||||
type: 'line',
|
||||
yAxisIndex: 1,
|
||||
label:{
|
||||
show:true,
|
||||
position:'top',
|
||||
formatter:function (params) {
|
||||
return params.value;
|
||||
}
|
||||
}
|
||||
|
||||
series: [
|
||||
{
|
||||
name:'DoH',
|
||||
barWidth: 45, //柱子宽度
|
||||
data:data.ydata4,
|
||||
// data:[650,790,560,180,660,15,85],
|
||||
color:["#117cf4"], //自定义颜色
|
||||
type: 'bar',
|
||||
label:{
|
||||
show:true,
|
||||
position:'top',
|
||||
formatter:function (params) {
|
||||
return params.value;
|
||||
}
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
],
|
||||
|
||||
]
|
||||
|
||||
//#region
|
||||
/*
|
||||
graphic: [
|
||||
{
|
||||
type:'group',
|
||||
@@ -388,14 +389,11 @@
|
||||
}
|
||||
|
||||
]
|
||||
*/
|
||||
//#endregion
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
myChart1.setOption(option,true)
|
||||
window.addEventListener("resize", () => {
|
||||
myChart1.resize();
|
||||
|
||||
@@ -23,6 +23,7 @@ export default {
|
||||
components: { TargetView, ImageView, SourceView, NodeView }
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.home {
|
||||
width: 100%;
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="login" ref="appRef" v-if="label==false">
|
||||
<div class="login-box">
|
||||
<p class="title" style="font-size: 48px;margin-top: 20%" @click="label=!label">进入YYDNS系统</p>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login" ref="appRef" v-if="label==true">
|
||||
<!-- 点击YYDNS前 -->
|
||||
<div class="login" ref="appRef" v-if="label==false">
|
||||
<div class="login-box">
|
||||
<p class="title" style="font-size: 48px;margin-top: 20%" @click="label=!label">进入YYDNS系统</p>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 点击YYDNS后 -->
|
||||
<div class="login" ref="appRef" v-if="label==true">
|
||||
<div class="login-box">
|
||||
<p class="title">登录到</p>
|
||||
<p class="title">YYDNS系统</p>
|
||||
@@ -38,7 +40,7 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</template>
|
||||
|
||||
75
src/views/menuMBZTGZ/echarts/nodewordmap.vue
Normal file
75
src/views/menuMBZTGZ/echarts/nodewordmap.vue
Normal file
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<div style="width: 100%;height: 100%;">
|
||||
<div style="width: 100%;height: 10%"><span style="margin-top: 1%;font-size: 20px;margin-left: 2%;float: left">节点分布</span></div>
|
||||
<div ref="worldmap" style="width: 100%;height: 90%"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
|
||||
const worldJson = require("../../../api/world_v2.json")
|
||||
export default {
|
||||
name: "WorldMap",
|
||||
props: {
|
||||
nodes:{
|
||||
type: Array,
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
nodes: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
console.log(val)
|
||||
this.drawMap(val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 调整节点信息格式
|
||||
getInfo(){
|
||||
return this.nodes.map(node => {
|
||||
return [node.Lng, node.Lat]
|
||||
})
|
||||
},
|
||||
// 绘制世界地图
|
||||
drawMap(){
|
||||
// 初始化myChart
|
||||
var myChart = echarts.init(this.$refs.worldmap)
|
||||
// 注册可用的地图
|
||||
echarts.registerMap('world', worldJson)
|
||||
var option = {
|
||||
tooltip: {
|
||||
show: true
|
||||
},
|
||||
geo: {
|
||||
tooltip: {
|
||||
show: true
|
||||
},
|
||||
map: 'world', // 使用registerMap注册的地图名称
|
||||
roam: true, // 开启鼠标缩放和平移漫游
|
||||
},
|
||||
series: {
|
||||
type: 'effectScatter',
|
||||
coordinateSystem: 'geo',
|
||||
itemStyle: {
|
||||
color: '#b02a02'
|
||||
},
|
||||
encode: {
|
||||
tooltip: 2
|
||||
},
|
||||
data: this.getInfo()
|
||||
}
|
||||
}
|
||||
myChart.setOption(option)
|
||||
},
|
||||
},
|
||||
mounted(){
|
||||
this.drawMap()
|
||||
},
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped="scoped">
|
||||
</style>
|
||||
@@ -62,6 +62,100 @@
|
||||
myChart1.clear()
|
||||
myChart1.off('mouseover')
|
||||
|
||||
|
||||
var option = {
|
||||
// dataset: {
|
||||
// source: data.xdata
|
||||
// },
|
||||
grid: {
|
||||
left: '5%',
|
||||
right: '5%',
|
||||
bottom: '5%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: data.xdata,
|
||||
// data: ["2024-1-1","2024-1-2","2024-1-3","2024-1-4","2024-1-5","2024-1-6","2024-1-7"],
|
||||
axisLine:{
|
||||
lineStyle:{
|
||||
color:'#f6fbfd'
|
||||
}
|
||||
},
|
||||
axisLabel:{
|
||||
interval:0,
|
||||
show: true
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow'
|
||||
},
|
||||
// 使用 formatter 函数设置内容和样式
|
||||
formatter: function(params) {
|
||||
var result = '<div style="text-align:left;">'; // 左对齐样式
|
||||
result += params[0].name + '<br>'; // 添加横坐标名称
|
||||
|
||||
params.forEach(function(item) {
|
||||
result += item.marker + ' ' + item.seriesName + ': ' + item.value + '<br>'; // 添加数据项
|
||||
});
|
||||
|
||||
result += '</div>';
|
||||
return result;
|
||||
}
|
||||
},
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
name: '时延(ms)',
|
||||
position: 'left', // 显示在左侧
|
||||
splitLine: {
|
||||
show: false,
|
||||
lineStyle: {
|
||||
color: "rgb(255,16,18)",
|
||||
}
|
||||
},
|
||||
axisLine: {
|
||||
lineStyle: {
|
||||
color: '#f4f2fd'
|
||||
}
|
||||
}
|
||||
},
|
||||
],
|
||||
visualMap: {
|
||||
orient: 'horizontal', // 组件布局方向
|
||||
top: '1%',
|
||||
right: '5%', // 组件位置
|
||||
min:0,
|
||||
max:800, // 映射数据范围
|
||||
// text: ['High Score', 'Low Score'],
|
||||
dimension: 1, // 映射数据的维度
|
||||
inRange: {
|
||||
color: ['#11da11', '#E15457']
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name:'时延',
|
||||
barWidth: 20, //柱子宽度
|
||||
data:data.ydata1,
|
||||
// data:[45,89,56,58,66,12,96],
|
||||
color:["#2386bf"], //自定义颜色
|
||||
type: 'bar',
|
||||
label:{
|
||||
show:true,
|
||||
position:'top',
|
||||
formatter:function (params) {
|
||||
return params.value;
|
||||
}
|
||||
}
|
||||
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
/*
|
||||
var option = {
|
||||
animation:false, //动态展示
|
||||
grid: {
|
||||
@@ -87,6 +181,7 @@
|
||||
interval:0,
|
||||
show: true
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
tooltip: {
|
||||
@@ -267,6 +362,7 @@
|
||||
//
|
||||
// ]
|
||||
};
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,419 +0,0 @@
|
||||
<template>
|
||||
<div style="width: 100%;height: 100%;">
|
||||
<div style="width: 100%;height: 10%"><span style="margin-top: 1%;font-size: 20px;margin-left: 2%;float: left">时延报警</span></div>
|
||||
<div ref="bingbox" style="width: 100%;height: 90%"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
var myChart1=null
|
||||
export default {
|
||||
props:{
|
||||
bing:{
|
||||
type:Object,
|
||||
default: function() {
|
||||
return {
|
||||
data:[],
|
||||
// signdata: [],
|
||||
// title:"",
|
||||
// bingtype:'',
|
||||
// AreaName:[],
|
||||
}
|
||||
}
|
||||
},
|
||||
active:{
|
||||
type:Number,
|
||||
default: function() {
|
||||
return {
|
||||
active:""
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
bing: {
|
||||
deep: true,
|
||||
handler(val) {
|
||||
console.log(val)
|
||||
this.bingfn(val)
|
||||
}
|
||||
},
|
||||
},
|
||||
data(){
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
bingfn(data){
|
||||
console.log(data)
|
||||
let that = this
|
||||
//当前视口宽度
|
||||
let nowClientWidth = document.documentElement.clientWidth;
|
||||
// 换算方法
|
||||
let nowSize = function (val, initWidth = 1920) {
|
||||
return val * (nowClientWidth / initWidth);
|
||||
};
|
||||
if (myChart1 != null) {
|
||||
myChart1.clear()//销毁
|
||||
}
|
||||
myChart1= echarts.init(this.$refs.bingbox)
|
||||
myChart1.clear()
|
||||
myChart1.off('mouseover')
|
||||
|
||||
// var option = {
|
||||
// animation:false, //动态展示
|
||||
// grid: {
|
||||
// left: '5%',
|
||||
// right: '5%',
|
||||
// bottom: '5%',
|
||||
// containLabel: true
|
||||
// },
|
||||
// xAxis: {
|
||||
// type: 'category',
|
||||
// data: data.xdata,
|
||||
// // data: ["2024-1-1","2024-1-2","2024-1-3","2024-1-4","2024-1-5","2024-1-6","2024-1-7"],
|
||||
// axisLine:{
|
||||
// lineStyle:{
|
||||
// color:'#f6fbfd'
|
||||
// }
|
||||
// },
|
||||
// axisLabel:{
|
||||
// interval:0,
|
||||
// show: true
|
||||
// }
|
||||
//
|
||||
// },
|
||||
// tooltip: {
|
||||
// trigger: 'axis',
|
||||
// axisPointer: {
|
||||
// type: 'shadow'
|
||||
// },
|
||||
// // 使用 formatter 函数设置内容和样式
|
||||
// formatter: function(params) {
|
||||
// var result = '<div style="text-align:left;">'; // 左对齐样式
|
||||
// result += params[0].name + '<br>'; // 添加横坐标名称
|
||||
//
|
||||
// params.forEach(function(item) {
|
||||
// result += item.marker + ' ' + item.seriesName + ': ' + item.value + '<br>'; // 添加数据项
|
||||
// });
|
||||
//
|
||||
// result += '</div>';
|
||||
// return result;
|
||||
// }
|
||||
// },
|
||||
// yAxis: [
|
||||
// {
|
||||
// type: 'value',
|
||||
// name: '分类统计(个)', // 水量单位
|
||||
// position: 'left', // 显示在左侧
|
||||
// splitLine: {
|
||||
// show: false,
|
||||
// lineStyle: {
|
||||
// color: "rgb(255,16,18)",
|
||||
// }
|
||||
// },
|
||||
// axisLine: {
|
||||
// lineStyle: {
|
||||
// color: '#f4f2fd'
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// {
|
||||
// type: 'value',
|
||||
// name: '总量统计(个)', // 数量单位
|
||||
// position: 'right', // 显示在右侧
|
||||
// splitLine: {
|
||||
// show: false,
|
||||
// lineStyle: {
|
||||
// color: "rgb(255,16,18)",
|
||||
// }
|
||||
// },
|
||||
// axisLine: {
|
||||
// lineStyle: {
|
||||
// color: '#f4be6a'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// series: [
|
||||
// {
|
||||
// name:'IPv6',
|
||||
// barWidth: 20, //柱子宽度
|
||||
// data:data.ydata1,
|
||||
// // data:[45,89,56,58,66,12,96],
|
||||
// color:["#2386bf"], //自定义颜色
|
||||
// type: 'bar',
|
||||
// label:{
|
||||
// show:true,
|
||||
// position:'top',
|
||||
// formatter:function (params) {
|
||||
// return params.value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// },
|
||||
// {
|
||||
// name:'DNS',
|
||||
// barWidth: 20, //柱子宽度
|
||||
// data:data.ydata2,
|
||||
// // data:[450,890,560,580,660,155,85],
|
||||
// color:["#434ff4"], //自定义颜色
|
||||
// type: 'bar',
|
||||
// label:{
|
||||
// show:true,
|
||||
// position:'top',
|
||||
// formatter:function (params) {
|
||||
// return params.value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// },
|
||||
// {
|
||||
// name:'DoT',
|
||||
// barWidth: 20, //柱子宽度
|
||||
// data:data.ydata3,
|
||||
// // data:[150,290,560,380,660,105,815],
|
||||
// color:["rgba(58,76,222,0.89)"], //自定义颜色
|
||||
// type: 'bar',
|
||||
// label:{
|
||||
// show:true,
|
||||
// position:'top',
|
||||
// formatter:function (params) {
|
||||
// return params.value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// },
|
||||
// {
|
||||
// name:'DoH',
|
||||
// barWidth: 20, //柱子宽度
|
||||
// data:data.ydata4,
|
||||
// // data:[650,790,560,180,660,15,85],
|
||||
// color:["#117cf4"], //自定义颜色
|
||||
// type: 'bar',
|
||||
// label:{
|
||||
// show:true,
|
||||
// position:'top',
|
||||
// formatter:function (params) {
|
||||
// return params.value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// ,
|
||||
// {
|
||||
// name:'总量',
|
||||
// barWidth: 20, //柱子宽度
|
||||
// data:data.ydata5,
|
||||
// // data:[1120,7090,5660,1800,6060,105,805],
|
||||
// color:["#f4be6a"], //自定义颜色
|
||||
// type: 'line',
|
||||
// yAxisIndex: 1,
|
||||
// label:{
|
||||
// show:true,
|
||||
// position:'top',
|
||||
// formatter:function (params) {
|
||||
// return params.value;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// }
|
||||
// ],
|
||||
// graphic: [
|
||||
// {
|
||||
// type:'group',
|
||||
// left:'center',
|
||||
// top:25,
|
||||
// children:[
|
||||
// {
|
||||
// type: 'rect',
|
||||
// shape: {
|
||||
// width: 20,
|
||||
// height: 20
|
||||
// },
|
||||
// style: {
|
||||
// fill: '#2386bf' // 设置第一个色块的颜色
|
||||
// },
|
||||
// z: 100
|
||||
// },
|
||||
// {
|
||||
// type: 'text',
|
||||
// left: 25,
|
||||
// style: {
|
||||
// text: 'IPv6', // 设置第一个色块的含义
|
||||
// fill: '#2386bf',
|
||||
// fontSize: 12
|
||||
// },
|
||||
// z: 100
|
||||
// },
|
||||
// ////////////////
|
||||
// {
|
||||
// type: 'rect',
|
||||
// left: 75,
|
||||
// shape: {
|
||||
// width: 20,
|
||||
// height: 20
|
||||
// },
|
||||
// style: {
|
||||
// fill: '#434ff4' // 设置第二个色块的颜色
|
||||
// },
|
||||
// z: 100
|
||||
// },
|
||||
// {
|
||||
// type: 'text',
|
||||
// left: 100,
|
||||
// style: {
|
||||
// text: 'DNS', // 设置第二个色块的含义
|
||||
// fill: '#434ff4',
|
||||
// fontSize: 12
|
||||
// },
|
||||
// z: 100
|
||||
// },
|
||||
// ///////////////
|
||||
// {
|
||||
// type: 'rect',
|
||||
// left:150 ,
|
||||
// shape: {
|
||||
// width: 20,
|
||||
// height: 20
|
||||
// },
|
||||
// style: {
|
||||
// fill: 'rgba(58,76,222,0.89)' // 设置第二个色块的颜色
|
||||
// },
|
||||
// z: 100
|
||||
// },
|
||||
// {
|
||||
// type: 'text',
|
||||
// left: 175,
|
||||
// style: {
|
||||
// text: 'DoT', // 设置第二个色块的含义
|
||||
// fill: 'rgba(58,76,222,0.89)',
|
||||
// fontSize: 12
|
||||
// },
|
||||
// z: 100
|
||||
// },
|
||||
// /////////////////////////////////
|
||||
// {
|
||||
// type: 'rect',
|
||||
// left: 225,
|
||||
// shape: {
|
||||
// width: 20,
|
||||
// height: 20
|
||||
// },
|
||||
// style: {
|
||||
// fill: '#117cf4' // 设置第二个色块的颜色
|
||||
// },
|
||||
// z: 100
|
||||
// },
|
||||
// {
|
||||
// type: 'text',
|
||||
// left: 250,
|
||||
// style: {
|
||||
// text: 'DoH', // 设置第二个色块的含义
|
||||
// fill: '#117cf4',
|
||||
// fontSize: 12
|
||||
// },
|
||||
// z: 100
|
||||
// },
|
||||
// // //////////////////////////////
|
||||
// // {
|
||||
// // type: 'rect',
|
||||
// // left: 300,
|
||||
// // shape: {
|
||||
// // width: 20,
|
||||
// // height: 20
|
||||
// // },
|
||||
// // style: {
|
||||
// // fill: '#f4be6a' // 设置第二个色块的颜色
|
||||
// // },
|
||||
// // z: 100
|
||||
// // },
|
||||
// // {
|
||||
// // type: 'text',
|
||||
// // left: 325,
|
||||
// // style: {
|
||||
// // text: '五线', // 设置第二个色块的含义
|
||||
// // fill: '#f4be6a',
|
||||
// // fontSize: 12
|
||||
// // },
|
||||
// // z: 100
|
||||
// // }
|
||||
// ]
|
||||
// }
|
||||
//
|
||||
// ]
|
||||
// };
|
||||
|
||||
var option = {
|
||||
dataset: {
|
||||
source:data.data
|
||||
// source: [
|
||||
// ['延时', '节点'],
|
||||
// [89.3, 'Matcha Latte'],
|
||||
// [57.1, 'Milk Tea'],
|
||||
// [74.4, 'Cheese Cocoa'],
|
||||
// [50.1, 'Cheese Brownie'],
|
||||
// [89.7, 'Matcha Cocoa'],
|
||||
// [68.1, 'Tea'],
|
||||
// [19.6, 'Orange Juice'],
|
||||
// [10.6, 'Lemon Juice'],
|
||||
// [32.7, 'Walnut Brownie']
|
||||
// ]
|
||||
},
|
||||
grid: {containLabel: true},
|
||||
xAxis: {name: 'amount'},
|
||||
yAxis: {type: 'category'},
|
||||
visualMap: {
|
||||
orient: 'horizontal',
|
||||
left: 'center',
|
||||
min: 0,
|
||||
max: 50,
|
||||
text: ['High Score', 'Low Score'],
|
||||
// Map the score column to color
|
||||
dimension: 0,
|
||||
inRange: {
|
||||
color: ['#11da11', '#E15457']
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'bar',
|
||||
encode: {
|
||||
// Map the "amount" column to X axis.
|
||||
x: 'score',
|
||||
// Map the "product" column to Y axis
|
||||
y: 'product'
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
myChart1.setOption(option,true)
|
||||
window.addEventListener("resize", () => {
|
||||
myChart1.resize();
|
||||
});
|
||||
}
|
||||
},
|
||||
beforeDestroy() {
|
||||
myChart1.clear()
|
||||
},
|
||||
destroyed() {
|
||||
myChart1.clear()
|
||||
},
|
||||
mounted(){
|
||||
// this.bingfn(this.bing)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped="scoped">
|
||||
</style>
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="home" ref="appRef">
|
||||
<div class="show">
|
||||
<div><span style="float: left;font-size: 20px;margin-left: 2%;color: #00C0FF;margin-top: 1%">{{"目标IP:"+parentLevelRow.target}}</span></div>
|
||||
<div><span style="float: left;font-size: 20px;margin-left: 2%;color: #00C0FF;margin-top: 1%">{{"目标IP: "+parentLevelRow.target}}</span></div>
|
||||
<div class="tag">
|
||||
<el-tag class="tags" :style="{'color': (tag==='目标时延') ? '#f8fdff': '#565e6e'}" @click="updateTag('目标时延')">目标时延:</el-tag>
|
||||
<el-tag class="tags1" :style="{'color': (tag1==='ICMP/v6延时') ? '#f8fdff': '#565e6e'}" @click="updateTag1('ICMP/v6延时')">ICMP/v6延时</el-tag>
|
||||
@@ -12,10 +12,9 @@
|
||||
<div v-if="tag==='应答内容'" class="answer">
|
||||
<div style="display: flex;margin-right: 5px;">
|
||||
<span style="margin-right: 2%;font-size: 25px;width: 15%">目标域名:</span>
|
||||
<el-input v-model="input" placeholder="请输入目标IP"></el-input>
|
||||
<el-input v-model="input" placeholder="请输入目标域名"></el-input>
|
||||
</div>
|
||||
<el-button type="primary" style="margin-top: 2%;margin-bottom: 10%">查询</el-button>
|
||||
|
||||
</div>
|
||||
<div class="top" v-if="tag==='目标时延'">
|
||||
<div class="top-left">
|
||||
@@ -52,7 +51,7 @@
|
||||
</div>
|
||||
<!-- <TargetView class="top-right"/>-->
|
||||
</div>
|
||||
<NodeView class="bottom" v-if="tag==='目标时延'"></NodeView>
|
||||
<NodeView class="bottom" v-if="tag==='目标时延'" :taskid=parentLevelRow.id></NodeView>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -62,21 +61,20 @@ import TargetView from './module/target.vue'
|
||||
import ImageView from './module/image.vue'
|
||||
import SourceView from './module/source.vue'
|
||||
import NodeView from './module/node.vue'
|
||||
|
||||
export default {
|
||||
name: 'home',
|
||||
name: 'menuMBZTGZ',
|
||||
components: { TargetView, ImageView, SourceView, NodeView },
|
||||
created() {
|
||||
this.parentLevelRow = this.$route.query.row;
|
||||
this.input=this.parentLevelRow.target_domain;
|
||||
|
||||
|
||||
this.left1data.taskid = this.parentLevelRow.id;
|
||||
this.left1data.target = this.parentLevelRow.target;
|
||||
},
|
||||
// mounted() {
|
||||
// this.$router.push('/range/home')
|
||||
// },
|
||||
methods:{
|
||||
|
||||
|
||||
updateTag(val){
|
||||
if(val==='目标时延'){
|
||||
this.tag=val
|
||||
@@ -85,14 +83,13 @@ export default {
|
||||
this.tag=val
|
||||
this.tag1=""
|
||||
}
|
||||
|
||||
},
|
||||
updateTag1(val){
|
||||
this.tag1=val;
|
||||
this.tag='目标时延';
|
||||
this.left1data.type=this.changePeram(val);
|
||||
this.left1data.target=this.parentLevelRow.target;
|
||||
|
||||
this.left1data.target=this.parentLevelRow.target; // 任务目标IP
|
||||
this.left1data.taskid=this.parentLevelRow.id; // 任务id
|
||||
},
|
||||
changePeram(val){
|
||||
switch (val) {
|
||||
@@ -114,7 +111,8 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
left1data:{
|
||||
target:'1.1.1.1',
|
||||
taskid:'',
|
||||
target:'',
|
||||
type:'icmp',
|
||||
},
|
||||
leftYdata:[],
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="list" >
|
||||
<el-table class="custom-table"
|
||||
ref="multipleTable"
|
||||
:data="tableData"
|
||||
:data="nodes"
|
||||
height="100%"
|
||||
style="width: 100%;"
|
||||
tooltip-effect="dark"
|
||||
@@ -16,42 +16,39 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center"
|
||||
prop="time"
|
||||
label="时间"
|
||||
prop="Id"
|
||||
label="ID"
|
||||
min-width="100">
|
||||
</el-table-column>
|
||||
<el-table-column align="center"
|
||||
prop="level"
|
||||
label="日志级别"
|
||||
prop="Ip"
|
||||
label="IP地址"
|
||||
min-width="100">
|
||||
</el-table-column>
|
||||
<el-table-column align="center" label="日志信息" min-width="300">
|
||||
<template slot-scope="scope" >{{ scope.row.info.substr(0, 80)}}</template>
|
||||
</el-table-column>
|
||||
<el-table-column align="center"
|
||||
prop="user"
|
||||
label="用户账号"
|
||||
prop="Name"
|
||||
label="节点名称"
|
||||
min-width="100">
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column align="center"
|
||||
prop="ip"
|
||||
label="用户IP"
|
||||
prop="Loc"
|
||||
label="节点位置"
|
||||
min-width="100">
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<!-- <el-pagination-->
|
||||
<!-- background-->
|
||||
<!-- :current-page="page"-->
|
||||
<!-- :page-sizes="[10, 20, 30, 40]"-->
|
||||
<!-- :page-size="10"-->
|
||||
<!-- :total="total"-->
|
||||
<!-- layout="total, sizes, prev, pager, next, jumper"-->
|
||||
<!-- @size-change="handleSizeChange"-->
|
||||
<!-- @current-change="handleCurrentChange"-->
|
||||
<!-- >-->
|
||||
<!-- </el-pagination>-->
|
||||
<!-- <el-pagination
|
||||
background
|
||||
:current-page="page"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="10"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
</el-pagination> -->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -64,285 +61,27 @@ export default {
|
||||
name: 'NodeView',
|
||||
data() {
|
||||
return {
|
||||
tableData:[],
|
||||
total:0,
|
||||
page:1,
|
||||
size:10,
|
||||
time1:'',
|
||||
time2:'',
|
||||
log_level:"",
|
||||
userAccount:"",
|
||||
pickerOptions: {
|
||||
shortcuts: [{
|
||||
text: '今天',
|
||||
onClick(picker) {
|
||||
picker.$emit('pick', new Date());
|
||||
}
|
||||
}, {
|
||||
text: '昨天',
|
||||
onClick(picker) {
|
||||
const date = new Date();
|
||||
date.setTime(date.getTime() - 3600 * 1000 * 24);
|
||||
picker.$emit('pick', date);
|
||||
}
|
||||
}, {
|
||||
text: '一周前',
|
||||
onClick(picker) {
|
||||
const date = new Date();
|
||||
date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
|
||||
picker.$emit('pick', date);
|
||||
}
|
||||
}]
|
||||
},
|
||||
|
||||
log_levels:[
|
||||
{
|
||||
value:'ERROR',
|
||||
label:'ERROR'
|
||||
},
|
||||
{
|
||||
value:'INFO',
|
||||
label:'INFO'
|
||||
},
|
||||
{
|
||||
value:'WARNING',
|
||||
label:'WARNING'
|
||||
},
|
||||
],
|
||||
|
||||
|
||||
nodes:[]
|
||||
}
|
||||
},
|
||||
props:['taskid'],
|
||||
created() {
|
||||
this.query()
|
||||
},
|
||||
mounted(){
|
||||
// this.init()
|
||||
},
|
||||
methods: {
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`);
|
||||
this.size=val
|
||||
this.query()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
console.log(`当前页: ${val}`);
|
||||
this.page=val
|
||||
this.query()
|
||||
},
|
||||
// 请求节点信息
|
||||
query(){
|
||||
let data={
|
||||
"per_page":this.size,
|
||||
"page":this.page,
|
||||
"begin":this.time1.toString(),
|
||||
"end":this.time2.toString(),
|
||||
"level":this.log_level,
|
||||
"user":this.userAccount,
|
||||
const reqParams = {
|
||||
"taskid": this.taskid,
|
||||
}
|
||||
// 使用 Object.entries() 来遍历对象的键值对,并过滤掉值为空的属性
|
||||
const filteredData = {};
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
if (value !== "") {
|
||||
filteredData[key] = value;
|
||||
}
|
||||
}
|
||||
this.$axios.get(this.$http.api.sysLog,filteredData).then(res=>{
|
||||
console.log('getImages======',res)
|
||||
if(res.code===200){
|
||||
this.tableData=res?.log_data
|
||||
// this.total=res?.total
|
||||
this.total=100
|
||||
}
|
||||
}).catch(err=>{
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.targetNodes, reqParams).then(res => {
|
||||
this.nodes = res.nodes
|
||||
console.log("Node Page Nodes: ", this.nodes)
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
},
|
||||
// async init() {
|
||||
// await this.getRangeDict()
|
||||
// await this.initData()
|
||||
// this.initEcharts()
|
||||
// },
|
||||
// initData() {
|
||||
// // this.nodeData = getTargetsResponse?.result
|
||||
// // for (const key in this.nodeData) {
|
||||
// // this.nodeDataX.push(key)
|
||||
// // this.nodeDataY.push(this.nodeData[key])
|
||||
// // }
|
||||
//
|
||||
// const reqParams = {}
|
||||
// if (this.target_id) {
|
||||
// reqParams.target_id = this.target_id
|
||||
// }
|
||||
// if (this.activeName !== 'total') {
|
||||
// reqParams.status = this.activeName
|
||||
// }
|
||||
// this.loading = true
|
||||
// return this.$axios.get(this.$http.api.getNodeStatistics, reqParams).then(res => {
|
||||
// if (res.code == 200 || res.code == "OK") {
|
||||
// this.nodeDataX = []
|
||||
// this.nodeDataY = []
|
||||
// this.nodeData = res?.result || {}
|
||||
// for (const key in this.nodeData) {
|
||||
// this.nodeDataX.push(key)
|
||||
// this.nodeDataY.push(this.nodeData[key])
|
||||
// }
|
||||
// }
|
||||
// }).catch(err => {
|
||||
// console.log(err)
|
||||
// }).finally(() => {
|
||||
// this.loading = false
|
||||
// })
|
||||
// },
|
||||
// 获取靶场列表字典
|
||||
// getRangeDict() {
|
||||
// const reqParams = {
|
||||
// page: 1,
|
||||
// size: 99,
|
||||
// }
|
||||
// return this.$axios.get(this.$http.api.getTargets, reqParams).then(res => {
|
||||
// if (res.code == 200 || res.code == "OK") {
|
||||
// this.rangeDict = res?.result?.items.map(item => {
|
||||
// return {
|
||||
// label: item.target_name,
|
||||
// value: item.id
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }).catch(err => {
|
||||
// console.log(err)
|
||||
// }).finally(() => {
|
||||
// this.rangeDict.unshift({label: '全部靶场', value: ''})
|
||||
// this.target_id = this.rangeDict[0].value
|
||||
// this.target_name = this.rangeDict[0].label
|
||||
// })
|
||||
// },
|
||||
// 改变靶场
|
||||
// async changeRange(val) {
|
||||
// this.target_name = this.rangeDict.find(item => item.value === val)?.label
|
||||
// await this.initData()
|
||||
// this.refreshData()
|
||||
// },
|
||||
// 改变靶场状态
|
||||
// async changeRangeStatus() {
|
||||
// await this.initData()
|
||||
// this.refreshData()
|
||||
// },
|
||||
// 刷新图标数据
|
||||
// refreshData(){
|
||||
// if(!this.nodeChart){
|
||||
// return
|
||||
// }
|
||||
// //更新数据
|
||||
// var option = this.nodeChart.getOption()
|
||||
// option.title[0].text = `${this.target_name}节点统计`,
|
||||
// option.xAxis[0].data = this.nodeDataX
|
||||
// option.series[0].data = this.nodeDataY
|
||||
// this.nodeChart.setOption(option)
|
||||
// },
|
||||
// 切换靶场状态
|
||||
// tabClick(tab, event) {
|
||||
// this.changeRangeStatus()
|
||||
// },
|
||||
//初始化节点柱状图
|
||||
// initEcharts() {
|
||||
// var chartDom = document.getElementById('node');
|
||||
// this.nodeChart = echarts.init(chartDom);
|
||||
// var option;
|
||||
//
|
||||
// option = {
|
||||
// title: {
|
||||
// text: `${this.target_name}节点统计`,
|
||||
// left: 'center',
|
||||
// top: '15',
|
||||
// textStyle: {
|
||||
// color: '#FFFFFF',
|
||||
// fontSize: 16,
|
||||
// fontWeight: 500
|
||||
// }
|
||||
// },
|
||||
// tooltip: {
|
||||
// // trigger: 'axis',
|
||||
// axisPointer: {
|
||||
// // type: 'shadow'
|
||||
// }
|
||||
// },
|
||||
// grid: {
|
||||
// top: '25%',
|
||||
// left: '3%',
|
||||
// right: '4%',
|
||||
// bottom: '8%',
|
||||
// containLabel: true
|
||||
// },
|
||||
// xAxis: [
|
||||
// {
|
||||
// type: 'category',
|
||||
// data: this.nodeDataX,
|
||||
// axisTick: {
|
||||
// // alignWithLabel: true
|
||||
// show: false
|
||||
// },
|
||||
// axisLabel: {
|
||||
// fontSize: '14',
|
||||
// color: 'rgba(255, 255, 255, 0.7)'
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// yAxis: [
|
||||
// {
|
||||
// type: 'value',
|
||||
// name: '靶场中各类节点数量',
|
||||
// nameTextStyle: {
|
||||
// color: 'rgba(255, 255, 255, 0.7)',
|
||||
// padding: [0, 0, 0, 70]
|
||||
// },
|
||||
// axisLabel: {
|
||||
// fontSize: '12',
|
||||
// color: 'rgba(255, 255, 255, 0.7)'
|
||||
// },
|
||||
// splitLine: {
|
||||
// show: true,
|
||||
// lineStyle: {
|
||||
// color: ['rgba(199, 199, 200, 0.1)'],
|
||||
// width: 1,
|
||||
// type: 'dashed'
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
// series: [
|
||||
// {
|
||||
// // name: 'Direct',
|
||||
// type: 'bar',
|
||||
// barWidth: '20%',
|
||||
// data: this.nodeDataY,
|
||||
// itemStyle: {
|
||||
// normal: {
|
||||
// label: {
|
||||
// show: true, // 是否显示
|
||||
// position: 'top', // 显示位置
|
||||
// color: '#FFFFFF',
|
||||
// formatter: function (params) {
|
||||
// // 核心部分 formatter 可以为字符串也可以是回调
|
||||
// if (parseInt(params.value) === 0) {
|
||||
// return '';
|
||||
// } else {
|
||||
// return params.data.label;
|
||||
// }
|
||||
// }
|
||||
// },
|
||||
// // 每个柱子的颜色即为colorList数组里的每一项,如果柱子数目多于colorList的长度,则柱子颜色循环使用该数组
|
||||
// color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
// { offset: 0, color: '#3280D7' },
|
||||
// { offset: 1, color: '#4122E2' }
|
||||
// ])
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// ]
|
||||
// };
|
||||
// option && this.nodeChart.setOption(option);
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -25,6 +25,7 @@ export default {
|
||||
// required: true,
|
||||
default: function() {
|
||||
return {
|
||||
taskid:'',
|
||||
target:'',
|
||||
type: '',
|
||||
}
|
||||
@@ -69,21 +70,24 @@ export default {
|
||||
// this.getRangeDict()
|
||||
},
|
||||
methods: {
|
||||
// 获取数据
|
||||
querydelay(){
|
||||
const reqParams = {
|
||||
"target": this.left1data.target,
|
||||
"taskid": this.left1data.taskid,
|
||||
"type": this.left1data.type,
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.targetDelay, reqParams).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.zhudouble.xdata = []
|
||||
this.zhudouble.ydata1 = []
|
||||
for (let i = 0; i <res.delay_data.length>10?10:res.delay_data.length ; i++) {
|
||||
this.zhudouble.xdata.push(res.delay_data[i].Id);
|
||||
this.zhudouble.ydata1.push(res.delay_data[i].CurrDelay);
|
||||
}
|
||||
}
|
||||
this.$refs.zhudouble.bingfn(this.zhudouble)
|
||||
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
|
||||
@@ -1,30 +1,20 @@
|
||||
<template>
|
||||
<div class="source-card">
|
||||
<!-- <div class="source-chart" id="resource" v-loading="loading"></div>-->
|
||||
<ZhuZhuangTu_jbs :active="0" ref="zhudouble" :bing="zhudouble"></ZhuZhuangTu_jbs>
|
||||
<!-- <div class="tabs-container">-->
|
||||
<!-- <el-tabs v-model="activeName" @tab-click="tabClick">-->
|
||||
<!-- <el-tab-pane label="CPU" name="CPU"></el-tab-pane>-->
|
||||
<!-- <el-tab-pane label="MEMEORY" name="MEMEORY"></el-tab-pane>-->
|
||||
<!-- </el-tabs>-->
|
||||
|
||||
|
||||
<!-- </div>-->
|
||||
<NodeWordMap :nodes="nodes"></NodeWordMap>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import ZhuZhuangTu_jbs from '../echarts/zhuzhuangtu_jbs'
|
||||
import NodeWordMap from '../echarts/nodewordmap'
|
||||
|
||||
import { getTargetsResponse } from './sourceMock.js'
|
||||
export default {
|
||||
name: 'TargetView',
|
||||
props: {
|
||||
left1data: {
|
||||
type: Object,
|
||||
// required: true,
|
||||
default: function() {
|
||||
return {
|
||||
taskid:'',
|
||||
target:'',
|
||||
type: '',
|
||||
}
|
||||
@@ -40,196 +30,32 @@
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
name: 'SourceView',
|
||||
components: {
|
||||
ZhuZhuangTu_jbs,
|
||||
},
|
||||
components: { NodeWordMap },
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
sourceData: {},
|
||||
sourceDataX: [],
|
||||
sourceDataY: [],
|
||||
cpuList: [],
|
||||
memoryList: [],
|
||||
activeName: 'CPU',
|
||||
sourceChart: null,
|
||||
rangeDict: [],
|
||||
zhudouble:{
|
||||
data:[
|
||||
['延时', '节点'],
|
||||
],
|
||||
},
|
||||
nodes:[]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
querydelay() {
|
||||
const reqParams = {
|
||||
"taskid": this.left1data.taskid,
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.targetNodes, reqParams).then(res => {
|
||||
this.nodes = res.nodes
|
||||
// console.log("Nodes: ", this.nodes)
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
},
|
||||
created() {
|
||||
},
|
||||
mounted(){
|
||||
this.init()
|
||||
// this.getRangeDict()
|
||||
this.querydelay()
|
||||
console.log("**********target mounted")
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
const reqParams = {
|
||||
"target": this.left1data.target,
|
||||
"type": this.left1data.type,
|
||||
}
|
||||
console.log("GGGGGGGGGGGGGGGGGGGG")
|
||||
console.log(reqParams)
|
||||
console.log("GGGGGGGGGGGGGGGGGGGG")
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.targetDelay,reqParams).then(res => {
|
||||
if (res.code == 200) {
|
||||
for (let i = 0; i <res.delay_data.length>10?10:res.delay_data.length ; i++) {
|
||||
let A=[];
|
||||
A.push(res.delay_data[i].CurrDelay);
|
||||
A.push(res.delay_data[i].Id);
|
||||
this.zhudouble.data.push(A);
|
||||
}
|
||||
this.$refs.zhudouble.bingfn(this.zhudouble)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
this.initEcharts()
|
||||
})
|
||||
},
|
||||
initEcharts() {
|
||||
let that = this
|
||||
var chartDom = document.getElementById('resource');
|
||||
this.sourceChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
title: {
|
||||
text: '靶场资源使用排序',
|
||||
left: '10',
|
||||
top: '15',
|
||||
textStyle: {
|
||||
color: '#FFFFFF',
|
||||
fontSize: 16,
|
||||
fontWeight: 500
|
||||
}
|
||||
},
|
||||
tooltip: {
|
||||
// trigger: 'axis',
|
||||
axisPointer: {
|
||||
// type: 'shadow'
|
||||
}
|
||||
},
|
||||
grid: {
|
||||
top: '25%',
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '8%',
|
||||
containLabel: true
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: this.sourceDataX,
|
||||
axisTick: {
|
||||
// alignWithLabel: true
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
fontSize: '14',
|
||||
color: 'rgba(255, 255, 255, 0.7)'
|
||||
}
|
||||
}
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
fontSize: '12',
|
||||
color: 'rgba(255, 255, 255, 0.7)',
|
||||
formatter: function(value) {
|
||||
return value + ' (mcore)'
|
||||
}
|
||||
},
|
||||
splitLine: {
|
||||
show: true,
|
||||
lineStyle: {
|
||||
color: ['rgba(199, 199, 200, 0.1)'],
|
||||
width: 1,
|
||||
type: 'dashed'
|
||||
},
|
||||
}
|
||||
}
|
||||
],
|
||||
series: [
|
||||
{
|
||||
// name: 'Direct',
|
||||
type: 'bar',
|
||||
barWidth: '20%',
|
||||
data: this.sourceDataY,
|
||||
itemStyle: {
|
||||
normal: {
|
||||
label: {
|
||||
show: true, // 是否显示
|
||||
position: 'top', // 显示位置
|
||||
color: '#FFFFFF',
|
||||
formatter: function (params) {
|
||||
// 核心部分 formatter 可以为字符串也可以是回调
|
||||
if (parseInt(params.value) === 0) {
|
||||
return '';
|
||||
} else {
|
||||
return params.data.label;
|
||||
}
|
||||
}
|
||||
},
|
||||
// 每个柱子的颜色即为colorList数组里的每一项,如果柱子数目多于colorList的长度,则柱子颜色循环使用该数组
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
|
||||
{ offset: 0, color: '#01FFFF' },
|
||||
{ offset: 1, color: '#2160B8' }
|
||||
])
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
option && this.sourceChart.setOption(option);
|
||||
//点击事件
|
||||
this.sourceChart.on('click', function(params) {
|
||||
const target_name = params.name
|
||||
const target_id = that.rangeDict.find(item => item.label === target_name)?.value
|
||||
that.$store.commit('globalAttrs/setCheckMenu', 'rangeConfigManage')
|
||||
that.$store.commit('range/setTargetId', target_id)
|
||||
});
|
||||
},
|
||||
// 刷新图标数据
|
||||
refreshData(data, tabName){
|
||||
if(!this.sourceChart){
|
||||
return
|
||||
}
|
||||
//更新数据
|
||||
var option = this.sourceChart.getOption()
|
||||
option.series[0].data = data
|
||||
option.yAxis[0].axisLabel.formatter = function(value) {
|
||||
if (tabName === 'MEMEORY') {
|
||||
return value + ' (Mi)'
|
||||
} else {
|
||||
return value + ' (mcore)'
|
||||
}
|
||||
}
|
||||
this.sourceChart.setOption(option)
|
||||
},
|
||||
tabClick(tab, event) {
|
||||
switch (tab.name) {
|
||||
case 'CPU':
|
||||
this.refreshData(this.cpuList, 'CPU')
|
||||
break;
|
||||
case 'MEMEORY':
|
||||
this.refreshData(this.memoryList, 'MEMEORY')
|
||||
break;
|
||||
default:
|
||||
this.refreshData(this.cpuList, 'CPU')
|
||||
break;
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -6,17 +6,18 @@
|
||||
></Header>
|
||||
<div class="header2">
|
||||
<div style="width: 45%; display: inline-block;">
|
||||
<el-descriptions class="custom-descriptions" >
|
||||
<el-descriptions-item label="任务名称" >{{parentLevelRow.name}}</el-descriptions-item>
|
||||
<el-descriptions-item label="代理编号">{{parentLevelRow.id}}</el-descriptions-item>
|
||||
<el-descriptions-item label="执行状态">
|
||||
<el-descriptions class="custom-descriptions">
|
||||
<el-descriptions-item label="任务名称" class="cell-item">{{parentLevelRow.name}}</el-descriptions-item>
|
||||
<el-descriptions-item label="代理编号" class="cell-item">{{parentLevelRow.id}}</el-descriptions-item>
|
||||
<el-descriptions-item label="执行状态" class="cell-item">
|
||||
<el-tag type="info" v-if="parentLevelRow.status==='stop'">暂停</el-tag>
|
||||
<el-tag type="warning" v-if="parentLevelRow.status==='working'">执行中</el-tag>
|
||||
<el-tag type="danger" v-if="parentLevelRow.status==='finish'">已完成</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="任务目标">
|
||||
<el-descriptions-item label="任务目标" class="cell-item">
|
||||
{{parentLevelRow.target}}
|
||||
<span style="color: #00C0FF;margin-left: 5%" @click="getInfoByIp(parentLevelRow)">查看实时状态</span>
|
||||
<!-- <span style="color: #00C0FF;margin-left: 5%" @click="getInfoByIp(parentLevelRow)">查看实时状态</span> -->
|
||||
<span style="color: #00C0FF;margin-left: 5%" @click="getInfoById(parentLevelRow)">查看实时状态</span>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
@@ -98,7 +99,7 @@
|
||||
label="执行输出"
|
||||
min-width="150">
|
||||
<template slot-scope="scope">
|
||||
<el-button type="primary" @click="newlyPztj=!newlyPztj,getLog(scope.row.policy_id)">查看</el-button>
|
||||
<el-button type="primary" @click="newlyPztj=!newlyPztj, getLog(scope.row.policy_id)">查看</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
@@ -200,7 +201,7 @@ import Header from './module/Header.vue'
|
||||
import UserForm from './module/UserForm.vue'
|
||||
import { getTargetsResponse } from './mock.js'
|
||||
export default {
|
||||
name: "RangeConfigManage",
|
||||
name: "menuTaskInfo",
|
||||
components:{ Header, UserForm },
|
||||
data(){
|
||||
return{
|
||||
@@ -227,12 +228,15 @@ export default {
|
||||
},
|
||||
watch: {},
|
||||
created() {
|
||||
this.parentLevelRow = this.$route.query.row;
|
||||
this.parentLevelRow = JSON.parse(this.$route.query.row);
|
||||
this.init()
|
||||
},
|
||||
methods:{
|
||||
getInfoByIp(val){
|
||||
this.$router.push({ path: 'menuMBZTGZ', query: { row: val } });
|
||||
// getInfoByIp(val){
|
||||
// this.$router.push({ path: 'menuMBZTGZ', query: { row: val } });
|
||||
// },
|
||||
getInfoById(val){
|
||||
this.$router.push({ path: 'menuMBZTGZ', query: { row: val } });
|
||||
},
|
||||
CustomizationImageFales(){
|
||||
this.newlyPztj=false;
|
||||
@@ -296,9 +300,9 @@ export default {
|
||||
this.$axios.get(this.$http.api.taskInfoLog, reqParams).then(res => {
|
||||
// if (res.code == 200 || res.code == "OK") {
|
||||
// this.total = res?.data?.total
|
||||
this.total = 100
|
||||
this.total = 10
|
||||
this.tableDataLog = res?.data
|
||||
this.tableData.map(item => {
|
||||
this.tableDataLog.map(item => {
|
||||
item.permissions.map(permi => {
|
||||
this.$set(permi, 'delLoading', false)
|
||||
return permi
|
||||
@@ -326,6 +330,7 @@ export default {
|
||||
// this.total = res?.data?.total
|
||||
this.total = 100
|
||||
this.tableDataLog = res?.data
|
||||
console.log("***********", this.tableDataLog)
|
||||
this.tableData.map(item => {
|
||||
item.permissions.map(permi => {
|
||||
this.$set(permi, 'delLoading', false)
|
||||
@@ -473,6 +478,10 @@ export default {
|
||||
/* float: right;*/
|
||||
/*}*/
|
||||
}
|
||||
.cell-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
/*.custom-paginationLog /deep/ .el-pagination /deep/ .is-background {*/
|
||||
/* float: right;*/
|
||||
/*}*/
|
||||
@@ -585,7 +594,7 @@ export default {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
margin-bottom: 1%;
|
||||
margin-left: 2.5%;
|
||||
margin-left: 2%;
|
||||
.enlarged-button {
|
||||
transform: scale(1.5); /* 将按钮和图标整体放大 1.5 倍 */
|
||||
margin-left: 3%;
|
||||
@@ -596,8 +605,6 @@ export default {
|
||||
/deep/ .el-descriptions-item{
|
||||
background-color: rgba(19, 34, 99, 1);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
/*.custom-descriptions /deep/ .el-descriptions{*/
|
||||
/* background-color: #f04247; !* 设置背景颜色为灰色 *!*/
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
element-loading-text="加载中..."
|
||||
height="100%"
|
||||
style="width: 100%;"
|
||||
:data="tableData"
|
||||
:data="tableData"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
>
|
||||
@@ -88,7 +88,7 @@ import Header from './module/Header.vue'
|
||||
import UserForm from './module/UserForm.vue'
|
||||
import { getTargetsResponse } from './mock.js'
|
||||
export default {
|
||||
name: "RangeConfigManage",
|
||||
name: "menuTaskManagement",
|
||||
components:{ Header, UserForm },
|
||||
data(){
|
||||
return{
|
||||
@@ -118,18 +118,18 @@ export default {
|
||||
...params
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.taskList, reqParams).then(res => {
|
||||
this.$axios.get(this.$http.api.taskList, reqParams).then(res => { // 获取任务信息
|
||||
// if (res.code == 200 || res.code == "OK") {
|
||||
// this.total = res?.data?.total
|
||||
this.total = 100
|
||||
this.tableData = res?.data
|
||||
this.tableData.map(item => {
|
||||
item.permissions.map(permi => {
|
||||
this.$set(permi, 'delLoading', false)
|
||||
return permi
|
||||
})
|
||||
return item
|
||||
})
|
||||
this.tableData = res?.data // 可选链(Optional Chaining)操作符
|
||||
// this.tableData.map(item => {
|
||||
// item.permissions.map(permi => {
|
||||
// this.$set(permi, 'delLoading', false)
|
||||
// return permi
|
||||
// })
|
||||
// return item
|
||||
// })
|
||||
// }
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
@@ -201,27 +201,28 @@ export default {
|
||||
},
|
||||
// 详情
|
||||
taskInfo(val) {
|
||||
this.$router.push({ path: 'menuTaskInfo', query: { row: val } });
|
||||
},
|
||||
// 获取权限字典
|
||||
getPermissionDict() {
|
||||
const params = {
|
||||
page: 1,
|
||||
size: 99
|
||||
}
|
||||
this.$axios.get(this.$http.api.getPermissionList, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.permissionDict = res?.result?.items.map(item => {
|
||||
return {
|
||||
label: item.permission_name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
// this.$router.push({ path: 'menuTaskInfo', query: { row: val } });
|
||||
this.$router.push({ path: 'menuTaskInfo', query: { row: JSON.stringify(val) } });
|
||||
},
|
||||
// // 获取权限字典
|
||||
// getPermissionDict() {
|
||||
// const params = {
|
||||
// page: 1,
|
||||
// size: 99
|
||||
// }
|
||||
// this.$axios.get(this.$http.api.getPermissionList, params).then(res => {
|
||||
// if (res.code == 200 || res.code == "OK") {
|
||||
// this.permissionDict = res?.result?.items.map(item => {
|
||||
// return {
|
||||
// label: item.permission_name,
|
||||
// value: item.id
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
// }).catch(err => {
|
||||
// console.log(err)
|
||||
// })
|
||||
// },
|
||||
// 修改每页数据条数
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`)
|
||||
|
||||
@@ -49,7 +49,6 @@
|
||||
<el-option
|
||||
v-for="item in strategy"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
@@ -134,10 +133,12 @@ export default {
|
||||
{ value: 'ddos', label: '拒绝服务', type: 'warning' },
|
||||
{ value: 'sjqp', label: '数据欺骗', type: 'warning' },
|
||||
],
|
||||
agencyChange:[
|
||||
{ value: '中国北京', label: '中国北京', type: 'success' },
|
||||
{ value: '美国纽约', label: '美国纽约', type: 'warning' },
|
||||
],
|
||||
// 代理选择列表
|
||||
// agencyChange:[
|
||||
// { value: '中国北京', label: '中国北京', type: 'success' },
|
||||
// { value: '美国纽约', label: '美国纽约', type: 'warning' },
|
||||
// ],
|
||||
agencyChange:[],
|
||||
stateAwareMode:[
|
||||
{ value: 'auto', label: '自动选择', type: 'success' },
|
||||
{ value: 'tcp', label: 'TCP时延', type: 'warning' },
|
||||
@@ -149,10 +150,11 @@ export default {
|
||||
{ value: 'now', label: '立刻执行', type: 'success' },
|
||||
{ value: 'man', label: '手动执行', type: 'warning' },
|
||||
]
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
created(){
|
||||
this.getAgenayList();
|
||||
},
|
||||
methods: {
|
||||
getTagsByIP(val){
|
||||
let data={
|
||||
@@ -165,8 +167,25 @@ export default {
|
||||
}).catch(err=>{
|
||||
console.log(err)
|
||||
})
|
||||
|
||||
|
||||
},
|
||||
// 获取代理信息
|
||||
getAgenayList(){
|
||||
let data={
|
||||
"atype":"gjst",
|
||||
"status":1,
|
||||
"idle":1
|
||||
}
|
||||
this.$axios.get(this.$http.api.agentQueryList,data).then(res=>{
|
||||
if(res.code===200){
|
||||
console.log("*****Length", res.total)
|
||||
for (let i=0; i<res.total; i++) {
|
||||
let id = res.agent_data[i].id
|
||||
this.agencyChange.push({value:id})
|
||||
}
|
||||
}
|
||||
}).catch(err=>{
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
close() {
|
||||
this.resetForm()
|
||||
@@ -175,16 +194,17 @@ export default {
|
||||
},
|
||||
submit() {
|
||||
let data={
|
||||
"target":this.form.domain,
|
||||
// "target":this.form.domain,
|
||||
"agent":this.form.agencyChange,
|
||||
"name":this.form.name,
|
||||
"target_domain":"",
|
||||
"target_rr":this.form.inject,
|
||||
"policy_time":this.form.switchoverTime,
|
||||
"run_time":this.form.executeTime,
|
||||
"run_flag":this.form.operationalConfiguration,
|
||||
"policy":this.form.strategy,
|
||||
"policy_time":this.form.switchoverTime,
|
||||
"run_flag":this.form.operationalConfiguration,
|
||||
"run_time":this.form.executeTime,
|
||||
"scan":this.form.stateAwareMode,
|
||||
"target": this.form.ip,
|
||||
"target_domain":this.form.domain,
|
||||
"target_rr":this.form.inject,
|
||||
}
|
||||
console.log("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE")
|
||||
console.log(data)
|
||||
|
||||
@@ -1,729 +0,0 @@
|
||||
<template>
|
||||
<div class="range-config-manage" ref="appRef">
|
||||
<div class="show">
|
||||
<Header
|
||||
:range-dict="rangeDict"
|
||||
:batch-deploy-loading="batchDeployLoading"
|
||||
:batch-del-loading="batchDelLoading"
|
||||
:batch-pipeline-loading="batchPipelineLoading"
|
||||
:disabled-batch-pipline="disabledBatchPipline"
|
||||
@addConfig="addConfig"
|
||||
@query="query"
|
||||
@batchDeploy="batchDeploy"
|
||||
@batchDel="batchDel"
|
||||
@batchPipeline="batchPipeline"
|
||||
></Header>
|
||||
<div class="list" >
|
||||
<el-table
|
||||
class="custom-table"
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
element-loading-text="加载中..."
|
||||
height="100%"
|
||||
style="width: 100%;"
|
||||
:data="tableData"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
@cell-mouse-enter="cellEnter"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
type="selection"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
type="index"
|
||||
label="序号"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="tor_version"
|
||||
label="Tor版本"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="role"
|
||||
label="角色"
|
||||
min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.role"></svg-icon>
|
||||
<span style="padding-left: 5px;">{{ roleDict[scope.row.role] || scope.row.role }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="replicas"
|
||||
label="复本数"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="service"
|
||||
label="服务"
|
||||
min-width="150"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="bandwidth"
|
||||
label="宽带"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="memory"
|
||||
label="内存"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="out_port"
|
||||
label="映射端口"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="has_deployed"
|
||||
label="部署状态"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.has_deployed ? '已部署' : '未部署' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="network_id"
|
||||
label="网络"
|
||||
min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<el-popover
|
||||
placement="top"
|
||||
width="180"
|
||||
trigger="hover"
|
||||
:visible-arrow="false"
|
||||
popper-class= "pop-con"
|
||||
:ref="`popover1` + scope.$index">
|
||||
<div v-if="popVisiable === true" style="text-align: center; margin: 0;">
|
||||
<p>国家:{{popoverData.country}}</p>
|
||||
<p>网络:{{popoverData.network}}</p>
|
||||
</div>
|
||||
<a
|
||||
class="network"
|
||||
slot="reference"
|
||||
@mouseenter="openAction(scope.$index)"
|
||||
@mouseleave="cancelAction(scope.$index)"
|
||||
>
|
||||
{{ `网络${scope.row.network_id}`}}
|
||||
</a>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="操作"
|
||||
min-width="300"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="medium" :disabled="scope.row.delLoading" @click="edit(scope.row)">修改</el-button>
|
||||
<el-button type="text" size="medium" :disabled="scope.row.has_deployed==true || scope.row.delLoading" :loading="scope.row.deployLoading" @click="deploy(scope.row)">部署</el-button>
|
||||
<el-button type="text" size="medium" :loading="scope.row.delLoading" @click="del(scope.row)">删除</el-button>
|
||||
<el-button type="text" size="medium" :disabled="scope.row.delLoading" :loading="scope.row.restartLoading" @click="restart(scope.row)">重启</el-button>
|
||||
<el-button type="text" size="medium" :disabled="scope.row.pipeline_id===0 || scope.row.delLoading" :loading="scope.row.pipelineLoading" @click="pipeline(scope.row)">运行流水线</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="page"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="10"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
<div class="mask"></div>
|
||||
<ConfigForm
|
||||
ref="configForm"
|
||||
:is-add="isAdd"
|
||||
:target_id="target_id"
|
||||
:other-role="otherRole"
|
||||
:country-dict="countryDict"
|
||||
@refresh="init"
|
||||
@openRoleForm="openRoleForm"
|
||||
@clearRoleForm="clearRoleForm">
|
||||
</ConfigForm>
|
||||
<RoleForm
|
||||
ref="roleForm"
|
||||
v-model="otherRole"
|
||||
@submitOtherRole="submitOtherRole">
|
||||
</RoleForm>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Header from './module/Header.vue'
|
||||
import ConfigForm from './module/ConfigForm.vue'
|
||||
import RoleForm from './module/RoleForm.vue'
|
||||
import { getTargetsResponse } from './mock.js'
|
||||
export default {
|
||||
name: "RangeConfigManage",
|
||||
components:{ Header, ConfigForm, RoleForm },
|
||||
// props:['fTag','fInput','fFrom','FIsConfigQuery'],
|
||||
data(){
|
||||
return{
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0,
|
||||
isAdd: false,
|
||||
loading: false,
|
||||
target_id: '',
|
||||
otherRole: '',
|
||||
tableData: [],
|
||||
selectConfigIds: [],
|
||||
countryDict: [],
|
||||
rangeDict: [],
|
||||
batchDeployLoading: false,
|
||||
batchDelLoading: false,
|
||||
batchPipelineLoading: false,
|
||||
networkVisible: false,
|
||||
popoverData: {},
|
||||
popVisiable: false,
|
||||
roleDict:{
|
||||
da: '权威目录节点',
|
||||
client: '客户端节点',
|
||||
guard: '入口节点',
|
||||
relay: '路由节点',
|
||||
exit: '出口节点',
|
||||
onion: '洋葱服务节点',
|
||||
},
|
||||
disabledBatchPipline: false,
|
||||
delTimer: null,
|
||||
counter: 0,
|
||||
batchDelTimer: null,
|
||||
batchCounter: 0,
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.targetId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.target_id = newVal
|
||||
this.init()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getCountryDict()
|
||||
this.getRangeDict()
|
||||
},
|
||||
destroyed() {
|
||||
if(this.delTimer) {
|
||||
clearInterval(this.delTimer)
|
||||
this.delTimer = null
|
||||
}
|
||||
if(this.batchDelTimer) {
|
||||
clearInterval(this.batchDelTimer)
|
||||
this.batchDelTimer = null
|
||||
}
|
||||
this.counter = 0
|
||||
this.batchCounter = 0
|
||||
},
|
||||
methods:{
|
||||
init(params={}) {
|
||||
// this.tableData = getTargetsResponse?.result?.items
|
||||
// this.total = getTargetsResponse?.result?.total
|
||||
|
||||
// TODO: 暂时注释接口
|
||||
const reqParams = {
|
||||
page: this.page,
|
||||
size: this.size,
|
||||
...params
|
||||
}
|
||||
if (this.target_id && this.target_id !== '') {
|
||||
reqParams.target_id = this.target_id
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.getSettingList, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.total = res?.result?.total
|
||||
this.tableData = res?.result?.items
|
||||
this.tableData.map(item => {
|
||||
this.$set(item, 'deployLoading', false)
|
||||
this.$set(item, 'delLoading', false)
|
||||
this.$set(item, 'restartLoading', false)
|
||||
this.$set(item, 'pipelineLoading', false)
|
||||
return item
|
||||
})
|
||||
// 是否全部都没有流水线
|
||||
this.disabledBatchPipline = this.tableData.findIndex(item => item.pipeline_id !== 0) === -1 ? true : false
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
query(params) {
|
||||
this.init(params)
|
||||
},
|
||||
// 打开添加配置dialog
|
||||
addConfig() {
|
||||
this.isAdd = true
|
||||
this.$refs.configForm.getTorDict()
|
||||
this.$refs.configForm.getImageDict()
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.configForm.visible = true
|
||||
},
|
||||
// 当选择其它角色时,弹窗填写角色窗
|
||||
openRoleForm() {
|
||||
this.$refs.roleForm.visible = true
|
||||
},
|
||||
clearRoleForm() {
|
||||
this.otherRole = ''
|
||||
},
|
||||
// 编辑Form重新请求镜像字典
|
||||
submitOtherRole() {
|
||||
this.$refs.configForm.getImageDict()
|
||||
},
|
||||
// 批量部署
|
||||
batchDeploy() {
|
||||
if (this.selectConfigIds.length === 0) {
|
||||
this.$notify({
|
||||
title: '请勾选要部署的数据!',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
return
|
||||
}
|
||||
this.batchDeployLoading = true
|
||||
this.$axios.post(this.$http.api.batchDeply, {setting_id_list: this.selectConfigIds}, {target_id: this.target_id}).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '批量部署成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.init()
|
||||
} else {
|
||||
this.$notify({
|
||||
title: res,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.batchDeployLoading = false
|
||||
})
|
||||
},
|
||||
// 批量删除
|
||||
batchDel() {
|
||||
if (this.selectConfigIds.length === 0) {
|
||||
this.$notify({
|
||||
title: '请勾选要删除的数据!',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
return
|
||||
}
|
||||
this.batchDelLoading = true
|
||||
this.$axios.delete(this.$http.api.asynBatchDel, {}, {setting_id_list: this.selectConfigIds}).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.init()
|
||||
this.$notify({
|
||||
title: res.message,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.batchDelTimer = setInterval(() => {
|
||||
this.getBatchTask(res?.result?.task_id || '')
|
||||
},10 * 1000)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.batchDelLoading = false
|
||||
})
|
||||
},
|
||||
// 批量运行流水线
|
||||
batchPipeline() {
|
||||
if (this.selectConfigIds.length === 0) {
|
||||
this.$notify({
|
||||
title: '请勾选要运行流水线的数据!',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
return
|
||||
}
|
||||
this.batchPipelineLoading = true
|
||||
this.$axios.post(this.$http.api.batchPipeline, {setting_id_list: this.selectConfigIds}, {}).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '批量运行流水线成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.batchPipelineLoading = false
|
||||
})
|
||||
},
|
||||
// 部署
|
||||
deploy(row) {
|
||||
row.deployLoading = true
|
||||
this.$axios.post(this.$http.api.singleDeploy, {}, {setting_id: row.id}).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '部署成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
row.deployLoading = false
|
||||
})
|
||||
},
|
||||
// 删除
|
||||
del(row) {
|
||||
const url = this.$http.api.asynDelSetting + '/' + row.id
|
||||
const params = {
|
||||
user_id: row.user_id
|
||||
}
|
||||
row.delLoading = true
|
||||
this.$axios.delete(url, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
// this.init()
|
||||
this.$notify({
|
||||
title: res.message,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
if (this.delTimer) {
|
||||
clearInterval(this.delTimer)
|
||||
this.delTimer = null
|
||||
this.counter = 0
|
||||
}
|
||||
this.delTimer = setInterval(() => {
|
||||
this.getTask(res?.result?.task_id || '')
|
||||
},10 * 1000)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
// row.delLoading = false
|
||||
})
|
||||
},
|
||||
// 重启
|
||||
restart(row) {
|
||||
row.restartLoading = true
|
||||
this.$axios.post(this.$http.api.singleDeploy, {}, { setting_id: row.id }).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '重启成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
row.restartLoading = false
|
||||
})
|
||||
},
|
||||
// 运行流水线
|
||||
pipeline(row) {
|
||||
row.pipelineLoading = true
|
||||
this.$axios.post(this.$http.api.pipeline, {}, { setting_id: row.id }).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '运行流水线成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
row.pipelineLoading = false
|
||||
})
|
||||
},
|
||||
// 修改
|
||||
edit(row) {
|
||||
this.isAdd = false
|
||||
this.$refs.configForm.setting_id = row.id
|
||||
this.$refs.configForm.form.role = row.role
|
||||
this.$refs.configForm.form.tor_version = row.tor_version
|
||||
this.$refs.configForm.form.image_id = row.image_id
|
||||
this.$refs.configForm.form.replicas = row.replicas
|
||||
this.$refs.configForm.form.bandwidth = row.bandwidth
|
||||
this.$refs.configForm.form.memory = row.memory
|
||||
this.$refs.configForm.form.service = row.service
|
||||
this.$refs.configForm.form.out_port = row.out_port
|
||||
this.$refs.configForm.form.or_port = row.or_port
|
||||
this.$refs.configForm.form.dir_port = row.dir_port
|
||||
this.$refs.configForm.form.socks_port = row.socks_port
|
||||
this.$refs.configForm.form.control_port = row.control_port
|
||||
this.$refs.configForm.form.country_id = row.country_id
|
||||
this.$refs.configForm.form.network_id = row.network_id
|
||||
this.$refs.configForm.form.direct = row.direct
|
||||
this.$refs.configForm.form.start_tcpdump = row.start_tcpdump
|
||||
if(row.start_tcpdump) {
|
||||
this.$refs.configForm.form.deployType = 'start'
|
||||
} else {
|
||||
if(row.direct) {
|
||||
this.$refs.configForm.form.deployType = 'direct'
|
||||
} else {
|
||||
this.$refs.configForm.form.deployType = 'only'
|
||||
}
|
||||
}
|
||||
this.$refs.configForm.getTorDict()
|
||||
this.$refs.configForm.getImageDict()
|
||||
this.$refs.configForm.getNetworkDict()
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.configForm.visible = true
|
||||
},
|
||||
// 获取国家字典
|
||||
getCountryDict() {
|
||||
const params = {
|
||||
page: 1,
|
||||
size: 99
|
||||
}
|
||||
this.$axios.get(this.$http.api.getCountryDict, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.countryDict = res?.result?.items.map(item => {
|
||||
return {
|
||||
label: item.country_name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 获取靶场列表字典
|
||||
getRangeDict() {
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 99,
|
||||
}
|
||||
this.$axios.get(this.$http.api.getTargets, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.rangeDict = res?.result?.items.map(item => {
|
||||
return {
|
||||
label: item.target_name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.rangeDict.unshift({label: '全部靶场', value: ''})
|
||||
})
|
||||
},
|
||||
// 处理选中的数据
|
||||
handleSelectionChange(selectData) {
|
||||
this.selectConfigIds = selectData.map(item => {
|
||||
return item.id
|
||||
})
|
||||
},
|
||||
// 修改每页数据条数
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`)
|
||||
this.page=1
|
||||
this.size=val
|
||||
this.query()
|
||||
},
|
||||
// 修改页数
|
||||
handleCurrentChange(val) {
|
||||
console.log(`当前页: ${val}`)
|
||||
this.page=val
|
||||
this.query()
|
||||
},
|
||||
// 获取网络
|
||||
cellEnter(row, column){
|
||||
if(column.label === "网络"){
|
||||
this.popoverData = {}
|
||||
this.popVisiable = false
|
||||
// this.loading = true
|
||||
const reqParams = {
|
||||
network_id: row.network_id,
|
||||
}
|
||||
this.$axios.get(this.$http.api.getNetwork, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.popoverData = res.result
|
||||
this.popVisiable = true;
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
// this.loading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
cancelAction(index){
|
||||
let refName = "popover1" + index;
|
||||
this.$refs[refName].doClose();
|
||||
},
|
||||
openAction(index){
|
||||
let refName = "popover1" + index;
|
||||
this.$refs[refName].doShow();
|
||||
},
|
||||
// 获取任务进度
|
||||
getTask(task_id) {
|
||||
const url = this.$http.api.task + '/' + task_id
|
||||
this.$axios.get(url, {}).then(res => {
|
||||
if(res.task_status !== 'PENDING' && res.task_status !== 'STARTED') {
|
||||
if (res?.task_result?.code === 500 || res?.task_result?.message === 'false') {
|
||||
this.$notify({
|
||||
title: res?.task_result?.result || `${res.task_id}任务执行失败`,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: res?.task_result?.result || `${res.task_id}任务执行完毕`,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
clearInterval(this.delTimer)
|
||||
this.delTimer = null
|
||||
this.counter = 0
|
||||
this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.counter++
|
||||
if (this.counter >= 60) {
|
||||
clearInterval(this.delTimer)
|
||||
this.delTimer = null
|
||||
this.counter = 0
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 获取任务进度
|
||||
getBatchTask(task_id) {
|
||||
const url = this.$http.api.task + '/' + task_id
|
||||
this.$axios.get(url, {}).then(res => {
|
||||
if(res.task_status !== 'PENDING' && res.task_status !== 'STARTED') {
|
||||
if (res?.task_result?.code === 500 || res?.task_result?.message === 'false') {
|
||||
this.$notify({
|
||||
title: res?.task_result?.result || `${res.task_id}任务执行失败`,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: res?.task_result?.result || `${res.task_id}任务执行完毕`,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
clearInterval(this.batchDelTimer)
|
||||
this.batchDelTimer = null
|
||||
this.batchCounter = 0
|
||||
this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.batchCounter++
|
||||
if (this.batchCounter >= 30) {
|
||||
clearInterval(this.batchDelTimer)
|
||||
this.batchDelTimer = null
|
||||
this.batchCounter = 0
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.custom-table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.range-config-manage{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/*background-color: #010f4e;*/
|
||||
// background-color: rgba(255, 25, 49, 0.4);
|
||||
float: right;
|
||||
position: relative; /* 确保相对定位生效 */
|
||||
|
||||
.show{
|
||||
width: 95%;
|
||||
height: 95%;
|
||||
/*background-color: #67c23a;*/
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../img/backgroundFourCorner.png');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
/*background-size: cover; !* 宽度为100%,高度自适应保持宽高比 *!*/
|
||||
|
||||
/*display: flex; !* 将容器设置为 Flex 容器 *!*/
|
||||
/*justify-content: center; !* 水平居中子元素 *!*/
|
||||
/*align-items: center; !* 垂直居中子元素 *!*/
|
||||
|
||||
.list{
|
||||
width: 95%;
|
||||
height: 84%;
|
||||
margin-left: 2.5%;
|
||||
overflow-y: auto;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
border: none;
|
||||
::v-deep .network {
|
||||
float: right;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
color: #857BFF;
|
||||
text-decoration: none; /* 取消默认的下划线 */
|
||||
}
|
||||
}
|
||||
.list::-webkit-scrollbar {
|
||||
width: 0px; /* 隐藏滚动条 */
|
||||
height: 0px;
|
||||
background-color: transparent; /* 让背景透明 */
|
||||
|
||||
}
|
||||
/* 隐藏火狐浏览器滚动条 */
|
||||
@-moz-document url-prefix() {
|
||||
.trackSource {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
}
|
||||
// 遮罩层
|
||||
.mask{
|
||||
position: fixed; /*将元素设置为固定定位*/
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0,0,0,0.5); /*通过rgba函数来控制遮罩层的透明度*/
|
||||
display: none; /*将元素隐藏*/
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,718 +0,0 @@
|
||||
const getTargetsResponse = {
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"result": {
|
||||
"total": 32,
|
||||
"page": 1,
|
||||
"size": 20,
|
||||
"pages": 0,
|
||||
"items": [
|
||||
{
|
||||
"tor_version": "4.6.10",
|
||||
"role": "da",
|
||||
"replicas": 1,
|
||||
"service": "string",
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"image_id": 0,
|
||||
"out_port": 0,
|
||||
"has_deployed": false,
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 0,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "relay",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 1,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "exit",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 2,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "onion",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 3,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "client",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 4,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "guard",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 5,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 6,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 7,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"tor_version": "4.6.10",
|
||||
"role": "string",
|
||||
"replicas": 1,
|
||||
"service": "string",
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"image_id": 0,
|
||||
"out_port": 0,
|
||||
"has_deployed": false,
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 0,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 1,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 2,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 3,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 4,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 5,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 6,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 7,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"tor_version": "4.6.10",
|
||||
"role": "string",
|
||||
"replicas": 1,
|
||||
"service": "string",
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"image_id": 0,
|
||||
"out_port": 0,
|
||||
"has_deployed": false,
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 0,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 1,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 2,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 3,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 4,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 5,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 6,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 7,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"tor_version": "4.6.10",
|
||||
"role": "string",
|
||||
"replicas": 1,
|
||||
"service": "string",
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"image_id": 0,
|
||||
"out_port": 0,
|
||||
"has_deployed": false,
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 0,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 1,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 2,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 3,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 4,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 5,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 6,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
},
|
||||
{
|
||||
"role": "string",
|
||||
"image_id": 0,
|
||||
"replicas": 1,
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": "string",
|
||||
"tor_version": "4.6.10",
|
||||
"create_time": "2023-12-19T14:17:19.290Z",
|
||||
"update_time": "2023-12-19T14:17:19.290Z",
|
||||
"out_port": 0,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"id": 7,
|
||||
"has_deployed": false,
|
||||
"start_tcpdump": 0,
|
||||
"user_id": 0,
|
||||
"target_id": 0,
|
||||
"network_id": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
export { getTargetsResponse }
|
||||
@@ -1,658 +0,0 @@
|
||||
<template>
|
||||
<div class="config-dialog" v-if="visible">
|
||||
<!-- 在此处指定弹窗的样式和内容 -->
|
||||
<i class="el-icon-close" @click="close"></i>
|
||||
<div class="tag">
|
||||
<el-tag class="tags" :style="{'color': (tag==='基本配置') ? '#f8fdff': '#565e6e'}">基本配置</el-tag>
|
||||
<el-tag class="tags" :style="{'color': (tag==='网络配置') ? '#f8fdff': '#565e6e'}">网络配置</el-tag>
|
||||
<el-tag class="tags" :style="{'color': (tag==='部署') ? '#f8fdff': '#565e6e'}">部署</el-tag>
|
||||
</div>
|
||||
<div class="basic-box" v-if="tag==='基本配置'">
|
||||
<el-form
|
||||
ref="basicForm"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="150px"
|
||||
class="basic-form"
|
||||
size="small"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="角色选择" prop="role">
|
||||
<el-select v-model="form.role" placeholder="请选择角色" @change="selectRole">
|
||||
<el-option
|
||||
v-for="item in roleDict"
|
||||
:key="item.value"
|
||||
:label="item.value === 'other' ? `${item.label}(${otherRole})` : `${item.label}(${item.value})`"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="form.role!=='other'">
|
||||
<el-col :span="20">
|
||||
<el-form-item label="tor 版本" prop="tor_version" :rules="form.role!=='other' ? rules.tor_version : [{required: false}]">
|
||||
<el-select v-model="form.tor_version" placeholder="tor 版本" @change="selectTor">
|
||||
<el-option
|
||||
v-for="item in torDict"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="选择镜像" prop="image_id">
|
||||
<el-select v-model="form.image_id" placeholder="镜像名">
|
||||
<el-option
|
||||
v-for="item in imageDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="复本数" prop="replicas">
|
||||
<el-input v-model="form.replicas" placeholder="请输入内容 (默认值是1)"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="带宽限制" prop="bandwidth">
|
||||
<el-input v-model="form.bandwidth" placeholder="请输入内容 (默认值是10)">
|
||||
<p slot="suffix" class="el-input__icon">M</p>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="内存限制" prop="memory">
|
||||
<el-input v-model="form.memory" placeholder="请输入内容 (默认值是100)">
|
||||
<p slot="suffix" class="el-input__icon">Mi</p>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="form.role==='onion'">
|
||||
<el-col :span="20">
|
||||
<el-form-item label="服务器地址" prop="service" :rules="form.role==='onion' ? rules.service : [{required: false}]">
|
||||
<el-input v-model="form.service" placeholder="请输入内容 (onion角色必填)"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="form.role==='client'">
|
||||
<el-col :span="20">
|
||||
<el-form-item label="代理端口" prop="out_port" :rules="form.role==='client' ? rules.out_port : [{required: false}]">
|
||||
<!-- <el-input v-model="form.out_port" placeholder="请输入内容 (client角色必填,范围30000-32767)"></el-input> -->
|
||||
<el-select v-model="form.out_port" placeholder="请选择代理端口" @visible-change="showOutPortSelect">
|
||||
<el-option
|
||||
v-for="item in outPortDict"
|
||||
:key="item"
|
||||
:label="item"
|
||||
:value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="form.role==='da'||form.role==='relay'||form.role ==='guard'||form.role ==='exit'">
|
||||
<el-col :span="20">
|
||||
<el-form-item label="or端口" prop="or_port">
|
||||
<el-input v-model="form.or_port" placeholder="请输入内容 (默认值是7000)"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="form.role==='da'||form.role==='relay'||form.role ==='guard'||form.role ==='exit'">
|
||||
<el-col :span="20">
|
||||
<el-form-item label="dir端口" prop="dir_port">
|
||||
<el-input v-model="form.dir_port" placeholder="请输入内容 (默认值是9030)"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="form.role ==='onion'||form.role==='client'">
|
||||
<el-col :span="20">
|
||||
<el-form-item label="客户端监听端口" prop="socks_port">
|
||||
<el-input v-model="form.socks_port" placeholder="请输入内容 (默认值是19001)"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row v-if="form.role ==='onion'||form.role==='client'">
|
||||
<el-col :span="20">
|
||||
<el-form-item label="客户端控制端口" prop="control_port">
|
||||
<el-input v-model="form.control_port" placeholder="请输入内容 (默认值是19002)"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<footer class="anDiv">
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('网络配置', true)">下一步</el-button>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="network-box" v-if="tag==='网络配置'">
|
||||
<el-form
|
||||
ref="netForm"
|
||||
:model="form"
|
||||
:rules="netFormRules"
|
||||
label-width="150px"
|
||||
class="basic-form"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="国家" prop="country_id">
|
||||
<el-select v-model="form.country_id" placeholder="请选择国家" @change="getNetworkDict">
|
||||
<el-option
|
||||
v-for="item in countryDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="网络" prop="network_id">
|
||||
<el-select v-model="form.network_id" placeholder="请选择网络地址">
|
||||
<el-option
|
||||
v-for="item in networkDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('基本配置')">上一步</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('部署', true)">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="deploy-box" v-if="tag==='部署'">
|
||||
<div class="dmgxdjx">
|
||||
<div style="margin-right: 45%">
|
||||
<el-radio-group v-model="form.deployType" @change="selectDeploy">
|
||||
<el-radio label="direct">直接部署</el-radio>
|
||||
<el-radio label="only">仅添加配置</el-radio>
|
||||
<el-radio label="start">启动采集程序并部署</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('网络配置')">上一步</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="submit" :loading="loading">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'ConfigForm',
|
||||
props: {
|
||||
isAdd: {
|
||||
typeof: Boolean,
|
||||
required: true
|
||||
},
|
||||
target_id: {
|
||||
typeof: String,
|
||||
required: true
|
||||
},
|
||||
otherRole: {
|
||||
typeof: String,
|
||||
required: true
|
||||
},
|
||||
countryDict: {
|
||||
typeof: Array,
|
||||
require: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible:false,
|
||||
loading: false,
|
||||
tag:"基本配置",
|
||||
setting_id: '',
|
||||
form: {
|
||||
role: 'da', // 角色选择
|
||||
tor_version: '', // tor版本
|
||||
image_id: '', // 选择镜像
|
||||
replicas: null, // 复本数
|
||||
bandwidth: null, // 宽带限制
|
||||
memory: null, // 内存限制
|
||||
service: '', // 服务器地址
|
||||
out_port: null, // 代理端口
|
||||
or_port: null, // or端口
|
||||
dir_port: null, // dir端口
|
||||
socks_port: null, // 客户端监听端口
|
||||
control_port: null, // 客户端控制端口
|
||||
|
||||
country_id: '', // 国家
|
||||
network_id: '', // 网络
|
||||
|
||||
deployType: 'direct', // 部署方式
|
||||
direct: true, // 直接部署true 仅添加配置false
|
||||
start_tcpdump: false // 启动采集程序并部署 true
|
||||
},
|
||||
roleDict:[
|
||||
{label: '权威目录节点', value: 'da'},
|
||||
{label: '路由节点', value: 'relay'},
|
||||
{label: '出口节点', value: 'exit'},
|
||||
{label: '洋葱服务节点', value: 'onion'},
|
||||
{label: '客户端节点', value: 'client'},
|
||||
{label: '入口节点', value: 'guard'},
|
||||
{label: '其他节点', value: 'other'}
|
||||
],
|
||||
torDict: [],
|
||||
networkDict: [],
|
||||
outPortDict: [],
|
||||
imageDict: [],
|
||||
rules: {
|
||||
role: [
|
||||
{ required: true, message: '请选择角色', trigger: 'blur' }
|
||||
],
|
||||
tor_version: [
|
||||
{ required: true, message: '请选择tor版本', trigger: 'blur' }
|
||||
],
|
||||
image_id: [
|
||||
{ required: true, message: '请选择镜像', trigger: 'blur' }
|
||||
],
|
||||
service:[
|
||||
{ required: true, message: '请输入服务器地址', trigger: 'blur' }
|
||||
],
|
||||
out_port: [
|
||||
{ required: true, message: '请输入代理端口', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
netFormRules: {
|
||||
country_id: [
|
||||
{ required: true, message: '请选择国家', trigger: 'blur' }
|
||||
],
|
||||
network_id: [
|
||||
{ required: true, message: '请选择网络', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getOutPortDict()
|
||||
},
|
||||
methods: {
|
||||
// 关闭
|
||||
close() {
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
this.tag = '基本配置'
|
||||
this.resetForm()
|
||||
this.$emit('clearRoleForm')
|
||||
},
|
||||
// 重置
|
||||
resetForm() {
|
||||
this.form = {
|
||||
role: 'da', // 角色选择
|
||||
tor_version: '', // tor版本
|
||||
image_id: '', // 选择镜像
|
||||
replicas: '', // 复本数
|
||||
bandwidth: '', // 宽带限制
|
||||
memory: '', // 内存限制
|
||||
service: '', // 服务器地址
|
||||
out_port: '', // 代理端口
|
||||
or_port: '', // or端口
|
||||
dir_port: '', // dir端口
|
||||
socks_port: '', // 客户端监听端口
|
||||
control_port: '', // 客户端控制端口
|
||||
|
||||
country_id: '', // 国家
|
||||
network_id: '', // 网络
|
||||
|
||||
deployType: 'direct', // 部署方式
|
||||
direct: true, // 直接部署true 仅添加配置false
|
||||
start_tcpdump: false // 启动采集程序并部署 true
|
||||
}
|
||||
},
|
||||
// 选择哪个页面
|
||||
updateTag(val, isValid){
|
||||
if (val === '网络配置' && isValid) {
|
||||
this.$refs.basicForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.tag=val
|
||||
}
|
||||
})
|
||||
} else if(val === '部署' && isValid) {
|
||||
this.$refs.netForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.tag=val
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.tag=val
|
||||
}
|
||||
},
|
||||
// 角色选择
|
||||
selectRole(node) {
|
||||
if (node === 'other') {
|
||||
this.$emit('openRoleForm')
|
||||
}
|
||||
// 获取torDict
|
||||
this.getTorDict()
|
||||
// 获取镜像字典
|
||||
this.getImageDict()
|
||||
},
|
||||
// tor选择
|
||||
selectTor(tor){
|
||||
// 获取镜像字典
|
||||
this.getImageDict()
|
||||
},
|
||||
// 获取torDick
|
||||
getTorDict() {
|
||||
this.$axios.get(this.$http.api.getTorDict, {role: this.form.role}).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.torDict = res?.result
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 获取imageDict
|
||||
getImageDict(){
|
||||
const params = {
|
||||
page:1,
|
||||
size: 99,
|
||||
image_name: this.form.role,
|
||||
image_version: this.form.tor_version
|
||||
}
|
||||
if (this.form.role === 'other' && this.otherRole) {
|
||||
params.image_name = this.otherRole
|
||||
params.image_type = 'define'
|
||||
}
|
||||
this.$axios.get(this.$http.api.getImageDict, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.imageDict = res?.result?.items.map(item => {
|
||||
return {
|
||||
label: item.image_name + ':' + item.image_version,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 获取网络
|
||||
getNetworkDict(){
|
||||
const params = {
|
||||
page:1,
|
||||
size: 99,
|
||||
country_id: this.form.country_id,
|
||||
}
|
||||
this.$axios.get(this.$http.api.getNetworkDict, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.networkDict = res?.result?.items.map(item => {
|
||||
return {
|
||||
label: item.cidr,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
showOutPortSelect() {
|
||||
this.getOutPortDict()
|
||||
},
|
||||
// 获取代理端口字典
|
||||
getOutPortDict(){
|
||||
const params = {}
|
||||
this.$axios.get(this.$http.api.getOutPortDict, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.outPortDict = res?.result
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
selectDeploy(deployType) {
|
||||
switch (deployType) {
|
||||
case 'direct':
|
||||
this.form.direct = true
|
||||
break
|
||||
case 'only':
|
||||
this.form.direct = false
|
||||
break
|
||||
case 'start':
|
||||
this.form.start_tcpdump = true
|
||||
break
|
||||
default:
|
||||
this.form.direct = true
|
||||
break
|
||||
}
|
||||
},
|
||||
// 提交数据
|
||||
submit() {
|
||||
const submitForm = this.setSubmitForm()
|
||||
if (this.isAdd) {
|
||||
this.add(submitForm)
|
||||
} else {
|
||||
this.edit(submitForm)
|
||||
}
|
||||
},
|
||||
setSubmitForm() {
|
||||
const {role, image_id } = this.form
|
||||
let submitForm = {role, image_id }
|
||||
if (this.form.replicas) {
|
||||
submitForm.replicas = this.form.replicas
|
||||
}
|
||||
if (this.form.bandwidth) {
|
||||
submitForm.bandwidth = this.form.bandwidth
|
||||
}
|
||||
if (this.form.memory) {
|
||||
submitForm.memory = this.form.memory
|
||||
}
|
||||
if (this.form.role === 'other') {
|
||||
submitForm.role = this.otherRole
|
||||
} else if (this.form.role==='da'||this.form.role==='relay'||this.form.role ==='guard'||this.form.role ==='exit') {
|
||||
submitForm.tor_version = this.form.tor_version
|
||||
if (this.form.or_port) {
|
||||
submitForm.or_port = this.form.or_port
|
||||
}
|
||||
if (this.form.dir_port) {
|
||||
submitForm.dir_port = this.form.dir_port
|
||||
}
|
||||
} else if (this.form.role==='onion') {
|
||||
// 服务器地址
|
||||
submitForm.tor_version = this.form.tor_version
|
||||
submitForm.service = this.form.service
|
||||
if (this.form.socks_port) {
|
||||
submitForm.socks_port = this.form.socks_port
|
||||
}
|
||||
if (this.form.control_port) {
|
||||
submitForm.control_port = this.form.control_port
|
||||
}
|
||||
} else if (this.form.role==='client') {
|
||||
// 代理端口
|
||||
submitForm.tor_version = this.form.tor_version
|
||||
submitForm.out_port = this.form.out_port
|
||||
if (this.form.socks_port) {
|
||||
submitForm.socks_port = this.form.socks_port
|
||||
}
|
||||
if (this.form.control_port) {
|
||||
submitForm.control_port = this.form.control_port
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
return submitForm
|
||||
},
|
||||
add(submitForm) {
|
||||
this.loading = true
|
||||
const url = this.$http.api.setting + `/?target_id=${this.target_id}&network_id=${this.form.network_id}&direct=${this.form.direct}&start_tcpdump=${this.form.start_tcpdump}`
|
||||
this.$axios.post(url, submitForm).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
this.$notify({
|
||||
title: '添加靶场配置成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
edit(submitForm) {
|
||||
this.loading = true
|
||||
const url = this.$http.api.setting+ `/${this.setting_id}/?target_id=${this.target_id}&network_id=${this.form.network_id}&direct=${this.form.direct}&start_tcpdump=${this.form.start_tcpdump}`
|
||||
this.$axios.put(url, submitForm).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
this.$notify({
|
||||
title: '修改靶场配置成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.config-dialog{
|
||||
z-index: 997;
|
||||
width: 620px;
|
||||
height: 555px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../img/tjpz.svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.el-icon-close{
|
||||
float: right;
|
||||
padding-right: 7%;
|
||||
padding-top: 3%
|
||||
}
|
||||
.basic-form {
|
||||
margin-top: 20px;
|
||||
text-align: left;
|
||||
::v-deep .el-select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.tag{
|
||||
margin-left: 9%;
|
||||
.tags{
|
||||
margin-right: 5%;
|
||||
margin-top: 2%;
|
||||
font-size: 23px;
|
||||
border: none;
|
||||
background-color: transparent !important;
|
||||
color: #565e6e;
|
||||
}
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA !important;
|
||||
}
|
||||
.basic-box{
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-bottom: 10%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
}
|
||||
}
|
||||
.network-box{
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 50%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
.deploy-box{
|
||||
.dmgxdjx{
|
||||
width: 100%;
|
||||
float: left;
|
||||
display: block;
|
||||
margin-top: 3%;
|
||||
margin-left: 20%;
|
||||
text-align: left;
|
||||
.el-radio {
|
||||
display: block;
|
||||
margin-top: 20%;
|
||||
font-size: 14px;
|
||||
color: #ffffff;
|
||||
}
|
||||
}
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 49%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,204 +0,0 @@
|
||||
<template>
|
||||
<div class="head">
|
||||
<div class="target-select">
|
||||
<el-select class="range-select" style="width:220px;" v-model="target_id" placeholder="全部靶场" @change="changeRange">
|
||||
<el-option
|
||||
v-for="item in rangeDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<img src="../../../img/btn/addConfigBtn.svg" style="height: 70% ;width: 10%;margin-right: 2%;color: #ffffff" @click="addConfig">
|
||||
<div class="role-select">
|
||||
<el-select v-model="role" clearable placeholder="节点角色" @change="query">
|
||||
<el-option
|
||||
v-for="item in roleDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
<svg-icon :icon-class="item.value"></svg-icon>
|
||||
<span style="margin-left: 10px;">{{ item.label }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="deploy-select">
|
||||
<el-select v-model="has_deployed" clearable placeholder="部署状态" @change="query">
|
||||
<el-option
|
||||
v-for="item in statusDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button type="primary" :loading="batchDeployLoading" @click="batchDeploy">批量部署</el-button>
|
||||
<el-button type="primary" :loading="batchDelLoading" @click="batchDel">批量删除</el-button>
|
||||
<el-button type="primary" :loading="batchPipelineLoading" @click="batchPipeline" :disabled="disabledBatchPipline">批量运行流水线</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Header',
|
||||
props: ['rangeDict', 'batchDeployLoading', 'batchDelLoading', 'batchPipelineLoading', 'disabledBatchPipline'],
|
||||
data() {
|
||||
return {
|
||||
target_id: '',
|
||||
role: '',
|
||||
has_deployed: '',
|
||||
roleDict:[
|
||||
{label: '权威目录节点', value: 'da'},
|
||||
{label: '路由节点', value: 'relay'},
|
||||
{label: '出口节点', value: 'exit'},
|
||||
{label: '洋葱服务节点', value: 'onion'},
|
||||
{label: '客户端节点', value: 'client'},
|
||||
{label: '入口节点', value: 'guard'}
|
||||
],
|
||||
statusDict: [
|
||||
{value: 'true', label: '已部署'},
|
||||
{value: 'false',label: '未部署'}
|
||||
]
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.targetId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.target_id = newVal
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 改变靶场
|
||||
changeRange() {
|
||||
this.$store.commit('range/setTargetId', this.target_id)
|
||||
},
|
||||
// 新增
|
||||
addConfig() {
|
||||
this.$emit('addConfig')
|
||||
},
|
||||
// 查询
|
||||
query() {
|
||||
let params = {}
|
||||
if (this.has_deployed !== '') {
|
||||
params.has_deployed = this.has_deployed
|
||||
}
|
||||
if (this.role !== '') {
|
||||
params.role = this.role
|
||||
}
|
||||
this.$emit('query', params)
|
||||
},
|
||||
// 批量部署
|
||||
batchDeploy() {
|
||||
this.$emit('batchDeploy')
|
||||
},
|
||||
// 批量删除
|
||||
batchDel() {
|
||||
this.$emit('batchDel')
|
||||
},
|
||||
// 批量运行流水线
|
||||
batchPipeline() {
|
||||
this.$emit('batchPipeline')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.head{
|
||||
width: 95%;
|
||||
height: 7%;
|
||||
margin-top: 1%;
|
||||
margin-left: 2.5%;
|
||||
text-align: right;
|
||||
|
||||
/*background-color: #5daf34;*/
|
||||
.block{
|
||||
display: inline-block;
|
||||
margin-left: 2%;
|
||||
|
||||
}
|
||||
.input{
|
||||
display: inline-block;
|
||||
height: 60%;
|
||||
width: 10%;
|
||||
margin-left: 0.5%;
|
||||
.el-input::placeholder {
|
||||
width: auto;
|
||||
}
|
||||
.icon-group {
|
||||
display: flex; /* 设置容器为 Flexbox 容器 */
|
||||
align-items: center; /* 垂直居中图片 */
|
||||
gap: 5px; /* 图片和文字之间的间距,可以根据需要进行调整 */
|
||||
|
||||
}
|
||||
.icon-group img {
|
||||
transform: scale(1);
|
||||
margin-right: 15px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
}
|
||||
.target-select{
|
||||
font-size: 10px;
|
||||
float: left;
|
||||
margin-top: 0.5%;
|
||||
display: inline-block;
|
||||
height: 60%;
|
||||
width: 10%;
|
||||
margin-left: 0.5%;
|
||||
.custom-popper .el-select-dropdown {
|
||||
max-height: 3px;
|
||||
}
|
||||
.range-select {
|
||||
::v-deep .el-input {
|
||||
width: 80%;
|
||||
.el-input__inner {
|
||||
background-color: transparent !important;
|
||||
border-color: transparent;
|
||||
border-radius: 0;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
/* select去除竖线 */
|
||||
.el-input__suffix::before {
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
}
|
||||
/*select的上下箭头图标样式*/
|
||||
.el-select__caret {
|
||||
color: rgba(2, 221, 234, 0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.deploy-select{
|
||||
display: inline-block;
|
||||
height: 60%;
|
||||
width: 10%;
|
||||
margin-top: 0.5%;
|
||||
margin-left: 0.5%;
|
||||
margin-right: 1%;
|
||||
.el-input::placeholder {
|
||||
width: auto;
|
||||
}
|
||||
.icon-group {
|
||||
display: flex; /* 设置容器为 Flexbox 容器 */
|
||||
align-items: center; /* 垂直居中图片 */
|
||||
gap: 5px; /* 图片和文字之间的间距,可以根据需要进行调整 */
|
||||
}
|
||||
.icon-group img {
|
||||
transform: scale(1);
|
||||
margin-right: 15px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
::v-deep .role-select{
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,94 +0,0 @@
|
||||
<template>
|
||||
<div class="role-dialog" v-if="visible">
|
||||
<i class="el-icon-close" style="float: right; padding-right: 7%;padding-top: 2.8%" @click="close"></i>
|
||||
<div class="fbs">
|
||||
<span style="margin-right: 4.5%">角色</span>
|
||||
<el-input
|
||||
:value="value"
|
||||
placeholder="请输入内容"
|
||||
style="width: 50%"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners">
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="submit">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RoleForm',
|
||||
props: {
|
||||
value: {
|
||||
typeof: String,
|
||||
require: true,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
},
|
||||
submit() {
|
||||
this.$emit('submitOtherRole')
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.role-dialog{
|
||||
z-index: 998;
|
||||
width: 30%;
|
||||
height: 20%;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../img/jbpzxybqr.png');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.fbs{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 3%;
|
||||
text-align: center;
|
||||
}
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 10%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 10%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,420 +0,0 @@
|
||||
<template>
|
||||
<div class="range-manage" ref="appRef">
|
||||
<div class="show">
|
||||
<Header
|
||||
@quickAdd="quickAdd"
|
||||
@customAdd="customAdd"
|
||||
@query="query"
|
||||
></Header>
|
||||
<div class="list" id="show-loading">
|
||||
<div class="single" v-for="rangeItem in rangeList" :key="rangeItem.target_name">
|
||||
<div class="title">
|
||||
<span style="font-size: 20px;">{{rangeItem.target_name+' '+getDeployStatus(rangeItem,'deploy_status')}}</span>
|
||||
<el-button :class="rangeItem.attribute==='public' ? 'gOsY' : 'gOsN'" >{{rangeItem.attribute==='public' ? '公用' : '私有'}}</el-button>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="cell">{{`正常节点:${getDeployStatus(rangeItem,'deploy_success_count')}个`}}</div>
|
||||
<div class="cell">{{`异常节点:${getDeployStatus(rangeItem,'deploy_default_count')}个`}}</div>
|
||||
<div class="cell">{{`节点数量:${getDeployStatus(rangeItem,'deploy_total_count')}个`}}</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="cell small">{{`创建人:${rangeItem.username}`}}</div>
|
||||
<div class="cell large">{{`创建时间:${formatTime(rangeItem.create_time)}`}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="buttons">
|
||||
<el-button class="glBut" type="primary" :disabled="rangeItem.delLoading" @click="edit(rangeItem)">修改</el-button>
|
||||
<el-button class="glBut" type="primary" :loading="rangeItem.delLoading" @click="del(rangeItem)">删除</el-button>
|
||||
<el-button class="glBut" type="primary" @click="goConfigManage(rangeItem.id)">配置管理</el-button>
|
||||
<el-button class="glBut" type="primary" @click="goNodeManage(rangeItem.id)">节点管理</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="page"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="10"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
<div class="mask"></div>
|
||||
<QuickCreate ref="quickCreate" :is-add="isAdd" @refresh="init"></QuickCreate>
|
||||
<CustomCreate ref="customCreate" :is-add="isAdd" @refresh="init"></CustomCreate>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Header from './module/Header.vue'
|
||||
import QuickCreate from './module/QuickCreate.vue'
|
||||
import CustomCreate from './module/CustomCreate.vue'
|
||||
import { formatTime } from '../../utils'
|
||||
import { Loading } from 'element-ui'
|
||||
import { getTargetsResponse } from './mock.js'
|
||||
export default {
|
||||
name: "RangeMange",
|
||||
components:{
|
||||
Header,
|
||||
QuickCreate,
|
||||
CustomCreate
|
||||
},
|
||||
// props:['fTag','fInput','fFrom','FIsConfigQuery'],
|
||||
data(){
|
||||
return{
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0,
|
||||
isAdd: false,
|
||||
rangeList:[],
|
||||
delTimer: null,
|
||||
pendingTimer: null,
|
||||
counter: 0
|
||||
}
|
||||
},
|
||||
computed: {},
|
||||
watch: {},
|
||||
filters: {},
|
||||
created() {
|
||||
// this.init()
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
destroyed() {
|
||||
if (this.pendingTimer) {
|
||||
clearInterval(this.pendingTimer)
|
||||
this.pendingTimer = null
|
||||
}
|
||||
if(this.delTimer) {
|
||||
clearInterval(this.delTimer)
|
||||
this.delTimer = null
|
||||
}
|
||||
this.counter = 0
|
||||
},
|
||||
methods:{
|
||||
formatTime,
|
||||
// 初始化数据
|
||||
init(params={}) {
|
||||
// this.rangeList = getTargetsResponse?.result?.items
|
||||
// this.total = getTargetsResponse?.result?.total
|
||||
const reqParams = {
|
||||
page: this.page,
|
||||
size: this.size,
|
||||
...params
|
||||
}
|
||||
// TODO: 网络请求先注释,后期放开
|
||||
let loadingInstance = Loading.service({ text: '加载中...', target: '#show-loading'}) // 开启loading
|
||||
this.$axios.get(this.$http.api.getTargets, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.total = res?.result?.total
|
||||
this.rangeList = res?.result?.items
|
||||
this.rangeList.map(item => {
|
||||
this.$set(item, 'delLoading', false)
|
||||
return item
|
||||
})
|
||||
let index = this.rangeList?.findIndex(item => {
|
||||
return item.target_deploy_statuses[0].deploy_status === '部署中' || item.target_deploy_statuses[0].deploy_status === '删除中'
|
||||
})
|
||||
if (index !== -1) {
|
||||
if (!this.pendingTimer) {
|
||||
this.pendingTimer = setInterval(() => {
|
||||
this.timerUpdateList(params)
|
||||
}, 10 * 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
loadingInstance.close()
|
||||
if(this.rangeList.length === 0) {
|
||||
this.$store.commit('range/setTargetId', '')
|
||||
}
|
||||
})
|
||||
},
|
||||
// 查询
|
||||
query(params) {
|
||||
this.init(params)
|
||||
},
|
||||
// pendingTimer只刷新数据
|
||||
timerUpdateList(params) {
|
||||
const reqParams = {
|
||||
page: this.page,
|
||||
size: this.size,
|
||||
...params
|
||||
}
|
||||
this.$axios.get(this.$http.api.getTargets, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.total = res?.result?.total
|
||||
this.rangeList = res?.result?.items
|
||||
this.rangeList.map(item => {
|
||||
this.$set(item, 'delLoading', false)
|
||||
return item
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
if(this.rangeList.length === 0) {
|
||||
this.$store.commit('range/setTargetId', '')
|
||||
}
|
||||
})
|
||||
},
|
||||
// 修改
|
||||
edit(rangeItem) {
|
||||
if (rangeItem.create_mode === 'define') {
|
||||
// 自定义靶场修改
|
||||
this.isAdd = false
|
||||
this.$refs.customCreate.target_id = rangeItem.id
|
||||
this.$refs.customCreate.user_id = rangeItem.user_id
|
||||
this.$refs.customCreate.form.target_name = rangeItem.target_name
|
||||
this.$refs.customCreate.form.description = rangeItem.description
|
||||
this.$refs.customCreate.form.attribute = rangeItem.attribute
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.customCreate.visible = true
|
||||
} else {
|
||||
// 快速创建靶场修改
|
||||
this.isAdd = false
|
||||
this.$refs.quickCreate.target_id = rangeItem.id
|
||||
this.$refs.quickCreate.user_id = rangeItem.user_id
|
||||
this.$refs.quickCreate.form.target_name = rangeItem.target_name
|
||||
this.$refs.quickCreate.form.description = rangeItem.description
|
||||
if (rangeItem.ratio === 0.01 || rangeItem.ratio === 0.005 || rangeItem.ratio === 0.002 || rangeItem.ratio === 0.001) {
|
||||
this.$refs.quickCreate.form.ratio = rangeItem.ratio.toString()
|
||||
} else {
|
||||
this.$refs.quickCreate.otherRatio = rangeItem.ratio.toString()
|
||||
}
|
||||
this.$refs.quickCreate.form.attribute = rangeItem.attribute
|
||||
this.$refs.quickCreate.form.createType = rangeItem.create_mode
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.quickCreate.visible = true
|
||||
}
|
||||
},
|
||||
// 删除
|
||||
del(rangeItem) {
|
||||
rangeItem.delLoading = true
|
||||
const url = this.$http.api.asyncTarget + '/' + rangeItem.id
|
||||
this.$axios.delete(url, {}).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: res.message,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.delTimer = setInterval(() => {
|
||||
this.getTask(res?.result?.task_id || '')
|
||||
}, 10 * 1000)
|
||||
// this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
// rangeItem.delLoading = false
|
||||
})
|
||||
},
|
||||
// 获取任务进度
|
||||
getTask(task_id) {
|
||||
const url = this.$http.api.task + '/' + task_id
|
||||
this.$axios.get(url, {}).then(res => {
|
||||
if(res.task_status !== 'PENDING' && res.task_status !== 'STARTED') {
|
||||
if (res?.task_result?.code === 500 || res?.task_result?.message === 'false') {
|
||||
this.$notify({
|
||||
title: `${res.task_id}任务执行完毕,删除靶场失败`,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: `${res.task_id}任务执行完毕,删除靶场成功`,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
clearInterval(this.delTimer)
|
||||
this.delTimer = null
|
||||
this.counter = 0
|
||||
this.init()
|
||||
} else {
|
||||
// this.$notify({
|
||||
// title: `${res.task_id}任务执行中`,
|
||||
// type: 'success',
|
||||
// duration: 2500
|
||||
// })
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.counter++
|
||||
if (this.counter >= 60) {
|
||||
clearInterval(this.delTimer)
|
||||
this.delTimer = null
|
||||
this.counter = 0
|
||||
this.init()
|
||||
}
|
||||
})
|
||||
},
|
||||
// 配置管理页面
|
||||
goConfigManage(id) {
|
||||
this.$store.commit('globalAttrs/setCheckMenu', 'rangeConfigManage')
|
||||
this.$store.commit('range/setTargetId', id)
|
||||
},
|
||||
// 节点管理页面
|
||||
goNodeManage(id) {
|
||||
this.$store.commit('globalAttrs/setCheckMenu', 'rangeNodeManage')
|
||||
this.$store.commit('range/setTargetId', id)
|
||||
},
|
||||
// 打开快速创建靶场
|
||||
quickAdd() {
|
||||
this.isAdd = true
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.quickCreate.visible = true
|
||||
},
|
||||
// 打开自定义创建靶场
|
||||
customAdd() {
|
||||
this.isAdd = true
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.customCreate.visible = true
|
||||
},
|
||||
getDeployStatus(rangeItem, deploy) {
|
||||
if (rangeItem.target_deploy_statuses.length > 0) {
|
||||
return rangeItem.target_deploy_statuses[0][deploy]
|
||||
} else {
|
||||
return '--'
|
||||
}
|
||||
},
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`)
|
||||
this.page=1
|
||||
this.size=val
|
||||
this.query()
|
||||
},
|
||||
handleCurrentChange(val) {
|
||||
console.log(`当前页: ${val}`)
|
||||
this.page=val
|
||||
this.query()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
.range-manage{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
float: right;
|
||||
position: relative; /* 确保相对定位生效 */
|
||||
|
||||
.show{
|
||||
width: 95%;
|
||||
height: 95%;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../img/backgroundFourCorner.png');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.list{
|
||||
display:flex;
|
||||
flex-direction: row;
|
||||
justify-content: flex-start;
|
||||
flex-wrap: wrap;
|
||||
align-content: flex-start;
|
||||
width: 95%;
|
||||
height: 84%;
|
||||
margin-left: 2.5%;
|
||||
.single:hover{
|
||||
border: 1px solid #159dd3;
|
||||
}
|
||||
.single{
|
||||
width: 32%;
|
||||
height: 23.5%;
|
||||
border-radius: 8px;
|
||||
border: 1px solid rgba(186, 208, 241, 0.10);
|
||||
background: rgba(25, 33, 61, 0.40);
|
||||
margin-left: 1.2%;
|
||||
margin-bottom: 0.9%;
|
||||
padding: 20px;
|
||||
display: inline-block;
|
||||
.title{
|
||||
text-align: left;
|
||||
.gOsY{
|
||||
width: 15px;
|
||||
height: 10px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 2%;
|
||||
background-color: rgba(121, 175, 122, 0.3);
|
||||
color: #36af04;
|
||||
}
|
||||
.gOsN{
|
||||
width: 15px;
|
||||
height: 10px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 2%;
|
||||
background-color: rgba(207, 172, 93, 0.3);
|
||||
color: #af8109;
|
||||
}
|
||||
}
|
||||
.container{
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin: 10px 0 10px 0;
|
||||
.row {
|
||||
display: flex;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.cell {
|
||||
flex-grow: 1;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
border: none;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.cell.small {
|
||||
flex-basis: 33.5%;
|
||||
}
|
||||
|
||||
.cell.large {
|
||||
flex-basis: 66.5%;
|
||||
}
|
||||
}
|
||||
.buttons{
|
||||
text-align: left;
|
||||
|
||||
.glBut{
|
||||
width: 80px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// margin-left: 2%;
|
||||
background-color: rgba(14, 61, 138, 0.50);
|
||||
color: #02DDEA;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 遮罩层
|
||||
.mask{
|
||||
position: fixed; /*将元素设置为固定定位*/
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0,0,0,0.5); /*通过rgba函数来控制遮罩层的透明度*/
|
||||
display: none; /*将元素隐藏*/
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,90 +0,0 @@
|
||||
const getTargetsResponse = {
|
||||
"code":200,
|
||||
"message":"success",
|
||||
"result":{
|
||||
"items":[
|
||||
{
|
||||
"target_name":"tor测试靶场",
|
||||
"description":"部署tor网络",
|
||||
"attribute":"public",
|
||||
"id":23,
|
||||
"user_id":2,
|
||||
"create_time":"2023-12-07T14:24:53",
|
||||
"target_deploy_statuses":[
|
||||
{
|
||||
"deploy_status":"部署成功",
|
||||
"deploy_total_count":20,
|
||||
"deploy_success_count":18,
|
||||
"deploy_default_count":2
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name":"tor测试靶场2",
|
||||
"description":"部署tor网络",
|
||||
"attribute":"public",
|
||||
"id":24,
|
||||
"user_id":2,
|
||||
"create_time":"2023-12-07T14:37:54",
|
||||
"target_deploy_statuses":[
|
||||
{
|
||||
"deploy_status":"未部署",
|
||||
"deploy_total_count":0,
|
||||
"deploy_success_count":0,
|
||||
"deploy_default_count":0
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name":"string",
|
||||
"description":"string",
|
||||
"attribute":"public",
|
||||
"id":34,
|
||||
"user_id":4,
|
||||
"create_time":"2023-12-13T00:30:22",
|
||||
"target_deploy_statuses":[
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name":"XT-RANGE1",
|
||||
"description":"XT测试靶场1",
|
||||
"attribute":"public",
|
||||
"id":35,
|
||||
"user_id":4,
|
||||
"create_time":"2023-12-13T10:57:05",
|
||||
"target_deploy_statuses":[
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name":"XT-RANGE2",
|
||||
"description":"XT测试靶场2",
|
||||
"attribute":"public",
|
||||
"id":36,
|
||||
"user_id":4,
|
||||
"create_time":"2023-12-13T15:55:12",
|
||||
"target_deploy_statuses":[
|
||||
|
||||
]
|
||||
},
|
||||
{
|
||||
"target_name":"XT-RANGE3",
|
||||
"description":"XT测试靶场3",
|
||||
"attribute":"public",
|
||||
"id":37,
|
||||
"user_id":4,
|
||||
"create_time":"2023-12-13T15:55:12",
|
||||
"target_deploy_statuses":[
|
||||
|
||||
]
|
||||
}
|
||||
],
|
||||
"total":6,
|
||||
"page":1,
|
||||
"size":50,
|
||||
"pages":1
|
||||
}
|
||||
}
|
||||
|
||||
export { getTargetsResponse }
|
||||
@@ -1,183 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="custom-dialog"
|
||||
v-if="visible"
|
||||
>
|
||||
<!-- 在此处指定弹窗的样式和内容 -->
|
||||
<i class="el-icon-close" style="float: right; padding-right: 8%;padding-top: 3%" @click="close"></i>
|
||||
<el-form
|
||||
ref="customForm"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="150px"
|
||||
class="custom-form"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="靶场名称" prop="target_name">
|
||||
<el-input v-model="form.target_name" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="靶场描述" prop="description">
|
||||
<el-input type="textarea" v-model="form.description" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="所有权" prop="attribute">
|
||||
<el-radio v-model="form.attribute" label="private">私有</el-radio>
|
||||
<el-radio v-model="form.attribute" label="public">公用</el-radio>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="submit-footer">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="resetForm">重置</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="submit" :loading="loading">{{isAdd ? '提交' : '确定'}}</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'CustomCreate',
|
||||
props: {
|
||||
isAdd: {
|
||||
typeof: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
loading: false,
|
||||
form: {
|
||||
target_name: '', // 靶场名称
|
||||
description: '', // 靶场描述
|
||||
attribute: '' // 所有权
|
||||
},
|
||||
target_id: '',
|
||||
user_id: '',
|
||||
rules: {
|
||||
target_name: [
|
||||
{ required: true, message: '请输入靶场名称', trigger: 'blur' }
|
||||
],
|
||||
description: [
|
||||
{ required: false, message: '请输入描述', trigger: 'blur' }
|
||||
],
|
||||
attribute: [
|
||||
{ required: true, message: '请选择所有权', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.resetForm()
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
},
|
||||
submit() {
|
||||
this.$refs.customForm.validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.isAdd) {
|
||||
this.add()
|
||||
} else {
|
||||
this.edit()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
add () {
|
||||
this.loading = true
|
||||
const url = this.$http.api.target
|
||||
this.$axios.post(url, this.form).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.resetForm()
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
this.$notify({
|
||||
title: '创建靶场成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
edit() {
|
||||
this.loading = true
|
||||
const url = this.$http.api.target + `/${this.target_id}`
|
||||
this.$axios.put(url, this.form).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.resetForm()
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
this.$notify({
|
||||
title: '编辑靶场成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.form = {
|
||||
target_name: '', // 靶场名称
|
||||
description: '', // 靶场描述
|
||||
attribute: '' // 所有权
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.custom-dialog{
|
||||
width: 520px;
|
||||
height: 363px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../img/Group.svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
|
||||
.custom-form {
|
||||
margin-top: 70px;
|
||||
text-align: left;
|
||||
}
|
||||
.submit-footer{
|
||||
width: 100%;
|
||||
float: left;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 2%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,161 +0,0 @@
|
||||
<template>
|
||||
<div class="head">
|
||||
<span style="font-size: 14px;float: left;padding-top: 1%">靶场列表</span>
|
||||
<img src="../../../img/btn/quickCreateRangeBtn.svg" style="height: 70% ;width: 10%;margin-right: 7%;color: #ffffff" @click="quickAdd">
|
||||
<img src="../../../img/btn/customCreateRangeBtn.svg" style="height: 70% ;width: 10%;margin-right: 7%;color: #ffffff" @click="customAdd">
|
||||
<div class="input">
|
||||
<el-input v-model="target_name" placeholder="名称查询输入" suffix icon="">
|
||||
<template v-slot:suffix>
|
||||
<div class="icon-group">
|
||||
<img src="../../../img/inputl.png" alt="**">
|
||||
<img src="../../../img/inputIcon.png" alt="*" @click="query">
|
||||
</div>
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="state">
|
||||
<el-select v-model="deploy_status" placeholder="全部状态" clearable @change="query">
|
||||
<el-option
|
||||
v-for="item in statusDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="project">
|
||||
<el-select v-model="attribute" placeholder="全部工程" clearable @change="query">
|
||||
<el-option
|
||||
v-for="item in attributeDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
target_name: '', // 靶场名称
|
||||
deploy_status: '',
|
||||
attribute: '',
|
||||
statusDict: [
|
||||
{
|
||||
value: '部署成功',
|
||||
label: '部署成功'
|
||||
},
|
||||
{
|
||||
value: '部署中',
|
||||
label: '部署中'
|
||||
},
|
||||
{
|
||||
value: '未部署',
|
||||
label: '未部署'
|
||||
},
|
||||
{
|
||||
value: '部署失败',
|
||||
label: '部署失败'
|
||||
}
|
||||
],
|
||||
attributeDict: [
|
||||
{
|
||||
value: 'public',
|
||||
label: '公用'
|
||||
},
|
||||
{
|
||||
value: 'private',
|
||||
label: '私有'
|
||||
}
|
||||
],
|
||||
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
quickAdd() {
|
||||
this.$emit('quickAdd')
|
||||
},
|
||||
customAdd() {
|
||||
this.$emit('customAdd')
|
||||
},
|
||||
query() {
|
||||
const params = {
|
||||
target_name: this.target_name,
|
||||
deploy_status: this.deploy_status,
|
||||
attribute: this.attribute
|
||||
}
|
||||
this.$emit('query', params)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.head{
|
||||
width: 95%;
|
||||
height: 7%;
|
||||
margin-top: 1%;
|
||||
margin-left: 2.5%;
|
||||
text-align: right;
|
||||
.block{
|
||||
display: inline-block;
|
||||
margin-left: 2%;
|
||||
|
||||
}
|
||||
.input{
|
||||
display: inline-block;
|
||||
height: 60%;
|
||||
width: 10%;
|
||||
margin-left: 0.5%;
|
||||
.el-input::placeholder {
|
||||
width: auto;
|
||||
}
|
||||
.icon-group {
|
||||
display: flex; /* 设置容器为 Flexbox 容器 */
|
||||
align-items: center; /* 垂直居中图片 */
|
||||
gap: 5px; /* 图片和文字之间的间距,可以根据需要进行调整 */
|
||||
|
||||
}
|
||||
.icon-group img {
|
||||
transform: scale(1);
|
||||
margin-right: 15px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
.state{
|
||||
display: inline-block;
|
||||
height: 60%;
|
||||
width: 10%;
|
||||
margin-left: 0.5%;
|
||||
.custom-popper .el-select-dropdown {
|
||||
max-height: 3px;
|
||||
}
|
||||
}
|
||||
.project{
|
||||
display: inline-block;
|
||||
height: 60%;
|
||||
width: 10%;
|
||||
margin-left: 0.5%;
|
||||
margin-right: 3%;
|
||||
.el-input::placeholder {
|
||||
width: auto;
|
||||
}
|
||||
.icon-group {
|
||||
display: flex; /* 设置容器为 Flexbox 容器 */
|
||||
align-items: center; /* 垂直居中图片 */
|
||||
gap: 5px; /* 图片和文字之间的间距,可以根据需要进行调整 */
|
||||
|
||||
}
|
||||
.icon-group img {
|
||||
transform: scale(1);
|
||||
margin-right: 15px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,437 +0,0 @@
|
||||
<template>
|
||||
<div class="quick-dialog" v-if="visible">
|
||||
<!-- 在此处指定弹窗的样式和内容 -->
|
||||
<i class="el-icon-close" style="float: right; padding-right: 8%;padding-top: 3%" @click="close"></i>
|
||||
<el-form
|
||||
ref="quickForm"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="150px"
|
||||
class="quick-form"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="靶场名称" prop="target_name">
|
||||
<el-input v-model="form.target_name" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="靶场描述" prop="description">
|
||||
<el-input type="textarea" v-model="form.description" placeholder="请输入内容"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="选择按真实tor网络模拟比例" class="long-item-label">
|
||||
<el-radio-group v-model="form.ratio" @change="radioChange">
|
||||
<el-radio label="0.01">1:100</el-radio>
|
||||
<el-radio label="0.02">1:200</el-radio>
|
||||
<el-radio label="0.002">1:500</el-radio>
|
||||
<el-radio label="0.001">1:1000</el-radio>
|
||||
</el-radio-group>
|
||||
<div style="margin-top: 2%">
|
||||
<span style="font-size: 12px">其他</span>
|
||||
<el-input v-model="otherRatio" class="other-ratio" placeholder="请输入内容" @focus="otherRadioInput"></el-input>
|
||||
<span style="font-size: 14px;color: #4b5362">(范围在0.001-0.01之间)</span>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="所有权" prop="attribute">
|
||||
<el-radio v-model="form.attribute" label="private">私有</el-radio>
|
||||
<el-radio v-model="form.attribute" label="public">公用</el-radio>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="创建方式" prop="createType">
|
||||
<el-radio v-model="form.createType" label="interface">接口模式</el-radio>
|
||||
<el-radio v-model="form.createType" label="consensus">consensus模式</el-radio>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<div class="upload-file" v-if="form.createType==='consensus'">
|
||||
<input class="uploadBgImg" type="file" name="file" @change="selectFile" />
|
||||
<div v-if="fileName">{{ fileName }}</div>
|
||||
</div>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="submit-footer" :style="form.createType==='consensus'?'margin-top: 60px':'margin-top: 120px'">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="resetForm">重置</el-button>
|
||||
<el-button class="glBut but-color" style="width: 120px;" type="primary" @click="submitConfig" :loading="configLoading">仅添加配置</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="submit" :loading="createLoading">创建</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'QuickCreate',
|
||||
props: {
|
||||
isAdd: {
|
||||
typeof: Boolean,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
createLoading: false,
|
||||
configLoading: false,
|
||||
form: {
|
||||
target_name: '', // 靶场名称
|
||||
description: '', // 靶场描述
|
||||
ratio: '', // 选择按真实tor网络模拟的比例
|
||||
attribute: '', // 所有权
|
||||
createType: '', // 创建方式
|
||||
file: '' // 上传文件
|
||||
},
|
||||
target_id: '',
|
||||
user_id: '',
|
||||
fileName: '',
|
||||
otherRatio: '',
|
||||
delTimer: null,
|
||||
counter: 0,
|
||||
rules: {
|
||||
target_name: [
|
||||
{ required: true, message: '请输入靶场名称', trigger: 'blur' }
|
||||
],
|
||||
description: [
|
||||
{ required: false, message: '请输入描述', trigger: 'blur' }
|
||||
],
|
||||
ratio: [
|
||||
{ required: true, message: '请选择模拟比例', trigger: 'change' }
|
||||
],
|
||||
attribute: [
|
||||
{ required: true, message: '请选择所有权', trigger: 'change' }
|
||||
],
|
||||
createType: [
|
||||
{ required: true, message: '请选择创建方式', trigger: 'change' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
destroyed() {
|
||||
if(this.delTimer) {
|
||||
clearInterval(this.delTimer)
|
||||
this.delTimer = null
|
||||
}
|
||||
this.counter = 0
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.resetForm()
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
},
|
||||
// 重置
|
||||
resetForm() {
|
||||
this.fileName = ''
|
||||
this.otherRatio = ''
|
||||
this.form = {
|
||||
target_name: '', // 靶场名称
|
||||
description: '', // 靶场描述
|
||||
ratio: '', // 选择按真实tor网络模拟的比例
|
||||
attribute: '', // 所有权
|
||||
createType: '', // 创建方式
|
||||
file: '' // 上传文件
|
||||
}
|
||||
},
|
||||
// 仅添加配置
|
||||
submitConfig() {
|
||||
this.$refs.quickForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.configLoading = true
|
||||
if (this.isAdd) {
|
||||
this.add(false)
|
||||
} else {
|
||||
this.edit(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
// 创建
|
||||
submit() {
|
||||
this.$refs.quickForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.createLoading = true
|
||||
if (this.isAdd) {
|
||||
this.add(true)
|
||||
} else {
|
||||
this.edit(true)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
add (isCreate) {
|
||||
if (this.form.createType === 'interface') {
|
||||
// 接口模式
|
||||
const url = isCreate ? this.$http.api.asyncQuickInterface + '/?direct=true' : this.$http.api.asyncQuickInterface + '/?direct=false'
|
||||
const {target_name, description, ratio, attribute} = this.form
|
||||
const submitForm = { target_name, description, ratio, attribute }
|
||||
this.$axios.postForm(url, submitForm).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
// this.$notify({
|
||||
// title: '创建靶场成功',
|
||||
// type: 'success',
|
||||
// duration: 2500
|
||||
// })
|
||||
this.$notify({
|
||||
title: res.message,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.delTimer = setInterval(() => {
|
||||
this.getTask(res?.result?.task_id || '')
|
||||
}, 10 * 1000)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.createLoading = false
|
||||
this.configLoading = false
|
||||
})
|
||||
} else {
|
||||
// consensus模式
|
||||
const url = isCreate ? this.$http.api.asyncQuickConsensus + '/?direct=true' : this.$http.api.asyncQuickConsensus + '/?direct=false'
|
||||
const submitForm = new FormData()
|
||||
for(const key in this.form) {
|
||||
submitForm.append(key, this.form[key])
|
||||
}
|
||||
this.$axios.postFormData(url, submitForm).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
// this.$notify({
|
||||
// title: '创建靶场成功',
|
||||
// type: 'success',
|
||||
// duration: 2500
|
||||
// })
|
||||
this.$notify({
|
||||
title: res.message,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.delTimer = setInterval(() => {
|
||||
this.getTask(res?.result?.task_id || '')
|
||||
},10 * 1000)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.createLoading = false
|
||||
this.configLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
edit(isCreate) {
|
||||
const editForm = JSON.parse(JSON.stringify(this.form))
|
||||
if (this.otherRatio !== '') {
|
||||
editForm.ratio = this.otherRatio
|
||||
}
|
||||
if(this.form.createType === 'interface') {
|
||||
// 接口模式
|
||||
const url = isCreate ? this.$http.api.asyncQuickInterface + `/${this.target_id}/?direct=true` : this.$http.api.asyncQuickInterface + `/${this.target_id}/?direct=false`
|
||||
this.$axios.putForm(url, editForm).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
// this.$notify({
|
||||
// title: '编辑靶场成功',
|
||||
// type: 'success',
|
||||
// duration: 2500
|
||||
// })
|
||||
this.$notify({
|
||||
title: res.message,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.delTimer = setInterval(() => {
|
||||
this.getTask(res?.result?.task_id || '')
|
||||
}, 10 * 1000)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.createLoading = false
|
||||
this.configLoading = false
|
||||
})
|
||||
} else {
|
||||
// consensus模式
|
||||
const url = isCreate ? this.$http.api.asyncQuickConsensus + `/${this.target_id}/?direct=true` : this.$http.api.asyncQuickConsensus + `/${this.target_id}/?direct=false`
|
||||
const submitForm = new FormData()
|
||||
for(const key in editForm) {
|
||||
submitForm.append(key, editForm[key])
|
||||
}
|
||||
this.$axios.putFormData(url, submitForm).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
// this.$notify({
|
||||
// title: '编辑靶场成功',
|
||||
// type: 'success',
|
||||
// duration: 2500
|
||||
// })
|
||||
this.$notify({
|
||||
title: res.message,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.delTimer = setInterval(() => {
|
||||
this.getTask(res?.result?.task_id || '')
|
||||
},10 * 1000)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.createLoading = false
|
||||
this.configLoading = false
|
||||
})
|
||||
}
|
||||
},
|
||||
// 模拟比例选择
|
||||
radioChange(val) {
|
||||
this.otherRatio = ''
|
||||
},
|
||||
// 模拟比例用输入框输入
|
||||
otherRadioInput(val) {
|
||||
this.form.ratio = ''
|
||||
},
|
||||
// 选择上传文件
|
||||
selectFile(e) {
|
||||
this.form.file = e.target.files[0]
|
||||
this.fileName = e.target.files[0].name
|
||||
},
|
||||
// 获取任务进度
|
||||
getTask(task_id) {
|
||||
const url = this.$http.api.task + '/' + task_id
|
||||
this.$axios.get(url, {}).then(res => {
|
||||
if(res.task_status !== 'PENDING' && res.task_status !== 'STARTED') {
|
||||
if (res?.task_result?.code === 500 || res?.task_result?.message === 'false') {
|
||||
this.$notify({
|
||||
title: res?.task_result?.result || `${res.task_id}任务执行完毕,靶场部署失败`,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: res?.task_result?.result || `${res.task_id}任务执行完毕,靶场部署成功`,
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
clearInterval(this.delTimer)
|
||||
this.delTimer = null
|
||||
this.counter = 0
|
||||
this.$emit('refresh')
|
||||
} else {
|
||||
// this.$notify({
|
||||
// title: `${res.task_id}任务执行中`,
|
||||
// type: 'success',
|
||||
// duration: 2500
|
||||
// })
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.counter++
|
||||
if (this.counter >= 60) {
|
||||
clearInterval(this.delTimer)
|
||||
this.delTimer = null
|
||||
this.counter = 0
|
||||
this.$emit('refresh')
|
||||
}
|
||||
})
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.quick-dialog{
|
||||
width: 620px;
|
||||
height: 675px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../img/Group (2).svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.quick-form {
|
||||
margin-top: 70px;
|
||||
text-align: left;
|
||||
.long-item-label {
|
||||
::v-deep .el-form-item__label {
|
||||
padding: 12px 12px 0 70px;
|
||||
}
|
||||
}
|
||||
.other-ratio{
|
||||
width: 40%;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
background-color: #0c295b;
|
||||
display: inline-block;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
.upload-file{
|
||||
width: 100%;
|
||||
/*margin-left: 10%;*/
|
||||
text-align: center;
|
||||
color: rgba(81, 84, 102, 0.84);
|
||||
::v-deep .el-upload-list {
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
width: 400px !important;
|
||||
padding-left: 20%;
|
||||
}
|
||||
.uploadBgImg{
|
||||
width: 320px;
|
||||
height: 40px;
|
||||
background-image: url("../../../img/shangchuan.png");
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% auto; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
text-align: right;
|
||||
padding-right: 10%;
|
||||
padding-top: 2%;
|
||||
font-size: 0;
|
||||
}
|
||||
.uploadBgImg::file-selector-button{
|
||||
padding: 0;
|
||||
background-color: transparent;
|
||||
cursor: pointer;
|
||||
font-size: 0;
|
||||
}
|
||||
}
|
||||
.submit-footer{
|
||||
width: 100%;
|
||||
float: left;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 2%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,220 +0,0 @@
|
||||
<template>
|
||||
<div :class="['tabs', {'is--maximize': isFullscreen}]">
|
||||
<el-tabs
|
||||
v-model="activeName"
|
||||
@tab-click="handleClick"
|
||||
>
|
||||
<el-tab-pane label="控制台" name="first">
|
||||
<ControlConsole ref="controlConsole"></ControlConsole>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="文件管理" name="second">
|
||||
<FileManage :is-fullscreen="isFullscreen"></FileManage>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<el-button class="console-btn" v-if="activeName === 'first'" :loading="consoleLoading" type="primary" @click="openConsoleTab">打开控制台</el-button>
|
||||
<span class="icon-span" slot="label">
|
||||
<i v-if="closeIcon" class="el-icon-close icon-zoom" @click="backNodeList"></i>
|
||||
<svg-icon v-else class="icon-zoom" :icon-class="fullscreenIcon" @click="zoomEvent"></svg-icon>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import ControlConsole from './module/ControlConsole'
|
||||
import FileManage from './module/FileManage'
|
||||
export default {
|
||||
components: { ControlConsole, FileManage },
|
||||
data() {
|
||||
return {
|
||||
activeName: 'first',
|
||||
nodeId: '',
|
||||
// consoleUrl: '',
|
||||
fullscreenIcon: 'fullscreen',
|
||||
isFullscreen: false,
|
||||
closeIcon: false,
|
||||
consoleLoading: false
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.nodeId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.nodeId = newVal
|
||||
// this.init()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.closeIcon = this.$route.params.closeIcon
|
||||
},
|
||||
methods: {
|
||||
backNodeList() {
|
||||
this.$router.push({ name: 'rangeNodeManage' })
|
||||
},
|
||||
handleClick(tab, event) {
|
||||
console.log(tab, event);
|
||||
},
|
||||
openConsoleTab() {
|
||||
// 获取webshell地址,用浏览器打开
|
||||
const reqParams = { node_id: this.nodeId }
|
||||
this.consoleLoading = true
|
||||
this.$axios.get(this.$http.api.getWebshell, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
const consoleUrl = 'http://' + window.g.baseURL + '/' + res?.result
|
||||
window.open(consoleUrl, '_blank')
|
||||
// var newWindow = window.open(consoleUrl , '_blank');
|
||||
// // 等待新页面加载完成
|
||||
// newWindow.onload = function() {
|
||||
// console.log('1111111111111111')
|
||||
// // 选择用户名输入框
|
||||
// var usernameInput = newWindow.document.querySelector('input[name="username"]');
|
||||
// // 选择密码输入框
|
||||
// var passwordInput = newWindow.document.querySelector('input[name="password"]');
|
||||
// // 选择登录按钮
|
||||
// var loginButton = newWindow.document.querySelector('button[type="submit"]');
|
||||
// console.log(usernameInput, 'usernameInput====')
|
||||
|
||||
// // 如果输入框存在,则自动输入用户名和密码
|
||||
// if (usernameInput && passwordInput && loginButton) {
|
||||
// usernameInput.value = 'admin';
|
||||
// passwordInput.value = 'Bjhit@2020';
|
||||
|
||||
// // 触发登录按钮的点击事件
|
||||
// loginButton.click();
|
||||
// }
|
||||
// };
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.consoleLoading = false
|
||||
})
|
||||
},
|
||||
zoomEvent() {
|
||||
if (this.activeName === 'first') {
|
||||
|
||||
this.isFullscreen = !this.isFullscreen
|
||||
this.fullscreenIcon = this.isFullscreen ? 'narrow' : 'fullscreen'
|
||||
// this.$refs.controlConsole.resize()
|
||||
} else {
|
||||
this.isFullscreen = !this.isFullscreen
|
||||
this.fullscreenIcon = this.isFullscreen ? 'narrow' : 'fullscreen'
|
||||
}
|
||||
},
|
||||
findNthOccurrence(str, char, n) {
|
||||
let index = str.indexOf(char)
|
||||
while (--n > 0 && index !== -1) {
|
||||
index = str.indexOf(char, index + 1)
|
||||
}
|
||||
return index
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.is--maximize {
|
||||
position: fixed !important;
|
||||
top: 9%;
|
||||
left: 10%;
|
||||
width: 90% !important;
|
||||
height: 91% !important;
|
||||
padding: 0.5em 1em;
|
||||
// background-color: #17234e;
|
||||
background: url(../../../../img/background/bgMain.svg);
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-position: center; /* 居中显示 */
|
||||
background-size: cover; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
z-index: 1000;
|
||||
}
|
||||
// ::v-deep #tab-zoom {
|
||||
// border: none;
|
||||
// background-color: #1b202e !important;
|
||||
// position: absolute;
|
||||
// top: 1px;
|
||||
// right: 0;
|
||||
// font-weight: 500;
|
||||
// font-size: 15px;
|
||||
// color: #fff !important;
|
||||
// }
|
||||
.tabs {
|
||||
width: 100%;
|
||||
height: calc(100% - 15px);
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding: 0 0;
|
||||
// display: flex;
|
||||
// .detail-title {
|
||||
// position:absolute;
|
||||
// margin: 10px;
|
||||
// }
|
||||
}
|
||||
::v-deep .el-tabs{
|
||||
color: #000;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
padding: 0 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.el-tabs__header {
|
||||
margin: 15px 30px;
|
||||
}
|
||||
.el-tabs__content {
|
||||
width: 100%;
|
||||
height: calc(100% - 50px);
|
||||
.el-tab-pane {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 去除灰色横条 */
|
||||
::v-deep .el-tabs__nav-wrap::after {
|
||||
position: static !important;
|
||||
}
|
||||
/* 设置滑块颜色 */
|
||||
::v-deep .el-tabs__active-bar{
|
||||
background-color: #0E3D8A !important;
|
||||
}
|
||||
/* 设置滑块停止位置 */
|
||||
::v-deep .el-tabs__active-bar.is-top{
|
||||
height: 37px;
|
||||
width: 104px ! important;
|
||||
border-radius: 17px;
|
||||
top: 0px !important;
|
||||
left: -18px !important;
|
||||
position: absolute !important;
|
||||
z-index: 1;
|
||||
}
|
||||
/* 设置当前选中样式 */
|
||||
::v-deep .el-tabs__item.is-active{
|
||||
color:#02DDEA !important;
|
||||
z-index: 2;
|
||||
}
|
||||
/* 设置未被选中样式 */
|
||||
::v-deep .el-tabs__item{
|
||||
padding: 0 20px !important;
|
||||
width: 104px;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
position: relative !important;
|
||||
color:#02DDEA !important;
|
||||
z-index: 2;
|
||||
}
|
||||
.console-btn {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 100px;
|
||||
}
|
||||
.icon-span {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 30px;
|
||||
.icon-zoom {
|
||||
font-size: 30px;
|
||||
color: #02DDEA;
|
||||
margin: 0 15px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,389 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
style="height: 100%;
|
||||
background: #002833;"
|
||||
>
|
||||
<div class="indexContainer" id="terminal" ref="terminal" v-resize="onResize"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 引入xterm,请注意这里和3.x版本的引入路径不一样
|
||||
import { Terminal } from "xterm";
|
||||
import "xterm/css/xterm.css";
|
||||
import "xterm/lib/xterm.js";
|
||||
import { FitAddon } from "xterm-addon-fit";
|
||||
|
||||
import resize from 'vue-resize-directive';
|
||||
|
||||
export default {
|
||||
name: "Shell",
|
||||
directives: { resize },
|
||||
data() {
|
||||
return {
|
||||
nodeId: '',
|
||||
showOrder: "", // 保存服务端返回的命令
|
||||
inputList: [],
|
||||
shellWs: "",
|
||||
term: "", // 保存terminal实例
|
||||
rows: 40,
|
||||
cols: 100,
|
||||
urlParam: {
|
||||
Tag: 'tag',
|
||||
name: 'name',
|
||||
pod: 'pod'
|
||||
},
|
||||
commandPrefix: 'target'
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.nodeId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.nodeId = newVal
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getWebsocketUrl()
|
||||
},
|
||||
|
||||
mounted() {
|
||||
let _this = this;
|
||||
localStorage.setItem('commands', '')
|
||||
// 获取容器宽高/字号大小,定义行数和列数
|
||||
this.rows = document.querySelector(".indexContainer").offsetHeight / 16 - 6;
|
||||
this.cols = document.querySelector(".indexContainer").offsetWidth / 14;
|
||||
|
||||
let term = new Terminal({
|
||||
rendererType: "canvas", //渲染类型
|
||||
rows: parseInt(_this.rows), //行数
|
||||
cols: parseInt(_this.cols), // 不指定行数,自动回车后光标从下一行开始
|
||||
convertEol: true, //启用时,光标将设置为下一行的开头
|
||||
fontSize: 16, //字体大小
|
||||
// scrollback: 50, //终端中的回滚量
|
||||
disableStdin: false, //是否应禁用输入。
|
||||
cursorStyle: "underline", //光标样式
|
||||
cursorBlink: true, //光标闪烁
|
||||
// theme: {
|
||||
// foreground: "#7e9192", //字体
|
||||
// background: "#002833", //背景色
|
||||
// cursor: "help", //设置光标
|
||||
// lineHeight: 16
|
||||
// }
|
||||
theme: {
|
||||
foreground: "yellow", //字体
|
||||
background: "#060101", //背景色
|
||||
cursor: "help", //设置光标
|
||||
lineHeight: 16
|
||||
}
|
||||
});
|
||||
|
||||
// 创建terminal实例
|
||||
term.open(this.$refs["terminal"]);
|
||||
|
||||
// 换行并输入起始符“$”
|
||||
term.prompt = () => {
|
||||
term.write("\r\n$ ");
|
||||
};
|
||||
term.prompt();
|
||||
|
||||
// // canvas背景全屏
|
||||
var fitAddon = new FitAddon();
|
||||
term.loadAddon(fitAddon);
|
||||
fitAddon.fit();
|
||||
|
||||
window.addEventListener("resize", resizeScreen);
|
||||
// document.querySelector(".indexContainer").addEventListener('resize', resizeScreen)
|
||||
|
||||
// 内容全屏显示
|
||||
function resizeScreen() {
|
||||
// 不传size
|
||||
try {
|
||||
fitAddon.fit();
|
||||
// 窗口大小改变时触发xterm的resize方法,向后端发送行列数,格式由后端决定
|
||||
// 这里不使用size默认参数,因为改变窗口大小只会改变size中的列数而不能改变行数,所以这里不使用size.clos,而直接使用获取我们根据窗口大小计算出来的行列数
|
||||
term.onResize(() => {
|
||||
_this.onSend({ Op: "resize", Cols: term.cols, Rows: term.rows });
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("e", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
function runFakeTerminal(_this) {
|
||||
if (term._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
term._initialized = true;
|
||||
|
||||
term.prompt = () => {
|
||||
term.write("\r\n ");
|
||||
};
|
||||
|
||||
// term.writeln("Welcome to xterm.js");
|
||||
// term.writeln(
|
||||
// "This is a local terminal emulation, without a real terminal in the back-end."
|
||||
// );
|
||||
// term.writeln("Type some keys and commands to play around.");
|
||||
|
||||
// term.writeln("root@target:/#");
|
||||
// term.prompt();
|
||||
|
||||
// 监控键盘输入事件
|
||||
// / **
|
||||
// *添加事件监听器,用于按下键时的事件。事件值包含
|
||||
// *将在data事件以及DOM事件中发送的字符串
|
||||
// *触发了它。
|
||||
// * @返回一个IDisposable停止监听。
|
||||
// * /
|
||||
let last = 0;
|
||||
|
||||
term.onKey(function(event) {
|
||||
// 可打印状态,即不是alt键ctrl等功能健时
|
||||
// console.log(event, '输入的key======')
|
||||
const printable = !event.domEvent.altKey && !event.domEvent.altGraphKey && !event.domEvent.ctrlKey && !event.domEvent.metaKey
|
||||
|
||||
// !key.charCodeAt(0).altKey && !key.charCodeAt(0).altGraphKey && !key.charCodeAt(0).ctrlKey && !key.charCodeAt(0).metaKey;
|
||||
|
||||
// 因服务端返回命令包含乱码,但使用write方法输出时并不显示,故将真实显示内容截取出来
|
||||
let show = ''
|
||||
if (_this.showOrder) {
|
||||
let index = _this.showOrder.indexOf("sh");
|
||||
show = _this.showOrder.substr(index, _this.showOrder.length - 1);
|
||||
}
|
||||
|
||||
// 当输入回车时
|
||||
if (event.domEvent.keyCode === 13) {
|
||||
if (_this.order == "cls" || _this.order == "clear") {
|
||||
_this.order = "";
|
||||
return false;
|
||||
}
|
||||
//先将数据发送
|
||||
term.prompt();
|
||||
// 判断如果不是英文给出提醒
|
||||
let reg = /[a-zA-Z]/;
|
||||
// let order = {
|
||||
// Data: _this.order,
|
||||
// Op: "stdin"
|
||||
// };
|
||||
let order = _this.order
|
||||
|
||||
if (!reg.test(_this.order)) {
|
||||
term.writeln("请输入有效指令~");
|
||||
} else {
|
||||
// 发送数据
|
||||
_this.inputList.push(_this.order);
|
||||
last = _this.inputList.length - 1;
|
||||
_this.onSend(order);
|
||||
// 清空输入内容变量
|
||||
}
|
||||
} else if (event.domEvent.keyCode === 8) {
|
||||
// 当输入退
|
||||
// 当前行字符长度如果等于后端返回字符就不进行删除
|
||||
if(_this.order.length > 0) {
|
||||
_this.order = _this.order.substr(0, _this.order.length - 1);
|
||||
term.write('\b \b')
|
||||
}
|
||||
// if (term._core.buffer.x > _this.showOrder.length) {
|
||||
// term.write("\b \b"); // 输出退格
|
||||
// }
|
||||
|
||||
// // 将输入内容变量删除
|
||||
|
||||
// if (_this.trim(_this.order) == _this.trim(_this.showOrder)) {
|
||||
// return false;
|
||||
// } else {
|
||||
// _this.order = _this.order.substr(0, _this.order.length - 1);
|
||||
// term.write('\b \b')
|
||||
// }
|
||||
} else if (event.domEvent.keyCode === 127) {
|
||||
if (term._core.buffer.x > (_this.showOrder.length + 2)) {
|
||||
term.write('\b \b')
|
||||
_this.order = _this.order.substr(0, _this.order.length - 1)
|
||||
}
|
||||
} else if (event.domEvent.keyCode === 38 || event.domEvent.keyCode === 40) {
|
||||
let len = _this.inputList.length;
|
||||
let code = event.domEvent.keyCode;
|
||||
|
||||
if (code === 38 && last <= len && last >= 0) {
|
||||
// 直接取出字符串数组最后一个元素
|
||||
let inputVal = _this.inputList[last];
|
||||
term.write(inputVal);
|
||||
if (last > 0) {
|
||||
last--;
|
||||
}
|
||||
}
|
||||
if (code === 40 && last < len) {
|
||||
// last现在为当前元素
|
||||
if (last == len - 1) {
|
||||
return;
|
||||
}
|
||||
if (last < len - 1) {
|
||||
last++;
|
||||
}
|
||||
|
||||
let inputVal = _this.inputList[last];
|
||||
term.write(inputVal);
|
||||
}
|
||||
} else if (event.domEvent.keyCode === 9) {
|
||||
// 如果按tab键前输入了之前后端返回字符串的第一个字符,就显示此命令
|
||||
if (_this.order !== "" && show.indexOf(_this.order) == 0) {
|
||||
term.write(_this.showOrder);
|
||||
}
|
||||
} else if (event.key == '\x16') {
|
||||
navigator.clipboard.readText().then(clipText => {
|
||||
term.write(clipText);
|
||||
})
|
||||
//ctrol+ c copy
|
||||
} else if (event.key == '\x03' && term.hasSelection()) {
|
||||
navigator.clipboard.writeText(term.getSelection())
|
||||
} else if (printable) {
|
||||
let key = event.key
|
||||
// // 当为可打印内容时
|
||||
// if (/[a-zA-Z]/.test(key)) {
|
||||
// key = key.toLowerCase();
|
||||
// }
|
||||
// 存入输入内容变量
|
||||
_this.order = _this.order + key;
|
||||
// 将变量写入终端内
|
||||
term.write(key);
|
||||
}
|
||||
});
|
||||
|
||||
_this.term = term;
|
||||
}
|
||||
runFakeTerminal(_this);
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* **wsShell 创建页面级别的websocket,加载页面数据
|
||||
* ws 接口:/xxx/xxx/xxx
|
||||
* 参数:无
|
||||
* ws参数:
|
||||
* @deployId 任务id
|
||||
* @tagString 当前节点
|
||||
* 返回:无
|
||||
* **/
|
||||
wsShell(url) {
|
||||
const _this = this;
|
||||
let tag = this.urlParam.Tag;
|
||||
let name= this.urlParam.name;
|
||||
let pod= this.urlParam.pod;
|
||||
|
||||
// let query = `?tag=${tag}&name=${name}&pod=${pod}`;
|
||||
// let url = `xxxx/xxxx${query}`;// websocket连接接口
|
||||
// let url = 'ws://172.16.0.120:30598/ws/target-19-austria-80-110-35-0-24/target-19-relay-127-54b5df76f4-8958g/relay-127'
|
||||
|
||||
this.shellWs = this.websoketLib.WS({
|
||||
url,
|
||||
isInit: true,
|
||||
openFn(e) {
|
||||
console.log('连接websocket成功===', e)
|
||||
// _this.term.resize({ rows: _this.rows, cols: 100 }); //终端窗口重新设置大小 并触发term.on("resize")
|
||||
},
|
||||
messageFn(e) {
|
||||
console.log("message", e);
|
||||
if (e) {
|
||||
// let data = JSON.parse(e.data);
|
||||
// if (data.Data == "\n" || data.Data == "\r\nexit\r\n") {
|
||||
// _this.$message("连接已关闭");
|
||||
// }
|
||||
// 打印后端返回数据
|
||||
// _this.term.write(data.Data);
|
||||
|
||||
let backData = ''
|
||||
// 创建一个包含文本内容的Blob对象
|
||||
var blob = e.data;
|
||||
// 创建一个新的FileReader对象
|
||||
var reader = new FileReader();
|
||||
// 读取Blob并将其转换为字符串
|
||||
reader.onloadend = function () {
|
||||
backData = reader.result; // 获得转换后的字符串
|
||||
console.log(backData, '服务端websockt返回结果===='); // 输出结果到控制台
|
||||
let index = _this.order?.length;
|
||||
backData = backData.substring(index, backData.length);
|
||||
// 如果返回字符包含这些字符显示close提示
|
||||
if (backData == "\n" || backData == "\r\nexit\r\n") {
|
||||
alert("closed");
|
||||
}
|
||||
if (backData.trim() === 'Connected!') {
|
||||
_this.term.write(backData)
|
||||
_this.term.write(`root@${_this.commandPrefix}:/#`);
|
||||
} else {
|
||||
_this.term.write(backData)
|
||||
}
|
||||
_this.showOrder = backData;
|
||||
_this.order = "";
|
||||
};
|
||||
|
||||
// 开始读取Blob
|
||||
reader.readAsText(blob);
|
||||
}
|
||||
},
|
||||
errorFn(e) {
|
||||
//出现错误关闭当前ws,并且提示
|
||||
console.log("error", e);
|
||||
_this.$message.error({
|
||||
message: "ws 请求失败,请刷新重试~",
|
||||
duration: 5000
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onSend(data) {
|
||||
// data = this.websoketLib.isObject(data) ? JSON.stringify(data) : data;
|
||||
// data = this.websoketLib.isArray(data) ? data.toString() : data;
|
||||
// data = data.replace(/\\\\/, "\\");
|
||||
this.shellWs.onSend(data);
|
||||
},
|
||||
|
||||
//删除左右两端的空格
|
||||
trim(str) {
|
||||
return str.replace(/(^\s*)|(\s*$)/g, "");
|
||||
},
|
||||
// 获取websocket地址
|
||||
getWebsocketUrl() {
|
||||
const reqParams = { node_id: this.nodeId }
|
||||
this.$axios.get(this.$http.api.getWebsocketUrl, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
const wsUrl = 'ws://' + window.g.baseURL + res.result
|
||||
this.wsShell(wsUrl)
|
||||
this.commandPrefix = res.result.split('/')[3]
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 全屏时重新设置控制台行高
|
||||
onResize() {
|
||||
this.rows = document.querySelector(".indexContainer").offsetHeight / 16 - 6;
|
||||
this.cols = document.querySelector(".indexContainer").offsetWidth / 14;
|
||||
this.term.resize(parseInt(this.rows), parseInt(this.cols))
|
||||
// canvas背景全屏
|
||||
var fitAddon = new FitAddon();
|
||||
this.term.loadAddon(fitAddon);
|
||||
try {
|
||||
fitAddon.fit();
|
||||
// 窗口大小改变时触发xterm的resize方法,向后端发送行列数,格式由后端决定
|
||||
// 这里不使用size默认参数,因为改变窗口大小只会改变size中的列数而不能改变行数,所以这里不使用size.clos,而直接使用获取我们根据窗口大小计算出来的行列数
|
||||
term.onResize(() => {
|
||||
this.onSend({ Op: "resize", Cols: term.cols, Rows: term.rows });
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("e", e.message);
|
||||
}
|
||||
},
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.indexContainer {
|
||||
height: calc(100% - 0px);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,306 +0,0 @@
|
||||
<template>
|
||||
<!-- <div class="content"> -->
|
||||
<!-- <div id="xterm" class="xterm"/> -->
|
||||
<div id="log" style="margin:10px auto;">
|
||||
<div class="console" id="terminal"></div>
|
||||
</div>
|
||||
<!-- </div> -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import "xterm/css/xterm.css";
|
||||
import { Terminal } from "xterm";
|
||||
import { FitAddon } from "xterm-addon-fit";
|
||||
import { AttachAddon } from "xterm-addon-attach";
|
||||
export default {
|
||||
name: "Xterm",
|
||||
props: {
|
||||
// socketURI: {
|
||||
// type: String,
|
||||
// default: ''
|
||||
// },
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
term: null, //terminal 黑窗口容器
|
||||
socket: null,
|
||||
rows: 32,
|
||||
cols: 20,
|
||||
SetOut: false,
|
||||
isKey: false,
|
||||
prefix: "[root@serverip ~]# "//前缀
|
||||
// inputText: "",//输入内容,每次回车后进行ws通信然后清空此数据
|
||||
};
|
||||
},
|
||||
watch: {},
|
||||
mounted() {
|
||||
this.initSocket()
|
||||
},
|
||||
beforeDestroy() {
|
||||
this.socket.close()
|
||||
// this.term.dispose()
|
||||
},
|
||||
methods: {
|
||||
//初始化黑窗口
|
||||
async initTerm() {
|
||||
const term = new Terminal({
|
||||
rendererType: "canvas", //渲染类型
|
||||
rows: this.rows, //行数
|
||||
// cols: this.cols,// 设置之后会输入多行之后覆盖现象
|
||||
convertEol: true, //启用时,光标将设置为下一行的开头
|
||||
// scrollback: 10,//终端中的回滚量
|
||||
fontSize: 14, //字体大小
|
||||
disableStdin: false, //是否应禁用输入。
|
||||
cursorStyle: "block", //光标样式
|
||||
// cursorBlink: true, //光标闪烁
|
||||
scrollback: 30,
|
||||
tabStopWidth: 4,
|
||||
theme: {
|
||||
foreground: "yellow", //字体
|
||||
background: "#060101", //背景色
|
||||
cursor: "help" //设置光标
|
||||
}
|
||||
});
|
||||
|
||||
const attachAddon = new AttachAddon(this.socket);
|
||||
const fitAddon = new FitAddon();
|
||||
term.loadAddon(attachAddon);
|
||||
term.loadAddon(fitAddon);
|
||||
//开启Xterm终端
|
||||
term.open(document.getElementById("terminal"));
|
||||
|
||||
term.write('Hello from \x1B[1;3;31mxterm.js\x1B[0m $ ');
|
||||
term.focus();
|
||||
this.term = term;
|
||||
// if (term._initialized) {
|
||||
// return
|
||||
// }
|
||||
// term._initialized = true
|
||||
// term.prompt = () => {
|
||||
// term.write('\r\n$ ')
|
||||
// }
|
||||
|
||||
// term.writeln('Welcome to xterm.js')
|
||||
// term.writeln('This is a local terminal emulation, Type some keys and commands to play around')
|
||||
// term.writeln('')
|
||||
// term.prompt()
|
||||
|
||||
// // xterm.4.x 输入
|
||||
// term.onKey(e => {
|
||||
// const ev = e.domEvent
|
||||
// const printable = !ev.altKey && !ev.altGraphKey && !ev.ctrlKey && !ev.metaKey
|
||||
// if (ev.keyCode === 13) {
|
||||
// term.prompt()
|
||||
// } else if (ev.keyCode === 8) {
|
||||
// // Do not delete the prompt
|
||||
// if (term._core.buffer.x > 2) {
|
||||
// term.write('\b \b')
|
||||
// }
|
||||
// } else if (printable) {
|
||||
// term.write(e.key);
|
||||
// this.socket.send(e.key);
|
||||
// }
|
||||
// })
|
||||
// this.term = term
|
||||
},
|
||||
// //事件
|
||||
// termKeyCode() {
|
||||
// const TERMINAL_INPUT_KEY = {
|
||||
// BACK: 8, // 退格删除键
|
||||
// ENTER: 13, // 回车键
|
||||
// UP: 38, // 方向盘上键
|
||||
// DOWN: 40, // 方向盘键
|
||||
// LEFT: 37, // 方向盘左键
|
||||
// RIGHT: 39, // 方向盘右键
|
||||
// };
|
||||
// const { eqpCode, server } = this.selectObj;
|
||||
// let inputText = "";
|
||||
// let currentIndex = 0;
|
||||
// let inputTextList = [];
|
||||
// this.term.onKey((e) => {
|
||||
// const { key, domEvent } = e;
|
||||
// const { keyCode, altKey, altGraphKey, ctrlKey, metaKey } = domEvent;
|
||||
|
||||
// const printAble = !(altKey || altGraphKey || ctrlKey || metaKey); // 禁止相关按键
|
||||
// const totalOffsetLength = inputText.length + this.prefix.length; // 总偏移量
|
||||
// const currentOffsetLength = this.term._core.buffer.x; // 当前x偏移量
|
||||
|
||||
// switch (keyCode) {
|
||||
// //删除
|
||||
// case TERMINAL_INPUT_KEY.BACK:
|
||||
// if (currentOffsetLength > this.prefix.length) {
|
||||
// const cursorOffSetLength = this.getCursorOffsetLength(totalOffsetLength - currentOffsetLength, "\x1b[D"); // 保留原来光标位置
|
||||
|
||||
// this.term._core.buffer.x = currentOffsetLength - 1;
|
||||
// this.term.write("\x1b[?K" + inputText.slice(currentOffsetLength - this.prefix.length));
|
||||
// this.term.write(cursorOffSetLength);
|
||||
// inputText = `${inputText.slice(0, currentOffsetLength - this.prefix.length - 1)}${inputText.slice(
|
||||
// currentOffsetLength - this.prefix.length
|
||||
// )}`;
|
||||
// }
|
||||
// break;
|
||||
// //回车
|
||||
// case TERMINAL_INPUT_KEY.ENTER: {
|
||||
// this.term.write("\r\n");
|
||||
// console.log("inputText", inputText);
|
||||
// //ws 通信参数
|
||||
// let wsParams = { EqpCode: eqpCode, Action: "terminal", Data: inputText };
|
||||
// this.$emit("websocketSend", wsParams, server);
|
||||
|
||||
// if (!inputText.trim()) {
|
||||
// this.term.prompt();
|
||||
// return;
|
||||
// }
|
||||
|
||||
// if (inputTextList.indexOf(inputText) === -1) {
|
||||
// inputTextList.push(inputText);
|
||||
// currentIndex = inputTextList.length;
|
||||
// }
|
||||
|
||||
// this.term.prompt();
|
||||
// inputText = "";
|
||||
// break;
|
||||
// }
|
||||
|
||||
// case TERMINAL_INPUT_KEY.UP: {
|
||||
// if (!inputTextList[currentIndex - 1]) break;
|
||||
|
||||
// const offsetLength = this.getCursorOffsetLength(inputText.length, "\x1b[D");
|
||||
|
||||
// inputText = inputTextList[currentIndex - 1];
|
||||
// this.term.write(offsetLength + "\x1b[?K");
|
||||
// this.term.write(inputTextList[currentIndex - 1]);
|
||||
// this.term._core.buffer.x = totalOffsetLength;
|
||||
// currentIndex--;
|
||||
|
||||
// break;
|
||||
// }
|
||||
// case TERMINAL_INPUT_KEY.LEFT:
|
||||
// if (currentOffsetLength > this.prefix.length) {
|
||||
// this.term.write(key); // '\x1b[D'
|
||||
// }
|
||||
// break;
|
||||
|
||||
// case TERMINAL_INPUT_KEY.RIGHT:
|
||||
// if (currentOffsetLength < totalOffsetLength) {
|
||||
// this.term.write(key); // '\x1b[C'
|
||||
// }
|
||||
// break;
|
||||
// default: {
|
||||
// // 在当前的坐标写上 key 和坐标后面的字符
|
||||
// // 移动停留在当前位置的光标
|
||||
// if (!printAble) break;
|
||||
// if (totalOffsetLength >= this.term.cols) break;
|
||||
// if (currentOffsetLength >= totalOffsetLength) {
|
||||
// this.term.write(key);
|
||||
// inputText += key;
|
||||
// break;
|
||||
// }
|
||||
// let cursorOffSetLength = this.getCursorOffsetLength(totalOffsetLength - currentOffsetLength, "\x1b[D");
|
||||
// this.term.write("\x1b[?K" + `${key}${inputText.slice(currentOffsetLength - this.prefix.length)}`);
|
||||
// this.term.write(cursorOffSetLength);
|
||||
// inputText = inputText.slice(0, currentOffsetLength) + key + inputText.slice(totalOffsetLength - currentOffsetLength);
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
// },
|
||||
// //限制和后端交互,只有输入回车键才显示结果
|
||||
// termPromt() {
|
||||
// this.term.prompt = () => {
|
||||
// this.term.write(this.prefix);
|
||||
// };
|
||||
// },
|
||||
//获取光标当前位置
|
||||
getCursorOffsetLength(offsetLength, subString) {
|
||||
let cursorOffsetLength = "";
|
||||
for (let offset = 0; offset < offsetLength; offset++) {
|
||||
cursorOffsetLength += subString;
|
||||
}
|
||||
return cursorOffsetLength;
|
||||
},
|
||||
//写入黑窗口
|
||||
wirteTerm(data) {
|
||||
console.log("写入黑窗口", data);
|
||||
this.term.writeln(data);
|
||||
this.term.prompt();
|
||||
},
|
||||
// //加载基础数据
|
||||
// pageLoad(data) {
|
||||
// this.selectObj = data;
|
||||
// this.drawerFlag = true;
|
||||
// this.$nextTick(() => {
|
||||
// this.initTerm();
|
||||
// });
|
||||
// },
|
||||
cancelClick() {
|
||||
this.drawerFlag = false;
|
||||
//关闭弹框
|
||||
this.term.dispose(document.getElementById("xterm"));
|
||||
},
|
||||
initSocket() {
|
||||
const wsurl = 'ws://172.16.0.120:30598/ws/target-19-austria-80-110-35-0-24/target-19-relay-127-54b5df76f4-8958g/relay-127'
|
||||
this.socket = new WebSocket(wsurl);
|
||||
this.socketOnClose();
|
||||
this.socketOnOpen();
|
||||
this.socketOnError();
|
||||
},
|
||||
socketOnOpen() {
|
||||
this.socket.onopen = () => {
|
||||
console.log('socket 连接成功')
|
||||
// 链接成功后
|
||||
this.initTerm()
|
||||
}
|
||||
// this.socket.onmessage = function(evt) {
|
||||
// // let str = new TextDecoder().decode(evt.data);
|
||||
// // console.log('onmessage=====', evt)
|
||||
// this.term.write(evt.data);
|
||||
// };
|
||||
// //返回
|
||||
// this.socket.onmessage = function(evt) {
|
||||
// // let str = new TextDecoder().decode(evt.data);
|
||||
// console.log('onmessage=====', evt)
|
||||
// this.term.write(evt.data);
|
||||
// };
|
||||
},
|
||||
socketOnClose() {
|
||||
this.socket.onclose = (e) => {
|
||||
console.log('socket 关闭:' + '错误码==' + e.code + ';错误原因==' + e.reason + ';wasClean==' + e.wasClean)
|
||||
}
|
||||
},
|
||||
socketOnError() {
|
||||
this.socket.onerror = () => {
|
||||
console.log('socket 连接失败')
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
// .content {
|
||||
// ::v-deep .el-textarea__inner {
|
||||
// background-color: #1A2648;
|
||||
// }
|
||||
// }
|
||||
.xterm-screen{
|
||||
min-height: calc(100vh);
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
h1, h2 {
|
||||
font-weight: normal;
|
||||
}
|
||||
ul {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
}
|
||||
li {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
}
|
||||
a {
|
||||
color: #42b983;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,349 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
style="height: 100%;
|
||||
background: #002833;"
|
||||
>
|
||||
<div class="indexContainer" id="terminal" ref="terminal"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// 引入xterm,请注意这里和3.x版本的引入路径不一样
|
||||
import { Terminal } from "xterm";
|
||||
import "xterm/css/xterm.css";
|
||||
import "xterm/lib/xterm.js";
|
||||
import { FitAddon } from "xterm-addon-fit";
|
||||
|
||||
export default {
|
||||
name: "Shell",
|
||||
data() {
|
||||
return {
|
||||
nodeId: '',
|
||||
shellWs: "",
|
||||
term: "", // 保存terminal实例
|
||||
rows: 40,
|
||||
cols: 100,
|
||||
urlParam: {
|
||||
Tag: 'tag',
|
||||
name: 'name',
|
||||
pod: 'pod'
|
||||
}
|
||||
};
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.nodeId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.nodeId = newVal
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getWebsocketUrl()
|
||||
},
|
||||
|
||||
mounted() {
|
||||
let _this = this;
|
||||
localStorage.setItem('commands', '')
|
||||
// 获取容器宽高/字号大小,定义行数和列数
|
||||
// this.rows = document.querySelector(".indexContainer").offsetHeight / 16 - 6;
|
||||
// this.cols = document.querySelector(".indexContainer").offsetWidth / 14;
|
||||
|
||||
let term = new Terminal({
|
||||
rendererType: "canvas", //渲染类型
|
||||
// rows: parseInt(_this.rows), //行数
|
||||
// cols: parseInt(_this.cols), // 不指定行数,自动回车后光标从下一行开始
|
||||
convertEol: true, //启用时,光标将设置为下一行的开头
|
||||
// scrollback: 50, //终端中的回滚量
|
||||
disableStdin: false, //是否应禁用输入。
|
||||
cursorStyle: "underline", //光标样式
|
||||
cursorBlink: true, //光标闪烁
|
||||
theme: {
|
||||
foreground: "#7e9192", //字体
|
||||
background: "#002833", //背景色
|
||||
cursor: "help", //设置光标
|
||||
lineHeight: 16
|
||||
}
|
||||
});
|
||||
|
||||
// 创建terminal实例
|
||||
term.open(this.$refs["terminal"]);
|
||||
|
||||
// 换行并输入起始符“$”
|
||||
term.prompt = () => {
|
||||
term.write("\r\n$ ");
|
||||
};
|
||||
term.prompt();
|
||||
|
||||
// // canvas背景全屏
|
||||
var fitAddon = new FitAddon();
|
||||
term.loadAddon(fitAddon);
|
||||
fitAddon.fit();
|
||||
|
||||
window.addEventListener("resize", resizeScreen);
|
||||
|
||||
// 内容全屏显示
|
||||
function resizeScreen() {
|
||||
// 不传size
|
||||
|
||||
try {
|
||||
fitAddon.fit();
|
||||
|
||||
// 窗口大小改变时触发xterm的resize方法,向后端发送行列数,格式由后端决定
|
||||
// 这里不使用size默认参数,因为改变窗口大小只会改变size中的列数而不能改变行数,所以这里不使用size.clos,而直接使用获取我们根据窗口大小计算出来的行列数
|
||||
term.onResize(() => {
|
||||
_this.onSend({ Op: "resize", Cols: term.cols, Rows: term.rows });
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("e", e.message);
|
||||
}
|
||||
}
|
||||
|
||||
// function runFakeTerminal(_this) {
|
||||
// if (term._initialized) {
|
||||
// return;
|
||||
// }
|
||||
// // 初始化
|
||||
// term._initialized = true;
|
||||
|
||||
// term.writeln("Welcome to use Superman. ");
|
||||
// term.writeln(
|
||||
// `This is Web Terminal of pod\x1B[1;3;31m ${
|
||||
// _this.urlParam.podName
|
||||
// }\x1B[0m in namespace\x1B[1;3;31m ${_this.urlParam.namespace}\x1B[0m`
|
||||
// );
|
||||
|
||||
// term.prompt();
|
||||
|
||||
// // / **
|
||||
// // *添加事件监听器,用于按下键时的事件。事件值包含
|
||||
// // *将在data事件以及DOM事件中发送的字符串
|
||||
// // *触发了它。
|
||||
// // * @返回一个IDisposable停止监听。
|
||||
// // * /
|
||||
// // / ** 更新:xterm 4.x(新增)
|
||||
// // *为数据事件触发时添加事件侦听器。发生这种情况
|
||||
// // *用户输入或粘贴到终端时的示例。事件值
|
||||
// // *是`string`结果的结果,在典型的设置中,应该通过
|
||||
// // *到支持pty。
|
||||
// // * @返回一个IDisposable停止监听。
|
||||
// // * /
|
||||
// // 支持输入与粘贴方法
|
||||
// term.onData(function(key) {
|
||||
// let order = {
|
||||
// Data: key,
|
||||
// Op: "stdin"
|
||||
// };
|
||||
// _this.onSend(key);
|
||||
// // 为解决窗体resize方法才会向后端发送列数和行数,所以页面加载时也要触发此方法
|
||||
// _this.onSend({
|
||||
// Op: "resize",
|
||||
// Cols: parseInt(term.cols),
|
||||
// Rows: parseInt(term.rows)
|
||||
// });
|
||||
// });
|
||||
|
||||
// _this.term = term;
|
||||
// }
|
||||
// runFakeTerminal(_this);
|
||||
function runFakeTerminal(_this) {
|
||||
if (term._initialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
term._initialized = true;
|
||||
|
||||
term.prompt = () => {
|
||||
term.write("\r\n ");
|
||||
};
|
||||
|
||||
term.writeln("Welcome to xterm.js");
|
||||
term.writeln(
|
||||
"This is a local terminal emulation, without a real terminal in the back-end."
|
||||
);
|
||||
term.writeln("Type some keys and commands to play around.");
|
||||
term.writeln("");
|
||||
term.prompt();
|
||||
|
||||
// 监控键盘输入事件
|
||||
// / **
|
||||
// *添加事件监听器,用于按下键时的事件。事件值包含
|
||||
// *将在data事件以及DOM事件中发送的字符串
|
||||
// *触发了它。
|
||||
// * @返回一个IDisposable停止监听。
|
||||
// * /
|
||||
// 添加事件监听器,支持输入方法
|
||||
term.onData( function (key) {
|
||||
if (key.charCodeAt(0) == 13) { // 回车
|
||||
if(_this.command === 'clear') {
|
||||
term.clear()
|
||||
}
|
||||
if (_this.command.trim().length === 0) {
|
||||
term.prompt()
|
||||
} else {
|
||||
// 保存命令
|
||||
let commands = localStorage.getItem('commands') ? JSON.parse(localStorage.getItem('commands')) : []
|
||||
commands.push(_this.command)
|
||||
localStorage.setItem('commands', JSON.stringify(commands))
|
||||
localStorage.setItem('index', commands.length)
|
||||
// _this.sendData(0)
|
||||
_this.onSend(localStorage.getItem('commands'))
|
||||
}
|
||||
_this.command = ''
|
||||
} else if (key === '\u001b[A') { // 向上方向
|
||||
let commands = localStorage.getItem('commands') ? JSON.parse(localStorage.getItem('commands')) : []
|
||||
// console.log(commands)
|
||||
let index = localStorage.getItem('index') ? localStorage.getItem('index') : commands.length
|
||||
index = parseInt(index)
|
||||
if (commands.length && index < commands.length + 1 && index > 0) {
|
||||
// 删除现有命令
|
||||
for (let i = 0; i < _this.command.length; i++) {
|
||||
if (term._core.buffer.x > (_this.sshPrompt.length + 2)) {
|
||||
term.write('\b \b')
|
||||
}
|
||||
}
|
||||
_this.command = commands[index - 1]
|
||||
term.write(_this.command)
|
||||
localStorage.setItem('index', index - 1)
|
||||
}
|
||||
} else if (key === '\u001b[B') { // 向下方向
|
||||
let commands = localStorage.getItem('commands') ? JSON.parse(localStorage.getItem('commands')) : []
|
||||
let index = localStorage.getItem('index') ? localStorage.getItem('index') : commands.length
|
||||
index = parseInt(index)
|
||||
if (commands.length && index < commands.length - 1 && index > -1) {
|
||||
// 删除现有命令
|
||||
for (let i = 0; i < _this.command.length; i++) {
|
||||
if (term._core.buffer.x > (_this.sshPrompt.length + 2)) {
|
||||
term.write('\b \b')
|
||||
}
|
||||
}
|
||||
_this.command = commands[index + 1]
|
||||
term.write(_this.command)
|
||||
localStorage.setItem('index', index + 1)
|
||||
}
|
||||
} else if (key.charCodeAt(0) === 9) { // tab键
|
||||
let params = {
|
||||
consoleUUID: _this.activeMsf,
|
||||
cmd: _this.command
|
||||
}
|
||||
// tab补全
|
||||
_this.$apis.readTabsComplete(params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
if (res.data.length) {
|
||||
for (let i = 0; i < _this.command.length; i++) {
|
||||
term.write('\b \b')
|
||||
}
|
||||
let data = res.data.join('\r\n')
|
||||
_this.command = res.data[res.data.length - 1]
|
||||
if (res.data.length > 1) {
|
||||
term.write('\r\n')
|
||||
term.write(data)
|
||||
term.prompt()
|
||||
term.write(res.data[res.data.length - 1])
|
||||
} else {
|
||||
term.write(_this.command)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
_this.$message.error(res.message())
|
||||
}
|
||||
})
|
||||
} else if (key.charCodeAt(0) === 127) {
|
||||
if (term._core.buffer.x > (_this.sshPrompt.length + 2)) {
|
||||
term.write('\b \b')
|
||||
_this.command = _this.command.substr(0, _this.command.length - 1)
|
||||
}
|
||||
} else{
|
||||
_this.command += key
|
||||
term.write(key)
|
||||
}
|
||||
})
|
||||
|
||||
_this.term = term;
|
||||
|
||||
// 粘贴事件
|
||||
term.onData(function(data) {
|
||||
_this.order = data;
|
||||
term.write(data);
|
||||
});
|
||||
}
|
||||
runFakeTerminal(_this);
|
||||
},
|
||||
|
||||
methods: {
|
||||
|
||||
/**
|
||||
* **wsShell 创建页面级别的websocket,加载页面数据
|
||||
* ws 接口:/xxx/xxx/xxx
|
||||
* 参数:无
|
||||
* ws参数:
|
||||
* @deployId 任务id
|
||||
* @tagString 当前节点
|
||||
* 返回:无
|
||||
* **/
|
||||
wsShell(url) {
|
||||
const _this = this;
|
||||
let tag = this.urlParam.Tag;
|
||||
let name= this.urlParam.name;
|
||||
let pod= this.urlParam.pod;
|
||||
|
||||
// let query = `?tag=${tag}&name=${name}&pod=${pod}`;
|
||||
// let url = `xxxx/xxxx${query}`;// websocket连接接口
|
||||
// let url = 'ws://172.16.0.120:30598/ws/target-19-austria-80-110-35-0-24/target-19-relay-127-54b5df76f4-8958g/relay-127'
|
||||
|
||||
this.shellWs = this.websoketLib.WS({
|
||||
url,
|
||||
isInit: true,
|
||||
openFn(e) {
|
||||
console.log('连接websocket成功===', e)
|
||||
// _this.term.resize({ rows: _this.rows, cols: 100 }); //终端窗口重新设置大小 并触发term.on("resize")
|
||||
},
|
||||
messageFn(e) {
|
||||
console.log("message", e);
|
||||
if (e) {
|
||||
_this.term.write(e.data);
|
||||
// let data = JSON.parse(e.data);
|
||||
// if (data.Data == "\n" || data.Data == "\r\nexit\r\n") {
|
||||
// _this.$message("连接已关闭");
|
||||
// }
|
||||
// 打印后端返回数据
|
||||
// _this.term.write(data.Data);
|
||||
}
|
||||
},
|
||||
errorFn(e) {
|
||||
//出现错误关闭当前ws,并且提示
|
||||
console.log("error", e);
|
||||
_this.$message.error({
|
||||
message: "ws 请求失败,请刷新重试~",
|
||||
duration: 5000
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onSend(data) {
|
||||
// data = this.websoketLib.isObject(data) ? JSON.stringify(data) : data;
|
||||
// data = this.websoketLib.isArray(data) ? data.toString() : data;
|
||||
// data = data.replace(/\\\\/, "\\");
|
||||
this.shellWs.onSend(data);
|
||||
},
|
||||
|
||||
//删除左右两端的空格
|
||||
trim(str) {
|
||||
return str.replace(/(^\s*)|(\s*$)/g, "");
|
||||
},
|
||||
// 获取websocket地址
|
||||
getWebsocketUrl() {
|
||||
const reqParams = { node_id: this.nodeId }
|
||||
this.$axios.get(this.$http.api.getWebsocketUrl, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.wsShell(res.result)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
@@ -1,95 +0,0 @@
|
||||
<template>
|
||||
<div class="role-dialog" v-if="visible">
|
||||
<i class="el-icon-close" style="float: right; padding-right: 7%;padding-top: 2.8%" @click="close"></i>
|
||||
<div class="fbs">
|
||||
<span style="margin-right: 3%">文件名</span>
|
||||
<el-input
|
||||
size="mini"
|
||||
:value="value"
|
||||
placeholder="请输入内容"
|
||||
style="width: 50%"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners">
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="submit">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RoleForm',
|
||||
props: {
|
||||
value: {
|
||||
typeof: String,
|
||||
require: true,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
// document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
},
|
||||
submit() {
|
||||
// document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
this.$emit('makeDirSubmit')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.role-dialog{
|
||||
z-index: 998;
|
||||
width: 300px;
|
||||
height: 120px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 100px; /* 向下偏移50% */
|
||||
left: 150px; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../../../img/jbpzxybqr.png');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.fbs{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 3%;
|
||||
text-align: center;
|
||||
}
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 5%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 50px;
|
||||
height: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,538 +0,0 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<el-tree
|
||||
ref="tree"
|
||||
class="tree-section"
|
||||
lazy
|
||||
node-key="id"
|
||||
:data="treeData"
|
||||
:props="defaultProps"
|
||||
:load="loadNode"
|
||||
@node-click="handleNodeClick"
|
||||
@node-contextmenu="openTreeMenu"
|
||||
v-dragresize="dragConfig"
|
||||
>
|
||||
</el-tree>
|
||||
<div class="tree-content">
|
||||
<div style="text-align:left; width: 100%; padding: 10px;border: 1px solid #BAD0F11A;">
|
||||
<span>
|
||||
{{ dirTitle }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="clear:both;text-align:left; padding: 10px;border-bottom: 1px solid #BAD0F11A;">
|
||||
<img src="../../../../../img/icon/file-icon.svg" alt="" style="width: 18px; height: 18px;">
|
||||
{{ currentFileName }}
|
||||
</div>
|
||||
<ul style="clear:both;" class="tree-content-ul">
|
||||
<li v-for="fileItem in fileList" :key="fileItem.name" @contextmenu.prevent="openMenu(fileItem, $event)">
|
||||
<i class="el-icon-document file-info" style="font-size: 20px"></i>
|
||||
<span class="file-info">{{ fileItem.name }}</span>
|
||||
<span class="file-info">{{ fileItem.size }}</span>
|
||||
<span class="file-info">{{ fileItem.time }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="empty-box" @contextmenu.prevent="openUploadMenu($event)"></div>
|
||||
</div>
|
||||
<ul
|
||||
v-show="visible"
|
||||
:style="{left:left+'px',top:top+'px'}"
|
||||
class="contextmenu"
|
||||
>
|
||||
<li>
|
||||
<label
|
||||
class="button"
|
||||
for="file-input"
|
||||
>
|
||||
<span>上传</span>
|
||||
<input
|
||||
type="file"
|
||||
class="file-input"
|
||||
id="file-input"
|
||||
@change="upload"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
<li @click="download">下载</li>
|
||||
<li @click="del">删除</li>
|
||||
</ul>
|
||||
<ul
|
||||
v-show="uploadVisible"
|
||||
:style="{left:left+'px',top:top+'px'}"
|
||||
class="contextmenu"
|
||||
>
|
||||
<li>
|
||||
<label
|
||||
class="button"
|
||||
for="file-input"
|
||||
>
|
||||
<span>上传</span>
|
||||
<input
|
||||
type="file"
|
||||
class="file-input"
|
||||
id="file-input"
|
||||
@change="upload"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
</ul>
|
||||
<div v-show="showTreeMenu" class="treeMenu">
|
||||
<div @click="makeDir">新建文件夹</div>
|
||||
<div @click="delDir">删除文件夹</div>
|
||||
</div>
|
||||
<DirNameForm
|
||||
ref="dirNameForm"
|
||||
v-model="dirName"
|
||||
@makeDirSubmit="makeDirSubmit">
|
||||
</DirNameForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DirNameForm from './DirNameForm.vue'
|
||||
export default {
|
||||
components: { DirNameForm },
|
||||
props: ['isFullscreen'],
|
||||
data() {
|
||||
return{
|
||||
visible: false,
|
||||
uploadVisible: false,
|
||||
top: 0,
|
||||
left: 0,
|
||||
nodeId: '',
|
||||
dirTitle: '>',
|
||||
currentFileName: '',
|
||||
fileList: [],
|
||||
selectFile: {},
|
||||
selectNode: {path: '/'},
|
||||
rightClickNode: {},
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
},
|
||||
showTreeMenu: false, // 文件夹数据
|
||||
contextNode: {}, // 文件夹数据
|
||||
dirName: '', // 新建文件名
|
||||
treeData: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dragConfig() {
|
||||
return [
|
||||
{
|
||||
dragBorder: "right",
|
||||
setCssProperty: "width",
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.nodeId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.nodeId = newVal
|
||||
// this.getCurrentDirFile('/')
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
visible(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.closeMenu)
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.closeMenu)
|
||||
}
|
||||
},
|
||||
uploadVisible(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.closeUploadMenu)
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.closeUploadMenu)
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
upload(e) {
|
||||
const params = {
|
||||
node_id : this.nodeId,
|
||||
current_dir: this.selectNode.path,
|
||||
file: e.target.files[0]
|
||||
}
|
||||
const submitForm = new FormData()
|
||||
for(const key in params) {
|
||||
submitForm.append(key, params[key])
|
||||
}
|
||||
this.$axios.postFormData(this.$http.api.uploadFile, submitForm).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '上传文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// this.$refs.tree.remove(item.id);
|
||||
// this.setCurrentHighlight(null);
|
||||
// console.log('选中了节点id===', this.selectNode.$treeNodeId)
|
||||
// 更新当前路径下的文件信息
|
||||
this.handleNodeClick(this.selectNode)
|
||||
this.setCurrentHighlight(this.selectNode.$treeNodeId);
|
||||
// this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
download() {
|
||||
// this.downLoading = true
|
||||
const reqParams = {
|
||||
node_id : this.nodeId,
|
||||
current_dir: this.selectNode.path,
|
||||
filename: this.selectFile.name
|
||||
}
|
||||
this.$axios.getFile(this.$http.api.downloadFile, reqParams).then(res => {
|
||||
if (res.status == 200 || res.statusText == "OK") {
|
||||
const { data, headers } = res
|
||||
const fileName = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1')
|
||||
// 此处当返回json文件时需要先对data进行JSON.stringify处理,其他类型文件不用做处理
|
||||
//const blob = new Blob([JSON.stringify(data)], ...)
|
||||
const blob = new Blob([data], {type: headers['content-type']})
|
||||
let dom = document.createElement('a')
|
||||
let url = window.URL.createObjectURL(blob)
|
||||
dom.href = url
|
||||
dom.download = decodeURI(fileName)
|
||||
dom.style.display = 'none'
|
||||
document.body.appendChild(dom)
|
||||
dom.click()
|
||||
dom.parentNode.removeChild(dom)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$notify({
|
||||
title: '下载文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: '下载文件失败',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
// this.downLoading = false
|
||||
})
|
||||
},
|
||||
del() {
|
||||
const params = {
|
||||
node_id : this.nodeId,
|
||||
current_dir: this.selectNode.path,
|
||||
filename: this.selectFile.name
|
||||
}
|
||||
this.$axios.delete(this.$http.api.delFile, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '删除文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// 更新当前路径下的文件信息
|
||||
this.handleNodeClick(this.selectNode)
|
||||
// console.log('选中了节点id===', this.selectNode.$treeNodeId)
|
||||
this.setCurrentHighlight(this.selectNode.$treeNodeId);
|
||||
// this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 设置当前选中高亮
|
||||
setCurrentHighlight (id) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tree.setCurrentKey(id)
|
||||
})
|
||||
},
|
||||
openMenu(fileItem, e) {
|
||||
const menuMinWidth = 105
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
||||
const offsetWidth = this.$el.offsetWidth // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||
const left = e.clientX - offsetLeft + 60 // 15: margin right
|
||||
|
||||
if (left > maxLeft) {
|
||||
this.left = maxLeft
|
||||
} else {
|
||||
this.left = left
|
||||
}
|
||||
|
||||
if (this.isFullscreen) {
|
||||
this.top = e.clientY - 180 // fix 位置bug
|
||||
} else {
|
||||
this.top = e.clientY - 600 // fix 位置bug
|
||||
}
|
||||
this.uploadVisible = false
|
||||
this.visible = true
|
||||
this.selectFile = fileItem
|
||||
},
|
||||
closeMenu() {
|
||||
this.visible = false
|
||||
},
|
||||
openUploadMenu(e) {
|
||||
const menuMinWidth = 105
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
||||
const offsetWidth = this.$el.offsetWidth // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||
const left = e.clientX - offsetLeft + 60 // 15: margin right
|
||||
|
||||
if (left > maxLeft) {
|
||||
this.left = maxLeft
|
||||
} else {
|
||||
this.left = left
|
||||
}
|
||||
|
||||
if (this.isFullscreen) {
|
||||
this.top = e.clientY - 120 // fix 位置bug
|
||||
} else {
|
||||
this.top = e.clientY - 600 // fix 位置bug
|
||||
}
|
||||
this.visible = false
|
||||
this.uploadVisible = true
|
||||
},
|
||||
closeUploadMenu() {
|
||||
this.uploadVisible = false
|
||||
},
|
||||
// 获取当前路径下文件
|
||||
getCurrentDirFile(currentDir) {
|
||||
const reqParams = { node_id: this.nodeId, current_dir: currentDir }
|
||||
return this.$axios.get(this.$http.api.getCurrentDir, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
const dirs = res?.result?.object?.dir || []
|
||||
this.fileList = res?.result?.object?.file || []
|
||||
// console.log('请求回来files===', files)
|
||||
const nodeData = dirs.map(dir => {
|
||||
return {
|
||||
name: dir.name,
|
||||
path: currentDir === '/' ? currentDir + dir.name : `${currentDir}/${dir.name}`,
|
||||
fileList: []
|
||||
}
|
||||
})
|
||||
// console.log('请求回来nodeData====', nodeData)
|
||||
return nodeData
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 点击加载子节点
|
||||
async loadNode(node, reslove) {
|
||||
// console.log('执行了loadNode', node)
|
||||
if (node.level == 0) {
|
||||
let nodeData = await this.getCurrentDirFile('/')
|
||||
// console.log('初始化数据===', nodeData)
|
||||
// this.selectNode = nodeData.fileList
|
||||
return reslove(nodeData)
|
||||
} else {
|
||||
let nodeData = await this.getCurrentDirFile(node.data.path)
|
||||
// 判断是否为最底层节点 根据自己接口返回的数据判断即可
|
||||
nodeData.forEach(item => {
|
||||
item.hasChildren == false ? item.lastNode = ture : null
|
||||
});
|
||||
// this.selectNode = nodeData.fileList
|
||||
return reslove(nodeData)
|
||||
}
|
||||
},
|
||||
handleNodeClick(node) {
|
||||
// this.fileList = []
|
||||
// console.log('选中了节点===', node)
|
||||
// console.log('选中了节点id===', node.$treeNodeId)
|
||||
this.selectNode = node
|
||||
this.dirTitle = node.path.split('/').join('>')
|
||||
this.currentFileName = node.name
|
||||
// this.fileList = node.fileList
|
||||
const reqParams = { node_id: this.nodeId, current_dir: node.path }
|
||||
this.$axios.get(this.$http.api.getCurrentDir, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.fileList = res?.result?.object?.file || []
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
openTreeMenu(event, data, node, target) {
|
||||
// console.log(event, data, node, target)
|
||||
this.showTreeMenu = true // 显示菜单
|
||||
this.contextNode = data // 存储数据
|
||||
// console.log(this.contextNode, 'contextNode')
|
||||
// console.log(node, 'rightClickNode=======')
|
||||
// console.log(target, 'target')
|
||||
this.rightClickNode = node
|
||||
document.querySelector('.treeMenu').setAttribute('style',`top:${event.clientY + 30}px;left:${event.clientX + 30}px;`)
|
||||
document.addEventListener('click', this.closeTreeMenu)
|
||||
document.addEventListener('contextmenu', this.closeTreeMenu)
|
||||
},
|
||||
closeTreeMenu() {
|
||||
this.showTreeMenu = false // 关闭菜单
|
||||
document.removeEventListener('click', this.closeTreeMenu)
|
||||
document.removeEventListener('contextmenu', this.closeTreeMenu)
|
||||
},
|
||||
// 当新建文件夹时,先填写名字
|
||||
openDirNameForm() {
|
||||
this.$refs.dirNameForm.visible = true
|
||||
},
|
||||
// 新建文件夹
|
||||
makeDir() {
|
||||
this.openDirNameForm()
|
||||
},
|
||||
// 新建文件夹接口提交
|
||||
makeDirSubmit() {
|
||||
const reqParams = { node_id: this.nodeId, current_dir: this.contextNode.path, new_dir_name: this.dirName }
|
||||
this.$axios.get(this.$http.api.makeDir, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '新建文件夹成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// 页面增加子节点
|
||||
let id = Math.ceil(Math.random() * 1000000)
|
||||
var addNode = {
|
||||
path: this.contextNode.path + '/' + this.dirName,
|
||||
name: this.dirName,
|
||||
fileList: []
|
||||
// id: id,
|
||||
// name: this.dirName,
|
||||
// data: {
|
||||
// path: this.contextNode.path,
|
||||
// name: this.dirName,
|
||||
// fileList: []
|
||||
// }
|
||||
}
|
||||
// console.log(addNode, 'addNode======')
|
||||
this.$refs.tree.append(addNode, this.rightClickNode)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 删除文件夹
|
||||
delDir() {
|
||||
const index = this.contextNode.path.lastIndexOf('/')
|
||||
const path = this.contextNode.path.substring(0, index);
|
||||
const params = {
|
||||
node_id : this.nodeId,
|
||||
current_dir: path,
|
||||
dir_name: this.contextNode.name
|
||||
}
|
||||
this.$axios.delete(this.$http.api.delDir, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '删除文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// 页面删除子节点
|
||||
this.$refs.tree.remove(this.rightClickNode)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
color: #ffffff;
|
||||
.tree-section {
|
||||
width: 25%;
|
||||
height: 100%;
|
||||
// background-color: #1A2648;
|
||||
background: transparent;
|
||||
border: 1px solid #BAD0F11A;
|
||||
color: #06F7FF;
|
||||
overflow-y: auto;
|
||||
::v-deep .el-tree-node__content:hover {
|
||||
background-color: rgba(14, 61, 138, 0.3) !important;
|
||||
}
|
||||
::v-deep .el-tree-node.is-current > .el-tree-node__content {
|
||||
background-color: rgba(14, 61, 138, 0.3) !important;
|
||||
}
|
||||
}
|
||||
.tree-content {
|
||||
width: 75%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.tree-content-ul {
|
||||
width: 100%;
|
||||
background-color: rgba(25, 33, 61, 0.4);
|
||||
overflow-y: auto;
|
||||
li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
border-bottom: 1px solid #BAD0F11A;
|
||||
padding: 5px;
|
||||
.file-info {
|
||||
flex: 1 1 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.empty-box {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.contextmenu {
|
||||
margin: 0;
|
||||
border-radius: 6px;
|
||||
border: 0.5px solid rgba(6, 247, 255, 0.20);
|
||||
background: #0E3D8A;
|
||||
z-index: 3000;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #06F7FF;
|
||||
// box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.10), 0px 8px 10px 1px rgba(0, 0, 0, 0.06), 0px 3px 14px 2px rgba(0, 0, 0, 0.05);
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 30px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
border-radius: 3px;
|
||||
background: rgba(6, 247, 255, 0.10);
|
||||
}
|
||||
}
|
||||
.file-input {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.treeMenu {
|
||||
position: fixed;
|
||||
z-index: 99999;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
color: #06F7FF;
|
||||
background-color: #0E3D8A;
|
||||
overflow: hidden;
|
||||
border-radius: 5px;
|
||||
border: 0.5px solid rgba(6, 247, 255, 0.20);
|
||||
box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.10), 0px 8px 10px 1px rgba(0, 0, 0, 0.06), 0px 3px 14px 2px rgba(0, 0, 0, 0.05);
|
||||
div{
|
||||
padding: 7px 30px;
|
||||
box-sizing: border-box;
|
||||
//width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
div:hover{
|
||||
border-radius: 3px;
|
||||
background: rgba(6, 247, 255, 0.10);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,107 +0,0 @@
|
||||
<template>
|
||||
<div class="tabs">
|
||||
<i class="el-icon-back back-btn" @click="backNodeList"></i>
|
||||
<el-tabs v-model="activeName" @tab-click="handleClick">
|
||||
<el-tab-pane label="详细信息" name="second">
|
||||
<DetailInfo></DetailInfo>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="资源监控" name="third">
|
||||
<ResourceMonitor v-if="activeName==='third'"></ResourceMonitor>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DetailInfo from './module/DetailInfo'
|
||||
import ResourceMonitor from './module/ResourceMonitor'
|
||||
export default {
|
||||
components: { DetailInfo, ResourceMonitor },
|
||||
data() {
|
||||
return {
|
||||
activeName: 'second'
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
handleClick(tab, event) {
|
||||
},
|
||||
backNodeList() {
|
||||
this.$router.push({ name: 'rangeNodeManage' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.tabs {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
position: relative;
|
||||
padding: 8px 0 0;
|
||||
display: flex;
|
||||
.detail-title {
|
||||
position:absolute;
|
||||
margin: 10px;
|
||||
}
|
||||
.back-btn {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
font-size: 30px;
|
||||
color: #02DDEA;
|
||||
margin: 5px;
|
||||
z-index: 10;
|
||||
}
|
||||
}
|
||||
::v-deep .el-tabs{
|
||||
color: #000;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.el-tabs__header {
|
||||
margin-left: 100px;
|
||||
}
|
||||
.el-tabs__content{
|
||||
height: calc(100% - 48px);
|
||||
// height: 100%;
|
||||
.el-tab-pane{
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
/* 去除灰色横条 */
|
||||
::v-deep .el-tabs__nav-wrap::after {
|
||||
position: static !important;
|
||||
}
|
||||
/* 设置滑块颜色 */
|
||||
::v-deep .el-tabs__active-bar{
|
||||
background-color: #0E3D8A !important;
|
||||
}
|
||||
/* 设置滑块停止位置 */
|
||||
::v-deep .el-tabs__active-bar.is-top{
|
||||
height: 37px;
|
||||
width: 104px ! important;
|
||||
border-radius: 17px;
|
||||
top: 0px !important;
|
||||
left: -18px !important;
|
||||
position: absolute !important;
|
||||
z-index: 1;
|
||||
}
|
||||
/* 设置当前选中样式 */
|
||||
::v-deep .el-tabs__item.is-active{
|
||||
color:#02DDEA !important;
|
||||
z-index: 2;
|
||||
}
|
||||
/* 设置未被选中样式 */
|
||||
::v-deep .el-tabs__item{
|
||||
padding: 0 20px !important;
|
||||
width: 104px;
|
||||
box-sizing: border-box;
|
||||
display: inline-block;
|
||||
position: relative !important;
|
||||
color:#02DDEA !important;
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
||||
@@ -1,82 +0,0 @@
|
||||
<template>
|
||||
<div class="detail-info" v-loading="loading">
|
||||
<el-descriptions :column="2">
|
||||
<el-descriptions-item label="Tor版本">{{nodeDetail.owner_setting && nodeDetail.owner_setting.tor_version}}</el-descriptions-item>
|
||||
<el-descriptions-item label="状态">{{nodeDetail.status}}</el-descriptions-item>
|
||||
<el-descriptions-item label="角色">{{nodeDetail.owner_setting && nodeDetail.owner_setting.role}}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建开始时间">{{formatTime(nodeDetail.create_time)}}</el-descriptions-item>
|
||||
<el-descriptions-item label="宽带">{{nodeDetail.owner_setting && nodeDetail.owner_setting.bandwidth + ' M'}}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建完成时间">{{formatTime(nodeDetail.complete_time)}}</el-descriptions-item>
|
||||
<el-descriptions-item label="内存">{{nodeDetail.owner_setting && nodeDetail.owner_setting.memory + ' Mi'}}</el-descriptions-item>
|
||||
<el-descriptions-item label="ip">{{nodeDetail.ip}}</el-descriptions-item>
|
||||
<el-descriptions-item label="sockport">{{nodeDetail.owner_setting && nodeDetail.owner_setting.socks_port}}</el-descriptions-item>
|
||||
<el-descriptions-item label="pod名">{{nodeDetail.pod_name}}</el-descriptions-item>
|
||||
<el-descriptions-item label="网络">{{nodeDetail.owner_setting && nodeDetail.owner_setting.use_network.cidr}}</el-descriptions-item>
|
||||
<el-descriptions-item label="节点昵称">{{nodeDetail.nick_name}}</el-descriptions-item>
|
||||
<el-descriptions-item v-if="nodeDetail.owner_setting && nodeDetail.owner_setting.role==='onion'" label="onion">{{nodeDetail.onion}}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { formatTime } from '../../../../../utils'
|
||||
export default {
|
||||
name: 'DetailInfo',
|
||||
data() {
|
||||
return {
|
||||
loading: false,
|
||||
nodeId: '',
|
||||
nodeDetail: {}
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.nodeId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.nodeId = newVal
|
||||
this.init()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
formatTime,
|
||||
init() {
|
||||
// this.nodeDetail = getTargetsResponse?.result
|
||||
this.loading = true
|
||||
const reqParams = { node_id: this.nodeId }
|
||||
this.$axios.get(this.$http.api.getNodeDetail, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.nodeDetail = res?.result
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.detail-info {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(25, 33, 61, 0.4);
|
||||
border: 1px solid rgba(186, 208, 241, 0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.el-descriptions{
|
||||
::v-deep .el-descriptions__body {
|
||||
background-color: transparent;
|
||||
.el-descriptions__table .el-descriptions-item__cell {
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 20px;
|
||||
padding-top: 20px;
|
||||
padding-left: 25px;
|
||||
color: rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,199 +0,0 @@
|
||||
<template>
|
||||
<div class="dresource-monitor">
|
||||
<div class="cpu">
|
||||
<div style="width:100%;height: 100%;" id="cpu"></div>
|
||||
</div>
|
||||
<div class="memory">
|
||||
<div style="width:100%;height: 100%;" id="memory"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts';
|
||||
import dayjs from 'dayjs'
|
||||
import { getCpuResponse } from './cpuMock.js'
|
||||
import { getMemoryResponse } from './memoryMock.js'
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
nodeId: '',
|
||||
cpuData: [],
|
||||
cpuDataX: [],
|
||||
cpuDataY: [],
|
||||
memoryData: [],
|
||||
memoryDataX: [],
|
||||
memoryDataY: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.nodeId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.nodeId = newVal
|
||||
// this.init()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
mounted() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
// this.cpuData = getCpuResponse.result
|
||||
// this.cpuData.forEach(item => {
|
||||
// this.cpuDataX.push(item[0])
|
||||
// this.cpuDataY.push(item[1])
|
||||
// })
|
||||
// this.memoryData = getMemoryResponse.result
|
||||
// this.memoryData.forEach(item => {
|
||||
// this.memoryDataX.push(item[0])
|
||||
// this.memoryDataY.push(item[1])
|
||||
// })
|
||||
// this.setCpuEcharts()
|
||||
// this.setMemoryEcharts()
|
||||
|
||||
const reqParams = {
|
||||
node_id: this.nodeId,
|
||||
}
|
||||
this.$axios.get(this.$http.api.getCpu, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.cpuData = res?.result
|
||||
this.cpuData.forEach(item => {
|
||||
this.cpuDataX.push(dayjs(item[0]).format('HH:mm:ss'))
|
||||
this.cpuDataY.push(item[1])
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.setCpuEcharts()
|
||||
})
|
||||
this.$axios.get(this.$http.api.getMemory, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.memoryData = res?.result
|
||||
this.memoryData.forEach(item => {
|
||||
this.memoryDataX.push(dayjs(item[0]).format('HH:mm:ss'))
|
||||
this.memoryDataY.push(item[1])
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.setMemoryEcharts()
|
||||
})
|
||||
},
|
||||
setCpuEcharts() {
|
||||
var chartDom = document.getElementById('cpu');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
// color: ['rgba(61, 255, 220, 0.2)'],
|
||||
grid: {
|
||||
// top: '8%',
|
||||
height: '55%' // 设置折线图的高度为容器高度的百分之四十
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: this.cpuDataX,
|
||||
axisLabel: {
|
||||
color: '#ffffff'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: 'cpu (mcore)',
|
||||
nameTextStyle: {
|
||||
color: '#ffffff'
|
||||
},
|
||||
axisLine: {
|
||||
show: true // 将此处改为 true 则显示 Y 轴刻度线
|
||||
// symbol: ['none', 'arrow']
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#ffffff'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: this.cpuDataY,
|
||||
type: 'line',
|
||||
smooth: true
|
||||
}
|
||||
]
|
||||
}
|
||||
option && myChart.setOption(option)
|
||||
},
|
||||
|
||||
setMemoryEcharts() {
|
||||
var chartDom = document.getElementById('memory');
|
||||
var myChart = echarts.init(chartDom);
|
||||
var option;
|
||||
|
||||
option = {
|
||||
grid: {
|
||||
top: '18%',
|
||||
height: '55%' // 设置折线图的高度为容器高度的百分之四十
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: this.memoryDataX,
|
||||
axisLabel: {
|
||||
color: '#ffffff'
|
||||
}
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
name: '内存 (Mi)',
|
||||
nameTextStyle: {
|
||||
color: '#ffffff'
|
||||
},
|
||||
axisLine: {
|
||||
show: true // 将此处改为 true 则显示 Y 轴刻度线
|
||||
// symbol: ['none', 'arrow']
|
||||
},
|
||||
splitLine: {
|
||||
show: false
|
||||
},
|
||||
axisLabel: {
|
||||
color: '#ffffff'
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
data: this.memoryDataY,
|
||||
type: 'line',
|
||||
smooth: true
|
||||
}
|
||||
]
|
||||
}
|
||||
option && myChart.setOption(option)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.dresource-monitor {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: rgba(25, 33, 61, 0.4);
|
||||
border: 1px solid rgba(186, 208, 241, 0.1);
|
||||
border-radius: 8px;
|
||||
}
|
||||
.cpu {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
}
|
||||
.memory {
|
||||
width: 100%;
|
||||
height: 50%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,91 +0,0 @@
|
||||
const getCpuResponse = {
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"result": [
|
||||
[
|
||||
"2024-01-06 09:33:14",
|
||||
1.757
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:34:14",
|
||||
1.694
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:35:14",
|
||||
1.573
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:36:14",
|
||||
0.877
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:37:14",
|
||||
0.774
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:38:14",
|
||||
0.855
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:39:14",
|
||||
0.903
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:40:14",
|
||||
0.929
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:41:14",
|
||||
1.135
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:42:14",
|
||||
1.154
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:43:14",
|
||||
0.963
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:44:14",
|
||||
1.195
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:45:14",
|
||||
1.128
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:46:14",
|
||||
1.294
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:47:14",
|
||||
1.698
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:48:14",
|
||||
1.136
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:49:14",
|
||||
1.554
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:50:14",
|
||||
1.721
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:51:14",
|
||||
1.941
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:52:14",
|
||||
1.594
|
||||
],
|
||||
[
|
||||
"2024-01-06 09:53:14",
|
||||
1.955
|
||||
]
|
||||
]
|
||||
}
|
||||
export { getCpuResponse }
|
||||
@@ -1,91 +0,0 @@
|
||||
const getMemoryResponse = {
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"result": [
|
||||
[
|
||||
"2024-01-07 01:08:27",
|
||||
42.371
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:09:27",
|
||||
42.371
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:10:27",
|
||||
42.371
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:11:27",
|
||||
42.371
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:12:27",
|
||||
42.371
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:13:27",
|
||||
42.371
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:14:27",
|
||||
42.371
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:15:27",
|
||||
42.383
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:16:27",
|
||||
42.383
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:17:27",
|
||||
42.383
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:18:27",
|
||||
42.383
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:19:27",
|
||||
42.383
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:20:27",
|
||||
42.383
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:21:27",
|
||||
42.383
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:22:27",
|
||||
42.383
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:23:27",
|
||||
42.383
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:24:27",
|
||||
42.383
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:25:27",
|
||||
42.395
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:26:27",
|
||||
42.402
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:27:27",
|
||||
42.41
|
||||
],
|
||||
[
|
||||
"2024-01-07 01:28:27",
|
||||
42.422
|
||||
]
|
||||
]
|
||||
}
|
||||
export { getMemoryResponse }
|
||||
@@ -1,304 +0,0 @@
|
||||
<template>
|
||||
<div class="range-config-manage" ref="appRef">
|
||||
<Header
|
||||
:down-loading="downLoading"
|
||||
:start-loading="startLoading"
|
||||
:stop-loading="stopLoading"
|
||||
@download="download"
|
||||
@start="start"
|
||||
@end="end"
|
||||
></Header>
|
||||
<div class="list" >
|
||||
<el-table
|
||||
class="custom-table"
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
height="100%"
|
||||
style="width: 100%;"
|
||||
:data="tableData"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
type="selection"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="bucket_name"
|
||||
label="桶名称"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="file_name"
|
||||
label="文件名"
|
||||
min-width="250"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="size"
|
||||
label="文件大小"
|
||||
min-width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="last_modified"
|
||||
label="更新时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.last_modified | formatTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<!-- <el-pagination
|
||||
background
|
||||
:current-page="page"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="10"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
</el-pagination> -->
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Header from './module/Header.vue'
|
||||
import { getTargetsResponse } from './mock.js'
|
||||
export default {
|
||||
name: "File",
|
||||
components:{ Header},
|
||||
data(){
|
||||
return{
|
||||
target_id: '',
|
||||
nodeId: '',
|
||||
tableData: [],
|
||||
selectFileNameList: [],
|
||||
selectTableData: [],
|
||||
loading: false,
|
||||
downLoading: false,
|
||||
startLoading: false,
|
||||
stopLoading: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.nodeId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.nodeId = newVal
|
||||
this.init()
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
'$store.state.node.startTraffic': {
|
||||
handler(newVal, oldVal) {
|
||||
this.startLoading = newVal
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
'$store.state.node.endTraffic': {
|
||||
handler(newVal, oldVal) {
|
||||
this.stopLoading = newVal
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods:{
|
||||
init() {
|
||||
// this.tableData = getTargetsResponse?.result
|
||||
const reqParams = { node_id: this.nodeId }
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.getNodeFile, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.tableData = res?.result
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 当选择其它角色时,弹窗填写角色窗
|
||||
openRoleForm() {
|
||||
this.$refs.roleForm.visible = true
|
||||
},
|
||||
// 下载
|
||||
download() {
|
||||
if (this.selectFileNameList.length <= 0) {
|
||||
this.$notify({
|
||||
title: '请勾选要下载的文件',
|
||||
type: 'warning',
|
||||
duration: 2500
|
||||
})
|
||||
return
|
||||
}
|
||||
const reqParams = {
|
||||
node_id: this.nodeId,
|
||||
object_file_name_list: JSON.stringify(this.selectFileNameList)
|
||||
}
|
||||
this.downLoading = true
|
||||
this.$axios.getFile(this.$http.api.downloadNodeFile, reqParams).then(res => {
|
||||
if (res.status == 200 || res.statusText == "OK") {
|
||||
const { data, headers } = res
|
||||
const fileName = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1')
|
||||
// 此处当返回json文件时需要先对data进行JSON.stringify处理,其他类型文件不用做处理
|
||||
//const blob = new Blob([JSON.stringify(data)], ...)
|
||||
const blob = new Blob([data], {type: headers['content-type']})
|
||||
let dom = document.createElement('a')
|
||||
let url = window.URL.createObjectURL(blob)
|
||||
dom.href = url
|
||||
dom.download = decodeURI(fileName)
|
||||
dom.style.display = 'none'
|
||||
document.body.appendChild(dom)
|
||||
dom.click()
|
||||
dom.parentNode.removeChild(dom)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$notify({
|
||||
title: '下载文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: '下载文件失败',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.downLoading = false
|
||||
})
|
||||
},
|
||||
// 开始
|
||||
start() {
|
||||
const reqParams = {
|
||||
node_id: this.nodeId,
|
||||
command: 'start'
|
||||
}
|
||||
// this.startLoading = true
|
||||
this.$store.commit('node/setStartTraffic', true)
|
||||
this.$axios.get(this.$http.api.flowStartStop, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '开始统计流量',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
this.$store.commit('node/setStartTraffic', false)
|
||||
}).finally(() => {
|
||||
// this.startLoading = false
|
||||
})
|
||||
},
|
||||
// 结束
|
||||
end() {
|
||||
const reqParams = {
|
||||
node_id: this.nodeId,
|
||||
command: 'stop'
|
||||
}
|
||||
// this.stopLoading = true
|
||||
this.$store.commit('node/setEndTraffic', true)
|
||||
this.$axios.get(this.$http.api.flowStartStop, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$store.commit('node/setStartTraffic', false)
|
||||
this.$notify({
|
||||
title: '结束统计流量',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
setTimeout(() => {
|
||||
// this.stopLoading = false
|
||||
this.$store.commit('node/setEndTraffic', false)
|
||||
this.init()
|
||||
}, 5 * 1000);
|
||||
})
|
||||
},
|
||||
// 处理选中的数据
|
||||
handleSelectionChange(selectData) {
|
||||
this.selectTableData = selectData
|
||||
this.selectFileNameList = selectData.map(item => {
|
||||
return item.file_name
|
||||
})
|
||||
},
|
||||
// // 修改每页数据条数
|
||||
// handleSizeChange(val) {
|
||||
// console.log(`每页 ${val} 条`)
|
||||
// this.page=1
|
||||
// this.size=val
|
||||
// this.query()
|
||||
// },
|
||||
// // 修改页数
|
||||
// handleCurrentChange(val) {
|
||||
// console.log(`当前页: ${val}`)
|
||||
// this.page=val
|
||||
// this.query()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.el-pagination {
|
||||
padding: 15px 55px 0 0 !important;
|
||||
}
|
||||
.custom-table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.range-config-manage{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// justify-content: flex-start;
|
||||
.list{
|
||||
width: 100%;
|
||||
height: 87%;
|
||||
background-color: rgba(25, 33, 61, 0.4);
|
||||
border: 1px solid rgba(186, 208, 241, 0.1);
|
||||
border-radius: 8px;
|
||||
// margin-left: 2.5%;
|
||||
// overflow-y: auto;
|
||||
// overflow-y: scroll;
|
||||
// overflow-x: hidden;
|
||||
// border: none;
|
||||
}
|
||||
.list::-webkit-scrollbar {
|
||||
width: 0px; /* 隐藏滚动条 */
|
||||
height: 0px;
|
||||
background-color: transparent; /* 让背景透明 */
|
||||
|
||||
}
|
||||
/* 隐藏火狐浏览器滚动条 */
|
||||
@-moz-document url-prefix() {
|
||||
.trackSource {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
}
|
||||
// 遮罩层
|
||||
.mask{
|
||||
position: fixed; /*将元素设置为固定定位*/
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0,0,0,0.5); /*通过rgba函数来控制遮罩层的透明度*/
|
||||
display: none; /*将元素隐藏*/
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,26 +0,0 @@
|
||||
const getTargetsResponse = {
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"result": [
|
||||
{
|
||||
"bucket_name": "target-1-da-15",
|
||||
"file_name": "target_1_da_15_2023_07_31_23_58_04.pcap",
|
||||
"last_modified": "2023-07-31T15:58:40.158000+00:00",
|
||||
"size": "3.80KiB"
|
||||
},
|
||||
{
|
||||
"bucket_name": "target-1-da-15",
|
||||
"file_name": "target_1_da_15_2023_08_01_00_04_07.pcap",
|
||||
"last_modified": "2023-07-31T16:07:09.377000+00:00",
|
||||
"size": "21.7KiB"
|
||||
},
|
||||
{
|
||||
"bucket_name": "target-1-da-15",
|
||||
"file_name": "target_1_da_15_2023_08_01_00_13_41.pcap",
|
||||
"last_modified": "2023-07-31T16:16:08.605000+00:00",
|
||||
"size": "20.6KiB"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export { getTargetsResponse }
|
||||
@@ -1,74 +0,0 @@
|
||||
<template>
|
||||
<div class="head">
|
||||
<span class="title">节点流量采集文件</span>
|
||||
<el-button :loading="downLoading" type="primary" @click="download">下载</el-button>
|
||||
<el-button :loading="startLoading" type="primary" @click="start">开始</el-button>
|
||||
<el-button :loading="stopLoading" type="primary" @click="end">结束</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Header',
|
||||
props: ['startLoading', 'stopLoading', 'downLoading'],
|
||||
data() {
|
||||
return {
|
||||
role: '',
|
||||
has_deployed: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 下载
|
||||
download() {
|
||||
this.$emit('download')
|
||||
},
|
||||
// 开始
|
||||
start() {
|
||||
this.$emit('start')
|
||||
},
|
||||
// 结束
|
||||
end() {
|
||||
this.$emit('end')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.head{
|
||||
width: 100%;
|
||||
height: 12%;
|
||||
margin-top: 1%;
|
||||
text-align: right;
|
||||
|
||||
/*background-color: #5daf34;*/
|
||||
.block{
|
||||
display: inline-block;
|
||||
margin-left: 2%;
|
||||
|
||||
}
|
||||
.input{
|
||||
display: inline-block;
|
||||
height: 60%;
|
||||
width: 10%;
|
||||
margin-left: 0.5%;
|
||||
.el-input::placeholder {
|
||||
width: auto;
|
||||
}
|
||||
.icon-group {
|
||||
display: flex; /* 设置容器为 Flexbox 容器 */
|
||||
align-items: center; /* 垂直居中图片 */
|
||||
gap: 5px; /* 图片和文字之间的间距,可以根据需要进行调整 */
|
||||
|
||||
}
|
||||
.icon-group img {
|
||||
transform: scale(1);
|
||||
margin-right: 15px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
margin-right: 60%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,80 +0,0 @@
|
||||
<template>
|
||||
<div class="container">
|
||||
<div class="card-detail">
|
||||
<Detail></Detail>
|
||||
</div>
|
||||
<div class="card-file">
|
||||
<File></File>
|
||||
</div>
|
||||
<div class="card">
|
||||
<Log></Log>
|
||||
</div>
|
||||
<div class="card">
|
||||
<Console></Console>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Detail from './detail/index.vue'
|
||||
import File from './file/index.vue'
|
||||
import Log from './log/index.vue'
|
||||
import Console from './console/index.vue'
|
||||
import { getTargetsResponse } from './mock.js'
|
||||
export default {
|
||||
name: "NodeDetail",
|
||||
components: { Detail, File, Log, Console},
|
||||
data(){
|
||||
return{
|
||||
nodeId: '',
|
||||
nodeDetail: {}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
},
|
||||
// watch: {
|
||||
// '$store.state.range.nodeId': {
|
||||
// handler(newVal, oldVal) {
|
||||
// this.nodeId = newVal
|
||||
// this.init()
|
||||
// },
|
||||
// immediate: true
|
||||
// }
|
||||
// },
|
||||
created() {
|
||||
},
|
||||
methods:{
|
||||
// init() {}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.container{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
align-content: space-around;
|
||||
background-image:url('../../../img/background/backgroundFourCorner.svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% auto; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.card{
|
||||
width: 48%;
|
||||
height: 46%;
|
||||
background-image:url('../../../img/backgroundFourCorner.png');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
}
|
||||
.card-detail{
|
||||
width: 48%;
|
||||
height: 46%;
|
||||
}
|
||||
.card-file{
|
||||
width: 48%;
|
||||
height: 46%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,227 +0,0 @@
|
||||
<template>
|
||||
<div :class="['range-config-manage', {'is--maximize': isFullscreen}]" ref="appRef">
|
||||
<Header
|
||||
:is-fullscreen="isFullscreen"
|
||||
:fullscreen-icon="fullscreenIcon"
|
||||
:down-loading="downLoading"
|
||||
:close-icon="closeIcon"
|
||||
@query="query"
|
||||
@download="download"
|
||||
@zoomEvent="zoomEvent"
|
||||
></Header>
|
||||
<div class="list">
|
||||
<el-table
|
||||
class="custom-table"
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
height="100%"
|
||||
style="width: 100%;"
|
||||
:data="tableData"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="id"
|
||||
label="id"
|
||||
min-width="50"/>
|
||||
<el-table-column
|
||||
align="left"
|
||||
prop="content"
|
||||
label="日志内容"
|
||||
min-width="500"
|
||||
show-overflow-tooltip/>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Header from './module/Header.vue'
|
||||
import { getTargetsResponse } from './mock.js'
|
||||
export default {
|
||||
name: "File",
|
||||
components:{ Header},
|
||||
data(){
|
||||
return{
|
||||
fullscreenIcon: 'fullscreen',
|
||||
isFullscreen: false,
|
||||
target_id: '',
|
||||
nodeId: '',
|
||||
downLoading: false,
|
||||
tableData: [],
|
||||
inverterMonTimer: null,
|
||||
loading: false,
|
||||
closeIcon: false
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.closeIcon = this.$route.params.closeIcon
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.nodeId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.nodeId = newVal
|
||||
this.init()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.inverterMonTimer = setInterval(() => {
|
||||
this.init()
|
||||
}, 5 * 1000)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.inverterMonTimer) {
|
||||
clearInterval(this.inverterMonTimer);
|
||||
this.inverterMonTimer = null;
|
||||
}
|
||||
},
|
||||
|
||||
methods:{
|
||||
zoomEvent() {
|
||||
this.isFullscreen = !this.isFullscreen
|
||||
this.fullscreenIcon = this.isFullscreen ? 'narrow' : 'fullscreen'
|
||||
},
|
||||
init(params={}) {
|
||||
// const responseData = getTargetsResponse?.result
|
||||
// this.tableData = responseData.map((item, index) => {
|
||||
// return {
|
||||
// id: index,
|
||||
// content: item
|
||||
// }
|
||||
// })
|
||||
// TODO: 暂时注释接口
|
||||
const reqParams = {
|
||||
node_id: this.nodeId,
|
||||
...params
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.getLog, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
const responseData = res?.result
|
||||
this.tableData = responseData.map((item, index) => {
|
||||
return {
|
||||
id: index,
|
||||
content: item
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
query(params) {
|
||||
// 清除日志轮询器
|
||||
if (this.inverterMonTimer) {
|
||||
clearInterval(this.inverterMonTimer);
|
||||
this.inverterMonTimer = null;
|
||||
}
|
||||
// 立即查询一次
|
||||
this.init(params)
|
||||
// 开启新的日志轮询器
|
||||
this.inverterMonTimer = setInterval(() => {
|
||||
this.init(params)
|
||||
}, 5 * 1000)
|
||||
},
|
||||
// 下载
|
||||
download() {
|
||||
this.downLoading = true
|
||||
const reqParams = { node_id: this.nodeId }
|
||||
this.$axios.getFile(this.$http.api.downloadLog, reqParams).then(res => {
|
||||
if (res.status == 200 || res.statusText == "OK") {
|
||||
const { data, headers } = res
|
||||
const fileName = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1')
|
||||
// 此处当返回json文件时需要先对data进行JSON.stringify处理,其他类型文件不用做处理
|
||||
//const blob = new Blob([JSON.stringify(data)], ...)
|
||||
const blob = new Blob([data], {type: headers['content-type']})
|
||||
let dom = document.createElement('a')
|
||||
let url = window.URL.createObjectURL(blob)
|
||||
dom.href = url
|
||||
dom.download = decodeURI(fileName)
|
||||
dom.style.display = 'none'
|
||||
document.body.appendChild(dom)
|
||||
dom.click()
|
||||
dom.parentNode.removeChild(dom)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$notify({
|
||||
title: '下载日志成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: '下载日志失败',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.downLoading = false
|
||||
})
|
||||
},
|
||||
// // 修改每页数据条数
|
||||
// handleSizeChange(val) {
|
||||
// console.log(`每页 ${val} 条`)
|
||||
// this.page=1
|
||||
// this.size=val
|
||||
// this.query()
|
||||
// },
|
||||
// // 修改页数
|
||||
// handleCurrentChange(val) {
|
||||
// console.log(`当前页: ${val}`)
|
||||
// this.page=val
|
||||
// this.query()
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.is--maximize {
|
||||
position: fixed;
|
||||
top: 9%;
|
||||
left: 10%;
|
||||
width: 90% !important;
|
||||
height: 91% !important;
|
||||
padding: 0.5em 1em;
|
||||
// background-color: #17234e;
|
||||
background: url(../../../../img/background/bgMain.svg);
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-position: center; /* 居中显示 */
|
||||
background-size: cover; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
z-index: 1000;
|
||||
}
|
||||
.el-pagination {
|
||||
padding: 15px 55px 0 0 !important;
|
||||
}
|
||||
.custom-table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border-radius: 0px;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.range-config-manage{
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.list{
|
||||
width: 100%;
|
||||
height: calc(100% - 85px);
|
||||
}
|
||||
// 遮罩层
|
||||
.mask{
|
||||
position: fixed; /*将元素设置为固定定位*/
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0,0,0,0.5); /*通过rgba函数来控制遮罩层的透明度*/
|
||||
display: none; /*将元素隐藏*/
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,12 +0,0 @@
|
||||
const getTargetsResponse ={
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"result": [
|
||||
"Jan 02 18:36:44.000 [notice] Tor 0.4.8.10 opening new log file.\n",
|
||||
"Jan 02 18:36:44.000 [info] Tor 0.4.8.10 opening new log file.\n",
|
||||
"Jan 02 18:36:44.000 [warn] Tor 0.4.8.10 opening new log file.\n",
|
||||
"Jan 02 18:36:44.000 [warn] Tor 0.4.8.10 opening new log file.\n",
|
||||
]
|
||||
}
|
||||
|
||||
export { getTargetsResponse }
|
||||
@@ -1,122 +0,0 @@
|
||||
<template>
|
||||
<div class="head">
|
||||
<span class="title">日志流展示</span>
|
||||
<div class="role-select">
|
||||
<el-select v-model="level" clearable placeholder="全部" @change="query">
|
||||
<el-option
|
||||
v-for="item in levelDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button :loading="downLoading" class="role-select" type="primary" @click="download">下载</el-button>
|
||||
<span class="icon-span" slot="label">
|
||||
<i v-if="closeIcon" class="el-icon-close icon-zoom" @click="backNodeList"></i>
|
||||
<svg-icon v-else class="icon-zoom" :icon-class="fullscreenIcon" @click="zoomEvent"></svg-icon>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Header',
|
||||
props: {
|
||||
isFullscreen: {
|
||||
typeof: Boolean
|
||||
},
|
||||
fullscreenIcon: {
|
||||
typeof: String
|
||||
},
|
||||
downLoading: {
|
||||
typeof: Boolean
|
||||
},
|
||||
closeIcon: {
|
||||
typeof:Boolean
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
level: '',
|
||||
levelDict:[
|
||||
{label: 'info', value: 'info'},
|
||||
{label: 'notice', value: 'notice'},
|
||||
{label: 'warn', value: 'warn'}
|
||||
],
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
zoomEvent() {
|
||||
this.$emit('zoomEvent')
|
||||
// this.fullscreenIcon = this.isFullscreen ? 'el-icon-full-screen' : 'el-icon-rank'
|
||||
},
|
||||
// 查询
|
||||
query() {
|
||||
let params = {}
|
||||
if (this.level) {
|
||||
params.level = this.level
|
||||
}
|
||||
this.$emit('query', params)
|
||||
},
|
||||
// 下载
|
||||
download() {
|
||||
this.$emit('download')
|
||||
},
|
||||
backNodeList() {
|
||||
this.$router.push({ name: 'rangeNodeManage' })
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.head{
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
margin-top: 20px;
|
||||
text-align: right;
|
||||
|
||||
/*background-color: #5daf34;*/
|
||||
.block{
|
||||
display: inline-block;
|
||||
margin-left: 2%;
|
||||
|
||||
}
|
||||
.input{
|
||||
display: inline-block;
|
||||
height: 60%;
|
||||
width: 10%;
|
||||
margin-left: 0.5%;
|
||||
.el-input::placeholder {
|
||||
width: auto;
|
||||
}
|
||||
.icon-group {
|
||||
display: flex; /* 设置容器为 Flexbox 容器 */
|
||||
align-items: center; /* 垂直居中图片 */
|
||||
gap: 5px; /* 图片和文字之间的间距,可以根据需要进行调整 */
|
||||
|
||||
}
|
||||
.icon-group img {
|
||||
transform: scale(1);
|
||||
margin-right: 15px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
.title {
|
||||
margin-right: 45%;
|
||||
}
|
||||
::v-deep .role-select{
|
||||
display: inline-block;
|
||||
margin-right: 15px;
|
||||
}
|
||||
.icon-span {
|
||||
padding: 12px 20px;
|
||||
.icon-zoom {
|
||||
font-size: 30px;
|
||||
color: #02DDEA;
|
||||
margin: 0 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,38 +0,0 @@
|
||||
const getTargetsResponse = {
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"result": {
|
||||
"setting_id": 70,
|
||||
"traffic_collect_task_id": null,
|
||||
"id": 90,
|
||||
"ip": "148.113.2.0",
|
||||
"pod_name": "target-11-relay-90-568f44566f-rvbl8",
|
||||
"nick_name": "relaykmuhkwkdjy",
|
||||
"status": "complete",
|
||||
"onion": null,
|
||||
"create_time": "2024-01-02T18:36:37",
|
||||
"complete_time": "2024-01-02T18:48:20",
|
||||
"role": null,
|
||||
"owner_setting": {
|
||||
"role": "relay",
|
||||
"image_id": 34,
|
||||
"replicas": "1",
|
||||
"bandwidth": 10,
|
||||
"memory": 100,
|
||||
"service": null,
|
||||
"tor_version": "4.8.9",
|
||||
"create_time": null,
|
||||
"update_time": null,
|
||||
"out_port": null,
|
||||
"socks_port": 19001,
|
||||
"control_port": 19002,
|
||||
"or_port": 7000,
|
||||
"dir_port": 9030,
|
||||
"use_network": {
|
||||
"cidr": "148.113.2.0/24"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export { getTargetsResponse }
|
||||
@@ -1,146 +0,0 @@
|
||||
<template>
|
||||
<div class="range-node-manage">
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<el-select class="range-select" v-model="target_id" placeholder="全部靶场" @change="changeRange">
|
||||
<el-option
|
||||
v-for="item in rangeDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="map-container">
|
||||
<WorldMap></WorldMap>
|
||||
<TopologyMap></TopologyMap>
|
||||
</div>
|
||||
<div class="list-container">
|
||||
<NodeList></NodeList>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import WorldMap from './worldMap'
|
||||
import TopologyMap from './topologyMap'
|
||||
import NodeList from './nodeList'
|
||||
export default {
|
||||
name: 'rangeNodeManage',
|
||||
components: {
|
||||
NodeList,
|
||||
WorldMap,
|
||||
TopologyMap
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
target_id: '',
|
||||
rangeDict: []
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.targetId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.target_id = newVal
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.getRangeDict()
|
||||
},
|
||||
methods: {
|
||||
// 改变靶场
|
||||
changeRange() {
|
||||
this.$store.commit('range/setTargetId', this.target_id)
|
||||
},
|
||||
// 获取靶场列表字典
|
||||
getRangeDict() {
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 99,
|
||||
}
|
||||
this.$axios.get(this.$http.api.getTargets, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.rangeDict = res?.result?.items.map(item => {
|
||||
return {
|
||||
label: item.target_name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.rangeDict.unshift({label: '全部靶场', value: ''})
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
.range-node-manage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
.container {
|
||||
width: 95%;
|
||||
height: 95%;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../img/backgroundFourCorner.png');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
padding: 1.5%;
|
||||
}
|
||||
}
|
||||
.header {
|
||||
height: 7%;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
.range-select {
|
||||
// position: absolute;
|
||||
// top: 12px;
|
||||
// right: 140px;
|
||||
::v-deep .el-input {
|
||||
width: 60%;
|
||||
.el-input__inner {
|
||||
background-color: transparent !important;
|
||||
border-color: transparent;
|
||||
border-radius: 0;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
/* select去除竖线 */
|
||||
.el-input__suffix::before {
|
||||
content: "";
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
}
|
||||
/*select的上下箭头图标样式*/
|
||||
.el-select__caret {
|
||||
color: rgba(2, 221, 234, 0.9);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.map-container{
|
||||
height: 43%;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
|
||||
}
|
||||
.list-container{
|
||||
height: 50%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
@@ -1,95 +0,0 @@
|
||||
<template>
|
||||
<div class="role-dialog" v-if="visible">
|
||||
<i class="el-icon-close" style="float: right; padding-right: 7%;padding-top: 2.8%" @click="close"></i>
|
||||
<div class="fbs">
|
||||
<span style="margin-right: 3%">文件名</span>
|
||||
<el-input
|
||||
size="mini"
|
||||
:value="value"
|
||||
placeholder="请输入内容"
|
||||
style="width: 50%"
|
||||
v-bind="$attrs"
|
||||
v-on="$listeners">
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="submit">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RoleForm',
|
||||
props: {
|
||||
value: {
|
||||
typeof: String,
|
||||
require: true,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible: false
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value: {
|
||||
handler(val) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
// document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
},
|
||||
submit() {
|
||||
// document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
this.$emit('makeDirSubmit')
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.role-dialog{
|
||||
z-index: 998;
|
||||
width: 300px;
|
||||
height: 120px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 100px; /* 向下偏移50% */
|
||||
left: 150px; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../../img/jbpzxybqr.png');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.fbs{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 3%;
|
||||
text-align: center;
|
||||
}
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 5%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 50px;
|
||||
height: 18px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,536 +0,0 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<el-tree
|
||||
ref="tree"
|
||||
class="tree-section"
|
||||
lazy
|
||||
node-key="id"
|
||||
:data="treeData"
|
||||
:props="defaultProps"
|
||||
:load="loadNode"
|
||||
@node-click="handleNodeClick"
|
||||
v-dragresize="dragConfig"
|
||||
>
|
||||
</el-tree>
|
||||
<div class="tree-content">
|
||||
<div style="text-align:left; width: 100%; padding: 10px;border: 1px solid #BAD0F11A;">
|
||||
<span>
|
||||
{{ dirTitle }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="clear:both;text-align:left; padding: 10px;border-bottom: 1px solid #BAD0F11A;">
|
||||
<img src="../../../../img/icon/file-icon.svg" alt="" style="width: 18px; height: 18px;">
|
||||
{{ currentFileName }}
|
||||
</div>
|
||||
<ul style="clear:both;" class="tree-content-ul">
|
||||
<li v-for="fileItem in fileList" :key="fileItem.name">
|
||||
<el-radio v-model="filename" :label="fileItem.name" class="file-info">{{ '' }}</el-radio>
|
||||
<span class="file-info">{{ fileItem.name }}</span>
|
||||
<i class="el-icon-document file-info" style="font-size: 20px"></i>
|
||||
<span class="file-info">{{ fileItem.size }}</span>
|
||||
<span class="file-info">{{ fileItem.time }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="empty-box"></div>
|
||||
</div>
|
||||
<!-- <ul
|
||||
v-show="visible"
|
||||
:style="{left:left+'px',top:top+'px'}"
|
||||
class="contextmenu"
|
||||
>
|
||||
<li>
|
||||
<label
|
||||
class="button"
|
||||
for="file-input"
|
||||
>
|
||||
<span>上传</span>
|
||||
<input
|
||||
type="file"
|
||||
class="file-input"
|
||||
id="file-input"
|
||||
@change="upload"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
<li @click="download">下载</li>
|
||||
<li @click="del">删除</li>
|
||||
</ul> -->
|
||||
<!-- <ul
|
||||
v-show="uploadVisible"
|
||||
:style="{left:left+'px',top:top+'px'}"
|
||||
class="contextmenu"
|
||||
>
|
||||
<li>
|
||||
<label
|
||||
class="button"
|
||||
for="file-input"
|
||||
>
|
||||
<span>上传</span>
|
||||
<input
|
||||
type="file"
|
||||
class="file-input"
|
||||
id="file-input"
|
||||
@change="upload"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
</ul> -->
|
||||
<!-- <div v-show="showTreeMenu" class="treeMenu">
|
||||
<div @click="makeDir">新建文件夹</div>
|
||||
<div @click="delDir">删除文件夹</div>
|
||||
</div> -->
|
||||
<DirNameForm
|
||||
ref="dirNameForm"
|
||||
v-model="dirName"
|
||||
@makeDirSubmit="makeDirSubmit">
|
||||
</DirNameForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DirNameForm from './DirNameForm.vue'
|
||||
export default {
|
||||
components: { DirNameForm },
|
||||
props: ['isFullscreen'],
|
||||
data() {
|
||||
return{
|
||||
visible: false,
|
||||
uploadVisible: false,
|
||||
top: 0,
|
||||
left: 0,
|
||||
nodeId: '',
|
||||
dirTitle: '>',
|
||||
currentFileName: '',
|
||||
fileList: [],
|
||||
selectFile: {},
|
||||
selectNode: {path: '/'},
|
||||
rightClickNode: {},
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
},
|
||||
showTreeMenu: false, // 文件夹数据
|
||||
contextNode: {}, // 文件夹数据
|
||||
dirName: '', // 新建文件名
|
||||
treeData: [],
|
||||
filename: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dragConfig() {
|
||||
return [
|
||||
{
|
||||
dragBorder: "right",
|
||||
setCssProperty: "width",
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.nodeId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.nodeId = newVal
|
||||
// this.getCurrentDirFile('/')
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
visible(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.closeMenu)
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.closeMenu)
|
||||
}
|
||||
},
|
||||
uploadVisible(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.closeUploadMenu)
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.closeUploadMenu)
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// upload(e) {
|
||||
// this.$emit('selectUploadFile', {path: this.selectNode.path, file: e.target.files[0]})
|
||||
// const params = {
|
||||
// node_id : this.nodeId,
|
||||
// current_dir: this.selectNode.path,
|
||||
// file: e.target.files[0]
|
||||
// }
|
||||
// const submitForm = new FormData()
|
||||
// for(const key in params) {
|
||||
// submitForm.append(key, params[key])
|
||||
// }
|
||||
// this.$axios.postFormData(this.$http.api.uploadFile, submitForm).then(res => {
|
||||
// if (res.code == 200 || res.code == "OK") {
|
||||
// this.$notify({
|
||||
// title: '上传文件成功',
|
||||
// type: 'success',
|
||||
// duration: 2500
|
||||
// })
|
||||
// // 更新当前路径下的文件信息
|
||||
// this.handleNodeClick(this.selectNode)
|
||||
// this.setCurrentHighlight(this.selectNode.$treeNodeId);
|
||||
// }
|
||||
// }).catch(err => {
|
||||
// console.log(err)
|
||||
// })
|
||||
// },
|
||||
// download() {
|
||||
// // this.downLoading = true
|
||||
// const reqParams = {
|
||||
// master_node_id : this.nodeId,
|
||||
// current_dir: this.selectNode.path,
|
||||
// filename: this.selectFile.name
|
||||
// }
|
||||
// this.$axios.getFile(this.$http.api.batchDownload, reqParams).then(res => {
|
||||
// if (res.status == 200 || res.statusText == "OK") {
|
||||
// const { data, headers } = res
|
||||
// const fileName = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1')
|
||||
// // 此处当返回json文件时需要先对data进行JSON.stringify处理,其他类型文件不用做处理
|
||||
// //const blob = new Blob([JSON.stringify(data)], ...)
|
||||
// const blob = new Blob([data], {type: headers['content-type']})
|
||||
// let dom = document.createElement('a')
|
||||
// let url = window.URL.createObjectURL(blob)
|
||||
// dom.href = url
|
||||
// dom.download = decodeURI(fileName)
|
||||
// dom.style.display = 'none'
|
||||
// document.body.appendChild(dom)
|
||||
// dom.click()
|
||||
// dom.parentNode.removeChild(dom)
|
||||
// window.URL.revokeObjectURL(url)
|
||||
// this.$notify({
|
||||
// title: '下载文件成功',
|
||||
// type: 'success',
|
||||
// duration: 2500
|
||||
// })
|
||||
// } else {
|
||||
// this.$notify({
|
||||
// title: '下载文件失败',
|
||||
// type: 'success',
|
||||
// duration: 2500
|
||||
// })
|
||||
// }
|
||||
// }).catch(err => {
|
||||
// console.log(err)
|
||||
// }).finally(() => {
|
||||
// // this.downLoading = false
|
||||
// })
|
||||
// },
|
||||
del() {
|
||||
const params = {
|
||||
node_id : this.nodeId,
|
||||
current_dir: this.selectNode.path,
|
||||
filename: this.selectFile.name
|
||||
}
|
||||
this.$axios.delete(this.$http.api.delFile, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '删除文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// 更新当前路径下的文件信息
|
||||
this.handleNodeClick(this.selectNode)
|
||||
// console.log('选中了节点id===', this.selectNode.$treeNodeId)
|
||||
this.setCurrentHighlight(this.selectNode.$treeNodeId);
|
||||
// this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 设置当前选中高亮
|
||||
setCurrentHighlight (id) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tree.setCurrentKey(id)
|
||||
})
|
||||
},
|
||||
openMenu(fileItem, e) {
|
||||
const menuMinWidth = 105
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
||||
const offsetWidth = this.$el.offsetWidth // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||
const left = e.clientX - offsetLeft + 60 // 15: margin right
|
||||
|
||||
if (left > maxLeft) {
|
||||
this.left = maxLeft
|
||||
} else {
|
||||
this.left = left
|
||||
}
|
||||
|
||||
if (this.isFullscreen) {
|
||||
this.top = e.clientY - 180 // fix 位置bug
|
||||
} else {
|
||||
this.top = e.clientY - 160 // fix 位置bug
|
||||
}
|
||||
this.uploadVisible = false
|
||||
this.visible = true
|
||||
this.selectFile = fileItem
|
||||
},
|
||||
closeMenu() {
|
||||
this.visible = false
|
||||
},
|
||||
openUploadMenu(e) {
|
||||
const menuMinWidth = 105
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
||||
const offsetWidth = this.$el.offsetWidth // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||
const left = e.clientX - offsetLeft + 60 // 15: margin right
|
||||
|
||||
if (left > maxLeft) {
|
||||
this.left = maxLeft
|
||||
} else {
|
||||
this.left = left
|
||||
}
|
||||
|
||||
if (this.isFullscreen) {
|
||||
this.top = e.clientY - 120 // fix 位置bug
|
||||
} else {
|
||||
this.top = e.clientY - 160 // fix 位置bug
|
||||
}
|
||||
this.visible = false
|
||||
this.uploadVisible = true
|
||||
},
|
||||
closeUploadMenu() {
|
||||
this.uploadVisible = false
|
||||
},
|
||||
// 获取当前路径下文件
|
||||
getCurrentDirFile(currentDir) {
|
||||
const reqParams = { node_id: this.nodeId, current_dir: currentDir }
|
||||
return this.$axios.get(this.$http.api.getCurrentDir, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
const dirs = res?.result?.object?.dir || []
|
||||
this.fileList = res?.result?.object?.file || []
|
||||
// console.log('请求回来files===', files)
|
||||
const nodeData = dirs.map(dir => {
|
||||
return {
|
||||
name: dir.name,
|
||||
path: currentDir === '/' ? currentDir + dir.name : `${currentDir}/${dir.name}`,
|
||||
fileList: []
|
||||
}
|
||||
})
|
||||
// console.log('请求回来nodeData====', nodeData)
|
||||
return nodeData
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 点击加载子节点
|
||||
async loadNode(node, reslove) {
|
||||
// console.log('执行了loadNode', node)
|
||||
if (node.level == 0) {
|
||||
let nodeData = await this.getCurrentDirFile('/')
|
||||
// console.log('初始化数据===', nodeData)
|
||||
// this.selectNode = nodeData.fileList
|
||||
return reslove(nodeData)
|
||||
} else {
|
||||
let nodeData = await this.getCurrentDirFile(node.data.path)
|
||||
// 判断是否为最底层节点 根据自己接口返回的数据判断即可
|
||||
nodeData.forEach(item => {
|
||||
item.hasChildren == false ? item.lastNode = ture : null
|
||||
});
|
||||
// this.selectNode = nodeData.fileList
|
||||
return reslove(nodeData)
|
||||
}
|
||||
},
|
||||
handleNodeClick(node) {
|
||||
// this.fileList = []
|
||||
// console.log('选中了节点===', node)
|
||||
// console.log('选中了节点id===', node.$treeNodeId)
|
||||
this.selectNode = node
|
||||
this.dirTitle = node.path.split('/').join('>')
|
||||
this.currentFileName = node.name
|
||||
// this.fileList = node.fileList
|
||||
const reqParams = { node_id: this.nodeId, current_dir: node.path }
|
||||
this.$axios.get(this.$http.api.getCurrentDir, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.fileList = res?.result?.object?.file || []
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
openTreeMenu(event, data, node, target) {
|
||||
// console.log(event, data, node, target)
|
||||
this.showTreeMenu = true // 显示菜单
|
||||
this.contextNode = data // 存储数据
|
||||
// console.log(this.contextNode, 'contextNode')
|
||||
// console.log(node, 'rightClickNode=======')
|
||||
// console.log(target, 'target')
|
||||
this.rightClickNode = node
|
||||
document.querySelector('.treeMenu').setAttribute('style',`top:${event.clientY + 30}px;left:${event.clientX + 30}px;`)
|
||||
document.addEventListener('click', this.closeTreeMenu)
|
||||
document.addEventListener('contextmenu', this.closeTreeMenu)
|
||||
},
|
||||
closeTreeMenu() {
|
||||
this.showTreeMenu = false // 关闭菜单
|
||||
document.removeEventListener('click', this.closeTreeMenu)
|
||||
document.removeEventListener('contextmenu', this.closeTreeMenu)
|
||||
},
|
||||
// 当新建文件夹时,先填写名字
|
||||
openDirNameForm() {
|
||||
this.$refs.dirNameForm.visible = true
|
||||
},
|
||||
// 新建文件夹
|
||||
makeDir() {
|
||||
this.openDirNameForm()
|
||||
},
|
||||
// 新建文件夹接口提交
|
||||
makeDirSubmit() {
|
||||
const reqParams = { node_id: this.nodeId, current_dir: this.contextNode.path, new_dir_name: this.dirName }
|
||||
this.$axios.get(this.$http.api.makeDir, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '新建文件夹成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// 页面增加子节点
|
||||
let id = Math.ceil(Math.random() * 1000000)
|
||||
var addNode = {
|
||||
path: this.contextNode.path + '/' + this.dirName,
|
||||
name: this.dirName,
|
||||
fileList: []
|
||||
// id: id,
|
||||
// name: this.dirName,
|
||||
// data: {
|
||||
// path: this.contextNode.path,
|
||||
// name: this.dirName,
|
||||
// fileList: []
|
||||
// }
|
||||
}
|
||||
// console.log(addNode, 'addNode======')
|
||||
this.$refs.tree.append(addNode, this.rightClickNode)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 删除文件夹
|
||||
delDir() {
|
||||
const index = this.contextNode.path.lastIndexOf('/')
|
||||
const path = this.contextNode.path.substring(0, index);
|
||||
const params = {
|
||||
node_id : this.nodeId,
|
||||
current_dir: path,
|
||||
dir_name: this.contextNode.name
|
||||
}
|
||||
this.$axios.delete(this.$http.api.delDir, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '删除文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// 页面删除子节点
|
||||
this.$refs.tree.remove(this.rightClickNode)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
color: #ffffff;
|
||||
.tree-section {
|
||||
width: 25%;
|
||||
height: 100%;
|
||||
// background-color: #1A2648;
|
||||
background: transparent;
|
||||
border: 1px solid #BAD0F11A;
|
||||
color: #06F7FF;
|
||||
overflow-y: auto;
|
||||
::v-deep .el-tree-node__content:hover {
|
||||
background-color: rgba(14, 61, 138, 0.3) !important;
|
||||
}
|
||||
::v-deep .el-tree-node.is-current > .el-tree-node__content {
|
||||
background-color: rgba(14, 61, 138, 0.3) !important;
|
||||
}
|
||||
}
|
||||
.tree-content {
|
||||
width: 75%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.tree-content-ul {
|
||||
width: 100%;
|
||||
background-color: rgba(25, 33, 61, 0.4);
|
||||
overflow-y: auto;
|
||||
li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
border-bottom: 1px solid #BAD0F11A;
|
||||
padding: 5px;
|
||||
.file-info {
|
||||
flex: 1 1 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.empty-box {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.contextmenu {
|
||||
margin: 0;
|
||||
border-radius: 6px;
|
||||
border: 0.5px solid rgba(6, 247, 255, 0.20);
|
||||
background: #0E3D8A;
|
||||
z-index: 3000;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #06F7FF;
|
||||
// box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.10), 0px 8px 10px 1px rgba(0, 0, 0, 0.06), 0px 3px 14px 2px rgba(0, 0, 0, 0.05);
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 30px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
border-radius: 3px;
|
||||
background: rgba(6, 247, 255, 0.10);
|
||||
}
|
||||
}
|
||||
.file-input {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.treeMenu {
|
||||
position: fixed;
|
||||
z-index: 99999;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
color: #06F7FF;
|
||||
background-color: #0E3D8A;
|
||||
overflow: hidden;
|
||||
border-radius: 5px;
|
||||
border: 0.5px solid rgba(6, 247, 255, 0.20);
|
||||
box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.10), 0px 8px 10px 1px rgba(0, 0, 0, 0.06), 0px 3px 14px 2px rgba(0, 0, 0, 0.05);
|
||||
div{
|
||||
padding: 7px 30px;
|
||||
box-sizing: border-box;
|
||||
//width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
div:hover{
|
||||
border-radius: 3px;
|
||||
background: rgba(6, 247, 255, 0.10);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,98 +0,0 @@
|
||||
<template>
|
||||
<el-table
|
||||
class="main-table styleTable"
|
||||
ref="mainTable"
|
||||
height="500px"
|
||||
style="width: 100%;"
|
||||
:data="nodeList"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column label="" width="80" align="center">
|
||||
<template slot-scope="scope">
|
||||
<el-radio v-model="master_node_id" :label="scope.row.id" @input="handleSelectionMain(scope.row)">{{ "" }}</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="id"
|
||||
label="id"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="nick_name"
|
||||
label="节点昵称"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="role"
|
||||
label="角色"
|
||||
min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.role"></svg-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="ip"
|
||||
label="ip"
|
||||
min-width="150"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="status"
|
||||
label="部署状态"
|
||||
min-width="100"
|
||||
>
|
||||
<!-- <template slot-scope="scope">
|
||||
{{ scope.row.has_deployed ? '已部署' : '未部署' }}
|
||||
</template> -->
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="create_time"
|
||||
label="创建开始时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.create_time | formatTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="complete_time"
|
||||
label="创建完成时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.complete_time | formatTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'MainNode',
|
||||
props: {
|
||||
nodeList: {
|
||||
typeof: Array,
|
||||
require: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
master_node_id: ''
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 选择主节点
|
||||
handleSelectionMain(row) {
|
||||
this.$emit('selectMainNode', row)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,98 +0,0 @@
|
||||
<template>
|
||||
<el-table
|
||||
class="main-table styleTable"
|
||||
ref="operateTable"
|
||||
height="500px"
|
||||
style="width: 100%;"
|
||||
:data="nodeList"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
@selection-change="handleSelectionOperateNode"
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
type="selection"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="id"
|
||||
label="id"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="nick_name"
|
||||
label="节点昵称"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="role"
|
||||
label="角色"
|
||||
min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.role"></svg-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="ip"
|
||||
label="ip"
|
||||
min-width="150"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="status"
|
||||
label="部署状态"
|
||||
min-width="100"
|
||||
>
|
||||
<!-- <template slot-scope="scope">
|
||||
{{ scope.row.has_deployed ? '已部署' : '未部署' }}
|
||||
</template> -->
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="create_time"
|
||||
label="创建开始时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.create_time | formatTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="complete_time"
|
||||
label="创建完成时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.complete_time | formatTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'OperateNode',
|
||||
props: {
|
||||
nodeList: {
|
||||
typeof: Array,
|
||||
require: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 选择被操作节点
|
||||
handleSelectionOperateNode(selectRows) {
|
||||
this.$emit('selectOperateNode', selectRows)
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
|
||||
</style>
|
||||
@@ -1,520 +0,0 @@
|
||||
<template>
|
||||
<div class="content">
|
||||
<el-tree
|
||||
ref="tree"
|
||||
class="tree-section"
|
||||
lazy
|
||||
node-key="id"
|
||||
:data="treeData"
|
||||
:props="defaultProps"
|
||||
:load="loadNode"
|
||||
@node-click="handleNodeClick"
|
||||
v-dragresize="dragConfig"
|
||||
>
|
||||
</el-tree>
|
||||
<div class="tree-content">
|
||||
<div style="text-align:left; width: 100%; padding: 10px;border: 1px solid #BAD0F11A;">
|
||||
<span>
|
||||
{{ dirTitle }}
|
||||
</span>
|
||||
</div>
|
||||
<div style="clear:both;text-align:left; padding: 10px;border-bottom: 1px solid #BAD0F11A;">
|
||||
<img src="../../../../img/icon/file-icon.svg" alt="" style="width: 18px; height: 18px;">
|
||||
{{ currentFileName }}
|
||||
</div>
|
||||
<ul style="clear:both;" class="tree-content-ul">
|
||||
<li v-for="fileItem in fileList" :key="fileItem.name">
|
||||
<i class="el-icon-document file-info" style="font-size: 20px"></i>
|
||||
<span class="file-info">{{ fileItem.name }}</span>
|
||||
<span class="file-info">{{ fileItem.size }}</span>
|
||||
<span class="file-info">{{ fileItem.time }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- <div class="empty-box" @contextmenu.prevent="openUploadMenu($event)"></div> -->
|
||||
</div>
|
||||
<ul
|
||||
v-show="visible"
|
||||
:style="{left:left+'px',top:top+'px'}"
|
||||
class="contextmenu"
|
||||
>
|
||||
<!-- <li>
|
||||
<label
|
||||
class="button"
|
||||
for="file-input"
|
||||
>
|
||||
<span>上传</span>
|
||||
<input
|
||||
type="file"
|
||||
class="file-input"
|
||||
id="file-input"
|
||||
@change="upload"
|
||||
/>
|
||||
</label>
|
||||
</li> -->
|
||||
<!-- <li @click="download">下载</li>
|
||||
<li @click="del">删除</li> -->
|
||||
</ul>
|
||||
<!-- <ul
|
||||
v-show="uploadVisible"
|
||||
:style="{left:left+'px',top:top+'px'}"
|
||||
class="contextmenu"
|
||||
>
|
||||
<li>
|
||||
<label
|
||||
class="button"
|
||||
for="file-input"
|
||||
>
|
||||
<span>上传</span>
|
||||
<input
|
||||
type="file"
|
||||
class="file-input"
|
||||
id="file-input"
|
||||
@change="upload"
|
||||
/>
|
||||
</label>
|
||||
</li>
|
||||
</ul> -->
|
||||
<!-- <div v-show="showTreeMenu" class="treeMenu">
|
||||
<div @click="makeDir">新建文件夹</div>
|
||||
<div @click="delDir">删除文件夹</div>
|
||||
</div> -->
|
||||
<DirNameForm
|
||||
ref="dirNameForm"
|
||||
v-model="dirName"
|
||||
@makeDirSubmit="makeDirSubmit">
|
||||
</DirNameForm>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DirNameForm from './DirNameForm.vue'
|
||||
export default {
|
||||
components: { DirNameForm },
|
||||
props: ['isFullscreen'],
|
||||
data() {
|
||||
return{
|
||||
visible: false,
|
||||
uploadVisible: false,
|
||||
top: 0,
|
||||
left: 0,
|
||||
nodeId: '',
|
||||
dirTitle: '>',
|
||||
currentFileName: '',
|
||||
fileList: [],
|
||||
selectFile: {},
|
||||
selectNode: {path: '/'},
|
||||
rightClickNode: {},
|
||||
defaultProps: {
|
||||
children: 'children',
|
||||
label: 'name',
|
||||
},
|
||||
showTreeMenu: false, // 文件夹数据
|
||||
contextNode: {}, // 文件夹数据
|
||||
dirName: '', // 新建文件名
|
||||
treeData: []
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dragConfig() {
|
||||
return [
|
||||
{
|
||||
dragBorder: "right",
|
||||
setCssProperty: "width",
|
||||
},
|
||||
];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.nodeId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.nodeId = newVal
|
||||
// this.getCurrentDirFile('/')
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
visible(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.closeMenu)
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.closeMenu)
|
||||
}
|
||||
},
|
||||
uploadVisible(value) {
|
||||
if (value) {
|
||||
document.body.addEventListener('click', this.closeUploadMenu)
|
||||
} else {
|
||||
document.body.removeEventListener('click', this.closeUploadMenu)
|
||||
}
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
// upload(e) {
|
||||
// this.$emit('selectUploadFile', {path: this.selectNode.path, file: e.target.files[0]})
|
||||
// const params = {
|
||||
// node_id : this.nodeId,
|
||||
// current_dir: this.selectNode.path,
|
||||
// file: e.target.files[0]
|
||||
// }
|
||||
// },
|
||||
updateCurrentPath() {
|
||||
this.handleNodeClick(this.selectNode)
|
||||
this.setCurrentHighlight(this.selectNode.$treeNodeId);
|
||||
},
|
||||
download() {
|
||||
// this.downLoading = true
|
||||
const reqParams = {
|
||||
node_id : this.nodeId,
|
||||
current_dir: this.selectNode.path,
|
||||
filename: this.selectFile.name
|
||||
}
|
||||
this.$axios.getFile(this.$http.api.downloadFile, reqParams).then(res => {
|
||||
if (res.status == 200 || res.statusText == "OK") {
|
||||
const { data, headers } = res
|
||||
const fileName = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1')
|
||||
// 此处当返回json文件时需要先对data进行JSON.stringify处理,其他类型文件不用做处理
|
||||
//const blob = new Blob([JSON.stringify(data)], ...)
|
||||
const blob = new Blob([data], {type: headers['content-type']})
|
||||
let dom = document.createElement('a')
|
||||
let url = window.URL.createObjectURL(blob)
|
||||
dom.href = url
|
||||
dom.download = decodeURI(fileName)
|
||||
dom.style.display = 'none'
|
||||
document.body.appendChild(dom)
|
||||
dom.click()
|
||||
dom.parentNode.removeChild(dom)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$notify({
|
||||
title: '下载文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: '下载文件失败',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
// this.downLoading = false
|
||||
})
|
||||
},
|
||||
del() {
|
||||
const params = {
|
||||
node_id : this.nodeId,
|
||||
current_dir: this.selectNode.path,
|
||||
filename: this.selectFile.name
|
||||
}
|
||||
this.$axios.delete(this.$http.api.delFile, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '删除文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// 更新当前路径下的文件信息
|
||||
this.handleNodeClick(this.selectNode)
|
||||
// console.log('选中了节点id===', this.selectNode.$treeNodeId)
|
||||
this.setCurrentHighlight(this.selectNode.$treeNodeId);
|
||||
// this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 设置当前选中高亮
|
||||
setCurrentHighlight (id) {
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tree.setCurrentKey(id)
|
||||
})
|
||||
},
|
||||
openMenu(fileItem, e) {
|
||||
const menuMinWidth = 105
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
||||
const offsetWidth = this.$el.offsetWidth // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||
const left = e.clientX - offsetLeft + 60 // 15: margin right
|
||||
|
||||
if (left > maxLeft) {
|
||||
this.left = maxLeft
|
||||
} else {
|
||||
this.left = left
|
||||
}
|
||||
|
||||
if (this.isFullscreen) {
|
||||
this.top = e.clientY - 180 // fix 位置bug
|
||||
} else {
|
||||
this.top = e.clientY - 160 // fix 位置bug
|
||||
}
|
||||
this.uploadVisible = false
|
||||
this.visible = true
|
||||
this.selectFile = fileItem
|
||||
},
|
||||
closeMenu() {
|
||||
this.visible = false
|
||||
},
|
||||
openUploadMenu(e) {
|
||||
const menuMinWidth = 105
|
||||
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
|
||||
const offsetWidth = this.$el.offsetWidth // container width
|
||||
const maxLeft = offsetWidth - menuMinWidth // left boundary
|
||||
const left = e.clientX - offsetLeft + 60 // 15: margin right
|
||||
|
||||
if (left > maxLeft) {
|
||||
this.left = maxLeft
|
||||
} else {
|
||||
this.left = left
|
||||
}
|
||||
|
||||
if (this.isFullscreen) {
|
||||
this.top = e.clientY - 120 // fix 位置bug
|
||||
} else {
|
||||
this.top = e.clientY - 160 // fix 位置bug
|
||||
}
|
||||
this.visible = false
|
||||
this.uploadVisible = true
|
||||
},
|
||||
closeUploadMenu() {
|
||||
this.uploadVisible = false
|
||||
},
|
||||
// 获取当前路径下文件
|
||||
getCurrentDirFile(currentDir) {
|
||||
const reqParams = { node_id: this.nodeId, current_dir: currentDir }
|
||||
return this.$axios.get(this.$http.api.getCurrentDir, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
const dirs = res?.result?.object?.dir || []
|
||||
this.fileList = res?.result?.object?.file || []
|
||||
// console.log('请求回来files===', files)
|
||||
const nodeData = dirs.map(dir => {
|
||||
return {
|
||||
name: dir.name,
|
||||
path: currentDir === '/' ? currentDir + dir.name : `${currentDir}/${dir.name}`,
|
||||
fileList: []
|
||||
}
|
||||
})
|
||||
// console.log('请求回来nodeData====', nodeData)
|
||||
return nodeData
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 点击加载子节点
|
||||
async loadNode(node, reslove) {
|
||||
// console.log('执行了loadNode', node)
|
||||
if (node.level == 0) {
|
||||
let nodeData = await this.getCurrentDirFile('/')
|
||||
// console.log('初始化数据===', nodeData)
|
||||
// this.selectNode = nodeData.fileList
|
||||
return reslove(nodeData)
|
||||
} else {
|
||||
let nodeData = await this.getCurrentDirFile(node.data.path)
|
||||
// 判断是否为最底层节点 根据自己接口返回的数据判断即可
|
||||
nodeData.forEach(item => {
|
||||
item.hasChildren == false ? item.lastNode = ture : null
|
||||
});
|
||||
// this.selectNode = nodeData.fileList
|
||||
return reslove(nodeData)
|
||||
}
|
||||
},
|
||||
handleNodeClick(node) {
|
||||
// this.fileList = []
|
||||
// console.log('选中了节点===', node)
|
||||
// console.log('选中了节点id===', node.$treeNodeId)
|
||||
this.selectNode = node
|
||||
this.dirTitle = node.path.split('/').join('>')
|
||||
this.currentFileName = node.name
|
||||
// this.fileList = node.fileList
|
||||
const reqParams = { node_id: this.nodeId, current_dir: node.path }
|
||||
this.$axios.get(this.$http.api.getCurrentDir, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.fileList = res?.result?.object?.file || []
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
openTreeMenu(event, data, node, target) {
|
||||
// console.log(event, data, node, target)
|
||||
this.showTreeMenu = true // 显示菜单
|
||||
this.contextNode = data // 存储数据
|
||||
// console.log(this.contextNode, 'contextNode')
|
||||
// console.log(node, 'rightClickNode=======')
|
||||
// console.log(target, 'target')
|
||||
this.rightClickNode = node
|
||||
document.querySelector('.treeMenu').setAttribute('style',`top:${event.clientY + 30}px;left:${event.clientX + 30}px;`)
|
||||
document.addEventListener('click', this.closeTreeMenu)
|
||||
document.addEventListener('contextmenu', this.closeTreeMenu)
|
||||
},
|
||||
closeTreeMenu() {
|
||||
this.showTreeMenu = false // 关闭菜单
|
||||
document.removeEventListener('click', this.closeTreeMenu)
|
||||
document.removeEventListener('contextmenu', this.closeTreeMenu)
|
||||
},
|
||||
// 当新建文件夹时,先填写名字
|
||||
openDirNameForm() {
|
||||
this.$refs.dirNameForm.visible = true
|
||||
},
|
||||
// 新建文件夹
|
||||
makeDir() {
|
||||
this.openDirNameForm()
|
||||
},
|
||||
// 新建文件夹接口提交
|
||||
makeDirSubmit() {
|
||||
const reqParams = { node_id: this.nodeId, current_dir: this.contextNode.path, new_dir_name: this.dirName }
|
||||
this.$axios.get(this.$http.api.makeDir, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '新建文件夹成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// 页面增加子节点
|
||||
let id = Math.ceil(Math.random() * 1000000)
|
||||
var addNode = {
|
||||
path: this.contextNode.path + '/' + this.dirName,
|
||||
name: this.dirName,
|
||||
fileList: []
|
||||
// id: id,
|
||||
// name: this.dirName,
|
||||
// data: {
|
||||
// path: this.contextNode.path,
|
||||
// name: this.dirName,
|
||||
// fileList: []
|
||||
// }
|
||||
}
|
||||
// console.log(addNode, 'addNode======')
|
||||
this.$refs.tree.append(addNode, this.rightClickNode)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 删除文件夹
|
||||
delDir() {
|
||||
const index = this.contextNode.path.lastIndexOf('/')
|
||||
const path = this.contextNode.path.substring(0, index);
|
||||
const params = {
|
||||
node_id : this.nodeId,
|
||||
current_dir: path,
|
||||
dir_name: this.contextNode.name
|
||||
}
|
||||
this.$axios.delete(this.$http.api.delDir, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '删除文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// 页面删除子节点
|
||||
this.$refs.tree.remove(this.rightClickNode)
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
color: #ffffff;
|
||||
.tree-section {
|
||||
width: 25%;
|
||||
height: 100%;
|
||||
// background-color: #1A2648;
|
||||
background: transparent;
|
||||
border: 1px solid #BAD0F11A;
|
||||
color: #06F7FF;
|
||||
overflow-y: auto;
|
||||
::v-deep .el-tree-node__content:hover {
|
||||
background-color: rgba(14, 61, 138, 0.3) !important;
|
||||
}
|
||||
::v-deep .el-tree-node.is-current > .el-tree-node__content {
|
||||
background-color: rgba(14, 61, 138, 0.3) !important;
|
||||
}
|
||||
}
|
||||
.tree-content {
|
||||
width: 75%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.tree-content-ul {
|
||||
width: 100%;
|
||||
background-color: rgba(25, 33, 61, 0.4);
|
||||
overflow-y: auto;
|
||||
li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-around;
|
||||
border-bottom: 1px solid #BAD0F11A;
|
||||
padding: 5px;
|
||||
.file-info {
|
||||
flex: 1 1 100px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.empty-box {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.contextmenu {
|
||||
margin: 0;
|
||||
border-radius: 6px;
|
||||
border: 0.5px solid rgba(6, 247, 255, 0.20);
|
||||
background: #0E3D8A;
|
||||
z-index: 3000;
|
||||
position: absolute;
|
||||
list-style-type: none;
|
||||
padding: 5px 0;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: #06F7FF;
|
||||
// box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
|
||||
box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.10), 0px 8px 10px 1px rgba(0, 0, 0, 0.06), 0px 3px 14px 2px rgba(0, 0, 0, 0.05);
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 7px 30px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
border-radius: 3px;
|
||||
background: rgba(6, 247, 255, 0.10);
|
||||
}
|
||||
}
|
||||
.file-input {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.treeMenu {
|
||||
position: fixed;
|
||||
z-index: 99999;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
color: #06F7FF;
|
||||
background-color: #0E3D8A;
|
||||
overflow: hidden;
|
||||
border-radius: 5px;
|
||||
border: 0.5px solid rgba(6, 247, 255, 0.20);
|
||||
box-shadow: 0px 5px 5px -3px rgba(0, 0, 0, 0.10), 0px 8px 10px 1px rgba(0, 0, 0, 0.06), 0px 3px 14px 2px rgba(0, 0, 0, 0.05);
|
||||
div{
|
||||
padding: 7px 30px;
|
||||
box-sizing: border-box;
|
||||
//width: 50px;
|
||||
text-align: center;
|
||||
}
|
||||
div:hover{
|
||||
border-radius: 3px;
|
||||
background: rgba(6, 247, 255, 0.10);
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,445 +0,0 @@
|
||||
<template>
|
||||
<div class="range-config-manage" ref="appRef">
|
||||
<Header
|
||||
:range-dict="rangeDict"
|
||||
@query="query"
|
||||
@batchConsole="batchConsole"
|
||||
@batchDownload="batchDownload"
|
||||
@batchUpload="batchUpload"
|
||||
@batchCollectTraffic="batchCollectTraffic"
|
||||
></Header>
|
||||
<div class="list" >
|
||||
<el-table
|
||||
class="custom-table"
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
element-loading-text="加载中..."
|
||||
height="100%"
|
||||
style="width: 100%;"
|
||||
:data="tableData"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="id"
|
||||
label="id"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="nick_name"
|
||||
label="节点昵称"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="role"
|
||||
label="角色"
|
||||
min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.role"></svg-icon>
|
||||
<span style="padding-left: 5px;">{{ roleDict[scope.row.role] || scope.row.role }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="ip"
|
||||
label="ip"
|
||||
min-width="150"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="status"
|
||||
label="部署状态"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<div class="deploy-status">
|
||||
<svg-icon :icon-class="getStatus(scope.row.status).class"></svg-icon>
|
||||
<span :class="getStatus(scope.row.status).class">{{ getStatus(scope.row.status).label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="collecting"
|
||||
label="正在采集流量"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.collecting ? '是' : '否' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="create_time"
|
||||
label="创建开始时间"
|
||||
min-width="150"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.create_time | formatTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="complete_time"
|
||||
label="创建完成时间"
|
||||
min-width="150"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.complete_time | formatTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="操作"
|
||||
min-width="200"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="medium" :disabled="scope.row.status==='pending' || !roleDict[scope.row.role]" @click="console(scope.row)">控制台</el-button>
|
||||
<el-button type="text" size="medium" :disabled="!roleDict[scope.row.role]" @click="log(scope.row)">日志</el-button>
|
||||
<el-button type="text" size="medium" :disabled="!roleDict[scope.row.role]" @click="detail(scope.row)">详情</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<el-pagination
|
||||
background
|
||||
:current-page="page"
|
||||
:page-sizes="[10, 20, 30, 40]"
|
||||
:page-size="10"
|
||||
:total="total"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
@size-change="handleSizeChange"
|
||||
@current-change="handleCurrentChange"
|
||||
>
|
||||
</el-pagination>
|
||||
<div class="mask"></div>
|
||||
<Console
|
||||
ref="batchConsole"
|
||||
:target_id="target_id"
|
||||
@refresh="init">
|
||||
</Console>
|
||||
<Download
|
||||
ref="batchDownload"
|
||||
:target_id="target_id"
|
||||
@refresh="init">
|
||||
</Download>
|
||||
<Upload
|
||||
ref="batchUpload"
|
||||
:target_id="target_id"
|
||||
@refresh="init">
|
||||
</Upload>
|
||||
<CollectTraffic
|
||||
ref="batchCollectTraffic"
|
||||
:target_id="target_id"
|
||||
@refresh="init">
|
||||
</CollectTraffic>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Header from './module/Header.vue'
|
||||
import Console from './module/Console.vue'
|
||||
import Download from './module/Download.vue'
|
||||
import Upload from './module/Upload.vue'
|
||||
import CollectTraffic from './module/CollectTraffic'
|
||||
|
||||
export default {
|
||||
name: "RangeConfigManage",
|
||||
components:{ Header, Console, Download, Upload, CollectTraffic },
|
||||
|
||||
data(){
|
||||
return{
|
||||
page: 1,
|
||||
size: 10,
|
||||
total: 0,
|
||||
target_id: '',
|
||||
tableData: [],
|
||||
selectConfigIds: [],
|
||||
rangeDict: [],
|
||||
loading: false,
|
||||
pendingTimer: null,
|
||||
roleDict:{
|
||||
da: '权威目录节点',
|
||||
client: '客户端节点',
|
||||
guard: '入口节点',
|
||||
relay: '路由节点',
|
||||
exit: '出口节点',
|
||||
onion: '洋葱服务节点',
|
||||
}
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.targetId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.target_id = newVal
|
||||
this.init()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {},
|
||||
beforeDestroy() {
|
||||
if (this.pendingTimer) {
|
||||
clearInterval(this.pendingTimer)
|
||||
this.pendingTimer = null
|
||||
}
|
||||
},
|
||||
methods:{
|
||||
init(params={}) {
|
||||
// this.tableData = getTargetsResponse?.result?.items
|
||||
// this.total = getTargetsResponse?.result?.total
|
||||
|
||||
// const checkedData = JSON.parse(JSON.stringify(this.selectTableData))
|
||||
const reqParams = {
|
||||
page: this.page,
|
||||
size: this.size,
|
||||
...params
|
||||
}
|
||||
if (this.target_id && this.target_id !== '') {
|
||||
reqParams.target_id = this.target_id
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.getNodeList, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.tableData = res?.result?.items
|
||||
this.total = res?.result?.total
|
||||
// this.$nextTick(()=>{
|
||||
// checkedData.forEach((item) => {
|
||||
// this.$refs?.multipleTable?.toggleRowSelection(this.tableData.find(val => val.id === item.id), true)
|
||||
// })
|
||||
// })
|
||||
let index = this.tableData?.findIndex(item => {
|
||||
return item.status === 'pending'
|
||||
})
|
||||
if (index !== -1) {
|
||||
if (!this.pendingTimer) {
|
||||
this.pendingTimer = setInterval(() => {
|
||||
this.init()
|
||||
}, 10 * 1000)
|
||||
}
|
||||
}
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
query(params) {
|
||||
this.init(params)
|
||||
},
|
||||
// 批量控制台
|
||||
batchConsole() {
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.batchConsole.visible = true
|
||||
},
|
||||
// 批量下载文件
|
||||
batchDownload() {
|
||||
this.$refs.batchDownload.getNodeList()
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.batchDownload.visible = true
|
||||
},
|
||||
// 批量上传
|
||||
batchUpload() {
|
||||
// this.$store.commit('range/setNodeId', '')
|
||||
this.$refs.batchUpload.getNodeList()
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.batchUpload.visible = true
|
||||
},
|
||||
// 批量运行流水线
|
||||
batchCollectTraffic() {
|
||||
this.$refs.batchCollectTraffic.getNodeList()
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.batchCollectTraffic.visible = true
|
||||
},
|
||||
// 控制台
|
||||
console(row) {
|
||||
this.$store.commit('range/setNodeId', row.id)
|
||||
this.$router.push({
|
||||
name: 'console',
|
||||
params: {
|
||||
closeIcon : true
|
||||
}
|
||||
})
|
||||
},
|
||||
// 日志
|
||||
log(row) {
|
||||
this.$store.commit('range/setNodeId', row.id)
|
||||
this.$router.push({
|
||||
name: 'log',
|
||||
params: {
|
||||
closeIcon : true
|
||||
}
|
||||
})
|
||||
},
|
||||
// 详情
|
||||
detail(row) {
|
||||
this.$store.commit('range/setNodeId', row.id)
|
||||
//保存是否正在采集流量
|
||||
this.$store.commit('node/setStartTraffic', row.collecting)
|
||||
//保存是否正在结束采集流量
|
||||
this.$store.commit('node/setEndTraffic', row.analysing)
|
||||
this.$router.push({ name: 'nodeDetail'})
|
||||
},
|
||||
getStatus(status) {
|
||||
let statusInfo = {
|
||||
label: '',
|
||||
class: ''
|
||||
}
|
||||
switch (status) {
|
||||
case 'true':
|
||||
case 'pending':
|
||||
statusInfo.label = '正在部署'
|
||||
statusInfo.class = 'deployNormal'
|
||||
break;
|
||||
case 'false':
|
||||
statusInfo.label = '部署失败'
|
||||
statusInfo.class = 'deployFail'
|
||||
break;
|
||||
case 'complete':
|
||||
statusInfo.label = '部署完成'
|
||||
statusInfo.class = 'deploySuccess'
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return statusInfo
|
||||
},
|
||||
// 获取靶场列表字典
|
||||
getRangeDict() {
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 99,
|
||||
}
|
||||
this.$axios.get(this.$http.api.getTargets, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.rangeDict = res?.result?.items.map(item => {
|
||||
return {
|
||||
label: item.target_name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// // 处理选中的数据
|
||||
// handleSelectionChange(selectData) {
|
||||
// this.selectTableData = selectData
|
||||
// this.selectConfigIds = selectData.map(item => {
|
||||
// return item.id
|
||||
// })
|
||||
// },
|
||||
// 修改每页数据条数
|
||||
handleSizeChange(val) {
|
||||
console.log(`每页 ${val} 条`)
|
||||
this.page=1
|
||||
this.size=val
|
||||
this.query()
|
||||
},
|
||||
// 修改页数
|
||||
handleCurrentChange(val) {
|
||||
console.log(`当前页: ${val}`)
|
||||
this.page=val
|
||||
this.query()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped="scoped">
|
||||
.el-pagination {
|
||||
padding: 15px 55px 0 0 !important;
|
||||
}
|
||||
.custom-table {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.deploy-status {
|
||||
display: inline-block;
|
||||
width: 103px;
|
||||
height: 28px;
|
||||
color:#FFFFFF;
|
||||
border-radius: 3px;
|
||||
padding: 5px, 12px, 5px, 12px;
|
||||
background: rgba(227, 249, 233, 0.2);
|
||||
}
|
||||
.deployFail {
|
||||
color: #E9473E;
|
||||
width: 56px;
|
||||
height: 28px;
|
||||
margin-left: 10px;
|
||||
font-family: PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
}
|
||||
.deployNormal {
|
||||
color: #02DDEA;
|
||||
width: 56px;
|
||||
height: 28px;
|
||||
margin-left: 10px;
|
||||
font-family: PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
}
|
||||
.deploySuccess {
|
||||
color: #0CCB64;
|
||||
width: 56px;
|
||||
height: 28px;
|
||||
margin-left: 10px;
|
||||
font-family: PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
.range-config-manage {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// justify-content: flex-start;
|
||||
.list{
|
||||
width: 100%;
|
||||
height: 73%;
|
||||
// margin-left: 2.5%;
|
||||
// overflow-y: auto;
|
||||
// overflow-y: scroll;
|
||||
// overflow-x: hidden;
|
||||
// border: none;
|
||||
}
|
||||
.list::-webkit-scrollbar {
|
||||
width: 0px; /* 隐藏滚动条 */
|
||||
height: 0px;
|
||||
background-color: transparent; /* 让背景透明 */
|
||||
|
||||
}
|
||||
/* 隐藏火狐浏览器滚动条 */
|
||||
@-moz-document url-prefix() {
|
||||
.trackSource {
|
||||
scrollbar-width: none;
|
||||
}
|
||||
}
|
||||
// 遮罩层
|
||||
.mask{
|
||||
position: fixed; /*将元素设置为固定定位*/
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0,0,0,0.5); /*通过rgba函数来控制遮罩层的透明度*/
|
||||
display: none; /*将元素隐藏*/
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,38 +0,0 @@
|
||||
const getTargetsResponse ={
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"result": {
|
||||
"items": [
|
||||
{
|
||||
"setting_id": 1,
|
||||
"traffic_collect_task_id": 1,
|
||||
"id": 1,
|
||||
"ip": "1.1.1.1",
|
||||
"pod_name": "pod_name1",
|
||||
"nick_name": "第一节点",
|
||||
"status": "未部署",
|
||||
"onion": "onion1",
|
||||
"create_time": "2023-12-26T10:51:15.363Z",
|
||||
"complete_time": "2023-12-26T10:51:15.363Z"
|
||||
},
|
||||
{
|
||||
"setting_id": 2,
|
||||
"traffic_collect_task_id": 2,
|
||||
"id": 2,
|
||||
"ip": "2.2.2.2",
|
||||
"pod_name": "pod_name2",
|
||||
"nick_name": "第二节点",
|
||||
"status": "已部署",
|
||||
"onion": "onion2",
|
||||
"create_time": "2023-12-26T10:51:15.363Z",
|
||||
"complete_time": "2023-12-26T10:51:15.363Z"
|
||||
}
|
||||
],
|
||||
"total": 0,
|
||||
"page": 1,
|
||||
"size": 50,
|
||||
"pages": 0
|
||||
}
|
||||
}
|
||||
|
||||
export { getTargetsResponse }
|
||||
@@ -1,207 +0,0 @@
|
||||
<template>
|
||||
<div class="rule-section">
|
||||
<div class="task-box">
|
||||
<div class="header">
|
||||
<span>规则列表</span>
|
||||
<img @click="addRule" src="../../../../../img/icon/addTrafficBtn.png" alt="" style="width: 194px; height: 40px;">
|
||||
</div>
|
||||
<div class="content">
|
||||
<el-table
|
||||
class="main-table styleTable"
|
||||
ref="multipleTable"
|
||||
height="230px"
|
||||
style="width: 100%;"
|
||||
:data="tableData"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="id"
|
||||
label="id"
|
||||
width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="rule_name"
|
||||
label="规则名称"
|
||||
min-width="130"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="rule"
|
||||
label="具体规则"
|
||||
min-width="130"
|
||||
>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="update_time"
|
||||
label="添加时间"
|
||||
min-width="130"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.update_time | formatDate }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="use_count"
|
||||
label="使用次数"
|
||||
min-width="130"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="操作"
|
||||
min-width="300"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="medium" @click="editRule(scope.row)">修改</el-button>
|
||||
<el-button type="text" size="medium" :loading="scope.row.delLoading" @click="delRule(scope.row)">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mask"></div>
|
||||
<RuleForm ref="ruleForm" :is-add="isAdd" @refresh="init"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import RuleForm from './module/RuleForm'
|
||||
export default {
|
||||
name: 'RuleList',
|
||||
components: { RuleForm },
|
||||
props: {},
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
isAdd: true,
|
||||
visible:false,
|
||||
loading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 99,
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.getRuleList, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.tableData = res?.result?.items
|
||||
this.tableData.map(item => {
|
||||
this.$set(item, 'delLoading', false)
|
||||
return item
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 添加流量采集规则
|
||||
addRule() {
|
||||
this.isAdd = true
|
||||
this.$refs.ruleForm.visible = true
|
||||
},
|
||||
// 编辑规则
|
||||
editRule(row) {
|
||||
this.isAdd = false
|
||||
this.$refs.ruleForm.rule_id = row.id
|
||||
this.$refs.ruleForm.form.rule_name = row.rule_name
|
||||
this.$refs.ruleForm.form.rule = row.rule
|
||||
this.$refs.ruleForm.visible = true
|
||||
},
|
||||
// 删除规则
|
||||
delRule(row) {
|
||||
this.$confirm('此操作将永久删除该规则, 是否继续?', '确认删除', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.del(row)
|
||||
}).catch(() => {
|
||||
this.$notify({
|
||||
title: '已取消删除',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
});
|
||||
},
|
||||
del(row) {
|
||||
const url = this.$http.api.rule + '/' + row.id
|
||||
row.delLoading = true
|
||||
this.$axios.delete(url, {}).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '删除成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
row.delLoading = false
|
||||
})
|
||||
},
|
||||
// 关闭
|
||||
close() {
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
this.resetForm()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.rule-section{
|
||||
// height: 100%;
|
||||
.styleTable {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.base-input {
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 2%;
|
||||
text-align: center;
|
||||
}
|
||||
.label-span {
|
||||
margin-right: 3%;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA !important;
|
||||
}
|
||||
.task-box{
|
||||
width: 100%;
|
||||
min-height: 45%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2%;
|
||||
.header{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
margin-bottom: 1%;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
::v-deep .el-textarea__inner {
|
||||
background-color: #1A2648;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,391 +0,0 @@
|
||||
<template>
|
||||
<div class="task-section">
|
||||
<div class="task-box">
|
||||
<div class="header">
|
||||
<span>任务列表</span>
|
||||
<img @click="addTask" src="../../../../../img/icon/batchCollectBtn.png" alt="" style="width: 194px; height: 40px;">
|
||||
</div>
|
||||
<div class="content">
|
||||
<el-table
|
||||
class="main-table styleTable"
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
height="230px"
|
||||
style="width: 100%;"
|
||||
:data="tableData"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
@expand-change="handleExpandChange"
|
||||
>
|
||||
<el-table-column type="expand">
|
||||
<template slot-scope="props">
|
||||
<el-table
|
||||
class="main-table styleTable"
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
height="100%"
|
||||
style="width: 100%; margin: 5px 20px;"
|
||||
:data="taskNodeList"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="id"
|
||||
label="id"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="nick_name"
|
||||
label="节点昵称"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="role"
|
||||
label="角色"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="ip"
|
||||
label="ip"
|
||||
min-width="150"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="status"
|
||||
label="部署状态"
|
||||
min-width="100"
|
||||
>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="create_time"
|
||||
label="创建开始时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.create_time | formatDate }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="complete_time"
|
||||
label="创建完成时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.complete_time | formatDate }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="id"
|
||||
label="id"
|
||||
width="60"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="task_name"
|
||||
label="任务名称"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="traffic_collect_rule_id"
|
||||
label="采集规则"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="collecting"
|
||||
label="任务状态"
|
||||
min-width="80"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.collecting ? (scope.row.collecting === true ? '正在采集' : '采集失败') : '采集完成' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="start_collect_time"
|
||||
label="开始时间"
|
||||
min-width="110"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.start_collect_time | formatTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="end_collect_time"
|
||||
label="结束时间"
|
||||
min-width="110"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.end_collect_time | formatTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
label="操作"
|
||||
min-width="300"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<el-button type="text" size="medium" :loading="startLoading" @click="startTask(scope.row)">开始</el-button>
|
||||
<el-button type="text" size="medium" :loading="stopLoading" @click="endTask(scope.row)">结束</el-button>
|
||||
<el-button type="text" size="medium" @click="editTask(scope.row)">修改</el-button>
|
||||
<el-button type="text" size="medium" @click="delTask(scope.row)">删除</el-button>
|
||||
<!-- <el-button type="text" size="medium" @click="viewNode(scope.row)">查看节点</el-button> -->
|
||||
<el-button type="text" size="medium" @click="downloadPack(scope.row)">下载pcap包</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mask"></div>
|
||||
<TaskForm
|
||||
ref="taskForm"
|
||||
:node-list="nodeList"
|
||||
:is-add="isAdd"
|
||||
:target_id="target_id"
|
||||
@refresh="init">
|
||||
</TaskForm>
|
||||
<DownloadPack
|
||||
ref="downloadPack"
|
||||
@refresh="init">
|
||||
</DownloadPack>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TaskForm from './module/TaskForm'
|
||||
import DownloadPack from './module/DownloadPack'
|
||||
export default {
|
||||
name: 'Console',
|
||||
components: { TaskForm, DownloadPack },
|
||||
props: {
|
||||
target_id: {
|
||||
typeof: String,
|
||||
default: 0
|
||||
},
|
||||
nodeList: {
|
||||
typeof: Array,
|
||||
require: true
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
tableData: [],
|
||||
taskNodeList: [],
|
||||
isAdd: true,
|
||||
visible:false,
|
||||
loading: false,
|
||||
startLoading: false,
|
||||
stopLoading: false
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.init()
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 99,
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.getTaskList, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
// this.total = res?.result?.total
|
||||
this.tableData = res?.result?.items
|
||||
this.tableData.map(item => {
|
||||
this.$set(item, 'delLoading', false)
|
||||
return item
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 创建批量采集流量任务
|
||||
addTask() {
|
||||
this.isAdd = true
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.taskForm.visible = true
|
||||
},
|
||||
// 编辑任务
|
||||
editTask(row) {
|
||||
this.isAdd = false
|
||||
this.$refs.taskForm.task_id = row.id ?? ''
|
||||
this.$refs.taskForm.form.task_name = row.task_name ?? ''
|
||||
this.$refs.taskForm.form.task_description = row.task_description ?? ''
|
||||
this.$refs.taskForm.form.node_list = row.node_list ?? []
|
||||
this.$refs.taskForm.form.traffic_collect_rule_id = row.traffic_collect_rule_id
|
||||
this.$refs.taskForm.form.filter_noise = row.filter_noise ?? ''
|
||||
this.$refs.taskForm.visible = true
|
||||
},
|
||||
// 开始
|
||||
startTask(row) {
|
||||
const reqParams = {
|
||||
task_id: row.id,
|
||||
command: 'start'
|
||||
}
|
||||
this.startLoading = true
|
||||
this.$axios.get(this.$http.api.taskStartStop, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '开始统计流量',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
this.startLoading = false
|
||||
}).finally(() => {
|
||||
})
|
||||
},
|
||||
// 结束
|
||||
endTask(row) {
|
||||
const reqParams = {
|
||||
task_id: row.id,
|
||||
command: 'stop'
|
||||
}
|
||||
this.stopLoading = true
|
||||
this.$axios.get(this.$http.api.taskStartStop, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.startLoading = false
|
||||
this.$notify({
|
||||
title: '结束统计流量',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
setTimeout(() => {
|
||||
this.stopLoading = false
|
||||
this.init()
|
||||
}, 5 * 1000)
|
||||
})
|
||||
},
|
||||
|
||||
// 删除任务
|
||||
delTask(row) {
|
||||
this.$confirm('此操作将永久删除该任务, 是否继续?', '确认删除', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
this.del(row)
|
||||
}).catch(() => {
|
||||
this.$notify({
|
||||
title: '已取消删除',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
});
|
||||
},
|
||||
del(row) {
|
||||
const url = this.$http.api.trafficTask + '/' + row.id
|
||||
row.delLoading = true
|
||||
this.$axios.delete(url, {delete_minio: true}).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '删除任务成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
this.init()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
row.delLoading = false
|
||||
})
|
||||
},
|
||||
// 展开行查看节点
|
||||
handleExpandChange(row, expandedRows) {
|
||||
this.getTaskNodeList(row)
|
||||
},
|
||||
// // 查看节点
|
||||
// viewNode(row) {
|
||||
// this.getTaskNodeList(row)
|
||||
// },
|
||||
// 获取节点列表
|
||||
getTaskNodeList(row) {
|
||||
const params = {
|
||||
page:1,
|
||||
size: 99,
|
||||
task_id: row.id
|
||||
}
|
||||
this.$axios.get(this.$http.api.getTrafficNodes, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.taskNodeList = res?.result?.items
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 下载pcap包
|
||||
downloadPack(row) {
|
||||
this.$refs.downloadPack.task_id = row.id
|
||||
this.$refs.downloadPack.init()
|
||||
document.querySelector('.mask').style.display = 'block'
|
||||
this.$refs.downloadPack.visible = true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.task-section{
|
||||
// height: 100%;
|
||||
.styleTable {
|
||||
background-color: transparent !important;
|
||||
}
|
||||
.base-input {
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 2%;
|
||||
text-align: center;
|
||||
}
|
||||
.label-span {
|
||||
margin-right: 3%;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA !important;
|
||||
}
|
||||
.task-box{
|
||||
width: 100%;
|
||||
min-height: 45%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2%;
|
||||
.header{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
margin-bottom: 1%;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
::v-deep .el-textarea__inner {
|
||||
background-color: #1A2648;
|
||||
}
|
||||
}
|
||||
}
|
||||
::v-deep .el-table tbody tr>td {
|
||||
// background: rgba(25, 33, 61, 0.5) !important;
|
||||
background-color: #0A162C !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,110 +0,0 @@
|
||||
<template>
|
||||
<div class="console-dialog" v-if="visible">
|
||||
<!-- 在此处指定弹窗的样式和内容 -->
|
||||
<i class="el-icon-close" @click="close"></i>
|
||||
<div class="tag">
|
||||
<el-tag class="tags" style="color:#f8fdff">批量采集流量</el-tag>
|
||||
</div>
|
||||
<TaskList style="height: 42%;" :node-list="nodeList"></TaskList>
|
||||
<RuleList style="height: 42%;"></RuleList>
|
||||
<footer class="anDiv">
|
||||
<el-button class="glBut but-color" type="primary" @click="close">确定</el-button>
|
||||
</footer>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import TaskList from './TaskList'
|
||||
import RuleList from './RuleList'
|
||||
export default {
|
||||
name: 'Console',
|
||||
components: { TaskList, RuleList },
|
||||
props: {
|
||||
target_id: {
|
||||
typeof: String,
|
||||
required: true,
|
||||
default: 0
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible:false,
|
||||
nodeList: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
getNodeList() {
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 99
|
||||
}
|
||||
if (this.target_id && this.target_id !== '') {
|
||||
reqParams.target_id = this.target_id
|
||||
}
|
||||
this.$axios.get(this.$http.api.getNodeList, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.nodeList = res?.result?.items
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {})
|
||||
},
|
||||
// 关闭
|
||||
close() {
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.$emit('refresh')
|
||||
this.visible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.console-dialog{
|
||||
z-index: 997;
|
||||
width: 1331px;
|
||||
height: 800px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../../../img/background/NodeListDialog.svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: cover; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.el-icon-close{
|
||||
float: right;
|
||||
padding-right: 8%;
|
||||
padding-top: 1.8%
|
||||
}
|
||||
.tag{
|
||||
text-align: left;
|
||||
margin-left: 2%;
|
||||
.tags{
|
||||
margin-top: 1%;
|
||||
font-size: 23px;
|
||||
border: none;
|
||||
background-color: transparent !important;
|
||||
color: #565e6e;
|
||||
}
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA !important;
|
||||
}
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,223 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="custom-dialog"
|
||||
v-if="visible"
|
||||
>
|
||||
<span class="dialog-title">pcap包下载</span>
|
||||
<!-- 在此处指定弹窗的样式和内容 -->
|
||||
<i class="el-icon-close" style="float: right; padding-right: 10%;padding-top: 2.5%" @click="close"></i>
|
||||
<div class="basic-box">
|
||||
<div class="content">
|
||||
<el-table
|
||||
class="main-table styleTable"
|
||||
ref="multipleTable"
|
||||
v-loading="loading"
|
||||
height="420px"
|
||||
style="width: 100%;"
|
||||
:data="tableData"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
type="selection"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="bucket_name"
|
||||
label="桶名称"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="file_name"
|
||||
label="文件名称"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="size"
|
||||
label="文件大小"
|
||||
min-width="100"
|
||||
>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="last_modified"
|
||||
label="生成时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.last_modified | formatTime }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut but-color" type="primary" @click="download" :loading="downLoading">下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RuleForm',
|
||||
// props: ['task_id'],
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
loading: false,
|
||||
downLoading: false,
|
||||
task_id: '',
|
||||
tableData: [],
|
||||
selectFileNameList: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 99,
|
||||
task_id: this.task_id
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.getPackList, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.tableData = res?.result?.items
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
handleSelectionChange(selectData) {
|
||||
this.selectFileNameList = selectData.map(item => {
|
||||
return {
|
||||
bucket_name: item.bucket_name,
|
||||
file_name: item.file_name
|
||||
}
|
||||
})
|
||||
},
|
||||
close() {
|
||||
this.resetForm()
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
},
|
||||
resetForm() {
|
||||
},
|
||||
// 下载
|
||||
download() {
|
||||
if (this.selectFileNameList.length <= 0) {
|
||||
this.$notify({
|
||||
title: '请勾选要下载的文件',
|
||||
type: 'warning',
|
||||
duration: 2500
|
||||
})
|
||||
return
|
||||
}
|
||||
const reqParams = {
|
||||
task_id: this.task_id,
|
||||
object_info_list: JSON.stringify(this.selectFileNameList)
|
||||
}
|
||||
this.downLoading = true
|
||||
this.$axios.getFile(this.$http.api.downloadPacp, reqParams).then(res => {
|
||||
if (res.status == 200 || res.statusText == "OK") {
|
||||
const { data, headers } = res
|
||||
const fileName = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1')
|
||||
// 此处当返回json文件时需要先对data进行JSON.stringify处理,其他类型文件不用做处理
|
||||
//const blob = new Blob([JSON.stringify(data)], ...)
|
||||
const blob = new Blob([data], {type: headers['content-type']})
|
||||
let dom = document.createElement('a')
|
||||
let url = window.URL.createObjectURL(blob)
|
||||
dom.href = url
|
||||
dom.download = decodeURI(fileName)
|
||||
dom.style.display = 'none'
|
||||
document.body.appendChild(dom)
|
||||
dom.click()
|
||||
dom.parentNode.removeChild(dom)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$notify({
|
||||
title: '下载文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: '下载文件失败',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.downLoading = false
|
||||
this.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.custom-dialog{
|
||||
z-index: 998;
|
||||
width: 620px;
|
||||
height: 555px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../../../../img/background/dialog660-553.svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
// background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.dialog-title {
|
||||
font-size: 20px;
|
||||
float: left;
|
||||
margin: 11px 0 11px 20px;
|
||||
}
|
||||
.basic-box {
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
padding: 2%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.content {
|
||||
flex: 1;
|
||||
.bcmcDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 5%;
|
||||
text-align: center;
|
||||
.bcmc {
|
||||
width: 60%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-bottom: 10%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 2%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,164 +0,0 @@
|
||||
<template>
|
||||
<div
|
||||
class="rule-dialog"
|
||||
v-if="visible"
|
||||
>
|
||||
<!-- 在此处指定弹窗的样式和内容 -->
|
||||
<i class="el-icon-close" style="float: right; padding-right: 8%;padding-top: 3%" @click="close"></i>
|
||||
<el-form
|
||||
ref="ruleForm"
|
||||
:model="form"
|
||||
:rules="rules"
|
||||
label-width="150px"
|
||||
class="rule-form"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="规则名称" prop="rule_name">
|
||||
<el-input v-model="form.rule_name" placeholder="请输入规则名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="20">
|
||||
<el-form-item label="规则" prop="rule">
|
||||
<el-input type="textarea" v-model="form.rule" placeholder="请输入规则"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="submit-footer">
|
||||
<div>
|
||||
<el-button class="glBut but-color" type="primary" @click="submit" :loading="loading">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="mask"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'RuleForm',
|
||||
props: ['isAdd'],
|
||||
data() {
|
||||
return {
|
||||
visible: false,
|
||||
loading: false,
|
||||
form: {
|
||||
rule_name: '', // 规则名称
|
||||
rule: '' // 规则
|
||||
},
|
||||
rule_id: '',
|
||||
rules: {
|
||||
rule_name: [
|
||||
{ required: true, message: '请输入规则名称', trigger: 'blur' }
|
||||
],
|
||||
rule: [
|
||||
{ required: true, message: '请输入规则', trigger: 'blur' }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
close() {
|
||||
this.resetForm()
|
||||
// document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
},
|
||||
submit() {
|
||||
this.$refs.ruleForm.validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.isAdd) {
|
||||
this.add()
|
||||
} else {
|
||||
this.edit()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
add () {
|
||||
this.loading = true
|
||||
const url = this.$http.api.rule
|
||||
this.$axios.post(url, this.form).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.resetForm()
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
this.$notify({
|
||||
title: '创建规则成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
edit() {
|
||||
this.loading = true
|
||||
const url = this.$http.api.rule + `/${this.rule_id}`
|
||||
this.$axios.put(url, this.form).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.resetForm()
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
this.$notify({
|
||||
title: '编辑规则成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
resetForm() {
|
||||
this.form = {
|
||||
rule_name: '', // 规则名称
|
||||
rule: '' // 规则
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.rule-dialog{
|
||||
width: 620px;
|
||||
height: 555px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../../../../img/background/addRuleBg.svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: 100% 100%; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.rule-form {
|
||||
margin-top: 70px;
|
||||
text-align: left;
|
||||
}
|
||||
.submit-footer{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 200px;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 2%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,522 +0,0 @@
|
||||
<template>
|
||||
<div class="console-dialog" v-if="visible">
|
||||
<!-- 在此处指定弹窗的样式和内容 -->
|
||||
<i class="el-icon-close" @click="close"></i>
|
||||
<div class="tag">
|
||||
<el-tag class="tags" :style="{'color': (tag==='添加任务') ? '#f8fdff': '#565e6e'}">添加任务</el-tag>
|
||||
<el-tag class="tags" :style="{'color': (tag==='被操作节点') ? '#f8fdff': '#565e6e'}">被操作节点</el-tag>
|
||||
<el-tag class="tags" :style="{'color': (tag==='操作') ? '#f8fdff': '#565e6e'}">操作</el-tag>
|
||||
</div>
|
||||
<div class="basic-box" v-if="tag==='添加任务'">
|
||||
<el-form
|
||||
ref="addTaskForm"
|
||||
:model="form"
|
||||
:rules="taskRules"
|
||||
label-width="150px"
|
||||
class="basic-form"
|
||||
size="small"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="12" :offset="6">
|
||||
<el-form-item label="任务名称" prop="task_name">
|
||||
<el-input v-model="form.task_name" placeholder="请输入任务名称"></el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12" :offset="6">
|
||||
<el-form-item label="任务描述" prop="task_description">
|
||||
<el-input v-model="form.task_description" placeholder="请输入任务描述">
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<footer class="anDiv">
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('被操作节点', true)">下一步</el-button>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="table-box" v-if="tag==='被操作节点'">
|
||||
<div class="tip-message">请勾选多个节点</div>
|
||||
<div class="content">
|
||||
<el-table
|
||||
class="main-table styleTable"
|
||||
ref="multipleTable"
|
||||
height="100%"
|
||||
style="width: 100%;"
|
||||
:data="nodeList"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
@selection-change="handleSelectionNode"
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
type="selection"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="id"
|
||||
label="id"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="nick_name"
|
||||
label="节点昵称"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="role"
|
||||
label="角色"
|
||||
min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.role"></svg-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="ip"
|
||||
label="ip"
|
||||
min-width="150"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="status"
|
||||
label="部署状态"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
<div class="deploy-status">
|
||||
<svg-icon :icon-class="getStatus(scope.row.status).class"></svg-icon>
|
||||
<span :class="getStatus(scope.row.status).class">{{ getStatus(scope.row.status).label }}</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="create_time"
|
||||
label="创建开始时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.create_time | formatDate }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="complete_time"
|
||||
label="创建完成时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.complete_time | formatDate }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('添加任务')">上一步</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('操作', true)">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="basic-box" v-if="tag==='操作'">
|
||||
<el-form
|
||||
ref="operateForm"
|
||||
:model="form"
|
||||
:rules="oprateRules"
|
||||
label-width="150px"
|
||||
class="basic-form"
|
||||
size="small"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="12" :offset="6">
|
||||
<el-form-item label="选择规则" prop="traffic_collect_rule_id">
|
||||
<el-select v-model="form.traffic_collect_rule_id" placeholder="请选择规则" @focus="getRuleList">
|
||||
<el-option
|
||||
v-for="item in ruleDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="12" :offset="6">
|
||||
<el-form-item label="是否过滤噪音流量" prop="filter_noise">
|
||||
<el-radio-group v-model="form.filter_noise">
|
||||
<el-radio :label="true">是</el-radio>
|
||||
<el-radio :label="false">否</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('被操作节点')">上一步</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="submit" :loading="loading">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TaskForm',
|
||||
props: {
|
||||
isAdd: {
|
||||
typeof: Boolean,
|
||||
required: true
|
||||
},
|
||||
nodeList: {
|
||||
typeof: Array,
|
||||
require: true
|
||||
},
|
||||
target_id: {
|
||||
typeof: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible:false,
|
||||
loading: false,
|
||||
tag:"添加任务",
|
||||
task_id: '',
|
||||
ruleDict: [],
|
||||
form: {
|
||||
task_name: '', // 任务名称
|
||||
task_description: '', // 任务描述
|
||||
node_list: [], // 节点列表
|
||||
traffic_collect_rule_id: null, // 选择规则
|
||||
filter_noise: true, // 是否过滤噪音流量
|
||||
},
|
||||
taskRules: {
|
||||
task_name:[
|
||||
{ required: true, message: '请输入任务名称', trigger: 'blur' }
|
||||
],
|
||||
task_description: [
|
||||
{ required: true, message: '请输入任务描述', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
oprateRules: {
|
||||
traffic_collect_rule_id: [
|
||||
{ required: true, message: '请选择规则', trigger: 'blur' }
|
||||
],
|
||||
filter_noise: [
|
||||
{ required: true, message: '请选择是否过滤噪音流量', trigger: 'blur' }
|
||||
]
|
||||
},
|
||||
setting_id: '',
|
||||
torDict: [],
|
||||
networkDict: [],
|
||||
imageDict: []
|
||||
}
|
||||
},
|
||||
created() {
|
||||
// 获取规则字典
|
||||
this.getRuleList()
|
||||
},
|
||||
methods: {
|
||||
// 选择添加任务
|
||||
handleSelectionNode(selectRows) {
|
||||
this.form.node_list = selectRows.map(row => {
|
||||
return row.id
|
||||
})
|
||||
},
|
||||
// 关闭
|
||||
close() {
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
this.tag = '添加任务'
|
||||
this.resetForm()
|
||||
},
|
||||
// 重置
|
||||
resetForm() {
|
||||
this.form = {
|
||||
task_name: '', // 任务名称
|
||||
task_description: '', // 任务描述
|
||||
node_list: [], // 节点列表
|
||||
traffic_collect_rule_id: null, // 选择规则
|
||||
filter_noise: true, // 是否过滤噪音流量
|
||||
}
|
||||
},
|
||||
// 选择哪个页面
|
||||
updateTag(val, isValid){
|
||||
if (val === '被操作节点' && isValid) {
|
||||
this.$refs.addTaskForm.validate((valid) => {
|
||||
if (valid) {
|
||||
this.tag=val
|
||||
}
|
||||
})
|
||||
} else if(val === '操作' && isValid) {
|
||||
if (this.form.node_list.length > 0) {
|
||||
this.tag=val
|
||||
} else {
|
||||
this.$notify({
|
||||
title: '请勾选要被操作的节点!',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
this.tag=val
|
||||
}
|
||||
},
|
||||
// 获取规则列表
|
||||
getRuleList() {
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 99,
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.getRuleList, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.ruleDict = res?.result?.items.map(item => {
|
||||
return {
|
||||
label: item.rule_name,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
// 提交数据
|
||||
submit() {
|
||||
this.$refs.operateForm.validate((valid) => {
|
||||
if (valid) {
|
||||
if (this.isAdd) {
|
||||
this.add()
|
||||
} else {
|
||||
this.edit()
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
add () {
|
||||
this.loading = true
|
||||
const url = this.$http.api.trafficTask
|
||||
this.$axios.post(url, this.form).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.resetForm()
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
this.$notify({
|
||||
title: '创建任务成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
edit() {
|
||||
this.loading = true
|
||||
const url = this.$http.api.trafficTask + `/${this.task_id}`
|
||||
this.$axios.put(url, this.form).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.resetForm()
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
this.$notify({
|
||||
title: '编辑任务成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
getStatus(status) {
|
||||
let statusInfo = {
|
||||
label: '',
|
||||
class: ''
|
||||
}
|
||||
switch (status) {
|
||||
case 'true':
|
||||
case 'pending':
|
||||
statusInfo.label = '正在部署'
|
||||
statusInfo.class = 'deployNormal'
|
||||
break;
|
||||
case 'false':
|
||||
statusInfo.label = '部署失败'
|
||||
statusInfo.class = 'deployFail'
|
||||
break;
|
||||
case 'complete':
|
||||
statusInfo.label = '部署完成'
|
||||
statusInfo.class = 'deploySuccess'
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return statusInfo
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.console-dialog{
|
||||
z-index: 997;
|
||||
width: 1171px;
|
||||
height: 705px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../../../../img/background/NodeListDialog.svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: cover; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
|
||||
.el-icon-close{
|
||||
float: right;
|
||||
padding-right: 7%;
|
||||
padding-top: 1.8%
|
||||
}
|
||||
.tag{
|
||||
margin-left: 9%;
|
||||
.tags{
|
||||
margin-right: 5%;
|
||||
margin-top: 1%;
|
||||
font-size: 23px;
|
||||
border: none;
|
||||
background-color: transparent !important;
|
||||
color: #565e6e;
|
||||
}
|
||||
}
|
||||
.basic-form {
|
||||
margin-top: 50px;
|
||||
text-align: left;
|
||||
::v-deep .el-select {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA !important;
|
||||
}
|
||||
|
||||
.basic-box{
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 450px;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.table-box{
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2%;
|
||||
.tip-message{
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
text-align: left;
|
||||
padding: 11px 0 12px 20px;
|
||||
font-size: 20px;
|
||||
background-color: #1A2648;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 2%;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
.deploy-status {
|
||||
display: inline-block;
|
||||
width: 103px;
|
||||
height: 28px;
|
||||
color:#FFFFFF;
|
||||
border-radius: 3px;
|
||||
padding: 5px, 12px, 5px, 12px;
|
||||
background: rgba(227, 249, 233, 0.2);
|
||||
}
|
||||
.deployFail {
|
||||
color: #E9473E;
|
||||
width: 56px;
|
||||
height: 28px;
|
||||
margin-left: 10px;
|
||||
font-family: PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
}
|
||||
.deployNormal {
|
||||
color: #02DDEA;
|
||||
width: 56px;
|
||||
height: 28px;
|
||||
margin-left: 10px;
|
||||
font-family: PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
}
|
||||
.deploySuccess {
|
||||
color: #0CCB64;
|
||||
width: 56px;
|
||||
height: 28px;
|
||||
margin-left: 10px;
|
||||
font-family: PingFang SC;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 28px;
|
||||
letter-spacing: 0em;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-bottom: 7%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,521 +0,0 @@
|
||||
<template>
|
||||
<div class="console-dialog" v-if="visible">
|
||||
<!-- 在此处指定弹窗的样式和内容 -->
|
||||
<i class="el-icon-close" @click="close"></i>
|
||||
<div class="tag">
|
||||
<el-tag class="tags" :style="{'color': (tag==='主节点') ? '#f8fdff': '#565e6e'}" @click="updateTag('主节点')">主节点</el-tag>
|
||||
<el-tag class="tags" :style="{'color': (tag==='被操作节点') ? '#f8fdff': '#565e6e'}" @click="updateTag('被操作节点')">被操作节点</el-tag>
|
||||
<el-tag class="tags" :style="{'color': (tag==='控制台') ? '#f8fdff': '#565e6e'}" @click="updateTag('控制台')">控制台</el-tag>
|
||||
</div>
|
||||
<div class="basic-box" v-if="tag==='主节点'">
|
||||
<div class="tip-message">请选择主节点作为打开文件系统的依据</div>
|
||||
<div class="content">
|
||||
<el-table
|
||||
class="main-table styleTable"
|
||||
ref="multipleTable"
|
||||
height="100%"
|
||||
style="width: 100%;"
|
||||
:data="tableData"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
@selection-change="handleSelectionMain"
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
type="selection"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="id"
|
||||
label="id"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="nick_name"
|
||||
label="节点昵称"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="role"
|
||||
label="角色"
|
||||
min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.role"></svg-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="ip"
|
||||
label="ip"
|
||||
min-width="150"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="status"
|
||||
label="部署状态"
|
||||
min-width="100"
|
||||
>
|
||||
<!-- <template slot-scope="scope">
|
||||
{{ scope.row.has_deployed ? '已部署' : '未部署' }}
|
||||
</template> -->
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="create_time"
|
||||
label="创建开始时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.create_time | formatDate }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="complete_time"
|
||||
label="创建完成时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.complete_time | formatDate }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<footer class="anDiv">
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('被操作节点')">下一步</el-button>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="basic-box" v-if="tag==='被操作节点'">
|
||||
<div class="tip-message">请勾选多个节点</div>
|
||||
<div class="content">
|
||||
<el-table
|
||||
class="main-table styleTable"
|
||||
ref="multipleTable"
|
||||
height="100%"
|
||||
style="width: 100%;"
|
||||
:data="tableData"
|
||||
tooltip-effect="dark"
|
||||
highlight-current-row
|
||||
@selection-change="handleSelectionMain"
|
||||
>
|
||||
<el-table-column
|
||||
align="center"
|
||||
type="selection"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="id"
|
||||
label="id"
|
||||
width="80"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="nick_name"
|
||||
label="节点昵称"
|
||||
min-width="100"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="role"
|
||||
label="角色"
|
||||
min-width="100">
|
||||
<template slot-scope="scope">
|
||||
<svg-icon :icon-class="scope.row.role"></svg-icon>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="ip"
|
||||
label="ip"
|
||||
min-width="150"/>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="status"
|
||||
label="部署状态"
|
||||
min-width="100"
|
||||
>
|
||||
<!-- <template slot-scope="scope">
|
||||
{{ scope.row.has_deployed ? '已部署' : '未部署' }}
|
||||
</template> -->
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="create_time"
|
||||
label="创建开始时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.create_time | formatDate }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
align="center"
|
||||
prop="complete_time"
|
||||
label="创建完成时间"
|
||||
min-width="100"
|
||||
>
|
||||
<template slot-scope="scope">
|
||||
{{ scope.row.complete_time | formatDate }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('主节点')">上一步</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('控制台')">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="basic-box" v-if="tag==='控制台'">
|
||||
<div class="tip-message">主节点名称:10-relaytwlgthavry
|
||||
已勾选的节点名称列表:11-relayyqbgcxuewy、12-relayztitnmkgfl…</div>
|
||||
<div class="content">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:rows="25"
|
||||
placeholder="请输入命令"
|
||||
v-model="textarea">
|
||||
</el-input>
|
||||
</div>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('被操作节点')">上一步</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="submit" :loading="loading">确认</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Console',
|
||||
props: {
|
||||
tableData: {
|
||||
typeof: Array,
|
||||
require: true
|
||||
},
|
||||
target_id: {
|
||||
typeof: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
visible:false,
|
||||
loading: false,
|
||||
tag:"主节点",
|
||||
setting_id: '',
|
||||
form: {
|
||||
role: '', // 角色选择
|
||||
tor_version: '', // tor版本
|
||||
image_id: '', // 选择镜像
|
||||
replicas: '', // 复本数
|
||||
bandwidth: '', // 宽带限制
|
||||
memory: '', // 内存限制
|
||||
service: '', // 服务器地址
|
||||
out_port: '', // 代理端口
|
||||
or_port: '', // or端口
|
||||
dir_port: '', // dir端口
|
||||
socks_port: '', // 客户端监听端口
|
||||
control_port: '', // 客户端控制端口
|
||||
|
||||
country_id: '', // 国家
|
||||
network_id: '', // 网络
|
||||
|
||||
deployType: '', // 部署方式
|
||||
direct: false, // 直接部署true 仅添加配置false
|
||||
start_tcpdump: false // 启动采集程序并部署 true
|
||||
},
|
||||
roleDict:[
|
||||
{label: '权威目录节点', value: 'da'},
|
||||
{label: '路由节点', value: 'relay'},
|
||||
{label: '出口节点', value: 'exit'},
|
||||
{label: '洋葱服务节点', value: 'onion'},
|
||||
{label: '客户端节点', value: 'client'},
|
||||
{label: '入口节点', value: 'guard'},
|
||||
{label: '其他节点', value: 'other'}
|
||||
],
|
||||
torDict: [],
|
||||
networkDict: [],
|
||||
imageDict: []
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 选择主节点
|
||||
handleSelectionMain() {},
|
||||
// 关闭
|
||||
close() {
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
this.tag = '主节点'
|
||||
this.resetForm()
|
||||
},
|
||||
// 重置
|
||||
resetForm() {
|
||||
this.form = {
|
||||
role: '', // 角色选择
|
||||
tor_version: '', // tor版本
|
||||
image_id: '', // 选择镜像
|
||||
replicas: '', // 复本数
|
||||
bandwidth: '', // 宽带限制
|
||||
memory: '', // 内存限制
|
||||
service: '', // 服务器地址
|
||||
out_port: '', // 代理端口
|
||||
or_port: '', // or端口
|
||||
dir_port: '', // dir端口
|
||||
socks_port: '', // 客户端监听端口
|
||||
control_port: '', // 客户端控制端口
|
||||
|
||||
country_id: '', // 国家
|
||||
network_id: '', // 网络
|
||||
|
||||
deployType: '', // 部署方式
|
||||
direct: false, // 直接部署true 仅添加配置false
|
||||
start_tcpdump: false // 启动采集程序并部署 true
|
||||
}
|
||||
},
|
||||
// 选择哪个页面
|
||||
updateTag(val){
|
||||
this.tag=val
|
||||
},
|
||||
// 角色选择
|
||||
selectRole(node) {
|
||||
if (node === 'other') {
|
||||
this.$emit('openRoleForm')
|
||||
}
|
||||
// 获取torDict
|
||||
this.$axios.get(this.$http.api.getTorDict, {role: this.form.role}).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.torDict = res?.result
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
// 获取镜像字典
|
||||
this.getImageDict()
|
||||
|
||||
},
|
||||
// tor选择
|
||||
selectTor(tor){
|
||||
// 获取镜像字典
|
||||
this.getImageDict()
|
||||
},
|
||||
// 获取imageDict
|
||||
getImageDict(){
|
||||
const params = {
|
||||
page:1,
|
||||
size: 99,
|
||||
image_name: this.form.role,
|
||||
image_version: this.form.tor_version
|
||||
}
|
||||
this.$axios.get(this.$http.api.getImageDict, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.imageDict = res?.result?.items.map(item => {
|
||||
return {
|
||||
label: item.image_name + '-' + item.image_version,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
// 获取网络
|
||||
getNetworkDict(){
|
||||
const params = {
|
||||
page:1,
|
||||
size: 99,
|
||||
country_id: this.form.country_id,
|
||||
}
|
||||
this.$axios.get(this.$http.api.getNetworkDict, params).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.networkDict = res?.result?.items.map(item => {
|
||||
return {
|
||||
label: item.cidr,
|
||||
value: item.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
})
|
||||
},
|
||||
selectDeploy(deployType) {
|
||||
switch (deployType) {
|
||||
case 'direct':
|
||||
this.form.direct = true
|
||||
break
|
||||
case 'only':
|
||||
this.form.direct = false
|
||||
break
|
||||
case 'start':
|
||||
this.form.start_tcpdump = true
|
||||
break
|
||||
default:
|
||||
this.form.direct = true
|
||||
break
|
||||
}
|
||||
},
|
||||
// 提交数据
|
||||
submit() {
|
||||
// const submitForm = this.setSubmitForm()
|
||||
// if (this.isAdd) {
|
||||
// this.add(submitForm)
|
||||
// } else {
|
||||
// this.edit(submitForm)
|
||||
// }
|
||||
},
|
||||
setSubmitForm() {
|
||||
const {role, image_id, replicas, bandwidth, memory } = this.form
|
||||
let submitForm = {role, image_id, replicas, bandwidth, memory }
|
||||
if (this.form.role === 'other') {
|
||||
submitForm.role = this.otherRole
|
||||
} else if (this.form.role==='da'||this.form.role==='relay'||this.form.role ==='guard'||this.form.role ==='exit') {
|
||||
submitForm.tor_version = this.form.tor_version
|
||||
submitForm.or_port = this.form.or_port
|
||||
submitForm.dir_port = this.form.dir_port
|
||||
} else if (this.form.role==='onion') {
|
||||
submitForm.tor_version = this.form.tor_version
|
||||
submitForm.service = this.form.service
|
||||
submitForm.socks_port = this.form.socks_port
|
||||
submitForm.control_port = this.form.control_port
|
||||
} else if (this.form.role==='client') {
|
||||
submitForm.tor_version = this.form.tor_version
|
||||
submitForm.out_port = this.form.out_port
|
||||
submitForm.socks_port = this.form.socks_port
|
||||
submitForm.control_port = this.form.control_port
|
||||
}
|
||||
return submitForm
|
||||
},
|
||||
add(submitForm) {
|
||||
this.loading = true
|
||||
const url = this.$http.api.setting + `/?target_id=${this.target_id}&network_id=${this.form.network_id}&direct=${this.form.direct}&start_tcpdump=${this.form.start_tcpdump}`
|
||||
this.$axios.post(url, submitForm).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
this.$notify({
|
||||
title: '添加靶场配置成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
},
|
||||
edit(submitForm) {
|
||||
this.loading = true
|
||||
const url = this.$http.api.setting + `/?target_id=${this.target_id}&setting_id=${this.setting_id}&network_id=${this.form.network_id}&direct=${this.form.direct}&start_tcpdump=${this.form.start_tcpdump}`
|
||||
this.$axios.put(url, submitForm).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.close()
|
||||
this.$emit('refresh')
|
||||
this.$notify({
|
||||
title: '添加靶场配置成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.console-dialog{
|
||||
z-index: 997;
|
||||
width: 1331px;
|
||||
height: 800px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../../img/background/NodeListDialog.svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: cover; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.el-icon-close{
|
||||
float: right;
|
||||
padding-right: 8%;
|
||||
padding-top: 1.8%
|
||||
}
|
||||
.tag{
|
||||
margin-left: 15%;
|
||||
.tags{
|
||||
margin-right: 5%;
|
||||
margin-top: 1%;
|
||||
font-size: 23px;
|
||||
border: none;
|
||||
background-color: transparent !important;
|
||||
color: #565e6e;
|
||||
}
|
||||
}
|
||||
|
||||
.base-input {
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 2%;
|
||||
text-align: center;
|
||||
}
|
||||
.label-span {
|
||||
margin-right: 3%;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA !important;
|
||||
}
|
||||
.basic-box{
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2%;
|
||||
.tip-message{
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
font-size: 20px;
|
||||
background-color: #1A2648;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 2%;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
::v-deep .el-textarea__inner {
|
||||
background-color: #1A2648;
|
||||
}
|
||||
}
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-bottom: 7%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,274 +0,0 @@
|
||||
<template>
|
||||
<div class="console-dialog" v-if="visible">
|
||||
<i class="el-icon-close" @click="close"></i>
|
||||
<div class="tag">
|
||||
<el-tag class="tags" :style="{'color': (tag==='主节点') ? '#f8fdff': '#565e6e'}" @click="updateTag('主节点')">主节点</el-tag>
|
||||
<el-tag class="tags" :style="{'color': (tag==='被操作节点') ? '#f8fdff': '#565e6e'}" @click="updateTag('被操作节点')">被操作节点</el-tag>
|
||||
<el-tag class="tags" :style="{'color': (tag==='文件管理') ? '#f8fdff': '#565e6e'}" @click="updateTag('文件管理')">文件管理</el-tag>
|
||||
</div>
|
||||
<div class="basic-box" v-show="tag==='主节点'">
|
||||
<div class="tip-message">请选择主节点作为打开文件系统的依据</div>
|
||||
<div class="content">
|
||||
<!-- 主节点 -->
|
||||
<MainNode
|
||||
:node-list="nodeList"
|
||||
@selectMainNode="selectMainNode"
|
||||
></MainNode>
|
||||
</div>
|
||||
<footer class="anDiv">
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('被操作节点')">下一步</el-button>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="basic-box" v-show="tag==='被操作节点'">
|
||||
<div class="tip-message">请勾选多个节点</div>
|
||||
<div class="content">
|
||||
<!-- 被操作节点 -->
|
||||
<OperateNode
|
||||
:node-list="nodeList"
|
||||
@selectOperateNode="selectOperateNode"
|
||||
></OperateNode>
|
||||
</div>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('主节点')">上一步</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('文件管理')">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="basic-box" v-if="tag==='文件管理'">
|
||||
<div class="tip-message" style="height: 76px;">
|
||||
<span>{{`主节点名称:${master_node_name}`}}<br/>{{`已勾选的节点名称列表:${batch_node_name_list.join('、')}`}}</span>
|
||||
</div>
|
||||
<!-- 文件管理 -->
|
||||
<FileManager
|
||||
ref="fileManager"
|
||||
></FileManager>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('被操作节点')">上一步</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="download" :loading="loading">下载</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MainNode from '../components/MainNode.vue'
|
||||
import OperateNode from '../components/OperateNode.vue'
|
||||
import FileManager from '../components/DownloadFileManager.vue'
|
||||
import qs from 'qs'
|
||||
export default {
|
||||
name: 'Download',
|
||||
components:{
|
||||
MainNode,
|
||||
OperateNode,
|
||||
FileManager
|
||||
},
|
||||
props: {
|
||||
target_id: {
|
||||
typeof: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
nodeList: [],
|
||||
downloadForm: {
|
||||
master_node_id: '',
|
||||
batch_node_id_list: [],
|
||||
current_dir: '',
|
||||
filename: ''
|
||||
},
|
||||
master_node_name: '',
|
||||
batch_node_name_list: [],
|
||||
visible:false,
|
||||
loading: false,
|
||||
tag:"主节点",
|
||||
form: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取节点列表
|
||||
getNodeList() {
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 99
|
||||
}
|
||||
if (this.target_id && this.target_id !== '') {
|
||||
reqParams.target_id = this.target_id
|
||||
}
|
||||
this.$axios.get(this.$http.api.getNodeList, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.nodeList = res?.result?.items
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {})
|
||||
},
|
||||
// 选择主节点
|
||||
selectMainNode({ id, nick_name }) {
|
||||
this.$store.commit('range/setNodeId', id)
|
||||
this.downloadForm.master_node_id = id
|
||||
this.master_node_name = nick_name
|
||||
},
|
||||
// 选择被操作节点
|
||||
selectOperateNode(selectRows) {
|
||||
this.batch_node_name_list = []
|
||||
this.downloadForm.batch_node_id_list = selectRows.map(row => {
|
||||
this.batch_node_name_list.push(row.nick_name)
|
||||
return row.id
|
||||
})
|
||||
},
|
||||
// 选择上传文件
|
||||
selectUploadFile({path, file}) {
|
||||
this.downloadForm.current_dir = path
|
||||
this.downloadForm.file = file
|
||||
},
|
||||
// 关闭
|
||||
close() {
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
this.tag = '主节点'
|
||||
this.resetForm()
|
||||
},
|
||||
// 重置
|
||||
resetForm() {
|
||||
this.form = {}
|
||||
},
|
||||
// 选择哪个页面
|
||||
updateTag(val){
|
||||
this.tag=val
|
||||
},
|
||||
// 提交数据
|
||||
download() {
|
||||
this.downloadForm.current_dir = this.$refs.fileManager.selectNode.path
|
||||
this.downloadForm.filename = this.$refs.fileManager.filename
|
||||
const reqParams = qs.stringify(this.downloadForm, { arrayFormat: 'repeat' })
|
||||
const url = this.$http.api.batchDownload + '?' + reqParams
|
||||
this.$axios.getFile(url).then(res => {
|
||||
if (res.status == 200 || res.statusText == "OK") {
|
||||
const { data, headers } = res
|
||||
const fileName = headers['content-disposition'].replace(/\w+;filename=(.*)/, '$1')
|
||||
// 此处当返回json文件时需要先对data进行JSON.stringify处理,其他类型文件不用做处理
|
||||
//const blob = new Blob([JSON.stringify(data)], ...)
|
||||
const blob = new Blob([data], {type: headers['content-type']})
|
||||
let dom = document.createElement('a')
|
||||
let url = window.URL.createObjectURL(blob)
|
||||
dom.href = url
|
||||
dom.download = decodeURI(fileName)
|
||||
dom.style.display = 'none'
|
||||
document.body.appendChild(dom)
|
||||
dom.click()
|
||||
dom.parentNode.removeChild(dom)
|
||||
window.URL.revokeObjectURL(url)
|
||||
this.$notify({
|
||||
title: '下载文件成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
} else {
|
||||
this.$notify({
|
||||
title: '下载文件失败',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.close()
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.console-dialog{
|
||||
z-index: 997;
|
||||
width: 1331px;
|
||||
height: 800px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../../img/background/NodeListDialog.svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: cover; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.el-icon-close{
|
||||
float: right;
|
||||
padding-right: 8%;
|
||||
padding-top: 1.8%
|
||||
}
|
||||
.tag{
|
||||
margin-left: 15%;
|
||||
.tags{
|
||||
margin-right: 5%;
|
||||
margin-top: 1%;
|
||||
font-size: 23px;
|
||||
border: none;
|
||||
background-color: transparent !important;
|
||||
color: #565e6e;
|
||||
}
|
||||
}
|
||||
.base-input {
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 2%;
|
||||
text-align: center;
|
||||
}
|
||||
.label-span {
|
||||
margin-right: 3%;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA !important;
|
||||
}
|
||||
.basic-box{
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2%;
|
||||
.tip-message{
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
text-align: left;
|
||||
padding: 11px 0 12px 20px;
|
||||
font-size: 20px;
|
||||
background-color: #1A2648;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 2%;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-bottom: 7%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,146 +0,0 @@
|
||||
<template>
|
||||
<div class="head">
|
||||
<div class="role-select">
|
||||
<el-select v-model="role" clearable placeholder="节点角色" @change="query">
|
||||
<el-option
|
||||
v-for="item in roleDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
<svg-icon :icon-class="item.value ? item.value : 'other'"></svg-icon>
|
||||
<span style="margin-left: 10px;">{{ item.label }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<div class="deploy-select">
|
||||
<el-select v-model="status" clearable placeholder="节点部署状态" @change="query">
|
||||
<el-option
|
||||
v-for="item in statusDict"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</div>
|
||||
<el-button v-if="false" type="primary" @click="batchConsole">批量控制台</el-button>
|
||||
<el-button v-if="true" type="primary" @click="batchDownload">批量下载文件</el-button>
|
||||
<el-button v-if="true" type="primary" @click="batchUpload">批量上传文件</el-button>
|
||||
<el-button v-if="true" type="primary" @click="batchCollectTraffic">批量采集流量</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'Header',
|
||||
data() {
|
||||
return {
|
||||
role: '',
|
||||
status: '',
|
||||
roleDict:[
|
||||
{label: '全部节点', value: ''},
|
||||
{label: '权威目录节点', value: 'da'},
|
||||
{label: '路由节点', value: 'relay'},
|
||||
{label: '出口节点', value: 'exit'},
|
||||
{label: '洋葱服务节点', value: 'onion'},
|
||||
{label: '客户端节点', value: 'client'},
|
||||
{label: '入口节点', value: 'guard'}
|
||||
],
|
||||
statusDict: [
|
||||
{value: 'pending', label: '正在部署'},
|
||||
{value: 'false', label: '部署失败'},
|
||||
{value: 'complete',label: '部署完成'}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 查询
|
||||
query() {
|
||||
let params = {}
|
||||
if (this.role !== '') {
|
||||
params.role = this.role
|
||||
}
|
||||
if (this.status !== '') {
|
||||
params.status = this.status
|
||||
}
|
||||
this.$emit('query', params)
|
||||
},
|
||||
// 批量控制台
|
||||
batchConsole() {
|
||||
this.$emit('batchConsole')
|
||||
},
|
||||
// 批量下载文件
|
||||
batchDownload() {
|
||||
this.$emit('batchDownload')
|
||||
},
|
||||
// 批量上传文件
|
||||
batchUpload() {
|
||||
this.$emit('batchUpload')
|
||||
},
|
||||
// 批量采集流量
|
||||
batchCollectTraffic() {
|
||||
this.$emit('batchCollectTraffic')
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.head{
|
||||
width: 100%;
|
||||
height: 15%;
|
||||
margin-top: 1%;
|
||||
text-align: right;
|
||||
|
||||
/*background-color: #5daf34;*/
|
||||
.block{
|
||||
display: inline-block;
|
||||
margin-left: 2%;
|
||||
|
||||
}
|
||||
.input{
|
||||
display: inline-block;
|
||||
height: 60%;
|
||||
width: 10%;
|
||||
margin-left: 0.5%;
|
||||
.el-input::placeholder {
|
||||
width: auto;
|
||||
}
|
||||
.icon-group {
|
||||
display: flex; /* 设置容器为 Flexbox 容器 */
|
||||
align-items: center; /* 垂直居中图片 */
|
||||
gap: 5px; /* 图片和文字之间的间距,可以根据需要进行调整 */
|
||||
|
||||
}
|
||||
.icon-group img {
|
||||
transform: scale(1);
|
||||
margin-right: 15px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
|
||||
}
|
||||
.deploy-select{
|
||||
display: inline-block;
|
||||
height: 60%;
|
||||
width: 10%;
|
||||
margin-top: 0.5%;
|
||||
margin-left: 0.5%;
|
||||
margin-right: 1%;
|
||||
.el-input::placeholder {
|
||||
width: auto;
|
||||
}
|
||||
.icon-group {
|
||||
display: flex; /* 设置容器为 Flexbox 容器 */
|
||||
align-items: center; /* 垂直居中图片 */
|
||||
gap: 5px; /* 图片和文字之间的间距,可以根据需要进行调整 */
|
||||
}
|
||||
.icon-group img {
|
||||
transform: scale(1);
|
||||
margin-right: 15px;
|
||||
margin-top: 6px;
|
||||
}
|
||||
}
|
||||
::v-deep .role-select{
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,285 +0,0 @@
|
||||
<template>
|
||||
<div class="console-dialog" v-if="visible">
|
||||
<i class="el-icon-close" @click="close"></i>
|
||||
<div class="tag">
|
||||
<el-tag class="tags" :style="{'color': (tag==='主节点') ? '#f8fdff': '#565e6e'}" @click="updateTag('主节点')">主节点</el-tag>
|
||||
<el-tag class="tags" :style="{'color': (tag==='被操作节点') ? '#f8fdff': '#565e6e'}" @click="updateTag('被操作节点')">被操作节点</el-tag>
|
||||
<el-tag class="tags" :style="{'color': (tag==='文件管理') ? '#f8fdff': '#565e6e'}" @click="updateTag('文件管理')">文件管理</el-tag>
|
||||
</div>
|
||||
<div class="basic-box" v-show="tag==='主节点'">
|
||||
<div class="tip-message">请选择主节点作为打开文件系统的依据</div>
|
||||
<div class="content">
|
||||
<!-- 主节点 -->
|
||||
<MainNode
|
||||
:node-list="nodeList"
|
||||
@selectMainNode="selectMainNode"
|
||||
></MainNode>
|
||||
</div>
|
||||
<footer class="anDiv">
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('被操作节点')">下一步</el-button>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="basic-box" v-show="tag==='被操作节点'">
|
||||
<div class="tip-message">请勾选多个节点</div>
|
||||
<div class="content">
|
||||
<!-- 被操作节点 -->
|
||||
<OperateNode
|
||||
:node-list="nodeList"
|
||||
@selectOperateNode="selectOperateNode"
|
||||
></OperateNode>
|
||||
</div>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('主节点')">上一步</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('文件管理')">下一步</el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="basic-box" v-if="tag==='文件管理'">
|
||||
<div class="tip-message" style="height: 76px;">
|
||||
<span>{{`主节点名称:${master_node_name}`}}<br/>{{`已勾选的节点名称列表:${batch_node_name_list.join('、')}`}}</span>
|
||||
</div>
|
||||
<!-- 文件管理 -->
|
||||
<FileManager
|
||||
ref="fileManager"
|
||||
></FileManager>
|
||||
<div class="anDiv">
|
||||
<div>
|
||||
<el-button class="glBut" type="primary" @click="close">取消</el-button>
|
||||
<el-button class="glBut but-color" type="primary" @click="updateTag('被操作节点')">上一步</el-button>
|
||||
<label
|
||||
class="file-upload-button"
|
||||
for="file-input-upload"
|
||||
>
|
||||
<span>上传</span>
|
||||
<input
|
||||
type="file"
|
||||
id="file-input-upload"
|
||||
@change="submit"
|
||||
style="display: none;"
|
||||
/>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import MainNode from '../components/MainNode.vue'
|
||||
import OperateNode from '../components/OperateNode.vue'
|
||||
import FileManager from '../components/UploadFileManager.vue'
|
||||
import qs from 'qs'
|
||||
export default {
|
||||
name: 'Upload',
|
||||
components:{
|
||||
MainNode,
|
||||
OperateNode,
|
||||
FileManager
|
||||
},
|
||||
props: {
|
||||
target_id: {
|
||||
typeof: String,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
nodeList: [],
|
||||
uploadForm: {
|
||||
master_node_id: '',
|
||||
batch_node_id_list: [],
|
||||
current_dir: ''
|
||||
},
|
||||
file: '',
|
||||
master_node_name: '',
|
||||
batch_node_name_list: [],
|
||||
visible:false,
|
||||
loading: false,
|
||||
tag:"主节点",
|
||||
form: {}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 获取节点列表
|
||||
getNodeList() {
|
||||
const reqParams = {
|
||||
page: 1,
|
||||
size: 99
|
||||
}
|
||||
if (this.target_id && this.target_id !== '') {
|
||||
reqParams.target_id = this.target_id
|
||||
}
|
||||
this.$axios.get(this.$http.api.getNodeList, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.nodeList = res?.result?.items
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {})
|
||||
},
|
||||
// 选择主节点
|
||||
selectMainNode({ id, nick_name }) {
|
||||
this.$store.commit('range/setNodeId', id)
|
||||
this.uploadForm.master_node_id = id
|
||||
this.master_node_name = nick_name
|
||||
},
|
||||
// 选择被操作节点
|
||||
selectOperateNode(selectRows) {
|
||||
this.batch_node_name_list = []
|
||||
this.uploadForm.batch_node_id_list = selectRows.map(row => {
|
||||
this.batch_node_name_list.push(row.nick_name)
|
||||
return row.id
|
||||
})
|
||||
},
|
||||
// // 选择上传文件
|
||||
// selectUploadFile({path}) {
|
||||
// this.uploadForm.current_dir = path
|
||||
// // this.file = file
|
||||
// },
|
||||
// 关闭
|
||||
close() {
|
||||
document.querySelector('.mask').style.display = 'none'
|
||||
this.visible = false
|
||||
this.tag = '主节点'
|
||||
this.resetForm()
|
||||
},
|
||||
// 重置
|
||||
resetForm() {
|
||||
this.form = {}
|
||||
},
|
||||
// 选择哪个页面
|
||||
updateTag(val){
|
||||
this.tag=val
|
||||
},
|
||||
// 提交数据
|
||||
submit(e) {
|
||||
this.uploadForm.current_dir = this.$refs.fileManager.selectNode.path
|
||||
this.file = e.target.files[0]
|
||||
const params = qs.stringify(this.uploadForm, { arrayFormat: 'repeat' })
|
||||
const url = this.$http.api.batchUpload + '?' + params
|
||||
const submitForm = new FormData()
|
||||
submitForm.append('file', this.file)
|
||||
this.loading = true
|
||||
this.$axios.postFormData(url, submitForm).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.$notify({
|
||||
title: '批量上传成功',
|
||||
type: 'success',
|
||||
duration: 2500
|
||||
})
|
||||
// 更新当前路径下的文件信息
|
||||
this.$refs.fileManager.updateCurrentPath()
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.console-dialog{
|
||||
z-index: 997;
|
||||
width: 1331px;
|
||||
height: 800px;
|
||||
position: absolute; /* 绝对定位 */
|
||||
top: 50%; /* 向下偏移50% */
|
||||
left: 50%; /* 向右偏移50% */
|
||||
transform: translate(-50%, -50%); /* 回移50% */
|
||||
background-image:url('../../../../img/background/NodeListDialog.svg');
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-size: cover; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
.el-icon-close{
|
||||
float: right;
|
||||
padding-right: 8%;
|
||||
padding-top: 1.8%
|
||||
}
|
||||
.tag{
|
||||
margin-left: 15%;
|
||||
.tags{
|
||||
margin-right: 5%;
|
||||
margin-top: 1%;
|
||||
font-size: 23px;
|
||||
border: none;
|
||||
background-color: transparent !important;
|
||||
color: #565e6e;
|
||||
}
|
||||
}
|
||||
.base-input {
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-top: 2%;
|
||||
text-align: center;
|
||||
}
|
||||
.label-span {
|
||||
margin-right: 3%;
|
||||
display: inline-block;
|
||||
width: 60px;
|
||||
text-align: right;
|
||||
}
|
||||
.but-color {
|
||||
background-color: #02DDEA !important;
|
||||
}
|
||||
|
||||
.file-upload-button {
|
||||
display: inline-block;
|
||||
padding: 5px 33px;
|
||||
background-color: #02DDEA;
|
||||
color: #1b7cc4;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
font-size: 14px;
|
||||
}
|
||||
.file-upload-button:hover {
|
||||
background-color: #02DDEA;
|
||||
}
|
||||
|
||||
.basic-box{
|
||||
width: 100%;
|
||||
min-height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 2%;
|
||||
.tip-message{
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
text-align: left;
|
||||
padding: 11px 0 12px 20px;
|
||||
font-size: 20px;
|
||||
background-color: #1A2648;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 2%;
|
||||
}
|
||||
.content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
.anDiv{
|
||||
width: 100%;
|
||||
float: left;
|
||||
margin-bottom: 7%;
|
||||
text-align: center;
|
||||
.glBut{
|
||||
width: 90px;
|
||||
height: 30px;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 5%;
|
||||
margin-right: 5%;
|
||||
background-color: rgba(24, 133, 234, 0.2);
|
||||
color: #1b7cc4;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,30 +0,0 @@
|
||||
<template>
|
||||
<div class="topology-map">
|
||||
<!-- <img class="img-topology" src="../../../img/background/topology.svg"> -->
|
||||
<div class="img-topology"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'TopologyMap',
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
.topology-map {
|
||||
width: 49%;
|
||||
height: 100%;
|
||||
background-image:url('../../../img/backgroundFourCorner.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
.img-topology {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image:url('../../../img/background/topology.svg');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
}
|
||||
</style>
|
||||
@@ -1,217 +0,0 @@
|
||||
<template>
|
||||
<div class='wrapper' v-loading="loading">
|
||||
<div class='chart' id='chart' style="width: 100%; height: 100%;"></div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getTargetsResponse } from './mock.js'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
loading: false,
|
||||
worldChart: {},
|
||||
target_id: '',
|
||||
NodeList: [],
|
||||
namemap: {},
|
||||
geoCoordMap: {}
|
||||
}
|
||||
},
|
||||
props:{
|
||||
},
|
||||
watch: {
|
||||
'$store.state.range.targetId': {
|
||||
handler(newVal, oldVal) {
|
||||
this.target_id = newVal
|
||||
this.init()
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
},
|
||||
mounted () {
|
||||
},
|
||||
methods: {
|
||||
init() {
|
||||
this.initData()
|
||||
},
|
||||
initData() {
|
||||
// 获取world.json里面国家数据和坐标数据
|
||||
var json = require('/src/api/world.json')
|
||||
this.geoCoordMap = json.trapeze
|
||||
this.namemap = json.namemap
|
||||
// 获取接口节点数据
|
||||
// this.NodeList = getTargetsResponse?.result
|
||||
|
||||
const reqParams = {}
|
||||
if (this.target_id && this.target_id !== '') {
|
||||
reqParams.target_id = this.target_id
|
||||
}
|
||||
this.loading = true
|
||||
this.$axios.get(this.$http.api.worldMap, reqParams).then(res => {
|
||||
if (res.code == 200 || res.code == "OK") {
|
||||
this.NodeList = res?.result
|
||||
}
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
}).finally(() => {
|
||||
this.loading = false
|
||||
this.initWorldMap(this.namemap, this.geoCoordMap)
|
||||
})
|
||||
},
|
||||
initWorldMap(namemap, geoCoordMap) {
|
||||
// var that = this
|
||||
// 获取echarts的容器
|
||||
this.worldChart = this.$echarts.init(document.getElementById("chart"));
|
||||
var pointData = this.NodeList
|
||||
var series = [];
|
||||
[[, pointData]].forEach(function (item, i) {
|
||||
series.push({
|
||||
type: "effectScatter",
|
||||
coordinateSystem: "geo",
|
||||
zlevel: 2,
|
||||
rippleEffect: {
|
||||
//涟漪特效
|
||||
period: 4, //动画时间,值越小速度越快
|
||||
brushType: "stroke", //波纹绘制方式 stroke, fill
|
||||
scale: 4
|
||||
//波纹圆环最大限制,值越大波纹越大
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: "right", //显示位置
|
||||
offset: [5, 0], //偏移设置
|
||||
formatter: "{b}" //圆环显示文字
|
||||
},
|
||||
emphasis: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
symbol: "circle",
|
||||
symbolSize: function (val) {
|
||||
return 5; //圆环大小
|
||||
},
|
||||
data: item[1].map(dataItem => {
|
||||
let countryName = ''
|
||||
for (let key in namemap) {
|
||||
if(key.toLowerCase() === dataItem.country.replace('-', ' ').toLowerCase()) {
|
||||
countryName = namemap[key]
|
||||
}
|
||||
}
|
||||
const location = [Number(dataItem.location_info.longitude), Number(dataItem.location_info.latitude), dataItem.id]
|
||||
return {
|
||||
name: dataItem.nick_name,
|
||||
value: location,
|
||||
nodeInfo: dataItem
|
||||
}
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
let option = {
|
||||
// backgroundColor: '#000',
|
||||
// 图表主标题
|
||||
title: {
|
||||
text: '世界地图展示各节点位置', // 主标题文本,支持使用 \n 换行
|
||||
top: 5, // 定位 值: 'top', 'middle', 'bottom' 也可以是具体的值或者百分比
|
||||
left: 'left', // 值: 'left', 'center', 'right' 同上
|
||||
textStyle: { // 文本样式
|
||||
fontSize: 14,
|
||||
fontWeight: 400,
|
||||
color: '#ffffff'
|
||||
}
|
||||
},
|
||||
//悬浮提示
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
// backgroundColor: "#1540a1",
|
||||
borderColor: "#FFFFFF",
|
||||
showDelay: 0,
|
||||
hideDelay: 0,
|
||||
// enterable: true,
|
||||
transitionDuration: 0,
|
||||
// extraCssText: "z-index:100",formatter
|
||||
formatter: function (params, ticket, callback) {
|
||||
// console.log(params)
|
||||
//根据业务自己拓展要显示的内容
|
||||
var res = "";
|
||||
const { name, value, nodeInfo } = params.data
|
||||
res = "<span style='display:inline-block; text-align:left;'>id:" + value[2]
|
||||
+ "<br/>昵称:" + name.toString()
|
||||
+ "<br/>国家:" + nodeInfo.location_info.country
|
||||
+ "<br/>ip:" + nodeInfo.ip
|
||||
+ "<br/>经纬度:" + value[0] + ',' + value[1]
|
||||
+ "</span>";
|
||||
return res;
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
//图例值控制
|
||||
show: false,
|
||||
min: 0,
|
||||
max: 1000,
|
||||
text:['max','min'],
|
||||
realtime: false,
|
||||
calculable: true,
|
||||
color: ['orangered']
|
||||
// color: ['#0064d0','#c3e0ff'],
|
||||
},
|
||||
geo: {
|
||||
map: "world",
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
roam: true, //是否允许缩放
|
||||
layoutCenter: ["50%", "50%"], //地图位置
|
||||
layoutSize: "180%",
|
||||
itemStyle: {
|
||||
normal: {
|
||||
areaColor: '#0064d0', //地图背景色
|
||||
// color: ["#04284e"], //地图背景色
|
||||
borderColor: "#5bc1c9" //省市边界线
|
||||
},
|
||||
emphasis: {
|
||||
color: "rgba(37, 43, 61, .5)" //选中区域的颜色
|
||||
// areaColor: '#3742ff' // 选中区域的颜色
|
||||
}
|
||||
},
|
||||
nameMap: this.namemap
|
||||
},
|
||||
series: series,
|
||||
loading: true // 开启Loading效果
|
||||
};
|
||||
this.worldChart.setOption(option);
|
||||
//点击事件,根据点击某个节点跳转到节点详情页
|
||||
this.worldChart.on("click", params => {
|
||||
if (params.value) {
|
||||
this.$store.commit('range/setNodeId', params.value[2])
|
||||
//保存是否正在采集流量
|
||||
this.$store.commit('node/setStartTraffic', params?.data?.nodeInfo?.collecting)
|
||||
//保存是否正在结束采集流量
|
||||
this.$store.commit('node/setEndTraffic', params?.data?.nodeInfo?.analysing)
|
||||
this.$router.push({ name: 'nodeDetail'})
|
||||
}
|
||||
});
|
||||
}
|
||||
// drawChart () {
|
||||
// this.initWorldMap(this.namemap, this.geoCoordMap)
|
||||
// }
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
.wrapper {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.wrapper .chart {
|
||||
width: 100%;
|
||||
margin:0 auto;
|
||||
height: 100%;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,68 +0,0 @@
|
||||
<template>
|
||||
<div :class="['world-map', {'is--maximize': isFullscreen}]">
|
||||
<Map ref="worldMap"></Map>
|
||||
<span class="icon-span" slot="label">
|
||||
<svg-icon class="icon-zoom" :icon-class="fullscreenIcon" @click="zoomEvent"></svg-icon>
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Map from './Map'
|
||||
export default {
|
||||
name: 'WorldMap',
|
||||
components: { Map },
|
||||
data() {
|
||||
return {
|
||||
isFullscreen: false,
|
||||
fullscreenIcon: 'fullscreen'
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
zoomEvent() {
|
||||
this.isFullscreen = !this.isFullscreen
|
||||
this.fullscreenIcon = this.isFullscreen ? 'narrow' : 'fullscreen'
|
||||
this.$nextTick(() => {
|
||||
this.$refs.worldMap.worldChart.resize()
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang='less' scoped>
|
||||
.world-map {
|
||||
position: relative;
|
||||
width: 49%;
|
||||
height: 100%;
|
||||
background-image:url('../../../img/backgroundFourCorner.png');
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
.icon-span {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: -10px;
|
||||
padding: 12px 20px;
|
||||
.icon-zoom {
|
||||
font-size: 30px;
|
||||
color: #02DDEA;
|
||||
margin: 0 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.is--maximize {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100% !important;
|
||||
height: 100% !important;
|
||||
padding: 0.5em 1em;
|
||||
// background-color: #17234e;
|
||||
background: url(../../../img/background/bgMain.svg);
|
||||
background-repeat: no-repeat; /* 可选,防止图像重复 */
|
||||
background-position: center; /* 居中显示 */
|
||||
background-size: cover; /* 宽度为100%,高度自适应保持宽高比 */
|
||||
z-index: 1000;
|
||||
}
|
||||
</style>
|
||||
@@ -1,113 +0,0 @@
|
||||
const getTargetsResponse = {
|
||||
"code": 200,
|
||||
"message": "success",
|
||||
"result": [
|
||||
{
|
||||
"setting_id": 1,
|
||||
"traffic_collect_task_id": null,
|
||||
"id": 1,
|
||||
"ip": "171.25.193.64",
|
||||
"pod_name": "target-1-da-1-6cddf4649d-mfpx7",
|
||||
"nick_name": "daoranjcqlil",
|
||||
"status": "complete",
|
||||
"onion": null,
|
||||
"create_time": "2023-12-29T18:50:32",
|
||||
"complete_time": "2023-12-29T19:00:17",
|
||||
"role": "da",
|
||||
"country": "sweden",
|
||||
"cidr": "171.25.193.0/24"
|
||||
},
|
||||
{
|
||||
"setting_id": 2,
|
||||
"traffic_collect_task_id": null,
|
||||
"id": 2,
|
||||
"ip": "193.31.27.64",
|
||||
"pod_name": "target-1-relay-2-8df66cb66-slhnv",
|
||||
"nick_name": "relayjknwkekrof",
|
||||
"status": "complete",
|
||||
"onion": null,
|
||||
"create_time": "2023-12-29T18:50:34",
|
||||
"complete_time": "2023-12-29T19:05:01",
|
||||
"role": "relay",
|
||||
"country": "germany",
|
||||
"cidr": "193.31.27.0/24"
|
||||
},
|
||||
{
|
||||
"setting_id": 3,
|
||||
"traffic_collect_task_id": null,
|
||||
"id": 3,
|
||||
"ip": "80.228.207.64",
|
||||
"pod_name": "target-1-relay-3-6f57bf5448-snwgk",
|
||||
"nick_name": "relayuyunnharjw",
|
||||
"status": "complete",
|
||||
"onion": null,
|
||||
"create_time": "2023-12-29T18:50:36",
|
||||
"complete_time": "2023-12-29T19:05:00",
|
||||
"role": "relay",
|
||||
"country": "germany",
|
||||
"cidr": "80.228.207.0/24"
|
||||
},
|
||||
{
|
||||
"setting_id": 4,
|
||||
"traffic_collect_task_id": null,
|
||||
"id": 4,
|
||||
"ip": "116.202.169.64",
|
||||
"pod_name": "target-1-relay-4-557dcdfbd7-4lx5p",
|
||||
"nick_name": "relayslwwaewlzs",
|
||||
"status": "complete",
|
||||
"onion": null,
|
||||
"create_time": "2023-12-29T18:50:38",
|
||||
"complete_time": "2023-12-29T19:04:00",
|
||||
"role": "relay",
|
||||
"country": "germany",
|
||||
"cidr": "116.202.169.0/24"
|
||||
},
|
||||
{
|
||||
"setting_id": 6,
|
||||
"traffic_collect_task_id": null,
|
||||
"id": 6,
|
||||
"ip": "84.19.188.64",
|
||||
"pod_name": "target-1-relay-6-8559fc5858-j2v5m",
|
||||
"nick_name": "relayzvrsfipwmd",
|
||||
"status": "complete",
|
||||
"onion": null,
|
||||
"create_time": "2023-12-29T18:50:46",
|
||||
"complete_time": "2023-12-29T19:08:06",
|
||||
"role": "relay",
|
||||
"country": "germany",
|
||||
"cidr": "84.19.188.0/24"
|
||||
},
|
||||
{
|
||||
"setting_id": 7,
|
||||
"traffic_collect_task_id": null,
|
||||
"id": 7,
|
||||
"ip": "45.138.16.0",
|
||||
"pod_name": "target-1-exit-7-78984bf859-2hmnc",
|
||||
"nick_name": "exitpzidrsshzi",
|
||||
"status": "complete",
|
||||
"onion": null,
|
||||
"create_time": "2023-12-29T18:50:48",
|
||||
"complete_time": "2023-12-29T19:07:12",
|
||||
"role": "exit",
|
||||
"country": "netherlands",
|
||||
"cidr": "45.138.16.0/24"
|
||||
},
|
||||
{
|
||||
"setting_id": 8,
|
||||
"traffic_collect_task_id": null,
|
||||
"id": 8,
|
||||
"ip": "77.91.68.64",
|
||||
"pod_name": "target-1-exit-8-865cf99948-gw7br",
|
||||
"nick_name": "exitgqnnvqsoux",
|
||||
"status": "complete",
|
||||
"onion": null,
|
||||
"create_time": "2023-12-29T18:50:49",
|
||||
"complete_time": "2023-12-29T19:04:56",
|
||||
"role": "exit",
|
||||
"country": "russia",
|
||||
"cidr": "77.91.68.0/24"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
export { getTargetsResponse }
|
||||
@@ -1,242 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>echarts世界地图闪烁点 www.bootstrapmb.com</title>
|
||||
<script type="text/javascript" src="echarts.min.js"></script>
|
||||
<script type="text/javascript" src="world_new.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="main" style="width: 100%;height:120vh;"></div>
|
||||
|
||||
<script>
|
||||
// 获取echarts的容器
|
||||
var chart = echarts.init(document.getElementById("main"));
|
||||
var geoCoordMap = {
|
||||
北京: [116.28, 39.54],
|
||||
// 杭州: [120.10, 30.15],
|
||||
// 南宁: [108.479, 23.1152],
|
||||
// 广州: [113.5107, 23.2196],
|
||||
重庆: [107.7539, 30.1904],
|
||||
上海: [121.4648, 31.2891],
|
||||
尼日利亚: [-4.388361, 11.186148],
|
||||
洛杉矶: [-118.24311, 34.052713],
|
||||
香港: [114.195466, 22.282751],
|
||||
芝加哥: [-87.801833, 41.870975],
|
||||
加纳库马西: [-4.62829, 7.72415],
|
||||
曼彻斯特: [-1.657222, 51.886863],
|
||||
汉堡: [10.01959, 54.38474],
|
||||
阿拉木图: [45.326912, 41.101891],
|
||||
伊尔库茨克: [89.116876, 67.757906],
|
||||
巴西: [-48.678945, -10.493623],
|
||||
埃及: [31.815593, 31.418032],
|
||||
巴塞罗纳: [2.175129, 41.385064],
|
||||
柬埔寨: [104.88659, 11.545469],
|
||||
米兰: [9.189948, 45.46623],
|
||||
蒙得维的亚: [-56.162231, -34.901113],
|
||||
莫桑比克: [32.608571, -25.893473],
|
||||
阿尔及尔: [3.054275, 36.753027],
|
||||
阿联酋迪拜: [55.269441, 25.204514],
|
||||
布达佩斯: [17.108519, 48.179162],
|
||||
悉尼: [150.993137, -33.675509],
|
||||
加州: [-121.910642, 41.38028],
|
||||
墨尔本: [144.999416, -37.781726],
|
||||
墨西哥: [-99.094092, 19.365711],
|
||||
温哥华: [-123.023921, 49.311753]
|
||||
};
|
||||
var BJData = [[{
|
||||
name: "北京",
|
||||
value: 12580
|
||||
}],[{
|
||||
name: "重庆",
|
||||
value: 10000000
|
||||
}],[{
|
||||
name: "上海",
|
||||
value: 9100
|
||||
}], [{
|
||||
name: "尼日利亚",
|
||||
value: 9100
|
||||
}], [{
|
||||
name: "洛杉矶",
|
||||
value: 2370
|
||||
}], [{
|
||||
name: "香港",
|
||||
value: 3130
|
||||
}], [{
|
||||
name: "芝加哥",
|
||||
value: 2350
|
||||
}], [{
|
||||
name: "加纳库马西",
|
||||
value: 5120
|
||||
}], [{
|
||||
name: "曼彻斯特",
|
||||
value: 3110
|
||||
}], [{
|
||||
name: "汉堡",
|
||||
value: 6280
|
||||
}], [{
|
||||
name: "阿拉木图",
|
||||
value: 7255
|
||||
}], [{
|
||||
name: "伊尔库茨克",
|
||||
value: 8125
|
||||
}], [{
|
||||
name: "巴西",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "埃及",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "巴塞罗纳",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "柬埔寨",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "米兰",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "蒙得维的亚",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "莫桑比克",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "阿尔及尔",
|
||||
value: 31590
|
||||
}], [{
|
||||
name: "阿联酋迪拜",
|
||||
value: 13590
|
||||
}], [{
|
||||
name: "布达佩斯",
|
||||
value: 23590
|
||||
}], [{
|
||||
name: "悉尼",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "加州",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "墨尔本",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "墨西哥",
|
||||
value: 3590
|
||||
}], [{
|
||||
name: "温哥华",
|
||||
value: 3590
|
||||
}]];
|
||||
|
||||
var series = [];
|
||||
[[, BJData]].forEach(function (item, i) {
|
||||
series.push({
|
||||
type: "effectScatter",
|
||||
coordinateSystem: "geo",
|
||||
zlevel: 2,
|
||||
rippleEffect: {
|
||||
//涟漪特效
|
||||
period: 4, //动画时间,值越小速度越快
|
||||
brushType: "stroke", //波纹绘制方式 stroke, fill
|
||||
scale: 4
|
||||
//波纹圆环最大限制,值越大波纹越大
|
||||
},
|
||||
label: {
|
||||
normal: {
|
||||
show: true,
|
||||
position: "right", //显示位置
|
||||
offset: [5, 0], //偏移设置
|
||||
formatter: "{b}" //圆环显示文字
|
||||
},
|
||||
emphasis: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
symbol: "circle",
|
||||
symbolSize: function (val) {
|
||||
var level = 0 ;
|
||||
var state= Math.floor(val[2]/5000) ;
|
||||
switch (state)
|
||||
{
|
||||
case 0: level=0; break;
|
||||
case 1: level=1; break;
|
||||
case 2: level=2; break;
|
||||
case 3: level=3; break;
|
||||
case 4: level=4; break;
|
||||
case 5: level=5; break;
|
||||
case 6: level=6; break;
|
||||
case 7: level=7; break;
|
||||
case 8: level=8; break;
|
||||
case 9: level=9; break;
|
||||
default: level=10;
|
||||
}
|
||||
return 5 + level; //圆环大小
|
||||
},
|
||||
data: item[1].map(function (dataItem) {
|
||||
return {
|
||||
name: dataItem[0].name/*+"\n"+dataItem[0].value*/,
|
||||
value: geoCoordMap[dataItem[0].name]
|
||||
.concat([dataItem[0].value])
|
||||
};
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
option = {
|
||||
backgroundColor: '#000',
|
||||
//悬浮提示
|
||||
tooltip: {
|
||||
trigger: "item",
|
||||
backgroundColor: "#1540a1",
|
||||
borderColor: "#FFFFCC",
|
||||
showDelay: 0,
|
||||
hideDelay: 0,
|
||||
// enterable: true,
|
||||
transitionDuration: 0,
|
||||
// extraCssText: "z-index:100",formatter
|
||||
formatter: function (params, ticket, callback) {
|
||||
//根据业务自己拓展要显示的内容
|
||||
var res = "";
|
||||
var name = params.name;
|
||||
var value = params.value[params.seriesIndex + 1];
|
||||
res = "<span style='color:#fff;'>" + name.toString().split(' ')[0]
|
||||
+ "</span><br/>爬虫:" + name.toString().split(' ')[1];
|
||||
return res;
|
||||
}
|
||||
},
|
||||
visualMap: {
|
||||
//图例值控制
|
||||
min: 0,
|
||||
max: 10000,
|
||||
text:['High','Low'],
|
||||
show: false,
|
||||
calculable: true,
|
||||
//color: ["#0bc7f3"],
|
||||
color: ['orangered','yellow','lightskyblue']
|
||||
},
|
||||
geo: {
|
||||
map: "world",
|
||||
label: {
|
||||
emphasis: {
|
||||
show: false
|
||||
}
|
||||
},
|
||||
roam: true, //是否允许缩放
|
||||
layoutCenter: ["50%", "50%"], //地图位置
|
||||
layoutSize: "180%",
|
||||
itemStyle: {
|
||||
normal: {
|
||||
color: ["#04284e"], //地图背景色
|
||||
//color: ['orangered','yellow','lightskyblue']
|
||||
borderColor: "#5bc1c9" //省市边界线
|
||||
},
|
||||
emphasis: {
|
||||
color: "rgba(37, 43, 61, .5)" //悬浮背景
|
||||
}
|
||||
}
|
||||
},
|
||||
series: series
|
||||
};
|
||||
chart.setOption(option);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user