当前位置: 首页 > 工具软件 > Web Terminal > 使用案例 >

基于Vue2.x+xterm4.x实现终端terminal

笪烨
2023-12-01

一、安装

npm install --save xterm   xterm-addon-fit xterm-addon-attach

xterm-addon-fit

xterm.js的插件,使终端的尺寸适合包含元素。

xterm-addon-attach

xterm.js的附加组件,用于附加到Web Socket

二、代码

<template>
  <div class="box">
    <div id="xterm" class="xterm-box"></div>
  </div>
</template>

<script>
import { Terminal } from 'xterm'
import { FitAddon } from 'xterm-addon-fit'
import 'xterm/css/xterm.css'
import 'xterm/lib/xterm.js'

export default {
  data() {
    return {
      term: null,
      socket: '',
      WebSocketUrl: 'ws:192.168.5.10:8080/webide/terminal/17317181717',
      //WebSocketUrl: 'ws:192.168.5.10:8080/terminal/17317181717', //ws接口ws:192.168.5.10:8080/terminal/17317181717
      // 心跳
      lockReconnect: false, //是否真正建立连接
      timeout: 60 * 1000, //60秒一次心跳
      timeoutObj: null, //心跳心跳倒计时
      serverTimeoutObj: null, //心跳倒计时
      timeoutnum: null, //断开 重连倒计时
    }
  },
  mounted() {
    this.init(this.WebSocketUrl)
  },
  //跑路前清除定时器
  beforeDestroy() {
    this.close()
    clearTimeout(this.timeoutObj)
    clearTimeout(this.serverTimeoutObj)
    clearTimeout(this.timeoutnum)
  },
  methods: {
    // 心跳函数--------------
    reconnect() {
      //重新连接
      var that = this
      if (that.lockReconnect) {
        return
      }
      that.lockReconnect = true
      //没连接上会一直重连,设置延迟避免请求过多
      that.timeoutnum && clearTimeout(that.timeoutnum)
      that.timeoutnum = setTimeout(function () {
        //新连接
        that.init(that.WebSocketUrl)
        that.lockReconnect = false
      }, 2000)
    },
    reset() {
      //重置心跳
      var that = this
      //清除时间
      clearTimeout(that.timeoutObj)
      clearTimeout(that.serverTimeoutObj)
      //重启心跳
      that.start()
    },
    start() {
      //开启心跳
      var self = this
      self.timeoutObj && clearTimeout(self.timeoutObj)
      self.serverTimeoutObj && clearTimeout(self.serverTimeoutObj)
      self.timeoutObj = setTimeout(function () {
        //这里发送一个心跳,后端收到后,返回一个心跳消息,
        if (self.socket.readyState == 1) {
          //如果连接正常,有事没事发ping,具体根据要求
          //self.socket.send('ping')
        } else {
          //否则重连
          self.reconnect()
        }
        self.serverTimeoutObj = setTimeout(function () {
          //超时关闭
          self.close()
        }, self.timeout)
      }, self.timeout)
    },
    //-----------------

    initXterm() {
      if (this.term) {
        this.term.dispose()
      }
      let height = document.body.clientHeight
      this.term = new Terminal({
        rendererType: 'canvas', //渲染类型
        rows: 35, //行数 18是字体高度,根据需要自己修改
        convertEol: true, //启用时,光标将设置为下一行的开头
        scrollback: 800, //终端中的回滚量
        disableStdin: false, //是否应禁用输入
        cursorStyle: 'underline', //光标样式
        cursorBlink: true, //光标闪烁
        tabStopWidth: 8, //制表宽度
        screenKeys: true,
        theme: {
          foreground: 'yellow', //字体
          background: '#060101', //背景色
          cursor: 'help', //设置光标
        },
      })
      this.term.writeln('Welcome to use Superman. ')
      this.term.open(document.getElementById('xterm'))
      const fitAddon = new FitAddon()
      this.term.loadAddon(fitAddon)
      fitAddon.fit() //全屏

      //window.addEventListener('resize', this.resizeScreen)

      // 支持输入与粘贴方法
      let _this = this //一定要重新定义一个this,不然this指向会出问题
      let code = ''
      let codeArr = '' //存储输入集合
      this.term.onData(function (key) {
        //codeArr += key
        //_this.term.write(key)
        let order = { operate: 'command', command: key }
        //这里key值是你输入的值,数据格式order一定要找后端要!!!!

        //if (key.length > 1) _this.term.write(key) //粘贴

        //console.log(key, JSON.stringify(key))

        _this.socket.onsend(JSON.stringify(order))
      })
    },


    init(url) {
      // 实例化socket
      this.socket = new WebSocket(url)
      // 监听socket连接
      this.socket.onopen = this.open
      // 监听socket错误信息
      this.socket.onerror = this.error
      // 监听socket消息
      this.socket.onmessage = this.getMessage
      // 发送socket消息
      this.socket.onsend = this.send
    },
    open: function () {
      console.log('socket连接成功')
      //this.term.write('Welcome to use')
      this.initXterm()
      //开启心跳
      this.start()
    },
    error: function () {
      console.log('连接错误')
      //重连
      this.reconnect()
    },
    close: function () {
      this.socket.close()
      console.log('socket已经关闭')
      //重连
      this.reconnect()
    },

    getMessage: function (msg) {
      //msg是返回的数据

      //msg = JSON.parse(msg.data)
      //msg = msg.data
      //console.log(msg)
      //this.socket.send('ping') //有事没事ping一下,看看ws还活着没
      //switch用于处理返回的数据,根据返回数据的格式去判断
      // switch (msg['operation']) {
      //   case 'stdout':
      //     this.term.write(msg['data']) //这里write也许不是固定的,失败后找后端看一下该怎么往term里面write
      //     break
      //   default:
      //     console.log('Unexpected message type:', msg) //但是错误是固定的。。。。
      // }

      this.term.write(msg['data']) //这里write也许不是固定的,失败后找后端看一下该怎么往term里面write
      //收到服务器信息,心跳重置
      this.reset()
    },
    send: function (order) {
      //console.log(order)
      this.socket.send(order)
    },
    resizeScreen(size) {
      console.log('size', size)
      var fitAddon = new FitAddon()
      term.loadAddon(fitAddon)
      try {
        fitAddon.fit()
        // 窗口大小改变时触发xterm的resize方法,向后端发送行列数,格式由后端决定
        term.onResize((size) => {
          //_this.onSend({ Op: 'resize', Cols: size.cols, Rows: size.rows })
        })
      } catch (e) {
        console.log('e', e.message)
      }
    },
  },
}
</script>

</script>

<style lang="scss" scoped>
.box {
  width: 100%;
  height: 100%;
  .xterm-box {
    width: 100%;
    height: 100%;
  }
}
</style>
 类似资料: