工业仿真(simulation)--前端(二)-资源管理器

展示

资源管理器如图-1

图-1

在资源管理器中用户最常用的部分就是下方的图层部分,也是资源管理器最重要的部分,熟悉pixso软件的同学都了解图层的主要作用是什么

图层下面的元素是和画布中的元素一一对应的,用户在修改名字,删除元素时,图层和画布都会同步修改,同时还一个很重要的功能,编组,如图二

图二

多选只需要按住ctrl或者shift,编组是将多个元素创建为一个元素,目的是为了方便用户管理,比如画布中如果有上千个元素,没有编组功能的话,管理就会及其混乱

除了编组外,我们还可以看到有锁定和解锁功能,这两个功能主要是和画布相关联,当选择锁定后,画布里面被锁定的元素,就不能与用户进行交互,比如拖拽,改变大小。

图层还有一个较为重要的功能就是改变元素的顺序,用户可以透过拖拽的方式改变元素顺序,当然元素顺序也是和画布绑定的

鉴于GIF图过于模糊,我就不再展示效果

代码

图层是一个较为复杂的功能,因为我并没有使用到任何的树组件,完全是靠自己以及GPT的帮忙,自主研发了一个tree结构组件,如果有需要,用户也可以自己复制粘贴,放到自己的项目中使用,该组件可以自定义图标,字体样式等等,话不多说,直接展示代码

tree组件

tree组件父级 VueTree.vue

html 复制代码
<template>
  <div class="tree-container">
    <tree-node
      v-for="node in treeData"
      :key="node.id"
      :node="node"
      :field-names="fieldNames"
      :level="0"
      @node-click="handleNodeClick"
      @switch-lock="emit('switchLock', $event)"
      @node-drop="emit('node-drop', $event)"
      @update-text="emit('update-text', $event)"
    />
  </div>
</template>

<script lang="ts" setup>
import { defineProps, defineEmits, watch, ref, provide } from 'vue'
import TreeNode from './TreeNode.vue'

const props = defineProps({
  data: {
    type: Array,
    required: true
  },
  fieldNames: {
    type: String,
    default: 'label'
  },
  parentSelect: {
    type: Array<string>
  }
})

const emit = defineEmits([
  'node-click',
  'selection-change',
  'switchLock',
  'node-drop',
  'update-text'
])

// 处理数据,确保每个节点都有children字段
const treeData = ref<any[]>([])
watch(
  props.data,
  () => {
    treeData.value = props.data.map((node: any) => ({
      ...node,
      children: node.children || []
    }))
  },
  {
    deep: true
  }
)
watch(
  () => props.parentSelect,
  (newVal) => {
    selectedNodes.value = new Set(props.parentSelect)
  }
)

// 多选状态管理
const selectedNodes = ref<Set<string>>(new Set())
const lastSelectedNode = ref<string | null>(null)

// 提供状态给子组件
provide('selectedNodes', selectedNodes)

// 扁平化树结构(用于shift选择)
const flattenTree = (nodes: any[], result: any[] = []): any[] => {
  nodes.forEach((node) => {
    result.push(node)
    if (node.children && node.children.length) {
      flattenTree(node.children, result)
    }
  })
  return result
}

const handleNodeClick = (data: { node: any; event: MouseEvent }): void => {
  const nodeId = data.node.id
  const allNodes = flattenTree(treeData.value)

  // Shift选择逻辑
  if (data.event.shiftKey && lastSelectedNode.value) {
    const allIds = allNodes.map((n) => n.id)
    const lastIndex = allIds.indexOf(lastSelectedNode.value)
    const currentIndex = allIds.indexOf(nodeId)

    if (lastIndex !== -1 && currentIndex !== -1) {
      const start = Math.min(lastIndex, currentIndex)
      const end = Math.max(lastIndex, currentIndex)
      const newSelection = allIds.slice(start, end + 1)

      // 保留Ctrl选择项,只添加区间
      if (data.event.ctrlKey) {
        selectedNodes.value = new Set(newSelection)
      } else {
        newSelection.forEach((id) => selectedNodes.value.add(id))
      }
    }
  }
  // Ctrl选择逻辑
  else if (data.event.ctrlKey) {
    if (selectedNodes.value.has(nodeId)) {
      selectedNodes.value.delete(nodeId)
    } else {
      selectedNodes.value.add(nodeId)
    }
  }
  // 普通选择逻辑
  else {
    selectedNodes.value = new Set([nodeId])
  }

  // 更新最后选择的节点(Shift操作时不更新)
  lastSelectedNode.value = nodeId

  emit('node-click', data.node)
  emit('selection-change', Array.from(selectedNodes.value))
}

//强制更新数据
const forceUpdate = (newData) => {
  treeData.value = newData.map((node: any) => ({
    ...node,
    children: node.children || []
  }))
}
//清空选中
const clearSelection = () => {
  selectedNodes.value.clear()
}
//暴露出去
defineExpose({ forceUpdate, clearSelection })
</script>

<style scoped lang="scss">
/* 原有样式保持不变 */
</style>

tree组件子级 TreeNode.vue

html 复制代码
<template>
  <div class="tree-node">
    <div
      class="node-content"
      :class="{ 'has-children': hasChildren, 'drag-over': dragOverNode === node.id }"
      :draggable="true"
      :style="{
        backgroundColor: isSelected ? '#e4efff' : 'transparent',
        color: themeStyle[theme].textColor2
      }"
      @dragstart="onDragStart"
      @dragover="onDragOver"
      @drop="onDrop"
      @dragleave="onDragLeave"
      @click="toggle($event)"
      @contextmenu="toggle($event)"
    >
      <div class="node-content-left">
        <span v-if="hasChildren" class="toggle-icon" @click.stop="expandIcon">
          <AppstoreOutlined />
        </span>
        <span v-else class="toggle-icon">
          <CodeSandboxOutlined />
        </span>
        <div class="node-label">
          <DblclickEdit
            v-model:text="node[fieldNames]"
            @start-edit="dbclickStartEdit"
            @edited="emit('update-text', { id: node.id, text: $event })"
          />
        </div>
      </div>
      <div class="node-content-right" :style="{ display: node.lock ? 'block' : 'none' }">
        <LockOutlined v-if="node.lock" @click="switchLock" />
        <UnlockOutlined v-else @click="switchLock" />
      </div>
    </div>

    <div class="animationDiv" :style="{ height }">
      <template v-if="hasChildren && expandItems.includes(node.id)">
        <div :id="id" class="children">
          <div class="line" :style="{ backgroundColor: themeStyle[theme].borderColor1 }"></div>
          <tree-node
            v-for="child in node.children"
            :key="child.id"
            :node="child"
            :field-names="fieldNames"
            :level="level + 1"
            @node-click="$emit('node-click', $event)"
            @expanded="$emit('expanded', $event)"
            @switch-lock="$emit('switchLock', $event)"
            @node-drop="$emit('node-drop', $event)"
            @update-text="$emit('update-text', $event)"
          />
        </div>
      </template>
    </div>
  </div>
</template>

<script lang="ts" setup>
import {
  AppstoreOutlined,
  CodeSandboxOutlined,
  UnlockOutlined,
  LockOutlined
} from '@ant-design/icons-vue'
import { computed, ref, inject, Ref, watch } from 'vue'
import { expandItems, setExpandItems, currentExpandItem } from './hook'
import { generateUUID } from '@renderer/utils/utils'
import { useStyleStore } from '@renderer/store/globalStyle/style'
import { storeToRefs } from 'pinia'
import DblclickEdit from '../dbclickEdit/DblclickEdit.vue'

const props = defineProps({
  node: {
    type: Object,
    required: true
  },
  fieldNames: {
    type: String,
    default: 'label'
  },
  level: {
    type: Number,
    default: 0
  }
})

const node = ref(props.node)
watch(
  () => props.node,
  (val) => {
    node.value = val
  }
)

const id = ref<string>(generateUUID())

const emit = defineEmits(['node-click', 'expanded', 'switchLock', 'node-drop', 'update-text'])

// 注入选中状态
const selectedNodes = inject<Ref<Set<string>>>('selectedNodes')!

// 计算当前节点是否被选中
const isSelected = computed(() => selectedNodes.value.has(props.node.id))

const hasChildren = computed(() => {
  return props.node.children && props.node.children.length > 0
})

//延时展开
const expandTimer = ref<any>(null)

