puzhibing
6 天以前 0db8c9b94a9ac655549d5eba5c2a6c92ac386c06
culture/src/views/pedigree-chart/addProgenitor.vue
@@ -1,96 +1,158 @@
<template>
  <div>
    <div class="header-title" style="width: 100%;">
      <div class="header-title-left">
        <img src="@/assets/public/headercard.png" />
        <div>所属项目组</div>
      </div>
      <div class="header-title-right">
        <el-button @click="showChoose = true" :disabled="!!$route.query.id" class="el-icon-circle-plus-outline" type="primary">
          选择项目组</el-button>
      </div>
    </div>
    <Table :height="null" :queryForm="queryForm" :total="0" :data="projectData" style="margin-bottom: 20px">
      <template>
        <el-table-column prop="teamName" label="项目组名称" />
        <el-table-column prop="personCharge" label="项目负责人" />
        <el-table-column prop="staff" label="项目组成员" />
        <el-table-column prop="createTime" label="创建时间" />
      </template>
    </Table>
    <el-form :model="form" :rules="rules" ref="pedigreeForm" label-position="top" class="strain-form">
      <div class="card">
        <div class="form-items-row">
          <el-form-item label="传代菌种编号" prop="strainNo" required>
            <el-input v-model="form.strainNo" placeholder="请输入" class="fixed-width-input"></el-input>
          <el-form-item label="传代菌种编号" prop="strainCode" required>
            <el-input v-model="form.strainCode" :disabled="!!$route.query.id" placeholder="请输入"
              class="fixed-width-input"></el-input>
          </el-form-item>
          <el-form-item label="传代菌种名称" prop="strainName" required>
            <el-input v-model="form.strainName" placeholder="请输入" class="fixed-width-input"></el-input>
            <el-input v-model="form.strainName" placeholder="请输入" :disabled="!!$route.query.id"
              class="fixed-width-input"></el-input>
          </el-form-item>
        </div>
      </div>
      <div class="card" style="margin-top: 30px;">
        <Table :height="null" :total="0" :tableData="tableData">
          <el-table-column label="菌株类型" prop="strainSource" />
          <el-table-column label="来源获得/菌落编号" prop="strainNo" />
          <el-table-column label="菌种编号" prop="strainName" />
      <div class="card" style="margin-top: 30px; display: block" v-if="roleType != 4">
        <div class="title" style="padding: 25px 0">待确认传代计划数</div>
        <Table :height="null" :total="newTotal" :data="newTableData" :queryForm="newQueryForm"
          @handleCurrentChange="handleNewCurrentChange" @handleSizeChange="handleNewSizeChange">
          <el-table-column label="菌种工程师" prop="createBy" />
          <el-table-column label="菌种编号" prop="strainCode" />
          <el-table-column label="菌种名称" prop="strainName" />
          <el-table-column label="入库总数" prop="strainName" />
          <el-table-column label="保存/废弃" prop="strainName" />
          <el-table-column label="菌种入库时间" prop="strainName" />
          <el-table-column label="接种操作人" prop="strainName" />
          <el-table-column label="操作">
          <el-table-column label="传代计划数量" prop="generationCount" />
          <el-table-column label="操作" v-if="roleType == 2">
            <template #default="{ row }">
              <el-button type="text">确认入库</el-button>
              <el-button type="text" @click="handleNewConfirm(row)">确认</el-button>
            </template>
          </el-table-column>
        </Table>
        <div class="title" style="padding: 25px 0">待确认入库菌种列表</div>
        <Table :height="null" :total="total" :data="tableData" :queryForm="queryForm"
          @handleCurrentChange="handleCurrentChange" @handleSizeChange="handleSizeChange">
          <el-table-column label="菌种工程师" prop="experimenter" />
          <el-table-column label="菌种编号" prop="strainCode" />
          <el-table-column label="菌种名称" prop="strainName" />
          <el-table-column label="保存/废弃">
            <template #default="{ row }">
              {{ row.status === 1 ? "保存" : "废弃" }}
            </template>
          </el-table-column>
          <el-table-column label="菌种入库时间/菌种废弃时间" prop="createTime" />
          <el-table-column label="操作" v-if="roleType == 2">
            <template #default="{ row }">
              <el-button type="text" @click="handleConfirm(row)">确认入库</el-button>
            </template>
          </el-table-column>
        </Table>
      </div>
      <div class="chart">
        <div class="header">
          <div class="title">菌种传代生产谱系图</div>
          <div class="option-btn">
            <el-button type="primary" class="el-icon-plus" @click="addNode"> 新增</el-button>
            <el-button type="primary" @click="setGenerationPlan">设置传代计划数</el-button>
            <el-button type="primary" class="el-icon-plus" @click="addNode" :disabled="editBoolean" v-if="!isReadOnly && (roleType == 3 || roleType == 1)">
              <!-- v-if="$route.query.type != 1" -->
              新增</el-button>
            <el-button type="primary" v-if="!isReadOnly && (roleType == 3 || roleType == 1)" :disabled="editBoolean"
              @click="setGenerationPlan">设置传代计划数</el-button>
            <el-button type="primary" @click="showDetail">详情</el-button>
            <el-button type="primary" @click="handleEdit" v-if="!isReadOnly && (roleType == 1 || roleType == 3)" :disabled="editBoolean">编辑</el-button>
            <el-button type="primary" @click="handleDelete" v-if="!isReadOnly && (roleType == 1 || roleType == 3)" :disabled="editBoolean">删除</el-button>
          </div>
        </div>
        <div class="strain-flow-chart">
          <div id="mountNode"></div>
          <div ref="mountNode" class="mount-node"></div>
        </div>
      </div>
      <div class="end-btn">
        <el-button type="primary" @click="handleSubmit">提交</el-button>
        <el-button @click="handleDraft">存草稿</el-button>
        <el-button @click="handleCancel">取消</el-button>
        <div v-if="!isReadOnly && (roleType == 1 || roleType == 3)">
          <el-button type="primary" @click="handleSubmit('1')" style="width: 150px" :disabled="editBoolean">保存</el-button>
          <el-button type="primary" @click="handleSubmit('2')" style="width: 150px; margin-left: 20px" :disabled="editBoolean">提交</el-button>
        </div>
      </div>
    </el-form>
    <AddAncestor ref="addAncestor" @addNodeSign="addNodeSign" />
    <PlanForm ref="planForm" @addNodeSign="addNodeSign" />
    <AddSublevelForm ref="addSublevelForm" @addNodeSign="addNodeSign" />
    <ConfirmStorageDialog name="接种操作人签字" :visible.sync="confirmStorageDialogVisible"
    <AddAncestor :key="pageInstanceKey" ref="addAncestor" @addNodeSign="addNodeSign" />
    <PlanForm :key="pageInstanceKey" ref="planForm" @addNodeSign="addNodeSign" />
    <AddSublevelForm :key="pageInstanceKey" ref="addSublevelForm" @addNodeSign="addNodeSign" />
    <ConfirmStorageDialog :key="`signature-${pageInstanceKey}`" name="接种操作人签字" :visible.sync="confirmStorageDialogVisible"
      @confirm="handleSignatureConfirm" />
    <!-- 菌种工程师 -->
    <ConfirmStorageDialog name="菌种保藏人签字" text="是否确认该项菌种信息入库" :visible.sync="storageVisible"
      @confirm="handleSignatureConfirm" />
    <ConfirmStorageDialog :key="`storage-${pageInstanceKey}`" name="菌种保藏人签字" text="是否确认该项菌种信息入库" :visible.sync="storageVisible"
      @confirm="handleSignatureConfirm1" />
    <!-- 传代数审批 -->
    <ApprovalCount :key="`approval-${pageInstanceKey}`" ref="approvalCount" @addNodeSign="sureApprovalCountSign" />
    <chooseProject :key="`project-${pageInstanceKey}`" @submit="getProjectData" :show="showChoose" @close="showChoose = false"></chooseProject>
  </div>
