Newer
Older
robot_dog_patrol_front / src / views / patrol / navigation / index.vue
dutingting on 18 Mar 14 KB 机器狗初版
<!-- 巡检导航 -->
<script setup lang="ts">
import { ElMessage, ElMessageBox } from 'element-plus'
import addRouteDialog from './dialog/editRouteDialog.vue'
import threeCom from '@/components/threejs/index.vue'
import { controlDevice, delRoute, getRouteByDog, getRouteList } from '@/api/patrol/navigation'
import useSocket from '@/store/modules/websocket'
import { getDeviceList } from '@/api/patrol/manage'
const socket = useSocket()
const currentDevice = ref({ // 当前设备信息
  id: '',
  robotName: '--', // 设备名称
  robotCode: '--', // 设备编号
  robotStatus: '--', // 1,有效,0无效
  robotCell: '--', // 电量
  routeId: '', // 正在执行路线id
  route: '--',
  speed: '--',
  name: '--', // 小区名称
  address: '--', // 小区地址
})

const btnStatusMap = ref('') // 按钮状态
const routeInfoDetailList: any = ref([]) // 巡检路线的点的集合
// 点击更换设备
const changeDevice = () => {
  ElMessageBox.confirm(
    '确定更换设备吗?',
    '提示',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    },
  ).then(() => {
    ElMessage.info('敬请期待')
  })
}

// 点击解除绑定
const unbind = () => {
  ElMessageBox.confirm(
    '确定解除绑定吗?',
    '提示',
    {
      confirmButtonText: '确定',
      cancelButtonText: '取消',
      type: 'warning',
    },
  ).then(() => {
    ElMessage.info('敬请期待')
  })
}

// -----------------------------------------------表格------------------------------------------------------
const addRouteDialogRef = ref()
// 表格表头
const columns: any = ref([
  { text: '巡检路线名称', value: 'routeName', align: 'center' },
  { text: '街道/小区名称', value: 'communityName', align: 'center' },
  { text: '小区地址', value: 'communityAddress', align: 'center' },
  { text: '机器人', value: 'robotName', align: 'center' },
  { text: '更新时间', value: 'time', align: 'center', width: '120' },
])
const listQuery = ref({
  communityAddress: '', //	小区地址
  communityName: '', //		小区名称
  robotName: '', //		机器人名称
  routeName: '', //		巡航线名称
  limit: 10,
  offset: 1,
})
const list: any = ref([]) // 数据列表
const listLoading = ref(false)
const total = ref(0)
// 获取路线列表
const fetchRouteList = () => {
  listLoading.value = true
  getRouteList(listQuery.value).then((res) => {
    list.value = res.data.rows.map((item: { updateTime: string; createTime: string }) => {
      return {
        ...item,
        time: !item.updateTime ? item.createTime : item.updateTime, // 更新时间
      }
    })
    total.value = res.data.total
    listLoading.value = false
  }).catch(() => { listLoading.value = false })
}

// 页数发生变化后的操作,可能是页码变化,可能是每页容量变化,此函数必写
function changePage(val: { size: number; page: number }) {
  if (val && val.size) {
    listQuery.value.limit = val.size
  }
  if (val && val.page) {
    listQuery.value.offset = val.page
  }
  fetchRouteList()
}

// 编辑、详情、删除
const handleRouteEdit = (row: any, type: 'detail' | 'edit' | 'del') => {
  if (type === 'detail' || type === 'edit') {
    addRouteDialogRef.value.initDialog(type, { ...currentDevice.value, robotId: currentDevice.value.id }, row)
  }
  else if (type === 'del') {
    ElMessageBox.confirm(
      '确认删除吗?',
      '提示',
      {
        confirmButtonText: '确认',
        cancelButtonText: '取消',
        type: 'warning',
      },
    )
      .then(() => {
        delRoute({ ids: [row.id] }).then((res) => {
          ElMessage({
            type: 'success',
            message: '删除成功',
          })
          fetchRouteList()
        })
      })
  }
}
// ------------------------------------------------控制--------------------------------------------------
const map: any = {
  startRecordMap: 'w/', // 开始地图录入
  endRecordMap: 'e/', // 完成地图录入
  startRecordRoutInfo: 'x/', // 添加巡检点
  endRecordRoutInfo: 'c/', // 保存巡检点和地图
  delRecordRoutInfo: 'v/', // 删除所有的巡检点和地图
  startNavigation: 'a/', // 开始巡检
  pauseNavigation: 's/', // 暂停巡检
  recoverNavigation: 'd/', // 恢复巡检
}

