Browse Source

feat: 完善节点数据处理

jiaxing.liao 1 week ago
parent
commit
65a0895fca

+ 2 - 0
src/renderer/components.d.ts

@@ -13,6 +13,7 @@ export {}
 declare module 'vue' {
   export interface GlobalComponents {
     CodeEditor: typeof import('./src/components/CodeEditor/index.vue')['default']
+    EditorModal: typeof import('./src/components/EditorModal/index.vue')['default']
     ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
     ElAutoComplete: typeof import('element-plus/es')['ElAutoComplete']
     ElButton: typeof import('element-plus/es')['ElButton']
@@ -82,6 +83,7 @@ declare module 'vue' {
 // For TSX support
 declare global {
   const CodeEditor: typeof import('./src/components/CodeEditor/index.vue')['default']
+  const EditorModal: typeof import('./src/components/EditorModal/index.vue')['default']
   const ElAutocomplete: typeof import('element-plus/es')['ElAutocomplete']
   const ElAutoComplete: typeof import('element-plus/es')['ElAutoComplete']
   const ElButton: typeof import('element-plus/es')['ElButton']

+ 7 - 0
src/renderer/src/components/EditorModal/index.vue

@@ -0,0 +1,7 @@
+<template>
+  <div></div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style scoped></style>

src/renderer/src/views/designer/workspace/composite/eventEdit/useAllWidgets.ts → src/renderer/src/hooks/useAllWidgets.ts


+ 2 - 2
src/renderer/src/theme/vars.css

@@ -26,10 +26,10 @@
   --el-bg-color: var(--bg-primary);
   --el-bg-color-overlay: var(--bg-secondary) !important;
 
-  --el-text-color-primary: var(--text-secondary) !important;
+  /* --el-text-color-primary: var(--text-secondary) !important;
   --el-text-color-regular: var(--text-secondary) !important;
   --el-text-color-placeholder: #545454 !important;
-  --el-text-color-disabled: #454545 !important;
+  --el-text-color-disabled: #454545 !important; */
 
   --el-border-color: var(--border-color) !important;
   --el-border-radius-base: 6px !important;

+ 134 - 9
src/renderer/src/views/designer/workspace/composite/eventEdit/ActionNode.vue

@@ -1,19 +1,28 @@
 <template>
-  <div class="h-32px bg-#4EB2BF rounded-4px flex items-center pr-4px">
+  <div class="min-h-32px bg-#4EB2BF rounded-4px flex items-center pr-4px">
     <div class="w-160px text-#fff grid place-items-center shrink-0">
       {{ node.name }}
     </div>
     <div>
       <!-- 自定义代码 -->
-      <div v-if="node.name === 'custom_code'" class="w-120px">Edit Code</div>
+      <div v-if="node.name === 'custom_code'" class="w-120px">
+        <el-select style="width: 120px" v-model="value" size="small">
+          <el-option
+            v-for="item in projectStore.project?.methods || []"
+            :key="item.id"
+            :label="item.name"
+            :value="item.id"
+          />
+        </el-select>
+      </div>
       <!-- 字符串 -->
       <div v-if="node?.config?.data?.valueType === 'string'">
-        <el-input v-model="node.config.value" size="small" />
+        <el-input v-model="value" size="small" />
       </div>
       <!-- 数字 -->
       <div v-if="node?.config?.data?.valueType === 'number'">
         <el-input-number
-          v-model="node.config.value"
+          v-model="value"
           :min="node.config.data?.min"
           :max="node.config.data?.max"
           size="small"
@@ -21,18 +30,21 @@
       </div>
       <!-- 布尔 -->
       <div v-if="node?.config?.data?.valueType === 'boolean'">
-        <el-switch v-model="node.config.value" size="small" />
+        <el-switch v-model="value" size="small" />
       </div>
       <!-- 枚举 -->
       <div v-if="node?.config?.data?.valueType === 'select'">
         <el-select
           style="width: 160px"
-          v-model="node.config.value"
+          v-model="value"
           size="small"
           placeholder="please select"
+          :multiple="node.config.data?.multiple"
+          collapse-tags
+          collapse-tags-tooltip
         >
           <el-option
-            v-for="item in node.config.data.options"
+            v-for="item in optionMap?.[node.name] || []"
             :key="item.value"
             :label="item.label"
             :value="item.value"
@@ -41,20 +53,133 @@
       </div>
       <!-- 颜色 -->
       <div v-if="node?.config?.data?.valueType === 'color'">
-        <el-color-picker v-model="node.config.value" size="small" />
+        <el-color-picker v-model="value" size="small" />
+      </div>
+      <!-- 动画 -->
+      <div
+        v-if="node?.config?.data?.valueType === 'animation'"
+        class="py-4px flex flex-col gap-4px"
+      >
+        <div>
+          <span class="inline-block w-80px">动画:</span>
+          <el-select
+            style="width: 160px"
+            v-model="value.animation"
+            size="small"
+            placeholder="select animation"
+          >
+            <el-option
+              v-for="item in projectStore.project?.animations || []"
+              :key="item.name"
+              :label="item.name"
+              :value="item.name"
+            />
+          </el-select>
+        </div>
+        <div>
+          <span class="inline-block w-80px">动画前事件:</span>
+          <el-select
+            style="width: 160px"
+            v-model="value.before"
+            size="small"
+            placeholder="before event"
+            clearable
+          >
+            <el-option
+              v-for="item in projectStore.project?.methods || []"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </div>
+        <div>
+          <span class="inline-block w-80px">动画后事件:</span>
+          <el-select
+            style="width: 160px"
+            v-model="value.after"
+            size="small"
+            placeholder="after event"
+            clearable
+          >
+            <el-option
+              v-for="item in projectStore.project?.methods || []"
+              :key="item.id"
+              :label="item.name"
+              :value="item.id"
+            />
+          </el-select>
+        </div>
+      </div>
+      <!-- 旋转 -->
+      <div v-if="node?.config?.data?.valueType === 'rotate'" class="py-4px flex flex-col gap-4px">
+        <div>
+          <span class="inline-block w-40px">X:</span>
+          <el-input-number
+            v-model="value.x"
+            :min="node.config.data?.min"
+            :max="node.config.data?.max"
+            size="small"
+          />
+        </div>
+        <div>
+          <span class="inline-block w-40px">Y:</span>
+          <el-input-number
+            v-model="value.y"
+            :min="node.config.data?.min"
+            :max="node.config.data?.max"
+            size="small"
+          />
+        </div>
+        <div>
+          <span class="inline-block w-40px">度数:</span>
+          <el-input-number
+            v-model="value.angle"
+            :min="node.config.data?.min"
+            :max="node.config.data?.max"
+            size="small"
+          />
+        </div>
       </div>
     </div>
+    <!-- 删除按钮 -->
+    <div
+      v-if="node.name !== 'custom_code'"
+      class="mx-4px p-2px cursor-pointer"
+      @click="emit('delete', node.id)"
+    >
+      <LuTrash2 size="12px" class="text-accent-red" />
+    </div>
   </div>
 </template>
 
 <script setup lang="ts">
 import type { NodeItemType } from './type'
+import { ref, watch } from 'vue'
+import { LuTrash2 } from 'vue-icons-plus/lu'
+import { useProjectStore } from '@/store/modules/project'
+import { optionMap } from './config'
 
+const emit = defineEmits<{
+  (e: 'delete', id: string): void
+  (e: 'update', node: NodeItemType): void
+}>()
 const props = defineProps<{
   node: NodeItemType
 }>()
+const projectStore = useProjectStore()
+
+const value = ref(props.node.value)
 
-console.log(props.node)
+watch(
+  () => value.value,
+  (val) => {
+    emit('update', { ...props.node, value: val })
+  },
+  {
+    deep: true
+  }
+)
 </script>
 
 <style scoped></style>

+ 17 - 10
src/renderer/src/views/designer/workspace/composite/eventEdit/NodeItem.vue

@@ -28,11 +28,7 @@
     class="w-200px h-40px bg-#4b85e2 text-#fff grid place-items-center rounded-4px relative group"
   >
     <div class="absolute top-4px right-4px gap-4px hidden group-hover:flex">
-      <SelectPopover
-        v-bind="getAddOptions('root')"
-        :multiple="false"
-        @confirm="(val) => val && emit('change', node.id, val)"
-      >
+      <SelectPopover v-bind="getAddOptions('root')" :multiple="false" @confirm="handleChangeEvent">
         <div class="bg-[rgba(0,0,0,.5)] p-2px cursor-pointer"><LuPencilLine size="12px" /></div>
       </SelectPopover>
 
@@ -61,6 +57,7 @@
     v-if="node.nodeType === 'action'"
     :node="node"
     @delete="() => emit('delete', node.id)"
+    @update="(val) => emit('change', val)"
   />
 </template>
 
@@ -72,11 +69,12 @@ import { LuPlus, LuTrash2, LuPencilLine } from 'vue-icons-plus/lu'
 import SelectPopover from './SelectPopover.vue'
 import ActionNode from './ActionNode.vue'
 import { widgetEventOptions, pageEventOptions, actionOptions } from './config'
-import { useAllWidgets } from './useAllWidgets'
+import { useAllWidgets } from '@/hooks/useAllWidgets'
+import { useProjectStore } from '@/store/modules/project'
 
 const emit = defineEmits<{
   (e: 'add', parentId: string, parentType: string, value: any[]): void
-  (e: 'change', id: string, value: any[]): void
+  (e: 'change', node: NodeItemType): void
   (e: 'delete', nodeId: string): void
 }>()
 const props = defineProps<{
@@ -84,16 +82,18 @@ const props = defineProps<{
 }>()
 
 const { allWidgets } = useAllWidgets()
+const projectStore = useProjectStore()
 
 // 添加节点弹窗配置
 const getAddOptions = (type: string) => {
+  const widget = projectStore.activeWidgets?.at(-1)
   switch (type) {
     case 'root':
       return {
         title: '触发方式',
         multiple: true,
         // 判断选择元素是否是页面
-        options: props.node.isPage ? pageEventOptions : widgetEventOptions
+        options: widget?.type === 'page' ? pageEventOptions : widgetEventOptions
       }
     case 'event':
       const options = allWidgets.value.map((item) => ({
@@ -123,6 +123,7 @@ const getAddOptions = (type: string) => {
   return
 }
 
+// 新增按钮样式
 const addBtnStyle = computed(() => {
   const node = props.node
   return {
@@ -130,6 +131,12 @@ const addBtnStyle = computed(() => {
       node.parentType === 'root' ? '#4b85e2' : node.parentType === 'event' ? '#4B9889' : '#4EB2BF'
   }
 })
-</script>
 
-<style scoped></style>
+const handleChangeEvent = (val?: any[]) => {
+  if (val?.length) {
+    const item = val[0]
+    props.node.name = item.value
+    props.node.config.data = item?.data
+  }
+}
+</script>

+ 60 - 22
src/renderer/src/views/designer/workspace/composite/eventEdit/config.ts

@@ -61,20 +61,25 @@ export const actionOptions = [
   { label: 'Height', value: 'height', valueType: 'number', defaultValue: 100 },
   { label: 'Text', value: 'text', valueType: 'string', defaultValue: 'default' },
   { label: 'Font Size', value: 'font_size', valueType: 'number', defaultValue: 12 },
-  { label: 'Add Flag', value: 'add_flag', valueType: 'select', options: flagOptions },
-  { label: 'Remove Flag', value: 'remove_flag', valueType: 'select', options: flagOptions },
+  {
+    label: 'Add Flag',
+    value: 'add_flag',
+    valueType: 'select',
+    multiple: true
+  },
+  {
+    label: 'Remove Flag',
+    value: 'remove_flag',
+    valueType: 'select',
+    multiple: true
+  },
   { label: 'Background Color', value: 'bg_color', valueType: 'color' },
   { label: 'Background Alpha', value: 'bg_alpha', valueType: 'number', min: 0, max: 255 },
   { label: 'Gradient Color', value: 'bg_grad_color', valueType: 'color' },
   {
     label: 'Gradient Direction',
-    value: '',
-    valueType: 'select',
-    options: [
-      { label: 'None', value: 'none' },
-      { label: 'Vertical', value: 'vertical' },
-      { label: 'Horizontal', value: 'horizontal' }
-    ]
+    value: 'grad_dir',
+    valueType: 'select'
   },
   { label: 'Background Image Alpha', value: 'bg_img_alpha', valueType: 'number', min: 0, max: 255 },
   { label: 'Background Image Render Color', value: 'bg_img_render_color', valueType: 'color' },
@@ -85,15 +90,7 @@ export const actionOptions = [
   {
     label: 'Border',
     value: 'border_type',
-    valueType: 'select',
-    options: [
-      { label: 'None', value: 'none' },
-      { label: 'All', value: 'all' },
-      { label: 'Top', value: 'top' },
-      { label: 'Bottom', value: 'bottom' },
-      { label: 'Left', value: 'left' },
-      { label: 'Right', value: 'right' }
-    ]
+    valueType: 'select'
   },
   { label: 'Border Size', value: 'border_width', valueType: 'number', defaultValue: 1 },
   { label: 'Border Alpha', value: 'border_alpha', valueType: 'number', min: 0, max: 255 },
@@ -102,10 +99,51 @@ export const actionOptions = [
   { label: 'Color', value: 'led_color', valueType: 'color' },
   { label: 'Radius', value: 'radius', valueType: 'number' },
   { label: 'Visible', value: 'visible', valueType: 'boolean' },
-  { label: 'Add State', value: 'add_state', valueType: 'select', options: stateOptions },
-  { label: 'Remove State', value: 'remove_state', valueType: 'select', options: stateOptions },
-  { label: 'Rotate', value: 'rotate', valueType: 'rotate' },
+  { label: 'Add State', value: 'add_state', valueType: 'select' },
+  { label: 'Remove State', value: 'remove_state', valueType: 'select' },
+  {
+    label: 'Rotate',
+    value: 'rotate',
+    valueType: 'rotate',
+    defaultValue: {
+      x: 0,
+      y: 0,
+      angle: 0
+    }
+  },
   { label: 'Zoom', value: 'zoom', valueType: 'number' },
   { label: 'Widget Value', value: 'widget_value', valueType: 'string' },
-  { label: 'Play Animation', value: 'play_animation', valueType: 'animation' }
+  {
+    label: 'Play Animation',
+    value: 'play_animation',
+    valueType: 'animation',
+    defaultValue: {
+      animation: undefined,
+      before: undefined,
+      after: undefined
+    }
+  }
 ]
+
+/**
+ * @description: 选项map
+ */
+export const optionMap = {
+  add_flag: flagOptions,
+  remove_flag: flagOptions,
+  add_state: stateOptions,
+  remove_state: stateOptions,
+  grid_dir: [
+    { label: 'None', value: 'none' },
+    { label: 'Vertical', value: 'vertical' },
+    { label: 'Horizontal', value: 'horizontal' }
+  ],
+  border_type: [
+    { label: 'None', value: 'none' },
+    { label: 'All', value: 'all' },
+    { label: 'Top', value: 'top' },
+    { label: 'Bottom', value: 'bottom' },
+    { label: 'Left', value: 'left' },
+    { label: 'Right', value: 'right' }
+  ]
+}

+ 65 - 20
src/renderer/src/views/designer/workspace/composite/eventEdit/index.vue

@@ -11,6 +11,7 @@ import { useResizeObserver } from '@vueuse/core'
 import NodeItem from './NodeItem.vue'
 import { useProjectStore } from '@/store/modules/project'
 import { bfsWalk } from 'simple-mind-map/src/utils'
+import { klona } from 'klona'
 
 import type { NodeItemType, OptionType } from './type'
 import { v4 } from 'uuid'
@@ -32,9 +33,9 @@ const mindMapData = ref<NodeItemType>()
 watch(
   () => projectStore.activeWidgets,
   async (list) => {
-    const widget = list?.at(-1)
-    // 当前选择的最后一个元素
-    if (widget) {
+    const widget = klona(list?.at(-1))
+    // 当前选择的最后一个元素且不是当前元素 切换
+    if (widget && widget.id !== mindMapData.value?.id) {
       const data: NodeItemType = {
         id: widget.id,
         name: widget.name,
@@ -44,8 +45,8 @@ watch(
       }
       // 添加增加节点
       bfsWalk(data, (child) => {
-        if (child.children) {
-          child.children.push(getAddNode(child.id, child.nodeType, widget.type === 'page'))
+        if (child.children && child.nodeType !== 'action') {
+          child.children.push(getAddNode(child.id, child.nodeType))
         }
       })
       mindMapData.value = data
@@ -55,24 +56,40 @@ watch(
   }
 )
 
+watch(
+  () => mindMapData.value,
+  () => {
+    const data = klona(mindMapData.value)
+    const item = projectStore.activeWidgets?.at(-1)
+    if (data && item?.id === data.id) {
+      // 移除添加按钮
+      bfsWalk(data, (child) => {
+        if (child.children) {
+          child.children = child.children.filter((item) => item.nodeType !== 'add')
+        }
+      })
+      item.events = data.children
+    }
+  },
+  {
+    immediate: true,
+    deep: true
+  }
+)
+
 // 刷新节点
 const refresh = () => {
   mindMap.value?.setData(mindMapData.value)
 }
 
 // 获取添加按钮
-const getAddNode = (
-  parentId: string,
-  parentType: NodeItemType['parentType'],
-  isPage: boolean = false
-): NodeItemType => {
+const getAddNode = (parentId: string, parentType: NodeItemType['parentType']): NodeItemType => {
   return {
     id: v4(),
     name: 'add_btn',
     nodeType: 'add',
     parentId,
-    parentType,
-    isPage
+    parentType
   }
 }
 
@@ -88,7 +105,7 @@ const handleAddNode = (parentId: string, parentType: string, values: OptionType[
           name: val.value,
           nodeType: 'event',
           config: {
-            data: val
+            data: val?.data
           },
           children: [getAddNode(id, 'event')]
         })
@@ -101,10 +118,10 @@ const handleAddNode = (parentId: string, parentType: string, values: OptionType[
         if (val.value === 'customCode') {
           nodes.push({
             id,
-            name: val.label,
+            name: val.value,
             nodeType: 'target',
             config: {
-              data: val
+              data: val?.data
             },
             children: [
               {
@@ -124,7 +141,7 @@ const handleAddNode = (parentId: string, parentType: string, values: OptionType[
             name: val.label,
             nodeType: 'target',
             config: {
-              data: val
+              data: val?.data
             },
             children: [getAddNode(id, 'target')]
           })
@@ -138,9 +155,9 @@ const handleAddNode = (parentId: string, parentType: string, values: OptionType[
           id,
           name: val.value,
           nodeType: 'action',
+          value: klona(val?.defaultValue),
           config: {
-            data: val,
-            value: val?.defaultValue
+            data: val
           },
           children: []
         })
@@ -153,10 +170,31 @@ const handleAddNode = (parentId: string, parentType: string, values: OptionType[
       node.children.splice(node.children.length - 1, 0, ...nodes)
     }
   })
-  // 刷新画布
   refresh()
 }
 
+// 删除节点
+const handleDeleteNode = (id: string) => {
+  bfsWalk(mindMapData.value, (node) => {
+    if (node.children) {
+      node.children = node.children.filter((item) => item.id !== id)
+    }
+  })
+  refresh()
+}
+
+// 修改节点
+const handleChangeNode = (node: NodeItemType) => {
+  if (!node) return
+  bfsWalk(mindMapData.value, (child) => {
+    if (child.id === node.id) {
+      child.name = node.name
+      child.config = node.config
+      child.value = node.value
+    }
+  })
+}
+
 watchOnce(
   () => [width.value, height.value],
   ([w, h]) => {
@@ -175,7 +213,14 @@ watchOnce(
           const sourceData = node.nodeData
           const App = defineComponent({
             render() {
-              return <NodeItem node={sourceData as NodeItemType} onAdd={handleAddNode} />
+              return (
+                <NodeItem
+                  node={sourceData as NodeItemType}
+                  onAdd={handleAddNode}
+                  onDelete={handleDeleteNode}
+                  onChange={handleChangeNode}
+                />
+              )
             }
           })
 

+ 4 - 4
src/renderer/src/views/designer/workspace/composite/eventEdit/type.d.ts

@@ -7,6 +7,10 @@ export type NodeItemType = {
    * @description: 节点名称
    */
   name: string
+  /**
+   * @description: 数据值
+   */
+  value?: any
   /**
    * @description: 节点类型
    */
@@ -27,10 +31,6 @@ export type NodeItemType = {
    * @description: 父节点类型
    */
   parentType?: 'root' | 'event' | 'target'
-  /**
-   * @description: 是否页面
-   */
-  isPage?: boolean
 }
 
 export type OptionType = { label: string; value: string; [key: string]: any }