工业仿真(simulation)--前端(六)--属性栏

70 阅读4分钟

本章主要讲解属性栏的设计与开发

如图一,不同节点有不同的属性数据

图一

那如何去设计这个属性界面,在保证美观的前提下,还要满足下面的要求

  1. 代码复用性
  2. 代码可维护性
  3. 功能可扩展性

首先要说明一点,我们的画布组件和属性栏组件之间,传递消息不能直接调用,消息的传递要通过主进程,熟悉electron的同学应该知道

如果画布组件和消息栏组件是通过pinia直接进行消息传递,虽然很方便,但是会导致一个问题,就是一旦属性栏变成一个独立的窗口,那么消息通信将直接失效,因为不同的窗口之间要想进行消息传递,必须通过主进程,这是很重要的一点,后面还有很多组件相互通信也是通过主进程

设计思路

  1. 主进程开发两个接口,一个接收消息接口,一个发送消息接口
  2. 在画布组件中,我们就需要对显示属性消息进行一个统一的处理,将这些消息分成不同的type
  3. 在属性栏组件中,我们就要去监听主进程发送消息的接口
  4. 根据消息里面不同的type,再显示不同的组件

消息传递如图二

图二

接下来我们从代码层面看一下如何实现

代码层面

发消息

比如说我们要显示画布属性或者说节点的属性,在我们点击画布时,就需要做出相应的处理,在前面的章节中,我已经讲过,画布实例有很多内置的监听事件

  //画布-点击事件
  canvas.on('blank:click', () => {
    //打开画布属性面板
    renderShowCanvasProperty()
  })

然后进行简单的处理

//显示画布属性
export const renderShowCanvasProperty = (): void => {
  const canvasProperties = window.api.handleCanvasData('getCanvasProperties', 'canvas2D', false, [])
  const data = {
    title: '画布属性',
    id: generateUUID(),
    type: 'canvas',
    data: canvasProperties
  }

  window.electron.ipcRenderer.send('openAttributeWindow', data)
}

window.electron.ipcRenderer.send('openAttributeWindow', data)

这一行代码就是向主进程发送消息,当然除了显示画布属性外,我们还可以显示其他元素的属性,比如说

//显示节点属性
export const renderShowNodeProperty = (
  node: Node,
  type: 'node' | 'freeEdit' | 'plaintext' = 'node'
): void => {
  const nodeProperties = window.api.handleCanvasData('getNodes', 'canvas2D', false, ['id', node.id])
  const data = {
    title: '节点属性',
    id: generateUUID(),
    type: type,
    data: nodeProperties[0]
  }
  window.electron.ipcRenderer.send('openAttributeWindow', data)
}

//显示节点批量编辑属性
export const renderNodeBulkEdit = (): void => {
  const data = {
    title: '批量节点',
    id: generateUUID(),
    type: 'bulkNode'
  }
  window.electron.ipcRenderer.send('openAttributeWindow', data)
}

//显示边属性
export const renderShowEdgeProperty = (edge: Edge): void => {
  const edgeProperties = window.api.handleCanvasData('getEdges', 'canvas2D', false, ['id', edge.id])
  if (edge.shape === 'conveyor-belt') {
    const data = {
      title: '边属性',
      id: generateUUID(),
      type: 'conveyor',
      data: edgeProperties[0]
    }
    window.electron.ipcRenderer.send('openAttributeWindow', data)
  } else if (edge.shape === 'road-edge') {
    const data = {
      title: '边属性',
      id: generateUUID(),
      type: 'road',
      data: edgeProperties[0]
    }
    window.electron.ipcRenderer.send('openAttributeWindow', data)
  } else {
    const data = {
      title: '边属性',
      id: generateUUID(),
      type: 'edge',
      data: edgeProperties[0]
    }
    window.electron.ipcRenderer.send('openAttributeWindow', data)
  }
}

这些方法无一例外都是通过openAttributeWindow接口向主进程发送消息,然后我们在主进程中看看怎么接收和发送

中间代理

