欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-06-15 22:10:01
网页端使用主流的vue进行搭建。在element和iview之间选择了iview这个前端框架。从个人角度element对开发者更加友好一些,很多内置的方法、组件都可以自定义实现,而且element社区要比iview活跃的多。那么为什么最终选择了iview呢,因为iview的界面ui比element相对丝滑一些。比较符合我的审美。主要原因还是因为懒惰。去年我已经用iview开发过了一套系统。所以这次就直接复用了之前的用户、权限、机构、角色这些大模块。减少了一部分工作量。当然后续如果有机会肯定是优先选择......

    网页端使用主流的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环境是否安装成功

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

然后配置淘宝镜像源

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

2.获取源码

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

然后执行npm install、npm run dev

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

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

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"/>

效果图

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

 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()
	})
},

效果图

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

 4.设备树

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

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

首先在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端并不难,这篇文章更多的是一个引子,你可以用不同的方式去实现更酷的界面,更好用的功能。前端的路很精彩!

本文地址:https://blog.csdn.net/qq_35921773/article/details/107937401