Parcourir la source

feat: 集成右侧属性面板

Gaokun Wang il y a 3 jours
Parent
commit
f17fc920e7

+ 53 - 1
package-lock.json

@@ -14,6 +14,7 @@
         "@tailwindcss/vite": "^4.1.10",
         "@vueuse/core": "^13.3.0",
         "@zklogic/draw.io": "^15.7.2-rc1",
+        "clipboard": "^2.0.11",
         "electron-updater": "^6.6.2",
         "elkjs": "^0.10.0",
         "mxgraph": "^4.2.2",
@@ -21,7 +22,8 @@
         "ramda": "^0.31.3",
         "tailwindcss": "^4.1.10",
         "tdesign-icons-vue-next": "^0.3.6",
-        "tdesign-vue-next": "^1.13.2"
+        "tdesign-vue-next": "^1.13.2",
+        "vue3-json-viewer": "^2.4.0"
       },
       "devDependencies": {
         "@electron-toolkit/eslint-config-prettier": "3.0.0",
@@ -3506,6 +3508,17 @@
         "url": "https://github.com/chalk/strip-ansi?sponsor=1"
       }
     },
+    "node_modules/clipboard": {
+      "version": "2.0.11",
+      "resolved": "https://registry.npmmirror.com/clipboard/-/clipboard-2.0.11.tgz",
+      "integrity": "sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==",
+      "license": "MIT",
+      "dependencies": {
+        "good-listener": "^1.2.2",
+        "select": "^1.1.2",
+        "tiny-emitter": "^2.0.0"
+      }
+    },
     "node_modules/cliui": {
       "version": "8.0.1",
       "resolved": "https://registry.npmmirror.com/cliui/-/cliui-8.0.1.tgz",
@@ -3926,6 +3939,12 @@
         "node": ">=0.4.0"
       }
     },
+    "node_modules/delegate": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmmirror.com/delegate/-/delegate-3.2.0.tgz",
+      "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==",
+      "license": "MIT"
+    },
     "node_modules/delegates": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/delegates/-/delegates-1.0.0.tgz",
@@ -5430,6 +5449,15 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/good-listener": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmmirror.com/good-listener/-/good-listener-1.2.2.tgz",
+      "integrity": "sha512-goW1b+d9q/HIwbVYZzZ6SsTr4IgE+WA44A0GmPIQstuOrgsFcT7VEJ48nmr9GaRtNu0XTKacFLGnBPAM6Afouw==",
+      "license": "MIT",
+      "dependencies": {
+        "delegate": "^3.1.2"
+      }
+    },
     "node_modules/gopd": {
       "version": "1.2.0",
       "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz",
@@ -8688,6 +8716,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/select": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/select/-/select-1.1.2.tgz",
+      "integrity": "sha512-OwpTSOfy6xSs1+pwcNrv0RBMOzI39Lp3qQKUTPVVPRjCdNa5JH/oPRiqsesIskK8TVgmRiHwO4KXlV2Li9dANA==",
+      "license": "MIT"
+    },
     "node_modules/semver": {
       "version": "6.3.1",
       "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz",
@@ -9276,6 +9310,12 @@
         "node": ">= 10.0.0"
       }
     },
+    "node_modules/tiny-emitter": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/tiny-emitter/-/tiny-emitter-2.1.0.tgz",
+      "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==",
+      "license": "MIT"
+    },
     "node_modules/tiny-typed-emitter": {
       "version": "2.1.0",
       "resolved": "https://registry.npmmirror.com/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz",
@@ -9975,6 +10015,18 @@
         "typescript": ">=5.0.0"
       }
     },
+    "node_modules/vue3-json-viewer": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmmirror.com/vue3-json-viewer/-/vue3-json-viewer-2.4.0.tgz",
+      "integrity": "sha512-hwTusxs58pDnPiMCoLefX0IFXSPBYyK4zsAMOBY7BQ/WaWrIdhs/lZtTKOeiAy2/haKcQi+KV8NudC6z3VONCQ==",
+      "license": "ISC",
+      "dependencies": {
+        "clipboard": "^2.0.10"
+      },
+      "peerDependencies": {
+        "vue": "^3.5.16"
+      }
+    },
     "node_modules/wcwidth": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/wcwidth/-/wcwidth-1.0.1.tgz",

+ 3 - 1
package.json

@@ -26,6 +26,7 @@
     "@tailwindcss/vite": "^4.1.10",
     "@vueuse/core": "^13.3.0",
     "@zklogic/draw.io": "^15.7.2-rc1",
+    "clipboard": "^2.0.11",
     "electron-updater": "^6.6.2",
     "elkjs": "^0.10.0",
     "mxgraph": "^4.2.2",
@@ -33,7 +34,8 @@
     "ramda": "^0.31.3",
     "tailwindcss": "^4.1.10",
     "tdesign-icons-vue-next": "^0.3.6",
-    "tdesign-vue-next": "^1.13.2"
+    "tdesign-vue-next": "^1.13.2",
+    "vue3-json-viewer": "^2.4.0"
   },
   "devDependencies": {
     "@electron-toolkit/eslint-config-prettier": "3.0.0",

+ 2 - 0
src/main.ts

@@ -3,11 +3,13 @@ import router from './router'
 import { createApp } from 'vue'
 import App from './App.vue'
 import { setupAuthRoutes } from '@/router/before'
+import JsonViewer from 'vue3-json-viewer'
 
 // 创建实例
 const setupAll = async () => {
   const app = createApp(App)
   app.use(router)
+  app.use(JsonViewer)
   app.use(setupAuthRoutes)
   app.mount('#app')
 }

+ 20 - 0
src/types/auto-components.d.ts

@@ -8,16 +8,36 @@ export {}
 /* prettier-ignore */
 declare module 'vue' {
   export interface GlobalComponents {
+    Json: typeof import('./../views/components/MyMxGraph/Json.vue')['default']
+    MxProperties: typeof import('./../views/components/MyMxGraph/MxProperties.vue')['default']
     MyMxGraph: typeof import('./../views/components/MyMxGraph/index.vue')['default']
     RouterLink: typeof import('vue-router')['RouterLink']
     RouterView: typeof import('vue-router')['RouterView']
     TButton: typeof import('tdesign-vue-next')['Button']
+    TCheckbox: typeof import('tdesign-vue-next')['Checkbox']
+    TCheckboxGroup: typeof import('tdesign-vue-next')['CheckboxGroup']
     TCol: typeof import('tdesign-vue-next')['Col']
     TCollapse: typeof import('tdesign-vue-next')['Collapse']
     TCollapsePanel: typeof import('tdesign-vue-next')['CollapsePanel']
+    TColorPicker: typeof import('tdesign-vue-next')['ColorPicker']
     TConfigProvider: typeof import('tdesign-vue-next')['ConfigProvider']
+    TForm: typeof import('tdesign-vue-next')['Form']
+    TFormItem: typeof import('tdesign-vue-next')['FormItem']
     THeadMenu: typeof import('tdesign-vue-next')['HeadMenu']
+    TIcon: typeof import('tdesign-vue-next')['Icon']
+    TImage: typeof import('tdesign-vue-next')['Image']
+    TInput: typeof import('tdesign-vue-next')['Input']
+    TInputNumber: typeof import('tdesign-vue-next')['InputNumber']
     TMenuItem: typeof import('tdesign-vue-next')['MenuItem']
+    TOption: typeof import('tdesign-vue-next')['Option']
+    TRadio: typeof import('tdesign-vue-next')['Radio']
+    TRadioGroup: typeof import('tdesign-vue-next')['RadioGroup']
+    TRow: typeof import('tdesign-vue-next')['Row']
+    TSelect: typeof import('tdesign-vue-next')['Select']
+    TSpace: typeof import('tdesign-vue-next')['Space']
+    TSwitch: typeof import('tdesign-vue-next')['Switch']
+    TTabPanel: typeof import('tdesign-vue-next')['TabPanel']
+    TTabs: typeof import('tdesign-vue-next')['Tabs']
     TTooltip: typeof import('tdesign-vue-next')['Tooltip']
   }
 }

+ 22 - 0
src/views/components/MyMxGraph/Json.vue

@@ -0,0 +1,22 @@
+<template>
+  <div class="json-viewer">
+    <json-viewer :value="jsonData" copyable boxed sort />
+  </div>
+</template>
+
+<script setup>
+import { reactive, ref } from 'vue'
+import HelloWorld from './components/HelloWorld.vue'
+let obj = {
+  name: 'qiu', //字符串
+  age: 18, //数组
+  isMan: false, //布尔值
+  date: new Date(),
+  fn: () => {},
+  arr: [1, 2, 5]
+}
+const jsonData = reactive(obj)
+const strData = ref('http://www.baidu.com')
+</script>
+
+<style></style>

+ 336 - 0
src/views/components/MyMxGraph/MxProperties.vue

@@ -0,0 +1,336 @@
+<template>
+  <div class="styleTool">
+    <t-tabs theme="card">
+      <t-tab-panel value="style" label="样式">
+        <t-form label-align="left" size="small" class="style-form">
+          <t-form-item label="线条样式">
+            <t-select v-model="form.dashed" @change="emit('changeDashed', form.dashed)">
+              <t-option value="1" label="-------"></t-option>
+              <t-option value="0" label="———"></t-option>
+            </t-select>
+          </t-form-item>
+
+          <t-row>
+            <t-col>
+              <t-form-item label="线条颜色">
+                <t-color-picker
+                  v-model="form.strokeColor"
+                  :show-alpha="true"
+                  :color-modes="['monochrome']"
+                  @change="emit('changeStrokeColor', form.strokeColor)"></t-color-picker>
+              </t-form-item>
+            </t-col>
+            <t-col v-if="props.isNode">
+              <t-form-item label="填充颜色">
+                <t-color-picker
+                  v-model="form.fillColor"
+                  :show-alpha="true"
+                  :color-modes="['monochrome']"
+                  @change="emit('changeFillColor', form.fillColor)"></t-color-picker>
+              </t-form-item>
+            </t-col>
+          </t-row>
+
+          <t-form-item label="线条宽度">
+            <t-input-number
+              v-model="form.strokeWidth"
+              @change="emit('changeStrokeWidth', form.strokeWidth)"
+              :min="1"
+              :max="10"></t-input-number>
+          </t-form-item>
+
+          <t-form-item label="线条风格" v-if="!props.isNode">
+            <t-select
+              class="select-edgetype"
+              v-model="form.edgeStyle"
+              @change="emit('edgeChange', form.edgeStyle)"
+              placeholder="请选择">
+              <t-option
+                v-for="item in edgeOptions"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"></t-option>
+            </t-select>
+          </t-form-item>
+
+          <t-form-item v-if="props.isNode" label="配置序号(预留)">
+            <t-input-number
+              v-model="newConfigOrder"
+              @change="emit('changeConfigOrder', { newConfigOrder })"
+              :min="1"
+              :max="10"></t-input-number>
+          </t-form-item>
+        </t-form>
+      </t-tab-panel>
+
+      <t-tab-panel value="text" label="文本">
+        <t-form label-align="left" size="small" class="style-form">
+          <t-form-item label="文字内容">
+            <t-input
+              v-model="cellTextValue"
+              @change="emit('textValueChange', cellTextValue)"></t-input>
+          </t-form-item>
+
+          <t-form-item label="字体大小">
+            <t-input-number
+              v-model="form.fontSize"
+              @change="emit('changeFontSize', form.fontSize)"></t-input-number>
+          </t-form-item>
+
+          <t-row>
+            <t-col>
+              <t-form-item>
+                <t-checkbox v-model="fontIsBold">加粗</t-checkbox>
+              </t-form-item>
+            </t-col>
+            <t-col>
+              <t-form-item>
+                <t-checkbox v-model="fontIsIncline">倾斜</t-checkbox>
+              </t-form-item>
+            </t-col>
+            <t-col>
+              <t-form-item>
+                <t-checkbox v-model="fontIsUnderline">下划线</t-checkbox>
+              </t-form-item>
+            </t-col>
+            <t-col>
+              <t-form-item>
+                <t-checkbox v-model="fontIsStrickout">删除线</t-checkbox>
+              </t-form-item>
+            </t-col>
+          </t-row>
+
+          <t-form-item label="字体颜色">
+            <t-color-picker
+              v-model="form.fontColor"
+              :show-alpha="true"
+              :color-modes="['monochrome']"
+              @change="emit('changeFontColor', form.fontColor)"></t-color-picker>
+          </t-form-item>
+
+          <t-form-item label="文本域背景颜色">
+            <t-color-picker
+              :show-alpha="true"
+              :color-modes="['monochrome']"
+              v-model="form.labelBackgroundColor"
+              @change="
+                emit('changeLabelBackgroundColor', form.labelBackgroundColor)
+              "></t-color-picker>
+          </t-form-item>
+        </t-form>
+      </t-tab-panel>
+    </t-tabs>
+
+    <!-- 鼠标坐标展示 -->
+    <div class="graphLocation">
+      <span style="width: 60px; display: inline-block">X:{{ graphX }}</span>
+      <span style="width: 60px; display: inline-block">Y:{{ graphY }}</span>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+interface FormData {
+  edgeStyle: string
+  dashed: string
+  strokeColor: string
+  fontColor: string
+  strokeWidth: number | string
+  labelBackgroundColor: string
+  fontSize: number
+  fillColor: string
+  shadow: boolean | number
+  fontBold: number
+  fontStyle: number
+  orderPoint?: boolean
+  image?: string
+}
+
+interface EdgeOption {
+  label: string
+  value: string
+}
+
+const props = defineProps<{
+  isNode: boolean
+  cellStyle: Record<string, any>
+  graphY: number
+  graphX: number
+  currentNormalType: { title: number }
+  textValue: string
+}>()
+
+const emit = defineEmits([
+  'changeDashed',
+  'changeStrokeColor',
+  'changeFillColor',
+  'changeShadow',
+  'changeStrokeWidth',
+  'edgeChange',
+  'changeConfigOrder',
+  'changeNodeimage',
+  'textValueChange',
+  'changeFontSize',
+  'changeFontColor',
+  'changeLabelBackgroundColor',
+  'changeFontStyle'
+])
+
+const edgeOptions = ref<EdgeOption[]>([
+  { label: '直线', value: 'SideToSide' },
+  { label: '折线', value: 'orthogonalEdgeStyle' }
+])
+
+const form = ref<FormData>({
+  edgeStyle: '折线',
+  dashed: '0',
+  strokeColor: '#6482b9',
+  fontColor: '#6482b9',
+  strokeWidth: 1,
+  labelBackgroundColor: '#FFFFFF',
+  fontSize: 12,
+  fillColor: '#FFFFFF',
+  shadow: false,
+  fontBold: 0,
+  fontStyle: 0
+})
+
+const fontIsBold = ref(false)
+const fontIsIncline = ref(false)
+const fontIsUnderline = ref(false)
+const fontIsStrickout = ref(false)
+const newConfigOrder = ref(0)
+const newConfigOrderId = ref('')
+const cellTextValue = ref('')
+const fontStyle = ref(0)
+const nodeImageUrl = ref('')
+
+const predefineColors = [
+  '#ff4500',
+  '#ff8c00',
+  '#ffd700',
+  '#90ee90',
+  '#00ced1',
+  '#1e90ff',
+  '#c71585',
+  'rgba(255, 69, 0, 0.68)',
+  'rgb(255, 120, 0)',
+  'hsv(51, 100, 98)',
+  'hsva(120, 40, 94, 0.5)',
+  'hsl(181, 100%, 37%)',
+  'hsla(209, 100%, 56%, 0.73)',
+  '#c7158577'
+]
+
+const selectfontStyle = computed(() => {
+  return (
+    Number(fontIsBold.value) * 1 +
+    Number(fontIsIncline.value) * 2 +
+    Number(fontIsUnderline.value) * 4 +
+    Number(fontIsStrickout.value) * 8
+  )
+})
+
+watch(selectfontStyle, newValue => {
+  emit('changeFontStyle', newValue)
+})
+
+watch(
+  () => props.cellStyle,
+  newValue => {
+    if (newValue) {
+      form.value.dashed = newValue.dashed ? newValue.dashed : '0'
+      form.value.strokeWidth = newValue.strokeWidth
+      form.value.strokeColor = newValue.strokeColor
+      form.value.fontColor = newValue.fontColor ? newValue.fontColor : '#000000'
+      form.value.labelBackgroundColor = newValue.labelBackgroundColor
+        ? newValue.labelBackgroundColor
+        : '#FFFFFF'
+      form.value.fillColor = newValue.fillColor ? newValue.fillColor : '#FFFFFF'
+      form.value.fontSize = newValue.fontSize ? newValue.fontSize : 12
+      form.value.shadow = newValue.shadow ? Boolean(newValue.shadow) : false
+      form.value.fontStyle = newValue.fontStyle ? newValue.fontStyle : 0
+      form.value.edgeStyle = newValue.edgeStyle ? newValue.edgeStyle : 'orthogonalEdgeStyle'
+      form.value.orderPoint = newValue.orderPoint ? newValue.orderPoint : false
+      nodeImageUrl.value = newValue.image ? newValue.image : ''
+
+      const style = parseInt(newValue.fontStyle?.toString() || '0')
+      fontIsBold.value = [1, 3, 5, 7, 9, 11, 13, 15].includes(style)
+      fontIsIncline.value = [2, 3, 6, 7, 10, 11, 14, 15].includes(style)
+      fontIsUnderline.value = [4, 5, 6, 7, 12, 13, 14, 15].includes(style)
+      fontIsStrickout.value = [8, 9, 10, 11, 12, 13, 14, 15].includes(style)
+    }
+  },
+  { deep: true, immediate: true }
+)
+
+watch(
+  () => props.currentNormalType,
+  newValue => {
+    if (newValue) {
+      newConfigOrder.value = newValue.title
+    }
+  }
+)
+
+watch(
+  () => props.textValue,
+  newValue => {
+    cellTextValue.value = newValue
+  }
+)
+
+onMounted(() => {
+  // Initialization code if needed
+})
+</script>
+
+<style lang="scss">
+.styleTool {
+  width: 100%;
+  height: 30%;
+  transition: all 1s linear;
+
+  .hide-styleTool {
+    right: -340px;
+  }
+
+  .style-form {
+    display: flex;
+    flex-direction: column;
+    justify-content: center;
+    flex-wrap: wrap;
+    padding: 10px 0 0 10px;
+  }
+
+  .t-tabs__content {
+    border: none;
+    box-shadow: none;
+  }
+
+  .graphLocation {
+    background-color: #fff;
+    border: 1px solid #ededed;
+    padding: 10px;
+    padding-left: 20px;
+    box-sizing: border-box;
+    box-shadow: 9px 9px 12px -4px #cdc7c7;
+    border-radius: 13px;
+    position: absolute;
+    left: -160px;
+    bottom: 5px;
+  }
+}
+
+.t-select-dropdown {
+  .t-select-option {
+    text-align: center;
+
+    img {
+      display: inline-block;
+      width: 30px;
+      height: 30px;
+    }
+  }
+}
+</style>

+ 2 - 2
src/views/components/MyMxGraph/graph_shapes.ts

@@ -30,13 +30,13 @@ const shapes: PaletteShape[] = [
     }
   },
   {
-    index: 1,
+    index: 2,
     idSeed: 1,
     type: myMxConstants.SHAPE_TRIANGLE,
     id: myMxConstants.SHAPE_TRIANGLE,
     title: '三角形',
     class: 'common triangle',
-    width: 120,
+    width: 90,
     height: 60,
     style: {
       [myMxConstants.STYLE_SHAPE]: 'triangle',

+ 409 - 170
src/views/components/MyMxGraph/index.vue

@@ -11,70 +11,71 @@
             :style="shape.style"
             :class="shape.class"
             ref="baseShapeRef">
-            <!-- <span class="shape-label">{{ shape.title }}</span> -->
+            <span class="shape-label">{{ shape.title }}</span>
           </span>
         </t-collapse-panel>
       </t-collapse>
     </div>
     <!-- 顶部工具 -->
     <div class="mx-toolbar">
-      <t-col :span="4">
-        <div
-          class="grid-content bg-purple"
-          style="color: rgb(64, 158, 255); font-weight: 800; font-size: 22px; margin-left: 44px">
-          测试用例
-        </div>
-      </t-col>
-      <t-col :span="4" class="tools-group">
-        <t-tooltip class="item" effect="dark" content="放大" placement="bottom">
-          <t-button variant="text">
-            <template #icon><add-icon /></template>
-          </t-button>
-        </t-tooltip>
-        <t-tooltip class="item" effect="dark" content="缩小" placement="bottom">
-          <t-button variant="text">
-            <template #icon><MinusIcon /></template>
-          </t-button>
-        </t-tooltip>
-      </t-col>
+      <t-row>
+        <t-col :span="4">
+          <div
+            class="grid-content bg-purple"
+            style="color: rgb(64, 158, 255); font-weight: 800; font-size: 22px; margin-left: 44px">
+            测试用例
+          </div>
+        </t-col>
+        <t-col :span="4" class="tools-group">
+          <t-tooltip class="item" effect="dark" content="放大" placement="bottom">
+            <t-button variant="text">
+              <template #icon><add-icon /></template>
+            </t-button>
+          </t-tooltip>
+          <t-tooltip class="item" effect="dark" content="缩小" placement="bottom">
+            <t-button variant="text">
+              <template #icon><MinusIcon /></template>
+            </t-button>
+          </t-tooltip>
+        </t-col>
+      </t-row>
       <input ref="fileInput" type="file" accept=".xml" style="display: none" />
     </div>
     <!-- 中心画布 -->
     <div ref="graphContainerRef" id="mxgraph-container" class="mxgraph-container"></div>
     <!-- 右侧属性 -->
     <div class="mx-properties">
-      <div class="properties-title">属性</div>
-      <div v-if="selectedCell" class="properties-content">
-        <div class="property-item">
-          <label>文本:</label>
-          <input v-model="cellText" @change="updateCellText" type="text" />
-        </div>
-        <div class="property-item">
-          <label>宽度:</label>
-          <input
-            v-model.number="cellGeometry.width"
-            @change="updateCellGeometry"
-            type="number"
-            min="10" />
-        </div>
-        <div class="property-item">
-          <label>高度:</label>
-          <input
-            v-model.number="cellGeometry.height"
-            @change="updateCellGeometry"
-            type="number"
-            min="10" />
-        </div>
-        <div class="property-item">
-          <label>填充色:</label>
-          <input v-model="cellStyle.fillColor" @change="updateCellStyle" type="color" />
-        </div>
-        <div class="property-item">
-          <label>边框色:</label>
-          <input v-model="cellStyle.strokeColor" @change="updateCellStyle" type="color" />
-        </div>
+      <MxProperties
+        @changeDashed="changeDashed"
+        @changeStrokeColor="changeStrokeColor"
+        @changeStrokeWidth="changeStrokeWidth"
+        @changeFontSize="changeFontSize"
+        @changeFontColor="changeFontColor"
+        @changeLabelBackgroundColor="changeLabelBackgroundColor"
+        @changeConfigOrder="changeConfigOrder"
+        @changeFillColor="changeFillColor"
+        @changeShadow="changeShadow"
+        @changeFontStyle="changeFontStyle"
+        @changeNodeimage="changeNodeimage"
+        @edgeChange="edgeChange"
+        @textValueChange="textValueChange"
+        :textValue="textValue"
+        :isNode="isNode"
+        :cellStyle="cellStyle"
+        :currentNormalType="currentNormalType"
+        :graphX="graphX"
+        :graphY="graphY"
+        ref="styleSelect" />
+      <div class="json-viewer">
+        <h4 style="text-align: center">Json数据结构</h4>
+        <json-viewer
+          :value="jsonData"
+          style="height: 80%"
+          :expand-depth="5"
+          copyable
+          boxed
+          sort></json-viewer>
       </div>
-      <div v-else class="properties-empty">未选中任何元素</div>
     </div>
   </div>
 </template>
@@ -97,7 +98,6 @@ import {
   myMxGeometry,
   myMxRubberband,
   myMxUndoManager,
-  myMxOutline,
   myMxEventObject,
   myMxGraphHandler,
   myMxEdgeHandler,
@@ -107,7 +107,8 @@ import {
   myMxEllipse,
   myMxShape,
   myMxConnectionConstraint,
-  myMxPolyline
+  myMxPolyline,
+  myMxEdgeStyle
 } from '@/graph/mxGraph'
 import { CellGeometry, GraphData } from '@/types/mxgraph'
 import { shapes } from '@/views/components/MyMxGraph/graph_shapes'
@@ -118,7 +119,9 @@ const baseShapeRef = ref<HTMLElement[]>([])
 const graph = ref<typeof myMxGraph | null>(null)
 const editor = ref<typeof myMxEditor | null>(null)
 const undoMng = ref<typeof myMxUndoManager | null>(null)
-const outline = ref<typeof myMxOutline | null>(null)
+// const outline = ref<typeof myMxOutline | null>(null)
+const currentNormalType = ref<typeof myMxCell | null>({})
+
 // const currentNormalType = ref<typeof myMxCell | null>(null)
 const textValue = ref('')
 // const uploadDataVisible = ref(false)
@@ -171,47 +174,24 @@ watch(
   { immediate: true }
 )
 
-function updateCellText() {
-  if (!graph?.value || !selectedCell.value) return
-  graph.value.getModel().setValue(selectedCell.value, cellText.value)
-}
-
-function updateCellGeometry() {
-  if (!graph?.value || !selectedCell.value) return
-  const geo = selectedCell.value.getGeometry()
-  if (geo) {
-    geo.width = cellGeometry.value.width
-    geo.height = cellGeometry.value.height
-    graph.value.getModel().setGeometry(selectedCell.value, geo)
-  }
-}
-
-function updateCellStyle() {
-  if (!graph?.value || !selectedCell.value) return
-  const newStyle = {
-    [myMxConstants.STYLE_FILLCOLOR]: cellStyle.value.fillColor,
-    [myMxConstants.STYLE_STROKECOLOR]: cellStyle.value.strokeColor
-  }
-  graph.value
-    .getModel()
-    .setStyle(
-      selectedCell.value,
-      myMxUtils.setStyle(graph.value.getModel().getStyle(selectedCell.value) || '', newStyle)
-    )
-}
+// 初始化
 const initGraph = (container: HTMLElement) => {
+  // 创建graph
   editor.value = new myMxEditor()
   graph.value = unref(editor).graph
   editor.value.setGraphContainer(container)
 
   // 配置样式
   configureStyles(unref(graph))
+
   // 去锯齿效果
   myMxRectangleShape.prototype.crisp = true
+
   // 定义全局变量,如。用于触发建立新的连接的活动区域的最小尺寸(以像素为单位),该部分(100%)的小区区域被用于触发新的连接,以及一些窗口和“下拉菜菜单选择
   myMxConstants.MIN_HOTSPOT_SIZE = 16
   myMxConstants.DEFAULT_HOTSPOT = 1
 
+  //cell创建支持传入html
   graph.value.setHtmlLabels(true)
   graph.value.setDropEnabled(true)
   graph.value.setSplitEnabled(false)
@@ -278,16 +258,47 @@ const initGraph = (container: HTMLElement) => {
   // }
 
   // 设置默认组
-  const group = new myMxCell('Group', new myMxGeometry(), 'group;fontColor=white;')
-  group.setVertex(true)
-  group.setConnectable(true)
-  editor.value.defaultGroup = group
+  const group_ = new myMxCell('Group', new myMxGeometry(), 'group;fontColor=white;')
+  group_.setVertex(true)
+
+  // 设置组可连接
+  group_.setConnectable(true)
+  editor.value.defaultGroup = group_
   editor.value.groupBorderSize = 80
 
   // 是否根元素
-  // graph.value.isValidRoot = function (cell) {
-  //   return this.isValidDropTarget(cell)
-  // }
+  graph.value.isValidRoot = function (cell: any) {
+    return graph.value.isValidDropTarget(cell)
+  }
+
+  // 返回元素
+  graph.value.getLabel = function (cell: any) {
+    let tmp = myMxGraph.prototype.getLabel.apply(this, arguments) // "supercall"
+    if (graph.value.isCellLocked(cell)) {
+      // 如元素被锁定 返回空标签
+      return ''
+    } else if (graph.value.isCellCollapsed(cell)) {
+      let index = tmp.indexOf('</h1>')
+      if (index > 0) {
+        tmp = tmp.substring(0, index + 5)
+      }
+    }
+    return tmp
+  }
+
+  // 目标是否有效
+  graph.value.isValidDropTarget = function (cell: any) {
+    // console.log(cell, cells, evt);
+    return graph.value.isSwimlane(cell)
+  }
+
+  // 是否根元素
+  graph.value.isValidRoot = function (cell: any) {
+    return graph.value.isValidDropTarget(cell)
+  }
+
+  // 是否可以被选中
+  graph.value.isCellSelectable(true)
 
   // 允许重复连接
   graph.value.setMultigraph(true)
@@ -306,7 +317,8 @@ const initGraph = (container: HTMLElement) => {
   graph.value.setResizeContainer(true)
 
   // 开启画布平滑移动
-  graph.value.setPanning(true)
+  graph.value.setPanning = true
+  // graph.value.setPanning(true)
   // 开启提示
   graph.value.setTooltips(false)
   // 允许连线
@@ -327,80 +339,85 @@ const initGraph = (container: HTMLElement) => {
   graph.value.getView().addListener(myMxEvent.UNDO, listener)
 
   // 创建缩略图
-  outline.value = new myMxOutline(graph.value, document.createElement('div'))
-
+  // outline.value = new myMxOutline(graph.value, document.createElement('div'))
+  if (graph.value == null || graph.value == undefined) {
+    return
+  }
   // 从value中获取显示的内容(如果节点的value为空则显示节点的title)
   graph.value.convertValueToString = (cell: { [x: string]: any }) => {
     return cell['value'] ? cell['value'] : cell['title']
   }
 }
 
-const configureStyles = (graph: typeof myMxGraph) => {
+const configureStyles = (_graph: typeof myMxGraph) => {
   // 设置节点的文字可被移动
-  graph.vertexLabelsMovable = false
-
-  const vertexStyle = graph.getStylesheet().getDefaultVertexStyle()
-  vertexStyle[myMxConstants.STYLE_SHAPE] = myMxConstants.SHAPE_LABEL
-  vertexStyle[myMxConstants.STYLE_PERIMETER] = myMxPerimeter.RectanglePerimeter
-  vertexStyle[myMxConstants.STYLE_VERTICAL_ALIGN] = myMxConstants.ALIGN_MIDDLE
-  vertexStyle[myMxConstants.STYLE_ALIGN] = myMxConstants.ALIGN_CENTER
-  vertexStyle[myMxConstants.STYLE_IMAGE_ALIGN] = myMxConstants.ALIGN_CENTER
-  vertexStyle[myMxConstants.STYLE_IMAGE_VERTICAL_ALIGN] = myMxConstants.ALIGN_CENTER
+  _graph.vertexLabelsMovable = false
+
+  // const vertexStyle = _graph.getStylesheet().getDefaultVertexStyle()
+
+  let style = new Object()
+  style[myMxConstants.STYLE_SHAPE] = myMxConstants.SHAPE_LABEL
+  style[myMxConstants.STYLE_PERIMETER] = myMxPerimeter.RectanglePerimeter
+  style[myMxConstants.STYLE_VERTICAL_ALIGN] = myMxConstants.ALIGN_MIDDLE
+  style[myMxConstants.STYLE_ALIGN] = myMxConstants.ALIGN_CENTER
+  style[myMxConstants.STYLE_IMAGE_ALIGN] = myMxConstants.ALIGN_CENTER
+  style[myMxConstants.STYLE_IMAGE_VERTICAL_ALIGN] = myMxConstants.ALIGN_CENTER
   // style[myMxConstants.STYLE_SPACING_TOP] = 6;
-  vertexStyle[myMxConstants.STYLE_SPACING_LEFT] = 5
+  style[myMxConstants.STYLE_SPACING_LEFT] = 5
   // style[myMxConstants.STYLE_GRADIENTCOLOR] = 'skyblue'; // 渐变颜色
-  vertexStyle[myMxConstants.STYLE_STROKECOLOR] = '#5d65df' // 线条颜色
-  vertexStyle[myMxConstants.STYLE_FILLCOLOR] = '#FFFFFF'
-  vertexStyle[myMxConstants.STYLE_FONTCOLOR] = '#1d258f' // 字体颜色
-  vertexStyle[myMxConstants.STYLE_FONTFAMILY] = 'Verdana' // 字体风格
-  vertexStyle[myMxConstants.STYLE_FONTSIZE] = '12' // 字体大小
-  vertexStyle[myMxConstants.STYLE_FONTSTYLE] = '0' // 斜体字
-  vertexStyle[myMxConstants.WORD_WRAP] = 'normal' // 文字换行    word-break: break-all;
-  vertexStyle[myMxConstants['word-break']] = 'break-all' // 文字换行
-  vertexStyle[myMxConstants.STYLE_WHITE_SPACE] = 'wrap' // 文字换行
-  vertexStyle[myMxConstants.STYLE_ROUNDED] = false // 圆角
-  vertexStyle[myMxConstants.STYLE_IMAGE_WIDTH] = '28' // 图片宽度
-  vertexStyle[myMxConstants.STYLE_IMAGE_HEIGHT] = '28' // 图片高度
-  vertexStyle[myMxConstants.STYLE_OPACITY] = '100' // 节点透明度(不包含字体)
-
-  const cellStyle = graph.getStylesheet().getCellStyle('group')
-  cellStyle[myMxConstants.STYLE_SHAPE] = myMxConstants.SHAPE_IMAGE
-  cellStyle[myMxConstants.STYLE_FONTCOLOR] = '#774400'
-  cellStyle[myMxConstants.STYLE_PERIMETER] = myMxPerimeter.RectanglePerimeter
-  cellStyle[myMxConstants.STYLE_PERIMETER_SPACING] = '6'
-  cellStyle[myMxConstants.STYLE_ALIGN] = myMxConstants.ALIGN_LEFT
-  cellStyle[myMxConstants.STYLE_VERTICAL_ALIGN] = myMxConstants.ALIGN_MIDDLE
-  cellStyle[myMxConstants.STYLE_FONTSIZE] = '10'
-  cellStyle[myMxConstants.STYLE_FONTSTYLE] = 2
-  cellStyle[myMxConstants.STYLE_IMAGE_WIDTH] = '16'
-  cellStyle[myMxConstants.STYLE_IMAGE_HEIGHT] = '16'
-  cellStyle[myMxConstants.STYLE_BACKGROUNDCOLOR] = 'transparent'
-  // graph.getStylesheet().putCellStyle('group', cellStyle)
-
-  const portCellStyle = graph.getStylesheet().getCellStyle('port')
-  portCellStyle[myMxConstants.STYLE_SHAPE] = myMxConstants.SHAPE_IMAGE
-  portCellStyle[myMxConstants.STYLE_FONTCOLOR] = '#774400'
-  portCellStyle[myMxConstants.STYLE_PERIMETER] = myMxPerimeter.RectanglePerimeter
-  portCellStyle[myMxConstants.STYLE_PERIMETER_SPACING] = '6'
-  portCellStyle[myMxConstants.STYLE_ALIGN] = myMxConstants.ALIGN_LEFT
-  portCellStyle[myMxConstants.STYLE_VERTICAL_ALIGN] = myMxConstants.ALIGN_MIDDLE
-  portCellStyle[myMxConstants.STYLE_FONTSIZE] = '10'
-  portCellStyle[myMxConstants.STYLE_FONTSTYLE] = 2
-  portCellStyle[myMxConstants.STYLE_IMAGE_WIDTH] = '16'
-  portCellStyle[myMxConstants.STYLE_IMAGE_HEIGHT] = '16'
-  portCellStyle[myMxConstants.STYLE_BACKGROUNDCOLOR] = 'transparent'
-  // graph.getStylesheet().putCellStyle('port', portCellStyle)
-
-  const edgeStyle = graph.getStylesheet().getDefaultEdgeStyle()
-
-  Object.assign(edgeStyle, {
-    [myMxConstants.STYLE_LABEL_BACKGROUNDCOLOR]: '#FFFFFF',
-    [myMxConstants.STYLE_STROKEWIDTH]: '2',
-    [myMxConstants.STYLE_ROUNDED]: false
-  })
+  style[myMxConstants.STYLE_STROKECOLOR] = '#5d65df' // 线条颜色
+  style[myMxConstants.STYLE_FILLCOLOR] = '#FFFFFF'
+  style[myMxConstants.STYLE_FONTCOLOR] = '#1d258f' // 字体颜色
+  style[myMxConstants.STYLE_FONTFAMILY] = 'Verdana' // 字体风格
+  style[myMxConstants.STYLE_FONTSIZE] = '12' // 字体大小
+  style[myMxConstants.STYLE_FONTSTYLE] = '0' // 斜体字
+  style[myMxConstants.WORD_WRAP] = 'normal' // 文字换行    word-break: break-all;
+  style[myMxConstants['word-break']] = 'break-all' // 文字换行
+  style[myMxConstants.STYLE_WHITE_SPACE] = 'wrap' // 文字换行
+  style[myMxConstants.STYLE_ROUNDED] = false // 圆角
+  style[myMxConstants.STYLE_IMAGE_WIDTH] = '28' // 图片宽度
+  style[myMxConstants.STYLE_IMAGE_HEIGHT] = '28' // 图片高度
+  style[myMxConstants.STYLE_OPACITY] = '100' // 节点透明度(不包含字体)
+  _graph.getStylesheet().putDefaultVertexStyle(style)
+
+  style = new Object()
+  style[myMxConstants.STYLE_SHAPE] = myMxConstants.SHAPE_SWIMLANE
+  style[myMxConstants.STYLE_PERIMETER] = myMxPerimeter.RectanglePerimeter
+  style[myMxConstants.STYLE_ALIGN] = myMxConstants.ALIGN_CENTER
+  style[myMxConstants.STYLE_VERTICAL_ALIGN] = myMxConstants.ALIGN_TOP
+  style[myMxConstants.STYLE_FILLCOLOR] = '#409eff'
+  style[myMxConstants.STYLE_STROKECOLOR] = '#409eff'
+  style[myMxConstants.STYLE_FONTCOLOR] = '#000000'
+  style[myMxConstants.STYLE_ROUNDED] = false
+  style[myMxConstants.STYLE_OPACITY] = '80'
+  style[myMxConstants.STYLE_STARTSIZE] = '30'
+  style[myMxConstants.STYLE_FONTSIZE] = '16'
+  style[myMxConstants.STYLE_FONTSTYLE] = 1
+  _graph.getStylesheet().putCellStyle('group', style)
+
+  style = new Object()
+  style[myMxConstants.STYLE_SHAPE] = myMxConstants.SHAPE_IMAGE
+  style[myMxConstants.STYLE_FONTCOLOR] = '#774400'
+  style[myMxConstants.STYLE_PERIMETER] = myMxPerimeter.RectanglePerimeter
+  style[myMxConstants.STYLE_PERIMETER_SPACING] = '6'
+  style[myMxConstants.STYLE_ALIGN] = myMxConstants.ALIGN_LEFT
+  style[myMxConstants.STYLE_VERTICAL_ALIGN] = myMxConstants.ALIGN_MIDDLE
+  style[myMxConstants.STYLE_FONTSIZE] = '10'
+  style[myMxConstants.STYLE_FONTSTYLE] = 2
+  style[myMxConstants.STYLE_IMAGE_WIDTH] = '16'
+  style[myMxConstants.STYLE_IMAGE_HEIGHT] = '16'
+  style[myMxConstants.STYLE_BACKGROUNDCOLOR] = 'transparent'
+  _graph.getStylesheet().putCellStyle('port', style)
+
+  // const edgeStyle = _graph.getStylesheet().getDefaultEdgeStyle()
+  style = _graph.getStylesheet().getDefaultEdgeStyle()
+  style[myMxConstants.STYLE_LABEL_BACKGROUNDCOLOR] = '#FFFFFF'
+  style[myMxConstants.STYLE_STROKEWIDTH] = '2'
+  style[myMxConstants.STYLE_ROUNDED] = false
+  style[myMxConstants.STYLE_EDGE] = myMxEdgeStyle.ElbowConnector
 
-  var edgeStyles = graph.getStylesheet().getDefaultEdgeStyle()
-  // let labelStyle = graph.getStylesheet().getDefaultVertexStyle()
+  var edgeStyles = _graph.getStylesheet().getDefaultEdgeStyle()
+  // let labelStyle = _graph.getStylesheet().getDefaultVertexStyle()
 
   // 设置连线风格(设置为正交折线)
   edgeStyles['edgeStyle'] = 'orthogonalEdgeStyle'
@@ -425,7 +442,7 @@ const configureStyles = (graph: typeof myMxGraph) => {
   // 应该使用实时预览的最大单元数。默认值为0,表示没有实时预览。
   myMxGraphHandler.prototype.maxLivePreview = 100
   // Alt 按下禁用导航线
-  myMxGraphHandler.prototype.useGuidesForEvent = me => {
+  myMxGraphHandler.prototype.useGuidesForEvent = (me: { getEvent: () => any }) => {
     return !myMxEvent.isAltDown(me.getEvent())
   }
   // 导航线颜色
@@ -451,13 +468,13 @@ const configureStyles = (graph: typeof myMxGraph) => {
   // mxGraph.prototype.collapsedImage = new mxImage('images/collapsed.gif', 15, 15);
   // mxGraph.prototype.expandedImage = new mxImage('images/expanded.gif', 15, 15);
 
-  // 配置节点中心的连接图标(注釋掉即可指定錨點連接到另一個節點的錨點上)
-  myMxConnectionHandler.prototype.connectImage = new myMxImage('./icon/connectionpoint.png', 14, 14)
+  // 配置节点中心的连接图标(注释掉即可指定锚点连接到另一个节点的锚点上)
+  myMxConnectionHandler.prototype.connectImage = new myMxImage('icon/connectionpoint.png', 14, 14)
   // 显示中心端口图标
-  graph.connectionHandler.targetConnectImage = false
+  _graph.connectionHandler.targetConnectImage = false
 
   // 是否开启浮动自动连接
-  graph.connectionHandler.isConnectableCell = () => {
+  graph.value.connectionHandler.isConnectableCell = () => {
     return true
   }
 
@@ -492,7 +509,7 @@ const eventCenter = () => {
   if (!graph.value) return
   // 监听自定义事件
   // graph.value.addListener(myMxEvent.NORMAL_TYPE_CLICKED, (_sender: any, evt: any) => {
-  //   const cell = evt.properties.cell.state.cell
+  //   const cell = evt.properties.cell?.state.cell
   //   console.log('cell', cell)
   //   currentNormalType.value = cell
   // })
@@ -520,6 +537,63 @@ const eventCenter = () => {
       }
     })
   })
+
+  // 拖动节点
+  graph.value.addListener(
+    myMxEvent.CELLS_MOVED,
+    (_sender: any, evt: { properties: { cells: any[] } }) => {
+      // console.log(this.graph, 'graph');
+      let cellsName: any = []
+      nextTick(() => {
+        evt.properties.cells.forEach(
+          (item: { parent: { id: string | string[] }; title: string }) => {
+            item.parent.id.includes('group') && cellsName.push(item.title)
+          }
+        )
+        // evt.properties.cells[0].parent.id !== '1' &&
+        //   this.$message.info(`${[...cellsName]}节点进入${evt.properties.cells[0].parent.title}`)
+      })
+    }
+  )
+  // 删除节点
+  // 删除节点触发事件
+  graph.value.addListener(
+    myMxEvent.CELLS_REMOVED,
+    (_sender: any, evt: { properties: { cells: any } }) => {
+      nextTick(() => {
+        let removeCells = evt.properties.cells
+        console.log(removeCells, 'removeCells')
+        removeCells.forEach((item: { vertex: any; isGroup: any; id: string; edge: any }) => {
+          // 拿每一个cellId在jsonData中进行遍历,并进行移除
+          if (item.vertex) {
+            // 判断是否为组节点
+            if (item.isGroup) {
+              jsonData.value.cells.groups.splice(
+                jsonData.value.cells.groups.findIndex(jsonItem => {
+                  return jsonItem.id === item.id
+                }),
+                1
+              )
+            } else {
+              jsonData.value.cells.nodes.splice(
+                jsonData.value.cells.nodes.findIndex(jsonItem => {
+                  return jsonItem.id === item.id
+                }),
+                1
+              )
+            }
+          } else if (item.edge) {
+            jsonData.value.edges.splice(
+              jsonData.value.edges.findIndex(jsonItem => {
+                return jsonItem.id === item.id
+              }),
+              1
+            )
+          }
+        })
+      })
+    }
+  )
 }
 
 // 配置鼠标事件
@@ -530,7 +604,17 @@ const configMouseEvent = () => {
     currentState: null,
     previousStyle: null,
 
-    mouseDown: (_sender, evt) => {
+    mouseDown: (
+      _sender: any,
+      evt: {
+        state: {
+          cell: {
+            style: any
+            edge: any
+          }
+        }
+      }
+    ) => {
       if (!evt.state) {
         console.log('点击了画布')
         return
@@ -552,7 +636,7 @@ const configMouseEvent = () => {
       }
     },
 
-    mouseMove: (_sender, me) => {
+    mouseMove: (_sender: any, me: { graphX: number; graphY: number }) => {
       graphX.value = Math.ceil(me.graphX)
       graphY.value = Math.ceil(me.graphY)
     },
@@ -578,7 +662,7 @@ const configMouseEvent = () => {
             if (getcellStyle) {
               const arr = getcellStyle.split(';')
               const styleObject: Record<string, any> = {}
-              arr.forEach(item => {
+              arr.forEach((item: { split: (arg0: string) => [any, any] }) => {
                 const [key, value] = item.split('=')
                 if (key && value) {
                   styleObject[key] = value
@@ -622,7 +706,7 @@ const initGeneralTool = () => {
     const itemClass = toolItem.class
     // 新增基础节点
     const generalDropHandler = (
-      graph: typeof myMxGraph,
+      _graph: typeof myMxGraph,
       _evt: any,
       dropCell: any,
       x: number,
@@ -636,10 +720,10 @@ const initGeneralTool = () => {
       const style = Object.keys(styleObj)
         .map(attr => `${attr}=${styleObj[attr]}`)
         .join(';')
-      const parent = drop ? dropCell : graph.getDefaultParent()
-      graph.getModel().beginUpdate()
+      const parent = drop ? dropCell : graph.value.getDefaultParent()
+      graph.value.getModel().beginUpdate()
       try {
-        const vertex = graph.insertVertex(
+        const vertex = graph.value.insertVertex(
           parent,
           null,
           null,
@@ -649,13 +733,18 @@ const initGeneralTool = () => {
           height,
           style + ';whiteSpace=wrap;word-break=break-all'
         )
-        vertex.title = `<div style='word-break:break-all'>` + toolItem['title'] + '</div>'
+        console.log('Edge:', vertex)
+        console.log('Source:', vertex.source)
+        console.log('Target:', vertex.target)
+        vertex.title = `<div style='word-break:break-all;'>` + toolItem['title'] + '</div>'
         vertex.dropAble = toolItem['dropAble']
         vertex.id = toolItem['id'] + '-' + toolItem['idSeed']
         toolItem['idSeed']++
         vertex['isGroup'] = toolItem['id'].includes('group') ? true : false
       } finally {
-        graph.getModel().endUpdate()
+        graph.value.getModel().endUpdate()
+        // graph.view.validate()
+        // graph.value.refresh()
       }
     }
 
@@ -683,6 +772,138 @@ const initGeneralTool = () => {
     ds.setGuidesEnabled(true)
   })
 }
+// 设置连线样式
+const changeDashed = (value: string) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  graph.value.setCellStyles(myMxConstants.STYLE_DASHED, value, [...cell])
+}
+
+// 设置线条颜色样式
+const changeStrokeColor = (value: string) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  graph.value.setCellStyles(myMxConstants.STYLE_STROKECOLOR, value, [...cell])
+}
+
+// 设置线条宽度
+const changeStrokeWidth = (value: string) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  graph.value.setCellStyles(myMxConstants.STYLE_STROKEWIDTH, value, [...cell])
+}
+
+// 设置字体大小
+const changeFontSize = (value: string) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  graph.value.setCellStyles(myMxConstants.STYLE_FONTSIZE, value, [...cell])
+}
+
+// 设置字体颜色
+const changeFontColor = (value: string) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  graph.value.setCellStyles(myMxConstants.STYLE_FONTCOLOR, value, [...cell])
+}
+
+// 设置线条说明的背景颜色
+const changeLabelBackgroundColor = (value: string) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  graph.value.setCellStyles(myMxConstants.STYLE_LABEL_BACKGROUNDCOLOR, value, [...cell])
+}
+
+const changeFillColor = (value: string) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  graph.value.setCellStyles(myMxConstants.STYLE_FILLCOLOR, value, [...cell])
+}
+
+const changeShadow = (value: boolean) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  graph.value.setCellStyles(myMxConstants.STYLE_SHADOW, +value, [...cell])
+}
+
+const changeFontStyle = (value: string) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  graph.value.setCellStyles(myMxConstants.STYLE_FONTSTYLE, value, [...cell])
+}
+
+const changeNodeimage = (value: string) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  graph.value.setCellStyles(myMxConstants.STYLE_IMAGE, value, [...cell])
+}
+
+// 删除节点
+const deleteNode = () => {
+  if (!graph.value) return
+
+  const cells = graph.value.getSelectionCells()
+  graph.value.removeCells([...cells])
+}
+
+// 修改连线样式
+const edgeChange = (value: string) => {
+  if (!graph.value) return
+
+  try {
+    const cell = graph.value.getSelectionCells()
+    graph.value.setCellStyles('edgeStyle', value, [...cell])
+    const style = cell[0].style
+    const valStr = cell[0].value
+    graph.value.removeCells(cell)
+    const parent = graph.value.getDefaultParent()
+    let v1 = ''
+    let v2 = ''
+    // 获取ID单元
+    parent['children'].forEach(item => {
+      item['id'] === cell[0].source.id ? (v1 = item) : false
+      item['id'] === cell[0].target.id ? (v2 = item) : false
+    })
+    graph.value.getModel().beginUpdate()
+    graph.value.insertEdge(parent, null, valStr, v1, v2, style)
+    graph.value.getModel().endUpdate()
+  } catch (error) {
+    console.log(error)
+  }
+}
+
+// 修改节点文本内容
+const textValueChange = (value: string) => {
+  if (!graph.value) return
+
+  const cell = graph.value.getSelectionCells()
+  console.log(value, '节点文本新内容', graph.value)
+  graph.value.cellLabelChanged(cell[0], value)
+}
+
+const changeConfigOrder = (val: any) => {
+  if (!graph.value || !currentNormalType.value) return
+
+  // 获取当前的normalType元素,并更新他的title
+  currentNormalType.value.title = val.newConfigOrder
+  // 修改指定cell的背景图片
+  graph.value.setCellStyles(
+    myMxConstants.STYLE_IMAGE,
+    `./images/order/unselect-${val.newConfigOrder}.png`,
+    [currentNormalType.value]
+  )
+  graph.value.refresh(currentNormalType.value)
+}
 onMounted(async () => {
   if (!myMxClient.isBrowserSupported()) {
     alert('当前浏览器不支持拓扑图功能,请更换浏览器访问,建议使用Chrome浏览器访问!')
@@ -704,6 +925,7 @@ onMounted(async () => {
       return null
     }
     if (graphContainerRef.value) {
+      // 初始化画布
       initGraph(graphContainerRef.value)
       eventCenter()
       configMouseEvent()
@@ -808,6 +1030,7 @@ onMounted(async () => {
 }
 </style>
 <style>
+@import '@/assets/graph/style/general-shap.css';
 .t-collapse-panel__wrapper .t-collapse-panel__content {
   width: 100%;
   display: flex;
@@ -822,7 +1045,6 @@ onMounted(async () => {
     white-space: wrap;
     text-align: center;
     position: relative;
-
     .shape-label {
       position: absolute;
       bottom: -30px;
@@ -831,4 +1053,21 @@ onMounted(async () => {
     }
   }
 }
+.common {
+  width: 30%;
+  cursor: pointer;
+  height: 50px;
+  white-space: wrap;
+  text-align: center;
+  position: relative;
+  .shape-label {
+    position: absolute;
+    bottom: -30px;
+    left: 0;
+    width: 100%;
+  }
+}
+.mxCellEditor.mxPlainTextEditor {
+  position: absolute;
+}
 </style>