mirror of
https://github.com/harvester/harvester-ui-extension.git
synced 2025-12-13 21:21:44 +00:00
350 lines
7.7 KiB
Vue
350 lines
7.7 KiB
Vue
<script>
|
|
import { STEVE } from '@shell/config/types';
|
|
import { escapeHtml } from '@shell/utils/string';
|
|
import { allHash } from '@shell/utils/promise';
|
|
import KeyTable from '@novnc/novnc/core/input/keysym';
|
|
import * as KeyboardUtil from '@novnc/novnc/core/input/util';
|
|
import { HCI } from '../../types';
|
|
import NovncConsole from './NovncConsole';
|
|
import NovncConsoleItem from './NovncConsoleItem';
|
|
import NovncConsoleCustomKeys from './NovncConsoleCustomKeys';
|
|
|
|
const PREFERED_SHORTCUT_KEYS = 'prefered-shortcut-keys';
|
|
|
|
const SHORT_KEYS = {
|
|
ControlLeft: {
|
|
label: 'Ctrl',
|
|
value: KeyTable.XK_Control_L,
|
|
},
|
|
AltLeft: {
|
|
label: 'Alt',
|
|
value: KeyTable.XK_Alt_L,
|
|
}
|
|
};
|
|
|
|
const FUNCTION_KEYS = {
|
|
Delete: {
|
|
label: 'Del',
|
|
value: KeyTable.XK_Delete,
|
|
},
|
|
PrintScreen: {
|
|
label: 'Print Screen',
|
|
value: KeyTable.XK_Print,
|
|
},
|
|
};
|
|
|
|
const NORMAL_KEYS = {
|
|
KeyN: {
|
|
label: 'N',
|
|
value: KeyTable.XK_n,
|
|
},
|
|
KeyT: {
|
|
label: 'T',
|
|
value: KeyTable.XK_t,
|
|
},
|
|
KeyW: {
|
|
label: 'W',
|
|
value: KeyTable.XK_w,
|
|
},
|
|
KeyY: {
|
|
label: 'Y',
|
|
value: KeyTable.XK_y,
|
|
},
|
|
};
|
|
|
|
const F_KEYS = {
|
|
F1: {
|
|
label: 'F1',
|
|
value: KeyTable.XK_F1,
|
|
},
|
|
F2: {
|
|
label: 'F2',
|
|
value: KeyTable.XK_F2,
|
|
},
|
|
F3: {
|
|
label: 'F3',
|
|
value: KeyTable.XK_F3,
|
|
},
|
|
F4: {
|
|
label: 'F4',
|
|
value: KeyTable.XK_F4,
|
|
},
|
|
F5: {
|
|
label: 'F5',
|
|
value: KeyTable.XK_F5,
|
|
},
|
|
F6: {
|
|
label: 'F6',
|
|
value: KeyTable.XK_F6,
|
|
},
|
|
F7: {
|
|
label: 'F7',
|
|
value: KeyTable.XK_F7,
|
|
},
|
|
F8: {
|
|
label: 'F8',
|
|
value: KeyTable.XK_F8,
|
|
},
|
|
F9: {
|
|
label: 'F9',
|
|
value: KeyTable.XK_F9,
|
|
},
|
|
F10: {
|
|
label: 'F10',
|
|
value: KeyTable.XK_F10,
|
|
},
|
|
F11: {
|
|
label: 'F11',
|
|
value: KeyTable.XK_F11,
|
|
},
|
|
F12: {
|
|
label: 'F12',
|
|
value: KeyTable.XK_F12,
|
|
},
|
|
};
|
|
|
|
export default {
|
|
components: {
|
|
NovncConsole, NovncConsoleItem, NovncConsoleCustomKeys
|
|
},
|
|
|
|
async fetch() {
|
|
const _hash = { vmResource: this.$store.dispatch('harvester/find', { type: HCI.VM, id: this.value.id }) };
|
|
|
|
const hash = await allHash(_hash);
|
|
|
|
this.vmResource = hash.vmResource;
|
|
},
|
|
|
|
props: {
|
|
value: {
|
|
type: Object,
|
|
required: true,
|
|
default: () => {
|
|
return {};
|
|
}
|
|
}
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
keysRecord: [],
|
|
vmResource: {},
|
|
renderKeysModal: false,
|
|
currentUser: null,
|
|
hideCustomKeysBar: false,
|
|
};
|
|
},
|
|
|
|
computed: {
|
|
savedShortcutKeys() {
|
|
const preference = this.$store.getters['management/all'](STEVE.PREFERENCE);
|
|
const preferedShortcutKeys = preference?.[0]?.data?.[PREFERED_SHORTCUT_KEYS];
|
|
let out = [];
|
|
|
|
if (!preference?.[0]?.data) {
|
|
// eslint-disable-next-line vue/no-side-effects-in-computed-properties
|
|
this.hideCustomKeysBar = true;
|
|
|
|
return out;
|
|
}
|
|
|
|
if (!preferedShortcutKeys) {
|
|
return out;
|
|
}
|
|
|
|
try {
|
|
out = JSON.parse(preferedShortcutKeys);
|
|
} catch (err) {
|
|
this.$store.dispatch('growl/fromError', {
|
|
title: this.t('generic.notification.title.error', { name: escapeHtml(this.value.metadata.name) }),
|
|
err,
|
|
}, { root: true });
|
|
}
|
|
|
|
return out;
|
|
},
|
|
|
|
isDown() {
|
|
return this.isEmpty(this.value);
|
|
},
|
|
|
|
url() {
|
|
const ip = `${ window.location.hostname }:${ window.location.port }`;
|
|
|
|
return `wss://${ ip }${ this.value?.getVMIApiPath }`;
|
|
},
|
|
|
|
allKeys() {
|
|
return {
|
|
...SHORT_KEYS,
|
|
...FUNCTION_KEYS,
|
|
...NORMAL_KEYS,
|
|
...F_KEYS,
|
|
};
|
|
},
|
|
|
|
keymap() {
|
|
const out = {
|
|
...SHORT_KEYS,
|
|
PrintScreen: FUNCTION_KEYS.PrintScreen,
|
|
...F_KEYS,
|
|
};
|
|
|
|
out.AltLeft.keys = { PrintScreen: FUNCTION_KEYS.PrintScreen, ...F_KEYS };
|
|
out.ControlLeft.keys = {
|
|
AltLeft: {
|
|
...Object.assign(SHORT_KEYS.AltLeft, {}),
|
|
keys: { Delete: FUNCTION_KEYS.Delete }
|
|
},
|
|
...NORMAL_KEYS,
|
|
};
|
|
|
|
return out;
|
|
},
|
|
|
|
hasSoftRebootAction() {
|
|
return !!this.vmResource?.actions?.softreboot;
|
|
},
|
|
|
|
preferredShortcutKeys() {
|
|
return (this.savedShortcutKeys || []).map((item) => {
|
|
return {
|
|
label: item.map(K => K.key.charAt(0).toUpperCase() + K.key.slice(1)).join('+'),
|
|
value: item
|
|
};
|
|
});
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
isEmpty(o) {
|
|
return o !== undefined && Object.keys(o).length === 0;
|
|
},
|
|
|
|
close() {
|
|
this.$refs.novncConsole.disconnect();
|
|
},
|
|
|
|
update({ key, pos }) {
|
|
this.keysRecord.splice(pos, this.keysRecord.length - pos, key);
|
|
},
|
|
|
|
// Send function key, e.g. ALT + F
|
|
sendKeys() {
|
|
this.keysRecord.forEach((key) => {
|
|
this.$refs.novncConsole.sendKey(this.allKeys[key].value, key, true);
|
|
});
|
|
|
|
this.keysRecord.reverse().forEach((key) => {
|
|
this.$refs.novncConsole.sendKey(this.allKeys[key].value, key, false);
|
|
});
|
|
|
|
this.$refs.popover.isOpen = false;
|
|
this.keysRecord = [];
|
|
},
|
|
|
|
sendCustomKeys(keys) {
|
|
const keyList = [].concat(keys);
|
|
|
|
keyList.forEach((K) => {
|
|
this.$refs.novncConsole.sendKey(KeyboardUtil.getKeysym(K), KeyboardUtil.getKeycode(K), true);
|
|
});
|
|
|
|
keyList.reverse().forEach((K) => {
|
|
this.$refs.novncConsole.sendKey(KeyboardUtil.getKeysym(K), KeyboardUtil.getKeycode(K), false);
|
|
});
|
|
},
|
|
|
|
softReboot() {
|
|
this.vmResource.softrebootVM();
|
|
},
|
|
|
|
showKeysModal() {
|
|
this.renderKeysModal = true;
|
|
this.$nextTick(() => {
|
|
this.$refs.keysModal.show();
|
|
});
|
|
},
|
|
|
|
hideKeysModal() {
|
|
this.renderKeysModal = false;
|
|
},
|
|
}
|
|
};
|
|
</script>
|
|
|
|
<template>
|
|
<div id="app">
|
|
<div class="vm-console">
|
|
<div class="combination-keys">
|
|
<v-popover
|
|
ref="popover"
|
|
placement="top"
|
|
trigger="click"
|
|
:container="false"
|
|
@auto-hide="keysRecord = []"
|
|
>
|
|
<button class="btn btn-sm bg-primary">
|
|
{{ t("harvester.virtualMachine.detail.console.shortcutKeys") }}
|
|
</button>
|
|
|
|
<template v-slot:popover>
|
|
<novnc-console-item :items="keymap" :path="keysRecord" :pos="0" @update="update" @send-keys="sendKeys" />
|
|
</template>
|
|
</v-popover>
|
|
|
|
<button v-if="hasSoftRebootAction" class="btn btn-sm bg-primary" @click="softReboot">
|
|
{{ t("harvester.action.softreboot") }}
|
|
</button>
|
|
|
|
<v-popover
|
|
v-if="!hideCustomKeysBar"
|
|
ref="customKeyPopover"
|
|
placement="top"
|
|
trigger="click"
|
|
:container="false"
|
|
>
|
|
<button class="btn btn-sm bg-primary">
|
|
{{ t("harvester.virtualMachine.detail.console.customShortcutKeys") }}
|
|
</button>
|
|
|
|
<template v-slot:popover>
|
|
<div>
|
|
<button class="btn btn-sm bg-primary" @click="showKeysModal">
|
|
{{ t("harvester.virtualMachine.detail.console.management") }}
|
|
</button>
|
|
</div>
|
|
|
|
<hr>
|
|
|
|
<div v-for="(keys, index) in preferredShortcutKeys" :key="index">
|
|
<button class="btn btn-sm bg-primary" @click="sendCustomKeys(keys.value)">
|
|
{{ keys.label }}
|
|
</button>
|
|
</div>
|
|
</template>
|
|
</v-popover>
|
|
|
|
<NovncConsoleCustomKeys v-if="renderKeysModal" ref="keysModal" :current-user="currentUser" @close="hideKeysModal" />
|
|
</div>
|
|
<NovncConsole v-if="url && !isDown" ref="novncConsole" :url="url" />
|
|
<p v-if="isDown">
|
|
{{ t("harvester.virtualMachine.detail.console.down") }}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style lang="scss" scoped>
|
|
.vm-console {
|
|
height: 100%;
|
|
display: grid;
|
|
grid-template-rows: 30px auto;
|
|
}
|
|
|
|
.combination-keys {
|
|
background: rgb(40, 40, 40);
|
|
}
|
|
</style>
|