zouyu
20 小时以前 6d2f26e4c867c1cfba4ebe86d4162edb01da80a0
src/views/environmentAccess/remoteMonitoringOfEquipment/index.vue
@@ -1,9 +1,633 @@
<template>
  <div></div>
  <div class="app-container">
    <el-form :model="filters" :inline="true">
      <el-form-item label="设备名称">
        <el-input
          v-model="filters.name"
          style="width: 240px"
          placeholder="请输入设备名称"
          clearable
          :prefix-icon="Search"
          @change="getTableData"
        />
      </el-form-item>
      <el-form-item label="设备状态">
        <el-select
          v-model="filters.status"
          style="width: 240px"
          placeholder="请选择设备状态"
          clearable
          @change="getTableData"
        >
          <el-option label="在线" value="1"></el-option>
          <el-option label="离线" value="0"></el-option>
          <el-option label="故障" value="2"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item label="设备类型">
        <el-select
          v-model="filters.type"
          style="width: 240px"
          placeholder="请选择设备类型"
          clearable
          @change="getTableData"
        >
          <el-option label="门禁设备" value="0"></el-option>
          <el-option label="环境监测设备" value="1"></el-option>
          <el-option label="视频监控设备" value="2"></el-option>
          <el-option label="智能道闸" value="3"></el-option>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" @click="getTableData">搜索</el-button>
        <el-button @click="resetFilters">重置</el-button>
      </el-form-item>
    </el-form>
    <div class="monitor-container">
      <div class="monitor-stats">
        <div class="stat-card">
          <div class="stat-title">在线设备</div>
          <div class="stat-value online">{{ onlineCount }}</div>
        </div>
        <div class="stat-card">
          <div class="stat-title">离线设备</div>
          <div class="stat-value offline">{{ offlineCount }}</div>
        </div>
        <div class="stat-card">
          <div class="stat-title">故障设备</div>
          <div class="stat-value error">{{ errorCount }}</div>
        </div>
        <div class="stat-card">
          <div class="stat-title">设备总数</div>
          <div class="stat-value total">{{ totalCount }}</div>
        </div>
      </div>
      <div class="table_list">
        <PIMTable
          rowKey="id"
          :column="columns"
          :tableData="dataList"
          :page="{
            current: pagination.currentPage,
            size: pagination.pageSize,
            total: pagination.total,
          }"
          @pagination="changePage"
        >
        </PIMTable>
      </div>
    </div>
    <!-- 设备详情弹窗 -->
    <el-dialog
      v-model="detailVisible"
      :title="`设备详情 - ${selectedDevice.name}`"
      width="800px"
    >
      <div v-if="selectedDevice" class="device-detail">
        <div class="detail-section">
          <h3>基本信息</h3>
          <el-descriptions :column="2" border>
            <el-descriptions-item label="设备名称">{{
              selectedDevice.name
            }}</el-descriptions-item>
            <el-descriptions-item label="设备类型">{{
              typeMap[selectedDevice.type]
            }}</el-descriptions-item>
            <el-descriptions-item label="设备状态">{{
              statusMap[selectedDevice.status]
            }}</el-descriptions-item>
            <el-descriptions-item label="设备编号">{{
              selectedDevice.code
            }}</el-descriptions-item>
            <el-descriptions-item label="安装位置">{{
              selectedDevice.location
            }}</el-descriptions-item>
            <el-descriptions-item label="安装时间">{{
              selectedDevice.installTime
            }}</el-descriptions-item>
            <el-descriptions-item label="最后维护时间">{{
              selectedDevice.maintainTime
            }}</el-descriptions-item>
            <el-descriptions-item label="责任人">{{
              selectedDevice.responsiblePerson
            }}</el-descriptions-item>
          </el-descriptions>
        </div>
        <div class="detail-section">
          <h3>实时数据</h3>
          <div class="realtime-data">
            <div
              v-for="data in selectedDevice.realtimeData"
              :key="data.name"
              class="data-item"
            >
              <div class="data-name">{{ data.name }}</div>
              <div class="data-value">{{ data.value }}{{ data.unit }}</div>
              <div class="data-status" :class="data.status">
                {{ data.status === "normal" ? "正常" : "异常" }}
              </div>
            </div>
          </div>
        </div>
        <div class="detail-section">
          <h3>控制操作</h3>
          <div class="control-buttons">
            <el-button
              type="primary"
              @click="controlDevice('restart')"
              :disabled="selectedDevice.status !== '1'"
              >重启设备</el-button
            >
            <el-button
              type="warning"
              @click="controlDevice('reset')"
              :disabled="selectedDevice.status !== '1'"
              >重置设备</el-button
            >
            <el-button
              type="danger"
              @click="controlDevice('shutdown')"
              :disabled="selectedDevice.status !== '1'"
              >关闭设备</el-button
            >
            <el-button
              type="success"
              @click="controlDevice('update')"
              :disabled="selectedDevice.status !== '1'"
              >固件更新</el-button
            >
          </div>
        </div>
      </div>
      <template #footer>
        <span class="dialog-footer">
          <el-button @click="detailVisible = false">关闭</el-button>
        </span>
      </template>
    </el-dialog>
  </div>
