美文网首页Node-RED基础教程
Node-RED与uibuilder构建自定义UI

Node-RED与uibuilder构建自定义UI

作者: 梅干菜烧饼不加肉 | 来源:发表于2022-10-23 14:58 被阅读0次

    简介

    Node-RED是一款可以进行可视化编程的低代码工具, 在快速构建原型和做小型应用有着较大优势. 在Node-RED中构建图形化(GUI)界面通常使用Dashboard完成, 其UI简约好看, 但其界面无法自定义, 只能使用现有的节点组件, 对于特殊界面无法满足. 因此Node-RED社区推出了uibuilder. 其可以使用HTML/JS/CSS等自定义构建页面, 同时也可以引入其它框架(Vue, React等)和组件库(Vue-Bootstrap等), 在通讯层面则通过封装的socket.io与Node-RED通讯. 在本文中就uibuilder与Node-RED的使用做出简要说明.

    uibuilder安装

    1. 点击右上角打开菜单, 进入节点管理页面


      节点管理
    2. 点击安装, 在输入框输入uibuilder, 点击安装
      搜索安装
      [注] 如果安装遇到问题, 切换网络再次尝试, 如果仍有问题, 可以参照官网安装教程

    uibuilder初始项目解析

    实例化uibuilder

    从左侧找到uibuilder节点, 双击进行配置.
    URL为访问地址, 不可重复. 配置完成后, 点击完成.


    image.png

    再点击部署后, uibuilder即可正常使用. 访问对应的URL即可看到如下页面.


    image.png

    文件结构

    如果Template 选择的是 'Blank template, no framework' 即 空白模板, 不使用框架. 则uibuilder对于该节点仅有三个文件

    • index.html 页面结构
    • index.js 完成对应功能
    • index.css 页面样式(美化)
      image.png

    页面结构

    <!doctype html>
    <html lang="en"><head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Blank template - Node-RED uibuilder</title>
        <meta name="description" content="Node-RED uibuilder - Blank template">
        <link rel="icon" href="./images/node-blue.ico">
        <link type="text/css" rel="stylesheet" href="./index.css" media="all">
    </head><body class="uib">
        <h1>uibuilder Blank Template</h1>
        <button onclick="fnSendToNR('A message from the sharp end!')">Send a msg back to Node-RED</button>
        <pre id="msg" class="syntax-highlight">Waiting for a message from Node-RED</pre>
        <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
        <script src="./uibuilderfe.min.js"></script>
        <script src="./index.js"></script>
    </body></html>
    

    该HTML分为两部分<head><body>. <head>制定页面的元信息, 标题/ICON/引入样式等.
    <body>为页面实际主体, 最主要的是<button><pre>; <button>即为按钮, 其onclick(点击)绑定为fnSendToNR('A message from the sharp end!'). 当点击该按钮时, 会调用函数fnSendToNR, 并且以'A message from the sharp end!'为参数.
    <pre>为后续显示消息的容器, 绑定idmsg, 后续会根据id查找到该元素进行操作.
    其次引入了三个外部的JavaScript文件, socket.io.js用于和Node-RED通信, uibuilderfe.min.jsuibuilder自身依赖提供简单易用接口, index.js为自定义的JavaScript文件.

    页面功能

    // Send a message back to Node-RED
    window.fnSendToNR = function fnSendToNR(payload) {
        uibuilder.send({
            'topic': 'msg-from-uibuilder-front-end',
            'payload': payload,
        })
    }
    // run this function when the document is loaded
    window.onload = function() {
        // Start up uibuilder - see the docs for the optional parameters
        uibuilder.start()
    
        // Listen for incoming messages from Node-RED
        uibuilder.onChange('msg', function(msg){
            console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
    
            // dump the msg as text to the "msg" html element
            const eMsg = document.getElementById('msg')
            eMsg.innerHTML = window.syntaxHighlight(msg)
        })
    }
    

    index.js中较为核心的是两个函数fnSendToNRwindow.onload.
    index.html中, <button>onclick属性绑定的方法具体实现就在这里. 调用该方法既是调用uibuild.send(该接口来自于uibuilderfe.min.js), 其向Node-RED发送一个对象, 其中payload对应函数的参数, 即index.html中的'A message from the sharp end!'; window.onload为一个回调函数, 当页面加载完成后会调用该函数, 在该函数中, 首先通过uibuilder.start()与Node-RED建立socket.io通信, 之后通过uibuilder.onChange('msg', function(msg){ ... })监听来自Node-RED的数据. 收到数据后, 首先通过document.getElementById获取到放置消息的容器, 之后通过eMsg.innerHTML = window.syntaxHighlight(msg)将收到的数据放入该容器.

    页面样式

    @import url("./uib-styles.css");
    

    页面样式较为简单, 仅引入了uibuilder公共样式.

    案例

    为了更完整的介绍uibuilder使用, 这里通过一个小案例引入. 假如我们需要实现一个显示当前温度的页面, 如下图(项目来自于CodePen).

    image.png
    其代码可以在CodePen找到.
    首先将index.html的代码进行合并(删除原有buttonpre, 新增span, inputp).
    <!doctype html>
    <html lang="en"><head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Blank template - Node-RED uibuilder</title>
        <meta name="description" content="Node-RED uibuilder - Blank template">
        <link rel="icon" href="./images/node-blue.ico">
        <link type="text/css" rel="stylesheet" href="./index.css" media="all">
    </head><body class="uib">
        <span class="emoji" role="img" aria-label="happy face">😊</span>
        <input type="range" class="slider" min="0" max="40" value="20" aria-label="temperature in degrees celsius">
        <p class="temperature"><span class="temperature-output">20</span>&deg;C</p>
        <script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
        <script src="./uibuilderfe.min.js"></script>
        <script src="./index.js"></script>
    </body></html>
    

    修改index.js

    document.addEventListener("DOMContentLoaded", () => {
        const emoji = document.querySelector('.emoji'),
            slider = document.querySelector('.slider'),
            tempOutput = document.querySelector('.temperature-output'),
            displayTemp = temperature => {
                //Display temperature
                tempOutput.textContent = temperature;
    
                //Display emoji
                if (temperature >= 0 && temperature <= 8) {
                    emoji.textContent = '🥶';
                    emoji.setAttribute('aria-label', 'freezing face');
                } else if (temperature > 8 && temperature <= 16) {
                    emoji.textContent = '😬';
                    emoji.setAttribute('aria-label', 'cold face');
                } else if (temperature > 16 && temperature <= 24) {
                    emoji.textContent = '😊';
                    emoji.setAttribute('aria-label', 'happy face');
                } else if (temperature > 24 && temperature <= 32) {
                    emoji.textContent = '😅';
                    emoji.setAttribute('aria-label', 'warm face');
                } else {
                    emoji.textContent = '🥵';
                    emoji.setAttribute('aria-label', 'hot face');
                }
    
                uibuilder.send({
                    'topic': 'msg-from-uibuilder-front-end',
                    'payload': temperature,
                })
            }
    
        // Start up uibuilder - see the docs for the optional parameters
        uibuilder.start()
    
        // Listen for incoming messages from Node-RED
        uibuilder.onChange('msg', function (msg) {
            console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', msg)
    
            // dump the msg as text to the "msg" html element
    
            displayTemp(msg.payload);
            slider.value = msg.payload;
            
            // const eMsg = document.getElementById('msg')
            // eMsg.innerHTML = window.syntaxHighlight(msg)
        })
        slider.addEventListener('input', () => displayTemp(slider.value));
    });
    
    // Send a message back to Node-RED
    window.fnSendToNR = function fnSendToNR(payload) {
        uibuilder.send({
            'topic': 'msg-from-uibuilder-front-end',
            'payload': payload,
        })
    }
    

    替换index.css

    :root {
        font-size: 20vmin;
    }
    body {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        height: 100vh;
    }
    .emoji {
        font-size: 1em;
        margin-bottom: 0.3em;
        text-align: center;
        text-shadow: 0.02em 0.02em 0.02em rgba(0, 0, 0, 0.3);
    }
    .slider {
        font:inherit;
        width: 4em;
        height: 0.2em;
        border-radius: 1em;  
        background-image: linear-gradient(90deg, #384bdc, #33994a, #df3b33); 
        box-shadow: inset 0 0 0.05em rgba(0, 0, 0, 0.6);
        -webkit-appearance: none;
           -moz-appearance: none;
                appearance: none;
    }
    .slider::-webkit-slider-thumb {
        position: relative;
        width: 0.25em;
        height: 0.38em;
        border-radius: 0.08em; 
        background-image: radial-gradient(#eee, #ccc);    
        filter: drop-shadow(0.02em 0.02em 0.02em rgba(0, 0, 0, 0.5));
        cursor: pointer;
        -webkit-appearance: none;
                appearance: none;
    }
    .slider::-moz-range-thumb {
        position: relative;
        width: 0.25em;
        height: 0.38em;
        border-radius: 0.08em; 
        background-image: radial-gradient(#eee, #bbb);    
        filter: drop-shadow(0.02em 0.02em 0.02em rgba(0, 0, 0, 0.5));
        cursor: pointer;
        border: none;
        -moz-appearance: none;
             appearance: none;
    }
    .temperature {
        font-family: 'Open Sans', Arial, sans-serif;
        font-size: 0.5em;
        font-weight: 400;
        margin-top: 0.45em;
        color: #111;
        text-shadow: 0.02em 0.02em 0.02em rgba(0, 0, 0, 0.1);
    }
    
    image.png
    image.png

    相关文章

      网友评论

        本文标题:Node-RED与uibuilder构建自定义UI

        本文链接:https://www.haomeiwen.com/subject/swjizrtx.html