learshaw преди 1 месец
родител
ревизия
8ab5b9db4c

+ 5 - 6
ems-ui-cloud/src/views/analysis/report/statement-consume.vue

@@ -74,7 +74,7 @@
             :total="total"
             :page.sync="queryParams.pageNum"
             :limit.sync="queryParams.pageSize"
-            @pagination="getAreaConsumeList"
+            @pagination="getConsumeList"
           />
 
         </el-form>
@@ -299,13 +299,12 @@ export default {
     },
     // 追溯顶级节点ID的辅助函数
     getTopLevelId(node) {
-      let id = node.parent.data.id
+      let currentNode = node;
 
-      if (id === undefined || id === null) {
-        id = node.data.id
+      while (currentNode.parent && currentNode.parent.id !== 0) {
+        currentNode = currentNode.parent;
       }
-
-      return id
+      return currentNode.data.id;
     },
     // 获取本月1号 00:00:00
     getFirstDayOfMonth() {

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

@@ -115,8 +115,8 @@ export default {
         pageNum: 1,
         pageSize: 10,
         areaCode: null,
-        startRecTime: null,
-        endRecTime: null,
+        startRecTime: this.getFirstDayOfMonth(), // 本月1号
+        endRecTime: this.getTodayEndTime(),    // 当天结束时间
       },
       // 表单参数
       form: {},
@@ -132,23 +132,43 @@ export default {
       // 时间选择器配置
       startPickerOptions: {
         disabledDate: (time) => {
-          return time.getTime() > Date.now() - 8.64e7 // 禁用未来时间
+          // 禁用未来时间和超过90天前的时间
+          const ninetyDaysAgo = new Date();
+          ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
+          return time.getTime() > Date.now() - 8.64e7 || time.getTime() < ninetyDaysAgo.getTime();
         },
         selectableRange: this.generateHourRanges()
       },
       endPickerOptions: {
         disabledDate: (time) => {
           if (this.queryParams.startRecTime) {
-            return time.getTime() < new Date(this.queryParams.startRecTime).getTime() ||
-              time.getTime() > Date.now() - 8.64e7
+            // 结束时间不能早于开始时间,不能晚于今天,且不能超过开始时间90天后
+            const startDate = new Date(this.queryParams.startRecTime);
+            const endDateLimit = new Date(startDate);
+            endDateLimit.setDate(endDateLimit.getDate() + 90);
+            endDateLimit.setHours(23, 59, 59);
+
+            return time.getTime() < startDate.getTime() ||
+              time.getTime() > endDateLimit.getTime() ||
+              time.getTime() > Date.now() - 8.64e7;
           }
-          return time.getTime() > Date.now() - 8.64e7
+          // 禁用未来时间和超过90天前的时间
+          const ninetyDaysAgo = new Date();
+          ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90);
+          return time.getTime() > Date.now() - 8.64e7 || time.getTime() < ninetyDaysAgo.getTime();
         },
         selectableRange: this.generateHourRanges()
       }
     };
   },
   created() {
+    // 初始化时间格式
+    if (this.queryParams.startRecTime) {
+      this.queryParams.startRecTime = this.formatDateTime(this.queryParams.startRecTime);
+    }
+    if (this.queryParams.endRecTime) {
+      this.queryParams.endRecTime = this.formatDateTime(this.queryParams.endRecTime);
+    }
     this.getAreaList();
     this.getList();
   },
@@ -235,6 +255,38 @@ export default {
         ...this.queryParams
       }, `产能报表_${new Date().getTime()}.xlsx`)
     },
+
+    // 获取本月1号 00:00:00
+    getFirstDayOfMonth() {
+      const date = new Date();
+      date.setDate(1);
+      date.setHours(0, 0, 0, 0);
+      return this.formatDateTime(date);
+    },
+
+    // 获取当天 23:59:59
+    getTodayEndTime() {
+      const date = new Date();
+      date.setHours(23, 59, 59, 999);
+      return this.formatDateTime(date);
+    },
+
+    // 格式化日期时间为 yyyy-MM-dd HH:mm:ss
+    formatDateTime(date) {
+      if (!date) return '';
+      if (typeof date === 'string') {
+        date = new Date(date);
+      }
+
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, '0');
+      const day = String(date.getDate()).padStart(2, '0');
+      const hours = String(date.getHours()).padStart(2, '0');
+      const minutes = String(date.getMinutes()).padStart(2, '0');
+      const seconds = String(date.getSeconds()).padStart(2, '0');
+
+      return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+    },
     // 筛选节点
     filterNode (value, data) {
       if (!value) return true
@@ -246,6 +298,7 @@ export default {
       this.selectedLabel = data.label
       this.getList()
     }
+
   }
 };
 </script>

+ 178 - 303
ems-ui-cloud/src/views/analysis/report/statement-self.vue

@@ -1,342 +1,217 @@
 <template>
   <div class="app-container">
