vue + xtermjs + websocket 前端网页版终端,操作后端的docker容器

简介:
1.自动版,无需维护websocket,由xterm-addon-attach插件监控输入输出(推荐,简单好用)
2.自定义版,维护websocket,自己实现输入输出
3.xtermjs、xterm-addon-attach、xterm-addon-fit、docker
4.以上版本都是由cdn形式html页面实现,如需vue-cli工程化实现,只需import引入fitAddon 、attachAddon 替换即可

一、自动版(推荐)
如图所示:

vue + xtermjs + websocket 前端网页版终端,操作后端的docker容器_第1张图片

代码(复制另存txt,修改.html直接运行)
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>前端终端,操作后端的docker容器</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/css/xterm.css">
</head>


<body class="bodyCss">
    <div id="app">
        <div id="terminal" ></div>
    </div>
</body>
<script src="https://unpkg.com/[email protected]/lib/xterm.js"></script>
<script src="https://lib.baomitu.com/vue/2.6.14/vue.js"></script>
<script  src="https://unpkg.com/[email protected]/lib/xterm-addon-fit.js"></script>
<script  src="https://unpkg.com/[email protected]/lib/xterm-addon-attach.js"></script>

<script>
    let wsTime = null
    new Vue({
        el: '#app',
        data() {
            return {
                // 终端
                term:{},
                // websocket
                ws:{}
            }
        },
        created(){
            // 初始化终端
            this.initTerminal() 
        },
        mounted() {
            // 建立websocket连接
            this.websocket()
        },
        beforeDestroy() {
            this.ws.close()
            this.term.dispose()
        },
        methods: {
            // 初始化终端配置
            initTerminal(){
                this.term = new Terminal({
                    rendererType: "canvas", //渲染类型
                    // rows: 40, //行数,影响最小高度
                    // cols: 100, // 列数,影响最小宽度
                    convertEol: true, //启用时,光标将设置为下一行的开头
                    // scrollback: 50, //终端中的滚动条回滚量
                    disableStdin: false, //是否应禁用输入。
                    cursorStyle: "underline", //光标样式
                    cursorBlink: true, //光标闪烁
                    theme: {
                        foreground: '#F8F8F8',
                        background: '#2D2E2C',
                        cursor: "help", //设置光标
                        lineHeight: 16,
                    },
                    fontFamily: '"Cascadia Code", Menlo, monospace'
                });
            },
            // 自定义终端默认展示内容
            writeDefaultInfo(){
                let defaultInfo = [
                '┌\x1b[1m terminals \x1b[0m─────────────────────────────────────────────────────────────────┐ ',
                '│                                                                            │ ',
                '│  \x1b[1;34m 欢迎使用Web Docker SSH \x1b[0m                                                  │ ',
                '│                                                                            │ ',
                '└────────────────────────────────────────────────────────────────────────────┘ ',]
                this.term.write(defaultInfo.join('\n\r'))
            },
            // 建立websocket连接
            websocket() {
                // WebSocket start
                if ('WebSocket' in window) {
                	//需要修改ip和id
                	//例如:const url = `ws://192.168.111.222:2375/v1.41/containers/0eb8aafb4e6e/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1`
                    const url = `ws://你的ip/v1.41/containers/你的id/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1`
                    const ws = new WebSocket(url)
                    this.ws = ws
                    ws.onopen = (event) => {
                        console.log('已建立连接:',event)
                        // 输入换行符可让终端显示当前用户的工作路径
                        ws.send('\n') 
                        // 窗口自适应插件
                        const fitAddon = new FitAddon.FitAddon();
                        // websocket自动收发消息插件
                        const attachAddon = new AttachAddon.AttachAddon(ws)
                        this.term.loadAddon(attachAddon)
                        this.term.loadAddon(fitAddon)
                        this.term.open(document.getElementById('terminal'));
                        // 聚焦闪烁光标
                        this.term.focus()
                        // 窗口尺寸变化时,终端尺寸自适应
                        window.onresize = () => { 
                            fitAddon.fit()
                        }
                        // 自定义终端默认展示内容
                        this.writeDefaultInfo()
                    };
                    ws.onmessage = (event) => {
                        console.log('接收信息:',event)
                    };
                    ws.onerror = (event) => {
                        console.log('错误信息:', event)
                        if (wsTime) {
                            window.clearTimeout(wsTime)
                            wsTime = null
                        }
                        wsTime = window.setTimeout(() => {
                            this.websocket()
                        }, 3000)
                    };
                    ws.onclose = (event) => {
                        console.log('已关闭连接:', event)
                        // if (wsTime) {
                        //    window.clearTimeout(wsTime)
                        //    wsTime = null
                        // }
                        // wsTime = window.setTimeout(() => {
                        //    this.websocket()
                        // }, 3000)
                    };
                } else {
                    console.log('浏览器不支持 WebSocket..')
                }
                // WebSocket end
            }
        }
    })
