vue3 终端实现 (vue3+xterm+websocket)

 

目录

一、xterm介绍

二、效果展示

三、vue文件实现代码


一、xterm介绍

xterm是一个使用 TypeScript 编写的前端终端组件,可以直接在浏览器中实现一个命令行终端应用,通常与websocket一起使用。

二、效果展示

三、vue文件实现代码

<template>
  <div class="bg-main">
    <div
      ref="terminal"
      v-loading="loading"
      class="terminal"
      element-loading-text="拼命连接中"
    ></div>
  </div>
</template>
<script setup>
  import { ref, onMounted, onBeforeUnmount } from 'vue'
  import { debounce } from 'lodash'
  import { Terminal } from 'xterm'
  import { FitAddon } from 'xterm-addon-fit'
  import 'xterm/css/xterm.css'

  const terminal = ref(null)
  const fitAddon = new FitAddon()

  let first = ref(true)
  let loading = ref(true)
  let terminalSocket = ref(null)
  let term = ref(null)

  // 初始化WS
  const initWS = () => {
    if (!terminalSocket.value) {
      createWS()
    }
    if (terminalSocket.value && terminalSocket.value.readyState > 1) {
      terminalSocket.value.close()
      createWS()
    }
  }

  // 创建WS
  const createWS = () => {
    // const url = `/access/Api/ws/ssh/b172df81-2485-453d-a6ff-120c03821536?userName=test&passwd=1`
    terminalSocket.value = new WebSocket(
      `wss://XXXX`
    )
    terminalSocket.value.onopen = runRealTerminal //WebSocket 连接已建立
    terminalSocket.value.onmessage = onWSReceive //收到服务器消息
    terminalSocket.value.onclose = closeRealTerminal //WebSocket 连接已关闭
    terminalSocket.value.onerror = errorRealTerminal //WebSocket 连接出错
  }

  //WebSocket 连接已建立
  const runRealTerminal = () => {
    loading.value = false
  }
  //WebSocket收到服务器消息
  const onWSReceive = (message) => {
    // 首次接收消息,发送给后端,进行同步适配尺寸
    if (first.value === true) {
      first.value = false
      resizeRemoteTerminal()
    }
    const data = message.data
    // base64解密
    const reader = new FileReader()
    reader.onload = function (e) {
      const base64Content = e.target.result
      console.log(base64Content, 1)
      term.value.write(base64Content)
    }
    reader.readAsText(data) // 以text文本显示readAsText
    term.value.element && term.value.focus()
  }
  //WebSocket 连接出错
  const errorRealTerminal = (ex) => {
    let message = ex.message
    if (!message) message = 'disconnected'
    term.value.write(`\x1b[31m${message}\x1b[m\r\n`)
    console.log('err')
  }
  //WebSocket 连接已关闭
  const closeRealTerminal = () => {
    console.log('close')
  }

  // 初始化Terminal
  const initTerm = () => {
    term.value = new Terminal({
      // lineHeight: 1.2,
      fontSize: 14,
      fontFamily: "Monaco, Menlo, Consolas, 'Courier New', monospace",
      theme: {
        background: '#181d28',
      },
      // 光标闪烁
      cursorBlink: true,
      cursorStyle: 'underline',
      // scrollback: 100,
      // tabStopWidth: 4,
    })
    term.value.open(terminal.value) //挂载dom窗口
    term.value.loadAddon(fitAddon) //自适应尺寸
    // 不能初始化的时候fit,需要等terminal准备就绪,可以设置延时操作
    setTimeout(() => {
      fitAddon.fit()
    }, 5)
    termData() //Terminal 事件挂载
  }

  // 终端输入触发事件
  const termData = () => {
    // 输入与粘贴的情况,onData不能重复绑定,不然会发送多次
    term.value.onData((data) => {
      console.log(data, '传入服务器')
      if (isWsOpen()) {
        terminalSocket.value.send(
          JSON.stringify({
            type: 'terminal',
            data: {
              base64: btoa(data),
            },
          })
        )
      }
    })
    // 终端尺寸变化触发
    term.value.onResize(() => {
      resizeRemoteTerminal()
    })
  }

  //尺寸同步 发送给后端,调整后端终端大小,和前端保持一致,不然前端只是范围变大了,命令还是会换行
  const resizeRemoteTerminal = () => {
    const { cols, rows } = term.value
    if (isWsOpen()) {
      terminalSocket.value.send(
        JSON.stringify({
          type: 'resize',
          data: {
            rows: rows,
            cols: cols,
          },
        })
      )
    }
  }

  // 是否连接中0 1 2 3 状态
  const isWsOpen = () => {
    const readyState = terminalSocket.value && terminalSocket.value.readyState
    return readyState === 1
  }

  // 适应浏览器尺寸变化
  const fitTerm = () => {
    fitAddon.fit()
  }
  const onResize = debounce(() => fitTerm(), 500)
  const onTerminalResize = () => {
    window.addEventListener('resize', onResize)
  }
  const removeResizeListener = () => {
    window.removeEventListener('resize', onResize)
  }

  onMounted(() => {
    initWS()
    initTerm()
    onTerminalResize()
  })

  onBeforeUnmount(() => {
    removeResizeListener()
    terminalSocket.value && terminalSocket.value.close()
  })
</script>
<style lang="scss" scoped>
  .terminal {
    width: 100%;
    height: calc(100% - 62px);
  }
</style>

  • 12
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
要在 Vue 中使用 xterm.js 和 WebSocket 实现终端,你需要将用户输入的命令发送给后端,然后将后端返回的结果输出到 xterm.js 终端中。以下是一个简单的示例: ```html <template> <div id="terminal"></div> </template> <script> import { Terminal } from 'xterm'; import { FitAddon } from 'xterm-addon-fit'; export default { data() { return { socket: null, // WebSocket 实例 term: null, // Terminal 实例 }; }, mounted() { // 创建 WebSocket 实例 this.socket = new WebSocket('ws://localhost:8080'); // 创建 Terminal 实例 this.term = new Terminal(); const fitAddon = new FitAddon(); this.term.loadAddon(fitAddon); this.term.open(document.getElementById('terminal')); // 处理 WebSocket 消息 this.socket.onmessage = (event) => { this.term.write(event.data); }; // 处理输入事件 this.term.onData(data => { this.socket.send(data); }); // 调整终端大小 this.term.onResize(size => { const cols = size.cols; const rows = size.rows; this.socket.send(JSON.stringify({ type: 'resize', cols, rows })); }); // 发送 resize 消息 const cols = this.term.cols; const rows = this.term.rows; this.socket.send(JSON.stringify({ type: 'resize', cols, rows })); }, beforeDestroy() { // 关闭 WebSocket 连接 this.socket.close(); } } </script> ``` 以上代码中,我们首先在 `mounted` 钩子函数中创建了一个 WebSocket 实例和一个 Terminal 实例。然后我们为 WebSocket 实例添加了一个 `onmessage` 事件监听器,该监听器会在接收到服务器返回的消息时触发,我们在该事件处理函数中将消息输出到终端中。 接着,我们为 Terminal 实例添加了一个 `onData` 事件监听器,该监听器会在用户输入时触发,我们在该事件处理函数中向服务器发送用户输入的命令。同时,我们还为 Terminal 实例添加了一个 `onResize` 事件监听器,该监听器会在终端大小调整时触发,我们在该事件处理函数中向服务器发送终端大小变化的消息。 最后,我们在 `beforeDestroy` 钩子函数中关闭了 WebSocket 连接。 需要注意的是,以上代码中的 WebSocket 连接是通过 `ws://localhost:8080` 连接本地服务器的,你需要根据实际情况修改 WebSocket 连接地址。另外,代码中的消息格式和处理逻辑也需要根据实际情况进行修改。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天高任鸟飞dyz

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值