<template>
  <div class="edit-map-page" @wheel="handleWheel">
    <div class="top-tool">
      <div class="top-tool-list-container">
        <el-scrollbar>
          <div class="top-tool-list">
            <div v-for="item in state.topToolList" :key="item.switchType" class="top-tool-item">
              <el-popover
                placement="bottom"
                :width="170"
                trigger="click"
                v-if="item.switchType === 'move' || item.switchType === 'revolve'"
                :disabled="state.currentItemIndex === -1"
              >
                <template #reference>
                  <div
                    class="tool-item"
                    :class="
                      toolbarSwitchType === item.switchType
                        ? 'tool-active'
                        : item.isActive
                          ? 'right-tool-active'
                          : ''
                    "
                    @click="toolbarClick(item)"
                  >
                    <Icon :icon="item.icon" :size="20" />
                    <div class="name"> {{ item.name }} </div>
                  </div>
                </template>
                <!-- 位置 -->
                <el-form :model="state.moveForm" v-if="item.switchType === 'move'" class="mt-2">
                  <el-form-item label="X">
                    <el-input-number
                      v-model="state.moveForm.locationX"
                      :min="0"
                      :max="imgBgObj.width"
                      controls-position="right"
                    />
                  </el-form-item>
                  <el-form-item label="Y">
                    <el-input-number
                      v-model="state.moveForm.locationY"
                      :min="0"
                      :max="imgBgObj.height"
                      controls-position="right"
                    />
                  </el-form-item>
                  <div style="text-align: right">
                    <el-button
                      size="small"
                      style="width: 4rem; height: 1.875rem; background: #00329f"
                      color="#00329F"
                      @click="moveFormSubmit()"
                      >确认</el-button
                    >
                  </div>
                </el-form>
                <!-- 旋转 -->
                <el-form
                  :model="state.rotationForm"
                  v-if="item.switchType === 'revolve'"
                  class="mt-2"
                >
                  <el-form-item label="角度">
                    <el-input-number
                      v-model="state.rotationForm.angle"
                      :min="0"
                      :max="259"
                      controls-position="right"
                    />
                  </el-form-item>
                  <div style="text-align: right">
                    <el-button
                      size="small"
                      style="width: 4rem; height: 1.875rem; background: #00329f"
                      color="#00329F"
                      @click="rotationFormSubmit"
                      >确认</el-button
                    >
                  </div>
                </el-form>
              </el-popover>
              <el-popover
                placement="bottom"
                :width="170"
                trigger="click"
                v-else-if="item.switchType === 'grid'"
              >
                <template #reference>
                  <div
                    class="tool-item"
                    :class="
                      toolbarSwitchType === item.switchType
                        ? 'tool-active'
                        : item.isActive
                          ? 'right-tool-active'
                          : ''
                    "
                    @click="toolbarClick(item)"
                  >
                    <Icon :icon="item.icon" :size="20" />
                    <div class="name"> {{ item.name }} </div>
                  </div>
                </template>
                <!-- 网格  -->
                <el-form :model="state.gridForm" v-if="item.switchType === 'grid'" class="mt-2">
                  <el-form-item label="网格">
                    <el-select v-model="state.gridForm.gridNum" placeholder="选择">
                      <el-option label="0.5米" value="10px" />
                      <el-option label="1米" value="20px" />
                      <el-option label="2米" value="40px" />
                      <el-option label="3米" value="60px" />
                    </el-select>
                  </el-form-item>
                </el-form>
              </el-popover>
              <!-- 线库 -->
              <el-popover
                placement="bottom"
                trigger="click"
                v-else-if="item.switchType === 'lineLibrary'"
                :popper-style="{ padding: '0rem' }"
              >
                <template #reference>
                  <div
                    class="tool-item"
                    :class="
                      toolbarSwitchType === 'lineLibrary' ||
                      toolbarSwitchType === 'createLineLibrary' ||
                      toolbarSwitchType === 'lineLibraryManage'
                        ? 'tool-active'
                        : item.isActive
                          ? 'right-tool-active'
                          : ''
                    "
                    @click="toolbarClick(item)"
                  >
                    <Icon :icon="item.icon" :size="20" />
                    <div class="name"> {{ item.name }} </div>
                  </div>
                </template>
                <div v-if="item.switchType === 'lineLibrary'" class="drop-down-menu">
                  <div
                    class="drop-down-menu-item"
                    :class="toolbarSwitchType === 'createLineLibrary' ? 'right-tool-active' : ''"
                    @click="toolbarClick({ switchType: 'createLineLibrary' })"
                    >生成线库</div
                  >
                  <div
                    class="drop-down-menu-item"
                    :class="toolbarSwitchType === 'lineLibraryManage' ? 'right-tool-active' : ''"
                    @click="toolbarClick({ switchType: 'lineLibraryManage' })"
                    >线库管理</div
                  >
                </div>
              </el-popover>
              <!-- 区域  -->
              <el-popover
                placement="bottom"
                trigger="click"
                v-else-if="item.switchType === 'region'"
                :popper-style="{ padding: '0rem' }"
              >
                <template #reference>
                  <div
                    class="tool-item"
                    :class="
                      toolbarSwitchType === 'region' ||
                      toolbarSwitchType === 'createRegion' ||
                      toolbarSwitchType === 'regionManage'
                        ? 'tool-active'
                        : item.isActive
                          ? 'right-tool-active'
                          : ''
                    "
                    @click="toolbarClick(item)"
                  >
                    <Icon :icon="item.icon" :size="20" />
                    <div class="name"> {{ item.name }} </div>
                  </div>
                </template>
                <div v-if="item.switchType === 'region'" class="drop-down-menu">
                  <div
                    class="drop-down-menu-item"
                    :class="toolbarSwitchType === 'createRegion' ? 'right-tool-active' : ''"
                    @click="toolbarClick({ switchType: 'createRegion' })"
                    >生成物料区域</div
                  >
                  <div
                    class="drop-down-menu-item"
                    :class="toolbarSwitchType === 'regionManage' ? 'right-tool-active' : ''"
                    @click="toolbarClick({ switchType: 'regionManage' })"
                    >物料区域管理</div
                  >
                </div>
              </el-popover>
              <!-- 标记 -->
              <el-popover
                placement="bottom"
                trigger="click"
                v-else-if="item.switchType === 'marker'"
                width="220"
                :visible="state.markPopoverVisible"
              >
                <template #reference>
                  <div
                    class="tool-item"
                    :class="
                      toolbarSwitchType === item.switchType
                        ? 'tool-active'
                        : item.isActive
                          ? 'right-tool-active'
                          : ''
                    "
                    @click="toolbarClick(item)"
                  >
                    <Icon :icon="item.icon" :size="20" />
                    <div class="name"> {{ item.name }} </div>
                  </div>
                </template>
                <!-- 位置 -->
                <el-form :model="state.markForm" class="mt-2" label-width="68">
                  <el-form-item label="标记车辆">
                    <el-select
                      v-model="state.markForm.macAddress"
                      placeholder="请选择"
                      @change="macAddressChange"
                    >
                      <el-option
                        v-for="car in state.mapMarkCarList"
                        :key="car.id"
                        :label="car.robotNo"
                        :value="car.macAddress"
                      />
                    </el-select>
                  </el-form-item>
                  <el-form-item label="标记属性" v-if="state.markForm.markProperty">
                    <el-input v-model="state.markForm.markProperty" style="width: 15rem" disabled />
                  </el-form-item>
                  <el-form-item label="原节点" v-if="state.markForm.originalNode">
                    <el-input v-model="state.markForm.originalNode" style="width: 15rem" disabled />
                  </el-form-item>
                  <div style="text-align: right">
                    <el-button
                      size="small"
                      style="width: 4rem; height: 1.875rem; background: #efefef"
                      @click="markFormCancel()"
                      >取消</el-button
                    >
                    <el-button
                      size="small"
                      style="width: 4rem; height: 1.875rem; background: #00329f"
                      color="#00329F"
                      @click="markFormSubmit()"
                      >确认</el-button
                    >
                  </div>
                </el-form>
              </el-popover>
              <div
                v-else
                class="tool-item"
                :class="
                  toolbarSwitchType === item.switchType
                    ? 'tool-active'
                    : item.isActive
                      ? 'right-tool-active'
                      : ''
                "
                @click="toolbarClick(item)"
              >
                <Icon :icon="item.icon" :size="20" />
                <div class="name"> {{ item.name }} </div>
              </div>
              <!-- 分隔线 -->
              <div
                class="line"
                v-if="
                  item.switchType === 'save' ||
                  item.switchType === 'delete' ||
                  item.switchType === 'grid' ||
                  (item.switchType === 'next' &&
                    (toolbarSwitchType === 'createLineLibrary' ||
                      toolbarSwitchType === 'createRegion' ||
                      toolbarSwitchType === 'drawRoute' ||
                      toolbarSwitchType === 'generateLine' ||
                      toolbarSwitchType === 'batchCopying' ||
                      toolbarSwitchType === 'bulkEditingRoute' ||
                      toolbarSwitchType === 'bulkEditingNode' ||
                      toolbarSwitchType === 'bulkDelete'))
                "
              ></div>
              <el-button
                v-if="
                  item.switchType === 'next' &&
                  (toolbarSwitchType === 'createLineLibrary' ||
                    toolbarSwitchType === 'createRegion' ||
                    toolbarSwitchType === 'drawRoute' ||
                    toolbarSwitchType === 'generateLine' ||
                    toolbarSwitchType === 'batchCopying' ||
                    toolbarSwitchType === 'bulkEditingRoute' ||
                    toolbarSwitchType === 'bulkEditingNode' ||
                    toolbarSwitchType === 'bulkDelete')
                "
                type="danger"
                class="selection-area-btn"
                @click="clickDrawSelectionArea"
                >确定</el-button
              >
            </div>
          </div>
        </el-scrollbar>
        <div class="search-select">
          <img
            class="search-icon"
            v-if="!state.isSearchSelectVisible"
            @click="toggleSelect"
            src="@/assets/search.png"
          />
          <el-select
            class="!w-160px"
            v-else
            v-model="state.searchSelectedOption"
            @blur="toggleSelect"
            placeholder="请选择节点号"
            @change="searchSelectChange"
            filterable
          >
            <el-option
              v-for="item in state.haveSortNumMapPointInfo"
              :key="item.sortNum"
              :label="item.sortNum"
              :value="item.sortNum"
            />
          </el-select>
        </div>
      </div>

      <div class="right-tool-list" v-if="state.isShowToolbar">
        <el-scrollbar height="calc(100vh - 200px)">
          <div
            v-for="item in state.rightToolList"
            :key="item.switchType"
            class="tool-item"
            :class="
              toolbarSwitchType === item.switchType
                ? 'tool-active'
                : item.isActive
                  ? 'right-tool-active'
                  : ''
            "
            @click="toolbarClick(item)"
          >
            <el-popover
              placement="left"
              :width="170"
              trigger="click"
              v-if="item.switchType === 'routeWidth' || item.switchType === 'clickDrawRoute'"
            >
              <template #reference>
                <div @click="toolbarClick(item)">
                  <Icon :icon="item.icon" :size="20" />
                  <div class="name"> {{ item.name }} </div>
                </div>
              </template>
              <!-- 网格  -->
              <el-form
                :model="state.routeWidthForm"
                v-if="item.switchType === 'routeWidth'"
                class="mt-2"
              >
                <el-form-item label="路线宽度">
                  <el-select v-model="state.routeWidthForm.routeWidth" placeholder="选择">
                    <el-option label="1" :value="1" />
                    <el-option label="2" :value="2" />
                    <el-option label="3" :value="3" />
                    <el-option label="4" :value="4" />
                    <el-option label="5" :value="5" />
                  </el-select>
                </el-form-item>
              </el-form>
              <!-- 新增路线 -->
              <el-form v-if="item.switchType === 'clickDrawRoute'" class="mt-2">
                <el-form-item label="路线方向">
                  <el-select v-model="state.routeDirection" placeholder="选择">
                    <el-option label="单向" :value="1" />
                    <el-option label="双向" :value="2" />
                  </el-select>
                </el-form-item>
              </el-form>
            </el-popover>
            <!-- 手动录入实际点位 -->
            <el-popover
              placement="left"
              trigger="click"
              v-else-if="item.switchType === 'entryActualNode'"
              width="250"
              :visible="state.entryNodePopoverVisible"
            >
              <template #reference>
                <div @click="toolbarClick(item)">
                  <Icon :icon="item.icon" :size="20" />
                  <div class="name"> {{ item.name }} </div>
                </div>
              </template>
              <!-- 位置 -->
              <el-form :model="state.entryActualNodeForm" class="mt-2" label-width="78">
                <el-form-item label="实际点位X">
                  <el-input v-model="state.entryActualNodeForm.x" placeholder="请输入" />
                </el-form-item>
                <el-form-item label="实际点位Y">
                  <el-input v-model="state.entryActualNodeForm.y" placeholder="请输入" />
                </el-form-item>
                <el-form-item label="弧度">
                  <el-input-number
                    v-model="state.entryActualNodeForm.locationYaw"
                    :min="-state.MathPI"
                    :max="state.MathPI"
                    placeholder="请输入"
                    :precision="2"
                    controls-position="right"
                  />
                </el-form-item>
                <div style="text-align: right">
                  <el-button
                    size="small"
                    style="width: 4rem; height: 1.875rem; background: #efefef"
                    @click="entryActualNodeFormCancel()"
                    >取消</el-button
                  >
                  <el-button
                    size="small"
                    style="width: 4rem; height: 1.875rem; background: #00329f"
                    color="#00329F"
                    @click="entryActualNodeFormSubmit()"
                    >确认</el-button
                  >
                </div>
              </el-form>
            </el-popover>
            <el-popover
              placement="left"
              trigger="click"
              v-else-if="item.switchType === 'bulkOperation'"
              :popper-style="{ padding: '0rem' }"
            >
              <template #reference>
                <div @click="toolbarClick(item)">
                  <Icon :icon="item.icon" :size="20" />
                  <div class="name"> {{ item.name }} </div>
                </div>
              </template>
              <div class="drop-down-menu">
                <div
                  v-for="action in state.bulkOperationList"
                  :key="action.switchType"
                  class="drop-down-menu-item"
                  :class="toolbarSwitchType === action.switchType ? 'right-tool-active' : ''"
                  @click="toolbarClick(action)"
                  >{{ action.name }}</div
                >
              </div>
            </el-popover>
            <div v-else>
              <Icon :icon="item.icon" :size="20" />
              <div class="name"> {{ item.name }} </div>
            </div>
          </div>
        </el-scrollbar>
      </div>
    </div>

    <div
      class="map-container"
      ref="mapContainerRef"
      :style="{ cursor: state.cursorStyle }"
      v-if="imgBgObj.width && imgBgObj.height"
    >
      <!-- <map-scale-tool :stepLength="50" :width="imgBgObj.width" :height="imgBgObj.height"> -->
      <!-- transform: `scale(${state.imageChangeMultiple}) rotate(-5deg)`, -->
      <div
        class="map-bg"
        :style="{
          backgroundImage: `url(${imgBgObj.imgUrl})`,
          transform: `scale(${state.imageChangeMultiple})`,
          transformOrigin: '0 0',
          width: `${imgBgObj.width}px`,
          height: `${imgBgObj.height}px`
        }"
        @mousedown="startDrawSelection"
        @mousemove="updateDrawSelection"
        @mouseup="endDrawSelection"
      >
        <div
          ref="mapBackgroundRef"
          class="map-box-inner"
          @click="mapClick"
          :style="gradientBackground"
          v-if="interfaceRefreshed"
        >
          <VueDragResizeRotate
            v-for="(item, index) in state.allMapPointInfo"
            :key="index + Number(item.locationX)"
            :parent="true"
            :x="Number(item.locationX) - Number(item.locationWidePx) / 2"
            :y="Number(item.locationY) - Number(item.locationDeepPx) / 2"
            :w="Number(item.locationWidePx)"
            :h="Number(item.locationDeepPx)"
            :r="item.angle"
            :snap="true"
            :snapTolerance="10"
            @rotatestop="(degree) => rotateEnd(degree, item, index)"
            @dragstop="(x, y) => dragEnd(x, y, item, index)"
            @resizestop="(x, y, width, height) => resizeEnd(x, y, width, height, item, index)"
            @activated="() => activatedHandle(item, index)"
            @deactivated="deactivatedHandle"
            :draggable="!state.prohibitedOperation && item.draggable"
            :resizable="!state.prohibitedOperation && item.resizable"
            :rotatable="!state.prohibitedOperation && item.rotatable"
            :lock-aspect-ratio="item.lockAspectRatio"
            :scaleRatio="state.imageChangeMultiple"
            style="border: none; z-index: 999"
            @ref-line-params="getRefLineParams"
          >
            <!-- 节点合集 -->
            <div
              @mousedown="startFromPoint(index, $event)"
              @click.stop="handleNodeClick(item)"
              :style="{ width: item.locationWidePx + 'px', height: item.locationDeepPx + 'px' }"
            >
              <!-- 1 路径点 -->
              <el-tooltip effect="dark" placement="top" trigger="click">
                <template #content>
                  <div v-if="item.type === 1">
                    <div>序号：{{ item.sortNum || '节点未保存' }}</div>
                  </div>
                  <div v-else-if="item.type === 2">
                    <div>序号：{{ item.sortNum || '节点未保存' }}</div>
                    <div class="item-tooltip-name" v-if="item.laneId && item.laneName">
                      所属线库：{{ item.laneName }}
                    </div>
                    <div class="item-tooltip-name" v-if="item.areaId && item.areaName">
                      所属区域：{{ item.areaName }}
                    </div>
                    <div class="item-tooltip-name" v-if="item.areaId && item.skuInfo">
                      物料信息：{{ item.skuInfo }}
                    </div>
                  </div>
                  <div v-else-if="item.type === 3">
                    <div>序号：{{ item.sortNum || '节点未保存' }}</div>
                    <div class="item-tooltip-name" v-if="item.deviceType">
                      设备类型：{{ getDeviceTypeName(item.deviceType) }}
                    </div>
                    <div class="item-tooltip-name" v-if="item.deviceNo">
                      设备编号：{{ item.deviceNo }}
                    </div>
                  </div>
                  <div v-else>
                    <div>序号：{{ item.sortNum || '节点未保存' }}</div>
                    <div class="item-tooltip-name">
                      节点类型：{{
                        item.type == 4
                          ? '停车点'
                          : item.type == 5
                            ? '区域变更点'
                            : item.type == 6
                              ? '等待点'
                              : ''
                      }}
                    </div>
                  </div>
                </template>
                <div @contextmenu="handleContextMenu(item, index, $event)">
                  <div
                    v-if="
                      item.type === 1 &&
                      item.layerSelectionShow &&
                      state.isShowSortNum &&
                      item.sortNum
                    "
                    class="sort-num"
                    :style="getSortNumStyle(item, index)"
                  >
                    {{ item.sortNum }}
                  </div>
                  <div
                    v-if="
                      item.type !== 1 &&
                      item.layerSelectionShow &&
                      state.isShowSortNum &&
                      item.sortNum
                    "
                    class="sort-num-location"
                    :style="getSortNumLocationStyle(item, index)"
                  >
                    {{ item.sortNum }}
                  </div>
                  <div
                    class="sort-num-location"
                    :style="getLocationNumberStyle(item, index)"
                    v-if="
                      toolbarSwitchType === 'createLineLibrary' &&
                      item.type === 2 &&
                      item.locationNumber
                    "
                  >
                    {{ item.locationNumber }}
                  </div>
                  <div
                    v-if="item.type === 1 && item.layerSelectionShow"
                    :style="{
                      width: item.locationWidePx + 'px',
                      height: item.locationDeepPx + 'px',
                      backgroundColor: state.currentItemIndex === index ? '#5ecc62' : '#000',
                      borderRadius: '50%',
                      zIndex: 999
                    }"
                  >
                  </div>
                  <!-- 2 库位点 -->
                  <img
                    v-if="item.type === 2 && item.layerSelectionShow"
                    src="@/assets/imgs/indexPage/bin-location.png"
                    alt=""
                    :style="binLocationStyle(item, index)"
                  />
                  <!-- 3 设备点 -->
                  <img
                    v-if="item.type === 3 && item.layerSelectionShow"
                    :src="item.mapImageUrl || '@/assets/imgs/indexPage/equipment.png'"
                    alt=""
                    style="background: #fff"
                    :style="nodeStyle(item, index)"
                  />
                  <!-- 4 停车点 -->
                  <img
                    v-if="item.type === 4 && item.layerSelectionShow"
                    src="@/assets/imgs/indexPage/stop-car.png"
                    alt=""
                    style="background: #fff"
                    :style="nodeStyle(item, index)"
                  />
                  <!-- 5 区域变更点 -->
                  <img
                    v-if="item.type === 5 && item.layerSelectionShow"
                    src="@/assets/imgs/indexPage/change-point.png"
                    alt=""
                    style="background: #fff"
                    :style="nodeStyle(item, index)"
                  />
                  <!-- 6 等待点 -->
                  <img
                    v-if="item.type === 6 && item.layerSelectionShow"
                    src="@/assets/imgs/indexPage/wait-point.png"
                    alt=""
                    style="background: #fff"
                    :style="nodeStyle(item, index)"
                  />
                </div>
              </el-tooltip>
            </div>
            <div
              class="node-text"
              v-if="item.type === 7 && item.layerSelectionShow"
              :style="{
                position: 'absolute',
                color: item.fontColor,
                fontSize: item.fontSize + 'px',
                fontFamily: item.fontFamily,
                border: state.currentItemIndex === index ? '.0625rem dashed #000' : 'none'
              }"
            >
              {{ item.text }}
            </div>
          </VueDragResizeRotate>
          <span
            class="ref-line v-line"
            v-for="(item, index) in state.vLine"
            :key="'v_' + index"
            v-show="item.display"
            :style="{
              left: item.position,
              top: item.origin,
              height: item.lineLength
            }"
          ></span>
          <span
            class="ref-line h-line"
            v-for="(item, index) in state.hLine"
            :key="'h_' + index"
            :style="{
              top: item.position,
              left: item.origin,
              width: item.lineLength
            }"
          ></span>

          <div v-if="imgBgObj.width && imgBgObj.height">
            <svg
              :width="imgBgObj.width"
              :height="imgBgObj.height"
              :style="{
                position: 'absolute',
                top: 0,
                left: 0,
                zIndex: state.svgRouteZIndex
              }"
              id="svgId"
            >
              <!-- 实时绘制当前直线 -->
              <line
                v-if="state.isDrawing && toolbarSwitchType === 'clickDrawRoute'"
                :x1="Number(state.startDrawPoint.locationX)"
                :y1="Number(state.startDrawPoint.locationY)"
                :x2="Number(state.currentDrawX)"
                :y2="Number(state.currentDrawY)"
                stroke="#2d72d9"
                :stroke-width="state.routeWidthForm.routeWidth"
              />
              <template v-if="state.mapRouteList.length > 0">
                <template v-for="(curve, index) in state.mapRouteList" :key="index">
                  <!-- 直线 -->
                  <template v-if="curve.method === 0">
                    <line
                      :x1="Number(curve.startPointX)"
                      :y1="Number(curve.startPointY)"
                      :x2="Number(curve.endPointX)"
                      :y2="Number(curve.endPointY)"
                      :stroke="curve.isSelected ? '#f48924' : '#2d72d9'"
                      :stroke-width="state.routeWidthForm.routeWidth"
                      @click="(e) => handleChooseRoute(curve, index, 'line', e)"
                      @contextmenu="handleCurveContextMenu(curve, index, $event)"
                    />
                    <text
                      style="user-select: none"
                      :x="(Number(curve.startPointX) + Number(curve.endPointX)) / 2"
                      :y="(Number(curve.startPointY) + Number(curve.endPointY) - 18) / 2"
                      font-size="11"
                      text-anchor="middle"
                      fill="black"
                      v-if="curve.isSelected"
                      @click="(e) => handleChooseRoute(curve, index, 'line', e)"
                    >
                      {{ calculateRouteLength(curve, 'line') }}米
                    </text>
                  </template>

                  <template v-else>
                    <template v-if="state.isCurveDisplay">
                      <path
                        id="curvePath"
                        :d="getCurvePath(curve)"
                        :stroke="curve.isSelected ? '#f48924' : '#2d72d9'"
                        :stroke-width="state.routeWidthForm.routeWidth"
                        fill="none"
                        @click="handleChooseRoute(curve, index)"
                        @contextmenu="handleCurveContextMenu(curve, index, $event)"
                      />
                      <text
                        style="user-select: none"
                        :x="computedCurveTextX(curve)"
                        :y="computedCurveTextY(curve)"
                        font-size="11"
                        text-anchor="middle"
                        fill="black"
                        v-if="curve.isSelected"
                        @click="handleChooseRoute(curve, index)"
                      >
                        {{ calculateRouteLength(curve, 'curve') }}米
                      </text>

                      <!-- 第一条控制线 -->
                      <line
                        v-if="state.currentDragTarget.index == index"
                        :x1="Number(curve.startPointX)"
                        :y1="Number(curve.startPointY)"
                        :x2="curve.beginControlX"
                        :y2="curve.beginControlY"
                        :stroke="curve.isSelected ? '#f48924' : '#2d72d9'"
                        stroke-dasharray="4"
                        stroke-width="2"
                      />
                      <!-- 第二条控制线 -->
                      <line
                        v-if="state.currentDragTarget.index == index"
                        :x1="Number(curve.endPointX)"
                        :y1="Number(curve.endPointY)"
                        :x2="curve.endControlX"
                        :y2="curve.endControlY"
                        :stroke="curve.isSelected ? '#f48924' : '#2d72d9'"
                        stroke-dasharray="4"
                        stroke-width="2"
                      />
                    </template>
                    <!-- 控制点circle已移至SVG末尾 -->
                  </template>
                </template>
              </template>
              <!-- 控制点circle统一渲染，保证层级最高 -->
              <template
                v-if="
                  state.currentDragTarget.index !== null &&
                  state.mapRouteList[state.currentDragTarget.index].method !== 0
                "
              >
                <circle
                  id="startCircle"
                  :cx="state.mapRouteList[state.currentDragTarget.index].beginControlX"
                  :cy="state.mapRouteList[state.currentDragTarget.index].beginControlY"
                  r="5"
                  :fill="
                    state.mapRouteList[state.currentDragTarget.index].isSelected
                      ? '#f48924'
                      : '#2d72d9'
                  "
                  @mousedown="
                    startDrag(
                      state.mapRouteList[state.currentDragTarget.index],
                      state.currentDragTarget.index,
                      'start'
                    )
                  "
                />
                <circle
                  id="endCircle"
                  :cx="state.mapRouteList[state.currentDragTarget.index].endControlX"
                  :cy="state.mapRouteList[state.currentDragTarget.index].endControlY"
                  r="5"
                  :fill="
                    state.mapRouteList[state.currentDragTarget.index].isSelected
                      ? '#f48924'
                      : '#2d72d9'
                  "
                  @mousedown="
                    startDrag(
                      state.mapRouteList[state.currentDragTarget.index],
                      state.currentDragTarget.index,
                      'end'
                    )
                  "
                />
              </template>
            </svg>
          </div>
        </div>

        <!-- 框选区域 -->
        <div
          v-for="(box, index) in state.allDrawSelectionAreaBox"
          :key="index"
          :style="{
            position: 'absolute',
            left: `${box.x}px`,
            top: `${box.y}px`,
            width: `${box.width}px`,
            height: `${box.height}px`,
            border: '.125rem dashed #007bff',
            backgroundColor: 'rgba(0, 123, 255, 0.1)',
            zIndex: 999
          }"
        ></div>
        <!-- 当前框选区域 -->
        <div
          v-if="state.drawSelectionAreaShow"
          :style="{
            position: 'absolute',
            left: `${state.drawSelectionAreaBox.x}px`,
            top: `${state.drawSelectionAreaBox.y}px`,
            width: `${state.drawSelectionAreaBox.width}px`,
            height: `${state.drawSelectionAreaBox.height}px`,
            border: '.125rem dashed #007bff',
            backgroundColor: 'rgba(0, 123, 255, 0.1)',
            zIndex: 9999
          }"
        ></div>

        <!-- 测距 绘制点和连线 -->
        <template v-if="state.measureDistancesPoints.length > 0">
          <!-- 测距的线 -->
          <div v-if="state.measureDistancesPoints.length === 2" :style="rangingLineStyle"></div>
          <!-- 显示距离信息 -->
          <div v-if="state.measureDistancesNum !== null" :style="rangingTextStyle">
            {{ state.measureDistancesNum.toFixed(2) }}米
          </div>
          <!-- 测距的点 -->
          <div
            v-for="(point, index) in state.measureDistancesPoints"
            :key="index"
            :style="getRangingPointStyle(point)"
          ></div>
        </template>

        <!-- 文字输入区域 -->
        <input
          v-if="state.showInputBox"
          ref="inputBoxRef"
          v-model="state.inputBoxValue"
          @keyup.enter="handleInputEnd"
          @blur="handleInputEnd"
          :style="{
            fontSize: state.inputBoxStyle.fontSize + 'px',
            fontFamily: state.inputBoxStyle.fontFamily,
            color: state.inputBoxStyle.fontColor,
            left: state.inputBoxStyle.locationX + 'px',
            top: state.inputBoxStyle.locationY + 'px'
          }"
          :maxlength="30"
          class="input-box-class"
        />
      </div>
      <!-- </map-scale-tool> -->
    </div>

    <div class="actual-location" v-if="state.actualLocation.x && state.actualLocation.y">
      <div>X：{{ state.actualLocation.x }}</div>
      <div>Y：{{ state.actualLocation.y }}</div>
    </div>

    <!-- 节点编辑 -->
    <editNodeProperties
      v-if="imgBgObj.positionMapId"
      ref="editNodePropertiesRef"
      :positionMapId="imgBgObj.positionMapId"
      :imgBgObj="imgBgObj"
      @submit-node-success="submitNodeSuccess"
      @add-event-listener="addEventListener"
    />
    <!-- 文字输入弹窗 -->
    <textFormToolDialog
      ref="textFormToolDialogRef"
      v-if="state.textFormToolShow"
      :inputBoxStyle="state.inputBoxStyle"
      @text-form-success="textFormSuccess"
    />
    <!-- 图层选择 -->
    <layerSelectionToolDialog
      v-if="state.isShowLayer"
      ref="layerSelectionToolDialogRef"
      @layer-selection-success="layerSelectionSuccess"
      @is-curve-display-change="isCurveDisplayChange"
    />
    <!-- 设备弹窗选择 -->
    <equipmentToolDialog
      ref="equipmentToolDialogRef"
      :positionMapId="imgBgObj.positionMapId"
      @add-event-listener="addEventListener"
    />
    <!-- 区域选择 -->
    <itemAreaSettingDialog
      ref="itemAreaSettingDialogRef"
      :positionMapId="imgBgObj.positionMapId"
      @add-event-listener="addEventListener"
      @item-area-setting-submit-success="itemAreaSettingSubmitSuccess"
    />
    <!-- 线库设置 -->
    <lineLibrarySettingDialog
      ref="lineLibrarySettingDialogRef"
      :positionMapId="imgBgObj.positionMapId"
      @add-event-listener="addEventListener"
      @submit-line-library-form-success="submitLineLibraryFormSuccess"
    />
    <!-- 编辑地图路线 -->
    <editMapRouteDialog
      v-if="imgBgObj.positionMapId"
      ref="editMapRouteDialogRef"
      @edit-map-route-dialog-submit="editMapRouteDialogSubmit"
      @add-event-listener="addEventListener"
      :imgBgObj="imgBgObj"
    />
    <!-- 线库管理 -->
    <lineLibraryManagementDialog
      ref="lineLibraryManagementDialogRef"
      :positionMapId="imgBgObj.positionMapId"
      @add-event-listener="addEventListener"
      @line-library-management-delete="lineLibraryManagementDelete"
      @line-library-management-edit="lineLibraryManagementEdit"
    />
    <!-- 区域管理 -->
    <itemAreaManagementDialog
      ref="itemAreaManagementDialogRef"
      :positionMapId="imgBgObj.positionMapId"
      @add-event-listener="addEventListener"
      @item-area-management-delete="itemAreaManagementDelete"
      @item-area-management-edit="itemAreaManagementEdit"
    />
    <!-- 生成直线 选择开始点和结束点 -->
    <GenerateStraightLinesDialog
      ref="GenerateStraightLinesDialogRef"
      @generate-straight-lines-submit="GenerateStraightLinesSubmit"
      @add-event-listener="addEventListener"
    />
    <!-- 批量复制节点和路线 -->
    <BatchCopyingDialogForm
      ref="BatchCopyingDialogFormRef"
      :imgBgObj="imgBgObj"
      @submit-batch-copying-form-success="submitBatchCopyingFormSuccess"
      @add-event-listener="addEventListener"
    />
    <!-- 等距复制 -->
    <IsometricReplicationDialog
      ref="IsometricReplicationDialogRef"
      :imgBgObj="imgBgObj"
      @add-event-listener="addEventListener"
      @equal-distance-replication-succeeded="equalDistanceReplicationSucceeded"
    />
    <!-- 批量编辑路线 -->
    <BatchEditRoutePropertiesDialog
      ref="BatchEditRoutePropertiesDialogRef"
      @add-event-listener="addEventListener"
      @edit-nodes-and-alignments-succeeded="BatchEditRoutePropertiesSucceeded"
    />
    <!-- 批量编辑库位 -->
    <BatchEditNodePropertiesDialog
      ref="BatchEditNodePropertiesDialogRef"
      @add-event-listener="addEventListener"
      @batch-edit-node-properties-succeeded="BatchEditNodePropertiesSucceeded"
    />

    <!-- 节点右击菜单 -->
    <div
      v-if="state.contextMenu.visible"
      class="context-menu"
      :style="{ left: state.contextMenu.x + 'px', top: state.contextMenu.y + 'px' }"
      @click.stop
    >
      <div class="context-menu-item" @click="deleteSingleNode">删除</div>
      <div class="context-menu-item" @click="contextMenuEditNode">编辑节点</div>
      <div
        class="context-menu-item"
        @click="isometricReplication"
        v-if="
          state.contextMenu.currentItem.type === 1 ||
          state.contextMenu.currentItem.type === 2 ||
          state.contextMenu.currentItem.type === 4 ||
          state.contextMenu.currentItem.type === 6
        "
        >等距复制</div
      >
      <div
        class="context-menu-item"
        @click="generateDetectionPoint"
        v-if="state.contextMenu.currentItem.type === 2"
        >生成检测点</div
      >
    </div>
    <!-- 曲线右击菜单 -->
    <div
      v-if="state.curveContextMenu.visible"
      class="context-menu"
      :style="{ left: state.curveContextMenu.x + 'px', top: state.curveContextMenu.y + 'px' }"
      @click.stop
    >
      <div class="context-menu-item" @click="deleteCurveRoute">删除</div>
      <div class="context-menu-item" @click="contextMenuEditRoute">编辑路线</div>
      <template
        v-for="dir in getAvailableRightAngleDirections(state.curveContextMenu.currentCurve)"
        :key="dir"
      >
        <div class="context-menu-item" @click="() => setCurveRightAngle(dir)">
          {{ rightAngleDirectionLabel(dir) }}
        </div>
      </template>
    </div>
  </div>
</template>

<script setup>
import JSONBigInt from 'json-bigint'
import { ElLoading } from 'element-plus'
import { ref, defineComponent, reactive, nextTick, onMounted } from 'vue'
import editMapRouteDialog from './components-tool/editMapRouteDialog.vue'
import editNodeProperties from './components-tool/editNodeProperties.vue'
import textFormToolDialog from './components-tool/textFormToolDialog.vue'
import equipmentToolDialog from './components-tool/equipmentToolDialog.vue'
import itemAreaSettingDialog from './components-tool/itemAreaSettingDialog.vue'
import BatchCopyingDialogForm from './components-tool/BatchCopyingDialogForm.vue'
import lineLibrarySettingDialog from './components-tool/lineLibrarySettingDialog.vue'
import layerSelectionToolDialog from './components-tool/layerSelectionToolDialog.vue'
import itemAreaManagementDialog from './components-tool/itemAreaManagementDialog.vue'
import IsometricReplicationDialog from './components-tool/IsometricReplicationDialog.vue'
import lineLibraryManagementDialog from './components-tool/lineLibraryManagementDialog.vue'
import GenerateStraightLinesDialog from './components-tool/GenerateStraightLinesDialog.vue'
import BatchEditNodePropertiesDialog from './components-tool/BatchEditNodePropertiesDialog.vue'
import BatchEditRoutePropertiesDialog from './components-tool/BatchEditRoutePropertiesDialog.vue'
import mapScaleTool from './components-tool/map-scale-tool.vue'

import * as MapApi from '@/api/map/map'
import cursorCollection from './cursorCollection'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'

defineOptions({ name: 'EditMapPageRealTimeMap' })

const BatchEditRoutePropertiesDialogRef = ref() //批量编辑路线
const BatchEditNodePropertiesDialogRef = ref() //批量编辑
const GenerateStraightLinesDialogRef = ref() //生成直线的
const lineLibraryManagementDialogRef = ref() //线库管理
const IsometricReplicationDialogRef = ref() //等距离复制
const itemAreaManagementDialogRef = ref() //区域管理
const lineLibrarySettingDialogRef = ref() //线库设置
const itemAreaSettingDialogRef = ref() //物料区域设置
const BatchCopyingDialogFormRef = ref() //批量复制
const equipmentToolDialogRef = ref() //设备弹窗
const textFormToolDialogRef = ref() //文字输入弹窗
const editMapRouteDialogRef = ref() //编辑地图路线的弹窗
const mapBackgroundRef = ref()
const mapContainerRef = ref()
const inputBoxRef = ref() //文字输入框

const message = useMessage() // 消息弹窗

//其他节点的样式
const nodeStyle = (item, index) => {
  return {
    verticalAlign: 'top',
    width: item.locationWidePx + 'px',
    height: item.locationDeepPx + 'px',
    border: state.currentItemIndex === index ? '.0625rem dashed #000' : 'none',
    borderRadius: '.1875rem'
  }
}
//sortNum路径点的样式
const getSortNumStyle = (item, index) => {
  let leftNum = 0
  if (item.sortNum.toString().length === 1) {
    leftNum = 3
  } else if (item.sortNum.toString().length === 2) {
    leftNum = 7
  } else if (item.sortNum.toString().length === 3) {
    leftNum = 10
  } else if (item.sortNum.toString().length === 4) {
    leftNum = 14
  } else if (item.sortNum.toString().length === 5) {
    leftNum = 18
  } else if (item.sortNum.toString().length === 6) {
    leftNum = 21
  } else if (item.sortNum.toString().length === 7) {
    leftNum = 25
  } else if (item.sortNum.toString().length === 8) {
    leftNum = 28
  } else if (item.sortNum.toString().length === 9) {
    leftNum = 31
  }
  return {
    left: Number(item.locationWidePx) / 2 - leftNum + 'px',
    top: 6 + 'px'
  }
}
//sortNum非路径点的样式
const getSortNumLocationStyle = (item, index) => {
  let leftNum = 0
  if (item.sortNum.toString().length === 1) {
    leftNum = 3
  } else if (item.sortNum.toString().length === 2) {
    leftNum = 7
  } else if (item.sortNum.toString().length === 3) {
    leftNum = 10
  } else if (item.sortNum.toString().length === 4) {
    leftNum = 14
  } else if (item.sortNum.toString().length === 5) {
    leftNum = 18
  } else if (item.sortNum.toString().length === 6) {
    leftNum = 21
  } else if (item.sortNum.toString().length === 7) {
    leftNum = 25
  } else if (item.sortNum.toString().length === 8) {
    leftNum = 28
  } else if (item.sortNum.toString().length === 9) {
    leftNum = 31
  }
  return {
    left: Number(item.locationWidePx) / 2 - leftNum + 'px',
    top: Number(item.locationDeepPx) / 2 - 2 + 'px'
  }
}
//排序的样式
const getLocationNumberStyle = (item, index) => {
  let leftNum = 0
  if (item.locationNumber.toString().length === 1) {
    leftNum = 3
  } else if (item.locationNumber.toString().length === 2) {
    leftNum = 7
  } else if (item.locationNumber.toString().length === 3) {
    leftNum = 10
  } else if (item.locationNumber.toString().length === 4) {
    leftNum = 14
  } else if (item.locationNumber.toString().length === 5) {
    leftNum = 18
  } else if (item.locationNumber.toString().length === 6) {
    leftNum = 21
  } else if (item.locationNumber.toString().length === 7) {
    leftNum = 25
  } else if (item.locationNumber.toString().length === 8) {
    leftNum = 28
  } else if (item.locationNumber.toString().length === 9) {
    leftNum = 31
  }
  return {
    left: Number(item.locationWidePx) / 2 - leftNum + 'px',
    top: Number(item.locationDeepPx) / 2 - 2 + 'px'
  }
}
//库位的样式
const binLocationStyle = (item, index) => {
  let laneId
  let areaId
  if (state.currentItemIndex !== -1) {
    laneId = state.allMapPointInfo[state.currentItemIndex].laneId
    areaId = state.allMapPointInfo[state.currentItemIndex].areaId
  }
  return {
    verticalAlign: 'top',
    width: item.locationWidePx + 'px',
    height: item.locationDeepPx + 'px',
    border:
      state.currentItemIndex === index
        ? '.0625rem dashed #000'
        : state.noLocationNumberList.includes(index)
          ? '2px dashed red'
          : laneId && item.laneId === laneId
            ? '2px dashed #ff6a00'
            : areaId && item.areaId === areaId
              ? '2px dashed #00aeff'
              : 'none'
  }
}

