Kaynağa Gözat

feat(device): 添加电表设备列表和详情功能

- 新增设备列表组件,展示电表设备信息和统计
- 实现电表详情功能,包括电压、电流、功率等数据展示
- 添加电表历史数据表格和趋势图表- 优化用户界面,增加搜索、分页、刷新等功能
- 引入 Lucide 图标库和 Vue ECharts 组件
nahida 1 yıl önce
ebeveyn
işleme
1c740665c6

+ 3 - 1
package.json

@@ -21,18 +21,20 @@
     "@vueuse/core": "10.11.0",
     "axios": "0.28.1",
     "clipboard": "2.0.11",
-    "echarts": "5.5.1",
+    "echarts": "^5.5.1",
     "element-plus": "2.7.6",
     "file-saver": "2.0.5",
     "fuse.js": "6.6.2",
     "js-beautify": "1.14.11",
     "js-cookie": "3.0.5",
     "jsencrypt": "3.3.2",
+    "lucide-vue-next": "^0.479.0",
     "nprogress": "0.2.0",
     "pinia": "2.1.7",
     "splitpanes": "3.1.5",
     "vue": "3.4.31",
     "vue-cropper": "1.1.1",
+    "vue-echarts": "^7.0.3",
     "vue-router": "4.4.0",
     "vuedraggable": "4.1.0"
   },

+ 125 - 0
src/views/device/electricity/components/device-list.vue

