<template>
  <div id="page">
    <div class="header">
      <div class="images flex a-c" v-for="item in imagesList" :key="item.id">
        <el-tooltip effect="dark" content="绘制矩形区域">
          <img :src="item.src" @click="drawRectangle" />
        </el-tooltip>
        <div class="sure" @click="analyzeAverageSlope">确定</div>
        <div class="right flex a-c">
          <div class="sure" :disabled="showExport" @click="clickExport">导出</div>
          <div class="sure" @click="handleExportJosn">json编辑</div>
        </div>
      </div>
    </div>

    <div class="content flex j-s a-c">
      <div class="left">
        <el-form label-position="left" size="mini">
          <!-- 范围 -->
          <div class="control-panel">
            <div class="title">范围</div>
            <el-form-item label="左上角：">
              <el-input v-model="form.leftTop" @blur="updateArea" placeholder="经纬度，以 ',' 隔开'"></el-input>
            </el-form-item>
            <el-form-item label="右下角：">
              <el-input v-model="form.rightBottom" @blur="updateArea" placeholder="经纬度，以 ',' 隔开'"> </el-input>
            </el-form-item>
            <input type="file" ref="fileInput" @change="handleFileUpload" style="display: none" />
            <div class="importJson flex j-c a-c" @click="triggerFileUpload">导入json文件</div>
          </div>

          <!-- 权重 -->
          <div class="control-panel">
            <div class="title">权重</div>
            <el-form-item label="坡度：">
              <el-input v-model="form.slopePer"></el-input>
            </el-form-item>
            <el-form-item label="居民地：">
              <el-input v-model="form.peoplePer"></el-input>
            </el-form-item>
            <el-form-item label="植被：">
              <el-input v-model="form.plantPer"></el-input>
            </el-form-item>
            <el-form-item label="土壤：">
              <el-input v-model="form.soilPer"></el-input>
            </el-form-item>
          </div>

          <!-- 条件 -->
          <div class="control-panel">
            <div class="title">网格条件</div>
            <el-form-item label="列(个)：">
              <el-input v-model="form.xLength"></el-input>
            </el-form-item>
            <el-form-item label="行(个)：">
              <el-input v-model="form.yLength"></el-input>
            </el-form-item>
            <el-form-item label="最大坡度(°)：">
              <el-input v-model="form.maxSlope"></el-input>
            </el-form-item>
            <el-form-item label="居民地距离(米)：">
              <el-input v-model="form.minPeopleDis"></el-input>
            </el-form-item>
          </div>
        </el-form>
      </div>

      <!-- 地图 -->
      <div class="center" id="cesiumContainer" v-loading="mapLoading" :element-loading-text="mapLoadText"></div>

      <div class="right flex column" v-loading="showLoading" element-loading-text="数据加载中">
        <!-- 路点 -->
        <div class="table-title" v-if="roadPointList.length > 0">道路附属点</div>
        <vxe-table class="item" :data="roadPointList" v-if="roadPointList.length > 0">
          <vxe-column type="seq" width="70"></vxe-column>
          <vxe-column field="编码" title="编码"></vxe-column>
          <vxe-column field="名称" title="名称"></vxe-column>
          <vxe-column field="A" title="种类"></vxe-column>
        </vxe-table>

        <!-- 路线 -->
        <div class="table-title" v-if="roadLineList.length > 0">道路附属线</div>
        <vxe-table class="item" :data="roadLineList" v-if="roadLineList.length > 0">
          <vxe-column type="seq" width="70"></vxe-column>
          <vxe-column field="编码" title="编码"></vxe-column>
          <vxe-column field="名称" title="名称"></vxe-column>
          <vxe-column field="A" title="种类"></vxe-column>
        </vxe-table>

        <!-- 水点 -->
        <div class="table-title" v-if="waterPointList.length > 0">水系附属点</div>
        <vxe-table class="item" :data="waterPointList" v-if="waterPointList.length > 0">
          <vxe-column type="seq" width="70"></vxe-column>
          <vxe-column field="编码" title="编码"></vxe-column>
          <vxe-column field="名称" title="名称"></vxe-column>
          <vxe-column field="类型" title="类型"></vxe-column>
          <vxe-column field="A" title="种类"></vxe-column>
        </vxe-table>

        <!-- 水线 -->
        <div class="table-title" v-if="waterLineList.length > 0">水系附属线</div>
        <vxe-table class="item" :data="waterLineList" v-if="waterLineList.length > 0">
          <vxe-column type="seq" width="70"></vxe-column>
          <vxe-column field="编码" title="编码"></vxe-column>
          <vxe-column field="名称" title="名称"></vxe-column>
          <vxe-column field="A" title="种类"></vxe-column>
        </vxe-table>
      </div>

      <!-- 导出界面 -->
      <el-dialog :visible.sync="dialogVisible" title="导出目录" width="90%" top="10vh">
        <el-tabs v-model="activeName" type="card">
          <el-tab-pane v-for="(item, index) in tabs" :label="item.label" :name="item.id">
            <div class="road flex j-s a-c">
              <!-- 道路附属点 -->
              <div class="point">
                <div class="table-title">
                  <span>道路附属点</span>
                  <div class="sure" @click="handleAdd('roadPointList')">新增</div>
                </div>
                <vxe-table
                  class="item"
                  :ref="`${item.id}_roadPointListREF`"
                  :data="currentTabList.roadPointList"
                  align="center"
                  :height="tableHeight"
                  :row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
                >
                  <vxe-column type="seq" width="70"></vxe-column>
                  <vxe-column field="编码" title="编码">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.编码" size="mini"></el-input>
                      <span v-else>{{ row.编码 }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column field="名称" title="名称">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.名称" size="mini"></el-input>
                      <span v-else>{{ row.名称 }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column field="A" title="种类">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.A" size="mini"></el-input>
                      <span v-else>{{ row.A }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column title="操作" width="100">
                    <template v-slot="{row}">
                      <div class="sure" v-if="!row.editing" type="text" @click="handleEdit(row)">编辑</div>
                      <div class="sure" v-else type="text" @click="handleSave(row)">保存</div>
                      <div class="sure" type="text" @click="handleDelete('roadPointList', row)">删除</div>
                    </template>
                  </vxe-column>
                </vxe-table>
              </div>

              <!-- 道路附属线 -->
              <div class="line">
                <div class="table-title">
                  <span>道路附属线</span>
                  <div class="sure" @click="handleAdd('roadLineList')">新增</div>
                </div>
                <vxe-table
                  class="item"
                  :ref="`${item.id}_roadLineListREF`"
                  :data="currentTabList.roadLineList"
                  align="center"
                  :height="tableHeight"
                  :row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
                >
                  <vxe-column type="seq" width="70"></vxe-column>
                  <vxe-column field="编码" title="编码">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.编码" size="mini"></el-input>
                      <span v-else>{{ row.编码 }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column field="名称" title="名称">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.名称" size="mini"></el-input>
                      <span v-else>{{ row.名称 }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column field="A" title="种类">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.A" size="mini"></el-input>
                      <span v-else>{{ row.A }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column title="操作" width="100">
                    <template v-slot="{row}">
                      <div class="sure" v-if="!row.editing" @click="handleEdit(row)">编辑</div>
                      <div class="sure" v-else @click="handleSave(row)">保存</div>
                      <div class="sure" @click="handleDelete('roadLineList', row)">删除</div>
                    </template>
                  </vxe-column>
                </vxe-table>
              </div>
            </div>

            <div class="water flex j-s a-c">
              <!-- 水系附属点 -->
              <div class="point">
                <div class="table-title">
                  <span>水系附属点</span>
                  <div class="sure" @click="handleAdd('waterPointList')">新增</div>
                </div>
                <vxe-table
                  class="item"
                  :ref="`${item.id}_waterPointListREF`"
                  :data="currentTabList.waterPointList"
                  align="center"
                  :height="tableHeight"
                  :row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
                >
                  <vxe-column type="seq" width="70"></vxe-column>
                  <vxe-column field="编码" title="编码">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.编码" size="mini"></el-input>
                      <span v-else>{{ row.编码 }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column field="名称" title="名称">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.名称" size="mini"></el-input>
                      <span v-else>{{ row.名称 }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column field="A" title="种类">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.A" size="mini"></el-input>
                      <span v-else>{{ row.A }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column title="操作" width="100">
                    <template v-slot="{row}">
                      <div class="sure" v-if="!row.editing" @click="handleEdit(row)">编辑</div>
                      <div class="sure" v-else @click="handleSave(row)">保存</div>
                      <div class="sure" @click="handleDelete('waterPointList', row)">删除</div>
                    </template>
                  </vxe-column>
                </vxe-table>
              </div>

              <!-- 水系附属线 -->
              <div class="line">
                <div class="table-title">
                  <span>水系附属线</span>
                  <div class="sure" @click="handleAdd('waterLineList')">新增</div>
                </div>
                <vxe-table
                  class="item"
                  :ref="`${item.id}_waterLineListREF`"
                  :data="currentTabList.waterLineList"
                  align="center"
                  :height="tableHeight"
                  :row-config="{isCurrent: true, isHover: true, keyField: 'seq'}"
                >
                  <vxe-column type="seq" width="70"></vxe-column>
                  <vxe-column field="编码" title="编码">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.编码" size="mini"></el-input>
                      <span v-else>{{ row.编码 }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column field="名称" title="名称">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.名称" size="mini"></el-input>
                      <span v-else>{{ row.名称 }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column field="A" title="种类">
                    <template v-slot="{row}">
                      <el-input v-if="row.editing" v-model="row.A" size="mini"></el-input>
                      <span v-else>{{ row.A }}</span>
                    </template>
                  </vxe-column>
                  <vxe-column title="操作" width="100">
                    <template v-slot="{row}">
                      <div class="sure" v-if="!row.editing" @click="handleEdit(row)">编辑</div>
                      <div class="sure" v-else @click="handleSave(row)">保存</div>
                      <div class="sure" @click="handleDelete('waterLineList', row)">删除</div>
                    </template>
                  </vxe-column>
                </vxe-table>
              </div>
            </div>
          </el-tab-pane>
        </el-tabs>

        <div slot="footer" class="dialog-footer">
          <div class="sure" @click="dialogVisible = false">取消</div>
          <div class="sure" @click="exportJSON">导出</div>
        </div>
      </el-dialog>

      <!-- 右侧json编辑 -->
      <el-dialog :visible.sync="dialogJsonVisible" title="json编辑" width="600px">
        <div v-loading="jsonLoading">
          <div style="margin-bottom: 10px">
            <el-button type="primary" size="mini" @click="handleJson">选择</el-button>
          </div>
          <el-input type="textarea" resize="none" :rows="18" v-model="jsonInfo.json" size="mini"></el-input>
        </div>
        <div slot="footer" class="dialog-footer">
          <el-button size="mini" @click="closeJson">取消</el-button>
          <el-button type="primary" size="mini" @click="confirmJson">保存</el-button>
        </div>
      </el-dialog>
    </div>
  </div>
</template>

<script>
import {getStorage} from '@/utils/localStorage'
import plantJson from '/public/config/plant.json'
import soilJson from '/public/config/soil.json'
import FileSaver from 'file-saver'
import axios from 'axios'
import iniParser from 'ini-parser'

export default {
  name: '',
  data() {
    return {
      showLoading: false,
      imagesList: [{id: 1, src: require('@/assets/image/crop.png')}],
      roadPointList: [],
      roadLineList: [],
      waterPointList: [],
      waterLineList: [],
      mapLoading: true,
      mapLoadText: '地图数据加载中',
      form: {
        leftTop: '',
        rightBottom: '',
        slopePer: 0.5,
        peoplePer: 0.2,
        plantPer: 0.2,
        soilPer: 0.1,
        xLength: 3,
        yLength: 3,
        maxSlope: 6,
        minPeopleDis: 1,
      },
      analyzing: false,
      rectCount: 0,
      isHand: false,

      selectedRect: null,
      rectangles: [],
      labels: [],

      peopleGeo: null,
      plantGeo: null,
      soilGeo: null,
      plantJson: null,
      soilJson: null,
      roadPointGeo: null,
      roadLineGeo: null,
      waterPointGeo: null,
      waterLineGeo: null,

      minDistance: null,
      _polyCache: null,
      validBlocks: [],
      colorList: [],
      roadWaterLock: false,

      dialogVisible: false,

      tabs: [],
      tabList: [],
      rectTotalScore: '',
      activeName: '',

      tableHeight: 280,
      showExport: true,

      dialogJsonVisible: false,
      jsonInfo: {
        json: '',
        path: '',
      },
      jsonLoading: false,
    }
  },
  created() {
    this._polyCache = new WeakMap()
  },

  mounted() {
    this.getMapJson()
    this.getColorList()
  },

  computed: {
    // 示例区域：矩形四个角点
    positions() {
      let a = this.form.leftTop.split(',')
      let b = this.form.rightBottom.split(',')
      return [
        [a[0], b[1]],
        [b[0], b[1]],
        [b[0], a[1]],
        [a[0], a[1]],
      ]
    },

    // 导出目录的当前 tab
    currentTabList() {
      let list = this.tabList.find((item) => item.id === this.activeName)
      return list[list?.key]
    },
  },

  beforeDestroy() {
    delete window.viewer
    delete window.graphicLayer
  },

  methods: {
    // 获取地图配置
    getMapJson() {
      window.mars3d.Util.fetchJson({
        url: `./config/map.json`,
      }).then((res) => {
        let mapJSonStr = getStorage('localmap')
        let mapJson
        if (mapJSonStr) {
          mapJson = mars3d.Util.merge(res.map3d, mapJSonStr)
        } else {
          mapJson = res.map3d
        }
        this.viewerOptions = mapJson
        this.plantJson = plantJson
        this.soilJson = soilJson
        this.initMarsMap()
        setTimeout(() => {
          this.mapLoading = false
        }, 1000)
      })
    },

    // 导入json文件
    triggerFileUpload() {
      this.$refs.fileInput.click()
    },

    handleFileUpload(event) {
      const file = event.target.files[0]
      if (file) {
        const reader = new FileReader()
        reader.onload = (e) => {
          try {
            const uploadedJson = JSON.parse(e.target.result)
            this.importJson(uploadedJson)
          } catch (error) {
            console.error('解析 JSON 文件失败:', error)
            this.$message.error('解析 JSON 文件失败')
          }
        }
        reader.readAsText(file)
      }
    },

    async importJson(uploadedJson) {
      if (!uploadedJson) {
        this.$message.warning('请先选择一个 JSON 文件')
        return
      }
      // 清除现有矩形
      this.clearRectangles()
      if (this.polygon && (uploadedJson?.leftTop || uploadedJson?.rightBottom)) {
        window.graphicLayer.removeGraphic(this.polygon)
        this.polygon = null
      }

      this.form.leftTop = uploadedJson.leftTop
      this.form.rightBottom = uploadedJson.rightBottom
      this.drawInitialArea()
    },

    getColorList() {
      this.colorList = colorMap.createColormap({
        colormap: 'jet',
        nshades: 10,
        format: 'hex',
        alpha: 1,
      })
    },

    // 初始化地图
    initMarsMap() {
      window.viewer = new window.mars3d.Map('cesiumContainer', this.viewerOptions)

      window.graphicLayer = new window.mars3d.layer.GraphicLayer()
      window.viewer.addLayer(window.graphicLayer)
      window.shortestPathLayer = new window.mars3d.layer.GraphicLayer()
      window.viewer.addLayer(window.shortestPathLayer)
      // 添加地图点击事件监听，用于结束绘制
      window.viewer.on(mars3d.EventType.dblClick, (event) => {
        // 如果正在绘制，点击地图可以结束绘制(除了绘制点)
        window.graphicLayer.stopDraw()
        this.form.leftTop = ''
        this.form.rightBottom = ''
      })
      this.loadGeoJson()
    },

    loadGeoJson() {
      // 延迟加载，按需显示
      this.layers = {
        people: this.createLayerConfig('./config/people.geojson', '#ff0000'),
        plant: this.createLayerConfig('./config/plant.geojson', '#00ff00'),
        soil: this.createLayerConfig('./config/soil.geojson', '#0000ff'),
        roadPoint: this.createLayerConfig('./config/road_points.geojson', '#ff0ff0', 'point'),
        roadLine: this.createLayerConfig('./config/road_lines.geojson', '#ff0ff0', 'polyline'),
        waterPoint: this.createLayerConfig('./config/water_points.geojson', '#ff0ff0', 'point'),
        waterLine: this.createLayerConfig('./config/water_lines.geojson', '#ff0ff0', 'polyline'),
      }

      // 默认只加载一个，其他按需加载
      // this.loadLayers(['people', 'plant', 'soil'], (results) => {
      //   console.log('所有图层加载完成', results)
      // })
      this.loadLayer('people')
      this.loadLayer('plant', false)
      this.loadLayer('soil', false)
    },

    createLayerConfig(url, color, type = 'polygon') {
      const baseConfig = {
        layer: null,
        config: {
          url: url,
          crs: 'EPSG:4326',
          flyTo: true, // 关闭自动飞行
          // 视域裁剪，只显示视野内的要素
          cull: {
            enabled: true,
            cullFace: 'back',
          },
          // 简化参数
          simplify: {
            enabled: true,
            tolerance: 0.0001,
          },
        },
      }

      // 根据不同类型设置不同的符号样式
      switch (type) {
        case 'point':
          baseConfig.config.symbol = {
            type: 'point',
            styleOptions: {
              color: Cesium.Color.fromCssColorString(color),
              pixelSize: 8,
              outlineColor: Cesium.Color.WHITE,
              outlineWidth: 1,
              scaleByDistance: new Cesium.NearFarScalar(1.5e2, 1.0, 1.5e6, 0.5),
              heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
            },
          }
          break

        case 'polyline':
          baseConfig.config.symbol = {
            type: 'polyline',
            styleOptions: {
              color: Cesium.Color.fromCssColorString(color),
              width: 3,
              opacity: 0.8,
              clampToGround: true,
            },
          }
          break

        case 'polygon':
        default:
          baseConfig.config.symbol = {
            type: 'polygon',
            styleOptions: {
              color: Cesium.Color.fromCssColorString(color),
              opacity: 0.3,
              outline: false,
              // 如果需要轮廓线可以取消注释
              // outlineColor: color,
              // outlineWidth: 1,
              clampToGround: true, // 贴地显示
            },
          }
          break
      }

      return baseConfig
    },

    async loadLayer(layerName, isLoad = true) {
      if (this.layers[layerName].layer) {
        this.layers[layerName].layer.show = !this.layers[layerName].layer.show
        return
      }

      this.layers[layerName].layer = new mars3d.layer.GeoJsonLayer(this.layers[layerName].config)

      isLoad ? await window.viewer.addLayer(this.layers[layerName].layer) : null
    },

    // 新增方法：同时加载多个图层
    async loadLayers(names = [], cb, show = true) {
      const tasks = names.map((name) => {
        const cfg = this.layers[name]
        if (!cfg) return Promise.resolve(null) // 无配置直接过

        // 已经加载过：只切换可见性
        if (cfg.layer) {
          cfg.layer.show = true
          return Promise.resolve(cfg.layer)
        }

        // 首次加载
        return new Promise((resolve) => {
          const layer = new mars3d.layer.GeoJsonLayer(cfg.config)
          layer.once(mars3d.EventType.load, () => resolve(layer))
          window.viewer.addLayer(layer)
          cfg.layer = layer // 缓存
        })
      })

      return Promise.all(tasks).then((loadedLayers) => {
        if (cb) cb(loadedLayers.filter(Boolean)) // 过滤空值
        return loadedLayers.filter(Boolean)
      })
    },

    // 算矩形到 geoJSONLayer 的最小距离(米)，判断是否在其内部
    calcMinDistance(rectPositions, geoJSONLayer) {
      if (!geoJSONLayer || !rectPositions?.length) return Infinity

      // 1. 矩形边界 + 中心点
      const rectLine = turf.lineString(rectPositions.concat([rectPositions[0]]))
      const rectCenter = turf.centroid(rectLine)

      let minDist = Infinity
      let inside = false

      const polygons = geoJSONLayer.getGraphics().filter((g) => g.type === 'polygon')

      polygons.forEach((g) => {
        const lnglats = g.positions.map((p) => {
          const carto = Cesium.Cartographic.fromCartesian(p)
          return [Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude)]
        })
        lnglats.push(lnglats[0])
        const poly = turf.polygon([lnglats])

        if (turf.booleanPointInPolygon(rectCenter, poly)) {
          inside = true
          return
        }

        const polyLine = turf.lineString(lnglats)
        rectLine.geometry.coordinates.forEach((pt) => {
          const closest = turf.nearestPointOnLine(polyLine, pt)
          const d = turf.distance(pt, closest, {units: 'metres'})
          minDist = Math.min(minDist, d)
        })
        polyLine.geometry.coordinates.forEach((pt) => {
          const closest = turf.nearestPointOnLine(rectLine, pt)
          const d = turf.distance(pt, closest, {units: 'metres'})
          minDist = Math.min(minDist, d)
        })
      })

      console.log('矩形 → polygonP 边界最短距离(米)', inside, minDist)

      // 5. 面内直接返回 -1
      return inside ? -1 : minDist
    },

    // 判断矩形与 mars3d GeoJSON 图层是否相交(不转 GeoJSON)
    getIntersectId(position, layer) {
      if (!position || !layer || !layer.graphics) return null

      const rectCoords = position.concat([position[0]]) // 闭合

      const rectPoly = turf.polygon([rectCoords])
      const [minX, minY, maxX, maxY] = turf.bbox(rectPoly) // 矩形 bbox

      let fc = this._polyCache.get(layer)

      if (!fc) {
        const features = []
        layer.graphics.forEach((g) => {
          // 处理面要素
          if (g.type === 'polygon') {
            const coords = g.positions.map((p) => {
              const carto = Cesium.Cartographic.fromCartesian(p)
              return [Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude)]
            })
            coords.push(coords[0]) // 闭合
            features.push({
              type: 'Feature',
              geometry: {type: 'Polygon', coordinates: [coords]},
              properties: {id: g.attr?.id ?? g.id},
            })
          }
          // 处理线要素
          else if (g.type === 'polyline') {
            const coords = g.positions.map((p) => {
              const carto = Cesium.Cartographic.fromCartesian(p)
              return [Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude)]
            })
            features.push({
              type: 'Feature',
              geometry: {type: 'LineString', coordinates: coords},
              properties: {id: g.attr?.id ?? g.id},
            })
          }
          // 处理点要素
          else if (g.type === 'point') {
            const carto = Cesium.Cartographic.fromCartesian(g.position)
            const coord = [Cesium.Math.toDegrees(carto.longitude), Cesium.Math.toDegrees(carto.latitude)]
            features.push({
              type: 'Feature',
              geometry: {type: 'Point', coordinates: coord},
              properties: {id: g.attr?.id ?? g.id},
            })
          }
        })

        fc = turf.featureCollection(features)
        this._polyCache.set(layer, fc)
      }

      // 先进行边界框筛选
      const candidates = fc.features.filter((f) => {
        const [fminX, fminY, fmaxX, fmaxY] = turf.bbox(f)
        return !(fmaxX < minX || fminX > maxX || fmaxY < minY || fminY > maxY)
      })

      // 精确相交检测
      for (const f of candidates) {
        let isIntersect = false

        // 根据几何类型使用不同的相交检测方法
        if (f.geometry.type === 'Polygon') {
          isIntersect = turf.booleanIntersects(rectPoly, f)
        } else if (f.geometry.type === 'LineString') {
          // 线与面的相交检测
          isIntersect = turf.booleanIntersects(rectPoly, f)
        } else if (f.geometry.type === 'Point') {
          // 点与面的包含检测
          isIntersect = turf.booleanPointInPolygon(f, rectPoly)
        }

        if (isIntersect) return String(f.properties.id)
      }

      return null
    },

    // 手动绘制矩形
    async drawRectangle(clampToGround) {
      this.clearRectangles()
      if (this.polygon) {
        window.graphicLayer.removeGraphic(this.polygon)
        this.polygon = null
      }
      this.isHand = true
      let isEntityGraphic = false
      this.polygon = await window.graphicLayer.startDraw({
        type: isEntityGraphic ? 'rectangle' : 'rectangleP',
        style: {
          color: clampToGround ? '#ffff00' : '#3388ff',
          opacity: 0.6,
          outline: true,
          outlineColor: '#ffffff',
          outlineWidth: 2.0,
          clampToGround,
        },
      })

      let positions = this.polygon.toJSON().positions
      this.form.leftTop = positions[0][0] + ',' + positions[0][1]
      this.form.rightBottom = positions[1][0] + ',' + positions[1][1]
    },

    // 绘制矩形区域
    drawInitialArea() {
      // 如果多边形尚未创建，则创建它
      if (!this.polygon) {
        console.log('this.positions===>', this.positions)
        this.polygon = new mars3d.graphic.PolygonEntity({
          positions: this.positions.map((p) => Cesium.Cartesian3.fromDegrees(p[0], p[1], 0)),
          style: {
            color: '#ffff00',
            opacity: 0.3,
            outline: true,
            outlineColor: '#ffffff',
            outlineWidth: 2,
          },
          label: {
            text: '分析区域',
            font: '16px 等线',
            color: '#ffffff',
            outline: true,
            outlineColor: '#000000',
            outlineWidth: 2,
            pixelOffset: new Cesium.Cartesian2(0, -40),
          },
        })

        window.graphicLayer.addGraphic(this.polygon)
        this.isInitialized = true
      } else {
        // 如果多边形已存在，则更新它的位置
        this.updateArea()
      }
    },

    updateArea() {
      if (this.isHand) {
        this.isHand = false
        return
      }

      if (!this.form.leftTop || !this.form.rightBottom) return

      if (this.form.leftTop && this.form.rightBottom && !this.polygon) {
        this.drawInitialArea()
      }

      // 将经纬度转换为笛卡尔坐标
      const cartesianPositions = this.positions.map((p) => Cesium.Cartesian3.fromDegrees(p[0], p[1], 0))

      // 更新多边形位置
      this.polygon.positions = cartesianPositions

      // 触发更新(根据mars3d的具体API可能需要调用其他方法)
      if (this.polygon.update) {
        this.polygon.update()
      }

      console.log('区域已更新', this.positions)
    },

    // 分析平均坡度
    async analyzeAverageSlope() {
      if (!this.form.leftTop || !this.form.rightBottom) {
        return this.$message.warning('请选择范围！')
      }
      this.mapLoading = true
      this.$message.success('开始分析')
      this.mapLoadText = '坡度分析中'
      this.analyzing = true
      this.selectedRect = null

      // 清除之前的矩形
      this.clearRectangles()

      // 获取分割参数
      const xSplit = parseInt(this.form.xLength) || 3
      const ySplit = parseInt(this.form.yLength) || 3

      // 计算区域边界

      console.log('this.positions===>', this.positions)
      const lats = this.positions.map((p) => p[1])
      const lngs = this.positions.map((p) => p[0])
      const minLat = Math.min(...lats)
      const maxLat = Math.max(...lats)
      const minLng = Math.min(...lngs)
      const maxLng = Math.max(...lngs)

      const latStep = (maxLat - minLat) / ySplit
      const lngStep = (maxLng - minLng) / xSplit

      // 创建小矩形
      this.rectCount = xSplit * ySplit
      let rectId = 1

      // 收集所有采样点
      const samplePoints = []
      const rectInfoMap = new Map() // 存储矩形ID对应的信息

      for (let i = 0; i < xSplit; i++) {
        for (let j = 0; j < ySplit; j++) {
          const rectMinLng = minLng + i * lngStep
          const rectMaxLng = minLng + (i + 1) * lngStep
          const rectMinLat = minLat + j * latStep
          const rectMaxLat = minLat + (j + 1) * latStep

          // 矩形中心点作为采样点
          const centerLng = (rectMinLng + rectMaxLng) / 2
          const centerLat = (rectMinLat + rectMaxLat) / 2

          samplePoints.push(Cesium.Cartesian3.fromDegrees(centerLng, centerLat))

          // 存储矩形信息
          rectInfoMap.set(rectId, {
            id: rectId,
            minLng: rectMinLng,
            maxLng: rectMaxLng,
            minLat: rectMinLat,
            maxLat: rectMaxLat,
            center: [centerLng, centerLat],
          })

          rectId++
        }
      }

      try {
        const result = await mars3d.thing.Slope.getSlope({
          map: window.viewer,
          positions: samplePoints,
          splitNum: 1,
          radius: 10,
          count: 8,
          exact: true,
        })

        console.log('坡度分析结果:', result)

        const slopes = result.data.map((d) => d.slope)

        for (let i = 0; i < xSplit; i++) {
          for (let j = 0; j < ySplit; j++) {
            const index = i * ySplit + j
            const rectInfo = rectInfoMap.get(index + 1)
            const rectPos = [
              [rectInfo.minLng, rectInfo.minLat],
              [rectInfo.maxLng, rectInfo.minLat],
              [rectInfo.maxLng, rectInfo.maxLat],
              [rectInfo.minLng, rectInfo.maxLat],
            ]

            const slope = slopes[index]
            const distance = this.calcMinDistance(rectPos, this.layers['people'].layer)

            if (slope < this.form.maxSlope && distance > this.form.minPeopleDis) {
              const soilGeoId = this.getIntersectId(rectPos, this.layers['soil'].layer)
              const plantGeoId = this.getIntersectId(rectPos, this.layers['plant'].layer)
              const plantScore = plantGeoId
                ? this.getScore(
                    this.layers['plant'].layer.getGraphicById(plantGeoId).options.attr['编码'],
                    this.plantJson
                  )
                : 0
              const soilScore = soilGeoId
                ? this.getScore(this.layers['soil'].layer.getGraphicById(soilGeoId).options.attr['编码'], this.soilJson)
                : 1

              /* 2. 合并成一条记录 */
              this.validBlocks.push({
                id: `rect_${index + 1}`,
                positions: rectPos,
                center: [(rectInfo.minLng + rectInfo.maxLng) / 2, (rectInfo.minLat + rectInfo.maxLat) / 2],
                slope,
                distance,
                plantScore,
                soilScore,
                index,
              })
            }
          }
        }
      } catch (e) {
        console.error('坡度分析失败', e)
        this.$message.error('坡度分析失败，请检查地形数据')
        this.mapLoading = false
      } finally {
        rectInfoMap.clear()
        this.drawLabelAndRec()
      }
    },

    drawLabelAndRec() {
      this.analyzing = false

      /* 1. 计算总分(保持原逻辑) */
      const list = this.calcTotalScore(this.validBlocks)
      const len = list.length
      if (!len) {
        this.$message.success('计算结束，暂无符合条件的区域！')
        this.mapLoading = false
        return
      }

      /* 2. 一次性构造 options 数组，不 new Graphic */
      const rectOpts = []
      const labelOpts = []

      for (let i = 0; i < len; i++) {
        const b = list[i]

        const color = this.getColors(list[i].totalScore)
        rectOpts.push({
          id: b.id,
          positions: Cesium.Cartesian3.fromDegreesArray(b.positions.flat()),
          style: {
            color: color,
            opacity: 0.7,
            outline: true,
            outlineColor: '#ffffff',
            outlineWidth: 1,
            fill: true,
            material: color,
            classificationType: Cesium.ClassificationType.BOTH,
          },
          center: new mars3d.LngLatPoint(b.center[0], b.center[1], 0),
          attr: {
            isRectangle: true,
            originalData: b,
          },
          depthTestAgainstTerrain: false,
          allowPicking: true,
        })

        let tab = {
          id: i + 1 + '',
          key: b.totalScore,
          [b.totalScore]: {
            roadPointList: [],
            roadLineList: [],
            waterPointList: [],
            waterLineList: [],
          },
        }

        this.tabs.push({
          id: i + 1 + '',
          label: b.totalScore,
        })

        this.tabList.push(tab)

        labelOpts.push({
          id: `label_${b.id}`,
          position: new mars3d.LngLatPoint(b.center[0], b.center[1], 0),
          style: {
            text: String(b.totalScore),
            font_size: 24,
            font_family: '等线',
            color: '#000000',
            outline: false,
            horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            visibleDepth: false,
            scaleByDistance: true,
            disablePicking: true,
          },
          attr: {
            isLabel: true,
            relatedRectId: b.id,
          },
        })
      }

      this.rectangles = rectOpts.map((opt) => new mars3d.graphic.PolygonEntity(opt))
      this.activeName = this.tabs[0].id
      console.log('this.rectangles===>', this.rectangles, this.rectangles.length)

      this.labels = labelOpts.map((opt) => new mars3d.graphic.LabelEntity(opt))

      window.graphicLayer.addGraphic(this.rectangles)
      window.graphicLayer.addGraphic(this.labels)

      window.graphicLayer.off(mars3d.EventType.click, this._selectRect, this)

      this.rectangles.forEach((rectGraphic) => {
        rectGraphic.on(mars3d.EventType.click, (event) => {
          this._selectRect(event)
        })
      })

      this._selectRect = (event) => {
        const graphic = event.graphic
        if (graphic && graphic.attr && graphic.attr.isRectangle) {
          this.selectRectangle(graphic)
        }
      }
      if (!this.roadWaterLock) {
        this.loadLayers(['roadPoint', 'roadLine', 'waterPoint', 'waterLine'], (results) => {
          this.roadWaterLock = true
          this.$message.success(`计算成功！共有${len}个符合条件的区域`)
          this.showExport = false
          this.mapLoading = false
        })
      } else {
        this.mapLoading = false
      }
    },

    /**
     * 计算综合得分
     * @param {Array} list 原始数据
     * @returns {Array} 原数组就地添加 totalScore
     */
    calcTotalScore(data) {
      // 提前计算并缓存范围值
      let minSlope = Infinity
      let maxSlope = -Infinity
      let minDistance = Infinity
      let maxDistance = -Infinity

      // 单次遍历计算所有范围值
      for (let i = 0; i < data.length; i++) {
        const item = data[i]
        if (item.slope < minSlope) minSlope = item.slope
        if (item.slope > maxSlope) maxSlope = item.slope
        if (item.distance < minDistance) minDistance = item.distance
        if (item.distance > maxDistance) maxDistance = item.distance
      }

      // 计算常量值，避免重复计算
      const slopeRange = maxSlope - minSlope
      const distanceRange = maxDistance - minDistance
      const isDistanceRangeSmall = distanceRange < 0.0001

      // 设置权重
      const slopePer = this.form.slopePer
      const peoplePer = this.form.peoplePer
      const plantPer = this.form.plantPer
      const soilPer = this.form.soilPer

      const defaultPeoplePer = 0.5 * peoplePer

      // 预分配结果数组
      const results = new Array(data.length)

      // 使用for循环代替map，性能更好
      for (let i = 0; i < data.length; i++) {
        const item = data[i]

        // 计算slope分数
        const slopeScore = slopeRange > 0 ? ((item.slope - minSlope) / slopeRange) * slopePer : 0.5 * slopePer // 处理slope范围很小的情况

        // 计算distance分数
        const peopleScore = isDistanceRangeSmall
          ? defaultPeoplePer
          : ((item.distance - minDistance) / distanceRange) * peoplePer

        // 计算其他分数
        const plantScore = (item.plantScore || 0) * plantPer
        const soilScore = item.soilScore * soilPer

        const totalScore = (slopeScore + peopleScore + plantScore + soilScore).toFixed(2)

        results[i] = {
          ...item,
          slopeScore,
          peopleScore,
          plantScore,
          soilScore,
          totalScore,
        }
      }

      return results
    },

    // 根据坡度值获取颜色
    getColors(total) {
      const idx = Math.round(Math.max(0, Math.min(9, total * 9)))
      return Cesium.Color.fromCssColorString(this.colorList[idx])
    },

    // 根据 code 查 score
    getScore(targetCode, list) {
      if (!targetCode) {
        return 0
      }
      const hit = list.find((item) => Array.isArray(item.code) && item.code.includes(targetCode))
      if (hit) return hit.score

      const fallback = list.find((item) => item.code === null)
      return fallback ? fallback.score : 0
    },

    // 选择矩形
    selectRectangle(rect) {
      this.showLoading = true
      setTimeout(() => {
        this.showLoading = false
      }, 500)

      if (this.selectedRect?.id === rect.id) return
      this.selectedRect = {
        id: rect.id,
        center: rect.center,
      }

      this.rectTotalScore = rect.attr.originalData.totalScore

      this.roadPointList = []
      this.roadLineList = []
      this.waterPointList = []
      this.waterLineList = []

      // 清除tablist中原本存在的数据
      let tabIndex = this.tabList.findIndex((item) => item.key === this.rectTotalScore)
      if (tabIndex !== -1) {
        this.tabList[tabIndex][this.rectTotalScore].roadPointList = []
        this.tabList[tabIndex][this.rectTotalScore].roadLineList = []
        this.tabList[tabIndex][this.rectTotalScore].waterPointList = []
        this.tabList[tabIndex][this.rectTotalScore].waterLineList = []
      }

      this.getRoadWaterIds(rect, tabIndex)
    },

    // 获取相交的道路和水路
    getRoadWaterIds(rect, tabIndex) {
      const positions = rect.points.map((d) => [d.lng, d.lat])

      let roadPointId = this.getIntersectId(positions, this.layers['roadPoint'].layer)
      let roadLineId = this.getIntersectId(positions, this.layers['roadLine'].layer)
      let waterPointId = this.getIntersectId(positions, this.layers['waterPoint'].layer)
      let waterLineId = this.getIntersectId(positions, this.layers['waterLine'].layer)

      if (roadPointId) {
        this.roadPointList.push(this.layers['roadPoint'].layer.getGraphicById(roadPointId).options.attr)
        this.tabList[tabIndex][this.rectTotalScore].roadPointList.push(
          this.layers['roadPoint'].layer.getGraphicById(roadPointId).options.attr
        )
      } else if (roadLineId) {
        this.roadLineList.push(this.layers['roadLine'].layer.getGraphicById(roadLineId).options.attr)
        this.tabList[tabIndex][this.rectTotalScore].roadLineList.push(
          this.layers['roadLine'].layer.getGraphicById(roadLineId).options.attr
        )
      } else if (waterPointId) {
        this.waterPointList.push(this.layers['waterPoint'].layer.getGraphicById(waterPointId).options.attr)
        this.tabList[tabIndex][this.rectTotalScore].waterPointList.push(
          this.layers['waterPoint'].layer.getGraphicById(waterPointId).options.attr
        )
      } else if (waterLineId) {
        this.waterLineList.push(this.layers['waterLine'].layer.getGraphicById(waterLineId).options.attr)
        this.tabList[tabIndex][this.rectTotalScore].waterLineList.push(
          this.layers['waterLine'].layer.getGraphicById(waterLineId).options.attr
        )
      }
    },

    // 点击导出按钮
    clickExport() {
      this.rectangles.forEach((rectGraphic) => {
        this.selectRectangle(rectGraphic)
      })

      this.dialogVisible = true
    },

    // 导出为 JSON文件
    exportJSON() {
      // const data = JSON.stringify(this.tabList)
      // FileSaver.saveAs(new Blob([data], {type: 'application/json'}), 'data.json')

      try {
        fetch('./config.ini')
          .then((r) => r.text())
          .then((res) => {
            const parsedData = iniParser.parse(res)
            axios
              .post(
                `http://${parsedData.http.address}:${parsedData.http.port}/api/area`,
                JSON.stringify(this.tabList),
                {
                  headers: {'Content-Type': 'application/json'},
                }
              )
              .then((res) => {
                this.$message.success(res)
                this.$message.success('导出成功!')
              })
              .catch((error) => {
                this.$message.error(error)
              })
          })
      } catch (error) {
        console.error(error)
      }
    },

    // json编辑
    handleExportJosn() {
      this.dialogJsonVisible = true
    },

    decodeEscapedJson(str) {
      // 去掉字符串首尾的空白字符
      str = str.trim()
      // 直接解析 JSON 字符串
      try {
        return JSON.parse(str)
      } catch (e) {
        console.error('Error parsing JSON:', e)
        return null
      }
    },

    handleJson() {
      this.jsonLoading = true
      fetch('./config.ini')
        .then((response) => response.text())
        .then((text) => {
          const parsedData = iniParser.parse(text)
          axios
            .post(`http://${parsedData.http.address}:${parsedData.http.port}/api/json/select`, {
              headers: {
                'Content-Type': 'application/json',
              },
              timeout: 10 * 60 * 1000,
            })
            .then((response) => {
              this.jsonInfo = {
                path: response.data.path,
                json: JSON.stringify(this.decodeEscapedJson(response.data.json), null, 2),
              }
              this.jsonLoading = false
            })
            .catch((error) => {
              this.jsonLoading = false
            })
        })
    },

    confirmJson() {
      fetch('./config.ini')
        .then((response) => response.text())
        .then((text) => {
          const parsedData = iniParser.parse(text)
          axios
            .post(
              `http://${parsedData.http.address}:${parsedData.http.port}/api/json/save?path=${this.jsonInfo.path}`,
              JSON.stringify(JSON.parse(this.jsonInfo.json)),
              {
                headers: {
                  'Content-Type': 'application/json',
                },
              }
            )
            .then((response) => {
              this.$message.success('保存成功')
              this.closeJson()
            })
            .catch((error) => {})
        })
    },

    closeJson() {
      this.jsonInfo.path = ''
      this.jsonInfo.json = ''
      this.dialogJsonVisible = false
    },

    // 新增
    handleAdd(type) {
      let tabIndex = this.tabList.findIndex((item) => item.id === this.activeName)
      let list = this.tabList[tabIndex]

      const newRow = {
        seq: list[list.key][type].length + 1,
        编码: '',
        名称: '',
        A: '',
        editing: true,
      }
      list[list.key][type].push(newRow)
      setTimeout(() => {
        this.$refs[`${this.activeName}_${type}REF`].refreshScroll()
        this.$refs[`${this.activeName}_${type}REF`].scrollToRow(newRow, 'id')
      }, 50)
    },

    handleEdit(row) {
      this.$set(row, 'editing', true)
    },

    handleSave(row) {
      this.$set(row, 'editing', false)
    },

    handleDelete(type, row) {
      let tabIndex = this.tabList.findIndex((item) => item.id === this.activeName)
      let list = this.tabList[tabIndex]

      const index = list[list.key][type].findIndex((item) => item.id === row.id)
      if (index !== -1) {
        list[list.key][type].splice(index, 1)
      }
    },

    // 清除所有矩形
    clearRectangles() {
      if (this.rectangles.length > 0) {
        this.rectangles.forEach((item) => item.remove())
        window.graphicLayer.removeGraphic(this.rectangles)
        this.rectangles = []
        this.rectCount = 0
        this.validBlocks = []
      }
      if (this.labels.length > 0) {
        this.labels.forEach((item) => item.remove())
        window.graphicLayer.removeGraphic(this.labels)
        this.labels = []
      }
      this.roadPointList = []
      this.roadLineList = []
      this.waterPointList = []
      this.waterLineList = []
      this.tabs = []
      this.tabList = []
      this.rectTotalScore = ''
      this.activeName = ''
    },
  },
}
</script>

<style scoped lang="scss">
.sure {
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
  gap: 10px;
  background: #176363;
  border-radius: 4px;
  min-width: 14px;
  padding: 0 10px;
  height: 24px;
  color: #ffffff;
  font-weight: 400;
  font-size: 14px;
  margin-right: 10px;
  cursor: pointer;
}

#page {
  width: 340px;
  height: 100%;
  overflow: hidden;
  box-sizing: border-box;
  position: relative;
}
.header {
  width: 100%;
  height: 60px;
  background-color: #abc6bc;
  position: relative;
  .images {
    position: relative;
    top: 50%;
    transform: translateY(-50%);
    cursor: pointer;
    margin-left: 32px;
    img {
      margin-right: 30px;
    }
  }
  .btn {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    right: 32px;
  }
  .right {
    position: absolute;
    right: 30px;
  }
}

.content {
  width: 100%;
  height: calc(100% - 60px);

  .left {
    width: 348px;
    height: 100%;
    background-color: #d4e5db;
    .control-panel {
      width: 340px;
      padding: 20px 26px;
      box-sizing: border-box;
      background-size: cover;
      background: #d4e5db;
      .title {
        color: #1c1c1c;
        margin-bottom: 10px;
      }
    }

    .importJson {
      background: #2f705f;
      border-radius: 4px;
      color: #fff;
      width: 260px;
      height: 24px;
      font-size: 14px;
      text-align: center;
      margin-left: 20px;
      cursor: pointer;
    }
  }

  .center {
    width: calc(100% - 348px - 452px);
    height: 100%;
  }

  .right {
    width: 452px;
    height: 100%;
    box-sizing: border-box;
    background-color: #d4e5db;
    padding-left: 20px;
    padding-right: 10px;
    overflow: auto;

    .item {
      margin-bottom: 10px;
    }
  }
}
.table-title {
  font-size: 16px;
  font-weight: bold;
  letter-spacing: 0.1em;
  margin-bottom: 10px;
  span {
    margin-right: 10px;
  }
}

.export {
  position: relative;
  top: -20px;
}
.road,
.water {
  width: 100%;
  .point,
  .line {
    width: 49%;
  }
}
.road {
  margin-bottom: 20px;
}
</style>