</script>

</html>



二、自定义版
如图所示:

vue + xtermjs + websocket 前端网页版终端,操作后端的docker容器_第2张图片

代码(复制另存txt,修改.html直接运行)
<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>前端终端,操作后端的docker容器</title>
    <!-- 引入样式 -->
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/css/xterm.css">
</head>

<body class="bodyCss">
    <div id="app">
        <div id="terminal" ></div>
    </div>
</body>
<script src="https://unpkg.com/[email protected]/lib/xterm.js"></script>
<script src="https://lib.baomitu.com/vue/2.6.14/vue.js"></script>
<script  src="https://unpkg.com/[email protected]/lib/xterm-addon-fit.js"></script>

<script>
    let wsTime = null
    new Vue({
        el: '#app',
        data() {
            return {
                // 终端
                term:{},
                // websocket
                ws:{},
                // 用户输入
                command:''
            }
        },
        created(){
            // 初始化终端
            this.initTerminal() 
        },
        mounted() {
            // 建立websocket连接
            this.websocket()
        },
        beforeDestroy() {
            this.ws.close()
            this.term.dispose()
        },
        methods: {
            // 初始化终端配置
            initTerminal(){
                this.term = new Terminal({
                    rendererType: "canvas", //渲染类型
                    // rows: 40, //行数,影响最小高度
                    // cols: 100, // 列数,影响最小宽度
                    convertEol: true, //启用时,光标将设置为下一行的开头
                    // scrollback: 50, //终端中的滚动条回滚量
                    disableStdin: false, //是否应禁用输入。
                    cursorStyle: "underline", //光标样式
                    cursorBlink: true, //光标闪烁
                    theme: {
                        foreground: '#F8F8F8',
                        background: '#2D2E2C',
                        cursor: "help", //设置光标
                        lineHeight: 16,
                    },
                    fontFamily: '"Cascadia Code", Menlo, monospace'
                });
            },
            // 自定义终端默认展示内容
            writeDefaultInfo(){
                let defaultInfo = [
                '┌\x1b[1m terminals \x1b[0m─────────────────────────────────────────────────────────────────┐ ',
                '│                                                                            │ ',
                '│  \x1b[1;34m 欢迎使用Web Docker SSH \x1b[0m                                                  │ ',
                '│                                                                            │ ',
                '└────────────────────────────────────────────────────────────────────────────┘ ',]
                // 测试颜色区间 
                // let arr = Array.from({length:100},(v,i)=>v = i)
                // console.log(arr)
                // arr.map((item,i) => {
                //     defaultInfo.push(`Hello from \x1B[1;3;${i}m ${i} \x1B[0m  \u2764\ufe0f   ${i}`)
                // })
                this.term.write(defaultInfo.join('\n\r'))
                this.writeOfColor('我是加粗斜体红色的字呀','1;3;','31m')
                // this.term.write('\n\r$ ')
            },
            // 
            writeOfColor(txt, fontCss = "", bgColor = ""){
                // 在Linux脚本中以 \x1B[ 开始,中间前部分是样式+内容,以 \x1B[0m 结尾
                // 示例 \x1B[1;3;31m 内容 \x1B[0m  
                // fontCss
                // 0;-4;字体样式(0;正常 1;加粗 2;变细 3;斜体 4;下划线)
                // bgColor
                // 30m-37m字体颜色(30m:黑色 31m:红色 32m:绿色 33m:棕色字 34m:蓝色 35m:洋红色/紫色 36m:蓝绿色/浅蓝色 37m:白色)
                // 40m-47m背景颜色(40m:黑色 41m:红色 42m:绿色 43m:棕色字 44m:蓝色 45m:洋红色/紫色 46m:蓝绿色/浅蓝色 47m:白色)
                this.term.write(`\x1B[${fontCss}${bgColor}${txt}\x1B[0m`)
            },
            // 监听输入
            userWrite(){
                this.term.onData(e => {
                    switch (e) {
                        case '\u0003': // Ctrl+C
                            this.term.write('^C ');
                            this.term.write('\r\n$ ')
                        break;
                        case '\r': // Enter
                            this.ws.send(this.command)
                            this.ws.send('\n') 
                            this.command = ''
                            // this.term.write('\r\n$ ')
                        break;
                        case '\u007F': // Backspace (DEL)
                            // Do not delete the prompt
                            if (this.term._core.buffer.x > 2) {
                                this.term.write('\b \b');
                                if (this.command.length > 0) {
                                    this.command = this.command.substr(0, this.command.length - 1);
                                }
                            }
                        break;
                        default: // Print all other characters for demo
                            if (e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7E) || e >= '\u00a0') {
                                this.command += e;
                                this.writeOfColor(e,'2;3;','33m')
                                console.log('用户输入command',this.command)
                            }
                    }
                });
            },
            // 建立websocket连接
            websocket() {
                // WebSocket start
                if ('WebSocket' in window) {
                    //需要修改ip和id
                	//例如:const url = `ws://192.168.111.222:2375/v1.41/containers/0eb8aafb4e6e/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1`
                    const url = `ws://你的ip/v1.41/containers/你的id/attach/ws?logs=0&stream=1&stdin=1&stdout=1&stderr=1`
                    const ws = new WebSocket(url)
                    this.ws = ws
                    this.$nextTick(()=>{
                        this.userWrite()
                    })
                    ws.onopen = (event) => {
                        console.log('已建立连接:',event)
                        // 输入换行符可让终端显示当前用户的工作路径
                        ws.send('\n') 
                        // 窗口自适应插件
                        const fitAddon = new FitAddon.FitAddon();
                        // 窗口尺寸变化时,终端尺寸自适应
                        window.onresize = () => { 
                            fitAddon.fit()
                        }
                        this.term.loadAddon(fitAddon)
                        this.term.open(document.getElementById('terminal'));
                        this.term.focus()
                        // 自定义终端默认展示内容
                        this.writeDefaultInfo()
                    };
                    ws.onmessage = (event) => {
                        console.log('接收信息:',event)
                        //将字符串转换成 Blob对象
                        const blob = new Blob([event.data], {
                            type: 'text/plain'
                        });
                        //将Blob 对象转换成字符串
                        const reader = new FileReader();
                        reader.readAsText(blob, 'utf-8');
                        reader.onload = (e) => {
                            // 可以根据返回值判断使用何种颜色或者字体,不过返回值自带了一些字体颜色
                            this.writeOfColor(reader.result,'0;','37m')
                        }
                    };
                    ws.onerror = (event) => {
                        console.log('错误信息:', event)
                        if (wsTime) {
                            window.clearTimeout(wsTime)
                            wsTime = null
                        }
                        wsTime = window.setTimeout(() => {
                            this.websocket()
                        }, 3000)
                    };
                    ws.onclose = (event) => {
                        console.log('已关闭连接:', event)
                        // if (wsTime) {
                        //    window.clearTimeout(wsTime)
                        //    wsTime = null
                        // }
                        // wsTime = window.setTimeout(() => {
                        //    this.websocket()
                        // }, 3000)
                    };
                } else {
                    console.log('浏览器不支持 WebSocket..')
                }
                // WebSocket end
            }
        }
    })
</script>

</html>

你可能感兴趣的:(Element,javascript,vue.js,websocket,Terminal,PowerShell,终端)