learshaw 5 месяцев назад
Родитель
Сommit
8b4732b07b

+ 60 - 0
ems-ui-cloud/src/api/basecfg/PlantCarbonSink.js

@@ -0,0 +1,60 @@
+import request from '@/utils/request'
+
+// 查询植物碳汇维列表
+export function listPlantCarbonSink(query) {
+  return request({
+    url: '/ems/plantCarbonSink/list',
+    method: 'get',
+    params: query
+  })
+}
+
+// 查询植物碳汇维详细
+export function getPlantCarbonSink(id) {
+  return request({
+    url: '/ems/plantCarbonSink/' + id,
+    method: 'get'
+  })
+}
+
+// 新增植物碳汇维
+export function addPlantCarbonSink(data) {
+  return request({
+    url: '/ems/plantCarbonSink',
+    method: 'post',
+    data: data
+  })
+}
+
+// 修改植物碳汇维
+export function updatePlantCarbonSink(data) {
+  return request({
+    url: '/ems/plantCarbonSink',
+    method: 'put',
+    data: data
+  })
+}
+
+// 删除植物碳汇维
+export function delPlantCarbonSink(id) {
+  return request({
+    url: '/ems/plantCarbonSink/' + id,
+    method: 'delete'
+  })
+}
+
+// 获取区域种植配置
+export function getAreaPlantCfg(areaCode) {
+  return request({
+    url: '/ems/plantCarbonSink/areaCfg?areaCode=' + areaCode,
+    method: 'get'
+  })
+}
+
+export function saveAreaPlantCfg(areaCode, data) {
+  return request({
+    url: '/ems/plantCarbonSink/areaCfg?areaCode=' + areaCode,
+    method: 'put',
+    data: data
+  });
+}

+ 220 - 36
ems-ui-cloud/src/views/adapter/nhjc/index.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="app-container">
-    <!-- 系统基础信息卡片 - 添加编辑按钮 -->
+    <!-- 系统基础信息卡片 -->
     <el-card class="system-info-card">
       <div slot="header" class="card-header">
         <span class="title">
@@ -60,7 +60,7 @@
           <div class="info-group">
             <div class="group-title">设备统计</div>
             <div class="info-item">
-              <label>设备总数:</label>
+              <label>网关设备:</label>
               <span class="stat-number">{{ deviceStats.total }}</span>
             </div>
             <div class="info-item">
@@ -171,8 +171,8 @@
           </div>
         </el-tab-pane>
 
-        <!-- 关联设备标签页 -->
-        <el-tab-pane label="关联设备" name="devices">
+        <!-- 能耗采集标签页 -->
+        <el-tab-pane label="能耗采集" name="devBaGa">
           <div class="tab-content">
             <!-- 设备统计 -->
             <div class="device-stats">
@@ -315,6 +315,94 @@
           </div>
         </el-tab-pane>
 
+        <!-- 楼控设备标签页 - 新增 -->
+        <el-tab-pane label="楼控设备" name="baDevices">
+          <div class="tab-content">
+            <!-- 设备类型选择 -->
+            <div class="ba-device-header">
+              <el-select v-model="selectedBaDeviceModel" placeholder="请选择设备类型" @change="handleBaDeviceTypeChange" style="width: 200px;">
+                <el-option
+                  v-for="item in baDeviceModels"
+                  :key="item.modelCode"
+                  :label="item.modelName"
+                  :value="item.modelCode">
+                </el-option>
+              </el-select>
+              <el-button type="primary" icon="el-icon-refresh" @click="refreshBaDevices" :loading="baDeviceLoading" style="margin-left: 10px;">
+                刷新
+              </el-button>
+            </div>
+
+            <!-- 设备列表 -->
+            <el-table
+              :data="baDeviceList"
+              border
+              stripe
+              v-loading="baDeviceLoading"
+              style="margin-top: 15px;">
+              <el-table-column type="expand">
+                <template slot-scope="props">
+                  <div class="ba-device-detail">
+                    <el-tabs v-model="props.row.detailTab">
+                      <!-- 基础属性 -->
+                      <el-tab-pane label="基础属性" name="base">
+                        <el-descriptions :column="2" border size="small" v-if="getBaDeviceAttrs(props.row.deviceCode, 'Base').length > 0">
+                          <el-descriptions-item
+                            v-for="attr in getBaDeviceAttrs(props.row.deviceCode, 'Base')"
+                            :key="attr.attrKey"
+                            :label="attr.attrName">
+                            {{ attr.attrValue || '-' }}
+                          </el-descriptions-item>
+                        </el-descriptions>
+                        <el-empty v-else description="暂无基础属性"></el-empty>
+                      </el-tab-pane>
+
+                      <!-- 状态属性 -->
+                      <el-tab-pane label="状态属性" name="state">
+                        <el-table
+                          :data="getBaDeviceAttrs(props.row.deviceCode, 'State')"
+                          border
+                          size="small"
+                          v-if="getBaDeviceAttrs(props.row.deviceCode, 'State').length > 0">
+                          <el-table-column prop="attrName" label="属性名称" width="180"></el-table-column>
+                          <el-table-column label="属性值" width="150">
+                            <template slot-scope="scope">
+                              <el-tag v-if="scope.row.attrValueType === 'Enum' && scope.row.attrValueName" size="small">
+                                {{ scope.row.attrValueName }}
+                              </el-tag>
+                              <span v-else>
+                                {{ formatBaAttrValue(scope.row) }}
+                              </span>
+                            </template>
+                          </el-table-column>
+                          <el-table-column prop="attrUnit" label="单位" width="100"></el-table-column>
+                          <el-table-column prop="updateTime" label="更新时间" width="160"></el-table-column>
+                        </el-table>
+                        <el-empty v-else description="暂无状态数据"></el-empty>
+                      </el-tab-pane>
+                    </el-tabs>
+                  </div>
+                </template>
+              </el-table-column>
+              <el-table-column prop="deviceCode" label="设备代码" width="180"></el-table-column>
+              <el-table-column prop="deviceName" label="设备名称" min-width="200"></el-table-column>
+              <el-table-column prop="location" label="安装位置" min-width="150"></el-table-column>
+              <el-table-column prop="areaCode" label="区域" width="100">
+                <template slot-scope="scope">
+                  {{ scope.row.areaCode && scope.row.areaCode.includes('3001') ? '北区' : '南区' }}
+                </template>
+              </el-table-column>
+              <el-table-column label="设备状态" width="100" align="center">
+                <template slot-scope="scope">
+                  <el-tag :type="scope.row.deviceStatus === 1 ? 'success' : 'info'">
+                    {{ scope.row.deviceStatus === 1 ? '在线' : '离线' }}
+                  </el-tag>
+                </template>
+              </el-table-column>
+            </el-table>
+          </div>
+        </el-tab-pane>
+
         <!-- 调用日志标签页 -->
         <el-tab-pane label="调用日志" name="callLogs">
           <div class="tab-content">
@@ -527,7 +615,7 @@
       </div>
     </el-dialog>
 
-    <!-- 能力执行弹窗(支持Slider和Input类型) -->
+    <!-- 能力执行弹窗 -->
     <el-dialog :title="abilityDialogTitle" :visible.sync="abilityDialogVisible" width="500px" append-to-body :close-on-click-modal="false">
       <el-form ref="abilityForm" :model="abilityForm" label-width="100px">
         <el-form-item label="能力名称">
