- node-red的基础使用不讲了,百度有较多资料,跟着官网也可以写个简单的自定义组件
- 这里主要讲的是如何将复杂的组件输出成一个 独立的html页面并于vue框架结合形成一个友好的频繁交互页面
- 第一:创建一个”config“类型的配置项,从而创建一个websocket长连接,进行频繁的数据交换
- 第二:创建一个html文件,引入vue,写一个漂亮一些的可视化页面
- 第三:创建一个组件,提供一些node服务,并关联上你写的websocket服务,启动你的html页面
配置socket
选择配置
流程
产生url
- ui文件夹是组件加入后需要公开的文件
- mycom 是自定义组件
- socket 是长链接配置
- 下面上代码
my-com.js
module.exports = function (RED) {
"use strict";
const path = require('path');
const express = require('express');
const { networkInterfaces } = RED.require('os');
function myCom(config) {
RED.nodes.createNode(this, config);
const node = this;
const socket = RED.nodes.getNode(config.socket);
// 提供一些node能力 比如检测IPv4网口
function getWorks() {
const nif = networkInterfaces();
const works = [];
Object.keys(nif).forEach(i => {
nif[i].forEach(j => {
j.family == 'IPv4' && works.push({ ...j, name: `${i} ( ${j.address} )` })
})
})
return works;
}
node.getWorks = getWorks;
node.on('input', ({ topic }) => {
const order = `http://127.0.0.1:1880/ui.html?id=${config.id}&port=${socket.port}`; // 发布之后的url
if (socket.statusText == 'start') { // socket已启动
const app = RED.httpNode;
app.use(express.static(path.join(__dirname, 'ui'))); // 发布html
node.send({ topic, payload: order }); // 打印发布之后的url
} else {
node.send({ topic, payload: socket.statusText + order }); // 打印socket的状态
}
})
}
RED.nodes.registerType("my-com", myCom);
}
mycom.html
<!-- 注册 -->
<script type="text/javascript">
RED.nodes.registerType('my-com', {
category: 'my', // 分类
color: '#a6bbcf', // 背景颜色
defaults: {
name: { value: "我的组件" },
socket: { value: '', type: "my-socket" }
},
inputs: 1, // 上游:输入 0 或者 1
outputs: 1, // 输出至下游 0 或者 more
icon: 'fa fa-anchor', // 标签
label: function () {
return this.name;
}
});
</script>
<!-- 配置窗口 -->
<script type="text/html" data-template-name="my-com">
<div class="form-row">
<label for="node-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-input-name" placeholder="Name"/>
</div>
<div class="form-row">
<label for="node-input-socket"><i class="fa fa-globe"></i> <span>socket服务</span></label>
<input type="text" id="node-input-socket">
</div>
</script>
<!-- hover提示 -->
<script type="text/html" data-help-name="my-com">
<p>码代码的小公举</p>
</script>
socket.html
<!-- 注册节点 -->
<script type="text/javascript">
RED.nodes.registerType('my-socket', {
category: 'config', // 节点类型:配置,设置类
defaults: {
name: { value: "my-socket" },
port: { value: '1881', required: true }
},
label: function () {
return this.name;
}
});
</script>
<!-- 展示内容 -->
<script type="text/html" data-template-name="my-socket">
<div class="form-row">
<label for="node-config-input-name"><i class="fa fa-tag"></i> Name</label>
<input type="text" id="node-config-input-name" placeholder="Name" />
</div>
<div class="form-row">
<label for="node-config-input-port"><i class="fa fa-tag"></i> Port</label>
<input type="text" id="node-config-input-port" placeholder="Port"/>
</div>
</script>
socket.js
module.exports = function (RED) {
"use strict";
const webServer = require('ws').Server;
function mySocket(n) {
RED.nodes.createNode(this, n);
const node = this;
node.port = n.port;
node.statusText = 'init';
const wss = new webServer({ port: node.port }, () => {
console.log('start')
node.statusText = 'start';
});
wss.on('connection', (ws) => {
console.log('connected')
node.statusText = 'connected';
node.ws = ws;
ws.send('connected');
node.start = false;
let timmer;
ws.on('message', (data) => {
try {
const { code, id } = JSON.parse(data.toString());
const targetNode = RED.nodes.getNode(id);
console.log(code, node.start, targetNode.getWorks)
if (code == 'start') {
// 持续检测
if (node.start) {
return;
}
node.start = true;
const loop = () => {
if (node.start) {
const works = targetNode.getWorks();
ws.send(JSON.stringify({ code: 'start', payload: works }))
timmer = setTimeout(loop, 100);
} else {
clearTimeout(timmer);
timmer = null;
}
}
loop();
} else if (code == 'stop') {
node.start = false;
ws.send(JSON.stringify({ code: 'stop', payload: 'stoped' }))
}
} catch (err) {
}
});
ws.on('close', () => {
node.statusText = '浏览器断开连接';
console.log('closed')
})
});
wss.on('close', () => {
node.statusText = 'closed';
console.log('closed')
})
wss.on('error', (error) => {
if (error.toString().indexOf('already') > -1 ) {
console.log('已启动')
node.statusText = 'start';
return;
}
console.log(error)
node.statusText = 'error';
})
}
RED.nodes.registerType("my-socket", mySocket);
}
ui.html
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>码代码的小公举</title>
<script src="./vue.js"></script>
<style>
* {
padding: 0;
margin: 0;
}
#app {
margin: 40px;
height: 200px;
line-height: 26px;
}
.items {
margin: 10px;
}
th,td {
width: 300px;
}
button {
width: 100%;
height: 40px;
background: #3872e0;
border-radius: 4px;
color: #fff;
border: 1px solid #3872e0;
font-size: 16px;
cursor: pointer;
}
</style>
</head>
<body>
<div id='app'>
<div class='box'>
<div style="padding: 0 10px; margin-bottom: 10px;">
<button v-show="!loading" @click="handlerCheck">开始检测</button>
<button v-show="loading" @click="handlerStopCheck">停止检测</button>
</div>
<div class="items">
<table border="1">
<tr>
<th>name</th>
<th>mac</th>
<th>family</th>
<th>netmask</th>
<th>address</th>
</tr>
<tr v-for="w of works" class="item">
<td>{{w.name}}</td>
<td>{{w.mac}}</td>
<td>{{w.family}}</td>
<td>{{w.netmask}}</td>
<td>{{w.address}}</td>
</tr>
</table>
</div>
</div>
</div>
<script>
const params = {};
try {
const arr = location.search.split('?')[1].split('&');
arr.forEach(i => {
const obj = i.split('=');
params[obj[0]] = obj[1];
})
} catch (err) {
}
const id = params.id;
const port = params.port;
console.log(id, port)
const { createApp } = Vue
const HelloVueApp = {
data() {
return {
loading: false, // 检测按钮
works: [],
}
},
mounted() {
const ws = new WebSocket(`ws://localhost:${port}`);
ws.addEventListener('open', (event) => {
console.log('WebSocket connected!');
this.connected = true; // 连接成功
});
ws.addEventListener('message', ({ data }) => {
try {
const { code, payload } = JSON.parse(data);
console.log(payload)
if (code == 'start') {
this.works = payload;
} else if (code == 'stop') {
// this.works = [];
}
} catch (err) { }
});
ws.addEventListener('close', (event) => {
console.log('WebSocket disconnected!');
this.connected = false;
});
this.ws = ws;
},
methods: {
handlerCheck() {
this.loading = true;
this.ws.send(JSON.stringify({ code: 'start', id }))
},
handlerStopCheck() {
this.loading = false;
this.ws.send(JSON.stringify({ code: 'stop', id }))
}
}
}
Vue.createApp(HelloVueApp).mount('#app')
</script>
</body>
</html>
- port是配置的所以需要传递过去给html
- id是获取node能力所需要的
- html可以自由扩展发挥,项目化也是可以的,node-red作为配置以及服务器使用
- vue.js 是vue 3 官网下载的
- 写的毕竟粗糙
- 发布之后的打开url:http://127.0.0.1:1880/ui.html?id=xxx&prot=xxx 查看