Browse Source

智能巡检提交

luogang 6 tháng trước cách đây
mục cha
commit
dc114b3b2c

BIN
src/assets/images/camera-data.png


BIN
src/assets/images/camera.png


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 0 - 0
src/assets/images/login-bg.svg


BIN
src/assets/images/logo.png


BIN
src/assets/images/road.png


+ 442 - 0
src/views/deviceCheck/smartPatrolConfig/index.vue

@@ -0,0 +1,442 @@
+<template>
+  <div class="config">
+    <el-menu default-active="2" class="custom-menu">
+      <el-menu-item index="2">
+        <el-icon><icon-menu /></el-icon>
+        <template #title>视频灯检测</template>
+      </el-menu-item>
+      <!-- <el-menu-item index="1">
+      <el-icon><MoreFilled /></el-icon>
+      <template #title>电流电压检测</template>
+    </el-menu-item> -->
+
+    </el-menu>
+    <div class="right-content">
+      <el-steps style="margin-top: 10px;" class="step-content" finish-status="success" :space="200" :active="activeStep"
+        simple>
+        <el-step title="选择视频点位" />
+        <el-step title="选择算法" />
+        <el-step title="点位设备关联" />
+        <el-step title="在线检测" />
+      </el-steps>
+      <div class="center-content">
+        <div v-if="activeStep === 0" class="camera">
+          <div class="camera-op">
+            <el-select v-model="camera" placeholder="请选择" clearable style="width: 180px" size="small">
+              <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+            </el-select>
+            <el-button type="primary" size="small" @click="showCamera">获取画面</el-button>
+          </div>
+          <div class="camera-content">
+            <img v-if="cameraImgShow" class="camera-img" src="@/assets/images/camera.png" />
+          </div>
+        </div>
+        <div v-else-if="activeStep === 1" class="camera">
+          <div class="camera-op">
+            <el-select v-model="algorithm" placeholder="请选择算法" clearable style="width: 180px" size="small">
+              <el-option v-for="item in algorithmOptions" :key="item.value" :label="item.label" :value="item.value" />
+            </el-select>
+            <el-button type="primary" size="small" @click="showCameraData">获取位置读数</el-button>
+          </div>
+          <div class="camera-content">
+            <img v-if="!cameraDataShow" class="camera-img" src="@/assets/images/camera.png" />
+            <img v-else class="camera-img" src="@/assets/images/camera-data.png" />
+          </div>
+        </div>
+        <div v-else-if="activeStep === 2">
+          <el-table :data="tableData">
+            <el-table-column label="id" align="center" prop="id" width="80"/>
+            <el-table-column label="位置" align="center" prop="position" />
+            <el-table-column label="算法" align="center" show-overflow-tooltip width="150" >
+              <template #default="{ row }">
+                {{ algorithmOptions.filter(item => item.value == row.algorithmId)[0]?.label }}
+              </template>
+            </el-table-column>
+            <el-table-column label="设备类型" align="center" prop="deviceType">
+              <template #default="{ row }">
+                {{ deviceTypeOptions.filter(item => item.id == row.deviceType)[0]?.name }}
+              </template>
+            </el-table-column>
+            <el-table-column label="品牌型号" align="center" prop="brandXh">
+              <template #default="{ row }">
+                {{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.ext1.brand }}{{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.xh }}
+              </template>
+            </el-table-column>
+            <el-table-column label="设备" align="center">
+              <template #default="{ row }">
+                {{ deviceOptions.filter(item => item.id == row.device)[0]?.name }}
+              </template>
+            </el-table-column>
+            <el-table-column label="参数" align="center" prop="param" />
+            <el-table-column label="状态" align="center" prop="status">
+              <template #default="scope">
+                <el-tag v-if="scope.row.status === '1'" type="info">未配</el-tag>
+                <el-tag v-else type="success">已配</el-tag>
+              </template>
+            </el-table-column>
+            <el-table-column label="操作" align="center" width="100" fixed="right">
+              <template #default="{row,$index}">
+                <el-button size="small" link type="primary" @click="showDetail(row)">详情</el-button>
+                <el-button v-if="row.status == 1" size="small" link type="primary"
+                  @click="handleEdit(row,$index)">编辑</el-button>
+              </template>
+            </el-table-column>
+          </el-table>
+        </div>
+        <div v-else-if="activeStep === 3">
+          <el-table :data="tableData">
+            <el-table-column label="id" align="center" prop="id" width="80"/>
+            <el-table-column label="位置" align="center" prop="position" />
+            <el-table-column label="算法" align="center" show-overflow-tooltip width="150" >
+              <template #default="{ row }">
+                {{ algorithmOptions.filter(item => item.value == row.algorithmId)[0]?.label }}
+              </template>
+            </el-table-column>
+            <el-table-column label="设备类型" align="center" prop="deviceType">
+              <template #default="{ row }">
+                {{ deviceTypeOptions.filter(item => item.id == row.deviceType)[0]?.name }}
+              </template>
+            </el-table-column>
+            <el-table-column label="品牌型号" align="center" prop="brandXh">
+              <template #default="{ row }">
+                {{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.ext1.brand }}{{ deviceTypeDetailOptions.filter(item => item.id == row.brandXh)[0]?.xh }}
+              </template>
+            </el-table-column>
+            <el-table-column label="设备" align="center">
+              <template #default="{ row }">
+                {{ deviceOptions.filter(item => item.id == row.device)[0]?.name }}
+              </template>
+            </el-table-column>
+            <el-table-column label="参数" align="center" prop="param" />
+            <el-table-column label="读数" align="center" prop="reading" />
+          </el-table>
+        </div>
+      </div>
+      <!-- 信息对话框 -->
+      <el-dialog v-model="dialog.visible" :title="dialog.title" width="600px" append-to-body>
+        <el-form ref="addFormRef" :model="form" class="custom-form" label-width="80px" :disabled="detailDisabled">
+          <el-row :gutter="20">
+            <el-col :span="12">
+              <el-form-item label="位置" prop="position"
+                :rules="[{ required: true, message: '位置不能为空', trigger: 'blur' }]">
+                <el-input v-model="form.position" placeholder="请输入位置" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="算法" prop="algorithmId"
+                :rules="[{ required: true, message: '算法不能为空', trigger: 'change' }]">
+                <el-select v-model="form.algorithmId" placeholder="请选择算法" clearable>
+                  <el-option v-for="item in algorithmOptions" :key="item.value" :label="item.label"
+                    :value="item.value" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="设备类型" prop="deviceType"
+                :rules="[{ required: true, message: '设备类型不能为空', trigger: 'change' }]">
+                <el-select v-model="form.deviceType" clearable placeholder="请选择设备类型" @change="form.brandXh = ''">
+                  <el-option v-for="dict in deviceTypeOptions" :key="dict.id" :label="dict.name" :value="dict.id">
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="品牌型号" prop="brandXh"
+                :rules="[{ required: true, message: '品牌型号不能为空', trigger: 'change' }]">
+                <el-select v-model="form.brandXh" clearable placeholder="请选择品牌型号">
+                  <el-option
+                    v-for="dict in deviceTypeDetailOptions.filter(item => item.deviceTypeId === form.deviceType)"
+                    :key="dict.id" :label="`${dict.ext1.brand}${dict.xh}`" :value="dict.id">
+                  </el-option>
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="设备" prop="device"
+                :rules="[{ required: true, message: '设备不能为空', trigger: 'change' }]">
+                <el-select v-model="form.device" placeholder="请选择设备" clearable>
+                  <el-option v-for="item in deviceOptions" :key="item.id" :label="item.name" :value="item.id" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="参数" prop="param">
+                <el-input v-model="form.param" placeholder="请输入" />
+              </el-form-item>
+            </el-col>
+            <el-col :span="12">
+              <el-form-item label="状态" prop="status"
+                :rules="[{ required: true, message: '状态不能为空', trigger: 'change' }]">
+                <el-select v-model="form.status" placeholder="请选择状态" clearable>
+                  <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" />
+                </el-select>
+              </el-form-item>
+            </el-col>
+          </el-row>
+
+        </el-form>
+        <template #footer>
+          <div class="dialog-footer">
+            <el-button :loading="buttonLoading" type="primary" @click="submitForm"> 确 定 </el-button>
+            <el-button @click="cancel"> 取 消 </el-button>
+          </div>
+        </template>
+      </el-dialog>
+      <div class="bottom-btn">
+        <el-button v-if="activeStep > 0" @click="lastStep">上一步</el-button>
+        <el-button v-if="activeStep < 3" type="primary" @click="nextStep">下一步</el-button>
+        <el-button v-else type="primary" @click="confirmClick">完成</el-button>
+      </div>
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+import {
+  Menu as IconMenu,
+} from '@element-plus/icons-vue'
+import { listDevice } from '@/api/deviceManage/device';
+import { listDeviceType, getDeviceTypeDetailList } from '@/api/deviceManage/deviceType';
+const { proxy } = getCurrentInstance() as ComponentInternalInstance;
+const addFormRef = ref<ElFormInstance>()
+const buttonLoading = ref(false);
+const form = ref({
+  position: undefined,
+  algorithmId: undefined,
+  deviceType: undefined,
+  brandXh: undefined,
+  device: undefined,
+  param: undefined,
+  status: undefined,
+} as any);
+const dialog = ref({
+  visible: false,
+  title: '',
+});
+const camera = ref('')
+const algorithm = ref('')
+const activeStep = ref(0)
+const cameraImgShow = ref(false)
+const cameraDataShow = ref(false)
+const deviceTypeOptions = ref([])
+const deviceTypeDetailOptions = ref([])
+const deviceOptions = ref([])
+const curInd = ref(0)
+const detailDisabled = ref(false)
+const options = [
+  {
+    value: '2',
+    label: '摄像头001',
+  },
+  {
+    value: '2',
+    label: '摄像头002',
+  },
+  {
+    value: '3',
+    label: '摄像头003',
+  },
+]
+const algorithmOptions = [
+  {
+    value: '1',
+    label: '表针表计识别',
+  },
+  {
+    value: '2',
+    label: 'LED表计识别',
+  },
+  {
+    value: '3',
+    label: '指示灯状态识别',
+  },
+]
+const statusOptions = [
+  {
+    value: '1',
+    label: '未配',
+  },
+  {
+    value: '2',
+    label: '已配',
+  },
+]
+const tableData = ref([])
+const showCamera = () => {
+  if (!camera.value) return proxy?.$modal.msgError('请先选择摄像头')
+  cameraImgShow.value = true
+}
+const showCameraData = () => {
+  if (!algorithm.value) return proxy?.$modal.msgError('请先选择算法')
+  cameraDataShow.value = true
+}
+const nextStep = () => {
+  if (activeStep.value === 0) {
+    if (!camera.value||!cameraImgShow.value) return proxy?.$modal.msgError('请先获取画面')
+  }
+  if (activeStep.value === 1) {
+    if (!algorithm.value) return proxy?.$modal.msgError('请先选择算法')
+    if (!cameraDataShow.value) return proxy?.$modal.msgError('请获取位置读数')
+    const tmpArr = [];
+    for (let i = 0; i < 3; i++) {
+      tmpArr.push({
+        id: `${i + 1}`,
+        position: `L-000${i + 1}`,
+        algorithmId: algorithm.value,
+        deviceType: '',
+        brandXh: '',
+        device: '',
+        param: '',
+        status: '1'
+      })
+    }
+    tableData.value = tmpArr
+  }
+  if (activeStep.value === 2) {
+    if (tableData.value.some(item => item.status === '1')) return proxy?.$modal.msgError('请先配置设备')
+    tableData.value = tableData.value.map(item => ({
+      ...item,
+      reading: '200V'
+    }))
+  }
+  activeStep.value++
+}
+const lastStep = () => {
+  activeStep.value--
+}
+const confirmClick = () => {
+
+}
+const showDetail = (row) => {
+  detailDisabled.value = true
+  dialog.value.visible = true;
+  dialog.value.title = '详情';
+  addFormRef.value?.resetFields();
+  form.value = Object.assign(form.value, row);
+}
+const handleEdit = (row, index) => {
+  detailDisabled.value = false
+  dialog.value.visible = true;
+  dialog.value.title = '编辑信息';
+  addFormRef.value?.resetFields();
+  curInd.value = index
+  form.value = Object.assign(form.value, row);
+
+}
+const submitForm = () => {
+  addFormRef.value.validate(async (valid) => {
+    if (valid) {
+      buttonLoading.value = true;
+      setTimeout(() => {
+        tableData.value[curInd.value] = Object.assign(tableData.value[curInd.value], form.value);
+        buttonLoading.value = false;
+        dialog.value.visible = false;
+      }, 500);
+    }
+  });
+
+}
+const cancel = () => {
+  dialog.value.visible = false;
+}
+const getDeviceTypeList = async () => {
+  let deviceTypeList = []
+  let deviceTypeDetailList = []
+  await getDeviceTypeDetailList({}).then(({ code, rows }) => {
+    if (code === 200) {
+      deviceTypeDetailList = rows.map((item) => ({
+        ...item,
+        ext1: item.ext1 ? JSON.parse(item.ext1) : null
+      }));
+    }
+  });
+  await listDeviceType({}).then(({ code, rows }) => {
+    if (code === 200) {
+      deviceTypeList = (rows || []).map(item => ({
+        ...item,
+        deviceTypeDetailList: deviceTypeDetailList.filter(el => el.deviceTypeId === item.id)
+      }))
+    }
+  })
+  deviceTypeOptions.value = deviceTypeList
+  deviceTypeDetailOptions.value = deviceTypeDetailList
+};
+const getDeviceList = () => {
+  listDevice({}).then(({ code, rows }) => {
+    if (code === 200) {
+      deviceOptions.value = rows
+    }
+  })
+}
+onMounted(() => {
+  getDeviceTypeList()
+  getDeviceList()
+})
+</script>
+<style lang="scss" scoped>
+.config {
+  padding: 0 10px;
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-start;
+  height: calc(100vh - 84px);
+}
+
+.right-content {
+  flex: 1;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+
+  .step-content {
+    margin-left: 10px;
+  }
+
+  .bottom-btn {
+    height: 60px;
+    border-top: 1px solid #ccc;
+    display: flex;
+    justify-content: flex-end;
+    align-items: center;
+  }
+}
+
+.custom-menu {
+  width: 200px;
+  height: 100%;
+}
+
+.center-content {
+  padding-left: 10px;
+  margin-top: 10px;
+  flex: 1;
+}
+
+.camera {
+  .camera-op {
+    display: flex;
+    justify-content: flex-end;
+
+    .el-button {
+      margin-left: 5px;
+    }
+  }
+
+  .camera-content {
+    flex: 1;
+
+    .camera-img {
+      margin-top: 10px;
+      width: 100%;
+      height: 450px;
+    }
+  }
+}
+.custom-form{
+  :deep(.el-input.is-disabled .el-input__wrapper) {
+    background: initial;
+  }
+  :deep(.el-select__wrapper.is-disabled) {
+    background: initial;
+  }
+}
+</style>