const toggle = (event: MouseEvent): void => {
  if (hasChildren.value && expandTimer.value === null) {
    expandTimer.value = setTimeout(() => {
      expandIcon()
      expandTimer.value = null
    }, 50)
  }
  if (!isSelected.value) {
    // 传递事件对象给父组件
    emit('node-click', { node: props.node, event: event })
  }
}

const dbclickStartEdit = (): void => {
  if (expandTimer.value) {
    clearTimeout(expandTimer.value)
    expandTimer.value = null
  }
}

const expandIcon = (): void => {
  currentExpandItem.value = props.node.id
  // 判断是展开还是收起
  if (expandItems.value.includes(props.node.id)) {
    // 收起
    const divElement = document.getElementById(id.value) as HTMLElement
    if (divElement) {
      // 获取divElement的高度
      const divHeight = divElement.offsetHeight
      height.value = divHeight + 'px'
      setTimeout(() => {
        height.value = '0px'
        setTimeout(() => {
          height.value = undefined
          setExpandItems(props.node.id)
        }, 300)
      }, 100)
    }
  } else {
    // 展开
    height.value = '0px'
    setTimeout(() => {
      const divElement = document.getElementById(id.value) as HTMLElement
      if (divElement) {
        // 获取divElement的高度
        const divHeight = divElement.offsetHeight
        height.value = divHeight + 'px'
        setTimeout(() => {
          height.value = undefined
        }, 300)
      }
    }, 100)
    setExpandItems(props.node.id)
  }

  emit('expanded', props.node)
}

const height = ref<undefined | string>(undefined)

const globalStyleStore = useStyleStore()
const { theme, themeStyle } = storeToRefs(globalStyleStore)

const switchLock = (e: MouseEvent): void => {
  //阻止事件冒泡
  e.stopPropagation()
  emit('switchLock', props.node.id)
}

const dragOverNode = ref<string | null>(null)

const onDragStart = (e: DragEvent) => {
  e.dataTransfer?.setData('application/node-id', props.node.id)
  e.stopPropagation()
}

const onDragOver = (e: DragEvent) => {
  e.preventDefault()
  dragOverNode.value = props.node.id
}

const onDrop = (e: DragEvent) => {
  e.preventDefault()
  const sourceId = e.dataTransfer?.getData('application/node-id')
  if (sourceId && sourceId !== props.node.id) {
    emit('node-drop', { sourceId, targetId: props.node.id })
  }
  dragOverNode.value = null
}

const onDragLeave = () => {
  dragOverNode.value = null
}
</script>

<style scoped lang="scss">
.tree-node {
  margin: 3px 0;
  margin-left: 0px;
  cursor: pointer;
  width: 100%;
  text-wrap: nowrap;

  .node-content {
    padding: 2px;
    padding-left: 5px;
    transition: background-color 0.2s;
    display: flex;
    justify-content: space-between;
    align-items: center;

    &.drag-over {
      border-top: 1px solid #409eff;
      background-color: #f0faff;
    }

    &:hover {
      .node-content-right {
        display: block !important;
      }
    }

    .node-content-left {
      display: flex;
      justify-content: left;
      align-items: center;

      .toggle-icon {
        display: inline-block;
        width: 15px;
        text-align: center;
        margin-right: 5px;
      }

      .node-label {
        width: 100%;
        user-select: none;
        cursor: pointer;
        line-height: 20px;
      }
    }
    .node-content-right {
      padding-right: 10px;
      display: none;
    }
  }
}

.animationDiv {
  transition: all 0.3s ease-in-out;
  overflow: hidden;

  .children {
    // max-height: 200px;
    width: calc(100% - 15px);
    padding-left: 15px;
    position: relative;
    overflow-y: hidden;
    overflow-x: hidden;

    .line {
      position: absolute;
      width: 1px;
      height: calc(100% - 10px);
      top: 5px;
      left: 5.5px;
    }
  }
}
</style>

tree组件 hook.ts

ts 复制代码
import { ref } from 'vue'

export const expandItems = ref<string[]>([])

export const currentExpandItem = ref('')

/**
 * 设置展开项
 * 如果有参数传入,就从expandItems中查找,如果找到了,就删除,如果没有找到,就添加
 * 如果没有参数传入,就清空expandItems
 */
export const setExpandItems = (id?: string): void => {
  if (id) {
    const index = expandItems.value.indexOf(id)
    if (index > -1) {
      expandItems.value.splice(index, 1)
    } else {
      expandItems.value.push(id)
    }
  } else {
    expandItems.value = []
  }
}

上面是一个完整的tree组件源代码,用户可以完全复制上面的代码,进行运用,该tree组件主要是运用到了递归调用的设计思想,在子级中,如果检测到有children,就会自己调用自己,达到多层级显示的效果,递归调用不仅仅在js,ts代码中能够运用,在html中也可以通过该思想实现一些比较复杂的功能

接下来我将展示我的图层代码

图层代码

layer.vue源代码,里面直接使用到了上述的tree组件

html 复制代码
<template>
  <div class="layer-home">
    <Dropdown :trigger="['contextmenu']">
      <VueTree
        ref="treeRef"
        :data="layers"
        :field-names="'name'"
        :parent-select="currentSelectNodeIds"
        @selection-change="layerStore.selectNodes"
        @switch-lock="layerStore.toggleLock"
        @node-drop="layerStore.moveNode"
        @update-text="layerStore.updateNodeText"
      ></VueTree>
      <template #overlay>
        <div
          class="layer-menu-container"
          :style="{
            backgroundColor: themeStyle[theme].backgroundColor1,
            color: themeStyle[theme].textColor1
          }"
        >
          <template v-for="(item, index) in layerMenu" :key="index">
            <div class="layer-menu-item" @click="item.action">
              <span>{{ item.name }}</span>
              <span>{{ item.shortcutKeys }}</span>
            </div>
          </template>
        </div>
      </template>
    </Dropdown>
  </div>
</template>

<script setup lang="ts">
import VueTree from '@renderer/components/vueTree/VueTree.vue'
import { useLayerStore } from '@renderer/store/resourceBar/layer'
import { storeToRefs } from 'pinia'
import { Dropdown } from 'ant-design-vue'
import { useStyleStore } from '@renderer/store/globalStyle/style'

const layerStore = useLayerStore()
const { layers, treeRef, currentSelectNodeIds } = storeToRefs(layerStore)

const styleStore = useStyleStore()
const { theme, themeStyle } = storeToRefs(styleStore)

const layerMenu = [
  {
    name: '创建编组',
    shortcutKeys: 'Ctrl G',
    action: () => {
      layerStore.createGroup()
    }
  },
  {
    name: '取消编组',
    shortcutKeys: 'Ctrl Shift G',
    action: () => {
      layerStore.ungroup()
    }
  },
  {
    name: '锁定',
    shortcutKeys: 'Ctrl L'
  },
  {
    name: '解锁',
    shortcutKeys: 'Ctrl Shift L'
  },
  {
    name: '删除',
    shortcutKeys: 'Delete',
    action: () => {
      layerStore.deleteNode()
    }
  }
]
</script>

<style scoped lang="scss">
.layer-home {
  width: 100%;
  height: 100%;
  padding-top: 5px;
  overflow: auto;

  /* 设置滚动条样式 */
  &::-webkit-scrollbar {
    width: 4px;
  }

  &::-webkit-scrollbar-thumb {
    background-color: #cccccc85;
    border-radius: 4px;
  }

  &::-webkit-scrollbar-thumb:hover {
    background-color: #aaaaaa85;
  }
}

.layer-menu-container {
  width: 160px;
  box-shadow: 0px 0px 10px #0000001a;
  border-radius: 5px;
  font-size: 13px;
  padding: 10px;
  display: flex;
  flex-direction: column;
  .layer-menu-item {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 5px;
    border-radius: 5px;
    cursor: pointer;
    &:hover {
      background-color: #81818121;
    }
  }
}
</style>

接下来是图层的store部分代码,store采用的是pinia组件,图层的所有功能的数据处理都在store中进行,包括什么创建编组,取消编组,改名字,删除元素,修改等等

ts 复制代码
import { ILayerItem } from '@renderer/types/layerType'
import { generateUUID } from '@renderer/utils/utils'
import { defineStore } from 'pinia'

export const useLayerStore = defineStore('layer', {
  state: () => ({
    layers: [] as ILayerItem[],
    //锁定的节点
    lockNodeIds: [] as string[],
    //当前选择的节点ids
    currentSelectNodeIds: [] as string[],
    //树形结构ref
    treeRef: null as any
  }),

  actions: {
    canvasAddLayer(nodes: any[]) {
      console.log(nodes)
      nodes.forEach((node: any) => {
        this.layers.unshift({
          id: node.id,
          name: node.name,
          zIndex: node.zIndex,
          lock: false,
          type: 'leaf'
        })
      })
    },

    // 获取所有 leaf 类型节点的 id
    getLeafNodeIds(ids: string[]): string[] {
      const result: string[] = []

      for (const id of ids) {
        const item = this.findNodeById(this.layers, id)
        if (item) {
          if (item.type === 'leaf') {
            result.push(item.id)
          } else if (item.type === 'branch') {
            const childIds = item.children?.map((el) => el.id) || []
            const leafIds = this.getLeafNodeIds(childIds)
            result.push(...leafIds)
          }
        } else {
          result.concat([])
        }
      }

      return result
    },

    //选择节点,单个或多个
    selectNodes(ids: string[]) {
      this.currentSelectNodeIds = ids
      const leafIds = this.getLeafNodeIds(ids)
      window.api.handleCanvasData('selectNodesOrEdges', 'resourceMana', true, [leafIds])
    },

    //主进程选择节点
    selectNodesByMain(result: string[]) {
      let ids = [] as string[]
      ids = result.filter((id: string) => !this.lockNodeIds.includes(id))
      this.currentSelectNodeIds = ids
    },

    //切换锁的状态
    toggleLock(id: string) {
      // 找到选中的节点
      const item = this.findNodeById(this.layers, id)
      if (item) {
        item.lock = !item.lock
        const currentLockNodeIds = [] as string[]
        currentLockNodeIds.push(item.id)
        if (item.type === 'branch') {
          const getAllIds = (node: ILayerItem) => {
            node.lock = item.lock
            if (node.type === 'leaf') {
              currentLockNodeIds.push(node.id)
            } else {
              node?.children?.forEach((child: ILayerItem) => {
                getAllIds(child)
              })
            }
          }
          getAllIds(item)
        }

        if (item.lock) {
          this.lockNodeIds = [...this.lockNodeIds, ...currentLockNodeIds]
        } else {
          this.lockNodeIds = this.lockNodeIds.filter(
            (id: string) => !currentLockNodeIds.includes(id)
          )
        }
      }
    },

    // 查找所有选中节点信息(含 parent 和 index)
    getSelectedNodeInfos(
      layers: ILayerItem[],
      selectedIds: string[]
    ): { node: ILayerItem; parent: ILayerItem | null; index: number }[] {
      const selected: { node: ILayerItem; parent: ILayerItem | null; index: number }[] = []

      const find = (nodes: ILayerItem[], parent: ILayerItem | null): boolean => {
        for (let i = 0; i < nodes.length; i++) {
          const node = nodes[i]

          if (selectedIds.includes(node.id)) {
            selected.push({ node, parent, index: i })
            if (selected.length === selectedIds.length) return true
          }

          if (node.children && find(node.children, node)) {
            return true
          }
        }
        return false
      }

      find(layers, null)
      return selected
    },

    // 获取首个选中节点的信息(用于插入位置)
    getFirstSelectedNodeInfo(
      nodeInfos: { node: ILayerItem; parent: ILayerItem | null; index: number }[],
      firstSelectedId: string
    ) {
      return nodeInfos.find((info) => info.node.id === firstSelectedId)!
    },
    // 创建新编组对象
    createNewGroup(zIndex: number): ILayerItem {
      return {
        id: generateUUID(),
        name: '自定义编组',
        lock: false,
        type: 'branch',
        zIndex,
        children: []
      }
    },
    // 将节点移入编组中(注意逆序)
    moveNodesToGroup(
      nodes: { node: ILayerItem; parent: ILayerItem | null; index: number }[],
      group: ILayerItem
    ) {
      ;[...nodes]
        .sort((a, b) => b.index - a.index)
        .forEach((info) => {
          const sourceList = info.parent ? info.parent.children! : this.layers
          sourceList.splice(info.index, 1)
          group.children!.unshift(info.node)
        })
    },
    // 将新编组插入正确位置
    insertGroupAtTargetPosition(
      group: ILayerItem,
      targetInfo: { parent: ILayerItem | null; index: number }
    ) {
      if (targetInfo.parent) {
        if (!targetInfo.parent.children) targetInfo.parent.children = []
        targetInfo.parent.children.splice(targetInfo.index, 0, group)
      } else {
        this.layers.splice(targetInfo.index, 0, group)
      }
    },
    createGroup() {
      if (this.currentSelectNodeIds.length === 0) return

      const selectedNodeInfos = this.getSelectedNodeInfos(this.layers, this.currentSelectNodeIds)
      if (selectedNodeInfos.length === 0) return

      const firstSelectedInfo = this.getFirstSelectedNodeInfo(
        selectedNodeInfos,
        this.currentSelectNodeIds[0]
      )

      const newGroup = this.createNewGroup(firstSelectedInfo.node.zIndex)

      this.moveNodesToGroup(selectedNodeInfos, newGroup)

      this.insertGroupAtTargetPosition(newGroup, firstSelectedInfo)
    },
    // 查找节点 by id
    findNodeById(nodes: ILayerItem[], id: string): ILayerItem | null {
      for (const node of nodes) {
        if (node.id === id) return node
        if (node.children) {
          const foundNode = this.findNodeById(node.children, id)
          if (foundNode) return foundNode
        }
      }
      return null
    },
    // 查找父节点
    findParentNode(nodes: ILayerItem[], childId: string): ILayerItem | null {
      for (const node of nodes) {
        if (node.children && node.children.some((child) => child.id === childId)) {
          return node
        }
        if (node.children) {
          const parent = this.findParentNode(node.children, childId)
          if (parent) return parent
        }
      }
      return null
    },
    // 取消编组
    ungroup() {
      if (this.currentSelectNodeIds.length !== 1) return
      const selectedNodeId = this.currentSelectNodeIds[0]

      // 找到选中的节点
      const selectedNode = this.findNodeById(this.layers, selectedNodeId)
      if (!selectedNode || selectedNode.type !== 'branch') return // 确保是 branch 类型的节点

      // 查找父节点及其索引位置
      const parent = this.findParentNode(this.layers, selectedNodeId)
      const nodeIndex = parent
        ? parent.children!.indexOf(selectedNode)
        : this.layers.indexOf(selectedNode)

      if (parent) {
        // 如果有父节点,将子节点提升到父节点的同一层级
        parent.children = parent.children!.filter((child) => child.id !== selectedNodeId)
        parent.children!.splice(nodeIndex, 0, ...selectedNode.children!) // 在原位置插入子节点
      } else {
        // 如果没有父节点,说明是根节点,直接操作根层级
        //删除index索引元素
        this.layers.splice(nodeIndex, 1)
        this.layers.splice(nodeIndex, 0, ...selectedNode.children!) // 在原位置插入子节点
      }

      // 删除该编组节点的子节点
      selectedNode.children = [] // 清空子节点
    },

    //编组拖拽排序
    moveNode(data: any) {
      const { sourceId, targetId } = data
      if (sourceId === targetId) return

      const sourceNode = this.findNodeById(this.layers, sourceId)
      const sourceParent = this.findParentNode(this.layers, sourceId)

      const targetNode = this.findNodeById(this.layers, targetId)
      const targetParent = this.findParentNode(this.layers, targetId)

      const sourceIndex = sourceParent
        ? sourceParent.children!.indexOf(sourceNode!)
        : this.layers.indexOf(sourceNode!)

      if (sourceIndex !== -1) {
        if (sourceParent) {
          sourceParent.children!.splice(sourceIndex, 1)
        } else {
          this.layers.splice(sourceIndex, 1)
        }
      }

      const targetIndex = targetParent
        ? targetParent.children!.indexOf(targetNode!)
        : this.layers.indexOf(targetNode!)
      if (targetIndex !== -1) {
        if (targetParent) {
          targetParent.children!.splice(targetIndex, 0, sourceNode!)
        } else {
          this.layers.splice(targetIndex, 0, sourceNode!)
        }
      }

      this.updateZIndex(sourceId, targetId)
    },

    //删除节点
    deleteNode(data?: any[]) {
      const delHandle = (id: string) => {
        const parent = this.findParentNode(this.layers, id)

        const index =
          parent?.children?.findIndex((item) => item.id === id) ??
          this.layers.findIndex((item) => item.id === id)

        if (index !== -1) {
          if (parent) {
            parent.children?.splice(index, 1)
          } else {
            this.layers.splice(index, 1)
          }
        }
      }
      this.currentSelectNodeIds.forEach((id) => {
        delHandle(id)
      })

      if (data) {
        const ids = data.map((item) => item.id)
        ids.forEach((id) => {
          delHandle(id)
        })
      }
    },

    updateNodeText(data: { id: string; text: string }) {
      const nodes = window.api.handleCanvasData('getNodes', 'resourceMana', false, ['id', data.id])
      if (!nodes || nodes.length === 0) {
        this.mainUpdateNameToLayer({ id: data.id, name: data.text })
        return
      }
      const node = nodes[0]
      node.name = data.text
      window.api.handleCanvasData('updateNodes', 'property', true, [node])
    },

    mainUpdateNameToLayer(data: { id: string; name: string }) {
      const result = this.findNodeById(this.layers, data.id)
      if (result) {
        result.name = data.name
      }
    },

    //更改画布里面节点的zIndex
    updateZIndex(sourceId: string, targetId: string) {
      const sourceLeftNodes = this.getLeafNodeIds([sourceId])
      const targetLeftNodes = this.getLeafNodeIds([targetId])

      const targetNodes = [] as any[]
      targetLeftNodes.forEach((id) => {
        const nodes = window.api.handleCanvasData('getNodes', 'resourceMana', false, ['id', id])
        if (nodes && nodes.length > 0) {
          targetNodes.push(nodes[0])
        }

        const edges = window.api.handleCanvasData('getEdges', 'resourceMana', false, ['id', id])
        if (edges && edges.length > 0) {
          targetNodes.push(edges[0])
        }
      })

      //找到targetNodes里面zIndex最大的值
      const maxZIndex = Math.max(...targetNodes.map((node) => node.zIndex))

      const sourceNodes = [] as any[]
      sourceLeftNodes.forEach((id) => {
        const nodes = window.api.handleCanvasData('getNodes', 'resourceMana', false, ['id', id])
        if (nodes && nodes.length > 0) {
          sourceNodes.push(nodes[0])
        }

        const edges = window.api.handleCanvasData('getEdges', 'resourceMana', false, ['id', id])
        if (edges && edges.length > 0) {
          sourceNodes.push(edges[0])
        }
      })
      sourceNodes.forEach((node) => {
        node.zIndex = maxZIndex
      })
      window.api.handleCanvasData('updateNodes', 'resourceMana', true, [sourceNodes])
      window.api.handleCanvasData('updateEdges', 'resourceMana', true, [sourceNodes])
    },

    //清空数据
    clear() {
      this.layers.splice(0, this.layers.length)
    }
  }
})

