Ver Fonte

prop datas

Alsmile há 2 anos atrás
pai
commit
958ac6d33e

BIN
public/data.xlsx


+ 99 - 3
src/styles/app.css

@@ -96,6 +96,14 @@ h5 {
   cursor: pointer !important;
 }
 
+.hover-background:hover {
+  background-color: var(--color-background-popup-hover);
+}
+
+.ellipsis {
+  text-overflow: ellipsis;
+}
+
 a {
   color: var(--color-primary);
   cursor: pointer;
@@ -226,10 +234,18 @@ a.hover:hover {
   display: grid;
 }
 
+.left {
+  text-align: left;
+}
+
 .center {
   text-align: center;
 }
 
+.right {
+  text-align: right;
+}
+
 .w-full {
   width: 100%;
 }
@@ -374,18 +390,98 @@ a.hover:hover {
   background-color: #ff400030;
   color: var(--color-bland);
   padding: 0 6px;
-  margin-left: 4px;
   border-radius: 2px;
 }
 
+.form-item {
+  display: flex;
+
+  & > label {
+    width: 76px;
+    font-size: 12px;
+    line-height: 30px;
+    flex-shrink: 0;
+    color: var(--color);
+  }
+
+  .t-input-number {
+    width: 100%;
+    line-height: 30px;
+    height: 30px;
+
+    .t-input__wrap {
+      height: 100%;
+
+      .t-input {
+        height: 100%;
+        background: none;
+        color: var(--color-title);
+        box-shadow: none;
+
+        /* &:hover,
+        &.t-is-focused {
+          border-color: var(--color-primary);
+        } */
+      }
+    }
+
+    .t-input-number__increase,
+    .t-input-number__decrease {
+      width: 20px;
+    }
+  }
+
+  .t-input,
+  .t-textarea textarea {
+    .t-input__inner {
+      text-overflow: unset;
+      &::placeholder {
+        color: var(--color-gray);
+      }
+    }
+
+    &.t-is-disabled {
+      border-color: var(--color-border-input) !important;
+
+      .t-input__inner {
+        color: var(--color-desc);
+      }
+    }
+  }
+
+  & > input {
+    background: none;
+    outline: none;
+    color: var(--color-desc);
+
+    &::placeholder {
+      color: var(--color-gray);
+    }
+  }
+
+  .t-icon {
+    font-size: 16px;
+    height: 30px;
+  }
+
+  .t-tag {
+    line-height: 24px;
+    .t-icon {
+      height: 23px;
+      margin-left: 4px;
+      margin-right: -4px;
+    }
+  }
+}
+
 *::placeholder {
   color: var(--color-gray);
 }
 
 /*定义滚动条轨道 内阴影+圆角*/
 ::-webkit-scrollbar {
-  width: 5px;
-  height: 7px;
+  width: 3px;
+  height: 6px;
   background: transparent;
 }
 

+ 2 - 72
src/styles/props.css

@@ -10,30 +10,10 @@
   }
 
   .form-item {
-    display: flex;
-
-    & > label {
-      width: 76px;
-      font-size: 12px;
-      line-height: 30px;
-      flex-shrink: 0;
-      color: var(--color);
-    }
-
     .t-input-number {
-      width: 100%;
-      line-height: 30px;
-      height: 30px;
-
       .t-input__wrap {
-        height: 100%;
-
         .t-input {
-          height: 100%;
-          background: none;
           border-color: transparent;
-          color: var(--color-title);
-          box-shadow: none;
 
           &:hover,
           &.t-is-focused {
@@ -41,59 +21,20 @@
           }
         }
       }
-
-      .t-input-number__increase,
-      .t-input-number__decrease {
-        width: 20px;
-      }
     }
 
-    .t-input {
+    .t-input,
+    .t-textarea textarea {
       border-color: transparent;
 
-      .t-input__inner {
-        text-overflow: unset;
-        &::placeholder {
-          color: var(--color-gray);
-        }
-      }
       &:hover,
       &.t-is-focused {
         border-color: var(--color-border-input);
       }
-
-      &.t-is-disabled {
-        border-color: var(--color-border-input) !important;
-
-        .t-input__inner {
-          color: var(--color-desc);
-        }
-      }
     }
 
     & > input {
-      background: none;
       border: none;
-      outline: none;
-      color: var(--color-desc);
-
-      &::placeholder {
-        color: var(--color-gray);
-      }
-    }
-
-    .t-icon {
-      font-size: 16px;
-      height: 30px;
-    }
-
-    .t-tag {
-      line-height: 24px;
-      .t-icon {
-        height: 23px;
-        margin-left: 4px;
-        margin-right: -4px;
-      }
     }
   }
 
@@ -282,12 +223,6 @@
     overflow-y: auto;
   }
 
-  .t-input {
-    height: 30px;
-    background: none;
-    border: 1px solid var(--color-border-input);
-  }
-
   .t-slider {
     .t-slider__rail,
     .t-slider__track {
@@ -390,9 +325,4 @@
       }
     }
   }
-
-  ::-webkit-scrollbar {
-    width: 2px;
-    height: 6px;
-  }
 }

+ 36 - 9
src/styles/tdesign.css

@@ -5,10 +5,13 @@
 
 .t-input {
   font-size: 13px;
+  background: none;
+  border: 1px solid var(--color-border-input);
+  height: 30px;
 
   &:hover,
   &.t-is-focused {
-    border-color: var(--color-border-input);
+    border-color: var(--color-primary);
   }
 
   &.t-is-error {
@@ -27,6 +30,12 @@
   }
 }
 
+.t-select {
+  .t-input.t-is-readonly {
+    background: none;
+  }
+}
+
 .t-select > .t-tag:before {
   content: none;
 }
@@ -122,10 +131,23 @@
           border-radius: 2px;
         }
       }
+
+      .t-input__wrap {
+        width: calc(100% + 32px);
+        margin-left: -16px;
+        .t-input {
+          background: none;
+          border-color: var(--color-border-input);
+          &:hover {
+            border-color: var(--color-primary);
+          }
+        }
+      }
     }
 
     &.t-dropdown__item--disabled {
       .t-dropdown__item-text {
+        cursor: default;
         background-color: var(--color-background-popup) !important;
         color: var(--color-desc);
       }
@@ -139,7 +161,7 @@
   }
 
   &::-webkit-scrollbar {
-    width: 7px !important;
+    width: 6px !important;
   }
 }
 
@@ -168,7 +190,7 @@
   }
 
   &::-webkit-scrollbar {
-    width: 7px !important;
+    width: 6px !important;
   }
 }
 
@@ -405,10 +427,6 @@
 .t-button--variant-base {
   color: var(--color);
 
-  &:hover {
-    color: #000000e6;
-  }
-
   &.t-button--theme-primary {
     color: #ffffff !important;
   }
@@ -457,8 +475,17 @@
       margin-left: -20px;
       padding: 0 20px;
       overflow: auto;
-      &::-webkit-scrollbar {
-        width: 3px !important;
+
+      &.t-tabs {
+        padding: 0;
+        & > div {
+          padding: 0 20px;
+        }
+
+        .t-tabs__content {
+          height: 340px;
+          overflow: auto;
+        }
       }
     }
   }