</template>
<script>
import G6 from '@antv/g6';
import G6 from "@antv/g6";
import PlanForm from "./progenitorComponents/PlanForm.vue";
import AddAncestor from "./progenitorComponents/AddAncestor.vue";
import AddSublevelForm from "./progenitorComponents/AddSublevelForm.vue";
import ConfirmStorageDialog from "@/components/confirm-storage-dialog";
import {
  add,
  getDetail,
  addProgenitorChild,
  addCount,
  addProgenitor,
  pageListDetail,
  pageListCountDetail,
  confirmStorage,
  deleteNode,
  updateChild,
  edit,
  confirmCount,
} from "./service";
import ApprovalCount from "./progenitorComponents/approvalCount.vue";
import ChooseProject from "@/components/chooseProject/index.vue";
export default {
  name: "AddPedigree",
  components: {
    ChooseProject,
    PlanForm,
    AddAncestor,
    AddSublevelForm,
    ConfirmStorageDialog
    ConfirmStorageDialog,
    ApprovalCount,
  },
  data() {
    return {
      showChoose: false,
      projectData: [], // 存储选中的项目组数据
      queryForm: {
        pageSize: 10,
        pageNum: 1,
      },
      signatureVisible: false,
      form: {
        strainSource: "",
        generation: "",
        cellBank: "",
        strainNo: "",
        strainCode: "",
        strainName: "",
        remarks: "",
        generationType: "2",
      },
      rules: {
        strainSource: [
          { required: true, message: "请输入菌种源", trigger: "blur" },
        ],
        strainNo: [
        strainCode: [
          { required: true, message: "请输入传代菌种编号", trigger: "blur" },
        ],
        strainName: [
@@ -102,308 +164,1138 @@
      selectedNode: null,
      graphData: {
        nodes: [],
        edges: []
        edges: [],
      },
      // 弹窗相关数据
      dialogVisible: false,
      dialogTitle: '',
      formLabel: '',
      inputType: 'text',
      dialogTitle: "",
      formLabel: "",
      inputType: "text",
      showDiscarded: false,
      isAddingNode: false,
      nodeData: {},
      nodeType: '',//1祖代 2计划数 3母代
      nodeType: "", //1祖代 2计划数 3母代
      tableData: [],
      confirmStorageDialogVisible: false,
      storageVisible: false
      storageVisible: false,
      editBoolean: false,//编辑控制
      total: 0,
      current: 1,
      size: 10,
      comfirmId: "",
      roleType: Number(
        JSON.parse(sessionStorage.getItem("userInfo")).roleType || 1
      ), // 1.超级管理员 2.审批人 3.工程师 4.实验员
      // 2.0
      newTotal: 0,
      newCurrent: 1,
      newSize: 10,
      newTableData: [],
      newQueryForm: {
        pageSize: 10,
        pageNum: 1,
      },
      activeRouteKey: "",
      graphLoadId: 0,
      pageInstanceKey: 0,
    };
  },
  computed: {
    isReadOnly() {
      return this.$route.path.includes('detail') || this.$route.query.mode === 'detail';
    },
    canAddNode() {
      // 如果没有节点,可以新增母代
      if (this.graphData.nodes.length === 0) {
        return true;
      }
      // 如果选中了传代计划数节点,可以新增下一代
      if (this.selectedNode && this.selectedNode.label === '传代计划数') {
      if (this.selectedNode && this.selectedNode.label === "传代计划数") {
        return true;
      }
      return false;
    },
  },
  watch: {
    '$route': {
      immediate: true,
      handler(to) {
        if (!["AddProgenitor", "DetailProgenitor"].includes(to.name)) {
          return;
        }
        console.log('变动');
        const routeKey = to.fullPath;
        const routeChanged = this.activeRouteKey && this.activeRouteKey !== routeKey;
        this.activeRouteKey = routeKey;
        if (routeChanged || !to.query.id) {
          this.resetPageState();
        }
        if (this.graph && to.query.id) {
          this.reloadData();
        }
        this.editBoolean = this.shouldDisableEditing(to.query);
      }
    }
  },
  mounted() {
    this.initGraph();
    this.initEvents();
    this.resetMountNode();
    if (this.$route.query.id) {
      this.reloadData();
    }
  },
  beforeDestroy() {
    this.graph?.destroy();
    window.removeEventListener('resize', this.handleResize);
    this.destroyGraph();
  },
  methods: {
    shouldDisableEditing(query = this.$route.query) {
      const status = query && query.status;
      return Number(this.roleType) !== 1
          && status !== undefined
          && status !== null
          && status !== ""
          && Number(status) !== 1;
    },
    destroyGraph() {
      window.removeEventListener("resize", this.handleResize);
      if (this.graph && !this.graph.get("destroyed")) {
        this.graph.destroy();
      }
      this.graph = null;
      const container = this.$refs.mountNode;
      if (container) {
        while (container.firstChild) {
          container.removeChild(container.firstChild);
        }
      }
    },
    resetMountNode() {
      this.destroyGraph();
      if (!this.$refs.mountNode) return;
      this.initGraph();
      this.initEvents();
    },
    resetPageState() {
      this.pageInstanceKey += 1;
      this.form = {
        strainCode: "",
        strainName: "",
        generationType: "2",
      };
      this.projectData = [];
      this.tableData = [];
      this.newTableData = [];
      this.total = 0;
      this.current = 1;
      this.size = 10;
      this.newTotal = 0;
      this.newCurrent = 1;
      this.newSize = 10;
      this.queryForm = { pageSize: 10, pageNum: 1 };
      this.newQueryForm = { pageSize: 10, pageNum: 1 };
      this.showChoose = false;
      this.dialogVisible = false;
      this.confirmStorageDialogVisible = false;
      this.storageVisible = false;
      this.nodeData = {};
      this.nodeType = "";
      this.isAddingNode = false;
      this.showDiscarded = false;
      this.editBoolean = this.shouldDisableEditing();
      this.resetGraphData();
      this.$nextTick(() => {
        this.$refs.pedigreeForm && this.$refs.pedigreeForm.clearValidate();
      });
    },
    resetGraphData() {
      this.graphLoadId += 1;
      this.graphData = {
        nodes: [],
        edges: [],
      };
      this.selectedNode = null;
      this.nodeCount = 0;
      if (this.graph) {
        this.graph.changeData({ nodes: [], edges: [] });
        this.graph.render();
      }
    },
    getProjectData(data) {
      this.projectData = [data]; // 将选中的项目组数据存储到数组中
      this.showChoose = false;
      this.form.projectId = this.projectData[0].id;
    },
    handleDelete() {
      console.log('handleDelete selectedNode', this.selectedNode)
      if (!this.selectedNode) {
        this.$message.warning("请先选择节点");
        return;
      }
      if (
        this.selectedNode &&
        this.selectedNode.data &&
        this.selectedNode.data.codeStatus !== null &&
        this.selectedNode.data.codeStatus == 2
      ) {
        this.$message.warning("谱系图已提交,不能删除");
        return;
      }
      this.$confirm("确定删除该节点吗?", "提示", {
        confirmButtonText: "确定",
        cancelButtonText: "取消",
        type: "warning",
      }).then(() => {
        // 判断是新增模式还是编辑模式
        if (!this.$route.query.id) {
          // 新增模式:前端直接删除节点
          this.deleteNodeFromGraph(this.selectedNode.id);
          this.$message.success("删除成功");
        } else {
          // 编辑模式:调用后端删除接口
          deleteNode({ id: this.selectedNode.id }).then((res) => {
            if (res.code == 200) {
              this.$message.success("删除成功");
              this.reloadData();
            }
          });
        }
      });
    },
    // 前端删除节点方法
    deleteNodeFromGraph(nodeId) {
      // 递归删除节点
      const deleteNodeRecursive = (nodes, targetId) => {
        for (let i = 0; i < nodes.length; i++) {
          if (nodes[i].id === targetId) {
            // 找到目标节点,直接删除
            nodes.splice(i, 1);
            return true;
          }
          // 在子节点中查找
          if (nodes[i].children && nodes[i].children.length > 0) {
            if (deleteNodeRecursive(nodes[i].children, targetId)) {
              // 如果删除的是传代计划数的子节点(母代),需要更新计划数的当前数量
              if (nodes[i].label === '传代计划数') {
                nodes[i].currentCount = nodes[i].children.length;
              }
              return true;
            }
          }
        }
        return false;
      };
      // 执行删除
      const deleted = deleteNodeRecursive(this.graphData.nodes, nodeId);
      if (deleted) {
        // 清空选中节点
        this.selectedNode = null;
        // 转换树形数据为G6可用的格式
        const convertToG6Data = (nodes) => {
          const g6Nodes = [];
          const g6Edges = [];
          const processNode = (node, parentId = null) => {
            g6Nodes.push({
              id: node.id,
              label: node.label,
              number: node.number,
              data: node.data,
              isDiscarded: node.isDiscarded,
              style: node.style,
              planCount: node.planCount,
              currentCount: node.currentCount,
            });
            if (parentId) {
              g6Edges.push({
                source: parentId,
                target: node.id,
                style: {
                  stroke: "rgba(4, 156, 154, 1)",
                  lineWidth: 1,
                },
              });
            }
            if (node.children && node.children.length > 0) {
              node.children.forEach((child) => processNode(child, node.id));
            }
          };
          nodes.forEach((node) => processNode(node));
          return { nodes: g6Nodes, edges: g6Edges };
        };
        // 更新图表
        const g6Data = convertToG6Data(this.graphData.nodes);
        this.graph.changeData(g6Data);
        this.graph.render();
        this.graph.fitView();
      }
    },
    handleEdit() {
      console.log('handleEdit selectedNode', this.selectedNode)
      if (!this.selectedNode) {
        this.$message.warning("请先选择节点");
        return;
      }
      if (
        this.selectedNode &&
        this.selectedNode.data &&
        this.selectedNode.data.codeStatus != null &&
        this.selectedNode.data.codeStatus == 2
      ) {
        this.$message.warning("谱系图已提交,不能编辑");
        return
      }
      const nodeModel = this.selectedNode;
      if (nodeModel.label === "祖代") {
        this.$refs.addAncestor.openInitData({
          id: nodeModel.id,
          ...nodeModel.data,
          formStatus: "edit",
        });
      } else if (nodeModel.label === "母代") {
        nodeModel.data.strainCode1 = this.form.strainCode;
        nodeModel.data.strainName1 = this.form.strainName;
        this.$refs.addSublevelForm.openInitData({
          title: "编辑母代",
          form: { ...nodeModel.data },
          formStatus: "edit",
        });
      } else if (nodeModel.label === "传代计划数") {
        this.$refs.planForm.openInitData({
          ...nodeModel.data,
          formStatus: "edit",
        });
      }
    },
    handleConfirm(row) {
      this.comfirmId = row.id;
      this.storageVisible = true;
    },
    // 处理每页显示数量变化
    handleSizeChange(val) {
      this.size = val;
      this.queryForm.pageSize = val;
      this.getList();
    },
    // 处理页码变化
    handleCurrentChange(val) {
      this.current = val;
      this.queryForm.pageNum = val;
      this.getList();
    },
    async getList() {
      try {
        // 获取列表数据
        const listRes = await pageListDetail({
          id: this.$route.query.id,
          current: this.current,
          size: this.size,
        });
        if (listRes.code === 200) {
          this.tableData = listRes.data.records;
          this.total = listRes.data.total;
          this.current = listRes.data.current;
          this.size = listRes.data.size;
        }
        // ... 其他现有的 reloadData 代码 ...
      } catch (error) {
        console.error("Failed to reload data:", error);
        this.$message.error("数据加载失败");
      }
    },
    handleNewConfirm(row) {
      console.log("row row", row);
      this.$refs.approvalCount.openInitData(row);
    },
    // 处理每页显示数量变化
    handleNewSizeChange(val) {
      this.newSize = val;
      this.newQueryForm.pageSize = val;
      this.getNewList();
    },
    // 处理页码变化
    handleNewCurrentChange(val) {
      this.newCurrent = val;
      this.newQueryForm.pageNum = val;
      this.getNewList();
    },
    async getNewList() {
      try {
        // 获取列表数据
        const listRes = await pageListCountDetail({
          id: this.$route.query.id,
          current: this.current,
          size: this.size,
        });
        console.log("待确认传代计划数 待确认传代计划数", listRes);
        if (listRes.code === 200) {
          this.newTableData = listRes.data.records;
          this.newTotal = listRes.data.total;
          this.newCurrent = listRes.data.current;
          this.newSize = listRes.data.size;
        }
        // ... 其他现有的 reloadData 代码 ...
      } catch (error) {
        console.error("Failed to reload data:", error);
        this.$message.error("数据加载失败");
      }
    },
    async reloadData() {
      this.resetPageState();
      this.resetMountNode();
      const graphLoadId = this.graphLoadId;
      const routeKey = this.$route.fullPath;
      await this.getList();
      await this.getNewList();
      if (graphLoadId !== this.graphLoadId || routeKey !== this.$route.fullPath) {
        return;
      }
      if (!this.$route.query.id) {
        return
      }
      getDetail({ id: this.$route.query.id }).then((res) => {
        if (graphLoadId !== this.graphLoadId || routeKey !== this.$route.fullPath) {
          return;
        }
        this.projectData = res.tprojectTeamList
        const detailData = res.pedigreeChartParentAllDetailDTO;
        // 设置基础表单数据
        this.form = {
          strainCode: res.strainCode,
          strainName: res.strainName,
          generationType: "2",
        };
        // 递归处理节点数据,构建树形结构
        function processNode(node, parentType = null) {
          // label 逻辑与新增时保持一致
         let roleType= Number(JSON.parse(sessionStorage.getItem("userInfo")).roleType || 1)
            let label = "";
            if (node.type === 2) {
              label = "传代计划数";
            } else if (parentType === 2) {
              label = "母代";
            } else {
              label = "祖代";
            }
            const processedNode = {
              id: node.id,
              label,
              number: node.strainCode,
              data: node,
              isDiscarded: node.status === 1,
              style: {
                fill: node.status === 1 ? "#00B5AA" : "#999",
                opacity: node.status === 1 ? 0.6 : 0.3,
              },
              children: [],
            };
            if (node.type === 2) {
              processedNode.planCount = node.generationCount;
              processedNode.currentCount = node.children
                ? node.children.length
                : 0;
            }
            if (node.children && node.children.length > 0) {
              // 审批人需要查看完整谱系,包括仍处于保存状态的节点。
              processedNode.children = node.children
                .filter((child) => roleType === 2 || child.codeStatus == 2 || (child.codeStatus == 1 && roleType === 3))
                .map((child) => processNode(child, node.type));
            }
            return processedNode;
        }
        // 构建完整的树形结构,添加空值校验
        this.graphData.nodes = detailData?.one
          ? detailData.one.map((node) => processNode(node))
          : [];
        // 在数据加载完成后重新渲染图表
        this.$nextTick(() => {
          console.log('this.graphData.nodes',this.graphData.nodes)
          if (this.graph && this.graphData.nodes.length > 0) {
            const g6Data = this.convertToG6Data(this.graphData.nodes);
            this.graph.changeData(g6Data);
            this.graph.render();
            this.graph.fitView();
          }
        });
      });
    },
    // 转换树形数据为G6可用的格式
    convertToG6Data(nodes) {
      const g6Nodes = [];
      const g6Edges = [];
      const processNode = (node, parentId = null) => {
        g6Nodes.push({
          id: node.id,
          label: node.label,
          number: node.number,
          data: node.data,
          isDiscarded: node.isDiscarded,
          style: node.style,
          planCount: node.planCount,
          currentCount: node.currentCount,
        });
        if (parentId) {
          g6Edges.push({
            source: parentId,
            target: node.id,
            style: {
              stroke: "rgba(4, 156, 154, 1)",
              lineWidth: 1,
            },
          });
        }
        if (node.children && node.children.length > 0) {
          node.children.forEach((child) => processNode(child, node.id));
        }
      };
      nodes.forEach((node) => processNode(node));
      return { nodes: g6Nodes, edges: g6Edges };
    },
    addNodeSign(value, type) {
      this.nodeData = value
      this.nodeType = type
      this.nodeData = value;
      //todo 同步画布数据 nodeData.strainType
      this.nodeType = type;
      // this.$refs.addAncestor.closeDialog()
      this.$refs.addSublevelForm.closeDialog();
      this.confirmStorageDialogVisible = true;
    },
    handleSubmit() {
    // 传代计划数  审批
    sureApprovalCountSign(value) {
      console.log("value", value);
      confirmCount({
        id: value.id,
        preserveSignature: value.preserveSignature,
      }).then((res) => {
        if (res.code == 200) {
          this.$message.success("审批传代计划数成功");
          this.$refs.approvalCount.closeDialog();
          this.reloadData();
        }
      });
    },
    handleSubmit(type) {
      this.$refs.pedigreeForm.validate((valid) => {
        if (valid) {
          this.confirmStorageDialogVisible = true;
          // 递归处理节点数据,将data对象的内容提取到上一级并删除data属性
          const processNodeData = (nodes, type) => {
            return nodes.map((node) => {
              const { data, children, ...rest } = node;
              const processedNode = {
                ...rest,
                ...data, // 将data对象的内容展开到当前节点
                codeStatus: type == 2 ? '2' : data.codeStatus || type,
                children: children ? processNodeData(children, type) : [],
              };
              return processedNode;
            });
          };
          // 获取基础表单数据
          let baseData = {
            parent: { ...this.form, status: type },
            one: processNodeData(this.graphData.nodes, type),
          };
          console.log("baseData baseData", baseData);
          // return
          if (this.$route.query.id) {
            baseData.parent.id = this.$route.query.id;
            edit({ ...baseData, id: this.$route.query.id }).then((res) => {
              if (res.code == 200) {
                this.$message.success("编辑成功");
                this.$closeCurrentView('/strain/pedigree-vhart');
              }
            });
            return;
          }
          add(baseData).then((res) => {
            if (res.code == 200) {
              this.$message.success("保存成功");
              this.$closeCurrentView('/strain/pedigree-vhart');
            }
          });
        }
      });
    },
    handleDraft() {
      // 实现存草稿逻辑
      console.log("save draft", this.form);
    },
    handleCancel() {
      this.$router.back();
      this.$closeCurrentView('/strain/pedigree-vhart');
    },
    handleSignatureConfirm(signatureImage) {
      this.confirmStorageDialogVisible = false;
      console.log("submit form with signature:", signatureImage);
      this.nodeData.vaccinateSignature = signatureImage.signature;
      if (this.nodeType === 1) {
        this.handleAddParent(this.nodeData)
        this.handleAddParent(this.nodeData);
      } else if (this.nodeType === 2) {
        this.handleAddPlan(this.nodeData)
        this.handleAddPlan(this.nodeData);
      } else if (this.nodeType === 3) {
        this.handleAddSublevel(this.nodeData)
        this.handleAddSublevel(this.nodeData);
      }
      // 处理提交逻辑
    },
    handleSignatureConfirm1(signatureImage) {
      confirmStorage({
        id: this.comfirmId,
        preserveSignature: signatureImage.signature,
      }).then((res) => {
        if (res.code == 200) {
          this.$message.success("确认入库成功");
          this.reloadData();
        }
      });
    },
    addNode() {
      // 如果没有节点,新增祖代
      console.log("选择的节点", this.selectedNode);
      // if (this.selectedNode && this.selectedNode.data && this.selectedNode.data.auditStatus && this.selectedNode.data.auditStatus != 2) {
      //   this.$message.warning("该传代计划数未审核,请先审核");
      //   return;
      // }
      if (
        this.selectedNode &&
        this.selectedNode.data &&
        this.selectedNode.data.auditStatus !== null &&
        this.selectedNode.data.auditStatus != 2
      ) {
        this.$message.warning("该传代计划数未审核,请先审核");
        return;
      }
      if (this.graphData.nodes.length === 0) {
        this.$refs.addAncestor.openInitData({
          strainName: this.form.strainName,
          strainNo: this.form.strainNo,
          status: 'add',
          activeType: 1,
          isDiscarded: true,
        this.$refs.pedigreeForm.validate((valid) => {
          if (valid) {
            this.$refs.addAncestor.openInitData({
              source: "", // 来源获得/菌落编号
              strainCode: "", // 菌种编号
              strainName: "", // 菌种名称
              colonyNumber: "", // 菌落编号
              strainType: "1", // 1原始祖代菌株 2分离菌落 3祖代菌株
              status: 1, // 1保存 2废弃
              type: 1, // 固定为1
              isDiscarded: true, // 仅用于前端切换保存/废弃
              activeType: "1", // 仅用于前端切换类型
              confirmTime: "",
              formStatus: "add",
            });
          }
        });
        return
        return;
      }
      //
      if (
        !this.$route.query.id &&
        this.selectedNode &&
        this.selectedNode.label === "传代计划数"
      ) {
        this.$message.warning("一次只能添加一个传代");
        return;
      }
      // 如果选中了传代计划数节点,新增母代
      if (this.selectedNode && this.selectedNode.label === '传代计划数') {
      if (this.selectedNode && this.selectedNode.label === "传代计划数") {
        const nodeModel = this.selectedNode;
        // 检查是否已达到计划数
        if (nodeModel.currentCount >= nodeModel.planCount) {
          this.$message.warning('已达到计划数,不能再添加');
          this.$message.warning("已达到计划数,不能再添加");
          return;
        }
        // 获取父节点
        const parentEdge = this.graphData.edges.find(e => e.target === nodeModel.id);
        const parentNode = this.graphData.nodes.find(n => n.id === parentEdge.source);
        const findParentNode = (nodes) => {
          for (let node of nodes) {
            if (node.children && node.children.length > 0) {
              for (let child of node.children) {
                if (child.id === nodeModel.id) {
                  return node;
                }
              }
              const found = findParentNode(node.children);
              if (found) return found;
            }
          }
          return null;
        };
        const parentNode = findParentNode(this.graphData.nodes);
        // 如果父节点是母代,不允许添加
        if (parentNode.label === '母代') {
          this.$message.warning('母代节点不能再生成下一代');
        if (parentNode && parentNode.label === "母代") {
          this.$message.warning("母代节点不能再生成下一代");
          return;
        }
        this.showDiscarded = true;
        this.isAddingNode = true;
        this.$refs.addSublevelForm.openInitData({
          title: '新增菌种传代项',
          title: "新增菌种传代项",
          form: {
            isDiscarded: true,
            ...nodeModel.data
          }
        })
            strainName1: this.form.strainName,
            strainCode1: this.form.strainCode,
            status: 1,
            parentId: this.selectedNode.id,
          },
        });
      } else {
        this.$message.warning('请选择传代计划数节点');
        this.$message.warning("请选择传代计划数节点");
      }
    },
    handleAddParent(value) {
      console.log('handleAddParent 编辑修改', value);
      if (value.formStatus == "edit") {
        updateChild({ ...value, codeStatus: '1' }).then((res) => {
          if (res.code == 200) {
            this.$message.success("操作成功");
            this.$refs.addAncestor.closeDialog();
            // this.reloadData();
            // 1. 如果是编辑模式(前端编辑),先删除旧节点
            if (this.selectedNode) {
              // 删除旧节点
              this.deleteNodeFromGraph(this.selectedNode.id);
            }
          }
        });
        // return;
      }
      if (this.$route.query.id) {
        value.pedigreeChartId = this.$route.query.id;
        value.parentId = "0";
        addProgenitor({ ...value, codeStatus: '1' }).then((res) => {
          if (res.code == 200) {
            this.$message.success("操作成功");
            this.$refs.addAncestor.closeDialog();
            this.reloadData();
          }
        });
        return;
      }
      const parentId = `parent-${++this.nodeCount}`;
      this.graphData.nodes.push({
      const parentNode = {
        id: parentId,
        label: '祖代',
        number: value.inoculateNo.trim(),
        label: "祖代",
        number: value.strainCode.trim(),
        data: value,
        isDiscarded: true,
        isDiscarded: value.status == "1" ? true : false,
        x: 200,
        y: 200,
        style: {
          fill: '#00B5AA',
          fill: "#00B5AA",
        },
      });
        children: [],
      };
      this.graphData.nodes = [parentNode];
      this.graph.changeData(this.graphData);
      this.$message.success('祖代节点添加成功');
      this.$message.success("祖代节点添加成功");
      this.$refs.addAncestor.closeDialog();
    },
    handleDialogClose() {
      this.$refs.form.resetFields();
    },
    handleAddSublevel(value) {
      console.log('222222222222 编辑编辑', value)
      if (value.formStatus == "edit") {
        updateChild({ ...value, codeStatus: '1' }).then((res) => {
          if (res.code == 200) {
            this.$message.success("操作成功");
            this.$refs.addSublevelForm.closeDialog();
            this.reloadData();
          }
        });
        return;
      }
      if (this.$route.query.id) {
        addProgenitor({ ...value, codeStatus: '1' }).then((res) => {
          if (res.code === 200) {
            this.$message.success("操作成功");
            this.$refs.addSublevelForm.closeDialog();
            this.reloadData();
          }
        });
        return;
      }
      if (this.isAddingNode) {
        // 新增节点的处理逻辑
        const nodeModel = this.selectedNode;
        const childId = `child-${++this.nodeCount}`;
        this.graphData.nodes.push({
        const childNode = {
          id: childId,
          label: '母代',
          number: value.inoculateNo.trim(),
          isDiscarded: value.isDiscarded,
          label: "母代",
          number: value.strainCode.trim(),
          isDiscarded: value.status == "1" ? true : false,
          data: value,
          style: {
            fill: value.isDiscarded ? '#999' : '#00B5AA',
            opacity: value.isDiscarded ? 0.3 : 0.6,
            fill: value.status == "1" ? "#999" : "#00B5AA",
            opacity: value.status == "1" ? 0.3 : 0.6,
          },
        });
          children: [],
        };
        this.graphData.edges.push({
          source: nodeModel.id,
          target: childId,
          style: {
            stroke: 'rgba(4, 156, 154, 1)',
            lineWidth: 1,
          },
        });
        // 找到父节点并添加子节点
        const findAndAddChild = (nodes) => {
          for (let node of nodes) {
            if (node.id === nodeModel.id) {
              if (!node.children) {
                node.children = [];
              }
              node.children.push(childNode);
              node.currentCount = (node.currentCount || 0) + 1;
              return true;
            }
            if (node.children && node.children.length > 0) {
              if (findAndAddChild(node.children)) {
                return true;
              }
            }
          }
          return false;
        };
        const nodeIndex = this.graphData.nodes.findIndex(n => n.id === nodeModel.id);
        this.graphData.nodes[nodeIndex].currentCount++;
        findAndAddChild(this.graphData.nodes);
        this.graph.changeData(this.graphData);
        this.$message.success('母代添加成功');
        // 转换树形数据为G6可用的格式
        const convertToG6Data = (nodes) => {
          const g6Nodes = [];
          const g6Edges = [];
          const processNode = (node, parentId = null) => {
            g6Nodes.push({
              id: node.id,
              label: node.label,
              number: node.number,
              data: node.data,
              isDiscarded: node.isDiscarded,
              style: node.style,
              planCount: node.planCount,
              currentCount: node.currentCount,
            });
            if (parentId) {
              g6Edges.push({
                source: parentId,
                target: node.id,
                style: {
                  stroke: "rgba(4, 156, 154, 1)",
                  lineWidth: 1,
                },
              });
            }
            if (node.children && node.children.length > 0) {
              node.children.forEach((child) => processNode(child, node.id));
            }
          };
          nodes.forEach((node) => processNode(node));
          return { nodes: g6Nodes, edges: g6Edges };
        };
        const g6Data = convertToG6Data(this.graphData.nodes);
        this.graph.changeData(g6Data);
        this.$message.success("母代添加成功");
        this.$refs.addSublevelForm.closeDialog();
        this.isAddingNode = false;
      } else {
        // 编辑节点的处理逻辑
        const nodeModel = this.selectedNode;
        const nodeIndex = this.graphData.nodes.findIndex(n => n.id === nodeModel.id);
        if (nodeIndex > -1) {
          if (nodeModel.label === '传代计划数') {
            this.graphData.nodes[nodeIndex].planCount = parseInt(value.value);
          } else {
            this.graphData.nodes[nodeIndex].number = value.value.trim();
            if (this.showDiscarded) {
              this.graphData.nodes[nodeIndex].isDiscarded = value.isDiscarded;
              // 如果设置为废弃状态,同时废弃所有子节点
              if (value.isDiscarded) {
                const discardChildren = (parentId) => {
                  const childEdges = this.graphData.edges.filter(e => e.source === parentId);
                  childEdges.forEach(edge => {
                    const childNode = this.graphData.nodes.find(n => n.id === edge.target);
                    if (childNode) {
                      const childIndex = this.graphData.nodes.findIndex(n => n.id === childNode.id);
                      if (childIndex > -1) {
                        this.graphData.nodes[childIndex].isDiscarded = true;
                        this.graphData.nodes[childIndex].style.fill = '#999';
                        this.graphData.nodes[childIndex].style.opacity = 0.3;
                        // 递归处理子节点的子节点
                        discardChildren(childNode.id);
                      }
                    }
                  });
                };
                discardChildren(nodeModel.id);
        const updateNode = (nodes) => {
          for (let node of nodes) {
            if (node.id === nodeModel.id) {
              if (nodeModel.label === "传代计划数") {
                node.planCount = parseInt(value.value);
              } else {
                node.number = value.value.trim();
                if (this.showDiscarded) {
                  node.isDiscarded = value.isDiscarded;
                  // 如果设置为废弃状态,同时废弃所有子节点
                  if (value.isDiscarded && node.children) {
                    const discardChildren = (children) => {
                      children.forEach((child) => {
                        child.isDiscarded = true;
                        child.style.fill = "#999";
                        child.style.opacity = 0.3;
                        if (child.children) {
                          discardChildren(child.children);
                        }
                      });
                    };
                    discardChildren(node.children);
                  }
                }
              }
              return true;
            }
            if (node.children && node.children.length > 0) {
              if (updateNode(node.children)) {
                return true;
              }
            }
          }
          this.graph.changeData(this.graphData);
          this.$message.success('修改成功');
          this.dialogVisible = false;
        }
          return false;
        };
        updateNode(this.graphData.nodes);
        // 转换树形数据为G6可用的格式
        const convertToG6Data = (nodes) => {
          const g6Nodes = [];
          const g6Edges = [];
          const processNode = (node, parentId = null) => {
            g6Nodes.push({
              id: node.id,
              label: node.label,
              number: node.number,
              data: node.data,
              isDiscarded: node.isDiscarded,
              style: node.style,
              planCount: node.planCount,
              currentCount: node.currentCount,
            });
            if (parentId) {
              g6Edges.push({
                source: parentId,
                target: node.id,
                style: {
                  stroke: "rgba(4, 156, 154, 1)",
                  lineWidth: 1,
                },
              });
            }
            if (node.children && node.children.length > 0) {
              node.children.forEach((child) => processNode(child, node.id));
            }
          };
          nodes.forEach((node) => processNode(node));
          return { nodes: g6Nodes, edges: g6Edges };
        };
        const g6Data = convertToG6Data(this.graphData.nodes);
        this.graph.changeData(g6Data);
        this.$message.success("修改成功");
        this.dialogVisible = false;
      }
    },
    setGenerationPlan() {
      if (this.newTableData.length != 0) {
        this.$message.warning("有未审核的待确认传代计划数,请先审核");
        return;
      }
      if (!this.selectedNode) {
        this.$message.warning('请先选择节点');
        this.$message.warning("请先选择节点");
        return;
      }
      const nodeModel = this.selectedNode;
      if (nodeModel.label === '母代') {
        this.$message.warning('母代节点不能再生成传代计划数');
      if (nodeModel.label === "母代") {
        this.$message.warning("母代节点不能再生成传代计划数");
        return;
      }
      if (nodeModel.label === '传代计划数') {
        this.$message.warning('传代计划数节点不能再设置计划数');
      if (nodeModel.label === "传代计划数") {
        this.$message.warning("传代计划数节点不能再设置计划数");
        return;
      }
      const hasGenerationNode = this.graphData.edges.some(e =>
        e.source === nodeModel.id &&
        this.graphData.nodes.some(n => n.id === e.target && n.label === '传代计划数')
      const hasGenerationNode = this.graphData.edges.some(
        (e) =>
          e.source === nodeModel.id &&
          this.graphData.nodes.some(
            (n) => n.id === e.target && n.label === "传代计划数"
          )
      );
      if (hasGenerationNode) {
        this.$message.warning('该节点已经存在传代计划数节点');
        this.$message.warning("该节点已经存在传代计划数节点");
        return;
      }
      this.$refs.planForm.openInitData({
        ...nodeModel.data,
        label: nodeModel.label,
        strainName: this.form.strainName,
        strainNo: this.form.strainNo,
      })
        formStatus: "add",
      });
    },
    handleAddPlan(value) {
      if (this.$route.query.id) {
        console.log('value', value)
        if (value.formStatus == "add") {
          let params = {
            parentId: value.id,
            generationCount: value.generationCount,
            vaccinateSignature: value.vaccinateSignature,
          };
          addCount({ ...params, codeStatus: '1' }).then((res) => {
            if (res.code === 200) {
              this.$message.success("操作成功");
              this.$refs.planForm.closeDialog();
              this.reloadData();
            }
          });
        } else {
          let params = {
            id: value.id,
            generationCount: value.generationCount,
            vaccinateSignature: value.vaccinateSignature,
          };
          addProgenitorChild({ ...params, codeStatus: '1' }).then((res) => {
            if (res.code === 200) {
              this.$message.success("操作成功");
              this.$refs.planForm.closeDialog();
              this.reloadData();
            }
          });
        }
        return;
      }
      const nodeModel = this.selectedNode;
      const generationId = `generation-${++this.nodeCount}`;
      this.graphData.nodes.push({
      const generationNode = {
        id: generationId,
        label: '传代计划数',
        planCount: value.count,
        label: "传代计划数",
        planCount: value.generationCount,
        data: value,
        isDiscarded: true,
        currentCount: 0,
        style: {
          fill: '#00B5AA',
          fill: "#00B5AA",
        },
      });
        children: [],
      };
      this.graphData.edges.push({
        source: nodeModel.id,
        target: generationId,
        style: {
          stroke: 'rgba(4, 156, 154, 1)',
          lineWidth: 1,
        },
      });
      // 找到父节点并添加子节点
      const findAndAddChild = (nodes) => {
        for (let node of nodes) {
          if (node.id === nodeModel.id) {
            if (!node.children) {
              node.children = [];
            }
            node.children.push(generationNode);
            return true;
          }
          if (node.children && node.children.length > 0) {
            if (findAndAddChild(node.children)) {
              return true;
            }
          }
        }
        return false;
      };
      this.graph.changeData(this.graphData);
      this.$message.success('传代计划数设置成功');
      this.$refs.planForm.closeDialog()
      findAndAddChild(this.graphData.nodes);
      // 转换树形数据为G6可用的格式
      const convertToG6Data = (nodes) => {
        const g6Nodes = [];
        const g6Edges = [];
        const processNode = (node, parentId = null) => {
          g6Nodes.push({
            id: node.id,
            label: node.label,
            number: node.number,
            data: node.data,
            isDiscarded: node.isDiscarded,
            style: node.style,
            planCount: node.planCount,
            currentCount: node.currentCount,
          });
          if (parentId) {
            g6Edges.push({
              source: parentId,
              target: node.id,
              style: {
                stroke: "rgba(4, 156, 154, 1)",
                lineWidth: 1,
              },
            });
          }
          if (node.children && node.children.length > 0) {
            node.children.forEach((child) => processNode(child, node.id));
          }
        };
        nodes.forEach((node) => processNode(node));
        return { nodes: g6Nodes, edges: g6Edges };
      };
      const g6Data = convertToG6Data(this.graphData.nodes);
      this.graph.changeData(g6Data);
      this.$message.success("传代计划数设置成功");
      this.$refs.planForm.closeDialog();
    },
    showDetail() {
      if (!this.selectedNode) {
        this.$message.warning('请先选择节点');
        this.$message.warning("请先选择节点");
        return;
      }
      const nodeModel = this.selectedNode;
      if (nodeModel.label === '祖代') {
        this.$refs.addAncestor.openInitData({ ...nodeModel.data, status: 'detail' });
      } else if (nodeModel.label === '母代') {
        this.dialogTitle = '母代详情';
      if (nodeModel.label === "祖代") {
        this.$refs.addAncestor.openInitData({
          ...nodeModel.data,
          formStatus: "detail",
        });
      } else if (nodeModel.label === "母代") {
        nodeModel.data.strainCode1 = this.form.strainCode;
        nodeModel.data.strainName1 = this.form.strainName;
        this.dialogTitle = "母代详情";
        this.$refs.addSublevelForm.openInitData({
          title: '母代详情',
          form: { ...nodeModel.data }
        })
      } else if (nodeModel.label === '传代计划数') {
        this.$refs.planForm.openInitData({ ...nodeModel.data, status: 'detail' });
          title: "母代详情",
          form: { ...nodeModel.data },
        });
      } else if (nodeModel.label === "传代计划数") {
        // this.$refs.planForm.openInitData({
        //   ...nodeModel.data,
        //   formStatus: "detail",
        // });
      }
    },
    initGraph() {
      const container = document.getElementById('mountNode');
      const container = this.$refs.mountNode;
      if (!container) return;
      const width = container.scrollWidth;
      const height = container.scrollHeight || 600;
      // 自定义节点
      G6.registerNode('custom-node', {
      G6.registerNode("custom-node", {
        draw(cfg, group) {
          const width = 120;
          const titleHeight = 30;
@@ -413,13 +1305,25 @@
          // 根据节点状态设置颜色
          const isDiscarded = !cfg.isDiscarded;
          const titleFill = isDiscarded ? 'rgba(245, 248, 250, 1)' : (cfg.selected ? 'l(0) 0:#0ACBCA 1:#049C9A' : 'l(0) 0:#0ACBCA 1:#049C9A');
          const contentFill = isDiscarded ? 'rgba(245, 248, 250, 1)' : (cfg.selected ? 'rgba(4,156,154,0.2)' : 'rgba(4,156,154,0.1)');
          const textFill = isDiscarded ? 'rgba(144, 147, 153, 1)' : '#049C9A';
          const stroke = isDiscarded ? '#DCDFE6' : (cfg.selected ? '#049C9A' : 'transparent');
          const titleFill = isDiscarded
            ? "rgba(245, 248, 250, 1)"
            : cfg.selected
              ? "l(0) 0:#0ACBCA 1:#049C9A"
              : "l(0) 0:#0ACBCA 1:#049C9A";
          const contentFill = isDiscarded
            ? "rgba(245, 248, 250, 1)"
            : cfg.selected
              ? "rgba(4,156,154,0.2)"
              : "rgba(4,156,154,0.1)";
          const textFill = isDiscarded ? "rgba(144, 147, 153, 1)" : "#049C9A";
          const stroke = isDiscarded
            ? "#DCDFE6"
            : cfg.selected
              ? "#049C9A"
              : "transparent";
          // 创建渐变
          const gradient = group.addShape('rect', {
          const gradient = group.addShape("rect", {
            attrs: {
              x: -width / 2,
              y: -totalHeight / 2,
@@ -427,15 +1331,15 @@
              height: titleHeight,
              radius: 20,
              fill: titleFill,
              cursor: 'move',
              cursor: "move",
              stroke: stroke,
              lineWidth: isDiscarded ? 1 : (cfg.selected ? 2 : 0),
              lineWidth: isDiscarded ? 1 : cfg.selected ? 2 : 0,
            },
            name: 'title-box',
            name: "title-box",
          });
          // 下部分 - 内容背景
          const contentBox = group.addShape('rect', {
          const contentBox = group.addShape("rect", {
            attrs: {
              x: -width / 2,
              y: -totalHeight / 2 + titleHeight + gap,
@@ -443,52 +1347,62 @@
              height: contentHeight,
              fill: contentFill,
              radius: 15,
              cursor: 'move',
              cursor: "move",
              stroke: stroke,
              lineWidth: isDiscarded ? 1 : (cfg.selected ? 2 : 0),
              lineWidth: isDiscarded ? 1 : cfg.selected ? 2 : 0,
            },
            name: 'content-box',
            name: "content-box",
          });
          // 标题文本
          if (cfg.label) {
            group.addShape('text', {
            group.addShape("text", {
              attrs: {
                text: cfg.label,
                x: 0,
                y: -totalHeight / 2 + titleHeight / 2,
                fill: isDiscarded ? 'rgba(144, 147, 153, 1)' : '#fff',
                fontSize: 12,
                textAlign: 'center',
                textBaseline: 'middle',
                fontWeight: 'bold',
                cursor: 'move',
                fill: isDiscarded ? "rgba(144, 147, 153, 1)" : "#fff",
                fontSize: 14,
                textAlign: "center",
                textBaseline: "middle",
                fontWeight: "bold",
                cursor: "move",
              },
              name: 'title-text',
              name: "title-text",
            });
          }
          // 内容文本
          let content = '';
          if (cfg.label === '传代计划数') {
          let content = "";
          if (cfg.label === "传代计划数") {
            content = `${cfg.planCount || 0}`;
          } else if (cfg.number) {
            content = cfg.label === '母代' ? `代传菌种编号:${cfg.number}` : `接种菌种编号:${cfg.number}`;
            // 获取类型名称
            let typeName = "接种菌种";
            if (cfg.data && cfg.data.strainType) {
              const typeMap = {
                1: "原始祖代菌株SO",
                2: "分离菌落 CO",
                3: "祖代菌株 O",
              };
              typeName = typeMap[cfg.data.strainType] || "接种菌种";
            }
            content = `${typeName}编号:${cfg.number}`;
          }
          if (content) {
            group.addShape('text', {
            group.addShape("text", {
              attrs: {
                text: content,
                x: 0,
                y: -totalHeight / 2 + titleHeight + gap + contentHeight / 2,
                fill: textFill,
                fontSize: 10,
                textAlign: 'center',
                textBaseline: 'middle',
                cursor: 'move',
                fontSize: 11,
                textAlign: "center",
                textBaseline: "middle",
                cursor: "move",
              },
              name: 'content-text',
              name: "content-text",
            });
          }
@@ -508,71 +1422,71 @@
      });
      this.graph = new G6.Graph({
        container: 'mountNode',
        container,
        width,
        height,
        fitView: true,
        fitViewPadding: 30,
        animate: false,
        enabledStack: false,
        renderer: 'canvas',
        renderer: "canvas",
        minZoom: 0.3,
        maxZoom: 2,
        defaultZoom: 1,
        layout: {
          type: 'dagre',
          rankdir: 'LR',
          align: 'UL',
          nodesep: 30,  // 减小节点间距
          ranksep: 50,  // 减小层级间距
          type: "dagre",
          rankdir: "LR",
          align: "UL",
          nodesep: 50,
          ranksep: 70,
          controlPoints: true,
        },
        modes: {
          default: [
            {
              type: 'drag-canvas',
              type: "drag-canvas",
              enableOptimize: true,
              direction: 'both',
              direction: "both",
              scalableRange: 0.1,
              dragTimesOfScale: 0.1,
              onlyChangeComputeZoom: true,
            },
            {
              type: 'zoom-canvas',
              type: "zoom-canvas",
              sensitivity: 1.5,
              enableOptimize: true,
            },
            {
              type: 'drag-node',
              type: "drag-node",
              enableDelegate: true,
              delegateStyle: {
                fill: '#f3f3f3',
                stroke: '#ccc',
                fill: "#f3f3f3",
                stroke: "#ccc",
                opacity: 0.5,
              },
              updateEdge: false,
              enableOptimize: true,
              optimizeZoom: 0.7,
              damping: 0.1,
            }
          ]
            },
          ],
        },
        defaultNode: {
          type: 'custom-node',
          type: "custom-node",
          style: {
            fill: 'l(0) 0:#0ACBCA 1:#049C9A',
            fill: "l(0) 0:#0ACBCA 1:#049C9A",
          },
        },
        defaultEdge: {
          type: 'cubic-horizontal',
          type: "cubic-horizontal",
          style: {
            stroke: 'rgba(4, 156, 154, 1)',
            stroke: "rgba(4, 156, 154, 1)",
            lineWidth: 1,
            opacity: 0.5,
            endArrow: {
              path: G6.Arrow.triangle(6, 6),
              fill: 'rgba(4, 156, 154, 1)',
              stroke: 'rgba(4, 156, 154, 1)',
              fill: "rgba(4, 156, 154, 1)",
              stroke: "rgba(4, 156, 154, 1)",
            },
          },
        },
@@ -580,61 +1494,63 @@
        optimizeLayoutAnimation: true,
      });
      const canvas = this.graph.get('canvas');
      canvas.set('localRefresh', false);
      canvas.set('autoDraw', true);
      canvas.set('animating', false);
      const canvas = this.graph.get("canvas");
      canvas.set("localRefresh", false);
      canvas.set("autoDraw", true);
      canvas.set("animating", false);
      let throttleTimer = null;
      const throttleInterval = 16;
      this.graph.on('node:dragstart', () => {
        canvas.set('localRefresh', false);
        this.graph.get('canvas').draw();
      this.graph.on("node:dragstart", () => {
        canvas.set("localRefresh", false);
        this.graph.get("canvas").draw();
      });
      this.graph.on('node:drag', (e) => {
      this.graph.on("node:drag", (e) => {
        if (throttleTimer) return;
        throttleTimer = setTimeout(() => {
          const model = e.item.get('model');
          const edges = this.graph.getEdges().filter(edge => {
          const model = e.item.get("model");
          const edges = this.graph.getEdges().filter((edge) => {
            const source = edge.getSource();
            const target = edge.getTarget();
            return source.get('id') === model.id || target.get('id') === model.id;
            return (
              source.get("id") === model.id || target.get("id") === model.id
            );
          });
          edges.forEach(edge => {
          edges.forEach((edge) => {
            this.graph.refreshItem(edge);
          });
          throttleTimer = null;
        }, throttleInterval);
      });
      this.graph.on('node:dragend', (e) => {
      this.graph.on("node:dragend", (e) => {
        if (throttleTimer) {
          clearTimeout(throttleTimer);
          throttleTimer = null;
        }
        const model = e.item.get('model');
        const edges = this.graph.getEdges().filter(edge => {
        const model = e.item.get("model");
        const edges = this.graph.getEdges().filter((edge) => {
          const source = edge.getSource();
          const target = edge.getTarget();
          return source.get('id') === model.id || target.get('id') === model.id;
          return source.get("id") === model.id || target.get("id") === model.id;
        });
        edges.forEach(edge => {
        edges.forEach((edge) => {
          this.graph.refreshItem(edge);
        });
        canvas.set('localRefresh', true);
        this.graph.get('canvas').draw();
        canvas.set("localRefresh", true);
        this.graph.get("canvas").draw();
      });
      this.graph.data(this.graphData);
      this.graph.render();
      let debounceTimer = null;
      this.graph.on('afterchange', () => {
      this.graph.on("afterchange", () => {
        if (debounceTimer) clearTimeout(debounceTimer);
        debounceTimer = setTimeout(() => {
          if (!canvas.get('destroyed')) {
          if (!canvas.get("destroyed")) {
            canvas.draw();
          }
        }, 16);
@@ -642,41 +1558,136 @@
    },
    initEvents() {
      // 监听窗口大小变化
      window.addEventListener('resize', this.handleResize);
      window.addEventListener("resize", this.handleResize);
      // 节点点击事件
      this.graph.on('node:click', (evt) => {
      const handleNodeClick = (evt) => {
        evt.preventDefault(); // 阻止默认触摸行为
        const node = evt.item;
        const nodeModel = node.getModel();
        // 如果节点已废弃,不允许任何操作
        if (!nodeModel.isDiscarded) {
          this.$message.warning('该节点已废弃,不能进行操作');
          return;
        }
        // 如果节点已废弃,不允许任何操作    放开
        // if (!nodeModel.isDiscarded) {
        //   this.$message.warning("该节点已废弃,不能进行操作");
        //   return;
        // }
        // 更新选中节点
        this.selectedNode = nodeModel;
        // 更新节点选中状态
        this.graphData.nodes.forEach(n => {
          n.selected = n.id === nodeModel.id;
        });
        this.graph.changeData(this.graphData);
      });
        const updateNodeSelected = (nodes) => {
          for (let node of nodes) {
            node.selected = node.id === nodeModel.id;
            if (node.children && node.children.length > 0) {
              updateNodeSelected(node.children);
            }
          }
        };
        updateNodeSelected(this.graphData.nodes);
      // 画布点击事件,取消选中节点
      this.graph.on('canvas:click', () => {
        // 转换树形数据为G6可用的格式
        const convertToG6Data = (nodes) => {
          const g6Nodes = [];
          const g6Edges = [];
          const processNode = (node, parentId = null) => {
            g6Nodes.push({
              id: node.id,
              label: node.label,
              number: node.number,
              data: node.data,
              isDiscarded: node.isDiscarded,
              style: node.style,
              planCount: node.planCount,
              currentCount: node.currentCount,
              selected: node.selected,
            });
            if (parentId) {
              g6Edges.push({
                source: parentId,
                target: node.id,
                style: {
                  stroke: "rgba(4, 156, 154, 1)",
                  lineWidth: 1,
                },
              });
            }
            if (node.children && node.children.length > 0) {
              node.children.forEach((child) => processNode(child, node.id));
            }
          };
          nodes.forEach((node) => processNode(node));
          return { nodes: g6Nodes, edges: g6Edges };
        };
        const g6Data = convertToG6Data(this.graphData.nodes);
        this.graph.changeData(g6Data);
      };
      this.graph.on("node:click", handleNodeClick);
      this.graph.on("node:touchstart", handleNodeClick);
      // 画布点击事件,取消选中节点(添加触摸支持)
      const handleCanvasClick = (evt) => {
        evt.preventDefault();
        this.selectedNode = null;
        this.graphData.nodes.forEach(n => {
          n.selected = false;
        });
        this.graph.changeData(this.graphData);
      });
        const updateNodeSelected = (nodes) => {
          for (let node of nodes) {
            node.selected = false;
            if (node.children && node.children.length > 0) {
              updateNodeSelected(node.children);
            }
          }
        };
        updateNodeSelected(this.graphData.nodes);
        // 转换树形数据为G6可用的格式
        const convertToG6Data = (nodes) => {
          const g6Nodes = [];
          const g6Edges = [];
          const processNode = (node, parentId = null) => {
            g6Nodes.push({
              id: node.id,
              label: node.label,
              number: node.number,
              data: node.data,
              isDiscarded: node.isDiscarded,
              style: node.style,
              planCount: node.planCount,
              currentCount: node.currentCount,
              selected: node.selected,
            });
            if (parentId) {
              g6Edges.push({
                source: parentId,
                target: node.id,
                style: {
                  stroke: "rgba(4, 156, 154, 1)",
                  lineWidth: 1,
                },
              });
            }
            if (node.children && node.children.length > 0) {
              node.children.forEach((child) => processNode(child, node.id));
            }
          };
          nodes.forEach((node) => processNode(node));
          return { nodes: g6Nodes, edges: g6Edges };
        };
        const g6Data = convertToG6Data(this.graphData.nodes);
        this.graph.changeData(g6Data);
      };
      this.graph.on("canvas:click", handleCanvasClick);
      this.graph.on("canvas:touchstart", handleCanvasClick);
    },
    handleResize() {
      if (this.graph) {
        const container = document.getElementById('mountNode');
        const container = this.$refs.mountNode;
        if (!container) return;
        const width = container.scrollWidth;
        const height = container.scrollHeight || 600;
        this.graph.changeSize(width, height);
@@ -703,7 +1714,7 @@
  background: rgba(255, 255, 255, 0.8);
  box-shadow: 0px 10px 19px 0px rgba(0, 0, 0, 0.06);
  border-radius: 16px;
  border: 4px solid #FFFFFF;
  border: 4px solid #ffffff;
  margin-top: 30px;
  .header {
@@ -742,6 +1753,40 @@
  }
}
.header-title {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 13px;
  .header-title-left {
    display: flex;
    align-items: center;
    gap: 13px;
    img {
      width: 12px;
      height: 19px;
    }
    div {
      flex-shrink: 0;
      font-weight: bold;
      font-size: 18px;
      color: #222222;
      line-height: 27px;
      font-family: 'Source Han Sans CN Bold Bold';
      &:before {
        content: '*';
        color: #F56C6C;
        margin-right: 4px;
      }
    }
  }
}
.strain-form {
  width: 100%;
@@ -772,6 +1817,10 @@
  }
}
.title {
  font-size: 18px;
}
.end-btn {
  margin-top: 20px;
  display: flex;
@@ -795,11 +1844,11 @@
    }
  }
  #mountNode {
  .mount-node {
    flex: 1;
    width: 100%;
    min-height: 500px;
    border-radius: 4px;
  }
}
</style>
</style>