@@ -0,0 +1,125 @@
+<template>
+  <div class="p-4">
+    <div class="flex justify-between items-center mb-4">
+      <h2 class="text-xl font-bold">电表设备列表</h2>
+      <el-input
+          v-model="searchQuery"
+          placeholder="搜索设备..."
+          class="w-60"
+          clearable
+          prefix-icon="Search"
+      >
+        <template #prefix>
+          <Search class="text-gray-400"/>
+        </template>
+      </el-input>
+    </div>
+
+    <el-card shadow="hover" class="mb-4">
+      <template #header>
+        <div class="flex justify-between items-center">
+          <span>设备统计</span>
+          <el-button type="primary" size="small">
+            <RefreshCw class="mr-1"/>
+            刷新
+          </el-button>
+        </div>
+      </template>
+      <div class="grid grid-cols-1 md:grid-cols-3 gap-4">
+        <div class="stat-card bg-blue-50 p-4 rounded-lg">
+          <div class="text-blue-500 text-lg font-medium">总设备数</div>
+          <div class="text-2xl font-bold mt-2">{{ meters.length }}</div>
+        </div>
+        <div class="stat-card bg-green-50 p-4 rounded-lg">
+          <div class="text-green-500 text-lg font-medium">在线设备</div>
+          <div class="text-2xl font-bold mt-2">{{ meters.filter(m => m.status === 'online').length }}</div>
+        </div>
+        <div class="stat-card bg-red-50 p-4 rounded-lg">
+          <div class="text-red-500 text-lg font-medium">离线设备</div>
+          <div class="text-2xl font-bold mt-2">{{ meters.filter(m => m.status === 'offline').length }}</div>
+        </div>
+      </div>
+    </el-card>
+
+    <el-table
+        :data="filteredMeters"
+        style="width: 100%"
+        border
+        stripe
+        @row-click="handleRowClick"
+        row-class-name="cursor-pointer hover:bg-gray-100"
+    >
+      <el-table-column prop="tableNumber" label="电表编号" width="180"/>
+      <el-table-column prop="location" label="安装位置"/>
+      <el-table-column prop="lastReading" label="最近读数" width="120"/>
+      <el-table-column label="状态" width="100">
+        <template #default="scope">
+          <el-tag :type="scope.row.status === 'online' ? 'success' : 'danger'" size="small">
+            {{ scope.row.status === 'online' ? '在线' : '离线' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="150">
+        <template #default="scope">
+          <el-button
+              type="primary"
+              size="small"
+              @click.stop="viewDetails(scope.row.tableNumber)"
+          >
+            <Eye class="mr-1"/>
+            查看详情
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <div class="flex justify-center mt-4">
+      <el-pagination
+          v-model:current-page="currentPage"
+          v-model:page-size="pageSize"
+          :page-sizes="[10, 20, 50, 100]"
+          layout="total, sizes, prev, pager, next, jumper"
+          :total="filteredMeters.length"
+      />
+    </div>
+  </div>
+</template>
+
+<script setup>
+import {computed, ref} from 'vue';
+import {Eye, RefreshCw, Search} from 'lucide-vue-next'
+
+// 模拟电表数据
+const meters = ref([
+  {tableNumber: '110125022003', location: '1号楼3单元', lastReading: '1532 kWh', status: 'online'}
+]);
+
+// 分页和搜索
+const currentPage = ref(1);
+const pageSize = ref(10);
+const searchQuery = ref('');
+
+// 过滤后的电表列表
+const filteredMeters = computed(() => {
+  const query = searchQuery.value.toLowerCase().trim();
+  if (!query) return meters.value;
+
+  return meters.value.filter(meter =>
+      meter.tableNumber.toLowerCase().includes(query) ||
+      meter.location.toLowerCase().includes(query)
+  );
+});
+
+// 定义emit以便向父组件传递事件
+const emit = defineEmits(['showDetails']);
+
+// 查看详情
+const viewDetails = (tableNumber) => {
+  emit('showDetails', tableNumber);
+};
+
+// 行点击事件
+const handleRowClick = (row) => {
+  viewDetails(row.tableNumber);
+};
+</script>

+ 621 - 0
src/views/device/electricity/components/electricity-detail.vue

@@ -0,0 +1,621 @@
+<script setup>
+import {computed, onMounted, ref} from 'vue';
+import request from "@/utils/request.js";
+import {ElMessage} from "element-plus";
+import ECharts from 'vue-echarts';
+import 'echarts';
+import {parseTime} from "@/utils/ruoyi.js";
+
+// Data refs
+const newElectricityData = ref(null);
+const electricityRecords = ref([]);
+const loading = ref(false);
+const tableLoading = ref(false);
+const totalRecords = ref(0);
+const currentPage = ref(1);
+const pageSize = ref(10);
+const searchQuery = ref('');
+const dateRange = ref([]);
+const props = defineProps({
+  tableNumber: {
+    type: String,
+    required: true
+  },
+});
+
+// Computed properties for dashboard metrics
+const formattedVoltages = computed(() => {
+  if (!newElectricityData.value) return {a: '0', b: '0', c: '0'};
+  return {
+    a: parseFloat(newElectricityData.value.aVoltage || 0).toFixed(2),
+    b: parseFloat(newElectricityData.value.bVoltage || 0).toFixed(2),
+    c: parseFloat(newElectricityData.value.cVoltage || 0).toFixed(2)
+  };
+});
+
+const formattedCurrents = computed(() => {
+  if (!newElectricityData.value) return {a: '0', b: '0', c: '0'};
+  return {
+    a: parseFloat(newElectricityData.value.aCurrent || 0).toFixed(2),
+    b: parseFloat(newElectricityData.value.bCurrent || 0).toFixed(2),
+    c: parseFloat(newElectricityData.value.cCurrent || 0).toFixed(2)
+  };
+});
+
+const totalPowerConsumption = computed(() => {
+  if (!newElectricityData.value) return '0';
+  return parseFloat(newElectricityData.value.totalElectricity || 0).toFixed(2);
+});
+
+// Chart options
+const voltageChartOption = computed(() => {
+  if (!electricityRecords.value.length) return {};
+
+  const data = electricityRecords.value.slice().reverse();
+  return {
+    tooltip: {
+      trigger: 'axis'
+    },
+    legend: {
+      data: ['A相电压', 'B相电压', 'C相电压']
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'category',
+      boundaryGap: false,
+      data: data.map(item => {
+        const date = new Date(item.createTime);
+        return `${date.getHours()}:${date.getMinutes()}`;
+      })
+    },
+    yAxis: {
+      type: 'value',
+      name: '电压 (V)'
+    },
+    series: [
+      {
+        name: 'A相电压',
+        type: 'line',
+        data: data.map(item => parseFloat(item.aVoltage || 0))
+      },
+      {
+        name: 'B相电压',
+        type: 'line',
+        data: data.map(item => parseFloat(item.bVoltage || 0))
+      },
+      {
+        name: 'C相电压',
+        type: 'line',
+        data: data.map(item => parseFloat(item.cVoltage || 0))
+      }
+    ]
+  };
+});
+
+const powerChartOption = computed(() => {
+  if (!electricityRecords.value.length) return {};
+
+  const data = electricityRecords.value.slice().reverse();
+  return {
+    tooltip: {
+      trigger: 'axis'
+    },
+    legend: {
+      data: ['总有功功率', 'A相功率', 'B相功率', 'C相功率']
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true
+    },
+    xAxis: {
+      type: 'category',
+      boundaryGap: false,
+      data: data.map(item => {
+        const date = new Date(item.createTime);
+        return `${date.getHours()}:${date.getMinutes()}`;
+      })
+    },
+    yAxis: {
+      type: 'value',
+      name: '功率 (kW)'
+    },
+    series: [
+      {
+        name: '总有功功率',
+        type: 'line',
+        data: data.map(item => parseFloat(item.totalActivePower || 0))
+      },
+      {
+        name: 'A相功率',
+        type: 'line',
+        data: data.map(item => parseFloat(item.aActivePower || 0))
+      },
+      {
+        name: 'B相功率',
+        type: 'line',
+        data: data.map(item => parseFloat(item.bActivePower || 0))
+      },
+      {
+        name: 'C相功率',
+        type: 'line',
+        data: data.map(item => parseFloat(item.cActivePower || 0))
+      }
+    ]
+  };
+});
+
+// Methods
+const getNewElectricityData = async () => {
+  loading.value = true;
+  try {
+    const arr = [{
+      column: "create_time",
+      type: "orderByDesc"
+    }];
+
+    const res = await request.get("/messageParse/getAll", {
+      params: {
+        conditionJson: encodeURIComponent(JSON.stringify(arr))
+      }
+    });
+
+    if (res.code !== 200) {
+      ElMessage.error(res.msg);
+      return;
+    }
+
+    if (res.data.length === 0) {
+      ElMessage.warning("暂无数据");
+      return;
+    }
+
+    newElectricityData.value = res.data[0];
+  } catch (error) {
+    ElMessage.error("获取最新电表数据失败");
+    console.error(error);
+  } finally {
+    loading.value = false;
+  }
+};
+
+const getElectricityData = async () => {
+  tableLoading.value = true;
+  try {
+    const conditions = [{
+      column: "create_time",
+      type: "orderByDesc"
+    }];
+    conditions.push({
+      column: "table_number",
+      type: "eq",
+      value: props.tableNumber
+    })
+
+    // Add search condition if query exists
+    if (searchQuery.value) {
+      conditions.push({
+        column: "table_number",
+        type: "like",
+        value: searchQuery.value
+      });
+    }
+
+    if (dateRange.value && dateRange.value.length === 2) {
+
+      conditions.push({
+        column: "create_time",
+        type: "between",
+        value: parseTime(dateRange.value[0]) + ',' + parseTime(dateRange.value[1])
+      });
+    }
+
+    const res = await request.get("/messageParse/findByPage", {
+      params: {
+        pageNum: currentPage.value,
+        pageSize: pageSize.value,
+        conditionJson: encodeURIComponent(JSON.stringify(conditions))
+      }
+    });
+
+    if (res.code !== 200) {
+      ElMessage.error(res.msg);
+      return;
+    }
+
+    electricityRecords.value = res.data.records;
+    totalRecords.value = res.data.total;
+  } catch (error) {
+    ElMessage.error("获取电表数据列表失败");
+    console.error(error);
+  } finally {
+    tableLoading.value = false;
+  }
+};
+
+const handlePageChange = (page) => {
+  currentPage.value = page;
+  getElectricityData();
+};
+
+const handleSizeChange = (size) => {
+  pageSize.value = size;
+  currentPage.value = 1;
+  getElectricityData();
+};
+
+const handleSearch = () => {
+  currentPage.value = 1;
+  getElectricityData();
+};
+
+const refreshData = () => {
+  getNewElectricityData();
+  getElectricityData();
+};
+
+// Format date for display
+const formatDate = (dateString) => {
+  if (!dateString) return '';
+  const date = new Date(dateString);
+  return date.toLocaleString();
+};
+
+
+// Lifecycle hooks
+onMounted(() => {
+  getNewElectricityData();
+  getElectricityData();
+
+  // Set up auto-refresh every 30 seconds
+  const refreshInterval = setInterval(refreshData, 30000);
+
+  // Clean up interval on component unmount
+  onUnmounted(() => {
+    clearInterval(refreshInterval);
+  });
+});
+</script>
+
+<template>
+  <div class="electricity-dashboard p-4">
+    <!-- Dashboard Cards -->
+    <el-row :gutter="20" class="mb-6">
+      <el-col :xs="24" :sm="12" :md="6">
+        <el-card shadow="hover" class="h-full">
+          <template #header>
+            <div class="flex items-center">
+              <i class="el-icon-data-line mr-2"></i>
+              <span>总用电量</span>
+            </div>
+          </template>
+          <div class="text-center">
+            <h3 class="text-3xl font-bold text-blue-600">{{ totalPowerConsumption }}</h3>
+            <p class="text-gray-500">千瓦时 (kWh)</p>
+          </div>
+        </el-card>
+      </el-col>
+
+      <el-col :xs="24" :sm="12" :md="6">
+        <el-card shadow="hover" class="h-full">
+          <template #header>
+            <div class="flex items-center">
+              <i class="el-icon-lightning mr-2"></i>
+              <span>电压 (V)</span>
+            </div>
+          </template>
+          <div class="grid grid-cols-3 gap-2 text-center">
+            <div>
+              <h4 class="text-sm text-gray-500">A相</h4>
+              <p class="text-lg font-semibold text-green-600">{{ formattedVoltages.a }}</p>
+            </div>
+            <div>
+              <h4 class="text-sm text-gray-500">B相</h4>
+              <p class="text-lg font-semibold text-green-600">{{ formattedVoltages.b }}</p>
+            </div>
+            <div>
+              <h4 class="text-sm text-gray-500">C相</h4>
+              <p class="text-lg font-semibold text-green-600">{{ formattedVoltages.c }}</p>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+
+      <el-col :xs="24" :sm="12" :md="6">
+        <el-card shadow="hover" class="h-full">
+          <template #header>
+            <div class="flex items-center">
+              <i class="el-icon-connection mr-2"></i>
+              <span>电流 (A)</span>
+            </div>
+          </template>
+          <div class="grid grid-cols-3 gap-2 text-center">
+            <div>
+              <h4 class="text-sm text-gray-500">A相</h4>
+              <p class="text-lg font-semibold text-orange-600">{{ formattedCurrents.a }}</p>
+            </div>
+            <div>
+              <h4 class="text-sm text-gray-500">B相</h4>
+              <p class="text-lg font-semibold text-orange-600">{{ formattedCurrents.b }}</p>
+            </div>
+            <div>
+              <h4 class="text-sm text-gray-500">C相</h4>
+              <p class="text-lg font-semibold text-orange-600">{{ formattedCurrents.c }}</p>
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+
+      <el-col :xs="24" :sm="12" :md="6">
+        <el-card shadow="hover" class="h-full">
+          <template #header>
+            <div class="flex items-center">
+              <i class="el-icon-timer mr-2"></i>
+              <span>最后更新时间</span>
+            </div>
+          </template>
+          <div class="text-center">
+            <h3 class="text-lg font-semibold text-gray-700">
+              {{ newElectricityData ? formatDate(newElectricityData.createTime) : '暂无数据' }}
+            </h3>
+            <el-button type="primary" size="small" @click="refreshData" :loading="loading" class="mt-2">
+              刷新数据
+            </el-button>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- Charts -->
+    <el-row :gutter="20" class="mb-6">
+      <el-col :xs="24" :lg="12">
+        <el-card shadow="hover">
+          <template #header>
+            <div class="flex items-center justify-between">
+              <span>电压趋势</span>
+              <el-tooltip content="显示最近10条记录的电压变化趋势">
+                <i class="el-icon-question"></i>
+              </el-tooltip>
+            </div>
+          </template>
+          <div class="h-80">
+            <e-charts v-if="electricityRecords.length" :option="voltageChartOption" autoresize/>
+            <div v-else class="h-full flex items-center justify-center text-gray-400">
+              暂无数据
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+
+      <el-col :xs="24" :lg="12">
+        <el-card shadow="hover">
+          <template #header>
+            <div class="flex items-center justify-between">
+              <span>功率趋势</span>
+              <el-tooltip content="显示最近10条记录的功率变化趋势">
+                <i class="el-icon-question"></i>
+              </el-tooltip>
+            </div>
+          </template>
+          <div class="h-80">
+            <e-charts v-if="electricityRecords.length" :option="powerChartOption" autoresize/>
+            <div v-else class="h-full flex items-center justify-center text-gray-400">
+              暂无数据
+            </div>
+          </div>
+        </el-card>
+      </el-col>
+    </el-row>
+
+    <!-- Data Table -->
+    <el-card shadow="hover">
+      <template #header>
+        <div class="flex flex-wrap items-center justify-between gap-4">
+          <h3 class="text-lg font-medium">电表历史数据</h3>
+
+          <div class="flex flex-wrap items-center gap-4">
+            <el-input
+                v-model="searchQuery"
+                placeholder="搜索表号"
+                clearable
+                @keyup.enter="handleSearch"
+                class="w-60"
+            >
+              <template #append>
+                <el-button @click="handleSearch">
+                  <i class="el-icon-search"></i>
+                </el-button>
+              </template>
+            </el-input>
+
+            <el-date-picker
+                v-model="dateRange"
+                type="daterange"
+                range-separator="至"
+                start-placeholder="开始日期"
+                end-placeholder="结束日期"
+                @change="handleSearch"
+            />
+
+            <el-button type="primary" @click="refreshData" :loading="tableLoading">
+              <i class="el-icon-refresh mr-1"></i> 刷新
+            </el-button>
+          </div>
+        </div>
+      </template>
+
+      <el-table
+          :data="electricityRecords"
+          stripe
+          border
+          style="width: 100%"
+          v-loading="tableLoading"
+          height="500"
+      >
+        <el-table-column prop="tableNumber" label="表号" min-width="120"/>
+        <el-table-column label="电压 (V)" min-width="220">
+          <template #default="scope">
+            <div class="grid grid-cols-3 gap-2">
+              <div>
+                <span class="text-xs text-gray-500">A相:</span>
+                <span>{{ parseFloat(scope.row.aVoltage || 0).toFixed(2) }}</span>
+              </div>
+              <div>
+                <span class="text-xs text-gray-500">B相:</span>
+                <span>{{ parseFloat(scope.row.bVoltage || 0).toFixed(2) }}</span>
+              </div>
+              <div>
+                <span class="text-xs text-gray-500">C相:</span>
+                <span>{{ parseFloat(scope.row.cVoltage || 0).toFixed(2) }}</span>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="电流 (A)" min-width="220">
+          <template #default="scope">
+            <div class="grid grid-cols-3 gap-2">
+              <div>
+                <span class="text-xs text-gray-500">A相:</span>
+                <span>{{ parseFloat(scope.row.aCurrent || 0).toFixed(2) }}</span>
+              </div>
+              <div>
+                <span class="text-xs text-gray-500">B相:</span>
+                <span>{{ parseFloat(scope.row.bCurrent || 0).toFixed(2) }}</span>
+              </div>
+              <div>
+                <span class="text-xs text-gray-500">C相:</span>
+                <span>{{ parseFloat(scope.row.cCurrent || 0).toFixed(2) }}</span>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="有功功率 (kW)" min-width="220">
+          <template #default="scope">
+            <div>
+              <div>
+                <span class="text-xs text-gray-500">总:</span>
+                <span>{{ parseFloat(scope.row.totalActivePower || 0).toFixed(2) }}</span>
+              </div>
+              <div class="grid grid-cols-3 gap-2 mt-1">
+                <div>
+                  <span class="text-xs text-gray-500">A相:</span>
+                  <span>{{ parseFloat(scope.row.aActivePower || 0).toFixed(2) }}</span>
+                </div>
+                <div>
+                  <span class="text-xs text-gray-500">B相:</span>
+                  <span>{{ parseFloat(scope.row.bActivePower || 0).toFixed(2) }}</span>
+                </div>
+                <div>
+                  <span class="text-xs text-gray-500">C相:</span>
+                  <span>{{ parseFloat(scope.row.cActivePower || 0).toFixed(2) }}</span>
+                </div>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column label="功率因数" min-width="220">
+          <template #default="scope">
+            <div>
+              <div>
+                <span class="text-xs text-gray-500">总:</span>
+                <span>{{ parseFloat(scope.row.totalPower || 0).toFixed(2) }}</span>
+              </div>
+              <div class="grid grid-cols-3 gap-2 mt-1">
+                <div>
+                  <span class="text-xs text-gray-500">A相:</span>
+                  <span>{{ parseFloat(scope.row.aPower || 0).toFixed(2) }}</span>
+                </div>
+                <div>
+                  <span class="text-xs text-gray-500">B相:</span>
+                  <span>{{ parseFloat(scope.row.bPower || 0).toFixed(2) }}</span>
+                </div>
+                <div>
+                  <span class="text-xs text-gray-500">C相:</span>
+                  <span>{{ parseFloat(scope.row.cPower || 0).toFixed(2) }}</span>
+                </div>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="totalElectricity" label="总用电量 (kWh)" min-width="120"/>
+        <el-table-column label="分时电量 (kWh)" min-width="220">
+          <template #default="scope">
+            <div class="grid grid-cols-2 gap-2">
+              <div>
+                <span class="text-xs text-gray-500">尖峰:</span>
+                <span>{{ parseFloat(scope.row.sharpActivePowerConsumption || 0).toFixed(2) }}</span>
+              </div>
+              <div>
+                <span class="text-xs text-gray-500">峰:</span>
+                <span>{{ parseFloat(scope.row.peakActivePowerConsumption || 0).toFixed(2) }}</span>
+              </div>
+              <div>
+                <span class="text-xs text-gray-500">平:</span>
+                <span>{{ parseFloat(scope.row.averageActivePowerConsumption || 0).toFixed(2) }}</span>
+              </div>
+              <div>
+                <span class="text-xs text-gray-500">谷:</span>
+                <span>{{ parseFloat(scope.row.valleyActivePower || 0).toFixed(2) }}</span>
+              </div>
+            </div>
+          </template>
+        </el-table-column>
+        <el-table-column prop="state" label="状态" min-width="100"/>
+        <el-table-column label="创建时间" min-width="180">
+          <template #default="scope">
+            {{ formatDate(scope.row.createTime) }}
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <div class="flex justify-center mt-4">
+        <el-pagination
+            background
+            layout="total, sizes, prev, pager, next, jumper"
+            :total="totalRecords"
+            :page-size="pageSize"
+            :current-page="currentPage"
+            :page-sizes="[10, 20, 50, 100]"
+            @size-change="handleSizeChange"
+            @current-change="handlePageChange"
+        />
+      </div>
+    </el-card>
+  </div>
+</template>
+
+<style scoped lang="scss">
+.electricity-dashboard {
+  .el-card {
+    transition: all 0.3s;
+
+    &:hover {
+      transform: translateY(-5px);
+    }
+  }
+
+  /* Custom scrollbar for tables */
+  :deep(.el-table__body-wrapper) {
+    &::-webkit-scrollbar {
+      width: 6px;
+      height: 6px;
+    }
+
+    &::-webkit-scrollbar-thumb {
+      background: #dcdfe6;
+      border-radius: 3px;
+    }
+
+    &::-webkit-scrollbar-track {
+      background: #f5f7fa;
+    }
+  }
+}
+</style>
+

+ 88 - 0
src/views/device/electricity/index.vue

@@ -0,0 +1,88 @@
+<template>
+  <div>
+    <el-container class="h-90vh">
+      <el-aside class="bg-gray-100 w-60">
+        <el-menu
+            :default-active="activeMenu"
+            mode="vertical"
+            @select="handleMenuSelect"
+            class="h-full border-r-0"
+        >
+          <el-menu-item index="deviceList">
+            <template #title>
+              <List class="mr-2" />
+              设备列表
+            </template>
+          </el-menu-item>
+        </el-menu>
+      </el-aside>
+      <el-main class="p-0">
+        <component :is="currentComponent" @show-details="showElectricityDetails"></component>
+      </el-main>
+    </el-container>
+
+    <!-- Full-screen dialog for electricity details -->
+    <el-dialog
+        v-model="dialogVisible"
+        :fullscreen="true"
+        :show-close="true"
+        :destroy-on-close="false"
+        class="electricity-detail-dialog"
+    >
+      <template #header>
+        <div class="flex items-center justify-between w-full">
+          <h3 class="text-xl font-medium">电表详情</h3>
+          <el-button type="primary" plain @click="dialogVisible = false">
+            <X class="mr-1" />
+            关闭
+          </el-button>
+        </div>
+      </template>
+      <electricityDetail :table-number="selectedTableNumber" v-if="dialogVisible" />
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {ref} from 'vue';
+import electricityDetail from './components/electricity-detail.vue';
+import deviceList from './components/device-list.vue';
+import {List, X} from 'lucide-vue-next';
+
+// 定义当前激活的菜单索引
+const activeMenu = ref('deviceList');
+// 定义当前要显示的组件
+const currentComponent = ref(deviceList);
+
+// 对话框控制
+const dialogVisible = ref(false);
+const selectedTableNumber = ref('');
+
+// 处理菜单选择事件
+const handleMenuSelect = (index) => {
+  activeMenu.value = index;
+  if (index === 'deviceList') {
+    currentComponent.value = deviceList;
+  }
+};
+
+// 显示电表详情
+const showElectricityDetails = (tableNumber) => {
+  selectedTableNumber.value = tableNumber;
+  dialogVisible.value = true;
+};
+</script>
+
+<style scoped>
+.electricity-detail-dialog :deep(.el-dialog__body) {
+  padding: 0;
+  height: calc(100vh - 60px);
+  overflow-y: auto;
+}
+
+.electricity-detail-dialog :deep(.el-dialog__header) {
+  margin: 0;
+  padding: 16px 20px;
+  border-bottom: 1px solid #e4e7ed;
+}
+</style>