从零开始搭建物联网平台(六)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环境是否安装成功
然后配置淘宝镜像源
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基本展示页面了
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"/>
效果图
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的报表图。
首先在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
上一篇: 2020-08-10
下一篇: 芯片制程与功耗,运算速度关系