2 Commits 30b30ebafd ... 794f8a8015

Tác giả SHA1 Thông báo Ngày
  learshaw 794f8a8015 update 2 tuần trước cách đây
  learshaw 4972a6229d 商户计费调整 2 tuần trước cách đây

+ 5 - 2
ems-ui-cloud/src/api/basecfg/elecAttr.js

@@ -12,8 +12,11 @@ export function listAttr(query) {
 // 查询服务区用电属性详细
 export function getEffectiveListByArea(areaCode) {
   return request({
-    url: '/ems/basecfg/elec/attr/getEffectiveListByArea?areaCode=' + areaCode,
-    method: 'get'
+    url: '/ems/basecfg/elec/attr/getEffectiveListByArea',
+    method: 'get',
+    params: {
+      areaCode: areaCode
+    }
   })
 }
 export function getEffectiveByArea(areaCode) {

+ 54 - 0
ems-ui-cloud/src/api/basecfg/objTag.js

@@ -0,0 +1,54 @@
+import request from '@/utils/request'
+
+// 保存对象-标签关系
+export function saveObjTagRel(tagCode, objType, objCode) {
+  return request({
+    url: '/ems/tag/rel',
+    method: 'post',
+    headers: {
+      repeatSubmit: false
+    },
+    params: {
+      tagCode: tagCode,
+      objType: objType,
+      objCode: objCode
+    }
+  })
+}
+
+// 删除对象-标签关系
+export function deleteObjTagRel(tagCode, objType, objCode) {
+  return request({
+    url: '/ems/tag/rel?tagCode=' + tagCode + '&objType=' + objType + '&objCode=' + objCode,
+    method: 'delete',
+    headers: {
+      repeatSubmit: false
+    }
+  })
+}
+
+// 查询对象&标签关系
+export function getRelByObjType(objType) {
+  return request({
+    url: '/ems/tag/rel/getByObjType?objType=' + objType,
+    method: 'get'
+  })
+}
+
+// 查询对象&标签关系
+export function selectRelByTagCode(objType, tagCode) {
+  return request({
+    url: '/ems/tag/rel/selectByTagCode?objType=' + objType + '&tagCode=' + tagCode,
+    method: 'get'
+  })
+}
+
+// 查询对象&标签关系
+export function selectRelByObjCode(objType, objCode) {
+  return request({
+    url: '/ems/tag/rel/selectByObjCode?objType=' + objType + '&objCode=' + objCode,
+    method: 'get'
+  })
+}
+
+

+ 0 - 44
ems-ui-cloud/src/api/basecfg/tag.js

@@ -1,44 +0,0 @@
-import request from '@/utils/request'
-
-// 查询标签分类列表
-export function listTag(query) {
-  return request({
-    url: '/ems/tag/list',
-    method: 'get',
-    params: query
-  })
-}
-
-// 查询标签分类详细
-export function getTag(id) {
-  return request({
-    url: '/ems/tag/' + id,
-    method: 'get'
-  })
-}
-
-// 新增标签分类
-export function addTag(data) {
-  return request({
-    url: '/ems/tag',
-    method: 'post',
-    data: data
-  })
-}
-
-// 修改标签分类
-export function updateTag(data) {
-  return request({
-    url: '/ems/tag',
-    method: 'put',
-    data: data
-  })
-}
-
-// 删除标签分类
-export function delTag(id) {
-  return request({
-    url: '/ems/tag/' + id,
-    method: 'delete'
-  })
-}

+ 44 - 6
ems-ui-cloud/src/api/mgr/charging.js

@@ -1,18 +1,56 @@
+// src/api/mgr/charging.js
 import request from '@/utils/request'
 
-// 查询商户计费数据
-export function chargingList(query) {
+// 查询总览数据
+export function getOverview(query) {
   return request({
-    url: '/ems/charging/bill/co',
+    url: '/ems/charging/bill/overview',
     method: 'get',
     params: query
   })
 }
-// 查询计费总览数据
-export function overviewInfo(query) {
+
+export function getOverviewHis(query) {
+  return request({
+    url: '/ems/charging/bill/overview/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询个户综合账单
+export function getSellerBill(query) {
+  return request({
+    url: '/ems/charging/bill/seller',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询历史账单列表
+export function getHistoryBills(query) {
   return request({
-    url: '/ems/charging/bill/getByTag',
+    url: '/ems/charging/bill/seller/history',
     method: 'get',
     params: query
   })
 }
+
+// 导出账单
+export function exportBill(query) {
+  return request({
+    url: '/ems/charging/bill/seller/export',
+    method: 'post',
+    params: query
+  })
+}
+
+// 查询区域计费策略
+export function getChargingStrategyByArea(areaCode) {
+  return request({
+    url: '/ems/basecfg/elecPrice/coCharging/getByArea',
+    method: 'get',
+    params: { areaCode }
+  })
+}
+

+ 0 - 1
ems-ui-cloud/src/views/analysis/device/warn.vue

@@ -93,7 +93,6 @@ import PieChartBlock from '@/components/Block/charts/PieChartBlock.vue';
 import {ALARM_STATE} from '@/enums/alarm';
 import {DateTool} from '@/utils/DateTool';
 import DeviceWaring from '@/views/analysis/device/DevcWarning/index.vue';
-import Tag from '@/views/basecfg/tag/index.vue';
 import dayjs from 'dayjs';
 import {areaTreeSelect} from '@/api/basecfg/area'
 import LineChartBlock from '../../../components/Block/charts/LineChartBlock.vue';

+ 410 - 0
ems-ui-cloud/src/views/basecfg/price/components/ElecClassify.vue

@@ -0,0 +1,410 @@
+<template>
+  <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="treeAreaOptions"
+          :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-col :span="20" :xs="24">
+      <el-row :gutter="10" class="mb8">
+        <el-col :span="1.5">
+          <el-button
+            type="primary"
+            plain
+            icon="el-icon-plus"
+            size="mini"
+            @click="handleAttrAdd"
+            v-hasPermi="['basecfg:price:add']"
+          >
+            新增
+          </el-button>
+        </el-col>
+      </el-row>
+
+      <!-- 属性列表表格 -->
+      <el-table v-loading="loading" :data="attrList">
+        <el-table-column label="区域" align="center" prop="areaName"/>
+        <el-table-column label="状态" align="center" width="100">
+          <template slot-scope="scope">
+            <el-tag :type="scope.$index === attrList.length - 1 ? 'success' : 'info'" disable-transitions>
+              {{ scope.$index === attrList.length - 1 ? '生效' : '继承' }}
+            </el-tag>
+          </template>
+        </el-table-column>
+        <el-table-column label="用电分类" align="center" prop="elecTypeName" />
+        <el-table-column label="电压等级" align="center" prop="voltageLevel" />
+        <el-table-column label="容(需)量策略" align="center">
+          <el-table-column label="容(需)量类型" align="center" prop="reqCapacityFlag" :formatter="matchReqCapacityFlag" />
+          <el-table-column label="变压器容量(千伏·安)" align="center" prop="transCapacity" />
+          <el-table-column label="最大需量(千瓦)" align="center" prop="reqQuantity" />
+        </el-table-column>
+        <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+          <template slot-scope="scope">
+            <el-tooltip
+              effect="dark"
+              :content="scope.row.areaCode !== currentAreaCode ? '请到上游节点编辑' : '修改'"
+              placement="top"
+              :open-delay="0"
+              :disabled="scope.row.areaCode === currentAreaCode">
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-edit"
+                :disabled="scope.row.areaCode !== currentAreaCode"
+                @click="handleAttrUpdate(scope.row)"
+                v-hasPermi="['basecfg:price:edit']">
+                修改
+              </el-button>
+            </el-tooltip>
+            <el-tooltip
+              effect="dark"
+              :content="scope.row.areaCode !== currentAreaCode ? '请到上游节点编辑' : '删除'"
+              placement="top"
+              :open-delay="0"
+              :disabled="scope.row.areaCode === currentAreaCode">
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-delete"
+                class="deleteBtn"
+                :disabled="scope.row.areaCode !== currentAreaCode"
+                @click="handleAttrDelete(scope.row)"
+                v-hasPermi="['basecfg:price:remove']">
+                删除
+              </el-button>
+            </el-tooltip>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <pagination
+        v-show="total > 0 || total === 0"
+        :total="total || 0"
+        :page.sync="queryAttrParams.pageNum"
+        :limit.sync="queryAttrParams.pageSize"
+        @pagination="refreshCurrentAreaData"
+      />
+
+      <!-- 添加或修改区域用电属性对话框 -->
+      <el-dialog :title="title" :visible.sync="attrOpen" width="500px" append-to-body>
+        <el-form ref="attrForm" :model="attrForm" :rules="attrRules" label-width="150px">
+          <el-form-item label="园区" prop="areaCode">
+            <el-input v-model="currentAreaName" disabled placeholder="园区名称" />
+            <input type="hidden" v-model="attrForm.areaCode" />
+          </el-form-item>
+          <el-form-item label="价格策略" prop="priceCode">
+            <el-select v-model="attrForm.priceCode">
+              <el-option
+                v-for="item in gwPriceOptions"
+                :label="`${item.elecTypeName}<${item.voltageLevel}>`"
+                :value="item.cfgCode"
+                :key="item.cfgCode"
+              />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="容(需)量类型" prop="reqCapacityFlag">
+            <el-select v-model="attrForm.reqCapacityFlag">
+              <el-option v-for="item in reqCapacityOptions" :label="item.name" :value="item.code" :key="item.code" />
+            </el-select>
+          </el-form-item>
+          <el-form-item label="变压器容量(kVA/月)" prop="transCapacity" v-if="attrForm.reqCapacityFlag === 1">
+            <el-input v-model="attrForm.transCapacity" placeholder="请输入变压器容量" />
+          </el-form-item>
+          <el-form-item label="最大需量(kW·h/月)" prop="reqQuantity" v-if="attrForm.reqCapacityFlag === 2">
+            <el-input v-model="attrForm.reqQuantity" placeholder="请输入最大需量" />
+          </el-form-item>
+        </el-form>
+        <div slot="footer" class="dialog-footer">
+          <el-button type="primary" @click="submitAttrForm">确 定</el-button>
+          <el-button @click="attrCancel">取 消</el-button>
+        </div>
+      </el-dialog>
+    </el-col>
+  </el-row>
+</template>
+
+<script>
+import {
+  listAttr,
+  getAttr,
+  delAttr,
+  addAttr,
+  updateAttr,
+  getEffectiveListByArea
+} from '@/api/basecfg/elecAttr'
+import { listGwPriceConfigAll } from '@/api/basecfg/elecGwPrice'
+import { areaTreeSelect } from '@/api/basecfg/area'
+
+export default {
+  name: 'ElecClassify',
+  props: {
+    currentAreaCode: {
+      type: String,
+      default: null
+    },
+    currentAreaName: {
+      type: String,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 总条数
+      total: 0,
+      // 区域用电属性表格数据
+      attrList: [],
+      // 弹出层标题
+      title: '',
+      // 区域名称
+      areaName: undefined,
+      // 区域树选项
+      treeAreaOptions: undefined,
+      defaultProps: {
+        children: 'children',
+        label: 'label'
+      },
+      // 内部维护的当前区域
+      internalAreaCode: null,
+      internalAreaName: '',
+      // 是否显示弹出层
+      attrOpen: false,
+      // 选项定义
+      gwPriceOptions: undefined,
+      reqCapacityOptions: [
+        { code: 0, name: '不涉及' },
+        { code: 1, name: '容量电价' },
+        { code: 2, name: '需量电价' }
+      ],
+      // 查询参数
+      queryAttrParams: {
+        pageNum: 1,
+        pageSize: 10
+      },
+      // 表单参数
+      attrForm: {},
+      // 表单校验
+      attrRules: {
+        areaCode: [{ required: true, message: '园区代码不能为空', trigger: 'blur' }],
+        elecType: [{ required: true, message: '请选择用电分类', trigger: 'change' }],
+        priceCode: [{ required: true, message: '价格编码不能为空', trigger: 'blur' }]
+      }
+    }
+  },
+  computed: {
+    // 使用props或内部值
+    currentAreaCodeComputed() {
+      return this.currentAreaCode || this.internalAreaCode
+    },
+    currentAreaNameComputed() {
+      return this.currentAreaName || this.internalAreaName
+    }
+  },
+  created() {
+    this.getAreaTree('0', 2)
+    this.getAttrList()
+  },
+  methods: {
+    /** 查询区域树结构 */
+    getAreaTree(areaCode, layer) {
+      areaTreeSelect(areaCode, layer).then(response => {
+        this.treeAreaOptions = response.data
+        // 自动选中并触发点击第一个节点
+        if (response.data && response.data.length > 0) {
+          const firstNode = response.data[0]
+          this.$refs.tree.setCurrentKey(firstNode.id)
+          this.handleNodeClick(firstNode)
+        }
+      })
+    },
+
+    // 筛选节点
+    filterNode(value, data) {
+      if (!value) return true
+      return data.label.indexOf(value) !== -1
+    },
+
+    // 节点单击事件
+    handleNodeClick(data) {
+      this.internalAreaCode = data.id
+      this.internalAreaName = data.label
+      this.attrForm.areaCode = data.id
+
+      // 触发事件通知父组件
+      this.$emit('area-selected', {
+        areaCode: data.id,
+        areaName: data.label
+      })
+
+      this.refreshCurrentAreaData()
+    },
+
+    refreshCurrentAreaData() {
+      const areaCode = this.currentAreaCodeComputed
+      if (areaCode) {
+        this.loading = true
+        getEffectiveListByArea(areaCode).then(res => {
+          this.attrList = res.data
+          this.loading = false
+        }).catch(error => {
+          console.error("Error fetching data:", error)
+          this.loading = false
+        })
+      }
+    },
+
+    matchReqCapacityFlag(row, column, cellValue, index) {
+      const reqCapacityFlagMap = {
+        0: '不涉及',
+        1: '容量电价',
+        2: '需量电价'
+      }
+      return reqCapacityFlagMap[cellValue] || '未知'
+    },
+
+    /** 查询区域用电属性列表 */
+    getAttrList() {
+      this.loading = true
+      listAttr(this.queryAttrParams).then(response => {
+        this.attrList = response.rows
+        this.total = response.total !== undefined ? response.total : 0
+        this.loading = false
+      })
+    },
+
+    // 取消按钮
+    attrCancel() {
+      this.attrOpen = false
+      this.attrReset()
+    },
+
+    // 表单重置
+    attrReset() {
+      this.attrForm = {
+        id: null,
+        areaCode: null,
+        elecType: null,
+        priceCode: null,
+        reqCapacityFlag: 0,
+        transCapacity: null,
+        reqQuantity: null,
+        createTime: null,
+        updateTime: null
+      }
+      this.resetForm('attrForm')
+    },
+
+    /** 新增按钮操作 */
+    handleAttrAdd() {
+      this.attrReset()
+      this.getGwPriceAll()
+      this.attrForm.areaCode = this.currentAreaCodeComputed
+      this.attrOpen = true
+      this.title = '添加区域用电属性'
+    },
+
+    /** 修改按钮操作 */
+    handleAttrUpdate(row) {
+      this.attrReset()
+      this.getGwPriceAll()
+      const id = row.id
+      getAttr(id).then(response => {
+        this.attrForm = response.data
+        this.attrOpen = true
+        this.title = '修改区域用电属性'
+      })
+    },
+
+    /** 提交按钮 */
+    submitAttrForm() {
+      this.$refs['attrForm'].validate(valid => {
+        if (valid) {
+          // 排除当前正在修改的园区
+          if (this.attrList.some(item => item.areaCode === this.attrForm.areaCode && item.id !== this.attrForm.id)) {
+            return this.$modal.msgError('当前园区已存在')
+          }
+          if (this.attrForm.id != null) {
+            updateAttr(this.attrForm).then(response => {
+              this.$modal.msgSuccess('修改成功')
+              this.attrOpen = false
+              this.refreshCurrentAreaData()
+            })
+          } else {
+            addAttr(this.attrForm).then(response => {
+              this.$modal.msgSuccess('新增成功')
+              this.attrOpen = false
+              this.refreshCurrentAreaData()
+            })
+          }
+        }
+      })
+    },
+
+    /** 删除按钮操作 */
+    handleAttrDelete(row) {
+      const ids = row.id
+      this.$modal
+        .confirm('是否确认删除区域用电属性编号为"' + ids + '"的数据项?')
+        .then(function() {
+          return delAttr(ids)
+        })
+        .then(() => {
+          this.refreshCurrentAreaData()
+          this.$modal.msgSuccess('删除成功')
+        })
+        .catch(() => {})
+    },
+
+    getGwPriceAll() {
+      listGwPriceConfigAll().then(response => {
+        this.gwPriceOptions = response.data
+      })
+    },
+
+    // 工具方法
+    resetForm(formName) {
+      if (this.$refs[formName]) {
+        this.$refs[formName].resetFields()
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../styles/dialog.scss';
+
+.app-container {
+  .el-button.deleteBtn:disabled {
+    color: #c0c4cc !important;
+  }
+  .el-button.deleteBtn:disabled:hover {
+    color: #c0c4cc !important;
+  }
+}
+</style>

+ 266 - 0
ems-ui-cloud/src/views/basecfg/price/components/GridPrice.vue

@@ -0,0 +1,266 @@
+<template>
+  <div>
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleGwPriceAdd"
+          v-hasPermi="['basecfg:price:add']"
+        >
+          新增
+        </el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" :search="false" @queryTable="getGwPriceList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="gwPriceList">
+      <el-table-column label="用电分类" align="center" prop="elecTypeName" />
+      <el-table-column label="电压等级" align="center" prop="voltageLevel" />
+      <el-table-column label="峰谷电价" align="center">
+        <el-table-column label="深谷" align="center" prop="dvPrice" />
+        <el-table-column label="谷段" align="center" prop="vPrice" />
+        <el-table-column label="平段" align="center" prop="opPrice" />
+        <el-table-column label="峰段" align="center" prop="pPrice" />
+        <el-table-column label="尖峰" align="center" prop="cpPrice" />
+      </el-table-column>
+      <el-table-column label="容(需)量用电价格" align="center">
+        <el-table-column label="最大需量 (元/千瓦时·月)" align="center" prop="maxReqPrice" width="80px" />
+        <el-table-column label="变压器容量(元/千伏安·月)" align="center" prop="transCapacityPrice" width="80px" />
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleGwPriceUpdate(scope.row)"
+            v-hasPermi="['basecfg:price:edit']"
+          >
+            修改
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            class="deleteBtn"
+            @click="handleGwPriceDelete(scope.row)"
+            v-hasPermi="['basecfg:price:remove']"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryGwPriceParams.pageNum"
+      :limit.sync="queryGwPriceParams.pageSize"
+      @pagination="getGwPriceList"
+    />
+
+    <!-- 添加或修改电价配置对话框 -->
+    <el-dialog :title="title" :visible.sync="gwPriceCfgOpen" width="500px" append-to-body>
+      <el-form ref="gwPriceForm" :model="gwPriceForm" :rules="gwPriceRules" label-width="150px">
+        <el-form-item label="配置代码" prop="cfgCode">
+          <el-input v-model="gwPriceForm.cfgCode" maxlength="4" placeholder="请输入配置代码" />
+        </el-form-item>
+        <el-form-item label="用电分类" prop="elecType">
+          <el-select v-model="gwPriceForm.elecType">
+            <el-option v-for="item in priceTypeOptions" :label="item.name" :value="item.code" :key="item.code" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="电压等级" prop="voltageLevel">
+          <el-input v-model="gwPriceForm.voltageLevel" placeholder="请输入电压等级" />
+        </el-form-item>
+        <el-form-item label="深谷价格" prop="dvPrice">
+          <el-input v-model="gwPriceForm.dvPrice" placeholder="请输入深谷价格" />
+        </el-form-item>
+        <el-form-item label="谷段价格" prop="vPrice">
+          <el-input v-model="gwPriceForm.vPrice" placeholder="请输入谷段价格" />
+        </el-form-item>
+        <el-form-item label="平段价格" prop="opPrice">
+          <el-input v-model="gwPriceForm.opPrice" placeholder="请输入平段价格" />
+        </el-form-item>
+        <el-form-item label="峰段价格" prop="pPrice">
+          <el-input v-model="gwPriceForm.pPrice" placeholder="请输入分时峰段价格" />
+        </el-form-item>
+        <el-form-item label="尖峰价格" prop="cpPrice">
+          <el-input v-model="gwPriceForm.cpPrice" placeholder="请输入分时尖时段价格" />
+        </el-form-item>
+
+        <el-form-item label="最大需量用电价格" prop="maxReqPrice" v-if="gwPriceForm.elecType === 4">
+          <el-input v-model="gwPriceForm.maxReqPrice" placeholder="请输入最大需量用电价格" />
+        </el-form-item>
+        <el-form-item label="变压器容量用电价格" prop="transCapacityPrice" v-if="gwPriceForm.elecType === 4">
+          <el-input v-model="gwPriceForm.transCapacityPrice" placeholder="请输入变压器容量用电价格" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitGwPriceForm">确 定</el-button>
+        <el-button @click="gwPriceCancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  listGwPriceConfig,
+  getGwPriceConfig,
+  addGwPriceConfig,
+  delGwPriceConfig,
+  updateGwPriceConfig
+} from '@/api/basecfg/elecGwPrice'
+import { getElecPriceType } from '@/api/commonApi'
+import commonMethods from '../mixins/commonMethods'
+
+export default {
+  name: 'GridPrice',
+  mixins: [commonMethods],
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 电价表格
+      gwPriceList: [],
+      // 弹出层标题
+      title: '',
+      gwPriceCfgOpen: false,
+      // 选项定义
+      priceTypeOptions: undefined,
+      // 查询参数
+      queryGwPriceParams: {
+        pageNum: 1,
+        pageSize: 10
+      },
+      // 表单参数
+      gwPriceForm: {},
+      // 表单校验
+      gwPriceRules: {
+        cfgCode: [{ required: true, message: '配置代码不为空', trigger: 'blur' }],
+        elecType: [{ required: true, message: '请选择用电分类', trigger: 'change' }],
+        voltageLevel: [{ required: true, message: '电压等级不为空', trigger: 'blur' }],
+        degreePrice: [{ required: true, message: '单位电价不为控', trigger: 'blur' }]
+      }
+    }
+  },
+  created() {
+    this.getGwPriceList()
+    this.getPriceType()
+  },
+  methods: {
+    getGwPriceList() {
+      this.loading = true
+      listGwPriceConfig(this.queryGwPriceParams).then(response => {
+        this.gwPriceList = response.rows
+        this.total = response.total
+        this.loading = false
+      })
+    },
+
+    // 取消按钮
+    gwPriceCancel() {
+      this.gwPriceCfgOpen = false
+      this.gwPriceReset()
+    },
+
+    // 表单重置
+    gwPriceReset() {
+      this.gwPriceForm = {
+        id: null,
+        cfgCode: null,
+        elecType: null,
+        voltageLevel: null,
+        dvPrice: null,
+        vPrice: null,
+        opPrice: null,
+        pPrice: null,
+        cpPrice: null,
+        maxReqPrice: null,
+        transCapacityPrice: null
+      }
+      this.resetForm('gwPriceForm')
+    },
+
+    /** 新增按钮操作 */
+    handleGwPriceAdd() {
+      this.gwPriceReset()
+      this.gwPriceCfgOpen = true
+      this.title = '添加电价配置'
+    },
+
+    /** 修改按钮操作 */
+    handleGwPriceUpdate(row) {
+      this.gwPriceReset()
+      const id = row.id
+      getGwPriceConfig(id).then(response => {
+        this.gwPriceForm = response.data
+        this.gwPriceCfgOpen = true
+        this.title = '修改电价配置'
+      })
+    },
+
+    /** 提交按钮 */
+    submitGwPriceForm() {
+      this.$refs['gwPriceForm'].validate(valid => {
+        if (valid) {
+          if (this.gwPriceForm.id != null) {
+            updateGwPriceConfig(this.gwPriceForm).then(response => {
+              this.$modal.msgSuccess('修改成功')
+              this.gwPriceCfgOpen = false
+              this.getGwPriceList()
+            })
+          } else {
+            addGwPriceConfig(this.gwPriceForm).then(response => {
+              this.$modal.msgSuccess('新增成功')
+              this.gwPriceCfgOpen = false
+              this.getGwPriceList()
+            })
+          }
+        }
+      })
+    },
+
+    /** 删除按钮操作 */
+    handleGwPriceDelete(row) {
+      const ids = row.id
+      this.$modal
+        .confirm('是否确认删除电价配置编号为"' + ids + '"的数据项?')
+        .then(function() {
+          return delGwPriceConfig(ids)
+        })
+        .then(() => {
+          this.getGwPriceList()
+          this.$modal.msgSuccess('删除成功')
+        })
+        .catch(() => {})
+    },
+
+    getPriceType() {
+      getElecPriceType().then(response => {
+        this.priceTypeOptions = response.data
+      })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../styles/dialog.scss';
+.el-button.deleteBtn:disabled {
+  color: #c0c4cc !important;
+
+  &:hover {
+    color: #c0c4cc !important;
+  }
+}
+</style>

+ 1134 - 0
ems-ui-cloud/src/views/basecfg/price/components/MerchantCharging.vue

@@ -0,0 +1,1134 @@
+<template>
+  <div>
+    <el-row :gutter="10" class="mb8">
+      <right-toolbar :showSearch.sync="showSearch" :search="false" @queryTable="getChargingList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="chargingList">
+      <el-table-column label="区域" align="center" prop="areaName" />
+      <el-table-column label="电计量单价" align="center" prop="elecUnitPrice" />
+      <el-table-column label="电公摊计算类型" align="center">
+        <template slot-scope="scope">
+          {{formatDict(scope.row.elecGtComputeType,'computeTypeOptions')}}
+        </template>
+      </el-table-column>
+      <el-table-column label="电费计算说明" align="center" prop="elecComputeDesc" show-overflow-tooltip />
+      <el-table-column label="水计量单价" align="center" prop="waterUnitPrice" />
+      <el-table-column label="水公摊计量类型" align="center">
+        <template slot-scope="scope">
+          {{formatDict(scope.row.waterGtComputeType,'computeTypeOptions')}}
+        </template>
+      </el-table-column>
+      <el-table-column label="水费说明" align="center" prop="waterComputeDesc" show-overflow-tooltip />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleChargingUpdate(scope.row)"
+            v-hasPermi="['basecfg:price:edit']"
+          >
+            修改
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 添加或修改对话框 -->
+    <el-dialog :title="title" :visible.sync="chargingDialog" custom-class="custom-dialog" width="650px" append-to-body>
+      <el-form ref="chargingForm" :model="chargingForm" label-width="120px">
+        <el-form-item label="区域名称" prop="areaCode" required :rules="[{required:true,message:'请选择区域名称'}]">
+          <el-select v-model="chargingForm.areaCode" disabled style="width:100%">
+            <el-option v-for="item in areaOptions" :label="item.label" :value="item.id" :key="item.id" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="电计量单价" prop="elecUnitPrice" required :rules="[{required:true,message:'电计量单价不能为空'}]">
+          <el-input v-model="chargingForm.elecUnitPrice" oninput="value=value.replace(/[^\d.]/g,'')" placeholder="请输入电计量单价" />
+        </el-form-item>
+        <el-form-item label="电公摊计算类型" prop="elecGtComputeType" required :rules="[{required:true,message:'请选择电公摊计算类型'}]">
+          <el-select v-model="chargingForm.elecGtComputeType" style="width:100%">
+            <el-option v-for="item in computeTypeOptions" :label="item.name" :value="item.value" :key="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="电费计算说明" prop="elecComputeDesc">
+          <el-input v-model="chargingForm.elecComputeDesc" placeholder="请输入电费计算说明" />
+        </el-form-item>
+        <el-form-item label="水计量单价" prop="waterUnitPrice" required :rules="[{required:true,message:'水计量单价不能为空'}]">
+          <el-input v-model="chargingForm.waterUnitPrice" oninput="value=value.replace(/[^\d.]/g,'')" placeholder="请输入水计量单价" />
+        </el-form-item>
+        <el-form-item label="水公摊计算类型" prop="waterGtComputeType" required :rules="[{required:true,message:'请选择水公摊计算类型'}]">
+          <el-select v-model="chargingForm.waterGtComputeType" style="width:100%">
+            <el-option v-for="item in computeTypeOptions" :label="item.name" :value="item.value" :key="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item label="水费说明" prop="waterComputeDesc">
+          <el-input v-model="chargingForm.waterComputeDesc" placeholder="请输入水费说明" />
+        </el-form-item>
+
+        <!-- 公摊表计配置项 -->
+        <el-form-item label="公摊表计" prop="sharedMeters">
+          <el-button type="primary" size="small" icon="el-icon-setting" @click="openMeterConfig">
+            配置表计
+          </el-button>
+          <span style="margin-left: 15px; color: #909399; font-size: 12px;">
+            点击配置该区域的公摊电表和水表
+          </span>
+        </el-form-item>
+
+        <!-- 商户区域配置项 -->
+        <el-form-item label="关联商户" prop="relCoList">
+          <el-button type="primary" size="small" icon="el-icon-office-building" @click="openAreaConfig">
+            配置商户
+          </el-button>
+          <span style="margin-left: 15px; color: #909399; font-size: 12px;">
+            点击选择需要计费的商户区域
+          </span>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitChargingForm">确 定</el-button>
+        <el-button @click="chargingCancel">取 消</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 公摊表计配置对话框 -->
+    <el-dialog
+      title="公摊表计配置"
+      :visible.sync="meterConfigDialog"
+      width="800px"
+      append-to-body
+      :close-on-click-modal="false">
+
+      <!-- 统计信息栏 -->
+      <div class="meter-stats-bar">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="stat-item">
+              <i class="el-icon-s-platform" style="color: #67C23A; font-size: 18px;"></i>
+              <span class="stat-label">已选电表:</span>
+              <span class="stat-value">{{ selectedElecMeters.length }}</span>
+              <span class="stat-unit">个</span>
+              <el-tag v-if="selectedElecMeters.length - originalElecMeters.length > 0"
+                      type="success" size="mini" class="change-tag">
+                +{{ selectedElecMeters.length - originalElecMeters.length }}
+              </el-tag>
+              <el-tag v-if="selectedElecMeters.length - originalElecMeters.length < 0"
+                      type="danger" size="mini" class="change-tag">
+                {{ selectedElecMeters.length - originalElecMeters.length }}
+              </el-tag>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="stat-item">
+              <i class="el-icon-s-platform" style="color: #409EFF; font-size: 18px;"></i>
+              <span class="stat-label">已选水表:</span>
+              <span class="stat-value">{{ selectedWaterMeters.length }}</span>
+              <span class="stat-unit">个</span>
+              <el-tag v-if="selectedWaterMeters.length - originalWaterMeters.length > 0"
+                      type="success" size="mini" class="change-tag">
+                +{{ selectedWaterMeters.length - originalWaterMeters.length }}
+              </el-tag>
+              <el-tag v-if="selectedWaterMeters.length - originalWaterMeters.length < 0"
+                      type="danger" size="mini" class="change-tag">
+                {{ selectedWaterMeters.length - originalWaterMeters.length }}
+              </el-tag>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+
+      <el-tabs v-model="activeMeterTab">
+        <el-tab-pane label="电表配置" name="elec">
+          <div style="height: 400px; overflow-y: auto; border: 1px solid #EBEEF5; padding: 10px;">
+            <el-tree
+              ref="elecTree"
+              :data="elecMeterTree"
+              :props="meterTreeProps"
+              node-key="id"
+              show-checkbox
+              :default-checked-keys="defaultElecCheckedKeys"
+              :check-strictly="false"
+              @check="handleElecTreeCheck">
+              <span class="custom-tree-node" slot-scope="{ node, data }">
+                <span>
+                  <i v-if="data.type === 'device'" class="el-icon-s-platform" style="color: #67C23A; margin-right: 5px;"></i>
+                  <i v-else class="el-icon-folder" style="color: #909399; margin-right: 5px;"></i>
+                  {{ node.label }}
+                </span>
+                <el-tag v-if="data.type === 'device' && isSharedMeter(data.id, 45)" type="success" size="mini" style="margin-left: 10px;">
+                  公摊
+                </el-tag>
+              </span>
+            </el-tree>
+          </div>
+        </el-tab-pane>
+
+        <el-tab-pane label="水表配置" name="water">
+          <div style="height: 400px; overflow-y: auto; border: 1px solid #EBEEF5; padding: 10px;">
+            <el-tree
+              ref="waterTree"
+              :data="waterMeterTree"
+              :props="meterTreeProps"
+              node-key="id"
+              show-checkbox
+              :default-checked-keys="defaultWaterCheckedKeys"
+              :check-strictly="false"
+              @check="handleWaterTreeCheck">
+              <span class="custom-tree-node" slot-scope="{ node, data }">
+                <span>
+                  <i v-if="data.type === 'device'" class="el-icon-s-platform" style="color: #409EFF; margin-right: 5px;"></i>
+                  <i v-else class="el-icon-folder" style="color: #909399; margin-right: 5px;"></i>
+                  {{ node.label }}
+                </span>
+                <el-tag v-if="data.type === 'device' && isSharedMeter(data.id, 70)" type="primary" size="mini" style="margin-left: 10px;">
+                  公摊
+                </el-tag>
+              </span>
+            </el-tree>
+          </div>
+        </el-tab-pane>
+      </el-tabs>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="meterConfigCancel">取 消</el-button>
+        <el-button type="primary" @click="saveMeterConfig" :loading="meterSaving">保 存</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 关联商户配置对话框保持不变 -->
+    <el-dialog
+      title="关联商户配置"
+      :visible.sync="relatedAreaDialog"
+      width="800px"
+      append-to-body
+      :close-on-click-modal="false">
+
+      <!-- 统计和搜索栏 -->
+      <div class="area-header">
+        <el-row :gutter="20" align="middle">
+          <el-col :span="12">
+            <div class="area-stats">
+              <i class="el-icon-office-building" style="color: #409EFF; font-size: 18px;"></i>
+              <span style="margin-left: 8px;">已选商户:</span>
+              <span style="font-weight: bold; color: #409EFF; font-size: 16px;">
+                {{ selectedAreas.length }}
+              </span>
+              <span> 个</span>
+              <el-tag v-if="selectedAreas.length - originalAreas.length !== 0"
+                      :type="selectedAreas.length - originalAreas.length > 0 ? 'success' : 'danger'"
+                      size="mini"
+                      style="margin-left: 10px;">
+                {{ selectedAreas.length - originalAreas.length > 0 ? '+' : '' }}{{ selectedAreas.length - originalAreas.length }}
+              </el-tag>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <el-input
+              v-model="areaSearchKeyword"
+              placeholder="搜索商户名称"
+              prefix-icon="el-icon-search"
+              size="small"
+              clearable
+              @input="handleAreaSearch">
+            </el-input>
+          </el-col>
+        </el-row>
+      </div>
+
+      <div style="height: 420px; overflow-y: auto; border: 1px solid #EBEEF5; padding: 10px; margin-top: 15px;">
+        <el-tree
+          ref="areaTree"
+          :data="areaTree"
+          :props="areaTreeProps"
+          node-key="id"
+          show-checkbox
+          :default-checked-keys="defaultCheckedAreas"
+          :check-strictly="true"
+          :filter-node-method="filterAreaNode"
+          @check="handleAreaTreeCheck">
+          <span class="custom-tree-node" slot-scope="{ node, data }">
+            <span :style="{opacity: data.disabled ? 0.5 : 1}">
+              <i v-if="data.children && data.children.length"
+                 class="el-icon-folder"
+                 :style="{color: data.disabled ? '#c0c4cc' : '#909399', marginRight: '5px'}"></i>
+              <i v-else
+                 class="el-icon-office-building"
+                 :style="{color: data.disabled ? '#c0c4cc' : '#409EFF', marginRight: '5px'}"></i>
+              <span :style="{color: data.disabled ? '#c0c4cc' : 'inherit'}">
+                {{ node.label }}
+              </span>
+            </span>
+            <span style="margin-left: 10px;">
+              <el-tag v-if="isRelatedArea(data.id) && !data.disabled"
+                      type="success"
+                      size="mini">
+                已选中
+              </el-tag>
+              <el-tag v-if="data.disabled"
+                      type="info"
+                      size="mini">
+                父节点已选
+              </el-tag>
+            </span>
+          </span>
+        </el-tree>
+      </div>
+
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="areaConfigCancel">取 消</el-button>
+        <el-button type="primary" @click="saveAreaConfig" :loading="areaSaving">保 存</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listCharging, updateCharging, addCharging, delCharging } from '@/api/basecfg/elecAttr'
+import { getDeviceTree } from '@/api/device/meterDevice'
+import { areaTreeSelect } from '@/api/basecfg/area'
+import { saveObjTagRel, deleteObjTagRel, selectRelByTagCode } from '@/api/basecfg/objTag'
+import commonMethods from '../mixins/commonMethods'
+
+export default {
+  name: 'MerchantCharging',
+  mixins: [commonMethods],
+  data() {
+    return {
+      // 遮罩层
+      loading: false,
+      // 显示搜索条件
+      showSearch: true,
+      // 商户计费
+      chargingList: [],
+      // 弹出层标题
+      title: '',
+      chargingDialog: false,
+      // 选项定义
+      areaOptions: undefined,
+      // 查询参数
+      chargingParams: {
+        pageNum: 1,
+        pageSize: 10
+      },
+      chargingForm: {
+        areaCode: '',
+        elecUnitPrice: '',
+        elecGtComputeType: '',
+        elecComputeDesc: '',
+        waterUnitPrice: '',
+        waterGtComputeType: '',
+        waterComputeDesc: ''
+      },
+
+      // 关联商户树相关数据
+      relatedAreaDialog: false,
+      areaTree: [],
+      areaTreeProps: {
+        children: 'children',
+        label: 'label'
+      },
+      selectedAreas: [], // 已选择的区域
+      defaultCheckedAreas: [], // 默认勾选的区域
+      originalAreas: [], // 原始已绑定的区域
+      areaSaving: false,
+      areaSearchKeyword: '', // 商户搜索关键词
+
+      // 公摊表计相关数据
+      meterConfigDialog: false,
+      activeMeterTab: 'elec',
+      elecMeterTree: [],
+      waterMeterTree: [],
+      meterTreeProps: {
+        children: 'children',
+        label: 'label'
+      },
+      selectedElecMeters: [], // 已选择的电表
+      selectedWaterMeters: [], // 已选择的水表
+      defaultElecCheckedKeys: [], // 默认勾选的电表
+      defaultWaterCheckedKeys: [], // 默认勾选的水表
+      originalElecMeters: [], // 原始已绑定的电表
+      originalWaterMeters: [], // 原始已绑定的水表
+      meterSaving: false,
+
+      // 定义新的标签常量
+      ELEC_METER_TAG: 'MeterDev_Gt_45', // 公摊电表标签
+      WATER_METER_TAG: 'MeterDev_Gt_70' // 公摊水表标签
+    }
+  },
+  computed: {
+    // 获取当前树中所有的节点ID列表
+    allTreeNodeIds() {
+      const ids = []
+      const traverse = (nodes) => {
+        nodes.forEach(node => {
+          ids.push(node.id)
+          if (node.children && node.children.length > 0) {
+            traverse(node.children)
+          }
+        })
+      }
+      if (this.areaTree && this.areaTree.length > 0) {
+        traverse(this.areaTree)
+      }
+      return ids
+    },
+
+    // 计算实际有效的已选区域(树节点与标签数据的交集)
+    validSelectedAreas() {
+      return this.selectedAreas.filter(areaId =>
+        this.allTreeNodeIds.includes(areaId)
+      )
+    }
+  },
+  created() {
+    this.getAreaList('0', 1)
+    this.getChargingList()
+  },
+  methods: {
+    /** 查询商户计费列表 */
+    getChargingList() {
+      const { pageNum, pageSize } = this.chargingParams
+      this.loading = true
+      listCharging({ pageNum, pageSize }).then(response => {
+        this.chargingList = response.rows
+        this.loading = false
+      })
+    },
+
+    // 查询区域列表
+    getAreaList(areaCode, layer) {
+      areaTreeSelect(areaCode, layer).then(response => {
+        this.areaOptions = response.data
+      })
+    },
+
+    handleChargingAdd() {
+      this.resetForm('chargingForm')
+      this.chargingForm = {
+        areaCode: '',
+        elecUnitPrice: '',
+        elecGtComputeType: '',
+        elecComputeDesc: '',
+        waterUnitPrice: '',
+        waterGtComputeType: '',
+        waterComputeDesc: ''
+      }
+      this.chargingDialog = true
+      this.title = '添加商户计费配置'
+    },
+
+    handleChargingUpdate(row) {
+      this.resetForm('chargingForm')
+      Object.assign(this.chargingForm, row)
+
+      // 清空关联商户选择
+      this.selectedAreas = []
+      this.originalAreas = []
+
+      // 加载该区域的关联商户配置
+      this.loadAreaRelatedShops()
+
+      // 清空公摊表计选择
+      this.selectedElecMeters = []
+      this.selectedWaterMeters = []
+      this.originalElecMeters = []
+      this.originalWaterMeters = []
+
+      // 加载该区域的公摊表计配置
+      this.loadAreaSharedMeters()
+
+      this.chargingDialog = true
+      this.title = '修改商户计费配置'
+    },
+
+    handleChargingDelete(row) {
+      this.$modal.confirm('是否确认删除').then(() => {
+        delCharging(row.id).then(() => {
+          this.getChargingList()
+          this.$modal.msgSuccess('删除成功')
+        })
+      })
+    },
+
+    chargingCancel() {
+      this.chargingDialog = false
+      this.resetForm('chargingForm')
+      this.chargingForm = {
+        areaCode: '',
+        elecUnitPrice: '',
+        elecGtComputeType: '',
+        elecComputeDesc: '',
+        waterUnitPrice: '',
+        waterGtComputeType: '',
+        waterComputeDesc: ''
+      }
+    },
+
+    submitChargingForm() {
+      this.$refs['chargingForm'].validate(valid => {
+        if (valid) {
+          if (this.chargingForm.id) {
+            updateCharging(this.chargingForm).then(() => {
+              this.$modal.msgSuccess('修改成功')
+              this.chargingDialog = false
+              this.getChargingList()
+            })
+          } else {
+            addCharging(this.chargingForm).then(() => {
+              this.$modal.msgSuccess('新增成功')
+              this.chargingDialog = false
+              this.getChargingList()
+            })
+          }
+        }
+      })
+    },
+
+    // 公摊表计相关方法
+    openMeterConfig() {
+      this.meterConfigDialog = true
+      this.loadMeterTree()
+    },
+
+    // 修改:分别加载电表和水表的公摊标签
+    async loadAreaSharedMeters() {
+      try {
+        // 加载电表公摊标签
+        const elecResponse = await selectRelByTagCode(4, this.ELEC_METER_TAG)
+        if (elecResponse.data && elecResponse.data.length > 0) {
+          const elecMeters = elecResponse.data.map(item => item.objCode)
+          this.selectedElecMeters = [...elecMeters]
+          this.originalElecMeters = [...elecMeters]
+        }
+
+        // 加载水表公摊标签
+        const waterResponse = await selectRelByTagCode(4, this.WATER_METER_TAG)
+        if (waterResponse.data && waterResponse.data.length > 0) {
+          const waterMeters = waterResponse.data.map(item => item.objCode)
+          this.selectedWaterMeters = [...waterMeters]
+          this.originalWaterMeters = [...waterMeters]
+        }
+      } catch (error) {
+        console.error('加载公摊表计配置失败:', error)
+      }
+    },
+
+    async loadMeterTree() {
+      try {
+        const elecResponse = await getDeviceTree(this.chargingForm.areaCode, 45, 0, 1)
+        this.elecMeterTree = this.processTreeData(elecResponse.data)
+        const waterResponse = await getDeviceTree(this.chargingForm.areaCode, 70, 0, 1)
+        this.waterMeterTree = this.processTreeData(waterResponse.data)
+        await this.loadTaggedMeters()
+      } catch (error) {
+        console.error('加载表计树失败:', error)
+        this.$message.error('加载表计数据失败')
+      }
+    },
+
+    processTreeData(treeData) {
+      const processNode = (node) => {
+        if (node.type === 'device') {
+          node.disabled = false
+        } else {
+          node.disabled = true
+        }
+        if (node.children && node.children.length > 0) {
+          node.children = node.children.map(child => processNode(child))
+        }
+        return node
+      }
+      return treeData.map(item => processNode(item))
+    },
+
+    // 修改:分别加载已标记的电表和水表
+    async loadTaggedMeters() {
+      try {
+        // 加载已标记的电表
+        const elecResponse = await selectRelByTagCode(4, this.ELEC_METER_TAG)
+        if (elecResponse.data && elecResponse.data.length > 0) {
+          const elecMeters = []
+          elecResponse.data.forEach(item => {
+            if (this.isInElecTree(item.objCode)) {
+              elecMeters.push(item.objCode)
+            }
+          })
+          this.selectedElecMeters = [...elecMeters]
+          this.originalElecMeters = [...elecMeters]
+          this.defaultElecCheckedKeys = [...elecMeters]
+          this.$nextTick(() => {
+            if (this.$refs.elecTree) {
+              this.$refs.elecTree.setCheckedKeys(elecMeters)
+            }
+          })
+        }
+
+        // 加载已标记的水表
+        const waterResponse = await selectRelByTagCode(4, this.WATER_METER_TAG)
+        if (waterResponse.data && waterResponse.data.length > 0) {
+          const waterMeters = []
+          waterResponse.data.forEach(item => {
+            if (this.isInWaterTree(item.objCode)) {
+              waterMeters.push(item.objCode)
+            }
+          })
+          this.selectedWaterMeters = [...waterMeters]
+          this.originalWaterMeters = [...waterMeters]
+          this.defaultWaterCheckedKeys = [...waterMeters]
+          this.$nextTick(() => {
+            if (this.$refs.waterTree) {
+              this.$refs.waterTree.setCheckedKeys(waterMeters)
+            }
+          })
+        }
+      } catch (error) {
+        console.error('加载已标记表计失败:', error)
+      }
+    },
+
+    isInElecTree(meterId) {
+      const findInTree = (nodes) => {
+        for (const node of nodes) {
+          if (node.id === meterId && node.type === 'device') {
+            return true
+          }
+          if (node.children && node.children.length > 0) {
+            const found = findInTree(node.children)
+            if (found) return true
+          }
+        }
+        return false
+      }
+      return findInTree(this.elecMeterTree)
+    },
+
+    isInWaterTree(meterId) {
+      const findInTree = (nodes) => {
+        for (const node of nodes) {
+          if (node.id === meterId && node.type === 'device') {
+            return true
+          }
+          if (node.children && node.children.length > 0) {
+            const found = findInTree(node.children)
+            if (found) return true
+          }
+        }
+        return false
+      }
+      return findInTree(this.waterMeterTree)
+    },
+
+    isSharedMeter(meterId, meterCls) {
+      if (meterCls === 45) {
+        return this.selectedElecMeters.includes(meterId)
+      } else if (meterCls === 70) {
+        return this.selectedWaterMeters.includes(meterId)
+      }
+      return false
+    },
+
+    handleElecTreeCheck(data, checked) {
+      const checkedNodes = checked.checkedNodes.filter(node => node.type === 'device')
+      this.selectedElecMeters = checkedNodes.map(node => node.id)
+    },
+
+    handleWaterTreeCheck(data, checked) {
+      const checkedNodes = checked.checkedNodes.filter(node => node.type === 'device')
+      this.selectedWaterMeters = checkedNodes.map(node => node.id)
+    },
+
+    meterConfigCancel() {
+      this.meterConfigDialog = false
+      this.selectedElecMeters = [...this.originalElecMeters]
+      this.selectedWaterMeters = [...this.originalWaterMeters]
+    },
+
+    // 修改:使用不同的标签保存电表和水表
+    async saveMeterConfig() {
+      this.meterSaving = true
+      try {
+        const toAddElec = this.selectedElecMeters.filter(id => !this.originalElecMeters.includes(id))
+        const toRemoveElec = this.originalElecMeters.filter(id => !this.selectedElecMeters.includes(id))
+        const toAddWater = this.selectedWaterMeters.filter(id => !this.originalWaterMeters.includes(id))
+        const toRemoveWater = this.originalWaterMeters.filter(id => !this.selectedWaterMeters.includes(id))
+
+        let successCount = 0
+        let failCount = 0
+
+        // 处理电表 - 使用 MeterDev_Gt_45 标签
+        for (const meterId of toAddElec) {
+          try {
+            const res = await saveObjTagRel(this.ELEC_METER_TAG, 4, meterId)
+            if (res.code === 200) {
+              successCount++
+            } else {
+              failCount++
+            }
+          } catch (error) {
+            failCount++
+          }
+        }
+
+        for (const meterId of toRemoveElec) {
+          try {
+            const res = await deleteObjTagRel(this.ELEC_METER_TAG, 4, meterId)
+            if (res.code === 200) {
+              successCount++
+            } else {
+              failCount++
+            }
+          } catch (error) {
+            failCount++
+          }
+        }
+
+        // 处理水表 - 使用 MeterDev_Gt_70 标签
+        for (const meterId of toAddWater) {
+          try {
+            const res = await saveObjTagRel(this.WATER_METER_TAG, 4, meterId)
+            if (res.code === 200) {
+              successCount++
+            } else {
+              failCount++
+            }
+          } catch (error) {
+            failCount++
+          }
+        }
+
+        for (const meterId of toRemoveWater) {
+          try {
+            const res = await deleteObjTagRel(this.WATER_METER_TAG, 4, meterId)
+            if (res.code === 200) {
+              successCount++
+            } else {
+              failCount++
+            }
+          } catch (error) {
+            failCount++
+          }
+        }
+
+        this.originalElecMeters = [...this.selectedElecMeters]
+        this.originalWaterMeters = [...this.selectedWaterMeters]
+
+        const totalOperations = toAddElec.length + toRemoveElec.length + toAddWater.length + toRemoveWater.length
+        if (totalOperations === 0) {
+          this.$message.info('没有需要保存的更改')
+        } else if (failCount === 0) {
+          this.$message.success(`公摊表计配置保存成功(共${successCount}项)`)
+        } else if (successCount > 0) {
+          this.$message.warning(`部分保存成功:成功${successCount}项,失败${failCount}项`)
+        } else {
+          this.$message.error(`保存失败,请稍后重试`)
+        }
+        this.meterConfigDialog = false
+      } catch (error) {
+        console.error('保存公摊表计配置失败:', error)
+        this.$message.error('保存失败,请稍后重试')
+      } finally {
+        this.meterSaving = false
+      }
+    },
+
+    // 关联商户相关方法(保持不变)
+    openAreaConfig() {
+      this.relatedAreaDialog = true
+      this.loadAreaTree()
+    },
+
+    async loadAreaRelatedShops() {
+      try {
+        const response = await selectRelByTagCode(1, 'Area_Seller')
+        if (response.data && response.data.length > 0) {
+          const areas = response.data.map(item => item.objCode)
+          this.selectedAreas = [...areas]
+          this.originalAreas = [...areas]
+        }
+      } catch (error) {
+        console.error('加载关联商户配置失败:', error)
+      }
+    },
+
+    async loadAreaTree() {
+      try {
+        const response = await areaTreeSelect(this.chargingForm.areaCode, 5)
+        this.areaTree = this.processAreaTreeData(response.data)
+        await this.loadTaggedAreas()
+        this.updateNodeDisabledState()
+      } catch (error) {
+        console.error('加载区域树失败:', error)
+        this.$message.error('加载区域数据失败')
+      }
+    },
+
+    processAreaTreeData(treeData) {
+      const processNode = (node) => {
+        node.disabled = false
+        if (node.children && node.children.length > 0) {
+          node.children = node.children.map(child => processNode(child))
+        }
+        return node
+      }
+      return treeData.map(item => processNode(item))
+    },
+
+    updateNodeDisabledState() {
+      const updateNodeAndChildren = (nodes, parentChecked = false) => {
+        nodes.forEach(node => {
+          if (parentChecked) {
+            node.disabled = true
+          } else {
+            const isChecked = this.selectedAreas.includes(node.id)
+            node.disabled = false
+            if (node.children && node.children.length > 0) {
+              updateNodeAndChildren(node.children, isChecked)
+            }
+          }
+        })
+      }
+      if (this.areaTree && this.areaTree.length > 0) {
+        updateNodeAndChildren(this.areaTree, false)
+        this.$forceUpdate()
+      }
+    },
+
+    async loadTaggedAreas() {
+      try {
+        const response = await selectRelByTagCode(1, 'Area_Seller')
+        if (response.data && response.data.length > 0) {
+          const taggedAreas = response.data.map(item => item.objCode)
+          const validTaggedAreas = taggedAreas.filter(areaId =>
+            this.allTreeNodeIds.includes(areaId)
+          )
+          this.selectedAreas = [...validTaggedAreas]
+          this.originalAreas = [...validTaggedAreas]
+          this.defaultCheckedAreas = [...validTaggedAreas]
+          this.$nextTick(() => {
+            if (this.$refs.areaTree) {
+              this.$refs.areaTree.setCheckedKeys(validTaggedAreas)
+              this.updateNodeDisabledState()
+            }
+          })
+        } else {
+          this.selectedAreas = []
+          this.originalAreas = []
+          this.defaultCheckedAreas = []
+          this.$nextTick(() => {
+            if (this.$refs.areaTree) {
+              this.$refs.areaTree.setCheckedKeys([])
+            }
+          })
+        }
+      } catch (error) {
+        console.error('加载已标记区域失败:', error)
+        this.selectedAreas = []
+        this.originalAreas = []
+        this.defaultCheckedAreas = []
+      }
+    },
+
+    isRelatedArea(areaId) {
+      return this.validSelectedAreas.includes(areaId)
+    },
+
+    handleAreaTreeCheck(data, checked) {
+      const isChecked = checked.checkedKeys.includes(data.id)
+      if (isChecked) {
+        const childIds = this.getAllChildIds(data)
+        this.selectedAreas = checked.checkedKeys.filter(id => !childIds.includes(id))
+        const parentId = this.findParentId(data.id)
+        if (parentId && this.selectedAreas.includes(parentId)) {
+          this.$message.warning('父节点已被选中,无法选择子节点')
+          this.selectedAreas = this.selectedAreas.filter(id => id !== data.id)
+          this.$nextTick(() => {
+            this.$refs.areaTree.setCheckedKeys(this.selectedAreas)
+          })
+          return
+        }
+      } else {
+        this.selectedAreas = checked.checkedKeys
+      }
+      this.updateNodeDisabledState()
+    },
+
+    getAllChildIds(node) {
+      const ids = []
+      const collectIds = (n) => {
+        if (n.children && n.children.length > 0) {
+          n.children.forEach(child => {
+            ids.push(child.id)
+            collectIds(child)
+          })
+        }
+      }
+      collectIds(node)
+      return ids
+    },
+
+    findParentId(nodeId) {
+      let parentId = null
+      const findParent = (nodes, parent = null) => {
+        for (const node of nodes) {
+          if (node.id === nodeId) {
+            parentId = parent ? parent.id : null
+            return true
+          }
+          if (node.children && node.children.length > 0) {
+            if (findParent(node.children, node)) {
+              return true
+            }
+          }
+        }
+        return false
+      }
+      findParent(this.areaTree)
+      return parentId
+    },
+
+    handleAreaSearch(val) {
+      this.$refs.areaTree && this.$refs.areaTree.filter(val)
+    },
+
+    filterAreaNode(value, data) {
+      if (!value) return true
+      return data.label.indexOf(value) !== -1
+    },
+
+    areaConfigCancel() {
+      this.relatedAreaDialog = false
+      this.selectedAreas = [...this.originalAreas]
+      this.areaSearchKeyword = ''
+    },
+
+    async saveAreaConfig() {
+      const hasConflict = this.checkParentChildConflict()
+      if (hasConflict) {
+        this.$message.error('存在父子节点同时被选中的情况,请检查')
+        return
+      }
+
+      this.areaSaving = true
+      try {
+        const currentTagResponse = await selectRelByTagCode(1, 'Area_Seller')
+        const currentTaggedAreas = currentTagResponse.data ?
+          currentTagResponse.data.map(item => item.objCode) : []
+
+        const validSelectedInTree = this.selectedAreas.filter(id =>
+          this.allTreeNodeIds.includes(id)
+        )
+        const validOriginalInTree = this.originalAreas.filter(id =>
+          this.allTreeNodeIds.includes(id)
+        )
+
+        const toAdd = validSelectedInTree.filter(id => !validOriginalInTree.includes(id))
+        const toRemove = validOriginalInTree.filter(id => !validSelectedInTree.includes(id))
+        const outsideTreeTags = currentTaggedAreas.filter(id =>
+          !this.allTreeNodeIds.includes(id)
+        )
+
+        let successCount = 0
+        let failCount = 0
+
+        for (const areaId of toAdd) {
+          try {
+            const res = await saveObjTagRel('Area_Seller', 1, areaId)
+            if (res.code === 200) {
+              successCount++
+            } else {
+              failCount++
+            }
+          } catch (error) {
+            failCount++
+          }
+        }
+
+        for (const areaId of toRemove) {
+          try {
+            const res = await deleteObjTagRel('Area_Seller', 1, areaId)
+            if (res.code === 200) {
+              successCount++
+            } else {
+              failCount++
+            }
+          } catch (error) {
+            failCount++
+          }
+        }
+
+        this.originalAreas = [...validSelectedInTree]
+        const totalOperations = toAdd.length + toRemove.length
+
+        if (totalOperations === 0) {
+          this.$message.info('没有需要保存的更改')
+        } else if (failCount === 0) {
+          this.$message.success(`关联商户配置保存成功(共${successCount}项)`)
+        } else if (successCount > 0) {
+          this.$message.warning(`部分保存成功:成功${successCount}项,失败${failCount}项`)
+        } else {
+          this.$message.error(`保存失败,请稍后重试`)
+        }
+
+        this.relatedAreaDialog = false
+        this.areaSearchKeyword = ''
+      } catch (error) {
+        console.error('保存关联商户配置失败:', error)
+        this.$message.error('保存失败,请稍后重试')
+      } finally {
+        this.areaSaving = false
+      }
+    },
+
+    checkParentChildConflict() {
+      for (const nodeId of this.selectedAreas) {
+        const parentId = this.findParentId(nodeId)
+        if (parentId && this.selectedAreas.includes(parentId)) {
+          return true
+        }
+      }
+      return false
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../styles/dialog.scss';
+.custom-tree-node {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 14px;
+  padding-right: 8px;
+}
+
+::v-deep .el-tree-node__content {
+  height: 32px;
+  &:hover {
+    background-color: #F5F7FA;
+  }
+}
+
+::v-deep .el-tree-node.is-disabled > .el-tree-node__content {
+  color: #909399;
+  cursor: not-allowed;
+  background-color: transparent !important;
+}
+
+// 统计栏样式
+.meter-stats-bar {
+  background: linear-gradient(135deg, #f5f7fa 0%, #e9ecef 100%);
+  padding: 15px 20px;
+  border-radius: 8px;
+  margin-bottom: 20px;
+  box-shadow: 0 2px 4px rgba(0,0,0,0.08);
+
+  .stat-item {
+    display: flex;
+    align-items: center;
+    font-size: 14px;
+
+    .stat-label {
+      margin-left: 8px;
+      color: #606266;
+    }
+
+    .stat-value {
+      font-weight: bold;
+      font-size: 16px;
+      color: #303133;
+      margin-left: 5px;
+    }
+
+    .stat-unit {
+      color: #606266;
+      margin-left: 2px;
+    }
+
+    .change-tag {
+      margin-left: 10px;
+      animation: fadeIn 0.3s ease-in-out;
+    }
+  }
+}
+
+// 区域头部样式
+.area-header {
+  background: #f5f7fa;
+  padding: 15px 20px;
+  border-radius: 8px;
+  border: 1px solid #e4e7ed;
+
+  .area-stats {
+    display: flex;
+    align-items: center;
+    font-size: 14px;
+  }
+}
+
+// 标签动画
+.el-tag {
+  animation: fadeIn 0.3s ease-in-out;
+  transition: all 0.3s;
+  &:hover {
+    transform: scale(1.05);
+  }
+}
+
+@keyframes fadeIn {
+  from {
+    opacity: 0;
+    transform: scale(0.8);
+  }
+  to {
+    opacity: 1;
+    transform: scale(1);
+  }
+}
+
+// 树节点禁用样式
+::v-deep .el-tree-node {
+  .el-checkbox.is-disabled {
+    .el-checkbox__inner {
+      background-color: #f5f7fa;
+      border-color: #dcdfe6;
+      cursor: not-allowed;
+      &::after {
+        border-color: #c0c4cc;
+      }
+    }
+    .el-checkbox__label {
+      color: #c0c4cc;
+      cursor: not-allowed;
+    }
+  }
+
+  &.is-disabled {
+    .el-tree-node__content {
+      cursor: not-allowed;
+      color: #c0c4cc;
+      &:hover {
+        background-color: transparent;
+      }
+    }
+  }
+}
+
+// 已选中和父节点已选的标签显示
+.custom-tree-node {
+  .el-tag {
+    &.el-tag--success {
+      background-color: #f0f9ff;
+      border-color: #b3d8ff;
+      color: #409eff;
+    }
+    &.el-tag--info {
+      background-color: #f4f4f5;
+      border-color: #d3d4d6;
+      color: #909399;
+    }
+  }
+}
+</style>

+ 267 - 0
ems-ui-cloud/src/views/basecfg/price/components/NonElecPrice.vue

@@ -0,0 +1,267 @@
+<template>
+  <div>
+    <el-form
+      :model="queryParams"
+      ref="queryForm"
+      size="small"
+      :inline="true"
+      v-show="showSearch"
+      label-width="68px"
+    >
+      <el-form-item label="计量介质" prop="meterCls">
+        <el-select
+          v-model="queryParams.meterCls"
+          placeholder="请选择计量介质"
+          clearable
+          @change="handleQuery"
+        >
+          <el-option
+            v-for="item in meterClsOptions"
+            :key="item.value"
+            :label="item.label"
+            :value="item.value"
+          />
+        </el-select>
+      </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="fdPriceList" @selection-change="handleSelectionChange">
+      <el-table-column label="区域" align="center">
+        <template slot-scope="scope">
+          {{ scope.row.areaName || '默认' }}
+        </template>
+      </el-table-column>
+      <el-table-column label="计量介质" align="center" prop="meterClsName" />
+      <el-table-column label="单位价格" align="center" prop="unitPrice" />
+      <el-table-column label="计量单位" align="center" prop="meterUnit" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handleUpdate(scope.row)"
+            v-hasPermi="['ems:FdEnergyPriceConfig:edit']"
+          >
+            修改
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <pagination
+      v-show="total>0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+
+    <!-- 添加或修改非电能源价格配置对话框 -->
+    <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="cfgCode">
+          <el-input v-model="form.cfgCode" placeholder="请输入配置代码" disabled />
+        </el-form-item>
+        <el-form-item label="区域" prop="areaName">
+          <el-input v-model="form.areaName" placeholder="园区名称" disabled />
+        </el-form-item>
+        <el-form-item label="计量介质" prop="meterClsName">
+          <el-input v-model="form.meterClsName" placeholder="计量介质" disabled />
+        </el-form-item>
+        <el-form-item label="单位价格" prop="unitPrice">
+          <el-input v-model="form.unitPrice" placeholder="请输入单位价格" />
+        </el-form-item>
+        <el-form-item label="计量单位" prop="meterUnit">
+          <el-input v-model="form.meterUnit" 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>
+  </div>
+</template>
+
+<script>
+import {
+  listFdEnergyPriceConfig,
+  getFdEnergyPriceConfig,
+  delFdEnergyPriceConfig,
+  addFdEnergyPriceConfig,
+  updateFdEnergyPriceConfig
+} from '@/api/basecfg/FdEnergyPriceConfig'
+import commonMethods from '../mixins/commonMethods'
+
+export default {
+  name: 'NonElecPrice',
+  mixins: [commonMethods],
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 非电价格列表
+      fdPriceList: [],
+      // 弹出层标题
+      title: '',
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+        cfgCode: null,
+        areaCode: null,
+        meterCls: null
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        cfgCode: [
+          { required: true, message: '配置代码不能为空', trigger: 'blur' }
+        ],
+        meterCls: [
+          { required: true, message: '计量介质不能为空', trigger: 'blur' }
+        ],
+        unitPrice: [
+          { required: true, message: '单位价格不能为空', trigger: 'blur' }
+        ]
+      }
+    }
+  },
+  created() {
+    this.getList()
+  },
+  methods: {
+    /** 查询非电能源价格配置列表 */
+    getList() {
+      this.loading = true
+      listFdEnergyPriceConfig(this.queryParams).then(response => {
+        this.fdPriceList = response.rows
+        this.total = response.total
+        this.loading = false
+      })
+    },
+
+    // 取消按钮
+    cancel() {
+      this.open = false
+      this.reset()
+    },
+
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        cfgCode: null,
+        areaCode: null,
+        meterCls: null,
+        unitPrice: null,
+        meterUnit: null
+      }
+      this.resetForm("form")
+    },
+
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNum = 1
+      this.getList()
+    },
+
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm")
+      this.handleQuery()
+    },
+
+    // 多选框选中数据
+    handleSelectionChange(selection) {
+      this.ids = selection.map(item => item.id)
+      this.single = selection.length !== 1
+      this.multiple = !selection.length
+    },
+
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset()
+      this.open = true
+      this.title = "添加非电能源价格配置"
+    },
+
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset()
+      const id = row.id || this.ids
+      getFdEnergyPriceConfig(id).then(response => {
+        this.form = response.data
+        // 设置园区名称和计量介质名称
+        this.form.areaName = response.data.areaName || '默认'
+        // 从meterClsOptions中查找计量介质名称
+        const meterClsOption = this.meterClsOptions.find(item => item.value === response.data.meterCls)
+        this.form.meterClsName = meterClsOption ? meterClsOption.label : ''
+        this.open = true
+        this.title = "修改非电能源价格配置"
+      })
+    },
+
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updateFdEnergyPriceConfig(this.form).then(response => {
+              this.$modal.msgSuccess("修改成功")
+              this.open = false
+              this.getList()
+            })
+          } else {
+            addFdEnergyPriceConfig(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 delFdEnergyPriceConfig(ids)
+      }).then(() => {
+        this.getList()
+        this.$modal.msgSuccess("删除成功")
+      }).catch(() => {})
+    },
+
+    /** 导出按钮操作 */
+    handleExport() {
+      this.download('ems/FdEnergyPriceConfig/export', {
+        ...this.queryParams
+      }, `FdEnergyPriceConfig_${new Date().getTime()}.xlsx`)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../styles/dialog.scss';
+</style>

+ 766 - 0
ems-ui-cloud/src/views/basecfg/price/components/PeakValleyStrategy.vue

@@ -0,0 +1,766 @@
+<template>
+  <div>
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button
+          type="primary"
+          plain
+          icon="el-icon-plus"
+          size="mini"
+          @click="handleStrategyAdd"
+          v-hasPermi="['basecfg:price:add']"
+        >
+          新增
+        </el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" :search="false" @queryTable="getStrategyList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="strategyList">
+      <el-table-column label="策略编码" align="center" prop="strategyCode" />
+      <el-table-column label="策略名称" align="center" prop="strategyName" />
+      <el-table-column label="执行月份" align="center" prop="execMonth" show-overflow-tooltip>
+        <template slot-scope="scope">
+          {{formatExecMonth(scope.row.execMonth)}}
+        </template>
+      </el-table-column>
+      <el-table-column label="执行日期" align="center" prop="execDateType">
+        <template slot-scope="scope">
+          {{formatDict(scope.row.execDateType,'execDateTypeOptions')}}
+        </template>
+      </el-table-column>
+      <el-table-column label="策略描述" align="center" prop="strategyDesc" show-overflow-tooltip />
+      <el-table-column label="分时类型" align="center" prop="supportType" show-overflow-tooltip>
+        <template slot-scope="scope">
+          {{formatSupportType(scope.row.supportType)}}
+        </template>
+      </el-table-column>
+      <el-table-column label="优先级" align="center" prop="priority" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-info"
+            v-if="scope.row.editFlag==0"
+            @click="handleStrategyDetail(scope.row)"
+            v-hasPermi="['basecfg:price:edit']"
+          >
+            查看
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            v-else
+            @click="handleStrategyUpdate(scope.row)"
+            v-hasPermi="['basecfg:price:edit']"
+          >
+            修改
+          </el-button>
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-delete"
+            class="deleteBtn"
+            @click="handleStrategyDelete(scope.row)"
+            v-hasPermi="['basecfg:price:remove']"
+            :disabled="scope.row.editFlag==0"
+          >
+            删除
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 添加或修改峰谷电价策略配置对话框 -->
+    <el-dialog :title="title" :visible.sync="strategyDialog" width="850px" append-to-body>
+      <el-form ref="strategyForm" class="strategyForm" :model="strategyForm" :disabled="ifDisabled" label-width="100px">
+        <el-form-item label="策略编码" prop="strategyCode" required :rules="[{required:true,message:'策略编码不能为空'}]">
+          <el-input v-model="strategyForm.strategyCode" placeholder="请输入策略编码" />
+        </el-form-item>
+        <el-form-item label="策略名称" prop="strategyName" required :rules="[{required:true,message:'策略名称不能为空'}]">
+          <el-input v-model="strategyForm.strategyName" placeholder="请输入策略名称" />
+        </el-form-item>
+        <el-form-item label="执行日期" prop="execDateType" required :rules="[{required:true,message:'请选择执行日期'}]">
+          <el-select v-model="strategyForm.execDateType" style="width:100%" placeholder="请选择执行日期" @change="execDateTypeChange">
+            <el-option v-for="item in execDateTypeOptions" :label="item.name" :value="item.value" :key="item.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item
+          label="执行说明"
+          required
+          :rules="[{required:true,message:'请选择执行说明'}]"
+          prop="execDate"
+          v-if="[1,5,6].includes(strategyForm.execDateType)"
+        >
+          <el-time-picker
+            v-if="strategyForm.execDateType==1"
+            v-model="strategyForm.execDate"
+            value-format="HH:mm:ss"
+            :picker-options="{selectableRange: '00:00:00 - 23:59:59'}"
+            placeholder="选择时间"
+          >
+          </el-time-picker>
+          <el-checkbox-group v-else-if="strategyForm.execDateType==5" v-model="strategyForm.execDate">
+            <el-checkbox v-for="day in weeks.filter(item=>item.value<6)" :label="day.value" :key="day.value">
+              {{day.name}}
+            </el-checkbox>
+          </el-checkbox-group>
+          <el-checkbox-group v-else-if="strategyForm.execDateType==6" v-model="strategyForm.execDate">
+            <el-checkbox v-for="day in weeks" :label="day.value" :key="day.value">{{day.name}}</el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="策略描述" prop="strategyDesc">
+          <el-input v-model="strategyForm.strategyDesc" placeholder="请输入策略描述" />
+        </el-form-item>
+        <el-form-item label="执行月份" prop="execMonth" required :rules="[{required:true,message:'请选择执行月份'}]">
+          <el-checkbox-group v-model="strategyForm.execMonth">
+            <el-tooltip
+              v-for="month in availableMonths"
+              :key="month.value"
+              :content="month.disabled ? '已占用' : ''"
+              :disabled="!month.disabled"
+              placement="top"
+              effect="dark">
+              <el-checkbox
+                :label="month.value"
+                :disabled="month.disabled">
+                <span :style="{color: month.disabled ? '#c0c4cc' : ''}">
+                  {{month.label}}
+                </span>
+              </el-checkbox>
+            </el-tooltip>
+          </el-checkbox-group>
+          <div v-if="monthConflictTip" style="color: #f56c6c; font-size: 12px; margin-top: 5px;">
+            {{ monthConflictTip }}
+          </div>
+        </el-form-item>
+        <el-form-item label="分时类型" prop="supportType" required :rules="[{required:true,message:'请选择分时类型'}]">
+          <el-checkbox-group v-model="strategyForm.supportType" @change="handleSupportTypeChange">
+            <el-checkbox
+              v-for="type in timeTypeOptions"
+              :label="type.value"
+              :key="type.value"
+              :disabled="type.value === 0"
+            >
+              {{type.label}}
+            </el-checkbox>
+          </el-checkbox-group>
+        </el-form-item>
+        <el-form-item label="优先级" prop="priority" required>
+          <el-input-number
+            v-model="strategyForm.priority"
+            :min="0"
+            :max="100"
+            @change="handlePriorityChange"
+            label="描述文字">
+          </el-input-number>
+          <span style="margin-left: 10px; color: #909399; font-size: 12px;">
+            相同优先级的策略月份不能重叠
+          </span>
+        </el-form-item>
+        <el-form-item label="小时电价">
+          <el-table class="sub-table" :data="strategyForm.hourList" max-height="200px">
+            <el-table-column label="开始时刻" align="center" prop="startTime">
+              <template slot-scope="scope">
+                <el-time-picker
+                  v-model="scope.row.startTime"
+                  size="mini"
+                  value-format="HH:mm:ss"
+                  :picker-options="{selectableRange: '00:00:00 - 23:59:59'}"
+                  placeholder="选择时间"
+                >
+                </el-time-picker>
+              </template>
+            </el-table-column>
+            <el-table-column label="结束时刻" align="center" prop="endTime">
+              <template slot-scope="scope">
+                <el-time-picker
+                  v-model="scope.row.endTime"
+                  size="mini"
+                  value-format="HH:mm:ss"
+                  :picker-options="{selectableRange: '00:00:00 - 23:59:59'}"
+                  placeholder="选择时间"
+                >
+                </el-time-picker>
+              </template>
+            </el-table-column>
+            <el-table-column label="分时类型" align="center" prop="type">
+              <template slot-scope="scope">
+                <el-select v-model="scope.row.type" size="mini" style="width:100%" placeholder="请选择">
+                  <el-option
+                    v-for="type in filteredTimeTypes"
+                    :label="type.label"
+                    :value="type.value"
+                    :key="type.value"
+                  />
+                </el-select>
+              </template>
+            </el-table-column>
+            <el-table-column align="center" width="100" v-if="!ifDisabled">
+              <template slot="header">
+                <div class="operateBtns" @click="addSub">
+                  <span>操作</span><i class="el-icon-circle-plus-outline"></i>
+                </div>
+              </template>
+              <template slot-scope="scope">
+                <i class="el-icon-delete" @click="deleteSub(scope.$index)"></i>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer" v-if="!ifDisabled">
+        <el-button type="primary" @click="submitStrategyForm">确 定</el-button>
+        <el-button @click="strategyCancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import { listStrategy, addStrategy, updateStrategy, delStrategy } from '@/api/basecfg/elecAttr'
+import commonMethods from '../mixins/commonMethods'
+
+export default {
+  name: 'PeakValleyStrategy',
+  mixins: [commonMethods],
+  data() {
+    return {
+      // 遮罩层
+      loading: false,
+      // 显示搜索条件
+      showSearch: true,
+      // 峰谷电价
+      strategyList: [],
+      // 弹出层标题
+      title: '',
+      strategyDialog: false,
+      ifDisabled: false,
+      // 查询参数
+      strategyParams: {
+        pageNum: 1,
+        pageSize: 10
+      },
+      // 执行日期选项
+      execDateTypeOptions: [
+        { name: '每天', value: 2 },
+        { name: '周一至周五', value: 5 },
+        { name: '自定义', value: 6 }
+      ],
+      weeks: [
+        { name: '周一', value: '1' },
+        { name: '周二', value: '2' },
+        { name: '周三', value: '3' },
+        { name: '周四', value: '4' },
+        { name: '周五', value: '5' },
+        { name: '周六', value: '6' },
+        { name: '周日', value: '7' }
+      ],
+      // 月份选项
+      monthOptions: [
+        { value: '01', label: '一月' },
+        { value: '02', label: '二月' },
+        { value: '03', label: '三月' },
+        { value: '04', label: '四月' },
+        { value: '05', label: '五月' },
+        { value: '06', label: '六月' },
+        { value: '07', label: '七月' },
+        { value: '08', label: '八月' },
+        { value: '09', label: '九月' },
+        { value: '10', label: '十月' },
+        { value: '11', label: '十一月' },
+        { value: '12', label: '十二月' }
+      ],
+      // 分时类型选项
+      timeTypeOptions: [
+        { value: -2, label: '低谷' },
+        { value: -1, label: '谷段' },
+        { value: 0, label: '平段' },
+        { value: 1, label: '峰段' },
+        { value: 2, label: '尖峰' }
+      ],
+      strategyForm: {
+        strategyCode: '',
+        strategyName: '',
+        strategyDesc: '',
+        execDateType: '',
+        execDate: [],
+        execMonth: [],
+        supportType: [0],
+        priority: 0,
+        hourList: []
+      }
+    }
+  },
+  computed: {
+    // 根据选中的分时类型过滤小时电价表格中的选项
+    filteredTimeTypes() {
+      return this.timeTypeOptions.filter(type =>
+        this.strategyForm.supportType.includes(type.value)
+      )
+    },
+    // 获取可用月份(动态禁用已被占用的月份)
+    availableMonths() {
+      const currentPriority = this.strategyForm.priority
+      const samePriorityStrategies = this.strategyList.filter(strategy => {
+        return strategy.priority === currentPriority &&
+          strategy.id !== this.strategyForm.id &&
+          strategy.editFlag !== 0
+      })
+
+      const occupiedMonths = new Set()
+      samePriorityStrategies.forEach(strategy => {
+        if (strategy.execMonth) {
+          strategy.execMonth.split(',').forEach(month => {
+            occupiedMonths.add(month)
+          })
+        }
+      })
+
+      return this.monthOptions.map(month => ({
+        ...month,
+        disabled: occupiedMonths.has(month.value)
+      }))
+    },
+    // 月份冲突提示
+    monthConflictTip() {
+      const currentPriority = this.strategyForm.priority
+      const samePriorityStrategies = this.strategyList.filter(strategy => {
+        return strategy.priority === currentPriority &&
+          strategy.id !== this.strategyForm.id &&
+          strategy.editFlag !== 0
+      })
+
+      if (samePriorityStrategies.length > 0) {
+        const strategies = samePriorityStrategies.map(s => s.strategyName).join('、')
+        return `提示:优先级${currentPriority}下已存在策略:${strategies}`
+      }
+      return ''
+    }
+  },
+  created() {
+    this.getStrategyList()
+  },
+  methods: {
+    // 格式化分时类型
+    formatSupportType(supportType) {
+      if (!supportType) return ''
+      const types = supportType.split(',').map(v => parseInt(v))
+      const labels = types.map(type => {
+        const found = this.timeTypeOptions.find(opt => opt.value === type)
+        return found ? found.label : ''
+      }).filter(label => label)
+      return labels.join('、')
+    },
+
+    // 格式化执行月份
+    formatExecMonth(execMonth) {
+      if (!execMonth) return ''
+      const months = execMonth.split(',')
+      const labels = months.map(month => {
+        const found = this.monthOptions.find(opt => opt.value === month)
+        return found ? found.label : ''
+      }).filter(label => label)
+      return labels.join('、')
+    },
+
+    // 处理分时类型变化
+    handleSupportTypeChange(values) {
+      // 确保平段始终被选中
+      if (!values.includes(0)) {
+        values.push(0)
+      }
+    },
+
+    // 处理优先级变化
+    handlePriorityChange(value) {
+      // 当优先级改变时,清空月份选择中的冲突月份
+      const availableMonthValues = this.availableMonths
+        .filter(m => !m.disabled)
+        .map(m => m.value)
+
+      // 过滤掉不可用的月份
+      this.strategyForm.execMonth = this.strategyForm.execMonth.filter(month =>
+        availableMonthValues.includes(month)
+      )
+    },
+
+    // 修改执行日期变更方法
+    execDateTypeChange(val) {
+      if (val == 5) {
+        this.strategyForm.execDate = this.weeks.filter(item => item.value < 6).map(item => item.value)
+      } else if (val == 6) {
+        this.strategyForm.execDate = []
+      } else {
+        this.strategyForm.execDate = ''
+      }
+    },
+
+    /** 查询峰谷电价列表 */
+    getStrategyList() {
+      const { pageNum, pageSize } = this.strategyParams
+      this.loading = true
+      listStrategy({ pageNum, pageSize }).then(response => {
+        this.strategyList = response.rows
+        this.loading = false
+      })
+    },
+
+    handleStrategyDetail(row) {
+      this.ifDisabled = true
+      this.resetForm('strategyForm')
+      // 深拷贝数据,避免直接修改原数据
+      this.strategyForm = JSON.parse(JSON.stringify(row))
+      // 处理执行日期
+      if ([5, 6].includes(this.strategyForm.execDateType)) {
+        this.strategyForm.execDate = this.strategyForm.execDate ?
+          this.strategyForm.execDate.split(',') : []
+      }
+      // 处理执行月份
+      this.strategyForm.execMonth = this.strategyForm.execMonth ?
+        this.strategyForm.execMonth.split(',') : []
+      // 处理分时类型
+      this.strategyForm.supportType = this.strategyForm.supportType ?
+        this.strategyForm.supportType.split(',').map(v => parseInt(v)) : [0]
+      this.strategyDialog = true
+      this.title = '查看峰谷电价策略'
+    },
+
+    handleStrategyAdd() {
+      this.ifDisabled = false
+      this.resetForm('strategyForm')
+      this.strategyForm = {
+        id: null,
+        strategyCode: '',
+        strategyName: '',
+        strategyDesc: '',
+        execDateType: '',
+        execDate: [],
+        execMonth: [],
+        supportType: [0],
+        priority: 1,
+        hourList: []
+      }
+      this.strategyDialog = true
+      this.title = '添加峰谷电价策略'
+    },
+
+    handleStrategyUpdate(row) {
+      this.ifDisabled = false
+      this.resetForm('strategyForm')
+      // 深拷贝数据,避免直接修改原数据
+      this.strategyForm = JSON.parse(JSON.stringify(row))
+      // 处理执行日期
+      if ([5, 6].includes(this.strategyForm.execDateType)) {
+        this.strategyForm.execDate = this.strategyForm.execDate ?
+          this.strategyForm.execDate.split(',') : []
+      }
+      // 处理执行月份
+      this.strategyForm.execMonth = this.strategyForm.execMonth ?
+        this.strategyForm.execMonth.split(',') : []
+      // 处理分时类型
+      this.strategyForm.supportType = this.strategyForm.supportType ?
+        this.strategyForm.supportType.split(',').map(v => parseInt(v)) : [0]
+      // 确保平段始终被选中
+      if (!this.strategyForm.supportType.includes(0)) {
+        this.strategyForm.supportType.push(0)
+      }
+      this.strategyDialog = true
+      this.title = '修改峰谷电价策略'
+    },
+
+    handleStrategyDelete(row) {
+      this.$modal.confirm('是否确认删除').then(() => {
+        delStrategy(row.id).then(() => {
+          this.getStrategyList()
+          this.$modal.msgSuccess('删除成功')
+        })
+      })
+    },
+
+    strategyCancel() {
+      this.strategyDialog = false
+      this.resetForm('strategyForm')
+      // 完全重置表单数据
+      this.strategyForm = {
+        id: null,
+        strategyCode: '',
+        strategyName: '',
+        strategyDesc: '',
+        execDateType: '',
+        execDate: [],
+        execMonth: [],
+        supportType: [0],
+        priority: 1,
+        hourList: []
+      }
+      // 清除可能的验证状态
+      this.$nextTick(() => {
+        if (this.$refs.strategyForm) {
+          this.$refs.strategyForm.clearValidate()
+        }
+      })
+    },
+
+    submitStrategyForm() {
+      this.$refs['strategyForm'].validate(valid => {
+        if (valid) {
+          if (this.strategyForm.hourList.length === 0) {
+            return this.$modal.msgError(`请添加小时电价数据`)
+          }
+
+          // 检查相同优先级的月份冲突
+          const monthConflict = this.checkMonthConflict()
+          if (monthConflict) {
+            return this.$modal.msgError(`执行月份冲突:${monthConflict}月份已被其他相同优先级的策略使用`)
+          }
+
+          // 验证小时电价数据
+          let subFlag = false
+          let dateFlag = false
+          let validIndex = 0
+          for (let i = 0; i < this.strategyForm.hourList.length; i++) {
+            const item = this.strategyForm.hourList[i]
+            if (item.startTime === '' || item.endTime === '' || item.type === '') {
+              subFlag = true
+              validIndex = i + 1
+              break
+            }
+            if (this.compareTime(item.startTime, item.endTime)) {
+              dateFlag = true
+              validIndex = i + 1
+              break
+            }
+          }
+          if (subFlag) {
+            return this.$modal.msgError(`请补全小时电价第${validIndex}行数据`)
+          }
+          if (dateFlag) {
+            return this.$modal.msgError(`小时电价第${validIndex}行开始时刻不能大于结束时刻`)
+          }
+
+          // 增强的时间段重叠检查
+          const overlapInfo = this.checkTimeRangesOverlapEnhanced()
+          if (overlapInfo) {
+            return this.$modal.msgError(`小时电价时间段冲突:${overlapInfo}`)
+          }
+
+          // 检查时间段完整性
+          const completenessCheck = this.checkTimeCompleteness()
+          if (completenessCheck) {
+            this.$modal.confirm(`${completenessCheck},是否继续保存?`).then(() => {
+              this.saveStrategyForm()
+            }).catch(() => {})
+          } else {
+            this.saveStrategyForm()
+          }
+        }
+      })
+    },
+
+    // 保存策略表单
+    saveStrategyForm() {
+      // 创建表单数据的副本以避免修改原数据
+      const formData = JSON.parse(JSON.stringify(this.strategyForm))
+
+      // 特殊处理执行日期
+      if ([5, 6].includes(formData.execDateType)) {
+        formData.execDate = Array.isArray(formData.execDate) ? formData.execDate.join(',') : formData.execDate
+      }
+
+      // 处理执行月份
+      formData.execMonth = Array.isArray(formData.execMonth) ? formData.execMonth.join(',') : formData.execMonth
+
+      // 处理分时类型
+      formData.supportType = Array.isArray(formData.supportType) ? formData.supportType.join(',') : formData.supportType
+
+      formData.hourList.forEach(item => {
+        item.strategyCode = formData.strategyCode
+      })
+
+      if (formData.id) {
+        updateStrategy(formData).then(() => {
+          this.$modal.msgSuccess('修改成功')
+          this.strategyDialog = false
+          this.getStrategyList()
+        })
+      } else {
+        addStrategy(formData).then(() => {
+          this.$modal.msgSuccess('新增成功')
+          this.strategyDialog = false
+          this.getStrategyList()
+        })
+      }
+    },
+
+    compareTime(t1, t2) {
+      const d = new Date()
+      const ft1 = d.setHours(t1.split(':')[0], t1.split(':')[1], t1.split(':')[2])
+      const ft2 = d.setHours(t2.split(':')[0], t2.split(':')[1], t2.split(':')[2])
+      return ft1 > ft2
+    },
+
+    // 增强版时间段重叠检查
+    checkTimeRangesOverlapEnhanced() {
+      const ranges = this.strategyForm.hourList.map((item, index) => {
+        const d = new Date()
+        const ft1 = d.setHours(item.startTime.split(':')[0], item.startTime.split(':')[1], item.startTime.split(':')[2])
+        const ft2 = d.setHours(item.endTime.split(':')[0], item.endTime.split(':')[1], item.endTime.split(':')[2])
+        return {
+          start: ft1,
+          end: ft2,
+          startStr: item.startTime,
+          endStr: item.endTime,
+          index: index + 1
+        }
+      })
+
+      // 按开始时间排序
+      const sortedRanges = ranges.slice().sort((a, b) => a.start - b.start)
+
+      // 检查重叠并返回详细信息
+      for (let i = 1; i < sortedRanges.length; i++) {
+        if (sortedRanges[i].start < sortedRanges[i - 1].end) {
+          return `第${sortedRanges[i - 1].index}行(${sortedRanges[i - 1].startStr}-${sortedRanges[i - 1].endStr})与第${sortedRanges[i].index}行(${sortedRanges[i].startStr}-${sortedRanges[i].endStr})存在时间重叠`
+        }
+      }
+      return null
+    },
+
+    // 检查时间完整性
+    checkTimeCompleteness() {
+      if (this.strategyForm.hourList.length === 0) return null
+
+      const ranges = this.strategyForm.hourList.map(item => {
+        const startParts = item.startTime.split(':')
+        const endParts = item.endTime.split(':')
+        return {
+          start: parseInt(startParts[0]) * 3600 + parseInt(startParts[1]) * 60 + parseInt(startParts[2]),
+          end: parseInt(endParts[0]) * 3600 + parseInt(endParts[1]) * 60 + parseInt(endParts[2]),
+          startStr: item.startTime,
+          endStr: item.endTime
+        }
+      }).sort((a, b) => a.start - b.start)
+
+      const gaps = []
+      const dayInSeconds = 24 * 3600
+      const tolerance = 1
+
+      // 检查开始是否从0点开始
+      if (ranges[0].start > tolerance) {
+        gaps.push(`00:00:00 - ${this.formatSeconds(ranges[0].start)}`)
+      }
+
+      // 检查中间的间隙
+      for (let i = 1; i < ranges.length; i++) {
+        const gap = ranges[i].start - ranges[i - 1].end
+        if (gap > tolerance) {
+          gaps.push(`${this.formatSeconds(ranges[i - 1].end)} - ${this.formatSeconds(ranges[i].start)}`)
+        }
+      }
+
+      // 检查结束是否到24点
+      if (ranges[ranges.length - 1].end < dayInSeconds - tolerance) {
+        gaps.push(`${this.formatSeconds(ranges[ranges.length - 1].end)} - 23:59:59`)
+      }
+
+      if (gaps.length > 0) {
+        return `以下时间段未配置:${gaps.join('、')}`
+      }
+      return null
+    },
+
+    // 辅助方法:将秒数转换为时间格式
+    formatSeconds(seconds) {
+      const h = Math.floor(seconds / 3600)
+      const m = Math.floor((seconds % 3600) / 60)
+      const s = seconds % 60
+      return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
+    },
+
+    // 检查相同优先级策略的月份冲突
+    checkMonthConflict() {
+      const currentPriority = this.strategyForm.priority
+      const samePriorityStrategies = this.strategyList.filter(strategy => {
+        return strategy.priority === currentPriority &&
+          strategy.id !== this.strategyForm.id &&
+          strategy.editFlag !== 0
+      })
+
+      const occupiedMonths = new Set()
+      samePriorityStrategies.forEach(strategy => {
+        if (strategy.execMonth) {
+          strategy.execMonth.split(',').forEach(month => {
+            occupiedMonths.add(month)
+          })
+        }
+      })
+
+      const conflictMonths = []
+      this.strategyForm.execMonth.forEach(month => {
+        if (occupiedMonths.has(month)) {
+          const monthLabel = this.monthOptions.find(m => m.value === month)?.label || month
+          conflictMonths.push(monthLabel)
+        }
+      })
+
+      if (conflictMonths.length > 0) {
+        return conflictMonths.join('、')
+      }
+      return null
+    },
+
+    addSub() {
+      this.strategyForm.hourList.push({
+        startTime: '',
+        endTime: '',
+        type: ''
+      })
+    },
+
+    deleteSub(index) {
+      this.strategyForm.hourList.splice(index, 1)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../styles/dialog.scss';
+.sub-table {
+  .el-icon-delete {
+    cursor: pointer;
+  }
+  .operateBtns {
+    cursor: pointer;
+    i {
+      color: #1990ff;
+      margin-left: 5px;
+    }
+  }
+  ::v-deep .el-date-editor {
+    width: 100% !important;
+  }
+}
+
+.custom-checkbox-group {
+  max-height: 120px;
+  overflow-y: auto;
+}
+
+.strategyForm {
+  ::v-deep .el-input.is-disabled .el-input__inner {
+    color: #606266;
+    background: #fff;
+  }
+}
+
+.deleteBtn:disabled {
+  color: #c0c4cc !important;
+  &:hover {
+    color: #c0c4cc !important;
+  }
+}
+</style>

+ 185 - 0
ems-ui-cloud/src/views/basecfg/price/components/PvPrice.vue

@@ -0,0 +1,185 @@
+<template>
+  <div>
+    <el-row :gutter="10" class="mb8">
+      <right-toolbar :showSearch.sync="showSearch" :search="false" @queryTable="getPvPriceList"></right-toolbar>
+    </el-row>
+
+    <el-table v-loading="loading" :data="pvPriceList">
+      <el-table-column label="价格名称" align="center" prop="priceName" />
+      <el-table-column label="用户电价/脱硫煤电价" align="center" prop="objPrice" />
+      <el-table-column label="国家补贴" align="center" prop="stateSubsidy" />
+      <el-table-column label="地方补贴" align="center" prop="localSubsidy" />
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button
+            size="mini"
+            type="text"
+            icon="el-icon-edit"
+            @click="handlePvPriceUpdate(scope.row)"
+            v-hasPermi="['basecfg:price:edit']"
+          >
+            修改
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 添加或修改电价配置对话框 -->
+    <el-dialog :title="title" :visible.sync="pvPriceOpen" width="500px" append-to-body>
+      <el-form ref="pvPriceForm" :model="pvPriceForm" :rules="pvPriceRules" label-width="150px">
+        <el-form-item label="配置代码" prop="cfgCode">
+          <el-input v-model="pvPriceForm.cfgCode" placeholder="请输入配置代码" />
+        </el-form-item>
+        <el-form-item label="价格名称" prop="priceName">
+          <el-input v-model="pvPriceForm.priceName" placeholder="请输入价格名称" />
+        </el-form-item>
+        <el-form-item label="用户/脱硫煤电价" prop="objPrice">
+          <el-input v-model="pvPriceForm.objPrice" placeholder="请输入价格" />
+        </el-form-item>
+        <el-form-item label="国家补贴" prop="stateSubsidy">
+          <el-input v-model="pvPriceForm.stateSubsidy" placeholder="请输入国家补贴" />
+        </el-form-item>
+        <el-form-item label="地方补贴" prop="localSubsidy">
+          <el-input v-model="pvPriceForm.localSubsidy" placeholder="请输入地方补贴" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitPvPriceForm">确 定</el-button>
+        <el-button @click="pvPriceCancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  addPvPriceConfig,
+  delPvPriceConfig,
+  getPvPriceConfig,
+  listPvPriceConfig,
+  updatePvPriceConfig
+} from '@/api/basecfg/elecPvPrice'
+import commonMethods from '../mixins/commonMethods'
+
+export default {
+  name: 'PvPrice',
+  mixins: [commonMethods],
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 光伏电价
+      pvPriceList: [],
+      // 弹出层标题
+      title: '',
+      pvPriceOpen: false,
+      // 查询参数
+      queryPvPriceParams: {
+        pageNum: 1,
+        pageSize: 10
+      },
+      // 表单参数
+      pvPriceForm: {},
+      // 表单校验
+      pvPriceRules: {
+        cfgCode: [{ required: true, message: '配置代码不为空', trigger: 'blur' }],
+        priceName: [{ required: true, message: '价格名称不为空', trigger: 'blur' }]
+      }
+    }
+  },
+  created() {
+    this.getPvPriceList()
+  },
+  methods: {
+    getPvPriceList() {
+      this.loading = true
+      listPvPriceConfig(this.queryPvPriceParams).then(response => {
+        this.pvPriceList = response.rows
+        this.total = response.total
+        this.loading = false
+      })
+    },
+
+    // 取消按钮
+    pvPriceCancel() {
+      this.pvPriceOpen = false
+      this.pvPriceReset()
+    },
+
+    // 表单重置
+    pvPriceReset() {
+      this.pvPriceForm = {
+        id: null,
+        cfgCode: null,
+        priceName: null,
+        objPrice: null,
+        stateSubsidy: null,
+        localSubsidy: null
+      }
+      this.resetForm('pvPriceForm')
+    },
+
+    /** 新增按钮操作 */
+    handlePvPriceAdd() {
+      this.pvPriceReset()
+      this.pvPriceOpen = true
+      this.title = '添加光伏电价配置'
+    },
+
+    /** 修改按钮操作 */
+    handlePvPriceUpdate(row) {
+      this.pvPriceReset()
+      const id = row.id
+      getPvPriceConfig(id).then(response => {
+        this.pvPriceForm = response.data
+        this.pvPriceOpen = true
+        this.title = '修改光伏电价配置'
+      })
+    },
+
+    /** 提交按钮 */
+    submitPvPriceForm() {
+      this.$refs['pvPriceForm'].validate(valid => {
+        if (valid) {
+          if (this.pvPriceForm.id != null) {
+            updatePvPriceConfig(this.pvPriceForm).then(response => {
+              this.$modal.msgSuccess('修改成功')
+              this.pvPriceOpen = false
+              this.getPvPriceList()
+            })
+          } else {
+            addPvPriceConfig(this.pvPriceForm).then(response => {
+              this.$modal.msgSuccess('新增成功')
+              this.pvPriceOpen = false
+              this.getPvPriceList()
+            })
+          }
+        }
+      })
+    },
+
+    /** 删除按钮操作 */
+    handlePvPriceDelete(row) {
+      const ids = row.id
+      this.$modal
+        .confirm('是否确认删除电价配置编号为"' + ids + '"的数据项?')
+        .then(function() {
+          return delPvPriceConfig(ids)
+        })
+        .then(() => {
+          this.getPvPriceList()
+          this.$modal.msgSuccess('删除成功')
+        })
+        .catch(() => {})
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+@import '../styles/dialog.scss';
+</style>

+ 57 - 1764
ems-ui-cloud/src/views/basecfg/price/index.vue

@@ -2,1807 +2,100 @@
   <div class="app-container">
     <el-tabs v-model="activeTab">
       <el-tab-pane label="用电分类" name="first">
-        <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="treeAreaOptions" :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-col :span="20" :xs="24">
-            <el-row :gutter="10" class="mb8">
-              <el-col :span="1.5">
-                <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAttrAdd" v-hasPermi="['basecfg:price:add']">新增
-                </el-button>
-              </el-col>
-            </el-row>
-
-            <el-table v-loading="loading" :data="attrList">
-              <el-table-column label="区域" align="center" prop="areaName"/>
-              <el-table-column label="状态" align="center" width="100">
-                <template slot-scope="scope">
-                  <el-tag :type="scope.$index === attrList.length - 1 ? 'success' : 'info'" disable-transitions>
-                    {{ scope.$index === attrList.length - 1 ? '生效' : '继承' }}
-                  </el-tag>
-                </template>
-              </el-table-column>
-              <el-table-column label="用电分类" align="center" prop="elecTypeName" />
-              <el-table-column label="电压等级" align="center" prop="voltageLevel" />
-              <el-table-column label="容(需)量策略" align="center">
-                <el-table-column label="容(需)量类型" align="center" prop="reqCapacityFlag" :formatter="matchReqCapacityFlag" />
-                <el-table-column label="变压器容量(千伏·安)" align="center" prop="transCapacity" />
-                <el-table-column label="最大需量(千瓦)" align="center" prop="reqQuantity" />
-              </el-table-column>
-              <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-                <template slot-scope="scope">
-                  <!-- 修改按钮 -->
-                  <el-tooltip
-                    effect="dark"
-                    :content="scope.row.areaCode !== currentAreaCode ? '请到上游节点编辑' : '修改'"
-                    placement="top"
-                    :open-delay="0"
-                    :disabled="scope.row.areaCode === currentAreaCode">
-                    <el-button
-                      size="mini"
-                      type="text"
-                      icon="el-icon-edit"
-                      :disabled="scope.row.areaCode !== currentAreaCode"
-                      @click="handleAttrUpdate(scope.row)"
-                      v-hasPermi="['basecfg:price:edit']">
-                      修改
-                    </el-button>
-                  </el-tooltip>
-                  <el-tooltip
-                    effect="dark"
-                    :content="scope.row.areaCode !== currentAreaCode ? '请到上游节点编辑' : '删除'"
-                    placement="top"
-                    :open-delay="0"
-                    :disabled="scope.row.areaCode === currentAreaCode">
-                    <el-button
-                      size="mini"
-                      type="text"
-                      icon="el-icon-delete"
-                      class="deleteBtn"
-                      :disabled="scope.row.areaCode !== currentAreaCode"
-                      @click="handleAttrDelete(scope.row)"
-                      v-hasPermi="['basecfg:price:remove']">
-                      删除
-                    </el-button>
-                  </el-tooltip>
-                </template>
-              </el-table-column>
-            </el-table>
-
-            <pagination v-show="total > 0 || total === 0" :total="total || 0" :page.sync="queryAttrParams.pageNum" :limit.sync="queryAttrParams.pageSize"
-                        @pagination="refreshCurrentAreaData" />
-            <!-- 添加或修改服务区用电属性对话框 -->
-            <el-dialog :title="title" :visible.sync="attrOpen" width="500px" append-to-body>
-              <el-form ref="attrForm" :model="attrForm" :rules="attrRules" label-width="150px">
-                <el-form-item label="园区" prop="areaCode">
-                  <el-input v-model="currentAreaName" disabled placeholder="园区名称" />
-                  <input type="hidden" v-model="attrForm.areaCode" />
-                </el-form-item>
-                <el-form-item label="价格策略" prop="priceCode">
-                  <el-select v-model="attrForm.priceCode">
-                    <el-option v-for="item in gwPriceOptions" :label="`${item.elecTypeName}<${item.voltageLevel}>`" :value="item.cfgCode"
-                               :key="item.cfgCode" />
-                  </el-select>
-                </el-form-item>
-                <el-form-item label="容(需)量类型" prop="reqCapacityFlag">
-                  <el-select v-model="attrForm.reqCapacityFlag">
-                    <el-option v-for="item in reqCapacityOptions" :label="item.name" :value="item.code" :key="item.code" />
-                  </el-select>
-                </el-form-item>
-                <el-form-item label="变压器容量(kVA/月)" prop="transCapacity" v-if="attrForm.reqCapacityFlag === 1">
-                  <el-input v-model="attrForm.transCapacity" placeholder="请输入变压器容量" />
-                </el-form-item>
-                <el-form-item label="最大需量(kW·h/月)" prop="reqQuantity" v-if="attrForm.reqCapacityFlag === 2">
-                  <el-input v-model="attrForm.reqQuantity" placeholder="请输入最大需量" />
-                </el-form-item>
-              </el-form>
-              <div slot="footer" class="dialog-footer">
-                <el-button type="primary" @click="submitAttrForm">确 定</el-button>
-                <el-button @click="attrCancel">取 消</el-button>
-              </div>
-            </el-dialog>
-          </el-col>
-        </el-row>
+        <elec-classify
+          :current-area-code="currentAreaCode"
+          :current-area-name="currentAreaName"
+          @area-selected="handleAreaSelection"
+        />
       </el-tab-pane>
 
       <el-tab-pane label="国网电价" name="second">
-        <el-row :gutter="10" class="mb8">
-          <el-col :span="1.5">
-            <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleGwPriceAdd" v-hasPermi="['basecfg:price:add']">新增
-            </el-button>
-          </el-col>
-          <right-toolbar :showSearch.sync="showSearch" :search="false" @queryTable="getGwPriceList"></right-toolbar>
-        </el-row>
-        <el-table v-loading="loading" :data="gwPriceList">
-          <el-table-column label="用电分类" align="center" prop="elecTypeName" />
-          <el-table-column label="电压等级" align="center" prop="voltageLevel" />
-          <el-table-column label="峰谷电价" align="center">
-            <el-table-column label="深谷" align="center" prop="dvPrice" />
-            <el-table-column label="谷段" align="center" prop="vPrice" />
-            <el-table-column label="平段" align="center" prop="opPrice" />
-            <el-table-column label="峰段" align="center" prop="pPrice" />
-            <el-table-column label="尖峰" align="center" prop="cpPrice" />
-          </el-table-column>
-          <el-table-column label="容(需)量用电价格" align="center">
-            <el-table-column label="最大需量 (元/千瓦时·月)" align="center" prop="maxReqPrice" width="80px" />
-            <el-table-column label="变压器容量(元/千伏安·月)" align="center" prop="transCapacityPrice" width="80px" />
-          </el-table-column>
-          <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-            <template slot-scope="scope">
-              <el-button size="mini" type="text"  icon="el-icon-edit" @click="handleGwPriceUpdate(scope.row)" v-hasPermi="['basecfg:price:edit']">
-                修改</el-button>
-              <el-button size="mini" type="text" icon="el-icon-delete" class="deleteBtn" @click="handleGwPriceDelete(scope.row)" v-hasPermi="['basecfg:price:remove']">
-                删除</el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-
-        <pagination v-show="total>0" :total="total" :page.sync="queryGwPriceParams.pageNum" :limit.sync="queryGwPriceParams.pageSize"
-                    @pagination="getGwPriceList" />
-
-        <!-- 添加或修改电价配置对话框 -->
-        <el-dialog :title="title" :visible.sync="gwPriceCfgOpen" width="500px" append-to-body>
-          <el-form ref="gwPriceForm" :model="gwPriceForm" :rules="gwPriceRules" label-width="150px">
-            <el-form-item label="配置代码" prop="cfgCode">
-              <el-input v-model="gwPriceForm.cfgCode" maxlength="4" placeholder="请输入配置代码" />
-            </el-form-item>
-            <el-form-item label="用电分类" prop="elecType">
-              <el-select v-model="gwPriceForm.elecType">
-                <el-option v-for="item in priceTypeOptions" :label="item.name" :value="item.code" :key="item.code" />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="电压等级" prop="voltageLevel">
-              <el-input v-model="gwPriceForm.voltageLevel" placeholder="请输入电压等级" />
-            </el-form-item>
-            <el-form-item label="深谷价格" prop="dvPrice">
-              <el-input v-model="gwPriceForm.dvPrice" placeholder="请输入深谷价格" />
-            </el-form-item>
-            <el-form-item label="谷段价格" prop="vPrice">
-              <el-input v-model="gwPriceForm.vPrice" placeholder="请输入谷段价格" />
-            </el-form-item>
-            <el-form-item label="平段价格" prop="opPrice">
-              <el-input v-model="gwPriceForm.opPrice" placeholder="请输入平段价格" />
-            </el-form-item>
-            <el-form-item label="峰段价格" prop="pPrice">
-              <el-input v-model="gwPriceForm.pPrice" placeholder="请输入分时峰段价格" />
-            </el-form-item>
-            <el-form-item label="尖峰价格" prop="cpPrice">
-              <el-input v-model="gwPriceForm.cpPrice" placeholder="请输入分时尖时段价格" />
-            </el-form-item>
-
-            <el-form-item label="最大需量用电价格" prop="maxReqPrice" v-if="gwPriceForm.elecType === 4">
-              <el-input v-model="gwPriceForm.maxReqPrice" placeholder="请输入最大需量用电价格" />
-            </el-form-item>
-            <el-form-item label="变压器容量用电价格" prop="transCapacityPrice" v-if="gwPriceForm.elecType === 4">
-              <el-input v-model="gwPriceForm.transCapacityPrice" placeholder="请输入变压器容量用电价格" />
-            </el-form-item>
-          </el-form>
-          <div slot="footer" class="dialog-footer">
-            <el-button type="primary" @click="submitGwPriceForm">确 定</el-button>
-            <el-button @click="gwPriceCancel">取 消</el-button>
-          </div>
-        </el-dialog>
+        <grid-price />
       </el-tab-pane>
-      <el-tab-pane label="光伏电价" name="third">
-        <!-- 光伏电价配置 -->
-        <el-row :gutter="10" class="mb8">
-          <!-- <el-col :span="1.5">
-            <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handlePvPriceAdd" v-hasPermi="['basecfg:price:add']">新增
-            </el-button>
-          </el-col> -->
-          <right-toolbar :showSearch.sync="showSearch" :search="false" @queryTable="getPvPriceList"></right-toolbar>
-        </el-row>
-        <el-table v-loading="loading" :data="pvPriceList">
-          <el-table-column label="价格名称" align="center" prop="priceName" />
-          <el-table-column label="用户电价/脱硫煤电价" align="center" prop="objPrice" />
-          <el-table-column label="国家补贴" align="center" prop="stateSubsidy" />
-          <el-table-column label="地方补贴" align="center" prop="localSubsidy" />
-          <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-            <template slot-scope="scope">
-              <el-button size="mini" type="text"  icon="el-icon-edit" @click="handlePvPriceUpdate(scope.row)" v-hasPermi="['basecfg:price:edit']">
-                修改</el-button>
-
-            </template>
-          </el-table-column>
-        </el-table>
-
-        <!-- 添加或修改电价配置对话框 -->
-        <el-dialog :title="title" :visible.sync="pvPriceOpen" width="500px" append-to-body>
-          <el-form ref="pvPriceForm" :model="pvPriceForm" :rules="pvPriceRules" label-width="150px">
-            <el-form-item label="配置代码" prop="cfgCode">
-              <el-input v-model="pvPriceForm.cfgCode" placeholder="请输入配置代码" />
-            </el-form-item>
-            <el-form-item label="价格名称" prop="priceName">
-              <el-input v-model="pvPriceForm.priceName" placeholder="请输入价格名称" />
-            </el-form-item>
-            <el-form-item label="用户/脱硫煤电价" prop="objPrice">
-              <el-input v-model="pvPriceForm.objPrice" placeholder="请输入价格" />
-            </el-form-item>
-            <el-form-item label="国家补贴" prop="stateSubsidy">
-              <el-input v-model="pvPriceForm.stateSubsidy" placeholder="请输入国家补贴" />
-            </el-form-item>
-            <el-form-item label="地方补贴" prop="localSubsidy">
-              <el-input v-model="pvPriceForm.localSubsidy" placeholder="请输入国家补贴" />
-            </el-form-item>
 
-          </el-form>
-          <div slot="footer" class="dialog-footer">
-            <el-button type="primary" @click="submitPvPriceForm">确 定</el-button>
-            <el-button @click="pvPriceCancel">取 消</el-button>
-          </div>
-        </el-dialog>
+      <el-tab-pane label="光伏电价" name="third">
+        <pv-price />
       </el-tab-pane>
-      <el-tab-pane label="峰谷策略" name="fourth">
-        <el-row :gutter="10" class="mb8">
-          <el-col :span="1.5">
-            <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleStrategyAdd" v-hasPermi="['basecfg:price:add']">新增
-            </el-button>
-          </el-col>
-          <right-toolbar :showSearch.sync="showSearch" :search="false" @queryTable="getStrategyList"></right-toolbar>
-        </el-row>
-        <el-table v-loading="strategyParams.loading" :data="strategyList">
-          <el-table-column label="策略编码" align="center" prop="strategyCode" />
-          <el-table-column label="策略名称" align="center" prop="strategyName" />
-          <el-table-column label="执行月份" align="center" prop="execMonth" show-overflow-tooltip>
-            <template slot-scope="scope">
-              {{formatExecMonth(scope.row.execMonth)}}
-            </template>
-          </el-table-column>
-          <el-table-column label="执行日期" align="center" prop="execDateType">
-            <template slot-scope="scope">
-              {{formatDict(scope.row.execDateType,'execDateTypeOptions')}}
-            </template>
-          </el-table-column>
-          <el-table-column label="策略描述" align="center" prop="strategyDesc" show-overflow-tooltip />
-          <el-table-column label="分时类型" align="center" prop="supportType" show-overflow-tooltip>
-            <template slot-scope="scope">
-              {{formatSupportType(scope.row.supportType)}}
-            </template>
-          </el-table-column>
-          <el-table-column label="优先级" align="center" prop="priority" />
-          <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-            <template slot-scope="scope">
-              <el-button size="mini" type="text" icon="el-icon-info"  v-if="scope.row.editFlag==0" @click="handleStrategyDetail(scope.row)" v-hasPermi="['basecfg:price:edit']">
-                查看</el-button>
-              <el-button size="mini" type="text"  icon="el-icon-edit"  v-else @click="handleStrategyUpdate(scope.row)" v-hasPermi="['basecfg:price:edit']">
-                修改</el-button>
-              <el-button size="mini" type="text" icon="el-icon-delete" class="deleteBtn" @click="handleStrategyDelete(scope.row)" v-hasPermi="['basecfg:price:remove']" :disabled="scope.row.editFlag==0">
-                删除</el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-
-        <!-- 添加或修改峰谷电价策略配置对话框 -->
-        <el-dialog :title="title" :visible.sync="strategyDialog" width="850px" append-to-body>
-          <el-form ref="strategyForm" class="strategyForm" :model="strategyForm" :disabled="ifDisabled" label-width="100px">
-            <el-form-item label="策略编码" prop="strategyCode" required :rules="[{required:true,message:'策略编码不能为空'}]">
-              <el-input v-model="strategyForm.strategyCode" placeholder="请输入策略编码" />
-            </el-form-item>
-            <el-form-item label="策略名称" prop="strategyName" required :rules="[{required:true,message:'策略名称不能为空'}]">
-              <el-input v-model="strategyForm.strategyName" placeholder="请输入策略名称" />
-            </el-form-item>
-            <el-form-item label="执行日期" prop="execDateType" required :rules="[{required:true,message:'请选择执行日期'}]">
-              <el-select v-model="strategyForm.execDateType" style="width:100%" placeholder="请选择执行日期" @change="execDateTypeChange">
-                <el-option v-for="item in execDateTypeOptions" :label="item.name" :value="item.value" :key="item.value" />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="执行说明" required :rules="[{required:true,message:'请选择执行说明'}]" prop="execDate"
-                          v-if="[1,5,6].includes(strategyForm.execDateType)">
-              <el-time-picker v-if="strategyForm.execDateType==1" v-model="strategyForm.execDate" value-format="HH:mm:ss"
-                              :picker-options="{selectableRange: '00:00:00 - 23:59:59'}" placeholder="选择时间">
-              </el-time-picker>
-              <el-checkbox-group v-else-if="strategyForm.execDateType==5" v-model="strategyForm.execDate">
-                <el-checkbox v-for="day in weeks.filter(item=>item.value<6)"  :label="day.value" :key="day.value">
-                  {{day.name}}</el-checkbox>
-              </el-checkbox-group>
-              <el-checkbox-group v-else-if="strategyForm.execDateType==6" v-model="strategyForm.execDate">
-                <el-checkbox v-for="day in weeks" :label="day.value" :key="day.value">{{day.name}}</el-checkbox>
-              </el-checkbox-group>
-            </el-form-item>
-            <el-form-item label="策略描述" prop="strategyDesc">
-              <el-input v-model="strategyForm.strategyDesc" placeholder="请输入策略描述" />
-            </el-form-item>
-            <el-form-item label="执行月份" prop="execMonth" required :rules="[{required:true,message:'请选择执行月份'}]">
-              <el-checkbox-group v-model="strategyForm.execMonth">
-                <el-tooltip
-                  v-for="month in availableMonths"
-                  :key="month.value"
-                  :content="month.disabled ? '已占用' : ''"
-                  :disabled="!month.disabled"
-                  placement="top"
-                  effect="dark">
-                  <el-checkbox
-                    :label="month.value"
-                    :disabled="month.disabled">
-                    <span :style="{color: month.disabled ? '#c0c4cc' : ''}">
-                      {{month.label}}
-                     </span>
-                  </el-checkbox>
-                </el-tooltip>
-              </el-checkbox-group>
-              <div v-if="monthConflictTip" style="color: #f56c6c; font-size: 12px; margin-top: 5px;">
-                {{ monthConflictTip }}
-              </div>
-            </el-form-item>
-            <el-form-item label="分时类型" prop="supportType" required :rules="[{required:true,message:'请选择分时类型'}]">
-              <el-checkbox-group v-model="strategyForm.supportType" @change="handleSupportTypeChange">
-                <el-checkbox v-for="type in timeTypeOptions"
-                             :label="type.value"
-                             :key="type.value"
-                             :disabled="type.value === 0">
-                  {{type.label}}
-                </el-checkbox>
-              </el-checkbox-group>
-            </el-form-item>
-            <el-form-item label="优先级" prop="priority" required>
-              <el-input-number v-model="strategyForm.priority"
-                               :min="0"
-                               :max="100"
-                               @change="handlePriorityChange"
-                               label="描述文字"></el-input-number>
-              <span style="margin-left: 10px; color: #909399; font-size: 12px;">
-                相同优先级的策略月份不能重叠
-              </span>
-            </el-form-item>
-            <el-form-item label="小时电价">
-              <el-table class="sub-table" :data="strategyForm.hourList" max-height="200px">
-                <el-table-column label="开始时刻" align="center" prop="startTime">
-                  <template slot-scope="scope">
-                    <el-time-picker v-model="scope.row.startTime" size="mini" value-format="HH:mm:ss"
-                                    :picker-options="{selectableRange: '00:00:00 - 23:59:59'}" placeholder="选择时间">
-                    </el-time-picker>
-                  </template>
-                </el-table-column>
-                <el-table-column label="结束时刻" align="center" prop="endTime">
-                  <template slot-scope="scope">
-                    <el-time-picker v-model="scope.row.endTime" size="mini" value-format="HH:mm:ss"
-                                    :picker-options="{selectableRange: '00:00:00 - 23:59:59'}" placeholder="选择时间">
-                    </el-time-picker>
-                  </template>
-                </el-table-column>
-                <el-table-column label="分时类型" align="center" prop="type">
-                  <template slot-scope="scope">
-                    <el-select v-model="scope.row.type" size="mini" style="width:100%" placeholder="请选择">
-                      <el-option v-for="type in filteredTimeTypes"
-                                 :label="type.label"
-                                 :value="type.value"
-                                 :key="type.value" />
-                    </el-select>
-                  </template>
-                </el-table-column>
-                <el-table-column align="center" width="100" v-if="!ifDisabled">
-                  <template slot="header">
-                    <div class="operateBtns" @click="addSub">
-                      <span>操作</span><i class="el-icon-circle-plus-outline"></i>
-                    </div>
-                  </template>
-                  <template slot-scope="scope">
-                    <i class="el-icon-delete" @click="deleteSub(scope.$index)"></i>
-                  </template>
-                </el-table-column>
-              </el-table>
-            </el-form-item>
 
-          </el-form>
-          <div slot="footer" class="dialog-footer" v-if="!ifDisabled">
-            <el-button type="primary" @click="submitStrategyForm">确 定</el-button>
-            <el-button @click="strategyCancel">取 消</el-button>
-          </div>
-        </el-dialog>
+      <el-tab-pane label="峰谷策略" name="fourth">
+        <peak-valley-strategy />
       </el-tab-pane>
-      <el-tab-pane label="非电价格" name="five">
-        <el-form :model="fdPriceQueryParams" ref="fdPriceQueryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
-          <el-form-item label="计量介质" prop="meterCls">
-            <el-select
-              v-model="fdPriceQueryParams.meterCls"
-              placeholder="请选择计量介质"
-              clearable
-              @change="handleFdPriceQuery"
-            >
-              <el-option
-                v-for="item in meterClsOptions"
-                :key="item.value"
-                :label="item.label"
-                :value="item.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item>
-            <el-button type="primary" icon="el-icon-search" size="mini" @click="handleFdPriceQuery">搜索</el-button>
-            <el-button icon="el-icon-refresh" size="mini" @click="resetFdPriceQuery">重置</el-button>
-          </el-form-item>
-        </el-form>
-
-        <el-table v-loading="fdPriceLoading" :data="fdPriceList" @selection-change="handleFdPriceSelectionChange">
-          <el-table-column label="区域" align="center">
-            <template slot-scope="scope">
-              {{ scope.row.areaName || '默认' }}
-            </template>
-          </el-table-column>
-          <el-table-column label="计量介质" align="center" prop="meterClsName" />
-          <el-table-column label="单位价格" align="center" prop="unitPrice" />
-          <el-table-column label="计量单位" align="center" prop="meterUnit" />
-          <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-            <template slot-scope="scope">
-              <el-button
-                size="mini"
-                type="text"
-                icon="el-icon-edit"
-                @click="handleFdPriceUpdate(scope.row)"
-                v-hasPermi="['ems:FdEnergyPriceConfig:edit']"
-              >修改</el-button>
-            </template>
-          </el-table-column>
-        </el-table>
 
-        <pagination
-          v-show="fdPriceTotal>0"
-          :total="fdPriceTotal"
-          :page.sync="fdPriceQueryParams.pageNum"
-          :limit.sync="fdPriceQueryParams.pageSize"
-          @pagination="getFdPriceList"
-        />
-
-        <!-- 添加或修改非电能源价格配置对话框 -->
-        <el-dialog :title="fdPriceTitle" :visible.sync="fdPriceOpen" width="500px" append-to-body>
-          <el-form ref="fdPriceForm" :model="fdPriceForm" :rules="fdPriceRules" label-width="80px">
-            <el-form-item label="配置代码" prop="cfgCode">
-              <el-input v-model="fdPriceForm.cfgCode" placeholder="请输入配置代码" disabled />
-            </el-form-item>
-            <el-form-item label="区域" prop="areaName">
-              <el-input v-model="fdPriceForm.areaName" placeholder="园区名称" disabled />
-            </el-form-item>
-            <el-form-item label="计量介质" prop="meterClsName">
-              <el-input v-model="fdPriceForm.meterClsName" placeholder="计量介质" disabled />
-            </el-form-item>
-            <el-form-item label="单位价格" prop="unitPrice">
-              <el-input v-model="fdPriceForm.unitPrice" placeholder="请输入单位价格" />
-            </el-form-item>
-            <el-form-item label="计量单位" prop="meterUnit">
-              <el-input v-model="fdPriceForm.meterUnit" placeholder="请输入计量单位" />
-            </el-form-item>
-          </el-form>
-          <div slot="footer" class="dialog-footer">
-            <el-button type="primary" @click="submitFdPriceForm">确 定</el-button>
-            <el-button @click="fdPriceCancel">取 消</el-button>
-          </div>
-        </el-dialog>
+      <el-tab-pane label="非电价格" name="five">
+        <non-elec-price />
       </el-tab-pane>
-      <el-tab-pane label="商户计价" name="six">
-        <!-- 商户计费配置 -->
-        <el-row :gutter="10" class="mb8">
-          <!-- <el-col :span="1.5">
-            <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleChargingAdd" v-hasPermi="['basecfg:price:add']">新增
-            </el-button>
-          </el-col> -->
-          <right-toolbar :showSearch.sync="showSearch" :search="false" @queryTable="getChargingList"></right-toolbar>
-        </el-row>
-        <el-table v-loading="chargingParams.loading" :data="chargingList">
-          <el-table-column label="区域" align="center" prop="areaName" />
-          <el-table-column label="电计量单价" align="center" prop="elecUnitPrice" />
-          <el-table-column label="电公摊计算类型" align="center">
-            <template slot-scope="scope">
-              {{formatDict(scope.row.elecGtComputeType,'computeTypeOptions')}}
-            </template>
-          </el-table-column>
-          <el-table-column label="电费计算说明" align="center" prop="elecComputeDesc" show-overflow-tooltip />
-          <el-table-column label="水计量单价" align="center" prop="waterUnitPrice" />
-          <el-table-column label="水公摊计量类型" align="center">
-            <template slot-scope="scope">
-              {{formatDict(scope.row.waterGtComputeType,'computeTypeOptions')}}
-            </template>
-          </el-table-column>
-          <el-table-column label="水费说明" align="center" prop="waterComputeDesc" show-overflow-tooltip />
-          <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-            <template slot-scope="scope">
-              <el-button size="mini" type="text"  icon="el-icon-edit" @click="handleChargingUpdate(scope.row)" v-hasPermi="['basecfg:price:edit']">
-                修改</el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-
-        <!-- 添加或修改对话框 -->
-        <el-dialog :title="title" :visible.sync="chargingDialog" custom-class="custom-dialog" width="650px" append-to-body>
-          <el-form ref="chargingForm" :model="chargingForm" label-width="120px">
-            <el-form-item label="服务区名称" prop="areaCode" required :rules="[{required:true,message:'请选择服务区名称'}]">
-              <el-select v-model="chargingForm.areaCode" disabled style="width:100%">
-                <el-option v-for="item in areaOptions" :label="item.label" :value="item.id" :key="item.id" />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="电计量单价" prop="elecUnitPrice" required :rules="[{required:true,message:'电计量单价不能为空'}]">
-              <el-input v-model="chargingForm.elecUnitPrice" oninput="value=value.replace(/[^\d.]/g,'')" placeholder="请输入电计量单价" />
-            </el-form-item>
-            <el-form-item label="电公摊计算类型" prop="elecGtComputeType" required :rules="[{required:true,message:'请选择电公摊计算类型'}]">
-              <el-select v-model="chargingForm.elecGtComputeType" style="width:100%">
-                <el-option v-for="item in computeTypeOptions" :label="item.name" :value="item.value" :key="item.value" />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="电费计算说明" prop="elecComputeDesc">
-              <el-input v-model="chargingForm.elecComputeDesc" placeholder="请输入电费计算说明" />
-            </el-form-item>
-            <el-form-item label="水计量单价" prop="waterUnitPrice" required :rules="[{required:true,message:'水计量单价不能为空'}]">
-              <el-input v-model="chargingForm.waterUnitPrice" oninput="value=value.replace(/[^\d.]/g,'')" placeholder="请输入水计量单价" />
-            </el-form-item>
-            <el-form-item label="水公摊计算类型" prop="waterGtComputeType" required :rules="[{required:true,message:'请选择水公摊计算类型'}]">
-              <el-select v-model="chargingForm.waterGtComputeType" style="width:100%">
-                <el-option v-for="item in computeTypeOptions" :label="item.name" :value="item.value" :key="item.value" />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="水费说明" prop="waterComputeDesc">
-              <el-input v-model="chargingForm.waterComputeDesc" placeholder="请输入水费说明" />
-            </el-form-item>
-            <el-form-item label="关联商户" prop="waterComputeDesc">
-              <el-checkbox-group v-model="chargingForm.relCoList" class="custom-checkbox-group">
-                <el-checkbox v-for="item in zoningList" :key="item.areaCode" :label="item.areaCode">{{item.areaName}}</el-checkbox>
-              </el-checkbox-group>
-            </el-form-item>
 
-          </el-form>
-          <div slot="footer" class="dialog-footer">
-            <el-button type="primary" @click="submitChargingForm">确 定</el-button>
-            <el-button @click="chargingCancel">取 消</el-button>
-          </div>
-        </el-dialog>
+      <el-tab-pane label="商户计价" name="six">
+        <merchant-charging />
       </el-tab-pane>
     </el-tabs>
-
   </div>
 </template>
 
 <script>
-import {
-  listAttr,
-  getAttr,
-  delAttr,
-  addAttr,
-  updateAttr,
-  listStrategy,
-  addStrategy,
-  updateStrategy,
-  delStrategy,
-  listCharging,
-  updateCharging,
-  addCharging,
-  delCharging,
-  getEffectiveByArea,
-  getEffectiveListByArea
-} from '@/api/basecfg/elecAttr'
-import {
-  listGwPriceConfig,
-  listGwPriceConfigAll,
-  getGwPriceConfig,
-  addGwPriceConfig,
-  delGwPriceConfig,
-  updateGwPriceConfig
-} from '@/api/basecfg/elecGwPrice'
-import { addPvPriceConfig, delPvPriceConfig, getPvPriceConfig, listPvPriceConfig, updatePvPriceConfig } from '@/api/basecfg/elecPvPrice'
-import { listFdEnergyPriceConfig, getFdEnergyPriceConfig, delFdEnergyPriceConfig, addFdEnergyPriceConfig, updateFdEnergyPriceConfig } from '@/api/basecfg/FdEnergyPriceConfig'
-import { getElecPriceType } from '@/api/commonApi'
-import { areaListByTag, areaTreeSelect } from '@/api/basecfg/area'
+// 引入各个子组件
+import ElecClassify from './components/ElecClassify'
+import GridPrice from './components/GridPrice'
+import PvPrice from './components/PvPrice'
+import PeakValleyStrategy from './components/PeakValleyStrategy'
+import NonElecPrice from './components/NonElecPrice'
+import MerchantCharging from './components/MerchantCharging'
 
 export default {
-  name: 'Attr',
+  name: 'PriceConfig',
+  components: {
+    ElecClassify,
+    GridPrice,
+    PvPrice,
+    PeakValleyStrategy,
+    NonElecPrice,
+    MerchantCharging
+  },
   data() {
     return {
       activeTab: 'first',
-      // 遮罩层
-      loading: true,
-      // 选中数组
-      ids: [],
-      // 非单个禁用
-      single: true,
-      // 非多个禁用
-      multiple: true,
-      // 显示搜索条件
-      showSearch: true,
-      // 总条数
-      total: 0,
-      // 服务区用电属性表格数据
-      attrList: [],
-      // 电价表格
-      gwPriceList: [],
-      // 光伏电价
-      pvPriceList: [],
-      // 峰谷电价
-      strategyList: [],
-      // 商户计费
-      chargingList: [],
-      // 非电价格
-      fdPriceList: [],
-      // 弹出层标题
-      title: '',
-      fdPriceTitle: '',
-
-      // 区域名称
-      areaName: undefined,
-      // 区域树选项
-      treeAreaOptions: undefined,
-      defaultProps: {
-        children: 'children',
-        label: 'label'
-      },
-      // 当前选中的区域代码
+      // 共享的数据
       currentAreaCode: null,
-      // 当前选中的区域名称
-      currentAreaName: '',
-      // 是否显示弹出层
-      attrOpen: false,
-      gwPriceCfgOpen: false,
-      pvPriceOpen: false,
-      strategyDialog: false,
-      chargingDialog: false,
-      fdPriceOpen: false,
-      // 选项定义
-      priceTypeOptions: undefined,
-      areaOptions: undefined,
-      gwPriceOptions: undefined,
-      // 计量介质选项
-      meterClsOptions: [
-        { value: 70, label: '自来水' }
-      ],
-      // 查询参数
-      queryGwPriceParams: {
-        pageNum: 1,
-        pageSize: 10
-      },
-      queryAttrParams: {
-        pageNum: 1,
-        pageSize: 10
-      },
-      queryPvPriceParams: {
-        pageNum: 1,
-        pageSize: 10
-      },
-      strategyParams: {
-        loading: false,
-        pageNum: 1,
-        pageSize: 10
-      },
-      chargingParams: {
-        loading: false,
-        pageNum: 1,
-        pageSize: 10
-      },
-      // 非电价格查询参数
-      fdPriceQueryParams: {
-        pageNum: 1,
-        pageSize: 10,
-        cfgCode: null,
-        areaCode: null,
-        meterCls: null
-      },
-      // 非电价格表单数据
-      fdPriceForm: {},
-      // 非电价格表单验证规则
-      fdPriceRules: {
-        cfgCode: [
-          { required: true, message: '配置代码不能为空', trigger: 'blur' }
-        ],
-        meterCls: [
-          { required: true, message: '计量介质不能为空', trigger: 'blur' }
-        ],
-        unitPrice: [
-          { required: true, message: '单位价格不能为空', trigger: 'blur' }
-        ]
-      },
-      // 非电价格加载状态
-      fdPriceLoading: true,
-      // 非电价格总条数
-      fdPriceTotal: 0,
-      // 非电价格选中数组
-      fdPriceIds: [],
-      // 非电价格非单个禁用
-      fdPriceSingle: true,
-      // 非电价格非多个禁用
-      fdPriceMultiple: true,
-      // 表单参数
-      attrForm: {},
-      gwPriceForm: {},
-      pvPriceForm: {},
-      // 表单校验
-      gwPriceRules: {
-        cfgCode: [{ required: true, message: '配置代码不为空', trigger: 'blur' }],
-        elecType: [{ required: true, message: '请选择用电分类', trigger: 'change' }],
-        voltageLevel: [{ required: true, message: '电压等级不为空', trigger: 'blur' }],
-        degreePrice: [{ required: true, message: '单位电价不为控', trigger: 'blur' }]
-      },
-      // 表单校验
-      attrRules: {
-        areaCode: [{ required: true, message: '园区代码不能为空', trigger: 'blur' }],
-        elecType: [{ required: true, message: '请选择用电分类', trigger: 'change' }],
-        priceCode: [{ required: true, message: '价格编码不能为空', trigger: 'blur' }]
-      },
-      // 表单校验
-      pvPriceRules: {
-        cfgCode: [{ required: true, message: '配置代码不为空', trigger: 'blur' }],
-        priceName: [{ required: true, message: '价格名称不为空', trigger: 'blur' }]
-      },
-      reqCapacityOptions: [
-        { code: 0, name: '不涉及' },
-        { code: 1, name: '容量电价' },
-        { code: 2, name: '需量电价' }
-      ],
-      // 执行日期选项(原repeatTypeOptions)
-      execDateTypeOptions: [
-        // { name: '执行1次', value: 1 },
-        { name: '每天', value: 2 },
-        // { name: '法定工作日(跳过节假日)', value: 3 },
-        // { name: '法定节假日(跳过工作日)', value: 4 },
-        { name: '周一至周五', value: 5 },
-        { name: '自定义', value: 6 }
-      ],
-      weeks: [
-        { name: '周一', value: '1' },
-        { name: '周二', value: '2' },
-        { name: '周三', value: '3' },
-        { name: '周四', value: '4' },
-        { name: '周五', value: '5' },
-        { name: '周六', value: '6' },
-        { name: '周日', value: '7' }
-      ],
-      // 新增月份选项
-      monthOptions: [
-        { value: '01', label: '一月' },
-        { value: '02', label: '二月' },
-        { value: '03', label: '三月' },
-        { value: '04', label: '四月' },
-        { value: '05', label: '五月' },
-        { value: '06', label: '六月' },
-        { value: '07', label: '七月' },
-        { value: '08', label: '八月' },
-        { value: '09', label: '九月' },
-        { value: '10', label: '十月' },
-        { value: '11', label: '十一月' },
-        { value: '12', label: '十二月' }
-      ],
-      // 新增分时类型选项
-      timeTypeOptions: [
-        { value: -2, label: '低谷' },
-        { value: -1, label: '谷段' },
-        { value: 0, label: '平段' }, // 默认选中且不可取消
-        { value: 1, label: '峰段' },
-        { value: 2, label: '尖峰' }
-      ],
-      strategyForm: {
-        strategyCode: '',
-        strategyName: '',
-        strategyDesc: '',
-        execDateType: '',  // 原repeatType
-        execDate: [],      // 原repeatParam
-        execMonth: [],     // 新增执行月份
-        supportType: [0],  // 新增分时类型,默认包含平段
-        priority: 0,
-        hourList: []
-      },
-      computeTypeOptions: [
-        { name: '不计入', value: 0 },
-        { name: '户数均摊', value: 1 },
-        { name: '面积均摊', value: 2 }
-      ],
-      chargingForm: {
-        areaCode: '',
-        elecUnitPrice: '',
-        elecGtComputeType: '',
-        elecComputeDesc: '',
-        waterUnitPrice: '',
-        waterGtComputeType: '',
-        waterComputeDesc: '',
-        relCoList: []
-      },
-      zoningList: [],
-      ifDisabled: false
+      currentAreaName: ''
     }
   },
-  computed: {
-    // 根据选中的分时类型过滤小时电价表格中的选项
-    filteredTimeTypes() {
-      return this.timeTypeOptions.filter(type =>
-        this.strategyForm.supportType.includes(type.value)
-      );
-    },
-    // 获取可用月份(动态禁用已被占用的月份)
-    availableMonths() {
-      const currentPriority = this.strategyForm.priority
-      const samePriorityStrategies = this.strategyList.filter(strategy => {
-        return strategy.priority === currentPriority &&
-          strategy.id !== this.strategyForm.id &&
-          strategy.editFlag !== 0
-      })
-
-      const occupiedMonths = new Set()
-      samePriorityStrategies.forEach(strategy => {
-        if (strategy.execMonth) {
-          strategy.execMonth.split(',').forEach(month => {
-            occupiedMonths.add(month)
-          })
-        }
-      })
-
-      return this.monthOptions.map(month => ({
-        ...month,
-        disabled: occupiedMonths.has(month.value)
-      }))
-    },
-    // 月份冲突提示
-    monthConflictTip() {
-      const currentPriority = this.strategyForm.priority
-      const samePriorityStrategies = this.strategyList.filter(strategy => {
-        return strategy.priority === currentPriority &&
-          strategy.id !== this.strategyForm.id &&
-          strategy.editFlag !== 0
-      })
-
-      if (samePriorityStrategies.length > 0) {
-        const strategies = samePriorityStrategies.map(s => s.strategyName).join('、')
-        return `提示:优先级${currentPriority}下已存在策略:${strategies}`
-      }
-      return ''
-    }
-  },
-  created() {
-    this.getAreaList('0', 1)
-    this.getAreaTree('0', 2)
-    this.getAttrList()
-    this.getGwPriceList()
-    this.getPvPriceList()
-    this.getPriceType()
-    this.getStrategyList()
-    this.getChargingList()
-    this.getRelateShopList('0')
-    this.getFdPriceList()
-  },
   methods: {
-    /** 查询区域树结构 */
-    getAreaTree(areaCode, layer) {
-      areaTreeSelect(areaCode, layer).then(response => {
-        this.treeAreaOptions =  response.data
-        // 自动选中并触发点击第一个节点
-        if (response.data && response.data.length > 0) {
-          const firstNode = response.data[0];
-          this.$refs.tree.setCurrentKey(firstNode.id); // 设置当前选中节点
-          this.handleNodeClick(firstNode); // 触发点击事件
-        }
-      });
-    },
-    // 筛选节点
-    filterNode(value, data) {
-      if (!value) return true
-      return data.label.indexOf(value) !== -1
-    },
-    // 节点单击事件
-    handleNodeClick(data) {
-      this.currentAreaCode = data.id; // 更新当前选中的区域代码
-      this.currentAreaName = data.label; // 更新当前选中的区域名称
-      this.attrForm.areaCode = data.id; // 确保 attrForm.areaCode 被赋值
-      console.log("data",data)
-      this.refreshCurrentAreaData();
-    },
-    refreshCurrentAreaData() {
-      if (this.currentAreaCode) {
-        this.loading = true;
-        getEffectiveListByArea(this.currentAreaCode).then(res => {
-          this.attrList = res.data;
-          this.loading = false;
-        }).catch(error => {
-          console.error("Error fetching data:", error);
-          this.loading = false;
-        });
-      }
-    },
-    formatDict(value, options, key = 'value', name = 'name') {
-      let text = ''
-      this[options].forEach(item => {
-        if (value === item[key]) {
-          text = item[name]
-        }
-      })
-      return text
-    },
-    // 新增分时类型格式化方法
-    formatSupportType(supportType) {
-      if (!supportType) return '';
-      const types = supportType.split(',').map(v => parseInt(v));
-      const labels = types.map(type => {
-        const found = this.timeTypeOptions.find(opt => opt.value === type);
-        return found ? found.label : '';
-      }).filter(label => label);
-      return labels.join('、');
-    },
-    // 新增执行月份格式化方法
-    formatExecMonth(execMonth) {
-      if (!execMonth) return '';
-      const months = execMonth.split(',');
-      const labels = months.map(month => {
-        const found = this.monthOptions.find(opt => opt.value === month);
-        return found ? found.label : '';
-      }).filter(label => label);
-      return labels.join('、');
-    },
-    // 处理分时类型变化
-    handleSupportTypeChange(values) {
-      // 确保平段始终被选中
-      if (!values.includes(0)) {
-        values.push(0);
-      }
-    },
-    // 处理优先级变化
-    handlePriorityChange(value) {
-      // 当优先级改变时,清空月份选择中的冲突月份
-      const availableMonthValues = this.availableMonths
-        .filter(m => !m.disabled)
-        .map(m => m.value)
-
-      // 过滤掉不可用的月份
-      this.strategyForm.execMonth = this.strategyForm.execMonth.filter(month =>
-        availableMonthValues.includes(month)
-      )
-    },
-    /** 查询非电能源价格配置列表 */
-    getFdPriceList() {
-      this.fdPriceLoading = true;
-      listFdEnergyPriceConfig(this.fdPriceQueryParams).then(response => {
-        this.fdPriceList = response.rows;
-        this.fdPriceTotal = response.total;
-        this.fdPriceLoading = false;
-      });
-    },
-    // 非电价格取消按钮
-    fdPriceCancel() {
-      this.fdPriceOpen = false;
-      this.fdPriceReset();
-    },
-    // 非电价格表单重置
-    fdPriceReset() {
-      this.fdPriceForm = {
-        id: null,
-        cfgCode: null,
-        areaCode: null,
-        meterCls: null,
-        unitPrice: null,
-        meterUnit: null
-      };
-      this.resetForm("fdPriceForm");
-    },
-    /** 非电价格搜索按钮操作 */
-    handleFdPriceQuery() {
-      this.fdPriceQueryParams.pageNum = 1;
-      this.getFdPriceList();
-    },
-    /** 非电价格重置按钮操作 */
-    resetFdPriceQuery() {
-      this.resetForm("fdPriceQueryForm");
-      this.handleFdPriceQuery();
-    },
-    // 非电价格多选框选中数据
-    handleFdPriceSelectionChange(selection) {
-      this.fdPriceIds = selection.map(item => item.id)
-      this.fdPriceSingle = selection.length !== 1
-      this.fdPriceMultiple = !selection.length
-    },
-    /** 非电价格新增按钮操作 */
-    handleFdPriceAdd() {
-      this.fdPriceReset();
-      this.fdPriceOpen = true;
-      this.fdPriceTitle = "添加非电能源价格配置";
-    },
-    /** 非电价格修改按钮操作 */
-    handleFdPriceUpdate(row) {
-      this.fdPriceReset();
-      const id = row.id || this.fdPriceIds
-      getFdEnergyPriceConfig(id).then(response => {
-        this.fdPriceForm = response.data;
-        // 设置园区名称和计量介质名称
-        this.fdPriceForm.areaName = response.data.areaName || '默认';
-        // 从meterClsOptions中查找计量介质名称
-        const meterClsOption = this.meterClsOptions.find(item => item.value === response.data.meterCls);
-        this.fdPriceForm.meterClsName = meterClsOption ? meterClsOption.label : '';
-        this.fdPriceOpen = true;
-        this.fdPriceTitle = "修改非电能源价格配置";
-      });
-    },
-    /** 非电价格提交按钮 */
-    submitFdPriceForm() {
-      this.$refs["fdPriceForm"].validate(valid => {
-        if (valid) {
-          if (this.fdPriceForm.id != null) {
-            updateFdEnergyPriceConfig(this.fdPriceForm).then(response => {
-              this.$modal.msgSuccess("修改成功");
-              this.fdPriceOpen = false;
-              this.getFdPriceList();
-            });
-          } else {
-            addFdEnergyPriceConfig(this.fdPriceForm).then(response => {
-              this.$modal.msgSuccess("新增成功");
-              this.fdPriceOpen = false;
-              this.getFdPriceList();
-            });
-          }
-        }
-      });
-    },
-    /** 非电价格删除按钮操作 */
-    handleFdPriceDelete(row) {
-      const ids = row.id || this.fdPriceIds;
-      this.$modal.confirm('是否确认删除非电能源价格配置编号为"' + ids + '"的数据项?').then(function() {
-        return delFdEnergyPriceConfig(ids);
-      }).then(() => {
-        this.getFdPriceList();
-        this.$modal.msgSuccess("删除成功");
-      }).catch(() => {});
-    },
-    /** 非电价格导出按钮操作 */
-    handleFdPriceExport() {
-      this.download('ems/FdEnergyPriceConfig/export', {
-        ...this.fdPriceQueryParams
-      }, `FdEnergyPriceConfig_${new Date().getTime()}.xlsx`)
-    },
-    getGwPriceList() {
-      this.loading = true
-      listGwPriceConfig(this.queryGwPriceParams).then(response => {
-        this.gwPriceList = response.rows
-        this.total = response.total
-        this.loading = false
-      })
-    },
-    // 取消按钮
-    gwPriceCancel() {
-      this.gwPriceCfgOpen = false
-      this.gwPriceReset()
-    },
-    // 表单重置
-    gwPriceReset() {
-      this.gwPriceForm = {
-        id: null,
-        cfgCode: null,
-        elecType: null,
-        voltageLevel: null,
-        dvPrice: null,
-        vPrice: null,
-        opPrice: null,
-        pPrice: null,
-        cpPrice: null,
-        maxReqPrice: null,
-        transCapacityPrice: null
-      }
-      this.resetForm('gwPriceForm')
-    },
-    /** 新增按钮操作 */
-    handleGwPriceAdd() {
-      this.gwPriceReset()
-      this.gwPriceCfgOpen = true
-      this.title = '添加电价配置'
-    },
-    /** 修改按钮操作 */
-    handleGwPriceUpdate(row) {
-      this.gwPriceReset()
-      const id = row.id || this.ids
-      getGwPriceConfig(id).then(response => {
-        this.gwPriceForm = response.data
-        this.gwPriceCfgOpen = true
-        this.title = '修改电价配置'
-      })
-    },
-    /** 提交按钮 */
-    submitGwPriceForm() {
-      this.$refs['gwPriceForm'].validate(valid => {
-        if (valid) {
-          if (this.gwPriceForm.id != null) {
-            updateGwPriceConfig(this.gwPriceForm).then(response => {
-              this.$modal.msgSuccess('修改成功')
-              this.gwPriceCfgOpen = false
-              this.getGwPriceList()
-            })
-          } else {
-            addGwPriceConfig(this.gwPriceForm).then(response => {
-              this.$modal.msgSuccess('新增成功')
-              this.gwPriceCfgOpen = false
-              this.getGwPriceList()
-            })
-          }
-        }
-      })
-    },
-    /** 删除按钮操作 */
-    handleGwPriceDelete(row) {
-      const ids = row.id || this.ids
-      this.$modal
-        .confirm('是否确认删除电价配置编号为"' + ids + '"的数据项?')
-        .then(function() {
-          return delGwPriceConfig(ids)
-        })
-        .then(() => {
-          this.getGwPriceList()
-          this.$modal.msgSuccess('删除成功')
-        })
-        .catch(() => {})
-    },
-    /** 查询服务区用电属性列表 */
-    getAttrList() {
-      this.loading = true
-      listAttr(this.queryAttrParams).then(response => {
-        this.attrList = response.rows
-        this.total = response.total !== undefined ? response.total : 0; // 提供默认值0
-        this.loading = false
-      })
-    },
-    // 取消按钮
-    attrCancel() {
-      this.attrOpen = false
-      this.attrReset()
-    },
-    // 表单重置
-    attrReset() {
-      this.attrForm = {
-        id: null,
-        areaCode: null,
-        elecType: null,
-        priceCode: null,
-        reqCapacityFlag: 0,
-        transCapacity: null,
-        reqQuantity: null,
-        createTime: null,
-        updateTime: null
-      }
-      this.resetForm('attrForm')
-    },
-    /** 搜索按钮操作 */
-    handleAttrQuery() {
-      this.queryAttrParams.pageNum = 1
-      this.getAttrList()
-    },
-    handleGwPriceQuery() {
-      this.queryGwPriceParams.pageNum = 1
-      this.getGwPriceList()
-    },
-    /** 重置按钮操作 */
-    resetQuery() {
-      this.handleAttrQuery()
-      this.handleGwPriceQuery()
-    },
-    /** 新增按钮操作 */
-    handleAttrAdd() {
-      this.attrReset()
-      this.getGwPriceAll()
-      this.attrForm.areaCode = this.currentAreaCode;
-      this.attrOpen = true
-      this.title = '添加服务区用电属性'
-    },
-    /** 修改按钮操作 */
-    handleAttrUpdate(row) {
-      this.attrReset()
-      this.getGwPriceAll()
-      const id = row.id || this.ids
-      getAttr(id).then(response => {
-        this.attrForm = response.data
-        this.attrOpen = true
-        this.title = '修改服务区用电属性'
-      })
-    },
-    /** 提交按钮 */
-    submitAttrForm() {
-      this.$refs['attrForm'].validate(valid => {
-        if (valid) {
-          console.log("Submitting form with areaCode:", this.attrForm.areaCode);
-          // 排除当前正在修改的园区
-          if(this.attrList.some(item => item.areaCode === this.attrForm.areaCode && item.id !== this.attrForm.id)){
-            return this.$modal.msgError('当前园区已存在')
-          }
-          if (this.attrForm.id != null) {
-            updateAttr(this.attrForm).then(response => {
-              this.$modal.msgSuccess('修改成功')
-              this.attrOpen = false
-              this.refreshCurrentAreaData();
-            })
-          } else {
-            addAttr(this.attrForm).then(response => {
-              this.$modal.msgSuccess('新增成功')
-              this.attrOpen = false
-              this.refreshCurrentAreaData();
-            })
-          }
-        }
-      })
-    },
-    /** 删除按钮操作 */
-    handleAttrDelete(row) {
-      const ids = row.id || this.ids
-      this.$modal
-        .confirm('是否确认删除服务区用电属性编号为"' + ids + '"的数据项?')
-        .then(function() {
-          return delAttr(ids)
-        })
-        .then(() => {
-          // this.getAttrList()
-          this.refreshCurrentAreaData();
-          this.$modal.msgSuccess('删除成功')
-        })
-        .catch(() => {})
-    },
-    getPvPriceList() {
-      this.loading = true
-      listPvPriceConfig(this.queryPvPriceParams).then(response => {
-        this.pvPriceList = response.rows
-        this.total = response.total
-        this.loading = false
-      })
-    },
-    // 取消按钮
-    pvPriceCancel() {
-      this.pvPriceOpen = false
-      this.pvPriceReset()
-    },
-    // 表单重置
-    pvPriceReset() {
-      this.pvPriceForm = {
-        id: null,
-        cfgCode: null,
-        priceName: null,
-        objPrice: null,
-        stateSubsidy: null,
-        localSubsidy: null
-      }
-      this.resetForm('pvPriceForm')
-    },
-    /** 新增按钮操作 */
-    handlePvPriceAdd() {
-      this.pvPriceReset()
-      this.pvPriceOpen = true
-      this.title = '添加光伏电价配置'
-    },
-    /** 修改按钮操作 */
-    handlePvPriceUpdate(row) {
-      this.pvPriceReset()
-      const id = row.id || this.ids
-      getPvPriceConfig(id).then(response => {
-        this.pvPriceForm = response.data
-        this.pvPriceOpen = true
-        this.title = '修改光伏电价配置'
-      })
-    },
-    /** 提交按钮 */
-    submitPvPriceForm() {
-      this.$refs['pvPriceForm'].validate(valid => {
-        if (valid) {
-          if (this.pvPriceForm.id != null) {
-            updatePvPriceConfig(this.pvPriceForm).then(response => {
-              this.$modal.msgSuccess('修改成功')
-              this.pvPriceOpen = false
-              this.getPvPriceList()
-            })
-          } else {
-            addPvPriceConfig(this.pvPriceForm).then(response => {
-              this.$modal.msgSuccess('新增成功')
-              this.pvPriceOpen = false
-              this.getPvPriceList()
-            })
-          }
-        }
-      })
-    },
-    /** 删除按钮操作 */
-    handlePvPriceDelete(row) {
-      const ids = row.id || this.ids
-      this.$modal
-        .confirm('是否确认删除电价配置编号为"' + ids + '"的数据项?')
-        .then(function() {
-          return delPvPriceConfig(ids)
-        })
-        .then(() => {
-          this.getPvPriceList()
-          this.$modal.msgSuccess('删除成功')
-        })
-        .catch(() => {})
-    },
-    matchReqCapacityFlag(row, column, cellValue, index) {
-      const reqCapacityFlagMap = {
-        0: '不涉及',
-        1: '容量电价',
-        2: '需量电价'
-      }
-      return reqCapacityFlagMap[cellValue] || '未知'
-    },
-    getPriceType() {
-      getElecPriceType().then(response => {
-        this.priceTypeOptions = response.data
-      })
-    },
-    // 查询区域列表
-    getAreaList(areaCode, layer) {
-      areaTreeSelect(areaCode, layer).then(response => {
-        this.areaOptions = response.data
-      })
-    },
-    getGwPriceAll() {
-      listGwPriceConfigAll().then(response => {
-        this.gwPriceOptions = response.data
-      })
-    },
-    // 修改执行日期变更方法(原repeatTypeChange)
-    execDateTypeChange(val) {
-      if (val == 5) {
-        this.strategyForm.execDate = this.weeks.filter(item => item.value < 6).map(item => item.value)
-      } else if (val == 6) {
-        this.strategyForm.execDate = []
-      } else {
-        this.strategyForm.execDate = ''
-      }
-    },
-    /** 查询峰谷电价列表 */
-    getStrategyList() {
-      const { pageNum, pageSize } = this.strategyParams
-      this.strategyParams.loading = true
-      listStrategy({ pageNum, pageSize }).then(response => {
-        this.strategyList = response.rows
-        this.strategyParams.loading = false
-      })
-    },
-    handleStrategyDetail(row) {
-      this.ifDisabled = true
-      this.resetForm('strategyForm')
-      // 深拷贝数据,避免直接修改原数据
-      this.strategyForm = JSON.parse(JSON.stringify(row))
-      // 处理执行日期
-      if ([5, 6].includes(this.strategyForm.execDateType)) {
-        this.strategyForm.execDate = this.strategyForm.execDate ?
-          this.strategyForm.execDate.split(',') : []
-      }
-      // 处理执行月份
-      this.strategyForm.execMonth = this.strategyForm.execMonth ?
-        this.strategyForm.execMonth.split(',') : []
-      // 处理分时类型
-      this.strategyForm.supportType = this.strategyForm.supportType ?
-        this.strategyForm.supportType.split(',').map(v => parseInt(v)) : [0]
-      this.strategyDialog = true
-      this.title = '查看峰谷电价策略'
-    },
-    handleStrategyAdd() {
-      this.ifDisabled = false
-      this.resetForm('strategyForm')
-      this.strategyForm = {
-        id: null, // 确保id为null
-        strategyCode: '',
-        strategyName: '',
-        strategyDesc: '',
-        execDateType: '',
-        execDate: [],
-        execMonth: [],
-        supportType: [0], // 默认选中平段
-        priority: 1, // 默认优先级为1
-        hourList: []
-      }
-      this.strategyDialog = true
-      this.title = '添加峰谷电价策略'
-    },
-    handleStrategyUpdate(row) {
-      this.ifDisabled = false
-      this.resetForm('strategyForm')
-      // 深拷贝数据,避免直接修改原数据
-      this.strategyForm = JSON.parse(JSON.stringify(row))
-      // 处理执行日期
-      if ([5, 6].includes(this.strategyForm.execDateType)) {
-        this.strategyForm.execDate = this.strategyForm.execDate ?
-          this.strategyForm.execDate.split(',') : []
-      }
-      // 处理执行月份
-      this.strategyForm.execMonth = this.strategyForm.execMonth ?
-        this.strategyForm.execMonth.split(',') : []
-      // 处理分时类型
-      this.strategyForm.supportType = this.strategyForm.supportType ?
-        this.strategyForm.supportType.split(',').map(v => parseInt(v)) : [0]
-      // 确保平段始终被选中
-      if (!this.strategyForm.supportType.includes(0)) {
-        this.strategyForm.supportType.push(0)
-      }
-      this.strategyDialog = true
-      this.title = '修改峰谷电价策略'
-    },
-    handleStrategyDelete(row) {
-      this.$modal.confirm('是否确认删除').then(() => {
-        delStrategy(row.id).then(() => {
-          this.getStrategyList()
-          this.$modal.msgSuccess('删除成功')
-        })
-      })
-    },
-    strategyCancel() {
-      this.strategyDialog = false
-      this.resetForm('strategyForm')
-      // 完全重置表单数据,确保不影响下次编辑
-      this.strategyForm = {
-        id: null,
-        strategyCode: '',
-        strategyName: '',
-        strategyDesc: '',
-        execDateType: '',
-        execDate: [],
-        execMonth: [],
-        supportType: [0], // 重置时也要默认选中平段
-        priority: 1,
-        hourList: []
-      }
-      // 清除可能的验证状态
-      this.$nextTick(() => {
-        if (this.$refs.strategyForm) {
-          this.$refs.strategyForm.clearValidate()
-        }
-      })
-    },
-    submitStrategyForm() {
-      this.$refs['strategyForm'].validate(valid => {
-        if (valid) {
-          if (this.strategyForm.hourList.length === 0) {
-            return this.$modal.msgError(`请添加小时电价数据`)
-          }
-
-          // 检查相同优先级的月份冲突
-          const monthConflict = this.checkMonthConflict()
-          if (monthConflict) {
-            return this.$modal.msgError(`执行月份冲突:${monthConflict}月份已被其他相同优先级的策略使用`)
-          }
-
-          let subFlag = false
-          let dateFlag = false
-          let validIndex = 0
-          for (let i = 0; i < this.strategyForm.hourList.length; i++) {
-            const item = this.strategyForm.hourList[i]
-            if (item.startTime === '' || item.endTime === '' || item.type === '') {
-              subFlag = true
-              validIndex = i + 1
-              break
-            }
-            if (this.compareTime(item.startTime, item.endTime)) {
-              dateFlag = true
-              validIndex = i + 1
-              break
-            }
-          }
-          if (subFlag) {
-            return this.$modal.msgError(`请补全小时电价第${validIndex}行数据`)
-          }
-          if (dateFlag) {
-            return this.$modal.msgError(`小时电价第${validIndex}行开始时刻不能大于结束时刻`)
-          }
-
-          // 增强的时间段重叠检查
-          const overlapInfo = this.checkTimeRangesOverlapEnhanced()
-          if (overlapInfo) {
-            return this.$modal.msgError(`小时电价时间段冲突:${overlapInfo}`)
-          }
-
-          // 检查时间段完整性(改为可选的警告,不阻止提交)
-          const completenessCheck = this.checkTimeCompleteness()
-          if (completenessCheck) {
-            // 使用confirm让用户选择是否继续
-            this.$modal.confirm(`${completenessCheck},是否继续保存?`).then(() => {
-              this.saveStrategyForm()
-            }).catch(() => {
-              // 用户选择取消,不做任何操作
-            })
-          } else {
-            // 没有时间段问题,直接保存
-            this.saveStrategyForm()
-          }
-        }
-      })
-    },
-    // 新增方法:保存策略表单
-    saveStrategyForm() {
-      // 创建表单数据的副本以避免修改原数据
-      const formData = JSON.parse(JSON.stringify(this.strategyForm))
-
-      // 特殊处理执行日期 - 确保是字符串
-      if ([5, 6].includes(formData.execDateType)) {
-        formData.execDate = Array.isArray(formData.execDate) ? formData.execDate.join(',') : formData.execDate
-      }
-
-      // 处理执行月份 - 确保是字符串
-      formData.execMonth = Array.isArray(formData.execMonth) ? formData.execMonth.join(',') : formData.execMonth
-
-      // 处理分时类型 - 确保是字符串
-      formData.supportType = Array.isArray(formData.supportType) ? formData.supportType.join(',') : formData.supportType
-
-      formData.hourList.forEach(item => {
-        item.strategyCode = formData.strategyCode
-      })
-
-      if (formData.id) {
-        updateStrategy(formData).then(() => {
-          this.$modal.msgSuccess('修改成功')
-          this.strategyDialog = false
-          this.getStrategyList()
-        })
-      } else {
-        addStrategy(formData).then(() => {
-          this.$modal.msgSuccess('新增成功')
-          this.strategyDialog = false
-          this.getStrategyList()
-        })
-      }
-    },
-    compareTime(t1, t2) {
-      const d = new Date()
-      const ft1 = d.setHours(t1.split(':')[0], t1.split(':')[1], t1.split(':')[2])
-      const ft2 = d.setHours(t2.split(':')[0], t2.split(':')[1], t2.split(':')[2])
-      return ft1 > ft2
-    },
-    checkTimeRangesOverlap() {
-      const ranges = this.strategyForm.hourList.map(item => {
-        const d = new Date()
-        const ft1 = d.setHours(item.startTime.split(':')[0], item.startTime.split(':')[1], item.startTime.split(':')[2])
-        const ft2 = d.setHours(item.endTime.split(':')[0], item.endTime.split(':')[1], item.endTime.split(':')[2])
-        return [ft1, ft2]
-      })
-      // 将时间段按照开始时间排序
-      const sortedRanges = ranges.slice().sort((a, b) => a[0] - b[0])
-      // 检查时间段是否重叠
-      for (let i = 1; i < sortedRanges.length; i++) {
-        if (sortedRanges[i][0] < sortedRanges[i - 1][1]) {
-          return true
-        }
-      }
-      // 所有时间段都没有重叠
-      return false
-    },
-    // 增强版时间段重叠检查,返回具体的冲突信息
-    checkTimeRangesOverlapEnhanced() {
-      const ranges = this.strategyForm.hourList.map((item, index) => {
-        const d = new Date()
-        const ft1 = d.setHours(item.startTime.split(':')[0], item.startTime.split(':')[1], item.startTime.split(':')[2])
-        const ft2 = d.setHours(item.endTime.split(':')[0], item.endTime.split(':')[1], item.endTime.split(':')[2])
-        return {
-          start: ft1,
-          end: ft2,
-          startStr: item.startTime,
-          endStr: item.endTime,
-          index: index + 1
-        }
-      })
-
-      // 按开始时间排序
-      const sortedRanges = ranges.slice().sort((a, b) => a.start - b.start)
-
-      // 检查重叠并返回详细信息
-      for (let i = 1; i < sortedRanges.length; i++) {
-        if (sortedRanges[i].start < sortedRanges[i - 1].end) {
-          return `第${sortedRanges[i - 1].index}行(${sortedRanges[i - 1].startStr}-${sortedRanges[i - 1].endStr})与第${sortedRanges[i].index}行(${sortedRanges[i].startStr}-${sortedRanges[i].endStr})存在时间重叠`
-        }
-      }
-      return null
-    },
-    // 检查时间完整性(24小时是否都被覆盖)
-    checkTimeCompleteness() {
-      if (this.strategyForm.hourList.length === 0) return null
-
-      const ranges = this.strategyForm.hourList.map(item => {
-        const startParts = item.startTime.split(':')
-        const endParts = item.endTime.split(':')
-        return {
-          start: parseInt(startParts[0]) * 3600 + parseInt(startParts[1]) * 60 + parseInt(startParts[2]),
-          end: parseInt(endParts[0]) * 3600 + parseInt(endParts[1]) * 60 + parseInt(endParts[2]),
-          startStr: item.startTime,
-          endStr: item.endTime
-        }
-      }).sort((a, b) => a.start - b.start)
-
-      const gaps = []
-      const dayInSeconds = 24 * 3600
-      const tolerance = 1 // 允许1秒的容差
-
-      // 检查开始是否从0点开始
-      if (ranges[0].start > tolerance) {
-        gaps.push(`00:00:00 - ${this.formatSeconds(ranges[0].start)}`)
-      }
-
-      // 检查中间的间隙(允许1秒的连续性)
-      for (let i = 1; i < ranges.length; i++) {
-        const gap = ranges[i].start - ranges[i - 1].end
-        // 如果间隙大于1秒,才认为是真正的间隙
-        if (gap > tolerance) {
-          gaps.push(`${this.formatSeconds(ranges[i - 1].end)} - ${this.formatSeconds(ranges[i].start)}`)
-        }
-      }
-
-      // 检查结束是否到24点(23:59:59)
-      if (ranges[ranges.length - 1].end < dayInSeconds - tolerance) {
-        gaps.push(`${this.formatSeconds(ranges[ranges.length - 1].end)} - 23:59:59`)
-      }
-
-      if (gaps.length > 0) {
-        return `以下时间段未配置:${gaps.join('、')}`
-      }
-      return null
-    },
-    // 辅助方法:将秒数转换为时间格式
-    formatSeconds(seconds) {
-      const h = Math.floor(seconds / 3600)
-      const m = Math.floor((seconds % 3600) / 60)
-      const s = seconds % 60
-      return `${h.toString().padStart(2, '0')}:${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`
-    },
-    // 检查相同优先级策略的月份冲突
-    checkMonthConflict() {
-      // 获取当前优先级
-      const currentPriority = this.strategyForm.priority
-
-      // 获取相同优先级的其他策略
-      const samePriorityStrategies = this.strategyList.filter(strategy => {
-        // 排除当前正在编辑的策略和默认策略
-        return strategy.priority === currentPriority &&
-          strategy.id !== this.strategyForm.id &&
-          strategy.editFlag !== 0
-      })
-
-      // 收集已被占用的月份
-      const occupiedMonths = new Set()
-      samePriorityStrategies.forEach(strategy => {
-        if (strategy.execMonth) {
-          strategy.execMonth.split(',').forEach(month => {
-            occupiedMonths.add(month)
-          })
-        }
-      })
-
-      // 检查当前选择的月份是否有冲突
-      const conflictMonths = []
-      this.strategyForm.execMonth.forEach(month => {
-        if (occupiedMonths.has(month)) {
-          const monthLabel = this.monthOptions.find(m => m.value === month)?.label || month
-          conflictMonths.push(monthLabel)
-        }
-      })
-
-      if (conflictMonths.length > 0) {
-        return conflictMonths.join('、')
-      }
-      return null
-    },
-    // 获取可用月份(用于动态禁用已被占用的月份)
-    getAvailableMonths() {
-      const currentPriority = this.strategyForm.priority
-      const samePriorityStrategies = this.strategyList.filter(strategy => {
-        return strategy.priority === currentPriority &&
-          strategy.id !== this.strategyForm.id &&
-          strategy.editFlag !== 0
-      })
-
-      const occupiedMonths = new Set()
-      samePriorityStrategies.forEach(strategy => {
-        if (strategy.execMonth) {
-          strategy.execMonth.split(',').forEach(month => {
-            occupiedMonths.add(month)
-          })
-        }
-      })
-
-      return this.monthOptions.map(month => ({
-        ...month,
-        disabled: occupiedMonths.has(month.value)
-      }))
-    },
-    addSub() {
-      this.strategyForm.hourList.push({
-        startTime: '',
-        endTime: '',
-        type: ''
-      })
-    },
-    deleteSub(index) {
-      this.strategyForm.hourList.splice(index, 1)
-    },
-    /** 查询商户计费列表 */
-    getChargingList() {
-      const { pageNum, pageSize } = this.chargingParams
-      this.chargingParams.loading = true
-      listCharging({ pageNum, pageSize }).then(response => {
-        this.chargingList = response.rows
-        this.chargingParams.loading = false
-      })
-    },
-    getRelateShopList(areaCode) {
-      areaListByTag(areaCode,'Area_01').then(response => {
-        this.zoningList = response.data
-      })
-    },
-    handleChargingAdd() {
-      this.resetForm('chargingForm')
-      this.chargingForm = this.$options.data().chargingForm
-      this.chargingDialog = true
-      this.title = '添加商户计费配置'
-    },
-    handleChargingUpdate(row) {
-      this.resetForm('chargingForm')
-      Object.assign(this.chargingForm, row)
-      this.chargingForm.relCoList = this.chargingForm.relCoList.map(item => item.areaCode)
-      this.getRelateShopList(this.chargingForm.areaCode)
-      this.chargingDialog = true
-      this.title = '修改商户计费配置'
-    },
-    handleChargingDelete(row) {
-      this.$modal.confirm('是否确认删除').then(() => {
-        delCharging(row.id).then(() => {
-          this.getChargingList()
-          this.$modal.msgSuccess('删除成功')
-        })
-      })
-    },
-    chargingCancel() {
-      this.chargingDialog = false
-      this.resetForm('chargingForm')
-      this.chargingForm = this.$options.data().chargingForm
-    },
-    submitChargingForm() {
-      this.$refs['chargingForm'].validate(valid => {
-        if (valid) {
-          this.chargingForm.relCoList = this.chargingForm.relCoList.map(item => ({ priceCfgId: this.chargingForm.id, areaCode: item }))
-          if (this.chargingForm.id) {
-            updateCharging(this.chargingForm).then(() => {
-              this.$modal.msgSuccess('修改成功')
-              this.chargingDialog = false
-              this.getChargingList()
-            })
-          } else {
-            addCharging(this.chargingForm).then(() => {
-              this.$modal.msgSuccess('新增成功')
-              this.chargingDialog = false
-              this.getChargingList()
-            })
-          }
-        }
-      })
+    // 处理区域选择
+    handleAreaSelection(data) {
+      this.currentAreaCode = data.areaCode
+      this.currentAreaName = data.areaName
     }
   }
 }
 </script>
+
 <style lang="scss" scoped>
 .app-container {
   ::v-deep .el-tabs__content {
     overflow: initial;
   }
-}
-.sub-table {
-  .el-icon-delete {
-    cursor: pointer;
-  }
-  .operateBtns {
-    cursor: pointer;
-    i {
-      color: #1990ff;
-      margin-left: 5px;
-    }
-  }
-  ::v-deep .el-date-editor {
-    width: 100% !important;
-  }
-}
-.custom-checkbox-group {
-  max-height: 120px;
-  overflow-y: auto;
-}
-.strategyForm {
-  ::v-deep .el-input.is-disabled .el-input__inner {
-    color: #606266;
-    background: #fff;
-  }
-}
 
-.app-container {
-  .el-button.deleteBtn:disabled {
-    color: #c0c4cc !important; /* 文字颜色 */
-  }
-  .el-button.deleteBtn:disabled:hover {
-    color: #c0c4cc !important;/*删除按钮颜色*/
+  // Tab标签栏美化
+  ::v-deep .el-tabs {
+    .el-tabs__item {
+      font-size: 14px;
+      font-weight: 500;
+      transition: all 0.3s;
 
+      &:hover {
+        color: #409EFF;
+      }
+
+      &.is-active {
+        color: #409EFF;
+        font-weight: 600;
+      }
+    }
+
+    .el-tabs__active-bar {
+      height: 3px;
+      background: linear-gradient(to right, #409EFF, #67C23A);
+    }
   }
 }
 </style>

+ 49 - 0
ems-ui-cloud/src/views/basecfg/price/mixins/commonMethods.js

@@ -0,0 +1,49 @@
+// 共享的方法和数据
+export default {
+  data() {
+    return {
+      // 公共的选项数据
+      computeTypeOptions: [
+        { name: '不计入', value: 0 },
+        { name: '户数均摊', value: 1 },
+        { name: '面积均摊', value: 2 }
+      ],
+      meterClsOptions: [
+        { value: 70, label: '自来水' }
+      ]
+    }
+  },
+  methods: {
+    // 通用的字典格式化方法
+    formatDict(value, options, key = 'value', name = 'name') {
+      let text = ''
+      this[options].forEach(item => {
+        if (value === item[key]) {
+          text = item[name]
+        }
+      })
+      return text
+    },
+
+    // 通用的表单重置方法
+    resetForm(formName) {
+      if (this.$refs[formName]) {
+        this.$refs[formName].resetFields()
+      }
+    },
+
+    // 通用的搜索方法
+    handleQuery(queryParams, callback) {
+      queryParams.pageNum = 1
+      if (callback && typeof callback === 'function') {
+        callback()
+      }
+    },
+
+    // 通用的重置查询方法
+    resetQuery(formName, queryParams, callback) {
+      this.resetForm(formName)
+      this.handleQuery(queryParams, callback)
+    }
+  }
+}

+ 86 - 0
ems-ui-cloud/src/views/basecfg/price/styles/dialog.scss

@@ -0,0 +1,86 @@
+// price/styles/dialog.scss
+// 弹窗美化样式
+
+// 对话框优化
+::v-deep .el-dialog {
+  border-radius: 8px;
+
+  .el-dialog__header {
+    background: linear-gradient(to right, #409EFF 0%, #67C23A 100%);
+    border-radius: 8px 8px 0 0;
+
+    .el-dialog__title {
+      color: white;
+      font-size: 16px;
+      font-weight: 500;
+    }
+
+    .el-dialog__close {
+      color: white;
+
+      &:hover {
+        color: #f0f0f0;
+      }
+    }
+  }
+
+  .el-dialog__body {
+    padding: 20px;
+  }
+
+  .el-dialog__footer {
+    padding: 15px 20px;
+    background: #f5f7fa;
+    border-top: 1px solid #e4e7ed;
+    border-radius: 0 0 8px 8px;
+  }
+}
+
+// 自定义对话框样式变体
+.custom-dialog {
+  ::v-deep .el-dialog__header {
+    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+  }
+}
+
+// 按钮优化样式
+.dialog-footer {
+  .el-button {
+    transition: all 0.3s;
+
+    &:hover {
+      transform: translateY(-2px);
+      box-shadow: 0 4px 8px rgba(0,0,0,0.12);
+    }
+
+    &--primary {
+      background: linear-gradient(135deg, #409EFF 0%, #67C23A 100%);
+      border: none;
+
+      &:hover {
+        background: linear-gradient(135deg, #66b1ff 0%, #85ce61 100%);
+      }
+    }
+  }
+}
+
+// 表单项在弹窗中的样式优化
+::v-deep .el-dialog {
+  .el-form-item__label {
+    font-weight: 500;
+    color: #606266;
+  }
+
+  .el-input__inner {
+    transition: all 0.3s;
+
+    &:focus {
+      border-color: #409EFF;
+      box-shadow: 0 0 0 2px rgba(64, 158, 255, 0.2);
+    }
+  }
+
+  .el-select {
+    width: 100%;
+  }
+}

+ 0 - 352
ems-ui-cloud/src/views/basecfg/tag/index.vue

@@ -1,352 +0,0 @@
-<template>
-  <div class="app-container">
-    <el-tabs v-model="activeTab" @tab-click="handleTabClick">
-      <el-tab-pane
-        v-for="item in tagModelOptions"
-        :key="item.code"
-        :label="item.name"
-        :name="item.code">
-    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
-<!--      <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-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:tag: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:tag: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:tag: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:tag:export']"
-        >导出</el-button>
-      </el-col>
-      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
-    </el-row>
-
-    <el-table v-loading="loading" :data="tagList" @selection-change="handleSelectionChange">
-      <el-table-column type="selection" width="55" align="center" />
-      <el-table-column label="标签分类" align="center" prop="tagModel">
-        <template>
-
-        </template>
-      </el-table-column>
-<!--      <el-table-column label="标签分类" align="center" prop="tagModel" >-->
-<!--        <template slot-scope="scope">-->
-<!--          <span>{{ getTagModelName(scope.row.tagModel)}}</span>-->
-<!--        </template>-->
-<!--      </el-table-column>-->
-      <el-table-column label="标签代码" align="center" prop="tagCode" />
-      <el-table-column label="标签名称" align="center" prop="tagName" />
-      <el-table-column label="标签颜色" align="center" prop="tagColor" >
-        <template slot-scope="scope">
-          <div v-if="scope.row.tagColor" class="color-display" :style="{ backgroundColor: scope.row.tagColor }"></div>
-          <div v-else>暂无颜色</div>
-        </template>
-      </el-table-column>
-      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
-        <template slot-scope="scope">
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-edit"
-            @click="handleUpdate(scope.row)"
-            v-hasPermi="['ems:tag:edit']"
-            v-if="scope.row.modFlag === 1">
-            修改
-          </el-button>
-          <el-button
-            size="mini"
-            type="text"
-            icon="el-icon-delete"
-            class="deleteBtn"
-            @click="handleDelete(scope.row)"
-            v-hasPermi="['basecfg:tag:remove']"
-            v-if="scope.row.modFlag === 1">
-            删除
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-
-    <pagination
-      v-show="total>0"
-      :total="total"
-      :page.sync="queryParams.pageNum"
-      :limit.sync="queryParams.pageSize"
-      @pagination="getList"
-    />
-      </el-tab-pane>
-    </el-tabs>
-    <!-- 添加或修改标签分类对话框 -->
-    <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="tagCode">
-          <el-input v-model="form.tagCode" placeholder="请输入标签代码" />
-        </el-form-item>
-        <el-form-item label="标签名称" prop="tagName">
-          <el-input v-model="form.tagName" placeholder="请输入标签名称" />
-        </el-form-item>
-        <el-form-item label="标签颜色" prop="tagColor">
-          <el-color-picker
-            v-model="form.tagColor"
-            show-alpha
-            :predefine="predefineColors">
-          </el-color-picker>
-        </el-form-item>
-
-        <!-- 隐藏的mod_flag字段 -->
-        <el-form-item label-hidden>
-          <el-input v-model="form.modFlag" type="hidden"></el-input>
-        </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>
-  </div>
-</template>
-
-<script>
-import { listTag, getTag, delTag, addTag, updateTag } from "@/api/basecfg/tag";
-
-export default {
-  name: "Tag",
-  data() {
-    return {
-      // 遮罩层
-      loading: true,
-      // 选中数组
-      ids: [],
-      // 非单个禁用
-      single: true,
-      // 非多个禁用
-      multiple: true,
-      // 显示搜索条件
-      showSearch: true,
-      // 总条数
-      total: 0,
-      // 标签分类表格数据
-      tagList: [],
-      // 弹出层标题
-      title: "",
-      // 是否显示弹出层
-      open: false,
-      // 能源分类树
-      tagModelOptions: [
-        {"code": "Area", "name": "区域标签"}
-      ],
-      // 当前激活的标签分类
-      activeTab: 'Area',
-      // 查询参数
-      queryParams: {
-        pageNum: 1,
-        pageSize: 10,
-        tagModel: null,
-        tagCode: null,
-        tagName: null,
-        modFlag:null,
-        tagColor:''
-      },
-      // 表单参数
-      form: {
-        id: null,
-        tagModel: null, // 默认值为null,将在打开弹框时设置
-        tagCode: null,
-        tagName: null,
-        modFlag:null,
-        tagColor:''
-      },
-      // 表单校验
-      rules: {
-        tagModel: [
-          { required: true, message: "标签分类不能为空", trigger: "blur" }
-        ],
-        tagCode: [
-          { required: true, message: "标签代码不能为空", trigger: "blur" }
-        ],
-        tagName: [
-          { required: true, message: "标签名称不能为空", trigger: "blur" }
-        ]
-      },
-      predefineColors: [
-        '#ff4500',
-        '#ff8c00',
-        '#ffd700',
-        '#90ee90',
-        '#00ced1',
-        '#1e90ff',
-        '#c71585',
-        'rgba(255, 69, 0, 0.68)',
-        'rgb(255, 120, 0)',
-        'hsv(51, 100, 98)',
-        'hsva(120, 40, 94, 0.5)',
-        'hsl(181, 100%, 37%)',
-        'hsla(209, 100%, 56%, 0.73)',
-        '#c7158577'
-      ],
-    };
-  },
-  created() {
-    this.getList();
-  },
-  methods: {
-    /** 查询标签分类列表 */
-    getList() {
-      this.loading = true;
-      listTag(this.queryParams).then(response => {
-        this.tagList = response.rows;
-        this.total = response.total;
-        this.loading = false;
-      });
-    },
-    // 取消按钮
-    cancel() {
-      this.open = false;
-      this.reset();
-    },
-    // 表单重置
-    reset() {
-      this.form = {
-        id: null,
-        tagModel: null,
-        tagCode: null,
-        tagName: null,
-        modFlag:null,
-        tagColor:''
-      };
-      this.resetForm("form");
-    },
-    /** 搜索按钮操作 */
-    handleQuery() {
-      this.queryParams.pageNum = 1;
-      this.getList();
-    },
-    /** 重置按钮操作 */
-    resetQuery() {
-      this.resetForm("queryForm");
-      this.handleQuery();
-    },
-    // 多选框选中数据
-    handleSelectionChange(selection) {
-      this.ids = selection.map(item => item.id)
-      this.single = selection.length!==1
-      this.multiple = !selection.length
-
-    },
-    /** 新增按钮操作 */
-    handleAdd() {
-      this.reset();
-      this.open = true;
-      this.title = "添加标签分类";
-      // 设置默认的标签分类
-      console.log("this.activeTab",this.activeTab)
-      this.form.tagModel = this.activeTab;
-      this.form.modFlag = 1;
-    },
-    /** 修改按钮操作 */
-    handleUpdate(row) {
-      this.reset();
-      const id = row.id || this.ids
-      getTag(id).then(response => {
-        this.form = response.data;
-        this.form.tagModel = this.activeTab; // 设置默认的标签分类
-        this.open = true;
-        this.title = "修改标签分类";
-      });
-    },
-    /** 提交按钮 */
-    submitForm() {
-      this.$refs["form"].validate(valid => {
-        if (valid) {
-          if (this.form.id != null) {
-            console.log("this.form",this.form)
-            updateTag(this.form).then(response => {
-              this.$modal.msgSuccess("修改成功");
-              this.open = false;
-              this.getList();
-            });
-          } else {
-            console.log("this.form",this.form)
-            addTag(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 delTag(ids);
-      }).then(() => {
-        this.getList();
-        this.$modal.msgSuccess("删除成功");
-      }).catch(() => {});
-    },
-    /** 导出按钮操作 */
-    handleExport() {
-      this.download('ems/tag/export', {
-        ...this.queryParams
-      }, `tag_${new Date().getTime()}.xlsx`)
-    },
-    getTagModelName(code) {
-      const modelMap = {
-        "Area": "区域标签"
-      };
-      return modelMap[code] || '未知';
-    },
-    handleTabClick(tab) {
-      this.activeTab = tab.name;
-      this.getList();
-    }
-  }
-};
-</script>
-<style lang="css">
-.color-display {
-  width: 20px;
-  height: 20px;
-  border-radius: 2px;
-  display: inline-block;
-}
-</style>

+ 345 - 618
ems-ui-cloud/src/views/mgr/charging.vue

@@ -1,686 +1,413 @@
 <template>
   <div class="app-container">
     <el-row :gutter="20">
-      <el-col :span="4" :xs="24">
+      <!-- 左侧树形区域 -->
+      <el-col :span="5" :xs="24">
         <div class="head-container">
-          <el-input v-model="areaName" placeholder="请输入服务区名称" clearable size="small" prefix-icon="el-icon-search"
-            style="margin-bottom: 20px" />
+          <el-input
+            v-model="areaName"
+            placeholder="请输入区域名称"
+            clearable
+            size="small"
+            prefix-icon="el-icon-search"
+            style="margin-bottom: 20px"
+            @input="filterTree"
+          />
         </div>
-        <div class="head-container">
-          <el-tree ref="tree" :data="areaOptions" :props="defaultProps" :expand-on-click-node="false" :filter-node-method="filterNode"
-            node-key="id" :default-expanded-keys="this.defalutArr" highlight-current @node-click="handleNodeClick" />
+        <div class="head-container tree-container">
+          <el-tree
+            ref="tree"
+            :data="areaOptions"
+            :props="defaultProps"
+            :expand-on-click-node="false"
+            :filter-node-method="filterNode"
+            node-key="id"
+            :default-expanded-keys="defaultExpandedKeys"
+            highlight-current
+            @node-click="handleNodeClick"
+          >
+            <span class="custom-tree-node" slot-scope="{ node, data }">
+              <span class="tree-label">
+                <i :class="getTreeIcon(data)" class="tree-icon"></i>
+                {{ node.label }}
+              </span>
+              <el-tag
+                v-if="data.type === 'seller'"
+                size="mini"
+                effect="plain"
+                class="tree-tag"
+              >
+                商户
+              </el-tag>
+            </span>
+          </el-tree>
         </div>
       </el-col>
-      <el-col :span="20" :xs="24">
-        <el-tabs v-model="activeTab" @tab-click="handleTabChange">
-          <el-tab-pane label="总览" name="first">
-            <div class="custom-form">
-              <el-select v-model="queryParams.meterCls" size="mini" @change="meterClsChange">
-                <el-option v-for="item in emsClsOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
-              </el-select>
-            </div>
-            <div class="panel">
-              <div class="panel-title">当月数据</div>
-              <div class="panel-body">
-                <BaseChart width="100%" height="300px" :option="pieOptions" />
-              </div>
-            </div>
-            <div class="panel">
-              <div class="panel-title">历史数据</div>
-              <div class="panel-body">
-                <div class="custom-form">
-                  <el-date-picker v-model="queryParams.historyRange" size="mini" type="monthrange" align="right" :clearable="false"
-                    range-separator="至" start-placeholder="开始月份" end-placeholder="结束月份" value-format="yyyyMM"
-                    :picker-options="pickerOptions" @change="getList">
-                  </el-date-picker>
-                </div>
-                <el-table :data="historyData" style="width: 100%;margin-bottom:20px" max-height="300px">
-                  <el-table-column prop="meteredTime" align="center" label="账单月份(年-月)">
-                  </el-table-column>
-                  <el-table-column
-                    prop="meteredValue"
-                    align="center"
-                    :label="`商户用量(${queryParams.meterCls === 45 ? 'kW·h' : '吨'})`">
-                    <template slot-scope="scope">
-                      {{ scope.row.meteredValue }}
-                    </template>
-                  </el-table-column>
-                  <el-table-column prop="meteredPrice" align="center" label="个户费用(¥)">
-                  </el-table-column>
-                  <el-table-column
-                    prop="sharedValue"
-                    align="center"
-                    :label="`公摊用量(${queryParams.meterCls === 45 ? 'kW·h' : '吨'})`">
-                    <template slot-scope="scope">
-                      {{ scope.row.meteredValue }}
-                    </template>
-                  </el-table-column>
-                  <el-table-column prop="sharedPrice" align="center" label="公摊费用(¥)">
-                  </el-table-column>
-                  <el-table-column
-                    prop="totalValue"
-                    align="center"
-                    :label="`总用量(${queryParams.meterCls === 45 ? 'kW·h' : '吨'})`">
-                    <template slot-scope="scope">
-                      {{ scope.row.meteredValue }}
-                    </template>
-                  </el-table-column>
-                  <el-table-column prop="totalPrice" align="center" label="合并费用(¥)">
-                  </el-table-column>
-                </el-table>
-                <BaseChart width="100%" height="300px" :option="overviewOptions" />
-              </div>
-            </div>
-          </el-tab-pane>
-          <el-tab-pane label="个户" name="second">
-            <div class="custom-form">
-              <el-select v-model="queryParams.meterCls" size="mini" @change="meterClsChange">
-                <el-option v-for="item in emsClsOptions" :key="item.code" :label="item.name" :value="item.code"></el-option>
-              </el-select>
-            </div>
-            <div class="panel">
-              <div class="panel-title">当月数据</div>
-              <div class="panel-body">
-                <el-table :data="currentData" style="width: 100%">
-                  <el-table-column prop="meteredTime" align="center" label="账单月份(年-月)">
-                  </el-table-column>
-                  <el-table-column
-                    prop="meteredValue"
-                    align="center"
-                    :label="`用量(${queryParams.meterCls === 45 ? 'kW·h' : '吨'})`">
-                    <template slot-scope="scope">
-                      {{ scope.row.meteredValue }}
-                    </template>
-                  </el-table-column>
-                  <el-table-column prop="meteredPrice" align="center" label="费用(¥)">
-                  </el-table-column>
-                  <el-table-column prop="sharedPrice" align="center" label="公摊费用(¥)">
-                  </el-table-column>
-                  <el-table-column prop="sharedComputeType" align="center" label="公摊类型">
-                    <template slot-scope="scope">
-                      {{formatDict(scope.row.sharedComputeType,'computeTypeOptions')}}
-                    </template>
-                  </el-table-column>
-                  <el-table-column prop="totalPrice" align="center" label="总费用(¥)">
-                  </el-table-column>
-                </el-table>
+
+      <!-- 右侧内容区域 -->
+      <el-col :span="19" :xs="24">
+        <div class="tab-container">
+          <div class="custom-tabs">
+            <div class="tabs-header">
+              <div
+                class="tab-item"
+                :class="{ active: activeTab === 'overview' }"
+                @click="handleTabChange('overview')"
+              >
+                <i class="el-icon-data-board"></i>
+                <span>总览</span>
               </div>
-            </div>
-            <div class="panel">
-              <div class="panel-title">历史数据</div>
-              <div class="panel-body">
-                <div class="custom-form">
-                  <el-date-picker v-model="queryParams.historyRange" size="mini" type="monthrange" align="right" :clearable="false"
-                    range-separator="至" start-placeholder="开始月份" end-placeholder="结束月份" value-format="yyyyMM"
-                    :picker-options="pickerOptions" @change="getList">
-                  </el-date-picker>
-                </div>
-                <el-table :data="historyData" style="width: 100%;margin-bottom:20px" max-height="300px">
-                  <el-table-column prop="meteredTime" align="center" label="账单月份(年-月)">
-                  </el-table-column>
-                  <el-table-column
-                    prop="meteredValue"
-                    align="center"
-                    :label="`用量(${queryParams.meterCls === 45 ? 'kW·h' : '吨'})`">
-                    <template slot-scope="scope">
-                      {{ scope.row.meteredValue }}
-                    </template>
-                  </el-table-column>
-                  <el-table-column prop="meteredPrice" align="center" label="个户费用(¥)">
-                  </el-table-column>
-                 <el-table-column prop="sharedPrice" align="center" label="公摊费用(¥)">
-                  </el-table-column>
-                  <!--
-                  <el-table-column prop="sharedComputeType" align="center" label="公摊类型">
-                    <template slot-scope="scope">
-                      {{formatDict(scope.row.sharedComputeType,'computeTypeOptions')}}
-                    </template>
-                  </el-table-column> -->
-                  <el-table-column prop="totalPrice" align="center" label="合并费用(¥)">
-                  </el-table-column>
-                </el-table>
-                <BaseChart width="100%" height="300px" :option="elecOptions" />
+              <div
+                class="tab-item"
+                :class="{ active: activeTab === 'seller' }"
+                @click="handleTabChange('seller')"
+              >
+                <i class="el-icon-user"></i>
+                <span>个户</span>
               </div>
             </div>
-          </el-tab-pane>
+            <div class="tabs-content">
+              <!-- 总览Tab -->
+              <OverviewPanel
+                v-if="activeTab === 'overview'"
+                ref="overviewPanel"
+                :query-params="queryParams"
+                :selected-area="selectedArea"
+                :area-tree-data="areaOptions"
+                @refresh="refreshData"
+              />
 
-        </el-tabs>
+              <!-- 个户Tab -->
+              <SellerPanel
+                v-if="activeTab === 'seller'"
+                ref="sellerPanel"
+                :query-params="queryParams"
+                :selected-area="selectedArea"
+                :area-tree-data="areaOptions"
+                @refresh="refreshData"
+              />
+            </div>
+          </div>
+        </div>
       </el-col>
     </el-row>
   </div>
 </template>
 
 <script>
-import { areaTreeByFacsCategory, areaTreeByTag, areaTreeSelect } from '@/api/basecfg/area'
-import Treeselect from '@riophae/vue-treeselect'
-import '@riophae/vue-treeselect/dist/vue-treeselect.css'
-import { chargingList, overviewInfo } from '@/api/mgr/charging.js'
-import { dateFormat } from '@/utils/index.js'
-import BaseChart from '@/components/BaseChart'
+import { areaTreeSelect, areaTreeByTag } from '@/api/basecfg/area'
+import OverviewPanel from './components/OverviewPanel'
+import SellerPanel from './components/SellerPanel'
+
 export default {
-  name: 'Device',
-  components: { Treeselect, BaseChart },
+  name: 'Charging',
+  components: {
+    OverviewPanel,
+    SellerPanel
+  },
   data() {
-    const nowDay = new Date()
-    const lastMonth = new Date(nowDay.getFullYear(), nowDay.getMonth() - 1)
+    const now = new Date()
+    const year = now.getFullYear()
+    const month = (now.getMonth() + 1).toString().padStart(2, '0')
+
     return {
-      activeTab: 'first',
-      // 总条数
-      areaName: undefined,
+      activeTab: 'overview',
+      areaName: '',
       areaOptions: [],
       defaultProps: {
         children: 'children',
         label: 'label'
       },
-      emsClsOptions: [
-        { code: 45, name: '电表' },
-        { code: 70, name: '水表' }
-      ],
-      // 查询参数
+      defaultExpandedKeys: [],
+      selectedArea: null,
       queryParams: {
-        pageNum: 1,
-        pageSize: 10,
         areaCode: null,
-        meterCls: 45,
-        historyRange: []
-      },
-      lastMonth: dateFormat(lastMonth, 'yyyyMM'),
-      currentData: [],
-      historyData: [],
-      defalutArr: [],
-      computeTypeOptions: [
-        { name: '不计入', code: 0 },
-        { name: '个户平摊', code: 1 },
-        { name: '面积公摊)', code: 2 }
-      ],
-      pickerOptions: {
-        disabledDate(time) {
-          const t = new Date().getDate()
-          return time.getTime() > Date.now() - 8.64e7 * t
-        }
+        objCode: null,
+        startTime: `${year}${month}`,
+        endTime: `${year}${month}`,
+        includeAuto: true,
+        includeManual: true,
+        includeShared: true
       }
     }
   },
   created() {
-    this.setDefaultMonthRange()
-    this.handleTabChange()
-
+    this.loadAreaTree()
   },
-  computed: {
-    pieOptions() {
-      let options = {}
-      if (this.activeTab == 'first') {
-        const { meterCls } = this.queryParams
-        let pieData = []
-        let total = ''
-        if (this.currentData.length) {
-          const { meteredValue, sharedValue, totalValue, meteredPrice, sharedPrice, totalPrice } = this.currentData[0]
-          total = totalValue
-          pieData = [
-            {
-              value: meteredValue,
-              price: meteredPrice,
-              name:  '商户用量',
-              totalValue,
-              totalPrice,
-              itemStyle: {
-                color: '#8d7fec'
-              }
-            },
-            {
-              value: sharedValue,
-              price: sharedPrice,
-              name: '公摊用量',
-              totalValue,
-              totalPrice,
-              itemStyle: {
-                color: '#6BD9BC'
-              }
-            }
-          ]
-        }
-        options = {
-          title: [
-            {
-              text: '总用量',
-              subtext: total + `${meterCls === 45 ? '度' : '吨'}`,
-              textStyle: {
-                fontSize: 15,
-                color: 'black'
-              },
-              subtextStyle: {
-                fontSize: 20,
-                color: 'black'
-              },
-              textAlign: 'center',
-              x: '34.5%',
-              y: '40%'
-            }
-          ],
-          tooltip: {
-            trigger: 'item',
-            formatter: function(parms) {
-              var str =
-                parms.data.name +
-                '</br>' +
-                '用量:' +
-                parms.data.value +
-                `${meterCls === 45 ? '度' : '吨'}` +
-                '</br>' +
-                '费用:' +
-                parms.data.price +
-                '元'
-              return str
-            }
-          },
-          legend: {
-            type: 'scroll',
-            orient: 'vertical',
-            left: '65%',
-            align: 'left',
-            top: 'middle',
-            textStyle: {
-              color: '#8C8C8C'
-            }
-          },
-          series: [
-            {
-              type: 'pie',
-              center: ['35%', '50%'],
-              radius: ['40%', '65%'],
-              clockwise: false, // 饼图的扇区是否是顺时针排布
-              avoidLabelOverlap: false,
-              itemStyle: {
-                // 图形样式
-                normal: {
-                  borderColor: '#ffffff',
-                  borderWidth: 1
-                }
-              },
-              label: {
-                formatter: function(val) {
-                  const unit = meterCls === 45 ? '度' : '吨'
-                  return '{a|' + val.name + '}{b|\n' + val.value + unit + '}{b|\n' + val.data.price + '元}'
-                },
-                textStyle: {
-                  rich: {
-                    a: {
-                      color: '#333333',
-                      fontSize: '12'
-                    },
-                    b: {
-                      color: '#0086FF',
-                      fontSize: '12',
-                      padding: [4, 0, 0, 0]
-                    }
-                  }
-                }
-              },
-              labelLine: {
-                length: 10,
-                length2: 50
-              },
-              labelLayout: {
-                verticalAlign: 'bottom',
-              },
-              data: pieData
-            }
-          ]
-        }
+  methods: {
+    getTreeIcon(data) {
+      if (data.type === 'seller') {
+        return 'el-icon-user'
       }
-
-      return options
+      return 'el-icon-office-building'
     },
-    overviewOptions() {
-      let option = {}
-      if (this.activeTab === 'first') {
-        const xData = this.historyData.map(item => item.meteredTime)
-        const quantity = this.historyData.map(item => item.meteredValue)
-        const shareQuantity = this.historyData.map(item => item.sharedValue)
-        const cost = this.historyData.map(item => item.meteredPrice)
-        const shareCost = this.historyData.map(item => item.sharedPrice)
-        const { meterCls } = this.queryParams
-        option = {
-          tooltip: {
-            trigger: 'axis',
-            axisPointer: {
-              type: 'cross',
-              crossStyle: {
-                color: '#999'
-              }
-            }
-          },
-          grid: {
-            left: '5%'
-          },
-          legend: {
-            data:['商户用量', '个户费用', '公摊用量', '公摊费用']
-          },
-          xAxis: {
-            type: 'category',
-            data: xData,
-            axisPointer: {
-              type: 'shadow'
-            }
-          },
-          yAxis: [
-            {
-              name: meterCls === 45 ? 'kW·h(千瓦时)' : '吨',
-              type: 'value'
-            },
-            {
-              name: '¥(元)',
-              type: 'value'
-            }
-          ],
-          series: [
-            {
-              name: '商户用量',
-              type: 'bar',
-              data: quantity,
-              barWidth: 30,
-              itemStyle: {
-                normal: {
-                  color: '#6395FA'
-                }
-              }
-            },
-            {
-              name: '公摊用量',
-              type: 'bar',
-              data: shareQuantity,
-              barWidth: 30,
-              itemStyle: {
-                normal: {
-                  color: '#8CDF6C'
-                }
-              }
-            },
-            {
-              name: '个户费用',
-              type: 'line',
-              yAxisIndex: 1,
-              data: cost,
-              showSymbol: true,
-              itemStyle: {
-                normal: {
-                  color: '#5BD9A5'
-                }
-              }
-            },
-            {
-              name: '公摊费用',
-              type: 'line',
-              yAxisIndex: 1,
-              data: shareCost,
-              showSymbol: true,
-            }
-          ]
+
+    // 加载区域树
+    async loadAreaTree() {
+      try {
+        let response
+        if (this.activeTab === 'overview') {
+          // 总览:加载所有顶级区域
+          response = await areaTreeSelect('0', 1)
+          this.areaOptions = response.data || []
+        } else {
+          // 个户:加载商户区域
+          response = await areaTreeByTag('0', 'Area_Seller')
+          this.areaOptions = response.data || []
         }
-      }
 
-      return option
-    },
-    elecOptions() {
-      const xData = this.historyData.map(item => item.meteredTime)
-      const quantity = this.historyData.map(item => item.meteredValue)
-      const cost = this.historyData.map(item => item.meteredPrice)
-      const { meterCls } = this.queryParams
-      const option = {
-        tooltip: {
-          trigger: 'axis',
-          axisPointer: {
-            type: 'cross',
-            crossStyle: {
-              color: '#999'
-            }
-          }
-        },
-        grid: {
-          left: '5%'
-        },
-        legend: {
-          data: ['用量', '费用']
-        },
-        xAxis: {
-          type: 'category',
-          data: xData,
-          axisPointer: {
-            type: 'shadow'
-          }
-        },
-        yAxis: [
-          {
-            name: meterCls === 45 ? 'kW·h(千瓦时)' : '吨',
-            type: 'value'
-          },
-          {
-            name: '¥(元)',
-            type: 'value'
-          }
-        ],
-        series: [
-          {
-            name: '用量',
-            type: 'bar',
-            data: quantity,
-            barWidth: 30,
-            itemStyle: {
-              normal: {
-                color: '#6395FA'
-              }
-            }
-          },
-          {
-            name: '费用',
-            type: 'line',
-            yAxisIndex: 1,
-            data: cost,
-            showSymbol: true,
-            itemStyle: {
-              normal: {
-                color: '#5BD9A5'
-              }
-            }
-          }
-        ]
-      }
-      return option
-    }
-  },
-  methods: {
-    meterClsChange(value) {
-      this.queryParams.meterCls = value;
-      this.getList('current')
-      this.getList()
-    },
-    setDefaultMonthRange() {
-      const nowDay = new Date()
-      const lastMonth = new Date(nowDay.getFullYear(), nowDay.getMonth() - 1)
-      const months = this.getPreviousFiveMonthsYearMonth(lastMonth)
-      const {year, month} = months[months.length - 1]
-      const lastFiveMonth = month < 10 ? `0${month}` : month
-      this.queryParams.historyRange = [`${year}${lastFiveMonth}`, this.lastMonth]
-    },
-    getPreviousFiveMonthsYearMonth(date) {
-      const months = [...Array(5).keys()].map(i => {
-        const year = date.getFullYear()
-        const month = date.getMonth() - i
-        if (month < 0) {
-          return {
-            year: year - 1,
-            month: 12 + month
+        console.log(`${this.activeTab} 模式加载的树形数据:`, this.areaOptions)
+
+        // 设置默认展开和选中
+        if (this.areaOptions.length > 0) {
+          this.defaultExpandedKeys = []
+          const firstNode = this.findFirstValidNode(this.areaOptions[0])
+          if (firstNode) {
+            this.collectParentIds(firstNode, this.areaOptions)
+            this.$nextTick(() => {
+              this.$refs.tree.setCurrentKey(firstNode.id)
+              this.handleNodeClick(firstNode)
+            })
           }
         }
-        return {year, month: month + 1}
-      })
-      return months
-    },
-    formatDict(val, options, key = 'code', text = 'name') {
-      let name = null
-      this[options].forEach(item => {
-        if (item[key] === val) {
-          name = item[text]
-        }
-      })
-      return name
-    },
-    async getList(type) {
-      let startTime = null;
-      let endTime = null;
-      const { areaCode, meterCls, historyRange } = this.queryParams;
-
-      if (type === 'current') {
-        startTime = this.lastMonth;
-        endTime = this.lastMonth;
-      } else {
-        startTime = historyRange[0];
-        endTime = historyRange[1];
+      } catch (error) {
+        console.error('加载区域树失败', error)
+        this.$message.error('加载区域树失败')
       }
+    },
 
-      try {
-        // 个户
-        if (this.activeTab === 'second') {
-          const response = await chargingList({
-            areaPath: areaCode,
-            meterCls,
-            startTime,
-            endTime
-          });
-          if (response && response.data) {
-            if (type === 'current') {
-              this.currentData = response.data;
-              console.log("现在数据", this.currentData);
-            } else {
-              this.historyData = response.data;
-            }
-          } else {
-            console.error('请求数据失败,无数据返回');
-          }
-        } else {
-          const response = await overviewInfo({
-            areaPath: areaCode,
-            meterCls,
-            startTime,
-            endTime
-          });
-          if (response && response.data) {
-            if (type === 'current') {
-              this.currentData = response.data;
-            } else {
-              this.historyData = response.data;
-            }
-          } else {
-            console.error('请求数据失败,无数据返回');
+    // 查找第一个有效节点
+    findFirstValidNode(node) {
+      if (this.activeTab === 'overview') {
+        // 总览模式:直接返回第一个顶级区域
+        return node
+      } else {
+        // 个户模式:寻找第一个商户节点
+        if (node.type === 'seller') {
+          return node
+        }
+        if (node.children && node.children.length > 0) {
+          for (const child of node.children) {
+            const result = this.findFirstValidNode(child)
+            if (result) return result
           }
         }
-      } catch (error) {
-        console.error('请求数据失败123:', error);
       }
+      return null
     },
 
-    async handleTabChange() {
-      if (this.activeTab === 'first') {
-        await this.getAreaTreeByFacsCategory('Z', '',false);
-        if (this.areaOptions.length > 0) {
-          // 设置默认展开的节点
-          this.defalutArr = [];
-          this.recursion(this.areaOptions[0], this.defalutArr);
-          this.queryParams.areaCode = this.defalutArr.join('/');
-          this.$refs['tree'].setCurrentKey(this.defalutArr[this.defalutArr.length - 1]);
-          this.$refs['tree'].setCheckedKeys([this.queryParams.areaCode]);
+    // 收集父节点ID用于展开
+    collectParentIds(targetNode, tree, parentIds = []) {
+      for (const node of tree) {
+        if (node.id === targetNode.id) {
+          this.defaultExpandedKeys = [...parentIds]
+          return true
+        }
+        if (node.children && node.children.length > 0) {
+          if (this.collectParentIds(targetNode, node.children, [...parentIds, node.id])) {
+            return true
+          }
         }
-      } else if (this.activeTab === 'second') {
-        await this.getAreaTreeByTag('0', 'Area_01');
-        this.defalutArr = [];
-        this.recursion(this.areaOptions[0], this.defalutArr);
-        this.queryParams.areaCode = this.defalutArr.join('/');
-        this.$refs['tree'].setCurrentKey(this.defalutArr[this.defalutArr.length - 1]);
-        this.$refs['tree'].setCheckedKeys([this.queryParams.areaCode]);
-
       }
-      this.meterClsChange(45);
+      return false
     },
 
-    recursion(data, defalutArr) {
-      defalutArr.push(data.id)
-      if (data.children && data.children.length) {
-        this.recursion(data.children[0], defalutArr)
+    // 树节点点击
+    handleNodeClick(data) {
+      // 个户模式下只允许点击商户节点
+      if (this.activeTab === 'seller' && data.type !== 'seller') {
+        return
       }
+
+      console.log('选中节点:', data)
+      this.selectedArea = data
+      this.queryParams.areaCode = data.id
+      this.queryParams.objCode = data.id
+
+      this.refreshData()
     },
-    /** 查询区域树结构 */
-    async getAreaTreeByTag(parentCode, tagCode) {
-      await areaTreeByTag(parentCode, tagCode).then(response => {
-        this.areaOptions = response.data
-      })
-    },
-    /** 查询区域树结构 */
-    async getAreaTreeByFacsCategory(category, subCategory, recursion) {
-      await areaTreeByFacsCategory(category, subCategory, recursion).then(response => {
-        this.areaOptions = response.data
-      })
+
+    // Tab切换
+    handleTabChange(tab) {
+      console.log('切换到tab:', tab)
+      this.activeTab = tab
+      this.selectedArea = null
+      this.queryParams.areaCode = null
+      this.queryParams.objCode = null
+
+      // 重新加载树形数据
+      this.loadAreaTree()
     },
-    // 筛选节点
+
+    // 树节点过滤
     filterNode(value, data) {
       if (!value) return true
       return data.label.indexOf(value) !== -1
     },
-    handleNodeClick(data, node) {
-      // 商户树形结构只能点击最后一级
-      if (this.activeTab === 'second' && node.level < 3) {
-        return this.$refs['tree'].setCurrentKey(null);
-      }
-      const nodeArr = [];
-      const selectNode = this.$refs.tree.getNode(data);
-      this.platform(selectNode, nodeArr);
-      this.queryParams.areaCode = nodeArr.map(item => item.id).join('/');
-      this.$refs['tree'].setCurrentKey(this.queryParams.areaCode);
-      this.getList('current'); // 获取当月数据
-      this.getList('history'); // 获取历史数据
+
+    // 过滤树
+    filterTree() {
+      this.$refs.tree.filter(this.areaName)
     },
-    // 递归函数
-    platform(node, array) {
-      if (!node.parent) {
-        return;
-      }
-      array.unshift(node.data);
-      this.platform(node.parent, array);
+
+    // 刷新数据
+    refreshData() {
+      this.$nextTick(() => {
+        if (this.activeTab === 'overview' && this.$refs.overviewPanel) {
+          this.$refs.overviewPanel.loadData()
+        } else if (this.activeTab === 'seller' && this.$refs.sellerPanel) {
+          this.$refs.sellerPanel.loadData()
+        }
+      })
     },
+
+    // 获取完整的区域树数据(供调试使用)
+    getFullAreaTree() {
+      return this.areaOptions
+    },
+
+    // 调试方法:输出树结构
+    debugTreeData() {
+      console.log('=== 父组件树形数据调试 ===')
+      console.log('activeTab:', this.activeTab)
+      console.log('selectedArea:', this.selectedArea)
+      console.log('areaOptions:', this.areaOptions)
+      console.log('树的层级结构:')
+      this.printTreeStructure(this.areaOptions, 0)
+    },
+
+    // 递归打印树结构
+    printTreeStructure(nodes, level) {
+      if (!nodes) return
+      const indent = '  '.repeat(level)
+      nodes.forEach(node => {
+        console.log(`${indent}${node.id} - ${node.label} (${node.type || 'area'})`)
+        if (node.children && node.children.length > 0) {
+          this.printTreeStructure(node.children, level + 1)
+        }
+      })
+    }
   }
 }
 </script>
+
 <style lang="scss" scoped>
 .app-container {
-  ::v-deep .el-tabs__content {
-    overflow: initial;
-  }
-}
-.custom-form {
-  padding-bottom: 10px;
-}
-.panel {
-  display: flex;
-  flex-direction: column;
-  .panel-title {
-    display: flex;
-    align-items: center;
-    font-size: 14px;
-    font-weight: 500;
-    &::before {
-      content: '';
-      display: inline-block;
-      height: 14px;
-      width: 3px;
-      border-radius: 6px;
-      background: #409eff;
-      margin-right: 5px;
+  padding: 20px;
+  background: #f5f7fa;
+  min-height: calc(100vh - 84px);
+
+  .head-container {
+    background: #fff;
+    padding: 15px;
+    border-radius: 8px;
+    margin-bottom: 15px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+
+    &.tree-container {
+      max-height: calc(100vh - 280px);
+      overflow-y: auto;
+
+      ::v-deep .el-tree {
+        background: transparent;
+
+        .el-tree-node__content {
+          height: 40px;
+          padding: 0 8px;
+
+          &:hover {
+            background-color: #f5f7fa;
+          }
+        }
+
+        .el-tree-node.is-current > .el-tree-node__content {
+          background-color: #ecf5ff;
+          color: #409eff;
+        }
+
+        .custom-tree-node {
+          flex: 1;
+          display: flex;
+          align-items: center;
+          justify-content: space-between;
+          font-size: 14px;
+
+          .tree-label {
+            display: flex;
+            align-items: center;
+
+            .tree-icon {
+              margin-right: 8px;
+              font-size: 16px;
+              color: #909399;
+            }
+          }
+
+          .tree-tag {
+            margin-right: 8px;
+          }
+        }
+      }
     }
   }
-  .panel-body {
-    flex: 1;
-    background: #fff;
-    padding: 10px 0;
+
+  .tab-container {
+    .custom-tabs {
+      background: #fff;
+      border-radius: 12px;
+      box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+      overflow: hidden;
+
+      .tabs-header {
+        display: flex;
+        background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+        padding: 0;
+
+        .tab-item {
+          flex: 1;
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          height: 60px;
+          color: rgba(255, 255, 255, 0.7);
+          font-size: 16px;
+          font-weight: 500;
+          cursor: pointer;
+          transition: all 0.3s;
+          position: relative;
+
+          i {
+            margin-right: 8px;
+            font-size: 20px;
+          }
+
+          &:hover {
+            color: rgba(255, 255, 255, 0.9);
+            background: rgba(255, 255, 255, 0.1);
+          }
+
+          &.active {
+            color: #fff;
+            background: rgba(255, 255, 255, 0.2);
+
+            &::after {
+              content: '';
+              position: absolute;
+              bottom: 0;
+              left: 20%;
+              right: 20%;
+              height: 3px;
+              background: #fff;
+              border-radius: 2px;
+            }
+          }
+        }
+      }
+
+      .tabs-content {
+        padding: 0;
+      }
+    }
   }
 }
 </style>
-

+ 1101 - 0
ems-ui-cloud/src/views/mgr/components/OverviewPanel.vue

@@ -0,0 +1,1101 @@
+<template>
+  <div class="overview-panel">
+    <!-- 月度统计概览 -->
+    <div class="section-block">
+      <div class="section-header">
+        <h3 class="section-title">
+          <i class="el-icon-data-analysis"></i>
+          月度统计概览
+        </h3>
+        <div class="section-controls">
+          <el-form :inline="true" size="small">
+            <el-form-item label="统计周期">
+              <el-date-picker
+                v-model="currentMonth"
+                type="month"
+                placeholder="选择月份"
+                value-format="yyyyMM"
+                :picker-options="monthPickerOptions"
+                @change="handleCurrentMonthChange"
+              />
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+
+      <div v-if="currentStats" class="statistics-content">
+        <el-row :gutter="20">
+          <!-- 电量统计卡片 -->
+          <el-col :span="12">
+            <div class="stat-card elec-card">
+              <div class="card-header">
+                <i class="el-icon-lightning"></i>
+                <span>电量统计</span>
+              </div>
+              <div class="card-body">
+                <div class="stat-group">
+                  <div class="stat-item">
+                    <span class="label">商户用量</span>
+                    <div class="value-group">
+                      <span class="value">{{ formatNumber(getMerchantElecQuantity()) }}</span>
+                      <span class="unit">kWh</span>
+                      <span class="cost">¥{{ formatNumber(getMerchantElecCost(), 2) }}</span>
+                    </div>
+                  </div>
+                  <div class="stat-item">
+                    <span class="label">公摊用量</span>
+                    <div class="value-group">
+                      <span class="value">{{ formatNumber(getSharedElecQuantity()) }}</span>
+                      <span class="unit">kWh</span>
+                      <span class="cost">¥{{ formatNumber(getSharedElecCost(), 2) }}</span>
+                    </div>
+                  </div>
+                  <div class="stat-item total">
+                    <span class="label">总用量</span>
+                    <div class="value-group">
+                      <span class="value">{{ formatNumber(getTotalElecQuantity()) }}</span>
+                      <span class="unit">kWh</span>
+                      <span class="cost">¥{{ formatNumber(getTotalElecCost(), 2) }}</span>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </el-col>
+
+          <!-- 水量统计卡片 -->
+          <el-col :span="12">
+            <div class="stat-card water-card">
+              <div class="card-header">
+                <i class="el-icon-set-up"></i>
+                <span>水量统计</span>
+              </div>
+              <div class="card-body">
+                <div class="stat-group">
+                  <div class="stat-item">
+                    <span class="label">商户用量</span>
+                    <div class="value-group">
+                      <span class="value">{{ formatNumber(getMerchantWaterQuantity()) }}</span>
+                      <span class="unit">m³</span>
+                      <span class="cost">¥{{ formatNumber(getMerchantWaterCost(), 2) }}</span>
+                    </div>
+                  </div>
+                  <div class="stat-item">
+                    <span class="label">公摊用量</span>
+                    <div class="value-group">
+                      <span class="value">{{ formatNumber(getSharedWaterQuantity()) }}</span>
+                      <span class="unit">m³</span>
+                      <span class="cost">¥{{ formatNumber(getSharedWaterCost(), 2) }}</span>
+                    </div>
+                  </div>
+                  <div class="stat-item total">
+                    <span class="label">总用量</span>
+                    <div class="value-group">
+                      <span class="value">{{ formatNumber(getTotalWaterQuantity()) }}</span>
+                      <span class="unit">m³</span>
+                      <span class="cost">¥{{ formatNumber(getTotalWaterCost(), 2) }}</span>
+                    </div>
+                  </div>
+                </div>
+              </div>
+            </div>
+          </el-col>
+        </el-row>
+
+        <!-- 费用汇总 -->
+        <div class="cost-summary">
+          <div class="summary-item">
+            <i class="el-icon-lightning"></i>
+            <span class="label">电费合计</span>
+            <span class="value">¥{{ formatNumber(getTotalElecCost(), 2) }}</span>
+          </div>
+          <div class="summary-item">
+            <i class="el-icon-set-up"></i>
+            <span class="label">水费合计</span>
+            <span class="value">¥{{ formatNumber(getTotalWaterCost(), 2) }}</span>
+          </div>
+          <div class="summary-item total">
+            <i class="el-icon-coin"></i>
+            <span class="label">总费用</span>
+            <span class="value">¥{{ formatNumber(getGrandTotalCost(), 2) }}</span>
+          </div>
+        </div>
+      </div>
+
+      <div v-else class="no-data">
+        <i class="el-icon-info"></i>
+        <span>暂无数据</span>
+      </div>
+    </div>
+
+    <!-- 历史用能分析 -->
+    <div class="section-block">
+      <div class="section-header">
+        <h3 class="section-title">
+          <i class="el-icon-time"></i>
+          历史用能分析
+        </h3>
+        <div class="section-controls">
+          <el-form :inline="true" size="small">
+            <el-form-item label="统计时间">
+              <el-date-picker
+                v-model="historyDateRange"
+                type="monthrange"
+                range-separator="至"
+                start-placeholder="开始月份"
+                end-placeholder="结束月份"
+                value-format="yyyyMM"
+                :picker-options="historyPickerOptions"
+                @change="handleHistoryQuery"
+              />
+            </el-form-item>
+            <el-form-item>
+              <el-button type="primary" icon="el-icon-search" @click="handleHistoryQuery">查询</el-button>
+              <el-button type="warning" plain icon="el-icon-download" @click="handleExport">导出</el-button>
+            </el-form-item>
+          </el-form>
+        </div>
+      </div>
+
+      <!-- 趋势图表 -->
+      <el-card class="chart-card" shadow="never">
+        <div slot="header" class="card-header">
+          <span>用能趋势图</span>
+          <el-radio-group v-model="chartType" size="mini" @change="buildChart">
+            <el-radio-button label="quantity">用量</el-radio-button>
+            <el-radio-button label="cost">费用</el-radio-button>
+          </el-radio-group>
+        </div>
+        <div class="chart-container">
+          <BaseChart
+            v-if="chartOption && Object.keys(chartOption).length > 0"
+            width="100%"
+            height="350px"
+            :option="chartOption"
+          />
+          <div v-else class="no-data">
+            <i class="el-icon-info"></i>
+            <span>暂无数据</span>
+          </div>
+        </div>
+      </el-card>
+
+      <!-- 历史账单明细 -->
+      <el-card class="table-card" shadow="never">
+        <div slot="header" class="card-header">
+          <span>历史账单明细</span>
+          <div class="table-controls">
+            <!-- 电量统计列控制 -->
+            <el-popover
+              placement="bottom"
+              width="200"
+              trigger="click"
+              v-model="elecPopoverVisible"
+            >
+              <div class="column-control">
+                <el-checkbox
+                  v-model="elecColumnsAll"
+                  :indeterminate="elecIndeterminate"
+                  @change="handleElecAllChange"
+                >
+                  电量统计
+                </el-checkbox>
+                <div class="column-options">
+                  <el-checkbox
+                    v-for="col in elecColumnOptions"
+                    :key="col.value"
+                    v-model="col.checked"
+                    @change="handleElecColumnChange"
+                  >
+                    {{ col.label }}
+                  </el-checkbox>
+                </div>
+              </div>
+              <el-button slot="reference" size="mini" type="primary" plain>
+                <i class="el-icon-lightning"></i>
+                电量统计
+                <i :class="elecColumnsAll ? 'el-icon-check' : 'el-icon-arrow-down'"></i>
+              </el-button>
+            </el-popover>
+
+            <!-- 水量统计列控制 -->
+            <el-popover
+              placement="bottom"
+              width="200"
+              trigger="click"
+              v-model="waterPopoverVisible"
+            >
+              <div class="column-control">
+                <el-checkbox
+                  v-model="waterColumnsAll"
+                  :indeterminate="waterIndeterminate"
+                  @change="handleWaterAllChange"
+                >
+                  水量统计
+                </el-checkbox>
+                <div class="column-options">
+                  <el-checkbox
+                    v-for="col in waterColumnOptions"
+                    :key="col.value"
+                    v-model="col.checked"
+                    @change="handleWaterColumnChange"
+                  >
+                    {{ col.label }}
+                  </el-checkbox>
+                </div>
+              </div>
+              <el-button slot="reference" size="mini" type="success" plain>
+                <i class="el-icon-set-up"></i>
+                水量统计
+                <i :class="waterColumnsAll ? 'el-icon-check' : 'el-icon-arrow-down'"></i>
+              </el-button>
+            </el-popover>
+          </div>
+        </div>
+        <el-table
+          :data="historyData"
+          style="width: 100%"
+          v-loading="loading"
+          border
+          stripe
+          :default-sort="{ prop: 'meteredTime', order: 'descending' }"
+        >
+          <el-table-column prop="meteredTime" label="账单月份" align="center" width="100" fixed>
+            <template slot-scope="scope">
+              <el-tag type="primary" size="small">
+                {{ formatYearMonth(scope.row.meteredTime) }}
+              </el-tag>
+            </template>
+          </el-table-column>
+
+          <!-- 电量统计列 -->
+          <el-table-column v-if="showElecColumn('merchant_quantity')" label="商户电量(kWh)" align="right" width="120">
+            <template slot-scope="scope">
+              {{ formatNumber(getMerchantElecQuantityByRow(scope.row)) }}
+            </template>
+          </el-table-column>
+          <el-table-column v-if="showElecColumn('merchant_cost')" label="商户电费(¥)" align="right" width="110">
+            <template slot-scope="scope">
+              <span class="text-warning">{{ formatNumber(getMerchantElecCostByRow(scope.row), 2) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column v-if="showElecColumn('shared_quantity')" label="公摊电量(kWh)" align="right" width="120">
+            <template slot-scope="scope">
+              {{ formatNumber(getSharedElecQuantityByRow(scope.row)) }}
+            </template>
+          </el-table-column>
+          <el-table-column v-if="showElecColumn('shared_cost')" label="公摊电费(¥)" align="right" width="110">
+            <template slot-scope="scope">
+              <span class="text-info">{{ formatNumber(getSharedElecCostByRow(scope.row), 2) }}</span>
+            </template>
+          </el-table-column>
+
+          <!-- 水量统计列 -->
+          <el-table-column v-if="showWaterColumn('merchant_quantity')" label="商户水量(m³)" align="right" width="110">
+            <template slot-scope="scope">
+              {{ formatNumber(getMerchantWaterQuantityByRow(scope.row)) }}
+            </template>
+          </el-table-column>
+          <el-table-column v-if="showWaterColumn('merchant_cost')" label="商户水费(¥)" align="right" width="110">
+            <template slot-scope="scope">
+              <span class="text-warning">{{ formatNumber(getMerchantWaterCostByRow(scope.row), 2) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column v-if="showWaterColumn('shared_quantity')" label="公摊水量(m³)" align="right" width="110">
+            <template slot-scope="scope">
+              {{ formatNumber(getSharedWaterQuantityByRow(scope.row)) }}
+            </template>
+          </el-table-column>
+          <el-table-column v-if="showWaterColumn('shared_cost')" label="公摊水费(¥)" align="right" width="110">
+            <template slot-scope="scope">
+              <span class="text-info">{{ formatNumber(getSharedWaterCostByRow(scope.row), 2) }}</span>
+            </template>
+          </el-table-column>
+
+          <el-table-column label="总费用(¥)" align="center" width="130" fixed="right">
+            <template slot-scope="scope">
+              <span class="total-cost">¥{{ formatNumber(calculateRowTotalCost(scope.row), 2) }}</span>
+            </template>
+          </el-table-column>
+        </el-table>
+      </el-card>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getOverview, getOverviewHis } from '@/api/mgr/charging'
+import BaseChart from '@/components/BaseChart'
+
+export default {
+  name: 'OverviewPanel',
+  components: { BaseChart },
+  props: {
+    queryParams: {
+      type: Object,
+      required: true
+    },
+    selectedArea: {
+      type: Object,
+      default: null
+    }
+  },
+  data() {
+    const now = new Date()
+    const year = now.getFullYear()
+    const month = (now.getMonth() + 1).toString().padStart(2, '0')
+
+    return {
+      loading: false,
+      currentMonth: `${year}${month}`,
+      currentStats: null,
+      historyDateRange: [],
+      historyData: [],
+      chartType: 'quantity',
+      chartOption: {},
+
+      // 列显示控制
+      elecPopoverVisible: false,
+      waterPopoverVisible: false,
+      elecColumnsAll: true,
+      waterColumnsAll: true,
+      elecIndeterminate: false,
+      waterIndeterminate: false,
+
+      elecColumnOptions: [
+        { label: '商户电量', value: 'merchant_quantity', checked: true },
+        { label: '商户电费', value: 'merchant_cost', checked: true },
+        { label: '公摊电量', value: 'shared_quantity', checked: true },
+        { label: '公摊电费', value: 'shared_cost', checked: true }
+      ],
+
+      waterColumnOptions: [
+        { label: '商户水量', value: 'merchant_quantity', checked: true },
+        { label: '商户水费', value: 'merchant_cost', checked: true },
+        { label: '公摊水量', value: 'shared_quantity', checked: true },
+        { label: '公摊水费', value: 'shared_cost', checked: true }
+      ],
+
+      monthPickerOptions: {
+        disabledDate(time) {
+          return time.getTime() > Date.now()
+        }
+      },
+      historyPickerOptions: {
+        disabledDate(time) {
+          return time.getTime() > Date.now()
+        }
+      }
+    }
+  },
+  created() {
+    this.initHistoryDateRange()
+  },
+  mounted() {
+    this.loadData()
+  },
+  watch: {
+    selectedArea: {
+      handler() {
+        this.loadData()
+      },
+      deep: true
+    }
+  },
+  methods: {
+    initHistoryDateRange() {
+      const now = new Date()
+      const startMonth = new Date(now.getFullYear(), now.getMonth() - 5, 1)
+      this.historyDateRange = [
+        this.formatMonth(startMonth),
+        this.formatMonth(now)
+      ]
+    },
+
+    formatMonth(date) {
+      const year = date.getFullYear()
+      const month = (date.getMonth() + 1).toString().padStart(2, '0')
+      return `${year}${month}`
+    },
+
+    formatYearMonth(ym) {
+      if (!ym || ym.length !== 6) return ym
+      return `${ym.substring(0, 4)}-${ym.substring(4, 6)}`
+    },
+
+    formatNumber(value, decimals = 0) {
+      if (value == null || value === undefined || isNaN(value)) {
+        return '0' + (decimals > 0 ? '.' + '0'.repeat(decimals) : '')
+      }
+      return Number(value).toFixed(decimals)
+    },
+
+    // 列显示控制方法
+    handleElecAllChange(val) {
+      this.elecColumnOptions.forEach(col => {
+        col.checked = val
+      })
+      this.elecIndeterminate = false
+    },
+
+    handleElecColumnChange() {
+      const checkedCount = this.elecColumnOptions.filter(col => col.checked).length
+      this.elecColumnsAll = checkedCount === this.elecColumnOptions.length
+      this.elecIndeterminate = checkedCount > 0 && checkedCount < this.elecColumnOptions.length
+    },
+
+    handleWaterAllChange(val) {
+      this.waterColumnOptions.forEach(col => {
+        col.checked = val
+      })
+      this.waterIndeterminate = false
+    },
+
+    handleWaterColumnChange() {
+      const checkedCount = this.waterColumnOptions.filter(col => col.checked).length
+      this.waterColumnsAll = checkedCount === this.waterColumnOptions.length
+      this.waterIndeterminate = checkedCount > 0 && checkedCount < this.waterColumnOptions.length
+    },
+
+    showElecColumn(columnValue) {
+      const column = this.elecColumnOptions.find(col => col.value === columnValue)
+      return column && column.checked
+    },
+
+    showWaterColumn(columnValue) {
+      const column = this.waterColumnOptions.find(col => col.value === columnValue)
+      return column && column.checked
+    },
+
+    // 动态计算行总费用
+    calculateRowTotalCost(row) {
+      let total = 0
+
+      // 根据显示的列动态计算
+      if (this.showElecColumn('merchant_cost')) {
+        total += this.getMerchantElecCostByRow(row)
+      }
+      if (this.showElecColumn('shared_cost')) {
+        total += this.getSharedElecCostByRow(row)
+      }
+      if (this.showWaterColumn('merchant_cost')) {
+        total += this.getMerchantWaterCostByRow(row)
+      }
+      if (this.showWaterColumn('shared_cost')) {
+        total += this.getSharedWaterCostByRow(row)
+      }
+
+      return total
+    },
+
+    // 获取商户电量
+    getMerchantElecQuantity() {
+      if (!this.currentStats) return 0
+      const auto = (this.currentStats.elecAutoStats && this.currentStats.elecAutoStats.totalQuantity) || 0
+      const manual = (this.currentStats.elecManualStats && this.currentStats.elecManualStats.totalQuantity) || 0
+      return auto + manual
+    },
+
+    getMerchantElecQuantityByRow(row) {
+      const auto = (row.elecAutoStats && row.elecAutoStats.totalQuantity) || 0
+      const manual = (row.elecManualStats && row.elecManualStats.totalQuantity) || 0
+      return auto + manual
+    },
+
+    // 获取商户电费
+    getMerchantElecCost() {
+      if (!this.currentStats) return 0
+      const auto = (this.currentStats.elecAutoStats && this.currentStats.elecAutoStats.totalCost) || 0
+      const manual = (this.currentStats.elecManualStats && this.currentStats.elecManualStats.totalCost) || 0
+      return auto + manual
+    },
+
+    getMerchantElecCostByRow(row) {
+      const auto = (row.elecAutoStats && row.elecAutoStats.totalCost) || 0
+      const manual = (row.elecManualStats && row.elecManualStats.totalCost) || 0
+      return auto + manual
+    },
+
+    // 获取公摊电量
+    getSharedElecQuantity() {
+      return (this.currentStats && this.currentStats.sharedData && this.currentStats.sharedData.elecSharedQuantity) || 0
+    },
+
+    getSharedElecQuantityByRow(row) {
+      return (row.sharedData && row.sharedData.elecSharedQuantity) || 0
+    },
+
+    // 获取公摊电费
+    getSharedElecCost() {
+      return (this.currentStats && this.currentStats.sharedData && this.currentStats.sharedData.elecSharedCost) || 0
+    },
+
+    getSharedElecCostByRow(row) {
+      return (row.sharedData && row.sharedData.elecSharedCost) || 0
+    },
+
+    // 获取总电量
+    getTotalElecQuantity() {
+      return (this.currentStats && this.currentStats.totalStats && this.currentStats.totalStats.elecTotalQuantity) || 0
+    },
+
+    getTotalElecQuantityByRow(row) {
+      return (row.totalStats && row.totalStats.elecTotalQuantity) || 0
+    },
+
+    // 获取总电费
+    getTotalElecCost() {
+      return (this.currentStats && this.currentStats.totalStats && this.currentStats.totalStats.elecTotalCost) || 0
+    },
+
+    getTotalElecCostByRow(row) {
+      return (row.totalStats && row.totalStats.elecTotalCost) || 0
+    },
+
+    // 获取商户水量
+    getMerchantWaterQuantity() {
+      if (!this.currentStats) return 0
+      const auto = (this.currentStats.waterAutoStats && this.currentStats.waterAutoStats.totalQuantity) || 0
+      const manual = (this.currentStats.waterManualStats && this.currentStats.waterManualStats.totalQuantity) || 0
+      return auto + manual
+    },
+
+    getMerchantWaterQuantityByRow(row) {
+      const auto = (row.waterAutoStats && row.waterAutoStats.totalQuantity) || 0
+      const manual = (row.waterManualStats && row.waterManualStats.totalQuantity) || 0
+      return auto + manual
+    },
+
+    // 获取商户水费
+    getMerchantWaterCost() {
+      if (!this.currentStats) return 0
+      const auto = (this.currentStats.waterAutoStats && this.currentStats.waterAutoStats.totalCost) || 0
+      const manual = (this.currentStats.waterManualStats && this.currentStats.waterManualStats.totalCost) || 0
+      return auto + manual
+    },
+
+    getMerchantWaterCostByRow(row) {
+      const auto = (row.waterAutoStats && row.waterAutoStats.totalCost) || 0
+      const manual = (row.waterManualStats && row.waterManualStats.totalCost) || 0
+      return auto + manual
+    },
+
+    // 获取公摊水量
+    getSharedWaterQuantity() {
+      return (this.currentStats && this.currentStats.sharedData && this.currentStats.sharedData.waterSharedQuantity) || 0
+    },
+
+    getSharedWaterQuantityByRow(row) {
+      return (row.sharedData && row.sharedData.waterSharedQuantity) || 0
+    },
+
+    // 获取公摊水费
+    getSharedWaterCost() {
+      return (this.currentStats && this.currentStats.sharedData && this.currentStats.sharedData.waterSharedCost) || 0
+    },
+
+    getSharedWaterCostByRow(row) {
+      return (row.sharedData && row.sharedData.waterSharedCost) || 0
+    },
+
+    // 获取总水量
+    getTotalWaterQuantity() {
+      return (this.currentStats && this.currentStats.totalStats && this.currentStats.totalStats.waterTotalQuantity) || 0
+    },
+
+    getTotalWaterQuantityByRow(row) {
+      return (row.totalStats && row.totalStats.waterTotalQuantity) || 0
+    },
+
+    // 获取总水费
+    getTotalWaterCost() {
+      return (this.currentStats && this.currentStats.totalStats && this.currentStats.totalStats.waterTotalCost) || 0
+    },
+
+    getTotalWaterCostByRow(row) {
+      return (row.totalStats && row.totalStats.waterTotalCost) || 0
+    },
+
+    // 获取总费用
+    getGrandTotalCost() {
+      return (this.currentStats && this.currentStats.totalStats && this.currentStats.totalStats.grandTotalCost) || 0
+    },
+
+    getGrandTotalCostByRow(row) {
+      return (row.totalStats && row.totalStats.grandTotalCost) || 0
+    },
+
+    // 主要的加载数据方法
+    async loadData() {
+      if (!this.selectedArea || !this.queryParams.areaCode) {
+        return
+      }
+
+      await this.loadCurrentMonthData()
+      await this.loadHistoryData()
+    },
+
+    // 加载当月数据
+    async loadCurrentMonthData() {
+      if (!this.selectedArea || !this.queryParams.areaCode) {
+        return
+      }
+
+      try {
+        const params = {
+          areaCode: this.queryParams.areaCode,
+          startTime: this.currentMonth,
+          endTime: this.currentMonth,
+          includeAuto: true,
+          includeManual: true,
+          includeShared: true
+        }
+
+        const response = await getOverview(params)
+        if (response.code === 200) {
+          this.currentStats = response.data
+        }
+      } catch (error) {
+        console.error('加载月度数据失败', error)
+        this.$message.error('加载月度数据失败')
+      }
+    },
+
+    // 加载历史数据
+    async loadHistoryData() {
+      if (!this.selectedArea || !this.queryParams.areaCode) {
+        return
+      }
+
+      this.loading = true
+      try {
+        const params = {
+          areaCode: this.queryParams.areaCode,
+          objCode: this.queryParams.objCode,
+          startTime: this.historyDateRange[0],
+          endTime: this.historyDateRange[1],
+          includeAuto: true,
+          includeManual: true,
+          includeShared: true
+        }
+
+        const response = await getOverviewHis(params)
+        if (response.code === 200) {
+          this.historyData = response.data || []
+          this.buildChart()
+        }
+      } catch (error) {
+        console.error('加载历史数据失败', error)
+        this.$message.error('加载历史数据失败')
+      } finally {
+        this.loading = false
+      }
+    },
+
+    buildChart() {
+      if (!this.historyData || this.historyData.length === 0) {
+        this.chartOption = {}
+        return
+      }
+
+      const xData = this.historyData.map(item => this.formatYearMonth(item.meteredTime))
+      const series = []
+
+      if (this.chartType === 'quantity') {
+        series.push({
+          name: '电量-商户',
+          type: 'bar',
+          stack: '电量',
+          data: this.historyData.map(item => this.getMerchantElecQuantityByRow(item)),
+          itemStyle: { color: '#409EFF' }
+        })
+        series.push({
+          name: '电量-公摊',
+          type: 'bar',
+          stack: '电量',
+          data: this.historyData.map(item => this.getSharedElecQuantityByRow(item)),
+          itemStyle: { color: '#67C23A' }
+        })
+        series.push({
+          name: '水量-商户',
+          type: 'bar',
+          stack: '水量',
+          data: this.historyData.map(item => this.getMerchantWaterQuantityByRow(item)),
+          itemStyle: { color: '#E6A23C' }
+        })
+        series.push({
+          name: '水量-公摊',
+          type: 'bar',
+          stack: '水量',
+          data: this.historyData.map(item => this.getSharedWaterQuantityByRow(item)),
+          itemStyle: { color: '#F56C6C' }
+        })
+      } else {
+        series.push({
+          name: '电费',
+          type: 'line',
+          data: this.historyData.map(item => this.getTotalElecCostByRow(item)),
+          smooth: true,
+          itemStyle: { color: '#409EFF' }
+        })
+        series.push({
+          name: '水费',
+          type: 'line',
+          data: this.historyData.map(item => this.getTotalWaterCostByRow(item)),
+          smooth: true,
+          itemStyle: { color: '#67C23A' }
+        })
+      }
+
+      this.chartOption = {
+        tooltip: {
+          trigger: 'axis',
+          axisPointer: {
+            type: this.chartType === 'quantity' ? 'shadow' : 'cross'
+          }
+        },
+        legend: {
+          data: series.map(s => s.name),
+          top: 10
+        },
+        grid: {
+          left: '3%',
+          right: '4%',
+          bottom: '3%',
+          top: '15%',
+          containLabel: true
+        },
+        xAxis: {
+          type: 'category',
+          data: xData
+        },
+        yAxis: {
+          type: 'value',
+          name: this.chartType === 'quantity' ? '用量' : '费用(¥)'
+        },
+        series: series
+      }
+    },
+
+    handleCurrentMonthChange() {
+      this.loadCurrentMonthData()
+    },
+
+    handleHistoryQuery() {
+      this.loadHistoryData()
+    },
+
+    handleExport() {
+      const params = {
+        areaCode: this.queryParams.areaCode,
+        startTime: this.historyDateRange[0],
+        endTime: this.historyDateRange[1],
+        includeAuto: true,
+        includeManual: true,
+        includeShared: true
+      }
+
+      this.download('/ems/charging/bill/export', params, `总览账单_${new Date().getTime()}.xlsx`)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.overview-panel {
+  padding: 20px;
+
+  .section-block {
+    background: #fff;
+    border-radius: 12px;
+    padding: 20px;
+    margin-bottom: 20px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+
+    .section-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20px;
+      padding-bottom: 15px;
+      border-bottom: 2px solid #f0f2f5;
+
+      .section-title {
+        margin: 0;
+        font-size: 18px;
+        font-weight: 600;
+        color: #303133;
+        display: flex;
+        align-items: center;
+
+        i {
+          margin-right: 10px;
+          color: #409eff;
+          font-size: 20px;
+        }
+      }
+
+      .section-controls {
+        .el-form-item {
+          margin-bottom: 0;
+        }
+      }
+    }
+  }
+
+  .statistics-content {
+    .stat-card {
+      background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
+      border-radius: 12px;
+      padding: 20px;
+      border: 1px solid #e6e8eb;
+      transition: all 0.3s;
+
+      &:hover {
+        transform: translateY(-2px);
+        box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
+      }
+
+      &.elec-card {
+        border-left: 4px solid #f39c12;
+      }
+
+      &.water-card {
+        border-left: 4px solid #3498db;
+      }
+
+      .card-header {
+        display: flex;
+        align-items: center;
+        margin-bottom: 20px;
+        padding-bottom: 12px;
+        border-bottom: 1px solid #e6e8eb;
+
+        i {
+          font-size: 24px;
+          margin-right: 10px;
+        }
+
+        span {
+          font-size: 16px;
+          font-weight: 600;
+          color: #303133;
+        }
+      }
+
+      .card-body {
+        .stat-group {
+          .stat-item {
+            display: flex;
+            align-items: center;
+            justify-content: space-between;
+            padding: 12px 0;
+            border-bottom: 1px dashed #e6e8eb;
+
+            &:last-child {
+              border-bottom: none;
+            }
+
+            .label {
+              color: #606266;
+              font-size: 14px;
+              min-width: 80px;
+            }
+
+            .value-group {
+              display: flex;
+              align-items: center;
+              gap: 12px;
+
+              .value {
+                font-size: 18px;
+                font-weight: 600;
+                color: #303133;
+              }
+
+              .unit {
+                font-size: 14px;
+                color: #909399;
+              }
+
+              .cost {
+                font-size: 16px;
+                color: #67c23a;
+                font-weight: 500;
+                margin-left: auto;
+                min-width: 100px;
+                text-align: right;
+              }
+            }
+
+            &.total {
+              background: #f8f9fa;
+              margin: 15px -20px -20px;
+              padding: 15px 20px;
+              border-radius: 0 0 12px 12px;
+              border: none;
+
+              .label {
+                font-weight: 600;
+              }
+
+              .value-group {
+                .value {
+                  font-size: 20px;
+                  color: #409eff;
+                }
+
+                .cost {
+                  font-size: 18px;
+                  color: #e6a23c;
+                }
+              }
+            }
+          }
+        }
+      }
+    }
+
+    .cost-summary {
+      margin-top: 20px;
+      padding: 20px;
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      border-radius: 12px;
+      display: flex;
+      justify-content: space-around;
+      align-items: center;
+
+      .summary-item {
+        display: flex;
+        align-items: center;
+        color: #fff;
+
+        i {
+          font-size: 24px;
+          margin-right: 10px;
+          opacity: 0.9;
+        }
+
+        .label {
+          margin-right: 10px;
+          font-size: 14px;
+          opacity: 0.9;
+        }
+
+        .value {
+          font-size: 20px;
+          font-weight: 700;
+        }
+
+        &.total {
+          padding: 0 30px;
+          border-left: 2px solid rgba(255, 255, 255, 0.3);
+          border-right: 2px solid rgba(255, 255, 255, 0.3);
+
+          .value {
+            font-size: 24px;
+          }
+        }
+      }
+    }
+  }
+
+  .chart-card, .table-card {
+    border: none;
+    margin-top: 20px;
+
+    ::v-deep .el-card__header {
+      border-bottom: 1px solid #e6e8eb;
+      padding: 15px 20px;
+    }
+
+    .card-header {
+      display: flex;
+      align-items: center;
+      justify-content: space-between;
+      font-size: 16px;
+      font-weight: 600;
+      color: #303133;
+    }
+
+    .chart-container {
+      min-height: 350px;
+
+      .no-data {
+        height: 350px;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        justify-content: center;
+        color: #909399;
+
+        i {
+          font-size: 48px;
+          margin-bottom: 15px;
+        }
+      }
+    }
+  }
+
+  .table-card {
+    .table-controls {
+      display: flex;
+      gap: 10px;
+
+      .el-button {
+        i:last-child {
+          margin-left: 5px;
+          transition: transform 0.3s;
+        }
+      }
+    }
+
+    .column-control {
+      padding: 10px 0;
+
+      .el-checkbox {
+        display: block;
+        margin-bottom: 10px;
+        font-weight: 600;
+      }
+
+      .column-options {
+        padding-left: 20px;
+
+        .el-checkbox {
+          font-weight: normal;
+          margin-bottom: 8px;
+        }
+      }
+    }
+
+    .total-cost {
+      font-size: 16px;
+      font-weight: bold;
+      color: #f56c6c;
+    }
+
+    .text-primary { color: #409eff; }
+    .text-warning { color: #e6a23c; }
+    .text-info { color: #909399; }
+    .text-success { color: #67c23a; }
+    .font-bold { font-weight: 600; }
+  }
+
+  .no-data {
+    padding: 60px;
+    text-align: center;
+    color: #909399;
+
+    i {
+      font-size: 48px;
+      margin-bottom: 15px;
+      display: block;
+    }
+
+    span {
+      font-size: 16px;
+    }
+  }
+}
+</style>

+ 1446 - 0
ems-ui-cloud/src/views/mgr/components/SellerPanel.vue

@@ -0,0 +1,1446 @@
+<template>
+  <div class="seller-panel">
+    <!-- 未选择商户提示 -->
+    <div v-if="!selectedArea || selectedArea.type !== 'seller'" class="empty-state">
+      <div class="empty-content">
+        <i class="el-icon-user-solid"></i>
+        <h3>请选择商户</h3>
+        <p>请在左侧树中选择一个商户查看其用能计费信息</p>
+      </div>
+    </div>
+
+    <!-- 商户数据展示 -->
+    <div v-else>
+      <!-- 月度统计概览 -->
+      <div class="section-block">
+        <div class="section-header">
+          <h3 class="section-title">
+            <i class="el-icon-data-analysis"></i>
+            月度统计概览
+          </h3>
+          <div class="section-controls">
+            <el-form :inline="true" size="small">
+              <el-form-item label="统计周期">
+                <el-date-picker
+                  v-model="currentMonth"
+                  type="month"
+                  placeholder="选择月份"
+                  value-format="yyyyMM"
+                  :picker-options="monthPickerOptions"
+                  @change="handleMonthChange"
+                />
+              </el-form-item>
+            </el-form>
+          </div>
+        </div>
+
+        <!-- 商户信息 -->
+        <div class="merchant-info">
+          <div class="info-header">
+            <div class="basic-info">
+              <h2 class="merchant-name">
+                <i class="el-icon-user"></i>
+                {{ selectedArea.label }}
+              </h2>
+              <div class="merchant-tags">
+                <el-tag type="info" size="small">编号: {{ selectedArea.id }}</el-tag>
+                <el-tag type="primary" size="small">{{ formatYearMonth(currentMonth) }}</el-tag>
+              </div>
+            </div>
+            <div v-if="pricingStrategy" class="pricing-info">
+              <div class="strategy-title">
+                {{ pricingStrategy.areaShortName || pricingStrategy.areaName }}计价策略
+              </div>
+              <div class="price-item">
+                <span class="label">电价:</span>
+                <span class="value">{{ pricingStrategy.elecUnitPrice }}元/kWh</span>
+              </div>
+              <div class="price-item">
+                <span class="label">水价:</span>
+                <span class="value">{{ pricingStrategy.waterUnitPrice }}元/m³</span>
+              </div>
+            </div>
+            <div v-else class="pricing-info">
+              <el-tag type="danger">未配置计费策略</el-tag>
+            </div>
+          </div>
+        </div>
+
+        <!-- 用能统计 -->
+        <div v-if="billData" class="statistics-content">
+          <el-row :gutter="20">
+            <!-- 电量统计 -->
+            <el-col :span="12">
+              <div class="usage-card elec-card">
+                <div class="card-header">
+                  <i class="el-icon-lightning"></i>
+                  <span>电量统计</span>
+                </div>
+                <div class="card-body">
+                  <!-- 自用电量 -->
+                  <div class="usage-section">
+                    <h4 class="section-subtitle">商户自用</h4>
+                    <div class="usage-detail">
+                      <div class="main-usage">
+                        <span class="label">总用量</span>
+                        <span class="value">{{ formatNumber(getMerchantElecQuantity()) }}</span>
+                        <span class="unit">kWh</span>
+                        <span class="cost">¥{{ formatNumber(getMerchantElecCost(), 2) }}</span>
+                      </div>
+                      <!-- 峰谷电量分解 -->
+                      <div v-if="hasPeakValleyData" class="peak-valley-breakdown">
+                        <div class="breakdown-title">峰谷分时明细:</div>
+                        <div class="breakdown-items">
+                          <div v-if="hasSharpPeak" class="breakdown-item">
+                            <span class="period-tag sharp">尖峰</span>
+                            <span class="period-value">{{ formatNumber(getSharpPeakQuantity()) }} kWh</span>
+                          </div>
+                          <div v-if="hasPeak" class="breakdown-item">
+                            <span class="period-tag peak">峰段</span>
+                            <span class="period-value">{{ formatNumber(getPeakQuantity()) }} kWh</span>
+                          </div>
+                          <div v-if="hasNormal" class="breakdown-item">
+                            <span class="period-tag normal">平段</span>
+                            <span class="period-value">{{ formatNumber(getNormalQuantity()) }} kWh</span>
+                          </div>
+                          <div v-if="hasValley" class="breakdown-item">
+                            <span class="period-tag valley">谷段</span>
+                            <span class="period-value">{{ formatNumber(getValleyQuantity()) }} kWh</span>
+                          </div>
+                          <div v-if="hasDeepValley" class="breakdown-item">
+                            <span class="period-tag deep-valley">深谷</span>
+                            <span class="period-value">{{ formatNumber(getDeepValleyQuantity()) }} kWh</span>
+                          </div>
+                        </div>
+                      </div>
+                    </div>
+                  </div>
+
+                  <!-- 公摊电量 -->
+                  <div v-if="hasElecShared" class="usage-section">
+                    <h4 class="section-subtitle">
+                      公摊分担
+                      <el-tag size="mini" type="info">{{ getElecSharedTypeName() }}</el-tag>
+                    </h4>
+                    <div class="usage-detail">
+                      <div class="main-usage">
+                        <span class="label">公摊量</span>
+                        <span class="value">{{ formatNumber(getElecSharedQuantity()) }}</span>
+                        <span class="unit">kWh</span>
+                        <span class="cost">¥{{ formatNumber(getElecSharedCost(), 2) }}</span>
+                      </div>
+                    </div>
+                  </div>
+
+                  <!-- 电量合计 -->
+                  <div class="usage-total">
+                    <span class="label">电量合计</span>
+                    <span class="value">{{ formatNumber(getTotalElecQuantity()) }} kWh</span>
+                    <span class="cost">¥{{ formatNumber(getTotalElecCost(), 2) }}</span>
+                  </div>
+                </div>
+              </div>
+            </el-col>
+
+            <!-- 水量统计 -->
+            <el-col :span="12">
+              <div class="usage-card water-card">
+                <div class="card-header">
+                  <i class="el-icon-set-up"></i>
+                  <span>水量统计</span>
+                </div>
+                <div class="card-body">
+                  <!-- 自用水量 -->
+                  <div class="usage-section">
+                    <h4 class="section-subtitle">商户自用</h4>
+                    <div class="usage-detail">
+                      <div class="main-usage">
+                        <span class="label">总用量</span>
+                        <span class="value">{{ formatNumber(getMerchantWaterQuantity()) }}</span>
+                        <span class="unit">m³</span>
+                        <span class="cost">¥{{ formatNumber(getMerchantWaterCost(), 2) }}</span>
+                      </div>
+                    </div>
+                  </div>
+
+                  <!-- 公摊水量 -->
+                  <div v-if="hasWaterShared" class="usage-section">
+                    <h4 class="section-subtitle">
+                      公摊分担
+                      <el-tag size="mini" type="info">{{ getWaterSharedTypeName() }}</el-tag>
+                    </h4>
+                    <div class="usage-detail">
+                      <div class="main-usage">
+                        <span class="label">公摊量</span>
+                        <span class="value">{{ formatNumber(getWaterSharedQuantity()) }}</span>
+                        <span class="unit">m³</span>
+                        <span class="cost">¥{{ formatNumber(getWaterSharedCost(), 2) }}</span>
+                      </div>
+                    </div>
+                  </div>
+
+                  <!-- 水量合计 -->
+                  <div class="usage-total">
+                    <span class="label">水量合计</span>
+                    <span class="value">{{ formatNumber(getTotalWaterQuantity()) }} m³</span>
+                    <span class="cost">¥{{ formatNumber(getTotalWaterCost(), 2) }}</span>
+                  </div>
+                </div>
+              </div>
+            </el-col>
+          </el-row>
+
+          <!-- 费用汇总 -->
+          <div class="cost-summary-card">
+            <div class="summary-item">
+              <i class="el-icon-lightning"></i>
+              <span class="label">电费合计</span>
+              <span class="value">¥{{ formatNumber(getTotalElecCost(), 2) }}</span>
+            </div>
+            <div class="summary-item">
+              <i class="el-icon-set-up"></i>
+              <span class="label">水费合计</span>
+              <span class="value">¥{{ formatNumber(getTotalWaterCost(), 2) }}</span>
+            </div>
+            <div class="summary-item">
+              <i class="el-icon-share"></i>
+              <span class="label">公摊合计</span>
+              <span class="value">¥{{ formatNumber(getTotalSharedCost(), 2) }}</span>
+            </div>
+            <div class="summary-item total">
+              <i class="el-icon-coin"></i>
+              <span class="label">总计费用</span>
+              <span class="value">¥{{ formatNumber(getGrandTotalCost(), 2) }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+
+      <!-- 历史账单明细 -->
+      <div class="section-block">
+        <div class="section-header">
+          <h3 class="section-title">
+            <i class="el-icon-time"></i>
+            历史账单明细
+          </h3>
+          <div class="section-controls">
+            <el-form :inline="true" size="small">
+              <el-form-item label="统计时间">
+                <el-date-picker
+                  v-model="historyDateRange"
+                  type="monthrange"
+                  range-separator="至"
+                  start-placeholder="开始月份"
+                  end-placeholder="结束月份"
+                  value-format="yyyyMM"
+                  :picker-options="historyPickerOptions"
+                  @change="handleHistoryQuery"
+                />
+              </el-form-item>
+              <el-form-item>
+                <el-button type="primary" icon="el-icon-search" @click="handleHistoryQuery">查询</el-button>
+                <el-button type="warning" plain icon="el-icon-download" @click="handleExport">导出</el-button>
+              </el-form-item>
+            </el-form>
+          </div>
+        </div>
+
+        <el-card shadow="never">
+          <div slot="header" class="table-header">
+            <span>账单数据</span>
+            <div class="table-controls">
+              <!-- 电量统计列控制 -->
+              <el-popover
+                placement="bottom"
+                width="200"
+                trigger="click"
+                v-model="elecPopoverVisible"
+              >
+                <div class="column-control">
+                  <el-checkbox
+                    v-model="elecColumnsAll"
+                    :indeterminate="elecIndeterminate"
+                    @change="handleElecAllChange"
+                  >
+                    电量统计
+                  </el-checkbox>
+                  <div class="column-options">
+                    <el-checkbox
+                      v-for="col in elecColumnOptions"
+                      :key="col.value"
+                      v-model="col.checked"
+                      @change="handleElecColumnChange"
+                    >
+                      {{ col.label }}
+                    </el-checkbox>
+                  </div>
+                </div>
+                <el-button slot="reference" size="mini" type="primary" plain>
+                  <i class="el-icon-lightning"></i>
+                  电量统计
+                  <i :class="elecColumnsAll ? 'el-icon-check' : 'el-icon-arrow-down'"></i>
+                </el-button>
+              </el-popover>
+
+              <!-- 水量统计列控制 -->
+              <el-popover
+                placement="bottom"
+                width="200"
+                trigger="click"
+                v-model="waterPopoverVisible"
+              >
+                <div class="column-control">
+                  <el-checkbox
+                    v-model="waterColumnsAll"
+                    :indeterminate="waterIndeterminate"
+                    @change="handleWaterAllChange"
+                  >
+                    水量统计
+                  </el-checkbox>
+                  <div class="column-options">
+                    <el-checkbox
+                      v-for="col in waterColumnOptions"
+                      :key="col.value"
+                      v-model="col.checked"
+                      @change="handleWaterColumnChange"
+                    >
+                      {{ col.label }}
+                    </el-checkbox>
+                  </div>
+                </div>
+                <el-button slot="reference" size="mini" type="success" plain>
+                  <i class="el-icon-set-up"></i>
+                  水量统计
+                  <i :class="waterColumnsAll ? 'el-icon-check' : 'el-icon-arrow-down'"></i>
+                </el-button>
+              </el-popover>
+            </div>
+          </div>
+
+          <el-table
+            :data="historyData"
+            style="width: 100%"
+            v-loading="historyLoading"
+            border
+            stripe
+            :default-sort="{ prop: 'meteredTime', order: 'descending' }"
+          >
+            <el-table-column prop="meteredTime" label="账单月份" align="center" width="100" fixed>
+              <template slot-scope="scope">
+                <el-tag type="primary" size="small">
+                  {{ formatYearMonth(scope.row.meteredTime) }}
+                </el-tag>
+              </template>
+            </el-table-column>
+
+            <!-- 电量统计列 -->
+            <el-table-column v-if="showElecColumn('merchant_quantity')" label="商户电量(kWh)" align="right" width="120">
+              <template slot-scope="scope">
+                {{ formatNumber(getMerchantElecQuantityByRow(scope.row)) }}
+              </template>
+            </el-table-column>
+            <el-table-column v-if="showElecColumn('merchant_cost')" label="商户电费(¥)" align="right" width="110">
+              <template slot-scope="scope">
+                <span class="text-warning">{{ formatNumber(getMerchantElecCostByRow(scope.row), 2) }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column v-if="showElecColumn('shared_quantity')" label="公摊电量(kWh)" align="right" width="120">
+              <template slot-scope="scope">
+                {{ formatNumber(getSharedElecQuantityByRow(scope.row)) }}
+              </template>
+            </el-table-column>
+            <el-table-column v-if="showElecColumn('shared_cost')" label="公摊电费(¥)" align="right" width="110">
+              <template slot-scope="scope">
+                <span class="text-info">{{ formatNumber(getSharedElecCostByRow(scope.row), 2) }}</span>
+              </template>
+            </el-table-column>
+
+            <!-- 水量统计列 -->
+            <el-table-column v-if="showWaterColumn('merchant_quantity')" label="商户水量(m³)" align="right" width="110">
+              <template slot-scope="scope">
+                {{ formatNumber(getMerchantWaterQuantityByRow(scope.row)) }}
+              </template>
+            </el-table-column>
+            <el-table-column v-if="showWaterColumn('merchant_cost')" label="商户水费(¥)" align="right" width="110">
+              <template slot-scope="scope">
+                <span class="text-warning">{{ formatNumber(getMerchantWaterCostByRow(scope.row), 2) }}</span>
+              </template>
+            </el-table-column>
+            <el-table-column v-if="showWaterColumn('shared_quantity')" label="公摊水量(m³)" align="right" width="110">
+              <template slot-scope="scope">
+                {{ formatNumber(getSharedWaterQuantityByRow(scope.row)) }}
+              </template>
+            </el-table-column>
+            <el-table-column v-if="showWaterColumn('shared_cost')" label="公摊水费(¥)" align="right" width="110">
+              <template slot-scope="scope">
+                <span class="text-info">{{ formatNumber(getSharedWaterCostByRow(scope.row), 2) }}</span>
+              </template>
+            </el-table-column>
+
+            <el-table-column label="总费用(¥)" align="center" width="120" fixed="right">
+              <template slot-scope="scope">
+                <span class="total-cost">{{ formatNumber(calculateRowTotalCost(scope.row), 2) }}</span>
+              </template>
+            </el-table-column>
+          </el-table>
+        </el-card>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { getSellerBill, getHistoryBills, getChargingStrategyByArea } from '@/api/mgr/charging'
+
+export default {
+  name: 'SellerPanel',
+  props: {
+    queryParams: {
+      type: Object,
+      required: true
+    },
+    selectedArea: {
+      type: Object,
+      default: null
+    },
+    // 新增:接收完整的树形数据
+    areaTreeData: {
+      type: Array,
+      default: () => []
+    }
+  },
+  data() {
+    const now = new Date()
+    const year = now.getFullYear()
+    const month = (now.getMonth() + 1).toString().padStart(2, '0')
+
+    return {
+      loading: false,
+      historyLoading: false,
+      currentMonth: `${year}${month}`,
+      billData: null,
+      pricingStrategy: null,
+      historyDateRange: [],
+      historyData: [],
+      topLevelAreaCode: null, // 存储顶级区域代码
+
+      // 列显示控制
+      elecPopoverVisible: false,
+      waterPopoverVisible: false,
+      elecColumnsAll: true,
+      waterColumnsAll: true,
+      elecIndeterminate: false,
+      waterIndeterminate: false,
+
+      elecColumnOptions: [
+        { label: '商户电量', value: 'merchant_quantity', checked: true },
+        { label: '商户电费', value: 'merchant_cost', checked: true },
+        { label: '公摊电量', value: 'shared_quantity', checked: true },
+        { label: '公摊电费', value: 'shared_cost', checked: true }
+      ],
+
+      waterColumnOptions: [
+        { label: '商户水量', value: 'merchant_quantity', checked: true },
+        { label: '商户水费', value: 'merchant_cost', checked: true },
+        { label: '公摊水量', value: 'shared_quantity', checked: true },
+        { label: '公摊水费', value: 'shared_cost', checked: true }
+      ],
+
+      monthPickerOptions: {
+        disabledDate(time) {
+          return time.getTime() > Date.now()
+        }
+      },
+      historyPickerOptions: {
+        disabledDate(time) {
+          return time.getTime() > Date.now()
+        }
+      }
+    }
+  },
+  computed: {
+    hasPeakValleyData() {
+      return this.billData && this.billData.elecAutoStats && (
+        this.hasSharpPeak || this.hasPeak || this.hasNormal || this.hasValley || this.hasDeepValley
+      )
+    },
+    hasSharpPeak() {
+      return this.billData && this.billData.elecAutoStats &&
+        this.billData.elecAutoStats.sharpPeakQuantity > 0
+    },
+    hasPeak() {
+      return this.billData && this.billData.elecAutoStats &&
+        this.billData.elecAutoStats.peakQuantity > 0
+    },
+    hasNormal() {
+      return this.billData && this.billData.elecAutoStats &&
+        this.billData.elecAutoStats.normalQuantity > 0
+    },
+    hasValley() {
+      return this.billData && this.billData.elecAutoStats &&
+        this.billData.elecAutoStats.valleyQuantity > 0
+    },
+    hasDeepValley() {
+      return this.billData && this.billData.elecAutoStats &&
+        this.billData.elecAutoStats.deepValleyQuantity > 0
+    },
+    hasElecShared() {
+      return this.billData && this.billData.sharedData &&
+        this.billData.sharedData.elecSharedType > 0
+    },
+    hasWaterShared() {
+      return this.billData && this.billData.sharedData &&
+        this.billData.sharedData.waterSharedType > 0
+    }
+  },
+  created() {
+    this.initHistoryDateRange()
+  },
+  watch: {
+    selectedArea: {
+      handler(newVal, oldVal) {
+        console.log('SellerPanel - 选中区域变化:', newVal)
+        if (newVal && newVal.type === 'seller') {
+          // 更新顶级区域代码
+          this.updateTopLevelAreaCode()
+          // 加载数据
+          this.loadData()
+        } else {
+          // 清空数据
+          this.billData = null
+          this.pricingStrategy = null
+          this.topLevelAreaCode = null
+        }
+      },
+      deep: true,
+      immediate: true
+    },
+
+    // 监听树形数据变化
+    areaTreeData: {
+      handler(newVal) {
+        console.log('SellerPanel - 树形数据变化:', newVal)
+        if (this.selectedArea && this.selectedArea.type === 'seller') {
+          this.updateTopLevelAreaCode()
+        }
+      },
+      deep: true
+    }
+  },
+  methods: {
+    initHistoryDateRange() {
+      const now = new Date()
+      const startMonth = new Date(now.getFullYear(), now.getMonth() - 5, 1)
+      this.historyDateRange = [
+        this.formatMonth(startMonth),
+        this.formatMonth(now)
+      ]
+    },
+
+    formatMonth(date) {
+      const year = date.getFullYear()
+      const month = (date.getMonth() + 1).toString().padStart(2, '0')
+      return `${year}${month}`
+    },
+
+    formatYearMonth(ym) {
+      if (!ym || ym.length !== 6) return ym
+      return `${ym.substring(0, 4)}-${ym.substring(4, 6)}`
+    },
+
+    formatNumber(value, decimals = 0) {
+      if (value == null || value === undefined || isNaN(value)) {
+        return '0' + (decimals > 0 ? '.' + '0'.repeat(decimals) : '')
+      }
+      return Number(value).toFixed(decimals)
+    },
+
+    /**
+     * 递归查找节点的顶级父节点(优化版本)
+     * @param {String} nodeId - 当前节点ID
+     * @param {Array} treeData - 树形数据
+     * @returns {String|null} 顶级节点ID
+     */
+    findTopLevelParent(nodeId, treeData) {
+      if (!nodeId || !treeData || !Array.isArray(treeData)) {
+        return null
+      }
+
+      // 递归查找函数
+      const findInTree = (targetId, nodes, topLevelId = null) => {
+        for (const node of nodes) {
+          // 记录当前层级的顶级ID
+          const currentTopLevel = topLevelId || node.id
+
+          // 如果找到目标节点,返回顶级ID
+          if (node.id === targetId) {
+            return currentTopLevel
+          }
+
+          // 如果有子节点,继续递归查找
+          if (node.children && Array.isArray(node.children) && node.children.length > 0) {
+            const result = findInTree(targetId, node.children, currentTopLevel)
+            if (result) {
+              return result
+            }
+          }
+        }
+        return null
+      }
+
+      return findInTree(nodeId, treeData)
+    },
+
+    /**
+     * 更新顶级区域代码(优化版本)
+     */
+    updateTopLevelAreaCode() {
+      if (!this.selectedArea || !this.selectedArea.id) {
+        console.log('SellerPanel - 未选择有效的区域节点')
+        this.topLevelAreaCode = null
+        return
+      }
+
+      // 优先使用props传入的树形数据
+      let treeData = this.areaTreeData
+
+      // 如果props没有数据,尝试从父组件获取
+      if (!treeData || treeData.length === 0) {
+        if (this.$parent && this.$parent.areaOptions) {
+          treeData = this.$parent.areaOptions
+        } else if (this.$parent && this.$parent.treeData) {
+          treeData = this.$parent.treeData
+        }
+      }
+
+      // 如果还是没有数据,尝试从根组件获取
+      if (!treeData || treeData.length === 0) {
+        let parent = this.$parent
+        while (parent && (!treeData || treeData.length === 0)) {
+          if (parent.areaOptions) {
+            treeData = parent.areaOptions
+            break
+          } else if (parent.treeData) {
+            treeData = parent.treeData
+            break
+          }
+          parent = parent.$parent
+        }
+      }
+
+      if (!treeData || treeData.length === 0) {
+        console.warn('SellerPanel - 无法获取树形数据,请确保父组件传递了 areaTreeData 属性')
+        this.topLevelAreaCode = this.selectedArea.id // 降级处理,使用当前节点ID
+        return
+      }
+
+      // 查找顶级节点ID
+      const topLevelId = this.findTopLevelParent(this.selectedArea.id, treeData)
+
+      if (topLevelId) {
+        this.topLevelAreaCode = topLevelId
+        console.log(`SellerPanel - 节点 ${this.selectedArea.id} 的顶级区域代码: ${topLevelId}`)
+      } else {
+        console.warn('SellerPanel - 未找到顶级区域代码,使用当前节点ID')
+        this.topLevelAreaCode = this.selectedArea.id
+      }
+    },
+
+    /**
+     * 加载计费策略(优化版本)
+     */
+    async loadPricingStrategy() {
+      if (!this.topLevelAreaCode) {
+        console.log('SellerPanel - 未找到顶级区域代码,无法查询计费策略')
+        this.pricingStrategy = null
+        return
+      }
+
+      try {
+        console.log('SellerPanel - 使用顶级区域代码查询计费策略:', this.topLevelAreaCode)
+
+        // 使用顶级区域代码查询计费策略
+        const response = await getChargingStrategyByArea(this.topLevelAreaCode)
+
+        if (response.code === 200 && response.data) {
+          this.pricingStrategy = response.data
+          console.log('SellerPanel - 计费策略加载成功:', this.pricingStrategy)
+        } else {
+          console.warn('SellerPanel - 未找到计费策略:', response.msg || '无数据')
+          this.pricingStrategy = null
+        }
+      } catch (error) {
+        console.error('SellerPanel - 加载计费策略失败:', error)
+        this.pricingStrategy = null
+        // 不显示错误提示,避免影响用户体验
+      }
+    },
+
+    /**
+     * 加载数据(优化版本)
+     */
+    async loadData() {
+      if (!this.selectedArea || this.selectedArea.type !== 'seller') {
+        this.billData = null
+        this.pricingStrategy = null
+        return
+      }
+
+      this.loading = true
+      try {
+        // 1. 确保顶级区域代码已更新
+        if (!this.topLevelAreaCode) {
+          this.updateTopLevelAreaCode()
+        }
+
+        // 2. 加载计费策略
+        await this.loadPricingStrategy()
+
+        // 3. 加载当月数据
+        const params = {
+          areaCode: this.selectedArea.id,
+          startTime: this.currentMonth,
+          endTime: this.currentMonth,
+          includeAuto: true,
+          includeManual: true,
+          includeShared: true
+        }
+
+        console.log('SellerPanel - 加载账单数据参数:', params)
+        const response = await getSellerBill(params)
+
+        if (response.code === 200) {
+          this.billData = response.data
+          console.log('SellerPanel - 账单数据加载成功:', this.billData)
+        } else {
+          console.warn('SellerPanel - 账单数据加载失败:', response.msg)
+          this.billData = null
+        }
+
+        // 4. 加载历史数据
+        await this.loadHistoryData()
+
+      } catch (error) {
+        console.error('SellerPanel - 加载数据失败:', error)
+        this.$message.error('加载数据失败: ' + (error.message || '未知错误'))
+      } finally {
+        this.loading = false
+      }
+    },
+
+    /**
+     * 调试方法:打印树形结构
+     */
+    debugTreeStructure() {
+      console.log('=== SellerPanel 调试树形结构 ===')
+      console.log('selectedArea:', this.selectedArea)
+      console.log('areaTreeData:', this.areaTreeData)
+      console.log('topLevelAreaCode:', this.topLevelAreaCode)
+
+      if (this.areaTreeData && this.areaTreeData.length > 0) {
+        console.log('树形数据第一层节点:', this.areaTreeData.map(node => ({
+          id: node.id,
+          name: node.label || node.name,
+          hasChildren: !!(node.children && node.children.length > 0)
+        })))
+      }
+    },
+
+    // 列显示控制方法
+    handleElecAllChange(val) {
+      this.elecColumnOptions.forEach(col => {
+        col.checked = val
+      })
+      this.elecIndeterminate = false
+    },
+
+    handleElecColumnChange() {
+      const checkedCount = this.elecColumnOptions.filter(col => col.checked).length
+      this.elecColumnsAll = checkedCount === this.elecColumnOptions.length
+      this.elecIndeterminate = checkedCount > 0 && checkedCount < this.elecColumnOptions.length
+    },
+
+    handleWaterAllChange(val) {
+      this.waterColumnOptions.forEach(col => {
+        col.checked = val
+      })
+      this.waterIndeterminate = false
+    },
+
+    handleWaterColumnChange() {
+      const checkedCount = this.waterColumnOptions.filter(col => col.checked).length
+      this.waterColumnsAll = checkedCount === this.waterColumnOptions.length
+      this.waterIndeterminate = checkedCount > 0 && checkedCount < this.waterColumnOptions.length
+    },
+
+    showElecColumn(columnValue) {
+      const column = this.elecColumnOptions.find(col => col.value === columnValue)
+      return column && column.checked
+    },
+
+    showWaterColumn(columnValue) {
+      const column = this.waterColumnOptions.find(col => col.value === columnValue)
+      return column && column.checked
+    },
+
+    // 动态计算行总费用
+    calculateRowTotalCost(row) {
+      let total = 0
+
+      // 根据显示的列动态计算
+      if (this.showElecColumn('merchant_cost')) {
+        total += this.getMerchantElecCostByRow(row)
+      }
+      if (this.showElecColumn('shared_cost')) {
+        total += this.getSharedElecCostByRow(row)
+      }
+      if (this.showWaterColumn('merchant_cost')) {
+        total += this.getMerchantWaterCostByRow(row)
+      }
+      if (this.showWaterColumn('shared_cost')) {
+        total += this.getSharedWaterCostByRow(row)
+      }
+
+      return total
+    },
+
+    // 获取尖峰电量
+    getSharpPeakQuantity() {
+      return this.billData && this.billData.elecAutoStats
+        ? this.billData.elecAutoStats.sharpPeakQuantity || 0
+        : 0
+    },
+
+    // 获取峰段电量
+    getPeakQuantity() {
+      return this.billData && this.billData.elecAutoStats
+        ? this.billData.elecAutoStats.peakQuantity || 0
+        : 0
+    },
+
+    // 获取平段电量
+    getNormalQuantity() {
+      return this.billData && this.billData.elecAutoStats
+        ? this.billData.elecAutoStats.normalQuantity || 0
+        : 0
+    },
+
+    // 获取谷段电量
+    getValleyQuantity() {
+      return this.billData && this.billData.elecAutoStats
+        ? this.billData.elecAutoStats.valleyQuantity || 0
+        : 0
+    },
+
+    // 获取深谷电量
+    getDeepValleyQuantity() {
+      return this.billData && this.billData.elecAutoStats
+        ? this.billData.elecAutoStats.deepValleyQuantity || 0
+        : 0
+    },
+
+    // 获取电量公摊
+    getElecSharedQuantity() {
+      return this.billData && this.billData.sharedData
+        ? this.billData.sharedData.elecSharedQuantity || 0
+        : 0
+    },
+
+    // 获取电费公摊
+    getElecSharedCost() {
+      return this.billData && this.billData.sharedData
+        ? this.billData.sharedData.elecSharedCost || 0
+        : 0
+    },
+
+    // 获取电量公摊类型名称
+    getElecSharedTypeName() {
+      return this.billData && this.billData.sharedData
+        ? this.billData.sharedData.elecSharedTypeName || ''
+        : ''
+    },
+
+    // 获取水量公摊
+    getWaterSharedQuantity() {
+      return this.billData && this.billData.sharedData
+        ? this.billData.sharedData.waterSharedQuantity || 0
+        : 0
+    },
+
+    // 获取水费公摊
+    getWaterSharedCost() {
+      return this.billData && this.billData.sharedData
+        ? this.billData.sharedData.waterSharedCost || 0
+        : 0
+    },
+
+    // 获取水量公摊类型名称
+    getWaterSharedTypeName() {
+      return this.billData && this.billData.sharedData
+        ? this.billData.sharedData.waterSharedTypeName || ''
+        : ''
+    },
+
+    // 获取商户电量
+    getMerchantElecQuantity() {
+      if (!this.billData) return 0
+      const auto = (this.billData.elecAutoStats && this.billData.elecAutoStats.totalQuantity) || 0
+      const manual = (this.billData.elecManualStats && this.billData.elecManualStats.totalQuantity) || 0
+      return auto + manual
+    },
+
+    getMerchantElecQuantityByRow(row) {
+      const auto = (row.elecAutoStats && row.elecAutoStats.totalQuantity) || 0
+      const manual = (row.elecManualStats && row.elecManualStats.totalQuantity) || 0
+      return auto + manual
+    },
+
+    // 获取商户电费
+    getMerchantElecCost() {
+      if (!this.billData) return 0
+      const auto = (this.billData.elecAutoStats && this.billData.elecAutoStats.totalCost) || 0
+      const manual = (this.billData.elecManualStats && this.billData.elecManualStats.totalCost) || 0
+      return auto + manual
+    },
+
+    getMerchantElecCostByRow(row) {
+      const auto = (row.elecAutoStats && row.elecAutoStats.totalCost) || 0
+      const manual = (row.elecManualStats && row.elecManualStats.totalCost) || 0
+      return auto + manual
+    },
+
+    // 获取商户水量
+    getMerchantWaterQuantity() {
+      if (!this.billData) return 0
+      const auto = (this.billData.waterAutoStats && this.billData.waterAutoStats.totalQuantity) || 0
+      const manual = (this.billData.waterManualStats && this.billData.waterManualStats.totalQuantity) || 0
+      return auto + manual
+    },
+
+    getMerchantWaterQuantityByRow(row) {
+      const auto = (row.waterAutoStats && row.waterAutoStats.totalQuantity) || 0
+      const manual = (row.waterManualStats && row.waterManualStats.totalQuantity) || 0
+      return auto + manual
+    },
+
+    // 获取商户水费
+    getMerchantWaterCost() {
+      if (!this.billData) return 0
+      const auto = (this.billData.waterAutoStats && this.billData.waterAutoStats.totalCost) || 0
+      const manual = (this.billData.waterManualStats && this.billData.waterManualStats.totalCost) || 0
+      return auto + manual
+    },
+
+    getMerchantWaterCostByRow(row) {
+      const auto = (row.waterAutoStats && row.waterAutoStats.totalCost) || 0
+      const manual = (row.waterManualStats && row.waterManualStats.totalCost) || 0
+      return auto + manual
+    },
+
+    // 获取公摊电量(行数据)
+    getSharedElecQuantityByRow(row) {
+      return (row.sharedData && row.sharedData.elecSharedQuantity) || 0
+    },
+
+    // 获取公摊电费(行数据)
+    getSharedElecCostByRow(row) {
+      return (row.sharedData && row.sharedData.elecSharedCost) || 0
+    },
+
+    // 获取公摊水量(行数据)
+    getSharedWaterQuantityByRow(row) {
+      return (row.sharedData && row.sharedData.waterSharedQuantity) || 0
+    },
+
+    // 获取公摊水费(行数据)
+    getSharedWaterCostByRow(row) {
+      return (row.sharedData && row.sharedData.waterSharedCost) || 0
+    },
+
+    // 获取总电量
+    getTotalElecQuantity() {
+      return this.billData && this.billData.totalStats
+        ? this.billData.totalStats.elecTotalQuantity || 0
+        : 0
+    },
+
+    // 获取总电费
+    getTotalElecCost() {
+      return this.billData && this.billData.totalStats
+        ? this.billData.totalStats.elecTotalCost || 0
+        : 0
+    },
+
+    // 获取总水量
+    getTotalWaterQuantity() {
+      return this.billData && this.billData.totalStats
+        ? this.billData.totalStats.waterTotalQuantity || 0
+        : 0
+    },
+
+    // 获取总水费
+    getTotalWaterCost() {
+      return this.billData && this.billData.totalStats
+        ? this.billData.totalStats.waterTotalCost || 0
+        : 0
+    },
+
+    // 获取公摊总费用
+    getTotalSharedCost() {
+      return this.billData && this.billData.totalStats
+        ? this.billData.totalStats.sharedTotalCost || 0
+        : 0
+    },
+
+    // 获取总费用
+    getGrandTotalCost() {
+      return this.billData && this.billData.totalStats
+        ? this.billData.totalStats.grandTotalCost || 0
+        : 0
+    },
+
+    async loadHistoryData() {
+      if (!this.selectedArea || this.selectedArea.type !== 'seller') {
+        return
+      }
+
+      this.historyLoading = true
+      try {
+        const params = {
+          areaCode: this.selectedArea.id,
+          startTime: this.historyDateRange[0],
+          endTime: this.historyDateRange[1],
+          includeAuto: true,
+          includeManual: true,
+          includeShared: true
+        }
+
+        const response = await getHistoryBills(params)
+        if (response.code === 200) {
+          this.historyData = response.rows || []
+        }
+      } catch (error) {
+        console.error('加载历史数据失败', error)
+        this.$message.error('加载历史数据失败')
+      } finally {
+        this.historyLoading = false
+      }
+    },
+
+    handleMonthChange() {
+      this.loadData()
+    },
+
+    handleHistoryQuery() {
+      this.loadHistoryData()
+    },
+
+    handleExport() {
+      const params = {
+        areaCode: this.selectedArea.id,
+        startTime: this.historyDateRange[0],
+        endTime: this.historyDateRange[1],
+        includeAuto: true,
+        includeManual: true,
+        includeShared: true
+      }
+
+      this.download('/ems/charging/bill/export', params, `个户账单_${this.selectedArea.label}_${new Date().getTime()}.xlsx`)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.seller-panel {
+  padding: 20px;
+
+  .empty-state {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    min-height: 400px;
+    background: #fff;
+    border-radius: 12px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+
+    .empty-content {
+      text-align: center;
+      color: #909399;
+
+      i {
+        font-size: 80px;
+        margin-bottom: 20px;
+        display: block;
+        color: #c0c4cc;
+      }
+
+      h3 {
+        margin-bottom: 15px;
+        color: #606266;
+        font-size: 20px;
+      }
+
+      p {
+        font-size: 14px;
+        line-height: 1.6;
+      }
+    }
+  }
+
+  .section-block {
+    background: #fff;
+    border-radius: 12px;
+    padding: 20px;
+    margin-bottom: 20px;
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
+
+    .section-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      margin-bottom: 20px;
+      padding-bottom: 15px;
+      border-bottom: 2px solid #f0f2f5;
+
+      .section-title {
+        margin: 0;
+        font-size: 18px;
+        font-weight: 600;
+        color: #303133;
+        display: flex;
+        align-items: center;
+
+        i {
+          margin-right: 10px;
+          color: #409eff;
+          font-size: 20px;
+        }
+      }
+
+      .section-controls {
+        .el-form-item {
+          margin-bottom: 0;
+        }
+      }
+    }
+  }
+
+  .merchant-info {
+    margin-bottom: 25px;
+
+    .info-header {
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      border-radius: 12px;
+      padding: 20px;
+      color: #fff;
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+
+      .basic-info {
+        .merchant-name {
+          margin: 0 0 10px 0;
+          font-size: 22px;
+          font-weight: 600;
+          display: flex;
+          align-items: center;
+
+          i {
+            margin-right: 10px;
+            font-size: 24px;
+          }
+        }
+
+        .merchant-tags {
+          .el-tag {
+            margin-right: 10px;
+          }
+        }
+      }
+
+      .pricing-info {
+        .strategy-title {
+          text-align: center;
+          font-size: 16px;
+          font-weight: 600;
+          margin-bottom: 15px;
+          padding-bottom: 10px;
+          border-bottom: 1px solid rgba(255, 255, 255, 0.3);
+          color: #ffd700;
+        }
+
+        .price-item {
+          margin-bottom: 5px;
+          text-align: right;
+
+          .label {
+            opacity: 0.9;
+            margin-right: 5px;
+          }
+
+          .value {
+            font-weight: 600;
+            color: #ffd700;
+          }
+        }
+      }
+    }
+  }
+
+  .statistics-content {
+    .usage-card {
+      background: #f8f9fa;
+      border-radius: 12px;
+      overflow: hidden;
+      height: 100%;
+
+      &.elec-card {
+        border-left: 4px solid #f39c12;
+      }
+
+      &.water-card {
+        border-left: 4px solid #3498db;
+      }
+
+      .card-header {
+        background: #fff;
+        padding: 15px 20px;
+        border-bottom: 1px solid #e6e8eb;
+        display: flex;
+        align-items: center;
+        font-size: 16px;
+        font-weight: 600;
+        color: #303133;
+
+        i {
+          margin-right: 10px;
+          font-size: 20px;
+        }
+      }
+
+      .card-body {
+        padding: 20px;
+
+        .usage-section {
+          margin-bottom: 20px;
+
+          .section-subtitle {
+            margin: 0 0 15px 0;
+            font-size: 14px;
+            color: #606266;
+            display: flex;
+            align-items: center;
+
+            .el-tag {
+              margin-left: 10px;
+            }
+          }
+
+          .usage-detail {
+            .main-usage {
+              display: flex;
+              align-items: center;
+              padding: 12px;
+              background: #fff;
+              border-radius: 8px;
+
+              .label {
+                color: #909399;
+                margin-right: 10px;
+              }
+
+              .value {
+                font-size: 20px;
+                font-weight: 600;
+                color: #303133;
+                margin-right: 5px;
+              }
+
+              .unit {
+                color: #606266;
+                margin-right: auto;
+              }
+
+              .cost {
+                font-size: 18px;
+                color: #67c23a;
+                font-weight: 600;
+              }
+            }
+
+            .peak-valley-breakdown {
+              margin-top: 10px;
+              padding: 10px 12px;
+              background: rgba(255, 255, 255, 0.5);
+              border-radius: 8px;
+
+              .breakdown-title {
+                font-size: 13px;
+                color: #909399;
+                margin-bottom: 8px;
+              }
+
+              .breakdown-items {
+                display: flex;
+                flex-wrap: wrap;
+                gap: 8px;
+
+                .breakdown-item {
+                  display: flex;
+                  align-items: center;
+                  padding: 4px 8px;
+                  background: #fff;
+                  border-radius: 4px;
+
+                  .period-tag {
+                    font-size: 12px;
+                    padding: 2px 6px;
+                    border-radius: 3px;
+                    margin-right: 5px;
+                    color: #fff;
+
+                    &.sharp { background: #f56c6c; }
+                    &.peak { background: #e6a23c; }
+                    &.normal { background: #909399; }
+                    &.valley { background: #67c23a; }
+                    &.deep-valley { background: #409eff; }
+                  }
+
+                  .period-value {
+                    font-size: 13px;
+                    color: #606266;
+                  }
+                }
+              }
+            }
+          }
+        }
+
+        .usage-total {
+          padding: 15px;
+          background: #fff;
+          border-radius: 8px;
+          display: flex;
+          align-items: center;
+          border: 2px solid #409eff;
+
+          .label {
+            font-weight: 600;
+            color: #303133;
+            margin-right: 15px;
+          }
+
+          .value {
+            font-size: 18px;
+            font-weight: 700;
+            color: #409eff;
+            margin-right: auto;
+          }
+
+          .cost {
+            font-size: 20px;
+            font-weight: 700;
+            color: #e6a23c;
+          }
+        }
+      }
+    }
+
+    .cost-summary-card {
+      margin-top: 20px;
+      padding: 20px;
+      background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+      border-radius: 12px;
+      display: flex;
+      justify-content: space-around;
+      align-items: center;
+
+      .summary-item {
+        display: flex;
+        align-items: center;
+        color: #fff;
+
+        i {
+          font-size: 24px;
+          margin-right: 10px;
+          opacity: 0.9;
+        }
+
+        .label {
+          margin-right: 10px;
+          font-size: 14px;
+          opacity: 0.9;
+        }
+
+        .value {
+          font-size: 18px;
+          font-weight: 600;
+        }
+
+        &.total {
+          padding: 0 30px;
+          border-left: 2px solid rgba(255, 255, 255, 0.3);
+
+          .value {
+            font-size: 22px;
+            font-weight: 700;
+          }
+        }
+      }
+    }
+  }
+
+  .table-header {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    font-size: 16px;
+    font-weight: 600;
+    color: #303133;
+  }
+
+  .table-controls {
+    display: flex;
+    gap: 10px;
+
+    .el-button {
+      i:last-child {
+        margin-left: 5px;
+        transition: transform 0.3s;
+      }
+    }
+  }
+
+  .column-control {
+    padding: 10px 0;
+
+    .el-checkbox {
+      display: block;
+      margin-bottom: 10px;
+      font-weight: 600;
+    }
+
+    .column-options {
+      padding-left: 20px;
+
+      .el-checkbox {
+        font-weight: normal;
+        margin-bottom: 8px;
+      }
+    }
+  }
+
+  .total-cost {
+    font-size: 16px;
+    font-weight: bold;
+    color: #f56c6c;
+  }
+
+  .text-primary { color: #409eff; }
+  .text-warning { color: #e6a23c; }
+  .text-info { color: #909399; }
+  .text-success { color: #67c23a; }
+  .font-bold { font-weight: 600; }
+}
+</style>