Explorar o código

Merge remote-tracking branch 'origin/master'

houyaf %!s(int64=3) %!d(string=hai) anos
pai
achega
57e36fb17c
Modificáronse 31 ficheiros con 1309 adicións e 20 borrados
  1. 35 0
      build/index.js
  2. 1 0
      package.json
  3. 82 0
      src/api/aqpt/visualEditor.js
  4. 6 6
      src/api/system/partnerCatApi.js
  5. 1 2
      src/layout/index.vue
  6. 67 0
      src/mixin/mqtt.js
  7. 20 1
      src/permission.js
  8. 14 0
      src/router/modules/aqpt.js
  9. 8 0
      src/utils/index.js
  10. 5 3
      src/utils/request.js
  11. 169 0
      src/views/aqpt/projectWork/components/projectWorkForm.vue
  12. 126 0
      src/views/aqpt/projectWork/index.vue
  13. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/1.png
  14. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/10.png
  15. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/11.png
  16. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/12.png
  17. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/13.png
  18. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/2.png
  19. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/3.png
  20. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/4.png
  21. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/5.png
  22. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/6.png
  23. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/7.png
  24. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/8.png
  25. BIN=BIN
      src/views/aqpt/visualEditor/images/layers/9.png
  26. BIN=BIN
      src/views/aqpt/visualEditor/images/layout_cont.png
  27. BIN=BIN
      src/views/aqpt/visualEditor/images/layout_cont1.png
  28. 575 0
      src/views/aqpt/visualEditor/index.vue
  29. 1 0
      src/views/login/auth-redirect.vue
  30. 22 3
      src/views/login/index.vue
  31. 177 5
      yarn.lock

+ 35 - 0
build/index.js

@@ -0,0 +1,35 @@
+const { run } = require('runjs')
+const chalk = require('chalk')
+const config = require('../vue.config.js')
+const rawArgv = process.argv.slice(2)
+const args = rawArgv.join(' ')
+
+if (process.env.npm_config_preview || rawArgv.includes('--preview')) {
+  const report = rawArgv.includes('--report')
+
+  run(`vue-cli-service build ${args}`)
+
+  const port = 9526
+  const publicPath = config.publicPath
+
+  var connect = require('connect')
+  var serveStatic = require('serve-static')
+  const app = connect()
+
+  app.use(
+    publicPath,
+    serveStatic('./dist', {
+      index: ['index.html', '/']
+    })
+  )
+
+  app.listen(port, function () {
+    console.log(chalk.green(`> Preview at  http://localhost:${port}${publicPath}`))
+    if (report) {
+      console.log(chalk.green(`> Report at  http://localhost:${port}${publicPath}report.html`))
+    }
+
+  })
+} else {
+  run(`vue-cli-service build ${args}`)
+}

+ 1 - 0
package.json

@@ -34,6 +34,7 @@
     "leaflet": "^1.7.1",
     "leaflet-draw": "^1.0.4",
     "leaflet-editable": "^1.2.0",
+    "mqtt": "^4.3.7",
     "normalize.css": "7.0.0",
     "nprogress": "0.2.0",
     "path-to-regexp": "2.4.0",

+ 82 - 0
src/api/aqpt/visualEditor.js

@@ -0,0 +1,82 @@
+import request from '@/utils/request'
+
+/**
+ * 工作项目查询
+ */
+export function getJobsApi() {
+  return request({
+    url: '/JobActivitys/logo/list'
+  })
+}
+export function getJobsByIdApi(typeId) {
+  return request({
+    url: `/JobActivitys/logo/${typeId}`
+  })
+}
+/**
+ * 工作项目新增
+ */
+export function jobAddApi(data) {
+  return request({
+    url: '/JobActivitys/logo/add',
+    method: 'post',
+    data
+  })
+}
+/**
+ * 工作项目删除
+ */
+export function jobDelApi(typeId) {
+  return request({
+    url: `/JobActivitys/logo/delete/${typeId}`,
+    method: 'delete'
+  })
+}
+/**
+ * 工作项目修改
+ */
+export function jobUpdateApi(data) {
+  return request({
+    url: `/JobActivitys/logo/update`,
+    method: 'put',
+    data
+  })
+}
+/**
+ * 查询
+ */
+export function getLayersApi() {
+  return request({
+    url: '/JobActivitys/vedit/list'
+  })
+}
+
+/**
+ * 新增
+ */
+export function addApi(data) {
+  return request({
+    url: '/JobActivitys/vedit/add',
+    method: 'post',
+    data
+  })
+}
+/**
+ * 删除
+ */
+export function delApi(veditId) {
+  return request({
+    url: `/JobActivitys/vedit/delete/${veditId}`,
+    method: 'delete'
+  })
+}
+/**
+ * 修改
+ */
+export function updateApi(data) {
+  return request({
+    url: `/JobActivitys/vedit/update`,
+    method: 'put',
+    data
+  })
+}

+ 6 - 6
src/api/system/partnerCatApi.js

