<template>
  <div
    class="mainContentBox"
    style="padding: 0; box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.1)"
  >
    <div class="header">
      <div class="title">绑定工序</div>
      <div class="processInfo">
        <span>工艺名称：{{ routeOperationData?.processRouteName }}</span>
        <span>产品名称：{{ routeOperationData?.prodtName }}</span>
        <span>产品型号: {{ routeOperationData?.prodtModel }}</span>
      </div>
      <el-button :icon="Back" size="small" @click="back">返回</el-button>
    </div>
    <div class="binContainer">
      <div class="processTree">
        <el-scrollbar>
          <!--          <el-select-->
          <!--            v-model="list1SearchGroupDictKey"-->
          <!--            placeholder="搜索工序类型"-->
          <!--            clearable-->
          <!--            filterable-->
          <!--          >-->
          <!--            <el-option-->
          <!--              v-for="dict in dictsArray"-->
          <!--              :label="dict.dictLabel"-->
          <!--              :value="dict.dictValue"-->
          <!--            />-->
          <!--          </el-select>-->
          <el-input
            v-model="list1SearchStr"
            placeholder="搜索工序名称"
            clearable
          />
          <el-collapse v-model="activeNames">
            <el-collapse-item
              v-for="(pProcess, pIndex) in showList"
              :key="pProcess.workSection"
              :name="pIndex"
            >
              <template #title>
                <div class="title2">
                  {{ pProcess.workSectionStr }}
                </div>
              </template>
              <div class="treeChild">
                <div
                  class="childItem"
                  v-for="(item, index) in pProcess?.baseOperationList"
                  :key="index"
                  :draggable="!editStatus && usableStatus"
                  @dragstart="
                    onDragStart($event, nodeType, item.operationName, item)
                  "
                >
                  {{ item.operationName }}
                </div>
              </div>
            </el-collapse-item>
          </el-collapse>
        </el-scrollbar>
      </div>
      <div
        class="flowBox"
        id="flowBox"
        style="height: 100%; width: 100%; overflow: hidden"
      >
        <div class="dnd-flow" @drop="onDrop">
          <VueFlow
            v-model:nodes="nodes"
            v-model:edges="edges"
            :apply-default="!editStatus && usableStatus"
            @connect="onConnectMethod"
            @dragover="onDragOver"
            @dragleave="onDragLeave"
            @node-click="nodeClick($event)"
            @edge-click="edgeClick($event)"
            @nodes-change="(data) => nodesChange(data)"
            @edges-change="(data) => edgesChange(data)"
            @node-drag-stop="nodeDragStop"
            @node-drag-start="nodeDragStart"
          >
            <MiniMap style="background-color: grey" v-show="!printStauts" />
            <!--            <template #edge-custom="props">-->
            <!--              <CustomConnectionLine v-bind="props" />-->
            <!--            </template>-->
            <!--            <template #connection-line="props">-->
            <!--              <CustomConnectionLine v-bind="props" />-->
            <!--            </template>-->
            <template #node-custom="props">
              <CustomNode v-bind="props" />
            </template>
            <DropzoneBackground
              :style="{
                backgroundColor: isDragOver ? '#e7f3ff' : 'transparent',
                transition: 'background-color 0.2s ease',
              }"
            >
              <p v-if="isDragOver">拖拽中</p>
            </DropzoneBackground>

            <Panel
              class="process-panel"
              position="top-right"
              v-show="!printStauts"
            >
              <div class="layout-panel">
                <button title="蛇形排列" @click="convertToSnakeLayout">
                  <PannelIcon name="liucheng" />
                </button>
                <button title="整齐排列" @click="layoutGraph('TB')">
                  <PannelIcon name="vertical" />
                </button>
                <button title="重置" @click="cancer">
                  <PannelIcon name="chongzhi" />
                </button>
                <button title="撤回" @click="goBack">
                  <PannelIcon name="fanhui" />
                </button>
              </div>
            </Panel>
          </VueFlow>

          <div
            :style="{
              width: flowBoxW - 20 + 'px',
              height: flowBoxH - 20 + 'px',
            }"
          ></div>
        </div>
      </div>
      <div class="detailInfo">
        <el-scrollbar>
          <div class="opBox">
            <el-text class="mx-1" type="warning"
              >切换和排序之前记得先保存</el-text
            >
            <el-button type="primary" @click="changeEditStatus">{{
              !editStatus ? "切换为工序信息编辑模式" : "切换为工序路线编辑模式"
            }}</el-button>

            <el-button @click="getPng">导出流程图 </el-button>
            <!-- <el-button type="success" @click="changeStyle">
              按钮风格切换
            </el-button> -->
            <div style="display: flex">
              <el-button type="success" @click="startGroup">
                进行分组
              </el-button>

              <el-button @click="toSort"> 分支排序 </el-button>
            </div>
          </div>

          <!-- 工艺路线编辑模式 -->
          <div v-if="!editStatus && usableStatus">
            <div :class="styleStatus ? 'btnsChange' : 'btns'">
              <div>
                <el-button type="primary" @click="saveFlow">
                  <span>保</span>
                  <span> &nbsp; </span>
                  <span>存</span>
                </el-button>
              </div>

              <div>
                <el-button
                  type="info"
                  v-if="selectNode || selectLine"
                  @click="deleteFlow"
                >
                  <span>删</span>
                  <span> &nbsp; </span>
                  <span>除</span></el-button
                >
              </div>
            </div>
          </div>
          <!-- 工序信息编辑模式 -->
          <div v-else>
            <div
              v-if="currentProcess.id"
              style="width: 310px; padding-right: 20px"
            >
              <avue-form
                ref="formRef"
                :option="formOption"
                v-model="currentProcess"
                :key="avueKey"
                style="padding: 10px; background-color: transparent"
              >
                <template #tbomUrl>
                  <FilesUpload
                    v-model:src="currentProcess.tbomUrl"
                    :show-tip="false"
                  />
                </template>
                <template #customFieldName>
                  <el-select
                    v-model="customFieldNameValues"
                    multiple
                    default-first-option
                    :reserve-keyword="false"
                    placeholder="请选择名称"
                  >
                    <el-option
                      v-for="item in customFieldOptions"
                      :key="item.value"
                      :label="item.value"
                      :value="item.value"
                    />
                  </el-select>
                </template>
              </avue-form>
              <div class="btns">
                <el-tooltip
                  class="box-item"
                  effect="dark"
                  content="会同时保存现线路情况，请确认好变更"
                  placement="bottom"
                >
                  <el-button type="primary" @click="saveInfo"
                    >保存工序详情</el-button
                  >
                </el-tooltip>
                <el-button type="danger" @click="cancelInfo"
                  >重置该工序详情</el-button
                >
              </div>
              <div class="editProcces">
                <el-button type="primary" @click="editProComponent"
                  >编辑工序组件</el-button
                >
              </div>
            </div>
            <div v-else-if="!usableStatus" class="tipContent">
              工艺路线已绑定或审核完成工序不可编辑
            </div>
            <div v-else class="tipContent">请选择工序</div>
          </div>
        </el-scrollbar>
      </div>
    </div>
    <GroupProcess ref="GroupProcessRef" @success="groupFinish"></GroupProcess>
    <GroupSort ref="GroupSortRef" />
  </div>
</template>

<script setup>
import {
  Back,
  Download,
  Document,
  Delete,
  Bottom,
  Grid,
} from "@element-plus/icons-vue";
import _ from "lodash-es";
import { useDebounceFn } from "@vueuse/core";
import { VueFlow, useVueFlow, Panel } from "@vue-flow/core";
import DropzoneBackground from "./components/DropzoneBackground/index.vue";
import CustomConnectionLine from "./components/CustomConnectionLine/index.vue";
import CustomNode from "./components/CustomNode/index.vue";
import useDragAndDrop from "@/hooks/useDnD.js";
import { MiniMap } from "@vue-flow/minimap";
import { processTreeList } from "@/api/craft/process/index";
import { useCommonStoreHook, useDictionaryStore } from "@/store";
import {
  processesByRouteId,
  saveProcessInRoute,
  toJavaSnake,
  updateProcess,
} from "@/api/craft/route/index";
import { formOption } from "./bindConfig";
import { ElMessage } from "element-plus";
import { useScreenshot } from "./screenshot.ts";
import { useLayout } from "@/hooks/useLayout";
import { useSnakeLayoutHook } from "@/hooks/vueflowHooks";
import GroupProcess from "./components/groupProcess.vue";
import GroupSort from "./components/groupSort.vue";

defineOptions({
  name: "bindProcess/:id/:prodtCode/:usable",
});

const styleStatus = ref(false);
const GroupSortRef = ref(null);
const toSort = () => {
  GroupSortRef.value.drawer = true;
};
const changeStyle = () => {
  styleStatus.value = !styleStatus.value;
};
const addHistory = (type) => {
  historyList.value.unshift(JSON.parse(JSON.stringify(flowData)));
};
const resetHistory = () => {
  historyList.value = [];
};
const nodesChange = (data) => {
  if (
    data.length > 0 &&
    data[0].type &&
    (data[0].type === "add" || data[0].type === "remove")
  ) {
    addHistory();
  }
};
const edgesChange = (data) => {
  if (
    data.length > 0 &&
    data[0].type &&
    (data[0].type === "add" || data[0].type === "remove")
  ) {
    addHistory();
  }
};

let dragStartFlowData = null;
const nodeDragStart = () => {
  dragStartFlowData = JSON.parse(JSON.stringify(flowData));
};
const nodeDragStop = () => {
  historyList.value.unshift(JSON.parse(JSON.stringify(dragStartFlowData)));
};

const goBack = () => {
  let firstHistory = historyList.value.shift();
  if (firstHistory) {
    flowData.nodes = firstHistory.nodes;
    flowData.edges = firstHistory.edges;
  } else {
    ElMessage.warning("已是初始线路");
  }
};
const historyList = ref([]);
const { capture } = useScreenshot();
const { layout } = useLayout();
const instance = useVueFlow();
const { addEdges, vueFlowRef, onEdgeUpdateEnd, applyEdgeChanges, fitView } =
  instance;
const { onDragOver, onDrop, onDragLeave, isDragOver, onDragStart } =
  useDragAndDrop();
// onConnect(addEdges);
const onConnectMethod = (toConnectedEdge) => {
  let hasSourceEdge = false;
  for (const edge of edges.value) {
    if (edge.source === toConnectedEdge.source) {
      hasSourceEdge = true;
      break;
    }
  }

  if (!hasSourceEdge) {
    toConnectedEdge.type = "smoothstep";
    toConnectedEdge.markerEnd = "arrowclosed";
    toConnectedEdge.style = { strokeWidth: "2" };
    addEdges(toConnectedEdge);
  } else {
    ElMessage.warning("工序只能有一个下一道工序！");
  }
};

const flowData = reactive({ edges: [], nodes: [] });
const flowDataCopy = ref({ edges: [], nodes: [] });
const currentProcess = ref({});
const selectNode = ref(null);
const selectLine = ref(null);
//true为可编辑线路
const usableStatus = ref(true);
provide("selectNode", selectNode);
provide("currentProcess", currentProcess);
provide("selectLine", selectLine);
const edgeClick = (event) => {
  if (usableStatus.value == false && !editStatus.value) return;
  if (!editStatus.value) {
    selectLine.value = event.edge;
    selectNode.value = null;
  }
};

// 工序分组功能模块
const GroupProcessRef = ref(null);
const startGroup = () => {
  GroupProcessRef.value &&
    GroupProcessRef.value.startGroup(
      flowData.nodes,
      route.fullPath.split("/")[4],
      route.fullPath.split("/")[6]
    );
};
const groupFinish = () => {
  loadProcessesFlow();
};

// 自定义工序项目字段名称相关方法
const customFieldNameValues = ref([]);
const customFieldOptions = ref([]);
function isJSON(str) {
  if (typeof str !== "string") {
    return false;
  }

  try {
    JSON.parse(str);
    return true;
  } catch (e) {
    return false;
  }
}
const avueKey = ref(false);
const nodeClick = (event) => {
  // 所有的批量报工设置为1
  event.node.batchReport = 1;
  if (usableStatus.value == false && !editStatus.value) return;
  if (!editStatus.value) {
    selectNode.value = event.node;
  } else {
    currentProcess.value = {};
    //让组件重新加载
    avueKey.value = !avueKey.value;
    currentProcess.value = event.node;

    // 设置自定义字段名称的选项内容等
    if (
      event.node.customFieldName &&
      isJSON(event.node.customFieldName) &&
      JSON.parse(event.node.customFieldName).length > 0
    ) {
      customFieldOptions.value = JSON.parse(event.node.customFieldName);
      customFieldNameValues.value = customFieldOptions.value
        .filter((item) => {
          return item.isSelected;
        })
        .map((item) => item.value);
    }
  }
};
//当使用回退时清空选择的node

const handleKeydown = () => {
  if (selectNode.value != null) {
    selectNode.value = null;
  }
  if (selectLine.value != null) {
    selectLine.value = null;
  }
};
const printStauts = ref(false);
const getPng = async () => {
  if (!vueFlowRef.value) {
    console.warn("VueFlow element not found");
    return;
  }
  printStauts.value = true;
  await nextTick();
  capture(vueFlowRef.value, { shouldDownload: true });
  setTimeout(() => {
    printStauts.value = false;
  }, 0);
};
const nodeType = ref("custom");
const editStatus = ref(false);
provide("editStatus", editStatus);
const changeEditStatus = () => {
  editStatus.value = !editStatus.value;
  if (editStatus.value == false) {
    currentProcess.value = {};
  } else {
    selectNode.value = null;
  }
};
const router = useRouter();
const route = useRoute();
// 数据字典相关
const { dicts } = useDictionaryStore();
//获取画布盒子具体长宽
const flowBoxH = ref(null);
const flowBoxW = ref(null);
const flowBoxScreen = () => {
  const flowBox = document.getElementById("flowBox");
  flowBoxH.value = flowBox.clientHeight;
  flowBoxW.value = flowBox.clientWidth;
};
// 顶部====================
const back = () => {
  router.back();
};

const download = () => {};

// 左侧工序树====================
const activeNames = ref([0]);
const list1 = ref([]);
const list1SearchStr = ref("");
const list1SearchGroupDictKey = ref("");
const showList = computed(() => {
  let array = _.cloneDeep(list1.value);
  // if (list1SearchGroupDictKey.value) {
  //   array = array.filter((item) => {
  //     return item.workSection === list1SearchGroupDictKey.value;
  //   });
  // }

  array.forEach((item) => {
    item.baseOperationList = item.baseOperationList
      .filter((op) => {
        return op.operationName.includes(list1SearchStr.value);
      })
      .sort((a, b) => {
        return a.operationCode.localeCompare(b.operationCode);
      });
  });
  return array;
});
const errArray = ref([]);
provide("errArray", errArray);
// 保存中间的工序列表
const saveFlow = async () => {
  const { code, msg } = await saveProcessInRoute({
    processRouteId: route.fullPath.split("/")[4],
    routeData: JSON.stringify({ ...flowData }),
  });
  if (code == "200") {
    try {
      if (JSON.parse(msg.replace("err:", ""))) {
        errArray.value = JSON.parse(msg.replace("err:", ""));
        ElMessage.error("请检查标记节点情况");
      }
    } catch (e) {
      errArray.value = [];
      ElMessage.success("保存成功");
      loadProcessesFlow();
    }
  }
};
const cancer = () => {
  ElMessageBox.confirm("取消的话会清空本次操作记录，确定吗?", "回退", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      cancelFlow();
    })
    .catch(() => {});
};
const cancelFlow = () => {
  flowData.nodes = flowDataCopy.value.nodes;
  flowData.edges = flowDataCopy.value.edges;
  if (selectNode.value != null) {
    selectNode.value = null;
  }
  if (selectLine.value != null) {
    selectLine.value = null;
  }
  resetHistory();
};
const deleteFlow = () => {
  addHistory();
  if (selectNode.value != null) {
    flowData.nodes.forEach((item, index) => {
      if (item.id == selectNode.value.id) {
        flowData.nodes.splice(index, 1);
      }
    });
  }
  if (selectLine.value != null) {
    flowData.edges.forEach((item, index) => {
      if (item.id == selectLine.value.id) {
        flowData.edges.splice(index, 1);
      }
    });
  }
  selectNode.value = null;
  selectLine.value = null;
  ElMessage.success("删除成功");
};

const saveInfo = async () => {
  // let isValid = await formRef.value.validate();
  // if (!isValid) {
  //   return;
  // }
  // 处理currentProcess的自定义对象名称字段，如果customFieldNameValues在customFieldOptions中存在，则设置isSelected为true，否则设置为false,最后转为json
  const customFieldArr = [];
  customFieldOptions.value.forEach((item) => {
    let obj = {
      value: item.value,
      isSelected: customFieldNameValues.value.includes(item.value),
    };
    customFieldArr.push(obj);
  });

  currentProcess.value.customFieldName = JSON.stringify(customFieldArr);

  flowData.nodes.forEach((item, index) => {
    if (item.id == currentProcess.value.id) {
      flowData.nodes[index] = currentProcess.value;
    }
  });
  saveFlow();
  currentProcess.value = {};
};
const cancelInfo = () => {
  flowData.nodes.forEach((item, index) => {
    if (item.id == currentProcess.value.id) {
      currentProcess.value = flowData.nodes[index];
    }
  });
};
const loadTreeData = () => {
  // processTreeList(list1SearchGroupDictKey.value).then((res) => {
  //   list1.value = res.data ?? [];
  //   filterDicts();
  // });
  processTreeList().then((res) => {
    list1.value = res.data ?? [];
    filterDicts();
  });
};

// 用于搜索的数据字典数组
const dictsArray = ref([]);
// 从dicts?.workshop_section筛选出list1中返回的字典值,用于最上方的选择框
const filterDicts = () => {
  dictsArray.value = dicts?.process_type ?? [];

  // const workSectionArray = list1.value.map((item) => {
  //   return item.workSection;
  // });
  //
  // const arr = [];
  // dicts?.workshop_section?.forEach((item) => {
  //   if (workSectionArray.includes(item?.dictValue)) {
  //     arr.push(_.cloneDeep(item));
  //   }
  // });
  // dictsArray.value = arr;
};

const routeOperationData = ref(null);
const cancelStatus = ref(true);
//通过id获取现存储信息
const loadProcessesFlow = async () => {
  const res = await processesByRouteId(route.fullPath.split("/")[4]);
  if (res.data) {
    routeOperationData.value = res.data;
    if (res.data.routeData && res.data.routeData != "") {
      let jsonData = JSON.parse(res.data.routeData);
      flowData.nodes = jsonData.nodes;
      flowData.edges = jsonData.edges;
      flowDataCopy.value = JSON.parse(JSON.stringify(jsonData));
      cancelStatus.value = true;
      if (res.data.usable == 1 || res.data.flowState === "1") {
        usableStatus.value = false;
      }

      resetHistory();
    } else {
      cancelStatus.value = false;
    }
  } else {
    flowDataCopy.value = JSON.parse(JSON.stringify(flowData));
  }
};

const formRef = ref(null);
const reset = () => {
  currentProcess.value = {};
  editStatus.value = false;
  routeOperationData.value = {};
};
const editProComponent = async () => {
  if (!currentProcess.value.operationId) {
    ElMessage.warning("请串联保存工序流程");
    return;
  }
  router.push({
    path: `/base/craftManagement/processCom/${currentProcess.value.operationId}/${route.fullPath.split("/")[5]}/${route.fullPath.split("/")[4]}`,
  });
};

// 全局=======
onMounted(async () => {
  await loadTreeData();
  await loadProcessesFlow();
  flowBoxScreen();
  window.addEventListener("keydown", handleKeydown);
});
onUnmounted(() => {
  window.removeEventListener("keydown", handleKeydown);
});
const getNameByDictType = (dictValue) => {
  let str = "";
  dicts?.workshop_section?.forEach((element) => {
    if (element?.dictValue === dictValue) {
      str = element?.dictLabel ?? "===";
    }
  });
  return str;
};

// watch(
//   () => flowData.edges,
//   () => {
//     flowData.edges.forEach((item) => {
//       item.type = "custom";
//     });
//   }
// );

watch(
  () => list1SearchGroupDictKey.value,
  () => {
    loadTreeData();
  }
);

const { nodes } = toRefs(flowData); //ref(initialNodes);
const { edges } = toRefs(flowData); //ref(initialEdges);
async function layoutGraph(direction) {
  addHistory();
  nodes.value = layout(nodes.value, edges.value, direction);
  nextTick(() => {
    fitView();
  });
}
// 转换为蛇形布局
// const convertToSnakeLayout = () => {
//   addHistory();
//   nodes.value = useSnakeLayoutHook(nodes.value, edges.value);
//   nextTick(() => {
//     fitView();
//   });
// };
const convertToSnakeLayout = async () => {
  const res = await toJavaSnake({
    id: route.fullPath.split("/")[4],
    routeData: JSON.stringify({ ...flowData }),
  });
  let jsonData = JSON.parse(res.data.routeData);
  flowData.nodes = jsonData.nodes;
  flowData.edges = jsonData.edges;

  // await loadProcessesFlow();
  // flowBoxScreen();
};
</script>

<style lang="scss" scoped>
.showStyle {
  transform: translateY(10px) rotate(90deg);
}
.fontStyle {
  transform: rotate(-90deg);
}
.binContainer {
  height: calc(100vh - 165px);
  background-color: #f5f7f9;
  // background-color: white;
  padding: 0;
  display: flex;
}
.btns {
  display: flex;
  justify-content: center;
  margin: 10px 0;
  div {
    width: 80px;
  }
}
.btnsChange {
  margin: 10px 0;
  div {
    display: flex;
    justify-content: center;
    margin-bottom: 10px;
  }
}
.header {
  height: 50px;
  display: flex;
  background-color: #f5f7f9;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}

.processTree {
  width: 220px;
  height: 100%;
  border-right: 1px solid rgba(0, 0, 0, 0.1);
  .treeChild {
    background: #f2f2f2;
    padding: 20px 8px 10px 8px;
    .childItem {
      width: 100%;
      height: 28px;
      background: #ffffff;
      border-radius: 4px 4px 4px 4px;
      border: 1px solid rgba(35, 32, 50, 0.1);
      margin-bottom: 10px;
      text-align: center;
    }
  }
}

.flowBox {
  flex: 1;
  display: flex;
  justify-content: center;
  overflow-y: auto;
  .flowItem:hover {
    .flowDelete {
      visibility: visible;
    }
  }
}

.detailInfo {
  width: 320px;
  height: 100%;
  background-color: white;
  border-left: 1px solid rgba(0, 0, 0, 0.1);
  .opBox {
    border-bottom: 1px solid rgba(0, 0, 0, 0.1);
    padding: 0 20px;
    height: 150px;
    display: flex;
    flex-direction: column;
    justify-content: space-evenly;
    align-items: center;
  }
  .editProcces {
    display: flex;
    justify-content: center;
    margin-bottom: 15px;
  }
}

.title,
.processInfo {
  line-height: 44px;
  color: #6f7991;
  font-size: 14px;
  font-weight: bold;
  text-align: left;
  margin-left: 20px;
}

.processInfo span {
  margin-left: 100px;
}

.processInfo {
  position: absolute;
  left: 5%;
}
.title2 {
  color: #232032;
  font-size: 14px;
  text-align: left;
  margin-left: 19px;
}
.tipContent {
  width: 100%;
  text-align: center;
  margin-top: 20px;
  color: #e6a23c;
}

.process-panel,
.layout-panel {
  display: flex;
  gap: 10px;
}

.process-panel {
  background-color: #2d3748;
  padding: 10px;
  border-radius: 8px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
  display: flex;
  flex-direction: column;
}

.process-panel button {
  border: none;
  cursor: pointer;
  background-color: #4a5568;
  border-radius: 8px;
  color: white;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
}

.process-panel button {
  font-size: 16px;
  width: 40px;
  height: 40px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.checkbox-panel {
  display: flex;
  align-items: center;
  gap: 10px;
}

.process-panel button:hover,
.layout-panel button:hover {
  background-color: #2563eb;
  transition: background-color 0.2s;
}

.process-panel label {
  color: white;
  font-size: 12px;
}

.stop-btn svg {
  display: none;
}

.stop-btn:hover svg {
  display: block;
}

.stop-btn:hover .spinner {
  display: none;
}

.spinner {
  border: 3px solid #f3f3f3;
  border-top: 3px solid #2563eb;
  border-radius: 50%;
  width: 10px;
  height: 10px;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
</style>

<style>
.el-collapse-item__content {
  padding: 0;
}
</style>