// 缩放停止
const interfaceRefreshed = ref(true)
const resizeEnd = (locationX, locationY, w, h, item, index) => {
  interfaceRefreshed.value = false
  nextTick(() => {
    let x = Number(locationX) + Number(item.locationWidePx) / 2
    let y = Number(locationY) + Number(item.locationDeepPx) / 2
    let width = Number(w) * imgBgObj.resolution * 100 //实际的宽高cm
    let height = Number(h) * imgBgObj.resolution * 100

    let actualPoint = disposeEventPoint(x, y)

    state.allMapPointInfo[index].locationX = x
    state.allMapPointInfo[index].locationY = y
    state.allMapPointInfo[index].locationWide = width
    state.allMapPointInfo[index].locationDeep = height
    state.allMapPointInfo[index].locationWidePx = w
    state.allMapPointInfo[index].locationDeepPx = h
    state.allMapPointInfo[index].actualLocationX = actualPoint.actualLocationX
    state.allMapPointInfo[index].actualLocationY = actualPoint.actualLocationY
    //更改路线里的
    state.mapRouteList.forEach((route) => {
      if (item.id === route.startingPointId) {
        route.startPointX = x
        route.startPointY = y
        route.actualStartPointX = actualPoint.actualLocationX
        route.actualStartPointY = actualPoint.actualLocationY
      }
      if (item.id === route.endPointId) {
        route.endPointX = x
        route.endPointY = y
        route.actualEndPointX = actualPoint.actualLocationX
        route.actualEndPointY = actualPoint.actualLocationY
      }
    })
    addEditHistory()
  })
}

// 拖拽停止
const dragEnd = (locationX, locationY, item, index) => {
  let x = Number(locationX) + Number(item.locationWidePx) / 2
  let y = Number(locationY) + Number(item.locationDeepPx) / 2

  if (x === item.locationX && y === item.locationY) return
  let actualPoint = disposeEventPoint(x, y)
  state.allMapPointInfo[index].locationX = x
  state.allMapPointInfo[index].locationY = y
  state.allMapPointInfo[index].actualLocationX = actualPoint.actualLocationX
  state.allMapPointInfo[index].actualLocationY = actualPoint.actualLocationY
  //更改路线里的
  state.mapRouteList.forEach((route) => {
    if (item.id === route.startingPointId) {
      route.startPointX = x
      route.startPointY = y
      route.actualStartPointX = actualPoint.actualLocationX
      route.actualStartPointY = actualPoint.actualLocationY
    }
    if (item.id === route.endPointId) {
      route.endPointX = x
      route.endPointY = y
      route.actualEndPointX = actualPoint.actualLocationX
      route.actualEndPointY = actualPoint.actualLocationY
    }
  })
  addEditHistory()
}
// 旋转
const rotateEnd = (angle, item, index) => {
  state.allMapPointInfo[index].angle = angle
  addEditHistory()
}

//节点选中
const editNodePropertiesRef = ref()
const activatedHandle = (item, index) => {
  if (toolbarSwitchType.value === 'editRoute') return

  state.currentItemIndex = index
  //让路线不选中
  state.selectedCurve = ''
  if (state.mapRouteList.length > 0 && state.currentDragTarget.index !== null) {
    state.mapRouteList[state.currentDragTarget.index].isSelected = false
    state.currentDragTarget = { index: null, type: null }
  }
  //显示出可走路线
  state.isCanTakeRoutes = state.mapRouteList.forEach((route) => {
    if (
      route.startingPointId === item.id ||
      (route.direction === 2 && route.endPointId === item.id)
    ) {
      route.isSelected = true
    } else {
      route.isSelected = false
    }
  })
  //节点编辑
  if (toolbarSwitchType.value === 'editNode' && item.type !== 7) {
    let list = state.allMapPointInfo.filter((item) => item.type === 3)
    removeEventListener() //移除监听
    editNodePropertiesRef.value.open(JSON.parse(JSON.stringify(item)), list)
  }
}

//非选中
const deactivatedHandle = () => {}

//添加历史记录
const addEditHistory = () => {
  //要判断是不是在最新的记录上修改 如果不是 要把currentIndex之后的记录都删掉 保持当前修改是最新的
  if (state.currentIndex < state.allHistoryList.length - 1) {
    state.allHistoryList.splice(state.currentIndex + 1)
  }
  state.allHistoryList.push({
    allMapPointInfo: JSON.parse(JSON.stringify(state.allMapPointInfo)),
    mapRouteList: JSON.parse(JSON.stringify(state.mapRouteList))
  })
  state.currentIndex = state.allHistoryList.length - 1
  interfaceRefreshed.value = true
}

//上一步
const backPreviousStep = () => {
  if (state.currentIndex > 0) {
    state.currentIndex--
    state.allMapPointInfo.splice(
      0,
      state.allMapPointInfo.length,
      ...JSON.parse(JSON.stringify(state.allHistoryList[state.currentIndex]?.allMapPointInfo))
    )
    state.mapRouteList.splice(
      0,
      state.mapRouteList.length,
      ...JSON.parse(JSON.stringify(state.allHistoryList[state.currentIndex]?.mapRouteList))
    )
  } else {
    message.warning('没了老铁')
  }
}
//下一步
const backNextStep = () => {
  if (state.currentIndex < state.allHistoryList.length - 1) {
    state.currentIndex++
    state.allMapPointInfo.splice(
      0,
      state.allMapPointInfo.length,
      ...JSON.parse(JSON.stringify(state.allHistoryList[state.currentIndex]?.allMapPointInfo))
    )
    state.mapRouteList.splice(
      0,
      state.mapRouteList.length,
      ...JSON.parse(JSON.stringify(state.allHistoryList[state.currentIndex]?.mapRouteList))
    )
  } else {
    message.warning('没了老铁')
  }
}

//地图点击
const mapClick = (e) => {
  try {
    // 提取坐标处理
    const { x, y, actualLocationX, actualLocationY } = disposeEventPoints(e)
    state.actualLocation.x = actualLocationX
    state.actualLocation.y = actualLocationY

    //新增节点
    if (toolbarSwitchType.value === 'drawNodes') {
      // 优化节点检查逻辑，比较一位小数
      const isDuplicate = state.allMapPointInfo.some(
        (item) =>
          Number(item.locationX).toFixed(1) === Number(x).toFixed(1) &&
          Number(item.locationY).toFixed(1) === Number(y).toFixed(1)
      )

      if (!isDuplicate) {
        state.allMapPointInfo.push({
          positionMapId: imgBgObj.positionMapId,
          layerSelectionShow: true,
          locationX: x,
          locationY: y,
          actualLocationX,
          actualLocationY,
          locationDeep: 40,
          locationWide: 40,
          locationDeepPx: 8,
          locationWidePx: 8,
          angle: 0,
          draggable: true,
          resizable: false,
          rotatable: false,
          lockAspectRatio: false,
          mapImageUrl: '',
          locationYaw: 0,
          type: 1,
          dataList: [],
          dataObj: {}
        })
        addEditHistory()
      } else {
        message.warning('该点位已经存在节点')
      }
    }
    //文字输入
    else if (toolbarSwitchType.value === 'text') {
      state.showInputBox = true
      state.inputBoxStyle = {
        locationX: x,
        locationY: y,
        actualLocationX,
        actualLocationY
      }

      // 聚焦输入框
      setTimeout(() => {
        inputBoxRef.value?.focus()
      }, 0)
    }
    //测距
    else if (toolbarSwitchType.value === 'ranging') {
      measureDistancesClick(e)
    }
  } catch (error) {
    console.error('地图点击处理错误:', error)
  }
}

//输入文字样式改变
const textFormSuccess = (form) => {
  state.inputBoxStyle.fontSize = `${form.fontSize}`
  state.inputBoxStyle.fontFamily = `${form.fontFamily}`
  state.inputBoxStyle.fontColor = `${form.fontColor}`
}
//输入结束
const handleInputEnd = () => {
  if (state.inputBoxValue) {
    state.showInputBox = false
    state.allMapPointInfo.push({
      type: 7, //类型  7文字
      positionMapId: imgBgObj.positionMapId, //地图的id
      locationX: state.inputBoxStyle.locationX, //x
      locationY: state.inputBoxStyle.locationY, //y
      actualLocationX: state.inputBoxStyle.actualLocationX,
      actualLocationY: state.inputBoxStyle.actualLocationY,
      locationDeep: null, //h
      locationWide: null, //w
      angle: 0, //旋转角度
      draggable: true, //是否可以拖动
      resizable: false, //是否可以调整大小
      rotatable: true, //是否可以旋转
      lockAspectRatio: true, //是否锁定比例
      mapImageUrl: '', //图片
      text: state.inputBoxValue, //文字内容
      fontColor: state.inputBoxStyle.fontColor, //文字颜色
      fontFamily: state.inputBoxStyle.fontFamily, //字体类型
      fontSize: state.inputBoxStyle.fontSize,
      dataObj: {}, //存 设备点 停车点 文字
      layerSelectionShow: true,
      locationDeep: 20,
      locationWide: 20,
      locationDeepPx: 4,
      locationWidePx: 4
    })
    addEditHistory()
    state.inputBoxValue = ''
  }
}
//编辑节点成功
const submitNodeSuccess = (form) => {
  let actualPoint = disposeEventPoint(form.locationX, form.locationY)
  form.locationDeepPx = Number(form.locationDeep) / 100 / imgBgObj.resolution
  form.locationWidePx = Number(form.locationWide) / 100 / imgBgObj.resolution
  form.actualLocationX = actualPoint.actualLocationX
  form.actualLocationY = actualPoint.actualLocationY
  state.allMapPointInfo[state.currentItemIndex] = form

  //节点位置改变 通知路径里面的节点也改变
  state.mapRouteList.forEach((item) => {
    if (form.id === item.startingPointId) {
      item.startPointX = form.locationX
      item.startPointY = form.locationY
      item.actualStartPointX = disposeEventPoint(form.locationX, form.locationY).actualLocationX
      item.actualStartPointY = disposeEventPoint(form.locationX, form.locationY).actualLocationY
    }
    if (form.id === item.endPointId) {
      item.endPointX = form.locationX
      item.endPointY = form.locationY
      item.actualEndPointX = disposeEventPoint(form.locationX, form.locationY).actualLocationX
      item.actualEndPointY = disposeEventPoint(form.locationX, form.locationY).actualLocationY
    }
  })
  addEditHistory()

  //存在库位未填写排序号的问题
  state.noLocationNumberList = state.allMapPointInfo.reduce((invalidIndexes, item, index) => {
    if (item.type === 2 && !item.locationNumber) {
      invalidIndexes.push(index) // 如果不满足条件，记录索引
    }
    return invalidIndexes
  }, [])
}

//工具栏点击
const toolbarSwitchType = ref('')
const state = reactive({
  topToolList: [
    {
      switchType: 'save',
      name: '保存',
      icon: 'ep:folder-checked',
      isActive: false
    },
    {
      switchType: 'choose',
      name: '选择',
      icon: 'ep:position',
      isActive: false
    },
    {
      switchType: 'move',
      name: '移动',
      icon: 'ep:rank',
      isActive: false
    },
    {
      switchType: 'revolve',
      name: '旋转',
      icon: 'ep:refresh-right',
      isActive: false
    },
    {
      switchType: 'copy',
      name: '复制',
      icon: 'ep:document',
      isActive: false
    },
    {
      switchType: 'paste',
      name: '粘贴',
      icon: 'ep:copy-document',
      isActive: false
    },
    {
      switchType: 'delete',
      name: '删除',
      icon: 'ep:delete',
      isActive: false
    },
    {
      switchType: 'tools',
      name: '工具',
      icon: 'ep:briefcase',
      isActive: true
    },
    {
      switchType: 'lineLibrary',
      name: '线库',
      icon: 'ep:message-box',
      isActive: false
    },
    {
      switchType: 'region',
      name: '区域',
      icon: 'ep:full-screen',
      isActive: false
    },
    {
      switchType: 'text',
      name: '文字',
      icon: 'ep:edit-pen',
      isActive: false
    },
    {
      switchType: 'equipment',
      name: '设备',
      icon: 'ep:video-camera-filled',
      isActive: false
    },
    {
      switchType: 'layer',
      name: '图层',
      icon: 'ep:copy-document',
      isActive: false
    },
    {
      switchType: 'marker',
      name: '标记',
      icon: 'ep:map-location',
      isActive: false
    },
    {
      switchType: 'grid',
      name: '网格',
      icon: 'ep:grid',
      isActive: false
    },
    {
      switchType: 'larger',
      name: '放大',
      icon: 'ep:zoom-in',
      isActive: false
    },
    {
      switchType: 'smaller',
      name: '缩小',
      icon: 'ep:zoom-out',
      isActive: false
    },
    {
      switchType: 'withdraw',
      name: '撤回',
      icon: 'ep:top-left',
      isActive: false
    },
    {
      switchType: 'next',
      name: '重做',
      icon: 'ep:top-right',
      isActive: false
    }
  ],
  rightToolList: [
    {
      switchType: 'drawNodes',
      name: '新增节点',
      icon: 'ep:circle-plus-filled',
      isActive: false
    },
    {
      switchType: 'editNode',
      name: '编辑节点',
      icon: 'ep:edit',
      isActive: false
    },
    {
      switchType: 'clickDrawRoute',
      name: '新增路线',
      icon: 'ep:semi-select',
      isActive: false
    },
    {
      switchType: 'editRoute',
      name: '编辑路线',
      icon: 'ep:edit-pen',
      isActive: false
    },
    {
      switchType: 'generateLine',
      name: '生成直线',
      icon: 'ep:finished',
      isActive: false
    },
    {
      switchType: 'routeWidth',
      name: '路线宽度',
      icon: 'ep:semi-select',
      isActive: false
    },
    {
      switchType: 'addPointOnline',
      name: '线上加点',
      icon: 'ep:add-location',
      isActive: false
    },
    {
      switchType: 'entryActualNode',
      name: '录入节点',
      icon: 'ep:avatar',
      isActive: false
    },
    {
      switchType: 'bulkOperation',
      name: '批量操作',
      icon: 'ep:arrow-left',
      isActive: false
    },
    {
      switchType: 'ranging',
      name: '距离测量',
      icon: 'ep:set-up',
      isActive: false
    }
  ],
  bulkOperationList: [
    {
      switchType: 'bulkDelete',
      name: '删除',
      icon: 'ep:delete',
      isActive: false
    },
    {
      switchType: 'batchCopying',
      name: '复制',
      icon: 'ep:document-copy',
      isActive: false
    },
    {
      switchType: 'bulkEditingRoute',
      name: '编辑路线',
      icon: 'ep:edit-pen',
      isActive: false
    },
    {
      switchType: 'bulkEditingNode',
      name: '编辑库位',
      icon: 'ep:edit-pen',
      isActive: false
    }
  ],
  isShowToolbar: true, //工具栏展示隐藏
  isShowGrid: false, //网格展示隐藏
  moveForm: {
    locationX: 0,
    locationY: 0
  }, //移动的表单
  rotationForm: {
    angle: 0
  }, //旋转的表单
  copyMapItem: '', //复制的值
  cursorStyle: 'auto',
  drawSelectionAreaShow: false, //绘制框选区域
  allDrawSelectionAreaBox: [], // 所有框选区域
  drawSelectionAreaBox: { x: 0, y: 0, width: 0, height: 0 }, //绘制区域的位置，长宽
  drawSelectionStartPoint: { x: 0, y: 0 }, //开始绘制的点位
  drawSelectionPointList: [], //绘制选中的list
  drawSelectionRouteList: [], //绘制选中的路线list
  textFormToolShow: false, //文字表单显示隐藏
  showInputBox: false, //输入框显示隐藏
  inputBoxStyle: {
    fontFamily: 'SimSun',
    fontSize: '14',
    fontColor: '#000000',
    locationX: 0,
    locationY: 0,
    actualLocationX: 0,
    actualLocationY: 0
  }, //输入框的样式
  inputBoxValue: '', //输入的值
  isShowLayer: false, //图层显示
  measureDistancesPoints: [], // 存储点击的点位
  measureDistancesNum: 0, // 存储两点之间的距离
  imageChangeMultiple: 1, //图片放大缩小的倍数
  prohibitedOperation: false, //禁止操作 在框选测距等操作时，禁止所有拖动等操作
  currentDragTarget: {
    index: null,
    type: null
  }, //当前拖拽的目标（起点、终点、控制点）
  selectedCurve: '', // 当前选中的曲线
  startDrawPointIndex: -1, // 起始点的索引
  startDrawPoint: null, //新增路线开始的点
  isDrawing: false, //正在绘制
  currentDrawX: 0, //正在绘制的x轴坐标
  currentDrawY: 0, //正在绘制的y轴坐标
  allHistoryList: [], //所有的历史记录 [{allMapPointInfo:[],mapRouteList:[]}]
  allMapPointInfo: [], //当前页面上的所有节点的列表
  mapRouteList: [], //当前页面上所有路线的列表
  currentIndex: 0, //当前处在哪条历史记录
  currentItemIndex: -1, //当前处在哪个节点上
  markForm: {
    macAddress: '', //	mac地址
    markProperty: '', //标记属性
    originalNode: '', //原节点
    robotNo: '' //车辆编号
  }, //标记的表单
  mapMarkCarList: [], //标记的车辆列表
  markPopoverVisible: false, //标记弹窗
  entryNodePopoverVisible: false, //录入点位弹窗
  entryActualNodeForm: {
    x: '',
    y: '',
    locationYaw: 0
  }, //录入点位的表单
  noLocationNumberList: [], //没有排序的库位index
  isShowSortNum: true, //是否显示序号
  actualLocation: {
    x: '',
    y: ''
  },
  gridForm: {
    gridNum: 20 + 'px'
  },
  routeWidthForm: {
    routeWidth: 3
  },
  isSearchSelectVisible: false, //是否显示选择框
  searchSelectedOption: '',
  haveSortNumMapPointInfo: [],
  MathPI: Math.PI,
  routeDirection: 2, //1单2双
  vLine: [],
  hLine: [],
  svgRouteZIndex: 9,
  isCurveDisplay: true,
  // 右击菜单相关状态
  contextMenu: {
    visible: false, // 菜单显示状态
    x: 0, // 菜单x坐标
    y: 0, // 菜单y坐标
    currentItem: null, // 当前右击的库位信息
    currentIndex: -1 // 当前右击的库位索引
  },
  curveContextMenu: {
    visible: false,
    x: 0,
    y: 0,
    currentCurve: null,
    currentIndex: -1
  },
  checkPointDistance: 0,
  curveOffsetValue: 0
})

const getRefLineParams = (params) => {
  const { vLine, hLine } = params
  state.vLine = vLine
  state.hLine = hLine
}

//网格的样式 - 优化计算属性
const gradientBackground = computed(() => {
  const baseStyle = {
    width: `${imgBgObj.width}px`,
    height: `${imgBgObj.height}px`
  }

  if (!state.isShowGrid) return baseStyle

  return {
    ...baseStyle,
    backgroundImage: `
      linear-gradient(to right,  rgba(0, 0, 0, 0.1) 1px, transparent 1px),
      linear-gradient(to bottom,  rgba(0, 0, 0, 0.1) 1px, transparent 1px)
    `,
    backgroundSize: `${state.gridForm.gridNum} ${state.gridForm.gridNum}`
  }
})

const toolbarClick = async (item) => {
  let type = item.switchType
  if (state.currentItemIndex === -1 && (type === 'move' || type === 'revolve' || type === 'copy')) {
    message.warning('请先选择要操作的对象')
    return
  }

  if (
    type === 'delete' &&
    state.currentDragTarget.index === null &&
    state.currentItemIndex === -1
  ) {
    message.warning('请先选择要操作的对象')
    return
  }

  //粘贴
  if (type === 'paste' && !state.copyMapItem) {
    message.warning('请先复制对象')
    return
  }

  if (
    (type === 'tools' || type === 'text' || type === 'layer' || type === 'grid') &&
    toolbarSwitchType.value === type
  ) {
    toolbarSwitchType.value = ''
  } else {
    toolbarSwitchType.value = type
  }

  //鼠标样式的问题
  if (toolbarSwitchType.value !== 'text') {
    state.cursorStyle = `auto`
    state.textFormToolShow = false
    state.showInputBox = false
    state.inputBoxValue = ''
  }
  if (
    toolbarSwitchType.value === 'generateLine' ||
    toolbarSwitchType.value === 'createLineLibrary' ||
    toolbarSwitchType.value === 'createRegion' ||
    toolbarSwitchType.value === 'bulkDelete' ||
    toolbarSwitchType.value === 'batchCopying' ||
    toolbarSwitchType.value === 'bulkEditingRoute' ||
    toolbarSwitchType.value === 'bulkEditingNode'
  ) {
    state.cursorStyle = 'crosshair'
  } else if (
    toolbarSwitchType.value === 'addPointOnline' ||
    toolbarSwitchType.value === 'editRoute' ||
    toolbarSwitchType.value === 'drawNodes' ||
    toolbarSwitchType.value === 'editNode' ||
    toolbarSwitchType.value === 'clickDrawRoute'
  ) {
    state.cursorStyle = 'pointer'
  } else {
    state.cursorStyle = `auto`
  }

  //编辑路线的时候更换svg层级
  if (toolbarSwitchType.value === 'editRoute') {
    state.svgRouteZIndex = 999
  } else {
    state.svgRouteZIndex = 9
  }

  //非测距
  if (toolbarSwitchType.value !== 'ranging') {
    state.measureDistancesPoints = [] // 清空存储点击的点位
    state.measureDistancesNum = 0 // 清空存储两点之间的距离
  }

  //禁止操作 在框选测距等操作时，禁止所有拖动等操作
  if (
    toolbarSwitchType.value === 'drawNodes' ||
    toolbarSwitchType.value === 'editNode' ||
    toolbarSwitchType.value === 'clickDrawRoute' ||
    toolbarSwitchType.value === 'drawRoute' ||
    toolbarSwitchType.value === 'createLineLibrary' ||
    toolbarSwitchType.value === 'createRegion' ||
    toolbarSwitchType.value === 'bulkDelete' ||
    toolbarSwitchType.value === 'batchCopying' ||
    toolbarSwitchType.value === 'bulkEditingRoute' ||
    toolbarSwitchType.value === 'bulkEditingNode' ||
    toolbarSwitchType.value === 'editRoute' ||
    toolbarSwitchType.value === 'generateLine'
  ) {
    state.prohibitedOperation = true
  } else {
    state.prohibitedOperation = false
  }

  //工具切换 不适用框选的 要把框选的信息都删掉
  state.drawSelectionAreaShow = false
  state.allDrawSelectionAreaBox = []
  state.drawSelectionPointList = []

  switch (type) {
    case 'open':
      // 打开
      break
    case 'save':
      //保存
      await message.confirm('请确认是否保存？')
      saveMap()
      break
    case 'saveAs':
      //另存为 存为json文件 无法直接访问用户的文件系统 不能选择存在那个文件夹里面
      const jsonString = JSON.stringify(state.allHistoryList[state.currentIndex], null, 2)
      const blob = new Blob([jsonString], { type: 'application/json' })
      const url = URL.createObjectURL(blob)

      const a = document.createElement('a')
      a.href = url
      a.download = 'mapJson.json'
      document.body.appendChild(a)
      a.click()
      document.body.removeChild(a)
      URL.revokeObjectURL(url)

      message.success('下载成功')
      break
    case 'choose':
      //选择
      break
    case 'move':
      //移动
      console.log(state.allMapPointInfo[state.currentItemIndex])
      state.moveForm.locationX = Number(state.allMapPointInfo[state.currentItemIndex].locationX)
      state.moveForm.locationY = Number(state.allMapPointInfo[state.currentItemIndex].locationY)
      break
    case 'revolve':
      state.rotationForm.angle = Number(state.allMapPointInfo[state.currentItemIndex]?.angle) || 0
      break
    case 'copy':
      //复制
      replicationNode()
      break
    case 'paste':
      //粘贴
      pasteNode()
      break
    case 'delete':
      //删除
      if (state.currentItemIndex !== -1) {
        let deleteId = state.allMapPointInfo[state.currentItemIndex].id
        // 点删除之后 跟点相关的路线都要删除
        state.mapRouteList = state.mapRouteList.filter(
          (item) => item.startingPointId !== deleteId && item.endPointId !== deleteId
        )
        state.allMapPointInfo.splice(state.currentItemIndex, 1)
        //恢复index
        state.currentItemIndex = -1
      }
      if (state.currentDragTarget.index !== null) {
        state.mapRouteList.splice(state.currentDragTarget.index, 1)
        state.currentDragTarget.index = null
      }
      addEditHistory()
      setTimeout(() => {
        toolbarSwitchType.value = ''
      }, 200)
      break
    case 'tools':
      //工具
      if (toolbarSwitchType.value === 'tools') {
        state.isShowToolbar = true
        item.isActive = true
      } else {
        state.isShowToolbar = false
        item.isActive = false
      }
      break
    case 'lineLibrary':
      // 线库
      break
    case 'region':
      // 区域
      break
    case 'createLineLibrary':
      // 生成线库
      state.isShowSortNum = false
      break
    case 'createRegion':
      // 生成区域
      break
    case 'lineLibraryManage':
      // 线库管理
      removeEventListener() //移除监听
      lineLibraryManagementDialogRef.value.open()
      break
    case 'regionManage':
      removeEventListener() //移除监听
      itemAreaManagementDialogRef.value.open()
      // 区域管理
      break
    case 'text':
      //文字
      if (toolbarSwitchType.value === 'text') {
        state.textFormToolShow = true
        state.cursorStyle = `url('${cursorCollection.input}'),auto`
      } else {
        state.cursorStyle = `auto`
        state.textFormToolShow = false
        state.showInputBox = false
        state.inputBoxValue = ''
      }
      break
    case 'ranging':
      // 测距
      break
    case 'layer':
      //图层
      if (toolbarSwitchType.value === 'layer') {
        state.isShowLayer = true
        item.isActive = true
      } else {
        state.isShowLayer = false
        item.isActive = false
      }
      break
    case 'marker':
      // 标记
      mapMark()
      break
    case 'grid':
      //网格
      if (toolbarSwitchType.value === 'grid') {
        state.isShowGrid = true
        item.isActive = true
      } else {
        state.isShowGrid = false
        item.isActive = false
      }
      break
    case 'larger':
      //放大
      if (state.imageChangeMultiple < 4) {
        state.imageChangeMultiple += 0.2
      } else {
        message.warning('不能在放大了')
      }
      break
    case 'smaller':
      //缩小
      if (state.imageChangeMultiple > 0.2) {
        state.imageChangeMultiple -= 0.2
      } else {
        message.warning('不能在缩小了')
      }
      break
    case 'withdraw':
      //上一步
      backPreviousStep()
      break
    case 'next':
      //下一步
      backNextStep()
      break
    case 'drawNodes':
      //新增节点
      break
    case 'editNode':
      // 编辑节点
      break
    case 'drawRoute':
      //新增路线
      break
    case 'editRoute':
      // 编辑路线

      break
    case 'equipment':
      // 设备
      let equipmentList = state.allMapPointInfo.filter((item) => {
        return item.type === 3
      })
      removeEventListener() //移除监听
      equipmentToolDialogRef.value.open(equipmentList)
      break
    case 'generateLine':
      // 生成直线
      break
    case 'bulkDelete':
      // 批量删除
      break
    case 'batchCopying':
      //批量复制
      break
    case 'bulkEditingRoute':
      //批量编辑路线
      break
    case 'bulkEditingNode':
      //批量编辑库位
      break
    case 'entryActualNode':
      // 录入实际点位
      state.entryNodePopoverVisible = true
      break
  }
}

//鼠标右击
const handleContextMenu = (item, index, event) => {
  // 阻止默认的浏览器右击菜单
  event.preventDefault()

  // 设置菜单显示状态和位置
  state.contextMenu.visible = true
  state.contextMenu.x = event.clientX
  state.contextMenu.y = event.clientY
  state.contextMenu.currentItem = item
  state.contextMenu.currentIndex = index
  state.currentItemIndex = index
}

// 隐藏右击菜单
const hideContextMenu = () => {
  state.contextMenu.visible = false
  state.contextMenu.currentIndex = -1
}

//右击删除
const deleteSingleNode = () => {
  if (state.contextMenu.currentIndex !== -1) {
    let deleteId = state.allMapPointInfo[state.contextMenu.currentIndex].id
    // 点删除之后 跟点相关的路线都要删除
    state.mapRouteList = state.mapRouteList.filter(
      (item) => item.startingPointId !== deleteId && item.endPointId !== deleteId
    )
    state.allMapPointInfo.splice(state.contextMenu.currentIndex, 1)
    //恢复index
    state.contextMenu.currentIndex = -1
    message.success('删除成功')
    addEditHistory()
    // 隐藏右击菜单
    hideContextMenu()
  }
}

//右击编辑
const contextMenuEditNode = () => {
  if (state.contextMenu.currentItem.type !== 7) {
    let list = state.allMapPointInfo.filter((item) => item.type === 3)
    removeEventListener() //移除监听
    editNodePropertiesRef.value.open(
      JSON.parse(JSON.stringify(state.contextMenu.currentItem)),
      list
    )
    // 隐藏右击菜单
    hideContextMenu()
  }
}

//等距复制
const isometricReplication = () => {
  removeEventListener() //移除监听
  IsometricReplicationDialogRef.value.open(state.contextMenu.currentItem)
  hideContextMenu()
}
//复制成功
const equalDistanceReplicationSucceeded = (list) => {
  // 过滤并收集重复点
  const duplicatePoints = []
  const filteredList = list.filter((newItem) => {
    const isDup = state.allMapPointInfo.some(
      (item) =>
        Number(item.locationX).toFixed(1) === Number(newItem.locationX).toFixed(1) &&
        Number(item.locationY).toFixed(1) === Number(newItem.locationY).toFixed(1)
    )
    if (isDup) {
      duplicatePoints.push(`(${newItem.locationX}, ${newItem.locationY})`)
    }
    return !isDup
  })
  state.allMapPointInfo.push(...filteredList)
  addEditHistory()
  if (duplicatePoints.length > 0) {
    message.warning(`以下点已存在，未添加：${duplicatePoints.join('、')}`)
  }
}

//生成检测点
const generateDetectionPoint = async () => {
  if (state.contextMenu.currentIndex === -1) return
  if (!state.contextMenu.currentItem.id) {
    return message.warning('该库位未保存')
  }

  const { locationX, locationY, locationYaw, id, actualLocationX, actualLocationY } =
    state.contextMenu.currentItem

  const newId = await MapApi.getNodeId()
  const distanceValue = Number(state.checkPointDistance) / Number(imgBgObj.resolution) //距离米

  // 将弧度转换为角度（用于理解，实际计算仍使用弧度）
  const angle = locationYaw * (180 / Math.PI)

  // 根据角度和距离计算新的坐标
  // 正东方向为0度，正北方向为90度，正西方向为180度，正南方向为-90度
  const newX = Number((Number(locationX) + distanceValue * Math.cos(locationYaw)).toFixed(2))
  const newY = Number((Number(locationY) - distanceValue * Math.sin(locationYaw)).toFixed(2))

  //先校验这个点是否存在,比较一位小数
  const isDuplicate = state.allMapPointInfo.some(
    (item) =>
      Number(item.locationX).toFixed(1) === Number(newX).toFixed(1) &&
      Number(item.locationY).toFixed(1) === Number(newY).toFixed(1)
  )

  if (!isDuplicate) {
    let actualPoint = disposeEventPoint(newX, newY)
    //检测点
    let pointItem = {
      id: newId,
      positionMapId: imgBgObj.positionMapId,
      layerSelectionShow: true,
      locationX: newX,
      locationY: newY,
      actualLocationX: actualPoint.actualLocationX,
      actualLocationY: actualPoint.actualLocationY,
      locationDeep: 40,
      locationWide: 40,
      locationDeepPx: 8,
      locationWidePx: 8,
      angle: 0,
      draggable: true,
      resizable: false,
      rotatable: false,
      lockAspectRatio: false,
      mapImageUrl: '',
      locationYaw: 0,
      type: 1,
      dataList: [],
      dataObj: {}
    }

    //生成监测点和库位之间的路线 这里把监测点当开始点 库位当结束点
    let newRoute = {
      isSelected: true,
      startingPointId: newId,
      endPointId: id,
      startPointX: newX,
      startPointY: newY,
      actualStartPointX: actualPoint.actualLocationX,
      actualStartPointY: actualPoint.actualLocationY,
      endPointX: locationX,
      endPointY: locationY,
      actualEndPointX: actualLocationX,
      actualEndPointY: actualLocationY,
      actualBeginControlX: '',
      actualBeginControlY: '',
      actualEndControlX: '',
      actualEndControlY: '',
      beginControlX: 0,
      beginControlY: 0,
      endControlX: 0,
      endControlY: 0,
      expansionZoneFront: 0,
      expansionZoneAfter: 0,
      expansionZoneLeft: 0,
      expansionZoneRight: 0,
      method: 0,
      direction: 2, //1单2双
      forwardSpeedLimit: 0.4,
      reverseSpeedLimit: 0.2,
      toward: 0,
      startingSortNum: undefined,
      endPointSortNum: undefined,
      startToEndToward: 1, //开始点到结束点
      endToStartToward: 0, //结束点到开始点
      collisionAvoidanceFlag: 1 // 是否开启防撞 0否 1是
    }

    // 将新创建的点添加到地图点列表中
    state.allMapPointInfo.push(pointItem)
    //将路线加入到路线列表中
    state.mapRouteList.push(newRoute)

    state.currentItemIndex = state.allMapPointInfo.length - 1
    addEditHistory()
    // 隐藏右击菜单
    hideContextMenu()
  } else {
    message.warning('该点已存在')
  }
}

//复制
const replicationNode = () => {
  let copyMapItem = state.allMapPointInfo[state.currentItemIndex]

  if (copyMapItem.type === 3) {
    message.warning('设备不能复制！')
    return
  }

  let locationX = Number(copyMapItem.locationX) + 20
  let locationY = Number(copyMapItem.locationY) + 20
  let actualPoint = disposeEventPoint(locationX, locationY)
  if (copyMapItem.dataList.length > 0) {
    copyMapItem.dataList.forEach((item) => {
      delete item.id
      delete item.locationNo
      delete item.mapItemId
    })
    copyMapItem.dataJson = JSON.stringify(copyMapItem.dataList)
  }

  if (copyMapItem.dataObj && JSON.stringify(copyMapItem.dataObj) !== '{}') {
    delete copyMapItem.dataObj.id
    delete copyMapItem.dataObj.positionMapItemId
    copyMapItem.dataJson = JSON.stringify(copyMapItem.dataObj)
  }
  state.copyMapItem = {
    positionMapId: copyMapItem.positionMapId,
    locationX: locationX,
    locationY: locationY,
    actualLocationX: actualPoint.actualLocationX,
    actualLocationY: actualPoint.actualLocationY,
    type: copyMapItem.type,
    dataJson: copyMapItem.dataJson || '',
    dataObj: copyMapItem.dataObj || {},
    dataList: copyMapItem.dataList || [],
    locationDeep: copyMapItem.locationDeep,
    locationWide: copyMapItem.locationWide,
    locationDeepPx: copyMapItem.locationDeepPx,
    locationWidePx: copyMapItem.locationWidePx,
    draggable: copyMapItem.draggable,
    resizable: copyMapItem.resizable,
    rotatable: copyMapItem.rotatable,
    lockAspectRatio: copyMapItem.lockAspectRatio,
    layerSelectionShow: true,
    mapImageUrl: copyMapItem.mapImageUrl,
    locationYaw: copyMapItem.locationYaw,
    areaId: null,
    locationNumber: null
  }
  message.success('复制成功')
}
//粘贴
const pasteNode = () => {
  let copyObj = JSON.parse(JSON.stringify(state.copyMapItem))

  // 检查节点是否已存在
  const isDuplicate = state.allMapPointInfo.some(
    (item) =>
      Number(item.locationX).toFixed(1) === Number(copyObj.locationX).toFixed(1) &&
      Number(item.locationY).toFixed(1) === Number(copyObj.locationY).toFixed(1)
  )

  if (isDuplicate) {
    message.warning('该位置已存在节点')
    return
  }

  state.allMapPointInfo.push(copyObj)
  message.success('粘贴成功')
  addEditHistory()
}
//移动工具表单提交
const moveFormSubmit = async () => {
  if (state.moveForm.locationX && state.moveForm.locationY) {
    let x = Number(state.moveForm.locationX)
    let y = Number(state.moveForm.locationY)
    let item = state.allMapPointInfo[state.currentItemIndex]

    if (x === item.locationX && y === item.locationY) return
    let actualPoint = disposeEventPoint(x, y)
    item.locationX = x
    item.locationY = y
    item.actualLocationX = actualPoint.actualLocationX
    item.actualLocationY = actualPoint.actualLocationY
    //更改路线里的
    state.mapRouteList.forEach((route) => {
      if (item.id === route.startingPointId) {
        route.startPointX = x
        route.startPointY = y
        route.actualStartPointX = actualPoint.actualLocationX
        route.actualStartPointY = actualPoint.actualLocationY
      }
      if (item.id === route.endPointId) {
        route.endPointX = x
        route.endPointY = y
        route.actualEndPointX = actualPoint.actualLocationX
        route.actualEndPointY = actualPoint.actualLocationY
      }
    })
    addEditHistory()
  } else {
    message.warning('请输入x和y')
  }
}
//旋转工具表单提交
const rotationFormSubmit = () => {
  state.allMapPointInfo[state.currentItemIndex].angle = state.rotationForm.angle
  addEditHistory()
}

//录入实际点位
const entryActualNodeFormCancel = () => {
  state.entryNodePopoverVisible = false
  state.entryActualNodeForm.x = ''
  state.entryActualNodeForm.y = ''
  state.entryActualNodeForm.locationYaw = 0
}

const entryActualNodeFormSubmit = () => {
  if (!state.entryActualNodeForm.x || !state.entryActualNodeForm.y) {
    message.warning('请输入实际点位X和Y')
    return
  } else {
    let pointPx = convertActualToBrowser(state.entryActualNodeForm.x, state.entryActualNodeForm.y)
    state.allMapPointInfo.push({
      positionMapId: imgBgObj.positionMapId, //地图的id
      layerSelectionShow: true,
      locationX: pointPx.x,
      locationY: pointPx.y,
      actualLocationX: state.entryActualNodeForm.x,
      actualLocationY: state.entryActualNodeForm.y,
      locationDeep: 40,
      locationWide: 40,
      locationDeepPx: 8,
      locationWidePx: 8,
      angle: 0,
      draggable: true,
      resizable: false,
      rotatable: false,
      lockAspectRatio: false, //横纵比
      mapImageUrl: '',
      locationYaw: state.entryActualNodeForm.locationYaw, //弧度
      type: 1, //默认类型1 路径节点
      dataList: [], //存库位的
      dataObj: {} //存 设备点 停车点 文字
    })
    addEditHistory()
    state.entryNodePopoverVisible = false
    message.success('录入成功')
    setTimeout(() => {
      state.entryActualNodeForm.x = ''
      state.entryActualNodeForm.y = ''
      state.entryActualNodeForm.locationYaw = 0
    }, 200)
  }
}

//标记
const mapMark = async () => {
  state.mapMarkCarList = await MapApi.getListByMapId(imgBgObj.positionMapId)

  if (state.currentItemIndex != -1) {
    let item = state.allMapPointInfo[state.currentItemIndex]
    state.markForm.originalNode = `[${item.locationX},${item.locationY}]`
    if (item.type == 1) {
      state.markForm.markProperty = '路径点'
    } else if (item.type == 2) {
      state.markForm.markProperty = '库位点'
    } else if (item.type == 3) {
      state.markForm.markProperty = '设备点'
    } else if (item.type == 4) {
      state.markForm.markProperty = '停车点'
    } else if (item.type == 5) {
      state.markForm.markProperty = '区域变更点'
    } else if (item.type == 6) {
      state.markForm.markProperty = '等待点'
    }
  } else {
    state.markForm.markProperty = ''
    state.markForm.originalNode = ''
  }
  state.markPopoverVisible = true
}
//标记提交
const macAddressChange = (e) => {
  const targetItem = state.mapMarkCarList.find((item) => item.macAddress === e)
  if (targetItem) {
    state.markForm.robotNo = targetItem.robotNo
  }
}
//取消标记
const markFormCancel = () => {
  state.markPopoverVisible = false
  state.markForm.markProperty = ''
  state.markForm.originalNode = ''
  state.markForm.macAddress = ''
  state.markForm.robotNo = ''
}
//确认标记
const markFormSubmit = async () => {
  if (!state.markForm.macAddress) {
    message.warning('请选择车辆')
    return
  }

  // 格式是 '{"area":"E区","batSoc":"80","floor":"3","robotNo":"100","x":"-1.720683217048645","y":"-14.308184623718262","yaw":"-3.0042707920074463"}'
  let res = await MapApi.getAGVPointInformation(state.markForm.macAddress)
  if (res) {
    //点
    let point = JSON.parse(res)
    let pointPx = convertActualToBrowser(point.x, point.y)

    // 检查节点是否已存在
    const isDuplicate = state.allMapPointInfo.some(
      (item) =>
        Number(item.locationX).toFixed(1) === Number(pointPx.x).toFixed(1) &&
        Number(item.locationY).toFixed(1) === Number(pointPx.y).toFixed(1)
    )

    if (isDuplicate) {
      message.warning('该位置已存在节点')
      state.markPopoverVisible = false
      return
    }

    if (state.currentItemIndex !== -1) {
      state.allMapPointInfo[state.currentItemIndex].locationYaw = point.yaw
      state.allMapPointInfo[state.currentItemIndex].locationX = pointPx.x
      state.allMapPointInfo[state.currentItemIndex].locationY = pointPx.y
      state.allMapPointInfo[state.currentItemIndex].actualLocationX = point.x
      state.allMapPointInfo[state.currentItemIndex].actualLocationY = point.y

      //更改路线里的
      let item = state.allMapPointInfo[state.currentItemIndex]
      state.mapRouteList.forEach((route) => {
        if (item.id === route.startingPointId) {
          route.startPointX = pointPx.x
          route.startPointY = pointPx.y
          route.actualStartPointX = point.x
          route.actualStartPointY = point.y
        }
        if (item.id === route.endPointId) {
          route.endPointX = pointPx.x
          route.endPointY = pointPx.y
          route.actualEndPointX = point.x
          route.actualEndPointY = point.y
        }
      })

      addEditHistory()
    } else {
      //新增一个节点
      state.allMapPointInfo.push({
        positionMapId: imgBgObj.positionMapId, //地图的id
        layerSelectionShow: true,
        locationX: pointPx.x,
        locationY: pointPx.y,
        actualLocationX: point.x,
        actualLocationY: point.y,
        locationDeep: 40,
        locationWide: 40,
        locationDeepPx: 8,
        locationWidePx: 8,
        angle: 0,
        draggable: true,
        resizable: false,
        rotatable: false,
        lockAspectRatio: false, //横纵比
        mapImageUrl: '',
        type: 1, //默认类型1 路径节点
        dataList: [], //存库位的
        dataObj: {}, //存 设备点 停车点 文字
        locationYaw: point.yaw //弧度
      })
      message.success('标记成功')
      addEditHistory()
    }
  } else {
    message.warning('未采集到该AGV点位信息')
  }
  state.markPopoverVisible = false
}

//鼠标拖动绘制节点
// 从点开始绘制
const startFromPoint = (index, event) => {
  if (toolbarSwitchType.value === 'clickDrawRoute') {
    let list = state.allMapPointInfo
    const point = list[index]
    if (point?.id) {
      state.startDrawPoint = { ...point } //创建点位数据副本
      state.startDrawPointIndex = index
      state.isDrawing = true
      event.preventDefault()
    } else {
      message.warning('选择的节点未保存')
      resetDrawState()
    }
  }
}

// 重置绘制状态
const resetDrawState = () => {
  state.startDrawPointIndex = -1
  state.startDrawPoint = null
  state.isDrawing = false
  state.currentDrawX = 0
  state.currentDrawY = 0
}

// 节流函数 - 优化版本
const throttle = (fn, delay) => {
  let lastTime = 0
  let timeoutId = null

  return function (...args) {
    const now = Date.now()

    if (now - lastTime >= delay) {
      lastTime = now
      return fn.apply(this, args)
    } else {
      // 清除之前的定时器
      if (timeoutId) clearTimeout(timeoutId)

      // 设置新的定时器
      timeoutId = setTimeout(
        () => {
          lastTime = Date.now()
          fn.apply(this, args)
        },
        delay - (now - lastTime)
      )
    }
  }
}

// 优化后的框选相关函数
const startDrawSelection = (event) => {
  if (
    toolbarSwitchType.value === 'createLineLibrary' ||
    toolbarSwitchType.value === 'createRegion' ||
    toolbarSwitchType.value === 'drawRoute' ||
    toolbarSwitchType.value == 'generateLine' ||
    toolbarSwitchType.value === 'bulkDelete' ||
    toolbarSwitchType.value === 'bulkEditingRoute' ||
    toolbarSwitchType.value === 'bulkEditingNode' ||
    toolbarSwitchType.value === 'batchCopying'
  ) {
    const { x, y } = disposeEventPoints(event)

    // 确保点击在背景区域内
    if (x >= 0 && x <= imgBgObj.width && y >= 0 && y <= imgBgObj.height) {
      state.drawSelectionAreaShow = true
      state.drawSelectionStartPoint = { x, y }
      state.drawSelectionAreaBox = { x, y, width: 0, height: 0 }
    }
    event.preventDefault()
  }
}

// 使用节流优化更新框选区域
const updateDrawSelection = throttle((event) => {
  const currentTool = toolbarSwitchType.value
  const isSelectionTool = [
    'createLineLibrary',
    'createRegion',
    'drawRoute',
    'generateLine',
    'bulkDelete',
    'bulkEditingRoute',
    'bulkEditingNode',
    'batchCopying'
  ].includes(currentTool)

  if (isSelectionTool) {
    if (state.drawSelectionAreaShow) {
      const { x, y } = disposeEventPoints(event)

      requestAnimationFrame(() => {
        state.drawSelectionAreaBox = {
          x: Math.min(state.drawSelectionStartPoint.x, x),
          y: Math.min(state.drawSelectionStartPoint.y, y),
          width: Math.abs(x - state.drawSelectionStartPoint.x),
          height: Math.abs(y - state.drawSelectionStartPoint.y)
        }
      })
    }
    event.preventDefault()
  }

  if (currentTool === 'clickDrawRoute' && state.isDrawing) {
    const { x, y } = disposeEventPoints(event)

    requestAnimationFrame(() => {
      state.currentDrawX = x
      state.currentDrawY = y
    })
    event.preventDefault()
  }
}, 16) // 约60fps的更新频率

// 优化结束框选
const endDrawSelection = (event) => {
  if (toolbarSwitchType.value === 'clickDrawRoute') {
    if (state.isDrawing) {
      const endPointIndex = findClosestPoint(state.currentDrawX, state.currentDrawY)

      if (endPointIndex !== null && endPointIndex !== state.startDrawPointIndex) {
        let list = state.allMapPointInfo
        const endPoint = list[endPointIndex]

        if (!endPoint?.id) {
          message.warning('选择的节点未保存')
          resetDrawState()
          return
        }

        // 检查路线是否重复
        if (checkRouteDuplicate(state.startDrawPoint, endPoint, state.mapRouteList)) {
          message.warning('此路线已存在')
        } else {
          // 创建新路线对象
          const newRoute = {
            isSelected: false,
            startingPointId: state.startDrawPoint.id,
            endPointId: endPoint.id,
            startPointX: state.startDrawPoint.locationX,
            startPointY: state.startDrawPoint.locationY,
            endPointX: endPoint.locationX,
            endPointY: endPoint.locationY,
            actualStartPointX: state.startDrawPoint.actualLocationX,
            actualStartPointY: state.startDrawPoint.actualLocationY,
            actualEndPointX: endPoint.actualLocationX,
            actualEndPointY: endPoint.actualLocationY,
            actualBeginControlX: '',
            actualBeginControlY: '',
            actualEndControlX: '',
            actualEndControlY: '',
            beginControlX: 0,
            beginControlY: 0,
            endControlX: 0,
            endControlY: 0,
            expansionZoneFront: 0,
            expansionZoneAfter: 0,
            expansionZoneLeft: 0,
            expansionZoneRight: 0,
            method: 0,
            direction: state.routeDirection,
            forwardSpeedLimit: 1.5,
            reverseSpeedLimit: 0.4,
            toward: 0,
            startingSortNum: state.startDrawPoint.sortNum,
            endPointSortNum: endPoint.sortNum,
            startToEndToward: 0, //开始点到结束点
            endToStartToward: 0, //结束点到开始点
            collisionAvoidanceFlag: 1 // 是否开启防撞 0否 1是
          }

          // 使用 requestAnimationFrame 优化渲染
          requestAnimationFrame(() => {
            state.mapRouteList.push(newRoute)
            addEditHistory()
          })
        }
      }
      resetDrawState()
    }
    event.preventDefault()
    return
  }

  if (
    toolbarSwitchType.value === 'createLineLibrary' ||
    toolbarSwitchType.value === 'createRegion' ||
    toolbarSwitchType.value === 'drawRoute' ||
    toolbarSwitchType.value === 'generateLine' ||
    toolbarSwitchType.value === 'bulkDelete' ||
    toolbarSwitchType.value === 'bulkEditingRoute' ||
    toolbarSwitchType.value === 'bulkEditingNode' ||
    toolbarSwitchType.value === 'batchCopying'
  ) {
    // 使用 requestAnimationFrame 优化渲染
    requestAnimationFrame(() => {
      state.drawSelectionAreaShow = false
      if (state.drawSelectionAreaBox.width > 0 && state.drawSelectionAreaBox.height > 0) {
        state.allDrawSelectionAreaBox.push({ ...state.drawSelectionAreaBox })
      }
      state.drawSelectionAreaBox = { x: 0, y: 0, width: 0, height: 0 }
    })
    event.preventDefault()
  }
}

//点击区域
const clickDrawSelectionArea = () => {
  state.drawSelectionPointList = []
  state.drawSelectionRouteList = []

  // 用于存储所有框选区域的数据
  const allSelectedPoints = new Map()
  const allSelectedRoutes = new Set()

  state.allDrawSelectionAreaBox.forEach((box) => {
    // 筛选点
    state.allMapPointInfo.forEach((point) => {
      if (
        point.locationX >= box.x &&
        point.locationX <= box.x + box.width &&
        point.locationY >= box.y &&
        point.locationY <= box.y + box.height
      ) {
        // 使用位置和类型组合作为唯一标识
        const pointKey = `${point.locationX},${point.locationY},${point.type}`
        if (!allSelectedPoints.has(pointKey)) {
          allSelectedPoints.set(pointKey, point)
        }
      }
    })

    // 筛选路线
    if (
      toolbarSwitchType.value === 'batchCopying' ||
      toolbarSwitchType.value === 'bulkEditingRoute'
    ) {
      state.mapRouteList.forEach((route) => {
        const startPointInBox =
          route.startPointX >= box.x &&
          route.startPointX <= box.x + box.width &&
          route.startPointY >= box.y &&
          route.startPointY <= box.y + box.height

        const endPointInBox =
          route.endPointX >= box.x &&
          route.endPointX <= box.x + box.width &&
          route.endPointY >= box.y &&
          route.endPointY <= box.y + box.height

        if (startPointInBox && endPointInBox) {
          // 使用起点ID和终点ID组合作为唯一标识
          const routeKey = `${route.startingPointId}-${route.endPointId}`
          if (!allSelectedRoutes.has(routeKey)) {
            allSelectedRoutes.add(routeKey)
          }
        }
      })
    }
  })

  // 将去重后的数据赋值给状态
  state.drawSelectionPointList = Array.from(allSelectedPoints.values())
  state.drawSelectionRouteList = state.mapRouteList.filter((route) =>
    allSelectedRoutes.has(`${route.startingPointId}-${route.endPointId}`)
  )

  // 清空框选区域
  state.allDrawSelectionAreaBox = []
  //只要库位的
  let binLocation = state.drawSelectionPointList.filter((item) => item.type === 2)
  //所以类型的
  let routeList = state.drawSelectionPointList

  let isHaveId = binLocation.every((item) => {
    item.id
  })

  //线库
  if (toolbarSwitchType.value === 'createLineLibrary') {
    //线库
    if (binLocation.length < 2) {
      message.warning('至少选择两个库位')
      return
    }

    // if (!isStraightLine(binLocation)) {
    //   message.warning('您选择的库位不在一条直线上')
    //   return
    // }

    let isHaveId = binLocation.every((item) => {
      return item.id
    })
    if (!isHaveId) {
      message.warning('您选择的库位存在未保存的')
      return
    }

    //判断线库的排序是不是一样
    const locationNumbers = findDuplicateLocationIds(binLocation)
    if (locationNumbers.length > 0) {
      message.warning('您选择的库位的序号不能重复')
      return
    }

    removeEventListener() //移除监听
    lineLibrarySettingDialogRef.value.open(binLocation)
  }
  //区域
  if (toolbarSwitchType.value === 'createRegion') {
    if (binLocation.length < 1) {
      message.warning('至少选择两个库位')
      return
    }
    let isHaveId = binLocation.every((item) => {
      return item.id
    })
    if (!isHaveId) {
      message.warning('您选择的库位存在未保存的')
      return
    }
    removeEventListener() //移除监听
    itemAreaSettingDialogRef.value.open(binLocation)
  }
  //绘制直线
  if (toolbarSwitchType.value === 'drawRoute') {
    if (routeList.length !== 2) {
      message.warning('只能选择两个路径点')
      return
    }
    let isHaveId = routeList.every((item) => {
      return item.id
    })
    if (!isHaveId) {
      message.warning('您选择的路径点存在未保存的')
      return
    }
    let curve = {
      isSelected: false, //是否选中
      startingPointId: routeList[0].id,
      endPointId: routeList[1].id,
      startPointX: routeList[0].locationX, //开始点
      startPointY: routeList[0].locationY, //开始点
      endPointX: routeList[1].locationX, //结束点
      endPointY: routeList[1].locationY, //结束点
      actualStartPointX: routeList[0].actualLocationX, //实际开始点位x轴
      actualStartPointY: routeList[0].actualLocationY, //实际开始点位y轴
      actualEndPointX: routeList[1].actualLocationX, //实际结束点位x轴
      actualEndPointY: routeList[1].actualLocationY, //实际结束点位y轴
      actualBeginControlX: '', //实际开始控制点x轴
      actualBeginControlY: '', //实际开始控制点y轴
      actualEndControlX: '', //实际结束控制点x轴
      actualEndControlY: '', //实际结束控制点y轴
      beginControlX: 0, //开始控制点x轴
      beginControlY: 0, //开始控制点y轴
      endControlX: 0, //结束控制点x轴
      endControlY: 0, //结束控制点y轴
      expansionZoneFront: 0, //膨胀区域前
      expansionZoneAfter: 0, //膨胀区域后
      expansionZoneLeft: 0, //	膨胀区域左
      expansionZoneRight: 0, //膨胀区域右
      method: 0, //行走方法 0.直线 1.上左曲线2.上右曲线3.下左曲线 4.下右曲线
      direction: 2, //方向 1.单向 2.双向,
      forwardSpeedLimit: 1.5, //正向限速
      reverseSpeedLimit: 0.4, //	反向限速
      toward: 0, //	车头朝向( 0:正正 1:正反 2:反正 3:反反)
      startToEndToward: 0, //开始点到结束点
      endToStartToward: 0, //结束点到开始点
      collisionAvoidanceFlag: 1 // 是否开启防撞 0否 1是
    }
    state.selectedCurve = curve
    state.mapRouteList.push(curve)
    addEditHistory()
  }
  //生成直线
  if (toolbarSwitchType.value === 'generateLine') {
    if (routeList.length < 3) {
      message.warning('至少框选三个点')
      return
    }
    let isHaveId = routeList.every((item) => {
      return item.id
    })
    if (!isHaveId) {
      message.warning('您选择的路径点存在未保存的')
      return
    }
    removeEventListener() //移除监听
    GenerateStraightLinesDialogRef.value.open(routeList)
  }
  //批量删除
  if (toolbarSwitchType.value === 'bulkDelete') {
    if (routeList.length < 1) {
      message.warning('至少选择一个点')
      return
    }
    //删除的节点id合集
    const routeIds = routeList.map((route) => route.id)
    //在数组中去除这些节点
    state.allMapPointInfo = state.allMapPointInfo.filter((item) => {
      return !routeIds.includes(item.id)
    })
    //将路线中的删除
    state.mapRouteList = state.mapRouteList.filter((item) => {
      return !routeIds.includes(item.startingPointId) && !routeIds.includes(item.endPointId)
    })
    state.currentItemIndex = -1
    addEditHistory()
  }

  //批量复制
  if (toolbarSwitchType.value === 'batchCopying') {
    if (state.drawSelectionPointList.length > 0) {
      let boundaryValue = {
        left: Math.min(...state.drawSelectionPointList.map((point) => Number(point.locationX))),
        right: Math.max(...state.drawSelectionPointList.map((point) => Number(point.locationX))),
        top: Math.min(...state.drawSelectionPointList.map((point) => Number(point.locationY))),
        bottom: Math.max(...state.drawSelectionPointList.map((point) => Number(point.locationY)))
      }
      removeEventListener() //移除监听
      BatchCopyingDialogFormRef.value.open(boundaryValue)
    } else {
      message.warning('至少选择一个点')
    }
  }

  //批量编辑路线
  if (toolbarSwitchType.value === 'bulkEditingRoute') {
    if (state.drawSelectionRouteList && state.drawSelectionRouteList.length > 0) {
      removeEventListener() //移除监听
      BatchEditRoutePropertiesDialogRef.value.open()
    } else {
      message.warning('请先框选路线')
    }
  }

  //批量编辑库位
  if (toolbarSwitchType.value === 'bulkEditingNode') {
    state.drawSelectionPointList = state.drawSelectionPointList.filter((item) => item.type === 2)
    if (state.drawSelectionPointList && state.drawSelectionPointList.length > 0) {
      removeEventListener() //移除监听
      BatchEditNodePropertiesDialogRef.value.open()
    } else {
      message.warning('请先框选库位')
    }
  }
}

//批量编辑库位成功
const BatchEditNodePropertiesSucceeded = (data) => {
  // 获取选中的库位节点ID列表
  const selectedNodeIds = state.drawSelectionPointList.map((item) => item.id)

  // 批量修改节点属性
  state.allMapPointInfo.forEach((node) => {
    if (selectedNodeIds.includes(node.id)) {
      data.batchKeys.forEach((key) => {
        if (data.formData.hasOwnProperty(key)) {
          node[key] = data.formData[key]

          // 如果修改了 locationTypeId，需要同时修改 locationDeepPx 和 locationWidePx
          if (
            key === 'locationTypeId' &&
            data.formData.hasOwnProperty('locationDeep') &&
            data.formData.hasOwnProperty('locationWide')
          ) {
            node.locationDeepPx = Number(data.formData.locationDeep) / 100 / imgBgObj.resolution
            node.locationWidePx = Number(data.formData.locationWide) / 100 / imgBgObj.resolution

            // 对于库位节点，还需要修改 dataList 中每个层的数据
            if (node.type === 2 && node.dataList && node.dataList.length > 0) {
              node.dataList.forEach((layer) => {
                layer.locationTypeId = data.formData.locationTypeId
                layer.locationDeep = data.formData.locationDeep
                layer.locationWide = data.formData.locationWide
              })
              // 更新 dataJson
              node.dataJson = JSON.stringify(node.dataList)
            }
          }

          // 如果修改了 layersNumber，需要重新构建 dataList
          if (key === 'layersNumber' && node.type === 2) {
            const newLayersNumber = data.formData.layersNumber
            const newDataList = []

            for (let index = 0; index < newLayersNumber; index++) {
              let item = {
                positionMapId: node.positionMapId,
                locationWide: data.formData.locationWide || node.locationWide,
                locationDeep: data.formData.locationDeep || node.locationDeep,
                direction: data.formData.direction || undefined,
                inDirection: data.formData.inDirection || undefined,
                outDirection: data.formData.outDirection || undefined,
                locationStorey: index + 1,
                locationTypeId: data.formData.locationTypeId || node.locationTypeId
              }

              // 保留原有数据中的关键字段
              if (
                node.dataList &&
                node.dataList.length > 0 &&
                node.dataList[index] &&
                node.dataList[index].id
              ) {
                item.id = String(node.dataList[index].id)
                item.locationNo = node.dataList[index].locationNo
                item.mapItemId = node.dataList[index].mapItemId
                item.areaId = node.dataList[index].areaId || ''
                item.areaName = node.dataList[index].areaName || ''
                item.laneId = node.dataList[index].laneId || ''
                item.laneName = node.dataList[index].laneName || ''
              }

              newDataList.push(item)
            }

            node.dataList = newDataList
            node.dataJson = JSON.stringify(node.dataList)
          }
        }
      })
    }
  })

  console.log(state.allMapPointInfo)

  // message.success('修改成功')
  // addEditHistory()
}

//批量路线编辑成功
const BatchEditRoutePropertiesSucceeded = (data) => {
  let newRoutes = JSON.parse(JSON.stringify(state.drawSelectionRouteList))
  const routeIdSet = new Set(newRoutes.map((route) => route.id))
  state.mapRouteList.forEach((route) => {
    if (routeIdSet.has(route.id)) {
      data.batchKeys.forEach((key) => {
        if (data.formData.hasOwnProperty(key)) {
          // 判断method是否变化
          if (key === 'method' && route[key] !== data.formData[key]) {
            // 先赋新method
            route[key] = data.formData[key]
            // 控制点赋值逻辑
            if (data.formData[key] === 0) {
              // 变为直线，控制点归零
              route.beginControlX = 0
              route.beginControlY = 0
              route.endControlX = 0
              route.endControlY = 0
            } else {
              // 变为曲线，按比例设置控制点
              route.beginControlX =
                Number(route.startPointX) +
                (Number(route.endPointX) - Number(route.startPointX)) * 0.3
              route.beginControlY =
                Number(route.startPointY) +
                (Number(route.endPointY) - Number(route.startPointY)) * 0.3
              route.endControlX =
                Number(route.startPointX) +
                (Number(route.endPointX) - Number(route.startPointX)) * 0.7
              route.endControlY =
                Number(route.startPointY) +
                (Number(route.endPointY) - Number(route.startPointY)) * 0.7
            }
          } else if (key !== 'method') {
            route[key] = data.formData[key]
          }
        }
      })
    }
  })
  message.success('修改成功')
  addEditHistory()
}

//批量复制
const submitBatchCopyingFormSuccess = async (form) => {
  let newPoints = JSON.parse(JSON.stringify(state.drawSelectionPointList))
  let newRoutes = JSON.parse(JSON.stringify(state.drawSelectionRouteList))

  // 创建节点ID映射
  const nodeIdMap = new Map()

  // 检查节点类型是否可复制
  const isValidNodeType = (type) => [1, 2, 4, 6].includes(type)

  // 检查路线是否可复制
  const isValidRoute = (route) => {
    const startNode = newPoints.find((node) => node.id === route.startingPointId)
    const endNode = newPoints.find((node) => node.id === route.endPointId)
    return startNode && endNode && isValidNodeType(startNode.type) && isValidNodeType(endNode.type)
  }

  // 过滤可复制的节点和路线
  const validNodes = newPoints.filter((node) => isValidNodeType(node.type))
  const validRoutes = newRoutes.filter((route) => isValidRoute(route))

  // 计算新坐标的辅助函数
  const calculateNewCoordinates = (x, y) => {
    const newX = (Number(x) + Number(form.x)).toString()
    const newY = (Number(y) + Number(form.y)).toString()
    const actualPoint = disposeEventPoint(newX, newY)
    return { newX, newY, actualPoint }
  }

  const newPointList = await Promise.all(
    validNodes.map(async (node) => {
      const newId = await MapApi.getNodeId()
      nodeIdMap.set(node.id, newId)

      const {
        newX: locationX,
        newY: locationY,
        actualPoint
      } = calculateNewCoordinates(node.locationX, node.locationY)

      // 检查节点是否重复
      const existingNode = state.allMapPointInfo.find(
        (item) =>
          Number(item.locationX).toFixed(1) === Number(locationX).toFixed(1) &&
          Number(item.locationY).toFixed(1) === Number(locationY).toFixed(1)
      )

      if (existingNode) {
        message.warning(`节点(${locationX}, ${locationY})已存在，已跳过`)
        // 记录重复节点的映射关系
        nodeIdMap.set(node.id, existingNode.id)
        return null
      }

      const { sortNum, createTime, ...restNode } = node

      let dataObj = {}
      let dataList = []
      let dataJson = ''
      if (restNode.type === 1 || restNode.type === 6) {
        dataObj = {}
        dataList = []
        dataJson = ''
      } else if (restNode.type === 2) {
        dataList = restNode.dataList.map(
          ({ locationDeep, locationStorey, locationWide, positionMapId, locationTypeId }) => ({
            locationDeep,
            locationStorey,
            locationWide,
            positionMapId,
            locationTypeId
          })
        )
        dataJson = JSON.stringify(dataList)
      } else if (restNode.type === 4) {
        dataObj = {
          locationDeep: restNode.dataObj.locationDeep,
          locationWide: restNode.dataObj.locationWide,
          positionMapId: restNode.dataObj.positionMapId
        }
        dataJson = JSON.stringify(dataObj)
      }

      return {
        ...restNode,
        id: newId,
        locationX,
        locationY,
        actualLocationX: actualPoint.actualLocationX,
        actualLocationY: actualPoint.actualLocationY,
        dataObj,
        dataList,
        dataJson
      }
    })
  )

  // 过滤掉重复的节点
  const filteredPointList = newPointList.filter((point) => point !== null)

  // 处理路线列表
  const newRouteList = validRoutes.map((route) => {
    const newRoute = { ...route }

    // 更新起点
    if (nodeIdMap.has(route.startingPointId)) {
      const { newX, newY, actualPoint } = calculateNewCoordinates(
        route.startPointX,
        route.startPointY
      )
      // 使用映射后的ID（可能是新ID或已存在节点的ID）
      newRoute.startingPointId = nodeIdMap.get(route.startingPointId)
      newRoute.startPointX = newX
      newRoute.startPointY = newY
      newRoute.actualStartPointX = actualPoint.actualLocationX
      newRoute.actualStartPointY = actualPoint.actualLocationY

      // 更新控制点
      if (route.method == 1) {
        const {
          newX: controlX,
          newY: controlY,
          actualPoint: controlPoint
        } = calculateNewCoordinates(route.beginControlX, route.beginControlY)
        newRoute.beginControlX = controlX
        newRoute.beginControlY = controlY
        newRoute.actualBeginControlX = controlPoint.actualLocationX
        newRoute.actualBeginControlY = controlPoint.actualLocationY
      } else {
        newRoute.beginControlX = 0
        newRoute.beginControlY = 0
        newRoute.actualBeginControlX = 0
        newRoute.actualBeginControlY = 0
      }
    }

    // 更新终点
    if (nodeIdMap.has(route.endPointId)) {
      const { newX, newY, actualPoint } = calculateNewCoordinates(route.endPointX, route.endPointY)
      // 使用映射后的ID（可能是新ID或已存在节点的ID）
      newRoute.endPointId = nodeIdMap.get(route.endPointId)
      newRoute.endPointX = newX
      newRoute.endPointY = newY
      newRoute.actualEndPointX = actualPoint.actualLocationX
      newRoute.actualEndPointY = actualPoint.actualLocationY

      // 更新控制点
      if (route.method == 1) {
        const {
          newX: controlX,
          newY: controlY,
          actualPoint: controlPoint
        } = calculateNewCoordinates(route.endControlX, route.endControlY)
        newRoute.endControlX = controlX
        newRoute.endControlY = controlY
        newRoute.actualEndControlX = controlPoint.actualLocationX
        newRoute.actualEndControlY = controlPoint.actualLocationY
      } else {
        newRoute.endControlX = 0
        newRoute.endControlY = 0
        newRoute.actualEndControlX = 0
        newRoute.actualEndControlY = 0
      }
    }

    const { id, createTime, startingSortNum, endPointSortNum, ...restRoute } = newRoute
    return restRoute
  })

  // 过滤掉重复的路线
  const filteredRouteList = newRouteList.filter((newRoute) => {
    // 检查路线是否重复
    const isDuplicate = state.mapRouteList.some(
      (existingRoute) =>
        Number(existingRoute.startPointX).toFixed(1) === Number(newRoute.startPointX).toFixed(1) &&
        Number(existingRoute.startPointY).toFixed(1) === Number(newRoute.startPointY).toFixed(1) &&
        Number(existingRoute.endPointX).toFixed(1) === Number(newRoute.endPointX).toFixed(1) &&
        Number(existingRoute.endPointY).toFixed(1) === Number(newRoute.endPointY).toFixed(1)
    )

    if (isDuplicate) {
      message.warning(
        `路线(${newRoute.startPointX}, ${newRoute.startPointY}) -> (${newRoute.endPointX}, ${newRoute.endPointY})已存在，已跳过`
      )
      return false
    }
    return true
  })

  console.log(filteredPointList)
  console.log(filteredRouteList)

  state.allMapPointInfo.push(...filteredPointList)
  state.mapRouteList.push(...filteredRouteList)
  message.success('复制成功')
  addEditHistory()
}

//生成直线 选择完成开始点和结束点
const GenerateStraightLinesSubmit = (pointList, form) => {
  // 使用 requestAnimationFrame 优化渲染
  requestAnimationFrame(() => {
    const list = mapPointsToLine(pointList, form.startPointId, form.endPointId)
    const idNameMap = new Map(list.map((item) => [item.id, item]))

    // 批量更新点位
    const updatedPoints = state.allMapPointInfo.map((item) => {
      if (idNameMap.has(item.id)) {
        const newPoint = idNameMap.get(item.id)
        const actualPoint = disposeEventPoint(newPoint.locationX, newPoint.locationY)
        return {
          ...item,
          locationX: newPoint.locationX,
          locationY: newPoint.locationY,
          actualLocationX: actualPoint.actualLocationX,
          actualLocationY: actualPoint.actualLocationY
        }
      }
      return item
    })

    // 批量更新路线
    const updatedRoutes = state.mapRouteList.map((item) => {
      const startPoint = idNameMap.get(item.startingPointId)
      const endPoint = idNameMap.get(item.endPointId)

      if (startPoint || endPoint) {
        const result = { ...item }

        if (startPoint) {
          const actualStartPoint = disposeEventPoint(startPoint.locationX, startPoint.locationY)
          result.startPointX = startPoint.locationX
          result.startPointY = startPoint.locationY
          result.actualStartPointX = actualStartPoint.actualLocationX
          result.actualStartPointY = actualStartPoint.actualLocationY
        }

        if (endPoint) {
          const actualEndPoint = disposeEventPoint(endPoint.locationX, endPoint.locationY)
          result.endPointX = endPoint.locationX
          result.endPointY = endPoint.locationY
          result.actualEndPointX = actualEndPoint.actualLocationX
          result.actualEndPointY = actualEndPoint.actualLocationY
        }

        return result
      }
      return item
    })
    // 批量更新状态
    state.allMapPointInfo = updatedPoints
    state.mapRouteList = updatedRoutes
    addEditHistory()
  })
}

//返回生成线库时 排序相同项的id
const findDuplicateLocationIds = (data) => {
  const locationMap = {}

  data.forEach((item) => {
    if (!locationMap[item.locationNumber]) {
      locationMap[item.locationNumber] = []
    }
    locationMap[item.locationNumber].push(item.id)
  })

  return Object.values(locationMap).filter((ids) => ids.length > 1)
}

//将一个数组中的点位 按照第一个和最后一个排成一条直线
const mapPointsToLine = (points, startPointId, endPointId) => {
  const startPoint = points.find((point) => point.id === startPointId)
  const endPoint = points.find((point) => point.id === endPointId)

  if (!startPoint || !endPoint) {
    message.warning('选择的点位有误')
    return
  }

  const dx = endPoint.locationX - startPoint.locationX
  const dy = endPoint.locationY - startPoint.locationY
  const length = Math.sqrt(dx * dx + dy * dy)

  return points.map((point) => {
    if (point.id === startPointId || point.id === endPointId) {
      return point
    }

    const vx = point.locationX - startPoint.locationX
    const vy = point.locationY - startPoint.locationY
    const dotProduct = vx * dx + vy * dy
    const projectionLength = dotProduct / length

    const t = Math.max(0, Math.min(1, projectionLength / length))

    const newX = startPoint.locationX + t * dx
    const newY = startPoint.locationY + t * dy

    return {
      ...point,
      locationX: newX,
      locationY: newY
    }
  })
}

//计算是不是在同一条直线的
const isStraightLine = (binLocation) => {
  if (binLocation.length <= 2) {
    return true // 两个点一定在一条直线上
  }

  const firstPoint = binLocation[0]
  const secondPoint = binLocation[1]

  // 检查是否垂直直线（所有 x 相同）
  const isVertical = binLocation.every(
    (point) => Number(point.locationX) === Number(firstPoint.locationX)
  )
  if (isVertical) {
    return true
  }

  // 检查是否水平直线（所有 y 相同）
  const isHorizontal = binLocation.every(
    (point) => Number(point.locationY) === Number(firstPoint.locationY)
  )
  if (isHorizontal) {
    return true
  }

  // 计算斜率
  const slope =
    Number(secondPoint.locationY) -
    Number(firstPoint.locationY) / (Number(secondPoint.locationX) - Number(firstPoint.locationX))

  // 检查所有点是否在同一条斜线上
  return binLocation.every((point) => {
    const currentSlope =
      (Number(point.locationY) - Number(firstPoint.locationY)) /
      (Number(point.locationX) - Number(firstPoint.locationX))
    return Math.abs(currentSlope - slope) < Number.EPSILON // 处理浮点数精度问题
  })
}
//三阶贝塞尔曲线
// 开始拖拽
const startDrag = (item, index, type) => {
  state.currentDragTarget = { index, type }
  window.addEventListener('mousemove', handleDrag)
  window.addEventListener('mouseup', endDrag)
}
// 处理拖动事件
const handleDrag = (event) => {
  let x = disposeEventPoints(event).x
  let y = disposeEventPoints(event).y

  if (state.currentDragTarget.index !== null) {
    const curve = state.mapRouteList[state.currentDragTarget.index]

    // 确保控制点不超出盒子范围
    x = Math.max(0, Math.min(x, imgBgObj.width))
    y = Math.max(0, Math.min(y, imgBgObj.height))

    if (state.currentDragTarget.type === 'start') {
      curve.beginControlX = x
      curve.beginControlY = y
    } else {
      curve.endControlX = x
      curve.endControlY = y
    }
  }
}
// 结束拖动
const endDrag = (event) => {
  const curve = state.mapRouteList[state.currentDragTarget.index]
  let actualBeginControl = disposeEventPoint(curve.beginControlX, curve.beginControlY)
  let actualEndControl = disposeEventPoint(curve.endControlX, curve.endControlY)

  curve.actualBeginControlX = actualBeginControl.actualLocationX
  curve.actualBeginControlY = actualBeginControl.actualLocationY

  curve.actualEndControlX = actualEndControl.actualLocationX
  curve.actualEndControlY = actualEndControl.actualLocationY

  addEditHistory()

  state.currentDragTarget.type = null
  window.removeEventListener('mousemove', handleDrag)
  window.removeEventListener('mouseup', endDrag)
}
// 获取曲线的路径
const getCurvePath = (curve) => {
  let startPointX = Number(curve.startPointX)
  let startPointY = Number(curve.startPointY)
  let endPointX = Number(curve.endPointX)
  let endPointY = Number(curve.endPointY)
  let path = `M ${startPointX} ${startPointY} C ${curve.beginControlX} ${curve.beginControlY}, ${curve.endControlX} ${curve.endControlY}, ${endPointX} ${endPointY}`

  curve.curvePath = path
  return path
}
// 编辑路线
const handleChooseRoute = async (item, index, type, e) => {
  if (toolbarSwitchType.value === 'addPointOnline') {
    if (!item.id) {
      message.warning('该路线未保存')
      return
    }
    //直线情况下 线上加点
    if (type === 'line') {
      const id = await MapApi.getNodeId()
      const pageX = disposeEventPoints(e).x
      const pageY = disposeEventPoints(e).y
      const x = mapPointToLineSegment(item, pageX, pageY).newX
      const y = mapPointToLineSegment(item, pageX, pageY).newY
      const actualLocationX = disposeEventPoint(x, y).actualLocationX
      const actualLocationY = disposeEventPoint(x, y).actualLocationY

      let newPoint = {
        id: id,
        positionMapId: imgBgObj.positionMapId, //地图的id
        layerSelectionShow: true,
        locationX: x,
        locationY: y,
        actualLocationX: actualLocationX,
        actualLocationY: actualLocationY,
        locationDeep: 40,
        locationWide: 40,
        locationDeepPx: 8,
        locationWidePx: 8,
        angle: 0,
        draggable: true,
        resizable: false,
        rotatable: false,
        lockAspectRatio: false, //横纵比
        mapImageUrl: '',
        locationYaw: 0, //弧度
        type: 1, //默认类型1 路径节点
        dataList: [], //存库位的
        dataObj: {} //存 设备点 停车点 文字
      }
      let positionMapLineOne = {
        isSelected: false, //是否选中
        startingPointId: item.startingPointId,
        endPointId: id,
        startPointX: item.startPointX, //开始点
        startPointY: item.startPointY, //开始点
        endPointX: x, //结束点
        endPointY: y, //结束点
        actualStartPointX: item.actualStartPointX, //实际开始点位x轴
        actualStartPointY: item.actualStartPointY, //实际开始点位y轴
        actualEndPointX: actualLocationX, //实际结束点位x轴
        actualEndPointY: actualLocationY, //实际结束点位y轴
        actualBeginControlX: '', //实际开始控制点x轴
        actualBeginControlY: '', //实际开始控制点y轴
        actualEndControlX: '', //实际结束控制点x轴
        actualEndControlY: '', //实际结束控制点y轴
        beginControlX: 0, //开始控制点x轴
        beginControlY: 0, //开始控制点y轴
        endControlX: 0, //结束控制点x轴
        endControlY: 0, //结束控制点y轴
        expansionZoneFront: 0, //膨胀区域前
        expansionZoneAfter: 0, //膨胀区域后
        expansionZoneLeft: 0, //	膨胀区域左
        expansionZoneRight: 0, //膨胀区域右
        method: 0, //行走方法 0.直线 1.上左曲线2.上右曲线3.下左曲线 4.下右曲线
        direction: 2, //方向 1.单向 2.双向,
        forwardSpeedLimit: 1.5, //正向限速
        reverseSpeedLimit: 0.4, //	反向限速
        toward: 0, //	车头朝向( 0:正正 1:正反 2:反正 3:反反)
        startToEndToward: 0, //开始点到结束点
        endToStartToward: 0, //结束点到开始点
        collisionAvoidanceFlag: 1 // 是否开启防撞 0否 1是
      }
      let positionMapLineTwo = {
        isSelected: false, //是否选中
        startingPointId: id,
        endPointId: item.endPointId,
        startPointX: x, //开始点
        startPointY: y, //开始点
        endPointX: item.endPointX, //结束点
        endPointY: item.endPointY, //结束点
        actualStartPointX: actualLocationX, //实际开始点位x轴
        actualStartPointY: actualLocationY, //实际开始点位y轴
        actualEndPointX: item.actualEndPointX, //实际结束点位x轴
        actualEndPointY: item.actualEndPointY, //实际结束点位y轴
        actualBeginControlX: '', //实际开始控制点x轴
        actualBeginControlY: '', //实际开始控制点y轴
        actualEndControlX: '', //实际结束控制点x轴
        actualEndControlY: '', //实际结束控制点y轴
        beginControlX: 0, //开始控制点x轴
        beginControlY: 0, //开始控制点y轴
        endControlX: 0, //结束控制点x轴
        endControlY: 0, //结束控制点y轴
        expansionZoneFront: 0, //膨胀区域前
        expansionZoneAfter: 0, //膨胀区域后
        expansionZoneLeft: 0, //	膨胀区域左
        expansionZoneRight: 0, //膨胀区域右
        method: 0, //行走方法 0.直线 1.上左曲线2.上右曲线3.下左曲线 4.下右曲线
        direction: 2, //方向 1.单向 2.双向,
        forwardSpeedLimit: 1.5, //正向限速
        reverseSpeedLimit: 0.4, //	反向限速
        toward: 0, //	车头朝向( 0:正正 1:正反 2:反正 3:反反)
        startToEndToward: 0, //开始点到结束点
        endToStartToward: 0, //结束点到开始点
        collisionAvoidanceFlag: 1 // 是否开启防撞 0否 1是
      }

      try {
        let res = await MapApi.lineAddItem([
          {
            ...newPoint,
            oldLineId: item.id,
            positionMapLines: [positionMapLineOne, positionMapLineTwo]
          }
        ])
        newPoint.sortNum = res?.ITEM?.sortNum || ''
        res?.LINE.forEach((lineItem) => {
          if (
            lineItem.startingPointId === positionMapLineOne.startingPointId &&
            lineItem.endPointId === positionMapLineOne.endPointId
          ) {
            positionMapLineOne.startingSortNum = lineItem.startingSortNum
            positionMapLineOne.endPointSortNum = lineItem.endPointSortNum
          }
          if (
            lineItem.startingPointId === positionMapLineTwo.startingPointId &&
            lineItem.endPointId === positionMapLineTwo.endPointId
          ) {
            positionMapLineTwo.startingSortNum = lineItem.startingSortNum
            positionMapLineTwo.endPointSortNum = lineItem.endPointSortNum
          }
        })

        state.allMapPointInfo.push(newPoint)
        state.mapRouteList.push(positionMapLineOne)
        state.mapRouteList.push(positionMapLineTwo)
        state.mapRouteList.splice(index, 1)
        state.allHistoryList[0] = {
          allMapPointInfo: JSON.parse(JSON.stringify(state.allMapPointInfo)),
          mapRouteList: JSON.parse(JSON.stringify(state.mapRouteList))
        }
        state.currentIndex = 0
        message.success('添加成功')
      } catch (e) {
        message.error(e)
      }
    }
  } else {
    state.mapRouteList.forEach((curve, i) => {
      curve.isSelected = i === index
    })
    state.selectedCurve = item
    state.currentDragTarget.index = index
    //让节点不选中
    state.currentItemIndex = -1

    if (toolbarSwitchType.value === 'editRoute') {
      removeEventListener() //移除监听
      editMapRouteDialogRef.value.open(JSON.parse(JSON.stringify(item)))
    }
  }
}

const mapPointToLineSegment = (linePoint, x, y) => {
  const dx = Number(linePoint.endPointX) - Number(linePoint.startPointX)
  const dy = Number(linePoint.endPointY) - Number(linePoint.startPointY)
  const length = Math.sqrt(dx * dx + dy * dy)

  const vx = x - Number(linePoint.startPointX)
  const vy = y - Number(linePoint.startPointY)
  const dotProduct = vx * dx + vy * dy
  const projectionLength = dotProduct / length

  const t = Math.max(0, Math.min(1, projectionLength / length))

  const newX = Number(linePoint.startPointX) + t * dx
  const newY = Number(linePoint.startPointY) + t * dy

  return { newX, newY }
}

//编辑路线成功
const editMapRouteDialogSubmit = (form) => {
  state.mapRouteList[state.currentDragTarget.index] = form

  //路线位置改变 通知路径里面的节点也改变
  state.mapRouteList.forEach((item) => {
    if (form.startingPointId === item.startingPointId) {
      item.startPointX = form.startPointX
      item.startPointY = form.startPointY
      item.actualStartPointX = disposeEventPoint(form.startPointX, form.startPointY).actualLocationX
      item.actualStartPointY = disposeEventPoint(form.startPointX, form.startPointY).actualLocationY
    }
    if (form.endPointId === item.endPointId) {
      item.endPointX = form.endPointX
      item.endPointY = form.endPointY
      item.actualEndPointX = disposeEventPoint(form.endPointX, form.endPointY).actualLocationX
      item.actualEndPointY = disposeEventPoint(form.endPointX, form.endPointY).actualLocationY
    }
  })
  state.allMapPointInfo.forEach((item) => {
    if (item.id === form.startingPointId) {
      item.locationX = form.startPointX
      item.locationY = form.startPointY
      item.actualLocationX = disposeEventPoint(form.startPointX, form.startPointY).actualLocationX
      item.actualLocationY = disposeEventPoint(form.startPointX, form.startPointY).actualLocationY
    }
    if (item.id === form.endPointId) {
      item.locationX = form.endPointX
      item.locationY = form.endPointY
      item.actualLocationX = disposeEventPoint(form.endPointX, form.endPointY).actualLocationX
      item.actualLocationY = disposeEventPoint(form.endPointX, form.endPointY).actualLocationY
    }
  })
  //增加一条历史记录
  addEditHistory() //重新监听键盘事件
}
//测距相关
// 计算连线的样式 - 优化计算
const rangingLineStyle = computed(() => {
  const points = state.measureDistancesPoints
  if (points.length !== 2) return {}

  const [point1, point2] = points
  const dx = point2.x - point1.x
  const dy = point2.y - point1.y
  const length = Math.sqrt(dx * dx + dy * dy)
  const angle = (Math.atan2(dy, dx) * 180) / Math.PI

  return {
    position: 'absolute',
    left: `${point1.x}px`,
    top: `${point1.y}px`,
    width: `${length}px`,
    height: '.125rem',
    backgroundColor: 'red',
    transform: `rotate(${angle}deg)`,
    transformOrigin: '0 0'
  }
})

// 计算距离信息的样式（显示在连线中间，并根据角度调整文字方向）
const rangingTextStyle = computed(() => {
  const points = state.measureDistancesPoints
  if (points.length !== 2) return {}

  const [point1, point2] = points
  const midX = (point1.x + point2.x) / 2
  const midY = (point1.y + point2.y) / 2
  const angle = (Math.atan2(point2.y - point1.y, point2.x - point1.x) * 180) / Math.PI

  // 调整文字方向，使其始终易于阅读
  const textRotation = angle > 90 || angle < -90 ? angle + 180 : angle

  return {
    position: 'absolute',
    left: `${midX}px`,
    top: `${midY}px`,
    transform: `translate(-50%, -50%) rotate(${textRotation}deg)`,
    backgroundColor: 'rgba(255, 255, 255, 0.8)',
    padding: '.125rem',
    borderRadius: '.25rem',
    fontSize: '.75rem',
    color: 'black',
    whiteSpace: 'nowrap',
    pointerEvents: 'none'
  }
})

// 计算点的样式
const getRangingPointStyle = (point) => ({
  position: 'absolute',
  left: `${point.x - 4}px`,
  top: `${point.y - 4}px`,
  width: '.5rem',
  height: '.5rem',
  backgroundColor: 'blue',
  borderRadius: '50%'
})

//计算路线的距离
const calculateRouteLength = (item, type) => {
  const {
    startPointX,
    startPointY,
    endPointX,
    endPointY,
    beginControlX,
    beginControlY,
    endControlX,
    endControlY
  } = item

  if (type == 'line') {
    const dx = startPointX - endPointX
    const dy = startPointY - endPointY
    const length = Math.sqrt(dx * dx + dy * dy) * Number(imgBgObj.resolution)
    return length.toFixed(2)
  } else {
    const steps = 100 // 离散化的步数，步数越多越精确
    let length = 0
    let prevPoint = null

    for (let i = 0; i <= steps; i++) {
      const t = i / steps
      // 三阶贝塞尔曲线公式
      const x =
        (1 - t) ** 3 * startPointX +
        3 * t * (1 - t) ** 2 * beginControlX +
        3 * t ** 2 * (1 - t) * endControlX +
        t ** 3 * endPointX
      const y =
        (1 - t) ** 3 * startPointY +
        3 * t * (1 - t) ** 2 * beginControlY +
        3 * t ** 2 * (1 - t) * endControlY +
        t ** 3 * endPointY
      const currentPoint = [x, y]

      if (prevPoint) {
        const dx = currentPoint[0] - prevPoint[0]
        const dy = currentPoint[1] - prevPoint[1]
        length += Math.sqrt(dx * dx + dy * dy)
      }
      prevPoint = currentPoint
    }
    length = (length * Number(imgBgObj.resolution)).toFixed(2)
    return length
  }
}

// 处理点击事件 - 优化版本
const measureDistancesClick = (event) => {
  const { x, y } = disposeEventPoints(event)
  const points = state.measureDistancesPoints

  if (points.length === 2) {
    // 如果已经有两个点，清空信息
    state.measureDistancesPoints = []
    state.measureDistancesNum = null
  } else {
    // 记录点击的点位
    state.measureDistancesPoints.push({ x, y })

    if (points.length === 1) {
      // 计算两点之间的距离
      const [point1, point2] = state.measureDistancesPoints
      const dx = point2.x - point1.x
      const dy = point2.y - point1.y
      const distancesNum = Math.sqrt(dx * dx + dy * dy)
      state.measureDistancesNum = distancesNum * Number(imgBgObj.resolution)
    }
  }
}
//获取扫描图  地图背景相关的信息
const imgBgObj = reactive({
  imgUrl: '',
  positionMapId: '',
  width: '',
  height: '',
  floor: '',
  area: '',
  resolution: 0,
  origin: null
})
//接收参数
const { query } = useRoute() // 查询参数
//获取地图点位
const getMapList = async () => {
  if (!query.mapId) return
  let res = await MapApi.getPositionMap(query.mapId)
  let yamlJson = JSON.parse(res.yamlJson)
  imgBgObj.positionMapId = res.id
  imgBgObj.floor = res.floor
  imgBgObj.area = res.area
  imgBgObj.width = yamlJson.width
  imgBgObj.height = yamlJson.height
  imgBgObj.origin = yamlJson.origin
  imgBgObj.resolution = yamlJson.resolution
  // 调转换成png的接口
  getMapData(imgBgObj)
}
//调转换成png的接口
const getMapData = async (mapInfo) => {
  let data = await MapApi.getPositionMapdDwnloadPngBase64({
    floor: mapInfo.floor,
    area: mapInfo.area
  })
  imgBgObj.imgUrl = data

  //获取节点 路径等信息
  await getAllNodeList()
  await getAllMapRoute()
}
//获取所有的点位 处理七种类型的
const getAllNodeList = async () => {
  let list = await MapApi.getPositionMapItemList({
    positionMapId: imgBgObj.positionMapId
  })
  state.allMapPointInfo = []
  state.currentIndex = 0
  list.forEach((item) => {
    item.layerSelectionShow = true //用于图层
    item.locationX = Number(item.locationX)
    item.locationY = Number(item.locationY)

    if (item.type === 1) {
      item.dataObj = {}
      item.dataList = []
      item.locationDeep = 40
      item.locationWide = 40
      item.draggable = true
      item.resizable = false
      item.rotatable = false
      item.lockAspectRatio = true
    } else if (item.type === 6) {
      item.dataObj = {}
      item.dataList = []
      item.locationDeep = 150
      item.locationWide = 150
      item.draggable = true
      item.resizable = false
      item.rotatable = false
      item.lockAspectRatio = true
    } else if (item.type === 2) {
      //库位点
      item.dataList = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
      item.locationDeep = item.dataList[0].locationDeep
      item.locationWide = item.dataList[0].locationWide
      item.areaName = item.dataList[0].areaName || undefined
      item.laneName = item.dataList[0].laneName || undefined
      item.skuInfo = item.dataList[0].skuInfo || undefined
      item.draggable = true
      item.resizable = true
      item.rotatable = false
      item.lockAspectRatio = true
    } else if (item.type === 3) {
      item.dataObj = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
      item.dataList = []
      item.locationDeep = item.dataObj.locationDeep
      item.locationWide = item.dataObj.locationWide
      item.deviceId = item.dataObj.id
      item.deviceNo = item.dataObj.deviceNo
      item.deviceType = item.dataObj.deviceType
      item.mapImageUrl = item.dataObj.mapImageUrl
      item.draggable = true
      item.resizable = true
      item.rotatable = false
      item.lockAspectRatio = true
    } else if (item.type === 4) {
      item.dataObj = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
      item.dataList = []
      item.locationDeep = item.dataObj.locationDeep
      item.locationWide = item.dataObj.locationWide
      item.draggable = true
      item.resizable = true
      item.rotatable = false
      item.lockAspectRatio = true
    } else if (item.type === 5) {
      item.dataObj = item.dataJson ? JSONBigInt({ storeAsString: true }).parse(item.dataJson) : {}
      item.dataList = []
      item.locationDeep = 150
      item.locationWide = 150
      item.draggable = true
      item.resizable = false
      item.rotatable = false
      item.lockAspectRatio = true
    } else if (item.type === 7) {
      item.dataObj = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
      item.text = item.dataObj.text
      item.fontColor = item.dataObj.fontColor
      item.fontFamily = item.dataObj.fontFamily
      item.fontSize = item.dataObj.fontSize
      item.angle = item.dataObj.angle
      item.draggable = true
      item.resizable = false
      item.rotatable = true
      item.lockAspectRatio = true
      item.locationDeep = 20
      item.locationWide = 20
    }

    //要将实际的cm改成px
    if (item.locationWide && item.locationDeep) {
      let pxObj = cmConversionPx(item.locationWide, item.locationDeep)
      item.locationWidePx = pxObj.pWidth
      item.locationDeepPx = pxObj.pHeight
    }

    state.allMapPointInfo.push(item)
  })

  //包含sortNumber的
  state.haveSortNumMapPointInfo = state.allMapPointInfo.filter((item) => {
    return item.sortNum
  })
}
//获取所有的路线
const getAllMapRoute = async () => {
  state.mapRouteList = await MapApi.getPositionMapLineByPositionMapId(imgBgObj.positionMapId)
  state.allHistoryList = []
  state.allHistoryList[0] = {
    allMapPointInfo: JSON.parse(JSON.stringify(state.allMapPointInfo)),
    mapRouteList: JSON.parse(JSON.stringify(state.mapRouteList))
  }
}
//获取设备类型
const getDeviceTypeName = (deviceType) => {
  let list = getIntDictOptions(DICT_TYPE.DEVICE_TYPE)
  let deviceItem = list.find((item) => {
    return item.value == deviceType
  })
  return deviceItem.label
}
//保存地图按钮
const saveMap = async () => {
  //判断是否存在库位未填写排序号
  state.noLocationNumberList = state.allMapPointInfo.reduce((invalidIndexes, item, index) => {
    if (item.type === 2 && !item.locationNumber) {
      invalidIndexes.push(index) // 如果不满足条件，记录索引
    }
    return invalidIndexes
  }, [])

  if (state.noLocationNumberList.length !== 0) {
    state.currentItemIndex = -1
    message.error('存在库位未填写排序号')
    return
  }

  const loading = ElLoading.service({
    lock: true,
    text: '保存中',
    background: 'rgba(255, 255, 255, 0.7)'
  })
  try {
    //节点的保存
    await saveNodeList()
    //路线的保存
    await saveMapRoute()
    //获取新的数据
    await getAllNodeList()
    await getAllMapRoute()
    //关闭loading
    loading.close()
    message.success('保存成功')
  } catch (error) {
    loading.close()
    message.error(error?.message || '保存失败')
  }
}
//节点的保存
const saveNodeList = async () => {
  let list = state.allMapPointInfo

  list.forEach((item) => {
    if (item.type === 2) {
      // 库位点 类型为数组
      item.dataList.forEach((node) => {
        node.locationDeep = item.locationDeep
        node.locationWide = item.locationWide
      })
      item.dataJson = JSON.stringify(item.dataList)
    } else if (item.type === 3 || item.type === 4) {
      //设备类型
      item.dataObj.locationWide = item.locationWide
      item.dataObj.locationDeep = item.locationDeep
      item.dataJson = JSON.stringify(item.dataObj)
    } else if (item.type === 7) {
      //文字类型
      item.dataObj.positionMapId = imgBgObj.positionMapId
      item.dataObj.text = item.text
      item.dataObj.fontColor = item.fontColor
      item.dataObj.fontType = item.fontType
      item.dataObj.fontFamily = item.fontFamily
      item.dataObj.fontSize = item.fontSize
      item.dataObj.rotatable = item.rotatable
      item.dataObj.angle = item.angle
      //dataJson数据
      item.dataJson = JSON.stringify(item.dataObj)
    }
  })
  try {
    await MapApi.batchSaveOrEditOrDelMapItem(imgBgObj.positionMapId, list)
  } catch (error) {
    throw new Error('节点保存失败：' + (error?.message || '未知错误'))
  }
}

//获取配置数据
const getCheckDistance = async () => {
  const res = await MapApi.getCheckDistance()
  state.checkPointDistance = Number(res.mapCheckDistance)
  state.curveOffsetValue = Number(res.mapArcOffset)
}

//路线的保存
const saveMapRoute = async () => {
  try {
    await MapApi.createOrEditOrDelPositionMapLine(imgBgObj.positionMapId, state.mapRouteList)
  } catch (error) {
    throw new Error('路线保存失败：' + (error?.message || '未知错误'))
  }
}
//线库新增 要在库位新增线库信息
const submitLineLibraryFormSuccess = (obj) => {
  state.allMapPointInfo.forEach((item) => {
    if (obj.mapItemIds.includes(item.id)) {
      item.laneName = obj.laneName
      item.laneId = obj.id
      item.dataList.forEach((node) => {
        node.laneName = obj.laneName
        node.laneId = obj.id
      })
    }
  })
}
//区域新增 要在库位新增区域信息
const itemAreaSettingSubmitSuccess = (obj) => {
  state.allMapPointInfo.forEach((item) => {
    if (obj.mapItemIds.includes(item.id)) {
      item.areaName = obj.areaName
      item.skuInfo = obj.skuInfo
      item.areaId = obj.id
      item.dataList.forEach((node) => {
        node.areaName = obj.areaName
        node.skuInfo = obj.skuInfo
        node.areaId = obj.id
      })
    }
  })
}
//线库删除 要在库位中删除线库信息
const lineLibraryManagementDelete = (mapItemIds) => {
  state.allMapPointInfo.forEach((item) => {
    if (mapItemIds.includes(item.id)) {
      item.laneId = undefined
      item.laneName = undefined
      item.dataList.forEach((node) => {
        node.laneName = undefined
        node.laneId = undefined
      })
    }
  })
}
//区域删除 要在库位删除区域信息
const itemAreaManagementDelete = (mapItemIds) => {
  state.allMapPointInfo.forEach((item) => {
    if (mapItemIds.includes(item.id)) {
      item.areaName = undefined
      item.skuInfo = undefined
      item.areaId = undefined
      item.dataList.forEach((node) => {
        node.areaName = undefined
        node.skuInfo = undefined
        node.areaId = undefined
      })
    }
  })
}
//线库编辑 要在库位中编辑线库信息
const lineLibraryManagementEdit = (obj) => {
  console.log(obj)
  state.allMapPointInfo.forEach((item) => {
    if (obj.mapItemIds.includes(item.id)) {
      item.laneName = obj.laneName
      item.laneId = obj.id
      item.dataList.forEach((node) => {
        node.laneName = obj.laneName
        node.laneId = obj.id
      })
    }
  })
}
//区域编辑 要在库位编辑区域信息
const itemAreaManagementEdit = (obj) => {
  console.log(obj)
  state.allMapPointInfo.forEach((item) => {
    if (obj.mapItemIds.includes(item.id)) {
      item.areaName = obj.areaName
      item.skuInfo = obj.skuInfo
      item.areaId = obj.id
      item.dataList.forEach((node) => {
        node.areaName = obj.areaName
        node.skuInfo = obj.skuInfo
        node.areaId = obj.id
      })
    }
  })
}

//筛选图层
const layerSelectionSuccess = (typeList) => {
  state.allMapPointInfo.forEach((item) => {
    // 如果 type 存在于第一个数组中，则将 layerSelectionShow 设置为 false
    if (typeList.includes(item.type)) {
      item.layerSelectionShow = false
    } else {
      item.layerSelectionShow = true
    }
  })
}
//路线的图层筛选
const isCurveDisplayChange = (category, show) => {
  if (category === 'curve') {
    //曲线
    state.isCurveDisplay = !state.isCurveDisplay
  } else if (category === 'sortNumber') {
    state.isShowSortNum = !state.isShowSortNum
  } else if (category === 'nodeImage') {
    if (!show) {
      state.allMapPointInfo.forEach((item) => {
        if (item.type === 2) {
          item.locationWidePx = 8
          item.locationDeepPx = 8
        }
      })
    } else {
      state.allMapPointInfo.forEach((item) => {
        //要将实际的cm改成px
        if (item.type === 2) {
          let pxObj = cmConversionPx(item.locationWide, item.locationDeep)
          item.locationWidePx = pxObj.pWidth
          item.locationDeepPx = pxObj.pHeight
        }
      })
    }
  }
}

//处理数据 考虑滚动条，缩放等各种情况的的坐标数据
const disposeEventPoints = (event) => {
  const rect = mapBackgroundRef.value.getBoundingClientRect()
  const scrollLeft = mapBackgroundRef.value.scrollLeft // 水平滚动条偏移量
  const scrollTop = mapBackgroundRef.value.scrollTop // 垂直滚动条偏移量
  // const devicePixelRatio = window.devicePixelRatio || 1 //浏览器缩放
  const devicePixelRatio = 1

  // 计算页面坐标（考虑设备像素比和滚动条偏移量）
  const x =
    Math.round(
      ((event.clientX - rect.left + scrollLeft) / state.imageChangeMultiple / devicePixelRatio) *
        1000
    ) / 1000
  const y =
    Math.round(
      ((event.clientY - rect.top + scrollTop) / state.imageChangeMultiple / devicePixelRatio) * 1000
    ) / 1000

  // 转换为实际坐标
  const actualLocationX = Math.round((imgBgObj.origin[0] + x * imgBgObj.resolution) * 1000) / 1000
  const actualLocationY =
    Math.round((imgBgObj.origin[1] + (imgBgObj.height - y) * imgBgObj.resolution) * 1000) / 1000

  return {
    x,
    y,
    actualLocationX,
    actualLocationY
  }
}
//实际坐标 传入x y轴坐标
const disposeEventPoint = (x, y) => {
  const actualLocationX = Number(imgBgObj.origin[0]) + Number(x) * Number(imgBgObj.resolution)
  const actualLocationY =
    Number(imgBgObj.origin[1]) + (Number(imgBgObj.height) - Number(y)) * Number(imgBgObj.resolution)
  return {
    actualLocationX,
    actualLocationY
  }
}
// 传入实际现场的数据，转换成浏览器坐标的数据
const convertActualToBrowser = (pointX, pointY) => {
  const y1 = Number(imgBgObj.origin[1]) + Number(imgBgObj.height) * Number(imgBgObj.resolution)
  let x = Math.max(Number(pointX) - Number(imgBgObj.origin[0]), 0) / Number(imgBgObj.resolution)
  let y = Math.max(y1 - Number(pointY), 0) / Number(imgBgObj.resolution)

  return {
    x,
    y
  }
}
//将节点实际宽高cm转换成px
const cmConversionPx = (cWidth, cHeight) => {
  let pWidth = Number(cWidth) / imgBgObj.resolution / 100
  let pHeight = Number(cHeight) / imgBgObj.resolution / 100

  return {
    pWidth,
    pHeight
  }
}

// 计算贝塞尔曲线中间文字的 x 坐标
const computedCurveTextX = (item) => {
  return (
    (Number(item.startPointX) +
      Number(item.beginControlX) +
      Number(item.endControlX) +
      Number(item.endPointX)) /
    4
  )
}
// 计算贝塞尔曲线中间文字的 y 坐标
const computedCurveTextY = (item) => {
  return (
    (Number(item.startPointY) +
      Number(item.beginControlY) +
      Number(item.endControlY) +
      Number(item.endPointY)) /
    4
  )
}
//搜索
const toggleSelect = () => {
  state.isSearchSelectVisible = !state.isSearchSelectVisible
  state.searchSelectedOption = ''
}
const searchSelectChange = (sortNum) => {
  const currentIndex = state.allMapPointInfo.findIndex((item) => item.sortNum === sortNum)
  const currentItem = state.allMapPointInfo.find((item) => item.sortNum === sortNum)

  // 计算并限制边界 滚动到指定位置
  const rect = mapContainerRef.value
  const maxScrollLeft = rect.scrollWidth - rect.clientWidth
  const maxScrollTop = rect.scrollHeight - rect.clientHeight
  const scrollToX = Math.max(
    0,
    Math.min(currentItem.locationX - rect.clientWidth / 2, maxScrollLeft)
  )
  const scrollToY = Math.max(
    0,
    Math.min(currentItem.locationY - rect.clientHeight / 2, maxScrollTop)
  )

  rect.scrollTo({
    left: scrollToX,
    top: scrollToY,
    behavior: 'smooth'
  })

  state.currentItemIndex = currentIndex
}

document.onmousedown = function (e) {
  // 点击外部隐藏右击菜单
  if (state.contextMenu.visible) {
    // 检查点击的元素是否在右击菜单内
    const contextMenu = document.querySelector('.context-menu')
    if (contextMenu && !contextMenu.contains(e.target)) {
      state.contextMenu.visible = false
    }
  }

  //右击
  if (e.button == 2) {
    state.selectedCurve = ''
    //清除路线的选中
    state.mapRouteList.forEach((item) => {
      item.isSelected = false
    })
    state.currentDragTarget = { index: null, type: null }
    state.currentItemIndex = -1
  }
}
// 阻止默认菜单弹出
window.document.oncontextmenu = function () {
  return false
}
//监听键盘事件
const handleKeyDown = (event) => {
  if (event.ctrlKey) {
    if (event.key === 'c') {
      //复制
      if (state.currentItemIndex === -1) {
        message.warning('请先选择要操作的对象')
        return
      }
      replicationNode()
    } else if (event.key === 'v') {
      //粘贴
      if (!state.copyMapItem) {
        message.warning('请先复制对象')
        return
      }
      pasteNode()
    } else if (event.key === 'z') {
      //撤回
      backPreviousStep()
    } else if (event.key === 'y') {
      //重写
      backNextStep()
    }
  }
}

//鼠标滚轮
const handleWheel = (event) => {
  // 判断 Ctrl 键是否被按下
  if (event.ctrlKey) {
    // 阻止默认的滚动行为
    event.preventDefault()

    // 根据滚轮滚动方向调整缩放比例
    if (event.deltaY < 0) {
      // 向上滚动，放大
      //放大
      if (state.imageChangeMultiple < 4) {
        state.imageChangeMultiple += 0.2
      } else {
        state.imageChangeMultiple = 3.8
        message.warning('不能在放大了')
      }
    } else {
      //缩小
      if (state.imageChangeMultiple > 0.2) {
        state.imageChangeMultiple -= 0.1
      } else {
        state.imageChangeMultiple = 0.1
        message.warning('不能在缩小了')
      }
    }
  }
}

const addEventListener = () => {
  window.addEventListener('keydown', handleKeyDown)
}
const removeEventListener = () => {
  window.removeEventListener('keydown', handleKeyDown)
}

const router = useRouter()
onBeforeRouteLeave((to, from) => {
  if (to.path == '/mapPage/realTimeMap' && to.query.mapId != imgBgObj.positionMapId) {
    router.replace({
      name: 'MapPageRealTimeMap',
      query: {
        mapId: imgBgObj.positionMapId
      }
    })
  }
})

onMounted(() => {
  getMapList()
  getCheckDistance()
  window.addEventListener('keydown', handleKeyDown)
})

onUnmounted(() => {
  window.removeEventListener('keydown', handleKeyDown)
})

// 检查路线是否重复的函数
const checkRouteDuplicate = (startPoint, endPoint, routeList) => {
  if (!startPoint?.id || !endPoint?.id) return false

  return routeList.some(
    (route) =>
      (route.startingPointId === startPoint.id && route.endPointId === endPoint.id) ||
      (route.startingPointId === endPoint.id && route.endPointId === startPoint.id)
  )
}

// 空间分区优化
const GRID_SIZE = 200 // 网格大小
const gridMap = new Map() // 网格映射

// 获取点所在的网格坐标
const getGridKey = (x, y) => {
  const gridX = Math.floor(x / GRID_SIZE)
  const gridY = Math.floor(y / GRID_SIZE)
  return `${gridX},${gridY}`
}

// 更新网格数据
const updateGridMap = (points) => {
  gridMap.clear()
  points.forEach((point, index) => {
    if (!point?.locationX || !point?.locationY) return
    const key = getGridKey(point.locationX, point.locationY)
    if (!gridMap.has(key)) {
      gridMap.set(key, [])
    }
    gridMap.get(key).push({ point, index })
  })
}

// 获取指定网格及其相邻网格中的点
const getNearbyPoints = (x, y) => {
  const centerKey = getGridKey(x, y)
  const [centerX, centerY] = centerKey.split(',').map(Number)
  const nearbyPoints = []

  // 检查中心网格及其相邻网格
  for (let dx = -1; dx <= 1; dx++) {
    for (let dy = -1; dy <= 1; dy++) {
      const key = `${centerX + dx},${centerY + dy}`
      const points = gridMap.get(key)
      if (points) {
        nearbyPoints.push(...points)
      }
    }
  }

  return nearbyPoints
}

// 优化后的findClosestPoint函数
const findClosestPoint = (x, y) => {
  const list = state.allMapPointInfo
  if (!Array.isArray(list) || list.length === 0) return null

  // 更新网格数据
  updateGridMap(list)

  let minDistance = Infinity
  let closestIndex = null

  // 获取附近的点
  const nearbyPoints = getNearbyPoints(x, y)

  // 在附近的点中找最近的
  for (const { point, index } of nearbyPoints) {
    const dx = point.locationX - x
    const dy = point.locationY - y
    const distance = dx * dx + dy * dy // 使用平方距离避免开方运算
    const captureRadius = (point.locationWide || 10) ** 2

    if (distance < minDistance && distance < captureRadius) {
      minDistance = distance
      closestIndex = index
    }
  }

  return closestIndex
}

// 曲线右击事件
const handleCurveContextMenu = (curve, index, event) => {
  event.preventDefault()
  state.curveContextMenu.visible = true
  state.curveContextMenu.x = event.clientX
  state.curveContextMenu.y = event.clientY
  state.curveContextMenu.currentCurve = curve
  state.curveContextMenu.currentIndex = index
  state.currentDragTarget.index = index
}

// 隐藏曲线右击菜单
const hideCurveContextMenu = () => {
  state.curveContextMenu.visible = false
  state.curveContextMenu.currentIndex = -1
}

// 曲线删除（仅UI，逻辑留空）
const deleteCurveRoute = () => {
  if (state.currentDragTarget.index !== null) {
    state.mapRouteList.splice(state.currentDragTarget.index, 1)
    state.currentDragTarget.index = null
  }
  addEditHistory()
  hideCurveContextMenu()
}

// 点击空白处关闭曲线菜单
window.addEventListener('click', (e) => {
  if (state.curveContextMenu.visible) {
    const menu = document.querySelector('.context-menu')
    if (menu && !menu.contains(e.target)) {
      hideCurveContextMenu()
    }
  }
})

// 直角拐角控制点计算
function getRightAngleControlPoints(direction, start, end) {
  let corner
  let offset = -state.curveOffsetValue || 0
  if (direction === 'leftTop') {
    corner = { x: Math.min(start.x, end.x), y: Math.min(start.y, end.y) }
  } else if (direction === 'rightBottom') {
    corner = { x: Math.max(start.x, end.x), y: Math.max(start.y, end.y) }
  } else if (direction === 'leftBottom') {
    corner = { x: Math.min(start.x, end.x), y: Math.max(start.y, end.y) }
  } else if (direction === 'rightTop') {
    corner = { x: Math.max(start.x, end.x), y: Math.min(start.y, end.y) }
  } else {
    corner = { x: end.x, y: start.y }
  }
  // 计算单位向量
  const getUnit = (from, to) => {
    const dx = from.x - to.x
    const dy = from.y - to.y
    const len = Math.sqrt(dx * dx + dy * dy) || 1
    return { x: dx / len, y: dy / len }
  }
  // 计算起点终点距离
  const distance = Math.sqrt((start.x - end.x) ** 2 + (start.y - end.y) ** 2) || 1
  // 归一化offset，标准距离为100像素
  const normalizedOffset = offset * (distance / 100)
  // 控制点分别在拐点往起点/终点方向各偏移normalizedOffset像素
  const u1 = getUnit(corner, start)
  const u2 = getUnit(corner, end)
  const beginControl = {
    x: corner.x + u1.x * normalizedOffset,
    y: corner.y + u1.y * normalizedOffset
  }
  const endControl = {
    x: corner.x + u2.x * normalizedOffset,
    y: corner.y + u2.y * normalizedOffset
  }
  return { beginControl, endControl }
}
// 设置曲线为直角拐角
function setCurveRightAngle(direction) {
  const curve = state.curveContextMenu.currentCurve
  if (!curve) return
  const start = { x: curve.startPointX, y: curve.startPointY }
  const end = { x: curve.endPointX, y: curve.endPointY }
  const { beginControl, endControl } = getRightAngleControlPoints(direction, start, end)
  curve.beginControlX = beginControl.x
  curve.beginControlY = beginControl.y
  curve.endControlX = endControl.x
  curve.endControlY = endControl.y
  curve.method = 1
  hideCurveContextMenu()
}

// 动态判断可用直角拐角方向
function getAvailableRightAngleDirections(curve) {
  if (!curve) return []
  const sx = curve.startPointX,
    sy = curve.startPointY,
    ex = curve.endPointX,
    ey = curve.endPointY
  const dx = ex - sx
  const dy = ey - sy
  if (dx === 0 || dy === 0) return []
  if (dx * dy > 0) return ['leftBottom', 'rightTop']
  if (dx * dy < 0) return ['leftTop', 'rightBottom']
  return []
}
// 方向label
function rightAngleDirectionLabel(dir) {
  switch (dir) {
    case 'rightTop':
      return '右上直角拐角'
    case 'rightBottom':
      return '右下直角拐角'
    case 'leftTop':
      return '左上直角拐角'
    case 'leftBottom':
      return '左下直角拐角'
    default:
      return ''
  }
}

//右击编辑路线
const contextMenuEditRoute = () => {
  removeEventListener() //移除监听
  let item = state.mapRouteList[state.currentDragTarget.index]
  editMapRouteDialogRef.value.open(JSON.parse(JSON.stringify(item)))
}

// 新增：处理节点点击，显示节点locationX/locationY - 优化版本
function handleNodeClick(item) {
  const { actualLocationX, actualLocationY } = item
  state.actualLocation.x = actualLocationX ? Number(actualLocationX).toFixed(3) : ''
  state.actualLocation.y = actualLocationY ? Number(actualLocationY).toFixed(3) : ''
}
</script>

<style lang="scss" scoped>
.item-tooltip-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 12.5rem;
}

.edit-map-page {
  -webkit-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;

  .map-container {
    position: relative;
    width: 100%;
    overflow: auto;
    height: calc(100vh - 120px);

    .map-bg {
      background-size: contain;
      background-repeat: no-repeat;
      position: absolute;
      top: 0;
      left: 0;

      .map-box-inner {
        position: absolute;
        top: 0;
        left: 0;
      }
    }
  }

  .top-tool {
    margin-bottom: 2px;

    .top-tool-list-container {
      display: flex;
      align-items: center;
      justify-content: space-between;
      background-color: #fff;
      box-shadow: rgba(0, 0, 0, 0.06) 0rem 0.125rem 0.1875rem;
      height: 60px;
      padding: 0 0 0 4px;

      .top-tool-list {
        display: flex;
        width: fit-content;
        align-items: center;
        text-align: center;
        position: relative;

        .top-tool-item {
          display: flex;
          align-items: center;

          .tool-item {
            cursor: pointer;
            width: 42px;
            height: 60px;
            display: flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;

            .name {
              cursor: pointer;
              font-family:
                PingFangSC,
                PingFang SC;
              font-weight: 400;
              font-size: 13px;
              color: #0d162a;
              line-height: 1.25rem;
              text-align: center;
              font-style: normal;
              margin-top: 0.25rem;
            }
          }
          .line {
            margin: 0 4px;
            width: 0.0625rem;
            height: 2.9375rem;
            border: 0.0625rem solid #cccccc;
          }
        }
      }
    }

    .right-tool-list {
      position: absolute;
      right: 0;
      top: 70px;
      background-color: #fff;
      z-index: 999;
      text-align: center;
      box-shadow: rgba(0, 0, 0, 0.05) 0rem 0rem 0rem 0.0625rem;

      .tool-item {
        cursor: pointer;
        padding: 6px 10px;
        .name {
          font-family:
            PingFangSC,
            PingFang SC;
          font-weight: 400;
          font-size: 12px;
          color: #0d162a;
          text-align: center;
          font-style: normal;
        }
      }
    }
  }

  .input-box-class {
    position: absolute;
    border: 0.0625rem solid #00329f;
    padding: 0.25rem;
    outline: none;
  }

  .selection-area-btn {
    width: 5rem;
    margin-left: 0.25rem;
  }

  .svg-div {
    z-index: 99999;

    svg {
      cursor: crosshair;

      path.selected {
        stroke: blue;
      }
    }
  }

  .node-text {
    white-space: nowrap; /* 防止文字自动换行 */
  }
}

.tool-active {
  background: #ebf1ff;
  border-bottom: 0.125rem solid #1677ff;
}

.right-tool-active {
  background: #ebf1ff !important;
}

.drop-down-menu {
  .drop-down-menu-item {
    cursor: pointer;
    display: flex;
    flex-direction: column;
    align-items: center;
    width: 100%;
    padding: 0.6875rem 0;
    font-family:
      PingFangSC,
      PingFang SC;
    font-weight: 400;
    font-size: 0.875rem;
    color: #0d162a;
    line-height: 1.25rem;
    text-align: left;
    font-style: normal;
    border-bottom: 0.0625rem solid #e9e9e9;
  }
}

.sort-num {
  position: absolute;
  font-size: 0.75rem;
  user-select: none;
  color: #000;
}
.sort-num-location {
  position: absolute;
  font-size: 0.75rem;
  user-select: none;
  color: #000;
}
.actual-location {
  position: fixed;
  bottom: 0.625rem;
  right: 1rem;
  background-color: #f48924;
  color: #fff;
  padding: 0.375rem;
  border-radius: 0.125rem;
  font-size: 0.875rem;
  user-select: none;
}

.search-select {
  padding: 0 20px;
  .search-icon {
    widows: 20px;
    height: 20px;
  }
}

// 美化滚动条
::-webkit-scrollbar {
  width: 10px;
  height: 10px;
}

::-webkit-scrollbar-track {
  width: 6px;
  background: rgba(#101f1c, 0.1);
  -webkit-border-radius: 2em;
  -moz-border-radius: 2em;
  border-radius: 2em;
}

::-webkit-scrollbar-thumb {
  background-color: rgba(144, 147, 153, 0.7);
  background-clip: padding-box;
  min-height: 28px;
  -webkit-border-radius: 2em;
  -moz-border-radius: 2em;
  border-radius: 2em;
  transition: background-color 0.3s;
  cursor: pointer;
}

::-webkit-scrollbar-thumb:hover {
  background-color: rgba(144, 147, 153, 0.3);
}

.ref-line {
  position: absolute;
  background-color: rgb(219, 89, 110);
  z-index: 9999;
}
.v-line {
  width: 1px;
}
.h-line {
  height: 1px;
}

// 右击菜单样式
.context-menu {
  position: fixed;
  background: #fff;
  border: 1px solid #e4e7ed;
  border-radius: 4px;
  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
  z-index: 99999;
  min-width: 120px;
  padding: 4px 0;

  .context-menu-item {
    z-index: 99999;
    padding: 8px 16px;
    cursor: pointer;
    font-size: 14px;
    color: #606266;
    transition: background-color 0.2s;

    &:hover {
      background-color: #f5f7fa;
      color: #409eff;
    }

    &:not(:last-child) {
      border-bottom: 1px solid #f0f0f0;
    }
  }
}
</style>