图层部分就到此结束啦,至于资源管理器下面的项目文件和工具预设,都是不重要的功能,作用也不大

相关推荐
@小红花3 小时前
从0到1学习Vue框架Day03
前端·javascript·vue.js·学习·ecmascript
前端与小赵3 小时前
vue3中 ref() 和 reactive() 的区别
前端·javascript·vue.js
魔云连洲3 小时前
Vue的响应式底层原理:Proxy vs defineProperty
前端·javascript·vue.js
专注VB编程开发20年3 小时前
CSS定义网格的列模板grid-template-columns什么意思,为什么要用这么复杂的单词
前端·css
IT_陈寒3 小时前
Redis性能提升50%的7个关键优化策略,90%开发者都不知道第5点!
前端·人工智能·后端
Hilaku3 小时前
深入URL和URLSearchParams:别再用正则表达式去折磨URL了
前端·javascript·代码规范
pubuzhixing4 小时前
Canvas 的性能卓越,用它解决一个棘手问题
前端
weixin_456904274 小时前
Vue.jsmain.js/request.js/user.js/store/index.js Vuex状态管理项目核心模块深度解析
前端·javascript·vue.js
伍哥的传说4 小时前
Vue 3.6 Alien Signals:让响应式性能飞跃式提升
前端·javascript·vue.js·vue性能优化·alien-signals·细粒度更新·vue 3.6新特性