This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
nezha-nezha-fronted/nezha-fronted/src/components/cli/webSSH.vue
hanyuxia 2cfe1947ae feat:新功能
webshell模块
1.上传功能
2.下载功能(还需要测试,进行中)
3.header顶部菜单分割线及xshell入口角标样式调整
2020-03-11 19:16:23 +08:00

490 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<style lang="scss">
@import './webSSH.scss';
</style>
<template>
<div id="shell-service" data-yunlog-scope="popup" :class="{'shell-service-max': isFullScreen}" v-show="consoleShow">
<div id="shell-service-resize-mask"></div>
<div id="shell-split" class="shell-split shell-iconfont" @mousedown="dragEagle" v-show="!isFullScreen"></div>
<div style='position: relative;border:solid 0px red;'>
<el-menu mode="horizontal" @select="handleSelect" style='position: absolute;left:0px;top:0px;border-top: 1px solid #DCDFE6;'>
<el-submenu index="1" style="width:40px;">
<template slot="title" ><i class="el-icon-setting " style="position: absolute;left: 10px;top: 4px;"></i></template>
<el-submenu index="1-1">
<template slot="title">文字大小</template>
<el-menu-item index="1-1-1">最小</el-menu-item>
<el-menu-item index="1-1-2"></el-menu-item>
<el-menu-item index="1-1-3"></el-menu-item>
<el-menu-item index="1-1-4"></el-menu-item>
</el-submenu>
<el-submenu index="1-2" >
<template slot="title">字体</template>
<el-menu-item index="1-2-1">Monosapace</el-menu-item>
<el-menu-item index="1-2-2">Courier New</el-menu-item>
</el-submenu>
</el-submenu>
<el-submenu index="2" style="width:50px;">
<template slot="title" ><i class="el-icon-upload console-title-icon" style="position: absolute;left: 10px;top: 4px;"></i></template>
<el-menu-item index="2-1" @click="showUploadBox">
<div>{{$t('webshell.upload')}}</div>
</el-menu-item>
<el-menu-item index="2-2" @click="showDownloadBox">
<div>{{$t('webshell.download')}}</div>
</el-menu-item>
</el-submenu>
</el-menu>
<el-tabs v-model="editableTabsValue"
@tab-click="handleClick"
@tab-remove="removeTab"
:before-leave="beforeLeave"
style='width:100%;'
type="border-card">
<el-tab-pane v-for="(item, index) in editableTabs"
:key="item.name"
:label="item.title"
:name="item.name"
closable
>
<!-- tab显示的内容 <div :class="{'active-icon green':scope.row.pingState == 1,'active-icon red':scope.row.pingState == 0}"></div>-->
<span slot="label" style="">
<div class="active-icon grey" style="margin-top: 0px;"></div>{{item.title}}
</span>
<my-console :terminal="item.terminal" :ref="'console'+index" @closeConsole="removeTab" :idIndex="index" :isFullScreen="isFullScreen"></my-console>
</el-tab-pane>
<el-tab-pane key="add" name="add">
<span slot="label" style="padding:8px;font-size:20px;font-weight:bold;">+</span>
</el-tab-pane>
</el-tabs>
<i style='right:70px;' @click="minScreen" class="el-icon-minus console-title-icon"></i>
<i style='right:38px;;' @click="fullScreen" class="el-icon-full-screen console-title-icon"></i>
<i style='right:8px;' @click="closeConsole" class="el-icon-close console-title-icon"></i>
<!--
<i style='right:103px;' @click="minScreen" class="el-icon-minus console-title-icon"></i>
<i style='right:70px;;' @click="fullScreen" class="el-icon-full-screen console-title-icon"></i>
<i style='right:38px;' class="el-icon-copy-document console-title-icon" ></i>
<i style='right:8px;' @click="closeConsole" class="el-icon-close console-title-icon"></i>
-->
<!--el-icon-setting el-icon-minus el-icon-full-screen el-icon-copy-document-->
</div>
<div >
<el-dialog :visible.sync="uploadBox.showUpload" :title="uploadBox.title" :modal-append-to-body='false' :show-close="true" width="500px" @close="closeDialog" class="nz-dialog" >
<div >
<div class="upload-body">
<el-row >
<el-col :span="24">
<el-upload class="upload-demo"
ref="uploadFile" action=""
:file-list="uploadFileList"
:on-change="handleChange"
:auto-upload="false" >
<button type="button" class="nz-btn nz-btn-size-normal nz-btn-style-normal">
<span class="top-tool-btn-txt" >{{$t('webshell.fileSelect')}}</span>
</button>
</el-upload>
</el-col>
</el-row>
<el-row style="margin-top: 20px;">
<el-col :span="3" style="text-align:center;line-height: 24px;">
<label>{{$t('webshell.filePath')}}</label>
</el-col>
<el-col :span="21">
<el-input v-model="uploadFile.path" size="mini"></el-input>
</el-col>
</el-row>
</div>
<div slot="footer" class="footer">
<div class="el-message-box__btns" style="text-align: right;margin-top: 20px;">
<button @click="upload" class="el-button el-button--default el-button--small">
<span>{{$t('webshell.uploadButtonTitle')}}</span>
</button>
<button @click="closeDialog" class="el-button el-button--default el-button--small" >
<span>{{$t('overall.cancel')}}</span>
</button>
</div>
</div>
</div>
</el-dialog>
<el-dialog :visible.sync="downloadBox.showDownload" :title="downloadBox.title" :modal-append-to-body='false' :show-close="true" width="500px" @close="closeDownloadDialog" class="nz-dialog" >
<div>
<div class="upload-body">
<el-row style="margin-top: 20px;">
<el-col :span="3" style="text-align:center;line-height: 24px;">
<label>{{$t('webshell.filePath')}}</label>
</el-col>
<el-col :span="21">
<el-input v-model="downloadFile.path" size="mini"></el-input>
</el-col>
</el-row>
</div>
<div slot="footer" class="footer">
<div class="el-message-box__btns" style="text-align: right;margin-top: 20px;">
<button @click="download" class="el-button el-button--default el-button--small">
<span>{{$t('webshell.downloadButtonTitle')}}</span>
</button>
<button @click="closeDownloadDialog" class="el-button el-button--default el-button--small">
<span>{{$t('overall.cancel')}}</span>
</button>
</div>
</div>
</div>
</el-dialog>
</div>
</div>
</template>
<script>
import Console from './console'
import uuidv1 from "uuid/v1";
import axios from 'axios'
export default {
name: 'webSSH',
components: {
'my-console': Console
},
data() {
return {
consoleShow:false,
isFullScreen:false,
initConsoleHeight:300,//只读,初始化高度
consoleHeight:300,
currentTransform:0,
editableTabsValue: '-1',//当前显示的console
currentIndex:'-1',
editableTabs: [ ],
tabIndex: -1,//添加tab的时候使用
//upoload-download
currentUuid:'',
uploadBox:{showUpload:false,title:this.$t('webshell.uploadTitle')},
uploadFile: {file: '',path: '',uuid: ''},
uploadFileList:[],
uploadResult:null,
downloadBox:{showDownload:false,title:this.$t('webshell.downloadTitle')},
downloadFile:{path: '',uuid: ''},
downloadFileList:[],
downloadResult:null,
}
},
methods: {
getUuid(){
let uuid = uuidv1();
let now = new Date().getTime();
uuid = uuid+"-"+now+"-"+now;
this.currentUuid = uuid;
return uuid;
},
addConsole(id,host,accountId,port){
if(!id){id=''}
if(!host){host=''}
if(!accountId){accountId=''}
if(!port){port=''}
let uuid = this.getUuid();
let newTabName = ++this.tabIndex + '';
let title = host;
if(port){
title = title+":"+port;
}
if(!title){
title=this.$t("webshell.shellTitle");
}
let width = document.body.clientWidth;//可视宽度
const console = {
title:title,
name:newTabName,
terminal: {
name:newTabName,
cols: 225,
rows: 200,
width:width,
height:this.consoleHeight,
assetId:id,
accountId:accountId,
uuid:uuid,
},
};
this.editableTabsValue = newTabName;
this.editableTabs.push(console);
setTimeout(function(){
let tabScroll = document.getElementsByClassName("el-tabs__nav is-top");
let tabViewWidth = document.getElementsByClassName("el-tabs__nav-scroll");
let scrollWidth = tabScroll[0].clientWidth;
let viewWidth = tabViewWidth[0].clientWidth;//可视宽度
if(viewWidth<scrollWidth){
tabScroll[0].style.transform = "translateX(" + (viewWidth-scrollWidth) + "px) ";//71(
}
},10)
//this.$store.commit('addConsole');
},
show(id,host,accountId,port){
this.addConsole(id,host,accountId,port);
this.consoleShow = true;
},
initDialog(){
},
//可以做最小化的处理点击窗口外的空白处会调用此方法
closeConsole(){
//关闭所有连接
this.editableTabs.forEach((tab, index) => {
this.$refs['console' + index][0].closeSocket();
});
this.editableTabs = [];
this.editableTabsValue= '-1',//当前显示的consol
this.tabIndex = -1
this.consoleShow = false;
this.isFullScreen = false;
let targetDiv= document.getElementById('shell-service');
targetDiv.style.height=this.initConsoleHeight+'px';
this.consoleHeight = this.initConsoleHeight;
this.$store.commit('closeConsole');
},
handleClick(){
},
removeTab(targetName) {
let tabs = this.editableTabs;
let activeName = this.editableTabsValue;
if (activeName === targetName) {
tabs.forEach((tab, index) => {
if (tab.name === targetName) {
let nextTab = tabs[index + 1] || tabs[index - 1];
if (nextTab) {
activeName = nextTab.name;
}
}
});
}
this.editableTabsValue = activeName;
this.editableTabs = tabs.filter(tab => tab.name !== targetName);
//关闭此console的链接
//?????????????????
this.$store.commit('removeConsole');
if(this.editableTabs.length<=0){
this.closeConsole();
}
},
/* 活动标签切换时触发 */
beforeLeave(currentName,oldName) {
var self=this;
//重点如果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;
}
},
addTab(targetName) {
this.$store.commit('addConsoleNum');
this.addConsole();
//<div role="tablist" class="el-tabs__nav is-top" style="transform: translateX(-1207.78px);">
/*
let newTabName = ++this.tabIndex + '';
this.editableTabs.push({
title: 'New Tab',
name: newTabName,
content: 'New Tab content'
});
this.editableTabsValue = newTabName;
this.currentIndex=newTabName;
*/
},
handleSelect(key, keyPath) {
//alert(keyPath);
},
/*upload--download start*/
closeDialog:function(){
this.uploadBox.showUpload=false;
this.uploadResult=null;
this.uploadFileList=[];
this.uploadFile={file: '',path: '',uuid: ''};
},
closeDownloadDialog(){
this.downloadBox.showDownload=false;
this.downloadResult=null;
this.downloadFileList=[];
this.downloadFile={path: '',uuid: ''};
},
showUploadBox(){
this.uploadBox.showUpload=true;
//this.uploadBox.width='600px';
},
/*
handleChange(file,fileList) {
this.uploadFile.file = file.raw;
},
*/
handleChange:function(file,fileList){
//if (fileList.length > 0) {
//this.uploadFileList = [fileList[fileList.length - 1]]
//}
//this.uploadFile.file = this.uploadFileList[0];
this.uploadFile.file = file;
},
upload() {
//if(this.uploadFile && this.uploadFile.file.raw){
let form = new FormData();
form.append("uuid", this.currentUuid);
form.append("file", this.uploadFile.file.raw);
form.append("path", this.uploadFile.path);
this.$post('terminal/upload', form,{'Content-Type': 'multipart/form-data'}).then(res => {
if(res.code == 200 ){
this.closeDialog();
this.$message({duration: 2000, type: 'success', message: this.$t("tip.saveSuccess")});
}else{
this.$message.error(res.msg);
}
})
//}
},
showDownloadBox(){
this.downloadBox.showDownload=true;
},
download(){
this.downloadFile.uuid = this.currentUuid;
this.$post('terminal/download',this.downloadFile,{responseType:'blob'}).then(res => {
let fileName= this.downloadFile.path.substring(this.downloadFile.path.indexOf('/')+1);
if(res.code == 200 ){
if(window.navigator.msSaveOrOpenBlob){
// 兼容ie11
let blobObject = new Blob([res.data]);
window.navigator.msSaveOrOpenBlob(blobObject, fileName);
}else{
let url = URL.createObjectURL(new Blob([res.data]));
let a = document.createElement('a');
document.body.appendChild(a); //此处增加了将创建的添加到body当中
a.href = url;
a.download = fileName;
a.target = '_blank';
a.click();
a.remove(); //将a标签移除
}
this.closeDownloadDialog();
}else{
this.$message.error(res.msg);
}
})
},
/*upload--download end*/
minScreen(){
this.consoleShow = false;
this.$store.commit('minConsole');
},
fullScreen(){
//dialog全屏
this.isFullScreen = !this.isFullScreen;
//所有的console全屏
this.editableTabs.forEach((tab, index) => {
this.$refs['console'+index][0].fullScreenConsole(this.isFullScreen);
});
if(!this.isFullScreen){
let targetDiv= document.getElementById('shell-service');
targetDiv.style.height=this.initConsoleHeight+'px';
}
//const dailog = document.getElementById("consoleDailog");
//dailog.style.width = `${100}%`;
},
dragEagle:function(e){
var targetDiv= document.getElementById('shell-service'); //e.target.parentNode.parentNode;.children[0]
//得到点击时该容器的宽高:
var targetDivHeight=targetDiv.offsetHeight;
var startY=e.clientY;
var _this=this;
document.onmousemove=function(e){
e.preventDefault();
//得到鼠标拖动的宽高距离:取绝对值
var distY=Math.abs(e.clientY-startY);
//往上方拖动:
if( e.clientY < startY){
targetDiv.style.height=targetDivHeight+distY+'px';
}
//往下方拖动:
if (e.clientY > startY) {
targetDiv.style.height=(targetDivHeight-distY)+'px';
}
let height = document.body.clientHeight;//可视高度
if(parseInt(targetDiv.style.height)>=height){
targetDiv.style.height=height+'px';
}
if(parseInt(targetDiv.style.height)<=10){
targetDiv.style.height=10+'px';
}
_this.editableTabs.forEach((tab, index) => {
_this.$refs['console'+index][0].resizeConsole(parseInt(targetDiv.style.height));
});
}
document.onmouseup=function(){
document.onmousemove=null;
_this.editableTabs.forEach((tab, index) => {
_this.$refs['console'+index][0].resizeServiceConsole(parseInt(targetDiv.style.height));
});
_this.consoleHeight = parseInt(targetDiv.style.height);
if(_this.consoleHeight===null || !_this.consoleHeight){_this.consoleHeight = _this.initConsoleHeight;}
}
},
},
watch: {
'$store.state.consoleShow':function(val){
if(val){
if(this.$store.state.isAddConsole){
let id = this.$store.state.consoleParam.id;
let host = this.$store.state.consoleParam.host;
let accountId = this.$store.state.consoleParam.accountId;
let port = this.$store.state.consoleParam.port;
this.show(id,host,accountId,port);
}else {
this.consoleShow = true;
}
this.$store.state.consoleShow = false;
}
}
},
created() {
},
mounted() {
},
}
</script>