+ 12 - 12
src/views/deviceManage/modeling/index.vue

@@ -15,14 +15,13 @@
             <div class="content">
                 <div ref="stencilContainer" class="stencil"></div>
                 <div id="graphContainer" ref="graphContainer" class="graph-content"></div>
-                <!-- <img src="@/assets/images/road.png" class="roadback" alt="" /> -->
             </div>
         </div>
     </div>
 </template>
 
 <script setup lang="ts">
-import { Graph, Path, Edge, StringExt, Node, Cell, Model, DataUri } from '@antv/x6';
+import { Graph, Edge, Node, Cell, Model, DataUri } from '@antv/x6';
 import { Transform } from '@antv/x6-plugin-transform';
 import { Selection } from '@antv/x6-plugin-selection';
 import { Snapline } from '@antv/x6-plugin-snapline';
@@ -43,27 +42,27 @@ const state = reactive({
                 id: 'ac51fb2f-2753-4852-8239-53672a29bb14',
                 position: {
                     x: 160,
-                    y: 250
+                    y: 350
                 },
                 data: {
-                    name: '烟感系统'
+                    name: '风机'
                 }
             },
             {
                 id: '81004c2f-0413-4cc6-8622-127004b3befa',
                 position: {
                     x: 220,
-                    y: 110
+                    y: 180
                 },
                 data: {
-                    name: '烟感系统'
+                    name: '照明设施'
                 }
             },
             {
                 id: 'f2db8c64-6ffe-4c9b-83a2-037c9154e599',
                 position: {
                     x: 50,
-                    y: 120
+                    y: 150
                 },
                 data: {
                     name: '摄像头'
@@ -115,7 +114,7 @@ const init = () => {
                 args: {
                     attrs: {
                         fill: '#fff',
-                        stroke: '#31d0c6',
+                        stroke: '#043447',
                         strokeWidth: 4
                     }
                 }
@@ -209,13 +208,14 @@ const init = () => {
     });
     stencilContainer.value.appendChild(stencil.container);
     graph.drawBackground({
-        color: '#ccc',
+        color: '#043447',
         position: 'center',
         size: {
-            width: '100%'
-            // height: 100
+            width: 900,
+            height: 200
         },
-        image: new URL('@/assets/images/road.png', import.meta.url).href // 设置背景图片
+      image: new URL('@/assets/images/road.png', import.meta.url).href, // 设置背景图片
+
     });
     // #region 快捷键与事件
     graph.bindKey(['meta+c', 'ctrl+c'], () => {

+ 49 - 8
src/views/login.vue

@@ -1,10 +1,18 @@
 <template>
   <div class="login">
+    <div class="login-title">
+      <img src="@/assets/images/logo.png" alt="">
+      <span>机电运维平台</span>
+    </div>
+    <div class="bgCover">
+      <img src="@/assets/images/login-bg.svg"/>
+    </div>
     <el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
-      <h3 class="title">机电运维平台</h3>
+      <h3 class="title">登录</h3>
       <el-form-item v-if="tenantEnabled" prop="tenantId">
         <el-select v-model="loginForm.tenantId" filterable placeholder="请选择/输入公司名称" style="width: 100%">
-          <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName" :value="item.tenantId"></el-option>
+          <el-option v-for="item in tenantList" :key="item.tenantId" :label="item.companyName"
+            :value="item.tenantId"></el-option>
           <template #prefix><svg-icon icon-class="company" class="el-input__icon input-icon" /></template>
         </el-select>
       </el-form-item>
@@ -14,12 +22,14 @@
         </el-input>
       </el-form-item>
       <el-form-item prop="password">
-        <el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码" @keyup.enter="handleLogin">
+        <el-input v-model="loginForm.password" type="password" size="large" auto-complete="off" placeholder="密码"
+          @keyup.enter="handleLogin">
           <template #prefix><svg-icon icon-class="password" class="el-input__icon input-icon" /></template>
         </el-input>
       </el-form-item>
       <el-form-item v-if="captchaEnabled" prop="code">
-        <el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%" @keyup.enter="handleLogin">
+        <el-input v-model="loginForm.code" size="large" auto-complete="off" placeholder="验证码" style="width: 63%"
+          @keyup.enter="handleLogin">
           <template #prefix><svg-icon icon-class="validCode" class="el-input__icon input-icon" /></template>
         </el-input>
         <div class="login-code">
@@ -214,22 +224,53 @@ onMounted(() => {
   justify-content: center;
   align-items: center;
   height: 100%;
-  background-image: url('../assets/images/login-background.jpg');
-  background-size: cover;
+  background: linear-gradient(to right, #DCE6F5, #f5f5f5) // background-image: url('../assets/images/login-bg.png');
+}
+
+.bgImg {
+  position: fixed;
+  width: 600px;
+  left: 100px;
+  filter: blur(5px);
+}
+
+.bgCover {
+  position: fixed;
+  left: 100px;
+  img {
+    width: 600px;
+  }
+}
+.login-title{
+  position: absolute;
+  top: 50px;
+  left: 50px;
+  display: flex;
+  align-items: center;
+  img{
+    height: 30px;
+  }
+  span{
+    font-size: 20px;
+    font-weight: 500;
+    color: #182B4E;
+    margin-left: 10px;
+  }
 }
 
 .title {
   margin: 0px auto 30px auto;
   text-align: center;
-  color: #707070;
+  color: #182B4E;
 }
 
 .login-form {
+  position: absolute;
+  right: 150px;
   border-radius: 6px;
   background: #ffffff;
   width: 400px;
   padding: 25px 25px 5px 25px;
-
   .el-input {
     height: 40px;
 

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác