杨磊
2025-05-26 96be59a64cc1d8fcaf1034e787717663c68df4a7
src/components/TreeMenu.vue
@@ -1,108 +1,642 @@
<template>
  <div class="tree-menu">
    <div class="topMenu">
      <span class="topMenu-title">模型库</span>
      <div class="btnGroup">
        <el-icon class="icon1"><FolderAdd /></el-icon>
        <el-icon class="icon2"><Edit /></el-icon>
        <el-icon class="icon3"><Delete /></el-icon>
  <div class="tree-menu-box">
    <div class="flex">
      <div class="tree-menu" v-if="$route.name == 'landerModel'">
        <div class="topMenu">
          <span class="topMenu-title">{{ menuName }}</span>
          <div
            class="btnGroup"
            v-if="$route.name == 'landerModel' || $route.name == 'systemUse'"
          >
            <el-dropdown>
              <el-icon class="icon1"><FolderAdd /></el-icon>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item @click="addBtn('parent')"
                    >新建频道</el-dropdown-item
                  >
                  <el-dropdown-item
                    :disabled="selectData ? false : true"
                    @click="addBtn('child')"
                    >新建子频道</el-dropdown-item
                  >
                </el-dropdown-menu>
              </template>
            </el-dropdown>
            <el-icon class="icon2" @click="editBtn"><Edit /></el-icon>
            <el-icon class="icon3" @click="delBtn"><Delete /></el-icon>
          </div>
        </div>
        <div v-if="loadTree && $route.name == 'landerModel'">
          <el-tree
            ref="treeRef"
            :key="treeKey"
            :props="defaultProps"
            :highlight-current="true"
            @node-click="handleNodeClick"
            :current-node-key="currentNodeKey"
            node-key="key"
            :data="filteredData"
            :load="loadNode"
            :expand-on-click-node="false"
            lazy
          >
            <template #default="{ node, data }">
              <span class="custom-tree-node">
                <el-icon v-if="data.icon"
                  ><component :is="data.icon"
                /></el-icon>
                <span>{{ node.label }}</span>
              </span>
            </template>
          </el-tree>
        </div>
      </div>
      <div class="tree-menu" v-if="$route.name.includes('simulation')">
        <span class="topMenu-title">{{ menuName }}</span>
        <div
          class="btnGroup"
          v-if="$route.name == 'landerModel' || $route.name == 'systemUse'"
        >
          <el-dropdown>
            <el-icon class="icon1"><FolderAdd /></el-icon>
            <template #dropdown>
              <el-dropdown-menu>
                <el-dropdown-item @click="addBtn('parent')"
                  >新建频道</el-dropdown-item
                >
                <el-dropdown-item
                  :disabled="selectData ? false : true"
                  @click="addBtn('child')"
                  >新建子频道</el-dropdown-item
                >
              </el-dropdown-menu>
            </template>
          </el-dropdown>
          <el-icon class="icon2" @click="editBtn"><Edit /></el-icon>
          <el-icon class="icon3" @click="delBtn"><Delete /></el-icon>
        </div>
        <el-tree
          ref="treeRef"
          :key="treeKey"
          :data="visualSimulationTreeData"
          :props="defaultProps"
          node-key="key"
          :current-node-key="simulationKey"
          :filter-node-method="filterNode1"
          :highlight-current="true"
          default-expand-all
          @node-click="systemClick"
        >
          <template #default="{ node, data }">
            <span class="custom-tree-node">
              <el-icon v-if="data.icon"><component :is="data.icon" /></el-icon>
              <span>{{ node.label }}</span>
            </span>
          </template>
        </el-tree>
      </div>
      <div class="tree-menu" v-if="seleStore.curTab == 'systemManage'">
        <el-tree
          ref="treeRef"
          :data="systemManageTreeData"
          :props="defaultProps"
          :key="systemKey"
          :current-node-key="systemKey"
          node-key="key"
          :filter-node-method="filterNode1"
          default-expand-all
          @node-click="systemClick"
          :highlight-current="true"
        >
          <template #default="{ node, data }">
            <span class="custom-tree-node">
              <el-icon v-if="data.icon"><component :is="data.icon" /></el-icon>
              <span>{{ node.label }}</span>
            </span>
          </template>
        </el-tree>
      </div>
    </div>
    <el-tree
      ref="treeRef"
      :data="filteredData"
      :props="defaultProps"
      :filter-node-method="filterNode"
      default-expand-all
      @node-click="handleNodeClick"
    >
      <template #default="{ node, data }">
        <span class="custom-tree-node">
          <el-icon v-if="data.icon"><component :is="data.icon" /></el-icon>
          <span>{{ node.label }}</span>
        </span>
      </template>
    </el-tree>
  </div>
  <el-dialog
    v-model="dialogFormVisible"
    :title="dialogTitle"
    width="500"
    @close="closeDialog(formRef)"
  >
    <el-form :model="form" ref="formRef" :rules="formRules" label-width="140px">
      <el-form-item label="名称" prop="name">
        <el-input
          v-model="form.name"
          autocomplete="off"
          placeholder="请输入"
          style="width: 240px"
        />
      </el-form-item>
      <el-form-item label="编码" prop="refCode">
        <el-input
          v-model="form.refCode"
          autocomplete="off"
          placeholder="请输入"
          style="width: 240px"
        />
      </el-form-item>
      <el-form-item label="描述">
        <el-input
          v-model="form.description"
          style="width: 240px"
          :rows="2"
          type="textarea"
          placeholder="请输入"
        />
      </el-form-item>
    </el-form>
    <template #footer>
      <div class="dialog-footer">
        <el-button @click="closeDialog(formRef)">取消</el-button>
        <el-button type="primary" @click="submitBtn(formRef)"> 确定 </el-button>
      </div>
    </template>
  </el-dialog>
</template>
<script setup lang="ts">
import { ref, watch, defineProps } from "vue";
import { useRouter } from "vue-router";
import {
  ref,
  reactive,
  defineProps,
  onMounted,
  inject,
  watch,
  nextTick,
} from "vue";
import { useRoute, useRouter } from "vue-router";
import type { FormInstance, FormRules } from "element-plus";
import { ElMessage, ElMessageBox } from "element-plus";
import { Document, FolderOpened } from "@element-plus/icons-vue";
import { fa } from "element-plus/es/locale";
const MG: any = inject("MG");
const toolClass: any = inject("toolClass");
import { curStoreInfo } from "@/store/index";
const seleStore = curStoreInfo();
const router = useRouter();
const treeRef = ref();
const loadTree = ref(false);
const treeRef = ref(null);
const curSelectNode = ref();
const props = defineProps<{
  menuItem: string;
  modelTreeData: any;
  storeInfo: any;
}>();
const menuName = ref("模型库");
const filteredData = ref();
const systemMenuName = ref("");
const currentNodeKey = ref(1);
const treeKey = ref(0);
const systemKey = ref(0);
const simulationKey = ref(0);
const systemData = ref([]);
const selectData = ref();
const createParent = ref(false);
interface TreeNode {
  label: string;
  path?: string;
  icon?: string;
  children?: TreeNode[];
}
const modelTreeData = ref<TreeNode[]>([
const defaultProps = {
  children: "children",
  label: "label",
  isLeaf: "leaf",
  key: "key",
};
const route = useRoute();
const visualSimulationTreeData = ref<TreeNode[]>([
  {
    label: "型号",
    icon: "FolderOpened",
    label: "测试仿真",
    path: "/testSimulation",
    icon: "Document",
    key: 0,
  },
  {
    label: "实时仿真",
    path: "realTimeSimulation",
    icon: "Document",
    key: 1,
  },
  {
    label: "自主功能",
    path: "/autonomousFunction",
    icon: "Document",
    key: 2,
  },
]);
const systemManageTreeData = ref<TreeNode[]>([
  {
    key: 0,
    label: "机构用户",
    path: "/userManage",
    icon: "Document",
  },
  { key: 1, label: "角色权限管理", path: "/roleManage", icon: "Document" },
]);
const systemTreeData = ref<TreeNode[]>([
  {
    label: "501",
    path: "",
    icon: "Document",
    children: [
      {
        label: "着陆器模型库",
        path: "/model/landerModel",
        icon: "Document",
        label: "一室",
        path: "",
        icon: "",
      },
      {
        label: "巡视器模型库",
        path: "/model/roverModel",
        icon: "Document",
        label: "二室",
        path: "",
        icon: "",
      },
      {
        label: "飞跃器模型库",
        path: "/model/leapMachineModel",
        icon: "Document",
        label: "三室",
        path: "",
        icon: "",
      },
    ],
  },
]);
const systemTreeData1 = ref<TreeNode[]>([
  {
    label: "系统管理员",
    path: "",
    icon: "",
  },
  {
    label: "模型管理员",
    path: "",
    icon: "",
  },
  {
    label: "测试人员",
    path: "",
    icon: "",
  },
  {
    label: "报告查看人员",
    path: "",
    icon: "",
  },
]);
const defaultProps = {
  children: "children",
  label: "label",
const dialogFormVisible = ref(false);
const dialogTitle = ref();
const formRef = ref<FormInstance>();
const form = reactive({
  name: "",
  refCode: "",
  description: "",
});
interface formInfo {
  name: string;
}
const formRules = reactive<FormRules<formInfo>>({
  name: [{ required: true, message: "名称不能为空", trigger: "blur" }],
});
watch(
  () => route,
  (newRoute) => {
    console.log("路由变化:", newRoute.path);
    // 处理路由变化逻辑
  },
  { deep: true }
);
watch(
  () => seleStore.curTab, // 监听 reactive 对象(默认深度监听)
  (newVal) => {
    if (newVal) {
      if (seleStore.curTab == "model") {
        menuName.value = "模型库";
      } else if (seleStore.curTab == "simulation") {
        menuName.value = "可视化仿真";
        filteredData.value = visualSimulationTreeData.value;
      } else if (seleStore.curTab == "systemManage") {
        menuName.value = "系统管理";
        filteredData.value = systemManageTreeData.value;
      } else {
        filteredData.value = [];
      }
      systemMenuName.value = systemManageTreeData.value[0].label;
    }
  }
);
onMounted(() => {
  console.log(route, "route.route");
});
watch(
  () => props.storeInfo, // 监听 reactive 对象(默认深度监听)
  (newVal) => {
    if (newVal) {
      loadTree.value = true;
    }
  }
);
const loadNode = async (node, resolve) => {
  if (node.level == 0) {
    const treeData = await getTableData(0);
    const list = treeData.filter((item) => item.data.refCode !== "testReport");
    const testReport = treeData.find(
      (item) => item.data.refCode === "testReport"
    );
    console.log(list, "list");
    console.log(testReport, "testReport");
    nextTick(() => {
      currentNodeKey.value = 0; // 确保节点已渲染
      seleStore.setChannelInfo(list[0]);
      seleStore.setTestReportChannel(testReport);
      selectData.value = list[0];
    });
    return resolve(list);
  }
  if (node.childNodes.length > 0) {
    return resolve([]);
  }
  // 显示加载状态
  node.loading = true;
  const childNodes = await getTableData(node.data.data);
  resolve(childNodes);
};
const filteredData = ref(modelTreeData.value);
//处理树形结构
const handleTreeData = (datas: any[], parent, noTriggerSelect = false) => {
  let parentData = {};
  if (parent) {
    parentData = { ...parent, parent: null };
  } else {
    parentData = null;
  }
  const list = [];
  for (let i = 0; i < datas.length; i++) {
    const data = datas[i];
    const obj = {
      label: data.name,
      key: parentData ? parentData.key + "_" + i : i + "",
      namePath: parentData ? parentData.namePath + "\\" + data.name : data.name,
      icon: data.icon,
      data: data,
      parent: parentData,
      leaf: false,
      children: [],
    };
    if (data["children"] && data["children"].length) {
      obj.leaf = false;
      obj.children = handleTreeData(
        data["children"],
        {
          ...data,
          key: obj.key,
          namePath: obj.namePath,
        },
        i == 0 ? false : true // 如果有子数据处理,只有第一条数据需要展开和回调
      );
    } else {
      obj.leaf =
        !data["childrenChannelCount"] || data["childrenChannelCount"] == 0;
    }
    list.push(obj);
  }
  console.log(list, "list");
  return list;
};
const getTableData = async (path) => {
  const treeData = await toolClass.getCmsItem({
    path: path.idPath
      ? path.idPath
      : props.storeInfo.repositoryData.rootChannel.id,
    storeId: props.storeInfo.storeId,
    repositoryId: props.storeInfo.repositoryId,
    type: "\\",
    sort: {
      LinkOrder: "Asc",
    },
    paging: {
      Start: 0,
      Size: 50,
    },
    filters: {
      "SysType=": ["CmsChannel"],
    },
    fields: {
      ChildrenCount: [],
    },
  });
  if (treeData && treeData.datas.length > 0) {
    const fomartData = handleTreeData(
      treeData.datas,
      path ? path : null,
      false
    );
    seleStore.setChannelInfo(fomartData[0]);
    seleStore.setChannelList(fomartData);
    return fomartData;
  } else {
    return [];
  }
};
const filterNode = (value: string, data: TreeNode) => {
  if (!value) return true;
  return data.label.toLowerCase().includes(value.toLowerCase());
};
const handleNodeClick = (data: TreeNode) => {
  if (data.path) {
    router.push(data.path);
  }
const filterNode1 = (value: string, data: TreeNode) => {
  if (!value) return true;
  return data.label.toLowerCase().includes(value.toLowerCase());
};
watch(
  () => props.menuItem,
  (value) => {
    if (value == "/" || value == "/model") {
      filteredData.value = modelTreeData.value;
    } else {
      filteredData.value = []
    }
const handleNodeClick = (data: TreeNode) => {
  seleStore.setChannelInfo(data);
  selectData.value = data;
};
//添加目录
const addBtn = (type) => {
  if (type == "child") {
    dialogTitle.value = "新建子频道";
    createParent.value = false;
  } else {
    dialogTitle.value = "新建频道";
    createParent.value = true;
  }
);
  dialogFormVisible.value = true;
};
const submitBtn = async (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  await formEl.validate(async (valid, fields) => {
    if (valid) {
      console.log(form.name, "fields");
      if (dialogTitle.value == "编辑") {
        const body = {
          accessPath: selectData.value.data.idPath,
          accessStoreId: selectData.value.data.storeId,
          accessRepositoryId: selectData.value.data.repositoryId,
          accessItemId: selectData.value.data.id,
          name: form.name, // form.name,
          description: form.description,
          refCode: form.refCode,
          state: selectData.value.data.state,
          icon: selectData.value.data.icon,
          type: selectData.value.data.type,
        };
        const updateCmsItem = await MG.dps5.UpdateCmsItem({
          updateList: [body],
        });
        if (updateCmsItem) {
          ElMessage({
            message: "编辑成功",
            type: "success",
          });
          dialogFormVisible.value = false;
          form.name = "";
          form.refCode = "";
          form.description = "";
          treeKey.value += 1;
        }
      } else {
        const body = {
          accessPath: createParent.value
            ? props.storeInfo.repositoryData.rootChannel.id
            : selectData.value.data.idPath,
          accessStoreId: props.storeInfo.storeId,
          accessRepositoryId: props.storeInfo.repositoryId,
          sysType: "CmsChannel",
          linkType: "Link",
          cmsItemRequest: {
            ...form,
            state: "Normal",
            type: "Normal",
            module: "",
            accessType: "Public",
          },
        };
        const newCmsItem = await MG.dps5.NewCmsItem(body);
        if (newCmsItem) {
          ElMessage({
            message: "添加成功",
            type: "success",
          });
          dialogFormVisible.value = false;
          form.name = "";
          form.refCode = "";
          form.description = "";
          treeKey.value += 1;
        } else {
          ElMessage({
            message: "添加失败",
            type: "error",
          });
        }
      }
    }
  });
};
const closeDialog = (formEl: FormInstance | undefined) => {
  if (!formEl) return;
  formEl.resetFields();
  dialogFormVisible.value = false;
};
const editBtn = () => {
  dialogTitle.value = "编辑";
  form.name = selectData.value.label;
  form.refCode = selectData.value.data.refCode;
  form.description = selectData.value.data.description;
  dialogFormVisible.value = true;
};
const delBtn = () => {
  if (selectData.value.data.childrenCmsItemCount > 0) {
    ElMessage({
      message: "频道下还有其他资源,请先删除资源后重试!",
      type: "warning",
    });
    return false;
  }
  if (selectData.value.data.childrenChannelCount > 0) {
    ElMessage({
      message: "频道下还有子频道,请先删除子频道后重试!",
      type: "warning",
    });
    return false;
  }
  ElMessageBox.confirm("确定要删除选中的数据?", "删除频道", {
    confirmButtonText: "确定",
    cancelButtonText: "取消",
    type: "warning",
  })
    .then(() => {
      console.log(selectData.value, "selectData.value");
      let accessPath = "";
      let accessItemId = 0;
      if (!selectData.value.parent) {
        accessPath = props.storeInfo.repositoryData.rootChannel.id + "";
        accessItemId = props.storeInfo.repositoryData.rootChannel.id;
      } else {
        accessPath = selectData.value.parent.data.idPath;
        accessItemId = selectData.value.parent.data.id;
      }
      MG.dps5
        .DelCmsItemByList({
          accessPath,
          accessStoreId: selectData.value.data.storeId,
          accessRepositoryId: selectData.value.data.repositoryId,
          accessItemId,
          itemIds: [selectData.value.data.id],
        })
        .then((res) => {
          ElMessage({
            message: "删除成功",
            type: "success",
          });
          treeKey.value += 1;
        });
    })
    .catch(() => {});
};
const systemClick = (data: TreeNode) => {
  curSelectNode.value = data;
  router.push({
    path: "/systemManage" + data.path,
  });
  console.log(curSelectNode.value, "curSelectNode");
};
</script>
<style lang="less" scoped>
.tree-menu {
.tree-menu-box {
  height: 100%;
  background-color: #fff;
  .flex {
    height: 100%;
  }
}
.tree-menu {
  width: 260px;
  height: 100%;
  border-right: 1px solid #e9eef3;
  :deep(.el-tree) {
    padding: 10px;
  }