UIBuilder + Bootstrap.Vue.js - Visor de archivo de datos
Este flujo que utiliza UIBuilder y Bootstrap-Vue.js, permite mostrar en una interface grafica diferente del dashboard - node red, los datos de un archivo de texto plano que corresponden a una base de datos. Los datos incluyen fotos, tanto archivo de datos como carpeta de fotos se encuentran en Dropbox.
[{"id":"6b3b5a4ca30fa8d1","type":"uibuilder","z":"407037fe.d66c18","name":"canarios","topic":"","url":"canarios","fwdInMessages":false,"allowScripts":false,"allowStyles":false,"copyIndex":true,"templateFolder":"blank","extTemplate":"","showfolder":false,"useSecurity":false,"sessionLength":432000,"tokenAutoExtend":false,"reload":false,"sourceFolder":"src","x":920,"y":2700,"wires":[["724a6f422d9197af"],["fe59236a23dd2a55"]]},{"id":"8ddd458a28f28228","type":"inject","z":"407037fe.d66c18","name":"","props":[{"p":"payload"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payloadType":"date","x":120,"y":2660,"wires":[["fe59236a23dd2a55"]]},{"id":"ef2b05656b7b80b2","type":"dropbox","z":"407037fe.d66c18","dropbox":"","filename":"","name":"","x":440,"y":2660,"wires":[["7cc23d93e8fc8991"]]},{"id":"51b86a7d3280ea93","type":"csv","z":"407037fe.d66c18","name":"","sep":".","hdrin":true,"hdrout":"none","multi":"mult","ret":"\\n","temp":"","skip":"0","strings":false,"include_empty_strings":"","include_null_values":true,"x":750,"y":2660,"wires":[["6b3b5a4ca30fa8d1"]]},{"id":"724a6f422d9197af","type":"function","z":"407037fe.d66c18","name":"foto","func":"msg.filename = \"Fotos_canarios/\" + msg.payload.substring(0,11) + \".jpg\";\nmsg.topic = \"foto\";\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":1050,"y":2600,"wires":[["ef2b05656b7b80b2","9bec5b69e6044f55"]]},{"id":"7cc23d93e8fc8991","type":"switch","z":"407037fe.d66c18","name":"","property":"topic","propertyType":"msg","rules":[{"t":"eq","v":"canarios","vt":"str"},{"t":"eq","v":"foto","vt":"str"}],"checkall":"true","repair":false,"outputs":2,"x":590,"y":2660,"wires":[["51b86a7d3280ea93"],["0bead597eb2bcf95"]]},{"id":"056ad493a150398f","type":"image","z":"407037fe.d66c18","name":"","width":160,"data":"payload","dataType":"msg","thumbnail":false,"active":true,"pass":false,"outputs":0,"x":700,"y":2800,"wires":[]},{"id":"0bead597eb2bcf95","type":"base64","z":"407037fe.d66c18","name":"","action":"","property":"payload","x":600,"y":2740,"wires":[["7d63d45a1939e97c","056ad493a150398f"]]},{"id":"7d63d45a1939e97c","type":"change","z":"407037fe.d66c18","name":"foto","rules":[{"t":"change","p":"payload","pt":"msg","from":"^(.*)$","fromt":"re","to":"data:image/x-icon;base64,$1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":750,"y":2740,"wires":[["6b3b5a4ca30fa8d1"]]},{"id":"fe59236a23dd2a55","type":"change","z":"407037fe.d66c18","name":"inicio","rules":[{"t":"set","p":"filename","pt":"msg","to":"Canarios_prueba.txt","tot":"str"},{"t":"set","p":"topic","pt":"msg","to":"canarios","tot":"str"},{"t":"delete","p":"uibuilderCtrl","pt":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":290,"y":2720,"wires":[["ef2b05656b7b80b2"]]},{"id":"dbee40e9add316c9","type":"comment","z":"407037fe.d66c18","name":"dropbox \\n -------Canarios_prueba.txt \\n ------đź“” Fotos_canarios","info":"","x":250,"y":3060,"wires":[]},{"id":"42ab4aefa7146bea","type":"comment","z":"407037fe.d66c18","name":"estructura regitro de datos \\n anillo.color.anilloPadre.colorPadre.anilloMadre.colorMadre.sexo.observ.","info":"","x":390,"y":3140,"wires":[]},{"id":"6391626a850ffa53","type":"comment","z":"407037fe.d66c18","name":"El servidor node red puede ejecutarse en un pc, tablet o celular. \\n Para crear el cliente usamos UIBuilder y Bootstrap-Vue.js \\n El cliente (interface grafica) se ejecuta en cualquier buscador que se ejecute en un pc, tablet o celular.\\n Este visor de archivo es parte de un programa..\\n El archivo de datos y la carpeta de fotos estan en Dropbox.","info":"","x":490,"y":2940,"wires":[]},{"id":"9bec5b69e6044f55","type":"debug","z":"407037fe.d66c18","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1150,"y":2820,"wires":[]},{"id":"118d9cdada1cc1b9","type":"comment","z":"407037fe.d66c18","name":"Debe copiarse los archivos HTML y java.script \\n en el nodo UIBuilder ","info":"","x":1226,"y":2924,"wires":[]},{"id":"8bfa92dfedddc57d","type":"comment","z":"407037fe.d66c18","name":"HTML","info":"<!doctype html><html lang=\"en\"><head>\n <meta charset=\"utf-8\">\n <meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n \n <link type=\"text/css\" rel=\"stylesheet\" href=\"../uibuilder/vendor/bootstrap/dist/css/bootstrap.min.css\" />\n <link type=\"text/css\" rel=\"stylesheet\" href=\"../uibuilder/vendor/bootstrap-vue@latest/dist/bootstrap-vue.css\" />\n\n <link rel=\"icon\" href=\"./images/node-blue.ico\">\n <link rel=\"stylesheet\" href=\"https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css\">\n\n <!-- Put your own custom styles in here -->\n <link rel=\"stylesheet\" href=\"./index.css\" media=\"all\">\n\n</head><body>\n \n <div id=\"app\">\n\t<b-container id=\"app_container\">\n\n <b-navbar toggleable=\"lg\" type=\"dark\" variant=\"info\">\n <b-navbar-brand href=\"#\"><img src=\"/home/pi/el_asistente.jpg\" width=\"30\" height=\"30\"> el_asistente</b-navbar-brand>\n\n <b-navbar-toggle target=\"nav-collapse\"></b-navbar-toggle>\n\n </b-navbar>\n <template>\n\t <div>\n <b-table striped hover margin :items=\"itemsCanarios\" selectable @row-selected=\"onRowSelected\" :fields=\"fieldsCan\" style=\"background: rgb(255, 255, 255); font-size: 10px;\">\n <template #cell(sexo)=\"data\">\n <span v-html=\"data.value\"></span>\n </template>\n </b-table>\n </div>\n </template>\n\n\t<b-modal v-model=\"showCanario\" hide-footer>\n <div class=\"card\" v-for=\"select in selected\">\n <img v-if=\"foto !== ''\" class=\"card-img-top\" :src=\"foto\">\n <div class=\"card-header\">\n <h4 class=\"card-title\">{{select.anillo}}</h4>\n <h5 class=\"card-title\">{{select.color}}</h5>\n </div>\n <div class=\"card-body\">\n <h5 class=\"card-text\"><i class=\"bi bi-gender-male\"></i> Padre</h5>\n <p class=\"card-text\">{{select.anilloPadre}}</p>\n <p class=\"card-text\">{{select.colorPadre}}</p>\n <p></p>\n <h5 class=\"card-text\"><i class=\"bi bi-gender-female\"></i> Madre</h5>\n <p class=\"card-text\">{{select.anilloMadre}}</p>\n <p class=\"card-text\">{{select.colorMadre}}</p>\n <p></p>\n <h5 class=\"mb-0\">{{select.sexo}}</h5>\n </div>\n </div>\n </b-modal>\n \n </b-container>\n </div>\n\n <!-- Vendor Libraries - Load in the right order -->\n <script src=\"../uibuilder/vendor/socket.io/socket.io.js\"></script>\n <script src=\"../uibuilder/vendor/vue/dist/vue.min.js\"></script>\n \n <script src=\"../uibuilder/vendor/bootstrap-vue/dist/bootstrap-vue.js\"></script>\n <script src=\"../uibuilder/vendor/http-vue-loader/src/httpVueLoader.js\"></script>\n <script src=\"../uibuilder/vendor/vuex/dist/vuex.js\"></script>\n\n <!-- REQUIRED: Sets up Socket listeners, the uibuilder and msg objects -->\n <script src=\"./uibuilderfe.min.js\"></script>\n\n <!-- Put your own custom code in here -->\n <script src=\"./index.js\"></script>\n\n</body></html>","x":1210,"y":2660,"wires":[]},{"id":"fd1fe1b09ede3af8","type":"comment","z":"407037fe.d66c18","name":"java.script","info":"/* jshint browser: true, esversion: 5, asi: true */\n/*globals Vue, uibuilder */\n// @ts-nocheck\n/*\n Built on the great foundational work of others, free to use, but at your own risk\n*/\n'use strict'\n\n/** @see https://github.com/TotallyInformation/node-red-contrib-uibuilder/wiki/Front-End-Library---available-properties-and-methods */\n\n// eslint-disable-next-line no-unused-vars\nvar app1 = new Vue({\n el: '#app',\n data: {\n foto : '',\n showCanario : false,\n itemsCanarios : [],\n fieldsCanarios : [\n { key: 'anillo', label: 'Anillo Canario', class: 'border-right text-center',},\n { key: 'color', label: 'Color Canario', class: 'border-right text-center',},\n { key: 'anilloPadre', label: 'Anillo Padre', class: 'border-right text-center',},\n { key: 'colorPadre', label: 'Color Padre', class: 'border-right text-center',},\n { key: 'anilloMadre', label: 'Anillo Madre', class: 'border-right text-center',},\n { key: 'colorMadre', label: 'Color Madre', class: 'border-right text-center',},\n { key: 'sexo', label: 'Sexo', class: 'border-right text-center',},\n ],\n fieldsCan : [\n { key: 'sexo', label: 'Sexo', class: 'text-center', formatter: value => {\n if(value == 'M'){return '<img src=\"/home/pi/Macho.jpg\" width=\"20\" height=\"20\">';}\n else{return '<img src=\"/home/pi/Hembra.jpg\" width=\"20\" height=\"20\">';}\n }},\n { key: 'anillo', label: 'Anillo', class: 'text-center',},\n { key: 'color', label: 'Color', class: 'text-center',},\n ],\n selected : []\n }, // --- End of data --- //\n // computed: dynamic data, used as {{ cName }} - cached\n computed: {\n }, // --- End of computed --- //\n // methods:\n methods: {\n onRowSelected(canario) {\n this.selected = canario\n this.showCanario = true\n uibuilder.send({'topic':'foto','payload':this.selected[0].anillo})\n },\n // return formatted HTML version of JSON object\n syntaxHighlight: function(json) {\n json = JSON.stringify(json, undefined, 4)\n json = json.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n return json.replace(/(\"(\\\\u[a-zA-Z0-9]{4}|\\\\[^u]|[^\\\\\"])*\"(\\s*:)?|\\b(true|false|null)\\b|-?\\d+(?:\\.\\d*)?(?:[eE][+\\-]?\\d+)?)/g, function (match) {\n var cls = 'number'\n if (/^\"/.test(match)) {\n if (/:$/.test(match)) {\n cls = 'key'\n } else {\n cls = 'string'\n }\n } else if (/true|false/.test(match)) {\n cls = 'boolean'\n } else if (/null/.test(match)) {\n cls = 'null'\n }\n return '<span class=\"' + cls + '\">' + match + '</span>'\n })\n } // --- End of syntaxHighlight --- //\n\n }, // --- End of methods --- //\n\n // Available hooks: init,mounted,updated,destroyed\n mounted: function(){\n uibuilder.start()\n\n var vueApp = this\n \n vueApp.feVersion = uibuilder.get('version')\n\n // Start countdown. If lastUpdate not updated in 2 minutes, show a warning.\n // vueApp.hTimer = vueApp.heatingUpdTimer()\n\n // On-load Reset the current tab to the one saved in session storage - strange, stored as number but retrieves as a string\n // vueApp.tabIndex = Number(sessionStorage.currentTab)\n\n // If msg changes - msg is updated when a standard msg is received from Node-RED over Socket.IO\n // Note that you can also listen for 'msgsReceived' as they are updated at the same time\n // but newVal relates to the attribute being listened to.\n uibuilder.onChange('msg', function(newVal){\n //console.debug('Vue:mounted:UIBUILDER: property msg changed! ', newVal)\n if(newVal.topic == 'canarios'){\n vueApp.itemsCanarios = newVal.payload\n }\n else if(msg.topic == 'foto'){\n vueApp.foto = msg.payload;\n this.showCanario = true\n }\n }) // ---- End of uibuilder.onChange() watcher function ---- //\n\n } // --- End of mounted hook --- //\n\n}) // --- End of app1 --- //\n\n// EOF","x":1220,"y":2700,"wires":[]}]