Alsmile hai 1 ano
pai
achega
5c2c933075

+ 6 - 2
src/services/common.ts

@@ -487,8 +487,12 @@ export const typeOptions = [
     value: 'string',
   },
   {
-    label: '数字',
-    value: 'number',
+    label: '整数',
+    value: 'integer',
+  },
+  {
+    label: '浮点数',
+    value: 'float',
   },
   {
     label: '布尔',

+ 1 - 1
src/services/defaults.ts

@@ -931,7 +931,7 @@ export const formComponents = [
           width: 160,
           height: 30,
           name: 'text',
-          text: 'le5le-meta2d',
+          text: '乐吾乐le5le - 大屏可视化',
         },
       },
       {

+ 42 - 14
src/styles/app.css

@@ -100,8 +100,27 @@ h5 {
   cursor: pointer !important;
 }
 
-.hover-background:hover {
-  background-color: var(--color-background-popup-hover);
+.hover-background {
+  &:hover {
+    background-color: var(--color-background-popup-hover);
+  }
+
+  &.item {
+    position: relative;
+    line-height: 1.5;
+    padding: 8px 20px 8px 8px;
+    border-radius: 2px;
+    & > .del {
+      font-size: 14px;
+      position: absolute;
+      right: 4px;
+      top: calc(50% - 12px);
+
+      &:hover {
+        color: var(--color-error);
+      }
+    }
+  }
 }
 
 .ellipsis {
@@ -255,6 +274,10 @@ a.hover:hover {
   width: 100%;
 }
 
+.mt-4 {
+  margin-top: 4px;
+}
+
 .ml-4 {
   margin-left: 4px;
 }
@@ -263,6 +286,10 @@ a.hover:hover {
   margin-right: 4px;
 }
 
+.mt-8 {
+  margin-top: 8px;
+}
+
 .mr-8 {
   margin-right: 8px;
 }
@@ -283,18 +310,6 @@ a.hover:hover {
   margin-left: 12px;
 }
 
-.ml-16 {
-  margin-left: 16px;
-}
-
-.mt-4 {
-  margin-top: 4px;
-}
-
-.mt-8 {
-  margin-top: 8px;
-}
-
 .mt-12 {
   margin-top: 12px;
 }
@@ -307,10 +322,19 @@ a.hover:hover {
   margin-right: 12px;
 }
 
+.mx-12 {
+  margin-left: 12px;
+  margin-right: 12px;
+}
+
 .mt-16 {
   margin-top: 16px;
 }
 
+.ml-16 {
+  margin-left: 16px;
+}
+
 .mr-16 {
   margin-right: 16px;
 }
@@ -388,6 +412,10 @@ a.hover:hover {
   padding: 16px;
 }
 
+.nowrap {
+  white-space: nowrap;
+}
+
 .border {
   border: 1px solid var(--color-sub-border);
 }

+ 15 - 0
src/styles/tdesign.css

@@ -208,6 +208,10 @@
 .t-table {
   font-size: 13px;
 
+  tr > th {
+    background-color: var(--td-bg-color-secondarycontainer);
+  }
+
   .t-table__pagination {
     padding: 16px 0;
 
@@ -598,3 +602,14 @@
 .t-image__wrapper {
   background: none;
 }
+
+.select-options {
+  .t-select-option {
+    height: 100%;
+    padding: 0;
+
+    &:not(.t-is-disabled):not(.t-is-selected):hover {
+      background: none !important ;
+    }
+  }
+}

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

@@ -210,7 +210,7 @@
           </div>
         </template>
         <template v-else-if="a.action == 15">
-          <Network v-model="a.network" mode="1" />
+          <Network v-model="a.network" type="publish" mode="1" />
 
           <div class="form-item mt-8">
             <label>数据对象</label>

+ 269 - 0
src/views/components/Dataset.vue

@@ -0,0 +1,269 @@
+<template>
+  <div class="dataset-component">
+    <div class="form-item mt-8">
+      <label>数据集名称</label>
+      <t-input v-model="modelValue.name" placeholder="名称" />
+    </div>
+
+    <div class="form-item mt-8">
+      <label>数据方式</label>
+      <t-radio-group v-model="modelValue.mode">
+        <t-radio value="api">HTTP请求</t-radio>
+        <t-radio value="">自定义</t-radio>
+      </t-radio-group>
+    </div>
+    <div v-if="modelValue.mode === 'api'" class="form-item mt-8">
+      <label>URL地址</label>
+      <t-input v-model="modelValue.url" @blur="getDatas" @enter="getDatas" />
+    </div>
+    <template v-else>
+      <div class="form-item mt-8">
+        <label>从Excel导入</label>
+        <div>
+          <t-button
+            class="shrink-0"
+            style="width: 90px; height: 30px"
+            @click="importDataset"
+          >
+            导入Excel
+          </t-button>
+          <a href="/data.xlsx" download class="ml-12 mt-4 nowrap">
+            下载Excel示例
+          </a>
+        </div>
+      </div>
+    </template>
+
+    <t-table
+      class="mt-16"
+      row-key="id"
+      :data="modelValue.data"
+      :columns="datasetColumns"
+      size="small"
+    >
+      <template #type="{ row }">
+        {{ row.type || 'string' }}
+      </template>
+      <template v-if="!modelValue.mode" #actions="{ row, rowIndex }">
+        <t-icon name="edit" class="hover" @click="showAddData(row)" />
+        <t-icon
+          name="delete"
+          class="ml-12 hover"
+          @click="modelValue.data.splice(rowIndex, 1)"
+        />
+      </template>
+    </t-table>
+
+    <t-dialog
+      v-if="addDataDialog.show"
+      :visible="true"
+      class="data-dialog"
+      :header="addDataDialog.header"
+      @close="addDataDialog.show = false"
+      @confirm="onOkAddData"
+    >
+      <div class="form-item mt-16">
+        <label>数据点名称</label>
+        <t-input v-model="addDataDialog.data.label" placeholder="简短描述" />
+      </div>
+      <div class="form-item mt-16">
+        <label>数据点ID</label>
+        <t-input v-model="addDataDialog.data.key" placeholder="数据ID" />
+      </div>
+      <div class="form-item mt-16">
+        <label>类型</label>
+        <t-select
+          class="w-full"
+          :options="typeOptions"
+          v-model="addDataDialog.data.type"
+          placeholder="字符串"
+          @change="addDataDialog.data.value = null"
+        />
+      </div>
+      <div class="form-item mt-16">
+        <label>值</label>
+        <div class="flex-grow" v-if="addDataDialog.data.type === 'integer'">
+          <t-input
+            class="w-full"
+            v-model="addDataDialog.data.value"
+            placeholder="整数"
+          />
+          <div class="desc mt-8">
+            固定数字:直接输入数字。例如:5<br />
+            随机范围数字 :最小值-最大值。例如:0-1 或 0-100
+            <br />
+            随机指定数字 :数字1,数字2,数字3... 。 例如:1,5,10,20<br />
+          </div>
+        </div>
+        <div class="flex-grow" v-else-if="addDataDialog.data.type === 'float'">
+          <t-input
+            class="w-full"
+            v-model="addDataDialog.data.value"
+            placeholder="浮点数"
+          />
+          <div class="desc mt-8">
+            固定数字:直接输入数字。例如:5.02<br />
+            随机范围数字 :最小值-最大值。例如:0-1.0 或 0-100
+            <br />
+            随机指定数字 :数字1,数字2,数字3... 。 例如:1.03,5.02,10,20.09<br />
+          </div>
+        </div>
+        <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 />
+            随机:随机生成一个布尔值<br />
+          </div>
+        </div>
+        <div
+          class="flex-grow"
+          v-else-if="
+            addDataDialog.data.type === 'array' ||
+            addDataDialog.data.type === 'object'
+          "
+        >
+          <CodeEditor v-model="addDataDialog.data.value" :json="true" />
+        </div>
+        <div class="flex-grow" v-else>
+          <t-input
+            class="w-full"
+            v-model="addDataDialog.data.value"
+            placeholder="字符串"
+          />
+          <div class="desc mt-8">
+            固定文字:直接输入。例如:大屏可视化<br />
+            随机文本:[文本长度]。例如:[8] 或 [16]<br />
+            随机指定文本:{文本1,文本2,文本3...} 。 例如:{大屏, 可视化}
+            <br />
+          </div>
+        </div>
+      </div>
+    </t-dialog>
+  </div>
+</template>
+
+<script lang="ts" setup>
+import { onBeforeMount, reactive, ref } from 'vue';
+import axios from 'axios';
+import { MessagePlugin } from 'tdesign-vue-next';
+
+import { importExcel } from '@/services/excel';
+import { typeOptions } from '@/services/common';
+
+import CodeEditor from './common/CodeEditor.vue';
+
+const { modelValue } = defineProps<{
+  modelValue: any;
+}>();
+
+const emit = defineEmits(['update:modelValue', 'change']);
+
+const datasetColumns = ref([
+  {
+    colKey: 'label',
+    title: '数据点名称',
+    ellipsis: true,
+  },
+  {
+    colKey: 'key',
+    title: '数据点ID',
+    ellipsis: true,
+  },
+  {
+    colKey: 'type',
+    title: '类型',
+    ellipsis: true,
+  },
+  {
+    colKey: 'value',
+    title: '值',
+    ellipsis: true,
+  },
+  {
+    colKey: 'actions',
+    title: '操作',
+    width: 80,
+  },
+]);
+
+const addDataDialog = reactive<any>({});
+
+onBeforeMount(() => {
+  if (!modelValue.data) {
+    modelValue.data = [];
+  }
+
+  getDatas();
+});
+
+const getDatas = async () => {
+  if (!modelValue.url) {
+    return;
+  }
+
+  const ret = await axios.get(modelValue.url);
+  if (ret) {
+    modelValue.data = ret;
+  }
+};
+
+const importDataset = async () => {
+  let columns: any = [
+    {
+      header: '数据点名称',
+      key: 'label',
+    },
+    {
+      header: '数据点ID',
+      key: 'key',
+    },
+    {
+      header: '类型',
+      key: 'type',
+    },
+    {
+      header: '值',
+      key: 'value',
+    },
+  ];
+  const data: any = await importExcel(columns);
+  modelValue.data = data;
+  emit('update:modelValue', modelValue);
+  emit('change', modelValue);
+};
+
+const showAddData = (row?: any) => {
+  if (row) {
+    addDataDialog.header = '编辑数据';
+    addDataDialog.data = row;
+  } else {
+    addDataDialog.header = '添加数据';
+    addDataDialog.data = { type: 'string' };
+  }
+
+  addDataDialog.show = true;
+};
+
+const onOkAddData = () => {
+  if (!addDataDialog.data.label) {
+    MessagePlugin.error('请填写名称');
+    return;
+  }
+  if (!addDataDialog.data.key) {
+    MessagePlugin.error('请填写数据ID');
+    return;
+  }
+
+  modelValue.data.push(addDataDialog.data);
+  addDataDialog.show = false;
+};
+</script>
+<style lang="postcss" scoped>
+.dataset-component {
+  padding-right: 16px;
+}
+</style>

+ 19 - 23
src/views/components/Network.vue

@@ -1,12 +1,12 @@
 <template>
-  <div class="props">
+  <div class="network-component">
     <div class="form-item mt-8">
-      <label>数据源名称</label>
+      <label>{{ type === 'subscribe' ? '数据订阅' : '数据发布' }}</label>
       <t-select-input
         v-if="mode"
         v-model:inputValue="modelValue.name"
         :value="modelValue.name"
-        placeholder="数据源"
+        placeholder="名称"
         allow-input
         clearable
         v-model:popup-visible="popupVisible"
@@ -36,15 +36,15 @@
           </ul>
         </template>
       </t-select-input>
-      <t-input v-else v-model="modelValue.name" />
+      <t-input v-else v-model="modelValue.name" placeholder="名称" />
     </div>
 
     <div class="form-item mt-8">
-      <label>网络类型</label>
+      <label>通信方式</label>
       <t-select
-        v-model="modelValue.type"
+        v-model="modelValue.protocol"
         placeholder="MQTT"
-        @change="typeChange"
+        @change="protocolChange"
       >
         <t-option key="mqtt" value="mqtt" label="MQTT" />
         <t-option key="websocket" value="websocket" label="Websocket" />
@@ -55,13 +55,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'">
+    <template v-if="modelValue.protocol === 'websocket'"> </template>
+    <template v-else-if="modelValue.protocol === 'http'">
       <div class="form-item mt-8">
         <label>请求方式</label>
         <t-select v-model="modelValue.method" @change="httpMethodChange">
@@ -72,7 +67,7 @@
       <div class="form-item mt-8">
         <label>请求头</label>
         <t-textarea
-          v-model="modelValue.httpHeaders"
+          v-model="modelValue.headers"
           :autosize="{ minRows: 3, maxRows: 5 }"
           placeholder="请输入"
         />
@@ -94,7 +89,7 @@
       <div class="form-item mt-8">
         <label>自动生成</label>
         <t-switch
-          class="ml-8 mt-8"
+          class="mt-8"
           v-model="modelValue.options.customClientId"
           size="small"
         />
@@ -128,6 +123,7 @@ import { debounce } from '@/services/debouce';
 
 const { modelValue, mode } = defineProps<{
   modelValue: any;
+  type: string;
   mode?: any;
 }>();
 
@@ -138,17 +134,16 @@ const networkList = ref<any[]>([]);
 
 onBeforeMount(() => {});
 
-const typeChange = (t: string) => {
-  if (t === 'http') {
+const protocolChange = (protocol: string) => {
+  if (protocol === 'http') {
     Object.assign(modelValue, {
-      http: '',
       httpTimeInterval: 1000,
-      httpHeaders: '',
+      headers: '',
       method: 'GET',
       body: '',
     });
-  } else if (t === 'websocket') {
-    modelValue.protocols = '';
+  } else if (protocol === 'websocket') {
+    // modelValue.url = '';
   } else {
     Object.assign(modelValue, {
       options: {
@@ -205,6 +200,7 @@ const onSelect = (item: any) => {
 };
 </script>
 <style lang="postcss" scoped>
-.props {
+.network-component {
+  padding-right: 16px;
 }
 </style>

+ 45 - 89
src/views/components/PenDatas.vue

@@ -17,18 +17,17 @@
           <label class="label">{{ item.label }}</label>
         </t-tooltip>
         <div class="value">
-          <t-tooltip :content="getBindsDesc(item)" placement="top">
-            <t-icon
-              name="link"
-              class="hover"
-              :class="{ primary: item.binds?.id }"
-              @click="onBind(item)"
-            />
-          </t-tooltip>
           <t-input
-            v-if="item.type === 'number'"
+            v-if="item.type === 'integer'"
+            v-model.number="pen[item.key]"
+            placeholder="整数"
+            @change="changeValue(item.key)"
+          />
+          <t-input-number
+            v-else-if="item.type === 'float'"
             v-model="pen[item.key]"
-            placeholder="数字"
+            placeholder="浮点数"
+            theme="normal"
             @change="changeValue(item.key)"
           />
           <t-switch
@@ -51,6 +50,15 @@
             placeholder="字符串"
             @change="changeValue(item.key)"
           />
+
+          <t-tooltip :content="getBindsDesc(item)" placement="top">
+            <t-icon
+              name="link"
+              class="hover ml-4"
+              :class="{ primary: item.binds?.id }"
+              @click="onBind(item)"
+            />
+          </t-tooltip>
         </div>
         <div>
           <t-tooltip :content="item.triggers?.length || '触发器'">
@@ -140,55 +148,6 @@
         @change="onKeyBlur"
       />
     </div>
-    <div class="form-item mt-16">
-      <label>值</label>
-      <div class="flex-grow" v-if="addDataDialog.data.type === 'number'">
-        <t-input
-          class="w-full"
-          v-model="addDataDialog.data.value"
-          placeholder="数字"
-        />
-        <div class="desc mt-8">
-          固定数字:直接输入数字。例如:5<br />
-          随机范围数字 :最小值-最大值。例如:0-1 或 0-100
-          <br />
-          随机指定数字 :数字1,数字2,数字3... 。 例如:1,5,10,20<br />
-        </div>
-      </div>
-      <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 />
-          随机:随机生成一个布尔值<br />
-        </div>
-      </div>
-      <div
-        class="flex-grow"
-        v-else-if="
-          addDataDialog.data.type === 'array' ||
-          addDataDialog.data.type === 'object'
-        "
-      >
-        <CodeEditor v-model="addDataDialog.data.value" :json="true" />
-      </div>
-      <div class="flex-grow" v-else>
-        <t-input
-          class="w-full"
-          v-model="addDataDialog.data.value"
-          placeholder="字符串"
-        />
-        <div class="desc mt-8">
-          固定文字:直接输入。例如:大屏可视化<br />
-          随机文本:[文本长度]。例如:[8] 或 [16]<br />
-          随机指定文本:{文本1,文本2,文本3...} 。 例如:{大屏, 可视化}
-          <br />
-        </div>
-      </div>
-    </div>
   </t-dialog>
 
   <t-dialog
@@ -196,26 +155,12 @@
     :visible="true"
     class="data-link-dialog"
     header="动态数据绑定"
-    @close="
-      // dataBindDialog.data.binds = dataBindDialog.bkBinds;
-      dataBindDialog.show = false
-    "
+    @close="dataBindDialog.show = false"
     @confirm="dataBindonConfirm"
     :width="700"
   >
     <div class="form-item">
       <label>当前绑定:</label>
-      <!-- <div class="label" v-if="dataBindDialog.data.binds?.length">
-        <t-tooltip
-          v-for="(tag, index) in dataBindDialog.data.binds"
-          :key="index"
-          :content="tag.id"
-        >
-          <t-tag class="mr-8 mb-8" closable @close="onRemoveBind(index)">
-            {{ tag.label }}
-          </t-tag>
-        </t-tooltip>
-      </div> -->
       <div class="label" v-if="dataBindDialog.data.binds?.id">
         <t-tooltip :content="dataBindDialog.data.binds?.id">
           <t-tag class="mr-8 mb-8" closable @close="onRemoveBind()">
@@ -240,7 +185,7 @@
     <t-table
       class="mt-12 data-list"
       row-key="id"
-      :data="dataBindDialog.dataSet"
+      :data="dataBindDialog.dataset"
       :columns="dataSetColumns"
       size="small"
       bordered
@@ -534,21 +479,26 @@ const dataSetColumns = [
     width: 50,
   },
   {
-    colKey: 'id',
-    title: '编号',
-    width: 150,
+    colKey: 'label',
+    title: '数据点名称',
+    width: 180,
     ellipsis: { theme: 'light', trigger: 'context-menu' },
   },
   {
-    colKey: 'label',
-    title: '动态数据名称',
-    width: 220,
+    colKey: 'id',
+    title: '数据点ID',
+    width: 180,
     ellipsis: { theme: 'light', trigger: 'context-menu' },
   },
   {
-    colKey: 'case',
-    title: '场景',
-    ellipsis: { theme: 'light', trigger: 'context-menu' },
+    colKey: 'type',
+    title: '类型',
+    width: 100,
+  },
+  {
+    colKey: 'value',
+    title: '值',
+    ellipsis: true,
   },
 ];
 
@@ -713,15 +663,21 @@ const getDataset = async () => {
   // @ts-ignore
   const data: Meta2dBackData = meta2d.data();
 
+  if (!data.dataset) {
+    return;
+  }
+
   dataBindDialog.loading = true;
+  if (data.dataset.url) {
+    const ret: any = await axios.get(data.dataset.url);
+    dataBindDialog.dataset = ret;
+    query.total = ret.total;
+  } else {
+    dataBindDialog.dataset = data.dataset.data;
+  }
 
   // 应该从data获取url或结果列表
-  const ret: any = await axios.get(
-    `/api/device/data/set?mock=1&q=${dataBindDialog.input}&current=${query.current}&pageSize=${query.pageSize}`
-  );
 
-  dataBindDialog.dataSet = ret.list;
-  query.total = ret.total;
   dataBindDialog.loading = false;
 };
 

+ 219 - 239
src/views/components/View.vue

@@ -305,18 +305,16 @@
                     <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"
+                          class="hover-background item"
+                          v-for="(item, i) in dataDialog.networkList"
                           :key="item.url"
-                          @click="() => onSelectNetWork(item)"
+                          @click="onSelectNetWork(item)"
                         >
                           名称: {{ item.name }}
                           <div class="desc">地址: {{ item.url }}</div>
+                          <span class="del" @click.stop="onDelNetWork(item, i)">
+                            <t-icon name="delete" />
+                          </span>
                         </li>
                         <li
                           v-if="!dataDialog.networkList.length"
@@ -348,8 +346,8 @@
                 :max-height="280"
                 size="small"
               >
-                <template #type="{ row, rowIndex }">
-                  {{ row.type || 'MQTT' }}
+                <template #protocol="{ row, rowIndex }">
+                  {{ row.protocol || 'MQTT' }}
                 </template>
                 <template #actions="{ row, rowIndex }">
                   <a @click="editNetwork(row)"> 编辑 </a>
@@ -381,7 +379,7 @@
                 </a>
               </div>
               <div style="height: 300px; overflow-y: auto">
-                <Network v-model="dataDialog.network" />
+                <Network v-model="dataDialog.network" type="subscribe" />
               </div>
             </div>
           </template>
@@ -389,73 +387,79 @@
         <t-tab-panel :value="2" :destroy-on-hide="false">
           <template #label> 数据集 </template>
           <template #panel>
-            <!--
-            <div class="form-item" style="margin-top: 28px">
-              <label style="width: 100px">
-                自定义
-                <t-tooltip content="初始静态或模拟数据,开发设计阶段使用">
-                  <t-icon
-                    name="help-circle"
-                    class="ml-4 hover"
-                    style="margin-top: -2px"
-                  />
-                </t-tooltip>
-              </label>
-              <div class="w-full flex">
-                <t-button @click="importDataset">从Excel导入</t-button>
-                <a href="/data.xlsx" download class="ml-16 mt-4">
-                  下载Excel示例
+            <template v-if="!dataDialog.editDataset">
+              <div class="form-item mt-16">
+                <label style="width: 100px"> 当前数据集 </label>
+                <div class="flex w-full">
+                  <t-select
+                    class="flex-grow"
+                    v-model="dataDialog.dataset.id"
+                    filterable
+                    placeholder="选择数据集"
+                    :on-search="onInputDatasets"
+                    :popup-props="{ overlayClassName: 'select-options' }"
+                    @change="onSelDataset"
+                  >
+                    <t-option
+                      v-for="(item, i) in dataDialog.datasetList"
+                      :key="item.id"
+                      :value="item.id"
+                      :label="item.name"
+                    >
+                      <div class="hover-background item">
+                        名称: {{ item.name }}
+                        <div v-if="item.url" class="desc">
+                          URL: {{ item.url }}
+                        </div>
+                        <div v-else class="desc">自定义</div>
+                        <span class="del" @click.stop="onDelDataset(item, i)">
+                          <t-icon name="delete" />
+                        </span>
+                      </div>
+                    </t-option>
+                  </t-select>
+
+                  <t-button
+                    class="ml-12 shrink-0"
+                    style="height: 30px"
+                    @click="addDataset"
+                  >
+                    添加数据集
+                  </t-button>
+                </div>
+              </div>
+
+              <t-table
+                class="mt-12"
+                row-key="id"
+                :data="dataDialog.dataset.data"
+                :columns="datasetColumns"
+                size="small"
+                :max-height="280"
+              >
+                <template #type="{ row }">
+                  {{ row.type || 'string' }}
+                </template>
+              </t-table>
+            </template>
+
+            <div v-else>
+              <div class="mt-8">
+                <a
+                  @click="
+                    onSelDataset();
+                    dataDialog.editDataset = false;
+                  "
+                  class="flex middle"
+                  style="width: 44px"
+                >
+                  <t-icon name="rollback" class="mr-4" /> 返回
                 </a>
-                <div class="flex-grow"></div>
-                <a class="mt-4" @click="showAddData()"> + 添加数据 </a>
               </div>
-            </div> -->
-
-            <div class="form-item mt-16">
-              <label style="width: 100px"> 当前数据集 </label>
-              <div class="flex">
-                <t-select
-                  v-model="dataDialog.dataset"
-                  filterable
-                  placeholder="选择数据集"
-                  :on-search="onInputDatasets"
-                  :options="dataDialog.datasetList"
-                />
-                <t-button style="height: 30px" @click="addNetwork">
-                  添加数据集
-                </t-button>
-                <t-button @click="importDataset">从Excel导入</t-button>
+              <div style="height: 300px; overflow-y: auto">
+                <Dataset v-model="dataDialog.dataset" />
               </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 #type="{ row }">
-                {{ row.type || 'string' }}
-              </template>
-              <template #actions="{ row, rowIndex }">
-                <t-icon name="edit" class="hover" @click="showAddData(row)" />
-                <t-icon
-                  name="delete"
-                  class="ml-12 hover"
-                  @click="dataDialog.dataset.splice(rowIndex, 1)"
-                />
-              </template>
-              <template #empty>
-                <div class="center">
-                  暂无数据, <a class="mt-4" @click="showAddData()"> + 添加 </a>
-                </div>
-              </template>
-            </t-table>
           </template>
         </t-tab-panel>
       </t-tabs>
@@ -471,15 +475,22 @@
           </t-checkbox>
           <t-button class="ml-16" @click="onOkNetwork">确定</t-button>
         </div>
-        <div
-          v-else-if="dataDialog.tab === 2 && 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 v-else-if="dataDialog.tab === 2" class="flex middle">
+          <template v-if="dataDialog.editDataset">
+            <div class="flex-grow"></div>
+            <t-checkbox v-model="dataDialog.save">
+              同时保存为我的数据集
+            </t-checkbox>
+            <t-button class="ml-16" @click="onOkDataset">确定</t-button>
+          </template>
+          <template v-else>
+            <a v-if="dataDialog.dataset.id" @click="dataDialog.editDataset = 2">
+              编辑当前数据集
+            </a>
+            <div class="flex-grow"></div>
+
+            <t-button class="ml-16" @click="onOkDatasources">完成</t-button>
+          </template>
         </div>
         <div v-else class="flex middle">
           <div class="flex-grow"></div>
@@ -488,83 +499,6 @@
       </template>
     </t-dialog>
 
-    <t-dialog
-      v-if="addDataDialog.show"
-      :visible="true"
-      class="data-dialog"
-      :header="addDataDialog.header"
-      @close="addDataDialog.show = false"
-      @confirm="onOkAddData"
-    >
-      <div class="form-item mt-16">
-        <label>名称</label>
-        <t-input v-model="addDataDialog.data.label" placeholder="简短描述" />
-      </div>
-      <div class="form-item mt-16">
-        <label>数据ID</label>
-        <t-input v-model="addDataDialog.data.key" placeholder="数据ID" />
-      </div>
-      <div class="form-item mt-16">
-        <label>类型</label>
-        <t-select
-          class="w-full"
-          :options="typeOptions"
-          v-model="addDataDialog.data.type"
-          placeholder="字符串"
-          @change="addDataDialog.data.value = null"
-        />
-      </div>
-      <div class="form-item mt-16">
-        <label>值</label>
-        <div class="flex-grow" v-if="addDataDialog.data.type === 'number'">
-          <t-input
-            class="w-full"
-            v-model="addDataDialog.data.value"
-            placeholder="数字"
-          />
-          <div class="desc mt-8">
-            固定数字:直接输入数字。例如:5<br />
-            随机范围数字 :最小值-最大值。例如:0-1 或 0-100
-            <br />
-            随机指定数字 :数字1,数字2,数字3... 。 例如:1,5,10,20<br />
-          </div>
-        </div>
-        <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 />
-            随机:随机生成一个布尔值<br />
-          </div>
-        </div>
-        <div
-          class="flex-grow"
-          v-else-if="
-            addDataDialog.data.type === 'array' ||
-            addDataDialog.data.type === 'object'
-          "
-        >
-          <CodeEditor v-model="addDataDialog.data.value" :json="true" />
-        </div>
-        <div class="flex-grow" v-else>
-          <t-input
-            class="w-full"
-            v-model="addDataDialog.data.value"
-            placeholder="字符串"
-          />
-          <div class="desc mt-8">
-            固定文字:直接输入。例如:大屏可视化<br />
-            随机文本:[文本长度]。例如:[8] 或 [16]<br />
-            随机指定文本:{文本1,文本2,文本3...} 。 例如:{大屏, 可视化}
-            <br />
-          </div>
-        </div>
-      </div>
-    </t-dialog>
-
     <t-dialog
       v-if="publishDialog.show"
       width="700px"
@@ -748,20 +682,18 @@ import {
   onScaleFull,
   onScaleWindow,
   useDot,
-  typeOptions,
 } from '@/services/common';
 import { useSelection } from '@/services/selections';
 import { defaultFormat } from '@/services/defaults';
 import { checkData, localStorageName, Meta2dBackData } from '@/services/utils';
 import { debounce } from '@/services/debouce';
-import { importExcel } from '@/services/excel';
 import { s8 } from '@/services/random';
+import { setCookie } from '@/services/cookie';
 
-import CodeEditor from './common/CodeEditor.vue';
 import ContextMenu from './ContextMenu.vue';
 import Network from './Network.vue';
+import Dataset from './Dataset.vue';
 import ChargeCloudPublish from './ChargeCloudPublish.vue';
-import { setCookie } from '@/services/cookie';
 
 const router = useRouter();
 const route = useRoute();
@@ -795,8 +727,6 @@ const publishDialog = reactive<any>({});
 
 const publishChargeDialog = reactive<any>({});
 
-const addDataDialog = reactive<any>({});
-
 onMounted(() => {
   meta2d = new Meta2d('meta2d', meta2dOptions);
   registerBasicDiagram();
@@ -1204,8 +1134,8 @@ const networkColumns = ref([
     width: 160,
   },
   {
-    colKey: 'type',
-    title: '类型',
+    colKey: 'protocol',
+    title: '通信方式',
     width: 120,
   },
   {
@@ -1220,7 +1150,7 @@ const onShowDataDialog = () => {
   dataDialog.input = '';
   dataDialog.networks = meta2d.store.data.networks || [];
   // @ts-ignore
-  dataDialog.dataset = meta2d.store.data.dataset;
+  dataDialog.dataset = meta2d.store.data.dataset || {};
   dataDialog.networkList = [];
   dataDialog.datasetList = [];
   dataDialog.editNetwork = false;
@@ -1235,21 +1165,39 @@ const onSelectNetWork = (item: any) => {
   dataDialog.popupVisible = false;
 };
 
+const onDelNetWork = async (item: any, i: number) => {
+  const ret: any = await axios.post(`/api/data/datasources/delete`, {
+    id: item._id || item.id,
+  });
+  if (ret) {
+    dataDialog.networkList.splice(i, 1);
+  }
+};
+
 const onInputNetwork = () => {
   debounce(getNetworks, 300);
 };
 
 // 请求我的数据订阅
 const getNetworks = async () => {
-  const ret: any = await axios.post(`/api/data/datasources/list`, {
-    q: {
-      name: dataDialog.input,
-    },
-    query: {
-      type: 'subscribe',
+  const ret: any = await axios.post(
+    `/api/data/datasources/list`,
+    {
+      q: {
+        name: dataDialog.input,
+      },
+      query: {
+        type: 'subscribe',
+      },
+      projection: { updatedAt: 0 },
     },
-    projection: { updatedAt: 0 },
-  });
+    {
+      params: {
+        current: 1,
+        pageSize: 10,
+      },
+    }
+  );
   if (ret) {
     dataDialog.networkList = ret.list;
   }
@@ -1270,7 +1218,12 @@ const getDatasets = async (name?: string) => {
   if (name) {
     body.q = { name };
   }
-  const ret: any = await axios.post(`/api/data/datasources/list`, body);
+  const ret: any = await axios.post(`/api/data/datasources/list`, body, {
+    params: {
+      current: 1,
+      pageSize: 10,
+    },
+  });
   if (ret) {
     dataDialog.datasetList = ret.list;
   }
@@ -1279,7 +1232,8 @@ const getDatasets = async (name?: string) => {
 const addNetwork = () => {
   dataDialog.network = {
     name: '',
-    type: '',
+    type: 'subscribe',
+    protocol: 'mqtt',
     url: '',
     options: {
       clientId: '',
@@ -1317,6 +1271,7 @@ const onOkNetwork = async () => {
       dataDialog.network.id = dataDialog.network._id;
     }
     dataDialog.networks.push(dataDialog.network);
+    dataDialog.networkList.push(dataDialog.network);
   } else if (dataDialog.editNetwork === 2) {
     if (dataDialog.save) {
       const ret: any = await axios.post(
@@ -1335,7 +1290,12 @@ const onOkNetwork = async () => {
 const datasetColumns = ref([
   {
     colKey: 'label',
-    title: '名称(数据ID)',
+    title: '数据点名称',
+    ellipsis: true,
+  },
+  {
+    colKey: 'key',
+    title: '数据点ID',
     ellipsis: true,
   },
   {
@@ -1348,77 +1308,97 @@ const datasetColumns = ref([
     title: '值',
     ellipsis: true,
   },
-  {
-    colKey: 'actions',
-    title: '操作',
-    width: 80,
-  },
 ]);
 
-const importDataset = async () => {
-  let columns: any = [
-    {
-      header: '名称',
-      key: 'label',
-    },
-    {
-      header: '数据ID',
-      key: 'key',
-    },
-    {
-      header: '类型',
-      key: 'type',
-    },
-    {
-      header: '场景',
-      key: 'case',
-    },
-    {
-      header: '值',
-      key: 'value',
-    },
-  ];
-  const data: any = await importExcel(columns);
-  dataDialog.dataset = data;
+const addDataset = () => {
+  dataDialog.dataset = {
+    name: '',
+    type: 'dataset',
+    mode: 'api',
+    url: '',
+    data: [],
+  };
+  dataDialog.editDataset = 1;
 };
 
-const onOkDatasources = () => {
-  meta2d.store.data.networks = dataDialog.networks;
-  // @ts-ignore
-  meta2d.store.data.dataset = dataDialog.dataset;
-  // @ts-ignore
-  meta2d.store.data.datasetUrl = dataDialog.datasetUrl;
-  setDot(true);
-  meta2d.connectNetwork();
-  dataDialog.show = false;
-};
+const onOkDataset = async () => {
+  if (!dataDialog.dataset.name) {
+    MessagePlugin.error('名称不能为空');
+    return;
+  }
 
-const showAddData = (row?: any) => {
-  if (row) {
-    addDataDialog.header = '编辑数据';
-    addDataDialog.data = row;
-  } else {
-    addDataDialog.header = '添加数据';
-    addDataDialog.data = { type: 'string' };
+  // 保存到我的数据源
+  if (dataDialog.editDataset === 1) {
+    if (dataDialog.save) {
+      const ret: any = await axios.post(
+        `/api/data/datasources/add`,
+        dataDialog.dataset
+      );
+      if (!ret) {
+        return;
+      }
+      dataDialog.dataset._id = ret._id || ret.id;
+      dataDialog.dataset.id = dataDialog.dataset._id;
+    }
+    dataDialog.datasetList.push(dataDialog.dataset);
+  } else if (dataDialog.editDataset === 2) {
+    if (dataDialog.save) {
+      const ret: any = await axios.post(
+        `/api/data/datasources/update`,
+        dataDialog.dataset
+      );
+      if (!ret) {
+        return;
+      }
+    }
   }
 
-  addDataDialog.show = true;
+  dataDialog.editDataset = false;
 };
 
-const onOkAddData = () => {
-  if (!addDataDialog.data.label) {
-    MessagePlugin.error('请填写名称');
-    return;
+const onDelDataset = async (item: any, i: number) => {
+  const ret: any = await axios.post(`/api/data/datasources/delete`, {
+    id: item._id || item.id,
+  });
+  if (ret) {
+    dataDialog.datasetList.splice(i, 1);
   }
-  if (!addDataDialog.data.key) {
-    MessagePlugin.error('请填写数据ID');
-    return;
+};
+
+const onSelDataset = async () => {
+  if (dataDialog.dataset.id) {
+    const dataset: any = dataDialog.datasetList.find(
+      (item: any) =>
+        item._id === dataDialog.dataset.id || item.id === dataDialog.dataset.id
+    );
+
+    if (!dataset.id) {
+      dataset.id = dataset._id;
+    } else if (!dataset._id) {
+      dataset._id = dataset.id;
+    }
+
+    if (dataset.url) {
+      const ret = await axios.get(dataset.url);
+      if (ret) {
+        dataset.data = ret;
+      }
+    }
+
+    dataDialog.dataset = JSON.parse(JSON.stringify(dataset));
   }
-  if (!dataDialog.dataset) {
-    dataDialog.dataset = [];
+};
+
+const onOkDatasources = () => {
+  meta2d.store.data.networks = dataDialog.networks;
+  if (dataDialog.dateset) {
+    delete dataDialog.dataset.data;
   }
-  dataDialog.dataset.push(addDataDialog.data);
-  addDataDialog.show = false;
+  // @ts-ignore
+  meta2d.store.data.dataset = dataDialog.dataset;
+  setDot(true);
+  meta2d.connectNetwork();
+  dataDialog.show = false;
 };
 
 const share = async () => {