// 开始录制地图、完成点云图录制、添加巡检点、完成巡检点录制
const control = (type: string) => {
  if (type === 'endRecordRoutInfo') {
    if (!routeInfoDetailList.value.length) {
      ElMessage.warning('没有巡检点-点云数据,请排查是否未添加巡检点或者上次添加是否还未完成并返回数据')
      return false
    }
    addRouteDialogRef.value.initDialog('add', { ...currentDevice.value, robotId: currentDevice.value.id }, null, routeInfoDetailList.value)
  }
  else {
    if (currentDevice.value.id) {
      const params = {
        command: map[type],
        robotDogId: currentDevice.value.id,
      }
      controlDevice(params).then(() => {
        ElMessage.success('操作成功')
      })
    }
  }
}
// -------------------------------------------------设备相关-------------------------------------------------
// 根据设备区查询当前巡检路线
const fetchRouteByDog = (id: string) => {
  getRouteByDog({ id }).then((res) => {
    currentDevice.value.routeId = res.data[0].id // 正在执行路线id
  })
}

// 获取设备列表
const fetchDeviceList = () => {
  const params = {
    robotName: '',
  }
  getDeviceList(params).then((res) => {
    currentDevice.value.id = res.data.rows[0].id
    currentDevice.value.robotName = res.data.rows[0].robotName // 设备名称
    currentDevice.value.robotCode = res.data.rows[0].robotCode // 设备编号
    currentDevice.value.robotStatus = `${res.data.rows[0].robotStatus}` === '1' ? '工作中' : '闲置' // 1,有效,0无效
    currentDevice.value.robotCell = `${res.data.rows[0].robotCell}%` // 电量
    // currentDevice.value.routeId = res.data.rows[0].routeId // 正在执行路线id
    fetchRouteByDog(currentDevice.value.id)
  })
}

// ---------------------------------------------------websocket----------------------------------------------------------
watch(() => socket.data, (newVal: any) => {
  console.log('socket监听到数据:', newVal)
  if (newVal && newVal.type === 'sendPoints') {
    if (Array.isArray(newVal.points) && newVal.points.length) {
      routeInfoDetailList.value = newVal.points
      console.log('socket监听到数据:', routeInfoDetailList.value)
    }
  }
}, {
  deep: true,
  immediate: true,
})
// --------------------------------------------------------------------------------------------------------------
onMounted(() => {
  list.value = [
    {
      content: '燃气管线周边是否施工',
      situation: '现场周边无施工现象',
      picture: '',
      route: '路线1',
      name: '二街坊社区',
      address: '北京市海淀区永定路街道',
      robotName: '机器狗1号',
      updateTime: '2025-03-10 14:42:00',
    },
    {
      content: '闸井燃气浓度是否正常',
      situation: '正常',
      picture: '',
      route: '路线2',
      name: '永定路57号院',
      address: '北京市海淀区永定路街道',
      robotName: '机器狗2号',
      updateTime: '2025-03-10 10:00:00',
    },
  ]
  fetchDeviceList() // 获取设备列表,默认第一个设备为当前设备
  fetchRouteList() // 获取巡检路线
})
</script>