代码如下

  // 监听打开属性栏窗口的事件, 拿到数据不做处理,直接发给属性栏窗口
  ipcMain.on('openAttributeWindow', (e, data) => {
    mainWindow.webContents.send('attributeWindowData', data)
  })

代码只有三行,也就是收到消息后,再发出去,不需要做任何处理,那么在我们的属性栏组件中,就需要去监听attributeWindowData接口发过来的消息

属性栏组件

我们需要在属性栏生命周期钩子函数里面注册一个监听函数

onMounted(() => {
  //监听主进程的属性栏数据处理事件
  window.electron.ipcRenderer.on('attributeWindowData', (e, data) => {
    switchAttribute(data)
  })
})

然后对于发过来的消息,我们这样处理

interface type {
  title: string
  id: string
  type:
    | 'canvas'
    | 'node'
    | 'edge'
    | 'conveyor'
    | 'road'
    | 'bulkNode'
    | 'freeEdit'
    | 'plaintext'
    | null
  data: any
}
const attributeData = ref<type>({
  title: '模型属性',
  id: '',
  type: null,
  data: {}
})

//在这里判断是否需要切换数据或是隐藏
const switchAttribute = (data: any): void => {
  console.log(data)
  if (isPushpin.value) {
    //这里是固钉状态,然后判断传进来的type如果是null
    if (data.type === null && data.id === attributeData.value.id) {
      attributeData.value = data
    }
  } else {
    attributeData.value = data
  }
}

然后再template里面就可以直接判断type的类型来显示哪一部分

<template>
  <div
    class="property-home"
    :style="{
      backgroundColor: themeStyle[theme].backgroundColor1,
      borderLeft: `1px solid ${themeStyle[theme].borderColor1}`
    }"
  >
    <div
      class="property-home-title"
      :style="{
        backgroundColor: themeStyle[theme].backgroundColor2,
        color: themeStyle[theme].textColor3
      }"
    >
      <span>属性</span>
      <div class="icon-group">
        <div class="pushpin" :class="isPushpin ? 'pushpinActive' : ''" @click="switchPushpin">
          <svg-icon
            name="fixedNail"
            size="1em"
            :color="themeStyle[theme].textColor1"
            class="icon"
          ></svg-icon>
        </div>

        <svg-icon
          name="funClose"
          size="0.9em"
          :color="themeStyle[theme].textColor1"
          class="icon"
          @click="closeAttribute"
        ></svg-icon>
      </div>
    </div>

    <template v-if="attributeData.type === 'canvas'">
      <CanvasProperty :data="attributeData.data" />
    </template>
    <template v-if="attributeData.type === 'node'">
      <NodeProTem :data="attributeData.data" />
    </template>
    <template v-if="attributeData.type === 'edge'">
      <EdgeProTem :data="attributeData.data" />
    </template>
    <template v-if="attributeData.type === 'conveyor'">
      <ConveyorProperty :data="attributeData.data" />
    </template>
    <template v-if="attributeData.type === 'road'">
      <RoadProperty :data="attributeData.data" />
    </template>
    <template v-if="attributeData.type === 'bulkNode'">
      <BulkNode :data="attributeData.data" />
    </template>
    <template v-if="attributeData.type === 'freeEdit'">
      <FreeEdit :data="attributeData.data" />
    </template>
    <template v-if="attributeData.type === 'plaintext'">
      <Plaintext :data="attributeData.data" />
    </template>
  </div>
</template>

<script setup lang="ts">
import { useStyleStore } from '@renderer/store/globalStyle/style'
import { storeToRefs } from 'pinia'
import { onMounted, watch, ref } from 'vue'
import CanvasProperty from './canvasPro/CanvasProperty.vue'
import NodeProTem from './nodePro/NodeProTem.vue'
import ConveyorProperty from './conveyorPro/ConveyorProperty.vue'
import BulkNode from './nodePro/BulkNode.vue'
import FreeEdit from './nodePro/FreeEdit.vue'
import EdgeProTem from './edgePro/EdgeProTem.vue'
import RoadProperty from './roadPro/RoadProperty.vue'
import Plaintext from './nodePro/Plaintext.vue'