|
12 | 12 | {{ online ? "Connect" : "Offline" }}
|
13 | 13 | </v-btn>
|
14 | 14 | </template>
|
| 15 | + |
15 | 16 | <v-dialog
|
16 | 17 | v-model="showTerminal"
|
17 | 18 | :fullscreen="!showLoginForm || $vuetify.display.smAndDown"
|
18 | 19 | :max-width="$vuetify.display.smAndDown ? undefined : $vuetify.display.thresholds.sm"
|
19 | 20 | @click:outside="close"
|
20 |
| - @keyup.esc="close()" |
21 |
| - persistent |
22 | 21 | >
|
23 | 22 | <v-card data-test="terminal-card" class="bg-v-theme-surface">
|
24 | 23 | <v-card-title
|
|
29 | 28 | <v-icon @click="close()" data-test="close-btn" class="bg-primary" size="24">mdi-close</v-icon>
|
30 | 29 | </v-card-title>
|
31 | 30 |
|
32 |
| - <div class="ma-0 pa-0 w-100 fill-height position-relative"> |
33 |
| - <div v-if="!showLoginForm" ref="terminal" class="terminal" /> |
| 31 | + <div class="ma-0 pa-0 w-100 fill-height position-relative" v-if="!showLoginForm"> |
| 32 | + <div ref="terminal" class="terminal" /> |
34 | 33 | </div>
|
35 | 34 |
|
36 | 35 | <div class="mt-2" v-if="showLoginForm">
|
@@ -166,14 +165,16 @@ import {
|
166 | 165 | computed,
|
167 | 166 | nextTick,
|
168 | 167 | watch,
|
| 168 | + onMounted, |
| 169 | + onBeforeUnmount, |
169 | 170 | } from "vue";
|
170 | 171 | import { useField } from "vee-validate";
|
171 | 172 | import "xterm/css/xterm.css";
|
172 | 173 | import { Terminal } from "xterm";
|
173 | 174 | import { FitAddon } from "xterm-addon-fit";
|
174 | 175 | import * as yup from "yup";
|
175 | 176 | import axios from "axios";
|
176 |
| -import { useEventListener, onKeyStroke } from "@vueuse/core"; |
| 177 | +import { useEventListener } from "@vueuse/core"; |
177 | 178 | import { useRoute } from "vue-router";
|
178 | 179 | import { useStore } from "../../store";
|
179 | 180 | import {
|
@@ -253,14 +254,14 @@ const nameOfPrivateKeys = computed(() => {
|
253 | 254 | return list.map((item: IPrivateKey) => item.name);
|
254 | 255 | });
|
255 | 256 |
|
256 |
| -useEventListener(window, "resize", (evt) => { |
| 257 | +useEventListener(window, "resize", () => { |
257 | 258 | nextTick(() => {
|
258 | 259 | fitAddon.value.fit();
|
259 | 260 | });
|
260 | 261 | });
|
261 | 262 |
|
262 | 263 | watch(showTerminal, (value) => {
|
263 |
| - if (value) showLoginForm.value = true; |
| 264 | + if (!value) showLoginForm.value = true; |
264 | 265 | });
|
265 | 266 |
|
266 | 267 | const encodeURLParams = (params: IParams) => Object.entries(params)
|
@@ -375,8 +376,8 @@ const open = () => {
|
375 | 376 | }
|
376 | 377 | };
|
377 | 378 |
|
378 |
| -watch(() => route.path, (Path) => { |
379 |
| - if (Path === `/devices/${props.uid}/terminal`) { |
| 379 | +watch(() => route.path, (path) => { |
| 380 | + if (path === `/devices/${props.uid}/terminal`) { |
380 | 381 | open();
|
381 | 382 | }
|
382 | 383 | }, { immediate: true });
|
@@ -423,8 +424,24 @@ const close = () => {
|
423 | 424 | store.dispatch("modal/toggleTerminal", "");
|
424 | 425 | };
|
425 | 426 |
|
426 |
| -onKeyStroke("Escape", () => { |
427 |
| - close(); |
| 427 | +let lastEscPress = 0; |
| 428 | +
|
| 429 | +const handleEscKey = (event: KeyboardEvent) => { |
| 430 | + if (event.key === "Escape" && !showLoginForm.value) { |
| 431 | + const currentTime = new Date().getTime(); |
| 432 | + if (currentTime - lastEscPress < 400) { |
| 433 | + close(); |
| 434 | + } |
| 435 | + lastEscPress = currentTime; |
| 436 | + } |
| 437 | +}; |
| 438 | +
|
| 439 | +onMounted(() => { |
| 440 | + window.addEventListener("keyup", handleEscKey); |
| 441 | +}); |
| 442 | +
|
| 443 | +onBeforeUnmount(() => { |
| 444 | + window.removeEventListener("keyup", handleEscKey); |
428 | 445 | });
|
429 | 446 |
|
430 | 447 | defineExpose({ open, showTerminal, showLoginForm, encodeURLParams, connect, privateKey, xterm, fitAddon, ws, close });
|
|
0 commit comments