fix: redis终端改为命令输入框,禁止窗口输入
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
import { message } from 'ant-design-vue/es';
|
||||||
|
import { ref, reactive, onMounted, onBeforeUnmount, nextTick } from 'vue';
|
||||||
import { FitAddon } from '@xterm/addon-fit';
|
import { FitAddon } from '@xterm/addon-fit';
|
||||||
import { Terminal } from '@xterm/xterm';
|
import { Terminal } from '@xterm/xterm';
|
||||||
import '@xterm/xterm/css/xterm.css';
|
import '@xterm/xterm/css/xterm.css';
|
||||||
@@ -31,8 +32,91 @@ const terminalDom = ref<HTMLElement | undefined>(undefined);
|
|||||||
/**终端输入实例对象 */
|
/**终端输入实例对象 */
|
||||||
const terminal = ref<any>(null);
|
const terminal = ref<any>(null);
|
||||||
|
|
||||||
/**终端输入命令 */
|
/**终端输入文字状态 */
|
||||||
const terminalCmd = ref<string>('');
|
const terminalState = reactive<{
|
||||||
|
/**输入值 */
|
||||||
|
text: string;
|
||||||
|
/**历史 */
|
||||||
|
history: {
|
||||||
|
label?: string;
|
||||||
|
value: string;
|
||||||
|
}[];
|
||||||
|
}>({
|
||||||
|
text: '',
|
||||||
|
history: [
|
||||||
|
{
|
||||||
|
value: 'info server',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'info replication',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'keys ausf:*',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'quit',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
/**自动完成根据输入项进行筛选 */
|
||||||
|
function fnAutoCompleteFilter(input: string, option: any) {
|
||||||
|
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**自动完成按键触发 */
|
||||||
|
function fnAutoCompleteKeydown(evt: KeyboardEvent) {
|
||||||
|
if (evt.key === 'Enter') {
|
||||||
|
// 阻止默认的换行行为
|
||||||
|
evt.preventDefault();
|
||||||
|
// 按下 Shift + Enter 键时换行
|
||||||
|
if (evt.shiftKey && evt.target) {
|
||||||
|
// 插入换行符
|
||||||
|
const textarea = evt.target as HTMLInputElement;
|
||||||
|
const start = textarea.selectionStart || 0;
|
||||||
|
const end = textarea.selectionEnd || 0;
|
||||||
|
const text = textarea.value;
|
||||||
|
textarea.value = text.substring(0, start) + '\n' + text.substring(end);
|
||||||
|
terminalState.text = textarea.value;
|
||||||
|
// 更新光标位置
|
||||||
|
textarea.selectionStart = textarea.selectionEnd = start + 1;
|
||||||
|
} else {
|
||||||
|
// ws未连接
|
||||||
|
if (ws.state() !== WebSocket.OPEN) {
|
||||||
|
message.error('disconnected');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 输入历史
|
||||||
|
const cmdStr = terminalState.text.trim().replace(/\n/g, '\r\n');
|
||||||
|
const hisIndex = terminalState.history.findIndex(
|
||||||
|
item => item.value === cmdStr
|
||||||
|
);
|
||||||
|
if (hisIndex === -1) {
|
||||||
|
terminalState.history.push({
|
||||||
|
value: cmdStr,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送文本
|
||||||
|
terminal.value.scrollToBottom();
|
||||||
|
terminal.value.writeln('\r\n> ' + cmdStr);
|
||||||
|
ws.send({
|
||||||
|
requestId: `redis_${props.hostId}`,
|
||||||
|
type: 'redis',
|
||||||
|
data: `${cmdStr}\r\n`,
|
||||||
|
});
|
||||||
|
terminalState.text = '';
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
|
if (['q', 'quit', 'exit'].includes(cmdStr)) {
|
||||||
|
setTimeout(() => {
|
||||||
|
ws.close();
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**终端输入渲染 */
|
/**终端输入渲染 */
|
||||||
function handleRanderXterm(container: HTMLElement | undefined) {
|
function handleRanderXterm(container: HTMLElement | undefined) {
|
||||||
@@ -49,47 +133,13 @@ function handleRanderXterm(container: HTMLElement | undefined) {
|
|||||||
scrollback: 1000,
|
scrollback: 1000,
|
||||||
scrollSensitivity: 15,
|
scrollSensitivity: 15,
|
||||||
tabStopWidth: 4,
|
tabStopWidth: 4,
|
||||||
disableStdin: false, // 禁止输入
|
disableStdin: true, // 禁止输入
|
||||||
});
|
});
|
||||||
// 挂载
|
// 挂载
|
||||||
xterm.open(container);
|
xterm.open(container);
|
||||||
// 自适应尺寸
|
// 自适应尺寸
|
||||||
const fitAddon = new FitAddon();
|
const fitAddon = new FitAddon();
|
||||||
xterm.loadAddon(fitAddon);
|
xterm.loadAddon(fitAddon);
|
||||||
// 终端输入字符按键监听
|
|
||||||
xterm.onKey(({ key, domEvent }) => {
|
|
||||||
// console.log(key, domEvent);
|
|
||||||
// 单键输入
|
|
||||||
switch (domEvent.key) {
|
|
||||||
case 'Enter':
|
|
||||||
const cmdStr = terminalCmd.value.trim();
|
|
||||||
// 发送文本
|
|
||||||
terminal.value.scrollToBottom();
|
|
||||||
terminal.value.writeln('\r\n');
|
|
||||||
ws.send({
|
|
||||||
requestId: `redis_${props.hostId}`,
|
|
||||||
type: 'redis',
|
|
||||||
data: `${cmdStr}\r\n`,
|
|
||||||
});
|
|
||||||
terminalCmd.value = '';
|
|
||||||
|
|
||||||
// 退出登录
|
|
||||||
if ('quit' === cmdStr) {
|
|
||||||
setTimeout(() => {
|
|
||||||
ws.close();
|
|
||||||
}, 1000);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'Backspace':
|
|
||||||
// 处理退格键,删除最后一个字符
|
|
||||||
xterm.write('\b \b');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
xterm.write(key);
|
|
||||||
terminalCmd.value += key;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 创建 ResizeObserver 实例
|
// 创建 ResizeObserver 实例
|
||||||
var observer = new ResizeObserver(entries => {
|
var observer = new ResizeObserver(entries => {
|
||||||
fitAddon.fit();
|
fitAddon.fit();
|
||||||
@@ -213,7 +263,21 @@ defineExpose({
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="terminal">
|
<div class="terminal">
|
||||||
<div ref="terminalDom" :id="id" class="terminal"></div>
|
<div ref="terminalDom" style="height: calc(100% - 36px)" :id="id"></div>
|
||||||
|
<a-auto-complete
|
||||||
|
v-model:value="terminalState.text"
|
||||||
|
:dropdown-match-select-width="500"
|
||||||
|
style="width: 100%"
|
||||||
|
:options="terminalState.history"
|
||||||
|
:filter-option="fnAutoCompleteFilter"
|
||||||
|
:defaultActiveFirstOption="false"
|
||||||
|
>
|
||||||
|
<a-textarea
|
||||||
|
:auto-size="{ minRows: 1, maxRows: 6 }"
|
||||||
|
placeholder="Execute command. Shift+Enter to line feed, Enter to send"
|
||||||
|
@keypress="fnAutoCompleteKeydown"
|
||||||
|
/>
|
||||||
|
</a-auto-complete>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user