从零开始搭建物联网平台(六)PC端

简介: 从零开始搭建物联网平台(六)PC端

   网页端使用主流的vue进行搭建。在element和iview之间选择了iview这个前端框架。从个人角度element对开发者更加友好一些,很多内置的方法、组件都可以自定义实现,而且element社区要比iview活跃的多。那么为什么最终选择了iview呢,因为iview的界面ui比element相对丝滑一些。比较符合我的审美。主要原因还是因为懒惰。去年我已经用iview开发过了一套系统。所以这次就直接复用了之前的用户、权限、机构、角色这些大模块。减少了一部分工作量。当然后续如果有机会肯定是优先选择用element翻新一下。其实从本质上,无论是element还是iview区别并不大,他们都是vue的生态圈相对火热的框架,所以无论选择哪一个都可以。这边就先用iview示范如何搭建PC端。


一、框架搭建


1.安装环境


首先下载node环境,安装nodejshttp://nodejs.cn/


然后打开cmd,输入node -v、npm -v确认node环境是否安装成功


20200811160241551.png

然后配置淘宝镜像源

1. npm config set registry https://registry.npm.taobao.org
2. npm config get registry

2.获取源码

从git上拉取https://github.com/iview/iview-admin.git

然后执行npm install、npm run dev

就可以看到iview基本展示页面了

iview-admin已经自带了基本的各种组件,可以很大程度上节省我们时间。缺点也很明显就是引用了过多的冗余组件,致使加载速度降低。当然这些我们都可以自行优化。

3.开发前导

在进行iview开发之前,我们要尽量对iview有足够的了解。建议多看看官网https://www.iviewui.com/docs/introduce

基本上都是即粘即用。大部分的组件都是iview已经为我们写好了,极个别没有的,我们也都可以用简单组件自己实现,所以尽量不是我们去创造组件,而是合理运用已经存在的组件,多去官网查找,复制粘贴才是无望而不利的神器。

二、基础开发

1.去除Eslint校验


iview-admin是内置了eslint的,这种校验对于一开始写代码是不太方便的,当然如果一直开着也可以规范我们的代码风格,我个人比较不喜欢这种约束。一般我都是把它关闭了,最后项目成型后,使用

npm run lint-fix


进行代码修复。在view中只需要在根目录的.eslintignore文件中写上*,就会自动忽略所有格式问题。

2.配置后台服务代理

一般服务都是要进行访问真实的后台接口,我们在使用iview后就尽量前后端分离部署。前台使用nginx部署,后台jar包方式部署。这样前台服务访问后台就需要代理进行搭桥。

在项目根目录的vue.config.js中可以找到devServer的属性,这个就是配置代理的地方

 devServer: {
      host: 'localhost',
      port: 8010,
        proxy: {
            '/netgate-server': {
              // target: 'http://localhost:8001/',
                target: 'http://127.0.0.1:8001/',
              //pathRewrite: {'^/netgate-server':''},
              changeOrigin: true
            }
        }
    }

这样当我前台使用netgate-server这个属性的 时候就会自动跳转到http://127.0.0.1:8001/这个后台地址

三、核心组件

1.MQTT通讯组件


etcloud采用的是emq作为broker,前台服务作为一个client,后台服务也是一个client,微信端同样是一个client。通过emq中间件进行消息的流通传递。

首先我们需要引入npm的mqtt模块


npm install mqtt@2.0.0

这里有个小坑,最新的mqtt模块和vue不兼容。因为ts的问题。我也没有过多研究,就直接降低了mqtt的版本。

页面上如何使用呢


<template>
    <Row>
    <Card>
      <p slot="title">
        <Icon type="android-person"></Icon> 设备客户端测试
      </p>
      <div>
        <Row>
          <i-col span="8">
            <Select style="width:200px" placeholder="请选择设备">
              <Option v-for="(item,index) in deviceList" :value="item.sn" :key="index" @click.native="choseDevice(item)">{{ item.sn }}</Option>
            </Select>
            <Button type="success" @click="connectMq">连接</Button>
            <Button type="error"  @click="disConnectMq">断开</Button>
          </i-col>
          <i-col span="16">
            <Row>
              <i-col span="12">
                产品ID:{{curDevice.pid}}
              </i-col>
              <i-col span="12">
                产品TOKEN:{{curDevice.token}}
              </i-col>
            </Row>
            <Row>
              <i-col span="12">
                设备序列号:{{curDevice.sn}}
              </i-col>
              <i-col span="12">
                产品名称:{{curDevice.pname}}
              </i-col>
            </Row>
          </i-col>
        </Row>
      </div>
    </Card>
</template>
<script>
  const uuidv1 = require('uuid/v1');
  import mqtt from 'mqtt'
  const moment = require('moment');
  // 连接选项
  let options = {
    clean: true, // 保留回话
    connectTimeout: 4000, // 超时时间
    // 认证信息
    clientId: 'test_client_'+Math.random(),
    username: 'test',
    password: 'test',
  }
  const connectUrl = 'wss://www.etcloud.club:8084/mqtt'
    export default {
    name: 'mqtt-test',
        data() {
            return {
              client:{},//mqtt客户端
            }
        },
     created(){
            //页面刚进入时开启长连接
      //this.getDeviceList();
        },
     destroyed: function() {
       //页面销毁时关闭长连接
      this.client.end();
      this.$Message.success('断开成功');
     },
     methods: {
       connectMq(){
        if(JSON.stringify(this.curDevice) == "{}"){
          this.$Message.info('请先选择设备');
          return;
        }
        if(this.client.connected){
          return
        }
        options.clientId = this.curDevice.sn;
        options.username = this.curDevice.pid;
        options.password = this.curDevice.token;
         this.client = mqtt.connect(connectUrl, options)
         console.log(this.client)
         this.client.on('connect', (e) => {
           this.$Message.success('连接成功');
         })
         // this.client.subscribe('/World1234', { qos: 1 }, (error) => {
          //  if (!error) {
          //   console.log('订阅成功')
          //  } else {
          //   console.log('订阅失败')
          //  }
         // })
         // 接收消息处理
         this.client.on('message', (topic, message) => {
           console.log('收到来自', topic, '的消息', message.toString())
           var msgObj = {};
           new Promise((resolve, reject) => {/* executor函数 */
             msgObj = JSON.parse(message.toString());
             resolve(msgObj);
           }).catch(function (value) {
             console.log('JSON转化异常')
             return;
           });
           this.sdData.push({time:moment().format("YYYY-MM-DD HH:mm:ss"),topic:topic,content:msgObj})
         })
         // 断开发起重连
         this.client.on('reconnect', (error) => {
           console.log('正在重连:', error)
         })
         // 链接异常处理
         this.client.on('error', (error) => {
           console.log('连接失败:', error)
         })
       },
       disConnectMq(){
         this.sbData = [];
         this.sdData = [];
         this.curMsg={
           topic:'',
           obj:{}
         },
         this.subTopic={};
         this.subTopicList=[];
         if(this.client.connected){
           this.client.end();
         }
         // this.curMsg.topic = item.pid+'/'+item.sn+'/client'
         // this.subTopic.topic =  item.pid+'/'+item.sn+'/server'
         // this.subTopic.qos = 0
         this.$Message.success('断开成功');
       },
      //订阅主题
      subTopicHandle(){
        console.log(this.subTopic)
        console.log(this.subTopic.qos)
        console.log(this.subTopic.qos=='')
         if(JSON.stringify(this.curDevice) == "{}"){
           this.$Message.info('请先选择设备');
           return;
         }else if(!this.client.connected){
           this.$Message.info('请先选连接设备');
           return;
         }else if(this.subTopic.topic==''){
           this.$Message.info('订阅主题不能为空');
           return;
         }
         for(let i=0;i<this.subTopicList.length;i++){
          if(this.subTopic.topic ==  this.subTopicList[i].topic){
            this.$Message.info('相同的主题无法订阅两次');
            return;
          }
         }
         // else if(this.subTopic.qos==''){
          //  this.$Message.info('订阅消息质量不能为空');
          //  return;
         // }
        this.client.subscribe(this.subTopic.topic, { qos: this.subTopic.qos }, (error) => {
          if (!error) {
            this.$Message.success('订阅成功');
            this.subTopicList.push({time:moment().format("YYYY-MM-DD HH:mm:ss"),topic:this.subTopic.topic,qos:this.subTopic.qos})
          } else {
            this.$Message.error('订阅失败');
          }
        })
       },
    },
  }
</script>

2.JsonEditor

可以格式化数据为json格式,因为系统中所有的消息都是采用json格式进行通讯的,所以引入这个组件可以有效的减少json出错率,十分好用


首先我们先引入JsonEditor

npm install jsoneditor --save

接下来我们把JsonEditor封装成一个组件

<!--codemirror-json格式化-->
<template>
  <div class="json-editor">
    <textarea ref="textarea"/>
  </div>
</template>
<script>
  import CodeMirror from 'codemirror'
  import 'codemirror/addon/lint/lint.css'
  import 'codemirror/lib/codemirror.css'
  import 'codemirror/theme/rubyblue.css'
  // require('script-loader!jsonlint')
  import 'codemirror/mode/javascript/javascript'
  // import 'codemirror/addon/lint/lint'
  import 'codemirror/addon/lint/json-lint'
  export default {
    name: 'JsonEditor',
    /* eslint-disable vue/require-prop-types */
    props: ['value'],
    data () {
      return {
        jsonEditor: false
      }
    },
    watch: {
      value (value) {
        const editor_value = this.jsonEditor.getValue()
        if (value !== editor_value) {
          this.jsonEditor.setValue(JSON.stringify(this.value, null, 2))
        }
      }
    },
    mounted () {
      this.jsonEditor = CodeMirror.fromTextArea(this.$refs.textarea, {
        lineNumbers: true,
        mode: 'application/json',
        gutters: ['CodeMirror-lint-markers'],
        theme: 'rubyblue',
        autoRefresh: true,
        lint: true
      })
      this.jsonEditor.setValue(JSON.stringify(this.value, null, 2))
      this.jsonEditor.on('change', cm => {
        this.$emit('changed', cm.getValue())
        this.$emit('input', cm.getValue())
      })
    },
    methods: {
      getValue () {
        return this.jsonEditor.getValue()
      },
      refresh() {
        this.jsonEditor && this.jsonEditor.refresh();
      }
    }
  }
</script>
<style scoped>
  .json-editor {
    height: 100%;
    position: relative;
  }
  .json-editor >>> .CodeMirror {
    height: auto;
    min-height: 180px;
  }
  .json-editor >>> .CodeMirror-scroll {
    min-height: 180px;
  }
  .json-editor >>> .cm-s-rubyblue span.cm-string {
    color: #f08047;
  }
</style>

在页面上调用组件,传入参数即可

import JsonEditor from '@/views/main-components/CodeEditor'

记得声明组件

components: { JsonEditor },

在页面中调用组件,然后对curMsg.obj传参即可使用

<json-editor style="width: 98%" ref="jsonEditor" v-model="curMsg.obj"/>

效果图

20200811171701619.png


3.粘贴板组件Clipboard

Clipboard可以实现点击复制某段文字,在我们输入密钥、各种ID的时候十分方便,点击即可复制某个字符串。

安装

npm install clipboard --save

引入

import Clipboard from 'clipboard';

页面中使用

 <Tooltip placement="top">
  <span style="display:block;cursor:pointer;width:100%;overflow: hidden;text-overflow:ellipsis;white-space: nowrap;height:12px;line-height: 15px;" class="tag-read2" :data-clipboard-text="productInfo.token" @click="copy(2)">{{productInfo.token}}</span>
  <div slot="content">
    点击复制
  </div>
</Tooltip>

方法

copy(type) {
  var clipboard = new Clipboard('.tag-read'+type)
  clipboard.on('success', e => {
    this.$Message.success('复制成功');
    // 释放内存
    clipboard.destroy()
  })
  clipboard.on('error', e => {
    // 不支持复制 释放内存
    this.$Message.error('不支持复制');
    clipboard.destroy()
  })
},

效果图

4.设备树

设备树可以直观的个人所拥有的产品、设备的概况、在线情况、连接日志、指令日志、功能日志……设备树是整个系统的门面和入口,当然后续我也打算做一些Echarts的报表图。


20200811173607578.png

首先在vue的data中声明

gatewaydata: [
  {
    title: '设备列表',
    expand: true,
    render: (h, { root, node, data }) => {
      return h('span', {
        style: {
          display: 'inline-block',
          width: '100%'
        }
      }, [
        h('span', [
          h('Icon', {
            props: {
              type: 'network',
              color: '#2d8cf0',
              size: '15'
            },
            style: {
              marginRight: '8px'
            }
          }),
          h('span', data.title)
        ]),
      ]);
    },
    children: []
  }
],
buttonProps: {
  type: 'ghost',
  size: 'small',
},

然后再页面中写入树

 <Tree :data="gatewaydata" :render="renderContent" style="overflow-y: auto;overflow-x: hidden " :style="treestyle"></Tree>

然后在methods中写明render对树进行渲染

renderContent (h, { root, node, data }) {
  return h('span', {
    style: {
      display: 'inline-block',
      width: '100%'
    }
  }, [
    h('span', [
      h('Icon', {
        props: {
          type: 'record',
          size: '12',
          color: data.color
        },
        style: {
          marginRight: '8px'
        }
      }),
      h('Button', {
        props: Object.assign({}, this.buttonProps, {
          type: data.buttontype,
          inner: data.title
        }),
        style: {
          margin: '-4px 0px 0px 0px'
        },
        on: {
          click: () => {
            this.handleTreeClick(data)
          },
          hover: () => {
            //console.log(11)
          }
        }
      }, data.title)
    ])
  ]);
},

对于树节点的设备的上线下线只需要更改data中gateawaydata子元素的color即可

if(topic.startsWith('$SYS/brokers/')){
  let status = topicArr[5]
  if(status=='connected'){
    for(let j=0;j<self.gatewaydata[0].children.length;j++){
      if(self.gatewaydata[0].children[j].id==msgObj.username){
        for(let k=0;k<self.gatewaydata[0].children[j].children.length;k++){
          if(self.gatewaydata[0].children[j].children[k].sn==msgObj.clientid){
            self.gatewaydata[0].children[j].children[k].color = '#19be6b';
            return;
          }
        }
      }
    }
  }else if(status=='disconnected'){
    for(let j=0;j<self.gatewaydata[0].children.length;j++){
      if(self.gatewaydata[0].children[j].id==msgObj.username){
        for(let k=0;k<self.gatewaydata[0].children[j].children.length;k++){
          if(self.gatewaydata[0].children[j].children[k].sn==msgObj.clientid){
            self.gatewaydata[0].children[j].children[k].color = '#bbbec4';
            return;
          }
        }
      }
    }
  }
} 

四、总结

  其实引用的组件远不止这些,vue还有各种很好玩的组件只要往往给人眼前一亮的感觉,所以要勇于尝试,不过结局如何,过程也会其乐无穷。PC端其实我并没有引用很复杂的东西,大部分功能都是在iview的组件基础上完成的,比如树形结构、比如扩展列、父子组件的传参监听、权限控制路由显示……。但是总体上PC端并不难,这篇文章更多的是一个引子,你可以用不同的方式去实现更酷的界面,更好用的功能。前端的路很精彩!

相关实践学习
钉钉群中如何接收IoT温控器数据告警通知
本实验主要介绍如何将温控器设备以MQTT协议接入IoT物联网平台,通过云产品流转到函数计算FC,调用钉钉群机器人API,实时推送温湿度消息到钉钉群。
阿里云AIoT物联网开发实战
本课程将由物联网专家带你熟悉阿里云AIoT物联网领域全套云产品,7天轻松搭建基于Arduino的端到端物联网场景应用。 开始学习前,请先开通下方两个云产品,让学习更流畅: IoT物联网平台:https://iot.console.aliyun.com/ LinkWAN物联网络管理平台:https://linkwan.console.aliyun.com/service-open
目录
相关文章
|
物联网
通过微信小程序体验阿里云IoT物联网平台
通过微信小程序体验阿里云IoT物联网平台
9172 0
|
3月前
|
消息中间件 传感器 物联网
手把手教你搭建物联网平台,轻松实现远程设备管理
嘿,大家好!我是技术小伙伴小米,今天分享的主题是“物联网平台接入”。在这个万物互联的时代,智能设备如雨后春笋般涌现。我们将探讨如何通过物联网平台实现设备远程控制,包括设备数据的上行和指令的下行。上行数据链路涉及设备通过MQTT协议上报数据至平台,并通过消息队列转发至业务系统;下行指令链路则是业务系统通过API调用云端服务,将控制指令下发给设备。整个过程高效便捷,让你轻松掌握物联网技术的核心流程。
64 5
|
存储 数据采集 传感器
阿里云物联网平台简介 | 学习笔记
快速学习 阿里云物联网平台简介
1376 15
阿里云物联网平台简介 | 学习笔记
|
小程序 Java 关系型数据库
从零开始搭建物联网平台(五)服务端
从零开始搭建物联网平台(五)服务端
963 0
|
机器学习/深度学习 存储 数据采集
从零开始搭建物联网平台(一)前言
从零开始搭建物联网平台(一)前言
231 1
《阿里云产品手册2022-2023 版》——物联网平台
《阿里云产品手册2022-2023 版》——物联网平台
516 1
|
弹性计算 小程序 数据挖掘
从零开始搭建物联网平台(二)环境准备
从零开始搭建物联网平台(二)环境准备
360 0
|
物联网
《阿里云IoT物联网平台技术介绍和入门实战》电子版地址
阿里云IoT物联网平台技术介绍和入门实战
163 15
《阿里云IoT物联网平台技术介绍和入门实战》电子版地址
|
监控 API 数据安全/隐私保护
阿里云物联网平台入门体验
阿里云物联网平台是一个集成了设备管理、数据安全通信和消息订阅等能力的一体化平台。向下支持连接海量设备,采集设备数据上云;向上提供云端API,服务端可通过调用云端API将指令下发至设备端,实现远程控制。此篇文章按照官方入门实例操作下
456 0
阿里云物联网平台入门体验
|
网络协议 物联网 数据处理
【平头哥RVB2601开发板试用体验】AT 解析器和通过w800 AT命令接入阿里云生活物联网平台
RISC-V RVB2601 Yoc 实现和测试验证阿里云平台接入的等相关功能,同时去了解YoC中网络设备和AT解析器框架。
703 0
【平头哥RVB2601开发板试用体验】AT 解析器和通过w800 AT命令接入阿里云生活物联网平台