@@ -3,7 +3,7 @@ import request from '@/utils/request'
 /**
  * 分页查询Partner Category
  * @param data
- * @returns
+ * @returns {AxiosPromise}
  */
 export function getPartnerCatByPage(data) {
   return request({
@@ -16,7 +16,7 @@ export function getPartnerCatByPage(data) {
 /**
  * 列表查询Partner Category
  * @param data
- * @returns
+ * @returns {AxiosPromise}
  */
 export function getPartnerCatByList(data) {
   return request({
@@ -29,7 +29,7 @@ export function getPartnerCatByList(data) {
 /**
  * 基于ID查询Partner Category
  * @param data
- * @returns
+ * @returns {AxiosPromise}
  */
 export function getPartnerCatById(id) {
   return request({
@@ -41,7 +41,7 @@ export function getPartnerCatById(id) {
 /**
  * 新增Partner Category
  * @param data
- * @returns
+ * @returns {AxiosPromise}
  */
 export function createPartnerCat(data) {
   return request({
@@ -54,7 +54,7 @@ export function createPartnerCat(data) {
 /**
  * 更新Partner Category
  * @param data
- * @returns
+ * @returns {AxiosPromise}
  */
 export function updatePartnerCat(data) {
   return request({
@@ -67,7 +67,7 @@ export function updatePartnerCat(data) {
 /**
  * 删除Partner Category
  * @param data
- * @returns
+ * @returns {AxiosPromise}
  */
 export function deletePartnerCatById(id) {
   return request({

+ 1 - 2
src/layout/index.vue

@@ -84,7 +84,7 @@ export default {
     position: relative;
     height: 100%;
     width: 100%;
-    background-image:  url('../assets/images/login/login_background.jpg');
+    background-image:  url("../assets/images/login/login_background.jpg");
     background-repeat: no-repeat;
     background-color: #071A29;
 
@@ -125,7 +125,6 @@ export default {
     .sidebar-container{
       display: none;
     }
-
     .main-container{
       margin-left: 0;
     }

+ 67 - 0
src/mixin/mqtt.js

@@ -0,0 +1,67 @@
+
+import { mqttUrl } from '@/settings'
+import mqtt from 'mqtt'
+export default {
+  destroyed() {
+    if (this.client.connected) {
+      try {
+        this.client.end(false, () => {
+          console.log('Successfully disconnected!')
+        })
+      } catch (error) {
+        console.log('Disconnect failed', error.toString())
+      }
+    }
+  },
+  methods: {
+    initMqtt(
+      params = {
+        mqttResponse: () => {}
+      }
+    ) {
+      const clientId = new Date().getTime()
+      const options = {
+        clean: true,
+        connectTimeout: 4000,
+        clientId: clientId,
+        username: 'admin',
+        password: 'houyaf1!',
+        will: {
+          topic: 'willTopic',
+          payload: clientId + '与服务器断开连接',
+          retain: false,
+          qos: 0
+        }
+      }
+      const client = mqtt.connect(mqttUrl, options)
+      client.on('connect', function() {
+        console.log('Connected')
+        client.subscribe('topic/alljobactivitys', function(err) {
+          console.log(err)
+          if (!err) {
+            client.publish('topic/1', 'Hello mqtt')
+          } else {
+            client.publish('topic/1', 'Hello mqtt err')
+          }
+        })
+      })
+      client.on('message', function(_topic, message) {
+        console.log('message')
+        if (params.mqttResponse)params.mqttResponse(message)
+      })
+      client.on('reconnect', (error) => {
+        console.log('正在重连:', error)
+      })
+      client.on('error', (error) => {
+        console.log('连接失败:', error)
+      })
+      client.on('close', function() {
+        console.log('Disconnected')
+      })
+      client.on('offline', function() {
+        console.log('offline')
+      })
+      this.client = client
+    }
+  }
+}

+ 20 - 1
src/permission.js

@@ -27,6 +27,15 @@ router.beforeEach(async(to, from, next) => {
     } else {
       const hasPermits = store.getters.permits && store.getters.permits.length > 0
       if (hasPermits) {
+        // 跳转首页但是没有首页
+        if (from.path === '/' && to.path === '/404' || from.path === '/login' && to.path === '/404') {
+          next({
+            path: store.state.permission.addRoutes[0].path,
+            replace: true
+          })
+          NProgress.done()
+          return
+        }
         next()
       } else {
         // 如果没有获取到vuex中到name
@@ -44,6 +53,15 @@ router.beforeEach(async(to, from, next) => {
           const accessRoutes = await store.dispatch('permission/generateRoutes', permits)
           // dynamically add accessible routes
           router.addRoutes(accessRoutes)
+          // 跳转首页但是没有首页
+          if (from.path === '/' && to.path === '/404' || from.path === '/login' && to.path === '/404') {
+            next({
+              path: accessRoutes[0].path,
+              replace: true
+            })
+            NProgress.done()
+            return
+          }
           next({
             ...to,
             replace: true
@@ -66,7 +84,8 @@ router.beforeEach(async(to, from, next) => {
       next()
     } else {
       // other pages that do not have permission to access are redirected to the login page.
-      next(`/login?redirect=${to.path}`)
+      // next(`/login?redirect=${to.path}`)
+      next(`/login`)
       NProgress.done()
     }
   }

+ 14 - 0
src/router/modules/aqpt.js

@@ -18,6 +18,13 @@ const aqptRouter = [
       }
     ]
   },
+  /** 不能跟路由1重复出现 */
+  {
+    path: '/',
+    component: () => import('@/views/aqpt/visualEditor/index'),
+    name: 'VisualEditor',
+    meta: { title: '作业活动可视化配置', noCache: true, permit: 'jyprj_setting' }
+  },
   {
     path: '/alert',
     component: Layout,
@@ -120,6 +127,13 @@ const aqptRouter = [
         component: () => import('@/views/aqpt/riskpoint/index'),
         name: 'entRiskPoint',
         meta: { title: '风险点', noCache: true, permit: 'aqpt_scene_riskpoint' }
+      },
+
+      {
+        path: 'projectWork',
+        component: () => import('@/views/aqpt/projectWork/index'),
+        name: 'projectWork',
+        meta: { title: '工作项目管理', noCache: true, permit: 'aqpt_scene_riskpoint' }
       }
     ]
   },

+ 8 - 0
src/utils/index.js

@@ -561,6 +561,14 @@ export function getNestedChildren(arr = [], parent = 0) {
   }
   return res
 }
+export function Uint8ArrayToString(fileData) {
+  var dataString = ''
+  for (var i = 0; i < fileData.length; i++) {
+    dataString += String.fromCharCode(fileData[i])
+  }
+
+  return dataString
+}
 // 风险类型
 export function riskType(val) {
   const strs = [

+ 5 - 3
src/utils/request.js

@@ -1,5 +1,5 @@
 import axios from 'axios'
-import { MessageBox, Message } from 'element-ui'
+import { MessageBox, Message, Loading } from 'element-ui'
 import store from '@/store'
 import { getToken } from '@/utils/auth'
 import { appID, appKey, appSecret, serverUrl, devServerUrl } from '@/settings'
@@ -11,7 +11,7 @@ const service = axios.create({
   timeout: 1000 * 60 * 3 // request timeout
 })
 
-// let loadingInstance = null
+let loadingInstance = null
 
 // request interceptor
 service.interceptors.request.use(
@@ -20,7 +20,7 @@ service.interceptors.request.use(
     config.headers['AppID'] = appID
     config.headers['AppKey'] = appKey
     config.headers['AppSecret'] = appSecret
-
+    loadingInstance = Loading.service({ fullscreen: true })
     if (store.getters.token) {
       config.headers['Authorization'] = 'Bearer ' + getToken()
     }
@@ -36,6 +36,7 @@ service.interceptors.request.use(
 // response interceptor
 service.interceptors.response.use(
   response => {
+    loadingInstance.close()
     const res = response.data
     if (res.code !== 200) {
       // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
@@ -63,6 +64,7 @@ service.interceptors.response.use(
     }
   },
   error => {
+    loadingInstance.close()
     Message({
       message: error.message || 'Error',
       type: 'error',

+ 169 - 0
src/views/aqpt/projectWork/components/projectWorkForm.vue

@@ -0,0 +1,169 @@
+<template>
+  <el-drawer
+    :title="title"
+    :modal-append-to-body="false"
+    :modal="false"
+    :wrapper-closable="false"
+    size="35%"
+    :visible.sync="dialogVisible"
+  >
+    <div class="content-container">
+      <vuescroll :ops="ops" style="height: calc(100vh - 250px)">
+        <el-form ref="ruleForm" :model="formData" :rules="rules" label-position="right" label-width="120px">
+          <el-form-item label="标题">
+            <el-input v-model="formData.jobactivityTitle" placeholder="输入标题" readonly />
+          </el-form-item>
+          {{ formData.jobactivityTitle }}
+          <el-form-item label="工作类型">
+            <el-select v-model="formData.typeId" style="width: 100%" filterable placeholder="选择类别" @change="changeType(formData.typeId)">
+              <el-option
+                v-for="item in typeList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="图标">
+            <FileUpload v-model="formData.logoUrl" />
+          </el-form-item>
+        </el-form>
+      </vuescroll>
+
+      <div class="btn-group">
+        <el-button type="primary" @click="submitForm('ruleForm')">确 定</el-button>
+        <el-button class="cancel-btn" @click="dialogVisible = false">取 消</el-button>
+      </div>
+    </div>
+
+  </el-drawer>
+</template>
+
+<script>
+import vuescroll from 'vuescroll'
+import FileUpload from '@/components/FileUpload/avatarUpload'
+import { getJobsByIdApi, jobAddApi, jobUpdateApi } from '@/api/aqpt/visualEditor'
+export default {
+  name: 'ProjectWorkForm',
+  components: {
+    vuescroll,
+    FileUpload
+  },
+  props: {
+    title: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      ops: {
+        bar: {
+          keepShow: false,
+          background: 'rgba(144, 147, 153, 0.4)',
+          onlyShowBarOnScroll: false
+        }
+      },
+      dialogVisible: false,
+      actionType: 1,
+      typeList: [
+        { id: 1, name: '动火作业' },
+        { id: 2, name: '有限空间作业' },
+        { id: 3, name: '高处作业' },
+        { id: 4, name: '起重作业' },
+        { id: 5, name: '交叉作业' },
+        { id: 6, name: '临时用电作业' },
+        { id: 7, name: '断路作业' },
+        { id: 8, name: '动土作业' },
+        { id: 9, name: '防护设备拆除' },
+        { id: 10, name: '高温及其他危险作业' },
+        { id: 11, name: '盲板抽堵作业' },
+        { id: 12, name: '预热器清堵作业' },
+        { id: 13, name: '篦冷机清大块作业' }
+      ],
+      rules: {
+        jobactivityTitle: [
+          { required: true, message: '请填写名称', trigger: 'blur' }
+        ]
+      },
+      formData: {
+        logoUrl: '',
+        jobactivityTitle: ''
+      }
+    }
+  },
+  methods: {
+
+    // Show Add Dialog
+    showAddView() {
+      this.resetFormData()
+      this.actionType = 1
+      this.dialogVisible = true
+    },
+
+    // Show Edit Dialog
+    showEditView(id) {
+      this.resetFormData()
+      this.actionType = 2
+      this.dialogVisible = true
+      getJobsByIdApi(id).then((res) => {
+        this.formData = res.data
+      })
+    },
+    changeType(id) {
+      this.formData.jobactivityTitle = this.typeList.filter((item) => item.id === id)[0]?.name
+    },
+    // Reset Form Data
+    resetFormData() {
+      this.formData = {
+        logoUrl: '',
+        jobactivityTitle: ''
+      }
+    },
+    // 提交
+    submitForm(formName) {
+      this.$refs[formName].validate((valid) => {
+        if (valid) {
+          if (this.actionType === 1) {
+            jobAddApi(this.formData).then((resp) => {
+              const { code, msg } = resp
+              if (code === 0) {
+                this.dialogVisible = false
+                this.$message.success(msg)
+                this.formSuccess()
+              } else {
+                this.$message.error(msg)
+              }
+            }).catch((error) => {
+              console.log(error)
+            })
+          } else {
+            jobUpdateApi(this.formData).then((resp) => {
+              const { code, msg } = resp
+              if (code === 0) {
+                this.dialogVisible = false
+                this.$message.success(msg)
+                this.formSuccess()
+              } else {
+                this.$message.error(msg)
+              }
+            }).catch((error) => {
+              console.log(error)
+            })
+          }
+        } else {
+          this.$message.error('操作失败')
+          return false
+        }
+      })
+    },
+    formSuccess() {
+      this.$emit('formSuccess')
+    },
+    resetForm(formName) {
+      this.$refs[formName].resetFields()
+    }
+  }
+}
+</script>
+

+ 126 - 0
src/views/aqpt/projectWork/index.vue

@@ -0,0 +1,126 @@
+<template>
+  <div class="content-container">
+
+    <div class="header">
+      <span class="title">创建工作项目</span>
+      <el-button type="primary" @click="handleAdd">新增</el-button>
+    </div>
+    <el-row class="content-body">
+      <el-table v-loading="listLoading" border :data="dataList" height="calc(100vh - 250px)">
+        <el-table-column type="index" label="序号" header-align="center" align="center" width="50" />
+        <el-table-column prop="jobactivityTitle" label="标题" header-align="center" align="center" />
+        <el-table-column prop="logoUrl" label="图标" header-align="left" align="left" width="100">
+          <template v-slot="{row}">
+            <el-avatar :src="row.logoUrl" />
+          </template>
+        </el-table-column>
+        <el-table-column label="操作" header-align="center" align="center">
+          <template v-slot="{row}">
+            <el-button size="mini" type="info" icon="el-icon-edit" plain @click="handleEdit(row)">修改</el-button>
+            <el-button v-if="row.isFixed !== 1" size="mini" icon="el-icon-delete" type="danger" @click="handleDelete(row)">删除</el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+    </el-row>
+    <project-work-form ref="project-work-form" @formSuccess="formSuccess" />
+  </div>
+</template>
+
+<script>
+
+import projectWorkForm from './components/projectWorkForm.vue'
+import { getJobsApi, jobDelApi } from '@/api/aqpt/visualEditor'
+export default {
+  components: {
+    projectWorkForm
+  },
+  data() {
+    return {
+      title: '风险点',
+      dataList: [],
+      total: 0,
+      listLoading: false,
+      conditions: {
+        page: 1,
+        limit: 10,
+        keywords: ''
+      },
+      handleModal: false,
+      miniUrl: '',
+      exportItems: []
+    }
+  },
+  created() {
+    this.getData()
+  },
+  methods: {
+
+    getData() {
+      this.listLoading = true
+      getJobsApi(this.conditions).then((resp) => {
+        this.listLoading = false
+        const { code, data, total, msg } = resp
+        if (code === 0) {
+          this.total = total
+          this.dataList = data
+        } else {
+          this.$message.error(msg)
+        }
+      }).catch((error) => {
+        console.log(error)
+      })
+    },
+
+    // 添加
+    handleAdd() {
+      this.$refs['project-work-form'].showAddView()
+    },
+
+    // 修改
+    handleEdit(data) {
+      const { typeId } = data
+      this.$refs['project-work-form'].showEditView(typeId)
+    },
+    // 删除
+    handleDelete(data) {
+      const { typeId } = data
+      this.$confirm(`此操作将删除该数据 ${data.riskPointTitle}, 是否继续?`, '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        jobDelApi(typeId).then((resp) => {
+          const { code, msg } = resp
+          if (code === 0) {
+            this.getData()
+            this.$message.success(msg)
+          } else {
+            this.$message.error(msg)
+          }
+        }).catch((error) => {
+          console.log(error)
+        })
+      }).catch(() => {
+        this.$message({ type: 'info', message: '已取消删除' })
+      })
+    },
+    formSuccess() {
+      this.getData()
+    }
+
+  }
+}
+</script>
+<style lang="scss" scoped>
+.header{
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    .title{
+        font-size: 18px;
+        font-weight: bold;
+        color: #FFFFFF;
+    }
+}
+</style>
+

BIN=BIN
src/views/aqpt/visualEditor/images/layers/1.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/10.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/11.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/12.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/13.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/2.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/3.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/4.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/5.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/6.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/7.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/8.png


BIN=BIN
src/views/aqpt/visualEditor/images/layers/9.png


BIN=BIN
src/views/aqpt/visualEditor/images/layout_cont.png


BIN=BIN
src/views/aqpt/visualEditor/images/layout_cont1.png


+ 575 - 0
src/views/aqpt/visualEditor/index.vue

@@ -0,0 +1,575 @@
+<template>
+  <div class="conatiner">
+    <div class="leftSide">
+      <div class="title">作业项目</div>
+      <div v-for="(item,index) in items" :key="index" :class="checkItemIndex===index?'item active':'item'" @click="checkItem(item,index)">
+        <img :src="item.icon" alt="" class="icon">
+        <div class="name">{{ item.name }}</div>
+      </div>
+    </div>
+    <div class="map-warp">
+      <div id="vmap" />
+    </div>
+    <div class="rightSide">
+      <div v-if="type==1" class="remark">
+        <div class="title">使用说明</div>
+        <ul>
+          <li>1.点击作业项目下面的图标</li>
+          <li>2.点击地图绘制点</li>
+          <li>3.编辑信息保存点位</li>
+        </ul>
+      </div>
+      <div v-if="type==2||type=='drage'" class="saveForm">
+        <div class="title">作业点基本信息</div>
+        <div class="item">
+          <div class="lable">作业项目:</div>
+          <div class="content">
+            <img class="icon" :src="saveForm.icon" alt="">
+            <span>{{ saveForm.name }}</span>
+          </div>
+        </div>
+        <div class="item">
+          <div class="lable">名称:</div>
+          <div class="content">
+            <span>{{ saveForm.name }}</span>
+          </div>
+        </div>
+        <div class="bt-wrap">
+          <div class="bt save" @click="save">保存</div>
+          <div class="bt margin" @click="cancel">撤销</div>
+        </div>
+      </div>
+      <div v-if="type==3" class="deleteForm">
+        <div class="title">作业点基本信息</div>
+        <div class="item">
+          <div class="lable">作业项目:</div>
+          <div class="content">
+            <img class="icon" :src="deleteForm.icon" alt="">
+            <span>{{ deleteForm.name }}</span>
+          </div>
+        </div>
+        <div class="item">
+          <div class="lable">名称:</div>
+          <div class="content">
+            <span>{{ deleteForm.name }}</span>
+          </div>
+        </div>
+        <div class="bt-wrap">
+          <div class="bt" @click="save">保存</div>
+          <div class="bt delete margin" @click="delMarker">删除</div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+import * as L from 'leaflet'
+import '@geoman-io/leaflet-geoman-free'
+import { getLayersApi, addApi, delApi, updateApi } from '@/api/aqpt/visualEditor'
+import mixins from '@/mixin/mqtt.js'
+import { Uint8ArrayToString } from '@/utils'
+export default {
+  mixins: [mixins],
+  data() {
+    return {
+      checkItemIndex: -1,
+      type: 1,
+      items: [],
+      saveForm: {
+        icon: '',
+        name: ''
+      },
+      deleteForm: {
+        icon: '',
+        name: ''
+      },
+      dragstartMarker: undefined,
+      map: undefined,
+      markerGroup: null,
+      mapList: [],
+      mapId: undefined,
+      mapRasterLayer: require('./images/layout_cont.png'),
+      layer: null,
+      state: 0,
+      veditId: undefined
+    }
+  },
+  created() {
+    this.init()
+  },
+  // beforeRouteEnter(to, from, next) {
+  //   const params = window.location.href.split('?')[1].split('&')
+  //   function getParams(name = 'name') {
+  //     let param
+  //     params.map(item => item.includes('name') ? (param = item.split('=')[1]) : '')
+  //     return param
+  //   }
+  //   next()
+  //   console.log(getParams())
+  // },
+  methods: {
+    init() {
+      // console.log(this.$route.query)
+      this.initProject()
+      this.$nextTick(() => {
+        this.initMap()
+        this.initMqtt({
+          mqttResponse: this.mqttResponse
+        })
+      })
+    },
+    initProject() {
+      this.items = [
+        { icon: require('./images/layers/1.png'), name: '动火作业' },
+        { icon: require('./images/layers/2.png'), name: '有限空间作业' },
+        { icon: require('./images/layers/3.png'), name: '高处作业' },
+        { icon: require('./images/layers/4.png'), name: '起重作业' },
+        { icon: require('./images/layers/5.png'), name: '预热器清堵作业' },
+        { icon: require('./images/layers/6.png'), name: '篦冷机清大块作业' },
+        { icon: require('./images/layers/7.png'), name: '交叉作业' },
+        { icon: require('./images/layers/8.png'), name: '临时用电作业' },
+        { icon: require('./images/layers/9.png'), name: '动土作业' },
+        { icon: require('./images/layers/10.png'), name: '断路作业' },
+        { icon: require('./images/layers/11.png'), name: '防护设备拆除' },
+        { icon: require('./images/layers/12.png'), name: '高温及其他危险作业' },
+        { icon: require('./images/layers/13.png'), name: '盲板抽堵作业' }
+      ]
+    },
+    checkItem(item, index) {
+      if (this.state === 1) {
+        this.$message('请保存后再操作!')
+        return
+      }
+      this.checkItemIndex = index
+      this.type = 2
+      this.saveForm.icon = item.icon
+      this.saveForm.name = item.name
+      this.map.pm.enableDraw('Marker', { snappable: false, tooltips: false })// tooltips: { 'placeMarker': '点击放置标记' },
+      console.log({
+        ' this.map.pm.enableDraw': this.map.pm.enableDraw
+      })
+      this.state = 1
+    },
+    save() {
+      if (this.type === 3) {
+        // 删除表单里面的保存
+        this.type = 1
+      } else if (this.type === 'drage') {
+        updateApi({
+          'jobactivityTitle': this.layer.options.icon.options.name,
+          'jobactQywzjd': this.layer._latlng.lng,
+          'jobactQywzwd': this.layer._latlng.lat,
+          'veditId': this.veditId,
+          'typeId': this.layer.options.icon.options.typeId
+        }).then(() => {
+          this.type = 1
+          this.checkItemIndex = -1
+          this.state = 2
+          this.saveForm.icon = ''
+          this.saveForm.name = ''
+          this.$message.success('修改成功!')
+          this.dragstartMarker = undefined
+          // this.getLayers()
+        })
+      } else {
+        // 新增
+        addApi({
+          'jobactivityTitle': this.layer.options.icon.options.name,
+          'jobactQywzjd': this.layer._latlng.lng,
+          'jobactQywzwd': this.layer._latlng.lat,
+          'veditId': this.veditId,
+          'typeId': this.layer.options.icon.options.typeId
+        }).then(() => {
+          this.type = 1
+          this.checkItemIndex = -1
+          this.state = 2
+          this.saveForm.icon = ''
+          this.saveForm.name = ''
+          this.$message.success('新增成功!')
+          // this.getLayers()
+        })
+      }
+    },
+    cancel() {
+      this.state = 2
+      this.checkItemIndex = -1
+      this.saveForm.icon = ''
+      this.saveForm.name = ''
+      if (this.type === 'drage') {
+        this.type = 1
+        this.dragstartMarker.marker.setLatLng(this.dragstartMarker._latlng)
+        this.dragstartMarker.marker.dragging.disable()
+        this.dragstartMarker = undefined
+        return
+      }
+      if (this.layer && this.layer._leaflet_id && this.map._layers[this.layer._leaflet_id]) {
+        this.map.removeLayer(this.map._layers[this.layer._leaflet_id])
+      }
+    },
+    delMarker() {
+      delApi(this.veditId).then(() => {
+        this.type = 1
+        this.state = 2
+        this.checkItemIndex = -1
+        this.deleteForm.icon = ''
+        this.deleteForm.name = ''
+        if (this.map._layers[this.markerId]) {
+          this.map.removeLayer(this.map._layers[this.markerId])
+        }
+        this.$message.success('删除成功!')
+        // this.getLayers()
+      })
+    },
+    initMap() {
+      this.map = L.map('vmap', {
+        minZoom: 1,
+        maxZoom: 3,
+        center: [0, 0],
+        zoom: 1,
+        crs: L.CRS.Simple,
+        zoomControl: false
+      })
+      this.setRasterLayer()
+      this.mapListener()
+    },
+    setRasterLayer() {
+      var yx = L.latLng
+      var xy = function xy(x, y) {
+        if (L.Util.isArray(x)) {
+          return yx(x[1], x[0])
+        }
+        return yx(y, x)
+      }
+      var bounds = [xy(-200, -200), xy(300, 185)]
+      L.imageOverlay(this.mapRasterLayer, bounds).addTo(this.map)
+      this.map.fitBounds(bounds)
+      this.getLayers()
+    },
+    getLayers() {
+      getLayersApi().then((res) => {
+        if (this.markerGroup !== null) {
+          this.markerGroup?.clearLayers()
+        }
+        this.initMarkers(res.data)
+      })
+    },
+    initMarkers(markerList = []) {
+      const self = this
+      const markers = L.layerGroup()
+      const coordinates = [-100, 100]
+      const options = { id: 1 }
+      const icon = 'project-12'
+      const tip = '盲板抽堵作业'
+      const iconSize = [27, 33]
+      function drawMark(option = { tip, icon, options, coordinates }) {
+        const myIcon = L.icon({
+          iconUrl: require(`./images/layers/${option.options.typeId}.png`),
+          className: option.icon,
+          iconSize: iconSize
+        })
+        const marker = L.marker(option.coordinates, { ...option.options, icon: myIcon, draggable: false })
+          // .bindPopup(option.tip)
+          .addTo(markers)
+        marker.on('click', function(ev) {
+          if (self.type === 'drage') {
+            self.$message.success('请先确认当前标记点操作!')
+            return
+          }
+          const id = ev.target.options?.id
+          self.type = 3
+          self.veditId = id
+          self.markerId = ev.target._leaflet_id
+          self.deleteForm.name = ev.target.options.name
+          self.deleteForm.icon = require(`./images/layers/${ev.target.options.typeId}.png`)
+          marker.dragging.enable()
+        })
+        marker.on('dragstart', function(ev) {
+          const id = ev.target.options?.id
+          self.type = 'drage'
+          if (self.dragstartMarker === undefined) {
+            self.dragstartMarker = { marker, _latlng: ev.target._latlng }
+          }
+          self.veditId = id
+          self.markerId = ev.target._leaflet_id
+          self.saveForm.name = ev.target.options.name
+          self.saveForm.icon = require(`./images/layers/${ev.target.options.typeId}.png`)
+        })
+        marker.on('dragend', function(ev) {
+          self.layer = ev.target
+        })
+      }
+      markers.addTo(this.map)
+      this.markerGroup = markers
+      if (markerList && markerList.length > 0) {
+        for (let i = 0; i < markerList.length; i++) {
+          drawMark({
+            // tip: `${markerList[i].jobactivityTitle}`,
+            icon: `project-${markerList[i].typeId}`,
+            coordinates: [markerList[i].jobactQywzwd, markerList[i].jobactQywzjd],
+            options: { id: markerList[i].veditId, typeId: markerList[i].typeId, name: markerList[i].jobactivityTitle }
+          })
+        }
+      }
+    },
+    mapListener() {
+      const self = this
+      this.map.on('pm:drawstart', (e) => { // 绘制开始时事件
+        if (e.shape === 'Marker') {
+          const iconUrl = this.saveForm.icon
+          const layer = e.workingLayer
+          var icon = L.icon({
+            iconUrl,
+            iconSize: [27, 33]
+          })
+          layer.setIcon(icon)
+        }
+      })
+      this.map.on('pm:create', (e) => {
+        if (e.shape === 'Marker') {
+          const iconUrl = this.saveForm.icon
+          const name = this.saveForm.name
+          const typeId = this.checkItemIndex + 1
+          const layer = e.layer
+          var icon = L.icon({
+            iconUrl,
+            name,
+            typeId,
+            iconSize: [27, 33]
+          })
+          layer.setIcon(icon)
+          layer.on('click', function(ev) {
+            self.type = 3
+            self.markerId = ev.target._leaflet_id
+            self.deleteForm.name = ev.target.options.icon.options.name
+            self.deleteForm.icon = ev.target.options.icon.options.iconUrl
+          })
+          self.layer = layer
+          layer.addTo(this.markerGroup)
+        }
+        this.map.pm.disableDraw(e.shape)
+      })
+      this.map.on('pm:cut', () => {
+        this.$message.success('裁剪完毕!')
+      })
+      this.map.on('pm:remove', (e) => {
+        console.log(e, '移除')
+      })
+      // this.map.on('pm:globaldragmodetoggled', e => {
+      //   console.log(e, '拖拽结束')
+      // })
+      this.map.on('contextmenu', () => {
+        // 取消所有状态
+        this.map.pm.disableGlobalDragMode()
+        this.cancel()
+      })
+      // this.map.on('zoomend', (e) => {
+      //   const scale = e.target.getZoom()
+      //   const markers = document.querySelectorAll('.leaflet-marker-icon')
+      //   for (let i = 0; i < markers.length; i++) {
+      //     console.log()
+      //     markers[i].style.transform = `${markers[i].style.transform} scale(${scale})`
+      //   }
+      //   console.log({ markers, scale })
+      //   // this.markerGroup.eachLayer(item => { // })
+      // })
+    },
+    mqttResponse(message) {
+      const data = message ? Uint8ArrayToString(message) : ''
+      this.getLayers()
+      console.log({ message, data })
+    }
+
+  }
+}
+</script>
+<style lang="scss" scoped>
+.conatiner{
+    height: 100%;
+    height: 100vh;
+    min-width: 1000px;
+    margin: 0 auto;
+    padding: 0 16px;
+    box-sizing: border-box;
+    background-color: #071C2B;
+    color: #fff;
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    overflow-x: auto;
+    .leftSide{
+        min-width: 266px;
+        height: 100%;
+        overflow-y: auto;
+        background-color: #193142;
+        box-sizing: border-box;
+        .title{
+          height: 42px;
+          line-height: 42px;
+          background-color: #36556b;
+          font-size: 16px;
+          color: #666;
+          text-indent: 10px;
+          color: #fff;
+        }
+        .item{
+          display: flex;
+          align-items: center;
+          height: 48px;
+          padding-left: 10px;
+          cursor: default;
+          &.active{
+            background-color: rgba(45, 140, 240, 0.16);
+          }
+          .icon{
+            width: 17.5px;
+            display: block;
+          }
+          .name{
+            font-size: 14px;
+            padding-left: 12px;
+          }
+        }
+    }
+    .map-warp{
+        flex: 1;
+        height: 100%;
+        background-color: #071c2b;
+        #vmap{
+          background-color: inherit;
+          width: 100%;
+          height: 100%;
+        }
+    }
+    .rightSide{
+        width: 269px;
+        height: 100%;
+        overflow-y: auto;
+        background-color: #193142;
+        box-sizing: border-box;
+        padding: 17px 16px;
+        .title{
+          color: #fff;
+          font-size: 16px;
+          margin-bottom: 16px;
+        }
+        .remark{
+          ul,li{
+            list-style: none;
+            margin: 0;
+            padding: 0;
+          }
+          li{
+            color: rgba(255, 255, 255, 0.7);
+            line-height: 28px;
+            font-size: 14px;
+            margin-bottom: 8px;
+          }
+        }
+        .saveForm,.deleteForm{
+          .item{
+            height: 48px;
+            display: flex;
+            justify-content: flex-start;
+            align-items: center;
+            font-size: 14px;
+            border-bottom:2px solid rgba(45, 140, 240, 0.16);
+            .lable{
+              width: 80px;
+              font-size: 14px;
+            }
+            .content{
+              display: flex;
+              justify-content: flex-start;
+              align-items: center;
+              overflow: hidden;
+              text-overflow: ellipsis;
+              .icon{
+                width: 17.5px;
+                display: block;
+                margin-right:8px;
+              }
+            }
+          }
+        }
+
+    }
+    .bt-wrap{
+      margin-top: 65px;
+      .bt{
+        width: 100px;
+        height: 36px;
+        color: #fff;
+        font-size: 14px;
+        line-height: 36px;
+        text-align: center;
+        background-color: rgba(24, 144, 255, 0.3);
+        display: inline-block;
+        cursor: pointer;
+        user-select: none;
+      -moz-user-select: none;
+      -webkit-user-select: none;
+      -ms-user-select: none;
+        &.save{
+          background-color: #1890ff;
+        }
+        &.delete{
+          background-color: #f97647;
+        }
+        &.margin{
+          margin-left: 12px;
+        }
+      }
+    }
+}
+</style>
+<style>
+/* 标记点 */
+.leaflet-marker-icon{
+  background-size: 100%;
+  background-repeat: no-repeat;
+}
+.leaflet-marker-icon.leaflet-marker-draggable{
+  box-shadow: 0 2px 4px #154c74;
+  background-color: #52b6ff;
+  border-radius: 4px;
+}
+.leaflet-marker-icon.project-1{
+  background-image: url("./images/layers/1.png");
+}
+.leaflet-marker-icon.project-2{
+  background-image: url("./images/layers/2.png");
+}
+.leaflet-marker-icon.project-3{
+  background-image: url("./images/layers/3.png");
+}
+.leaflet-marker-icon.project-4{
+  background-image: url("./images/layers/4.png");
+}
+.leaflet-marker-icon.project-5{
+  background-image: url("./images/layers/5.png");
+}
+.leaflet-marker-icon.project-6{
+  background-image: url("./images/layers/6.png");
+}
+.leaflet-marker-icon.project-7{
+  background-image: url("./images/layers/7.png");
+}
+.leaflet-marker-icon.project-8{
+  background-image: url("./images/layers/8.png");
+}
+.leaflet-marker-icon.project-9{
+  background-image: url("./images/layers/9.png");
+}
+.leaflet-marker-icon.project-10{
+  background-image: url("./images/layers/10.png");
+}
+.leaflet-marker-icon.project-11{
+  background-image: url("./images/layers/11.png");
+}
+.leaflet-marker-icon.project-12{
+  background-image: url("./images/layers/12.png");
+}
+.leaflet-marker-icon.project-13{
+  background-image: url("./images/layers/12.png");
+}
+</style>

+ 1 - 0
src/views/login/auth-redirect.vue

@@ -2,6 +2,7 @@
 export default {
   name: 'AuthRedirect',
   created() {
+    alert(1)
     const hash = window.location.search.slice(1)
     if (window.localStorage) {
       window.localStorage.setItem('x-admin-oauth-code', hash)

+ 22 - 3
src/views/login/index.vue

@@ -67,6 +67,7 @@
 </template>
 
 <script>
+import { mapGetters } from 'vuex'
 export default {
   name: 'Login',
   data() {
@@ -102,10 +103,28 @@ export default {
       otherQuery: {}
     }
   },
+  computed: {
+    ...mapGetters([
+      'permission_routes',
+      'permits'
+    ])
+  },
   watch: {
     $route: {
       handler: function(route) {
-        this.redirect = route.query && route.query.redirect
+        // this.redirect = route.query && route.query.redirect
+        // console.log(route.query)
+        if (route.query.username) {
+          this.$store.dispatch('user/login', {
+            username: route.query.username,
+            password: route.query.password
+          }).then(async(res) => {
+            this.loading = false
+            this.$router.push({ path: '/', query: this.otherQuery })
+          }).catch(() => {
+            this.loading = false
+          })
+        }
       },
       immediate: true
     }
@@ -140,9 +159,9 @@ export default {
       this.$refs.loginForm.validate(valid => {
         if (valid) {
           this.loading = true
-          this.$store.dispatch('user/login', this.loginForm).then(() => {
-            this.$router.push({ path: '/', query: this.otherQuery })
+          this.$store.dispatch('user/login', this.loginForm).then(async(res) => {
             this.loading = false
+            this.$router.push({ path: '/', query: this.otherQuery })
           }).catch(() => {
             this.loading = false
           })

+ 177 - 5
yarn.lock

@@ -2569,7 +2569,7 @@ base64-arraybuffer@^1.0.2:
   resolved "https://registry.yarnpkg.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#1c37589a7c4b0746e34bd1feb951da2df01c1bdc"
   integrity sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==
 
-base64-js@^1.0.2:
+base64-js@^1.0.2, base64-js@^1.3.1:
   version "1.5.1"
   resolved "https://registry.nlark.com/base64-js/download/base64-js-1.5.1.tgz?cache=0&sync_timestamp=1624607950711&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fbase64-js%2Fdownload%2Fbase64-js-1.5.1.tgz"
   integrity sha1-GxtEAWClv3rUC2UPCVljSBkDkwo=
@@ -2636,6 +2636,15 @@ bindings@^1.5.0:
   dependencies:
     file-uri-to-path "1.0.0"
 
+bl@^4.0.2:
+  version "4.1.0"
+  resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a"
+  integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==
+  dependencies:
+    buffer "^5.5.0"
+    inherits "^2.0.4"
+    readable-stream "^3.4.0"
+
 bluebird@^3.1.1, bluebird@^3.5.0, bluebird@^3.5.5:
   version "3.7.2"
   resolved "https://registry.nlark.com/bluebird/download/bluebird-3.7.2.tgz"
@@ -2879,6 +2888,14 @@ buffer@^4.3.0:
     ieee754 "^1.1.4"
     isarray "^1.0.0"
 
+buffer@^5.5.0:
+  version "5.7.1"
+  resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
+  integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
+  dependencies:
+    base64-js "^1.3.1"
+    ieee754 "^1.1.13"
+
 builtin-status-codes@^3.0.0:
   version "3.0.0"
   resolved "https://registry.nlark.com/builtin-status-codes/download/builtin-status-codes-3.0.0.tgz"
@@ -3476,6 +3493,14 @@ commander@~2.19.0:
   resolved "https://registry.nlark.com/commander/download/commander-2.19.0.tgz?cache=0&sync_timestamp=1631257318022&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcommander%2Fdownload%2Fcommander-2.19.0.tgz"
   integrity sha1-9hmKqE5bg8RgVLlN3tv+1e6f8So=
 
+commist@^1.0.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/commist/-/commist-1.1.0.tgz#17811ec6978f6c15ee4de80c45c9beb77cee35d5"
+  integrity sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==
+  dependencies:
+    leven "^2.1.0"
+    minimist "^1.1.0"
+
 commondir@^1.0.1:
   version "1.0.1"
   resolved "https://registry.nlark.com/commondir/download/commondir-1.0.1.tgz?cache=0&sync_timestamp=1624608091111&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fcommondir%2Fdownload%2Fcommondir-1.0.1.tgz"
@@ -3521,6 +3546,16 @@ concat-stream@^1.5.0:
     readable-stream "^2.2.2"
     typedarray "^0.0.6"
 
+concat-stream@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-2.0.0.tgz#414cf5af790a48c60ab9be4527d56d5e41133cb1"
+  integrity sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==
+  dependencies:
+    buffer-from "^1.0.0"
+    inherits "^2.0.3"
+    readable-stream "^3.0.2"
+    typedarray "^0.0.6"
+
 condense-newlines@^0.2.1:
   version "0.2.1"
   resolved "https://registry.nlark.com/condense-newlines/download/condense-newlines-0.2.1.tgz"
@@ -4080,6 +4115,13 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
   dependencies:
     ms "2.1.2"
 
+debug@^4.3.1:
+  version "4.3.4"
+  resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
+  integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
+  dependencies:
+    ms "2.1.2"
+
 decamelize@^1.2.0:
   version "1.2.0"
   resolved "https://registry.npmmirror.com/decamelize/download/decamelize-1.2.0.tgz?cache=0&sync_timestamp=1633055760479&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fdecamelize%2Fdownload%2Fdecamelize-1.2.0.tgz"
@@ -4435,6 +4477,16 @@ duplexify@^3.4.2, duplexify@^3.6.0:
     readable-stream "^2.0.0"
     stream-shift "^1.0.0"
 
+duplexify@^4.1.1:
+  version "4.1.2"
+  resolved "https://registry.yarnpkg.com/duplexify/-/duplexify-4.1.2.tgz#18b4f8d28289132fa0b9573c898d9f903f81c7b0"
+  integrity sha512-fz3OjcNCHmRP12MJoZMPglx8m4rrFP8rovnk4vT8Fs+aonZoCwGg10dSsQsfP/E62eZcPTMSMP6686fu9Qlqtw==
+  dependencies:
+    end-of-stream "^1.4.1"
+    inherits "^2.0.3"
+    readable-stream "^3.1.1"
+    stream-shift "^1.0.0"
+
 easings-css@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmjs.org/easings-css/-/easings-css-1.0.0.tgz"
@@ -4553,7 +4605,7 @@ encodeurl@~1.0.1, encodeurl@~1.0.2:
   resolved "https://registry.nlark.com/encodeurl/download/encodeurl-1.0.2.tgz"
   integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
 
-end-of-stream@^1.0.0, end-of-stream@^1.1.0:
+end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1:
   version "1.4.4"
   resolved "https://registry.nlark.com/end-of-stream/download/end-of-stream-1.4.4.tgz?cache=0&sync_timestamp=1624607958717&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fend-of-stream%2Fdownload%2Fend-of-stream-1.4.4.tgz"
   integrity sha1-WuZKX0UFe682JuwU2gyl5LJDHrA=
@@ -5618,6 +5670,18 @@ glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
     once "^1.3.0"
     path-is-absolute "^1.0.0"
 
+glob@^7.1.6:
+  version "7.2.3"
+  resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
+  integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
+  dependencies:
+    fs.realpath "^1.0.0"
+    inflight "^1.0.4"
+    inherits "2"
+    minimatch "^3.1.1"
+    once "^1.3.0"
+    path-is-absolute "^1.0.0"
+
 global-modules@^1.0.0:
   version "1.0.0"
   resolved "https://registry.nlark.com/global-modules/download/global-modules-1.0.0.tgz"
@@ -5875,6 +5939,14 @@ header-case@^1.0.0:
     no-case "^2.2.0"
     upper-case "^1.1.3"
 
+help-me@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/help-me/-/help-me-3.0.0.tgz#9803c81b5f346ad2bce2c6a0ba01b82257d319e8"
+  integrity sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==
+  dependencies:
+    glob "^7.1.6"
+    readable-stream "^3.6.0"
+
 hex-color-regex@^1.1.0:
   version "1.1.0"
   resolved "https://registry.nlark.com/hex-color-regex/download/hex-color-regex-1.1.0.tgz"
@@ -6134,7 +6206,7 @@ icss-utils@^4.0.0, icss-utils@^4.1.1:
   dependencies:
     postcss "^7.0.14"
 
-ieee754@^1.1.4:
+ieee754@^1.1.13, ieee754@^1.1.4:
   version "1.2.1"
   resolved "https://registry.nlark.com/ieee754/download/ieee754-1.2.1.tgz"
   integrity sha1-jrehCmP/8l0VpXsAFYbRd9Gw01I=
@@ -7360,6 +7432,11 @@ js-queue@2.0.2:
   dependencies:
     easy-stack "^1.0.1"
 
+js-sdsl@4.3.0:
+  version "4.3.0"
+  resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711"
+  integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==
+
 "js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
   version "4.0.0"
   resolved "https://registry.nlark.com/js-tokens/download/js-tokens-4.0.0.tgz"
@@ -7627,6 +7704,11 @@ left-pad@^1.3.0:
   resolved "https://registry.nlark.com/left-pad/download/left-pad-1.3.0.tgz"
   integrity sha1-W4o6d2Xf4AEmHd6RVYnngvjJTR4=
 
+leven@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/leven/-/leven-2.1.0.tgz#c2e7a9f772094dee9d34202ae8acce4687875580"
+  integrity sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==
+
 leven@^3.1.0:
   version "3.1.0"
   resolved "https://registry.nlark.com/leven/download/leven-3.1.0.tgz"
@@ -7939,6 +8021,13 @@ lru-cache@^5.1.1:
   dependencies:
     yallist "^3.0.2"
 
+lru-cache@^6.0.0:
+  version "6.0.0"
+  resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+  integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+  dependencies:
+    yallist "^4.0.0"
+
 make-dir@^2.0.0, make-dir@^2.1.0:
   version "2.1.0"
   resolved "https://registry.nlark.com/make-dir/download/make-dir-2.1.0.tgz"
@@ -8223,6 +8312,18 @@ minimatch@^3.0.4:
   dependencies:
     brace-expansion "^1.1.7"
 
+minimatch@^3.1.1:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
+  integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
+  dependencies:
+    brace-expansion "^1.1.7"
+
+minimist@^1.1.0:
+  version "1.2.8"
+  resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
+  integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==
+
 minimist@^1.1.1, minimist@^1.2.0, minimist@^1.2.5:
   version "1.2.5"
   resolved "https://registry.nlark.com/minimist/download/minimist-1.2.5.tgz?cache=0&sync_timestamp=1624607886507&other_urls=https%3A%2F%2Fregistry.nlark.com%2Fminimist%2Fdownload%2Fminimist-1.2.5.tgz"
@@ -8311,6 +8412,38 @@ move-concurrently@^1.0.1:
     rimraf "^2.5.4"
     run-queue "^1.0.3"
 
+mqtt-packet@^6.8.0:
+  version "6.10.0"
+  resolved "https://registry.yarnpkg.com/mqtt-packet/-/mqtt-packet-6.10.0.tgz#c8b507832c4152e3e511c0efa104ae4a64cd418f"
+  integrity sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==
+  dependencies:
+    bl "^4.0.2"
+    debug "^4.1.1"
+    process-nextick-args "^2.0.1"
+
+mqtt@^4.3.7:
+  version "4.3.7"
+  resolved "https://registry.yarnpkg.com/mqtt/-/mqtt-4.3.7.tgz#42985ca490ea25d2c12c119d83c632db6dc9d589"
+  integrity sha512-ew3qwG/TJRorTz47eW46vZ5oBw5MEYbQZVaEji44j5lAUSQSqIEoul7Kua/BatBW0H0kKQcC9kwUHa1qzaWHSw==
+  dependencies:
+    commist "^1.0.0"
+    concat-stream "^2.0.0"
+    debug "^4.1.1"
+    duplexify "^4.1.1"
+    help-me "^3.0.0"
+    inherits "^2.0.3"
+    lru-cache "^6.0.0"
+    minimist "^1.2.5"
+    mqtt-packet "^6.8.0"
+    number-allocator "^1.0.9"
+    pump "^3.0.0"
+    readable-stream "^3.6.0"
+    reinterval "^1.1.0"
+    rfdc "^1.3.0"
+    split2 "^3.1.0"
+    ws "^7.5.5"
+    xtend "^4.0.2"
+
 ms@2.0.0:
   version "2.0.0"
   resolved "https://registry.npmmirror.com/ms/download/ms-2.0.0.tgz"
@@ -8635,6 +8768,14 @@ num2fraction@^1.2.2:
   resolved "https://registry.nlark.com/num2fraction/download/num2fraction-1.2.2.tgz"
   integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4=
 
+number-allocator@^1.0.9:
+  version "1.0.14"
+  resolved "https://registry.yarnpkg.com/number-allocator/-/number-allocator-1.0.14.tgz#1f2e32855498a7740dcc8c78bed54592d930ee4d"
+  integrity sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==
+  dependencies:
+    debug "^4.3.1"
+    js-sdsl "4.3.0"
+
 number-is-nan@^1.0.0:
   version "1.0.1"
   resolved "https://registry.nlark.com/number-is-nan/download/number-is-nan-1.0.1.tgz"
@@ -9776,7 +9917,7 @@ printj@~1.3.0:
   resolved "https://registry.npmjs.org/printj/-/printj-1.3.0.tgz"
   integrity sha512-017o8YIaz8gLhaNxRB9eBv2mWXI2CtzhPJALnQTP+OPpuUfP0RMWqr/mHCzqVeu1AQxfzSfAtAq66vKB8y7Lzg==
 
-process-nextick-args@~2.0.0:
+process-nextick-args@^2.0.1, process-nextick-args@~2.0.0:
   version "2.0.1"
   resolved "https://registry.nlark.com/process-nextick-args/download/process-nextick-args-2.0.1.tgz"
   integrity sha1-eCDZsWEgzFXKmud5JoCufbptf+I=
@@ -10094,6 +10235,15 @@ read-pkg@^5.1.1:
     string_decoder "~1.1.1"
     util-deprecate "~1.0.1"
 
+readable-stream@^3.0.0, readable-stream@^3.0.2, readable-stream@^3.4.0:
+  version "3.6.2"
+  resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
+  integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
+  dependencies:
+    inherits "^2.0.3"
+    string_decoder "^1.1.1"
+    util-deprecate "^1.0.1"
+
 readable-stream@^3.0.6, readable-stream@^3.1.1, readable-stream@^3.6.0:
   version "3.6.0"
   resolved "https://registry.nlark.com/readable-stream/download/readable-stream-3.6.0.tgz"
@@ -10219,6 +10369,11 @@ regjsparser@^0.7.0:
   dependencies:
     jsesc "~0.5.0"
 
+reinterval@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/reinterval/-/reinterval-1.1.0.tgz#3361ecfa3ca6c18283380dd0bb9546f390f5ece7"
+  integrity sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==
+
 relateurl@0.2.x:
   version "0.2.7"
   resolved "https://registry.nlark.com/relateurl/download/relateurl-0.2.7.tgz"
@@ -10398,6 +10553,11 @@ retry@^0.12.0:
   resolved "https://registry.nlark.com/retry/download/retry-0.12.0.tgz"
   integrity sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=
 
+rfdc@^1.3.0:
+  version "1.3.0"
+  resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b"
+  integrity sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==
+
 rgb-regex@^1.0.1:
   version "1.0.1"
   resolved "https://registry.nlark.com/rgb-regex/download/rgb-regex-1.0.1.tgz"
@@ -11022,6 +11182,13 @@ split-string@^3.0.1, split-string@^3.0.2:
   dependencies:
     extend-shallow "^3.0.0"
 
+split2@^3.1.0:
+  version "3.2.2"
+  resolved "https://registry.yarnpkg.com/split2/-/split2-3.2.2.tgz#bf2cf2a37d838312c249c89206fd7a17dd12365f"
+  integrity sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==
+  dependencies:
+    readable-stream "^3.0.0"
+
 sprintf-js@~1.0.2:
   version "1.0.3"
   resolved "https://registry.nlark.com/sprintf-js/download/sprintf-js-1.0.3.tgz"
@@ -12642,6 +12809,11 @@ ws@^7.0.0:
   resolved "https://registry.npmmirror.com/ws/download/ws-7.5.5.tgz?cache=0&sync_timestamp=1633200058938&other_urls=https%3A%2F%2Fregistry.npmmirror.com%2Fws%2Fdownload%2Fws-7.5.5.tgz"
   integrity sha1-i0vEr1GM+r0Ec65PmRRCh7M+uIE=
 
+ws@^7.5.5:
+  version "7.5.9"
+  resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591"
+  integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==
+
 xlsx@0.14.1:
   version "0.14.1"
   resolved "https://registry.npmjs.org/xlsx/-/xlsx-0.14.1.tgz"
@@ -12665,7 +12837,7 @@ xmlchars@^2.1.1:
   resolved "https://registry.nlark.com/xmlchars/download/xmlchars-2.2.0.tgz"
   integrity sha1-Bg/hvLf5x2/ioX24apvDq4lCEMs=
 
-xtend@^4.0.0, xtend@~4.0.1:
+xtend@^4.0.0, xtend@^4.0.2, xtend@~4.0.1:
   version "4.0.2"
   resolved "https://registry.nlark.com/xtend/download/xtend-4.0.2.tgz"
   integrity sha1-u3J3n1+kZRhrH0OPZ0+jR/2121Q=