-    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
-      <el-form-item label="日期" prop="date">
-        <el-date-picker clearable
-                        v-model="queryParams.date"
-                        type="date"
-                        value-format="yyyy-MM-dd"
-                        placeholder="请选择日期">
-        </el-date-picker>
-      </el-form-item>
-      <el-form-item label="自定义" prop="rule">
-        <div style="display: flex;align-items: flex-start;justify-content: flex-start">
-          <el-select v-model="queryParams.conditionRule" placeholder="请选择条件">
-            <el-option
-                v-for="item in conditionRules"
-                :key="item.value"
-                :label="item.label"
-                :value="item.value">
-            </el-option>
-          </el-select>
-          <el-select v-model="queryParams.condition">
-            <el-option
-                v-for="item in condition"
-                :key="item.value"
-                :label="item.label"
-                :value="item.value">
-            </el-option>
-          </el-select>
-          <el-input
-              v-model="queryParams.conditionVal"
-              placeholder="请输入条件值"
-              clearable
-              @keyup.enter.native="handleQuery"
-          />
+    <el-row :gutter="20">
+      <!-- 区域树结构 -->
+      <el-col :span="4" :xs="24">
+        <div class="head-container">
+          <el-input v-model="areaName" placeholder="请输入区域名称" clearable size="small" prefix-icon="el-icon-search" style="margin-bottom: 20px" />
         </div>
+        <div class="head-container" style="height: 100vh; overflow: hidden; position: relative;">
+          <el-tree :data="areaOptions" :props="defaultProps" :expand-on-click-node="false" :filter-node-method="filterNode" ref="tree" node-key="id" default-expand-all highlight-current @node-click="handleNodeClick" style="height: calc(100vh - 50px); overflow-y: auto;" />
+        </div>
+      </el-col>
 
-      </el-form-item>
-      <el-form-item>
-        <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
-        <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
-      </el-form-item>
-    </el-form>
+      <!-- 查询条件与结果表格 -->
+      <el-col :span="20" :xs="24">
+        <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="80px">
+          <el-form-item label="报表类型">
+            <el-select v-model="queryParams.reportType" placeholder="请选择类型" @change="handleReportTypeChange">
+              <el-option label="产能" value="prod" />
+              <el-option label="用能" value="consume" />
+            </el-select>
+          </el-form-item>
 
-    <el-row :gutter="10" class="mb8">
-      <!--      <el-col :span="1.5">-->
-      <!--        <el-button-->
-      <!--          type="primary"-->
-      <!--          plain-->
-      <!--          icon="el-icon-plus"-->
-      <!--          size="mini"-->
-      <!--          @click="handleAdd"-->
-      <!--          v-hasPermi="['ems:EmsEcoD:add']"-->
-      <!--        >新增</el-button>-->
-      <!--      </el-col>-->
-      <!--      <el-col :span="1.5">-->
-      <!--        <el-button-->
-      <!--          type="success"-->
-      <!--          plain-->
-      <!--          icon="el-icon-edit"-->
-      <!--          size="mini"-->
-      <!--          :disabled="single"-->
-      <!--          @click="handleUpdate"-->
-      <!--          v-hasPermi="['ems:EmsEcoD:edit']"-->
-      <!--        >修改</el-button>-->
-      <!--      </el-col>-->
-      <!--      <el-col :span="1.5">-->
-      <!--        <el-button-->
-      <!--          type="danger"-->
-      <!--          plain-->
-      <!--          icon="el-icon-delete"-->
-      <!--          size="mini"-->
-      <!--          :disabled="multiple"-->
-      <!--          @click="handleDelete"-->
-      <!--          v-hasPermi="['ems:EmsEcoD:remove']"-->
-      <!--        >删除</el-button>-->
-      <!--      </el-col>-->
-      <!--      <el-col :span="1.5">-->
-      <!--        <el-button-->
-      <!--          type="warning"-->
-      <!--          plain-->
-      <!--          icon="el-icon-download"-->
-      <!--          size="mini"-->
-      <!--          @click="handleExport"-->
-      <!--          v-hasPermi="['ems:EmsEcoD:export']"-->
-      <!--        >导出</el-button>-->
-      <!--      </el-col>-->
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
+          <el-form-item label="指标项">
+            <el-select v-model="queryParams.metricField" placeholder="请选择指标">
+              <el-option v-for="item in metricOptions" :key="item.value" :label="item.label" :value="item.value" />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="条件">
+            <el-select v-model="queryParams.conditionType" placeholder="请选择条件">
+              <el-option label="大于" value="gt" />
+              <el-option label="小于" value="lt" />
+              <el-option label="等于" value="eq" />
+              <el-option label="不等于" value="ne" />
+            </el-select>
+          </el-form-item>
+
+          <el-form-item label="指标值">
+            <el-input v-model.number="queryParams.metricValue" type="number" placeholder="请输入值" />
+          </el-form-item>
+
+          <el-form-item label="开始时间">
+            <el-date-picker v-model="queryParams.startRecTime" type="datetime" value-format="yyyy-MM-dd HH:00:00" />
+          </el-form-item>
+
+          <el-form-item label="结束时间">
+            <el-date-picker v-model="queryParams.endRecTime" type="datetime" value-format="yyyy-MM-dd HH:00:00" />
+          </el-form-item>
+
+          <el-form-item>
+            <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">查询</el-button>
+            <el-button type="warning" plain icon="el-icon-download" size="mini" @click="handleExport">导出</el-button>
+          </el-form-item>
+        </el-form>
 
