Jelajahi Sumber

feat:数据管理重构

ananzhusen 1 tahun lalu
induk
melakukan
396d2b3975

TEMPAT SAMPAH
public/data.xlsx


+ 1 - 1
src/services/defaults.ts

@@ -1590,7 +1590,7 @@ export const formComponents = [
           width: 0,
           height: 0,
           disableAnchor: true,
-          disableSize: true,
+          // disableSize: true,
           colWidth: 150,
           rowHeight: 40,
           data: [

+ 22 - 22
src/views/components/Dataset.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="dataset-component">
     <div class="form-item mt-8">
-      <label>数据点列表</label>
+      <label>变量列表</label>
       <t-input v-model="modelValue.name" placeholder="名称" />
     </div>
 
@@ -63,7 +63,7 @@
       </template>
     </t-table>
 
-    <t-dialog
+    <!-- <t-dialog
       v-if="addDataDialog.show"
       :visible="true"
       class="data-dialog"
@@ -76,18 +76,18 @@
         <t-input v-model="addDataDialog.data.device" placeholder="设备名称" />
       </div>
       <div class="form-item mt-16">
-        <label>数据点名称</label>
+        <label>显示名称</label>
         <t-input
           v-model="addDataDialog.data.label"
-          placeholder="数据点简短描述"
+          placeholder="变量简短描述"
         />
       </div>
       <div class="form-item mt-16">
-        <label>数据点ID</label>
+        <label>变量名</label>
         <t-input
           @change="changeDataID($event)"
           :value="addDataDialog.data.id"
-          placeholder="数据点ID"
+          placeholder="变量名"
         />
       </div>
       <div class="form-item mt-16">
@@ -125,7 +125,7 @@
           </ul>
         </div>
       </div>
-    </t-dialog>
+    </t-dialog> -->
   </div>
 </template>
 
@@ -147,19 +147,19 @@ const { modelValue } = defineProps<{
 const emit = defineEmits(['update:modelValue', 'change']);
 
 const datasetColumns = ref([
-  {
-    colKey: 'device',
-    title: '设备',
-    ellipsis: true,
-  },
+  // {
+  //   colKey: 'device',
+  //   title: '设备',
+  //   ellipsis: true,
+  // },
   {
     colKey: 'label',
-    title: '数据点名称',
+    title: '显示名称',
     ellipsis: true,
   },
   {
     colKey: 'id',
-    title: '数据点ID',
+    title: '变量名',
     ellipsis: true,
   },
   {
@@ -232,16 +232,16 @@ const flattenTree = (root) =>{
 
 const importDataset = async () => {
   let columns: any = [
+    // {
+    //   header: '设备',
+    //   key: 'device',
+    // },
     {
-      header: '设备',
-      key: 'device',
-    },
-    {
-      header: '数据点名称',
+      header: '显示名称',
       key: 'label',
     },
     {
-      header: '数据点ID',
+      header: '变量名',
       key: 'id',
     },
     {
@@ -306,12 +306,12 @@ const onOkAddData = () => {
 
 const changeDataID = (value) => {
   if (!value) {
-    MessagePlugin.error('数据点id 不能为空!');
+    MessagePlugin.error('变量名不能为空!');
     return;
   }
   let item = modelValue.devices.filter((item) => item.id === value);
   if (item && item.length) {
-    MessagePlugin.error('数据点id重复!');
+    MessagePlugin.error('变量名重复!');
     return;
   }
   addDataDialog.data.id = value;

+ 9 - 1
src/views/components/Network.vue

@@ -2,7 +2,7 @@
   <div class="network-component">
     <div class="form-item mt-8">
       <label>
-        {{ modelValue.type === 'subscribe' ? '数据通信' : '数据发送' }}
+        {{ modelValue.type === 'subscribe' ? '数据获取' : '数据发送' }}
       </label>
       <t-select-input
         v-if="mode"
@@ -97,6 +97,14 @@
           <t-option key="POST" value="POST" label="POST" />
         </t-select>
       </div>
+      <div class="form-item mt-8">
+        <label>请求间隔</label>
+        <t-input-number
+          theme="column"
+          v-model="modelValue.interval"
+          placeholder="默认1000 ms"
+        />
+      </div>
       <div class="form-item mt-8">
         <label>请求头</label>
         <!-- <t-textarea

+ 31 - 29
src/views/components/PenDatas.vue

@@ -5,7 +5,7 @@
       expandIconPlacement="right"
       :borderless="true"
     >
-      <t-collapse-panel value="1" header="通信">
+      <!-- <t-collapse-panel value="1" header="通信">
         <t-space direction="vertical" size="small" class="w-full">
           <div class="form-item">
             <label title="http地址">http地址 </label>
@@ -27,13 +27,6 @@
           </div>
           <div class="form-item">
             <label title="请求头">请求头</label>
-            <!-- <CodeEditor
-              :json="true"
-              :key="props.pen.id"
-              v-model="props.pen.apiHeaders"
-              class="mt-4"
-              style="height: 50px"
-            /> -->
             <t-button
               class="ml-8"
               shape="square"
@@ -42,19 +35,10 @@
               @click="preShowPropsEdit('apiHeaders')"
             >
               <ellipsis-icon slot="icon"/>
-              <!-- <t-icon name="ellipsis" slot="icon"
-            /> -->
           </t-button>
           </div>
           <div class="form-item" v-if="props.pen.apiMethod === 'POST'">
             <label title="请求体">请求体</label>
-            <!-- <CodeEditor
-              :json="true"
-              :key="props.pen.id"
-              v-model="props.pen.apiBody"
-              class="mt-4"
-              style="height: 50px"
-            /> -->
             <t-button
               class="ml-8"
               shape="square"
@@ -63,8 +47,6 @@
               @click="preShowPropsEdit('apiBody')"
             >
               <ellipsis-icon slot="icon"/>
-              <!-- <t-icon name="ellipsis" slot="icon"
-            /> -->
             </t-button>
           </div>
           <div class="form-item">
@@ -77,7 +59,7 @@
             />
           </div>
         </t-space>
-      </t-collapse-panel>
+      </t-collapse-panel> -->
       <t-collapse-panel v-if="props.pen.props.custom" value="2" header="属性">
         <t-space direction="vertical" size="small" class="w-full">
           <div v-for="item in props.pen.props.custom" class="form-item">
@@ -371,7 +353,7 @@
     <t-tabs :defaultValue="1">
       <t-tab-panel
         :value="1"
-        label="绑定数据点"
+        label="绑定变量"
         :destroy-on-hide="false"
         style="height: 420px"
       >
@@ -822,19 +804,19 @@ const dataSetColumns = [
     type: 'single',
     width: 50,
   },
-  {
-    colKey: 'device',
-    title: '设备',
-    ellipsis: { theme: 'light', trigger: 'context-menu' },
-  },
+  // {
+  //   colKey: 'device',
+  //   title: '设备',
+  //   ellipsis: { theme: 'light', trigger: 'context-menu' },
+  // },
   {
     colKey: 'label',
-    title: '数据点名称',
+    title: '显示名称',
     ellipsis: { theme: 'light', trigger: 'context-menu' },
   },
   {
     colKey: 'id',
-    title: '数据点ID',
+    title: '变量名',
     ellipsis: { theme: 'light', trigger: 'context-menu' },
   },
   {
@@ -1036,10 +1018,27 @@ const onSearchDataset = () => {
 const getDataset = async () => {
   // @ts-ignore
   const data: Meta2dBackData = meta2d.data();
-
   if (!data.dataset) {
     return;
   }
+  dataBindDialog.dataset = data.dataset.devices;
+  dataBindDialog.datasetList = data.dataset.devices;
+  if(dataBindDialog.device || dataBindDialog.input){
+    dataBindDialog.dataset = dataBindDialog.datasetList.filter(
+        (item: any) => {
+          if (dataBindDialog.device && item.device !== dataBindDialog.device) {
+            return;
+          }
+
+          return (
+            searchPinyin(item.label, dataBindDialog.input) ||
+            item.id.indexOf(dataBindDialog.input) > -1
+          );
+        }
+      );
+  }
+  query.total = dataBindDialog.dataset.length;
+  return;
 
   dataBindDialog.loading = true;
   if (data.dataset.url) {
@@ -1054,6 +1053,8 @@ const getDataset = async () => {
     dataBindDialog.dataset = ret;
     dataBindDialog.url = true;
     query.total = ret.total;
+    console.log( dataBindDialog.dataset,
+    query.total);
   } else {
     dataBindDialog.url = false;
     if (!dataBindDialog.datasetList) {
@@ -1093,6 +1094,7 @@ const getDataset = async () => {
       dataBindDialog.dataset = [...dataBindDialog.datasetList];
       query.total = dataBindDialog.datasetList.length;
     }
+    console.log("dataBindDialog",dataBindDialog,dataBindDialog);
   }
   dataBindDialog.loading = false;
 };

+ 447 - 81
src/views/components/View.vue

@@ -365,22 +365,16 @@
       width="800px"
       header="数据管理"
       :visible="true"
-      @close="dataDialog.show = false"
+      @close="closeDataDialog"
     >
       <t-tabs v-model="dataDialog.tab" class="body">
-        <t-tab-panel :value="1" label="数据通信" :destroy-on-hide="false">
+        <t-tab-panel :value="1" label="数据获取" :destroy-on-hide="false">
           <template #panel>
             <div v-if="!dataDialog.editNetwork">
               <div class="mt-16 flex between middle">
                 <div>
-                  <t-checkbox
-                    style="display: inline"
-                    v-model="dataDialog.enableMock"
-                    @change="onChangeMock"
-                  >
-                    开启模拟数据
-                  </t-checkbox>
-                  <label class="ml-8"> 轮询间隔 </label>
+                  
+                  <!-- <label class="ml-8"> 轮询间隔 </label>
                   <t-input-number
                     style="width: 100px"
                     v-model="dataDialog.networkInterval"
@@ -389,12 +383,12 @@
                     :min="30"
                     placeholder="毫秒"
                     suffix="ms"
-                  />
+                  /> -->
                 </div>
                 <div>
                   <t-select-input
                     v-model:inputValue="dataDialog.input"
-                    placeholder="搜索我的数据通信"
+                    placeholder="搜索我的数据获取"
                     allow-input
                     clearable
                     @focus="dataDialog.popupVisible = true"
@@ -451,7 +445,7 @@
                     style="height: 30px"
                     @click="addNetwork"
                   >
-                    添加数据通信
+                    添加数据获取
                   </t-button>
                 </div>
               </div>
@@ -477,7 +471,7 @@
                   <div class="center">
                     <div>暂无数据</div>
                     <div class="mt-8">
-                      <a @click="addNetwork"> + 添加数据通信</a>
+                      <a @click="addNetwork"> + 添加数据获取</a>
                     </div>
                   </div>
                 </template>
@@ -505,55 +499,97 @@
           </template>
         </t-tab-panel>
         <t-tab-panel :value="2" :destroy-on-hide="false">
-          <template #label> 数据点列表 </template>
+          <template #label> 变量列表 </template>
           <template #panel>
             <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.datasetId"
-                    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="form-item mt-16 flex between middle">
+                <div>
+                <a @click="showAddData()"> + 新建变量</a>
+                <a style="margin-left: 8px;" @click="clearData()"> × 清空列表</a>
+                </div>
+                <div class="form-item flex">
+                  <!-- <div class="form-item flex"> -->
+                    <label style="text-align: end;margin-left: 8px;font-size: 13px;">导入变量:</label>
+                    <t-select
+                        style="width: 120px"
+                        v-model="dataDialog.datasetType"
+                        placeholder="选择变量"
+                      >
+                      <t-option :key="1" :value="1" label="我的变量列表"/>
+                      <t-option :key="2" :value="2" label="从Excel导入"/>
+                      <t-option :key="3" :value="3" label="在线接口"/>
+                    </t-select>
+                  <!-- </div> -->
+                  <!-- <label style="width: 100px"> 当前变量列表 </label> -->
+                  <!-- <div class="flex"> -->
+                  <div style="width: 240px;padding-left: 8px;">
+                    <t-select
+                      v-if="dataDialog.datasetType === 1"
+                      v-model="dataDialog.datasetId"
+                      filterable
+                      placeholder="选择变量列表"
+                      :on-search="onInputDatasets"
+                      :popup-props="{ overlayClassName: 'select-options' }"
+                      @change="onSelDataset()"
                     >
-                      <div class="hover-background item">
-                        <div style="font-size: 14px">{{ item.name }}</div>
-                        <div v-if="item.url" class="desc">
-                          {{ item.url }}
+                      <t-option
+                        v-for="(item, i) in dataDialog.datasetList"
+                        :key="item.id"
+                        :value="item.id"
+                        :label="item.name"
+                      >
+                        <div class="hover-background item">
+                          <div style="font-size: 14px">{{ item.name }}</div>
+                          <div v-if="item.url" class="desc">
+                            {{ item.url }}
+                          </div>
+                          <div v-else class="desc">自定义</div>
+                          <span class="del" @click.stop="onDelDataset(item, i)">
+                            <delete-icon />
+                            <!-- <t-icon name="delete" /> -->
+                          </span>
                         </div>
-                        <div v-else class="desc">自定义</div>
-                        <span class="del" @click.stop="onDelDataset(item, i)">
-                          <delete-icon />
-                          <!-- <t-icon name="delete" /> -->
-                        </span>
-                      </div>
-                    </t-option>
-                    <t-option
-                      v-if="dataDialog.datasetList.length >= 10"
-                      :disabled="true"
-                    >
-                      <div class="ml-8 gray">...</div>
-                    </t-option>
-                  </t-select>
-
-                  <t-button
+                      </t-option>
+                      <t-option
+                        v-if="dataDialog.datasetList.length >= 10"
+                        :disabled="true"
+                      >
+                        <div class="ml-8 gray">...</div>
+                      </t-option>
+                    </t-select>
+                    <div class="flex middle"  v-if="dataDialog.datasetType === 2">
+                      <t-button
+                        class="shrink-0"
+                        style="width: 90px; height: 30px"
+                        @click="importDataset"
+                      >
+                        导入Excel
+                      </t-button>
+                      <a
+                        :href="cdn ? cdn + '/v/data.xlsx' : '/data.xlsx'"
+                        download
+                        class="ml-12 mt-4 nowrap"
+                      >
+                        下载Excel示例
+                      </a>
+                    </div>
+                    <div v-if="dataDialog.datasetType === 3" class="form-item">
+                      <t-input label="地址:" placeholder="点击图标或回车获取数据"  v-model="dataDialog.dataset.url" @blur="getDatas" @enter="getDatas" >
+                        <template #suffixIcon>
+                          <CloudDownloadIcon @click="getDatas" :style="{ cursor: 'pointer' }" />
+                        </template>
+                      </t-input>
+                    </div>
+                  </div>
+                </div>
+                  <!-- <t-button
                     class="ml-12 shrink-0"
                     style="height: 30px"
                     @click="addDataset"
                   >
-                    添加数据点列表
-                  </t-button>
-                </div>
+                    添加变量列表
+                  </t-button> -->
+                <!-- </div> -->
               </div>
 
               <t-table
@@ -567,6 +603,11 @@
                 <template #type="{ row }">
                   {{ row.type || 'string' }}
                 </template>
+                <template  #actions="{ row, rowIndex }">
+                  <edit-icon class="hover" @click="showAddData(row, rowIndex)"/>
+                  <delete-icon class="ml-12 hover"
+                    @click="dataDialog.dataset.devices.splice(rowIndex, 1)"/>
+                </template>
               </t-table>
             </template>
             <div v-else>
@@ -592,7 +633,7 @@
         >
           <div class="flex-grow"></div>
           <t-checkbox v-model="dataDialog.save" class="mr-12">
-            同时保存到我的数据通信
+            同时保存到我的数据获取
           </t-checkbox>
           <t-button @click="onOkNetwork">确定</t-button>
         </div>
@@ -600,25 +641,40 @@
           <template v-if="dataDialog.editDataset === 1">
             <div class="flex-grow"></div>
             <t-checkbox v-model="dataDialog.save" class="mr-12">
-              同时保存为我的数据点列表
+              同时保存为我的变量列表
             </t-checkbox>
             <t-button @click="onOkDataset()">保存</t-button>
           </template>
           <template v-else-if="dataDialog.editDataset === 2">
             <div class="flex-grow"></div>
-            <t-button @click="onOkDataset(true)"> 另存为新数据点列表 </t-button>
+            <t-button @click="onOkDataset(true)"> 另存为新变量列表 </t-button>
             <t-button @click="onOkDataset()">保存</t-button>
           </template>
           <template v-else>
-            <a
+            <!-- <a
               v-if="dataDialog.dataset && dataDialog.dataset.id"
               @click="editDataset"
             >
-              编辑当前数据点列表
-            </a>
+              编辑当前变量列表
+            </a> -->
+            <t-checkbox
+              style="display: inline;"
+              v-model="dataDialog.enableMock"
+              @change="onChangeMock">
+              开启模拟数据
+            </t-checkbox>
             <div class="flex-grow"></div>
-
-            <t-button @click="dataDialog.show = false"> 完成 </t-button>
+            <t-button @click="downloadAsJson" variant="outline"  theme="primary"> 
+              <template #icon><FileIcon /></template>
+              下载为JSON 
+            </t-button>
+            <t-button @click="downloadAsExcel" variant="outline"  theme="primary"> 
+              <template #icon><FileExcelIcon /></template>
+              下载为Excel 
+            </t-button>
+            <t-button @click="onOkDataset()"> 保存为我的变量列表 </t-button>
+
+            <!-- <t-button @click="dataDialog.show = false"> 完成 </t-button> -->
           </template>
         </div>
         <div v-else class="flex middle">
@@ -789,6 +845,70 @@
 
     <!-- 分享弹窗 -->
     <ShareModal  :shared="shared" v-model:visible="shareVisible" />
+    <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.device" placeholder="设备名称" />
+      </div> -->
+      <div class="form-item mt-16">
+        <label>显示名称</label>
+        <t-input
+          @change="changeDataLabel($event)"
+          :value="addDataDialog.data.label"
+          placeholder="变量简短描述"
+        />
+      </div>
+      <div class="form-item mt-16">
+        <label>变量名</label>
+        <t-input
+          @change="changeDataID($event)"
+          :value="addDataDialog.data.id"
+          placeholder="变量名"
+        />
+      </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="w-full">
+          <t-input v-model="addDataDialog.data.mock" placeholder="值范围" />
+          <h6 class="desc mt-8">值范围说明</h6>
+          <ul class="desc ml-16">
+            <li>
+              <label class="inline" style="width: 80px">固定值:</label>
+              直接填写,例如:10
+            </li>
+            <li>
+              <label class="inline" style="width: 80px">随机值:</label>
+              {值1,值2,...}。例如:{1,2,3,4,5}
+            </li>
+            <li>
+              <label class="inline" style="width: 80px">范围数字:</label>
+              最小值-最大值。例如:0-1.0 或 0-100
+            </li>
+            <li>
+              <label class="inline" style="width: 80px">随机字符串:</label>
+              [长度]。例如:[8]
+            </li>
+          </ul>
+        </div>
+      </div>
+    </t-dialog>
   </div>
 </template>
 
@@ -839,8 +959,10 @@ import ContextMenu from './ContextMenu.vue';
 import Network from './Network.vue';
 import Dataset from './Dataset.vue';
 import ChargeCloudPublish from './ChargeCloudPublish.vue';
-import { AddIcon,SaveIcon, RootListIcon,SlashIcon, RefreshIcon, ServerIcon, CaretRightIcon, ShareIcon, QrcodeIcon, CloudIcon, DeleteIcon, SearchIcon, RollbackIcon, LaptopIcon, StopCircleIcon , PlayCircleIcon, PlayCircleStrokeIcon, LayersIcon, FullscreenExitIcon } from 'tdesign-icons-vue-next';
+import { AddIcon, EditIcon, SaveIcon, RootListIcon,SlashIcon, RefreshIcon, ServerIcon, CaretRightIcon, ShareIcon, QrcodeIcon, CloudIcon, DeleteIcon, SearchIcon, RollbackIcon, LaptopIcon, StopCircleIcon , PlayCircleIcon, PlayCircleStrokeIcon, LayersIcon, FullscreenExitIcon, FileIcon, FileExcelIcon, CloudDownloadIcon } from 'tdesign-icons-vue-next';
 import {transformData} from '@/services/utils';
+import { importExcel, saveAsExcel } from '@/services/excel';
+import { typeOptions } from '@/services/common';
 
 const router = useRouter();
 const route = useRoute();
@@ -1365,9 +1487,12 @@ const onShowDataDialog = async () => {
   dataDialog.editNetwork = false;
   dataDialog.save = true;
   dataDialog.show = true;
-  getNetworks();
+  if(!dataDialog.datasetType){
+    dataDialog.datasetType = 1;
+  }
+  await getNetworks();
   await getDatasets();
-  onSelDataset(true);
+  // onSelDataset(true);
 };
 
 const onFinishDataDialog = () => {
@@ -1561,19 +1686,19 @@ const onOkNetwork = async () => {
 };
 
 const datasetColumns = ref([
-  {
-    colKey: 'device',
-    title: '设备',
-    ellipsis: true,
-  },
+  // {
+  //   colKey: 'device',
+  //   title: '设备',
+  //   ellipsis: true,
+  // },
   {
     colKey: 'label',
-    title: '数据点名称',
+    title: '显示名称',
     ellipsis: true,
   },
   {
     colKey: 'id',
-    title: '数据点ID',
+    title: '变量名',
     ellipsis: true,
   },
   {
@@ -1586,8 +1711,176 @@ const datasetColumns = ref([
     title: '值范围',
     ellipsis: true,
   },
+  {
+    colKey: 'actions',
+    title: '操作',
+    width: 100,
+  },
 ]);
 
+const importDataset = async () => {
+  let columns: any = [
+    // {
+    //   header: '设备',
+    //   key: 'device',
+    // },
+    {
+      header: '显示名称',
+      key: 'label',
+    },
+    {
+      header: '变量名',
+      key: 'id',
+    },
+    {
+      header: '类型',
+      key: 'type',
+    },
+    {
+      header: '值范围',
+      key: 'mock',
+    },
+  ];
+  const data: any = await importExcel(columns);
+  // dataDialog.dataset.devices = data;
+  mergeDataset(dataDialog.dataset, data);
+  // modelValue.devices = data;
+  // emit('update:modelValue', modelValue);
+  // emit('change', modelValue);
+};
+
+const getDatas = async () => {
+  if (!dataDialog.dataset.url) {
+    return;
+  }
+  const ret = await axios.get(dataDialog.dataset.url);
+  let flattenRet = flattenTree(ret);
+  if (flattenRet) {
+    // dataDialog.dataset.devices = flattenRet;
+    mergeDataset(dataDialog.dataset,flattenRet);
+  }
+};
+
+//展开树
+const flattenTree = (root) =>{
+  if (!root) return []; // 空树返回空数组
+
+  const result = []; // 存储展开后的数组
+
+  // 递归遍历树
+  function dfs(node) {
+    result.push({
+      id: node.id,
+      label: node.label||node.name,
+      device: node.device||node.id,
+      type: node.type,
+      mock: node.mock,
+      // children: node.children,
+    }); // 将当前节点值添加到结果数组
+    // 遍历当前节点的子节点
+    if(node.children){
+      for (let child of node.children) {
+        dfs(child); // 递归调用DFS
+      }
+    }
+  }
+  root.forEach((item) => {
+    dfs(item);
+  });
+  // dfs(root); // 从根节点开始遍历
+
+  return result;
+}
+
+const addDataDialog = reactive<any>({});
+
+const showAddData = (row?: any, index?: number) => {
+  if (row) {
+    addDataDialog.header = '编辑数据';
+    addDataDialog.data = JSON.parse(JSON.stringify(row));
+    addDataDialog.index = index;
+  } else {
+    addDataDialog.header = '添加数据';
+    addDataDialog.data = { type: 'string' };
+  }
+
+  addDataDialog.show = true;
+};
+
+const clearData = () => {
+  dataDialog.datasetId = undefined;
+  dataDialog.dataset.id = undefined;
+  dataDialog.dataset.name = undefined;
+  dataDialog.dataset.url = undefined;
+  dataDialog.dataset.devices = [];
+  setDot(true);
+}
+
+const closeDataDialog = () => {
+  (meta2d.store.data as any).dataset = dataDialog.dataset;
+  dataDialog.show = false;
+  meta2d.connectNetwork();
+}
+
+
+const onOkAddData = () => {
+  if (!addDataDialog.data.label) {
+    MessagePlugin.error('请填写名称');
+    return;
+  }
+  if (!addDataDialog.data.id) {
+    MessagePlugin.error('请填写数据ID');
+    return;
+  }
+  if (addDataDialog.header === '添加数据') {
+    dataDialog.dataset.devices.push(addDataDialog.data);
+  } else {
+    dataDialog.dataset.devices[addDataDialog.index] = addDataDialog.data;
+
+    //更新所有绑定该id的pen label
+    let binds = meta2d.store.bind[addDataDialog.data.id];
+    if (binds) {
+      binds.forEach((item) => {
+        const pen: Pen = meta2d.findOne(item.id);
+        pen.realTimes &&
+          pen.realTimes.forEach((_realTime) => {
+            if (_realTime.key === item.key) {
+              _realTime.bind.label = addDataDialog.data.label;
+            }
+          });
+      });
+    }
+  }
+
+  addDataDialog.show = false;
+};
+
+const changeDataID = (value) => {
+  if (!value) {
+    MessagePlugin.error('变量名不能为空!');
+    return;
+  }
+  let item =  dataDialog.dataset.devices.filter((item) => item.id === value);
+  if (item && item.length) {
+    MessagePlugin.error('变量名重复!');
+    return;
+  }
+  addDataDialog.data.id = value;
+};
+
+const changeDataLabel = (value) => {
+  if (!value) {
+    MessagePlugin.error('显示名称不能为空!');
+    return;
+  }
+  let item =  dataDialog.dataset.devices.filter((item) => item.label === value);
+  if (item && item.length) {
+    MessagePlugin.error('显示名称重复!');
+    return;
+  }
+  addDataDialog.data.label = value;
+};
+
 const addDataset = () => {
   dataDialog.dataset = {
     name: '',
@@ -1615,17 +1908,23 @@ const backDataset = () => {
 };
 
 const onOkDataset = async (saveas = false) => {
-  if (!dataDialog.dataset.name) {
-    MessagePlugin.error('名称不能为空');
+  // if (!dataDialog.dataset.name) {
+  //   MessagePlugin.error('名称不能为空');
+  //   return;
+  // }
+  if(!(dataDialog.dataset.devices&&dataDialog.dataset.devices.length)){
+    MessagePlugin.error('变量列表不能为空');
     return;
   }
-
   const dataset = JSON.parse(JSON.stringify(dataDialog.dataset));
   let data = dataset;
   data.type = 'dataset';
+  if(!data.name){
+    data.name = meta2d.store.data.name;
+  }
   data = transformData(dataset,'toNetwork');
   // 保存到我的数据源
-  if (saveas || dataDialog.editDataset === 1) {
+  if (saveas || !dataset.id) {
     delete dataset.id;
     delete dataset._id;
     dataset.type = 'dataset';
@@ -1651,14 +1950,58 @@ const onOkDataset = async (saveas = false) => {
   }
   delete dataset.devices;
   // @ts-ignore
-  meta2d.store.data.dataset = dataset;
+  // console.log("dataset",dataset);
+  // meta2d.store.data.dataset = dataset;
 
-  setDot(true);
+  // setDot(true);
 
   dataDialog.editDataset = false;
   delete dataDialog.datesetBak;
 };
 
+const downloadAsExcel = () => {
+  if(!(dataDialog.dataset.devices&&dataDialog.dataset.devices.length)){
+    MessagePlugin.error('变量列表不能为空!');
+    return;
+  }
+  const name = meta2d.store.data.name;
+  const columns:any[] = [{
+    key: 'label',
+    header: '显示名称',
+  },
+  {
+    key: 'id',
+    header: '变量名',
+  },
+  {
+    key: 'type',
+    header: '类型',
+  },
+  {
+    key: 'mock',
+    header: '值范围',
+  }];
+  saveAsExcel(name,columns,dataDialog.dataset.devices);
+}
+
+const downloadAsJson = () => {
+  if(!(dataDialog.dataset.devices&&dataDialog.dataset.devices.length)){
+    MessagePlugin.error('变量列表不能为空!');
+    return;
+  }
+  import('file-saver').then(({ saveAs }) => {
+    saveAs(
+      new Blob(
+        [JSON.stringify(dataDialog.dataset)],
+        {
+          type: 'text/plain;charset=utf-8',
+        }
+      ),
+      `${dataDialog.dataset.name || '未命名'}.json`
+    );
+  });
+}
+
 const onDelDataset = async (item: any, i: number) => {
   const ret: any = await axios.post(`/api/data/datasource/delete`, {
     id: item.id,
@@ -1700,8 +2043,8 @@ const onSelDataset = async (init = false) => {
         Object.assign(dataset,{...ret.data});
       }
     }
-
-    dataDialog.dataset = dataset;
+    mergeDataset(dataDialog.dataset,dataset.devices);
+    // dataDialog.dataset = dataset;
 
     if (!init) {
       const d = JSON.parse(JSON.stringify(dataset));
@@ -1714,6 +2057,23 @@ const onSelDataset = async (init = false) => {
   }
 };
 
+const mergeDataset = (arr1:any,arr2:any[]) => {
+  if(!(arr2&&arr2.length)){
+    return;
+  }
+  if(!arr1.devices){
+    arr1.devices = [];
+  }
+  arr2.forEach((item)=>{
+    let index = arr1.devices.findIndex((elem)=>elem.id === item.id);
+    if(index>=0){
+      Object.assign(arr1.devices[index],item); 
+    }else{
+      arr1.devices.push(item);
+    }
+  });
+}
+
 const share = async () => {
   if (!(user && user.id)) {
     MessagePlugin.warning('请先登录!');
@@ -1950,4 +2310,10 @@ const theme = computed(() => {
     }
   }
 }
+
+.select-options{
+  .t-popup__content{
+    /* width: 240px !important; */
+  }
+}
 </style>