+ 5 - 0
src/styles/var.css

@@ -47,6 +47,8 @@
   --shadow-hover: 0px 4px 8px 0px rgba(0, 10, 38, 0.3);
   --shadow-panel: 0px 2px 6px 0px rgb(0 10 38 / 4%);
 
+  --td-bg-color-component-hover: var(--color-border-input-hover);
+  --td-bg-color-component-active: var(--color-background-input);
   --td-radius-medium: 4px;
   --td-bg-color-container: var(--color-background);
   --td-component-border: #42516c;
@@ -65,8 +67,11 @@
   --td-brand-color-light: var(--color-border-input);
   --td-mask-disabled: var(--color-background);
   --td-text-color-disabled: var(--color-gray);
+  --td-bg-color-component-disabled: var(--color-border-input-hover);
 
   --color-dialog-border: transparent;
+  --td-bg-color-specialcomponent: transparent;
+  --td-border-level-2-color: var(--color-border-input);
 
   --color-background-editor: #181b24;
   --color-border-editor: #1e2430;

+ 194 - 20
src/views/components/Actions.vue

@@ -41,13 +41,22 @@
             </t-radio-group>
           </div>
         </template>
-        <template v-if="a.action == 13">
+        <template v-else-if="a.action == 13">
           <div class="form-item mt-8">
             <label>视图</label>
             <t-input v-model="a.value" placeholder="ID" />
           </div>
         </template>
-        <template v-if="a.action == 2 || a.action == 3 || a.action == 4">
+        <template
+          v-else-if="
+            a.action == 2 ||
+            a.action == 3 ||
+            a.action == 4 ||
+            a.action == 8 ||
+            a.action == 9 ||
+            a.action == 10
+          "
+        >
           <div class="form-item mt-8">
             <label>对象类型</label>
             <t-radio-group v-model="a.targetType" @change="a.value = ''">
@@ -72,7 +81,7 @@
             />
           </div>
         </template>
-        <template v-if="a.action == 1">
+        <template v-else-if="a.action == 1">
           <div class="form-item mt-8">
             <label>对象类型</label>
             <t-radio-group v-model="a.targetType" @change="a.params = ''">
@@ -103,11 +112,30 @@
               <div class="prop-grid head">
                 <div>属性名</div>
                 <div>属性值</div>
-                <div>
-                  <t-dropdown :min-column-width="100">
+                <div class="right">
+                  <t-dropdown
+                    :min-column-width="160"
+                    @click="onAddValue(a, $event)"
+                  >
                     <t-icon name="add-circle" class="hover" />
 
                     <t-dropdown-menu>
+                      <t-dropdown-item
+                        key="custom"
+                        value="custom"
+                        disabled="true"
+                        divider="true"
+                        class="input"
+                      >
+                        <t-input
+                          v-model="a.input"
+                          placeholder="自定义"
+                          @enter="
+                            onAddValue(a, { key: a.input });
+                            a.input = '';
+                          "
+                        />
+                      </t-dropdown-item>
                       <t-dropdown-item
                         v-for="prop in a.props"
                         :key="prop.value"
@@ -115,9 +143,105 @@
                       >
                         {{ prop.label }}
                       </t-dropdown-item>
+                    </t-dropdown-menu>
+                  </t-dropdown>
+                </div>
+              </div>
+              <template
+                v-if="Object.keys(a.value).length"
+                class="center gray mt-8"
+              >
+                <div class="prop-grid mt-8" v-for="(value, key) in a.value">
+                  <div class="ml-8">
+                    <t-tooltip :content="key">
+                      {{ getPropDesc(a, key) }}
+                    </t-tooltip>
+                  </div>
+                  <div>
+                    <t-input v-model="a.value[key]" placeholder="值" />
+                  </div>
+                  <div class="right px-8" style="line-height: 20px">
+                    <t-icon
+                      name="delete"
+                      class="hover"
+                      @click="delete a.value[key]"
+                    />
+                  </div>
+                </div>
+              </template>
+              <div v-else class="center gray mt-8">暂无数据</div>
+            </div>
+          </div>
+        </template>
+        <template v-else-if="a.action == 14">
+          <div class="form-item mt-8">
+            <label>窗口标题</label>
+            <t-input v-model="a.value" placeholder="弹框标题" />
+          </div>
+          <div class="form-item mt-8">
+            <label>画面URL</label>
+            <t-input v-model="a.params" placeholder="窗口画面URL" />
+          </div>
+        </template>
+        <template v-else-if="a.action == 7">
+          <div class="form-item mt-8">
+            <label>消息名称</label>
+            <t-input v-model="a.value" placeholder="名称" />
+          </div>
+          <div class="form-item mt-8">
+            <label>消息参数</label>
+            <t-input v-model="a.params" placeholder="参数" />
+          </div>
+        </template>
+        <template v-else-if="a.action == 15">
+          <Network v-model="a.network" mode="1" />
+
+          <div class="form-item mt-8">
+            <label>数据对象</label>
+            <t-tree-select
+              v-model="a.params"
+              :data="penTree"
+              filterable
+              placeholder="默认自己"
+              @change="getProps(a)"
+            />
+          </div>
+          <div class="form-item mt-8">
+            <label>属性数据</label>
+            <div class="w-full">
+              <div class="prop-grid head">
+                <div>属性名</div>
+                <div>属性值</div>
+                <div class="right">
+                  <t-dropdown
+                    :min-column-width="160"
+                    @click="onAddValue(a, $event)"
+                  >
+                    <t-icon name="add-circle" class="hover" />
 
-                      <t-dropdown-item key="custom" value="custom">
-                        <t-input />
+                    <t-dropdown-menu>
+                      <t-dropdown-item
+                        key="custom"
+                        value="custom"
+                        disabled="true"
+                        divider="true"
+                        class="input"
+                      >
+                        <t-input
+                          v-model="a.input"
+                          placeholder="自定义"
+                          @enter="
+                            onAddValue(a, { key: a.input });
+                            a.input = '';
+                          "
+                        />
+                      </t-dropdown-item>
+                      <t-dropdown-item
+                        v-for="prop in a.props"
+                        :key="prop.value"
+                        :value="prop.value"
+                      >
+                        {{ prop.label }}
                       </t-dropdown-item>
                     </t-dropdown-menu>
                   </t-dropdown>
@@ -126,11 +250,43 @@
               <template
                 v-if="Object.keys(a.value).length"
                 class="center gray mt-8"
-              ></template>
+              >
+                <div class="prop-grid mt-8" v-for="(value, key) in a.value">
+                  <div class="ml-8">
+                    <t-tooltip :content="key">
+                      {{ getPropDesc(a, key) }}
+                    </t-tooltip>
+                  </div>
+                  <div>
+                    <t-input v-model="a.value[key]" placeholder="值" />
+                  </div>
+                  <div class="right px-8" style="line-height: 20px">
+                    <t-icon
+                      name="delete"
+                      class="hover"
+                      @click="delete a.value[key]"
+                    />
+                  </div>
+                </div>
+              </template>
               <div v-else class="center gray mt-8">暂无数据</div>
             </div>
           </div>
         </template>
