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 1a59bce56b feat:新功能
1.webshell模块:xterm替换滚动条,统一系统样式
2.webshell模块:tab增加激活样式
3.webshell模块:quit命令后关闭当前窗口
4.暂时隐藏字体及字体大小设置
fix:修改问题
1.字体模糊
2020-03-13 19:17:19 +08:00

511 lines
19 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;'>
<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;margin-left:40px;">
<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%; margin-left:0px;border-left:solid 1px black;border-bottom: 1px solid black;'
type="border-card" >
<el-tab-pane v-for="(item, index) in editableTabs"
:key="item.name"
:label="item.title"
:name="item.name"
closable
>
<!-- tab显示的内容 1 grey,2 green, 3 red-->
<span slot="label" style="">
<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 :terminal="item.terminal" @refreshConsoleTitle="refreshTabTitle" :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";
export default {
name: 'webSSH',
components: {
'my-console': Console
},
data() {
return {
consoleShow:false,
isFullScreen:false,
initConsoleHeight:300,//只读,初始化高度
consoleHeight:300,//console高度
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,
circleColor:1,//1 grey,2 green, 3 red
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');
window.removeEventListener('resize',this.windowChange);
},
handleClick(){
},
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;
}
});
}
},
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);
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();
},
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;
},
handleChange:function(file,fileList){
if (fileList.length > 0) {
this.uploadFileList = [fileList[fileList.length - 1]]
}
this.uploadFile.file = this.uploadFileList[0];
},
upload() {
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.lastIndexOf('/')+1);
if(window.navigator.msSaveOrOpenBlob){
// 兼容ie11
let blobObject = new Blob([res]);
window.navigator.msSaveOrOpenBlob(blobObject, fileName);
}else{
let url = URL.createObjectURL(new Blob([res]));
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();
})
},
/*upload--download end*/
minScreen(){
this.consoleShow = false;
this.$store.commit('minConsole');
},
fullScreen(isChange){
//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';
this.consoleHeight=this.initConsoleHeight;
}else {
let targetDiv= document.getElementById('shell-service');
targetDiv.style.height=`${100}%`;
let height = document.body.clientHeight;//可视高度
this.consoleHeight=height;
}
if (this.editableTabs && this.editableTabs.length > 0) {
this.editableTabs.forEach((tab, index) => {
if (tab.name === this.editableTabsValue) {
this.$refs['console' + index][0].focusConsole();
}
});
}
},
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.$refs['console'+index][0].resizeServiceConsole();
});
_this.consoleHeight = parseInt(targetDiv.style.height);
if(_this.consoleHeight===null || !_this.consoleHeight){_this.consoleHeight = _this.initConsoleHeight;}
}
},
windowChange(){
//alert('winChange');
if(this.editableTabs&&this.editableTabs.length>0){
let width = document.body.clientWidth;//可视宽度
var targetDiv= document.getElementById('shell-service'); //e.target.parentNode.parentNode;.children[0]
var targetDivHeight=targetDiv.offsetHeight;
this.editableTabs.forEach((tab, index) => {
this.$refs['console'+index][0].resize(targetDivHeight,width);
});
}
}
},
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;
//min后从header区域打开获得焦点
this.$nextTick(() => {
if (this.editableTabs && this.editableTabs.length > 0) {
this.editableTabs.forEach((tab, index) => {
if (tab.name === this.editableTabsValue ) {
this.$refs['console' + index][0].focusConsole();
}
});
}
});
}
this.$store.state.consoleShow = false;
}
}
},
created() {
window.addEventListener('resize',this.windowChange);
},
mounted() {
},
}
</script>