vuejs、eggjs、mqtt全栈式开发设备管理系统
vuejs、eggjs、mqtt全栈式开发简单设备管理系统
业余时间用eggjs、vuejs开发了一个设备管理系统,通过mqtt协议上传设备数据至web端实时展现,包含设备参数分析、发送设备报警等模块。收获还是挺多的,特别是vue的学习,这里简单记录一下:
源码地址:https://github.com/caiya/vuejs-admin,写文不易,有帮助的话麻烦给个star,感谢!
技术栈
前端:vue、vuex、vue-router、element-ui、axios、mqttjs
后端:eggjs、mysql、sequlize、restful、oauth2.0、mqtt、jwt
- 用户模块(用户管理,用户增删改查)
- 设备模块(设备管理、设备参数监控、设备参数记录、设备类别管理、参数管理等)
- 授权模块(引入OAuth2.0授权服务,方便将接口以OAuth提供第三方)
- 消息模块(用户申请帮助消息、设备参数告警消息等)
效果图(对一个后端css永远是内伤)
登录页:
主页:
设备页:
设备参数监控页:
前台
项目结构
前端使用vue-cli脚手架构建,基本目录结构如下:
main.js入口
vue项目的入口文件,这里主要是引入iconfont、element-ui、echarts、moment、vuex等模块。
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import { axios } from './http/base'
import ElementUI from 'element-ui'
import 'element-ui/lib/theme-chalk/index.css'
import './assets/fonts/iconfont.css'
import ECharts from 'vue-echarts/components/ECharts'
// import ECharts modules manually to reduce bundle size
import 'echarts/lib/chart/line'
import 'echarts/lib/component/tooltip'
// register component to use
Vue.component('chart', ECharts)
import store from './store'
import moment from 'moment'
Vue.prototype.$moment = moment
Vue.use(ElementUI)
// 引入mqtt
import './mq'
Vue.config.productionTip = false
// 挂载到prototype上面,确保组件中可以直接使用this.axios
// Vue.prototype.axios = axios
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
注意:
1、引入比较大的模块比如echarts时,尽量手动按需进行模块导入,节省打包文件大小
2、一般通过将模块比如moment挂载到Vue的prototype上面,这样就可以在任意vue组件中使用*this.$moment*进行moment操作了
3、iconfont是阿里的图标样式,下载下来后放入assets中再引入即可
vuex引入
vuex引入的时候采用了模块话引入,入口文件代码为:
import Vue from 'vue'
import Vuex from 'vuex'
import user from './modules/user'
import devArgsMsg from './modules/devArgsMsg'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
user,
devArgsMsg
}
})
其中user、devArgsMsg为两个独立模块,这样分模块引入可以避免项目过大结构不清晰的问题。其中user.js模块代码:
import * as TYPES from '../mutation.types'
const state = {
userInfo: JSON.parse(localStorage.getItem('userInfo') || '{}'),
token: localStorage.getItem('token') || ''
}
const actions = {
}
const mutations = {
[TYPES.LOGIN]: (state, loginData) => {
state.userInfo = loginData.user
state.token = loginData.token
localStorage.setItem('userInfo', JSON.stringify(loginData.user))
localStorage.setItem('token', loginData.token)
},
[TYPES.LOGOUT]: state => {
state.userInfo = {}
state.token = ''
localStorage.removeItem('userInfo')
localStorage.removeItem('token')
}
}
const getters = {
}
export default {
state,
actions,
mutations,
getters
}
关于mutations.type.js:
// 各种mutation类型
// 用户模块
export const LOGOUT = 'LOGOUT'
export const LOGIN = 'LOGIN'
// 设备模块
export const SETDEVARGSMSG = 'setDevArgsMsg'
注意:
1、mutations的名称定义时遵循官方,一般定义为常量
2、state的数据只有通过mutation才能操作,不能直接在组件中设置state,否则无效
3、mutation中的操作都是同步操作,异步操作或网络请求或同时多个mutation操作可以放入action中进行
4、用户信息、登录token一般放入h5的localStorage,这样刷新页面保证关键数据不丢失
5、vuex中的getters相当于state的计算属性,监听state数据变动时可以使用getters
vue-router路由模块
路由模块基本使用:
import Vue from 'vue'
import Router from 'vue-router'
import store from '../store'
Vue.use(Router)
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'Login',
component: resolve => require(['@/views/auth/Login'], resolve)
},
{
path: '', // 默认地址为登录页
name: '',
component: resolve => require(['@/views/auth/Login'], resolve)
},
{
path: '/main',
name: '',
component: resolve => require(['@/views/Main'], resolve),
meta: {
requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的
nav: '欢迎页'
},
children: [{
path: 'user',
component: resolve => require(['@/views/user/List'], resolve),
name: 'UserList',
meta: {
requireAuth: true,
nav: '用户管理',
activeItem: '1-1'
},
}, {
path: 'user/setting/:userId?',
name: 'UserSetting',
component: resolve => require(['@/views/user/Setting'], resolve),
meta: {
requireAuth: true,
nav: '资料设置',
activeItem: '1-2'
},
}, {
path: 'device',
component: resolve => require(['@/views/device/List'], resolve),
name: 'Device',
meta: {
requireAuth: true,
nav: '设备列表',
activeItem: '3-1'
},
},{
path: 'device/edit/:devId?',
component: resolve => require(['@/views/device/Edit'], resolve),
name: