<template>
  <div class="warp">
    <div class="warp-title">{{ templateInfo.name || "无" }}</div>
    <div class="warp-nav">
      <div class="warp-nav-tab">
        <div class="warp-nav-tab-list">
          <div
            v-for="item in navList"
            @click="setStepClick(item.index)"
            :title="item.title"
          >
            <el-icon
              :size="20"
              style="margin: 2px 4px 0 0"
              :color="'#2D8CF0'"
              v-if="item.index < 3"
            >
              <SuccessFilled v-if="showImage(item.index)" />
              <CircleClose v-else />
            </el-icon>
            <span :class="item.index === navListIndex ? 'active' : ''">{{
              item.title
            }}</span>
            <img src="@/assets/image/icon1.png" alt="" v-if="item.index !== 5" />
          </div>
        </div>
        <div
          class="warp-nav-tab-delete"
          title="清空全部痕迹"
          @click="clearFabricGroup"
          v-if="!isLockStatus"
        >
          <img src="@/assets/image/icon2.png" alt="" />
          <span>清空全部痕迹</span>
        </div>
      </div>
      <div class="warp-nav-title">
        <div class="warp-nav-title-text">
          <span>{{ placeholderText }}</span>
          <span style="margin: 0 10px" v-if="operationText">|</span>
          <span
            style="color: red; font-weight: bold; border-bottom: 1px red solid"
            v-if="operationText"
            >{{ operationText }}</span
          >
          <el-radio-group v-model="otherType" v-if="navListIndex === 5">
            <el-radio :value="1">缺考标记</el-radio>
            <el-radio :value="2">屏蔽区标记</el-radio>
            <el-radio :value="3">反面文字识别定位</el-radio>
          </el-radio-group>
        </div>
        <div class="warp-nav-title-operation">
          <div class="warp-nav-title-operation-btn" v-if="templateInfo.imgSrc.length > 1">
            <div :class="{ active: imgSrcIndex === 0 }" @click="changeImgSrcIndex(0)">
              正面
            </div>
            <div :class="{ active: imgSrcIndex === 1 }" @click="changeImgSrcIndex(1)">
              反面
            </div>
          </div>
          <div class="warp-nav-title-operation-other" v-if="navListIndex !== 1">
            <div @click="changeZoom()">恢复大小</div>
          </div>
          <div class="warp-nav-title-operation-icon" v-if="navListIndex !== 1">
            <img
              src="@/assets/image/icon4.png"
              alt=""
              @click="changeZoom(true)"
              title="放大画布"
            />
            <img
              src="@/assets/image/icon3.png"
              alt=""
              @click="changeZoom(false)"
              title="缩小画布"
            />
          </div>
        </div>
      </div>
    </div>
    <div class="warp-content">
      <div class="warp-content-left">
        <div class="warp-content-left-box" @click="contextmenu = null">
          <div :class="templateInfo.type" @contextmenu.prevent="onContextmenu">
            <div id="zoom-div">
              <div
                class="border-gray"
                v-if="setBoderLine()"
                :style="setBoderLine()"
              ></div>
              <div class="menu-button" v-if="contextmenu">
                <div title="置顶" @click="changeZindex(true)">置顶</div>
                <div title="置底" @click="changeZindex(false)">置底</div>
              </div>
              <img
                :src="templateInfo.imgSrc[imgSrcIndex]"
                alt=""
                id="picture-img"
                style="user-select: none"
                @load="canvasLoad"
              />
              <canvas id="picture"></canvas>
            </div>
          </div>
        </div>
      </div>
      <div class="warp-content-right">
        <div class="warp-content-right-info">
          <div class="warp-content-right-info-fb">
            定位点：<span>{{ templateInfo.hasPosition ? "有" : "无" }}</span>
          </div>
          <div class="warp-content-right-info-fb">
            纸张类型：<span>{{ templateInfo.type }}</span>
          </div>
          <div class="warp-content-right-info-fb">试题列表：</div>
          <div class="warp-content-right-info-table">
            <el-table
              :data="templateInfo.questionList"
              stripe
              border
              size="small"
              max-height="540px"
            >
              <el-table-column label="题型" width="60px" align="center">
                <template #default="scope">
                  {{ scope.row.fabricType === 3 ? "客观题" : "主观题" }}
                </template>
              </el-table-column>
              <el-table-column label="题号" width="60px" align="center">
                <template #default="scope">
                  {{ scope.row.customFrom.questionBefore }}-{{
                    scope.row.customFrom.questionAfter
                  }}
                </template>
              </el-table-column>
              <el-table-column label="排列" width="50px" align="center">
                <template #default="scope">
                  <span v-if="scope.row.customFrom.range === 0">横向</span>
                  <span v-else-if="scope.row.customFrom.range === 1">竖向</span>
                  <span v-else>无</span>
                </template>
              </el-table-column>
              <el-table-column label="题量" width="50px" align="center">
                <template #default="scope">
                  {{
                    scope.row.fabricType === 3
                      ? scope.row.customFrom.questionNumber
                      : scope.row.customFrom.questionAfter -
                        scope.row.customFrom.questionBefore +
                        1
                  }}
                </template>
              </el-table-column>
              <el-table-column label="每题分数" align="center">
                <template #default="scope">
                  {{ scope.row.customFrom.score }}
                </template>
              </el-table-column>
            </el-table>
          </div>
          <div class="warp-content-right-info-btn">
            <el-button
              type="primary"
              size="large"
              @click="setStepClick(navListIndex + 1)"
              v-if="navListIndex !== 5"
              >下一步</el-button
            >
            <el-button type="primary" size="large" @click="btnClick('save')"
              >保 存</el-button
            >
            <el-button
              type="primary"
              size="large"
              @click="btnClick('lock')"
              :disabled="isLockStatus || isLockDisabled"
              >{{ isLockStatus ? "已锁定" : "锁 定" }}</el-button
            >
          </div>
        </div>
      </div>
    </div>
    <!-- 框选学科 -->
    <el-drawer
      :modal="false"
      v-model="drawerList.subjectDrawer"
      title="请选择出学科字样(没有学科选择其他字样)"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
      class="drawer-custom-style"
    >
      <div class="drawer-custom-style-body">
        <div class="drawer-custom-style-body-content">
          <el-check-tag
            :checked="item.checked"
            type="primary"
            v-for="item in textList"
            @change="item.checked = !item.checked"
            style="margin: 0 10px 10px 0"
          >
            {{ item.text }}
          </el-check-tag>
        </div>
        <div class="drawer-custom-style-body-footer">
          <el-button type="primary" @click="changeFabricInfo(true)">确认</el-button>
          <el-button @click="changeFabricInfo(false)">取消</el-button>
        </div>
      </div>
    </el-drawer>
    <!-- 框选考号-->
    <el-drawer
      :modal="false"
      v-model="drawerList.examIdDrawer"
      title="编辑属性"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
      class="drawer-custom-style"
    >
      <div class="drawer-custom-style-body">
        <div class="drawer-custom-style-body-content examId">
          <div>
            <span>考试类型：</span>
            <div>
              <el-check-tag
                type="primary"
                :checked="drawerForm.examId.examType === 1"
                style="margin: 0 10px 0 0"
                @change="changeExamType(1)"
                >填涂式</el-check-tag
              >
              <el-check-tag
                type="primary"
                @change="changeExamType(2)"
                :checked="drawerForm.examId.examType === 2"
                >条码式</el-check-tag
              >
            </div>
          </div>
          <div v-if="drawerForm.examId.examType === 1">
            <span>考号长度：</span>
            <div>
              <el-input-number
                :disabled="isLockStatus"
                :min="4"
                :max="12"
                v-model="drawerForm.examId.examIdLength"
                :step-strictly="true"
              />
            </div>
          </div>
          <!-- <div>
            暂时不需要
            <span>考号样式：</span>
            <div>
              <el-radio-group v-model="drawerForm.examId.examIdStyle">
                <el-radio :value="1">0 1 2 3 4 5 6 7 8 9</el-radio>
                <el-radio :value="2">1 2 3 4 5 6 7 8 9 0</el-radio>
              </el-radio-group>
            </div>
          </div> -->
          <div class="full-filling" v-if="drawerForm.examId.examType === 1">
            <span>示意图</span>
            <div>
              <div v-for="item in 10">
                <span v-for="row in drawerForm.examId.examIdLength" :name="row">{{
                  item - 1
                }}</span>
              </div>
            </div>
          </div>
          <div class="bar-code" v-else>
            <span>示意图</span>
            <div>
              <div>贴条形码区</div>
            </div>
          </div>
          <FabricSize
            :change="changeFabricPositionNumber"
            ref="FabricSizeRef"
            v-if="currentFabric"
          />
        </div>
        <div class="drawer-custom-style-body-footer">
          <el-button type="primary" @click="changeFabricInfo(true)">确认</el-button>
          <el-button @click="changeFabricInfo(false)">取消</el-button>
        </div>
      </div>
    </el-drawer>
    <!-- 框选客观题 -->
    <el-drawer
      :modal="false"
      v-model="drawerList.objectiveDrawer"
      title="编辑属性"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
      class="drawer-custom-style"
    >
      <div class="drawer-custom-style-body">
        <div class="drawer-custom-style-body-content objective">
          <div>
            <span>题型：</span>
            <div>
              <el-check-tag
                :checked="drawerForm.objective.questionTypeValue === item.value"
                type="primary"
                v-for="item in drawerForm.objective.questionTypeList"
                @change="changeObjectiveQuestionType(item)"
                style="margin: 0 10px 0 0"
              >
                {{ item.title }}
              </el-check-tag>
            </div>
          </div>
          <div>
            <span>排列：</span>
            <div>
              <el-radio-group
                v-model="drawerForm.objective.range"
                :disabled="isLockStatus"
              >
                <el-radio :value="0">横向</el-radio>
                <el-radio :value="1">竖向</el-radio>
              </el-radio-group>
            </div>
          </div>
          <div>
            <span>选项个数：</span>
            <div>
              <el-input-number
                style="width: 164px"
                v-model="drawerForm.objective.optionNumber"
                :min="2"
                :disabled="drawerForm.objective.questionTypeValue === 2 || isLockStatus"
                :step-strictly="true"
                :max="24"
              />
            </div>
          </div>
          <div>
            <span>题量：</span>
            <div>
              <el-input-number
                :disabled="isLockStatus"
                style="width: 164px"
                v-model="drawerForm.objective.questionNumber"
                :min="1"
                :step-strictly="true"
                @change="changeQuestion()"
              />
            </div>
          </div>
          <div>
            <span>题号：</span>
            <div>
              <el-input-number
                :disabled="isLockStatus"
                v-model="drawerForm.objective.questionBefore"
                :min="1"
                :controls="false"
                style="width: 60px"
                @input="changeQuestion($event)"
              />
              ——
              <el-input-number
                v-model="drawerForm.objective.questionAfter"
                :controls="false"
                disabled
                style="width: 60px"
              />
            </div>
          </div>
          <div>
            <span>每题分数：</span>
            <div>
              <el-input-number
                :disabled="isLockStatus"
                style="width: 164px"
                v-model="drawerForm.objective.score"
                :min="0"
                :precision="1"
                :step="0.1"
              />
            </div>
          </div>
          <div :class="drawerForm.objective.range === 0 ? 'syt-row' : 'syt-column'">
            <span>示意图</span>
            <div v-if="drawerForm.objective.questionBefore % 1 === 0">
              <div v-for="item in drawerForm.objective.questionNumber">
                <span>{{ item - 1 + drawerForm.objective.questionBefore }}</span>
                <span v-for="row in drawerForm.objective.optionNumber" :name="item">{{
                  drawerForm.objective.questionTypeValue === 2
                    ? ["T", "F"][row - 1]
                    : drawerForm.objective.optionEnglish[row - 1]
                }}</span>
              </div>
            </div>
            <div v-else>
              <div v-for="item in drawerForm.objective.questionNumber">
                <span>
                  {{ drawerForm.objective.questionBefore.toString().split(".")[0] }}
                  .{{
                    Number(drawerForm.objective.questionBefore.toString().split(".")[1]) +
                    (item - 1)
                  }}
                </span>
                <span v-for="row in drawerForm.objective.optionNumber" :name="item">{{
                  drawerForm.objective.questionTypeValue === 2
                    ? ["T", "F"][row - 1]
                    : drawerForm.objective.optionEnglish[row - 1]
                }}</span>
              </div>
            </div>
          </div>
          <FabricSize
            :change="changeFabricPositionNumber"
            ref="FabricSizeRef"
            v-if="currentFabric"
          />
        </div>
        <div class="drawer-custom-style-body-footer">
          <el-button
            type="primary"
            @click="changeFabricInfo(true)"
            :disabled="!drawerForm.objective.questionBefore"
            >确认</el-button
          >
          <el-button @click="changeFabricInfo(false)">取消</el-button>
        </div>
      </div>
    </el-drawer>
    <!-- 框选主观题 -->
    <el-drawer
      :modal="false"
      v-model="drawerList.subjectiveDrawer"
      title="编辑属性"
      :close-on-click-modal="false"
      :close-on-press-escape="false"
      :show-close="false"
      class="drawer-custom-style"
    >
      <div class="drawer-custom-style-body">
        <div class="drawer-custom-style-body-content subjective">
          <div>
            <span>题型：</span>
            <div>
              <el-check-tag
                :checked="drawerForm.subjective.questionTypeValue === item.value"
                type="primary"
                v-for="item in drawerForm.subjective.questionTypeList"
                @change="changeObjectiveQuestionType(item)"
                style="margin: 0 10px 0 0"
              >
                {{ item.title }}
              </el-check-tag>
            </div>
          </div>
          <div>
            <span>题号：</span>
            <div>
              <el-input-number
                v-model="drawerForm.subjective.questionBefore"
                :disabled="isLockStatus"
                :min="1"
                :controls="false"
                :step-strictly="true"
                style="width: 54px"
                @change="
                  drawerForm.subjective.questionAfter =
                    drawerForm.subjective.questionBefore
                "
              />
              ——
              <el-input-number
                v-model="drawerForm.subjective.questionAfter"
                :disabled="isLockStatus"
                :min="1"
                :controls="false"
                :step-strictly="true"
                style="width: 54px"
              />
            </div>
          </div>
          <div>
            <span>每题分数：</span>
            <div>
              <el-input-number
                v-model="drawerForm.subjective.score"
                :disabled="isLockStatus"
                style="width: 152px"
                :min="0"
                :precision="1"
                :step="0.1"
              />
            </div>
          </div>
          <FabricSize
            :change="changeFabricPositionNumber"
            ref="FabricSizeRef"
            v-if="currentFabric"
          />
        </div>
        <div class="drawer-custom-style-body-footer">
          <el-button
            type="primary"
            @click="changeFabricInfo(true)"
            :disabled="
              drawerForm.subjective.questionBefore >
                drawerForm.subjective.questionAfter ||
              !drawerForm.subjective.questionBefore ||
              !drawerForm.subjective.questionAfter
            "
            >确认</el-button
          >
          <el-button @click="changeFabricInfo(false)">取消</el-button>
        </div>
      </div>
    </el-drawer>
    <!-- 锁定 -->
    <LockList ref="LockListRef" :change="lock" />
  </div>
</template>
<script lang="ts" setup>
import { onMounted, reactive, ref, nextTick, watch } from "vue";
import { fabric } from "fabric";
import { createWorker } from "tesseract.js";
import html2canvas from "html2canvas";
import { GetViewSize, GetOcrDetail, PostSave, GetLock } from "@/api/Home";
import FabricSize from "@/pages/Home/components/FabricSize.vue";
import LockList from "@/pages/Home/components/LockList.vue";
import { useRouter } from "vue-router";
const router = useRouter();
const FabricSizeRef = ref<any>(null);
const LockListRef = ref<any>(null);
const firstPosition = ref<any>(null);
const contextmenu = ref<any>(null);
const navList = reactive<any[]>([
  {
    title: "框选定位点",
    index: 0,
  },
  {
    title: "框选学科",
    index: 1,
  },
  {
    title: "框选考号",
    index: 2,
  },
  {
    title: "框选客观题",
    index: 3,
  },
  {
    title: "框选主观题",
    index: 4,
  },
  {
    title: "框选其他标记(缺考、屏蔽区、反面定位点)",
    index: 5,
  },
]);
const navListIndex = ref<number>(0);
const placeholderText = ref<string>("");
const operationText = ref<string>("");
const scale = ref<number>(0);
const imgDom = ref<any>(null);
const otherType = ref<number>(1);
const textFabricList = ref<string[]>(["删除", "编辑属性"]);
const positionTitle = ref<string[]>(["左上角", "右上角", "左下角", "右下角"]);
const disabledMove = ref<number[]>([0, 1]);
const drawerList = ref<{
  subjectDrawer: boolean;
  examIdDrawer: boolean;
  objectiveDrawer: boolean;
  subjectiveDrawer: boolean;
}>({
  subjectDrawer: false,
  examIdDrawer: false,
  objectiveDrawer: false,
  subjectiveDrawer: false,
});
const drawerForm = reactive<any>({
  examId: {
    examType: 1,
    examIdLength: 8,
    examIdStyle: 1,
  },
  objective: {
    questionTypeList: [
      {
        title: "单选题",
        value: 0,
      },
      {
        title: "多选题",
        value: 1,
      },
      {
        title: "判断题",
        value: 2,
      },
    ],
    questionTypeValue: 0,
    optionNumber: 4,
    optionEnglish: [
      "A",
      "B",
      "C",
      "D",
      "E",
      "F",
      "G",
      "H",
      "I",
      "J",
      "K",
      "L",
      "M",
      "N",
      "O",
      "P",
      "Q",
      "R",
      "S",
      "T",
      "U",
      "V",
      "W",
      "X",
      "Y",
      "Z",
    ],
    questionNumber: 5,
    questionBefore: 1,
    questionAfter: 5,
    range: 0,
    score: 0,
  },
  subjective: {
    questionTypeList: [
      {
        title: "填空题",
        value: 0,
      },
      {
        title: "解答题",
        value: 1,
      },
      {
        title: "语文作文",
        value: 2,
      },
      {
        title: "英文作文",
        value: 3,
      },
    ],
    questionTypeValue: 0,
    questionBefore: 1,
    questionAfter: 1,
    score: 0,
  },
  fabricPosition: {
    top: 0,
    left: 0,
    width: 0,
    height: 0,
  },
});
const textList = ref<any[]>([]);
const imgSrcIndex = ref<number>(0);
const templateInfo = reactive<any>({
  name: "",
  hasPosition: true,
  type: "A3",
  imgSrc: [],
  headList: [
    {
      //定位点
      point: [],
    },
    {
      //学科
      point: [],
    },
    {
      //考号
      point: [],
    },
    {
      //客观题
      point: [],
    },
    {
      //主观题
      point: [],
    },
    {
      //其他
      point: [],
    },
  ],
  tailsList: [
    {
      //定位点
      point: [],
    },
    {
      //学科
      point: [],
    },
    {
      //考号
      point: [],
    },
    {
      //客观题
      point: [],
    },
    {
      //主观题
      point: [],
    },
    {
      //其他
      point: [],
    },
  ],
  questionList: [],
  otherData: [],
});
const isLockStatus = ref<boolean>(false);
const isLockDisabled = ref<boolean>(false);
const currentNavListIndex = ref<number>(3);
// 画布相关
const ctx = ref<any>(null);
const fabricGroup = ref<any>(null);
const fabricRect = ref<any>(null);
const currentFabric = ref<any>(null);

onMounted(() => {
  GetOcrDetail(router.currentRoute.value.query.mid as string).then((res: any) => {
    if (res.code === 200) {
      const { mainName, hasPosition, paperType, detail, isLock } = res.data;
      isLockStatus.value = isLock;
      templateInfo.name = mainName;
      templateInfo.hasPosition = hasPosition;
      templateInfo.type = paperType === 1 ? "A3" : "A4";
      templateInfo.otherData = detail.sort((a: any, b: any) => a.pageIndex - b.pageIndex);
      templateInfo.otherData.forEach((item: any, index: number) => {
        templateInfo.imgSrc.push(item.tempPicture64);
        if (item.tempData) {
          index === 0
            ? (templateInfo.headList = JSON.parse(JSON.parse(item.tempData).tempData))
            : (templateInfo.tailsList = JSON.parse(JSON.parse(item.tempData).tempData));
        }
      });
    }
  });
});

watch(
  () => templateInfo,
  (_new, _old) => {
    if (!_new.headList[2].point.length && !_new.tailsList[2].point.length) {
      isLockDisabled.value = true;
      currentNavListIndex.value = 2;
      return;
    }
    if (!_new.headList[1].point.length && !_new.tailsList[1].point.length) {
      isLockDisabled.value = true;
      currentNavListIndex.value = 1;
      return;
    }
    if (_new.hasPosition) {
      if (_new.imgSrc.length === 2) {
        if (_new.headList[0].point.length !== 4 || _new.tailsList[0].point.length !== 4) {
          isLockDisabled.value = true;
          currentNavListIndex.value = 0;
          return;
        }
      } else {
        if (_new.headList[0].point.length !== 4) {
          isLockDisabled.value = true;
          currentNavListIndex.value = 0;
          return;
        }
      }
    }
    isLockDisabled.value = false;
  },
  { deep: true }
);

// 图片加载完成
const canvasLoad = (): void => {
  if (!ctx.value) {
    initCanvas();
    navListIndex.value = templateInfo.hasPosition ? 0 : 1; //无定位点默认起始下标为1
    if (!templateInfo.hasPosition) {
      //无定位点去除第一项
      navList.splice(0, 1);
    }
    setQuestionList(); //回显试题
    showFabricData(); //回显画布
    setStepClick(currentNavListIndex.value, false); //设置步骤
    setNavTitle(); //设置导航栏文字
  }
};

// 初始化画布并添加事件
const initCanvas = (): void => {
  imgDom.value = document.getElementById("picture-img");
  ctx.value = new fabric.Canvas("picture", {
    width: imgDom.value.clientWidth,
    height: imgDom.value.clientHeight,
  });
  ctx.value.on("mouse:down", (e: any) => {
    if (isLockStatus.value && !e.target) {
      ElMessage.error("已锁定，无法新增！");
      return;
    }
    const templateInfoItem = getTemplateInfo(true);
    if (templateInfoItem[0].point.length === 4) {
      if (isMoveOutView()) {
        if (
          getCanvasMousePos(e.e).x < getCalculateRectangleSize().left ||
          getCanvasMousePos(e.e).y < getCalculateRectangleSize().top ||
          getCanvasMousePos(e.e).x >
            getCalculateRectangleSize().width + getCalculateRectangleSize().left ||
          getCanvasMousePos(e.e).y >
            getCalculateRectangleSize().height + getCalculateRectangleSize().top
        ) {
          ElMessage.error("鼠标位置不在画布内！");
          return;
        }
      }
    }
    if (e.target) {
      setStepClick(e.target.get("fabricType"));
    }
    canvasMouseDown(e.e, e.target, e.subTargets);
  });
  ctx.value.on("mouse:move", (e: any) => {
    canvasMouseMove(e.e);
  });
  ctx.value.on("mouse:up", () => {
    canvasMouseUp();
  });
  ctx.value.on("selection:created", (e: any) => {
    if (e.selected.length > 1) {
      ctx.value.discardActiveObject();
    }
  });
  ctx.value.on("object:moving", (_e: any) => {
    if (disabledMove.value.includes(navListIndex.value)) {
      return;
    }
    isDragOutCanvas(isMoveOutView());
  });
  document.addEventListener("keydown", (e: any) => {
    const keyCodeList = ["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight"];
    if (ctx.value.getActiveObject()) {
      const allDrawerStatus = [
        drawerList.value.subjectDrawer,
        drawerList.value.examIdDrawer,
        drawerList.value.objectiveDrawer,
        drawerList.value.subjectiveDrawer,
      ];
      if (keyCodeList.includes(e.key) && !allDrawerStatus.find((item: boolean) => item)) {
        e.preventDefault();
        let obj = ctx.value.getActiveObject();
        switch (e.key) {
          case "ArrowUp":
            obj.set("top", obj.get("top") - 1);
            break;
          case "ArrowDown":
            obj.set("top", obj.get("top") + 1);
            break;
          case "ArrowLeft":
            obj.set("left", obj.get("left") - 1);
            break;
          case "ArrowRight":
            obj.set("left", obj.get("left") + 1);
            break;
        }
        isDragOutCanvas(isMoveOutView());
        saveFabricData();
      }
    }
  });
};

// 获取坐标
const getCanvasMousePos = (e: MouseEvent): { x: number; y: number } => {
  const rect = imgDom.value.getBoundingClientRect();
  const x = ((e.clientX - rect.left) * imgDom.value.width) / rect.width;
  const y = ((e.clientY - rect.top) * imgDom.value.height) / rect.height;
  return {
    x: x,
    y: y,
  };
};

// 鼠标按下
const canvasMouseDown = (e: MouseEvent, target: any, subTargets: any): void => {
  const mousePos = getCanvasMousePos(e);
  firstPosition.value = mousePos;
  if ((!target || !subTargets.length) && isDisabledDraw()) {
    fabricRect.value = new fabric.Rect({
      left: mousePos.x,
      top: mousePos.y,
      stroke: "red",
      strokeWidth: 1,
      fill: "transparent",
      fabricId: cteatedNumber(),
    });
  }
  // 鼠标按下显示功能文字
  ctx.value
    .getObjects()
    .filter((item: any) => item.fabricType === navListIndex.value)
    .forEach((item: any) => {
      item.forEachObject((row: any) => {
        if (textFabricList.value.includes(row.get("text"))) {
          if (target) {
            const itemTarget = target
              .getObjects()
              .find((i: any) => i.fabricId === row.fabricId);
            if (itemTarget && row.fabricId === itemTarget.fabricId) {
              if (isLockStatus.value && itemTarget.get("text") === "删除") {
                row.set({ visible: false });
              } else {
                itemTarget.set({ visible: true });
              }
            } else {
              row.set({ visible: false });
            }
          } else {
            row.set({ visible: false });
          }
        }
      });
    });
};

// 鼠标移动
const canvasMouseMove = (e: MouseEvent): void => {
  const mousePos = getCanvasMousePos(e);
  if (fabricRect.value) {
    const width = mousePos.x - fabricRect.value.left;
    const height = mousePos.y - fabricRect.value.top;
    const obj = {
      height:
        fabricRect.value.top + height >= imgDom.value.clientHeight
          ? imgDom.value.clientHeight - fabricRect.value.top
          : height,
      width:
        fabricRect.value.left + width >= imgDom.value.clientWidth
          ? imgDom.value.clientWidth - fabricRect.value.left
          : width,
    };
    if (isMoveOutView()) {
      const templateInfoItem = getTemplateInfo(true);
      if (templateInfoItem[0].point.length === 4) {
        if (
          obj.width + firstPosition.value.x - getCalculateRectangleSize().left >
          getCalculateRectangleSize().width
        ) {
          obj.width =
            getCalculateRectangleSize().width -
            firstPosition.value.x +
            getCalculateRectangleSize().left;
        }
        if (
          obj.height + firstPosition.value.y - getCalculateRectangleSize().top >
          getCalculateRectangleSize().height
        ) {
          obj.height =
            getCalculateRectangleSize().height -
            firstPosition.value.y +
            getCalculateRectangleSize().top;
        }
      }
    }
    fabricRect.value.set({
      ...obj,
    });
  }
};

// 鼠标抬起
const canvasMouseUp = async (): Promise<void> => {
  // 绘制图形
  function addFabric(_type: string, data: any, itemFabricRect?: any) {
    const group = [
      fabricRect.value,
      ...(itemFabricRect ? [itemFabricRect, ...createFabric()] : createFabric()),
    ];
    fabricGroup.value = new fabric.Group(group, {
      hasControls: false,
      subTargetCheck: true,
      borderColor: "transparent",
      selectionBackgroundColor: "red",
      fabricType: navListIndex.value,
      fabricId: cteatedNumber(),
    }).on("mousemove", (e: any) => {
      if (
        !disabledMove.value.includes(navListIndex.value) &&
        e.target.fabricType === navListIndex.value
      ) {
        e.target.set("selectable", e.subTargets.length ? true : false);
      }
    });
    // 功能添加文字事件
    fabricGroup.value.forEachObject((obj: any) => {
      if (textFabricList.value.includes(obj.get("text"))) {
        obj.on("mousedown", (e: any) => {
          addEventClick(e);
        });
        // 隐藏功能文字
        obj.set({ visible: false });
      }
    });
    fabricGroup.value.set("customFrom", data);
    ctx.value.add(fabricGroup.value);
  }
  if (fabricRect.value && fabricRect.value.get("width") > 10) {
    const obj = {
      left: fabricRect.value.get("left"),
      top: fabricRect.value.get("top"),
      width: fabricRect.value.get("width"),
      height: fabricRect.value.get("height"),
    };
    if (navListIndex.value === 0) {
      await GetViewSize({
        X: Math.round(fabricRect.value.get("left")),
        Y: Math.round(fabricRect.value.get("top")),
        Width: Math.round(fabricRect.value.get("width")),
        Height: Math.round(fabricRect.value.get("height")),
        ImageUrl: templateInfo.imgSrc[imgSrcIndex.value],
        ImageWidth: imgDom.value.clientWidth,
        ImageHeight: imgDom.value.clientHeight,
      }).then((res) => {
        if (res.code === 200) {
          const rectTitle = ["右上角", "右下角"];
          const rectFooterTitle = ["左下角", "右下角"];
          if (rectTitle.includes(getCurrentText())) {
            res.data.x = res.data.x + res.data.width;
          }
          if (rectFooterTitle.includes(getCurrentText())) {
            res.data.y = res.data.y - res.data.height;
          }
          addFabric(
            "rect",
            { ...obj, title: getTextTitle(), rectPosition: res.data },
            new fabric.Rect({
              left: rectTitle.includes(getCurrentText())
                ? res.data.x - res.data.width
                : res.data.x,
              top: rectFooterTitle.includes(getCurrentText())
                ? res.data.y + res.data.height
                : res.data.y,
              width: res.data.width,
              height: res.data.height,
              stroke: "red",
              strokeWidth: 1,
              fill: "transparent",
              fabricId: cteatedNumber(),
              rectPosition: res.data,
            })
          );
        }
      });
    } else if (navListIndex.value === 5) {
      addFabric("other", { type: otherType.value, ...obj });
    } else {
      addFabric("default", {
        ...obj,
      });
    }
  }
  //保存数据
  saveFabricData();
  // 各个类型框选过后的操作
  boxSelectAfter();
};

// 功能文字添加事件
const addEventClick = (obj: any) => {
  switch (obj.subTargets[0].get("text")) {
    case "删除":
      changeFabricCanvas(obj).delete();
      break;
    case "编辑属性":
      changeFabricCanvas(obj).edit();
      break;
  }
  saveFabricData();
};

// 画布按钮操作
const changeFabricCanvas = (obj: any): { delete: () => void; edit: () => void } => {
  return {
    delete: () => {
      ctx.value.remove(obj.target);
      ctx.value.discardActiveObject();
    },
    edit: () => {
      currentFabric.value = obj;
      changeDrawer(true);
    },
  };
};

//清除画布
const clearFabricGroup = (): void => {
  ElMessageBox.confirm("确定清除所有框选的信息吗？", "提示", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      templateInfo.tailsList.forEach((item: any) => {
        item.point = [];
      });
      templateInfo.headList.forEach((item: any) => {
        item.point = [];
      });
      templateInfo.questionList = [];
      ctx.value.clear();
      navListIndex.value = 0;
      setNavTitle();
      ElMessage.success("清除成功！");
    })
    .catch(() => {});
};

// 生成其他图形文字辅助函数
const createText = (
  text: string,
  left: number,
  top: number,
  backgroundColor: string = "rgba(0,0,0,0.5)",
  fill: string = "white",
  fontSize: number = 16,
  fontWeight: string = "normal"
): any => {
  return new fabric.Text(text, {
    left,
    top,
    fontSize,
    fill,
    backgroundColor,
    fontWeight,
    fabricId: cteatedNumber(),
  });
};

// 获取当前标题
const getCurrentText = (): string => {
  const fabricItem = getFabricItem();
  const fabricPositionTitle = [] as string[];
  fabricItem.forEach((row: any) => {
    row.forEachObject((item: any) => {
      if (positionTitle.value.includes(item.get("text"))) {
        fabricPositionTitle.push(item.get("text"));
      }
    });
  });
  return findFirstMissingValue(fabricPositionTitle);
};

// 设置标题
const getTextTitle = (bool?: boolean): string => {
  switch (navListIndex.value) {
    case 0:
      if (bool) {
        ElMessage.success(
          `${imgSrcIndex.value ? "反面" : "正面"}${getCurrentText()}框选完成`
        );
      }
      return getCurrentText();
    case 1:
      return "标题";
    case 5:
      let msg = "";
      switch (otherType.value) {
        case 1:
          msg = "缺考";
          break;
        case 2:
          msg = "屏蔽区";
          break;
        case 3:
          msg = "反面定位点";
          break;
      }
      return msg;
    default:
      return "无";
  }
};

// 生成其他图形文字
const createFabric = (): any[] => {
  const titleText = createText(
    getTextTitle(true),
    fabricRect.value.left,
    fabricRect.value.top - (navListIndex.value ? 20 : 0),
    "rgba(255,0,0,0.5)"
  );
  const deleteText = createText(
    "删除",
    fabricRect.value.left + (navListIndex.value ? fabricRect.value.width - 32 : 0),
    fabricRect.value.top +
      fabricRect.value.height -
      (navListIndex.value ? fabricRect.value.height + 20 : 20)
  );
  const editText = createText(
    "编辑属性",
    fabricRect.value.left,
    fabricRect.value.top + fabricRect.value.height
  );
  switch (navListIndex.value) {
    case 0:
      return [titleText, deleteText];
    case 1:
    case 5:
      return [titleText, deleteText];
    case 3:
      return [deleteText, editText];
    default:
      return [titleText, deleteText, editText];
  }
};
// 切换步骤
const setStepClick = (index: number, isElMessage: boolean = true): void => {
  if (navListIndex.value === index) {
    return;
  }
  let msg = "";

  function checkTemplateInfo(index: number): void {
    if (index > navListIndex.value) {
      if (index >= 2) {
        if (index >= 3) {
          if (
            !templateInfo.headList[2].point.length &&
            !templateInfo.tailsList[2].point.length
          ) {
            msg = "考号未框选！";
          }
        }
        if (
          !templateInfo.headList[1].point.length &&
          !templateInfo.tailsList[1].point.length
        ) {
          msg = "学科未框选！";
        }
      }
      templateInfo.hasPosition &&
        (index === 1 || index === 2 || index === 3 || index === 4 || index === 5
          ? templateInfo.headList[0].point.length !== 4
            ? ((msg = "正面定位点未框选完成！"), changeImgSrcIndex(0))
            : templateInfo.tailsList[0].point.length !== 4 &&
              templateInfo.imgSrc.length > 1
            ? ((msg = "反面定位点未框选完成！"), changeImgSrcIndex(1))
            : null
          : null);
    }
  }

  checkTemplateInfo(index);

  if (msg) {
    if (isElMessage) ElMessage.error(msg);
    return;
  }
  if (index === 1) {
    changeZoom();
  }
  navListIndex.value = index;
  changeImgSrcIndex(0);
  ctx.value.discardActiveObject();
  ctx.value.renderAll();
};

//保存图形
const saveFabricData = (): void => {
  const templateInfoItem = getTemplateInfo();
  templateInfoItem.point = [];
  const ctxObjects = ctx.value
    .getObjects()
    .filter((item: any) => item.fabricType === navListIndex.value);
  ctxObjects.forEach((item: any, index: number) => {
    templateInfoItem.point.push({
      left: item.left,
      top: item.top,
      width: item.width,
      height: item.height,
      fabricType: item.fabricType,
      customFrom: item.customFrom,
      children: [],
    });
    item.forEachObject((child: any) => {
      const childItem = {
        left: child.left,
        top: child.top,
        width: child.width,
        height: child.height,
      };
      if (child.get("type") === "text") {
        templateInfoItem.point[index].children.push({
          type: "text",
          text: child.get("text"),
          backgroundColor: child.get("backgroundColor"),
          fill: child.get("fill"),
          fontSize: child.get("fontSize"),
          fontWeight: child.get("fontWeight"),
          ...childItem,
        });
      } else {
        templateInfoItem.point[index].children.push({
          type: "rect",
          rectPosition: child.get("rectPosition"),
          ...childItem,
        });
      }
    });
  });

  // 设置题目列表
  setQuestionList();
  //设置导航栏文字
  setNavTitle();
  // 禁用其他图形
  disabledFabricData();
  // 更新画布
  ctx.value.renderAll();
};

// 禁用其他图形
const disabledFabricData = (): void => {
  const ctxObjects = ctx.value.getObjects();
  ctxObjects.forEach((item: any) => {
    item.set("hoverCursor", "default");
    if (disabledMove.value.includes(item.fabricType)) {
      item.forEachObject((row: any) => {
        item.set("selectable", false);
        if (row.get("type") === "rect") {
          row.set("hoverCursor", "not-allowed");
        } else {
          if (textFabricList.value.includes(row.get("text"))) {
            row.set(
              "hoverCursor",
              item.fabricType === navListIndex.value ? "pointer" : "not-allowed"
            );
          } else {
            row.set(
              "hoverCursor",
              item.fabricType === navListIndex.value ? "default" : "not-allowed"
            );
          }
        }
      });
    } else {
      item.forEachObject((row: any) => {
        item.set("selectable", item.fabricType === navListIndex.value);
        if (row.get("type") === "rect") {
          row.set(
            "hoverCursor",
            item.fabricType === navListIndex.value ? "move" : "not-allowed"
          );
        } else {
          if (textFabricList.value.includes(row.get("text"))) {
            row.set(
              "hoverCursor",
              item.fabricType === navListIndex.value ? "pointer" : "not-allowed"
            );
          } else {
            row.set(
              "hoverCursor",
              item.fabricType === navListIndex.value ? "default" : "not-allowed"
            );
          }
        }
      });
    }
  });
};

// 各个类型框选过后的操作
const boxSelectAfter = async (): Promise<void> => {
  if (fabricRect.value && fabricRect.value.get("width") > 10) {
    async function ocrImage(): Promise<void> {
      const loading = ElLoading.service({
        lock: true,
        text: `识别中...`,
        background: "rgba(0, 0, 0, 0.7)",
        body: true,
      });
      const canvas = await html2canvas(imgDom.value, {
        scale: 4,
        width: fabricRect.value.width,
        height: fabricRect.value.height,
        x: fabricGroup.value.left,
        y: fabricGroup.value.top + 20,
        useCORS: true,
      });
      const img = new Image();
      img.src = canvas.toDataURL("image/png");
      img.onload = async () => {
        const worker = (await createWorker("chi_sim", 1, {
          workerPath: "/src/assets/js/worker.min.js",
          corePath: "/src/assets/js/tesseract-core.wasm.js",
        })) as any;
        await worker.recognize(img).then((res: any) => {
          textList.value = res.data.words.map((item: any) => {
            return {
              ...item,
              checked: false,
            };
          });
          changeDrawer(true);
          loading.close();
        });
      };
    }
    switch (navListIndex.value) {
      case 0:
        // 定位点
        isShowElMessageBox();
        break;
      case 1:
        // 学科
        ocrImage();
        break;
      case 2:
      case 3:
      case 4:
        // 考号 客观题 主观题
        changeDrawer(true);
        break;
      case 5:
        // 其他
        if (otherType.value === 3) {
          ocrImage();
        }
        break;
    }
  }
  fabricRect.value = null;
  fabricGroup.value = null;
};

// 是否禁止禁止绘制
const isDisabledDraw = (): boolean => {
  let isDisabledDrawStatus = true;
  let msg = "";
  const fabricItem = getFabricItem();
  switch (navListIndex.value) {
    case 0:
      // 定位点
      isDisabledDrawStatus = fabricItem.length < 4;
      if (!isDisabledDrawStatus)
        msg = `${imgSrcIndex.value === 0 ? "正面" : "反面"}定位点已框选完毕！`;
      break;
    case 1:
      // 学科
      isDisabledDrawStatus = fabricItem.length === 0;
      if (!isDisabledDrawStatus) msg = "学科已框选！";
      break;
    case 2:
      // 考号
      isDisabledDrawStatus = fabricItem.length !== 2;
      if (!isDisabledDrawStatus) msg = "考号最多只能框选两个！";
      break;
    case 5:
      // 其他
      const hasQueKao = [] as any;
      if (otherType.value === 1) {
        fabricItem.forEach((item: any) => {
          let row = item.getObjects().filter((obj: any) => obj.get("text") === "缺考");
          if (row) {
            hasQueKao.push(...row);
          }
        });
        isDisabledDrawStatus = hasQueKao.length === 0;
        if (!isDisabledDrawStatus) msg = "缺考标记已框选！";
      }

      if (otherType.value === 3) {
        if (imgSrcIndex.value === 1) {
          fabricItem.forEach((item: any) => {
            let row = item
              .getObjects()
              .filter(
                (obj: any) =>
                  obj.get("text") && obj.get("text").indexOf("反面定位点") >= 0
              );
            if (row.length) {
              hasQueKao.push(...row);
            }
          });
          isDisabledDrawStatus = hasQueKao.length === 0;
          if (!isDisabledDrawStatus) msg = "反面定位点标记已框选！";
        } else {
          msg = "反面定位点只能在反面框选！";
          isDisabledDrawStatus = false;
        }
      }
      break;
  }
  if (msg) {
    ElMessage.success(msg);
  }
  return isDisabledDrawStatus;
};

// 是否需要弹窗提示
const isShowElMessageBox = () => {
  const fabricItem = getFabricItem();
  switch (navListIndex.value) {
    case 0:
      if (templateInfo.imgSrc.length > 1) {
        if (
          templateInfo.headList[navListIndex.value].point.length === 4 &&
          templateInfo.tailsList[navListIndex.value].point.length === 4
        ) {
          ElMessageBox.confirm("定位点已框选完毕，是否切换到框选学科？", "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning",
          })
            .then(() => {
              setStepClick(1);
            })
            .catch(() => {});
        } else if (
          templateInfo.headList[navListIndex.value].point.length === 4 &&
          imgSrcIndex.value !== 1
        ) {
          ElMessageBox.confirm("正面已框选完毕,是否切换到反面？", "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning",
          })
            .then(() => {
              changeImgSrcIndex(1);
            })
            .catch(() => {});
        } else if (
          templateInfo.tailsList[navListIndex.value].point.length === 4 &&
          imgSrcIndex.value !== 0
        ) {
          ElMessageBox.confirm("反面已框选完毕,是否切换到正面？", "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning",
          })
            .then(() => {
              changeImgSrcIndex(0);
            })
            .catch(() => {});
        }
      } else {
        if (templateInfo.headList[navListIndex.value].point.length === 4) {
          ElMessageBox.confirm("定位点已框选完毕，是否切换到框选学科？", "提示", {
            confirmButtonText: "确定",
            cancelButtonText: "取消",
            type: "warning",
          })
            .then(() => {
              setStepClick(1);
            })
            .catch(() => {});
        }
      }
      break;
    case 1:
      if (fabricItem.length !== 0) {
        ElMessageBox.confirm("学科已框选完毕,是否切换到框选考号？", "提示", {
          confirmButtonText: "确定",
          cancelButtonText: "取消",
          type: "warning",
        })
          .then(() => {
            setStepClick(2);
          })
          .catch(() => {});
      }
      break;
  }
};
// 回显画布
const showFabricData = (): void => {
  const templateInfoItem = getTemplateInfo(true);
  function createFabricChildren(children: any) {
    const textArr = [] as any;
    const rectArr = [] as any;
    children.forEach((item: any) => {
      if (item.type === "rect") {
        rectArr.push(
          new fabric.Rect({
            left: item.left,
            top: item.top,
            width: item.width,
            height: item.height,
            stroke: "red",
            strokeWidth: 1,
            fill: "transparent",
            fabricId: cteatedNumber(),
            rectPosition: item.rectPosition,
          })
        );
      } else {
        textArr.push(
          new fabric.Text(item.text, {
            left: item.left,
            top: item.top,
            width: item.width,
            height: item.height,
            backgroundColor: item.backgroundColor,
            fill: item.fill || "white",
            fontSize: item.fontSize || 16,
            fabricId: cteatedNumber(),
            visible: !textFabricList.value.includes(item.text),
            fontWeight: item.fontWeight || "normal",
          }).on("mousedown", (e: any) => {
            if (textFabricList.value.includes(item.text)) {
              addEventClick(e);
            }
          })
        );
      }
    });
    return [...rectArr, ...textArr];
  }
  templateInfoItem.forEach((row: any) => {
    row.point.forEach((item: any) => {
      ctx.value.add(
        new fabric.Group([...createFabricChildren(item.children)], {
          hasControls: false,
          subTargetCheck: true,
          borderColor: "transparent",
          selectionBackgroundColor: "rgba(255,0,0,0.1)",
          fabricType: item.fabricType,
          left: item.left,
          top: item.top,
          width: item.width,
          height: item.height,
          fabricId: cteatedNumber(),
          customFrom: item.customFrom,
        }).on("mousemove", (e: any) => {
          if (
            !disabledMove.value.includes(navListIndex.value) &&
            e.target.fabricType === navListIndex.value
          ) {
            e.target.set("selectable", e.subTargets.length ? true : false);
          }
        })
      );
    });
  });
  // 禁用其他图形
  disabledFabricData();
};

// 获取某一项所有图形
const getFabricItem = (): any => {
  return ctx.value.getObjects().filter((item: any) => {
    return item.fabricType === navListIndex.value;
  });
};

// 设置图片正反面
const changeImgSrcIndex = (index: number): void => {
  imgSrcIndex.value = index;
  ctx.value.clear();
  showFabricData();
  setNavTitle();
};

// 获取当前是哪面的数据
const getTemplateInfo = (bool?: boolean): any => {
  if (bool) {
    return imgSrcIndex.value === 0 ? templateInfo.headList : templateInfo.tailsList;
  } else {
    return imgSrcIndex.value === 0
      ? templateInfo.headList[navListIndex.value]
      : templateInfo.tailsList[navListIndex.value];
  }
};

// 放大缩小画布
const changeZoom = (bool?: boolean): void => {
  const dom = document.getElementById("zoom-div") as HTMLElement;
  if (typeof bool === "boolean") {
    bool ? (scale.value += 0.1) : (scale.value -= 0.1);
  } else {
    ElMessage.success("画布恢复默认值成功");
    scale.value = 1;
  }
  scale.value = Math.max(scale.value, 1);
  dom.style.transform = `scale(${scale.value})`;
};

// 生成随机数
const cteatedNumber = (): string => {
  return `${Math.floor(Math.random() * 100000000)}+${+new Date()}`;
};

// 修改尺寸数字
const changeFabricPositionNumber = (value: number | boolean, str: string): void => {
  let number = 0;
  if (typeof value === "boolean") {
    number = JSON.parse(JSON.stringify(currentFabric.value.target.get(str)));
    if (value) {
      number--;
    } else {
      number++;
    }
  } else {
    number = value;
  }
  changeFabricPosition(number, str);
};

// 编辑尺寸
const changeFabricPosition = (value: number, str: string): void => {
  if (value) {
    let maxRect = currentFabric.value.target
      .getObjects()
      .filter((item: any) => item.get("type") === "rect")
      .reduce(function (currentMax: any, obj: any) {
        return obj.get("width") > currentMax.get("width") ? obj : currentMax;
      }, currentFabric.value.target.getObjects()[0]);
    let textList = currentFabric.value.target
      .getObjects()
      .filter((item: any) => item.get("type") == "text");
    function changeTextPosition(): void {
      for (let index = 0; index < textList.length; index++) {
        switch (textList[index].get("text")) {
          case "删除":
            if (str === "width") {
              textList[index].set("left", value / 2 - textList[index].get("width"));
            } else {
              textList[index].set(
                "top",
                value / 2 - value - textList[index].get("height")
              );
            }
            break;
          case "编辑属性":
            if (str === "width") {
              textList[index].set("left", value / 2 - value);
            } else {
              textList[index].set("top", value / 2);
            }
            break;
          default:
            if (str === "width") {
              textList[index].set("left", value / 2 - value);
            } else {
              textList[index].set(
                "top",
                value / 2 - value - textList[index].get("height")
              );
            }
            break;
        }
      }
    }
    switch (str) {
      case "top":
      case "left":
        currentFabric.value.target.set(str, value);
        break;
      case "width":
      case "height":
        maxRect.set(str, value);
        if (str === "width") {
          maxRect.set("left", value / 2 - value);
          currentFabric.value.target.set(str, value);
        } else {
          maxRect.set("top", value / 2 - value);
          currentFabric.value.target.set(str, value + textList[0].height * 2);
        }
        let rectWidth = 0,
          rectHeight = 0;
        switch (navListIndex.value) {
          // 考号
          case 2:
            let examIdLength = currentFabric.value.target.customFrom.examIdLength;
            rectWidth = maxRect.get("width") / examIdLength;
            rectHeight = maxRect.get("height") / 10;
            changeTextPosition();
            deleteSmallRect();
            for (let index = 0; index < examIdLength * 10; index++) {
              currentFabric.value.target.add(
                new fabric.Rect({
                  left: maxRect.get("left") + (index % examIdLength) * rectWidth,
                  top:
                    maxRect.get("top") +
                    getOffsetTop(examIdLength, 10, index, rectHeight),
                  stroke: "red",
                  strokeWidth: 1,
                  fill: "transparent",
                  fabricId: cteatedNumber(),
                  width: rectWidth,
                  height: rectHeight,
                })
              );
            }
            break;
          case 3:
            // 客观题
            let questionNumber = currentFabric.value.target.customFrom.questionNumber;
            let optionNumber = currentFabric.value.target.customFrom.optionNumber;
            let range = currentFabric.value.target.customFrom.range;
            rectWidth = maxRect.get("width") / (range ? questionNumber : optionNumber);
            rectHeight = maxRect.get("height") / (range ? optionNumber : questionNumber);
            changeTextPosition();
            deleteSmallRect();
            for (let index = 0; index < optionNumber * questionNumber; index++) {
              currentFabric.value.target.add(
                new fabric.Rect({
                  left:
                    maxRect.get("left") +
                    (index % (range ? questionNumber : optionNumber)) * rectWidth,
                  top:
                    maxRect.get("top") +
                    getOffsetTop(
                      range ? questionNumber : optionNumber,
                      range ? optionNumber : questionNumber,
                      index,
                      rectHeight
                    ),
                  stroke: "red",
                  strokeWidth: 1,
                  fill: "transparent",
                  fabricId: cteatedNumber(),
                  width: rectWidth,
                  height: rectHeight,
                })
              );
            }
            // 修改题号位置
            const questionNumberList = currentFabric.value.target
              .getObjects()
              .filter(
                (item: any) =>
                  item.get("type") === "text" &&
                  !textFabricList.value.includes(item.get("text"))
              );
            questionNumberList.forEach((item: any, index: number) => {
              if (range) {
                item.set({
                  left:
                    maxRect.get("left") +
                    rectWidth * index +
                    rectWidth / 2 -
                    (questionNumber + 1),
                  top: maxRect.get("top") + 4,
                });
              } else {
                item.set({
                  top:
                    maxRect.get("top") +
                    rectHeight * index +
                    rectHeight / 2 -
                    (questionNumber + 1),
                  left: maxRect.get("left") + 4,
                });
              }
            });
            break;
          case 4:
            // 主观题
            changeTextPosition();
            break;
        }
        break;
    }
    isDragOutCanvas(isMoveOutView());
  }
  saveFabricData();
};

// 编辑尺寸状态
const changeFabricPositionDraw = (): void => {
  nextTick(() => {
    let maxRect = currentFabric.value.target
      .getObjects()
      .filter((item: any) => item.get("type") === "rect")
      .reduce(function (currentMax: any, obj: any) {
        return obj.get("width") > currentMax.get("width") ? obj : currentMax;
      }, currentFabric.value.target.getObjects()[0]);
    drawerForm.fabricPosition.top = currentFabric.value.target.get("top");
    drawerForm.fabricPosition.left = currentFabric.value.target.get("left");
    drawerForm.fabricPosition.width = maxRect.get("width");
    drawerForm.fabricPosition.height = maxRect.get("height");
    FabricSizeRef.value.setSize(
      drawerForm.fabricPosition,
      imgDom.value.width,
      imgDom.value.height
    );
  });
};

// 修改drawer
const changeDrawer = (bool: boolean): void => {
  let item = "" as any;
  if (currentFabric.value) {
    item = currentFabric.value.target;
  }
  switch (navListIndex.value) {
    case 1:
      // 学科
      drawerList.value.subjectDrawer = bool;
      break;
    case 2:
      // 考号
      if (bool && item) {
        drawerForm.examId.examType = item.customFrom.fabricAttribute;
        if (drawerForm.examId.examType === 1) {
          drawerForm.examId.examIdLength = item.customFrom.examIdLength;
        }
      }
      drawerList.value.examIdDrawer = bool;
      if (bool && item) {
        changeFabricPositionDraw();
      }
      break;
    case 3:
      // 客观题
      if (bool && item) {
        drawerForm.objective.questionTypeValue = item.customFrom.fabricAttribute;
        drawerForm.objective.questionBefore = item.customFrom.questionBefore;
        drawerForm.objective.questionAfter = item.customFrom.questionAfter;
        drawerForm.objective.range = item.customFrom.range;
        drawerForm.objective.questionNumber = item.customFrom.questionNumber;
        drawerForm.objective.optionNumber = item.customFrom.optionNumber;
        drawerForm.objective.score = item.customFrom.score;
      } else {
        drawerForm.objective.questionBefore = getQuestionNumer();
        drawerForm.objective.questionAfter =
          drawerForm.objective.questionBefore + drawerForm.objective.questionNumber - 1;
      }
      drawerList.value.objectiveDrawer = bool;
      if (bool && item) {
        changeFabricPositionDraw();
      }
      break;
    case 4:
      // 主观题
      if (bool && currentFabric.value) {
        drawerForm.subjective.questionTypeValue = item.customFrom.fabricAttribute;
        drawerForm.subjective.questionBefore = item.customFrom.questionBefore;
        drawerForm.subjective.questionAfter = item.customFrom.questionAfter;
        drawerForm.subjective.score = item.customFrom.score;
      } else {
        drawerForm.subjective.questionBefore = getQuestionNumer();
        drawerForm.subjective.questionAfter = drawerForm.subjective.questionBefore;
      }
      drawerList.value.subjectiveDrawer = bool;
      if (bool && currentFabric.value) {
        changeFabricPositionDraw();
      }
      break;
    case 5:
      // 其他
      drawerList.value.subjectDrawer = bool;
      break;
  }
};

// 修改图形
const changeFabricInfo = (bool: boolean): void => {
  const fabricItem = currentFabric.value
    ? currentFabric.value.target
    : getFabricItem().reduce(function (currentMax: any, obj: any) {
        return obj.fabricId.split("+")[1] > currentMax.fabricId.split("+")[1]
          ? obj
          : currentMax;
      }, getFabricItem()[0]);
  if (fabricItem) {
    const textTitle = fabricItem
      .getObjects()
      .find(
        (item: any) =>
          item.get("text") && !textFabricList.value.includes(item.get("text"))
      );
    const obj = {
      left: fabricItem.get("customFrom").left,
      top: fabricItem.get("customFrom").top,
      width: fabricItem.get("customFrom").width,
      height: fabricItem.get("customFrom").height,
    };
    if (bool) {
      switch (navListIndex.value) {
        case 1:
          // 学科
          if (textList.value.filter((item) => item.checked).length) {
            textTitle.set({
              text: textList.value
                .filter((item) => item.checked)
                .map((item) => item.text)
                .join(""),
            });
            fabricItem.set({
              customFrom: {
                title: textList.value
                  .filter((item) => item.checked)
                  .map((item) => item.text)
                  .join(""),
                ...obj,
              },
            });
          } else {
            ElMessage.error("请选择文字！");
            return;
          }
          isShowElMessageBox();
          break;
        case 2:
          // 考号
          function isRepeat() {
            let tx = 0,
              tm = 0,
              msg;
            getFabricItem().forEach((item: any) => {
              if (item.getObjects().find((row: any) => row.get("text") === "填涂式")) {
                tx++;
              }
              if (item.getObjects().find((row: any) => row.get("text") === "条码式")) {
                tm++;
              }
            });
            if (drawerForm.examId.examType === 1 && tx) {
              msg = "填涂式已存在";
            }
            if (drawerForm.examId.examType === 2 && tm) {
              msg = "条码式已存在";
            }
            return msg;
          }
          if (currentFabric.value) {
            if (
              currentFabric.value.target.customFrom.fabricAttribute !==
              drawerForm.examId.examType
            ) {
              let errorMsg = isRepeat();
              if (errorMsg) {
                ElMessage.error(errorMsg);
                return;
              }
            }
          } else {
            let errorMsg = isRepeat();
            if (errorMsg) {
              ElMessage.error(errorMsg);
              return;
            }
          }
          function createdExamIdRect(): void {
            let rect = fabricItem
              .getObjects()
              .find((row: any) => row.get("type") === "rect");
            let rectWidth = rect.get("width") / drawerForm.examId.examIdLength;
            let rectHeight = rect.get("height") / 10;
            for (let index = 0; index < drawerForm.examId.examIdLength * 10; index++) {
              fabricItem.add(
                new fabric.Rect({
                  left:
                    rect.get("left") +
                    (index % drawerForm.examId.examIdLength) * rectWidth,
                  top:
                    rect.get("top") +
                    getOffsetTop(drawerForm.examId.examIdLength, 10, index, rectHeight),
                  stroke: "red",
                  strokeWidth: 1,
                  fill: "transparent",
                  fabricId: cteatedNumber(),
                  width: rectWidth,
                  height: rectHeight,
                })
              );
            }
          }
          deleteSmallRect();
          if (drawerForm.examId.examType === 1) {
            createdExamIdRect();
          }
          textTitle.set({
            text: drawerForm.examId.examType === 1 ? "填涂式" : "条码式",
          });
          fabricItem.set({
            customFrom: {
              fabricAttribute: drawerForm.examId.examType,
              examIdLength: drawerForm.examId.examIdLength,
              ...obj,
            },
          });
          isShowElMessageBox();
          break;
        case 3:
          // 客观题
          // 生成小格子
          function createdObjectiveRect(
            rect: any,
            rectWidth: number,
            rectHeight: number
          ): void {
            for (
              let index = 0;
              index <
              drawerForm.objective.optionNumber * drawerForm.objective.questionNumber;
              index++
            ) {
              fabricItem.add(
                new fabric.Rect({
                  left:
                    rect.get("left") +
                    (index %
                      (drawerForm.objective.range
                        ? drawerForm.objective.questionNumber
                        : drawerForm.objective.optionNumber)) *
                      rectWidth,
                  top:
                    rect.get("top") +
                    getOffsetTop(
                      drawerForm.objective.range
                        ? drawerForm.objective.questionNumber
                        : drawerForm.objective.optionNumber,
                      drawerForm.objective.range
                        ? drawerForm.objective.optionNumber
                        : drawerForm.objective.questionNumber,
                      index,
                      rectHeight
                    ),
                  stroke: "red",
                  strokeWidth: 1,
                  fill: "transparent",
                  fabricId: cteatedNumber(),
                  width: rectWidth,
                  height: rectHeight,
                })
              );
            }
          }
          if (isQuestionNumerRepeat()) {
            ElMessage.error("题目重复！");
            return;
          } else {
            let rect = fabricItem
              .getObjects()
              .find((row: any) => row.get("type") === "rect");
            let rectWidth =
              rect.get("width") /
              (drawerForm.objective.range
                ? drawerForm.objective.questionNumber
                : drawerForm.objective.optionNumber);
            let rectHeight =
              rect.get("height") /
              (drawerForm.objective.range
                ? drawerForm.objective.optionNumber
                : drawerForm.objective.questionNumber);
            deleteSmallRect(navListIndex.value);
            createdObjectiveRect(rect, rectWidth, rectHeight);
            fabricItem.set({
              customFrom: {
                fabricAttribute: drawerForm.objective.questionTypeValue,
                range: drawerForm.objective.range,
                optionNumber: drawerForm.objective.optionNumber,
                questionNumber: drawerForm.objective.questionNumber,
                questionBefore: drawerForm.objective.questionBefore,
                questionAfter: drawerForm.objective.questionAfter,
                score: drawerForm.objective.score,
                ...obj,
              },
            });
            objectiveNumber(fabricItem, rect, rectWidth, rectHeight);
          }

          break;
        case 4:
          // 主观题
          if (isQuestionNumerRepeat()) {
            ElMessageBox.confirm("题目重复，是否设置为分页题目。", "提示", {
              confirmButtonText: "确定",
              cancelButtonText: "取消",
              type: "warning",
            })
              .then(() => {
                let numberIndex = getIndex();
                textTitle.set({
                  text: `${drawerForm.subjective.questionBefore}-${drawerForm.subjective.questionAfter}-${numberIndex}`,
                });
                fabricItem.set({
                  customFrom: {
                    fabricAttribute: drawerForm.objective.questionTypeValue,
                    questionBefore: drawerForm.subjective.questionBefore,
                    questionAfter: drawerForm.subjective.questionAfter,
                    score: drawerForm.subjective.score,
                    isPage: numberIndex,
                    ...obj,
                  },
                });
                saveFabricData();
                changeDrawer(false);
              })
              .catch(() => {});
            return;
          } else {
            textTitle.set({
              text: `${drawerForm.subjective.questionBefore}-${drawerForm.subjective.questionAfter}`,
            });
            fabricItem.set({
              customFrom: {
                fabricAttribute: drawerForm.objective.questionTypeValue,
                questionBefore: drawerForm.subjective.questionBefore,
                questionAfter: drawerForm.subjective.questionAfter,
                score: drawerForm.subjective.score,
                isPage: -1,
                ...obj,
              },
            });
          }
          break;
        case 5:
          // 其他
          if (textList.value.filter((item) => item.checked).length) {
            textTitle.set({
              text: `${textList.value
                .filter((item) => item.checked)
                .map((item) => item.text)
                .join("")}-反面定位点`,
            });
            fabricItem.set({
              customFrom: {
                title: textList.value
                  .filter((item) => item.checked)
                  .map((item) => item.text)
                  .join(""),
                ...obj,
              },
            });
          } else {
            ElMessage.error("请选择文字！");
            return;
          }
          break;
      }
      changeDrawer(false);
    } else {
      if (!currentFabric.value) {
        ctx.value.remove(fabricItem);
      }
      changeDrawer(false);
    }
    saveFabricData();
    changeImgSrcIndex(imgSrcIndex.value);
  }
  currentFabric.value = null;
};

// 获取距离
const getOffsetTop = (
  xNumber: number,
  yNumber: number,
  index: number,
  top: number
): number => {
  let offsetTop = 0;
  const allNumber = [...Array(xNumber * yNumber).keys()];
  Array.from({ length: Math.ceil(allNumber.length / xNumber) }, (_v: any, i) =>
    allNumber.slice(i * xNumber, i * xNumber + xNumber)
  ).forEach((item: number[], itemIndex: number) => {
    if (item.includes(index)) {
      offsetTop = itemIndex;
    }
  });
  return offsetTop * top;
};

// 客观题生成题号
const objectiveNumber = (
  fabricItem: any,
  rect: any,
  rectWidth: number,
  rectHeight: number
): void => {
  // 边距
  const obj = {
    fontSize: 12,
    fill: "black",
    fontWeight: "normal",
    fabricId: cteatedNumber(),
    backgroundColor: "transparent",
  };
  if (drawerForm.objective.range) {
    // 竖向
    for (let index = 0; index < drawerForm.objective.questionNumber; index++) {
      let textNumber = 0 as any;
      if (drawerForm.objective.questionBefore % 1 === 0) {
        textNumber = drawerForm.objective.questionBefore++;
      } else {
        let before = drawerForm.objective.questionBefore.toString().split(".")[0] * 1;
        let after = drawerForm.objective.questionBefore.toString().split(".")[1] * 1;
        textNumber = `${before}.${after + index}`;
      }
      fabricItem.add(
        new fabric.Text(`${textNumber}`, {
          left:
            rect.get("left") +
            rectWidth * index +
            rectWidth / 2 -
            (drawerForm.objective.questionNumber + 1),
          top: rect.get("top") + 4,
          ...obj,
        })
      );
    }
  } else {
    // 横向
    for (let index = 0; index < drawerForm.objective.questionNumber; index++) {
      let textNumber = 0 as any;
      if (drawerForm.objective.questionBefore % 1 === 0) {
        textNumber = drawerForm.objective.questionBefore++;
      } else {
        let before = drawerForm.objective.questionBefore.toString().split(".")[0] * 1;
        let after = drawerForm.objective.questionBefore.toString().split(".")[1] * 1;
        textNumber = `${before}.${after + index}`;
      }
      fabricItem.add(
        new fabric.Text(`${textNumber}`, {
          left: rect.get("left") + 4,
          top:
            rect.get("top") +
            rectHeight * index +
            rectHeight / 2 -
            (drawerForm.objective.questionNumber + 1),
          ...obj,
        })
      );
    }
  }
};

// 清空小矩形
const deleteSmallRect = (index?: number): void => {
  if (currentFabric.value) {
    const maxRect = currentFabric.value.target
      .getObjects()
      .reduce(function (currentMax: any, obj: any) {
        return obj.get("width") > currentMax.get("width") ? obj : currentMax;
      }, currentFabric.value.target.getObjects()[0]);
    const deleteRectArr = currentFabric.value.target
      .getObjects()
      .filter(
        (item: any) =>
          item.get("type") === "rect" && item.get("fabricId") !== maxRect.get("fabricId")
      );
    currentFabric.value.target.remove(...deleteRectArr);
    switch (index) {
      case 3:
        // 客观题删除题号
        const allDelText = currentFabric.value.target
          .getObjects()
          .filter(
            (item: any) =>
              item.get("type") === "text" &&
              !textFabricList.value.includes(item.get("text"))
          );
        currentFabric.value.target.remove(...allDelText);
        break;
    }
  }
};

const getIndex = (): number => {
  function findFirstNonSequentialFromOne(arr: number[]) {
    arr.sort((a: number, b: number) => a - b);
    if (arr[0] !== 1) {
      return 1;
    }
    let expected = 1;
    for (let i = 0; i < arr.length; i++) {
      if (arr[i] === expected) {
        expected++;
      } else if (arr[i] > expected) {
        return expected;
      }
    }

    return null;
  }
  let numberList = getAllQuestionNumerPage().map((item: any) => {
    return item.customFrom.isPage;
  });
  let index = 1 as number | null;
  if (findFirstNonSequentialFromOne(numberList)) {
    index = findFirstNonSequentialFromOne(numberList);
  } else {
    index = numberList.length ? numberList[numberList.length - 1] + 1 : 1;
  }
  return index || 1;
};

// 获取所有主观题(必须是分页题)
const getAllQuestionNumerPage = (): any => {
  let allQuestion = [] as any;
  function appendFabricQuestions(list: any, index: number): void {
    allQuestion.push(
      ...list[index].point.filter(
        (row: any) => row.customFrom && row.customFrom.isPage >= 0
      )
    );
  }
  appendFabricQuestions(templateInfo.headList, 4);
  appendFabricQuestions(templateInfo.tailsList, 4);
  return allQuestion;
};

// 获取所有题目(不包含分页题)
const getAllQuestionNumer = (): any => {
  let allQuestion = [] as any;
  function appendFabricQuestions(list: any, index: number): void {
    allQuestion.push(
      ...list[index].point.filter(
        (row: any) =>
          row.customFrom && (!row.customFrom.isPage || row.customFrom.isPage === -1)
      )
    );
  }
  appendFabricQuestions(templateInfo.headList, 3);
  appendFabricQuestions(templateInfo.headList, 4);
  appendFabricQuestions(templateInfo.tailsList, 3);
  appendFabricQuestions(templateInfo.tailsList, 4);
  return allQuestion;
};

// 获取当前最大题号
const getQuestionNumer = (): number => {
  let allQuestionNumer = getAllQuestionNumer().filter((item: any) => {
    return (item.customFrom.questionBefore * 1) % 1 === 0;
  });
  return allQuestionNumer.length
    ? Math.max(
        ...allQuestionNumer.map((item: any) => item.customFrom.questionAfter * 1)
      ) + 1
    : 1;
};

// 判断客观题主观题题目是否重复
const isQuestionNumerRepeat = (): boolean => {
  let allQuestionNumer = getAllQuestionNumer().map((item: any) => {
    return {
      start: item.customFrom.questionBefore * 1,
      end: item.customFrom.questionAfter * 1,
    };
  });

  // 题号是否重叠
  function hasOverlap(ranges: any): boolean {
    function isOverlapping(range1: any, range2: any) {
      const { start: start1, end: end1 } = range1;
      const { start: start2, end: end2 } = range2;
      return Math.max(start1, start2) <= Math.min(end1, end2);
    }
    for (let i = 0; i < ranges.length; i++) {
      for (let j = i + 1; j < ranges.length; j++) {
        if (isOverlapping(ranges[i], ranges[j])) {
          return true;
        }
      }
    }
    return false;
  }

  // 题号是否重复
  function hasIdenticalObjects(arrayOfObjects: any): boolean {
    const objectHashes = new Map();
    for (const obj of arrayOfObjects) {
      const jsonString = JSON.stringify(obj);
      if (objectHashes.has(jsonString)) {
        return true;
      }
      objectHashes.set(jsonString, true);
    }
    return false;
  }

  if (currentFabric.value) {
    let item = currentFabric.value.target;
    let itemQuestionNumerIndex = allQuestionNumer.findIndex(
      (row: any) =>
        row.start === item.customFrom.questionBefore &&
        row.end === item.customFrom.questionAfter
    );
    allQuestionNumer.splice(itemQuestionNumerIndex, 2, {
      start:
        navListIndex.value === 3
          ? drawerForm.objective.questionBefore
          : drawerForm.subjective.questionBefore,
      end:
        navListIndex.value === 3
          ? drawerForm.objective.questionAfter
          : drawerForm.subjective.questionAfter,
    });
  } else {
    allQuestionNumer.push({
      start:
        navListIndex.value === 3
          ? drawerForm.objective.questionBefore
          : drawerForm.subjective.questionBefore,
      end:
        navListIndex.value === 3
          ? drawerForm.objective.questionAfter
          : drawerForm.subjective.questionAfter,
    });
  }

  if (hasOverlap(allQuestionNumer) || hasIdenticalObjects(allQuestionNumer)) {
    return true;
  }
  return false;
};

// 修改考试类型
const changeExamType = (type: number): void => {
  if (isLockStatus.value) {
    ElMessage.error("已锁定，无法修改！");
    return;
  }
  drawerForm.examId.examType = type;
};

// 修改默认题号
const changeQuestion = (e?: any): void => {
  if (typeof e === "number") {
    if (e % 1 === 0) {
      drawerForm.objective.questionAfter =
        (e || 1) + drawerForm.objective.questionNumber - 1;
    } else {
      let decimalPointBeforeNumber = Number(e.toString().split(".")[1]);
      let decimalPointAfterNumber = Number(e.toString().split(".")[0]);
      drawerForm.objective.questionAfter = Number(
        decimalPointAfterNumber +
          "." +
          (decimalPointBeforeNumber + drawerForm.objective.questionNumber - 1)
      );
    }
  } else {
    if (drawerForm.objective.questionBefore % 1 === 0) {
      drawerForm.objective.questionAfter =
        drawerForm.objective.questionBefore + drawerForm.objective.questionNumber - 1;
    } else {
      let decimalPointBeforeNumber = Number(
        drawerForm.objective.questionBefore.toString().split(".")[1]
      );
      let decimalPointAfterNumber = Number(
        drawerForm.objective.questionBefore.toString().split(".")[0]
      );
      drawerForm.objective.questionAfter = Number(
        decimalPointAfterNumber +
          "." +
          (decimalPointBeforeNumber + drawerForm.objective.questionNumber - 1)
      );
    }
  }
};

// 修改客观题题型
const changeObjectiveQuestionType = (item: { value: number; text: string }): void => {
  if (isLockStatus.value) {
    ElMessage.error("已锁定，无法修改！");
    return;
  }
  if (navListIndex.value === 3) {
    drawerForm.objective.questionTypeValue = item.value;
    drawerForm.objective.optionNumber =
      item.value === 2 ? 2 : drawerForm.objective.optionNumber;
  } else {
    drawerForm.subjective.questionTypeValue = item.value;
  }
};

// 设置导航栏文字
const setNavTitle = (): void => {
  const fabricItem = getFabricItem();
  switch (navListIndex.value) {
    case 0:
      const fabricPositionTitle = [] as string[];
      fabricItem.forEach((row: any) => {
        row.forEachObject((item: any) => {
          if (positionTitle.value.includes(item.get("text"))) {
            fabricPositionTitle.push(item.get("text"));
          }
        });
      });
      let str = findFirstMissingValue(fabricPositionTitle),
        msg = "";
      placeholderText.value = `提示：请确保框选区域略大于定位点黑色图块，每面均需框选。`;
      if (templateInfo.imgSrc.length > 1) {
        if (
          templateInfo.headList[navListIndex.value].point.length === 4 &&
          templateInfo.tailsList[navListIndex.value].point.length === 4
        ) {
          msg = "定位点已框选完毕。";
        }
      } else {
        if (templateInfo.headList[navListIndex.value].point.length === 4) {
          msg = "定位点已框选完毕。";
        }
      }
      operationText.value = str
        ? `当前需框选：${imgSrcIndex.value ? "反面" : "正面"}${str}。`
        : msg
        ? msg
        : `${imgSrcIndex.value ? "反面" : "正面"}定位点已框选。`;
      break;
    case 1:
      placeholderText.value = `提示：请框选一行较长的文字，每面均需框选。`;
      operationText.value = `${
        !fabricItem.length ? "当前需框选：学科。" : "学科已框选。"
      }`;
      break;
    case 2:
      placeholderText.value = `提示：请框选完整的考号区域，仅正面需要框选。`;
      operationText.value = `${
        !fabricItem.length ? "当前需框选：考号。" : "考号已框选。"
      }`;
      break;
    case 3:
      placeholderText.value = `提示：请框选题号;不同小块(一般为5题)，不同题型分开框选。`;
      operationText.value = ``;
      break;
    case 4:
      placeholderText.value = `提示：请确保框选区域略大于作答区域。`;
      operationText.value = ``;
      break;
    case 5:
      placeholderText.value = ``;
      operationText.value = ``;
      break;
  }
};

// 获取定位胆标题
const findFirstMissingValue = (arr: string[]): string => {
  const setArr2 = new Set(arr);
  for (const value of positionTitle.value) {
    if (!setArr2.has(value)) {
      return value;
    }
  }
  return "";
};

// 按钮状态
const showImage = (index: number): boolean => {
  const headList = templateInfo.headList[index].point.length;
  const tailsList = templateInfo.tailsList[index].point.length;
  switch (index) {
    case 0:
      if (templateInfo.imgSrc.length > 1) {
        if (headList === 4 && tailsList === 4) {
          return true;
        }
      } else {
        if (headList === 4) {
          return true;
        }
      }
      break;
    case 1:
    case 2:
      if (headList > 0 || tailsList > 0) {
        return true;
      }
      break;
  }
  return false;
};

// 获取画布允许拖拽的范围
const getCalculateRectangleSize = () => {
  function calculateRectangleSize(pointA: any, pointB: any, pointC: any): any {
    // 定义点的坐标
    let x1 = Math.min(pointA.x, pointB.x);
    let x2 = Math.max(pointA.x, pointB.x);
    let y1 = Math.min(pointA.y, pointC.y);
    let y2 = Math.max(pointA.y, pointC.y);
    // 计算矩形的宽度和高度
    let width = x2 - x1;
    let height = y2 - y1 + 20;
    return { width, height, left: pointA.x, top: pointA.y };
  }
  const templateInfoItem = getTemplateInfo(true);
  const rectPositionArr = [] as any;
  templateInfoItem[0].point.forEach((item: any) => {
    let rectPosition = item.children.find((row: any) => {
      return row.rectPosition;
    }).rectPosition;
    let textPosition = item.children.find((row: any) => {
      return positionTitle.value.includes(row.text);
    }).text;
    if (rectPosition) {
      rectPositionArr.push({
        rectPosition,
        textPosition,
      });
    }
  });

  const sortedArray = [] as any;
  for (let index = 0; index < positionTitle.value.length; index++) {
    const item = positionTitle.value[index];
    const rectPostItem = rectPositionArr.find(
      (item1: any) => item1.textPosition === item
    );
    if (rectPostItem) {
      sortedArray.push(rectPostItem);
    }
  }
  return calculateRectangleSize(
    sortedArray[0].rectPosition,
    sortedArray[1].rectPosition,
    sortedArray[2].rectPosition
  );
};

// 设置边框线
const setBoderLine = (): any => {
  const templateInfoItem = getTemplateInfo(true);
  if (templateInfoItem[0].point.length === 4) {
    const size = getCalculateRectangleSize();
    return {
      border: "1px dashed red",
      width: `${size.width}px`,
      height: `${size.height}px`,
      top: `${size.top}px`,
      left: `${size.left}px`,
      position: "absolute",
      zIndex: 1,
    };
  }
  return false;
};

// 右键添加菜单
const onContextmenu = (e: any): void => {
  const target = ctx.value.findTarget(e);
  const mousePos = getCanvasMousePos(e);
  contextmenu.value = target;
  if (target) {
    nextTick(() => {
      const contextmenuDom = document.getElementsByClassName(
        "menu-button"
      )[0] as HTMLElement;
      contextmenuDom.style.left = `${mousePos.x}px`;
      contextmenuDom.style.top = `${mousePos.y}px`;
    });
  }
};

// 设置层级
const changeZindex = (bool: boolean): void => {
  if (bool) {
    ctx.value.bringToFront(contextmenu.value);
  } else {
    ctx.value.sendToBack(contextmenu.value);
  }
  ctx.value.discardActiveObject();
  ctx.value.renderAll();
  contextmenu.value = null;
  ElMessage.success("设置成功");
};

// 是否可以移出视图
const isMoveOutView = (): boolean => {
  let row = [],
    bool = true;
  if (ctx.value.getActiveObject()) {
    row = ctx.value
      .getActiveObject()
      .getObjects()
      .filter(
        (obj: any) => obj.get("text") && obj.get("text").indexOf("反面定位点") >= 0
      );
  }
  if (row.length || (otherType.value === 3 && !ctx.value.getActiveObject())) {
    bool = false;
  } else {
    bool = true;
  }
  return (
    (![0, 1, 5].includes(navListIndex.value) || (navListIndex.value === 5 && bool)) &&
    templateInfo.hasPosition
  );
};

// 图形移动限制操作
const isDragOutCanvas = (bool: boolean): void => {
  let obj = ctx.value.getActiveObject();
  if (bool) {
    const restrictedArea = {
      ...getCalculateRectangleSize(),
    };
    const newX = Math.max(
      restrictedArea.left,
      Math.min(obj.left, restrictedArea.left + restrictedArea.width - obj.width)
    );
    const newY = Math.max(
      restrictedArea.top,
      Math.min(obj.top, restrictedArea.top + restrictedArea.height - obj.height)
    );
    if (newX !== obj.left || newY !== obj.top) {
      obj.set({ left: newX, top: newY });
    }
  } else {
    if (obj.left < 0) {
      obj.left = 0;
    }
    if (obj.top < 0) {
      obj.top = 0;
    }
    if (obj.left + obj.width > 0 + ctx.value.getWidth()) {
      obj.left = ctx.value.getWidth() - obj.width;
    }
    if (obj.top + obj.height > 0 + ctx.value.getHeight()) {
      obj.top = ctx.value.getHeight() - obj.height;
    }
  }
};

// 设置题目列表
const setQuestionList = (): void => {
  templateInfo.questionList = [];
  let allQuestion = [] as any;
  function appendFabricQuestions(list: any, index: number): void {
    allQuestion.push(
      ...list[index].point.filter(
        (row: any) => row.customFrom && (row.fabricType === 3 || row.fabricType === 4)
      )
    );
  }
  appendFabricQuestions(templateInfo.headList, 3);
  appendFabricQuestions(templateInfo.headList, 4);
  appendFabricQuestions(templateInfo.tailsList, 3);
  appendFabricQuestions(templateInfo.tailsList, 4);
  templateInfo.questionList.push(...allQuestion);
};

// 修改数据
const changeData = (): any => {
  function divideByTwoProperties(array: any, property1Key: string, property2Key: string) {
    const categoriesMap = new Map();
    array.forEach((item: any) => {
      const value1 = item[property1Key];
      const value2 = item[property2Key];
      const key = `${value1}-${value2}`;

      if (!categoriesMap.has(key)) {
        categoriesMap.set(key, []);
      }
      categoriesMap.get(key).push(item);
    });

    return Array.from(categoriesMap.values());
  }
  const data = [] as any;
  templateInfo.otherData.forEach((item: any, index: number) => {
    data.push({
      id: item.id,
      index: index + 1,
      width: imgDom.value.clientWidth,
      height: imgDom.value.clientHeight,
      tempData: index === 0 ? templateInfo.headList : templateInfo.tailsList,
      from: {
        positioningPoint: {},
        subjectPoint: {},
        examIDPoint: {},
        objectiveQuestionsPoint: {},
        subjectiveQuestionsPoint: {},
        otherPoint: {},
      },
    });
  });
  data.forEach((item: any) => {
    item.tempData[0].point = item.tempData[0].point.sort((a: any, b: any) => {
      return (
        positionTitle.value.indexOf(a.customFrom.title) -
        positionTitle.value.indexOf(b.customFrom.title)
      );
    });
    item.from.positioningPoint = item.tempData[0].point.map((row: any) => {
      return row.customFrom;
    });
    item.from.subjectPoint =
      item.tempData[1].point.length == 1 ? item.tempData[1].point[0].customFrom : {};
    item.from.examIDPoint = item.tempData[2].point.map((row: any) => {
      return {
        ...row.customFrom,
        width: row.width,
        height: row.height - 36.16,
        top: row.top + 19,
        left: row.left,
      };
    });
    item.from.objectiveQuestionsPoint = item.tempData[3].point.map((row: any) => {
      return {
        ...row.customFrom,
        width: row.width,
        height: row.height - 36.16,
        top: row.top + 19,
        left: row.left,
      };
    });
    item.from.subjectiveQuestionsPoint = item.tempData[4].point.map((row: any) => {
      return {
        ...row.customFrom,
        width: row.width,
        height: row.height - 36.16,
        top: row.top + 19,
        left: row.left,
      };
    });
    item.from.otherPoint = item.tempData[5].point.map((row: any) => {
      return {
        ...row.customFrom,
        width: row.width,
        height: row.height - 36.16 / 2,
        top: row.top + 19,
        left: row.left,
      };
    });
    divideByTwoProperties(
      item.from.subjectiveQuestionsPoint,
      "questionAfter",
      "questionBefore"
    ).forEach((row: any) => {
      if (row.length > 1) {
        let findItem = row.find((i: any) => i.isPage === -1);
        findItem.isPage = 0;
      } else {
        row[0].isPage = -1;
      }
    });
  });
  return data;
};

// 保存
const btnClick = (str: string): void => {
  const data = changeData().map((item: any) => {
    return {
      ...item,
      tempData: JSON.stringify(item.tempData),
    };
  });
  let msg = "";
  if (templateInfo.hasPosition) {
    if (templateInfo.imgSrc.length === 2) {
      if (
        templateInfo.headList[0].point.length !== 4 ||
        templateInfo.tailsList[0].point.length !== 4
      ) {
        msg = "定位点框选完成才能保存！";
      }
    } else {
      if (templateInfo.headList[0].point.length !== 4) {
        msg = "定位点框选完成才能保存！";
      }
    }
  }
  if (msg) {
    ElMessage.error(msg);
    return;
  }
  if (str === "save") {
    save(true);
  } else {
    LockListRef.value.getData(data);
  }
};

// 锁定
const lock = async (): Promise<void> => {
  await save();
  GetLock(router.currentRoute.value.query.mid as string).then((res: any) => {
    if (res.code === 200) {
      ElMessage.success("锁定成功");
      isLockStatus.value = true;
    }
  });
};

// 保存
const save = async (isShow: boolean = false): Promise<void> => {
  const data = changeData().map((item: any) => {
    return {
      ...item,
      tempData: JSON.stringify(item.tempData),
    };
  });
  PostSave({
    templateData: data,
    mid: router.currentRoute.value.query.mid,
  }).then((res: any) => {
    if (res.code === 200) {
      if (isShow) {
        ElMessage.success("保存成功");
      }
    }
  });
};
</script>
<style lang="scss" scoped>
@mixin flexAlignCenter($align-center: "center") {
  display: flex;
  align-items: #{$align-center};
}

.warp {
  height: 100vh;
  width: 100vw;
  overflow: auto;
  min-width: 1200px;
  display: flex;
  flex-direction: column;

  &-title {
    background-color: #2d8cf0;
    line-height: 60px;
    height: 60px;
    padding: 0 40px;
    box-sizing: border-box;
    color: white;
    font-size: var(--ft20);
    flex-shrink: 0;
  }

  &-nav {
    flex-shrink: 0;
    z-index: 1;

    &-tab {
      @include flexAlignCenter();
      justify-content: space-between;
      height: 60px;
      padding: 0 40px;
      background-color: white;
      box-shadow: 0px 0px 6px 0px #0000001a;

      &-list {
        @include flexAlignCenter();

        > div {
          @include flexAlignCenter();
          cursor: pointer;

          > span {
            color: #727272;
            font-size: var(--ft16);
          }

          > img {
            margin: 0 17px;
          }

          .active {
            font-weight: bold;
            color: #2d8cf0;
          }
        }
      }

      &-delete {
        @include flexAlignCenter();
        cursor: pointer;

        > span {
          color: #ff6565;
          font-size: var(--ft16);
          margin-left: 8px;
        }
      }
    }

    &-title {
      @include flexAlignCenter();
      border-bottom: 1px solid #e1e1e1;
      padding: 10px 40px;
      box-sizing: border-box;
      flex-shrink: 0;

      &-text {
        color: #767676;
        font-size: var(--ft14);
        flex-grow: 1;
      }

      &-operation {
        flex-shrink: 0;
        margin-left: 10px;
        @include flexAlignCenter();

        $color: #2d8cf0;

        &-btn {
          @include flexAlignCenter();

          > div {
            font-weight: bold;
            font-size: var(--ft14);
            width: 80px;
            line-height: 30px;
            text-align: center;
            cursor: pointer;
            border-radius: 6px;
            margin-right: 10px;
            background-color: lighten($color, 10%);
            color: #fff;

            &:hover {
              background-color: darken($color, 1%);
            }

            &:active {
              background-color: darken($color, 6%);
            }

            &:last-child {
              margin: 0;
            }
          }

          .active {
            background-color: darken($color, 8%);
          }
        }

        &-other {
          margin: 0 20px;

          > div {
            font-weight: bold;
            font-size: var(--ft14);
            line-height: 30px;
            text-align: center;
            cursor: pointer;
            border-radius: 6px;
            background-color: darken($color, 8%);
            color: #fff;
            padding: 0 20px;

            &:hover {
              background-color: lighten($color, 10%);
            }

            &:active {
              background-color: darken($color, 10%);
            }
          }
        }

        &-icon {
          @include flexAlignCenter();

          > img:nth-child(1) {
            margin-right: 10px;
          }

          > img {
            cursor: pointer;
          }
        }
      }
    }
  }

  &-content {
    flex-grow: 1;
    background-color: rgb(242, 242, 242);
    @include flexAlignCenter("flex-start");

    &-left {
      flex-grow: 1;
      width: 0;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      box-sizing: border-box;

      &-box {
        height: 78vh;
        width: 100%;
        overflow: auto;

        @mixin isPageType($type) {
          width: 100%;
          height: 100%;

          > #zoom-div {
            margin: 0 auto;
            @if $type == "A3" {
              width: 1570px;
            } @else {
              width: 1200px;
            }

            height: auto;
            flex-shrink: 0;
            position: relative;
            transform-origin: left top;
            position: relative;

            .menu-button {
              border: 1px reds solid;
              position: absolute;
              left: 0;
              top: 0;
              z-index: 3;
              display: flex;
              flex-direction: column;
              border: 1px #ccc solid;
              background-color: white;

              > div {
                padding: 0 30px;
                border-bottom: 1px #ccc solid;
                cursor: pointer;
                line-height: 30px;
                font-size: var(--ft14);
                white-space: nowrap;
                text-overflow: ellipsis;
                overflow: hidden;

                &:last-child {
                  border-bottom: none;
                }
              }
            }

            > img {
              width: 100%;
              height: 100%;
              display: block;
            }

            :deep(.canvas-container) {
              position: absolute !important;
              top: 0;
              z-index: 2;
            }
          }
        }

        .A4 {
          @include isPageType("A4");
        }

        .A3 {
          @include isPageType("A3");
        }
      }
    }

    &-right {
      background-color: white;
      flex-shrink: 0;
      display: flex;
      flex-direction: column;
      width: 300px;
      height: 100%;
      padding: 20px;
      box-sizing: border-box;

      &-info {
        flex-grow: 1;
        box-sizing: border-box;
        overflow-y: auto;
        display: flex;
        flex-direction: column;

        &-fb {
          color: #8b8b8b;
          font-size: var(--ft16);
          margin-bottom: 12px;

          > span {
            color: black;
          }
        }

        &-table {
          margin: 0 0 40px;
          flex-grow: 1;
        }

        &-btn {
          display: flex;
          justify-content: center;

          > :deep(.el-button) {
            margin: 0 4px 0 0;

            &:last-child {
              margin: 0 0 0 0;
            }
          }
        }
      }
    }
  }
}

:deep(.drawer-custom-style) {
  .drawer-custom-style-body {
    height: 100%;
    display: flex;
    flex-direction: column;
    justify-content: space-between;

    &-content {
      height: 90%;
      overflow-y: auto;
    }

    &-footer {
      @include flexAlignCenter();
      justify-content: center;
      height: 8%;
    }

    .examId {
      > div {
        @include flexAlignCenter();
        margin-bottom: 10px;

        > span {
          color: #727272;
          font-size: var(--ft14);
        }
      }

      .full-filling {
        flex-direction: column;

        > div {
          border: 1px #727272 solid;
          box-sizing: border-box;
          padding: 20px;
          width: 100%;
          margin-top: 10px;

          > div {
            @include flexAlignCenter();
            justify-content: center;

            > span {
              border: 1px #727272 solid;
              width: 30px;
              text-align: center;
              font-size: var(--ft14);
            }
          }
        }
      }

      .bar-code {
        flex-direction: column;

        > div {
          border: 1px #727272 dashed;
          box-sizing: border-box;
          padding: 40px;
          width: 100%;
          margin-top: 10px;

          > div {
            border: 1px #727272 dashed;
            height: 200px;
            text-align: center;
            box-sizing: border-box;
            line-height: 200px;
            background-color: #e1e1e1;
          }
        }
      }
    }

    .objective {
      > div {
        @include flexAlignCenter();
        margin-bottom: 10px;

        > span {
          color: #727272;
          font-size: var(--ft14);
          text-align: right;
          width: 100px;
        }
      }

      .syt-row {
        flex-direction: column;

        > span {
          text-align: center;
        }

        > div {
          border: 1px #727272 solid;
          box-sizing: border-box;
          padding: 20px;
          width: 100%;
          margin-top: 10px;
          overflow-x: auto;

          > div {
            @include flexAlignCenter();
            justify-content: center;

            > span {
              &:first-child {
                font-size: var(--ft12);
                margin: 0 8px 4px 0;
                width: 30px;
                text-align: right;
              }

              &:not(:first-child) {
                border: 1px #727272 solid;
                width: 14px;
                text-align: center;
                flex-shrink: 0;
                font-size: var(--ft12);
                margin: 0 4px 4px 0;
              }

              &:last-child {
                margin-right: 0;
              }
            }
          }
        }
      }

      .syt-column {
        flex-direction: column;

        > span {
          text-align: center;
        }

        > div {
          border: 1px #727272 solid;
          box-sizing: border-box;
          padding: 20px;
          width: 100%;
          margin-top: 10px;
          overflow-x: auto;
          display: flex;
          justify-content: center;
          align-items: start;

          > div {
            display: flex;
            flex-direction: column;
            justify-content: center;
            margin: 0 4px 4px 0;

            > span {
              &:first-child {
                font-size: var(--ft12);
                text-align: center;
                text-align: center;
                margin: 0;
              }

              &:not(:first-child) {
                border: 1px #727272 solid;
                width: 14px;
                text-align: center;
                flex-shrink: 0;
                font-size: var(--ft12);
                margin: 4px 0 0 0;
              }
            }
          }
        }
      }
    }

    .subjective {
      > div {
        @include flexAlignCenter();
        margin-bottom: 10px;

        > span {
          color: #727272;
          font-size: var(--ft14);
          text-align: right;
          width: 80px;
        }
      }
    }
  }
}
</style>
