feat:外部打开Terminal (80%)
This commit is contained in:
56
nezha-fronted/src/assets/css/animate.css
vendored
56
nezha-fronted/src/assets/css/animate.css
vendored
@@ -950,15 +950,15 @@
|
||||
}
|
||||
@-webkit-keyframes backInRight {
|
||||
0% {
|
||||
-webkit-transform: translateX(2000px) scale(0.7);
|
||||
transform: translateX(2000px) scale(0.7);
|
||||
opacity: 0.7;
|
||||
-webkit-transform: translateX(2000px);
|
||||
transform: translateX(2000px);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
-webkit-transform: translateX(0px) scale(0.7);
|
||||
transform: translateX(0px) scale(0.7);
|
||||
opacity: 0.7;
|
||||
-webkit-transform: translateX(0px);
|
||||
transform: translateX(0px);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
@@ -969,15 +969,15 @@
|
||||
}
|
||||
@keyframes backInRight {
|
||||
0% {
|
||||
-webkit-transform: translateX(2000px) scale(0.7);
|
||||
transform: translateX(2000px) scale(0.7);
|
||||
opacity: 0.7;
|
||||
-webkit-transform: translateX(2000px);
|
||||
transform: translateX(2000px);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
80% {
|
||||
-webkit-transform: translateX(0px) scale(0.7);
|
||||
transform: translateX(0px) scale(0.7);
|
||||
opacity: 0.7;
|
||||
-webkit-transform: translateX(0px);
|
||||
transform: translateX(0px);
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
100% {
|
||||
@@ -1081,8 +1081,8 @@
|
||||
}
|
||||
|
||||
20% {
|
||||
-webkit-transform: translateX(0px) scale(0.7);
|
||||
transform: translateX(0px) scale(0.7);
|
||||
-webkit-transform: translateX(0px);
|
||||
transform: translateX(0px);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
@@ -1100,14 +1100,14 @@
|
||||
}
|
||||
|
||||
20% {
|
||||
-webkit-transform: translateX(0px) scale(0.7);
|
||||
transform: translateX(0px) scale(0.7);
|
||||
-webkit-transform: translateX(0px);
|
||||
transform: translateX(0px);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: translateX(-2000px) scale(0.7);
|
||||
transform: translateX(-2000px) scale(0.7);
|
||||
-webkit-transform: translateX(-2000px);
|
||||
transform: translateX(-2000px);
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
@@ -1123,15 +1123,13 @@
|
||||
}
|
||||
|
||||
20% {
|
||||
-webkit-transform: translateX(0px) scale(0.7);
|
||||
transform: translateX(0px) scale(0.7);
|
||||
opacity: 0.7;
|
||||
-webkit-transform: translateX(0px);
|
||||
transform: translateX(0px);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: translateX(2000px) scale(0.7);
|
||||
transform: translateX(2000px) scale(0.7);
|
||||
opacity: 0.7;
|
||||
-webkit-transform: translateX(2000px);
|
||||
transform: translateX(2000px);
|
||||
}
|
||||
}
|
||||
@keyframes backOutRight {
|
||||
@@ -1142,15 +1140,13 @@
|
||||
}
|
||||
|
||||
20% {
|
||||
-webkit-transform: translateX(0px) scale(0.7);
|
||||
transform: translateX(0px) scale(0.7);
|
||||
opacity: 0.7;
|
||||
-webkit-transform: translateX(0px);
|
||||
transform: translateX(0px);
|
||||
}
|
||||
|
||||
100% {
|
||||
-webkit-transform: translateX(2000px) scale(0.7);
|
||||
transform: translateX(2000px) scale(0.7);
|
||||
opacity: 0.7;
|
||||
-webkit-transform: translateX(2000px);
|
||||
transform: translateX(2000px);
|
||||
}
|
||||
}
|
||||
.animate__backOutRight {
|
||||
|
||||
@@ -1,26 +1,62 @@
|
||||
.fileDirectory {
|
||||
height: 80%;
|
||||
width: 45%;
|
||||
min-width: 700px;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
background: #1E1E1E;
|
||||
box-shadow: 5px 0 3px 0 #5E5E5E;
|
||||
width: 100% !important;
|
||||
top: -5px;
|
||||
height: calc(100% + 30px);
|
||||
right: -15px;
|
||||
background: $--background-color-empty;
|
||||
box-shadow: 5px 0 3px 0 $--explore-border-color-bottom;
|
||||
z-index: 10;
|
||||
font-size: 14px;
|
||||
.file-directory-header{
|
||||
display: flex;
|
||||
font-size: 14px;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
padding: 0 10px;
|
||||
background: #1E1E1E;
|
||||
padding: 0 20px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
background: $--background-color-empty;
|
||||
border-bottom: 1px solid rgba(51,51,51,0.10);
|
||||
box-sizing: border-box;
|
||||
color: #ffffff;
|
||||
color:$--color-text-regular;
|
||||
.header-option{
|
||||
>i {
|
||||
margin-right: 22px;
|
||||
}
|
||||
.nz-icon-close {
|
||||
margin-right: 15px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.file-directory-path{
|
||||
display: flex;
|
||||
width: 100%;
|
||||
justify-content: space-between;
|
||||
padding: 0 20px;
|
||||
background: $--background-color-empty;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
box-sizing: border-box;
|
||||
color:$--color-text-regular;
|
||||
.breadcrumb-box{
|
||||
.nz-icon-edit{
|
||||
margin-left: 15px;
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.breadcrumb-box:hover {
|
||||
.nz-icon-edit{
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.nz-icon-edit:hover{
|
||||
color: $--color-primary;
|
||||
cursor: pointer;
|
||||
}
|
||||
.breadcrumb-item{
|
||||
color: #ffffff
|
||||
color:$--color-text-regular
|
||||
}
|
||||
.breadcrumb-action{
|
||||
cursor: pointer;
|
||||
@@ -28,9 +64,17 @@
|
||||
.breadcrumb-action:hover{
|
||||
color: $--color-primary;
|
||||
}
|
||||
.path-option{
|
||||
display: inline-block;
|
||||
align-items: center;
|
||||
.nz-icon-a-newfolder{
|
||||
margin-left: 20px;
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.file-directory-content{
|
||||
height: calc(100% - 26px);
|
||||
height: calc(100% - 60px);
|
||||
width: calc(100% - 15px);
|
||||
overflow: auto;
|
||||
}
|
||||
@@ -38,61 +82,74 @@
|
||||
width: 6px;
|
||||
}
|
||||
.file-directory-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(244,244,244,0.16);
|
||||
border-radius: 4px;
|
||||
border:none
|
||||
width: 6px;
|
||||
}
|
||||
.file-directory-content::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(244,244,244,0.16);
|
||||
border-radius: 4px;
|
||||
border:none;
|
||||
.file-feature{
|
||||
display: none;
|
||||
width: 6px;
|
||||
}
|
||||
.directory-content-header{
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
background: $--background-color-2;
|
||||
width: calc(100% - 22px);
|
||||
color: $--color-text-regular;
|
||||
> div{text-transform:capitalize};
|
||||
}
|
||||
.file-name{
|
||||
width: 44%;
|
||||
box-sizing: border-box;
|
||||
padding-left: 10px;
|
||||
display: inline-block;
|
||||
}
|
||||
.file-size{
|
||||
width: 15%;
|
||||
display: inline-block;
|
||||
}
|
||||
.file-date{
|
||||
width: 25%;
|
||||
display: inline-block;
|
||||
}
|
||||
.file-opt{
|
||||
width: 14%;
|
||||
display: inline-block;
|
||||
.nz-icon-shuxing{
|
||||
margin-right: 20px;
|
||||
}
|
||||
}
|
||||
.file-item{
|
||||
font-family: Roboto-Regular;
|
||||
font-size: 14px;
|
||||
color: #B7B7B7;
|
||||
line-height: 21px;
|
||||
color: $--color-text-regular;
|
||||
font-weight: 400;
|
||||
margin-top: 8px;
|
||||
}
|
||||
.file-item{
|
||||
display: flex;
|
||||
padding: 0 10px;
|
||||
.file-name{
|
||||
width: calc(100% - 300px);
|
||||
flex: 1;
|
||||
}
|
||||
.file-feature{
|
||||
width: 100px;
|
||||
flex-shrink: 1;
|
||||
opacity: 0;
|
||||
>.nz-icon-download1 {
|
||||
margin-right: 24px;
|
||||
}
|
||||
}
|
||||
.file-date {
|
||||
width: 260px;
|
||||
flex-shrink: 1;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
line-height: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
.file-item:hover{
|
||||
background: rgba(255,134,0,0.50);
|
||||
font-family: Roboto-Regular;
|
||||
font-size: 14px;
|
||||
color: #FF9230;
|
||||
line-height: 21px;
|
||||
font-weight: 400;
|
||||
.file-feature{
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
.my-loading-box{
|
||||
background: #1a1a1a;
|
||||
}
|
||||
.nz-icon:hover{
|
||||
color: $--color-primary;
|
||||
}
|
||||
.file-info-item-header{
|
||||
padding-bottom: 20px; border-bottom: 1px solid $--border-color-light;
|
||||
}
|
||||
.file-info-item{
|
||||
display: flex;
|
||||
margin-top: 10px;
|
||||
align-items: center;
|
||||
|
||||
.file-info-item-left{
|
||||
width: 180px;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.file-info-item-right{
|
||||
width: 200px;
|
||||
flex-shrink: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,46 +3,48 @@
|
||||
z-index: 10;
|
||||
right:0;
|
||||
top:-100px;
|
||||
max-height: 194px;
|
||||
max-height: 246px;
|
||||
min-width: 96px;
|
||||
width: 308px;
|
||||
background: #222329;
|
||||
box-shadow: 1px 1px 4px -1px #1E1E1E;
|
||||
background: $--background-color-empty;
|
||||
box-shadow: -2px 1px 4px 0 rgba(0,0,0,0.06), 1px 1px 4px -1px rgba(0,0,0,0.16);
|
||||
border-radius: 2px;
|
||||
.file-state-panel-content::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
}
|
||||
.file-state-panel-content::-webkit-scrollbar-thumb {
|
||||
background: rgba(244,244,244,0.16);
|
||||
background: rgba(0,0,0,0.16);
|
||||
border-radius: 4px;
|
||||
border:none
|
||||
}
|
||||
.file-state-panel-content::-webkit-scrollbar-thumb:hover {
|
||||
background: rgba(244,244,244,0.16);
|
||||
background: rgba(0,0,0,0.16);
|
||||
border-radius: 4px;
|
||||
border:none
|
||||
}
|
||||
.file-state-panel-header{
|
||||
height: 48px;
|
||||
background: #222329;
|
||||
box-shadow: 0 1px 0 0 #19191C;
|
||||
background: $--background-color-empty;
|
||||
box-shadow: 0 1px 0 0 $--background-color-disabled;
|
||||
padding: 0 20px;
|
||||
line-height: 48px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
text-transform: capitalize;
|
||||
> i {
|
||||
color: #ffffff;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
}
|
||||
.file-state-panel-title{
|
||||
font-size: 14px;
|
||||
color: #E7EAED;
|
||||
letter-spacing: 0;
|
||||
font-weight: 500;
|
||||
text-transform: capitalize;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
.file-state-panel-content {
|
||||
min-height: 50px;
|
||||
max-height: 146px;
|
||||
max-height: 180px;
|
||||
line-height: 48px;
|
||||
padding-bottom: 15px;
|
||||
border-radius: 0 0 2px 2px;
|
||||
@@ -59,9 +61,9 @@
|
||||
.item-icon{
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: #19191C;
|
||||
background: $--background-color-base;
|
||||
border-radius: 2px;
|
||||
color: #fff;
|
||||
color: $--color-text-regular;
|
||||
display: flex;
|
||||
justify-items: center;
|
||||
justify-content: center;
|
||||
@@ -76,21 +78,21 @@
|
||||
flex-direction: column;
|
||||
.item-progress-top{
|
||||
width: 100%;
|
||||
color: #fff;
|
||||
color: $--color-text-regular;
|
||||
height: 15px;
|
||||
line-height: 15px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
margin-bottom: 3px;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.item-progress-middle{
|
||||
}
|
||||
.item-progress-bottom{
|
||||
margin-top: 3px;
|
||||
margin-top: 4px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
font-size: 10px;
|
||||
color: #B7B7B7;
|
||||
color: $--color-text-secondary;
|
||||
line-height: 12px;
|
||||
font-weight: 400;
|
||||
}
|
||||
@@ -99,7 +101,7 @@
|
||||
flex-shrink:0;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
color: #fff;
|
||||
color: $--color-text-regular;
|
||||
line-height: 28px;
|
||||
text-align: center;
|
||||
}
|
||||
@@ -122,6 +124,6 @@
|
||||
transform-origin: 70% 0%
|
||||
}
|
||||
.el-progress-bar__outer{
|
||||
background-color: #19191C;
|
||||
background-color: $--el-progress-bar__outer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,6 +253,38 @@
|
||||
.popper__arrow{
|
||||
display: none;
|
||||
}
|
||||
.webshell-box-top{
|
||||
border-bottom: 1px solid rgba(25,25,28,0.10);
|
||||
}
|
||||
.webshell-box-bottom{
|
||||
border-top: 1px solid rgba(25,25,28,0.10);
|
||||
}
|
||||
.webshell-box-item{
|
||||
height: 32px;
|
||||
font-size: 14px;
|
||||
color: $--color-text-primary;
|
||||
letter-spacing: 0;
|
||||
line-height: 32px;
|
||||
.nz-icon {
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
.webshell-box-item:hover{
|
||||
background: $--background-color-base;
|
||||
color: $--color-primary;
|
||||
}
|
||||
|
||||
}
|
||||
.popover-webshell-new{
|
||||
padding: 0 !important;
|
||||
.webshell-box-top, .webshell-box-bottom{
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.webshell-box-top, .webshell-box-bottom,.webshell-box-item{
|
||||
padding-left: 15px;
|
||||
}
|
||||
}
|
||||
div.sp-header{
|
||||
display: none;
|
||||
|
||||
147
nezha-fronted/src/assets/css/components/cli/webSSHNew.scss
Normal file
147
nezha-fronted/src/assets/css/components/cli/webSSHNew.scss
Normal file
@@ -0,0 +1,147 @@
|
||||
.web-terminal-new{
|
||||
height: calc(100vh - 68px);
|
||||
background: $--background-color-base;
|
||||
/*border-top: 1px solid #BEBEBE;*/
|
||||
box-shadow: 0 1px 0 0 $--border-color-light;
|
||||
/deep/ .el-tabs{
|
||||
border: none;
|
||||
height: 100%;
|
||||
width:100%;
|
||||
margin-left:0px;
|
||||
.el-tabs__nav-prev, .el-tabs__nav-next{
|
||||
display: inline-block;
|
||||
background: $--background-color-2 !important;
|
||||
width: 20px;
|
||||
height: 36px;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
.el-tabs__nav-prev.is-disabled, .el-tabs__nav-next.is-disabled{
|
||||
cursor: pointer !important;
|
||||
display: inline-block;
|
||||
background: $--background-color-2 !important;
|
||||
width: 20px;
|
||||
height: 36px;
|
||||
text-align: center;
|
||||
line-height: 40px;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
.el-tabs__header{
|
||||
height: 36px;
|
||||
width: 100%;
|
||||
.el-tabs__item{
|
||||
height: 36px;
|
||||
transform: translateY(-2px);
|
||||
padding: 0 10px 0 10px;
|
||||
margin-top: 0;
|
||||
border-top: 2px solid transparent;
|
||||
width: 126px;
|
||||
background: $--background-color-empty;
|
||||
line-height: 34px;
|
||||
display: inline-block;
|
||||
box-sizing: border-box;
|
||||
.el-tabs__item-label{
|
||||
display: inline-flex;
|
||||
width: calc(100% - 20px);
|
||||
align-items: center;
|
||||
.active-icon{
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.el-tabs__item-label-name{
|
||||
flex: 1;
|
||||
width: calc(100% - 30px);
|
||||
}
|
||||
}
|
||||
.el-icon-close{
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.el-tabs__item.is-active {
|
||||
width: 200px;
|
||||
border-top: 2px solid $--color-primary;
|
||||
font-size: 14px;
|
||||
color: $--color-text-primary;
|
||||
}
|
||||
.icon-reference{
|
||||
display: none;
|
||||
}
|
||||
.el-tabs__item:hover{
|
||||
width: 200px;
|
||||
.icon-reference{
|
||||
display: inline-block;
|
||||
}
|
||||
.el-icon-close{
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
.el-tabs__item:last-of-type {
|
||||
height: 36px;
|
||||
padding: 0 5px;
|
||||
border: none;
|
||||
border-top: 2px solid transparent;
|
||||
background: transparent;
|
||||
width: 40px;
|
||||
line-height: 34px;
|
||||
}
|
||||
}
|
||||
.el-tabs__content{
|
||||
height: calc(100% - 36px);
|
||||
background: #000;
|
||||
box-sizing: border-box;
|
||||
overflow: unset;
|
||||
padding: 5px 5px;
|
||||
.el-tab-pane{
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.webTerminal{
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
.web-terminal-header{
|
||||
height: 36px;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
box-sizing: border-box;
|
||||
padding: 0 15px;
|
||||
background: $--background-color-base;
|
||||
box-shadow: inset 0 -1px 0 0 $--dropdown-menu-box-shadow-color;
|
||||
font-size: 14px;
|
||||
line-height: 36px;
|
||||
color: $--color-text-primary;
|
||||
img {
|
||||
vertical-align: sub;
|
||||
height: 20px;
|
||||
width: 20px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
.right-tip {
|
||||
position: absolute;
|
||||
left: 8px;
|
||||
top: 4px;
|
||||
padding: 0 6px;
|
||||
line-height: 15px;
|
||||
height: 15px;
|
||||
background-color: #ba3939;
|
||||
opacity: .9;
|
||||
border-radius: 7px;
|
||||
color: white;
|
||||
font-size: 6px;
|
||||
}
|
||||
.shell-input{
|
||||
input {
|
||||
background: #1E1E1E !important;
|
||||
border: none;
|
||||
}
|
||||
input::input-placeholder{
|
||||
color: #7C7C7C;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
@import './cli/webSSH.scss';
|
||||
@import './cli/fileDirectory.scss';
|
||||
@import './cli/fileListState.scss';
|
||||
@import './cli/webSSHNew.scss';
|
||||
@import './common/alert/alertLabel.scss';
|
||||
@import './common/alert/alertStateInfo.scss';
|
||||
@import './common/alert/alertRuleInfo.scss';
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -5,6 +5,97 @@
|
||||
"css_prefix_text": "nz-icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "680988",
|
||||
"name": "属性",
|
||||
"font_class": "shuxing",
|
||||
"unicode": "e785",
|
||||
"unicode_decimal": 59269
|
||||
},
|
||||
{
|
||||
"icon_id": "33167519",
|
||||
"name": "套接字文件",
|
||||
"font_class": "taojieziwenjian",
|
||||
"unicode": "e7bb",
|
||||
"unicode_decimal": 59323
|
||||
},
|
||||
{
|
||||
"icon_id": "33166559",
|
||||
"name": "目录链接",
|
||||
"font_class": "mululianjie",
|
||||
"unicode": "e7b9",
|
||||
"unicode_decimal": 59321
|
||||
},
|
||||
{
|
||||
"icon_id": "33165863",
|
||||
"name": "套接字文件链接",
|
||||
"font_class": "taojieziwenjianlianjie",
|
||||
"unicode": "e7ba",
|
||||
"unicode_decimal": 59322
|
||||
},
|
||||
{
|
||||
"icon_id": "33158668",
|
||||
"name": "文件链接",
|
||||
"font_class": "wenjianlianjie",
|
||||
"unicode": "e7b5",
|
||||
"unicode_decimal": 59317
|
||||
},
|
||||
{
|
||||
"icon_id": "33158669",
|
||||
"name": "管道文件链接",
|
||||
"font_class": "guandaowenjianlianjie",
|
||||
"unicode": "e7b6",
|
||||
"unicode_decimal": 59318
|
||||
},
|
||||
{
|
||||
"icon_id": "33158670",
|
||||
"name": "块设备文件链接",
|
||||
"font_class": "kuaishebeiwenjianlianjie",
|
||||
"unicode": "e7b7",
|
||||
"unicode_decimal": 59319
|
||||
},
|
||||
{
|
||||
"icon_id": "33158671",
|
||||
"name": "字符串设备文件链接",
|
||||
"font_class": "zifuchuanshebeiwenjianlianjie",
|
||||
"unicode": "e7b8",
|
||||
"unicode_decimal": 59320
|
||||
},
|
||||
{
|
||||
"icon_id": "33158636",
|
||||
"name": "管道文件",
|
||||
"font_class": "guandaowenjian",
|
||||
"unicode": "e7b1",
|
||||
"unicode_decimal": 59313
|
||||
},
|
||||
{
|
||||
"icon_id": "33158637",
|
||||
"name": "链接文件",
|
||||
"font_class": "lianjiewenjian",
|
||||
"unicode": "e7b2",
|
||||
"unicode_decimal": 59314
|
||||
},
|
||||
{
|
||||
"icon_id": "33158639",
|
||||
"name": "块设备文件",
|
||||
"font_class": "kuaishebeiwenjian",
|
||||
"unicode": "e7b3",
|
||||
"unicode_decimal": 59315
|
||||
},
|
||||
{
|
||||
"icon_id": "33158641",
|
||||
"name": "字符设备文件",
|
||||
"font_class": "zifushebeiwenjian",
|
||||
"unicode": "e7b4",
|
||||
"unicode_decimal": 59316
|
||||
},
|
||||
{
|
||||
"icon_id": "33112141",
|
||||
"name": "home",
|
||||
"font_class": "home",
|
||||
"unicode": "e7af",
|
||||
"unicode_decimal": 59311
|
||||
},
|
||||
{
|
||||
"icon_id": "32964483",
|
||||
"name": "File",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -259,7 +259,8 @@ $--tooltip-background-color: #222329;
|
||||
$--tooltip-border-color: rgba(112,116,122,0.6);
|
||||
/* 17.label*/
|
||||
$--label-background-color: $--background-color-empty;
|
||||
|
||||
/* 18 进度条颜色 */
|
||||
$--el-progress-bar__outer: #19191C;
|
||||
/*** themes/common.scss是与主题切换无关的变量 ***/
|
||||
@import './src/common/var.scss';
|
||||
@import './common.scss';
|
||||
|
||||
@@ -254,7 +254,8 @@ $--tooltip-background-color: #ffffff;
|
||||
$--tooltip-border-color: rgba(119,131,145,0.6);
|
||||
/* 17.label*/
|
||||
$--label-background-color: #D8d8d8;
|
||||
|
||||
/* 18 进度条颜色 */
|
||||
$--el-progress-bar__outer: #ebeef5;
|
||||
/*** themes/common.scss是与主题切换无关的变量 ***/
|
||||
@import './src/common/var.scss';
|
||||
@import './common.scss';
|
||||
|
||||
@@ -699,7 +699,8 @@ const unitOptions = [
|
||||
}
|
||||
]
|
||||
const units = []
|
||||
window.onload = function () {
|
||||
window.addEventListener('load', function () {
|
||||
console.log(13213)
|
||||
if (units.length < 1) {
|
||||
unitOptions.forEach((item, index) => {
|
||||
item.children.forEach((n, i) => {
|
||||
@@ -707,8 +708,7 @@ window.onload = function () {
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
export default {
|
||||
unitOptions: function () {
|
||||
return unitOptions
|
||||
|
||||
323
nezha-fronted/src/components/cli/consoleNew.vue
Normal file
323
nezha-fronted/src/components/cli/consoleNew.vue
Normal file
@@ -0,0 +1,323 @@
|
||||
<style scoped>
|
||||
.console{
|
||||
height: 100%;
|
||||
padding:5px 5px;
|
||||
background-color: black;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
<template>
|
||||
<div :id="'ternimalContainer'+idIndex" class="console">
|
||||
<div :id="'terminal'+idIndex" style="height: 100%"></div>
|
||||
<fileDirectory :host="host" v-clickoutside="closeFileDir" :uuid="terminal.uuid" v-show="fileDirectoryShow" @close="showFileDir(false)" :fileDirectoryShow="fileDirectoryShow" ref="fileDirectory"/>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import Terminal from '../common/js/Xterm'
|
||||
import fileDirectory from './fileDirectory'
|
||||
export default {
|
||||
name: 'console',
|
||||
components: {
|
||||
fileDirectory
|
||||
},
|
||||
props: {
|
||||
terminal: { },
|
||||
terminalType: { default: '1' },
|
||||
idIndex: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
isFullScreen: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
fontSize: {}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
term: null,
|
||||
terminalSocket: null,
|
||||
termimalRows: 15,
|
||||
termimalHeight: 270,
|
||||
topMenuHeight: 30,
|
||||
dragHeigh: 8,
|
||||
minRow: 1,
|
||||
fontSpaceHeight: 2, // 一行高度=fontSize+fontSpaceHeight
|
||||
obj: {
|
||||
id: 2
|
||||
},
|
||||
isInit: false,
|
||||
successBackContent: 'Connecting to',
|
||||
failBackContent: 'Sorry',
|
||||
connectFailContent: 'Connection failed',
|
||||
welcomeBackContent: 'Welcome',
|
||||
psdCont: 'password: ',
|
||||
conFinish: false,
|
||||
conSuccessNum: 0,
|
||||
inputSecret: false,
|
||||
userName: '',
|
||||
fileDirectoryShow: false,
|
||||
host: ''
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
methods: {
|
||||
prompt (term) {
|
||||
term.write('\r\n ')// term.write('\r\n~$ ');
|
||||
},
|
||||
resize (consoleHeigt, consoleWidth) {
|
||||
this.term.fit()
|
||||
this.resizeServiceConsole()
|
||||
},
|
||||
resizeServiceConsole () {
|
||||
const consoleBox = document.getElementById('ternimalContainer' + this.idIndex)
|
||||
const width = document.body.clientWidth// 可视宽度
|
||||
const height = parseInt(consoleBox.offsetHeight)
|
||||
const winStyle = {
|
||||
width: width,
|
||||
height: height - 100,
|
||||
cols: this.term.cols, // cols和rows在resizeConsole方法已经设置
|
||||
rows: this.term.rows
|
||||
}
|
||||
this.$post('terminal/resize', winStyle).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.term.fit()
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
focusConsole () {
|
||||
this.term.focus()
|
||||
},
|
||||
beforeCreate () {
|
||||
let rows = this.termimalRows
|
||||
// const consoleBox = document.getElementById('ternimalContainer' + this.idIndex)
|
||||
const height = document.body.clientHeight// 高度
|
||||
// consoleBox.style.height = `${height - 120}px`
|
||||
rows = (height - this.topMenuHeight) / (this.fontSize + this.fontSpaceHeight)
|
||||
rows = parseInt(rows)
|
||||
const terminalContainer = document.getElementById('terminal' + this.idIndex)
|
||||
this.term = new Terminal({
|
||||
rows: rows, // 15行大概300px高,无法设置heigh,只能设置rows
|
||||
cursorStyle: 'block', // 光标样式 null | 'block' | 'underline' | 'bar'
|
||||
disableStdin: false, // 是否应禁用输入
|
||||
fontSize: this.fontSize
|
||||
})
|
||||
this.term.open(terminalContainer)
|
||||
this.term.focus()
|
||||
const params = {
|
||||
width: this.terminal.width,
|
||||
height: this.terminal.height,
|
||||
cols: this.terminal.cols,
|
||||
rows: this.terminal.rows,
|
||||
host: this.$loadsh.get(this.terminal, 'custom.host', ''),
|
||||
port: this.$loadsh.get(this.terminal, 'custom.port', ''),
|
||||
assetId: this.$loadsh.get(this.terminal, 'assetId', ''),
|
||||
accountId: this.$loadsh.get(this.terminal, 'accountId', ''),
|
||||
authProtocol: this.$loadsh.get(this.terminal, 'custom.authProtocol', ''),
|
||||
authProtocolPort: this.$loadsh.get(this.terminal, 'custom.authProtocolPort', ''),
|
||||
authType: this.$loadsh.get(this.terminal, 'custom.authType', ''),
|
||||
authUsername: this.$loadsh.get(this.terminal, 'custom.authUsername', ''),
|
||||
authPin: this.$loadsh.get(this.terminal, 'custom.authPin', ''),
|
||||
authPriKey: this.$loadsh.get(this.terminal, 'custom.authPriKey', ''),
|
||||
authUserTip: this.$loadsh.get(this.terminal, 'custom.authUserTip', ''),
|
||||
authPinTip: this.$loadsh.get(this.terminal, 'custom.authPinTip', '')
|
||||
}
|
||||
this.$post('/terminal/login', params).then(res => {
|
||||
console.log(res)
|
||||
if (res.code == 200) {
|
||||
this.terminal.uuid = res.data.uuid
|
||||
this.$forceUpdate()
|
||||
this.terminal.userName = res.data.authUsername + '@' + res.data.host
|
||||
this.host = res.data.host
|
||||
this.create()
|
||||
} else {
|
||||
this.terminal.uuid = res.data.uuid
|
||||
this.terminal.userName = res.data.authUsername + '@' + res.data.host
|
||||
this.host = res.data.host
|
||||
params.name = this.terminal.name
|
||||
this.$emit('loginFail', params)
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
})
|
||||
},
|
||||
create () {
|
||||
const that = this
|
||||
const token = localStorage.getItem('nz-token')
|
||||
let baseUrl = JSON.parse(JSON.stringify(this.$axios.defaults.baseURL))
|
||||
const protocol = window.location.protocol.indexOf('https') > -1 ? 'wss' : 'ws'
|
||||
if (baseUrl.startsWith('/')) {
|
||||
baseUrl = `${protocol}://` + window.location.host + baseUrl
|
||||
} else {
|
||||
baseUrl = baseUrl.replace('http://', 'ws://').replace('https://', 'wss://')
|
||||
}
|
||||
let url = ''
|
||||
this.terminal.height = document.body.clientHeight - 100
|
||||
if (this.terminal.type === 'asset') {
|
||||
url = baseUrl + '/terminal.ws?width=' + this.terminal.width + '&height=' + this.terminal.height + '&cols=' + this.terminal.cols + '&rows=' + this.terminal.rows + '&token=' + token + '&assetId=' + this.terminal.assetId + '&accountId=' + this.terminal.accountId + '&uuid=' + this.terminal.uuid
|
||||
} else if (this.terminal.type === 'custom') {
|
||||
url = baseUrl + '/terminal.ws?width=' + this.terminal.width + '&height=' + this.terminal.height + '&cols=' + this.terminal.cols + '&rows=' + this.terminal.rows + '&token=' + token + '&accountId=' + this.terminal.accountId + '&uuid=' + this.terminal.uuid
|
||||
Object.keys(this.terminal.custom).forEach(key => {
|
||||
if (this.terminal.custom[key]) {
|
||||
url += '&' + key + '=' + this.terminal.custom[key]
|
||||
}
|
||||
})
|
||||
}
|
||||
this.terminalSocket = new WebSocket(url)
|
||||
// 连接成功onclose
|
||||
this.terminalSocket.onopen = () => {
|
||||
this.terminal.isLogin = true
|
||||
this.isInit = true
|
||||
}
|
||||
// 登录后,你输入的内容从后台服务返回
|
||||
this.term.on('data', function (data) {
|
||||
/*
|
||||
let code = data.charCodeAt(0);
|
||||
if(code==13){
|
||||
}else {
|
||||
//that.term.write(data);
|
||||
} */
|
||||
})
|
||||
// 返回
|
||||
this.terminalSocket.onmessage = function (evt) {
|
||||
let backContent = evt.data
|
||||
const welComIndex = backContent.indexOf(that.welcomeBackContent)
|
||||
if (welComIndex > -1) { // 无服务器信息(只与nezha进行了连接)
|
||||
const connectResult = {
|
||||
title: '',
|
||||
color: 1
|
||||
}
|
||||
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
|
||||
} else {
|
||||
const successContentIndex = backContent.indexOf(that.successBackContent)
|
||||
if (successContentIndex > -1) {
|
||||
// that.conFinish = true;
|
||||
const startIndex = successContentIndex + that.successBackContent.length + 1
|
||||
backContent = backContent.substring(startIndex)
|
||||
const endIndex = backContent.indexOf('\r\n')
|
||||
const title = backContent.substring(0, endIndex)
|
||||
const connectResult = {
|
||||
title: title,
|
||||
color: 2
|
||||
}
|
||||
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
|
||||
} else { // 失败
|
||||
const failContentIndex = backContent.indexOf(that.failBackContent)
|
||||
const connectFailIndex = backContent.indexOf(that.connectFailContent)
|
||||
if (failContentIndex > -1) {
|
||||
// that.conFinish = true;
|
||||
const connectResult = {
|
||||
title: '',
|
||||
color: 3
|
||||
}
|
||||
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
|
||||
} else if (connectFailIndex > -1) {
|
||||
const connectResult = {
|
||||
title: '',
|
||||
color: 3
|
||||
}
|
||||
that.$emit('refreshConsoleTitle', connectResult)// 1:grey 2 green 3 red
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// 关闭
|
||||
this.terminalSocket.onclose = () => {
|
||||
this.terminal.isLogin = false
|
||||
// 报错sorry的,还没来得及看信息就关闭
|
||||
// this.$emit("closeConsole",this.terminal.name);//
|
||||
this.term && this.term.setOption('disableStdin', true)
|
||||
}
|
||||
|
||||
// 错误
|
||||
this.terminalSocket.onerror = (e) => {
|
||||
this.terminal.isLogin = false
|
||||
console.log(this.terminal.isLogin, 'close')
|
||||
}
|
||||
// 选中 复制
|
||||
this.term.on('selection', function () {})
|
||||
this.term.attachCustomKeyEventHandler(function (ev) { })
|
||||
|
||||
this.term.attach(this.terminalSocket)
|
||||
this.term._initialized = true
|
||||
this.term.fit()// 自适应大小(使终端的尺寸和几何尺寸适合于终端容器的尺寸) 只是width
|
||||
this.$nextTick(() => { // 解决进入全屏和退出全屏是底部隐藏
|
||||
this.setFontSize(this.fontSize)
|
||||
})
|
||||
},
|
||||
|
||||
closeSocket () {
|
||||
if (this.terminalSocket) {
|
||||
this.terminalSocket.close()
|
||||
}
|
||||
if (this.term) {
|
||||
this.term.destroy()
|
||||
}
|
||||
// 初始化console的高度
|
||||
this.conFinish = false
|
||||
},
|
||||
setFontSize (fontSize) {
|
||||
this.term && this.term.setOption('fontSize', fontSize)
|
||||
const consoleBox = document.getElementById('ternimalContainer' + this.idIndex)
|
||||
const width = document.body.clientWidth// 可视宽度
|
||||
console.log(consoleBox.offsetHeight)
|
||||
let height = parseInt(consoleBox.offsetHeight)
|
||||
if (height == null || !height) { height = this.termimalHeight }
|
||||
const winStyle = {
|
||||
width: width,
|
||||
height: height,
|
||||
cols: this.term.cols, // cols和rows在resizeConsole方法已经设置
|
||||
rows: this.term.rows
|
||||
}
|
||||
// 调整终端可视区域高度
|
||||
// document.getElementsByClassName('xterm-screen')[this.idIndex].style.height = height - 30 + 'px'
|
||||
this.$nextTick(() => {
|
||||
this.term.resize(this.term.cols, this.term.rows)
|
||||
this.$post('terminal/resize', winStyle).then(response => {
|
||||
if (response.code === 200) {
|
||||
this.term.fit()
|
||||
} else {
|
||||
this.$message.error(response.msg)
|
||||
}
|
||||
})
|
||||
})
|
||||
},
|
||||
reconnect () {
|
||||
this.terminal.isLogin = false
|
||||
this.closeSocket()
|
||||
this.term.off('selection')
|
||||
this.term.off('data')
|
||||
this.beforeCreate()
|
||||
},
|
||||
closeFileDir () {
|
||||
this.showFileDir(false)
|
||||
},
|
||||
showFileDir (show) {
|
||||
if (JSON.stringify(show) == JSON.stringify(this.fileDirectoryShow)) {
|
||||
return
|
||||
}
|
||||
if (show) {
|
||||
this.fileDirectoryShow = show
|
||||
}
|
||||
let animationClass = ''
|
||||
animationClass = show ? 'backInRight' : 'backOutRight'
|
||||
this.animateCSS(this.$refs.fileDirectory.$el, animationClass).then((message) => {
|
||||
this.fileDirectoryShow = show
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.beforeCreate()
|
||||
},
|
||||
beforeDestroy () {
|
||||
this.closeSocket()
|
||||
this.term.off('selection')
|
||||
this.term.off('data')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -1,35 +1,99 @@
|
||||
<template>
|
||||
<div class="fileDirectory" style="width: 100% !important;">
|
||||
<div class="fileDirectory">
|
||||
<div class="file-directory-header">
|
||||
<span style="color: #fff">
|
||||
{{$t('terminal.sftp')}}
|
||||
<span style="color: #B7B7B7;margin-left: 10px">
|
||||
<span @click="getSftpPath('/')" class="breadcrumb-item breadcrumb-action"><i class="nz-icon nz-icon-Computer"/></span>
|
||||
<span v-for="(item,index) in breadcrumb" :key="index" class="breadcrumb-item">
|
||||
/<span class="breadcrumb-action" @click="gotoPath(item,index)">{{item}}</span>
|
||||
</span>
|
||||
</span>
|
||||
</span>
|
||||
<span style="color: #fff" class="header-option">
|
||||
<i class="nz-icon nz-icon-a-newfolder" @click="newFolderBoxShow = true"></i>
|
||||
<span class="header-option">
|
||||
<i class="nz-icon nz-icon-upload" @click="uploadFile"></i>
|
||||
<i class="nz-icon nz-icon-close" @click="$emit('close')"></i>
|
||||
</span>
|
||||
</div>
|
||||
<div class="file-directory-content" v-my-loading="fileDirectoryLoading">
|
||||
<div v-if="fileDirectory !== '/'" @click="backFileDirectory" class="file-item"><i class="nz-icon nz-icon-a-upperlevel" style="margin-right: 10px"/>{{$t('terminal.back')}}</div>
|
||||
<div v-for="(item,index) in fileList" :key="index" class="file-item" @click="selectFile(item)">
|
||||
<div class="file-directory-path" v-clickoutside="hideEditPath">
|
||||
<span v-show="!editPathShow" class="breadcrumb-box">
|
||||
<span @click="getSftpPath('/')" class="breadcrumb-item breadcrumb-action"><i class="nz-icon nz-icon-home"/></span>
|
||||
<span v-for="(item,index) in breadcrumb" :key="index" class="breadcrumb-item">
|
||||
/<span class="breadcrumb-action" @click="gotoPath(item,index)">{{item}}</span>
|
||||
</span>
|
||||
<i class="nz-icon nz-icon-edit" @click="showEditPath"/>
|
||||
</span>
|
||||
<span v-show="editPathShow">
|
||||
<el-input v-model="editPath" size="small" @keyup.enter.native="goEditPath"/>
|
||||
</span>
|
||||
<div class="path-option">
|
||||
<span style="margin-right: 5px">Show hide File: </span>
|
||||
<el-switch v-model="showHideFile"/>
|
||||
<i class="nz-icon nz-icon-a-newfolder" @click="newFolderBoxShow = true"></i>
|
||||
<i class="nz-icon nz-icon-upload" @click="uploadFile"></i>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div style="padding: 0 20px;height: calc(100% - 50px)" v-my-loading="fileDirectoryLoading">
|
||||
<div class="directory-content-header">
|
||||
<div class="text-ellipsis file-name">
|
||||
<i class="nz-icon" :class="selIcon(item)"/>
|
||||
{{item.name}}
|
||||
{{$t('overall.name')}}
|
||||
</div>
|
||||
<div class="file-feature" v-if="!item.isDir">
|
||||
<i class="nz-icon nz-icon-download1" v-if="!item.isDir" @click="downloadFile(item)"></i>
|
||||
<i class="nz-icon nz-icon-delete" v-if="!item.isDir" @click="delFile(item)"></i>
|
||||
<div class="text-ellipsis file-size">
|
||||
{{$t('backup.size')}}
|
||||
</div>
|
||||
<div class="file-date">
|
||||
<span>{{momentTz(item.cts * 1000)}}</span>
|
||||
<span v-if="!item.isDir">{{bytes(item.size, 0, 0)}}</span>
|
||||
<div class="text-ellipsis file-date">
|
||||
{{$t('issue.createTime')}}
|
||||
</div>
|
||||
<div class="text-ellipsis file-opt">
|
||||
{{$t('overall.option')}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="file-directory-content">
|
||||
<div v-if="fileDirectory !== '/'" @click="backFileDirectory" class="file-item">
|
||||
<div class="text-ellipsis file-name">
|
||||
<i class="nz-icon nz-icon-a-upperlevel" style="margin-right: 10px"/>
|
||||
{{$t('terminal.back')}}
|
||||
</div>
|
||||
</div>
|
||||
<div v-for="(item,index) in fileList" :key="index" class="file-item" @click="selectFile(item)">
|
||||
<div class="text-ellipsis file-name">
|
||||
<i class="nz-icon" :class="selIcon(item)"/>
|
||||
{{item.name}}
|
||||
</div>
|
||||
<div class="text-ellipsis file-size">
|
||||
<span v-if="!item.isDir">{{bytes(item.size, 0, 0)}}</span>
|
||||
</div>
|
||||
<div class="text-ellipsis file-date">
|
||||
<span>{{momentTz(item.cts * 1000)}}</span>
|
||||
</div>
|
||||
<div class="text-ellipsis file-opt">
|
||||
<i class="nz-icon nz-icon-shuxing" @click.stop="showFileInfo(item)"/>
|
||||
<el-dropdown size="medium" trigger="click" @command="tableOperation">
|
||||
<div class="table-operation-item table-operation-item--more" @click.stop="" :title="$t('overall.moreOperations')">
|
||||
<i class="nz-icon nz-icon-more3"></i>
|
||||
</div>
|
||||
<el-dropdown-menu slot="dropdown" class="right-box-select-top right-public-box-dropdown-top">
|
||||
<el-dropdown-item
|
||||
:command="['rename', item]">
|
||||
<i class="nz-icon nz-icon-edit"></i>
|
||||
<span class="operation-dropdown-text">
|
||||
{{$t('terminal.rename')}}
|
||||
</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:disabled="item.isDir"
|
||||
:command="['download', item]">
|
||||
<i class="nz-icon nz-icon-download1"></i>
|
||||
<span class="operation-dropdown-text">
|
||||
{{$t('overall.download')}}
|
||||
</span>
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item
|
||||
:disabled="item.isDir"
|
||||
:command="['del', item]">
|
||||
<i class="nz-icon nz-icon-delete"></i>
|
||||
<span class="operation-dropdown-text">
|
||||
{{$t('overall.delete')}}
|
||||
</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<!-- <i class="nz-icon nz-icon-download1" v-if="!item.isDir" @click="downloadFile(item)"></i>-->
|
||||
<!-- <i class="nz-icon nz-icon-delete" v-if="!item.isDir" @click="delFile(item)"></i>-->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -43,7 +107,7 @@
|
||||
@close="newFolder(false)"
|
||||
>
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="width: 100px;flex-shrink: 1">{{$t('overall.folderName')}}</div>
|
||||
<div style="width: 100px;flex-shrink: 1;text-transform: capitalize">{{$t('overall.folderName')}}</div>
|
||||
<el-input v-model="folder" size="small" style="flex: 1"/>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
@@ -57,6 +121,87 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
class="nz-dialog snapshot-dialog"
|
||||
width="472px"
|
||||
:title='$t("terminal.rename")'
|
||||
destroy-on-close
|
||||
:modal-append-to-body="false"
|
||||
:visible.sync="renameBox"
|
||||
@close="renameBox = false"
|
||||
>
|
||||
<div style="display: flex; align-items: center">
|
||||
<div style="width: 100px;flex-shrink: 1;text-transform: capitalize">{{$t('overall.name')}}</div>
|
||||
<el-input v-model="renameStr" size="small" style="flex: 1"/>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="el-message-box__btns">
|
||||
<button class="nz-btn el-button el-button--small el-button--default" @click="renameBox = false">
|
||||
<span>{{$t('overall.cancel')}}</span>
|
||||
</button>
|
||||
<button class="nz-btn el-button--small nz-btn-style-normal" @click="setRename">
|
||||
<span style="text-transform:Capitalize">{{$t('overall.save')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog
|
||||
class="nz-dialog snapshot-dialog"
|
||||
width="472px"
|
||||
:title='$t("overall.delete")'
|
||||
destroy-on-close
|
||||
:modal-append-to-body="false"
|
||||
:visible.sync="delDialog"
|
||||
@close="delDialog = false"
|
||||
>
|
||||
<div style="display: flex; align-items: center" v-if="delObj">
|
||||
{{$t('terminal.delinfo',{fileName: delObj.name})}}
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="el-message-box__btns">
|
||||
<button class="nz-btn el-button el-button--small el-button--default" @click="delDialog = false" >
|
||||
<span>{{$t('tip.no')}}</span>
|
||||
</button>
|
||||
<button class="nz-btn el-button--small nz-btn-style-normal" @click="delFile">
|
||||
<span style="text-transform:Capitalize">{{$t('tip.yes')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
<!-- fileInfo-->
|
||||
<el-dialog
|
||||
class="nz-dialog snapshot-dialog"
|
||||
width="472px"
|
||||
:title='$t("overall.labels")'
|
||||
destroy-on-close
|
||||
:modal-append-to-body="false"
|
||||
:visible.sync="fileInfoShow"
|
||||
@close="fileInfoShow = false"
|
||||
>
|
||||
<div v-if="fileInfo">
|
||||
<div class="file-info-item-header">
|
||||
<i class="nz-icon" :class="selIcon(fileInfo)"/>
|
||||
{{fileInfo.name}}
|
||||
</div>
|
||||
<div v-for="item in fileAttr" :key="item.name" class="file-info-item">
|
||||
<div class="file-info-item-left" :class="{'is-disabled': fileInfo.isDir && item.key =='size'}">
|
||||
{{$t(item.name)}} :
|
||||
</div>
|
||||
<div class="file-info-item-right">
|
||||
{{selInfo(fileInfo, item.key)}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="footer">
|
||||
<div class="el-message-box__btns">
|
||||
<button class="nz-btn el-button--small nz-btn-style-normal" @click="fileInfoShow = false">
|
||||
<span style="text-transform:Capitalize">{{$t('overall.close')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -66,7 +211,8 @@ export default {
|
||||
name: 'fileDirectory',
|
||||
props: {
|
||||
uuid: {},
|
||||
fileDirectoryShow: {}
|
||||
fileDirectoryShow: {},
|
||||
host: {}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
@@ -76,7 +222,27 @@ export default {
|
||||
newFolderBoxShow: false,
|
||||
folder: '',
|
||||
fileDirectoryLoading: false,
|
||||
timer: ''
|
||||
timer: '',
|
||||
editPathShow: false,
|
||||
editPath: '',
|
||||
showHideFile: false,
|
||||
delObj: '',
|
||||
delDialog: false,
|
||||
renameBox: false,
|
||||
renameStr: '',
|
||||
fileInfo: '',
|
||||
fileInfoShow: false,
|
||||
fileAttr: [
|
||||
{ name: 'overall.type', key: 'isDir' },
|
||||
{ name: 'config.operationlog.ip', key: 'ip' },
|
||||
{ name: 'asset.location', key: 'location' },
|
||||
{ name: 'backup.size', key: 'size' },
|
||||
{ name: 'terminal.modifyTime', key: 'uts' },
|
||||
{ name: 'Owner', key: 'Owner' },
|
||||
{ name: 'dashboard.panel.chartForm.group', key: 'group' },
|
||||
{ name: 'config.menus.perms', key: 'permissionsString' }
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -113,12 +279,33 @@ export default {
|
||||
init () {
|
||||
this.getSftpPath(this.fileDirectory)
|
||||
},
|
||||
showEditPath () {
|
||||
this.editPath = this.fileDirectory
|
||||
this.editPathShow = true
|
||||
},
|
||||
hideEditPath () {
|
||||
this.editPath = ''
|
||||
this.editPathShow = false
|
||||
},
|
||||
goEditPath () {
|
||||
console.log('123123123')
|
||||
this.getSftpPath(this.editPath)
|
||||
setTimeout(() => {
|
||||
this.editPath = ''
|
||||
this.editPathShow = false
|
||||
})
|
||||
},
|
||||
selectFile (item) {
|
||||
if (item.isDir) {
|
||||
const path = this.fileDirectory == '/' ? '' : this.fileDirectory
|
||||
this.getSftpPath(path + '/' + item.name)
|
||||
}
|
||||
},
|
||||
showFileInfo (item) {
|
||||
console.log(item)
|
||||
this.fileInfo = item
|
||||
this.fileInfoShow = true
|
||||
},
|
||||
newFolder (flag) {
|
||||
if (!flag) {
|
||||
this.newFolderBoxShow = false
|
||||
@@ -146,16 +333,23 @@ export default {
|
||||
this.getSftpPath(path || '/')
|
||||
},
|
||||
getSftpPath (path) {
|
||||
this.breadcrumb = path.split('/').filter(item => item)
|
||||
const params = {
|
||||
uuid: this.uuid,
|
||||
path: path
|
||||
path: path,
|
||||
showHideFile: this.showHideFile
|
||||
}
|
||||
this.fileDirectoryLoading = true
|
||||
this.$post('/terminal/sftp/ls', params).then(res => {
|
||||
this.fileDirectoryLoading = false
|
||||
this.fileDirectory = res.data.path
|
||||
this.fileList = res.data.list
|
||||
if (res.code === 200) {
|
||||
this.fileDirectoryLoading = false
|
||||
this.fileDirectory = res.data.path
|
||||
this.fileList = res.data.list
|
||||
this.editPath = ''
|
||||
this.breadcrumb = res.data.path.split('/').filter(item => item)
|
||||
} else {
|
||||
this.$message.error(res.msg)
|
||||
this.editPath = ''
|
||||
}
|
||||
})
|
||||
},
|
||||
uploadFile () {
|
||||
@@ -166,6 +360,7 @@ export default {
|
||||
type: 'upload',
|
||||
isStart: false,
|
||||
isFinish: false,
|
||||
isError: false,
|
||||
importFileList: [],
|
||||
total: '',
|
||||
speed: '',
|
||||
@@ -174,7 +369,8 @@ export default {
|
||||
cancel: '',
|
||||
axios: '',
|
||||
timer: '',
|
||||
done: 0
|
||||
done: 0,
|
||||
delObj: ''
|
||||
}
|
||||
this.$store.dispatch('uploadFile', params)
|
||||
},
|
||||
@@ -192,6 +388,7 @@ export default {
|
||||
type: 'download',
|
||||
isStart: false,
|
||||
isFinish: false,
|
||||
isError: false,
|
||||
total: '',
|
||||
speed: '',
|
||||
fileLength: '',
|
||||
@@ -202,7 +399,11 @@ export default {
|
||||
this.$store.dispatch('dispatchAddFileList', params)
|
||||
}, 300)
|
||||
},
|
||||
delFile (item) {
|
||||
setRename () {
|
||||
this.renameBox = false
|
||||
},
|
||||
delFile () {
|
||||
const item = this.delObj
|
||||
const path = this.fileDirectory == '/' ? '' : this.fileDirectory
|
||||
const params = {
|
||||
uuid: this.uuid,
|
||||
@@ -210,8 +411,10 @@ export default {
|
||||
}
|
||||
this.$post('/terminal/sftp/rm', params).then(res => {
|
||||
if (res.code == 200) {
|
||||
this.delDialog = false
|
||||
this.getSftpPath(this.fileDirectory)
|
||||
} else {
|
||||
this.delDialog = false
|
||||
this.getSftpPath(this.fileDirectory)
|
||||
this.$message.error(res.msg)
|
||||
}
|
||||
@@ -227,6 +430,66 @@ export default {
|
||||
return 'nz-icon-File'
|
||||
}
|
||||
return 'nz-icon-File'
|
||||
},
|
||||
selInfo (item, key) {
|
||||
if (key === 'isDir') {
|
||||
if (item.isDir) {
|
||||
return this.$t('terminal.catalogueFile')
|
||||
} else {
|
||||
return this.$t('backup.File')
|
||||
}
|
||||
}
|
||||
if (key === 'ip') {
|
||||
return this.host
|
||||
}
|
||||
// fileAttr: [
|
||||
// { name: 'overall.type', key: 'isDir' },
|
||||
// { name: 'config.operationlog.ip', key: 'ip' },
|
||||
// { name: 'asset.location', key: 'location' },
|
||||
// { name: 'backup.size', key: 'size' },
|
||||
// { name: 'terminal.modifyTime', key: 'uts' },
|
||||
// { name: 'Owner', key: 'Owner' },
|
||||
// { name: 'dashboard.panel.chartForm.group', key: 'group' },
|
||||
// { name: 'config.menus.perms', key: 'permissionsString' }
|
||||
//
|
||||
// ]
|
||||
if (key === 'location') {
|
||||
return this.fileDirectory
|
||||
}
|
||||
if (key === 'size' && !item.isDir) {
|
||||
return this.bytes(item.size, 0, 0)
|
||||
} else if (key === 'size' && item.isDir) {
|
||||
return ''
|
||||
}
|
||||
if (key === 'uts') {
|
||||
return this.momentTz(item.uts * 1000)
|
||||
}
|
||||
if (key === 'Owner') {
|
||||
return 'Owner'
|
||||
}
|
||||
if (key === 'group') {
|
||||
return 'group'
|
||||
}
|
||||
if (key === 'permissionsString') {
|
||||
return item.permissionsString
|
||||
}
|
||||
return '-'
|
||||
},
|
||||
tableOperation ([command, row, param]) {
|
||||
switch (command) {
|
||||
case 'rename':
|
||||
this.renameStr = row.name
|
||||
this.renameBox = true
|
||||
break
|
||||
case 'download':
|
||||
this.downloadFile(row)
|
||||
break
|
||||
case 'del':
|
||||
this.delObj = row
|
||||
this.delDialog = true
|
||||
// this.delFile(row)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
<div class="item-progress">
|
||||
<div class="item-progress-top text-ellipsis">{{item.name}}</div>
|
||||
<div class="item-progress-middle">
|
||||
<el-progress :show-text="false" :percentage="item.done" :color="customColorMethod"></el-progress>
|
||||
<el-progress :show-text="false" :percentage="item.done" :color="customColorMethod.bind(item)"></el-progress>
|
||||
</div>
|
||||
<div class="item-progress-bottom">
|
||||
<span>{{bytes(item.total, 0, 0)}}</span>
|
||||
<span>{{item.speed}}</span>
|
||||
<span v-if="!item.isFinish && !item.isError">{{item.speed}}</span>
|
||||
<span v-if="item.isFinish">Completed</span>
|
||||
<span v-if="item.isError">Error</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="item-state">
|
||||
@@ -107,7 +109,8 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
bytes: chartDataFormat.getUnit(7).compute,
|
||||
customColorMethod (percentage) {
|
||||
customColorMethod (percentage, item) {
|
||||
console.log(item)
|
||||
if (percentage < 100) {
|
||||
return '#3B92F1'
|
||||
} else {
|
||||
|
||||
94
nezha-fronted/src/components/cli/terminal.vue
Normal file
94
nezha-fronted/src/components/cli/terminal.vue
Normal file
@@ -0,0 +1,94 @@
|
||||
<template>
|
||||
<div class="webTerminal">
|
||||
<div class="web-terminal-header">
|
||||
<div> <img alt="loading..." height="26" :src="logo?logo:require('../../assets/img/logo1-2.png')"/>Web terminal</div>
|
||||
<div>
|
||||
<div class="console-title-icon" style='right: 106px;display: inline;position: absolute' @click="showFileState" v-show="fileList.length">
|
||||
<i class="nz-icon nz-icon-a-filetransfer" :title="$t('terminal.filetransfer')"></i>
|
||||
<span v-show="fileList.length>0" class="right-tip">{{fileList.length<=99?fileList.length:'99+'}}</span>
|
||||
</div>
|
||||
{{name}}</div>
|
||||
</div>
|
||||
<fileListState v-clickoutside="hideFileState" ref="fileListState"/>
|
||||
<webSSHNew ref="websshNew" />
|
||||
<el-input :placeholder="'发送文本到所有SSH终端'" size="small" class="shell-input" v-model="message"/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import webSSHNew from '@/components/cli/webSSHNew'
|
||||
import fileListState from './fileListState'
|
||||
export default {
|
||||
name: 'terminal',
|
||||
components: {
|
||||
webSSHNew,
|
||||
fileListState
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
logo: '',
|
||||
fileListStateType: '',
|
||||
message: '',
|
||||
name: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
language () { return this.$store.getters.getLanguage },
|
||||
fileList () {
|
||||
return this.$store.getters.getFileList
|
||||
}
|
||||
},
|
||||
created () {
|
||||
const self = this
|
||||
window.addEventListener('setItemEvent', function (e) {
|
||||
if (e.key == 'nz-sys-logo' && e.value) {
|
||||
self.logo = e.value
|
||||
}
|
||||
})
|
||||
this.logo = localStorage.getItem('nz-sys-logo')
|
||||
},
|
||||
mounted () {
|
||||
const self = this
|
||||
this.name = localStorage.getItem('nz-username')
|
||||
window.onbeforeunload = () => {
|
||||
const opener = window.opener
|
||||
opener.postMessage(
|
||||
JSON.stringify({
|
||||
close: true
|
||||
})
|
||||
)
|
||||
}
|
||||
window.addEventListener('message', function (e) {
|
||||
console.log(e)
|
||||
if (e.data) {
|
||||
try {
|
||||
const data = JSON.parse(e.data)
|
||||
self.$get('asset/asset/' + data.id).then(res => {
|
||||
const asset = res.data
|
||||
self.$refs.websshNew.addConsole(asset.id, asset.manageIp, '', '', 'asset')
|
||||
})
|
||||
} catch (e) {
|
||||
console.log(e)
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
showFileState () {
|
||||
let type = 'down'
|
||||
this.fileListStateType = type = 'down'
|
||||
this.$refs.fileListState.fileStateShow(!this.$refs.fileListState.fileStateBox, type)
|
||||
},
|
||||
hideFileState () {
|
||||
this.$refs.fileListState.fileStateShow(false, this.fileListStateType)
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -31,12 +31,10 @@
|
||||
:popper-class="'popover-webshell'"
|
||||
>
|
||||
<div>
|
||||
Session ID : {{item.uuid.slice(0,7).toLocaleUpperCase()}}
|
||||
Session ID : {{item.terminal.uuid.slice(0,7).toLocaleUpperCase()}}
|
||||
</div>
|
||||
<span slot="reference"> <i class="nz-icon nz-icon-about" /></span>
|
||||
</el-popover>
|
||||
<div :class="{'active-icon grey':item.circleColor == 1,'active-icon green':item.circleColor == 2,'active-icon red':item.circleColor == 3}"
|
||||
style="margin-top: 0px;"></div>{{item.title}}
|
||||
</span>
|
||||
|
||||
<my-console
|
||||
|
||||
680
nezha-fronted/src/components/cli/webSSHNew.vue
Normal file
680
nezha-fronted/src/components/cli/webSSHNew.vue
Normal file
@@ -0,0 +1,680 @@
|
||||
<template>
|
||||
<div class="ani-webSHH-height web-terminal-new" id="web-terminal-new">
|
||||
<el-tabs :before-leave="beforeLeave"
|
||||
@tab-click="handleClick"
|
||||
@tab-remove="removeTab"
|
||||
type="border-card"
|
||||
v-model="editableTabsValue" >
|
||||
<el-tab-pane :key="item.name"
|
||||
:label="item.title"
|
||||
:name="item.name"
|
||||
closable
|
||||
v-for="(item, index) in editableTabs"
|
||||
>
|
||||
<!-- tab显示的内容 1 grey,2 green, 3 red-->
|
||||
<span slot="label" class="el-tabs__item-label">
|
||||
<div class="active-icon" :class="item.terminal.isLogin ? 'green-bg': 'red-bg'"></div>
|
||||
<div class="el-tabs__item-label-name text-ellipsis">
|
||||
{{item.terminal.userName}}
|
||||
</div>
|
||||
<el-popover
|
||||
slot="label"
|
||||
placement="bottom"
|
||||
width="180"
|
||||
trigger="click"
|
||||
:popper-class="'popover-webshell popover-webshell-new'"
|
||||
>
|
||||
<div class="popover-webshell-box">
|
||||
<div class="webshell-box-top">
|
||||
Session ID : {{item.terminal.uuid.slice(0,7).toLocaleUpperCase()}}
|
||||
</div>
|
||||
<div class="webshell-box-middle" v-show="item.terminal.userName">
|
||||
<div @click="duplicate(item, index)" class="webshell-box-item">
|
||||
<i class="nz-icon nz-icon-override"/>
|
||||
{{$t('overall.duplicate')}}
|
||||
</div>
|
||||
<div @click="reconnect(item, index)" v-show="item.terminal.userName" class="webshell-box-item">
|
||||
<i class="nz-icon nz-icon-reconnect"></i>
|
||||
{{$t('terminal.reconnect')}}
|
||||
</div>
|
||||
<div @click="showFileDir(item, index)" class="webshell-box-item" v-if="item.terminal.isLogin && item.terminal.terminalType == '1'">
|
||||
<i class="nz-icon nz-icon-SFTP"></i>
|
||||
{{$t('terminal.sftp')}}
|
||||
</div>
|
||||
</div>
|
||||
<div class="webshell-box-item webshell-box-bottom" @click="closeShell(item,index)">
|
||||
<div >
|
||||
<i class="nz-icon nz-icon-close"/>
|
||||
{{$t('overall.close')}}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<span slot="reference" class="icon-reference"> <i class="nz-icon nz-icon-more1" /></span>
|
||||
</el-popover>
|
||||
</span>
|
||||
|
||||
<terminal
|
||||
:fontSize="fontSize"
|
||||
:terminalType="item.terminal.terminalType"
|
||||
:idIndex="index"
|
||||
:ref="'console'+index"
|
||||
:terminal="item.terminal"
|
||||
@loginFail="loginFail"
|
||||
@closeConsole="removeTab"
|
||||
@refreshConsoleTitle="refreshTabTitle"
|
||||
></terminal>
|
||||
|
||||
</el-tab-pane>
|
||||
<el-tab-pane key="add" name="addConsole" class="add-console" style="width: 20px">
|
||||
<el-popover
|
||||
slot="label"
|
||||
placement="bottom-start"
|
||||
width="150"
|
||||
trigger="hover"
|
||||
:popper-class="'popover-webshell'"
|
||||
>
|
||||
<div>
|
||||
<div class="popover-webshell-item" @click="assetShowChange"><i class="nz-icon nz-icon-menu-assets" />{{$t('webshell.selAsset')}}</div>
|
||||
<div class="popover-webshell-item" @click="customShow=true"><i class="nz-icon nz-icon-edit" />{{$t('webshell.custom')}}</div>
|
||||
</div>
|
||||
<span slot="reference" style="padding:8px;font-size:20px;font-weight:bold;">+</span>
|
||||
</el-popover>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<!--弹窗-->
|
||||
<el-dialog :modal-append-to-body='false' :show-close="true" :visible.sync="assetShow" @close="closeAssetCustom" class="nz-dialog" width="620px">
|
||||
<div slot="title">{{$t('webshell.connect')}}</div>
|
||||
<div >
|
||||
<el-form label-width="120px" size="small" :model="assetContent" label-position = "top" :rules="rules" ref="assetConnect" v-my-loading="assetLoading" >
|
||||
<el-form-item :label='$t("overall.asset")' prop="assetId" class="flex">
|
||||
<el-dropdown trigger="click" class="header-el-dropdown">
|
||||
<span class="el-dropdown-link">
|
||||
<span>{{selectValue}}</span>
|
||||
<span><i class="el-icon-arrow-down el-icon--right"></i></span>
|
||||
</span>
|
||||
<el-dropdown-menu style="width: 118px" class="el-dropdown__width right-box-select-top right-public-box-dropdown-top" placement="bottom-end" slot="dropdown">
|
||||
<el-dropdown-item
|
||||
@click.native="selectAssetAuth(item.label, item.value)"
|
||||
v-for="item in searchMetrics"
|
||||
:key="item.value"><i class="nz-icon" :class="item.icon" style="margin-right: 5px"/>{{item.label}}</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
<v-selectpage
|
||||
ref="selectPage"
|
||||
style="flex: 1"
|
||||
data="asset/asset"
|
||||
:params="selectPageParams"
|
||||
:tb-columns="columns"
|
||||
key-field="id"
|
||||
show-field="manageIp"
|
||||
search-field="manageIp"
|
||||
v-model="assetContent.assetId"
|
||||
size="small"
|
||||
:language="language"
|
||||
:placeholder="$t('dashboard.panel.chartForm.selectAsset')"
|
||||
id="box-input-asset-id"
|
||||
:result-format="resultFormat"></v-selectpage>
|
||||
<button :disabled="prevent_opt.save" class="nz-btn nz-btn-size-normal nz-btn-style-normal" type="button" @click.prevent="connect">Connect</button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-dialog>
|
||||
<el-dialog :modal-append-to-body='false' :show-close="true" :visible.sync="customShow" @close="closeAssetCustom" class="nz-dialog" width="620px"destroy-on-close >
|
||||
<div slot="title">{{$t('webshell.connect')}}</div>
|
||||
<div >
|
||||
<el-form label-width="120px" size="small" :model="customConnect" label-position = "top" :rules=" customConnect.authProtocol ===2 ? rulesCustom2: rulesCustom" ref="customConnect" v-my-loading="assetLoading" class="custom">
|
||||
<el-form-item :label='$t("webshell.protocol")' prop="authProtocol">
|
||||
<el-select @change="protocolChange" value-key="id" popper-class="config-dropdown w260 right-box-select-top right-public-box-dropdown-top" v-model="customConnect.authProtocol" placeholder="" size="small" id="webshell-box-input-protocol">
|
||||
<el-option v-for="item in authProtocol" :id="'dc-principal-op-'+item.value" :key="item.value" :label="item.name" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label='$t("asset.authType")' prop="authType" @change="authTypeChange" >
|
||||
<el-select value-key="id" popper-class="config-dropdown w260 right-box-select-top right-public-box-dropdown-top" v-model="customConnect.authType" :disabled="customConnect.authProtocol === 2" placeholder="" size="small" id="webshell-box-input-protocol">
|
||||
<el-option v-for="item in authType" :id="'dc-principal-op-'+item.value" :key="item.value" :label="item.name" :value="item.value"></el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item :label='$t("asset.host")' prop="host">
|
||||
<el-input v-model="customConnect.host" size="small"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label='$t("asset.port")' prop="port">
|
||||
<el-input v-model="customConnect.port" size="small"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label='$t("profile.username")' prop="authUsername">
|
||||
<el-input v-model="customConnect.authUsername" size="small" autocomplete="new-password"/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="customConnect.authType === 2"
|
||||
:label='$t("asset.talon.token")'
|
||||
prop="authPriKey"
|
||||
>
|
||||
<el-input v-model="customConnect.authPriKey" size="small" autocomplete="new-password"/>
|
||||
</el-form-item>
|
||||
<el-form-item :label='$t("login.pin")' prop="authPin"
|
||||
:rules="[
|
||||
{ required: customConnect.authType ===1, message:$t('validate.required'), trigger: 'change'},
|
||||
]">
|
||||
<el-input v-model="customConnect.authPin" size="small" type="password" autocomplete="new-password"/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="customConnect.authProtocol === 2"
|
||||
:label='$t("asset.usernamePrompt")'
|
||||
prop="authUserTip">
|
||||
<el-input v-model="customConnect.authUserTip" size="small"/>
|
||||
</el-form-item>
|
||||
<el-form-item
|
||||
v-if="customConnect.authProtocol === 2"
|
||||
:label='$t("asset.pinPrompt")'
|
||||
prop="authPinTip"
|
||||
>
|
||||
<el-input v-model="customConnect.authPinTip" size="small"/>
|
||||
</el-form-item>
|
||||
<div class="right-box__footer custom-footer">
|
||||
<button id="asset-edit-cancel" @click="customShow=false" class="footer__btn footer__btn--light" type="button">
|
||||
<span>{{$t('overall.cancel')}}</span>
|
||||
</button>
|
||||
<button id="asset-edit-save" :disabled="prevent_opt.save" class="footer__btn" @click.prevent="connect" type="button">
|
||||
<span>{{$t('webshell.connect')}}</span>
|
||||
</button>
|
||||
</div>
|
||||
</el-form>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import terminal from './consoleNew'
|
||||
import { host, port } from '@/components/common/js/validate'
|
||||
export default {
|
||||
name: 'webSSH',
|
||||
components: {
|
||||
terminal
|
||||
},
|
||||
computed: {
|
||||
language () { return this.$store.getters.getLanguage },
|
||||
fileList () {
|
||||
return this.$store.getters.getFileList
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
tabIndex: 0,
|
||||
editableTabs: [],
|
||||
editableTabsValue: '',
|
||||
fontSize: 16,
|
||||
assetShow: false,
|
||||
selectValue: 'SSH',
|
||||
fileListStateType: 'down',
|
||||
searchMetrics: [
|
||||
{
|
||||
value: 'SSH',
|
||||
label: 'SSH'
|
||||
},
|
||||
{
|
||||
value: 'Telnet',
|
||||
label: 'Telnet'
|
||||
}
|
||||
],
|
||||
selectPageParams: {
|
||||
authProtocol: 1
|
||||
},
|
||||
authProtocol: [
|
||||
{
|
||||
value: 1,
|
||||
name: 'SSH'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
name: 'TELNET'
|
||||
}
|
||||
],
|
||||
authType: [
|
||||
{
|
||||
value: 1,
|
||||
name: 'Password'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
name: 'Key'
|
||||
}
|
||||
],
|
||||
consoleShow: false,
|
||||
closeConfirmShow: false,
|
||||
customConnect: {
|
||||
host: '',
|
||||
port: 22,
|
||||
authType: 1,
|
||||
authUsername: '',
|
||||
authPin: '',
|
||||
authPriKey: '',
|
||||
authUserTip: '',
|
||||
authPinTip: '',
|
||||
authProtocolPort: '',
|
||||
authProtocol: 1
|
||||
},
|
||||
columns: [
|
||||
{ title: 'ID', data: 'id' },
|
||||
{
|
||||
title: 'Name',
|
||||
data: function (row) {
|
||||
if (row.name.length > 15) {
|
||||
return row.name.substring(0, 12) + '...'
|
||||
}
|
||||
return row.name
|
||||
}
|
||||
},
|
||||
{ title: 'Manage Ip', data: 'manageIp' },
|
||||
{
|
||||
title: 'Type',
|
||||
data: (row) => {
|
||||
return row.type ? row.type.name : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Model',
|
||||
data: (row) => {
|
||||
return row.model ? row.model.name : ''
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'Datacenter',
|
||||
data: (row) => {
|
||||
return row.dc ? row.dc.name : ''
|
||||
}
|
||||
}
|
||||
],
|
||||
rules: {
|
||||
assetId: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
|
||||
]
|
||||
},
|
||||
rulesCustom: {
|
||||
authProtocol: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
|
||||
],
|
||||
authType: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
|
||||
],
|
||||
host: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' },
|
||||
{ validator: host, trigger: 'change' }
|
||||
],
|
||||
port: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' },
|
||||
{ validator: port, trigger: 'change' }
|
||||
],
|
||||
authUsername: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
|
||||
],
|
||||
authPin: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
|
||||
]
|
||||
// authUserTip: [
|
||||
// { validator: this.authUserTipValid, trigger: 'change' }
|
||||
// ],
|
||||
// authPinTip: [
|
||||
// { validator: this.authPinTipValid, trigger: 'change' }
|
||||
// ],
|
||||
// authPriKey: [
|
||||
// { validator: this.authPriKeyValid, trigger: 'change' }
|
||||
// ]
|
||||
},
|
||||
rulesCustom2: {
|
||||
authProtocol: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
|
||||
],
|
||||
authType: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
|
||||
],
|
||||
host: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' },
|
||||
{ validator: host, trigger: 'change' }
|
||||
],
|
||||
port: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' },
|
||||
{ validator: port, trigger: 'change' }
|
||||
],
|
||||
authUsername: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
|
||||
],
|
||||
authPin: [
|
||||
{ required: false, message: this.$t('validate.required'), trigger: 'change' }
|
||||
],
|
||||
authUserTip: [
|
||||
{ required: false, message: this.$t('validate.required'), trigger: 'change' }
|
||||
],
|
||||
authPinTip: [
|
||||
{ required: false, message: this.$t('validate.required'), trigger: 'change' }
|
||||
],
|
||||
authPriKey: [
|
||||
{ required: true, message: this.$t('validate.required'), trigger: 'change' }
|
||||
]
|
||||
},
|
||||
assetData: [],
|
||||
assetLoading: false,
|
||||
assetContent: {
|
||||
assetId: ''
|
||||
},
|
||||
customShow: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
selectAssetAuth (val, label) {
|
||||
this.$refs.selectPage.remove()
|
||||
if (val) {
|
||||
this.selectValue = val
|
||||
if (val === 'SSH') {
|
||||
this.selectPageParams = { authProtocol: 1, pageNumber: 1 }
|
||||
} else {
|
||||
this.selectPageParams = { authProtocol: 2, pageNumber: 1 }
|
||||
}
|
||||
} else {
|
||||
label = 'SSH'
|
||||
this.selectPageParams = { authProtocol: 1, pageNumber: 1 }
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.$refs.selectPage.pageChange()
|
||||
}, 100)
|
||||
},
|
||||
assetShowChange () {
|
||||
this.assetShow = true
|
||||
this.selectValue = 'SSH'
|
||||
this.selectPageParams = { authProtocol: 1, pageNumber: 1 }
|
||||
if (this.$refs.selectPage) {
|
||||
this.$refs.selectPage.remove()
|
||||
this.$refs.selectPage.pageChange()
|
||||
}
|
||||
// this.getAssetData()
|
||||
},
|
||||
/* 活动标签切换时触发 */
|
||||
beforeLeave (currentName, oldName) {
|
||||
// 重点,如果name是add,则什么都不触发
|
||||
if (currentName === 'add') {
|
||||
this.addTab()
|
||||
return false
|
||||
} else {
|
||||
// 切换tab
|
||||
this.$nextTick(() => {
|
||||
if (this.editableTabs && this.editableTabs.length > 0) {
|
||||
this.editableTabs.forEach((tab, index) => {
|
||||
if (tab.name === currentName) {
|
||||
this.$refs['console' + index][0].focusConsole()
|
||||
this.currentUuid = tab.terminal.uuid
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
this.currentIndex = currentName
|
||||
}
|
||||
},
|
||||
handleClick () {
|
||||
|
||||
},
|
||||
windowChange () { // 窗口大小改变
|
||||
// alert('winChange');
|
||||
if (this.editableTabs && this.editableTabs.length > 0) {
|
||||
this.editableTabs.forEach((tab, index) => {
|
||||
this.$refs['console' + index][0].resize()
|
||||
})
|
||||
}
|
||||
},
|
||||
removeTab (targetName) {
|
||||
const tabs = this.editableTabs
|
||||
let activeName = this.editableTabsValue
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
const nextTab = tabs[index + 1] || tabs[index - 1]
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
this.editableTabsValue = activeName
|
||||
this.editableTabs = tabs.filter(tab => tab.name !== targetName)
|
||||
|
||||
if (this.editableTabs.length <= 0) {
|
||||
// this.closeConsole()
|
||||
}
|
||||
},
|
||||
loginFail (params) {
|
||||
this.removeTab(params.name)
|
||||
setTimeout(() => {
|
||||
if (params.assetId) {
|
||||
this.assetShowChange()
|
||||
} else {
|
||||
this.customConnect = {
|
||||
...params,
|
||||
authType: Number(params.authType),
|
||||
authPin: atob(decodeURIComponent(params.authPin))
|
||||
}
|
||||
this.customShow = true
|
||||
}
|
||||
})
|
||||
},
|
||||
refreshTabTitle (connectResult) {
|
||||
if (this.editableTabs && this.editableTabs.length > 0) {
|
||||
this.editableTabs.forEach((tab, index) => {
|
||||
if (tab.name === this.editableTabsValue) {
|
||||
if (connectResult.title && connectResult.title != '') {
|
||||
tab.title = connectResult.title
|
||||
}
|
||||
tab.circleColor = connectResult.color
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
cancleConfirm () {
|
||||
this.closeConfirmShow = false
|
||||
},
|
||||
resultFormat (resp) {
|
||||
if (resp && resp.data) {
|
||||
const assetData = {}
|
||||
assetData.list = resp.data.list
|
||||
assetData.totalRow = resp.data.total
|
||||
return assetData
|
||||
}
|
||||
},
|
||||
getAssetData () {
|
||||
this.assetLoading = true
|
||||
this.$get('asset/asset', { pageSize: -1, typeIds: '1,2' }).then(res => {
|
||||
this.assetLoading = false
|
||||
this.assetData = res.data.list
|
||||
})
|
||||
},
|
||||
connect () {
|
||||
this.prevent_opt.save = true
|
||||
if (this.assetShow) {
|
||||
this.$refs.assetConnect.validate((valid) => {
|
||||
if (valid) {
|
||||
this.$get('asset/asset/' + this.assetContent.assetId).then(res => {
|
||||
const asset = res.data
|
||||
this.addConsole(asset.id, asset.manageIp, '', '', 'asset')
|
||||
this.assetShow = false
|
||||
this.prevent_opt.save = false
|
||||
})
|
||||
} else {
|
||||
this.prevent_opt.save = false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.$refs.customConnect.validate((valid) => {
|
||||
if (valid) {
|
||||
this.addConsole('', this.customConnect.host, '', this.customConnect.port, 'custom')
|
||||
this.customShow = false
|
||||
this.prevent_opt.save = false
|
||||
} else {
|
||||
this.prevent_opt.save = false
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
protocolChange () {
|
||||
if (this.customConnect.authProtocol === 1) {
|
||||
this.customConnect.authUserTip = ''
|
||||
this.customConnect.authPinTip = ''
|
||||
this.customConnect.port = 22
|
||||
} else {
|
||||
this.customConnect.authPriKey = ''
|
||||
this.customConnect.port = 23
|
||||
this.customConnect.authType = 1
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.$refs.customConnect.clearValidate()
|
||||
})
|
||||
},
|
||||
authTypeChange () {
|
||||
if (this.customConnect.authType === 1) {
|
||||
this.customConnect.authPriKey = ''
|
||||
}
|
||||
},
|
||||
encode (str) {
|
||||
// 对编码的字符串转化base64
|
||||
const base64 = encodeURIComponent(btoa(str))
|
||||
return base64
|
||||
},
|
||||
closeAssetCustom () {
|
||||
this.assetShow = false
|
||||
this.customShow = false
|
||||
this.assetContent.assetId = ''
|
||||
this.customConnect = {
|
||||
host: '',
|
||||
port: 22,
|
||||
authType: 1,
|
||||
authUsername: '',
|
||||
authPin: '',
|
||||
authPriKey: '',
|
||||
authUserTip: '',
|
||||
authPinTip: '',
|
||||
authProtocolPort: '',
|
||||
authProtocol: 1
|
||||
}
|
||||
},
|
||||
addConsole (id, host, accountId, port, type) {
|
||||
if (!id) { id = '' }
|
||||
if (!host) { host = '' }
|
||||
if (!accountId) { accountId = '' }
|
||||
if (!port) { port = '' }
|
||||
const uuid = ''
|
||||
const newTabName = ++this.tabIndex + ''
|
||||
let title = host
|
||||
if (port) {
|
||||
title = title + ':' + port
|
||||
}
|
||||
if (!title) {
|
||||
title = this.$t('webshell.shellTitle')
|
||||
}
|
||||
const width = document.body.clientWidth// 可视宽度
|
||||
const console = {
|
||||
title: title,
|
||||
name: newTabName,
|
||||
circleColor: 1, // 1 grey,2 green, 3 red
|
||||
uuid: uuid,
|
||||
terminal: {
|
||||
name: newTabName,
|
||||
cols: 225,
|
||||
rows: 200,
|
||||
width: width,
|
||||
height: this.consoleHeight,
|
||||
assetId: id,
|
||||
accountId: accountId,
|
||||
uuid: uuid,
|
||||
type: type,
|
||||
username: '',
|
||||
isLogin: false
|
||||
}
|
||||
}
|
||||
if (type === 'custom') {
|
||||
console.terminal.custom = {
|
||||
host: this.customConnect.host,
|
||||
port: this.customConnect.port,
|
||||
authType: this.customConnect.authType,
|
||||
terminalType: this.customConnect.authProtocol,
|
||||
authUsername: encodeURIComponent(this.customConnect.authUsername),
|
||||
authPin: this.encode(this.customConnect.authPin),
|
||||
authPriKey: encodeURIComponent(this.customConnect.authPriKey),
|
||||
authUserTip: encodeURIComponent(this.customConnect.authUserTip),
|
||||
authPinTip: encodeURIComponent(this.customConnect.authPinTip),
|
||||
authProtocolPort: encodeURIComponent(this.customConnect.authProtocolPort),
|
||||
authProtocol: encodeURIComponent(this.customConnect.authProtocol)
|
||||
}
|
||||
}
|
||||
if (id) {
|
||||
this.$get('/asset/asset/' + id).then(res => {
|
||||
console.terminal.terminalType = res.data.type.authProtocol
|
||||
console.terminal.username = ''
|
||||
this.editableTabsValue = newTabName
|
||||
this.editableTabs.push(console)
|
||||
})
|
||||
} else {
|
||||
console.terminal.username = ''
|
||||
this.editableTabsValue = newTabName
|
||||
this.editableTabs.push(console)
|
||||
}
|
||||
},
|
||||
duplicate (item, index) {
|
||||
console.log(item, index)
|
||||
if (item.terminal.assetId) {
|
||||
const newTabName = ++this.tabIndex + ''
|
||||
this.editableTabsValue = newTabName
|
||||
this.editableTabs.push(console)
|
||||
} else {
|
||||
this.customConnect = {
|
||||
...item.terminal.custom,
|
||||
authType: Number(item.terminal.custom.authType),
|
||||
authPin: atob(decodeURIComponent(item.terminal.custom.authPin))
|
||||
}
|
||||
this.customShow = true
|
||||
}
|
||||
},
|
||||
debounce (operate, delay) {
|
||||
let time = null
|
||||
let timer = null
|
||||
let newTime = null
|
||||
function task () {
|
||||
newTime = +new Date()
|
||||
if (newTime - time < delay) {
|
||||
timer = setTimeout(task, delay)
|
||||
} else {
|
||||
operate()
|
||||
timer = null
|
||||
}
|
||||
time = newTime
|
||||
}
|
||||
return function () {
|
||||
// 更新时间戳
|
||||
time = +new Date()
|
||||
if (!timer) {
|
||||
timer = setTimeout(task, delay)
|
||||
}
|
||||
}
|
||||
},
|
||||
reconnect (item, index) {
|
||||
console.log(item, index)
|
||||
this.$refs['console' + index][0].reconnect()
|
||||
},
|
||||
showFileDir (item, index) {
|
||||
this.$refs['console' + index][0].showFileDir(true)
|
||||
},
|
||||
closeShell (item, index) {
|
||||
this.removeTab(item.name)
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
},
|
||||
created () {
|
||||
},
|
||||
mounted () {
|
||||
window.addEventListener('resize', this.debounce(this.windowChange, 1000), false)
|
||||
},
|
||||
beforeDestroy () {
|
||||
window.removeEventListener('resize', this.debounce(this.windowChange, 1000), false)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -142,7 +142,12 @@ export default {
|
||||
methods: {
|
||||
...mapActions(['logoutSuccess']),
|
||||
cli () {
|
||||
this.$store.commit('openConsole')
|
||||
// this.$store.commit('openConsole')
|
||||
if (!this.externalTerminal) {
|
||||
this.$store.dispatch('dispatchExternalTerminal')
|
||||
} else {
|
||||
this.$store.dispatch('dispatchOpenExternalTerminalWindow')
|
||||
}
|
||||
},
|
||||
// 打开全局搜索
|
||||
openGlobalSearch () {
|
||||
@@ -283,6 +288,9 @@ export default {
|
||||
},
|
||||
isShrink () {
|
||||
return this.$store.getters.getIsShrink
|
||||
},
|
||||
externalTerminal () {
|
||||
return this.$store.getters.getExternalTerminal
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<template>
|
||||
<div class="home" :class="mode" v-my-loading.dark="loading">
|
||||
<left-menu @refresh="refresh"></left-menu>
|
||||
<div class="home" :class="mode" v-my-loading.dark="loading" >
|
||||
<left-menu @refresh="refresh" v-show="showMenu"></left-menu>
|
||||
<div ref="body" class="body">
|
||||
<Header></Header>
|
||||
<Header v-show="showHeader"></Header>
|
||||
<container v-if="containerShow" ref="container"></container>
|
||||
</div>
|
||||
<!--web-ssh-->
|
||||
@@ -46,6 +46,12 @@ export default {
|
||||
},
|
||||
loading () {
|
||||
return this.$store.getters.getHomeLoading
|
||||
},
|
||||
showMenu () {
|
||||
return !this.$route.meta.hideMenu
|
||||
},
|
||||
showHeader () {
|
||||
return !this.$route.meta.hideHeader
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -54,6 +60,7 @@ export default {
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
console.log(this.$router, this.$route)
|
||||
},
|
||||
destroyed () {
|
||||
localStorage.removeItem('moduleProjectId')
|
||||
|
||||
@@ -225,6 +225,11 @@ export default {
|
||||
detailViewTopSearch
|
||||
},
|
||||
mixins: [dataListMixin, detailViewMixin, routerPathParams],
|
||||
computed: {
|
||||
externalTerminal () {
|
||||
return this.$store.getters.getExternalTerminal
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
url: 'asset/asset',
|
||||
@@ -467,7 +472,12 @@ export default {
|
||||
host: row.manageIp,
|
||||
port: row.authProtocolPort
|
||||
}
|
||||
this.$store.commit('addConsole', consoleParam)
|
||||
if (!this.externalTerminal) {
|
||||
this.$store.dispatch('dispatchExternalTerminal', consoleParam)
|
||||
} else {
|
||||
this.$store.dispatch('dispatchOpenExternalTerminalWindow', consoleParam)
|
||||
}
|
||||
// this.$store.commit('addConsole', consoleParam)
|
||||
},
|
||||
duplicate (row) {
|
||||
this.$get(`${this.url}/${row.id}`).then(response => {
|
||||
|
||||
@@ -3,7 +3,8 @@ import bus from '../../../../libs/bus'
|
||||
import vm from '../../../../entrance/app/main'
|
||||
import exportHtml from '../../../../entrance/exportHtml/exportHtml'
|
||||
// const vm = window.dataJson ? exportHtml : app
|
||||
window.onload = function () {
|
||||
window.addEventListener('load', function () {
|
||||
console.log(123213)
|
||||
if (!window.dataJson) {
|
||||
commonOption.toolbox.feature.dataZoom.title.zoom = vm.$i18n.t('overall.toolBox.zoom')
|
||||
commonOption.toolbox.feature.dataZoom.title.back = vm.$i18n.t('overall.toolBox.back')
|
||||
@@ -13,7 +14,7 @@ window.onload = function () {
|
||||
commonOption.toolbox.feature.dataZoom.title.back = exportHtml.$i18n.t('overall.toolBox.back')
|
||||
commonOption.toolbox.feature.magicType.title.stack = exportHtml.$i18n.t('overall.toolBox.stack')
|
||||
}
|
||||
}
|
||||
})
|
||||
const bgColorList = ['#b3424a', '#7bbfea', '#f05b72', '#596032', '#bd6758',
|
||||
'#cd9a5b', '#918597', '#70a19f', '#005344', '#FF00FF',
|
||||
'#f7acbc', '#5f5d46', '#66ffff', '#ccFF66', '#f47920',
|
||||
|
||||
@@ -8,7 +8,7 @@ import VueResource from 'vue-resource'
|
||||
import bus from '@/libs/bus'
|
||||
Vue.use(VueResource)
|
||||
|
||||
const loginWhiteList = ['/setup', '/sys/license/upload', '/sys/license/state', '/sys/appearance', '/i18n'] // 免登陆白名单
|
||||
const loginWhiteList = ['/setup', '/sys/license/upload', '/sys/license/state', '/sys/appearance', '/i18n', '/externalTerminal'] // 免登陆白名单
|
||||
const permissionWhiteList = ['/profile', '/menu', ...loginWhiteList] // 权限白名单
|
||||
router.beforeEach((to, from, next) => {
|
||||
if (window.entrance) {
|
||||
|
||||
@@ -221,6 +221,14 @@ export default new Router({
|
||||
component: resolve => require(['@/components/page/tool/trace'], resolve)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
path: '/externalTerminal',
|
||||
component: resolve => require(['@/components/cli/terminal'], resolve),
|
||||
meta: {
|
||||
hideHeader: true,
|
||||
hideMenu: true
|
||||
}
|
||||
}
|
||||
]
|
||||
})
|
||||
|
||||
@@ -3,7 +3,10 @@ const terminalFile = {
|
||||
fileList: [],
|
||||
uploadItem: {},
|
||||
updateUuid: '', // 根据uuid的变化 判断页面是否需要刷新
|
||||
updateIndex: 1
|
||||
updateIndex: 1,
|
||||
externalTerminal: false,
|
||||
messageFunction: false,
|
||||
externalTerminalWindow: ''
|
||||
},
|
||||
mutations: {
|
||||
setFileList (state, arr) {
|
||||
@@ -18,6 +21,48 @@ const terminalFile = {
|
||||
setUploadItem (state, item) {
|
||||
state.uploadItem = item
|
||||
},
|
||||
setExternalTerminal (state, item) { // 第一次 关闭或者打开 termail
|
||||
state.externalTerminal = !state.externalTerminal
|
||||
if (!state.messageFunction) {
|
||||
state.messageFunction = true
|
||||
window.addEventListener('message', function (e) {
|
||||
console.log(e)
|
||||
const data = JSON.parse(e.data) // e.data 里面有自己所传的所有参数 可以根据参数做自己的判断
|
||||
if (data.close) {
|
||||
state.externalTerminal = false
|
||||
}
|
||||
})
|
||||
}
|
||||
if (state.externalTerminal) {
|
||||
state.externalTerminalWindow = window.open('/ui/externalTerminal', 'terminal')
|
||||
} else {
|
||||
state.externalTerminalWindow = ''
|
||||
}
|
||||
if (item && state.externalTerminal) {
|
||||
console.log('onloda', state.externalTerminalWindow)
|
||||
state.externalTerminalWindow.addEventListener('load', () => {
|
||||
console.log(123123123123123)
|
||||
setTimeout(() => {
|
||||
console.log('time')
|
||||
state.externalTerminalWindow.postMessage(
|
||||
JSON.stringify(item)
|
||||
)
|
||||
}, 3000)
|
||||
})
|
||||
}
|
||||
},
|
||||
openExternalTerminalWindow (state, item) { // 后续打开 termail
|
||||
if (state.externalTerminalWindow) {
|
||||
window.open('', 'terminal')
|
||||
} else {
|
||||
state.externalTerminalWindow = window.open('/ui/externalTerminal', 'terminal')
|
||||
}
|
||||
if (item) {
|
||||
state.externalTerminalWindow.postMessage(
|
||||
JSON.stringify(item)
|
||||
)
|
||||
}
|
||||
},
|
||||
setUpdateUuid (state, uuid) {
|
||||
state.updateUuid = uuid
|
||||
state.updateIndex++
|
||||
@@ -38,6 +83,9 @@ const terminalFile = {
|
||||
},
|
||||
getUpdateIndex (state) {
|
||||
return state.updateIndex
|
||||
},
|
||||
getExternalTerminal (state) {
|
||||
return state.externalTerminal
|
||||
}
|
||||
},
|
||||
actions: {
|
||||
@@ -55,6 +103,13 @@ const terminalFile = {
|
||||
},
|
||||
upDateConsole (store, uuid) {
|
||||
store.commit('setUpdateUuid', uuid)
|
||||
},
|
||||
// 打开外部termail
|
||||
dispatchExternalTerminal (store, item) { // 第一次打开termail
|
||||
store.commit('setExternalTerminal', item)
|
||||
},
|
||||
dispatchOpenExternalTerminalWindow (store, item) { // 后续操作termail
|
||||
store.commit('openExternalTerminalWindow', item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user