<template>
  <app-container>
    <div class="patrol-manage">
      <div class="box box-top">
        <div>
          <div class="button-area">
            <div>
              <span style="margin-right: 30px;">巡检导航测试</span>
              <el-button type="primary" :disabled="!currentDevice.id" @click="control('startRecordMap')">
                开始录制地图
              </el-button>
              <el-button type="success" :disabled="!currentDevice.id" @click="control('endRecordMap')">
                完成点云图录制
              </el-button>
            </div>
            <div style="margin: 20px 0;">
              <span style="margin-right: 30px;">巡检点录入</span>
              <el-button type="primary" :disabled="!currentDevice.id" @click="control('startRecordRoutInfo')">
                添加巡检点
              </el-button>
              <el-button type="danger" :disabled="!currentDevice.id" @click="control('endRecordMap')">
                删除上一个巡检点
              </el-button>
              <el-button type="success" :disabled="!currentDevice.id" @click="control('endRecordRoutInfo')">
                完成巡航点录入
              </el-button>
            </div>
          </div>
          <div class="box-top-item">
            <three-com style="width: 100%; height: 400px" class="three-area" />
          </div>
        </div>
        <div style="height: 100%; background-color: #ccc; width: 1px;" />
        <div class="box-top-item">
          <div class="box-title">
            <div class="title">
              <span>关联设备</span>
              <img
                style="width: 20px; height: 20px"
                src="../../../assets/tempImages/icon-link.svg"
              >
            </div>
            <div>
              <el-button type="primary" link @click="changeDevice">
                更换设备
              </el-button>
              <el-button type="danger" link @click="unbind">
                解除绑定
              </el-button>
            </div>
          </div>
          <div class="box-main">
            <img
              style="width: 200px; height: 200px"
              src="../../../assets/tempImages/dog.png"
            >
            <div class="box-device">
              <span>
                <span class="title">设备名称: </span>
                <span class="text">{{ currentDevice.robotName }}</span>
              </span>
              <span>
                <span class="title">设备状态: </span>
                <span class="text" style="color: #afcc85">{{ currentDevice.robotStatus }}</span>
              </span>
              <span>
                <span class="title">设备电量: </span>
                <span class="text">{{ currentDevice.robotCell }}</span>
              </span>
              <span>
                <span class="title">小区名称: </span>
                <span class="text">{{ currentDevice.name }}</span>
              </span>
              <span>
                <span class="title">小区地址: </span>
                <span class="text">{{ currentDevice.address }}</span>
              </span>
            </div>
          </div>
        </div>
      </div>
      <div class="box-bottom">
        <table-container title="路线管理" style="margin-top: 0px !important;">
          <normal-table
            :data="list"
            :total="total"
            :pagination="true"
            :columns="columns"
            :query="listQuery"
            :list-loading="listLoading"
            @change="changePage"
          >
            <template #preColumns>
              <el-table-column label="序号" width="55" align="center">
                <template #default="scope">
                  {{ (listQuery.offset - 1) * listQuery.limit + scope.$index + 1 }}
                </template>
              </el-table-column>
            </template>
            <template #columns>
              <el-table-column label="操作" width="160" align="center" fixed="right">
                <template #default="scope">
                  <el-button
                    type="primary"
                    link
                    size="small"
                    class="table-text-button"
                    @click="handleRouteEdit(scope.row, 'edit')"
                  >
                    编辑
                  </el-button>
                  <el-button
                    type="primary"
                    link
                    size="small"
                    class="table-text-button"
                    @click="handleRouteEdit(scope.row, 'detail')"
                  >
                    详情
                  </el-button>
                  <el-button
                    type="danger"
                    link
                    size="small"
                    class="table-text-button"
                    @click="handleRouteEdit(scope.row, 'del')"
                  >
                    删除
                  </el-button>
                </template>
              </el-table-column>
            </template>
          </normal-table>
        </table-container>
      </div>
    </div>
  </app-container>
  <add-route-dialog ref="addRouteDialogRef" @close-refresh="fetchRouteList()" />
</template>

<style scoped lang="scss">
.patrol-manage {
  display: flex;
  flex-direction: column;
  // gap: 10px;
}

.box {
  width: 100%;
  background-color: #fff;
  padding: 10px;
  border-radius: 7px;
}
.box-top {
  display: grid;
  /* 将容器划分为两列,左边占 2 份,右边占 1 份 */
  grid-template-columns: 2fr  0.05fr 0.6fr;
  /* 设置列之间的间距为 20 像素 */
  gap: 20px;
  .button-area {

  }

  .box-top-item {
    display: flex;
    flex-direction: column;
    // justify-content: center;
    // align-items: center;
    .three-area {
      position: relative;
      width: 100%;
      height: 400px;
    }
    .box-title {
      font-size: 14px;
      font-weight: 600;
      display: flex;
      justify-content: space-between;
      align-items: center;
      margin-bottom: 20px;
      .title {
        display: flex;
        justify-content: space-between;
        align-items: center;
      }
    }
    .box-main {
      display:flex;
      height: 100%;
      flex-direction: column;
      justify-content: space-around;
      align-items: center;
      .box-device {
        display: flex;
        flex-direction: column;
        line-height: 29px;
        color: rgba(0,0,0,1);
        font-size: 14px;
        text-align: left;
        font-family: SourceHanSansSC-regular;
        .text {
          font-weight: 600;
        }
      }
    }
  }
}

.box-bottom {
  display: flex;
  flex-direction: column;
  .title {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 20px;
  }
  .control-title {
    color: white;
    background-color: #ff9f11;
    border-bottom-right-radius: 10px;
    border-top-right-radius: 10px;
    line-height: 30px;
    font-weight: bold;
    text-align: center;
    letter-spacing: 1px;
    font-family: "Helvetica Neue", Helvetica, "PingFang SC", "Hiragino Sans GB", "Microsoft YaHei", SimSun, sans-serif;
    writing-mode: vertical-rl;
    text-orientation: upright;
    width: 30px;
    height: 100px;
  }
  .round-btn {
    position: absolute;
    top: 14px;
    left: 129px;
    width: 42px;
    height: 42px;
    z-index: 111111;
    border-radius: 21px;
    cursor: pointer;
    &:hover {
      background-color: rgb(61 125 254 / 53%);
    }
  }
}
</style>