+        <template v-else-if="a.action == 16">
+          <div class="form-item mt-8">
+            <label>函数</label>
+            <div class="w-full">
+              <div>function javascriptFn(pen) {</div>
+              <CodeEditor
+                v-model="a.value"
+                class="mt-4"
+                style="height: 200px"
+              />
+              <div class="mt-4">}</div>
+            </div>
+          </div>
+        </template>
       </div>
     </div>
     <div class="mt-8">
@@ -142,6 +298,9 @@
 <script lang="ts" setup>
 import { onBeforeMount, ref } from 'vue';
 
+import CodeEditor from '@/views/components/common/CodeEditor.vue';
+import Network from './Network.vue';
+
 import { getPenTree } from '@/services/common';
 
 const { data } = defineProps<{
@@ -182,17 +341,9 @@ const actionOptions = [
     value: 7,
   },
   {
-    label: '发送MQTT',
+    label: '发送数据',
     value: 15,
   },
-  {
-    label: '发送Websocket',
-    value: 16,
-  },
-  {
-    label: '发送HTTP(s)',
-    value: 17,
-  },
   {
     label: '播放视频',
     value: 8,
@@ -207,7 +358,7 @@ const actionOptions = [
   },
   {
     label: '自定义函数',
-    value: 18,
+    value: 16,
   },
 ];
 
@@ -247,8 +398,16 @@ const onChangeAction = (action: any) => {
       action.value = '';
       action.targetType = 'id';
       break;
-
+    case 15:
+      action.network = { options: {} };
+      action.params = '';
+      action.value = {};
+      action.targetType = 'id';
+      getProps(action);
+      break;
     default:
+      action.value = '';
+      action.params = '';
       break;
   }
 };
@@ -311,12 +470,27 @@ const getProps = (c: any) => {
     }
   }
 };
+
+const getPropDesc = (a: any, key: string) => {
+  const found = a.props.find((elem: any) => elem.value === key);
+  if (found) {
+    return found.label;
+  }
+
+  return key;
+};
+
+const onAddValue = (action: any, data: any) => {
+  if (!action.value[data.key]) {
+    action.value[data.key] = undefined;
+  }
+};
 </script>
 <style lang="postcss" scoped>
 .props {
   .prop-grid {
     display: grid;
-    grid-template-columns: 2fr 3fr 20px;
+    grid-template-columns: 2fr 3fr 32px;
     line-height: 30px;
 
     &.head {

+ 1 - 1
src/views/components/Header.vue

@@ -175,7 +175,7 @@
             </div>
           </a>
         </t-dropdown-item>
-        <t-dropdown-item>
+        <t-dropdown-item divider="true">
           <a @click="onToggleAnchor">
             <div
               class="flex"

+ 198 - 0
src/views/components/Network.vue

@@ -0,0 +1,198 @@
+<template>
+  <div class="props">
+    <div class="form-item mt-8">
+      <label>数据源名称</label>
+      <t-select-input
+        v-if="mode"
+        v-model:inputValue="modelValue.name"
+        :value="modelValue.name"
+        placeholder="数据源"
+        allow-input
+        clearable
+        v-model:popup-visible="popupVisible"
+        @focus="popupVisible = true"
+        @blur="popupVisible = undefined"
+        @input-change="onInput"
+      >
+        <template #panel>
+          <ul style="padding: 4px">
+            <li
+              class="hover-background"
+              style="line-height: 1.5; padding: 8px; border-radius: 2px"
+              v-for="item in networkList"
+              :key="item.url"
+              @click="() => onSelect(item)"
+            >
+              名称: {{ item.name }}
+              <div class="desc">地址: {{ item.url }}</div>
+            </li>
+            <li
+              v-if="!networkList.length"
+              style="line-height: 1.5; padding: 8px; border-radius: 2px"
+              :key="-1"
+            >
+              <div class="desc">暂无数据</div>
+            </li>
+          </ul>
+        </template>
+      </t-select-input>
+      <t-input v-else v-model="modelValue.name" />
+    </div>
+
+    <div class="form-item mt-8">
+      <label>网络类型</label>
+      <t-select
+        v-model="modelValue.type"
+        placeholder="MQTT"
+        @change="typeChange"
+      >
+        <t-option key="mqtt" value="mqtt" label="MQTT" />
+        <t-option key="websocket" value="websocket" label="Websocket" />
+        <t-option key="http" value="http" label="HTTP" />
+      </t-select>
+    </div>
+    <div class="form-item mt-8">
+      <label>URL地址</label>
+      <t-input v-model="modelValue.url" />
+    </div>
+    <template v-if="modelValue.type === 'websocket'">
+      <div class="form-item mt-8">
+        <label>Protocol</label>
+        <t-input v-model="modelValue.protocols" />
+      </div>
+    </template>
+    <template v-else-if="modelValue.type === 'http'">
+      <div class="form-item mt-8">
+        <label>请求方式</label>
+        <t-select v-model="modelValue.method" @change="httpMethodChange">
+          <t-option key="GET" value="GET" label="GET" />
+          <t-option key="POST" value="POST" label="POST" />
+        </t-select>
+      </div>
+      <div class="form-item mt-8">
+        <label>请求头</label>
+        <t-textarea
+          v-model="modelValue.httpHeaders"
+          :autosize="{ minRows: 3, maxRows: 5 }"
+          placeholder="请输入"
+        />
+      </div>
+      <div v-if="modelValue.method === 'POST'" class="form-item mt-8">
+        <label>请求体</label>
+        <t-textarea
+          v-model="modelValue.body"
+          :autosize="{ minRows: 3, maxRows: 5 }"
+          placeholder="请输入"
+        />
+      </div>
+    </template>
+    <template v-else>
+      <div class="form-item mt-8">
+        <label>Client Id</label>
+        <t-input v-model="modelValue.options.clientId" />
+      </div>
+      <div class="form-item mt-8">
+        <label>自动生成</label>
+        <t-switch
+          class="ml-8 mt-8"
+          v-model="modelValue.options.customClientId"
+          size="small"
+        />
+      </div>
+      <div class="form-item mt-8">
+        <label>用户名</label>
+        <t-input v-model="modelValue.options.username" />
+      </div>
+      <div class="form-item mt-8">
+        <label>密码</label>
+        <t-input v-model="modelValue.options.password" />
+      </div>
+      <div class="form-item mt-8">
+        <label>Topics</label>
+        <t-input v-model="modelValue.topics" />
+      </div>
+    </template>
+    <div class="form-item mt-8" v-if="mode">
+      <label> </label>
+      <div>
+        <t-button @click="onSave">保存到我的数据源</t-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { onBeforeMount, ref } from 'vue';
+
+import { debounce } from '@/services/debouce';
+
+const { modelValue, mode } = defineProps<{
+  modelValue: any;
+  mode: any;
+}>();
+
+const emit = defineEmits(['update:modelValue', 'change']);
+
+const popupVisible = ref<bool>(false);
+const networkList = ref<any[]>([]);
+
+onBeforeMount(() => {});
+
+const typeChange = (t: string) => {
+  if (t === 'http') {
+    Object.assign(modelValue, {
+      http: '',
+      httpTimeInterval: 1000,
+      httpHeaders: '',
+      method: 'GET',
+      body: '',
+    });
+  } else if (t === 'websocket') {
+    modelValue.protocols = '';
+  } else {
+    Object.assign(modelValue, {
+      options: {
+        clientId: '',
+        username: '',
+        password: '',
+        customClientId: false,
+      },
+    });
+  }
+};
+
+const httpMethodChange = (method: string) => {
+  if (method === 'GET') {
+    modelValue.body = undefined;
+  }
+};
+
+const onSave = () => {
+  emit('update:modelValue', modelValue);
+  emit('change', modelValue);
+
+  // 保存到我的数据源
+  // todo
+};
+
+const onInput = (text: string) => {
+  debounce(getNetworks, 300);
+};
+
+// 请求我的数据源接口
+const getNetworks = async () => {
+  // const ret: any = await axios.get(`/api/xxx`);
+  // if (ret) {
+  //   dataDialog.networkList = ret.list
+  // }
+};
+
+const onSelect = (item: any) => {
+  Object.assign(modelValue, item);
+  popupVisible = false;
+};
+</script>
+<style lang="postcss" scoped>
+.props {
+}
+</style>

+ 33 - 20
src/views/components/PenDatas.vue

@@ -139,18 +139,29 @@
         />
         <div class="desc mt-8">
           固定数字:直接输入数字。例如:5<br />
-          随机范围数字:最小值-最大值。例如:0-1 或 0-100<br />
-          随机指定数字:数字1,数字2,数字3... 。 例如:1,5,10,20<br />
+          随机范围数字
+          <label class="vip-label" title="会员权限">VIP</label
+          >:最小值-最大值。例如:0-1 或 0-100
+          <br />
+          随机指定数字
+          <label class="vip-label" title="会员权限">VIP</label
+          >:数字1,数字2,数字3... 。 例如:1,5,10,20<br />
         </div>
       </div>
-      <t-select
-        v-else-if="addDataDialog.data.type === 'bool'"
-        v-model="addDataDialog.data.value"
-      >
-        <t-option :key="true" :value="true" label="true"></t-option>
-        <t-option :key="false" :value="false" label="false"></t-option>
-        <t-option key="随机" label="随机"></t-option>
-      </t-select>
+      <div class="flex-grow" v-else-if="addDataDialog.data.type === 'bool'">
+        <t-select v-model="addDataDialog.data.value">
+          <t-option :key="true" :value="true" label="true"></t-option>
+          <t-option :key="false" :value="false" label="false"></t-option>
+          <t-option key="随机" label="随机"></t-option>
+        </t-select>
+        <div class="desc mt-8">
+          固定:指定true或false<br />
+          随机
+          <label class="vip-label" title="会员权限">VIP</label
+          >:随机生成一个布尔值<br />
+        </div>
+      </div>
+
       <div class="flex-grow" v-else>
         <t-input
           class="w-full"
@@ -159,8 +170,10 @@
         />
         <div class="desc mt-8">
           固定文字:直接输入。例如:大屏可视化<br />
-          随机文本:[文本长度]。例如:[8] 或 [16]<br />
-          随机指定文本:{文本1,文本2,文本3...} 。 例如:{大屏,可视化}
+          随机文本<label class="vip-label" title="会员权限">VIP</label
+          >:[文本长度]。例如:[8] 或 [16]<br />
+          随机指定文本<label class="vip-label" title="会员权限">VIP</label
+          >:{文本1,文本2,文本3...} 。 例如:{大屏, 可视化}
           <br />
         </div>
       </div>
@@ -198,11 +211,11 @@
       <t-input
         placeholder="搜索"
         v-model="dataBindDialog.input"
-        @change="onSearchDataSet"
-        @enter="onSearchDataSet"
+        @change="onSearchDataset"
+        @enter="onSearchDataset"
       >
         <template #suffixIcon>
-          <t-icon name="search" class="hover" @click="onSearchDataSet" />
+          <t-icon name="search" class="hover" @click="onSearchDataset" />
         </template>
       </t-input>
     </div>
@@ -661,14 +674,14 @@ const onBind = (item: any) => {
   dataBindDialog.bkBinds.push(...item.binds);
   dataBindDialog.show = true;
 
-  getDataSet();
+  getDataset();
 };
 
-const onSearchDataSet = () => {
-  debounce(getDataSet, 300);
+const onSearchDataset = () => {
+  debounce(getDataset, 300);
 };
 
-const getDataSet = async () => {
+const getDataset = async () => {
   // @ts-ignore
   const data: Meta2dBackData = meta2d.data();
 
@@ -691,7 +704,7 @@ const onChangePagination = (pageInfo: any) => {
   });
   query.current = pageInfo.current;
   query.pageSize = pageInfo.pageSize;
-  getDataSet();
+  getDataset();
 };
 
 const onSelectBindsChange = (value: string[], options: any) => {

+ 72 - 33
src/views/components/PenEvents.vue

@@ -1,11 +1,63 @@
 <template>
   <div class="props">
-    <div class="px-16 py-16" v-if="pen.events && pen.events.length"></div>
+    <div v-if="pen.events && pen.events.length">
+      <t-collapse
+        v-model="openedCollapses"
+        :borderless="true"
+        :expand-on-row-click="true"
+        expand-icon-placement="right"
+      >
+        <t-collapse-panel
+          v-for="(item, i) in pen.events"
+          :value="i"
+          :header="`交互事件${i + 1}`"
+        >
+          <template #headerRightContent>
+            <t-space size="small" @click.stop>
+              <t-popconfirm
+                content="确认删除该交互事件吗"
+                placement="left"
+                @confirm="pen.events.splice(i, 1)"
+              >
+                <t-icon name="delete" class="hover" />
+              </t-popconfirm>
+            </t-space>
+          </template>
+
+          <!-- <div class="form-item">
+            <label>交互事件</label>
+            <t-select v-model="item.name" :options="options" />
+          </div> -->
+          <div class="form-item banner">
+            <label>执行动作</label>
+          </div>
+          <!-- <Actions class="mt-8" :data="trigger" /> -->
+        </t-collapse-panel>
+      </t-collapse>
+      <t-divider />
+      <div class="mt-16 px-16">
+        <t-dropdown
+          :options="options"
+          @click="addEvent"
+          :minColumnWidth="254"
+          :maxHeight="360"
+        >
+          <t-button class="w-full" style="height: 30px">
+            添加交互事件
+          </t-button>
+        </t-dropdown>
+      </div>
+    </div>
     <div class="flex column center blank" v-else>
       <img src="/img/blank.png" />
       <div class="gray center">还没有交互事件</div>
       <div class="mt-8">
-        <t-dropdown :options="options" @click="addEvent" :minColumnWidth="150">
+        <t-dropdown
+          :options="options"
+          @click="addEvent"
+          :minColumnWidth="150"
+          :maxHeight="360"
+        >
           <t-button style="height: 30px"> 添加交互事件 </t-button>
         </t-dropdown>
       </div>
@@ -79,13 +131,25 @@ const options = ref<any>([
   },
   {
     value: 'message',
-    content: '消息',
+    content: '监听消息',
   },
 ]);
 
-onBeforeMount(() => {});
+const openedCollapses = ref([0]);
 
-const addEvent = () => {};
+onBeforeMount(() => {
+  if (!pen.events) {
+    pen.events = [];
+  }
+
+  for (const item of options.value) {
+    item.label = item.content;
+  }
+});
+
+const addEvent = (e: any) => {
+  pen.events.push({ name: e.value });
+};
 
 onUnmounted(() => {});
 </script>
@@ -101,34 +165,9 @@ onUnmounted(() => {});
     }
   }
 
-  .label {
-    width: fit-content;
-    font-size: 10px;
-    line-height: 28px;
-    color: var(--color-desc);
-  }
-
-  .value {
-    padding-right: 8px;
-
-    :deep(.t-input) {
-      height: 26px;
-      border-color: transparent;
-      &:hover {
-        border-color: var(--color-primary);
-      }
-    }
-  }
-
-  .actions {
-    svg {
-      margin-left: 6px;
-    }
-  }
-
-  .data-list {
-    height: 300px;
-    overflow: auto;
+  .banner {
+    background-color: var(--color-background-input);
+    padding: 0 12px;
   }
 }
 </style>

+ 232 - 511
src/views/components/View.vue

@@ -198,10 +198,10 @@
         <a @click="onScaleView"><t-icon name="refresh" /></a>
       </t-tooltip>
       <t-tooltip content="窗口大小" placement="bottom">
-        <a @click="onScaleWindow"><t-icon name="minus-rectangle" /></a>
+        <a @click="onScaleWindow"><t-icon name="fullscreen-exit" /></a>
       </t-tooltip>
       <t-tooltip content="数据源" placement="bottom">
-        <a @click="connectShow"><t-icon name="server" /></a>
+        <a @click="onShowDataDialog"><t-icon name="server" /></a>
       </t-tooltip>
 
       <div class="flex-grow"></div>
@@ -257,248 +257,180 @@
       @changeVisible="changeContextMenuVisible"
     />
     <t-dialog
+      v-if="dataDialog.show"
       width="800px"
       header="数据源管理"
-      v-model:visible="connectVisible"
-      :cancel-btn="null"
-      confirm-btn="完成"
+      :visible="true"
+      @close="dataDialog.show = false"
     >
-      <div class="body" style="height: 420px">
-        <t-tabs :default-value="1">
-          <t-tab-panel :value="1" label="通信" :destroy-on-hide="false">
-            <template #panel>
-              <div v-show="comShow">
-                <t-row class="mt-8" justify="end">
-                  <t-space :size="8">
-                    <t-select-input
-                      placeholder="搜索我的数据源"
-                      allow-input
-                      clearable
-                      :popup-visible="popupVisible"
-                      :popup-props="{ overlayInnerStyle: { padding: '6px' } }"
-                      @input-change="onInputChange"
-                      @popup-visible-change="onPopupVisibleChange"
-                    >
-                      <template #panel>
-                        <ul>
-                          <li
-                            style="line-height: 14px; margin: 8px 4px"
-                            v-for="item in comOptions"
-                            :key="item.url"
-                            @click="() => onOptionClick(item)"
-                          >
-                            名称: {{ item.name }}<br />
-                            地址: {{ item.url }}
-                          </li>
-                        </ul>
-                      </template>
-                      <template #suffixIcon>
-                        <t-icon name="search"></t-icon
-                      ></template>
-                    </t-select-input>
-                    <t-button style="height: 30px" @click="addCom"
-                      >添加数据源</t-button
-                    >
-                  </t-space>
-                </t-row>
-                <t-table
-                  class="mt-8"
-                  row-key="id"
-                  :data="comData"
-                  :columns="comColumns"
-                >
-                  <template #operation="{ row, rowIndex }">
-                    <t-link theme="primary" hover="color" @click="editCom(row)">
-                      编辑
-                    </t-link>
-                    <t-divider layout="vertical" />
-                    <t-popconfirm
-                      content="确认删除吗"
-                      @confirm="deleteCom(rowIndex)"
-                    >
-                      <t-link theme="primary" hover="color"> 删除 </t-link>
-                    </t-popconfirm>
-                  </template>
-                </t-table>
-              </div>
-              <div v-show="!comShow">
-                <t-row class="mt-8">
-                  <t-col :span="4">
-                    <t-button
-                      theme="primary"
-                      variant="text"
-                      ghost
-                      @click="comBack"
-                    >
-                      <template #icon>
-                        <t-icon name="rollback"></t-icon>
-                      </template>
-                      返回
-                    </t-button>
-                  </t-col>
-                  <t-col :span="4" :offset="4">
-                    <t-button @click="saveForCurrent" theme="primary"
-                      >仅当前页面使用</t-button
-                    >
-                    <t-button @click="saveToServer" theme="primary" class="ml-4"
-                      >保存到服务器</t-button
-                    >
-                  </t-col>
-                </t-row>
-                <div class="form-item mt-16">
-                  <label>名称</label>
-                  <t-input v-model="com.name" style="width: 200px" />
-                </div>
-
-                <div class="form-item">
-                  <label>连接类型</label>
-                  <t-select
-                    v-model="com.type"
-                    placeholder="请选择"
-                    style="width: 200px"
-                    :popup-props="{ overlayInnerStyle: { width: '200px' } }"
-                    :disabled="comType === 'edit'"
-                    @change="comTypeChange"
+      <t-tabs :default-value="1" class="body">
+        <t-tab-panel :value="1" label="数据订阅" :destroy-on-hide="false">
+          <template #panel>
+            <div v-if="!dataDialog.editNetwork">
+              <t-row class="mt-16" justify="end">
+                <t-space :size="12">
+                  <t-select-input
+                    placeholder="搜索我的数据源"
+                    allow-input
+                    clearable
+                    :popup-visible="dataDialog.popupVisible"
+                    @input-change="onInputNetwork"
+                    style="width: 240px"
                   >
-                    <t-option key="mqtt" value="mqtt" class="overlay-options">
-                    </t-option>
-                    <t-option
-                      key="websocket"
-                      value="websocket"
-                      class="overlay-options"
-                    >
-                    </t-option>
-                    <t-option key="http" value="http" class="overlay-options">
-                    </t-option>
-                  </t-select>
-                </div>
-                <template v-if="com.type === 'mqtt'">
-                  <div class="form-item">
-                    <label>接口地址</label>
-                    <t-input v-model="com.url" style="width: 200px" />
-                  </div>
-                  <div class="form-item">
-                    <label>Client Id</label>
-                    <t-input
-                      v-model="com.options.clientId"
-                      style="width: 200px"
-                    />
-                  </div>
-                  <div class="form-item">
-                    <label>关闭自动生成</label>
-                    <t-switch v-model="com.options.customClientId" />
-                  </div>
-                  <div class="form-item">
-                    <label>用户名</label>
-                    <t-input
-                      v-model="com.options.username"
-                      style="width: 200px"
-                    />
-                  </div>
-                  <div class="form-item">
-                    <label>密码</label>
-                    <t-input
-                      v-model="com.options.password"
-                      style="width: 200px"
-                    />
-                  </div>
-                  <div class="form-item">
-                    <label>Topics</label>
-                    <t-input v-model="com.topics" style="width: 200px" />
-                  </div>
-                </template>
-                <template v-else-if="com.type === 'http'">
-                  <div class="form-item">
-                    <label>请求方式</label>
-                    <t-select
-                      v-model="com.method"
-                      placeholder="请选择"
-                      style="width: 200px"
-                      :popup-props="{ overlayInnerStyle: { width: '200px' } }"
-                      @change="comHttpMethodChange"
-                    >
-                      <t-option key="GET" value="GET"> </t-option>
-                      <t-option key="POST" value="POST"> </t-option>
-                    </t-select>
-                  </div>
-                  <div class="form-item">
-                    <label>请求头</label>
-                    <t-textarea
-                      v-model="com.httpHeaders"
-                      placeholder="请输入请求头"
-                      name="description"
-                      :autosize="{ minRows: 3, maxRows: 5 }"
-                    />
-                  </div>
-                  <div v-if="com.method === 'POST'" class="form-item">
-                    <label>请求体</label>
-
-                    <t-textarea
-                      v-model="com.body"
-                      placeholder="请输入请求体"
-                      name="description"
-                      :autosize="{ minRows: 3, maxRows: 5 }"
-                    />
-                  </div>
-                </template>
-                <template v-else>
-                  <div class="form-item">
-                    <label>protocol</label>
-                    <t-input v-model="com.protocols" style="width: 200px" />
-                  </div>
-                </template>
-              </div>
-            </template>
-          </t-tab-panel>
-          <t-tab-panel :value="2" :destroy-on-hide="false">
-            <template #label
-              >数据集
-              <span><label class="vip-label">VIP</label></span></template
-            >
-            <template #panel>
-              <t-row class="mt-8" style="height: 32px; line-height: 32px">
-                <t-col flex="60px"> 网络接口 </t-col>
-                <t-col flex="auto">
-                  <t-input class="ml-8" style="width: 200px" />
-                </t-col>
-              </t-row>
-              <t-row class="mt-8">
-                <t-col flex="60px">自定义</t-col>
-                <t-col flex="auto">
-                  <t-button
-                    @click="importDataSet"
-                    class="ml-8"
-                    style="height: 30px"
-                    >从Excel导入</t-button
-                  >
-                  <t-button @click="downloadDataSet" variant="text">
-                    <template #icon
-                      ><t-icon name="download"></t-icon>
+                    <template #panel>
+                      <ul style="padding: 4px">
+                        <li
+                          class="hover-background"
+                          style="
+                            line-height: 1.5;
+                            padding: 8px;
+                            border-radius: 2px;
+                          "
+                          v-for="item in dataDialog.networkList"
+                          :key="item.url"
+                          @click="() => onSelectNetWork(item)"
+                        >
+                          名称: {{ item.name }}
+                          <div class="desc">地址: {{ item.url }}</div>
+                        </li>
+                        <li
+                          v-if="!dataDialog.networkList.length"
+                          style="
+                            line-height: 1.5;
+                            padding: 8px;
+                            border-radius: 2px;
+                          "
+                          :key="-1"
+                        >
+                          <div class="desc">暂无数据</div>
+                        </li>
+                      </ul>
+                    </template>
+                    <template #suffixIcon>
+                      <t-icon name="search" class="hover" />
                     </template>
-                    下载示例
+                  </t-select-input>
+                  <t-button style="height: 30px" @click="addNetwork">
+                    添加数据订阅
                   </t-button>
-                </t-col>
+                </t-space>
               </t-row>
               <t-table
-                class="mt-8"
+                class="mt-12"
                 row-key="id"
-                :data="dsData"
-                :columns="dsColumns"
+                :data="dataDialog.networks"
+                :columns="networkColumns"
+                :max-height="280"
+                size="small"
               >
-                <template #label="{ row }">
-                  {{ `${row.label}(${row.key})` }}
+                <template #actions="{ row, rowIndex }">
+                  <a @click="editNetwork(row)"> 编辑 </a>
+                  <t-popconfirm
+                    content="确认删除吗"
+                    @confirm="deleteNetwork(rowIndex)"
+                  >
+                    <a class="ml-12"> 删除 </a>
+                  </t-popconfirm>
+                </template>
+                <template #empty>
+                  <div class="center">
+                    <div>暂无数据</div>
+                    <div class="mt-8">
+                      <a @click="addNetwork"> + 添加数据订阅</a>
+                    </div>
+                  </div>
                 </template>
-                <template #operation="{ row }"> </template>
               </t-table>
-            </template>
-          </t-tab-panel>
-        </t-tabs>
-      </div>
+            </div>
+            <div v-else>
+              <div class="mt-8">
+                <a
+                  @click="dataDialog.editNetwork = false"
+                  class="flex middle"
+                  style="width: 44px"
+                >
+                  <t-icon name="rollback" class="mr-4" /> 返回
+                </a>
+              </div>
+              <div style="height: 300px; overflow-y: auto">
+                <Network v-model="dataDialog.network" />
+              </div>
+            </div>
+          </template>
+        </t-tab-panel>
+        <t-tab-panel :value="2" :destroy-on-hide="false">
+          <template #label>
+            数据集
+            <span><label class="vip-label ml-4">VIP</label></span>
+          </template>
+          <template #panel>
+            <div class="form-item mt-12">
+              <label>网络地址</label>
+              <div class="w-full">
+                <t-input v-model="dataDialog.datasetUrl" />
+                <div class="desc mt-4">
+                  高优先级。存在网络地址时,用网络数据代替自定义数据
+                </div>
+              </div>
+            </div>
+            <div class="form-item mt-16">
+              <label>自定义</label>
+              <div class="w-full">
+                <t-button @click="importDataset">从Excel导入</t-button>
+                <a href="/data.xlsx" download class="ml-16">下载Excel示例</a>
+                <span class="desc ml-16">
+                  “自定义”数据集 - 方便构建项目时了解数据结构
+                </span>
+              </div>
+            </div>
+
+            <t-table
+              class="mt-12"
+              row-key="id"
+              :data="dataDialog.dataset"
+              :columns="datasetColumns"
+              size="small"
+              :max-height="210"
+            >
+              <template #label="{ row }">
+                {{ `${row.label}(${row.key})` }}
+              </template>
+              <template #actions="{ row }"> </template>
+              <template #empty>
+                <div class="center">
+                  <div>暂无数据</div>
+                  <div class="mt-8">
+                    <a @click="importDataset"> 从Excel导入</a>
+                  </div>
+                </div>
+              </template>
+            </t-table>
+          </template>
+        </t-tab-panel>
+      </t-tabs>
+
+      <template #footer>
+        <div v-if="dataDialog.editNetwork" class="flex middle">
+          <div class="flex-grow"></div>
+          <t-checkbox v-model="dataDialog.save">
+            同时保存到我的数据源
+          </t-checkbox>
+          <t-button class="ml-16" @click="onOkNetwork">确定</t-button>
+        </div>
+        <div v-else class="flex middle">
+          <div class="flex-grow"></div>
+          <t-button class="ml-16" @click="dataDialog.show = false">
+            完成
+          </t-button>
+        </div>
+      </template>
     </t-dialog>
   </div>
 </template>
 
 <script lang="ts" setup>
+import { onMounted, onUnmounted, watch, ref, reactive } from 'vue';
+import { useRouter, useRoute } from 'vue-router';
 import {
   Meta2d,
   Options,
@@ -508,30 +440,34 @@ import {
   PenType,
   HoverType,
 } from '@meta2d/core';
-import { onMounted, onUnmounted, watch, ref, reactive } from 'vue';
+import localforage from 'localforage';
+import dayjs from 'dayjs';
+import axios from 'axios';
+import { MessagePlugin } from 'tdesign-vue-next';
+
 import { registerBasicDiagram } from '@/services/register';
-import { useRouter, useRoute } from 'vue-router';
 import { useUser } from '@/services/user';
-import { getLe5le2d } from '@/services/api';
-import { useDot } from '@/services/common';
+import { cdn, getLe5le2d } from '@/services/api';
 import {
   save,
   newFile,
   SaveType,
   onScaleView,
   onScaleWindow,
+  useDot,
 } from '@/services/common';
 import { useSelection, SelectionMode } from '@/services/selections';
 import { defaultFormat } from '@/services/defaults';
-import { MessagePlugin } from 'tdesign-vue-next';
-import { localMeta2dDataName } from '@/services/utils';
-import localforage from 'localforage';
-import { checkData, Meta2dBackData } from '@/services/utils';
-import { cdn } from '@/services/api';
-import dayjs from 'dayjs';
+import {
+  checkData,
+  localMeta2dDataName,
+  Meta2dBackData,
+} from '@/services/utils';
+import { debounce } from '@/services/debouce';
+import { importExcel } from '@/services/excel';
+
 import ContextMenu from './ContextMenu.vue';
-import { importExcel, saveAsExcel } from '@/services/excel';
-import { it } from 'node:test';
+import Network from './Network.vue';
 
 const router = useRouter();
 const route = useRoute();
@@ -572,13 +508,11 @@ onMounted(() => {
   meta2d.on('rotatePens', autoSave);
   meta2d.on('translatePens', autoSave);
 
-  //TODO所有编辑栏所做修改
+  // 所有编辑栏所做修改
   meta2d.on('components-update-value', autoSave);
   // @ts-ignore
   meta2d.on('contextmenu', contextmenu);
   meta2d.on('click', canvasClick);
-
-  meta2d.on('opened', dataSourceManage);
 });
 
 const watcher = watch(
@@ -636,7 +570,6 @@ onUnmounted(() => {
     meta2d.off('contextmenu', contextmenu);
     meta2d.off('click', canvasClick);
 
-    meta2d.off('opened', dataSourceManage);
     meta2d.destroy();
   }
 });
@@ -725,10 +658,7 @@ function scaleListener(newScale: number) {
   scale.value = Math.round(newScale * 100);
 }
 
-const connectVisible = ref(false);
-const connectShow = () => {
-  connectVisible.value = true;
-};
+const dataDialog = reactive<any>({});
 
 const currentLineType = ref('curve');
 const lineTypes = reactive([
@@ -968,128 +898,67 @@ const changeContextMenuVisible = (e: boolean) => {
   contextMenuVisible.value = e;
 };
 
-// const comConfirm = () => {
-//   connectVisible.value = false;
-// };
-
-interface Com {
-  name?: string;
-  type: 'mqtt' | 'websocket' | 'http';
-  url?: string;
-  //websocket
-  protocols?: string;
-  //mqtt
-  topics?: string;
-  options?: {
-    clientId?: string;
-    username?: string;
-    password?: string;
-    customClientId?: boolean;
-  };
-  //http
-  http?: string; // http 请求 Url
-  httpTimeInterval?: number; // http 请求间隔
-  httpHeaders?: HeadersInit; //请求头
-  method?: string;
-  body?: BodyInit | null;
-}
-
-const comData = ref<Com[]>([
-  {
-    name: '连接1',
-    type: 'mqtt',
-    url: 'wss://1',
-    topics: '',
-    options: {
-      clientId: '',
-      username: '',
-      password: '',
-      customClientId: false,
-    },
-  },
-  {
-    name: '连接2',
-    type: 'websocket',
-    url: 'wss://2',
-  },
-  {
-    name: '连接3',
-    type: 'http',
-    url: 'wss://3',
-  },
-]);
-const comColumns = ref([
+const networkColumns = ref([
   {
     colKey: 'name',
     title: '名称',
     ellipsis: true,
+    width: 160,
   },
   {
     colKey: 'type',
     title: '类型',
-    ellipsis: true,
+    width: 120,
   },
   {
     colKey: 'url',
-    title: '接口地址',
+    title: 'URL地址',
     ellipsis: true,
   },
-  { colKey: 'operation', title: '操作', width: 120, foot: '-' },
+  { colKey: 'actions', title: '操作', width: 100 },
 ]);
 
-const comOptions = ref<Com[]>([
-  {
-    name: '连接A',
-    type: 'mqtt',
-    url: 'ws://A',
-  },
-  {
-    name: '连接B',
-    type: 'mqtt',
-    url: 'ws://B',
-  },
-  {
-    name: '连接C',
-    type: 'mqtt',
-    url: 'ws://C',
-  },
-  {
-    name: '连接D',
-    type: 'mqtt',
-    url: 'ws://D',
-  },
-  {
-    name: '连接E',
-    type: 'mqtt',
-    url: 'ws://E',
-  },
-]);
-
-const dataSourceManage = () => {
-  comData.value = (meta2d.store.data as any).comData; //确定字段
-  dsData.value = (meta2d.store.data as any).dsData;
+const onShowDataDialog = () => {
+  // @ts-ignore
+  dataDialog.networks = meta2d.store.data.networks || [];
+  // @ts-ignore
+  dataDialog.dataset = meta2d.store.data.dataset;
+  dataDialog.networkList = [];
+  dataDialog.editNetwork = false;
+  dataDialog.save = true;
+  dataDialog.show = true;
+  getNetworks();
 };
 
-const onOptionClick = (item: Com) => {
-  comData.value.push(item);
-  popupVisible.value = false;
+const onOkData = () => {
+  // @ts-ignore
+  meta2d.store.data.networks = dataDialog.networks;
+  // @ts-ignore
+  meta2d.store.data.dataset = dataDialog.dataset;
+  dataDialog.show = false;
 };
 
-const popupVisible = ref(false);
-const onInputChange = (e) => {
-  console.log('change', e);
+const onSelectNetWork = (item: any) => {
+  dataDialog.networks.push(item);
+  dataDialog.popupVisible = false;
 };
-const onPopupVisibleChange = (val: boolean) => {
-  popupVisible.value = val;
+
+const onInputNetwork = (text: string) => {
+  debounce(getNetworks, 300);
 };
 
-const comType = ref('add'); //or edit
+// 请求我的数据源接口
+const getNetworks = async () => {
+  // const ret: any = await axios.get(`/api/xxx`);
+  // if (ret) {
+  //   dataDialog.networkList = ret.list
+  // }
+};
 
-const addCom = () => {
-  comType.value = 'add';
-  com.value = {
+const addNetwork = () => {
+  dataDialog.network = {
     name: '',
-    type: 'mqtt', //默认
+    type: '',
     url: '',
     options: {
       clientId: '',
@@ -1098,132 +967,35 @@ const addCom = () => {
       customClientId: false,
     },
   };
-  comShow.value = false;
-};
-const editCom = (row: Com) => {
-  comType.value = 'edit';
-  com.value = row;
-  comShow.value = false;
+  dataDialog.editNetwork = 1;
 };
 
-const deleteCom = (index: number) => {
-  comData.value.splice(index, 1);
-  dataToCom(comData.value);
+const editNetwork = (data: any) => {
+  dataDialog.network = data;
+  dataDialog.editNetwork = 2;
 };
 
-//将配置的通信列表转成核心库能识别的格式
-const dataToCom = (data: Com[]) => {
-  const mqtts: any = [];
-  const https: any = [];
-  const websockets: any = [];
-  data.forEach((item) => {
-    if (item.type === 'http') {
-      https.push({
-        http: item.http,
-        httpTimeInterval: item.httpTimeInterval,
-        httpHeaders: item.httpHeaders,
-        method: item.method,
-        body: item.body,
-      });
-    } else if (item.type === 'mqtt') {
-      mqtts.push({
-        url: item.url,
-        options: {
-          clientId: item.options?.clientId,
-          username: item.options?.username,
-          password: item.options?.password,
-          customClientId: item.options?.customClientId,
-        },
-        topics: item.topics,
-      });
-    } else if (item.type === 'websocket') {
-      websockets.push({
-        url: item.url,
-        protocols: item.protocols,
-      });
-    }
-  });
-  meta2d.store.data.mqtts = mqtts;
-  meta2d.store.data.https = https;
-  meta2d.store.data.websockets = websockets;
-  //TODO 建立通信连接
+const deleteNetwork = (index: number) => {
+  dataDialog.networks.splice(index, 1);
   meta2d.connectSocket();
 };
-const comShow = ref(true);
-
-const comBack = () => {
-  comShow.value = true;
-};
 
-const com = ref<Com>({
-  name: '连接E',
-  type: 'mqtt',
-  url: 'ws://E',
-  options: {
-    clientId: '',
-    username: '',
-    password: '',
-    customClientId: false,
-  },
-});
-
-const comTypeChange = (e: string) => {
-  console.log(e, com.value.type);
-  if (e === 'mqtt') {
-    Object.assign(com.value, {
-      options: {
-        clientId: '',
-        username: '',
-        password: '',
-        customClientId: false,
-      },
-    });
-  } else if (e === 'websocket') {
-    Object.assign(com.value, {
-      protocols: '',
-    });
-  } else {
-    Object.assign(com.value, {
-      http: '',
-      httpTimeInterval: 1000,
-      httpHeaders: '',
-      method: 'GET',
-      body: '',
-    });
+const onOkNetwork = () => {
+  if (dataDialog.editNetwork === 1) {
+    dataDialog.networks.push(dataDialog.network);
   }
-};
+  dataDialog.editNetwork = false;
 
-const comHttpMethodChange = (e: string) => {
-  if (e === 'GET') {
-    com.value.body = null;
+  // 保存到我的数据源
+  if (dataDialog.save) {
+    // todo
   }
 };
 
-const dsData = ref([
-  {
-    label: '数据点1',
-    key: 'a-1',
-    value: '12',
-    type: 'number/select/bool/color',
-  },
-  {
-    label: '数据点2',
-    key: 'a-2',
-    value: '12',
-    type: 'number/select/bool/color',
-  },
-  {
-    label: '数据点3',
-    key: 'a-3',
-    value: '12',
-    type: 'number/select/bool/color',
-  },
-]);
-
-const dsColumns = ref([
+const datasetColumns = ref([
   {
     colKey: 'label',
-    title: '名称(数据ID)',
+    title: '名称(数据ID)',
     ellipsis: true,
   },
   {
@@ -1238,20 +1010,7 @@ const dsColumns = ref([
   },
 ]);
 
-const saveForCurrent = () => {
-  comData.value.push(com.value);
-  comShow.value = true;
-  dataToCom(comData.value);
-};
-
-const saveToServer = () => {
-  //TODO 将com保存到服务器
-  comData.value.push(com.value);
-  comShow.value = true;
-  dataToCom(comData.value);
-};
-
-const importDataSet = async () => {
+const importDataset = async () => {
   let columns: any = [
     {
       header: '名称',
@@ -1270,40 +1029,9 @@ const importDataSet = async () => {
       key: 'value',
     },
   ];
-  // dsData.value = [];
-  let data: any = await importExcel(columns);
+  const data: any = await importExcel(columns);
   console.log('data', data);
-  // setTimeout(() => {
-  dsData.value = data;
-  // }, 2000);
-};
-
-const downloadDataSet = () => {
-  let name = '数据集示例';
-  let columns = [
-    {
-      header: '名称',
-      key: 'label',
-    },
-    {
-      header: '数据ID',
-      key: 'id',
-    },
-    {
-      header: '类型',
-      key: 'type',
-    },
-    {
-      header: '值',
-      key: 'value',
-    },
-  ];
-  let data = [
-    ['数据集1', 'dataId-1', 'string', 'zzz'],
-    ['数据集2', 'dataId-2', 'boolean', true],
-    ['数据集3', 'dataId-3', 'number', 12],
-  ];
-  saveAsExcel(name, columns, data);
+  dataDialog.dataset = data;
 };
 </script>
 <style lang="postcss" scoped>
@@ -1352,12 +1080,5 @@ const downloadDataSet = () => {
       background: var(--color-background);
     }
   }
-  .form-item {
-    display: flex;
-    margin-bottom: 8px;
-    label {
-      width: 100px;
-    }
-  }
 }
 </style>