From 67c9118cffb7d7407668bbbad4c64f9aaf21ba0d Mon Sep 17 00:00:00 2001
From: huang <1532065656@qq.com>
Date: 星期二, 18 三月 2025 10:06:37 +0800
Subject: [PATCH] 看板能耗管理

---
 JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/service/EnergyConsumptionService.java          |    8 
 JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/service/impl/EnergyConsumptionServiceImpl.java |   29 ++
 UI-Project/src/views/KanbanDisplay/kanbanDisplay.vue                                                                             |  129 +++++++++++-
 JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/controller/EnergyConsumptionController.java    |   89 ++++++++
 UI-Project/src/views/Energy/energyConsumption.vue                                                                                |  254 +++++++++++++++++++++++++
 JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/entity/EnergyConsumption.java                  |   32 +++
 JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/mapper/EnergyConsumptionMapper.java            |    9 
 7 files changed, 536 insertions(+), 14 deletions(-)

diff --git a/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/controller/EnergyConsumptionController.java b/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/controller/EnergyConsumptionController.java
new file mode 100644
index 0000000..a72a062
--- /dev/null
+++ b/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/controller/EnergyConsumptionController.java
@@ -0,0 +1,89 @@
+package com.mes.energy.controller;
+
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.mes.energy.entity.EnergyConsumption;
+import com.mes.energy.service.EnergyConsumptionService;
+import com.mes.utils.Result;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDateTime;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@Api(tags = "鑳芥簮绠$悊")
+@RestController
+@RequestMapping("/energy/consumption")
+public class EnergyConsumptionController {
+
+    @Autowired
+    private EnergyConsumptionService energyService;
+
+    @ApiOperation("鑾峰彇鑳借�楁暟鎹垪琛�")
+    @PostMapping("/listEnergy")
+    @ResponseBody
+    public Result list(@RequestBody Map<String, Object> params) {
+        Integer page = params.get("page") == null ? 1 : (Integer) params.get("page");
+        Integer pageSize = params.get("pageSize") == null ? 10 : (Integer) params.get("pageSize");
+        
+        Page<EnergyConsumption> pageData = energyService.page(
+            new Page<>(page, pageSize),
+            new QueryWrapper<EnergyConsumption>().orderByDesc("record_date")
+        );
+        
+        return Result.build(200, "鏌ヨ鎴愬姛", pageData);
+    }
+
+    @ApiOperation("娣诲姞鑳借�楁暟鎹�")
+    @PostMapping("/addEnergy")
+    @ResponseBody
+    public Result add(@RequestBody EnergyConsumption consumption) {
+        consumption.setCreatedAt(new Date());
+        consumption.setUpdatedAt(new Date());
+        int count = energyService.save(consumption) ? 1 : 0;
+        String message = count > 0 ? "娣诲姞鎴愬姛锛�" + count : "娣诲姞澶辫触!";
+        return Result.build(200, message, count);
+    }
+
+    @ApiOperation("淇敼鑳借�楁暟鎹�")
+    @PostMapping("/updateEnergy")
+    @ResponseBody
+    public Result update(@RequestBody EnergyConsumption consumption) {
+        consumption.setUpdatedAt(new Date());
+        int count = energyService.updateById(consumption) ? 1 : 0;
+        String message = count > 0 ? "淇敼鎴愬姛锛�" + count : "淇敼澶辫触!";
+        energyService.notifyEnergyUpdate(consumption);
+        return Result.build(200, message, count);
+    }
+
+    @ApiOperation("鍒犻櫎鑳借�楁暟鎹�")
+    @PostMapping("/deleteEnergy")
+    @ResponseBody
+    public Result delete(@RequestBody EnergyConsumption consumption) {
+        int count = energyService.removeById(consumption.getId()) ? 1 : 0;
+        String message = count > 0 ? "鍒犻櫎鎴愬姛锛�" + count : "鍒犻櫎澶辫触!";
+        return Result.build(200, message, count);
+    }
+
+    @ApiOperation("鑾峰彇鍥捐〃鏁版嵁")
+    @PostMapping("/chartEnergy")
+    @ResponseBody
+    public Result getChartData() {
+        List<EnergyConsumption> data = energyService.list(
+            new QueryWrapper<EnergyConsumption>()
+                .orderByAsc("record_date")
+                .last("limit 30")
+        );
+
+        Map<String, Object> result = new HashMap<>();
+        result.put("actual", data);
+        result.put("planned", null);
+
+        return Result.build(200, "鏌ヨ鎴愬姛", result);
+    }
+} 
\ No newline at end of file
diff --git a/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/entity/EnergyConsumption.java b/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/entity/EnergyConsumption.java
new file mode 100644
index 0000000..8568b56
--- /dev/null
+++ b/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/entity/EnergyConsumption.java
@@ -0,0 +1,32 @@
+package com.mes.energy.entity;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDate;
+import java.time.LocalDateTime;
+import java.util.Date;
+
+@Data
+@TableName("energy_consumption")
+public class EnergyConsumption {
+
+    @TableId(type = IdType.AUTO)
+    private Integer id;
+
+    @JsonFormat(pattern = "yyyy-MM-dd", timezone = "GMT+8")
+    private Date recordDate;
+
+    private Double energyValue;
+
+    private Integer userId;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date createdAt;
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date updatedAt;
+} 
\ No newline at end of file
diff --git a/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/mapper/EnergyConsumptionMapper.java b/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/mapper/EnergyConsumptionMapper.java
new file mode 100644
index 0000000..1477162
--- /dev/null
+++ b/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/mapper/EnergyConsumptionMapper.java
@@ -0,0 +1,9 @@
+package com.mes.energy.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.mes.energy.entity.EnergyConsumption;
+import org.apache.ibatis.annotations.Mapper;
+
+@Mapper
+public interface EnergyConsumptionMapper extends BaseMapper<EnergyConsumption> {
+} 
\ No newline at end of file
diff --git a/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/service/EnergyConsumptionService.java b/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/service/EnergyConsumptionService.java
new file mode 100644
index 0000000..862ce7c
--- /dev/null
+++ b/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/service/EnergyConsumptionService.java
@@ -0,0 +1,8 @@
+package com.mes.energy.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.mes.energy.entity.EnergyConsumption;
+
+public interface EnergyConsumptionService extends IService<EnergyConsumption> {
+    void notifyEnergyUpdate(EnergyConsumption consumption);
+} 
\ No newline at end of file
diff --git a/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/service/impl/EnergyConsumptionServiceImpl.java b/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/service/impl/EnergyConsumptionServiceImpl.java
new file mode 100644
index 0000000..a76bc5f
--- /dev/null
+++ b/JiuMuMesParent/moduleService/DeviceInteractionModule/src/main/java/com/mes/energy/service/impl/EnergyConsumptionServiceImpl.java
@@ -0,0 +1,29 @@
+package com.mes.energy.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.mes.energy.entity.EnergyConsumption;
+import com.mes.energy.mapper.EnergyConsumptionMapper;
+import com.mes.energy.service.EnergyConsumptionService;
+import com.mes.tools.WebSocketServer;
+import cn.hutool.json.JSONObject;
+import org.springframework.stereotype.Service;
+
+import java.util.ArrayList;
+
+@Service
+public class EnergyConsumptionServiceImpl extends ServiceImpl<EnergyConsumptionMapper, EnergyConsumption> implements EnergyConsumptionService {
+
+    @Override
+    public void notifyEnergyUpdate(EnergyConsumption consumption) {
+        JSONObject message = new JSONObject();
+        message.set("type", "energy_update");
+        message.set("data", consumption);
+
+        ArrayList<WebSocketServer> servers = WebSocketServer.sessionMap.get("energy");
+        if (servers != null) {
+            for (WebSocketServer server : servers) {
+                server.sendMessage(message.toString());
+            }
+        }
+    }
+} 
\ No newline at end of file
diff --git a/UI-Project/src/views/Energy/energyConsumption.vue b/UI-Project/src/views/Energy/energyConsumption.vue
new file mode 100644
index 0000000..f1fa606
--- /dev/null
+++ b/UI-Project/src/views/Energy/energyConsumption.vue
@@ -0,0 +1,254 @@
+<script setup>
+import { ref } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import request from '@/utils/request'
+
+// 鏁版嵁瀹氫箟
+const formData = ref({
+  recordDate: new Date().toISOString().slice(0, 10),
+  energyValue: null
+})
+const loading = ref(false)
+const energyData = ref([])
+
+// 鏃ユ湡鏍煎紡鍖�
+const formatDate = (dateStr) => {
+  const date = new Date(dateStr)
+  return date.toISOString().slice(0, 10)
+}
+
+// 妫�鏌ユ棩鏈熸槸鍚﹂噸澶�
+const checkDateExists = (date, excludeId = null) => {
+  return energyData.value.some(item => 
+    item.recordDate === date && (!excludeId || item.id !== excludeId)
+  )
+}
+
+// API 璇锋眰
+const loadDataFromApi = async () => {
+  loading.value = true
+  try {
+    const { data } = await request({
+      url: '/deviceInteraction/energy/consumption/listEnergy',
+      method: 'post',
+      data: { page: 1, pageSize: 10 }
+    })
+    if (data?.records) {
+      energyData.value = data.records.map(item => ({
+        ...item,
+        editing: false,
+        originalData: { ...item }
+      }))
+    }
+  } catch (error) {
+    ElMessage.error('鏁版嵁鍔犺浇澶辫触')
+    console.error(error)
+  } finally {
+    loading.value = false
+  }
+}
+
+// 琛ㄥ崟鎿嶄綔
+const handleSubmit = async () => {
+  if (!formData.value.recordDate || formData.value.energyValue === null) {
+    ElMessage.error('璇峰~鍐欏畬鏁翠俊鎭�')
+    return
+  }
+
+  if (checkDateExists(formData.value.recordDate)) {
+    ElMessage.error('璇ユ棩鏈熷凡瀛樺湪鑳借�楄褰�')
+    return
+  }
+
+  try {
+    await request({
+      url: '/deviceInteraction/energy/consumption/addEnergy',
+      method: 'post',
+      data: formData.value
+    })
+    ElMessage.success('娣诲姞鎴愬姛')
+    await loadDataFromApi()
+    resetForm()
+  } catch (error) {
+    ElMessage.error('娣诲姞澶辫触')
+    console.error(error)
+  }
+}
+
+const resetForm = () => {
+  formData.value = {
+    recordDate: new Date().toISOString().slice(0, 10),
+    energyValue: null
+  }
+}
+
+// 琛ㄦ牸鎿嶄綔
+const handleRowEdit = async (index, row) => {
+  if (!row.editing) {
+    row.editing = true
+    return
+  }
+
+  if (!row.recordDate || row.energyValue === null) {
+    ElMessage.error('璇峰~鍐欏畬鏁翠俊鎭�')
+    return
+  }
+
+  if (checkDateExists(row.recordDate, row.id)) {
+    ElMessage.error('璇ユ棩鏈熷凡瀛樺湪鑳借�楄褰�')
+    return
+  }
+
+  try {
+    await request({
+      url: '/deviceInteraction/energy/consumption/updateEnergy',
+      method: 'post',
+      data: {
+        id: row.id,
+        recordDate: row.recordDate,
+        energyValue: row.energyValue
+      }
+    })
+    row.editing = false
+    row.originalData = { ...row }
+    ElMessage.success('淇敼鎴愬姛')
+  } catch (error) {
+    ElMessage.error('淇敼澶辫触')
+    console.error(error)
+  }
+}
+
+const cancelEdit = (index, row) => {
+  Object.assign(row, row.originalData)
+  row.editing = false
+}
+
+const handleDelete = async (index) => {
+  try {
+    await ElMessageBox.confirm('纭鍒犻櫎璇ヨ褰曪紵', '璀﹀憡', {
+      type: 'warning'
+    })
+    const id = energyData.value[index].id
+    await request({
+      url: '/deviceInteraction/energy/consumption/deleteEnergy',
+      method: 'post',
+      data: { id }
+    })
+    energyData.value.splice(index, 1)
+    ElMessage.success('鍒犻櫎鎴愬姛')
+  } catch (error) {
+    if (error !== 'cancel') {
+      ElMessage.error('鍒犻櫎澶辫触')
+      console.error(error)
+    }
+  }
+}
+
+// 鍒濆鍖�
+loadDataFromApi()
+</script>
+
+<template>
+  <el-container>
+    <el-header class="header" style="height: auto; margin:20px 0 -10px ; ">
+      <el-form :inline="true" :model="formData" label-width="80px" class="form-container">
+        <el-form-item label="鏃ユ湡" prop="recordDate">
+          <el-date-picker
+            v-model="formData.recordDate"
+            type="date"
+            value-format="YYYY-MM-DD"
+            placeholder="閫夋嫨鏃ユ湡"
+            :default-value="new Date()"
+            style="width: 200px"
+          />
+        </el-form-item>
+        <el-form-item label="鑳借�楀��" prop="energyValue">
+          <el-input-number
+            v-model="formData.energyValue"
+            :precision="2"
+            :step="0.1"
+            :min="0"
+            controls-position="right"
+            style="width: 200px"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="handleSubmit">鎻愪氦</el-button>
+          <el-button @click="resetForm">閲嶇疆</el-button>
+        </el-form-item>
+      </el-form>
+    </el-header>
+    
+    <el-main class="main">
+      <div class="table-container">
+        <el-table 
+          :data="energyData" 
+          border 
+          style="width: 100%" 
+          height="500"
+          v-loading="loading"
+        >
+          <el-table-column prop="recordDate" label="鏃ユ湡" width="180">
+            <template #default="scope">
+              <el-date-picker
+                v-if="scope.row.editing"
+                v-model="scope.row.recordDate"
+                type="date"
+                value-format="YYYY-MM-DD"
+                placeholder="閫夋嫨鏃ユ湡"
+                style="width: 140px"
+              />
+              <span v-else>{{ formatDate(scope.row.recordDate) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column prop="energyValue" label="鑳借�楀��" width="180">
+            <template #default="scope">
+              <el-input-number
+                v-if="scope.row.editing"
+                v-model="scope.row.energyValue"
+                :precision="2"
+                :step="0.1"
+                :min="0"
+                controls-position="right"
+                style="width: 140px"
+              />
+              <span v-else>{{ scope.row.energyValue }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="鎿嶄綔" width="200" fixed="right">
+            <template #default="scope">
+              <el-button-group>
+                <el-button 
+                  size="small" 
+                  :type="scope.row.editing ? 'success' : 'primary'"
+                  @click="handleRowEdit(scope.$index, scope.row)"
+                >
+                  {{ scope.row.editing ? '淇濆瓨' : '缂栬緫' }}
+                </el-button>
+                <el-button 
+                  v-if="scope.row.editing" 
+                  size="small"
+                  @click="cancelEdit(scope.$index, scope.row)"
+                >
+                  鍙栨秷
+                </el-button>
+                <el-button 
+                  v-else 
+                  size="small" 
+                  type="danger" 
+                  @click="handleDelete(scope.$index)"
+                >
+                  鍒犻櫎
+                </el-button>
+              </el-button-group>
+            </template>
+          </el-table-column>
+        </el-table>
+      </div>
+    </el-main>
+  </el-container>
+</template>
+
+<style scoped>
+
+</style>
\ No newline at end of file
diff --git a/UI-Project/src/views/KanbanDisplay/kanbanDisplay.vue b/UI-Project/src/views/KanbanDisplay/kanbanDisplay.vue
index 2f445e8..be17efd 100644
--- a/UI-Project/src/views/KanbanDisplay/kanbanDisplay.vue
+++ b/UI-Project/src/views/KanbanDisplay/kanbanDisplay.vue
@@ -1,9 +1,111 @@
 <!--  绌虹櫧椤�  -->
-
-
+ 
+ 
 <script setup>
-import { ref, computed } from 'vue'
-import * as echarts from 'echarts'; 
+import { ref, onMounted } from 'vue'
+import * as echarts from 'echarts'
+import request from '@/utils/request'
+
+const energyData = ref([])
+
+// 鑾峰彇鑳借�楁暟鎹�
+const loadEnergyData = async () => {
+  try {
+    const res = await request({
+      url: '/deviceInteraction/energy/consumption/chartEnergy',
+      method: 'post'
+    })
+    if (res.code === 200) {
+      energyData.value = res.data.actual || [];
+      updateEnergyChart()
+    }
+  } catch (error) {
+    console.error('鑾峰彇鑳借�楁暟鎹け璐�:', error)
+  }
+}
+
+const draw = (name, Option) => {
+  var myChart = echarts.init(document.getElementById(name));
+  myChart.setOption(Option);
+}
+
+const drawDay = (name, Option) => {
+  Option.title.text = "鑳借�楃鐞�"; 
+  draw(name, Option);
+}
+
+// 鏇存柊鑳借�楀浘琛�
+const updateEnergyChart = () => {
+  // 鎸夋棩鏈熸帓搴忓苟鏍煎紡鍖栨棩鏈�
+  const sortedData = [...energyData.value].sort((a, b) => 
+    new Date(a.recordDate) - new Date(b.recordDate)
+  ).map(item => {
+    const date = new Date(item.recordDate);
+    return {
+      ...item,
+      recordDate: `${date.getMonth() + 1}-${date.getDate().toString().padStart(2, '0')}`
+    };
+  });
+
+  const energyoption = {
+    title: {
+      text: '鑳借�楃鐞�'
+    },
+    tooltip: {
+      trigger: 'axis',
+      axisPointer: {
+        type: 'cross',
+        label: {
+              backgroundColor: '#6a7985'
+        }
+      }
+    },
+    legend: {
+      data: ['鑳借�楀��']
+    },
+    toolbox: {
+      feature: {
+        saveAsImage: {}
+      }
+    },
+    grid: {
+      left: '3%',
+      right: '4%',
+      bottom: '3%',
+      containLabel: true
+    },
+    xAxis: [
+      {
+        type: 'category',
+        boundaryGap: false,
+        data: sortedData.map(item => item.recordDate)
+      }
+    ],
+    yAxis: [
+      {
+        type: 'value'
+      }
+    ],
+    series: [
+      {
+        name: '鑳借�楀��',
+        type: 'line',
+        areaStyle: {},
+        label: {
+          show: true,
+          position: 'top'
+        },
+        data: sortedData.map(item => item.energyValue)
+      }
+    ]
+  }
+  
+  drawDay('drawLineChart_day71', energyoption);
+}
+
+onMounted(() => {
+  loadEnergyData()
+})
 </script>
 <script>
 export default {
@@ -81,7 +183,7 @@
               100, 122, 101]
           }
         ]
-
+ 
       }
      this.drawDay('drawLineChart_day11',OptionDay, 1);
     // this.drawLineChart('drawLineChart_day11', 1);
@@ -93,17 +195,16 @@
     //this.drawBarchart('drawBarchart', 1);
   },
   methods: {
-    //鏂规硶
-    draw(name, Option,data) {
+    draw(name, Option, data) {
       var myChart = echarts.init(document.getElementById(name));
       myChart.setOption(Option);
     },
-    drawDay(name, Option,data) {
+    drawDay(name, Option, data) {
       Option.title.text="鏃ョ湅鏉�";
       console.log(Option);
-      this.draw(name, Option,data);
+      this.draw(name, Option, data);
     },
-    drawLineChart(name, Option,data) {
+    drawLineChart(name, Option, data) {
       console.log(name);
       console.log(data);
       var myChart = echarts.init(document.getElementById(name));
@@ -152,14 +253,14 @@
             }
           }
         ]
-
-
+ 
+ 
       });
     }
   }
 }
 </script>
-
+ 
 <template>
   <div>
     <div style="font-size: 30px;height: 70px;line-height: 70px;border: 1px solid #ccc;text-align: center;">
@@ -184,7 +285,7 @@
         <div id="drawLineChart_day71" style="height: 33.3%;width: 100%;border: 1px solid #ccc;">鑳借�楃鐞�-鎸夊ぉ鏄剧ず锛堟墜杈擄級</div>
       </div>
     </div>
-
+ 
     <!-- <div style="width:33% ;height: 880px;border: 1px solid #ccc;">
       <div id="drawLineChart_day1" style="height: 300px;width: 25%;border: 1px solid #ccc;float: left;"></div>
       <div id="drawLineChart_day2" style="height: 300px;width: 25%;border: 1px solid #ccc;float: left;"></div>

--
Gitblit v1.8.0