-    <el-table v-loading="loading" :data="pvConsumeHList">
-      <el-table-column label="对象名称" align="center" prop="objName"/>
-      <el-table-column label="日期" align="center" prop="date" width="180">
-        <template slot-scope="scope">
-          <span>{{ parseTime(scope.row.date, '{y}-{m}-{d}') }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="时间" align="center" prop="time">
-        <template slot-scope="scope">
-          <span>{{ scope.row.time }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="视在功率(kW)" align="center" prop="s"/>
-      <el-table-column label="有功功率(kW)" align="center" prop="p"/>
-      <el-table-column label="无功功率(kW)" align="center" prop="q"/>
-    </el-table>
-    <pagination
-        v-show="total>0"
-        :total="total"
-        :page.sync="queryParams.pageNum"
-        :limit.sync="queryParams.pageSize"
-        @pagination="getList"
-    />
+        <el-table v-loading="loading" :data="resultList">
+          <el-table-column v-for="col in resultTableCols" :key="col.prop" :label="col.label" :prop="col.prop" align="center" />
+        </el-table>
 
-    <!-- 添加或修改节能计量日对话框 -->
-    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
-      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
-        <el-form-item label="园区代码" prop="areaCode">
-          <el-input v-model="form.areaCode" placeholder="请输入园区代码"/>
-        </el-form-item>
-        <el-form-item label="日期" prop="date">
-          <el-date-picker clearable
-                          v-model="form.date"
-                          type="date"
-                          value-format="yyyy-MM-dd"
-                          placeholder="请选择日期">
-          </el-date-picker>
-        </el-form-item>
-        <el-form-item label="节电(千瓦时)" prop="elecEcoQuantity">
-          <el-input v-model="form.elecEcoQuantity" placeholder="请输入节电"/>
-        </el-form-item>
-        <el-form-item label="节电金额(元)" prop="elecEcoCost">
-          <el-input v-model="form.elecEcoCost" placeholder="请输入节电金额"/>
-        </el-form-item>
-        <el-form-item label="节水量 (吨)" prop="waterEcoQuantity">
-          <el-input v-model="form.waterEcoQuantity" placeholder="请输入节水量 "/>
-        </el-form-item>
-        <el-form-item label="节水金额(元)" prop="waterEcoCost">
-          <el-input v-model="form.waterEcoCost" placeholder="请输入节水金额"/>
-        </el-form-item>
-      </el-form>
-      <div slot="footer" class="dialog-footer">
-        <el-button type="primary" @click="submitForm">确 定</el-button>
-        <el-button @click="cancel">取 消</el-button>
-      </div>
-    </el-dialog>
+        <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize" @pagination="handleQuery" />
+      </el-col>
+    </el-row>
   </div>
 </template>
 
 <script>
-import {addEmsEcoD, delEmsEcoD, getEmsEcoD, updateEmsEcoD} from "@/api/ems/EmsEcoD";
-import {getPowerData} from "@/api/mgr/elecUseH";
-import {parseTime} from "@/utils/ruoyi";
+import { areaTreeSelect } from '@/api/basecfg/area'
+import { listAreaMeter } from '@/api/device/elecMeterH'
+import { listPvSupplyH } from '@/api/mgr/pgSupplyH'
 
 export default {
-  name: "EmsEcoD",
+  name: 'CustomReport',
   data() {
     return {
-      // 遮罩层
-      loading: true,
-      // 选中数组
-      ids: [],
-      // 非单个禁用
-      single: true,
-      // 非多个禁用
-      multiple: true,
-      // 显示搜索条件
-      showSearch: true,
-      // 总条数
+      loading: false,
+      areaName: '',
+      areaOptions: [],
       total: 0,
-      // 节能计量日表格数据
-      EmsEcoDList: [],
-      pvConsumeHList: [],
-      // 弹出层标题
-      title: "",
-      // 是否显示弹出层
-      open: false,
-      conditionRules: [
-        {
-          value: 's',
-          label: '视在功率'
-        },
-        {
-          value: 'p',
-          label: '有功功率'
-        },
-        {
-          value: 'q',
-          label: '无功功率'
-        }
-      ],
-      condition: [
-        {
-          value: 'equal',
-          label: '等于'
-        },
-        {
-          value: 'gt',
-          label: '大于'
-        },
-        {
-          value: 'lt',
-          label: '小于'
-        },
-        {
-          value: 'notequal',
-          label: '不等于'
-        },
-
-        {
-          value: 'notnull',
-          label: '非空'
-        },
-        {
-          value: 'null',
-          label: '为空'
-        },
-      ],
-      // 查询参数
+      resultList: [],
+      resultTableCols: [],
       queryParams: {
         pageNum: 1,
         pageSize: 10,
-        areaCode: null,
-        rule: null,
-        conditionRule: null,
-        condition: 'equal',
-        conditionVal: null,
-        date: parseTime(new Date(), '{y}-{m}-{d}'),
+        reportType: 'prod',
+        metricField: '',
+        conditionType: 'gt',
+        metricValue: null,
+        areaCode: '-1',
+        startRecTime: this.getFirstDayOfMonth(),
+        endRecTime: this.getTodayEndTime(),
       },
-      // 表单参数
-      form: {},
-      // 表单校验
-      rules: {
-        areaCode: [
-          {required: true, message: "园区代码不能为空", trigger: "blur"}
-        ],
-        date: [
-          {required: true, message: "日期不能为空", trigger: "blur"}
+      metricOptions: [],
+      metricOptionsMap: {
+        prod: [
+          { label: '总发电量', value: 'genElecQuantity' },
+          { label: '自用电量', value: 'useElecQuantity' },
+          { label: '上网电量', value: 'upElecQuantity' },
+          { label: '上网收益', value: 'upElecEarn' },
         ],
+        consume: [
+          { label: '用电量', value: 'elecQuantity' },
+          { label: '用电花费', value: 'useElecCost' },
+        ]
+      },
+      defaultProps: {
+        children: 'children',
+        label: 'label'
       }
-    };
+    }
   },
   created() {
-    this.getList();
+    this.metricOptions = this.metricOptionsMap[this.queryParams.reportType];
+    this.getAreaList();
   },
   methods: {
-    /** 查询节能计量日列表 */
-    getList() {
-      this.loading = true;
-
-      getPowerData(this.queryParams).then(response => {
-        this.pvConsumeHList = response.rows
-        this.total = response.total
-        this.loading = false
-      })
+    getAreaList() {
+      areaTreeSelect(0, 1).then(response => {
+        this.areaOptions = [{ id: '-1', label: '全部', children: [] }].concat(response.data || []);
+      });
     },
-    // 取消按钮
-    cancel() {
-      this.open = false;
-      this.reset();
+    handleNodeClick(data) {
+      this.queryParams.areaCode = data.id;
     },
-    // 表单重置
-    reset() {
-      this.form = {
-        id: null,
-        areaCode: null,
-        date: null
-      };
-      this.resetForm("form");
+    handleReportTypeChange(type) {
+      this.metricOptions = this.metricOptionsMap[type];
+      this.queryParams.metricField = '';
     },
-    /** 搜索按钮操作 */
     handleQuery() {
-      this.queryParams.pageNum = 1;
-      this.getList();
+      const { reportType, ...params } = this.queryParams;
+      this.loading = true;
+      const api = reportType === 'prod' ? listPvSupplyH : listAreaMeter;
+      api(params).then(res => {
+        const list = res.rows || [];
+        const metric = this.queryParams.metricField;
+        const value = this.queryParams.metricValue;
+        const op = this.queryParams.conditionType;
+        this.resultList = list.filter(item => {
+          const v = item[metric];
+          if (v === undefined || v === null) return false;
+          switch (op) {
+            case 'gt': return v > value;
+            case 'lt': return v < value;
+            case 'eq': return v == value;
+            case 'ne': return v != value;
+            default: return true;
+          }
+        });
+        this.resultTableCols = this.generateColumns(reportType);
+        this.total = this.resultList.length;
+        this.loading = false;
+      });
     },
-    /** 重置按钮操作 */
-    resetQuery() {
-      this.resetForm("queryForm");
-      this.handleQuery();
+    generateColumns(type) {
+      return type === 'prod'
+        ? [
+          { label: '日期', prop: 'date' },
+          { label: '时间', prop: 'time' },
+          { label: '总发电量', prop: 'genElecQuantity' },
+          { label: '自用电量', prop: 'useElecQuantity' },
+          { label: '上网电量', prop: 'upElecQuantity' },
+          { label: '上网收益', prop: 'upElecEarn' },
+        ]
+        : [
+          { label: '对象名称', prop: 'deviceName' },
+          { label: '日期', prop: 'date' },
+          { label: '时间', prop: 'time' },
+          { label: '用电量', prop: 'elecQuantity' },
+          { label: '用电花费', prop: 'useElecCost' },
+        ];
     },
-    // 多选框选中数据
-    handleSelectionChange(selection) {
-      this.ids = selection.map(item => item.id)
-      this.single = selection.length !== 1
-      this.multiple = !selection.length
+    handleExport() {
+      const { reportType } = this.queryParams;
+      const url = reportType === 'prod'
+        ? 'ems/prod/pv/hour/export'
+        : 'ems/elecMeterH/exportAreaMeter';
+      this.download(url, this.queryParams, `自定义报表_${new Date().getTime()}.xlsx`);
     },
-    /** 新增按钮操作 */
-    handleAdd() {
-      this.reset();
-      this.open = true;
-      this.title = "添加节能计量日";
+    getFirstDayOfMonth() {
+      const date = new Date();
+      date.setDate(1);
+      date.setHours(0, 0, 0, 0);
+      return this.formatDateTime(date);
     },
-    /** 修改按钮操作 */
-    handleUpdate(row) {
-      this.reset();
-      const id = row.id || this.ids
-      getEmsEcoD(id).then(response => {
-        this.form = response.data;
-        this.open = true;
-        this.title = "修改节能计量日";
-      });
+    getTodayEndTime() {
+      const date = new Date();
+      date.setHours(23, 59, 59, 999);
+      return this.formatDateTime(date);
     },
-    /** 提交按钮 */
-    submitForm() {
-      this.$refs["form"].validate(valid => {
-        if (valid) {
-          if (this.form.id != null) {
-            updateEmsEcoD(this.form).then(response => {
-              this.$modal.msgSuccess("修改成功");
-              this.open = false;
-              this.getList();
-            });
-          } else {
-            addEmsEcoD(this.form).then(response => {
-              this.$modal.msgSuccess("新增成功");
-              this.open = false;
-              this.getList();
-            });
-          }
-        }
-      });
-    },
-    /** 删除按钮操作 */
-    handleDelete(row) {
-      const ids = row.id || this.ids;
-      this.$modal.confirm('是否确认删除节能计量日编号为"' + ids + '"的数据项?').then(function () {
-        return delEmsEcoD(ids);
-      }).then(() => {
-        this.getList();
-        this.$modal.msgSuccess("删除成功");
-      }).catch(() => {
-      });
+    formatDateTime(date) {
+      if (!date) return '';
+      if (typeof date === 'string') date = new Date(date);
+      const y = date.getFullYear();
+      const m = String(date.getMonth() + 1).padStart(2, '0');
+      const d = String(date.getDate()).padStart(2, '0');
+      const h = String(date.getHours()).padStart(2, '0');
+      return `${y}-${m}-${d} ${h}:00:00`;
     },
-    /** 导出按钮操作 */
-    handleExport() {
-      this.download('ems/EmsEcoD/export', {
-        ...this.queryParams
-      }, `EmsEcoD_${new Date().getTime()}.xlsx`)
+    filterNode(value, data) {
+      if (!value) return true;
+      return data.label.indexOf(value) !== -1;
     }
   }
-};
+}
 </script>
+
+<style scoped>
+.el-tree .el-tree-node.is-leaf > .el-tree-node__content {
+  color: #333;
+  cursor: pointer;
+}
+
+.el-tree .el-tree-node:not(.is-leaf) > .el-tree-node__content {
+  color: #909399;
+  cursor: not-allowed;
+}
+</style>

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

@@ -308,9 +308,9 @@ export default {
     },
     /** 导出按钮操作 */
     handleExport() {
-      this.download('ems/EmsEcoD/export', {
+      this.download('ems/alarm-info/export', {
         ...this.queryParams
-      }, `EmsEcoD_${new Date().getTime()}.xlsx`)
+      }, `alarm_${new Date().getTime()}.xlsx`)
     },
     // 筛选节点
     filterNode(value, data) {

+ 5 - 5
ems-ui-cloud/src/views/ca/emission.vue

@@ -114,7 +114,7 @@ export default {
     };
   },
   created() {
-    this.setTodayDate();
+    this.initDateRange();
     this.getAreaList();
     this.getList();
   },
@@ -166,17 +166,17 @@ export default {
         this.queryParams.areaCode = '-1'
       })
     },
-    setTodayDate() {
+    initDateRange() {
       // 获取当前日期
       const today = new Date();
+      const yesterday = new Date(today);
+      yesterday.setDate(yesterday.getDate() - 1);
 
       // 设置开始日期为当月第一天
-      const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
+      const firstDayOfMonth = new Date(yesterday.getFullYear(), yesterday.getMonth(), 1);
       this.queryParams.startRecTime = this.formatDate(firstDayOfMonth);
 
       // 设置结束日期为昨天
-      const yesterday = new Date(today);
-      yesterday.setDate(yesterday.getDate() - 1);
       this.queryParams.endRecTime = this.formatDate(yesterday);
     },
     formatDate(date) {

+ 5 - 5
ems-ui-cloud/src/views/ca/emissionCalc.vue

@@ -114,7 +114,7 @@ export default {
     };
   },
   created() {
-    this.setTodayDate();
+    this.initDateRange();
     this.getAreaList();
     this.getList();
   },
@@ -166,17 +166,17 @@ export default {
         this.queryParams.areaCode = '-1'
       })
     },
-    setTodayDate() {
+    initDateRange() {
       // 获取当前日期
       const today = new Date();
+      const yesterday = new Date(today);
+      yesterday.setDate(yesterday.getDate() - 1);
 
       // 设置开始日期为当月第一天
-      const firstDayOfMonth = new Date(today.getFullYear(), today.getMonth(), 1);
+      const firstDayOfMonth = new Date(yesterday.getFullYear(), yesterday.getMonth(), 1);
       this.queryParams.startRecTime = this.formatDate(firstDayOfMonth);
 
       // 设置结束日期为昨天
-      const yesterday = new Date(today);
-      yesterday.setDate(yesterday.getDate() - 1);
       this.queryParams.endRecTime = this.formatDate(yesterday);
     },
     formatDate(date) {

+ 208 - 293
ems-ui-cloud/src/views/prediction/consume.vue

@@ -1,75 +1,64 @@
 <template>
   <div class="app-container">
+    <el-tabs v-model="activeName" @tab-click="tabClick">
+      <el-tab-pane label="区域预测" name="areaConsume">
+      </el-tab-pane>
+      <el-tab-pane label="设施预测" name="facsConsume">
+      </el-tab-pane>
+    </el-tabs>
     <el-row :gutter="20">
       <el-col :span="4" :xs="24">
         <div class="head-container">
           <el-input v-model="areaName" placeholder="请输入区域名称" clearable size="small" prefix-icon="el-icon-search"
-                    style="margin-bottom: 20px" />
+                    style="margin-bottom: 20px"
+          />
         </div>
         <div class="head-container" style="height: 100vh; overflow: hidden; position: relative;">
-          <el-tree :data="areaOptions" :props="defaultProps" :expand-on-click-node="false"
+          <el-tree :data="objOptions" :props="defaultProps" :expand-on-click-node="false"
                    :filter-node-method="filterNode" ref="tree" node-key="id" default-expand-all highlight-current
-                   @node-click="handleNodeClick" style="height: calc(100vh - 50px); overflow-y: auto;" />
+                   @node-click="handleNodeClick" style="height: calc(100vh - 50px); overflow-y: auto;"
+          />
         </div>
       </el-col>
       <el-col :span="20" :xs="24">
-        <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
-          <el-form-item label="对象类型" prop="objType">
-            <el-select v-model="queryParams.objType" placeholder="请选择对象类型" clearable>
-              <el-option
-                v-for="item in objTypeOptions"
-                :key="item.value"
-                :label="item.label"
-                :value="item.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item label="选择对象" prop="objCode">
-            <treeselect
-              v-model="queryParams.objCode"
-              :options="objOptions"
-              :show-count="true"
-              placeholder="请选择对象"
-              class="fixed-width-treeselect"
-            />
-          </el-form-item>
-
+        <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" label-width="68px">
           <el-form-item label="开始日期" prop="date">
             <el-date-picker clearable
                             v-model="queryParams.startRecTime"
                             type="date"
                             value-format="yyyy-MM-dd"
-                            placeholder="请选择日期">
+                            placeholder="请选择日期"
+            >
             </el-date-picker>
           </el-form-item>
+
+
           <el-form-item>
             <el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
             <el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
           </el-form-item>
-        </el-form>
 
-        <el-table v-loading="loading" :data="forecastConsumeList" @selection-change="handleSelectionChange">
-          <el-table-column label="对象名称" align="center" prop="objName" />
-          <el-table-column label="对象类型" align="center" prop="objType">
-            <template slot-scope="scope">
-              <dict-tag :options="dict.type.obj_type" :value="scope.row.objType" />
-            </template>
-          </el-table-column>
-          <el-table-column label="日期" align="center" prop="date" width="180">
-            <template slot-scope="scope">
-              <span>{{ parseTime(scope.row.date, '{y}-{m}-{d}') }}</span>
-            </template>
-          </el-table-column>
-          <el-table-column label="用电量(kW·h)" align="center" prop="elecUseQuantity" />
-        </el-table>
+          <el-table v-loading="loading" :data="forecastConsumeList" >
+            <el-table-column label="对象名称" align="center" prop="objName"/>
+
+            <el-table-column label="日期" align="center" prop="date" width="180">
+              <template slot-scope="scope">
+                <span>{{ parseTime(scope.row.date, '{y}-{m}-{d}') }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column label="用电量(kW·h)" align="center" prop="elecUseQuantity"/>
+          </el-table>
+
+          <pagination
+            v-show="total>0"
+            :total="total"
+            :page.sync="queryParams.pageNum"
+            :limit.sync="queryParams.pageSize"
+            @pagination="getConsumeList"
+          />
+
+        </el-form>
 
-        <pagination
-          v-show="total>0"
-          :total="total"
-          :page.sync="queryParams.pageNum"
-          :limit.sync="queryParams.pageSize"
-          @pagination="getList"
-        />
       </el-col>
     </el-row>
   </div>
@@ -77,310 +66,236 @@
 
 <script>
 
-import { listForecastConsume } from '@/api/prediction/forecastConsume';
-import { areaTreeByFacsCategory, areaTreeSelect } from '@/api/basecfg/area'
 import { getFacsCategoryTree } from '@/api/basecfg/emsfacs'
+import { areaTreeSelect } from '@/api/basecfg/area'
+import { listForecastConsume } from '@/api/prediction/forecastConsume'
 import Treeselect from '@riophae/vue-treeselect'
 import '@riophae/vue-treeselect/dist/vue-treeselect.css'
 
 export default {
-  name: 'ForecastConsume',
+  name: 'consume',
   components: { Treeselect },
-  dicts: ['obj_type'],
   data() {
     return {
       // 遮罩层
       loading: true,
-      // 选中数组
-      ids: [],
-      // 非单个禁用
-      single: true,
-      // 非多个禁用
-      multiple: true,
-      // 显示搜索条件
-      showSearch: true,
-      // 总条数
-      total: 0,
-      // 电力消耗预测表格数据
-      forecastConsumeList: [],
-      // 弹出层标题
-      title: '',
-      // 是否显示弹出层
-      open: false,
+      activeName: 'areaConsume',
       // 表单参数
       areaOptions: [],
+      objOptions: [],
       areaName: undefined,
-      facsCategory: 'Z',
-      facsSubCategory: '',
-      // 设施选项
-      facsOptions: undefined,
       defaultProps: {
-        children: "children",
-        label: "label"
-      },
-      objOptions: [],
-      objTypeOptions: [
-        { value: '1', label: '区域' },
-        { value: '2', label: '设施' }
-      ],
-      treeProps: {
-        label: 'name',
-        value: 'code',
-        children: 'children'
+        children: 'children',
+        label: 'label'
       },
+      // 总条数
+      total: 0,
+      forecastConsumeList: [],
       // 查询参数
       queryParams: {
         pageNum: 1,
         pageSize: 10,
-        areaCode: null,
+        areaCode: -1,
+        objType: '1',
         objCode: null,
-        objType: null,
-        startRecTime: null,
-        elecUseQuantity: null,
+        facsCategory: 'Z',
+        startRecTime: this.getFirstDayOfMonth(), // 本月1号
+        endRecTime: null
       },
-      // 表单参数
-      form: {},
-      // 表单校验
-      rules: {
-        areaCode: [
-          {
-            required: true,
-            message: '园区代码不能为空',
-            trigger: 'blur',
-          },
-        ],
-        objCode: [
-          {
-            required: true,
-            message: '对象代码不能为空',
-            trigger: 'blur',
-          },
-        ],
-        objType: [
-          {
-            required: true,
-            message: '对象类型不能为空',
-            trigger: 'change',
-          },
-        ],
-        date: [
-          {
-            required: true,
-            message: '日期不能为空',
-            trigger: 'blur',
-          },
-        ],
+      // 时间选择器配置
+      startPickerOptions: {
+        disabledDate: (time) => {
+          // 禁用未来时间和超过90天前的时间
+          const ninetyDaysAgo = new Date()
+          ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90)
+          return time.getTime() > Date.now() - 8.64e7 || time.getTime() < ninetyDaysAgo.getTime()
+        },
+        selectableRange: this.generateHourRanges()
       },
-    };
-  },
-  created() {
-    this.getAreaList();
-    this.setTodayDate();
-    this.getList();
-  },
-  watch: {
-// 监听 objType 的变化
-    'queryParams.objType': {
-      handler(newVal, oldVal) {
-        // 清空之前的选择
-        this.queryParams.objCode = null;
-        this.objOptions = [];
+      endPickerOptions: {
+        disabledDate: (time) => {
+          if (this.queryParams.startRecTime) {
+            // 结束时间不能早于开始时间,不能晚于今天,且不能超过开始时间90天后
+            const startDate = new Date(this.queryParams.startRecTime)
+            const endDateLimit = new Date(startDate)
+            endDateLimit.setDate(endDateLimit.getDate() + 90)
+            endDateLimit.setHours(23, 59, 59)
 
-        if (!newVal) return;
-
-        // 根据不同的对象类型调用不同的API
-        switch (newVal) {
-          case '1': // 区域
-            this.loadAreaData();
-            break;
-          case '2': // 设施
-            this.loadFacilityData();
-            break;
-        }
+            return time.getTime() < startDate.getTime() ||
+              time.getTime() > endDateLimit.getTime() ||
+              time.getTime() > Date.now() - 8.64e7
+          }
+          // 禁用未来时间和超过90天前的时间
+          const ninetyDaysAgo = new Date()
+          ninetyDaysAgo.setDate(ninetyDaysAgo.getDate() - 90)
+          return time.getTime() > Date.now() - 8.64e7 || time.getTime() < ninetyDaysAgo.getTime()
+        },
+        selectableRange: this.generateHourRanges()
       }
     }
   },
+  created() {
+    // 初始化时间格式
+    if (this.queryParams.startRecTime) {
+      this.queryParams.startRecTime = this.formatDateTime(this.queryParams.startRecTime)
+    }
+
+    this.getAreaList()
+    this.getConsumeList()
+  },
+  watch: {},
   methods: {
-    /** 查询电力消耗预测列表 */
-    getList() {
-      this.loading = true;
-      listForecastConsume(this.queryParams).then(response => {
-        this.forecastConsumeList = response.rows;
-        this.total = response.total;
-        this.loading = false;
-      });
+    tabClick() {
+      this.clear()
+      if (this.activeName === 'areaConsume') {
+        this.queryParams.objType = 1
+        this.getAreaList()
+        this.getConsumeList() // 初始化区域用能数据
+      } else if (this.activeName === 'facsConsume') {
+        this.queryParams.objType = 2
+        this.getFacsList()
+        this.getConsumeList() // 初始化设施用能数据
+      }
     },
-    // 查询区域列表
-    async getAreaList () {
-      await areaTreeByFacsCategory(this.facsCategory, this.facsSubCategory, false).then(response => {
-        this.areaOptions = [{
-          id: '-1',
-          label: '全部',
-          children: []
-        }].concat(response.data)
-        this.selectedLabel = '全部'
-        this.queryParams.areaCode = '-1'
+
+    // 表单重置
+    clear() {
+      this.queryParams = {
+        pageNum: 1,
+        pageSize: 10,
+        areaCode: -1,
+        objCode: null,
+        facsCategory: 'Z',
+        startRecTime: this.getFirstDayOfMonth(), // 本月1号
+        endRecTime: null
+      }
+      this.total = 0
+      this.consumeList = []
+    },
+    /** 查询能源区域树列表 */
+    getAreaList() {
+      areaTreeSelect(0, 3).then(response => {
+        this.objOptions = response.data
       })
     },
-    setTodayDate() {
-      // 获取当前日期
-      const today = new Date();
-      // 格式化日期为 yyyy-MM-dd 格式
-      const year = today.getFullYear();
-      const month = String(today.getMonth() + 1).padStart(2, '0');
-      const day = String(today.getDate()).padStart(2, '0');
-      // 设置日期选择器的值
-      this.queryParams.startRecTime = `${year}-${month}-${day}`;
+    /** 查询能源设施树列表 */
+    getFacsList() {
+      getFacsCategoryTree().then(response => {
+        this.objOptions = this.flattenTreeData(response.data)
+      })
     },
-    // 加载区域数据
-    loadAreaData() {
-      areaTreeSelect(0, 2).then(response => {
-        // 获取原始数据
-        const originalData = response.data;
+    // 核心处理函数:压缩层级并扁平化树形结构
+    flattenTreeData(regions) {
+      if (!Array.isArray(regions)) return []
 
-        // 根据 areaCode 进行过滤
-        if (this.queryParams.areaCode === '-1') {
-          // areaCode=-1 时,展示全部数据
-          this.objOptions = originalData;
-        } else {
-          // 否则,只展示匹配 areaCode 的数据
-          const filteredData = originalData.filter(item =>
-            item.id === this.queryParams.areaCode
-          );
-          // 如果找到匹配项,则使用过滤后的数据;否则使用空数组
-          this.objOptions = filteredData.length > 0 ? filteredData : [];
-        }
-      }).catch(error => {
-        console.error('加载设施数据失败:', error);
-        this.$message.error('加载设施数据失败');
-      });
-    },
-    // 加载设施数据
-    loadFacilityData() {
-      getFacsCategoryTree(0, "Z").then(response => {
-        // 获取原始数据
-        const originalData = response.data;
+      return regions.map(region => {
+        // 防御性处理children
+        const children = region.children || []
+        if (!Array.isArray(children)) return null
 
-        // 根据 areaCode 进行过滤
-        if (this.queryParams.areaCode === '-1') {
-          // areaCode=-1 时,展示全部数据
-          this.objOptions = originalData;
-        } else {
-          // 否则,只展示匹配 areaCode 的数据
-          const filteredData = originalData.filter(item =>
-            item.id === this.queryParams.areaCode
-          );
-          // 如果找到匹配项,则使用过滤后的数据;否则使用空数组
-          this.objOptions = filteredData.length > 0 ? filteredData : [];
+        // 查找Z用能设施节点
+        const zFacility = children.find(child => child.id === 'Z')
+
+        // 仅当存在Z节点时处理该区域
+        if (zFacility) {
+          return {
+            ...region,
+            // 直接使用Z节点的子节点作为区域的子节点
+            children: (zFacility.children || []).map(child => ({ ...child }))
+          }
         }
-      }).catch(error => {
-        console.error('加载设施数据失败:', error);
-        this.$message.error('加载设施数据失败');
-      });
-    },
-    // 取消按钮
-    cancel() {
-      this.open = false;
-      this.reset();
+        return null
+      }).filter(Boolean)
     },
-    // 表单重置
-    reset() {
-      this.form = {
-        id: null,
-        areaCode: null,
-        objCode: null,
-        objType: null,
-        date: null,
-        elecUseQuantity: null,
-      };
-      this.resetForm('form');
+    getConsumeList() {
+      this.loading = true
+      listForecastConsume(this.queryParams).then(response => {
+        this.forecastConsumeList = response.rows
+        this.total = response.total
+        this.loading = false
+      })
     },
+
     /** 搜索按钮操作 */
     handleQuery() {
-      this.queryParams.pageNum = 1;
-      this.getList();
+      this.queryParams.pageNum = 1
+      this.getConsumeList()
     },
     /** 重置按钮操作 */
     resetQuery() {
-      this.resetForm('queryForm');
-      this.handleQuery();
-    },
-    // 多选框选中数据
-    handleSelectionChange(selection) {
-      this.ids = selection.map(item => item.id);
-      this.single = selection.length !== 1;
-      this.multiple = !selection.length;
+      this.resetForm('queryForm')
+      this.handleQuery()
     },
+
     // 筛选节点
-    filterNode (value, data) {
+    filterNode(value, data) {
       if (!value) return true
       return data.label.indexOf(value) !== -1
     },
-    // 节点单击事件
-    handleNodeClick (data) {
-      this.queryParams.areaCode = data.id
-      this.queryParams.objType = null
-      this.queryParams.objCode = null
-      this.getList()
+    // 节点点击事件处理
+    handleNodeClick(data, node) {
+      this.queryParams.areaCode = this.getTopLevelId(node)
+      this.queryParams.objCode = data.id
+      this.queryParams.pageNum = 1
+      console.log(this.queryParams.areaCode)
+      this.getConsumeList()
     },
-  },
-};
-</script>
-<style scoped>
-.flex-form-container {
-  display: flex;
-  flex-wrap: wrap;
-  align-items: center;
-}
 
-.flex-form-item {
-  margin-right: 10px;
-  margin-bottom: 0;
-  flex: 1;
-  min-width: 180px;
-}
+    // 追溯顶级节点ID的辅助函数
+    getTopLevelId(node) {
+      let currentNode = node;
 
-.flex-form-item-buttons {
-  flex: 0 0 auto;
-  display: flex;
-  align-items: center;
-  margin-right: 0;
-}
+      while (currentNode.parent && currentNode.parent.id !== 0) {
+        currentNode = currentNode.parent;
+      }
+      return currentNode.data.id;
+    },
 
-.flex-form-item-buttons .el-button {
-  margin-right: 5px;
-}
+    // 获取本月1号 00:00:00
+    getFirstDayOfMonth() {
+      const date = new Date()
+      date.setDate(1)
+      return this.formatDateTime(date)
+    },
 
-.flex-form-item-buttons .el-button:last-child {
-  margin-right: 0;
-}
+    // 格式化日期时间为 yyyy-MM-dd HH:mm:ss
+    formatDateTime(date) {
+      if (!date) return ''
+      if (typeof date === 'string') {
+        date = new Date(date)
+      }
 
-/* 适配小屏幕 */
-@media (max-width: 768px) {
-  .flex-form-container {
-    justify-content: flex-start;
-  }
+      const year = date.getFullYear()
+      const month = String(date.getMonth() + 1).padStart(2, '0')
+      const day = String(date.getDate()).padStart(2, '0')
 
-  .flex-form-item {
-    min-width: 100%;
-    margin-right: 0;
-    margin-bottom: 10px;
-  }
+      return `${year}-${month}-${day}`
+    },
 
-  .flex-form-item-buttons {
-    min-width: 100%;
-    margin-bottom: 0;
+    // 生成整点时间范围(优化版)
+    generateHourRanges() {
+      const ranges = []
+      for (let i = 0; i < 24; i++) {
+        const start = `${String(i).padStart(2, '0')}:00:00`
+        const end = `${String(i).padStart(2, '0')}:59:59`
+        ranges.push(`${start} - ${end}`)
+      }
+      return ranges
+    }
   }
 }
+</script>
+<style scoped>
 
-/* 固定宽度的TreeSelect */
-.fixed-width-treeselect {
-  width: 200px; /* 设置固定宽度 */
+
+/* 自定义样式:父节点文字变灰,提示不可点击 */
+.el-tree .el-tree-node.is-leaf > .el-tree-node__content {
+  color: #333;
+  cursor: pointer;
 }
 
-/* 如果TreeSelect组件有特殊的样式需求,可能需要使用/deep/或::v-deep穿透选择器 */
-::v-deep .fixed-width-treeselect .el-input {
-  width: 100%;
+.el-tree .el-tree-node:not(.is-leaf) > .el-tree-node__content {
+  color: #909399;
+  cursor: not-allowed;
 }
 </style>
-