node-red 中的自定义 UI (node-red-contrib-uibuilder & VueJS & import package)



我使用node-red将数据从传感器写入数据库。我还计划使用它来显示数据。对于显示,我使用node-red-contrib-uibuilder,据我了解,它使用VueJS。最后,一切都很好,一切都正常。但问题暴露了。无法从 npm 添加包。例如 vue-datepicker。

如何添加包?

下面是以下代码:

.HTML:

<!doctype html>
<!-- Note that adding an appcache really speeds things up after the first load
You need to amend the appcache file to meet your needs.
Don't forget to change the appcache file if you update ANY
of the files in it otherwise the old versions will ALWAYS be used.
<html lang="en" manifest="./uibuilder.appcache">
-->
<html lang="en">
<!--
This is the default, template html for uibuilder.
It is meant to demonstrate the use of VueJS & bootstrap-vue to dynamically
update the ui based on incoming/outgoing messages from/to the
Node-RED server.
You will want to alter this to suite your own needs. To do so,
copy this file to <userDir>/uibuilder/<url>/src.
-->
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, minimum-scale=1, initial-scale=1, user-scalable=yes">
<title>Node-RED UI Builder</title>
<meta name="description" content="Node-RED UI Builder - VueJS + bootstrap-vue version">
<link rel="icon" href="./images/node-blue.ico">
<link rel="manifest" href="./manifest.json">
<meta name="theme-color" content="#3f51b5">
<!-- Used if adding to homescreen for Chrome on Android. Fallback for manifest.json -->
<meta name="mobile-web-app-capable" content="yes">
<meta name="application-name" content="Node-RED UI Builder">
<!-- Used if adding to homescreen for Safari on iOS -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
<meta name="apple-mobile-web-app-title" content="Node-RED UI Builder">
<!-- Homescreen icons for Apple mobile use if required
<link rel="apple-touch-icon" href="./images/manifest/icon-48x48.png">
<link rel="apple-touch-icon" sizes="72x72" href="./images/manifest/icon-72x72.png">
<link rel="apple-touch-icon" sizes="96x96" href="./images/manifest/icon-96x96.png">
<link rel="apple-touch-icon" sizes="144x144" href="./images/manifest/icon-144x144.png">
<link rel="apple-touch-icon" sizes="192x192" href="./images/manifest/icon-192x192.png">
-->
<link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css" />
<link type="text/css" rel="stylesheet" href="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.css" />
<link type="text/css" rel="stylesheet" href="./index.css" media="all">
<!--<script type="tex/javascript" src="https://cdn.jsdelivr.net/npm/vue-datetime@1.0.0-beta.11/dist/vue-datetime.min.js"></script>-->
<!--<script type="tex/javascript" src="https://moment.github.io/luxon/amd/luxon.js"></script>-->
<!--<link type="text/css" rel="stylesheet" href="https://cdn.jsdelivr.net/npm/vue-datetime@1.0.0-beta.11/dist/vue-datetime.min.css">-->
<!-- <link href="../uibuilder/vendor/air-datetimepicker/dist/css/datepicker.min.css" rel="stylesheet" type="text/css"> -->
</head>
<body class="bg-dark">
<div id="app" v-cloak>
<b-container id="app_container" class="container">
<div class="card shadow mb-4 mt-2">
<div class="card-header">
<span class="h3 text-primary">Данные по датчику</span>
</div>
<div class="card-body">
<div class="row mt-2">
<span class="label my-2 h-4">Название датчика:</span>
<select ref="selectSensor" class="form-control" v-on:input="sensorChange">
<option ref="optionForRemove" value="">Выберите датчик</option>
<option value="1111/Temperature/t1">Температура 1</option>
<option value="1111/Temperature/t2">Температура 2</option>
<option value="1111/Temperature/t3">Температура 3</option>
</select>
</div>
<div class="row mt-2">
<div class="col-6">
<input type='text' class="datepicker-here" data-position="right top" />
</div>
<div class="col-6"></div>
</div>
</div>
<div class="card-footer">
<div class="row mt-2">
<pre v-html="hLastRcvd" class="w-100 syntax-highlight"></pre>
</div>
</div>
</div>
</b-container>
</div>
<!-- These MUST be in the right order. Note no leading / -->
<!-- REQUIRED: Socket.IO is loaded only once for all instances
Without this, you don't get a websocket connection -->
<script src="../uibuilder/vendor/socket.io/socket.io.js"></script>
<!-- --- Vendor Libraries - Load in the right order --- -->
<script src="../uibuilder/vendor/vue/dist/vue.js"></script> <!-- dev version with component compiler -->
<!-- <script src="../uibuilder/vendor/vue/dist/vue.min.js"></script>   prod version with component compiler -->
<!-- <script src="../uibuilder/vendor/vue/dist/vue.runtime.min.js"></script>   prod version without component compiler -->
<script src="../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js"></script>
<!-- REQUIRED: Sets up Socket listeners and the msg object -->
<!-- <script src="./uibuilderfe.js"></script>   //dev version -->
<script src="./uibuilderfe.min.js"></script> <!--    //prod version -->
<!-- OPTIONAL: You probably want this. Put your custom code here -->
<script src="./index.js"></script>
<!-- <script type="text/javascript" src="../../../node_modules/air-datepicker/dist/js/datepicker.min.js"></script> -->
</body>
</html>

