1<template> 2 <div :class="marginClass"> 3 <div ref="toolbar" class="kvm-toolbar"> 4 <b-row class="d-flex"> 5 <b-col class="d-flex flex-column justify-content-end" cols="4"> 6 <dl class="mb-2" sm="2" md="2"> 7 <dt class="d-inline fw-bold me-1">{{ $t('pageKvm.status') }}:</dt> 8 <dd class="d-inline"> 9 <status-icon :status="serverStatusIcon" /> 10 <span class="d-none d-md-inline"> {{ serverStatus }}</span> 11 </dd> 12 </dl> 13 </b-col> 14 15 <b-col class="d-flex justify-content-end pe-1"> 16 <b-button 17 v-if="isConnected" 18 variant="link" 19 type="button" 20 @click="sendCtrlAltDel" 21 > 22 <icon-arrow-down /> 23 {{ $t('pageKvm.buttonCtrlAltDelete') }} 24 </b-button> 25 <b-button 26 v-if="!isFullWindow" 27 variant="link" 28 type="button" 29 @click="openConsoleWindow()" 30 > 31 <icon-launch /> 32 {{ $t('pageKvm.openNewTab') }} 33 </b-button> 34 </b-col> 35 </b-row> 36 </div> 37 <div id="terminal-kvm" ref="panel" :class="terminalClass"></div> 38 </div> 39</template> 40 41<script> 42import RFB from '@novnc/novnc/core/rfb'; 43import StatusIcon from '@/components/Global/StatusIcon'; 44import IconLaunch from '@carbon/icons-vue/es/launch/20'; 45import IconArrowDown from '@carbon/icons-vue/es/arrow--down/16'; 46import { throttle } from 'lodash'; 47import { useI18n } from 'vue-i18n'; 48import i18n from '@/i18n'; 49 50const Connecting = 0; 51const Connected = 1; 52const Disconnected = 2; 53 54export default { 55 name: 'KvmConsole', 56 components: { StatusIcon, IconLaunch, IconArrowDown }, 57 props: { 58 isFullWindow: { 59 type: Boolean, 60 default: true, 61 }, 62 }, 63 data() { 64 return { 65 $t: useI18n().t, 66 rfb: null, 67 isConnected: false, 68 terminalClass: this.isFullWindow ? 'full-window' : '', 69 marginClass: this.isFullWindow ? 'margin-left-full-window' : '', 70 status: Connecting, 71 convasRef: null, 72 resizeKvmWindow: null, 73 }; 74 }, 75 computed: { 76 serverStatusIcon() { 77 if (this.status === Connected) { 78 return 'success'; 79 } else if (this.status === Disconnected) { 80 return 'danger'; 81 } 82 return 'secondary'; 83 }, 84 serverStatus() { 85 if (this.status === Connected) { 86 return i18n.global.t('pageKvm.connected'); 87 } else if (this.status === Disconnected) { 88 return i18n.global.t('pageKvm.disconnected'); 89 } 90 return i18n.global.t('pageKvm.connecting'); 91 }, 92 }, 93 created() { 94 this.$store.dispatch('global/getSystemInfo'); 95 }, 96 mounted() { 97 this.openTerminal(); 98 }, 99 beforeUnmount() { 100 window.removeEventListener('resize', this.resizeKvmWindow); 101 this.closeTerminal(); 102 }, 103 methods: { 104 sendCtrlAltDel() { 105 this.rfb.sendCtrlAltDel(); 106 }, 107 closeTerminal() { 108 this.rfb.disconnect(); 109 this.rfb = null; 110 }, 111 openTerminal() { 112 const token = this.$store.getters['authentication/token']; 113 this.rfb = new RFB( 114 this.$refs.panel, 115 `wss://${window.location.host}/kvm/0`, 116 { wsProtocols: [token] }, 117 ); 118 119 this.rfb.scaleViewport = true; 120 this.rfb.clipViewport = true; 121 const that = this; 122 123 this.resizeKvmWindow = throttle(() => { 124 setTimeout(that.setWidthToolbar, 0); 125 }, 1000); 126 window.addEventListener('resize', this.resizeKvmWindow, { 127 passive: true, 128 }); 129 130 this.rfb.addEventListener('connect', () => { 131 that.isConnected = true; 132 that.status = Connected; 133 that.setWidthToolbar(); 134 }); 135 136 this.rfb.addEventListener('disconnect', () => { 137 this.isConnected = false; 138 that.status = Disconnected; 139 }); 140 }, 141 setWidthToolbar() { 142 if ( 143 this.$refs.panel.children && 144 this.$refs.panel.children.length > 0 && 145 this.$refs.panel.children[0].children.length > 0 146 ) { 147 this.$refs.toolbar.style.width = 148 this.$refs.panel.children[0].children[0].clientWidth - 10 + 'px'; 149 } 150 }, 151 openConsoleWindow() { 152 // If consoleWindow is not null 153 // Check the newly opened window is closed or not 154 if (this.$eventBus.$consoleWindow) { 155 // If window is not closed set focus to new window 156 // If window is closed, do open new window 157 if (!this.$eventBus.$consoleWindow.closed) { 158 this.$eventBus.$consoleWindow.focus(); 159 return; 160 } else { 161 this.openNewWindow(); 162 } 163 } else { 164 // If consoleWindow is null, open new window 165 this.openNewWindow(); 166 } 167 }, 168 openNewWindow() { 169 this.$eventBus.$consoleWindow = window.open( 170 '#/console/kvm', 171 'kvmConsoleWindow', 172 'directories=no,titlebar=no,toolbar=no,location=no,status=no,menubar=no,scrollbars=no,resizable=yes,width=700,height=550', 173 ); 174 }, 175 }, 176}; 177</script> 178 179<style scoped lang="scss"> 180.button-ctrl-alt-delete { 181 float: inline-end; 182} 183 184.kvm-status { 185 padding-top: calc(#{$spacer} / 2); 186 padding-inline-start: calc(#{$spacer} / 4); 187 display: inline-block; 188} 189 190.margin-left-full-window { 191 margin-inline-start: 5px; 192} 193</style> 194