</template>
<script>
<script setup>
import { ref, reactive, computed, onMounted } from "vue";
import {
  Search
} from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox } from "element-plus";
// 定义假数据
const mockDevices = [
  {
    id: 1,
    name: "东门门禁控制器",
    code: "DEV-20250001",
    type: "0",
    status: "1",
    location: "东门入口",
    installTime: "2025-01-15",
    maintainTime: "2025-12-20",
    responsiblePerson: "张三",
    realtimeData: [
      { name: "温度", value: 23.5, unit: "℃", status: "normal" },
      { name: "湿度", value: 45, unit: "%", status: "normal" },
      { name: "运行时长", value: 12560, unit: "h", status: "normal" },
      { name: "门禁开关次数", value: 234, unit: "次/天", status: "normal" },
    ],
  },
  {
    id: 2,
    name: "西门视频监控",
    code: "DEV-20250002",
    type: "2",
    status: "1",
    location: "西门出口",
    installTime: "2025-02-20",
    maintainTime: "2025-11-15",
    responsiblePerson: "李四",
    realtimeData: [
      { name: "视频清晰度", value: 1080, unit: "P", status: "normal" },
      { name: "帧率", value: 25, unit: "fps", status: "normal" },
      { name: "存储空间", value: 78, unit: "%", status: "warning" },
      { name: "网络延迟", value: 12, unit: "ms", status: "normal" },
    ],
  },
  {
    id: 3,
    name: "南门环境监测仪",
    code: "DEV-20250003",
    type: "1",
    status: "2",
    location: "南门广场",
    installTime: "2025-03-10",
    maintainTime: "2025-10-05",
    responsiblePerson: "王五",
    realtimeData: [
      { name: "PM2.5", value: 85, unit: "μg/m³", status: "error" },
      { name: "PM10", value: 120, unit: "μg/m³", status: "error" },
      { name: "噪声", value: 65, unit: "dB", status: "normal" },
      { name: "风速", value: 1.2, unit: "m/s", status: "normal" },
    ],
  },
  {
    id: 4,
    name: "北门智能道闸",
    code: "DEV-20250004",
    type: "3",
    status: "0",
    location: "北门入口",
    installTime: "2025-04-05",
    maintainTime: "2025-09-20",
    responsiblePerson: "赵六",
    realtimeData: [
      { name: "开关次数", value: 156, unit: "次/天", status: "normal" },
      { name: "运行电压", value: 220, unit: "V", status: "normal" },
      { name: "运行电流", value: 5.2, unit: "A", status: "normal" },
      { name: "故障代码", value: "E001", unit: "", status: "error" },
    ],
  },
  {
    id: 5,
    name: "中控室服务器",
    code: "DEV-20250005",
    type: "1",
    status: "1",
    location: "中控室",
    installTime: "2025-05-15",
    maintainTime: "2025-12-01",
    responsiblePerson: "孙七",
    realtimeData: [
      { name: "CPU使用率", value: 45, unit: "%", status: "normal" },
      { name: "内存使用率", value: 68, unit: "%", status: "warning" },
      { name: "磁盘使用率", value: 82, unit: "%", status: "warning" },
      { name: "网络流量", value: 125, unit: "Mbps", status: "normal" },
    ],
  },
];
// 响应式数据
const filters = reactive({
  name: "",
  status: "",
  type: "",
});
const dataList = ref([]);
const pagination = reactive({
  currentPage: 1,
  pageSize: 10,
  total: 0,
});
const detailVisible = ref(false);
const selectedDevice = ref({});
// 状态映射
const statusMap = {
  0: '<el-tag type="warning">离线</el-tag>',
  1: '<el-tag type="success">在线</el-tag>',
  2: '<el-tag type="danger">故障</el-tag>',
};
// 类型映射
const typeMap = {
  0: "门禁设备",
  1: "环境监测设备",
  2: "视频监控设备",
  3: "智能道闸",
};
// 设备数量统计
const onlineCount = computed(() => {
  return mockDevices.filter((device) => device.status === "1").length;
});
const offlineCount = computed(() => {
  return mockDevices.filter((device) => device.status === "0").length;
});
const errorCount = computed(() => {
  return mockDevices.filter((device) => device.status === "2").length;
});
const totalCount = computed(() => {
  return mockDevices.length;
});
// 表格列配置
const columns = [
  {
    label: "设备名称",
    align: "center",
    prop: "name",
  },
  {
    label: "设备编号",
    align: "center",
    prop: "code",
  },
  {
    label: "设备类型",
    align: "center",
    prop: "type",
    formatter: (row) => {
      return typeMap[row.type];
    },
  },
  {
    label: "设备状态",
    align: "center",
    prop: "status",
    formatter: (row) => {
      return statusMap[row.status];
    },
  },
  {
    label: "安装位置",
    align: "center",
    prop: "location",
  },
  {
    label: "责任人",
    align: "center",
    prop: "responsiblePerson",
  },
  {
    label: "最后维护时间",
    align: "center",
    prop: "maintainTime",
  },
  {
    dataType: "action",
    label: "操作",
    align: "center",
    fixed: "right",
    width: 140,
    operation: [
      {
        name: "查看详情",
        type: "text",
        clickFun: (row) => {
          viewDetail(row);
        },
      },
      {
        name: "实时监控",
        type: "text",
        clickFun: (row) => {
          realtimeMonitor(row);
        },
        visible: (row) => {
          return row.status === "1";
        },
      },
      {
        name: "重启设备",
        type: "text",
        clickFun: (row) => {
          controlDevice("restart", row);
        },
        visible: (row) => {
          return row.status === "1";
        },
      },
    ],
  },
];
// 过滤后的数据
const filteredData = computed(() => {
  return mockDevices.filter((item) => {
    const nameMatch = !filters.name || item.name.includes(filters.name);
    const statusMatch = !filters.status || item.status === filters.status;
    const typeMatch = !filters.type || item.type === filters.type;
    return nameMatch && statusMatch && typeMatch;
  });
});
// 获取表格数据
const getTableData = () => {
  pagination.total = filteredData.value.length;
  const start = (pagination.currentPage - 1) * pagination.pageSize;
  const end = start + pagination.pageSize;
  dataList.value = filteredData.value.slice(start, end);
};
// 重置过滤器
const resetFilters = () => {
  filters.name = "";
  filters.status = "";
  filters.type = "";
  pagination.currentPage = 1;
  getTableData();
};
// 分页变化
const changePage = ({ page, limit }) => {
  pagination.currentPage = page;
  pagination.pageSize = limit;
  getTableData();
};
// 查看设备详情
const viewDetail = (row) => {
  selectedDevice.value = row;
  detailVisible.value = true;
};
// 实时监控
const realtimeMonitor = (row) => {
  ElMessage.info(`正在跳转到${row.name}的实时监控页面...`);
  // 这里可以跳转到实时监控页面或打开监控窗口
};
// 设备控制
const controlDevice = (action, row = selectedDevice.value) => {
  const actionMap = {
    restart: "重启",
    reset: "重置",
    shutdown: "关闭",
    update: "固件更新",
  };
  ElMessageBox.confirm(
    `确定要对设备 ${row.name} 执行${actionMap[action]}操作吗?`,
    "提示",
    {
      confirmButtonText: "确定",
      cancelButtonText: "取消",
      type: "warning",
    }
  )
    .then(() => {
      ElMessage.success(`设备${actionMap[action]}操作已发送`);
      // 模拟设备响应
      setTimeout(() => {
        ElMessage.success(`设备${actionMap[action]}操作执行成功`);
      }, 1000);
    })
    .catch(() => {});
};
// 定时更新设备状态(模拟实时变化)
const updateDeviceStatus = () => {
  // 随机更新一些设备的状态和实时数据
  mockDevices.forEach((device) => {
    if (Math.random() > 0.8) {
      // 80%概率不更新
      return;
    }
    // 随机更新状态
    const statuses = ["0", "1", "2"];
    device.status = statuses[Math.floor(Math.random() * statuses.length)];
    // 更新实时数据
    device.realtimeData.forEach((data) => {
      if (data.name === "温度") {
        data.value = (20 + Math.random() * 10).toFixed(1);
        data.status = data.value > 30 ? "error" : "normal";
      } else if (data.name === "湿度") {
        data.value = Math.floor(30 + Math.random() * 50);
        data.status = data.value > 80 ? "error" : "normal";
      } else if (data.name === "CPU使用率") {
        data.value = Math.floor(20 + Math.random() * 60);
        data.status =
          data.value > 80 ? "error" : data.value > 60 ? "warning" : "normal";
      }
    });
  });
  // 刷新表格数据
  getTableData();
};
// 初始化数据
onMounted(() => {
  getTableData();
  // 每5秒更新一次设备状态
  const interval = setInterval(updateDeviceStatus, 5000);
  // 清理定时器
  return () => clearInterval(interval);
});
</script>
<style>
<style lang="scss" scoped>
.table_list {
  margin-top: unset;
}
.monitor-container {
  .monitor-stats {
    display: flex;
    gap: 20px;
    margin-bottom: 20px;
    .stat-card {
      flex: 1;
      background: #fff;
      padding: 20px;
      border-radius: 8px;
      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
      text-align: center;
      .stat-title {
        font-size: 14px;
        color: #606266;
        margin-bottom: 10px;
      }
      .stat-value {
        font-size: 32px;
        font-weight: bold;
        &.online {
          color: #67c23a;
        }
        &.offline {
          color: #e6a23c;
        }
        &.error {
          color: #f56c6c;
        }
        &.total {
          color: #409eff;
        }
      }
    }
  }
}
.device-detail {
  .detail-section {
    margin-bottom: 20px;
    h3 {
      font-size: 16px;
      font-weight: bold;
      margin-bottom: 10px;
      color: #303133;
    }
  }
  .realtime-data {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 15px;
    .data-item {
      background: #f5f7fa;
      padding: 15px;
      border-radius: 8px;
      .data-name {
        font-size: 14px;
        color: #606266;
        margin-bottom: 5px;
      }
      .data-value {
        font-size: 24px;
        font-weight: bold;
        color: #303133;
        margin-bottom: 5px;
      }
      .data-status {
        font-size: 12px;
        padding: 2px 8px;
        border-radius: 10px;
        &.normal {
          background: #f0f9eb;
          color: #67c23a;
        }
        &.warning {
          background: #fdf6ec;
          color: #e6a23c;
        }
        &.error {
          background: #fef0f0;
          color: #f56c6c;
        }
      }
    }
  }
  .control-buttons {
    display: flex;
    gap: 10px;
    margin-top: 10px;
  }
}
</style>