JS(esversion: 5(:

/* jshint browser: true, esversion: 5, asi: true */
/*globals Vue, uibuilder */
// @ts-nocheck
/*
Copyright (c) 2019 Julian Knight (Totally Information)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

'use strict'
// var datetime  = require('vue-datetime');
// import * as name from "vue-datetime"; 

/** @see https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */
// eslint-disable-next-line no-unused-vars
var app1 = new Vue({
el: '#app',
data: {
startMsg    : 'Vue has started, waiting for messages',
feVersion   : '',
socketConnectedState : false,
serverTimeOffset     : '[unknown]',
imgProps             : { width: 75, height: 75 },
chartData             : null,
msgRecvd    : '[Nothing]',
msgsReceived: 0,
msgCtrl     : '[Nothing]',
msgsControl : 0,
msgSent     : '[Nothing]',
msgsSent    : 0,
msgCtrlSent : '[Nothing]',
msgsCtrlSent: 0,
}, // --- End of data --- //
computed: {
hLastRcvd: function() {
this.chartData = this.msgRecvd.chartData; 
var msgRecvd = this.msgRecvd
if (typeof msgRecvd === 'string') return 'Last Message Received = ' + msgRecvd
else return 'Last Message Received = ' + this.syntaxHighlight(msgRecvd)
},
hLastSent: function() {
var msgSent = this.msgSent
if (typeof msgSent === 'string') return 'Last Message Sent = ' + msgSent
else return 'Last Message Sent = ' + this.syntaxHighlight(msgSent)
},
hLastCtrlRcvd: function() {
var msgCtrl = this.msgCtrl
if (typeof msgCtrl === 'string') return 'Last Control Message Received = ' + msgCtrl
else return 'Last Control Message Received = ' + this.syntaxHighlight(msgCtrl)
},
hLastCtrlSent: function() {
var msgCtrlSent = this.msgCtrlSent
if (typeof msgCtrlSent === 'string') return 'Last Control Message Sent = ' + msgCtrlSent
//else return 'Last Message Sent = ' + this.callMethod('syntaxHighlight', [msgCtrlSent])
else return 'Last Control Message Sent = ' + this.syntaxHighlight(msgCtrlSent)
},
}, // --- End of computed --- //
methods: {
sensorChange: function(){
this.$refs.optionForRemove.outerHTML = "";
var topic = 'getDataFromSensor';
uibuilder.send( {
'topic': topic,
'payload': {
'sensor':this.$refs.selectSensor.value,
}
} )
},
// --- End of increment --- //
// return formatted HTML version of JSON object
syntaxHighlight: function(json) {
json = JSON.stringify(json, undefined, 4)
json = json.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
json = json.replace(/("(\u[a-zA-Z0-9]{4}|\[^u]|[^\"])*"(s*:)?|b(true|false|null)b|-?d+(?:.d*)?(?:[eE][+-]?d+)?)/g, function (match) {
var cls = 'number'
if (/^"/.test(match)) {
if (/:$/.test(match)) {
cls = 'key'
} else {
cls = 'string'
}
} else if (/true|false/.test(match)) {
cls = 'boolean'
} else if (/null/.test(match)) {
cls = 'null'
}
return '<span class="' + cls + '">' + match + '</span>'
})
return json
}, // --- End of syntaxHighlight --- //
}, // --- End of methods --- //
// Available hooks: init,mounted,updated,destroyed
mounted: function(){
//console.debug('[indexjs:Vue.mounted] app mounted - setting up uibuilder watchers')
/** **REQUIRED** Start uibuilder comms with Node-RED @since v2.0.0-dev3
* Pass the namespace and ioPath variables if hosting page is not in the instance root folder
* The namespace is the "url" you put in uibuilder's configuration in the Editor.
* e.g. If you get continual `uibuilderfe:ioSetup: SOCKET CONNECT ERROR` error messages.
* e.g. uibuilder.start('uib', '/nr/uibuilder/vendor/socket.io') // change to use your paths/names
*/
uibuilder.start()
var vueApp = this
// Example of retrieving data from uibuilder
vueApp.feVersion = uibuilder.get('version')
/** You can use the following to help trace how messages flow back and forth.
* You can then amend this processing to suite your requirements.
*/
//#region ---- Trace Received Messages ---- //
// If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO
// newVal relates to the attribute being listened to.
uibuilder.onChange('msg', function(newVal){
//console.info('[indexjs:uibuilder.onChange] msg received from Node-RED server:', newVal)
vueApp.msgRecvd = newVal
})
// As we receive new messages, we get an updated count as well
uibuilder.onChange('msgsReceived', function(newVal){
//console.info('[indexjs:uibuilder.onChange] Updated count of received msgs:', newVal)
vueApp.msgsReceived = newVal
})
// If we receive a control message from Node-RED, we can get the new data here - we pass it to a Vue variable
uibuilder.onChange('ctrlMsg', function(newVal){
//console.info('[indexjs:uibuilder.onChange:ctrlMsg] CONTROL msg received from Node-RED server:', newVal)
vueApp.msgCtrl = newVal
})
// Updated count of control messages received
uibuilder.onChange('msgsCtrl', function(newVal){
//console.info('[indexjs:uibuilder.onChange:msgsCtrl] Updated count of received CONTROL msgs:', newVal)
vueApp.msgsControl = newVal
})
//#endregion ---- End of Trace Received Messages ---- //
//#region ---- Trace Sent Messages ---- //
// You probably only need these to help you understand the order of processing //
// If a message is sent back to Node-RED, we can grab a copy here if we want to
uibuilder.onChange('sentMsg', function(newVal){
//console.info('[indexjs:uibuilder.onChange:sentMsg] msg sent to Node-RED server:', newVal)
vueApp.msgSent = newVal
})
// Updated count of sent messages
uibuilder.onChange('msgsSent', function(newVal){
//console.info('[indexjs:uibuilder.onChange:msgsSent] Updated count of msgs sent:', newVal)
vueApp.msgsSent = newVal
})
// If we send a control message to Node-RED, we can get a copy of it here
uibuilder.onChange('sentCtrlMsg', function(newVal){
//console.info('[indexjs:uibuilder.onChange:sentCtrlMsg] Control message sent to Node-RED server:', newVal)
vueApp.msgCtrlSent = newVal
})
// And we can get an updated count
uibuilder.onChange('msgsSentCtrl', function(newVal){
//console.info('[indexjs:uibuilder.onChange:msgsSentCtrl] Updated count of CONTROL msgs sent:', newVal)
vueApp.msgsCtrlSent = newVal
})
//#endregion ---- End of Trace Sent Messages ---- //
// If Socket.IO connects/disconnects, we get true/false here
uibuilder.onChange('ioConnected', function(newVal){
//console.info('[indexjs:uibuilder.onChange:ioConnected] Socket.IO Connection Status Changed to:', newVal)
vueApp.socketConnectedState = newVal
})
// If Server Time Offset changes
uibuilder.onChange('serverTimeOffset', function(newVal){
//console.info('[indexjs:uibuilder.onChange:serverTimeOffset] Offset of time between the browser and the server has changed to:', newVal)
vueApp.serverTimeOffset = newVal
})
} // --- End of mounted hook --- //
}) // --- End of app1 --- //
// EOF

每当 npm 上有可用的软件包时,您也可以通过 jsdelivr CDN: https://www.jsdelivr.com/获取它。

例如,对于 vue-datepicker,请使用 : https://cdn.jsdelivr.net/npm/vue-datepicker/-> 这将为您提供可用文件的列表。

使用 https://cdn.jsdelivr.net/npm/vue-datepicker/filename并将其作为脚本标记添加到 uibuilder 索引的底部.html如下所示:<script src="https://cdn.jsdelivr.net/npm/vue-datepicker"></script>

之后,您可以在索引.js中使用它。

您可以在 uibuilder 节点中添加来自 NPM 的模块。

从uibuilder节点选项中单击"管理前端库",它将打开NPM的图形界面。

它们将进入您的 node-red node_modules 文件夹(默认情况下,~/.node-red/node_modules(

最新更新