@@ -603,7 +691,7 @@ export default {
   data() {
     return {
       // 系统代码
-      systemCode: 'SYS_NHJC',
+      systemCode: 'SYS_BA',
       // 系统信息
       systemInfo: {},
       // 系统状态
@@ -617,7 +705,7 @@ export default {
       // 系统能力
       systemAbilities: [],
       abilityLoading: false,
-      // 设备列表
+      // 设备列表(能耗网关)
       deviceList: [],
       deviceLoading: false,
       deviceAttrs: {},
@@ -634,8 +722,17 @@ export default {
       // 所有模型代码(系统 + 设备)
       allModelCodes: [],
       // 子设备相关
-      subDeviceAttrs: {}, // 子设备属性缓存
-      subDeviceGatewayMap: {}, // 子设备到网关的映射
+      subDeviceAttrs: {},
+      subDeviceGatewayMap: {},
+      // 楼控设备相关 - 新增
+      baDeviceModels: [
+        { modelCode: 'M_Z020_DEV_BA_XF', modelName: 'BA新风设备' },
+        { modelCode: 'M_Z020_DEV_BA_AHU', modelName: 'BA空调设备' }
+      ],
+      selectedBaDeviceModel: 'M_Z020_DEV_BA_XF',
+      baDeviceList: [],
+      baDeviceLoading: false,
+      baDeviceAttrs: {}, // 楼控设备属性缓存
       // 调用日志查询
       callLogQuery: {
         dateRange: [],
@@ -732,7 +829,6 @@ export default {
           updateSubsystem(this.editForm).then(response => {
             this.$message.success("修改成功")
             this.editDialogVisible = false
-            // 重新加载系统信息
             this.loadSystemInfo()
           }).catch(error => {
             this.$message.error("修改失败:" + error.message)
@@ -841,7 +937,7 @@ export default {
         }
 
         // 如果是抄表能力,刷新设备数据
-        if (ability.abilityKey === 'MeterReadingGw') {
+        if (ability.abilityKey === 'MeterReadingGw' || ability.abilityKey === 'MeterReadingTotal') {
           this.loadDevices()
         }
       }).catch(error => {
@@ -1010,12 +1106,13 @@ export default {
       }
     },
 
-    // 加载设备列表
+    // 加载设备列表(能耗网关) - 修改:添加deviceModel过滤
     async loadDevices() {
       this.deviceLoading = true
       try {
         const res = await getByCondition({
-          subsystemCode: this.systemCode
+          subsystemCode: this.systemCode,
+          deviceModel: 'M_W4_DEV_BA_GA' // 仅查询能耗网关
         })
 
         const deviceData = res.data || res.rows || []
@@ -1054,7 +1151,7 @@ export default {
           }
         })
 
-        // 批量加载网关设备属性(包含subDev)
+        // 批量加载网关设备属性
         for (const modelCode in devicesByModel) {
           await this.loadDeviceAttrsBatch(modelCode, devicesByModel[modelCode])
         }
@@ -1080,7 +1177,6 @@ export default {
           const attrData = batchData[deviceCode]
 
           if (attrData) {
-            // 保存基础属性
             const baseAttrs = {}
             if (attrData.Base) {
               attrData.Base.forEach(attr => {
@@ -1097,8 +1193,7 @@ export default {
 
     // 加载所有子设备属性
     async loadAllSubDeviceAttrs() {
-      // 收集所有子设备信息
-      const subDevicesByModel = {} // { modelCode: [deviceCodes] }
+      const subDevicesByModel = {}
 
       this.deviceList.forEach(device => {
         const subDevAttr = this.deviceAttrs[device.deviceCode]?.subDev
@@ -1110,8 +1205,6 @@ export default {
                 subDevicesByModel[subDev.modelCode] = []
               }
               subDevicesByModel[subDev.modelCode].push(subDev.deviceCode)
-
-              // 保存子设备与网关的关联关系
               this.subDeviceGatewayMap[subDev.deviceCode] = device.deviceCode
             })
           } catch (e) {
@@ -1120,7 +1213,6 @@ export default {
         }
       })
 
-      // 批量查询各个模型的子设备属性
       let totalPoints = 0
       for (const modelCode in subDevicesByModel) {
         const pointsCount = await this.loadSubDeviceAttrsBatch(modelCode)
@@ -1128,8 +1220,6 @@ export default {
       }
 
       this.deviceStats.points = totalPoints
-
-      // 组织成通道结构供展示
       this.organizeChannelsFromSubDevices()
     },
 
@@ -1141,7 +1231,6 @@ export default {
 
         let pointsCount = 0
 
-        // 保存子设备属性
         for (const deviceCode in batchData) {
           const attrData = batchData[deviceCode]
           this.subDeviceAttrs[deviceCode] = attrData
@@ -1168,8 +1257,6 @@ export default {
 
         try {
           const subDevices = JSON.parse(subDevAttr)
-
-          // 按通道分组
           const channelMap = {}
 
           subDevices.forEach(subDev => {
@@ -1178,7 +1265,6 @@ export default {
 
             if (!subDevAttrData) return
 
-            // 获取通道信息
             const interfaceAttr = subDevAttrData.Base?.find(attr => attr.attrKey === 'interface')
             const channelName = interfaceAttr?.attrValue || '未知通道'
 
@@ -1190,7 +1276,6 @@ export default {
               }
             }
 
-            // 构建测点信息
             const point = {
               key: subDevCode,
               name: subDevAttrData.Base?.find(attr => attr.attrKey === 'deviceName')?.attrValue || subDevCode,
@@ -1204,7 +1289,6 @@ export default {
             channelMap[channelName].points.push(point)
           })
 
-          // 转换为数组并排序
           const channels = Object.values(channelMap).sort((a, b) => {
             return a.key.localeCompare(b.key)
           })
@@ -1220,7 +1304,6 @@ export default {
 
     // 格式化通道名称
     formatChannelName(channelKey) {
-      // 将类似 "1003D1口" 转换为 "D1口通道"
       const match = channelKey.match(/(\d+)(D\d+)口/)
       if (match) {
         return `${match[2]}口通道`
@@ -1241,7 +1324,6 @@ export default {
     // 单个加载设备属性(用于刷新)
     async loadDeviceAttrs(deviceCode) {
       try {
-        // 加载网关属性
         const res = await getObjAttr(2, deviceCode)
         const attrData = res.data
 
@@ -1253,7 +1335,6 @@ export default {
         }
         this.deviceAttrs[deviceCode] = baseAttrs
 
-        // 重新加载该网关的所有子设备
         const subDevAttr = baseAttrs.subDev
         if (subDevAttr) {
           const subDevices = JSON.parse(subDevAttr)
@@ -1266,12 +1347,10 @@ export default {
             subDevicesByModel[subDev.modelCode].push(subDev.deviceCode)
           })
 
-          // 批量查询子设备属性
           for (const modelCode in subDevicesByModel) {
             await this.loadSubDeviceAttrsBatch(modelCode)
           }
 
-          // 重新组织该网关的通道数据
           this.organizeChannelsFromSubDevices()
         }
 
@@ -1314,7 +1393,6 @@ export default {
 
           this.$message.success(`${ability.abilityName}执行成功`)
 
-          // 如果是抄表相关能力,刷新测点数据
           if (ability.abilityKey.toLowerCase().includes('meter') ||
             ability.abilityKey.toLowerCase().includes('reading')) {
             await this.loadDeviceAttrs(device.deviceCode)
@@ -1341,7 +1419,6 @@ export default {
 
             this.$message.success(`${ability.abilityName}执行成功`)
 
-            // 如果是抄表相关能力,刷新测点数据
             if (ability.abilityKey.toLowerCase().includes('meter') ||
               ability.abilityKey.toLowerCase().includes('reading')) {
               await this.loadDeviceAttrs(device.deviceCode)
@@ -1358,15 +1435,110 @@ export default {
       this.$refs.deviceTable?.toggleRowExpansion(row)
     },
 
-    // 标签页切换
+    // ========== 楼控设备相关方法 - 新增 ==========
+
+    // 处理楼控设备类型切换
+    handleBaDeviceTypeChange() {
+      this.loadBaDevices()
+    },
+
+    // 刷新楼控设备
+    refreshBaDevices() {
+      this.loadBaDevices()
+    },
+
+    // 加载楼控设备列表
+    async loadBaDevices() {
+      if (!this.selectedBaDeviceModel) return
+
+      this.baDeviceLoading = true
+      try {
+        // 查询指定模型的设备
+        const res = await getByCondition({
+          subsystemCode: this.systemCode,
+          deviceModel: this.selectedBaDeviceModel
+        })
+
+        const deviceData = res.data || res.rows || []
+
+        this.baDeviceList = deviceData.map(device => ({
+          ...device,
+          detailTab: 'base'
+        }))
+
+        // 批量加载设备属性
+        if (this.baDeviceList.length > 0) {
+          await this.loadBaDeviceAttrsBatch(this.selectedBaDeviceModel)
+        }
+
+      } catch (error) {
+        this.$message.error('加载楼控设备失败:' + error.message)
+      } finally {
+        this.baDeviceLoading = false
+      }
+    },
+
+    // 批量加载楼控设备属性
+    async loadBaDeviceAttrsBatch(modelCode) {
+      try {
+        const res = await getObjAttrBatch(2, modelCode)
+        const batchData = res.data || {}
+
+        // 缓存所有设备的属性数据
+        for (const deviceCode in batchData) {
+          this.baDeviceAttrs[deviceCode] = batchData[deviceCode]
+        }
+
+      } catch (error) {
+        console.error(`批量加载楼控设备属性失败:`, error)
+      }
+    },
+
+    // 获取楼控设备的属性(按组)
+    getBaDeviceAttrs(deviceCode, attrGroup) {
+      const attrData = this.baDeviceAttrs[deviceCode]
+      if (!attrData || !attrData[attrGroup]) {
+        return []
+      }
+      return attrData[attrGroup]
+    },
+
+    // 格式化楼控设备属性值
+    formatBaAttrValue(attr) {
+      if (!attr.attrValue && attr.attrValue !== 0) {
+        return '-'
+      }
+
+      // 如果有枚举名称,优先显示
+      if (attr.attrValueName) {
+        return attr.attrValueName
+      }
+
+      // 数值类型,保留两位小数
+      if (attr.attrValueType === 'Value') {
+        const numValue = parseFloat(attr.attrValue)
+        if (!isNaN(numValue)) {
+          return numValue.toFixed(2)
+        }
+      }
+
+      return attr.attrValue
+    },
+
+    // ========== 标签页切换 ==========
+
     handleTabClick(tab) {
       if (tab.name === 'callLogs' && this.callLogList.length === 0) {
         this.queryCallLogs()
       } else if (tab.name === 'eventLogs' && this.eventLogList.length === 0) {
         this.queryEventLogs()
+      } else if (tab.name === 'baDevices' && this.baDeviceList.length === 0) {
+        this.loadBaDevices()
       }
     },
 
+    // ========== 日志查询相关 ==========
+
     // 查询调用日志
     async queryCallLogs() {
       this.logLoading = true
@@ -1621,6 +1793,18 @@ export default {
     }
   }
 
+  // 楼控设备样式 - 新增
+  .ba-device-header {
+    display: flex;
+    align-items: center;
+    margin-bottom: 15px;
+  }
+
+  .ba-device-detail {
+    padding: 20px;
+    background: #f5f7fa;
+  }
+
   .log-filter {
     margin-bottom: 20px;
     padding: 15px;

+ 546 - 0
ems-ui-cloud/src/views/basecfg/casinkcfg/index.vue

@@ -0,0 +1,546 @@
+<template>
+  <div class="app-container">
+    <el-tabs v-model="activeName" type="border-card">
+      <!-- 第一个tab: 碳汇系数管理 -->
+      <el-tab-pane label="碳汇系数" name="coefficient">
+        <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+        </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:PlantCarbonSink:add']"
+            >新增</el-button>
+          </el-col>
+          <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+        </el-row>
+
+        <el-table v-loading="loading" :data="PlantCarbonSinkList" @selection-change="handleSelectionChange">
+          <el-table-column label="种植方式" align="center" prop="plantMethod" show-overflow-tooltip />
+          <el-table-column label="固碳量 kgC02/m²" align="center" prop="carbonQuantity" />
+          <el-table-column label="数据版本" align="center" prop="version" />
+          <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:PlantCarbonSink:edit']"
+              >修改</el-button>
+              <el-button
+                size="mini"
+                type="text"
+                icon="el-icon-delete"
+                @click="handleDelete(scope.row)"
+                v-hasPermi="['ems:PlantCarbonSink:remove']"
+              >删除</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>
+
+      <!-- 第二个tab: 种植面积配置 -->
+      <el-tab-pane label="种植面积" name="plantArea">
+        <el-row :gutter="20">
+          <!-- 左侧区域树 -->
+          <el-col :span="6">
+            <div class="tree-container">
+              <div class="tree-header">
+                <span>区域列表</span>
+              </div>
+              <el-tree
+                ref="areaTree"
+                :data="areaTreeData"
+                :props="areaTreeProps"
+                node-key="id"
+                default-expand-all
+                :expand-on-click-node="false"
+                highlight-current
+                @node-click="handleAreaNodeClick"
+              >
+                <span class="custom-tree-node" slot-scope="{ node, data }">
+                  <span>{{ node.label }}</span>
+                </span>
+              </el-tree>
+            </div>
+          </el-col>
+
+          <!-- 右侧种植面积配置 -->
+          <el-col :span="18">
+            <div v-if="!currentAreaCode" class="empty-content">
+              <el-empty description="请选择左侧区域查看种植面积配置"></el-empty>
+            </div>
+
+            <div v-else>
+              <div class="area-info">
+                <el-tag type="success">当前区域:{{ currentAreaName }}</el-tag>
+                <el-button
+                  type="primary"
+                  size="small"
+                  icon="el-icon-check"
+                  style="float: right;"
+                  :loading="saveLoading"
+                  @click="handleSaveAreaPlant"
+                >保存配置</el-button>
+              </div>
+
+              <el-table
+                v-loading="areaPlantLoading"
+                :data="areaPlantList"
+                border
+                style="margin-top: 15px;"
+              >
+                <el-table-column label="种植方式" prop="plantMethod" show-overflow-tooltip min-width="350">
+                  <template slot-scope="scope">
+                    <el-tooltip :content="scope.row.plantMethod" placement="top">
+                      <span>{{ scope.row.plantMethod }}</span>
+                    </el-tooltip>
+                  </template>
+                </el-table-column>
+                <el-table-column label="种植面积 (m²)" width="250" align="center">
+                  <template slot-scope="scope">
+                    <el-input-number
+                      v-model="scope.row.plantSize"
+                      :min="0"
+                      :precision="2"
+                      :step="0.1"
+                      size="small"
+                      style="width: 200px;"
+                      placeholder="请输入面积"
+                      controls-position="right"
+                    ></el-input-number>
+                  </template>
+                </el-table-column>
+              </el-table>
+
+              <!-- 统计信息 -->
+              <div class="statistics-panel">
+                <el-row :gutter="20">
+                  <el-col :span="12">
+                    <div class="stat-item">
+                      <div class="stat-label">总种植面积</div>
+                      <div class="stat-value">{{ totalPlantSize.toFixed(2) }} m²</div>
+                    </div>
+                  </el-col>
+                  <el-col :span="12">
+                    <div class="stat-item">
+                      <div class="stat-label">已配置项数</div>
+                      <div class="stat-value">{{ configuredCount }} 项</div>
+                    </div>
+                  </el-col>
+                </el-row>
+              </div>
+            </div>
+          </el-col>
+        </el-row>
+      </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="100px">
+        <el-form-item label="种植方式" prop="plantMethod">
+          <el-input v-model="form.plantMethod" placeholder="请输入种植方式" type="textarea" :rows="3" />
+        </el-form-item>
+        <el-form-item label="固碳量" prop="carbonQuantity">
+          <el-input v-model="form.carbonQuantity" placeholder="请输入固碳量">
+            <template slot="append">kgCO₂/m²</template>
+          </el-input>
+        </el-form-item>
+        <el-form-item label="数据版本" prop="version">
+          <el-input v-model="form.version" 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 {
+  listPlantCarbonSink,
+  getPlantCarbonSink,
+  delPlantCarbonSink,
+  addPlantCarbonSink,
+  updatePlantCarbonSink,
+  getAreaPlantCfg,
+  saveAreaPlantCfg
+} from "@/api/basecfg/PlantCarbonSink";
+import { areaTreeSelect } from "@/api/basecfg/area"
+
+export default {
+  name: "PlantCarbonSink",
+  data() {
+    return {
+      // 当前激活的tab
+      activeName: 'coefficient',
+      // 遮罩层
+      loading: true,
+      // 选中数组
+      ids: [],
+      // 非单个禁用
+      single: true,
+      // 非多个禁用
+      multiple: true,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 植物碳汇维表格数据
+      PlantCarbonSinkList: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNum: 1,
+        pageSize: 10,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        plantMethod: [
+          { required: true, message: "种植方式不能为空", trigger: "blur" }
+        ],
+        carbonQuantity: [
+          { required: true, message: "固碳量 kgC02/m2不能为空", trigger: "blur" }
+        ],
+      },
+
+      // === 区域种植面积相关 ===
+      // 区域树数据
+      areaTreeData: [],
+      // 区域树配置
+      areaTreeProps: {
+        children: 'children',
+        label: 'label'
+      },
+      // 当前选中的区域代码
+      currentAreaCode: '',
+      // 当前选中的区域名称
+      currentAreaName: '',
+      // 区域种植面积列表
+      areaPlantList: [],
+      // 区域种植面积加载状态
+      areaPlantLoading: false,
+      // 保存加载状态
+      saveLoading: false
+    };
+  },
+  computed: {
+    // 计算总种植面积
+    totalPlantSize() {
+      return this.areaPlantList.reduce((sum, item) => {
+        return sum + (parseFloat(item.plantSize) || 0);
+      }, 0);
+    },
+    // 已配置项数(面积>0)
+    configuredCount() {
+      return this.areaPlantList.filter(item => (parseFloat(item.plantSize) || 0) > 0).length;
+    }
+  },
+  watch: {
+    // 监听tab切换
+    activeName(newVal) {
+      if (newVal === 'plantArea' && this.areaTreeData.length === 0) {
+        this.loadAreaTree();
+      }
+    }
+  },
+  created() {
+    this.getList();
+  },
+  methods: {
+    /** 查询植物碳汇维列表 */
+    getList() {
+      this.loading = true;
+      listPlantCarbonSink(this.queryParams).then(response => {
+        this.PlantCarbonSinkList = response.rows;
+        this.total = response.total;
+        this.loading = false;
+      });
+    },
+
+    // 取消按钮
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+
+    // 表单重置
+    reset() {
+      this.form = {
+        id: null,
+        plantMethod: null,
+        carbonQuantity: null,
+        version: 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
+      getPlantCarbonSink(id).then(response => {
+        this.form = response.data;
+        this.open = true;
+        this.title = "修改植物碳汇维";
+      });
+    },
+
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (valid) {
+          if (this.form.id != null) {
+            updatePlantCarbonSink(this.form).then(response => {
+              this.$modal.msgSuccess("修改成功");
+              this.open = false;
+              this.getList();
+            });
+          } else {
+            addPlantCarbonSink(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 delPlantCarbonSink(ids);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("删除成功");
+      }).catch(() => {});
+    },
+
+    // === 区域种植面积相关方法 ===
+
+    /** 加载区域树 */
+    loadAreaTree() {
+      areaTreeSelect('0', 1).then(response => {
+        this.areaTreeData = response.data || [];
+
+        // 自动选中并加载第一个节点
+        this.$nextTick(() => {
+          if (this.areaTreeData.length > 0) {
+            const firstNode = this.areaTreeData[0];
+            // 设置tree的当前选中节点
+            if (this.$refs.areaTree) {
+              this.$refs.areaTree.setCurrentKey(firstNode.id);
+            }
+            // 加载第一个节点的数据
+            this.handleAreaNodeClick(firstNode);
+          }
+        });
+      }).catch(error => {
+        this.$modal.msgError("加载区域树失败");
+      });
+    },
+
+    /** 点击区域树节点 */
+    handleAreaNodeClick(data) {
+      this.currentAreaCode = data.id;
+      this.currentAreaName = data.label;
+      this.loadAreaPlantConfig(data.id);
+    },
+
+    /** 加载区域种植配置 */
+    loadAreaPlantConfig(areaCode) {
+      this.areaPlantLoading = true;
+
+      // 先加载全局碳汇系数列表作为模板
+      listPlantCarbonSink({ pageNum: 1, pageSize: 1000 }).then(response => {
+        // 使用碳汇系数列表初始化数据,面积默认为0
+        const templateList = (response.rows || []).map(item => ({
+          id: item.id,
+          plantMethod: item.plantMethod,
+          carbonQuantity: item.carbonQuantity,
+          version: item.version,
+          areaCode: areaCode,
+          plantSize: 0  // 默认面积为0
+        }));
+
+        // 再加载区域已有的配置数据
+        getAreaPlantCfg(areaCode).then(areaResponse => {
+          const areaConfigList = areaResponse.data || [];
+
+          // 将区域配置的面积数据合并到模板中
+          templateList.forEach(template => {
+            const areaConfig = areaConfigList.find(config => config.id === template.id);
+            if (areaConfig && areaConfig.plantSize !== null && areaConfig.plantSize !== undefined) {
+              template.plantSize = areaConfig.plantSize;
+            }
+          });
+
+          this.areaPlantList = templateList;
+          this.areaPlantLoading = false;
+        }).catch(error => {
+          // 即使没有区域配置,也显示模板数据
+          this.areaPlantList = templateList;
+          this.areaPlantLoading = false;
+        });
+      }).catch(error => {
+        this.$modal.msgError("加载碳汇系数列表失败");
+        this.areaPlantLoading = false;
+      });
+    },
+
+    /** 保存区域种植配置 */
+    handleSaveAreaPlant() {
+      // 过滤出有效数据(面积>0的配置)
+      const validData = this.areaPlantList
+        .filter(item => (parseFloat(item.plantSize) || 0) > 0)
+        .map(item => ({
+          id: item.id,
+          areaCode: this.currentAreaCode,
+          plantSize: parseFloat(item.plantSize) || 0
+        }));
+
+      if (validData.length === 0) {
+        this.$modal.msgWarning("请至少配置一项种植面积");
+        return;
+      }
+
+      this.saveLoading = true;
+      saveAreaPlantCfg(this.currentAreaCode, validData).then(response => {
+        this.$modal.msgSuccess("保存成功");
+        this.saveLoading = false;
+        // 重新加载数据
+        this.loadAreaPlantConfig(this.currentAreaCode);
+      }).catch(error => {
+        this.$modal.msgError("保存失败");
+        this.saveLoading = false;
+      });
+    }
+  }
+};
+</script>
+
+<style scoped lang="scss">
+.tree-container {
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  padding: 10px;
+  height: calc(100vh - 250px);
+  overflow-y: auto;
+
+  .tree-header {
+    font-weight: bold;
+    margin-bottom: 10px;
+    padding-bottom: 10px;
+    border-bottom: 1px solid #ebeef5;
+  }
+}
+
+.empty-content {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  height: 400px;
+}
+
+.area-info {
+  padding: 15px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  margin-bottom: 15px;
+
+  .el-tag {
+    font-size: 14px;
+  }
+}
+
+.statistics-panel {
+  margin-top: 20px;
+  padding: 20px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+
+  .stat-item {
+    text-align: center;
+
+    .stat-label {
+      font-size: 14px;
+      color: #909399;
+      margin-bottom: 8px;
+    }
+
+    .stat-value {
+      font-size: 24px;
+      font-weight: bold;
+      color: #303133;
+    }
+  }
+}
+
+.custom-tree-node {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 14px;
+  padding-right: 8px;
+}
+
+::v-deep .el-tabs--border-card {
+  box-shadow: none;
+  border: 1px solid #dcdfe6;
+}
+
+::v-deep .el-table {
+  font-size: 13px;
+}
+</style>