learshaw před 2 měsíci
rodič
revize
583d345483
30 změnil soubory, kde provedl 6618 přidání a 2038 odebrání
  1. 0 77
      ems-ui-cloud/src/api/mgr/elecStoreH.js
  2. 124 0
      ems-ui-cloud/src/api/mgr/elecStoreHour.js
  3. 85 0
      ems-ui-cloud/src/api/mgr/elecStoreIndex.js
  4. 0 69
      ems-ui-cloud/src/api/mgr/pgSupplyH.js
  5. 246 0
      ems-ui-cloud/src/api/mgr/pvSupply.js
  6. 31 0
      ems-ui-cloud/src/api/prediction/predictionProd.js
  7. 0 10
      ems-ui-cloud/src/layout/components/Navbar.vue
  8. 34 12
      ems-ui-cloud/src/views/adapter/devc/index.vue
  9. 1 1
      ems-ui-cloud/src/views/adapter/zm/index.vue
  10. 34 18
      ems-ui-cloud/src/views/analysis/power/consume.vue
  11. 1522 283
      ems-ui-cloud/src/views/analysis/power/prod.vue
  12. 783 376
      ems-ui-cloud/src/views/analysis/power/save.vue
  13. 839 401
      ems-ui-cloud/src/views/analysis/power/store.vue
  14. 2 2
      ems-ui-cloud/src/views/analysis/report/statement-prod.vue
  15. 63 21
      ems-ui-cloud/src/views/largeScreen/dialog/pv-real.vue
  16. 61 56
      ems-ui-cloud/src/views/largeScreen/dialog/storage-real.vue
  17. 152 64
      ems-ui-cloud/src/views/largeScreen/home/left.vue
  18. 274 24
      ems-ui-cloud/src/views/largeScreen/home/right.vue
  19. 258 109
      ems-ui-cloud/src/views/largeScreen/net/left.vue
  20. 175 68
      ems-ui-cloud/src/views/largeScreen/net/right.vue
  21. 196 84
      ems-ui-cloud/src/views/largeScreen/soc/left.vue
  22. 119 50
      ems-ui-cloud/src/views/largeScreen/soc/right.vue
  23. 99 106
      ems-ui-cloud/src/views/largeScreen/source/left.vue
  24. 123 38
      ems-ui-cloud/src/views/largeScreen/source/right.vue
  25. 4 3
      ems-ui-cloud/src/views/mgr/powergrid.vue
  26. 715 8
      ems-ui-cloud/src/views/mgr/powerstore.vue
  27. 3 3
      ems-ui-cloud/src/views/mgr/strategy/editor.vue
  28. 3 3
      ems-ui-cloud/src/views/mgr/strategy/index.vue
  29. 3 3
      ems-ui-cloud/src/views/mgr/strategy/template.vue
  30. 669 149
      ems-ui-cloud/src/views/prediction/prod.vue

+ 0 - 77
ems-ui-cloud/src/api/mgr/elecStoreH.js

@@ -1,77 +0,0 @@
-import request from '@/utils/request';
-
-// 查询储能计量-小时列表
-export function listElecStoreH(query) {
-  return request({
-    url: '/ems/elec/store/hour/list',
-    method: 'get',
-    params: query
-  });
-}
-
-// 查询储能总览
-export function dayStatistics(query) {
-  return request({
-    url: '/ems/elec/store/day/statistics',
-    method: 'get',
-    params: query
-  });
-}
-
-// 查询储能计量-小时详细
-export function getElecStoreH(id) {
-  return request({
-    url: '/ems/elec/store/hour/' + id,
-    method: 'get'
-  });
-}
-
-export function getSumHStorage(param) {
-  return request({
-    url: '/ems/elec/store/sum/h/storage',
-    params: param,
-    method: 'get'
-  });
-}
-
-export function getSumRealTimeStorage(param) {
-  return request({
-    url: '/ems/elec/store/sum/realtime/storage',
-    params: param,
-    method: 'get'
-  });
-}
-
-// 新增储能计量-小时
-export function addElecStoreH(data) {
-  return request({
-    url: '/ems/elec/store/hour',
-    method: 'post',
-    data: data
-  });
-}
-
-// 修改储能计量-小时
-export function updateElecStoreH(data) {
-  return request({
-    url: '/ems/elec/store/hour',
-    method: 'put',
-    data: data
-  });
-}
-
-// 删除储能计量-小时
-export function delElecStoreH(id) {
-  return request({
-    url: '/ems/elec/store/hour/' + id,
-    method: 'delete'
-  });
-}
-
-export function getStoreDayList(query) {
-  return request({
-    url: '/ems/elec/store/day/list',
-    method: 'get',
-    params: query
-  });
-}

+ 124 - 0
ems-ui-cloud/src/api/mgr/elecStoreHour.js

@@ -0,0 +1,124 @@
+import request from '@/utils/request'
+
+/**
+ * 储能计量API - 对应 ElecStoreController
+ * 数据表: adm_ems_elec_store_h (小时充放电数据)
+ */
+
+/**
+ * 查询储能计量-小时列表(分页)
+ * @param {Object} query - 查询参数
+ * @param {string} query.areaCode - 园区代码
+ * @param {string} query.facsCode - 设施代码
+ * @param {string} query.date - 日期
+ * @param {string} query.startRecTime - 开始时间
+ * @param {string} query.endRecTime - 结束时间
+ */
+export function listElecStoreHour(query) {
+  return request({
+    url: '/ems/elec/store/hour/list',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 查询储能计量-日列表
+ * @param {Object} query - 查询参数
+ * @param {string} query.areaCode - 园区代码
+ * @param {string} query.facsSubCategory - 设施子类
+ * @param {string} query.startRecTime - 开始时间
+ * @param {string} query.endRecTime - 结束时间
+ */
+export function listElecStoreDay(query) {
+  return request({
+    url: '/ems/elec/store/day/list',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 获取储能计量-日统计分析
+ * @param {string} date - 日期 yyyy-MM-dd
+ */
+export function getDayStatistics(date) {
+  return request({
+    url: '/ems/elec/store/day/statistics',
+    method: 'get',
+    params: { date }
+  })
+}
+
+/**
+ * 查询储能计量-按日期范围汇总
+ * @param {Object} query - 查询参数
+ * @param {string} query.areaCode - 园区代码
+ * @param {string} query.startRecTime - 开始时间(月份 yyyy-MM)
+ */
+export function getStoreHByDataRange(query) {
+  return request({
+    url: '/ems/elec/store/sum/h/storage',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 获取储能计量-小时详细信息
+ * @param {number} id - 主键
+ */
+export function getElecStoreHour(id) {
+  return request({
+    url: '/ems/elec/store/hour/' + id,
+    method: 'get'
+  })
+}
+
+/**
+ * 新增储能计量-小时
+ * @param {Object} data - 储能计量数据
+ */
+export function addElecStoreHour(data) {
+  return request({
+    url: '/ems/elec/store/hour',
+    method: 'post',
+    data: data
+  })
+}
+
+/**
+ * 修改储能计量-小时
+ * @param {Object} data - 储能计量数据
+ */
+export function updateElecStoreHour(data) {
+  return request({
+    url: '/ems/elec/store/hour',
+    method: 'put',
+    data: data
+  })
+}
+
+/**
+ * 删除储能计量-小时
+ * @param {Array} ids - 主键数组
+ */
+export function delElecStoreHour(ids) {
+  return request({
+    url: '/ems/elec/store/hour/' + ids,
+    method: 'delete'
+  })
+}
+
+/**
+ * 导出储能计量-小时列表
+ * @param {Object} query - 查询参数
+ */
+export function exportElecStoreHour(query) {
+  return request({
+    url: '/ems/elec/store/hour/export',
+    method: 'post',
+    params: query,
+    responseType: 'blob'
+  })
+}

+ 85 - 0
ems-ui-cloud/src/api/mgr/elecStoreIndex.js

@@ -0,0 +1,85 @@
+import request from '@/utils/request'
+
+/**
+ * 储能设施指标API - 对应 ElecStoreIndexController
+ * 数据表: adm_ems_elec_store_index (10min实时充放电功率和容量信息)
+ */
+
+/**
+ * 查询储能设施指标列表(分页)
+ * @param {Object} query - 查询参数
+ * @param {string} query.areaCode - 园区代码
+ * @param {string} query.facsCode - 设施代码
+ * @param {string} query.deviceCode - 设备代码
+ * @param {string} query.date - 日期 yyyy-MM-dd
+ * @param {string} query.startRecTime - 开始时间
+ * @param {string} query.endRecTime - 结束时间
+ */
+export function listElecStoreIndex(query) {
+  return request({
+    url: '/ems/elec/store/index/list',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 查询储能设施指标列表(不分页)
+ * @param {Object} query - 查询参数
+ */
+export function listAllElecStoreIndex(query) {
+  return request({
+    url: '/ems/elec/store/index/listAll',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 查询每个设备最新一条记录
+ * @param {Object} query - 查询参数
+ * @param {string} query.areaCode - 园区代码(可选)
+ * @param {string} query.facsCode - 设施代码(可选)
+ */
+export function getLatestByDevice(query) {
+  return request({
+    url: '/ems/elec/store/index/latest',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 新增储能设施指标
+ * @param {Object} data - 储能设施指标数据
+ */
+export function addElecStoreIndex(data) {
+  return request({
+    url: '/ems/elec/store/index',
+    method: 'post',
+    data: data
+  })
+}
+
+/**
+ * 修改储能设施指标
+ * @param {Object} data - 储能设施指标数据
+ */
+export function updateElecStoreIndex(data) {
+  return request({
+    url: '/ems/elec/store/index',
+    method: 'put',
+    data: data
+  })
+}
+
+/**
+ * 删除储能设施指标
+ * @param {Array} ids - 主键数组
+ */
+export function delElecStoreIndex(ids) {
+  return request({
+    url: '/ems/elec/store/index/' + ids,
+    method: 'delete'
+  })
+}

+ 0 - 69
ems-ui-cloud/src/api/mgr/pgSupplyH.js

@@ -42,72 +42,3 @@ export function delPgSupplyH(id) {
     method: 'delete'
     method: 'delete'
   })
   })
 }
 }
-
-// ==================== 光伏产能统计相关接口 ====================
-
-/**
- * 查询光伏供应计量(统一接口,支持日/月/年统计)
- * @param {Object} query - 查询参数
- * @param {String} query.areaCode - 区域代码
- * @param {String} query.timeDimension - 时间维度:day/month/year
- * @param {String} query.startRecTime - 开始时间
- * @param {String} query.endRecTime - 结束时间
- * @param {String} query.orderFlag - 排序方式:asc/desc
- * @param {Number} query.pageNum - 页码
- * @param {Number} query.pageSize - 每页数量
- */
-export function listPvSupplyH(query) {
-  return request({
-    url: '/ems/prod/pv/hour/list',
-    method: 'get',
-    params: query
-  })
-}
-
-/**
- * 查询发电量-按日统计(简化版)
- * @param {Object} query - 查询参数
- * @param {String} query.areaCode - 区域代码
- * @param {String} query.startRecTime - 开始日期 yyyy-MM-dd
- * @param {String} query.endRecTime - 结束日期 yyyy-MM-dd
- */
-export function listPvSupplyD(query) {
-  // ✅ 统一使用 hour/list 接口,通过 timeDimension 参数指定为日统计
-  return request({
-    url: '/ems/prod/pv/hour/list',
-    method: 'get',
-    params: {
-      ...query,
-      timeDimension: 'day',  // 指定为日统计
-      orderFlag: 'asc',      // 按日期升序
-      pageNum: 1,
-      pageSize: 1000         // 图表展示,获取所有数据
-    }
-  }).then(response => {
-    // ✅ 转换数据格式,适配原有的图表组件
-    if (response.code === 200 && response.rows) {
-      return {
-        code: 200,
-        data: response.rows.map(item => ({
-          date: item.statisticDate || item.statisticMonth || item.statisticYear,
-          genElecQuantity: item.genElecQuantity || 0,
-          useElecQuantity: item.useElecQuantity || 0,
-          upElecQuantity: item.upElecQuantity || 0,
-          upElecEarn: item.upElecEarn || 0
-        }))
-      }
-    }
-    return { code: 500, data: [] }
-  })
-}
-
-/**
- * 查询光伏产能汇总统计
- */
-export function getPvSupplySummary(query) {
-  return request({
-    url: '/ems/prod/pv/hour/summary',
-    method: 'get',
-    params: query
-  })
-}

+ 246 - 0
ems-ui-cloud/src/api/mgr/pvSupply.js

@@ -0,0 +1,246 @@
+/**
+ * 光伏产能统计API
+ * @description 光伏并网计量数据接口 - 基于 PvSupplyVO 模型
+ * @author claude
+ * @date 2025-01-29
+ */
+import request from '@/utils/request'
+
+// ==================== 核心接口 ====================
+
+/**
+ * 查询光伏产能统计列表(统一接口,支持时/日/月/年统计)
+ * @param {Object} query - 查询参数
+ * @param {String} query.areaCode - 区域代码,'-1'或空表示全部
+ * @param {String} query.timeDimension - 时间维度:hour/day/month/year
+ * @param {String} query.startRecTime - 开始时间 yyyy-MM-dd HH:mm:ss
+ * @param {String} query.endRecTime - 结束时间 yyyy-MM-dd HH:mm:ss
+ * @param {String} query.orderFlag - 排序方式:asc/desc,默认desc
+ * @param {Number} query.pageNum - 页码,默认1
+ * @param {Number} query.pageSize - 每页数量,默认10
+ * @returns {Promise} 返回分页数据 { code, rows, total, msg }
+ *
+ * 响应数据结构 (PvSupplyVO):
+ * {
+ *   areaCode: '区域代码',
+ *   areaName: '区域名称',
+ *   facsCode: '设施代码',
+ *   facsName: '设施名称',
+ *   statisticDate: '统计日期 yyyy-MM-dd',
+ *   statisticYear: '统计年份',
+ *   statisticMonth: '统计月份 yyyy-MM',
+ *   timeDimension: '时间维度 HOUR/DAY/MONTH/YEAR',
+ *   genElecQuantity: '总发电量(kWh)',
+ *   useElecQuantity: '自用电量(kWh)',
+ *   upElecQuantity: '上网电量(kWh)',
+ *   upElecEarn: '上网收益(元)',
+ *   timeIndex: '时间序列(1-24)',
+ *   statisticHour: '统计小时 HH:00'
+ * }
+ */
+export function listPvSupply(query) {
+  return request({
+    url: '/ems/prod/pv/hour/list',
+    method: 'get',
+    params: query
+  })
+}
+
+export function listPvSupplyReport(query) {
+  return request({
+    url: '/ems/prod/pv/hour/list',
+    method: 'get',
+    params: {
+      ...query,
+      groupByArea: true  // 报表场景:按区域分组
+    }
+  })
+}
+
+/**
+ * 查询光伏产能汇总统计
+ * 用于大屏产能情况展示(当日/本月/本年汇总)
+ * @param {Object} query - 查询参数
+ * @param {String} query.areaCode - 区域代码,'-1'或空表示全部区域
+ * @param {String} query.startRecTime - 开始时间 yyyy-MM-dd HH:mm:ss
+ * @param {String} query.endRecTime - 结束时间 yyyy-MM-dd HH:mm:ss
+ * @returns {Promise} 返回汇总数据 { code, data: PvSupplyVO }
+ *
+ * 返回字段说明:
+ * - genElecQuantity: 总发电量(kWh)
+ * - useElecQuantity: 自用电量(kWh)
+ * - upElecQuantity: 上网电量(kWh)
+ * - upElecEarn: 上网收益(元)
+ */
+export function getPvSupplySummary(query) {
+  return request({
+    url: '/ems/prod/pv/hour/summary',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 导出光伏产能统计数据
+ * @param {Object} query - 查询参数(同 listPvSupply)
+ * @returns {Promise} 返回文件流
+ */
+export function exportPvSupply(query) {
+  return request({
+    url: '/ems/prod/pv/hour/export',
+    method: 'post',
+    params: query,
+    responseType: 'blob'
+  })
+}
+
+/**
+ * 查询光伏产能分布数据(按区域分组,用于饼图)
+ * @param {Object} query - 查询参数
+ * @param {String} query.areaCode - 区域代码,'-1'或空表示查询所有区域
+ * @param {String} query.startRecTime - 开始时间
+ * @param {String} query.endRecTime - 结束时间
+ * @returns {Promise} 返回按区域分组的产能数据
+ */
+export function getPvSupplyDistribution(query) {
+  return request({
+    url: '/ems/prod/pv/hour/distribution',
+    method: 'get',
+    params: query
+  })
+}
+
+/**
+ * 查询光伏产能分布明细数据(按区域和日期分组,用于区域对比柱状图)
+ * @param {Object} query - 查询参数
+ * @param {String} query.areaCode - 区域代码,'-1'或空表示查询所有区域
+ * @param {String} query.startRecTime - 开始时间
+ * @param {String} query.endRecTime - 结束时间
+ * @returns {Promise} 返回按区域和日期分组的产能数据
+ */
+export function getPvSupplyDistributionDetail(query) {
+  return request({
+    url: '/ems/prod/pv/hour/distribution/detail',
+    method: 'get',
+    params: query
+  })
+}
+
+// ==================== 便捷封装方法 ====================
+
+/**
+ * 查询光伏产能-按小时统计
+ * @param {Object} query - 查询参数
+ */
+export function listPvSupplyHourly(query) {
+  return listPvSupply({
+    ...query,
+    timeDimension: 'hour',
+    orderFlag: query.orderFlag || 'asc'
+  })
+}
+
+/**
+ * 查询光伏产能-按日统计
+ * @param {Object} query - 查询参数
+ */
+export function listPvSupplyDaily(query) {
+  return listPvSupply({
+    ...query,
+    timeDimension: 'day',
+    orderFlag: query.orderFlag || 'asc'
+  })
+}
+
+/**
+ * 查询光伏产能-按月统计
+ * @param {Object} query - 查询参数
+ */
+export function listPvSupplyMonthly(query) {
+  return listPvSupply({
+    ...query,
+    timeDimension: 'month',
+    orderFlag: query.orderFlag || 'asc'
+  })
+}
+
+/**
+ * 查询光伏产能-按年统计
+ * @param {Object} query - 查询参数
+ */
+export function listPvSupplyYearly(query) {
+  return listPvSupply({
+    ...query,
+    timeDimension: 'year',
+    orderFlag: query.orderFlag || 'asc'
+  })
+}
+
+/**
+ * 获取图表数据(不分页,获取全部数据用于图表展示)
+ * @param {Object} query - 查询参数
+ */
+export function getPvSupplyChartData(query) {
+  return listPvSupply({
+    ...query,
+    pageNum: 1,
+    pageSize: 10000,
+    orderFlag: 'asc'
+  }).then(response => {
+    if (response.code === 200) {
+      return {
+        code: 200,
+        data: response.rows || []
+      }
+    }
+    return { code: response.code, data: [] }
+  })
+}
+
+// ==================== 大屏专用便捷方法 ====================
+
+/**
+ * 获取当日光伏产能汇总
+ * @param {String} areaCode - 区域代码
+ */
+export function getTodayPvSummary(areaCode) {
+  const today = new Date()
+  const dateStr = today.toISOString().split('T')[0]
+  return getPvSupplySummary({
+    areaCode: areaCode || '-1',
+    startRecTime: `${dateStr} 00:00:00`,
+    endRecTime: `${dateStr} 23:59:59`
+  })
+}
+
+/**
+ * 获取本月光伏产能汇总
+ * @param {String} areaCode - 区域代码
+ */
+export function getMonthPvSummary(areaCode) {
+  const today = new Date()
+  const year = today.getFullYear()
+  const month = String(today.getMonth() + 1).padStart(2, '0')
+  const day = String(today.getDate()).padStart(2, '0')
+  return getPvSupplySummary({
+    areaCode: areaCode || '-1',
+    startRecTime: `${year}-${month}-01 00:00:00`,
+    endRecTime: `${year}-${month}-${day} 23:59:59`
+  })
+}
+
+/**
+ * 获取本年光伏产能汇总
+ * @param {String} areaCode - 区域代码
+ */
+export function getYearPvSummary(areaCode) {
+  const today = new Date()
+  const year = today.getFullYear()
+  const month = String(today.getMonth() + 1).padStart(2, '0')
+  const day = String(today.getDate()).padStart(2, '0')
+  return getPvSupplySummary({
+    areaCode: areaCode || '-1',
+    startRecTime: `${year}-01-01 00:00:00`,
+    endRecTime: `${year}-${month}-${day} 23:59:59`
+  })
+}

+ 31 - 0
ems-ui-cloud/src/api/prediction/predictionProd.js

@@ -42,3 +42,34 @@ export function delPredictionProd(id) {
     method: 'delete'
     method: 'delete'
   })
   })
 }
 }
+
+/**
+ * 查询日期范围内的发电量汇总数据(按日期分组)
+ * 用于趋势图表展示
+ *
+ * @param {Object} query - 查询参数
+ * @param {string} query.areaCode - 区域代码(可选,不传或传-1表示全部)
+ * @param {string} query.startRecTime - 开始日期 yyyy-MM-dd
+ * @param {string} query.endRecTime - 结束日期 yyyy-MM-dd
+ * @returns {Promise} 返回按日期汇总的数据
+ *
+ * 返回数据格式:
+ * {
+ *   code: 200,
+ *   data: [
+ *     {
+ *       date: "2025-09-03",
+ *       elecProdQuantity: 813.2,    // 当天发电量汇总
+ *       upElecQuantity: 1004.0      // 当天上网电量汇总
+ *     },
+ *     ...
+ *   ]
+ * }
+ */
+export function calcElecProdForecastDateRange(query) {
+  return request({
+    url: '/ems/predictionProd/date/range',
+    method: 'get',
+    params: query
+  })
+}

+ 0 - 10
ems-ui-cloud/src/layout/components/Navbar.vue

@@ -7,16 +7,6 @@
 
 
     <div class="right-menu">
     <div class="right-menu">
       <template v-if="device!=='mobile'">
       <template v-if="device!=='mobile'">
-        <search id="header-search" class="right-menu-item" />
-
-        <el-tooltip content="源码地址" effect="dark" placement="bottom">
-          <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
-        </el-tooltip>
-
-        <el-tooltip content="文档地址" effect="dark" placement="bottom">
-          <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
-        </el-tooltip>
-
         <screenfull id="screenfull" class="right-menu-item hover-effect" />
         <screenfull id="screenfull" class="right-menu-item hover-effect" />
 
 
         <el-tooltip content="布局大小" effect="dark" placement="bottom">
         <el-tooltip content="布局大小" effect="dark" placement="bottom">

+ 34 - 12
ems-ui-cloud/src/views/adapter/devc/index.vue

@@ -5,7 +5,7 @@
       <div slot="header" class="card-header">
       <div slot="header" class="card-header">
         <span class="title">
         <span class="title">
           <i class="el-icon-s-platform"></i>
           <i class="el-icon-s-platform"></i>
-          能耗监测
+          设备管理
         </span>
         </span>
         <el-tag :type="systemStatus === '1' ? 'success' : 'danger'" effect="dark">
         <el-tag :type="systemStatus === '1' ? 'success' : 'danger'" effect="dark">
           {{ systemStatus === '1' ? '正常' : '异常' }}
           {{ systemStatus === '1' ? '正常' : '异常' }}
@@ -56,20 +56,21 @@
             </div>
             </div>
           </div>
           </div>
         </el-col>
         </el-col>
+        <!-- 设备统计 部分修改 -->
         <el-col :span="6">
         <el-col :span="6">
           <div class="info-group">
           <div class="info-group">
             <div class="group-title">设备统计</div>
             <div class="group-title">设备统计</div>
             <div class="info-item">
             <div class="info-item">
-              <label>网关设备:</label>
+              <label>楼控设备:</label>
               <span class="stat-number">{{ deviceStats.total }}</span>
               <span class="stat-number">{{ deviceStats.total }}</span>
             </div>
             </div>
             <div class="info-item">
             <div class="info-item">
               <label>在线设备:</label>
               <label>在线设备:</label>
-              <span class="stat-number">{{ deviceStats.online }}</span>
+              <span class="stat-number online">{{ deviceStats.online }}</span>
             </div>
             </div>
             <div class="info-item">
             <div class="info-item">
-              <label>测点总数:</label>
-              <span class="stat-number">{{ deviceStats.points }}</span>
+              <label>离线设备:</label>
+              <span class="stat-number offline">{{ deviceStats.offline }}</span>
             </div>
             </div>
             <div style="text-align: right; margin-top: 10px;">
             <div style="text-align: right; margin-top: 10px;">
               <el-button
               <el-button
@@ -642,8 +643,7 @@ export default {
       deviceStats: {
       deviceStats: {
         total: 0,
         total: 0,
         online: 0,
         online: 0,
-        offline: 0,
-        points: 0
+        offline: 0
       },
       },
       // 所有模型代码(系统 + 设备)
       // 所有模型代码(系统 + 设备)
       allModelCodes: [],
       allModelCodes: [],
@@ -1102,7 +1102,7 @@ export default {
           this.systemStatus = statusAttr.attrValue
           this.systemStatus = statusAttr.attrValue
         }
         }
 
 
-        await this.loadDevices()
+        await this.loadBaDeviceStats()
       } catch (error) {
       } catch (error) {
         this.$message.error('加载系统信息失败:' + error.message)
         this.$message.error('加载系统信息失败:' + error.message)
       }
       }
@@ -1137,10 +1137,6 @@ export default {
           }
           }
         })
         })
 
 
-        this.deviceStats.total = this.deviceList.length
-        this.deviceStats.online = this.deviceList.filter(d => d.deviceStatus === 1).length
-        this.deviceStats.offline = this.deviceList.filter(d => d.deviceStatus !== 1).length
-
         const devicesByModel = {}
         const devicesByModel = {}
         this.deviceList.forEach(device => {
         this.deviceList.forEach(device => {
           if (device.deviceModel) {
           if (device.deviceModel) {
@@ -1648,6 +1644,32 @@ export default {
       } catch (error) {
       } catch (error) {
         this.$message.error('获取事件日志详情失败')
         this.$message.error('获取事件日志详情失败')
       }
       }
+    },
+
+    async loadBaDeviceStats() {
+      try {
+        let totalCount = 0
+        let onlineCount = 0
+
+        // 遍历所有楼控设备模型,统计设备数量
+        for (const model of this.baDeviceModels) {
+          const res = await getByCondition({
+            subsystemCode: this.systemCode,
+            deviceModel: model.modelCode
+          })
+
+          const deviceData = res.data || res.rows || []
+          totalCount += deviceData.length
+          onlineCount += deviceData.filter(d => d.deviceStatus === 1).length
+        }
+
+        this.deviceStats.total = totalCount
+        this.deviceStats.online = onlineCount
+        this.deviceStats.offline = totalCount - onlineCount
+
+      } catch (error) {
+        console.error('统计楼控设备失败:', error)
+      }
     }
     }
   }
   }
 }
 }

+ 1 - 1
ems-ui-cloud/src/views/adapter/zm/index.vue

@@ -623,7 +623,7 @@
                                       :key="attr.attrKey"
                                       :key="attr.attrKey"
                                       :label="attr.attrName">
                                       :label="attr.attrName">
                                       <template v-if="attr.attrValueType === 'Enum'">
                                       <template v-if="attr.attrValueType === 'Enum'">
-                                        <el-tag size="small" :type="getLampGroupAttrValue(scope.row.deviceCode, attr.attrKey) === '0' ? 'success' : 'danger'">
+                                        <el-tag size="small" :type="getLampGroupAttrValue(scope.row.deviceCode, attr.attrKey) === '1' ? 'success' : 'danger'">
                                           {{ getLampGroupAttrEnumName(scope.row.deviceCode, attr.attrKey, attr.valueEnums) }}
                                           {{ getLampGroupAttrEnumName(scope.row.deviceCode, attr.attrKey, attr.valueEnums) }}
                                         </el-tag>
                                         </el-tag>
                                       </template>
                                       </template>

+ 34 - 18
ems-ui-cloud/src/views/analysis/power/consume.vue

@@ -110,7 +110,7 @@
 
 
           <div class="summary-cards" v-loading="summaryLoading">
           <div class="summary-cards" v-loading="summaryLoading">
             <el-row :gutter="16">
             <el-row :gutter="16">
-              <el-col :span="6">
+              <el-col :span="energyType === 'elec' ? 6 : 12">
                 <el-tooltip
                 <el-tooltip
                   :disabled="!formattedSummary.totalQuantity.isShortened"
                   :disabled="!formattedSummary.totalQuantity.isShortened"
                   :content="'精确值: ' + formattedSummary.totalQuantity.full + (energyType === 'elec' ? ' kW·h' : ' t')"
                   :content="'精确值: ' + formattedSummary.totalQuantity.full + (energyType === 'elec' ? ' kW·h' : ' t')"
@@ -199,7 +199,7 @@
                 </el-tooltip>
                 </el-tooltip>
               </el-col>
               </el-col>
 
 
-              <el-col :span="energyType === 'elec' ? 6 : 9">
+              <el-col :span="energyType === 'elec' ? 6 : 12">
                 <el-tooltip
                 <el-tooltip
                   :disabled="!formattedSummary.totalCost.isShortened"
                   :disabled="!formattedSummary.totalCost.isShortened"
                   :content="'精确值: ¥' + formattedSummary.totalCost.full"
                   :content="'精确值: ¥' + formattedSummary.totalCost.full"
@@ -222,21 +222,6 @@
                   </div>
                   </div>
                 </el-tooltip>
                 </el-tooltip>
               </el-col>
               </el-col>
-
-              <el-col :span="energyType === 'elec' ? 6 : 9" v-if="energyType === 'water'">
-                <div class="stat-card count-card">
-                  <div class="card-icon">
-                    <i class="el-icon-files"></i>
-                  </div>
-                  <div class="card-content">
-                    <span class="card-label">统计记录</span>
-                    <span class="card-value">
-                      {{ total }}
-                      <small>条</small>
-                    </span>
-                  </div>
-                </div>
-              </el-col>
             </el-row>
             </el-row>
           </div>
           </div>
 
 
@@ -1371,6 +1356,31 @@ export default {
     .summary-cards {
     .summary-cards {
       margin-bottom: 24px;
       margin-bottom: 24px;
 
 
+      // 确保同一行的卡片高度一致
+      ::v-deep .el-row {
+        display: flex;
+        flex-wrap: wrap;
+
+        .el-col {
+          display: flex;
+
+          > .el-tooltip,
+          > .stat-card {
+            width: 100%;
+            display: flex;
+          }
+
+          // tooltip 包裹的情况
+          .el-tooltip {
+            width: 100%;
+
+            .stat-card {
+              width: 100%;
+            }
+          }
+        }
+      }
+
       .stat-card {
       .stat-card {
         background: #fff;
         background: #fff;
         border-radius: 12px;
         border-radius: 12px;
@@ -1381,6 +1391,9 @@ export default {
         transition: all 0.3s ease;
         transition: all 0.3s ease;
         position: relative;
         position: relative;
         overflow: hidden;
         overflow: hidden;
+        min-height: 96px;  /* 关键:设置最小高度保证一致 */
+        box-sizing: border-box;
+        flex: 1;  /* 让卡片填满容器 */
 
 
         &::before {
         &::before {
           content: '';
           content: '';
@@ -1456,6 +1469,8 @@ export default {
           flex: 1;
           flex: 1;
           display: flex;
           display: flex;
           flex-direction: column;
           flex-direction: column;
+          justify-content: center;  /* 垂直居中内容 */
+          min-height: 56px;  /* 与图标高度一致 */
 
 
           .card-label {
           .card-label {
             font-size: 13px;
             font-size: 13px;
@@ -1495,7 +1510,8 @@ export default {
           .card-sub {
           .card-sub {
             font-size: 12px;
             font-size: 12px;
             color: #909399;
             color: #909399;
-            margin-top: 6px;
+            margin-top: 4px;
+            line-height: 1.2;
           }
           }
         }
         }
       }
       }

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 1522 - 283
ems-ui-cloud/src/views/analysis/power/prod.vue


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 783 - 376
ems-ui-cloud/src/views/analysis/power/save.vue


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 839 - 401
ems-ui-cloud/src/views/analysis/power/store.vue


+ 2 - 2
ems-ui-cloud/src/views/analysis/report/statement-prod.vue

@@ -245,7 +245,7 @@
 
 
 <script>
 <script>
 import { areaTreeByFacsCategory } from '@/api/basecfg/area'
 import { areaTreeByFacsCategory } from '@/api/basecfg/area'
-import { listPvSupplyH } from "@/api/mgr/pgSupplyH"
+import { listPvSupplyReport } from '@/api/mgr/pvSupply'
 
 
 export default {
 export default {
   name: "PvSupplyStatistics",
   name: "PvSupplyStatistics",
@@ -422,7 +422,7 @@ export default {
     // 获取数据列表
     // 获取数据列表
     getList() {
     getList() {
       this.loading = true
       this.loading = true
-      listPvSupplyH(this.queryParams).then(response => {
+      listPvSupplyReport(this.queryParams).then(response => {
         this.pvSupplyHList = response.rows
         this.pvSupplyHList = response.rows
         this.total = response.total
         this.total = response.total
         this.calculateStats(response.rows)
         this.calculateStats(response.rows)

+ 63 - 21
ems-ui-cloud/src/views/largeScreen/dialog/pv-real.vue

@@ -26,10 +26,11 @@
 
 
 <script>
 <script>
 import threejsBaseDialog from './base.vue'
 import threejsBaseDialog from './base.vue'
-import {pgSupplyTotalPv} from "@/api/screen";
-import {numToStr} from "@/utils";
+import { getPvSupplySummary } from '@/api/mgr/pvSupply'
+import { numToStr } from "@/utils"
 
 
 let timer = null
 let timer = null
+
 export default {
 export default {
   components: {
   components: {
     threejsBaseDialog,
     threejsBaseDialog,
@@ -42,44 +43,85 @@ export default {
       },
       },
     },
     },
   },
   },
-  watch: {},
   data() {
   data() {
     return {
     return {
-      todayEnergy: {}
+      todayEnergy: {
+        gen: '0.00',
+        use: '0.00',
+        upload: '0.00',
+        earn: '0.00'
+      }
     };
     };
   },
   },
-  // 组件卸载前清空图层信息
   beforeDestroy() {
   beforeDestroy() {
     timer && clearInterval(timer)
     timer && clearInterval(timer)
   },
   },
-  created() {
-
-  },
   mounted() {
   mounted() {
     this.init();
     this.init();
   },
   },
   methods: {
   methods: {
     init() {
     init() {
       this.getPgSupply()
       this.getPgSupply()
+      // 每3分钟刷新一次数据
       timer = setInterval(this.getPgSupply, 1000 * 60 * 3)
       timer = setInterval(this.getPgSupply, 1000 * 60 * 3)
     },
     },
+
+    /**
+     * 获取当日光伏实时数据
+     * 使用新的后端接口 /ems/prod/pv/hour/summary
+     */
     async getPgSupply() {
     async getPgSupply() {
-      const {data: pgPvSupplyTotalPv} = await pgSupplyTotalPv({
-        areaCode: this.propData.areaCode
-      })
-      if (!pgPvSupplyTotalPv) {
-        return
+      try {
+        // 获取当前日期
+        const now = new Date()
+        const year = now.getFullYear()
+        const month = String(now.getMonth() + 1).padStart(2, '0')
+        const day = String(now.getDate()).padStart(2, '0')
+        const today = `${year}-${month}-${day}`
+
+        // 构建查询参数
+        const queryParams = {
+          areaCode: this.propData.areaCode || '-1', // 从propData获取区域代码
+          startRecTime: `${today} 00:00:00`,
+          endRecTime: `${today} 23:59:59`
+        }
+
+        console.log('查询光伏实时数据参数:', queryParams)
+
+        // 调用新的汇总接口
+        const { code, data } = await getPvSupplySummary(queryParams)
+
+        if (code === 200 && data) {
+          // 更新实时数据
+          this.todayEnergy = {
+            gen: numToStr(data.genElecQuantity || 0),
+            use: numToStr(data.useElecQuantity || 0),
+            upload: numToStr(data.upElecQuantity || 0),
+            earn: numToStr(data.upElecEarn || 0)
+          }
+
+          console.log('光伏实时数据更新成功:', this.todayEnergy)
+        } else {
+          console.warn('获取光伏实时数据失败或数据为空')
+          this.resetData()
+        }
+      } catch (error) {
+        console.error('获取光伏实时数据异常:', error)
+        this.resetData()
       }
       }
-      const {
-        thisDay,
-      } = pgPvSupplyTotalPv
+    },
+
+    /**
+     * 重置数据为0
+     */
+    resetData() {
       this.todayEnergy = {
       this.todayEnergy = {
-        gen: numToStr(thisDay.genElecQuantity),
-        use: numToStr(thisDay.useElecQuantity),
-        upload: numToStr(thisDay.upElecQuantity),
-        earn: numToStr(thisDay.upElecEarn),
+        gen: '0.00',
+        use: '0.00',
+        upload: '0.00',
+        earn: '0.00'
       }
       }
-    },
+    }
   },
   },
 };
 };
 </script>
 </script>

+ 61 - 56
ems-ui-cloud/src/views/largeScreen/dialog/storage-real.vue

@@ -4,11 +4,7 @@
       储能实时数据
       储能实时数据
     </template>
     </template>
     <template #data>
     <template #data>
-      <div>
-        <span>实时充电量:</span>
-        <span class="value">{{ energy }} %</span>
-      </div>
-      <div v-for="item in storageData">
+      <div v-for="item in storageList" :key="item.name">
         <span>{{ item.name }}:</span>
         <span>{{ item.name }}:</span>
         <span class="value">{{ item.value }}{{ item.unit }}</span>
         <span class="value">{{ item.value }}{{ item.unit }}</span>
       </div>
       </div>
@@ -18,12 +14,11 @@
 
 
 <script>
 <script>
 import threejsBaseDialog from './base.vue'
 import threejsBaseDialog from './base.vue'
-import {listConfig} from "@/api/system/config";
-import {DateTool} from "@/utils/DateTool";
-import {getSumHStorage, getSumRealTimeStorage} from "@/api/mgr/elecStoreH";
-import {numToStr} from "@/utils";
+import { getLatestByDevice } from "@/api/mgr/elecStoreIndex";
+import { numToStr } from "@/utils";
+
+let timer = null;
 
 
-let timer = null
 export default {
 export default {
   components: {
   components: {
     threejsBaseDialog,
     threejsBaseDialog,
@@ -36,71 +31,81 @@ export default {
       },
       },
     },
     },
   },
   },
-  watch: {},
   data() {
   data() {
     return {
     return {
-      cfg: {},
-      energy: 0,
-      storageData: [
+      storageList: [
         {
         {
-          name: "今日充电",
+          name: "当前容量",
+          value: 0,
           unit: "kW·h",
           unit: "kW·h",
         },
         },
         {
         {
-          name: "今日放电",
-          unit: "kW·h",
-        }
-      ]
+          name: "充电功率",
+          value: 0,
+          unit: "kW",
+        },
+        {
+          name: "放电功率",
+          value: 0,
+          unit: "kW",
+        },
+      ],
     };
     };
   },
   },
-  // 组件卸载前清空图层信息
   beforeDestroy() {
   beforeDestroy() {
-    timer && clearInterval(timer)
-  },
-  created() {
-
+    timer && clearInterval(timer);
   },
   },
   mounted() {
   mounted() {
     this.init();
     this.init();
   },
   },
   methods: {
   methods: {
     async init() {
     async init() {
-      const {rows} = await listConfig({
-        configKey: "storage-equipped-capacity",
-      })
-      if (!rows || !rows.length) {
-        return;
-      }
-      this.cfg = JSON.parse(rows[0].remark)
-      await this.getStorageData()
-      timer = setInterval(this.getStorageData, 1000 * 60 * 3)
+      await this.getStorageData();
+      timer = setInterval(this.getStorageData, 1000 * 60 * 3);
     },
     },
     async getStorageData() {
     async getStorageData() {
-      const {data: thisDay} = await getSumHStorage({
-        areaCode: this.propData.areaCode,
-        startRecTime: DateTool.now(),
-      })
-      const {data: hisTotal} = await getSumHStorage({
-        areaCode: this.propData.areaCode
-      })
-      const {data: realtimeStorage = {}} = await getSumRealTimeStorage({
-        areaCode: this.propData.areaCode,
-        startRecTime: DateTool.now()
-      })
-      let equipedStorage = this.cfg[this.propData.areaCode] ? this.cfg[this.propData.areaCode].store : 0
-      if (!this.propData.areaCode || this.propData.areaCode === '-1') {
-        for (let item in this.cfg) {
-          equipedStorage += this.cfg[item].store
+      try {
+        const params = {};
+
+        if (this.propData.areaCode && this.propData.areaCode !== '-1') {
+          params.areaCode = this.propData.areaCode;
+        }
+
+        if (this.propData.facsCode) {
+          params.facsCode = this.propData.facsCode;
+        }
+
+        const { data } = await getLatestByDevice(params);
+
+        if (!data || !Array.isArray(data) || data.length === 0) {
+          const [capacity, charge, discharge] = this.storageList;
+          capacity.value = 0;
+          charge.value = 0;
+          discharge.value = 0;
+          this.storageList = [capacity, charge, discharge];
+          return;
         }
         }
+
+        // 汇总所有设备的数据
+        let totalCapacity = 0;      // 当前容量总和(单位:kW·h)
+        let totalChargePower = 0;   // 充电功率总和(单位:W -> kW)
+        let totalDischargePower = 0; // 放电功率总和(单位:W -> kW)
+
+        data.forEach(device => {
+          totalCapacity += device.currentCapacity || 0;
+          totalChargePower += (device.chargeVoltage || 0) / 1000; // W转kW
+          totalDischargePower += (device.dischargePower || 0) / 1000; // W转kW
+        });
+
+        const [capacity, charge, discharge] = this.storageList;
+        capacity.value = numToStr(totalCapacity.toFixed(2));
+        charge.value = numToStr(totalChargePower.toFixed(2));
+        discharge.value = numToStr(totalDischargePower.toFixed(2));
+        this.storageList = [capacity, charge, discharge];
+
+      } catch (error) {
+        console.error('获取储能数据失败:', error);
       }
       }
-      const realTimeStorage = realtimeStorage.currentCapacity || 0
-      this.energy = (realTimeStorage / equipedStorage * 100).toFixed(2)
-      // const [thisDayCharge, thisDayDischarge, totalCharge, totalDischarge] = this.storageData
-    let  thisDayCharge = numToStr(thisDay.chargeElecQuantity)
-    let  thisDayDischarge = numToStr(thisDay.dischargeElecQuantity)
-    let  totalCharge = numToStr(hisTotal.chargeElecQuantity)
-     let totalDischarge = numToStr(hisTotal.dischargeElecQuantity)
-      this.storageData = [thisDayCharge, thisDayDischarge, totalCharge, totalDischarge]
     },
     },
   },
   },
 };
 };

+ 152 - 64
ems-ui-cloud/src/views/largeScreen/home/left.vue

@@ -20,10 +20,10 @@
       <div class="storage-container">
       <div class="storage-container">
         <div class="center-spinner"></div>
         <div class="center-spinner"></div>
         <div class="percentage-display">
         <div class="percentage-display">
-          <span class="value">{{ energy }}</span><span class="unit">%</span>
+          <span class="value">{{ currentCapacity }}</span><span class="unit">kW·h</span>
         </div>
         </div>
         <div class="charge-status">
         <div class="charge-status">
-          实时充电
+          当前容
         </div>
         </div>
       </div>
       </div>
       <div class="storage-grid">
       <div class="storage-grid">
@@ -48,12 +48,12 @@ import CusTabs from '../components/CusTabs.vue';
 import CusModule from '../components/CusModule.vue';
 import CusModule from '../components/CusModule.vue';
 import BaseChart from '@/components/BaseChart/index.vue'
 import BaseChart from '@/components/BaseChart/index.vue'
 import {mapState} from 'vuex';
 import {mapState} from 'vuex';
-import {pgSupplyTotalPv} from "@/api/screen";
 import {numToStr} from "@/utils";
 import {numToStr} from "@/utils";
 import {DateTool} from "@/utils/DateTool";
 import {DateTool} from "@/utils/DateTool";
 import { listFacsMeterHourSta } from '@/api/device/energyConsumption'
 import { listFacsMeterHourSta } from '@/api/device/energyConsumption'
-import {getSumHStorage, getSumRealTimeStorage} from "@/api/mgr/elecStoreH";
-import {listConfig} from "@/api/system/config";
+import { getStoreHByDataRange } from "@/api/mgr/elecStoreHour";
+import { getLatestByDevice } from "@/api/mgr/elecStoreIndex";
+import { getPvSupplySummary } from '@/api/mgr/pvSupply'
 
 
 export default {
 export default {
   name: 'HomeLeft',
   name: 'HomeLeft',
@@ -61,19 +61,26 @@ export default {
     return {
     return {
       dateType: '1',
       dateType: '1',
       dateTabs: [{name: '当日', value: '1'}, {name: '本月', value: '2'}, {name: '本年', value: '3'}],
       dateTabs: [{name: '当日', value: '1'}, {name: '本月', value: '2'}, {name: '本年', value: '3'}],
+      // 修改产能列表:光伏发电量、上网电量、自用电量
       productionList: [
       productionList: [
         {
         {
+          id: 'genElec',
           name: "光伏发电量",
           name: "光伏发电量",
+          value: '0',
           unit: "kW·h",
           unit: "kW·h",
           image: require("@/assets/images/source/item_bg1.png"),
           image: require("@/assets/images/source/item_bg1.png"),
         },
         },
         {
         {
-          name: "负荷消耗",
+          id: 'upElec',
+          name: "上网电量",
+          value: '0',
           unit: "kW·h",
           unit: "kW·h",
           image: require("@/assets/images/source/item_bg3.png"),
           image: require("@/assets/images/source/item_bg3.png"),
         },
         },
         {
         {
-          name: "入网",
+          id: 'useElec',
+          name: "自用电量",
+          value: '0',
           unit: "kW·h",
           unit: "kW·h",
           image: require("@/assets/images/source/item_bg4.png"),
           image: require("@/assets/images/source/item_bg4.png"),
         },
         },
@@ -81,27 +88,32 @@ export default {
       buildingType: '1',
       buildingType: '1',
       buildingTabs: [{name: '按建筑', value: '1'}, {name: '按设施', value: '2'}],
       buildingTabs: [{name: '按建筑', value: '1'}, {name: '按设施', value: '2'}],
       buildingData: [],
       buildingData: [],
-      energy: '100',
+      // 当前容量(所有设备current_capacity之和)
+      currentCapacity: '0.00',
       storageData: [
       storageData: [
         {
         {
           name: "今日充电",
           name: "今日充电",
           image: require("@/assets/images/home/icon1.png"),
           image: require("@/assets/images/home/icon1.png"),
           unit: "kW·h",
           unit: "kW·h",
+          value: '0'
         },
         },
         {
         {
           name: "今日放电",
           name: "今日放电",
           image: require("@/assets/images/home/icon2.png"),
           image: require("@/assets/images/home/icon2.png"),
           unit: "kW·h",
           unit: "kW·h",
+          value: '0'
         },
         },
         {
         {
           name: "累计充电",
           name: "累计充电",
           image: require("@/assets/images/home/icon3.png"),
           image: require("@/assets/images/home/icon3.png"),
           unit: "kW·h",
           unit: "kW·h",
+          value: '0'
         },
         },
         {
         {
           name: "累计放电",
           name: "累计放电",
           image: require("@/assets/images/home/icon4.png"),
           image: require("@/assets/images/home/icon4.png"),
           unit: "kW·h",
           unit: "kW·h",
+          value: '0'
         },
         },
       ]
       ]
     };
     };
@@ -205,36 +217,100 @@ export default {
       this.getPvDataList()
       this.getPvDataList()
       this.getDeviceElecMeter()
       this.getDeviceElecMeter()
     },
     },
+
+    /**
+     * 获取光伏产能数据
+     * 基于后端 /ems/prod/pv/hour/summary 接口
+     * 根据时间维度(当日/本月/本年)查询汇总数据
+     */
     async getPvDataList() {
     async getPvDataList() {
-      const {data} = await pgSupplyTotalPv({
-        areaCode: this.areaType,
-      })
-      if (!data) {
-        return
-      }
-      const {
-        thisDay,
-        thisMonth,
-        thisYear
-      } = data
-      const [prodPv, usePv, upPv] = this.productionList
-      if (this.dateType === '1') {
-        prodPv.value = numToStr(thisDay.genElecQuantity)
-        usePv.value = numToStr(thisDay.useElecQuantity)
-        upPv.value = numToStr(thisDay.upElecQuantity)
-      }
-      if (this.dateType === '2') {
-        prodPv.value = numToStr(thisMonth.genElecQuantity)
-        usePv.value = numToStr(thisMonth.useElecQuantity)
-        upPv.value = numToStr(thisMonth.upElecQuantity)
+      try {
+        // 根据时间维度计算时间范围
+        const timeRange = this.getTimeRangeByDateType(this.dateType)
+
+        // 构建查询参数
+        const queryParams = {
+          areaCode: this.areaType || '-1',  // -1表示全部区域
+          startRecTime: timeRange.startTime,
+          endRecTime: timeRange.endTime
+        }
+
+        console.log('查询光伏产能参数:', queryParams)
+
+        // 调用后端汇总接口
+        const { code, data } = await getPvSupplySummary(queryParams)
+
+        if (code === 200 && data) {
+          // 更新产能列表数据
+          const [genElec, upElec, useElec] = this.productionList
+
+          genElec.value = numToStr(data.genElecQuantity || 0)
+          upElec.value = numToStr(data.upElecQuantity || 0)
+          useElec.value = numToStr(data.useElecQuantity || 0)
+
+          this.productionList = [genElec, upElec, useElec]
+
+          console.log('光伏产能数据更新成功:', {
+            genElecQuantity: data.genElecQuantity,
+            upElecQuantity: data.upElecQuantity,
+            useElecQuantity: data.useElecQuantity
+          })
+        } else {
+          console.warn('获取光伏产能数据失败或数据为空')
+          this.resetProductionData()
+        }
+      } catch (error) {
+        console.error('获取光伏产能数据异常:', error)
+        this.resetProductionData()
       }
       }
-      if (this.dateType === '3') {
-        prodPv.value = numToStr(thisYear.genElecQuantity)
-        usePv.value = numToStr(thisYear.useElecQuantity)
-        upPv.value = numToStr(thisYear.upElecQuantity)
+    },
+
+    /**
+     * 根据日期类型获取时间范围
+     * @param {String} dateType - '1':当日, '2':本月, '3':本年
+     * @returns {Object} { startTime, endTime }
+     */
+    getTimeRangeByDateType(dateType) {
+      const now = new Date()
+      const year = now.getFullYear()
+      const month = String(now.getMonth() + 1).padStart(2, '0')
+      const day = String(now.getDate()).padStart(2, '0')
+      const today = `${year}-${month}-${day}`
+
+      let startTime, endTime
+
+      switch (dateType) {
+        case '1': // 当日
+          startTime = `${today} 00:00:00`
+          endTime = `${today} 23:59:59`
+          break
+        case '2': // 本月
+          startTime = `${year}-${month}-01 00:00:00`
+          endTime = `${today} 23:59:59`
+          break
+        case '3': // 本年
+          startTime = `${year}-01-01 00:00:00`
+          endTime = `${today} 23:59:59`
+          break
+        default:
+          startTime = `${today} 00:00:00`
+          endTime = `${today} 23:59:59`
       }
       }
-      this.productionList = [prodPv, usePv, upPv]
+
+      return { startTime, endTime }
+    },
+
+    /**
+     * 重置产能数据为0
+     */
+    resetProductionData() {
+      const [genElec, upElec, useElec] = this.productionList
+      genElec.value = '0'
+      upElec.value = '0'
+      useElec.value = '0'
+      this.productionList = [genElec, upElec, useElec]
     },
     },
+
     async getDeviceElecMeter() {
     async getDeviceElecMeter() {
       const params = {
       const params = {
         startRecTime: DateTool.now(DateTool.DateFormat.YYYY_MM_DD_00_00_00),
         startRecTime: DateTool.now(DateTool.DateFormat.YYYY_MM_DD_00_00_00),
@@ -251,39 +327,51 @@ export default {
       })
       })
     },
     },
 
 
+    /**
+     * 获取储能数据
+     * - 当前容量:从 elecStoreIndex 获取各设备最新记录的 currentCapacity 之和
+     * - 今日充电/放电:从 elecStoreH 获取今日汇总
+     * - 累计充电/放电:从 elecStoreH 获取历史累计汇总
+     */
     async getStorageData() {
     async getStorageData() {
-      const {rows} = await listConfig({
-        configKey: "storage-equipped-capacity",
-      })
-      if (!rows || !rows.length) {
-        return;
-      }
-      const cfg = JSON.parse(rows[0].remark)
-      const {data: thisDay} = await getSumHStorage({
-        areaCode: this.areaType,
-        startRecTime: DateTool.now(),
-      })
-      const {data: hisTotal} = await getSumHStorage({
-        areaCode: this.areaType
-      })
-      const {data: realtimeStorage = {}} = await getSumRealTimeStorage({
-        areaCode: this.areaType,
-        startRecTime: DateTool.now()
-      })
-      let equipedStorage = cfg[this.areaType] ? cfg[this.areaType].store : 0
-      if (!this.areaType || this.areaType === '-1') {
-        for (let item in cfg) {
-          equipedStorage += cfg[item].store
+      try {
+        // 构建区域查询参数
+        const areaParam = this.areaType && this.areaType !== '-1' ? { areaCode: this.areaType } : {}
+
+        // 1. 获取当前容量 - 查询各设备最新记录
+        const { data: latestList = [] } = await getLatestByDevice(areaParam)
+
+        // 计算所有设备的当前容量之和
+        let totalCurrentCapacity = 0
+        if (latestList && latestList.length > 0) {
+          totalCurrentCapacity = latestList.reduce((sum, item) => {
+            return sum + (item.currentCapacity || 0)
+          }, 0)
         }
         }
+        this.currentCapacity = numToStr(totalCurrentCapacity)
+
+        // 2. 获取今日充放电数据
+        const todayDate = DateTool.now(DateTool.DateFormat.YYYY_MM_DD)
+        const { data: todayData = {} } = await getStoreHByDataRange({
+          ...areaParam,
+          startRecTime: todayDate
+        })
+
+        // 3. 获取累计充放电数据(不传日期参数,查询所有历史数据)
+        const { data: totalData = {} } = await getStoreHByDataRange(areaParam)
+
+        // 4. 更新储能数据
+        const [todayCharge, todayDischarge, totalCharge, totalDischarge] = this.storageData
+        todayCharge.value = numToStr(todayData.chargeElecQuantity || 0)
+        todayDischarge.value = numToStr(todayData.dischargeElecQuantity || 0)
+        totalCharge.value = numToStr(totalData.chargeElecQuantity || 0)
+        totalDischarge.value = numToStr(totalData.dischargeElecQuantity || 0)
+
+        this.storageData = [todayCharge, todayDischarge, totalCharge, totalDischarge]
+
+      } catch (error) {
+        console.error('获取储能数据失败:', error)
       }
       }
-      const realTimeStorage = realtimeStorage.currentCapacity || 0
-      this.energy = (realTimeStorage / equipedStorage * 100).toFixed(2)
-      const [thisDayCharge, thisDayDischarge, totalCharge, totalDischarge] = this.storageData
-      thisDayCharge.value = numToStr(thisDay.chargeElecQuantity)
-      thisDayDischarge.value = numToStr(thisDay.dischargeElecQuantity)
-      totalCharge.value = numToStr(hisTotal.chargeElecQuantity)
-      totalDischarge.value = numToStr(hisTotal.dischargeElecQuantity)
-      this.storageData = [thisDayCharge, thisDayDischarge, totalCharge, totalDischarge]
     },
     },
   }
   }
 }
 }

+ 274 - 24
ems-ui-cloud/src/views/largeScreen/home/right.vue

@@ -10,7 +10,7 @@
           </div>
           </div>
           <div class="description">
           <div class="description">
             <span class="text">本月用电量</span>
             <span class="text">本月用电量</span>
-            <div class="rate" :style="`${useElec.trend === 'up'? 'red': 'green'}`">
+            <div class="rate" :style="`color: ${useElec.trend === 'up'? '#FF5252': '#00C852'}`">
               <span>环比</span>
               <span>环比</span>
               <img src="@/assets/images/home/down-arrow.svg" alt="">
               <img src="@/assets/images/home/down-arrow.svg" alt="">
               <span>{{ useElec.rate }}%</span>
               <span>{{ useElec.rate }}%</span>
@@ -51,6 +51,12 @@
         </div>
         </div>
       </div>
       </div>
     </CusModule>
     </CusModule>
+
+    <!-- 新增:发电趋势预测模块 -->
+    <CusModule class="module-top" title="发电趋势预测">
+      <BaseChart height="350px" width="100%" :option="elecProdForecastOptions"/>
+    </CusModule>
+
     <CusModule class="module-top" title="设备设施">
     <CusModule class="module-top" title="设备设施">
       <div class="equip">
       <div class="equip">
         <div v-for="item in equipment" :style="{ 'color': item.color }" class="equip-item" :key="item.name">
         <div v-for="item in equipment" :style="{ 'color': item.color }" class="equip-item" :key="item.name">
@@ -62,12 +68,14 @@
         <BaseChart height="100%" width="100%" :option="pieOptions"/>
         <BaseChart height="100%" width="100%" :option="pieOptions"/>
       </div>
       </div>
     </CusModule>
     </CusModule>
+
     <CusModule class="module-top" title="碳计量">
     <CusModule class="module-top" title="碳计量">
       <template v-slot:title-right>
       <template v-slot:title-right>
-        <div>本月累计碳排量:<span style="color: #00C852">{{ (useElec.value * elec2CFactor).toFixed(2)}}</span> kg</div>
+        <div>本月累计碳排量:<span style="color: #00C852">{{ monthCaEmission }}</span> kg</div>
       </template>
       </template>
-      <!--      <BaseChart height="350px" width="100%" :option="lineOptions"/>-->
+      <BaseChart height="350px" width="100%" :option="lineOptions"/>
     </CusModule>
     </CusModule>
+
     <CusModule class="module-top" title="告警信息">
     <CusModule class="module-top" title="告警信息">
       <div class="seamless-header">
       <div class="seamless-header">
         <div>告警内容</div>
         <div>告警内容</div>
@@ -85,6 +93,7 @@
 
 
   </div>
   </div>
 </template>
 </template>
+
 <script>
 <script>
 import CusModule from '../components/CusModule.vue';
 import CusModule from '../components/CusModule.vue';
 import BaseChart from '@/components/BaseChart/index.vue'
 import BaseChart from '@/components/BaseChart/index.vue'
@@ -99,6 +108,7 @@ import {listAlarmInfo} from "@/api/alarm/alarm-info";
 import {ALARM_STATE} from "@/enums/alarm";
 import {ALARM_STATE} from "@/enums/alarm";
 import {ApiCode} from "@/api/apiEmums";
 import {ApiCode} from "@/api/apiEmums";
 import {listSumCaMeterD} from "@/api/ca/caMeterD";
 import {listSumCaMeterD} from "@/api/ca/caMeterD";
+import {calcElecProdForecastDateRange} from "@/api/prediction/predictionProd";
 
 
 export default {
 export default {
   name: 'HomeRight',
   name: 'HomeRight',
@@ -106,31 +116,44 @@ export default {
     return {
     return {
       useElec: {
       useElec: {
         unit: '度',
         unit: '度',
+        value: 0,
+        rate: 0,
+        trend: 'down'
       },
       },
       userWater: {
       userWater: {
         unit: '吨',
         unit: '吨',
+        value: 0,
+        rate: 0,
+        trend: 'down'
       },
       },
       userEnergy: {
       userEnergy: {
         unit: '元',
         unit: '元',
+        value: 0,
+        rate: 0,
+        trend: 'down'
       },
       },
       equipment: [
       equipment: [
-        {name: '设备总数', unit: '个', color: '#01A7F0'},
-        {name: '在线设备', unit: '个', color: '#B3E3E8'},
-        {name: '离线设备', unit: '个', color: '#fff'}
+        {name: '设备总数', unit: '个', color: '#01A7F0', value: 0},
+        {name: '在线设备', unit: '个', color: '#B3E3E8', value: 0},
+        {name: '离线设备', unit: '个', color: '#fff', value: 0}
       ],
       ],
       equipmentPieData: [],
       equipmentPieData: [],
       classOption: {
       classOption: {
-        step: 0.3, // 数值越大速度滚动越快
-        limitMoveNum: 4, // 开始无缝滚动的数据量 this.dataList.length
-        hoverStop: true, // 是否开启鼠标悬停stop
-        direction: 1, // 0向下 1向上 2向左 3向右
-        openWatch: true, // 开启数据实时监控刷新dom
-        singleHeight: 0, // 单步运动停止的高度(默认值0是无缝不停止的滚动) direction => 0/1
-        singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
-        waitTime: 1000 // 单步运动停止
+        step: 0.3,
+        limitMoveNum: 4,
+        hoverStop: true,
+        direction: 1,
+        openWatch: true,
+        singleHeight: 0,
+        singleWidth: 0,
+        waitTime: 1000
       },
       },
       listData: [],
       listData: [],
-      lineData: []
+      lineData: [],
+      monthCaEmission: '0.00',
+      // 新增:发电趋势预测数据
+      elecProdForecastData: [],
+      totalElecProdQuantity: '0.00'
     }
     }
   },
   },
   components: {
   components: {
@@ -139,7 +162,7 @@ export default {
     vueSeamlessScroll
     vueSeamlessScroll
   },
   },
   computed: {
   computed: {
-    ...mapState('userState', ['areaType','elec2CFactor']),
+    ...mapState('userState', ['areaType', 'elec2CFactor']),
     pieOptions() {
     pieOptions() {
       return {
       return {
         legend: {
         legend: {
@@ -148,7 +171,7 @@ export default {
         tooltip: {
         tooltip: {
           trigger: 'item',
           trigger: 'item',
           formatter: (params) => {
           formatter: (params) => {
-            const {name, value, percent,} = params
+            const {name, value, percent} = params
             return `${name} : ${value}个 (${percent}%)`
             return `${name} : ${value}个 (${percent}%)`
           }
           }
         },
         },
@@ -162,7 +185,6 @@ export default {
             emphasis: {
             emphasis: {
               label: {
               label: {
                 show: true,
                 show: true,
-
               },
               },
             },
             },
             data: this.equipmentPieData,
             data: this.equipmentPieData,
@@ -196,7 +218,7 @@ export default {
       return {
       return {
         grid: {
         grid: {
           top: "10%",
           top: "10%",
-          bottom: "10%",//也可设置left和right设置距离来控制图表的大小
+          bottom: "10%",
           contain: true
           contain: true
         },
         },
         tooltip: {
         tooltip: {
@@ -211,15 +233,14 @@ export default {
         xAxis: {
         xAxis: {
           data: this.lineData.map(item => item.xData),
           data: this.lineData.map(item => item.xData),
           axisLine: {
           axisLine: {
-            show: true, //隐藏X轴轴线
+            show: true,
           },
           },
           axisTick: {
           axisTick: {
-            show: true //隐藏X轴刻度
+            show: true
           },
           },
           axisLabel: {
           axisLabel: {
             show: true,
             show: true,
           },
           },
-
         },
         },
         yAxis: [{
         yAxis: [{
           type: "value",
           type: "value",
@@ -257,6 +278,123 @@ export default {
           },
           },
         ]
         ]
       }
       }
+    },
+    // 新增:发电趋势预测图表配置
+    elecProdForecastOptions() {
+      return {
+        grid: {
+          top: "15%",
+          bottom: "10%",
+          left: "5%",
+          right: "5%",
+          contain: true
+        },
+        legend: {
+          data: ['发电量', '上网电量'],
+          textStyle: {
+            color: '#B3E3E8'
+          },
+          top: '5%'
+        },
+        tooltip: {
+          trigger: "axis",
+          axisPointer: {
+            type: "cross",
+            label: {
+              show: true
+            }
+          },
+          formatter: (params) => {
+            let result = `${params[0].axisValue}<br/>`;
+            params.forEach(item => {
+              result += `${item.marker}${item.seriesName}: ${item.value} kW·h<br/>`;
+            });
+            return result;
+          }
+        },
+        xAxis: {
+          type: 'category',
+          data: this.elecProdForecastData.map(item => item.date),
+          axisLine: {
+            show: true,
+            lineStyle: {
+              color: '#334E5E'
+            }
+          },
+          axisTick: {
+            show: true
+          },
+          axisLabel: {
+            show: true,
+            color: '#B3E3E8',
+            rotate: 30
+          },
+        },
+        yAxis: {
+          type: "value",
+          name: "kW·h",
+          nameTextStyle: {
+            color: '#B3E3E8'
+          },
+          axisLabel: {
+            color: '#B3E3E8'
+          },
+          splitLine: {
+            show: true,
+            lineStyle: {
+              color: '#334E5E'
+            }
+          },
+        },
+        series: [
+          {
+            name: "发电量",
+            type: "line",
+            smooth: true,
+            symbol: 'circle',
+            symbolSize: 6,
+            itemStyle: {
+              color: "#21C3E8"
+            },
+            areaStyle: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                {
+                  offset: 0,
+                  color: "rgba(33, 195, 232, 0.3)"
+                },
+                {
+                  offset: 1,
+                  color: "rgba(33, 195, 232, 0.1)"
+                }
+              ])
+            },
+            data: this.elecProdForecastData.map(item => item.elecProdQuantity)
+          },
+          {
+            name: "上网电量",
+            type: "line",
+            smooth: true,
+            symbol: 'circle',
+            symbolSize: 6,
+            itemStyle: {
+              color: "#F9AF39"
+            },
+            areaStyle: {
+              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                {
+                  offset: 0,
+                  color: "rgba(249, 175, 57, 0.3)"
+                },
+                {
+                  offset: 1,
+                  color: "rgba(249, 175, 57, 0.1)"
+                }
+              ])
+            },
+            data: this.elecProdForecastData.map(item => item.upElecQuantity)
+          }
+        ]
+      }
     }
     }
   },
   },
   watch: {
   watch: {
@@ -281,12 +419,15 @@ export default {
       this.cntListDeviceType()
       this.cntListDeviceType()
       this.getRealTimeAlarm()
       this.getRealTimeAlarm()
       this.getListSumCaMeterD()
       this.getListSumCaMeterD()
+      this.getMonthCaEmission()
+      this.getElecProdForecast()  // 新增:获取发电趋势预测
     },
     },
     async getElecWaterCost() {
     async getElecWaterCost() {
       const {data: thisMonthElecMeter} = await qryElecMeterByDate(DateTool.thisMonth(), this.areaType)
       const {data: thisMonthElecMeter} = await qryElecMeterByDate(DateTool.thisMonth(), this.areaType)
       const {data: thisMonthWaterMeter} = await qryWaterMeterByDate(DateTool.thisMonth(), this.areaType)
       const {data: thisMonthWaterMeter} = await qryWaterMeterByDate(DateTool.thisMonth(), this.areaType)
       const {data: lastMonthElecMeter} = await qryElecMeterByDate(DateTool.lastMonth(), this.areaType)
       const {data: lastMonthElecMeter} = await qryElecMeterByDate(DateTool.lastMonth(), this.areaType)
       const {data: lastMonthWaterMeter} = await qryWaterMeterByDate(DateTool.lastMonth(), this.areaType)
       const {data: lastMonthWaterMeter} = await qryWaterMeterByDate(DateTool.lastMonth(), this.areaType)
+
       const elec = {
       const elec = {
         unit: '度',
         unit: '度',
       }
       }
@@ -299,6 +440,7 @@ export default {
         elec.trend = 'down'
         elec.trend = 'down'
       }
       }
       this.useElec = elec
       this.useElec = elec
+
       const water = {
       const water = {
         unit: '吨',
         unit: '吨',
       }
       }
@@ -311,15 +453,16 @@ export default {
         water.trend = 'down'
         water.trend = 'down'
       }
       }
       this.userWater = water
       this.userWater = water
+
       const energy = {
       const energy = {
         unit: '元',
         unit: '元',
       }
       }
       energy.value = numToStr(thisMonthElecMeter.useCost + thisMonthWaterMeter.useCost)
       energy.value = numToStr(thisMonthElecMeter.useCost + thisMonthWaterMeter.useCost)
       if (thisMonthElecMeter.useCost + thisMonthWaterMeter.useCost > lastMonthElecMeter.useCost + lastMonthWaterMeter.useCost) {
       if (thisMonthElecMeter.useCost + thisMonthWaterMeter.useCost > lastMonthElecMeter.useCost + lastMonthWaterMeter.useCost) {
-        energy.rate = ((thisMonthElecMeter.useCost + thisMonthWaterMeter.useCost - lastMonthElecMeter.useCost - lastMonthWaterMeter.useCost) / lastMonthElecMeter.useCost * 100).toFixed(2)
+        energy.rate = ((thisMonthElecMeter.useCost + thisMonthWaterMeter.useCost - lastMonthElecMeter.useCost - lastMonthWaterMeter.useCost) / (lastMonthElecMeter.useCost || 1) * 100).toFixed(2)
         energy.trend = 'up'
         energy.trend = 'up'
       } else {
       } else {
-        energy.rate = ((lastMonthElecMeter.useCost + lastMonthWaterMeter.useCost - thisMonthElecMeter.useCost - thisMonthWaterMeter.useCost) / lastMonthElecMeter.useCost * 100).toFixed(2)
+        energy.rate = ((lastMonthElecMeter.useCost + lastMonthWaterMeter.useCost - thisMonthElecMeter.useCost - thisMonthWaterMeter.useCost) / (lastMonthElecMeter.useCost || 1) * 100).toFixed(2)
         energy.trend = 'down'
         energy.trend = 'down'
       }
       }
       this.userEnergy = energy
       this.userEnergy = energy
@@ -366,11 +509,15 @@ export default {
       }
       }
       this.listData = result;
       this.listData = result;
     },
     },
+    /**
+     * 获取近7天碳排放数据(用于图表)
+     */
     async getListSumCaMeterD() {
     async getListSumCaMeterD() {
       const {data} = await listSumCaMeterD({
       const {data} = await listSumCaMeterD({
         areaCode: this.areaType,
         areaCode: this.areaType,
         startRecTime: DateTool.lastXDay(30),
         startRecTime: DateTool.lastXDay(30),
         endRecTime: DateTool.now(),
         endRecTime: DateTool.now(),
+        timeType: 'day'
       })
       })
       const dates = DateTool.getDayOfRange(DateTool.lastXDay(7), DateTool.now());
       const dates = DateTool.getDayOfRange(DateTool.lastXDay(7), DateTool.now());
       const mapIndex = array2Map(data, "date");
       const mapIndex = array2Map(data, "date");
@@ -379,10 +526,104 @@ export default {
         xData: item,
         xData: item,
         yData1: mapIndex[item] ? mapIndex[item].caEmissionQuantity?.toFixed(2) : 0
         yData1: mapIndex[item] ? mapIndex[item].caEmissionQuantity?.toFixed(2) : 0
       }));
       }));
+    },
+    /**
+     * 获取本月累计碳排放量
+     * 调用 listSumCaMeterD 接口,timeType=month 按月汇总
+     */
+    async getMonthCaEmission() {
+      try {
+        const now = new Date();
+        const year = now.getFullYear();
+        const month = String(now.getMonth() + 1).padStart(2, '0');
+        const startRecTime = `${year}-${month}-01`;
+        const lastDay = new Date(year, now.getMonth() + 1, 0).getDate();
+        const endRecTime = `${year}-${month}-${String(lastDay).padStart(2, '0')}`;
+
+        const { code, data } = await listSumCaMeterD({
+          areaCode: this.areaType,
+          startRecTime: startRecTime,
+          endRecTime: endRecTime,
+          timeType: 'month'
+        });
+
+        if (code === 200 && data && data.length > 0) {
+          let totalEmission = 0;
+          data.forEach(item => {
+            totalEmission += item.caEmissionQuantity || 0;
+          });
+          this.monthCaEmission = totalEmission.toFixed(2);
+        } else {
+          this.monthCaEmission = '0.00';
+        }
+      } catch (error) {
+        console.error('获取本月碳排放数据失败:', error);
+        this.monthCaEmission = '0.00';
+      }
+    },
+    /**
+     * 新增:获取发电趋势预测数据
+     * 查询未来7天的发电预测数据
+     */
+    async getElecProdForecast() {
+      try {
+        const now = new Date();
+        const startRecTime = DateTool.now(); // 从今天开始
+
+        // 计算未来7天的日期
+        const futureDate = new Date(now);
+        futureDate.setDate(futureDate.getDate() + 7);
+        const year = futureDate.getFullYear();
+        const month = String(futureDate.getMonth() + 1).padStart(2, '0');
+        const day = String(futureDate.getDate()).padStart(2, '0');
+        const endRecTime = `${year}-${month}-${day}`;
+
+        const { code, data } = await calcElecProdForecastDateRange({
+          areaCode: this.areaType === '-1' ? undefined : this.areaType, // -1表示全部区域
+          startRecTime: startRecTime,
+          endRecTime: endRecTime
+        });
+
+        if (code === 200 && data && data.length > 0) {
+          // 处理数据:按日期分组汇总
+          this.elecProdForecastData = data.map(item => ({
+            date: this.formatDate(item.date),
+            elecProdQuantity: Number(item.elecProdQuantity || 0).toFixed(2),
+            upElecQuantity: Number(item.upElecQuantity || 0).toFixed(2)
+          }));
+
+          // 计算累计预测发电量
+          let total = 0;
+          data.forEach(item => {
+            total += Number(item.elecProdQuantity || 0);
+          });
+          this.totalElecProdQuantity = total.toFixed(2);
+        } else {
+          this.elecProdForecastData = [];
+          this.totalElecProdQuantity = '0.00';
+        }
+      } catch (error) {
+        console.error('获取发电趋势预测数据失败:', error);
+        this.elecProdForecastData = [];
+        this.totalElecProdQuantity = '0.00';
+      }
+    },
+    /**
+     * 格式化日期显示
+     * @param {Date|String} date - 日期对象或字符串
+     * @returns {String} 格式化后的日期字符串 MM-DD
+     */
+    formatDate(date) {
+      if (!date) return '';
+      const d = new Date(date);
+      const month = String(d.getMonth() + 1).padStart(2, '0');
+      const day = String(d.getDate()).padStart(2, '0');
+      return `${month}-${day}`;
     }
     }
   }
   }
 }
 }
 </script>
 </script>
+
 <style lang='scss' scoped>
 <style lang='scss' scoped>
 @import url("../index.scss");
 @import url("../index.scss");
 
 
@@ -564,4 +805,13 @@ export default {
     }
     }
   }
   }
 }
 }
+
+@keyframes spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
 </style>
 </style>

+ 258 - 109
ems-ui-cloud/src/views/largeScreen/net/left.vue

@@ -20,14 +20,21 @@
     </CusModule>
     </CusModule>
   </div>
   </div>
 </template>
 </template>
+
 <script>
 <script>
 import CusModule from '../components/CusModule.vue';
 import CusModule from '../components/CusModule.vue';
 import BaseChart from '@/components/BaseChart/index.vue'
 import BaseChart from '@/components/BaseChart/index.vue'
 import * as echarts from 'echarts'
 import * as echarts from 'echarts'
 import {array2Map, dateFormat, numToStr} from '@/utils'
 import {array2Map, dateFormat, numToStr} from '@/utils'
 import {mapState} from 'vuex';
 import {mapState} from 'vuex';
-import {pgSupplyDayTotalPv, pgSupplyTotalPv, predictionProdDateRange} from "@/api/screen";
+import {calcElecProdForecastDateRange} from "@/api/prediction/predictionProd";
 import {DateTool} from "@/utils/DateTool";
 import {DateTool} from "@/utils/DateTool";
+import {
+  getTodayPvSummary,
+  getMonthPvSummary,
+  getYearPvSummary,
+  listPvSupplyDaily
+} from "@/api/mgr/pvSupply";
 
 
 export default {
 export default {
   name: 'NetLeft',
   name: 'NetLeft',
@@ -52,6 +59,7 @@ export default {
       ],
       ],
       lineData: [],
       lineData: [],
       netlineData: [],
       netlineData: [],
+      totalUpElecQuantity: '0.00'
     };
     };
   },
   },
   components: {
   components: {
@@ -65,7 +73,7 @@ export default {
         grid: {
         grid: {
           top: "10%",
           top: "10%",
           right: '12%',
           right: '12%',
-          bottom: "10%",//也可设置left和right设置距离来控制图表的大小
+          bottom: "10%",
           contain: true
           contain: true
         },
         },
         tooltip: {
         tooltip: {
@@ -86,15 +94,14 @@ export default {
         xAxis: {
         xAxis: {
           data: this.lineData.map(item => item.xData),
           data: this.lineData.map(item => item.xData),
           axisLine: {
           axisLine: {
-            show: true, //隐藏X轴轴线
+            show: true,
           },
           },
           axisTick: {
           axisTick: {
-            show: true //隐藏X轴刻度
+            show: true
           },
           },
           axisLabel: {
           axisLabel: {
             show: true,
             show: true,
           },
           },
-
         },
         },
         yAxis: [{
         yAxis: [{
           type: "value",
           type: "value",
@@ -115,7 +122,7 @@ export default {
             },
             },
             axisLabel: {
             axisLabel: {
               show: true,
               show: true,
-              formatter: "{value} %", //右侧Y轴文字显示
+              formatter: "{value} %",
             }
             }
           },
           },
         ],
         ],
@@ -145,18 +152,18 @@ export default {
           {
           {
             name: "环比增长",
             name: "环比增长",
             type: "line",
             type: "line",
-            yAxisIndex: 1, //使用的 y 轴的 index,在单个图表实例中存在多个 y轴的时候有用
-            smooth: true, //平滑曲线显示
+            yAxisIndex: 1,
+            smooth: true,
             itemStyle: {
             itemStyle: {
               color: (params) => {
               color: (params) => {
-                return params.value > 0 ? '#F44336' : '#4CAF50'; // 绿色表示增长,红色表示下降
+                return params.value > 0 ? '#4CAF50' : '#F44336';
               },
               },
               borderColor: "#fff",
               borderColor: "#fff",
               borderWidth: 3
               borderWidth: 3
             },
             },
             lineStyle: {
             lineStyle: {
               color: (params) => {
               color: (params) => {
-                return params.value > 0 ? '#F44336' : '#4CAF50';
+                return params.value > 0 ? '#4CAF50' : '#F44336';
               }
               }
             },
             },
             data: this.lineData.map(item => item.yData2)
             data: this.lineData.map(item => item.yData2)
@@ -170,10 +177,18 @@ export default {
           show: false,
           show: false,
         },
         },
         grid: {
         grid: {
-          top: '10%'
+          top: '10%',
+          left: '5%',
+          right: '5%',
+          bottom: '10%',
+          containLabel: true
         },
         },
         tooltip: {
         tooltip: {
           trigger: "axis",
           trigger: "axis",
+          formatter: (params) => {
+            const item = params[0];
+            return `${item.axisValue}<br/>${item.marker}上网电量: ${item.value} kW·h`;
+          }
         },
         },
         yAxis: {
         yAxis: {
           name: "kW·h",
           name: "kW·h",
@@ -188,6 +203,10 @@ export default {
           splitLine: {
           splitLine: {
             show: false,
             show: false,
           },
           },
+          axisLabel: {
+            color: '#c1cadf',
+            fontSize: '10',
+          },
           data: this.netlineData.map(item => item.xData),
           data: this.netlineData.map(item => item.xData),
         },
         },
         series: [
         series: [
@@ -197,7 +216,7 @@ export default {
             smooth: true,
             smooth: true,
             lineStyle: {
             lineStyle: {
               normal: {
               normal: {
-                color: "#80DBE1", // 线条颜色
+                color: "#80DBE1",
               },
               },
             },
             },
             itemStyle: {
             itemStyle: {
@@ -205,9 +224,8 @@ export default {
               borderColor: "#fff",
               borderColor: "#fff",
               borderWidth: 3
               borderWidth: 3
             },
             },
-            areaStyle: { //区域填充样式
+            areaStyle: {
               normal: {
               normal: {
-                //线性渐变,前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是‘true’,则该四个值是绝对像素位置。
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                   offset: 0,
                   offset: 0,
                   color: "rgba(81, 139, 152,0.8)"
                   color: "rgba(81, 139, 152,0.8)"
@@ -217,8 +235,8 @@ export default {
                     color: "rgba(81, 139, 152, 0)"
                     color: "rgba(81, 139, 152, 0)"
                   }
                   }
                 ], false),
                 ], false),
-                shadowColor: 'rgba(81, 139, 152, 0.5)', //阴影颜色
-                shadowBlur: 20 //shadowBlur设图形阴影的模糊大小。配合shadowColor,shadowOffsetX/Y, 设置图形的阴影效果。
+                shadowColor: 'rgba(81, 139, 152, 0.5)',
+                shadowBlur: 20
               }
               }
             },
             },
             data: this.netlineData.map(item => item.yData),
             data: this.netlineData.map(item => item.yData),
@@ -243,112 +261,243 @@ export default {
     this.timer && clearInterval(this.timer)
     this.timer && clearInterval(this.timer)
   },
   },
   methods: {
   methods: {
-    getDatesOfLastTenDays() {
-      var dates = [];
-      var today = new Date();
-      for (var i = 1; i < 11; i++) {
-        var pastDate = new Date(today);
-        pastDate.setDate(today.getDate() + i); // 减去i天
-        var formattedDate = dateFormat(pastDate, 'MM-dd');
-        dates.push(formattedDate);
+    getNetData() {
+      this.getPvUpElecSummary()
+      this.getPvUpElecTrend()
+      this.getUpElecForecast()
+    },
+
+    /**
+     * 获取上网电量汇总(今日/本月/今年)
+     * 使用光伏产能接口
+     */
+    async getPvUpElecSummary() {
+      try {
+        const areaCode = this.areaType || '-1';
+
+        // 并发请求三个时间维度的数据
+        const [todayRes, monthRes, yearRes] = await Promise.all([
+          getTodayPvSummary(areaCode),
+          getMonthPvSummary(areaCode),
+          getYearPvSummary(areaCode)
+        ]);
+
+        // 更新数据
+        const [day, month, year] = this.energyList;
+
+        if (todayRes.code === 200 && todayRes.data) {
+          day.value = numToStr(todayRes.data.upElecQuantity || 0);
+        }
+
+        if (monthRes.code === 200 && monthRes.data) {
+          month.value = numToStr(monthRes.data.upElecQuantity || 0);
+        }
+
+        if (yearRes.code === 200 && yearRes.data) {
+          year.value = numToStr(yearRes.data.upElecQuantity || 0);
+        }
+
+        console.log('上网电量汇总:', {
+          今日: day.value,
+          本月: month.value,
+          今年: year.value
+        });
+      } catch (error) {
+        console.error('获取上网电量汇总失败:', error);
       }
       }
-      return dates.map(item => ({
-        xData: item,
-        yData1: parseFloat(((Math.random() * 100 + 200)).toFixed(1)),
-        yData2: parseFloat(((Math.random() * 100)).toFixed(1))
-      }));
     },
     },
-    getLatestDays() {
-      var dates = [];
-      var today = new Date();
-      for (var i = 1; i < 11; i++) {
-        var pastDate = new Date(today);
-        pastDate.setDate(today.getDate() + i); // 减去i天
-        var formattedDate = dateFormat(pastDate, 'MM-dd');
-        dates.push(formattedDate);
+
+    /**
+     * 获取光伏上网电量趋势(最近30天,含环比)
+     * 使用光伏产能按日统计接口
+     */
+    async getPvUpElecTrend() {
+      try {
+        const areaCode = this.areaType || '-1';
+        const endDate = new Date();
+        const thisMonthStartDate = new Date();
+        thisMonthStartDate.setDate(endDate.getDate() - 29); // 最近30天
+
+        const lastMonthStartDate = new Date();
+        lastMonthStartDate.setDate(endDate.getDate() - 60); // 前30天
+        const lastMonthEndDate = new Date();
+        lastMonthEndDate.setDate(endDate.getDate() - 30);
+
+        // 获取本月数据
+        const thisMonthRes = await listPvSupplyDaily({
+          areaCode: areaCode,
+          startRecTime: this.formatDateTime(thisMonthStartDate, '00:00:00'),
+          endRecTime: this.formatDateTime(endDate, '23:59:59'),
+          orderFlag: 'asc',
+          pageNum: 1,
+          pageSize: 100
+        });
+
+        // 获取上月数据
+        const lastMonthRes = await listPvSupplyDaily({
+          areaCode: areaCode,
+          startRecTime: this.formatDateTime(lastMonthStartDate, '00:00:00'),
+          endRecTime: this.formatDateTime(lastMonthEndDate, '23:59:59'),
+          orderFlag: 'asc',
+          pageNum: 1,
+          pageSize: 100
+        });
+
+        if (thisMonthRes.code === 200 && thisMonthRes.rows) {
+          // 生成日期范围
+          const days = this.generateDateRange(thisMonthStartDate, endDate);
+          const lastMonthDays = this.generateDateRange(lastMonthStartDate, lastMonthEndDate);
+
+          // 转换为日期索引Map
+          const thisMonthData = array2Map(
+            thisMonthRes.rows.map(item => ({
+              ...item,
+              date: this.formatDateOnly(item.statisticDate)
+            })),
+            'date'
+          );
+
+          const lastMonthData = array2Map(
+            lastMonthRes.rows ? lastMonthRes.rows.map(item => ({
+              ...item,
+              date: this.formatDateOnly(item.statisticDate)
+            })) : [],
+            'date'
+          );
+
+          // 生成图表数据
+          this.lineData = days.map((date, index) => {
+            const thisMonthItem = thisMonthData[date];
+            const thisMonthValue = thisMonthItem ? parseFloat(thisMonthItem.upElecQuantity || 0) : 0;
+
+            const lastDate = lastMonthDays[index];
+            const lastMonthItem = lastMonthData[lastDate];
+            const lastMonthValue = lastMonthItem ? parseFloat(lastMonthItem.upElecQuantity || 0) : 0;
+
+            // 计算环比增长率
+            let ratio = 0;
+            if (lastMonthValue > 0) {
+              ratio = parseFloat(((thisMonthValue - lastMonthValue) / lastMonthValue * 100).toFixed(1));
+            } else if (thisMonthValue > 0) {
+              ratio = 100; // 上月无数据,本月有数据,视为100%增长
+            }
+
+            return {
+              xData: this.formatDateDisplay(date),
+              yData1: thisMonthValue.toFixed(2),
+              yData2: ratio
+            };
+          });
+
+          console.log('光伏上网电量趋势加载成功:', this.lineData.length, '天');
+        }
+      } catch (error) {
+        console.error('获取光伏上网电量趋势失败:', error);
+        this.lineData = [];
       }
       }
-      return dates.map(item => ({
-        xData: item,
-        yData: parseFloat(((Math.random() * 100 + 100)).toFixed(1))
-      }));
     },
     },
-    getNetData() {
-      this.getPgSupplyDayTotalPv()
-      this.getNext5Days()
-      this.getPgSupply()
+
+    /**
+     * 获取上网电量预测(未来10天)
+     */
+    async getUpElecForecast() {
+      try {
+        const now = new Date();
+        const startRecTime = DateTool.now();
+
+        // 计算未来9天的日期(包含今天共10天)
+        const futureDate = new Date(now);
+        futureDate.setDate(futureDate.getDate() + 9);
+        const endRecTime = this.formatDateOnly(futureDate);
+
+        const {code, data} = await calcElecProdForecastDateRange({
+          areaCode: this.areaType === '-1' ? undefined : this.areaType,
+          startRecTime: startRecTime,
+          endRecTime: endRecTime
+        });
+
+        if (code === 200 && data && data.length > 0) {
+          // 生成日期范围
+          const dates = DateTool.getDayOfRange(startRecTime, endRecTime);
+          const mapIndex = array2Map(data, "date");
+
+          this.netlineData = dates.map(item => ({
+            xData: this.formatDateDisplay(item),
+            yData: mapIndex[item] ? Number(mapIndex[item].upElecQuantity).toFixed(2) : 0
+          }));
+
+          // 计算累计预测上网电量
+          let total = 0;
+          data.forEach(item => {
+            total += Number(item.upElecQuantity || 0);
+          });
+          this.totalUpElecQuantity = total.toFixed(2);
+
+          console.log('上网电量预测加载成功:', this.netlineData.length, '天');
+        } else {
+          this.netlineData = [];
+          this.totalUpElecQuantity = '0.00';
+        }
+      } catch (error) {
+        console.error('获取上网电量预测数据失败:', error);
+        this.netlineData = [];
+        this.totalUpElecQuantity = '0.00';
+      }
     },
     },
 
 
-    async getNext5Days() {
-      const next5Day = DateTool.addDays(DateTool.now(), 4);
-      const {data} = await predictionProdDateRange({
-        areaCode: this.areaType,
-        startRecTime: DateTool.now(),
-        endRecTime: next5Day,
-      })
-
-      const dates = DateTool.getDayOfRange(DateTool.now(), next5Day);
-      const mapIndex = array2Map(data, "date");
-      this.netlineData = dates.map(item => ({
-        xData: item,
-        yData: mapIndex[item] ? mapIndex[item].upElecQuantity : 0
-      }))
+    /**
+     * 格式化日期时间
+     */
+    formatDateTime(date, time) {
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      return `${year}-${month}-${day} ${time}`;
     },
     },
 
 
-    async getPgSupply() {
-      const {data: pgPvSupplyTotalPv} = await pgSupplyTotalPv({
-        areaCode: this.areaType
-      })
-      if (!pgPvSupplyTotalPv) {
-        return
+    /**
+     * 格式化日期为 YYYY-MM-DD
+     */
+    formatDateOnly(date) {
+      if (!date) return '';
+
+      if (typeof date === 'string') {
+        return date.split(' ')[0].split('T')[0];
       }
       }
-      const {
-        thisDay,
-        thisMonth,
-        thisYear,
-        total
-      } = pgPvSupplyTotalPv
-      const [day, monthPv, yearPv] = this.energyList
-      monthPv.value = numToStr(thisMonth.upElecQuantity)
-      yearPv.value = numToStr(thisYear.upElecQuantity)
-      day.value = numToStr(thisDay.upElecQuantity)
+
+      const dateObj = new Date(date);
+      const year = dateObj.getFullYear();
+      const month = String(dateObj.getMonth() + 1).padStart(2, '0');
+      const day = String(dateObj.getDate()).padStart(2, '0');
+      return `${year}-${month}-${day}`;
     },
     },
 
 
-    async getPgSupplyDayTotalPv() {
-      const {data: pgPvSupplyThisMonthTotalPv} = await pgSupplyDayTotalPv({
-        areaCode: this.areaType,
-        startRecTime: DateTool.lastXDay(30),
-        endRecTime: DateTool.now(),
-      })
-      if (!pgPvSupplyThisMonthTotalPv) {
-        return
+    /**
+     * 格式化日期显示为 MM-DD
+     */
+    formatDateDisplay(dateStr) {
+      if (!dateStr) return '';
+      const parts = dateStr.split('-');
+      if (parts.length === 3) {
+        return `${parts[1]}-${parts[2]}`;
       }
       }
-      const {data: pgPvSupplyLastMonthTotalPv} = await pgSupplyDayTotalPv({
-        areaCode: this.areaType,
-        startRecTime: DateTool.lastXDay(61),
-        endRecTime: DateTool.lastXDay(31),
-      })
-      const days = DateTool.getDayOfRange(DateTool.lastXDay(30), DateTool.now())
-      const lastMonthDays = DateTool.getDayOfRange(DateTool.lastXDay(61), DateTool.lastXDay(31))
-      const thisMonthData = array2Map(pgPvSupplyThisMonthTotalPv)
-      const lastMonthData = array2Map(pgPvSupplyLastMonthTotalPv)
-      this.lineData = days.map((item, index) => {
-        const thisMonthDataItem = thisMonthData[item] && thisMonthData[item].upElecQuantity
-        let lastMonthDataItem = 0;
-        const lastDate = lastMonthDays[index]
-        if (lastMonthData[lastDate]) {
-          lastMonthDataItem = lastMonthData[lastDate].upElecQuantity
-        }
-        let ratio = 0;
-        if (lastMonthDataItem) {
-          ratio = ((thisMonthDataItem - lastMonthDataItem) / lastMonthDataItem * 100).toFixed(1)
-        }
-        return {
-          xData: item,
-          yData1: thisMonthDataItem,
-          yData2: ratio
-        }
-      })
+      return dateStr;
     },
     },
+
+    /**
+     * 生成日期范围数组
+     */
+    generateDateRange(startDate, endDate) {
+      const dates = [];
+      const currentDate = new Date(startDate);
+
+      while (currentDate <= endDate) {
+        dates.push(this.formatDateOnly(new Date(currentDate)));
+        currentDate.setDate(currentDate.getDate() + 1);
+      }
+
+      return dates;
+    }
   }
   }
 }
 }
 </script>
 </script>

+ 175 - 68
ems-ui-cloud/src/views/largeScreen/net/right.vue

@@ -22,6 +22,7 @@
     </CusModule>
     </CusModule>
   </div>
   </div>
 </template>
 </template>
+
 <script>
 <script>
 import CusModule from '../components/CusModule.vue';
 import CusModule from '../components/CusModule.vue';
 import BaseChart from '@/components/BaseChart/index.vue'
 import BaseChart from '@/components/BaseChart/index.vue'
@@ -30,8 +31,9 @@ import {array2Map} from '@/utils';
 import * as echarts from 'echarts'
 import * as echarts from 'echarts'
 import {mapState} from 'vuex';
 import {mapState} from 'vuex';
 import {getPvPriceConfig} from "@/api/basecfg/elecPvPrice";
 import {getPvPriceConfig} from "@/api/basecfg/elecPvPrice";
-import {pgSupplyMonthTotalPg, pgSupplyMonthTotalPv} from "@/api/screen";
+import {pgSupplyMonthTotalPg} from "@/api/screen";
 import {DateTool} from "@/utils/DateTool";
 import {DateTool} from "@/utils/DateTool";
+import {listPvSupplyMonthly} from "@/api/mgr/pvSupply";
 
 
 export default {
 export default {
   name: 'NetRight',
   name: 'NetRight',
@@ -67,11 +69,14 @@ export default {
   computed: {
   computed: {
     ...mapState('userState', ['areaType']),
     ...mapState('userState', ['areaType']),
     barOptions() {
     barOptions() {
-      const xData = this.barData.map(item => item.xData)
+      const xData = this.barData.map(item => item.xData);
       const line = this.barData.map(item => item.yData);
       const line = this.barData.map(item => item.yData);
       return {
       return {
         tooltip: {
         tooltip: {
           trigger: 'item',
           trigger: 'item',
+          formatter: (params) => {
+            return `${params.name}<br/>${params.marker}上网收益: ${params.value} 万元`;
+          }
         },
         },
         grid: {
         grid: {
           left: '5%',
           left: '5%',
@@ -86,13 +91,13 @@ export default {
         xAxis: {
         xAxis: {
           data: xData,
           data: xData,
           axisLabel: {
           axisLabel: {
-            margin: 10, //刻度标签与轴线之间的距离。
+            margin: 10,
           },
           },
           axisLine: {
           axisLine: {
-            show: false //不显示x轴
+            show: false
           },
           },
           axisTick: {
           axisTick: {
-            show: false //不显示刻度
+            show: false
           },
           },
           boundaryGap: true,
           boundaryGap: true,
           splitLine: {
           splitLine: {
@@ -120,7 +125,7 @@ export default {
           }
           }
         }],
         }],
         series: [
         series: [
-          {//柱底圆片
+          {
             name: "",
             name: "",
             type: "pictorialBar",
             type: "pictorialBar",
             symbolSize: [20, 15],
             symbolSize: [20, 15],
@@ -141,9 +146,8 @@ export default {
             },
             },
             data: line
             data: line
           },
           },
-          //柱体
           {
           {
-            name: '',
+            name: '上网收益',
             type: 'bar',
             type: 'bar',
             barWidth: 20,
             barWidth: 20,
             barGap: '0%',
             barGap: '0%',
@@ -156,7 +160,7 @@ export default {
                   y2: 1,
                   y2: 1,
                   type: "linear",
                   type: "linear",
                   global: false,
                   global: false,
-                  colorStops: [{//第一节下面
+                  colorStops: [{
                     offset: 0,
                     offset: 0,
                     color: "rgba(0,255,245,0.5)"
                     color: "rgba(0,255,245,0.5)"
                   }, {
                   }, {
@@ -166,10 +170,8 @@ export default {
                 }
                 }
               }
               }
             },
             },
-
             data: line
             data: line
           },
           },
-          //柱顶圆片
           {
           {
             name: "",
             name: "",
             type: "pictorialBar",
             type: "pictorialBar",
@@ -180,16 +182,16 @@ export default {
             itemStyle: {
             itemStyle: {
               normal: {
               normal: {
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1,
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1,
-                    [{
-                      offset: 0,
-                      color: "rgba(89,211,255,1)"
-                    },
-                      {
-                        offset: 1,
-                        color: "rgba(23,237,194,1)"
-                      }
-                    ],
-                    false
+                  [{
+                    offset: 0,
+                    color: "rgba(89,211,255,1)"
+                  },
+                    {
+                      offset: 1,
+                      color: "rgba(23,237,194,1)"
+                    }
+                  ],
+                  false
                 ),
                 ),
               }
               }
             },
             },
@@ -208,6 +210,10 @@ export default {
         },
         },
         tooltip: {
         tooltip: {
           trigger: "axis",
           trigger: "axis",
+          formatter: (params) => {
+            const item = params[0];
+            return `${item.axisValue}<br/>${item.marker}购电费用: ${item.value} 万元`;
+          }
         },
         },
         yAxis: {
         yAxis: {
           name: "万元",
           name: "万元",
@@ -227,11 +233,11 @@ export default {
         series: [
         series: [
           {
           {
             type: 'line',
             type: 'line',
-            name: "费用",
+            name: "购电费用",
             smooth: true,
             smooth: true,
             lineStyle: {
             lineStyle: {
               normal: {
               normal: {
-                color: "#80DBE1", // 线条颜色
+                color: "#80DBE1",
               },
               },
             },
             },
             itemStyle: {
             itemStyle: {
@@ -239,9 +245,8 @@ export default {
               borderColor: "#fff",
               borderColor: "#fff",
               borderWidth: 3
               borderWidth: 3
             },
             },
-            areaStyle: { //区域填充样式
+            areaStyle: {
               normal: {
               normal: {
-                //线性渐变,前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是‘true’,则该四个值是绝对像素位置。
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                   offset: 0,
                   offset: 0,
                   color: "rgba(81, 139, 152,0.8)"
                   color: "rgba(81, 139, 152,0.8)"
@@ -251,15 +256,14 @@ export default {
                     color: "rgba(81, 139, 152, 0)"
                     color: "rgba(81, 139, 152, 0)"
                   }
                   }
                 ], false),
                 ], false),
-                shadowColor: 'rgba(81, 139, 152, 0.5)', //阴影颜色
-                shadowBlur: 20 //shadowBlur设图形阴影的模糊大小。配合shadowColor,shadowOffsetX/Y, 设置图形的阴影效果。
+                shadowColor: 'rgba(81, 139, 152, 0.5)',
+                shadowBlur: 20
               }
               }
             },
             },
             data: this.lineData.map(item => item.yData),
             data: this.lineData.map(item => item.yData),
           },
           },
         ],
         ],
       }
       }
-
     }
     }
   },
   },
   watch: {
   watch: {
@@ -279,62 +283,165 @@ export default {
   },
   },
   methods: {
   methods: {
     getNetData() {
     getNetData() {
-      this.pgSupplyMonthTotalPv()
+      this.getPvUpElecEarn()
       this.pgSupplyMonthTotalPg()
       this.pgSupplyMonthTotalPg()
       this.qryGwPriceConfig()
       this.qryGwPriceConfig()
     },
     },
+
+    /**
+     * 获取光伏上网电价配置
+     */
     async qryGwPriceConfig() {
     async qryGwPriceConfig() {
-      const {data} = await getPvPriceConfig(1)
+      const {data} = await getPvPriceConfig(1);
       if (!data) {
       if (!data) {
         return;
         return;
       }
       }
-      const [lessPrice, oriPrice, state, local] = this.priceList
-      lessPrice.value = data.objPrice + data.stateSubsidy + data.localSubsidy
-      oriPrice.value = data.objPrice
-      state.value = data.stateSubsidy
-      local.value = data.localSubsidy
-      this.priceList = [lessPrice, oriPrice, state, local]
+      const [lessPrice, oriPrice, state, local] = this.priceList;
+      lessPrice.value = data.objPrice + data.stateSubsidy + data.localSubsidy;
+      oriPrice.value = data.objPrice;
+      state.value = data.stateSubsidy;
+      local.value = data.localSubsidy;
+      this.priceList = [lessPrice, oriPrice, state, local];
     },
     },
-    async pgSupplyMonthTotalPv() {
-      const {data} = await pgSupplyMonthTotalPv({
-        startRecTime: DateTool.nearYearStartDay(),
-        endRecTime: DateTool.now(),
-        areaCode: this.areaType,
-      })
-      if (!data) {
-        return;
+
+    /**
+     * 获取上网收益(最近12个月)
+     * 使用光伏产能按月统计接口
+     */
+    async getPvUpElecEarn() {
+      try {
+        const areaCode = this.areaType || '-1';
+
+        // 计算最近12个月的起止时间
+        const endDate = new Date();
+        const startDate = new Date();
+        startDate.setMonth(endDate.getMonth() - 11); // 往前推11个月,加上当前月共12个月
+        startDate.setDate(1); // 设置为月初
+
+        const startRecTime = this.formatDateTime(startDate, '00:00:00');
+        const endRecTime = this.formatDateTime(endDate, '23:59:59');
+
+        console.log('查询上网收益时间范围:', { startRecTime, endRecTime });
+
+        const response = await listPvSupplyMonthly({
+          areaCode: areaCode,
+          startRecTime: startRecTime,
+          endRecTime: endRecTime,
+          orderFlag: 'asc',
+          pageNum: 1,
+          pageSize: 12
+        });
+
+        if (response.code === 200 && response.rows) {
+          // 生成最近12个月的月份列表
+          const months = this.getLast12Months();
+
+          // 转换为月份索引Map
+          const indexMap = array2Map(
+            response.rows.map(item => ({
+              ...item,
+              month: item.statisticMonth // yyyy-MM 格式
+            })),
+            'month'
+          );
+
+          // 生成图表数据
+          this.barData = months.map(month => {
+            const monthData = indexMap[month];
+            const earnValue = monthData ? parseFloat(monthData.upElecEarn || 0) / 10000 : 0;
+
+            return {
+              xData: month.substring(2), // 显示为 YY-MM
+              yData: earnValue.toFixed(1)
+            };
+          });
+
+          console.log('上网收益加载成功:', {
+            月份数: this.barData.length,
+            月份列表: months,
+            数据: this.barData
+          });
+        }
+      } catch (error) {
+        console.error('获取上网收益失败:', error);
+        this.barData = [];
       }
       }
-      const month = DateTool.getMonthsOfYearAgo()
-      const indexMap = array2Map(data, "startRecTime")
-      this.barData = month.map(item => (
-          {
-            xData: item.substring(2),
-            yData: indexMap[item] ? (indexMap[item].upElecEarn / 10000).toFixed(1) : 0
-          }
-      ))
     },
     },
+
+    /**
+     * 获取今年购电费用(最近12个月)
+     * 修改为与上网收益保持一致,显示最近12个月
+     */
     async pgSupplyMonthTotalPg() {
     async pgSupplyMonthTotalPg() {
-      const thisThisYearStart = `${DateTool.now(DateTool.DateFormat.YYYY)}-01-01`
-      const {data} = await pgSupplyMonthTotalPg({
-        startRecTime: thisThisYearStart,
-        endRecTime: DateTool.now(),
-        areaCode: this.areaType,
-      })
-      if (!data) {
-        return;
+      try {
+        // 计算最近12个月的起止时间
+        const endDate = new Date();
+        const startDate = new Date();
+        startDate.setMonth(endDate.getMonth() - 11);
+        startDate.setDate(1);
+
+        const startRecTime = this.formatDateTime(startDate, '00:00:00');
+        const endRecTime = this.formatDateTime(endDate, '23:59:59');
+
+        const {data} = await pgSupplyMonthTotalPg({
+          startRecTime: startRecTime,
+          endRecTime: endRecTime,
+          areaCode: this.areaType,
+        });
+
+        if (!data) {
+          return;
+        }
+
+        // 生成最近12个月的月份列表
+        const months = this.getLast12Months();
+        const indexMap = array2Map(data, "startRecTime");
+
+        this.lineData = months.map(month => ({
+          xData: month.substring(2), // 显示为 YY-MM
+          yData: indexMap[month] ? (indexMap[month].useElecCost / 10000).toFixed(1) : 0
+        }));
+
+        console.log('购电费用加载成功:', this.lineData.length, '个月');
+      } catch (error) {
+        console.error('获取购电费用失败:', error);
+        this.lineData = [];
       }
       }
-      const month = DateTool.getMonthsOfYearAgo()
-      const indexMap = array2Map(data, "startRecTime")
-      this.lineData = month.map(item => (
-          {
-            xData: item.substring(2),
-            yData: indexMap[item] ? (indexMap[item].useElecCost / 10000).toFixed(1) : 0
-          }
-      ))
+    },
+
+    /**
+     * 获取最近12个月的月份列表
+     * @returns {Array<String>} 返回格式为 ['yyyy-MM', ...] 的数组
+     */
+    getLast12Months() {
+      const months = [];
+      const currentDate = new Date();
+
+      for (let i = 11; i >= 0; i--) {
+        const date = new Date(currentDate);
+        date.setMonth(currentDate.getMonth() - i);
+
+        const year = date.getFullYear();
+        const month = String(date.getMonth() + 1).padStart(2, '0');
+        months.push(`${year}-${month}`);
+      }
+
+      return months;
+    },
+
+    /**
+     * 格式化日期时间
+     */
+    formatDateTime(date, time) {
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      return `${year}-${month}-${day} ${time}`;
     }
     }
   }
   }
 }
 }
 </script>
 </script>
+
 <style lang='scss' scoped>
 <style lang='scss' scoped>
 @import url("../index.scss");
 @import url("../index.scss");
 
 

+ 196 - 84
ems-ui-cloud/src/views/largeScreen/soc/left.vue

@@ -4,10 +4,10 @@
       <div class="soc-container">
       <div class="soc-container">
         <div class="soc-data-point soc-left-data">
         <div class="soc-data-point soc-left-data">
           <div>
           <div>
-            <span class="soc-value">{{ meterReading.actual }}</span>
+            <span class="soc-value">{{ meterReading.electricMeter }}</span>
             <span class="unit">个</span>
             <span class="unit">个</span>
           </div>
           </div>
-          <div class="soc-description">实采数据</div>
+          <div class="soc-description">电表数量</div>
         </div>
         </div>
         <div class="soc-central-data">
         <div class="soc-central-data">
           <span class="soc-value">{{ meterReading.rate }}</span>
           <span class="soc-value">{{ meterReading.rate }}</span>
@@ -15,17 +15,17 @@
         </div>
         </div>
         <div class="soc-data-point soc-right-data">
         <div class="soc-data-point soc-right-data">
           <div>
           <div>
-            <span class="soc-failure">{{ meterReading.fail }}</span>
-            <span class="soc-failure unit">个</span>
+            <span class="soc-value">{{ meterReading.waterMeter }}</span>
+            <span class="unit">个</span>
           </div>
           </div>
-          <div class="soc-description">失败数据</div>
+          <div class="soc-description">水表数量</div>
         </div>
         </div>
         <div class="soc-bottom-data">
         <div class="soc-bottom-data">
           <div>
           <div>
             <span class="soc-value">{{ meterReading.total }}</span>
             <span class="soc-value">{{ meterReading.total }}</span>
             <span class="unit">个</span>
             <span class="unit">个</span>
           </div>
           </div>
-          <div class="soc-description">应采数据</div>
+          <div class="soc-description">总表数</div>
         </div>
         </div>
       </div>
       </div>
     </CusModule>
     </CusModule>
@@ -33,30 +33,34 @@
       <div class="soc-pie-chart-container">
       <div class="soc-pie-chart-container">
         <BaseChart width="100%" height="100%" :option="pieOptions"></BaseChart>
         <BaseChart width="100%" height="100%" :option="pieOptions"></BaseChart>
       </div>
       </div>
-
     </CusModule>
     </CusModule>
     <CusModule class="module-top" title="新能源消纳情况">
     <CusModule class="module-top" title="新能源消纳情况">
       <BaseChart height="350px" width="100%" :option="lineOptions"/>
       <BaseChart height="350px" width="100%" :option="lineOptions"/>
     </CusModule>
     </CusModule>
   </div>
   </div>
 </template>
 </template>
+
 <script>
 <script>
 import CusModule from '../components/CusModule.vue';
 import CusModule from '../components/CusModule.vue';
 import BaseChart from '@/components/BaseChart/index.vue'
 import BaseChart from '@/components/BaseChart/index.vue'
-import {dateFormat} from '@/utils';
 import * as echarts from 'echarts'
 import * as echarts from 'echarts'
 import {mapState} from 'vuex';
 import {mapState} from 'vuex';
-import {cntDevice} from "@/api/device/meterDevice";
-import { listFacsMeterHourSta } from '@/api/device/energyConsumption'
 import {DateTool} from "@/utils/DateTool";
 import {DateTool} from "@/utils/DateTool";
-import {pgSupplyDayTotalPv} from "@/api/screen";
+import { listAreaDevice } from "@/api/device/meterDevice";
+import { listFacsMeterHourSta } from '@/api/device/energyConsumption'
+import { getPvSupplyDistributionDetail } from "@/api/mgr/pvSupply"; // 使用已有的API
 
 
 export default {
 export default {
   name: 'SocLeft',
   name: 'SocLeft',
   data() {
   data() {
     return {
     return {
       lineData: [],
       lineData: [],
-      meterReading: {},
+      meterReading: {
+        electricMeter: 0,
+        waterMeter: 0,
+        total: 0,
+        rate: 100
+      },
       energyUse: []
       energyUse: []
     };
     };
   },
   },
@@ -89,7 +93,7 @@ export default {
                   '{b|{b}}',
                   '{b|{b}}',
                   '{c|{c}}{b|kW·h}',
                   '{c|{c}}{b|kW·h}',
                   '{d|{d}%}'
                   '{d|{d}%}'
-                ].join('\n'), // 用\n来换行
+                ].join('\n'),
                 rich: {
                 rich: {
                   b: {
                   b: {
                     color: '#fff',
                     color: '#fff',
@@ -136,6 +140,13 @@ export default {
         },
         },
         tooltip: {
         tooltip: {
           trigger: "axis",
           trigger: "axis",
+          formatter: (params) => {
+            let result = params[0].name + '<br/>';
+            params.forEach(item => {
+              result += `${item.marker}光伏自用: ${item.value} kW·h<br/>`;
+            });
+            return result;
+          }
         },
         },
         yAxis: {
         yAxis: {
           name: "kW·h",
           name: "kW·h",
@@ -155,11 +166,11 @@ export default {
         series: [
         series: [
           {
           {
             type: 'line',
             type: 'line',
-            name: "",
+            name: "自用电量",
             smooth: true,
             smooth: true,
             lineStyle: {
             lineStyle: {
               normal: {
               normal: {
-                color: "#80DBE1", // 线条颜色
+                color: "#80DBE1",
               },
               },
             },
             },
             itemStyle: {
             itemStyle: {
@@ -167,9 +178,8 @@ export default {
               borderColor: "#fff",
               borderColor: "#fff",
               borderWidth: 3
               borderWidth: 3
             },
             },
-            areaStyle: { //区域填充样式
+            areaStyle: {
               normal: {
               normal: {
-                //线性渐变,前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是‘true’,则该四个值是绝对像素位置。
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                   offset: 0,
                   offset: 0,
                   color: "rgba(81, 139, 152,0.8)"
                   color: "rgba(81, 139, 152,0.8)"
@@ -179,8 +189,8 @@ export default {
                     color: "rgba(81, 139, 152, 0)"
                     color: "rgba(81, 139, 152, 0)"
                   }
                   }
                 ], false),
                 ], false),
-                shadowColor: 'rgba(81, 139, 152, 0.5)', //阴影颜色
-                shadowBlur: 20 //shadowBlur设图形阴影的模糊大小。配合shadowColor,shadowOffsetX/Y, 设置图形的阴影效果。
+                shadowColor: 'rgba(81, 139, 152, 0.5)',
+                shadowBlur: 20
               }
               }
             },
             },
             data: this.lineData.map(item => item.yData),
             data: this.lineData.map(item => item.yData),
@@ -205,41 +215,54 @@ export default {
     this.timer && clearInterval(this.timer)
     this.timer && clearInterval(this.timer)
   },
   },
   methods: {
   methods: {
-    getDatesOfLastTenDays() {
-      var dates = [];
-      var today = new Date();
-      for (var i = 0; i < 10; i++) {
-        var pastDate = new Date(today);
-        pastDate.setDate(today.getDate() - i); // 减去i天
-        var formattedDate = dateFormat(pastDate, 'MM-dd');
-        dates.push(formattedDate);
-      }
-      return dates.reverse().map(item => ({
-        xData: item,
-        yData: parseFloat(((Math.random() * 100 + 100)).toFixed(1))
-      }));
-    },
-
     qryPageDatas() {
     qryPageDatas() {
-      this.getCntDevice()
+      this.getMeterDeviceCount()
       this.getDeviceElecMeter()
       this.getDeviceElecMeter()
-      this.getPgSupplyDayTotalPv()
+      this.getPvSupplyData()
     },
     },
-    async getCntDevice() {
-      const {data} = await cntDevice({
-        areaCode: this.areaType
-      })
-      const {
-        totalDevice,
-        onlineDevice
-      } = data
-      this.meterReading = {
-        total: totalDevice,
-        actual: onlineDevice,
-        fail: totalDevice - onlineDevice,
-        rate: (onlineDevice / totalDevice * 100).toFixed(1)
+
+    /**
+     * 获取表计数量统计
+     */
+    async getMeterDeviceCount() {
+      try {
+        const northAreaCode = '321283124S3001'
+        const southAreaCode = '321283124S3002'
+
+        const params = [
+          { locationRef: northAreaCode, meterCls: 45, colMode: 0, pageNum: 1, pageSize: 1 },
+          { locationRef: northAreaCode, meterCls: 70, colMode: 0, pageNum: 1, pageSize: 1 },
+          { locationRef: southAreaCode, meterCls: 45, colMode: 0, pageNum: 1, pageSize: 1 },
+          { locationRef: southAreaCode, meterCls: 70, colMode: 0, pageNum: 1, pageSize: 1 }
+        ]
+
+        const results = await Promise.all(
+          params.map(param => listAreaDevice(param))
+        )
+
+        const [northElec, northWater, southElec, southWater] = results.map(res => res.total || 0)
+
+        const totalElectricMeter = northElec + southElec
+        const totalWaterMeter = northWater + southWater
+        const totalMeter = totalElectricMeter + totalWaterMeter
+
+        this.meterReading = {
+          electricMeter: totalElectricMeter,
+          waterMeter: totalWaterMeter,
+          total: totalMeter,
+          rate: 100
+        }
+      } catch (error) {
+        console.error('获取表计数量失败:', error)
+        this.meterReading = {
+          electricMeter: 0,
+          waterMeter: 0,
+          total: 0,
+          rate: 100
+        }
       }
       }
     },
     },
+
     async getDeviceElecMeter() {
     async getDeviceElecMeter() {
       const params = {
       const params = {
         startRecTime: DateTool.now(DateTool.DateFormat.YYYY_MM_DD_00_00_00),
         startRecTime: DateTool.now(DateTool.DateFormat.YYYY_MM_DD_00_00_00),
@@ -255,40 +278,138 @@ export default {
         }));
         }));
       })
       })
     },
     },
-    async getPgSupplyDayTotalPv() {
-      const {data: pgPvHisSupply} = await pgSupplyDayTotalPv({
-        areaCode: this.areaType,
-        startRecTime: DateTool.lastXDay(30),
-        endRecTime: DateTool.now(),
-      })
-      if (!pgPvHisSupply) {
-        return
-      }
 
 
-      const dateTips = DateTool.getDayOfRange(DateTool.lastXDay(30), DateTool.now());
-      const dateIndexMap = {}
-      pgPvHisSupply.forEach(item => {
-        dateIndexMap[item.date] = item
-      })
-      const lineData = []
-      dateTips.forEach((item, index) => {
-        if (dateIndexMap[item]) {
-          lineData.push({
-            xData: item,
-            yData: dateIndexMap[item].useElecQuantity
+    /**
+     * 获取光伏自用电量数据(新能源消纳情况)
+     * 调用后端接口 getPvSupplyDistributionDetail
+     * 获取最近30天的自用电量数据
+     */
+    async getPvSupplyData() {
+      try {
+        // 计算最近30天的日期范围
+        const endDate = new Date()
+        const startDate = new Date()
+        startDate.setDate(endDate.getDate() - 29) // 最近30天
+
+        const params = {
+          areaCode: this.areaType || '-1', // 如果未选择区域,传-1获取所有区域汇总
+          startRecTime: this.formatDateTime(startDate, '00:00:00'),
+          endRecTime: this.formatDateTime(endDate, '23:59:59')
+        }
+
+        console.log('查询光伏自用电量参数:', params)
+
+        // 调用后端的 getPvSupplyDistributionDetail 接口
+        const response = await getPvSupplyDistributionDetail(params)
+
+        if (response && response.code === 200 && response.data) {
+          // 生成最近30天的日期数组
+          const dateTips = this.generateDateRange(startDate, endDate)
+
+          // 按日期汇总数据(可能有多个区域)
+          const dateMap = {}
+          response.data.forEach(item => {
+            // statisticDate 格式:yyyy-MM-dd 或 Date对象
+            const dateKey = this.formatDateOnly(item.statisticDate)
+            if (!dateMap[dateKey]) {
+              dateMap[dateKey] = 0
+            }
+            // 累加自用电量
+            const useElecQuantity = parseFloat(item.useElecQuantity || 0)
+            dateMap[dateKey] += useElecQuantity
           })
           })
-        } else {
-          lineData.push({
-            xData: item,
-            yData: 0
+
+          // 生成图表数据,确保每天都有数据点
+          this.lineData = dateTips.map(date => ({
+            xData: this.formatDateDisplay(date), // MM-DD格式显示
+            yData: parseFloat((dateMap[date] || 0).toFixed(2)) // 保留2位小数
+          }))
+
+          console.log('光伏自用电量数据加载成功:', {
+            数据条数: response.data.length,
+            汇总后: Object.keys(dateMap).length,
+            图表数据: this.lineData
           })
           })
+        } else {
+          console.warn('未获取到光伏数据,使用空数据', response)
+          this.initEmptyLineData()
         }
         }
-      })
-      this.lineData = lineData
+      } catch (error) {
+        console.error('获取光伏自用电量数据失败:', error)
+        // 失败时显示空数据
+        this.initEmptyLineData()
+      }
+    },
+
+    /**
+     * 初始化空的折线图数据
+     */
+    initEmptyLineData() {
+      const endDate = new Date()
+      const startDate = new Date()
+      startDate.setDate(endDate.getDate() - 29)
+      const dateTips = this.generateDateRange(startDate, endDate)
+      this.lineData = dateTips.map(date => ({
+        xData: this.formatDateDisplay(date),
+        yData: 0
+      }))
+    },
+
+    /**
+     * 格式化日期时间为 YYYY-MM-DD HH:mm:ss
+     */
+    formatDateTime(date, time) {
+      const year = date.getFullYear()
+      const month = String(date.getMonth() + 1).padStart(2, '0')
+      const day = String(date.getDate()).padStart(2, '0')
+      return `${year}-${month}-${day} ${time}`
     },
     },
+
+    /**
+     * 格式化日期为 YYYY-MM-DD
+     */
+    formatDateOnly(date) {
+      if (!date) return ''
+
+      // 如果是字符串,提取日期部分
+      if (typeof date === 'string') {
+        return date.split(' ')[0].split('T')[0]
+      }
+
+      // 如果是Date对象
+      const dateObj = new Date(date)
+      const year = dateObj.getFullYear()
+      const month = String(dateObj.getMonth() + 1).padStart(2, '0')
+      const day = String(dateObj.getDate()).padStart(2, '0')
+      return `${year}-${month}-${day}`
+    },
+
+    /**
+     * 格式化日期为 MM-DD(用于显示)
+     */
+    formatDateDisplay(dateStr) {
+      const parts = dateStr.split('-')
+      return `${parts[1]}-${parts[2]}`
+    },
+
+    /**
+     * 生成日期范围数组
+     */
+    generateDateRange(startDate, endDate) {
+      const dates = []
+      const currentDate = new Date(startDate)
+
+      while (currentDate <= endDate) {
+        dates.push(this.formatDateOnly(new Date(currentDate)))
+        currentDate.setDate(currentDate.getDate() + 1)
+      }
+
+      return dates
+    }
   }
   }
 }
 }
 </script>
 </script>
+
 <style lang='scss' scoped>
 <style lang='scss' scoped>
 @import url("../index.scss");
 @import url("../index.scss");
 
 
@@ -353,15 +474,6 @@ export default {
       font-size: 14px;
       font-size: 14px;
     }
     }
   }
   }
-
-  .soc-failure {
-    color: #F05050;
-    font-size: 18px;
-
-    &.unit {
-      font-size: 14px;
-    }
-  }
 }
 }
 
 
 .soc-pie-chart-container {
 .soc-pie-chart-container {

+ 119 - 50
ems-ui-cloud/src/views/largeScreen/soc/right.vue

@@ -36,6 +36,7 @@
     </CusModule>
     </CusModule>
   </div>
   </div>
 </template>
 </template>
+
 <script>
 <script>
 import CusModule from '../components/CusModule.vue';
 import CusModule from '../components/CusModule.vue';
 import CusProgress from '../components/CusProgress.vue';
 import CusProgress from '../components/CusProgress.vue';
@@ -58,22 +59,17 @@ export default {
           name: "电",
           name: "电",
           image: require("@/assets/images/soc/item1.png"),
           image: require("@/assets/images/soc/item1.png"),
           unit: "kW·h",
           unit: "kW·h",
+          value: 0
         },
         },
-        // {
-        //   name: "燃气",
-        //   image: require("@/assets/images/soc/item2.png"),
-        //   value: 7945,
-        //   unit: "kg",
-        // },
         {
         {
           name: "水",
           name: "水",
           image: require("@/assets/images/soc/item3.png"),
           image: require("@/assets/images/soc/item3.png"),
           unit: "吨",
           unit: "吨",
+          value: 0
         },
         },
       ],
       ],
       ranking: [],
       ranking: [],
-      lineData: []
-
+      lineData: [] // 确保初始化为数组
     }
     }
   },
   },
   components: {
   components: {
@@ -87,7 +83,7 @@ export default {
       return {
       return {
         grid: {
         grid: {
           top: "10%",
           top: "10%",
-          bottom: "10%",//也可设置left和right设置距离来控制图表的大小
+          bottom: "10%",
           contain: true
           contain: true
         },
         },
         tooltip: {
         tooltip: {
@@ -97,20 +93,23 @@ export default {
             label: {
             label: {
               show: true
               show: true
             }
             }
+          },
+          formatter: (params) => {
+            const item = params[0];
+            return `${item.axisValue}<br/>${item.marker}用电量: ${item.value} kW·h`;
           }
           }
         },
         },
         xAxis: {
         xAxis: {
           data: this.lineData.map(item => item.xData),
           data: this.lineData.map(item => item.xData),
           axisLine: {
           axisLine: {
-            show: true, //隐藏X轴轴线
+            show: true,
           },
           },
           axisTick: {
           axisTick: {
-            show: true //隐藏X轴刻度
+            show: true
           },
           },
           axisLabel: {
           axisLabel: {
             show: true,
             show: true,
           },
           },
-
         },
         },
         yAxis: [{
         yAxis: [{
           type: "value",
           type: "value",
@@ -166,55 +165,125 @@ export default {
     this.timer && clearInterval(this.timer)
     this.timer && clearInterval(this.timer)
   },
   },
   methods: {
   methods: {
+    /**
+     * 获取设备用电排名
+     */
     async getDeviceElecMeter() {
     async getDeviceElecMeter() {
-      const params = {
-        startRecTime: DateTool.now(DateTool.DateFormat.YYYY_MM_DD_00_00_00),
-        endRecTime: DateTool.now(DateTool.DateFormat.YYYY_MM_DD_23_59_59),
-        meterCls: 45,
-        areaCode: this.areaType || -1,
-        facsCategory: 'Z',
-      };
-      listFacsMeterHourSta(params).then(response => {
-        const energyUse = response.data.map(item => ({
-          name: item.objName,
-          color: "#14c5f2",
-          value: item.totalElecQuantity || 0,
-        }));
-        this.ranking = _.slice(_.orderBy(energyUse, ['value'], ['desc']), 0, 5);
-      })
+      try {
+        const params = {
+          startRecTime: DateTool.now(DateTool.DateFormat.YYYY_MM_DD_00_00_00),
+          endRecTime: DateTool.now(DateTool.DateFormat.YYYY_MM_DD_23_59_59),
+          meterCls: 45,
+          areaCode: this.areaType || -1,
+          facsCategory: 'Z',
+        };
+
+        const response = await listFacsMeterHourSta(params);
+
+        if (response && response.data) {
+          const energyUse = response.data.map(item => ({
+            name: item.objName,
+            color: "#14c5f2",
+            value: parseFloat(item.totalElecQuantity || 0),
+          }));
+
+          // 取前5名
+          this.ranking = _.slice(_.orderBy(energyUse, ['value'], ['desc']), 0, 5);
+
+          console.log('用能排名加载成功:', this.ranking);
+        }
+      } catch (error) {
+        console.error('获取用能排名失败:', error);
+        this.ranking = [];
+      }
     },
     },
+
+    /**
+     * 获取未来5天用能预测
+     */
     async getDatesOfLastTenDays() {
     async getDatesOfLastTenDays() {
-      const next5Day = DateTool.addDays(DateTool.now(), 4);
-      const {data} = await listForecastConsumeDateRange({
-        areaCode: this.areaType,
-        startRecTime: DateTool.now(),
-        endRecTime: next5Day,
-      })
-      const dates = DateTool.getDayOfRange(DateTool.now(), next5Day);
-      const mapIndex = array2Map(data, "date");
-
-      this.lineData = dates.map(item => ({
-        xData: item,
-        yData1: mapIndex[item] ? mapIndex[item].elecUseQuantity : 0
-      }));
+      try {
+        const next5Day = DateTool.addDays(DateTool.now(), 4);
+
+        const {data} = await listForecastConsumeDateRange({
+          areaCode: this.areaType === '-1' ? undefined : this.areaType,
+          startRecTime: DateTool.now(),
+          endRecTime: next5Day,
+        });
+
+        // 生成日期范围
+        const dates = DateTool.getDayOfRange(DateTool.now(), next5Day);
+        const mapIndex = array2Map(data || [], "date");
+
+        this.lineData = dates.map(item => ({
+          xData: this.formatDateDisplay(item),
+          yData1: mapIndex[item] ? parseFloat(mapIndex[item].elecUseQuantity || 0).toFixed(2) : 0
+        }));
+
+        console.log('用能趋势预测加载成功:', this.lineData);
+      } catch (error) {
+        console.error('获取用能趋势预测失败:', error);
+        // 确保即使失败也初始化为空数组
+        this.lineData = [];
+      }
     },
     },
-    getSocData() {
-      this.lineData = this.getDatesOfLastTenDays()
-      this.getMeterElecWater()
-      this.getDeviceElecMeter()
+
+    /**
+     * 获取SOC数据(电、水)
+     */
+    async getSocData() {
+      // 注意:这里要使用 await 等待异步方法完成
+      await this.getDatesOfLastTenDays();
+      await this.getMeterElecWater();
+      await this.getDeviceElecMeter();
     },
     },
+
+    /**
+     * 获取电表和水表数据
+     */
     async getMeterElecWater() {
     async getMeterElecWater() {
-      const {data: elecMeter} = await qryElecMeterByDate(DateTool.now(), this.areaType)
-      const {data: waterMeter} = await qryWaterMeterByDate(DateTool.now(), this.areaType)
-      const [elec, water] = this.socList
-      elec.value = numToStr(elecMeter.quantity)
-      water.value = numToStr(waterMeter.quantity)
-      this.socList = [elec, water]
+      try {
+        const [elecMeterRes, waterMeterRes] = await Promise.all([
+          qryElecMeterByDate(DateTool.now(), this.areaType),
+          qryWaterMeterByDate(DateTool.now(), this.areaType)
+        ]);
+
+        const [elec, water] = this.socList;
+
+        if (elecMeterRes && elecMeterRes.data) {
+          elec.value = numToStr(elecMeterRes.data.quantity || 0);
+        }
+
+        if (waterMeterRes && waterMeterRes.data) {
+          water.value = numToStr(waterMeterRes.data.quantity || 0);
+        }
+
+        this.socList = [elec, water];
+
+        console.log('能耗统计加载成功:', {
+          电: elec.value,
+          水: water.value
+        });
+      } catch (error) {
+        console.error('获取能耗统计失败:', error);
+      }
     },
     },
 
 
+    /**
+     * 格式化日期显示为 MM-DD
+     */
+    formatDateDisplay(dateStr) {
+      if (!dateStr) return '';
+      const parts = dateStr.split('-');
+      if (parts.length === 3) {
+        return `${parts[1]}-${parts[2]}`;
+      }
+      return dateStr;
+    }
   }
   }
 }
 }
 </script>
 </script>
+
 <style lang='scss' scoped>
 <style lang='scss' scoped>
 @import url("../index.scss");
 @import url("../index.scss");
 
 

+ 99 - 106
ems-ui-cloud/src/views/largeScreen/source/left.vue

@@ -30,13 +30,18 @@
 <script>
 <script>
 import CusModule from '../components/CusModule.vue';
 import CusModule from '../components/CusModule.vue';
 import BaseChart from '@/components/BaseChart/index.vue'
 import BaseChart from '@/components/BaseChart/index.vue'
-import {copyObj, numToStr} from '@/utils';
-import {mapState} from 'vuex';
+import { copyObj, numToStr } from '@/utils';
+import { mapState } from 'vuex';
 import * as echarts from 'echarts'
 import * as echarts from 'echarts'
-import {pgSupplyHourList, pgSupplyHourTotalPv} from "@/api/screen";
-import {DateTool} from "@/utils/DateTool";
-
-const option = {
+import { DateTool } from "@/utils/DateTool";
+import {
+  getPvSupplySummary,
+  listPvSupplyHourly,
+  listPvSupplyDaily
+} from "@/api/mgr/pvSupply";
+
+// 基础图表配置
+const baseChartOption = {
   legend: {
   legend: {
     show: false,
     show: false,
   },
   },
@@ -68,6 +73,10 @@ const option = {
 
 
 export default {
 export default {
   name: 'SourceLeft',
   name: 'SourceLeft',
+  components: {
+    CusModule,
+    BaseChart
+  },
   data() {
   data() {
     return {
     return {
       todayEnergy: '0',
       todayEnergy: '0',
@@ -95,20 +104,16 @@ export default {
       realtimeLineData: []
       realtimeLineData: []
     };
     };
   },
   },
-  components: {
-    CusModule,
-    BaseChart
-  },
   computed: {
   computed: {
     ...mapState('userState', ['areaType']),
     ...mapState('userState', ['areaType']),
+
+    // 历史发电量折线图配置
     lineOptions() {
     lineOptions() {
-      const copyOptions = copyObj(option);
+      const copyOptions = copyObj(baseChartOption);
       return {
       return {
         ...copyOptions,
         ...copyOptions,
         xAxis: {
         xAxis: {
-          splitLine: {
-            show: false,
-          },
+          splitLine: { show: false },
           axisLabel: {
           axisLabel: {
             color: '#c1cadf',
             color: '#c1cadf',
             fontSize: '10',
             fontSize: '10',
@@ -121,9 +126,7 @@ export default {
             name: "发电量",
             name: "发电量",
             smooth: true,
             smooth: true,
             lineStyle: {
             lineStyle: {
-              normal: {
-                color: "#80DBE1",
-              },
+              normal: { color: "#80DBE1" },
             },
             },
             itemStyle: {
             itemStyle: {
               color: "#80DBE1",
               color: "#80DBE1",
@@ -132,14 +135,9 @@ export default {
             },
             },
             areaStyle: {
             areaStyle: {
               normal: {
               normal: {
-                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
-                  offset: 0,
-                  color: "rgba(81, 139, 152,0.8)"
-                },
-                  {
-                    offset: 1,
-                    color: "rgba(81, 139, 152, 0)"
-                  }
+                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  { offset: 0, color: "rgba(81, 139, 152,0.8)" },
+                  { offset: 1, color: "rgba(81, 139, 152, 0)" }
                 ], false),
                 ], false),
                 shadowColor: 'rgba(81, 139, 152, 0.5)',
                 shadowColor: 'rgba(81, 139, 152, 0.5)',
                 shadowBlur: 20
                 shadowBlur: 20
@@ -150,14 +148,14 @@ export default {
         ],
         ],
       }
       }
     },
     },
+
+    // 实时指标折线图配置
     realtimeLineOptions() {
     realtimeLineOptions() {
-      const copyOptions = copyObj(option);
+      const copyOptions = copyObj(baseChartOption);
       return {
       return {
         ...copyOptions,
         ...copyOptions,
         xAxis: {
         xAxis: {
-          splitLine: {
-            show: false,
-          },
+          splitLine: { show: false },
           axisLabel: {
           axisLabel: {
             color: '#c1cadf',
             color: '#c1cadf',
             fontSize: '10',
             fontSize: '10',
@@ -170,9 +168,7 @@ export default {
             name: "发电量",
             name: "发电量",
             smooth: true,
             smooth: true,
             lineStyle: {
             lineStyle: {
-              normal: {
-                color: "#80DBE1",
-              },
+              normal: { color: "#80DBE1" },
             },
             },
             itemStyle: {
             itemStyle: {
               color: "#80DBE1",
               color: "#80DBE1",
@@ -181,14 +177,9 @@ export default {
             },
             },
             areaStyle: {
             areaStyle: {
               normal: {
               normal: {
-                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
-                  offset: 0,
-                  color: "rgba(81, 139, 152,0.8)"
-                },
-                  {
-                    offset: 1,
-                    color: "rgba(81, 139, 152, 0)"
-                  }
+                color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+                  { offset: 0, color: "rgba(81, 139, 152,0.8)" },
+                  { offset: 1, color: "rgba(81, 139, 152, 0)" }
                 ], false),
                 ], false),
                 shadowColor: 'rgba(81, 139, 152, 0.5)',
                 shadowColor: 'rgba(81, 139, 152, 0.5)',
                 shadowBlur: 20
                 shadowBlur: 20
@@ -216,50 +207,61 @@ export default {
     this.timer && clearInterval(this.timer)
     this.timer && clearInterval(this.timer)
   },
   },
   methods: {
   methods: {
+    /**
+     * 获取区域代码参数
+     */
+    getAreaCodeParam() {
+      const areaCode = this.areaType
+      if (!areaCode || areaCode === '-1' || areaCode === -1) {
+        return '-1'
+      }
+      return areaCode
+    },
+
+    /**
+     * 主数据查询入口
+     */
     qryPageDatas() {
     qryPageDatas() {
-      this.getPgSupply()
-      this.getPgPvRealTimeSupply()
-      this.getHisPgPvSupply()
+      this.getPvSummaryData()
+      this.getPvRealTimeData()
+      this.getHisPvData()
     },
     },
 
 
     /**
     /**
      * 获取光伏汇总数据(今日、本月、今年、累计)
      * 获取光伏汇总数据(今日、本月、今年、累计)
      */
      */
-    async getPgSupply() {
+    async getPvSummaryData() {
       try {
       try {
+        const areaCode = this.getAreaCodeParam()
         const todayStr = DateTool.now(DateTool.DateFormat.YYYY_MM_DD)
         const todayStr = DateTool.now(DateTool.DateFormat.YYYY_MM_DD)
         const monthStart = DateTool.now(DateTool.DateFormat.YYYY_MM) + '-01'
         const monthStart = DateTool.now(DateTool.DateFormat.YYYY_MM) + '-01'
         const yearStart = DateTool.now(DateTool.DateFormat.YYYY) + '-01-01'
         const yearStart = DateTool.now(DateTool.DateFormat.YYYY) + '-01-01'
 
 
-        // 并发请求所有汇总数据
         const [todayRes, monthRes, yearRes, totalRes] = await Promise.all([
         const [todayRes, monthRes, yearRes, totalRes] = await Promise.all([
           // 今日发电量
           // 今日发电量
-          pgSupplyHourTotalPv({
-            areaCode: this.areaType,
-            date: todayStr
+          getPvSupplySummary({
+            areaCode,
+            startRecTime: `${todayStr} 00:00:00`,
+            endRecTime: `${todayStr} 23:59:59`
           }),
           }),
           // 本月发电量
           // 本月发电量
-          pgSupplyHourTotalPv({
-            areaCode: this.areaType,
-            startRecTime: monthStart,
-            endRecTime: todayStr
+          getPvSupplySummary({
+            areaCode,
+            startRecTime: `${monthStart} 00:00:00`,
+            endRecTime: `${todayStr} 23:59:59`
           }),
           }),
           // 今年发电量
           // 今年发电量
-          pgSupplyHourTotalPv({
-            areaCode: this.areaType,
-            startRecTime: yearStart,
-            endRecTime: todayStr
+          getPvSupplySummary({
+            areaCode,
+            startRecTime: `${yearStart} 00:00:00`,
+            endRecTime: `${todayStr} 23:59:59`
           }),
           }),
           // 累计发电量
           // 累计发电量
-          pgSupplyHourTotalPv({
-            areaCode: this.areaType
-          })
+          getPvSupplySummary({ areaCode })
         ])
         ])
 
 
         // 更新今日发电量
         // 更新今日发电量
-        if (todayRes?.data) {
-          this.todayEnergy = numToStr(todayRes.data.genElecQuantity || 0)
-        }
+        this.todayEnergy = numToStr(todayRes?.data?.genElecQuantity || 0)
 
 
         // 更新统计卡片
         // 更新统计卡片
         const [monthPv, yearPv, totalPv] = this.elecList
         const [monthPv, yearPv, totalPv] = this.elecList
@@ -275,22 +277,22 @@ export default {
     /**
     /**
      * 获取今日实时小时数据
      * 获取今日实时小时数据
      */
      */
-    async getPgPvRealTimeSupply() {
+    async getPvRealTimeData() {
       try {
       try {
-        const {data, rows} = await pgSupplyHourList({
-          areaCode: this.areaType,
-          date: DateTool.now(DateTool.DateFormat.YYYY_MM_DD),
-          timeDimension: 'hour',  // 关键参数:按小时维度查询
+        const areaCode = this.getAreaCodeParam()
+        const todayStr = DateTool.now(DateTool.DateFormat.YYYY_MM_DD)
+
+        const res = await listPvSupplyHourly({
+          areaCode,
+          startRecTime: `${todayStr} 00:00:00`,
+          endRecTime: `${todayStr} 23:59:59`,
           pageNum: 1,
           pageNum: 1,
-          pageSize: 24,  // 一天24小时
-          orderFlag: 'asc'  // 升序排列
+          pageSize: 24
         })
         })
 
 
-        // 兼容两种返回格式
-        const hourlyData = rows || data || []
+        const hourlyData = res.rows || []
 
 
         if (!Array.isArray(hourlyData) || hourlyData.length === 0) {
         if (!Array.isArray(hourlyData) || hourlyData.length === 0) {
-          console.warn('实时小时数据为空')
           this.realtimeLineData = this.getEmptyHourData()
           this.realtimeLineData = this.getEmptyHourData()
           return
           return
         }
         }
@@ -304,21 +306,16 @@ export default {
         })
         })
 
 
         // 生成24小时完整数据
         // 生成24小时完整数据
-        const timeLabels = DateTool.getTime(60) // 获取24小时的时间标签
-        const lineData = []
-
-        timeLabels.forEach((timeLabel, index) => {
-          const hourIndex = index + 1  // timeIndex 从1开始
+        const timeLabels = DateTool.getTime(60)
+        this.realtimeLineData = timeLabels.map((timeLabel, index) => {
+          const hourIndex = index + 1
           const hourData = timeIndexMap[hourIndex]
           const hourData = timeIndexMap[hourIndex]
-
-          lineData.push({
+          return {
             xData: timeLabel,
             xData: timeLabel,
-            yData: hourData ? (hourData.genElecQuantity || 0) : 0
-          })
+            yData: hourData ? Number(hourData.genElecQuantity || 0) : 0
+          }
         })
         })
 
 
-        this.realtimeLineData = lineData
-
       } catch (error) {
       } catch (error) {
         console.error('获取实时发电数据失败:', error)
         console.error('获取实时发电数据失败:', error)
         this.realtimeLineData = this.getEmptyHourData()
         this.realtimeLineData = this.getEmptyHourData()
@@ -328,26 +325,23 @@ export default {
     /**
     /**
      * 获取历史发电量(最近30天)
      * 获取历史发电量(最近30天)
      */
      */
-    async getHisPgPvSupply() {
+    async getHisPvData() {
       try {
       try {
+        const areaCode = this.getAreaCodeParam()
         const startDate = DateTool.lastXDay(30)
         const startDate = DateTool.lastXDay(30)
-        const endDate = DateTool.now()
+        const endDate = DateTool.now(DateTool.DateFormat.YYYY_MM_DD)
 
 
-        const {data, rows} = await pgSupplyHourList({
-          areaCode: this.areaType,
-          startRecTime: startDate,
-          endRecTime: endDate,
-          timeDimension: 'day',  // 按日维度查询
+        const res = await listPvSupplyDaily({
+          areaCode,
+          startRecTime: `${startDate} 00:00:00`,
+          endRecTime: `${endDate} 23:59:59`,
           pageNum: 1,
           pageNum: 1,
-          pageSize: 31,  // 最多31天
-          orderFlag: 'asc'
+          pageSize: 31
         })
         })
 
 
-        // 兼容两种返回格式
-        const dailyData = rows || data || []
+        const dailyData = res.rows || []
 
 
         if (!Array.isArray(dailyData)) {
         if (!Array.isArray(dailyData)) {
-          console.warn('历史发电数据格式错误')
           this.lineData = []
           this.lineData = []
           return
           return
         }
         }
@@ -359,24 +353,23 @@ export default {
         const dateIndexMap = {}
         const dateIndexMap = {}
         dailyData.forEach(item => {
         dailyData.forEach(item => {
           if (item.statisticDate) {
           if (item.statisticDate) {
-            // 格式化日期为 YYYY-MM-DD
-            const dateStr = item.statisticDate.split(' ')[0]
+            let dateStr = item.statisticDate
+            if (typeof dateStr === 'string') {
+              dateStr = dateStr.split(' ')[0].split('T')[0]
+            }
             dateIndexMap[dateStr] = item
             dateIndexMap[dateStr] = item
           }
           }
         })
         })
 
 
         // 生成完整的30天数据
         // 生成完整的30天数据
-        const lineData = []
-        dateTips.forEach(date => {
+        this.lineData = dateTips.map(date => {
           const dayData = dateIndexMap[date]
           const dayData = dateIndexMap[date]
-          lineData.push({
-            xData: date,
-            yData: dayData ? (dayData.genElecQuantity || 0) : 0
-          })
+          return {
+            xData: date.substring(5), // 只显示 MM-DD
+            yData: dayData ? Number(dayData.genElecQuantity || 0) : 0
+          }
         })
         })
 
 
-        this.lineData = lineData
-
       } catch (error) {
       } catch (error) {
         console.error('获取历史发电数据失败:', error)
         console.error('获取历史发电数据失败:', error)
         this.lineData = []
         this.lineData = []
@@ -384,7 +377,7 @@ export default {
     },
     },
 
 
     /**
     /**
-     * 获取空的24小时数据(用于异常情况)
+     * 获取空的24小时数据
      */
      */
     getEmptyHourData() {
     getEmptyHourData() {
       const timeLabels = DateTool.getTime(60)
       const timeLabels = DateTool.getTime(60)

+ 123 - 38
ems-ui-cloud/src/views/largeScreen/source/right.vue

@@ -14,15 +14,16 @@
         </div>
         </div>
       </vue-seamless-scroll>
       </vue-seamless-scroll>
     </CusModule>
     </CusModule>
-    <CusModule class="module-top" title="节能减排(今年)">
+    <!-- 修改:节能减排 -> 碳排放统计(今年) -->
+    <CusModule class="module-top" title="碳排放统计(今年)">
       <div class="energy">
       <div class="energy">
         <div class="energy-item">
         <div class="energy-item">
-          <div class="energy-item-value">{{ reduce.coal }}</div>
-          <div class="energy-item-name">标煤当量(吨)</div>
+          <div class="energy-item-value">{{ carbonData.emission }}</div>
+          <div class="energy-item-name">今年碳排(吨)</div>
         </div>
         </div>
         <div class="energy-item">
         <div class="energy-item">
-          <div class="energy-item-value">{{ reduce.co2 }}</div>
-          <div class="energy-item-name">CO2当量(吨)</div>
+          <div class="energy-item-value">{{ carbonData.sink }}</div>
+          <div class="energy-item-name">今年碳汇(吨)</div>
         </div>
         </div>
       </div>
       </div>
     </CusModule>
     </CusModule>
@@ -39,10 +40,12 @@ import {array2Map} from '@/utils';
 import * as echarts from 'echarts'
 import * as echarts from 'echarts'
 import {mapState} from 'vuex';
 import {mapState} from 'vuex';
 import {DateTool} from "@/utils/DateTool";
 import {DateTool} from "@/utils/DateTool";
-import {ecoDateRange, predictionProdDateRange} from "@/api/screen";
+import {calcElecProdForecastDateRange} from "@/api/prediction/predictionProd";
 import {listAlarmInfo} from "@/api/alarm/alarm-info";
 import {listAlarmInfo} from "@/api/alarm/alarm-info";
 import {ALARM_STATE} from "@/enums/alarm";
 import {ALARM_STATE} from "@/enums/alarm";
 import {ApiCode} from "@/api/apiEmums";
 import {ApiCode} from "@/api/apiEmums";
+// 新增:引入碳计量API
+import {listSumCaMeterD} from "@/api/ca/caMeterD";
 
 
 export default {
 export default {
   name: 'SourceRight',
   name: 'SourceRight',
@@ -50,10 +53,10 @@ export default {
   data() {
   data() {
     return {
     return {
       lineData: [],
       lineData: [],
-      reduce: {
-        coal: '',
-        co2: '',
-        tree: ''
+      // 修改:碳排放数据
+      carbonData: {
+        emission: '0.00',  // 今年碳排放量(吨)
+        sink: '0.00'       // 今年碳汇量(吨)
       },
       },
       classOption: {
       classOption: {
         step: 0.3, // 数值越大速度滚动越快
         step: 0.3, // 数值越大速度滚动越快
@@ -65,8 +68,8 @@ export default {
         singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
         singleWidth: 0, // 单步运动停止的宽度(默认值0是无缝不停止的滚动) direction => 2/3
         waitTime: 1000 // 单步运动停止
         waitTime: 1000 // 单步运动停止
       },
       },
-      listData: []
-
+      listData: [],
+      totalElecProdQuantity: '0.00' // 累计预测发电量
     }
     }
   },
   },
   components: {
   components: {
@@ -83,6 +86,10 @@ export default {
         },
         },
         tooltip: {
         tooltip: {
           trigger: "axis",
           trigger: "axis",
+          formatter: (params) => {
+            const item = params[0];
+            return `${item.axisValue}<br/>${item.marker}发电量: ${item.value} kW·h`;
+          }
         },
         },
         grid: {
         grid: {
           left: '5%',
           left: '5%',
@@ -127,7 +134,7 @@ export default {
             },
             },
             areaStyle: { //区域填充样式
             areaStyle: { //区域填充样式
               normal: {
               normal: {
-                //线性渐变,前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是‘true’,则该四个值是绝对像素位置。
+                //线性渐变,前4个参数分别是x0,y0,x2,y2(范围0~1);相当于图形包围盒中的百分比。如果最后一个参数是'true',则该四个值是绝对像素位置。
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                 color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                   offset: 0,
                   offset: 0,
                   color: "rgba(81, 139, 152,0.8)"
                   color: "rgba(81, 139, 152,0.8)"
@@ -164,38 +171,102 @@ export default {
   },
   },
   methods: {
   methods: {
     getDatas() {
     getDatas() {
-      this.getEcoDateRange()
-      this.getDatesOfLastTenDays()
+      this.getCarbonDataByYear()  // 修改:调用碳计量接口
+      this.getElecProdForecast()  // 修改:调用新的发电预测接口
       this.getRealTimeAlarm()
       this.getRealTimeAlarm()
     },
     },
-    async getDatesOfLastTenDays() {
-      const next5Day = DateTool.addDays(DateTool.now(), 4);
-      const {data} = await predictionProdDateRange({
-        areaCode: this.areaType,
-        startRecTime: DateTool.now(),
-        endRecTime: next5Day,
-      })
 
 
-      const dates = DateTool.getDayOfRange(DateTool.now(), next5Day);
-      const mapIndex = array2Map(data, "date");
-      this.lineData = dates.map(item => ({
-        xData: item,
-        yData: mapIndex[item] ? mapIndex[item].elecProdQuantity : 0
-      }))
+    /**
+     * 获取今年碳排放和碳汇数据
+     * 调用后端 /ems/caMeterD/list/all 接口,timeType=year 按年汇总
+     */
+    async getCarbonDataByYear() {
+      try {
+        const currentYear = new Date().getFullYear();
+        const startRecTime = `${currentYear}-01-01`;
+        const endRecTime = `${currentYear}-12-31`;
+
+        const { code, data } = await listSumCaMeterD({
+          areaCode: this.areaType,
+          startRecTime: startRecTime,
+          endRecTime: endRecTime,
+          timeType: 'year'  // 按年汇总
+        });
+
+        if (code === 200 && data && data.length > 0) {
+          // 汇总所有区域的数据(如果areaType为-1或空,可能返回多条)
+          let totalEmission = 0;
+          let totalSink = 0;
+
+          data.forEach(item => {
+            totalEmission += item.caEmissionQuantity || 0;
+            totalSink += item.caSinkQuantity || 0;
+          });
+
+          // 转换为吨(原数据单位为千克)
+          this.carbonData.emission = (totalEmission / 1000).toFixed(2);
+          this.carbonData.sink = (totalSink / 1000).toFixed(2);
+        } else {
+          this.carbonData.emission = '0.00';
+          this.carbonData.sink = '0.00';
+        }
+      } catch (error) {
+        console.error('获取碳计量数据失败:', error);
+        this.carbonData.emission = '0.00';
+        this.carbonData.sink = '0.00';
+      }
     },
     },
 
 
-    async getEcoDateRange() {
-      const {data: ecoDateRangeData} = await ecoDateRange({
-        areaCode: this.areaType,
-        date: DateTool.now(DateTool.DateFormat.YYYY)
-      })
-      if (!ecoDateRangeData) {
-        return
+    /**
+     * 修改:获取未来5天的发电趋势预测
+     * 调用新接口 calcElecProdForecastDateRange
+     */
+    async getElecProdForecast() {
+      try {
+        const now = new Date();
+        const startRecTime = DateTool.now();
+
+        // 计算未来4天的日期(包含今天共5天)
+        const futureDate = new Date(now);
+        futureDate.setDate(futureDate.getDate() + 4);
+        const year = futureDate.getFullYear();
+        const month = String(futureDate.getMonth() + 1).padStart(2, '0');
+        const day = String(futureDate.getDate()).padStart(2, '0');
+        const endRecTime = `${year}-${month}-${day}`;
+
+        const { code, data } = await calcElecProdForecastDateRange({
+          areaCode: this.areaType === '-1' ? undefined : this.areaType,
+          startRecTime: startRecTime,
+          endRecTime: endRecTime
+        });
+
+        if (code === 200 && data && data.length > 0) {
+          // 生成日期范围
+          const dates = DateTool.getDayOfRange(startRecTime, endRecTime);
+          const mapIndex = array2Map(data, "date");
+
+          this.lineData = dates.map(item => ({
+            xData: this.formatDate(item),
+            yData: mapIndex[item] ? Number(mapIndex[item].elecProdQuantity).toFixed(2) : 0
+          }));
+
+          // 计算累计预测发电量
+          let total = 0;
+          data.forEach(item => {
+            total += Number(item.elecProdQuantity || 0);
+          });
+          this.totalElecProdQuantity = total.toFixed(2);
+        } else {
+          this.lineData = [];
+          this.totalElecProdQuantity = '0.00';
+        }
+      } catch (error) {
+        console.error('获取发电趋势预测数据失败:', error);
+        this.lineData = [];
+        this.totalElecProdQuantity = '0.00';
       }
       }
-      const {elecEcoQuantity} = ecoDateRangeData;
-      this.reduce.coal = (elecEcoQuantity * this.elec2CFactor / 1000).toFixed(2)
-      this.reduce.co2 = (elecEcoQuantity * this.regionFactor / 1000).toFixed(2)
     },
     },
+
     async getRealTimeAlarm() {
     async getRealTimeAlarm() {
       let result = [];
       let result = [];
       const {
       const {
@@ -214,6 +285,20 @@ export default {
       }
       }
       this.listData = result;
       this.listData = result;
     },
     },
+
+    /**
+     * 格式化日期显示
+     * @param {String} date - 日期字符串 yyyy-MM-dd
+     * @returns {String} 格式化后的日期字符串 MM-DD
+     */
+    formatDate(date) {
+      if (!date) return '';
+      const parts = date.split('-');
+      if (parts.length === 3) {
+        return `${parts[1]}-${parts[2]}`;
+      }
+      return date;
+    }
   }
   }
 }
 }
 </script>
 </script>

+ 4 - 3
ems-ui-cloud/src/views/mgr/powergrid.vue

@@ -284,7 +284,8 @@
 import { ApiCode } from '@/api/apiEmums'
 import { ApiCode } from '@/api/apiEmums'
 import { areaTreeByFacsCategory } from '@/api/basecfg/area'
 import { areaTreeByFacsCategory } from '@/api/basecfg/area'
 import { get } from '@/api/commonApi'
 import { get } from '@/api/commonApi'
-import { listPgSupplyH, listPvSupplyH } from '@/api/mgr/pgSupplyH'
+import { listPgSupplyH } from '@/api/mgr/pgSupplyH'
+import { listPvSupply } from '@/api/mgr/pvSupply'
 import BaseChart from '@/components/BaseChart'
 import BaseChart from '@/components/BaseChart'
 import BarChartBlock from '@/components/Block/charts/BarChartBlock.vue'
 import BarChartBlock from '@/components/Block/charts/BarChartBlock.vue'
 import PieChartBlock from '@/components/Block/charts/PieChartBlock.vue'
 import PieChartBlock from '@/components/Block/charts/PieChartBlock.vue'
@@ -680,7 +681,7 @@ export default {
         const pvStart = targetDate.format('YYYY-MM-DD 00:00:00')
         const pvStart = targetDate.format('YYYY-MM-DD 00:00:00')
         const pvEnd = targetDate.format('YYYY-MM-DD 23:59:59')
         const pvEnd = targetDate.format('YYYY-MM-DD 23:59:59')
 
 
-        listPvSupplyH({
+        listPvSupply({
           startRecTime: pvStart,
           startRecTime: pvStart,
           endRecTime: pvEnd,
           endRecTime: pvEnd,
           areaCode: this.queryParams.areaCode, // 使用当前选中的 areaCode
           areaCode: this.queryParams.areaCode, // 使用当前选中的 areaCode
@@ -792,7 +793,7 @@ export default {
           this.loading = false
           this.loading = false
         })
         })
       } else {
       } else {
-        listPvSupplyH({
+        listPvSupply({
           startRecTime: this.queryParams.startRecTime,
           startRecTime: this.queryParams.startRecTime,
           endRecTime: this.queryParams.endRecTime,
           endRecTime: this.queryParams.endRecTime,
           areaCode: this.queryParams.areaCode,
           areaCode: this.queryParams.areaCode,

+ 715 - 8
ems-ui-cloud/src/views/mgr/powerstore.vue

@@ -16,6 +16,7 @@
     </el-card>
     </el-card>
 
 
     <el-tabs v-model="activeName" @tab-click="tabClick" class="main-tabs">
     <el-tabs v-model="activeName" @tab-click="tabClick" class="main-tabs">
+      <!-- 总览Tab -->
       <el-tab-pane label="总览" name="summary">
       <el-tab-pane label="总览" name="summary">
         <div class="chartGroup">
         <div class="chartGroup">
           <div class="chart-card">
           <div class="chart-card">
@@ -48,6 +49,161 @@
         </div>
         </div>
       </el-tab-pane>
       </el-tab-pane>
 
 
+      <!-- 实时分析Tab -->
+      <el-tab-pane label="实时监测" name="realtime">
+        <!-- 设备状态概览卡片 -->
+        <div class="overview-section">
+          <div class="section-header">
+            <h3 class="section-title">
+              <i class="el-icon-data-board"></i>
+              设备状态概览
+            </h3>
+            <el-button type="text" icon="el-icon-refresh" @click="refreshRealtimeData" :loading="realtimeLoading">
+              刷新数据
+            </el-button>
+          </div>
+          <el-row :gutter="16">
+            <el-col :span="6">
+              <div class="stat-card total-card">
+                <div class="card-icon">
+                  <i class="el-icon-s-platform"></i>
+                </div>
+                <div class="card-content">
+                  <span class="card-label">设备总数</span>
+                  <span class="card-value">{{ deviceSummary.totalCount }}<small>台</small></span>
+                </div>
+              </div>
+            </el-col>
+            <el-col :span="6">
+              <div class="stat-card charge-card">
+                <div class="card-icon">
+                  <i class="el-icon-upload2"></i>
+                </div>
+                <div class="card-content">
+                  <span class="card-label">总充电功率</span>
+                  <span class="card-value">{{ formatNumber(deviceSummary.totalChargePower) }}<small>W</small></span>
+                </div>
+              </div>
+            </el-col>
+            <el-col :span="6">
+              <div class="stat-card discharge-card">
+                <div class="card-icon">
+                  <i class="el-icon-download"></i>
+                </div>
+                <div class="card-content">
+                  <span class="card-label">总放电功率</span>
+                  <span class="card-value">{{ formatNumber(deviceSummary.totalDischargePower) }}<small>W</small></span>
+                </div>
+              </div>
+            </el-col>
+            <el-col :span="6">
+              <div class="stat-card capacity-card">
+                <div class="card-icon">
+                  <i class="el-icon-odometer"></i>
+                </div>
+                <div class="card-content">
+                  <span class="card-label">总当前容量</span>
+                  <span class="card-value">{{ formatNumber(deviceSummary.totalCapacity) }}<small>kW·h</small></span>
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+        </div>
+
+        <!-- 设备实时状态列表 -->
+        <div class="device-realtime-section" v-loading="realtimeLoading">
+          <div class="section-header">
+            <h3 class="section-title">
+              <i class="el-icon-cpu"></i>
+              设备实时状态
+            </h3>
+            <span class="update-time" v-if="lastUpdateTime">
+              <i class="el-icon-time"></i>
+              更新时间: {{ lastUpdateTime }}
+            </span>
+          </div>
+          <el-row :gutter="16">
+            <el-col
+              v-for="device in deviceRealtimeList"
+              :key="device.deviceCode"
+              :span="12"
+              :lg="8"
+              :xl="6"
+              class="device-card-col"
+            >
+              <div class="device-card" :class="getDeviceStatusClass(device)">
+                <div class="device-header">
+                  <div class="device-title">
+                    <i class="el-icon-cpu device-icon"></i>
+                    <span class="device-name">{{ device.deviceName || device.deviceCode }}</span>
+                  </div>
+                  <el-tag :type="getDeviceStatusType(device)" size="mini" effect="dark">
+                    {{ getDeviceStatusText(device) }}
+                  </el-tag>
+                </div>
+                <div class="device-body">
+                  <div class="device-info">
+                    <div class="info-row">
+                      <span class="info-label">设备编码:</span>
+                      <span class="info-value code">{{ device.deviceCode }}</span>
+                    </div>
+                    <div class="info-row">
+                      <span class="info-label">安装位置:</span>
+                      <span class="info-value">{{ device.location || '-' }}</span>
+                    </div>
+                  </div>
+                  <div class="device-metrics">
+                    <div class="metric-item charge">
+                      <div class="metric-icon">
+                        <i class="el-icon-top"></i>
+                      </div>
+                      <div class="metric-info">
+                        <span class="metric-label">充电功率</span>
+                        <span class="metric-value">{{ formatNumber(device.chargeVoltage) }} <small>W</small></span>
+                      </div>
+                    </div>
+                    <div class="metric-item discharge">
+                      <div class="metric-icon">
+                        <i class="el-icon-bottom"></i>
+                      </div>
+                      <div class="metric-info">
+                        <span class="metric-label">放电功率</span>
+                        <span class="metric-value">{{ formatNumber(device.dischargePower) }} <small>W</small></span>
+                      </div>
+                    </div>
+                  </div>
+                  <div class="capacity-bar">
+                    <div class="capacity-label">
+                      <span>当前容量</span>
+                      <span class="capacity-value">{{ formatNumber(device.currentCapacity) }} kW·h</span>
+                    </div>
+                    <el-progress
+                      :percentage="getCapacityPercentage(device)"
+                      :stroke-width="12"
+                      :color="getCapacityColor(device)"
+                      :show-text="false"
+                    />
+                    <div class="capacity-percent">{{ getCapacityPercentage(device).toFixed(1) }}%</div>
+                  </div>
+                </div>
+                <div class="device-footer">
+                  <span class="device-area">
+                    <i class="el-icon-location-outline"></i>
+                    {{ getAreaName(device.areaCode) }}
+                  </span>
+                  <span class="device-time" v-if="device.indexTime">
+                    <i class="el-icon-time"></i>
+                    {{ formatTime(device.indexTime) }}
+                  </span>
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+          <el-empty v-if="deviceRealtimeList.length === 0 && !realtimeLoading" description="暂无设备数据" />
+        </div>
+      </el-tab-pane>
+
+      <!-- 区块储能Tab -->
       <el-tab-pane label="区块储能" name="area">
       <el-tab-pane label="区块储能" name="area">
         <el-row :gutter="20">
         <el-row :gutter="20">
           <el-col :span="5" :xs="24">
           <el-col :span="5" :xs="24">
@@ -142,14 +298,17 @@
 </template>
 </template>
 
 
 <script>
 <script>
-import { dayStatistics, listElecStoreH } from '@/api/mgr/elecStoreH'
+// 新的API引用
+import { getLatestByDevice } from '@/api/mgr/elecStoreIndex'
+import { listElecStoreHour, getDayStatistics } from '@/api/mgr/elecStoreHour'
+import { getByCondition } from '@/api/device/device'
 import { areaTreeByFacsCategory } from '@/api/basecfg/area'
 import { areaTreeByFacsCategory } from '@/api/basecfg/area'
 import { dateFormat, numToStr } from '@/utils/index.js'
 import { dateFormat, numToStr } from '@/utils/index.js'
 import dayjs from 'dayjs'
 import dayjs from 'dayjs'
 import { DateTool } from '@/utils/DateTool'
 import { DateTool } from '@/utils/DateTool'
 import BaseChart from '@/components/BaseChart'
 import BaseChart from '@/components/BaseChart'
 import { listConfig } from '@/api/system/config'
 import { listConfig } from '@/api/system/config'
-import SubTitle from '@/components/SubTitle' // 确保引入SubTitle
+import SubTitle from '@/components/SubTitle'
 
 
 export default {
 export default {
   name: 'ElecStoreH',
   name: 'ElecStoreH',
@@ -225,7 +384,23 @@ export default {
       dateRange: [
       dateRange: [
         dayjs().format(DateTool.DateFormat.YYYY_MM_DD_00_00_00),
         dayjs().format(DateTool.DateFormat.YYYY_MM_DD_00_00_00),
         dayjs().format(DateTool.DateFormat.YYYY_MM_DD_23_59_59)
         dayjs().format(DateTool.DateFormat.YYYY_MM_DD_23_59_59)
-      ]
+      ],
+
+      // ========== 实时分析相关数据 ==========
+      realtimeLoading: false,
+      lastUpdateTime: '',
+      deviceList: [], // 设备基础信息列表
+      deviceRealtimeList: [], // 设备+指标合并后的列表
+      deviceSummary: {
+        totalCount: 0,
+        totalChargePower: 0,
+        totalDischargePower: 0,
+        totalCapacity: 0
+      },
+      // 假设最大容量为100kW·h,实际应从设备配置获取
+      maxCapacityConfig: 100,
+      // 区域映射
+      areaMap: {}
     }
     }
   },
   },
   computed: {
   computed: {
@@ -350,6 +525,51 @@ export default {
   methods: {
   methods: {
     numToStr,
     numToStr,
 
 
+    // ========== 工具方法 ==========
+    formatNumber(value) {
+      if (value === null || value === undefined) return '0.00'
+      const num = parseFloat(value)
+      if (isNaN(num)) return '0.00'
+      return num.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
+    },
+
+    formatTime(time) {
+      if (!time) return '-'
+      if (typeof time === 'string') {
+        // 如果是完整的日期时间字符串,只取时间部分
+        if (time.includes(' ')) {
+          return time.split(' ')[1]
+        }
+        return time
+      }
+      const d = new Date(time)
+      return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`
+    },
+
+    formatDateTime(date) {
+      if (!date) return ''
+      const d = new Date(date)
+      const year = d.getFullYear()
+      const month = String(d.getMonth() + 1).padStart(2, '0')
+      const day = String(d.getDate()).padStart(2, '0')
+      const hours = String(d.getHours()).padStart(2, '0')
+      const minutes = String(d.getMinutes()).padStart(2, '0')
+      const seconds = String(d.getSeconds()).padStart(2, '0')
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`
+    },
+
+    getAreaName(areaCode) {
+      if (!areaCode) return '-'
+      // 优先从映射中获取
+      if (this.areaMap[areaCode]) {
+        return this.areaMap[areaCode]
+      }
+      // 根据已知的区域代码返回名称
+      if (areaCode === '321283124S3001') return '常泰北区'
+      if (areaCode === '321283124S3002') return '常泰南区'
+      return areaCode
+    },
+
     // 获取树节点图标
     // 获取树节点图标
     getTreeIcon(data) {
     getTreeIcon(data) {
       if (data.facsCategory === 'C') {
       if (data.facsCategory === 'C') {
@@ -398,12 +618,26 @@ export default {
         const response = await areaTreeByFacsCategory(this.facsCategory, this.facsSubCategory, false)
         const response = await areaTreeByFacsCategory(this.facsCategory, this.facsSubCategory, false)
         this.areaOptions = [{ id: '-1', label: '全部', children: response.data || [] }]
         this.areaOptions = [{ id: '-1', label: '全部', children: response.data || [] }]
 
 
+        // 构建区域映射
+        this.buildAreaMap(response.data || [])
       } catch (error) {
       } catch (error) {
         this.$message.error('获取区域列表失败')
         this.$message.error('获取区域列表失败')
       }
       }
     },
     },
 
 
-    /** 查询储能计量-小时列表 */
+    // 构建区域映射
+    buildAreaMap(nodes) {
+      nodes.forEach(node => {
+        if (node.id && node.label) {
+          this.areaMap[node.id] = node.label
+        }
+        if (node.children && node.children.length > 0) {
+          this.buildAreaMap(node.children)
+        }
+      })
+    },
+
+    /** 查询储能计量-小时列表 (使用新API) */
     getList() {
     getList() {
       this.loading = true
       this.loading = true
       const { areaCode } = this.queryParams
       const { areaCode } = this.queryParams
@@ -414,7 +648,7 @@ export default {
         startRecTime = start
         startRecTime = start
         endRecTime = end
         endRecTime = end
       }
       }
-      listElecStoreH({
+      listElecStoreHour({
         areaCode,
         areaCode,
         pageNum: 1,
         pageNum: 1,
         pageSize: 999,
         pageSize: 999,
@@ -433,7 +667,7 @@ export default {
     getTodayChart() {
     getTodayChart() {
       const { areaCode } = this.queryParams
       const { areaCode } = this.queryParams
       const nowDay = dateFormat(new Date(), 'yyyy-MM-dd')
       const nowDay = dateFormat(new Date(), 'yyyy-MM-dd')
-      listElecStoreH({
+      listElecStoreHour({
         areaCode,
         areaCode,
         pageNum: 1,
         pageNum: 1,
         pageSize: 999,
         pageSize: 999,
@@ -448,7 +682,7 @@ export default {
 
 
     getSummary() {
     getSummary() {
       const date = dateFormat(new Date(), 'yyyy-MM-dd')
       const date = dateFormat(new Date(), 'yyyy-MM-dd')
-      dayStatistics({ date }).then(({ code, data }) => {
+      getDayStatistics(date).then(({ code, data }) => {
         if (code === 200) {
         if (code === 200) {
           this.hourSum = data.hourSum || []
           this.hourSum = data.hourSum || []
           this.daySum = data.daySum || {}
           this.daySum = data.daySum || {}
@@ -472,11 +706,15 @@ export default {
       this.getTodayChart()
       this.getTodayChart()
     },
     },
 
 
-    /** Tab 切换逻辑 - 修复树高亮问题 */
+    /** Tab 切换逻辑 */
     tabClick() {
     tabClick() {
       if (this.activeName === 'summary') {
       if (this.activeName === 'summary') {
         this.getSummary()
         this.getSummary()
+      } else if (this.activeName === 'realtime') {
+        // 实时分析Tab
+        this.loadRealtimeData()
       } else {
       } else {
+        // 区块储能Tab
         this.selectedLabel = '全部'
         this.selectedLabel = '全部'
         this.queryParams.areaCode = '-1'
         this.queryParams.areaCode = '-1'
 
 
@@ -492,6 +730,136 @@ export default {
         this.getTodayChart()
         this.getTodayChart()
         this.getList()
         this.getList()
       }
       }
+    },
+
+    // ========== 实时分析相关方法 ==========
+
+    /** 加载实时数据 - 先获取设备列表,再匹配指标数据 */
+    async loadRealtimeData() {
+      this.realtimeLoading = true
+      try {
+        // 1. 先获取设备列表
+        const deviceResponse = await getByCondition({ subsystemCode: 'SYS_GCC' })
+        this.deviceList = deviceResponse.data || []
+
+        // 2. 获取每个设备的最新指标数据
+        const indexResponse = await getLatestByDevice({})
+        const indexList = indexResponse.data || []
+
+        // 3. 将指标数据映射为 Map,以 deviceCode 为 key
+        const indexMap = new Map()
+        indexList.forEach(item => {
+          indexMap.set(item.deviceCode, item)
+        })
+
+        // 4. 合并设备信息和指标数据
+        this.deviceRealtimeList = this.deviceList.map(device => {
+          const indexData = indexMap.get(device.deviceCode) || {}
+          return {
+            // 设备基础信息
+            deviceCode: device.deviceCode,
+            deviceName: device.deviceName,
+            deviceBrand: device.deviceBrand,
+            deviceSpec: device.deviceSpec,
+            deviceStatus: device.deviceStatus,
+            location: device.location,
+            areaCode: device.areaCode,
+            refFacs: device.refFacs,
+            // 指标数据
+            chargeVoltage: indexData.chargeVoltage || 0,
+            dischargePower: indexData.dischargePower || 0,
+            currentCapacity: indexData.currentCapacity || 0,
+            indexTime: indexData.time || null
+          }
+        })
+
+        // 5. 计算汇总数据
+        this.calculateDeviceSummary()
+
+        // 6. 更新时间
+        this.lastUpdateTime = this.formatDateTime(new Date())
+      } catch (error) {
+        console.error('加载实时数据失败', error)
+        this.$message.error('加载实时数据失败')
+      } finally {
+        this.realtimeLoading = false
+      }
+    },
+
+    /** 刷新实时数据 */
+    refreshRealtimeData() {
+      this.loadRealtimeData()
+    },
+
+    /** 计算设备汇总数据 */
+    calculateDeviceSummary() {
+      const summary = {
+        totalCount: this.deviceRealtimeList.length,
+        totalChargePower: 0,
+        totalDischargePower: 0,
+        totalCapacity: 0
+      }
+
+      this.deviceRealtimeList.forEach(device => {
+        summary.totalChargePower += parseFloat(device.chargeVoltage) || 0
+        summary.totalDischargePower += parseFloat(device.dischargePower) || 0
+        summary.totalCapacity += parseFloat(device.currentCapacity) || 0
+      })
+
+      this.deviceSummary = summary
+    },
+
+    /** 获取设备状态样式类 */
+    getDeviceStatusClass(device) {
+      const chargePower = parseFloat(device.chargeVoltage) || 0
+      const dischargePower = parseFloat(device.dischargePower) || 0
+
+      if (chargePower > dischargePower) {
+        return 'charging'
+      } else if (dischargePower > chargePower) {
+        return 'discharging'
+      }
+      return 'idle'
+    },
+
+    /** 获取设备状态标签类型 */
+    getDeviceStatusType(device) {
+      const chargePower = parseFloat(device.chargeVoltage) || 0
+      const dischargePower = parseFloat(device.dischargePower) || 0
+
+      if (chargePower > dischargePower) {
+        return 'success'
+      } else if (dischargePower > chargePower) {
+        return 'warning'
+      }
+      return 'info'
+    },
+
+    /** 获取设备状态文本 */
+    getDeviceStatusText(device) {
+      const chargePower = parseFloat(device.chargeVoltage) || 0
+      const dischargePower = parseFloat(device.dischargePower) || 0
+
+      if (chargePower > dischargePower) {
+        return '充电中'
+      } else if (dischargePower > chargePower) {
+        return '放电中'
+      }
+      return '待机'
+    },
+
+    /** 获取容量百分比 */
+    getCapacityPercentage(device) {
+      const capacity = parseFloat(device.currentCapacity) || 0
+      return Math.min(100, Math.max(0, (capacity / this.maxCapacityConfig) * 100))
+    },
+
+    /** 获取容量进度条颜色 */
+    getCapacityColor(device) {
+      const percentage = this.getCapacityPercentage(device)
+      if (percentage >= 60) return '#67c23a'
+      if (percentage >= 30) return '#e6a23c'
+      return '#f56c6c'
     }
     }
   }
   }
 }
 }
@@ -617,6 +985,338 @@ export default {
   }
   }
 }
 }
 
 
+// ========== 实时分析Tab样式 ==========
+.overview-section,
+.device-realtime-section {
+  margin-bottom: 24px;
+}
+
+.section-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  margin-bottom: 16px;
+
+  .section-title {
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+    margin: 0;
+    display: flex;
+    align-items: center;
+
+    i {
+      margin-right: 10px;
+      color: #409eff;
+      font-size: 20px;
+    }
+  }
+
+  .update-time {
+    font-size: 12px;
+    color: #909399;
+
+    i {
+      margin-right: 4px;
+    }
+  }
+}
+
+// 统计卡片
+.stat-card {
+  background: #fff;
+  border-radius: 12px;
+  padding: 20px;
+  display: flex;
+  align-items: center;
+  border: 1px solid #ebeef5;
+  transition: all 0.3s ease;
+  position: relative;
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 4px;
+    height: 100%;
+  }
+
+  &:hover {
+    transform: translateY(-4px);
+    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
+  }
+
+  .card-icon {
+    width: 56px;
+    height: 56px;
+    border-radius: 12px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    margin-right: 16px;
+    flex-shrink: 0;
+
+    i {
+      font-size: 26px;
+      color: #fff;
+    }
+  }
+
+  .card-content {
+    flex: 1;
+    display: flex;
+    flex-direction: column;
+
+    .card-label {
+      font-size: 13px;
+      color: #909399;
+      margin-bottom: 6px;
+      font-weight: 500;
+    }
+
+    .card-value {
+      font-size: 24px;
+      font-weight: 700;
+      color: #303133;
+      line-height: 1.2;
+
+      small {
+        font-size: 13px;
+        font-weight: 400;
+        color: #909399;
+        margin-left: 4px;
+      }
+    }
+  }
+
+  // 卡片颜色变体
+  &.total-card {
+    &::before { background: #409eff; }
+    .card-icon { background: linear-gradient(135deg, #409eff 0%, #53a8ff 100%); }
+  }
+
+  &.charge-card {
+    &::before { background: #67c23a; }
+    .card-icon { background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%); }
+  }
+
+  &.discharge-card {
+    &::before { background: #e6a23c; }
+    .card-icon { background: linear-gradient(135deg, #e6a23c 0%, #f5c069 100%); }
+  }
+
+  &.capacity-card {
+    &::before { background: #909399; }
+    .card-icon { background: linear-gradient(135deg, #909399 0%, #b4b4b4 100%); }
+  }
+}
+
+// 设备卡片
+.device-card-col {
+  margin-bottom: 16px;
+}
+
+.device-card {
+  background: #fff;
+  border-radius: 12px;
+  padding: 16px;
+  border: 1px solid #ebeef5;
+  transition: all 0.3s ease;
+  position: relative;
+  overflow: hidden;
+
+  &::before {
+    content: '';
+    position: absolute;
+    top: 0;
+    left: 0;
+    width: 4px;
+    height: 100%;
+    background: #909399;
+  }
+
+  &.charging::before {
+    background: #67c23a;
+  }
+
+  &.discharging::before {
+    background: #e6a23c;
+  }
+
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.1);
+  }
+
+  .device-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    margin-bottom: 12px;
+
+    .device-title {
+      display: flex;
+      align-items: center;
+
+      .device-icon {
+        font-size: 20px;
+        color: #409eff;
+        margin-right: 8px;
+      }
+
+      .device-name {
+        font-size: 15px;
+        font-weight: 600;
+        color: #303133;
+      }
+    }
+  }
+
+  .device-body {
+    .device-info {
+      margin-bottom: 12px;
+      padding-bottom: 12px;
+      border-bottom: 1px dashed #ebeef5;
+
+      .info-row {
+        display: flex;
+        align-items: center;
+        margin-bottom: 6px;
+        font-size: 13px;
+
+        &:last-child {
+          margin-bottom: 0;
+        }
+
+        .info-label {
+          color: #909399;
+          width: 70px;
+          flex-shrink: 0;
+        }
+
+        .info-value {
+          color: #606266;
+
+          &.code {
+            font-family: 'Courier New', monospace;
+            background: #f4f4f5;
+            padding: 2px 6px;
+            border-radius: 4px;
+            font-size: 12px;
+          }
+        }
+      }
+    }
+
+    .device-metrics {
+      display: flex;
+      gap: 12px;
+      margin-bottom: 12px;
+
+      .metric-item {
+        flex: 1;
+        display: flex;
+        align-items: center;
+        padding: 10px;
+        background: #f5f7fa;
+        border-radius: 8px;
+
+        .metric-icon {
+          width: 32px;
+          height: 32px;
+          border-radius: 8px;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          margin-right: 10px;
+
+          i {
+            font-size: 16px;
+            color: #fff;
+          }
+        }
+
+        &.charge .metric-icon {
+          background: linear-gradient(135deg, #67c23a 0%, #85ce61 100%);
+        }
+
+        &.discharge .metric-icon {
+          background: linear-gradient(135deg, #e6a23c 0%, #f5c069 100%);
+        }
+
+        .metric-info {
+          display: flex;
+          flex-direction: column;
+
+          .metric-label {
+            font-size: 11px;
+            color: #909399;
+          }
+
+          .metric-value {
+            font-size: 14px;
+            font-weight: 600;
+            color: #303133;
+
+            small {
+              font-size: 11px;
+              font-weight: 400;
+              color: #909399;
+            }
+          }
+        }
+      }
+    }
+
+    .capacity-bar {
+      .capacity-label {
+        display: flex;
+        justify-content: space-between;
+        margin-bottom: 6px;
+        font-size: 12px;
+        color: #606266;
+
+        .capacity-value {
+          font-weight: 600;
+          color: #303133;
+        }
+      }
+
+      .capacity-percent {
+        text-align: right;
+        font-size: 11px;
+        color: #909399;
+        margin-top: 4px;
+      }
+
+      ::v-deep .el-progress-bar__outer {
+        border-radius: 6px;
+      }
+
+      ::v-deep .el-progress-bar__inner {
+        border-radius: 6px;
+      }
+    }
+  }
+
+  .device-footer {
+    display: flex;
+    justify-content: space-between;
+    margin-top: 12px;
+    padding-top: 12px;
+    border-top: 1px solid #ebeef5;
+    font-size: 12px;
+    color: #909399;
+
+    i {
+      margin-right: 4px;
+    }
+  }
+}
+
+// ========== 区块储能Tab样式 ==========
 .head-container {
 .head-container {
   background: #fff;
   background: #fff;
   padding: 15px;
   padding: 15px;
@@ -757,6 +1457,13 @@ export default {
       margin-bottom: 20px;
       margin-bottom: 20px;
     }
     }
   }
   }
+
+  .device-card {
+    .device-metrics {
+      flex-direction: column;
+      gap: 8px !important;
+    }
+  }
 }
 }
 
 
 @media (max-width: 768px) {
 @media (max-width: 768px) {

+ 3 - 3
ems-ui-cloud/src/views/mgr/strategy/editor.vue

@@ -577,9 +577,9 @@ export default {
       ],
       ],
 
 
       sceneTypes: [
       sceneTypes: [
-        { value: 'PV_ESS', label: '光储协同' },
-        { value: 'DEMAND_RESP', label: '需求响应' },
-        { value: 'PEAK_VALLEY', label: '削峰填谷' },
+        { value: 'PV_ESS', label: '源网协调' },
+        { value: 'DEMAND_RESP', label: '源荷互动' },
+        { value: 'PEAK_VALLEY', label: '网储互动' },
         { value: 'EMERGENCY', label: '应急保供' },
         { value: 'EMERGENCY', label: '应急保供' },
         { value: 'ENERGY_SAVE', label: '节能优化' }
         { value: 'ENERGY_SAVE', label: '节能优化' }
       ]
       ]

+ 3 - 3
ems-ui-cloud/src/views/mgr/strategy/index.vue

@@ -527,19 +527,19 @@ export default {
         },
         },
         {
         {
           value: 'PV_ESS',
           value: 'PV_ESS',
-          label: '光储协同',
+          label: '源网协调',
           icon: 'el-icon-sunny',
           icon: 'el-icon-sunny',
           gradient: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)'
           gradient: 'linear-gradient(135deg, #f093fb 0%, #f5576c 100%)'
         },
         },
         {
         {
           value: 'DEMAND_RESP',
           value: 'DEMAND_RESP',
-          label: '需求响应',
+          label: '源荷互动',
           icon: 'el-icon-s-marketing',
           icon: 'el-icon-s-marketing',
           gradient: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)'
           gradient: 'linear-gradient(135deg, #4facfe 0%, #00f2fe 100%)'
         },
         },
         {
         {
           value: 'PEAK_VALLEY',
           value: 'PEAK_VALLEY',
-          label: '削峰填谷',
+          label: '网储互动',
           icon: 'el-icon-data-analysis',
           icon: 'el-icon-data-analysis',
           gradient: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)'
           gradient: 'linear-gradient(135deg, #43e97b 0%, #38f9d7 100%)'
         },
         },

+ 3 - 3
ems-ui-cloud/src/views/mgr/strategy/template.vue

@@ -255,9 +255,9 @@ export default {
       currentScene: 'ALL',
       currentScene: 'ALL',
 
 
       sceneTypes: [
       sceneTypes: [
-        { value: 'PV_ESS', label: '光储协同', icon: 'el-icon-sunny' },
-        { value: 'DEMAND_RESP', label: '需求响应', icon: 'el-icon-s-marketing' },
-        { value: 'PEAK_VALLEY', label: '削峰填谷', icon: 'el-icon-data-analysis' },
+        { value: 'PV_ESS', label: '源网协调', icon: 'el-icon-sunny' },
+        { value: 'DEMAND_RESP', label: '源荷互动', icon: 'el-icon-s-marketing' },
+        { value: 'PEAK_VALLEY', label: '网储互动', icon: 'el-icon-data-analysis' },
         { value: 'EMERGENCY', label: '应急保供', icon: 'el-icon-warning-outline' },
         { value: 'EMERGENCY', label: '应急保供', icon: 'el-icon-warning-outline' },
         { value: 'ENERGY_SAVE', label: '节能优化', icon: 'el-icon-odometer' }
         { value: 'ENERGY_SAVE', label: '节能优化', icon: 'el-icon-odometer' }
       ],
       ],

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 669 - 149
ems-ui-cloud/src/views/prediction/prod.vue


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů