Pārlūkot izejas kodu

perfect_view_数据源管理

ananzhusen 2 gadi atpakaļ
vecāks
revīzija
702ce64561
4 mainītis faili ar 676 papildinājumiem un 304 dzēšanām
  1. 1 0
      package.json
  2. 274 266
      pnpm-lock.yaml
  3. 66 0
      src/services/excel.ts
  4. 335 38
      src/views/components/View.vue

+ 1 - 0
package.json

@@ -11,6 +11,7 @@
   "dependencies": {
     "axios": "^0.26.0",
     "dayjs": "^1.11.5",
+    "exceljs": "^4.3.0",
     "fast-xml-parser": "^4.0.1",
     "file-saver": "^2.0.5",
     "jszip": "^3.10.0",

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 274 - 266
pnpm-lock.yaml


+ 66 - 0
src/services/excel.ts

@@ -0,0 +1,66 @@
+import ExcelJS from "exceljs";
+import { saveAs } from "file-saver";
+import { MessagePlugin } from "tdesign-vue-next";
+
+export function importExcel() {
+  const input = document.createElement("input");
+  input.type = "file";
+  input.accept =
+    ".csv, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel,application/zip";
+  input.onchange = (event) => {
+    const elem: any = event.target;
+
+    if (elem.files && elem.files[0].name.indexOf(".xlsx") > 0) {
+      const workbook = new ExcelJS.Workbook();
+      const reader = new FileReader();
+      reader.readAsArrayBuffer(elem.files[0]);
+      reader.onload = async () => {
+        const buffer: any = reader.result;
+        await workbook.xlsx.load(buffer);
+        // 默认只解析第一个sheet
+        const worksheet = workbook.worksheets[0];
+        // 获取sheet1的所有rows
+        const rows = worksheet.getSheetValues();
+        if (rows.length === 0) {
+          MessagePlugin.warning("导入的excel文件不可为空!");
+          return;
+        }
+        let dataArry = [];
+        worksheet.eachRow((row, rowNumber) => {
+          if (rowNumber === 1) {
+          }
+          row.eachCell((cell, colNumber) => {
+            console.log("cell", cell, colNumber);
+          });
+        });
+        MessagePlugin.success("导入成功!");
+      };
+    }
+  };
+  input.click();
+}
+
+export function saveAsExcel(
+  name: string,
+  columns: { header: string; key: string }[],
+  data: any[]
+) {
+  const fileName = `${name}.xlsx`;
+  const workbook = new ExcelJS.Workbook();
+  // workbook.creator = 'Me';
+  // workbook.lastModifiedBy = 'Her';
+  workbook.created = new Date();
+  // workbook.modified = new Date();
+  // workbook.lastPrinted = new Date();
+  const worksheet = workbook.addWorksheet(`${name}`);
+  worksheet.columns = columns;
+
+  worksheet.addRows(data);
+  workbook.xlsx.writeBuffer().then((data) => {
+    const blob = new Blob([data], {
+      type: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=UTF-8",
+    });
+    saveAs(blob, fileName);
+    MessagePlugin.success("导出成功!");
+  });
+}

+ 335 - 38
src/views/components/View.vue

@@ -282,11 +282,11 @@
                         <li
                           style="line-height: 14px; margin: 8px 4px"
                           v-for="item in comOptions"
-                          :key="item.addr"
+                          :key="item.url"
                           @click="() => onOptionClick(item)"
                         >
                           名称: {{ item.name }}<br />
-                          地址: {{ item.addr }}
+                          地址: {{ item.url }}
                         </li>
                       </ul>
                     </template>
@@ -294,7 +294,9 @@
                       <t-icon name="search"></t-icon
                     ></template>
                   </t-select-input>
-                  <t-button style="height: 30px">添加数据源</t-button>
+                  <t-button style="height: 30px" @click="addCom"
+                    >添加数据源</t-button
+                  >
                 </t-space>
               </t-row>
               <t-table
@@ -316,29 +318,133 @@
                   </t-popconfirm>
                 </template>
               </t-table>
-              <!-- <p style="padding: 25px">选项卡1的内容,使用 t-tab-panel 渲染</p> -->
             </div>
             <div v-show="!comShow">
-              <t-button theme="primary" variant="text" ghost @click="comBack">
-                <template #icon>
-                  <t-icon name="rollback"></t-icon>
-                </template>
-                返回
-              </t-button>
-              <div class="form-item">
+              <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" class="ml-8" style="width: 200px" />
+                <t-input v-model="com.name" style="width: 200px" />
               </div>
+
               <div class="form-item">
                 <label>连接类型</label>
-                <t-input v-model="com.type" class="ml-8" style="width: 200px" />
-              </div>
-              <div class="form-item">
-                <label>接口地址</label>
-                <t-input v-model="com.addr" class="ml-8" style="width: 200px" />
+                <t-select
+                  v-model="com.type"
+                  placeholder="请选择"
+                  style="width: 200px"
+                  :popup-props="{ overlayInnerStyle: { width: '200px' } }"
+                  :disabled="comType === 'edit'"
+                  @change="comTypeChange"
+                >
+                  <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>
-              <t-button theme="primary">仅当前页面使用</t-button>
-              <t-button theme="primary">保存到服务器</t-button>
+              <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>
@@ -356,10 +462,16 @@
             <t-row class="mt-8">
               <t-col flex="60px">自定义</t-col>
               <t-col flex="auto">
-                <t-button class="ml-8" style="height: 30px"
+                <t-button
+                  @click="importDataSet"
+                  class="ml-8"
+                  style="height: 30px"
                   >从Excel导入</t-button
                 >
-                名称(数据点ID 类型 值
+                <t-button @click="downloadDataSet" variant="text">
+                  <template #icon><t-icon name="download"></t-icon> </template>
+                  下载示例
+                </t-button>
               </t-col>
             </t-row>
             <t-table
@@ -409,6 +521,8 @@ import { checkData, Meta2dBackData } from "@/services/utils";
 import { cdn } from "@/services/api";
 import dayjs from "dayjs";
 import ContextMenu from "./ContextMenu.vue";
+import { importExcel, saveAsExcel } from "@/services/excel";
+import { it } from "node:test";
 
 const router = useRouter();
 const route = useRoute();
@@ -848,24 +962,47 @@ const changeContextMenuVisible = (e: boolean) => {
 interface Com {
   name?: string;
   type: "mqtt" | "websocket" | "http";
-  addr: string;
+  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",
-    addr: "wss://1",
+    url: "wss://1",
+    topics: "",
+    options: {
+      clientId: "",
+      username: "",
+      password: "",
+      customClientId: false,
+    },
   },
   {
     name: "连接2",
-    type: "mqtt",
-    addr: "wss://2",
+    type: "websocket",
+    url: "wss://2",
   },
   {
     name: "连接3",
-    type: "mqtt",
-    addr: "wss://3",
+    type: "http",
+    url: "wss://3",
   },
 ]);
 const comColumns = ref([
@@ -891,27 +1028,27 @@ const comOptions = ref<Com[]>([
   {
     name: "连接A",
     type: "mqtt",
-    addr: "ws://A",
+    url: "ws://A",
   },
   {
     name: "连接B",
     type: "mqtt",
-    addr: "ws://B",
+    url: "ws://B",
   },
   {
     name: "连接C",
     type: "mqtt",
-    addr: "ws://C",
+    url: "ws://C",
   },
   {
     name: "连接D",
     type: "mqtt",
-    addr: "ws://D",
+    url: "ws://D",
   },
   {
     name: "连接E",
     type: "mqtt",
-    addr: "ws://E",
+    url: "ws://E",
   },
 ]);
 
@@ -921,19 +1058,79 @@ const onOptionClick = (item: Com) => {
 };
 
 const popupVisible = ref(false);
-const onInputChange = () => {};
+const onInputChange = (e) => {
+  console.log("change", e);
+};
 const onPopupVisibleChange = (val: boolean) => {
   popupVisible.value = val;
 };
+
+const comType = ref("add"); //or edit
+
+const addCom = () => {
+  comType.value = "add";
+  com.value = {
+    name: "",
+    type: "mqtt", //默认
+    url: "",
+    options: {
+      clientId: "",
+      username: "",
+      password: "",
+      customClientId: false,
+    },
+  };
+  comShow.value = false;
+};
 const editCom = (row: Com) => {
+  comType.value = "edit";
   com.value = row;
   comShow.value = false;
 };
 
 const deleteCom = (index: number) => {
   comData.value.splice(index, 1);
+  dataToCom(comData.value);
 };
 
+//将配置的通信列表转成核心库能识别的格式
+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 建立通信连接
+  meta2d.connectSocket();
+};
 const comShow = ref(true);
 
 const comBack = () => {
@@ -943,9 +1140,47 @@ const comBack = () => {
 const com = ref<Com>({
   name: "连接E",
   type: "mqtt",
-  addr: "ws://E",
+  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 comHttpMethodChange = (e: string) => {
+  if (e === "GET") {
+    com.value.body = null;
+  }
+};
+
 const dsData = ref([]);
 
 const dsColumns = ref([
@@ -965,6 +1200,51 @@ const dsColumns = ref([
     ellipsis: true,
   },
 ]);
+
+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 = () => {
+  importExcel();
+};
+
+const downloadDataSet = () => {
+  let name = "数据集示例";
+  let columns = [
+    {
+      header: "名称",
+      key: "name",
+    },
+    {
+      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);
+};
 </script>
 <style lang="postcss" scoped>
 .meta2d {
@@ -1016,11 +1296,24 @@ const dsColumns = ref([
 </style>
 
 <style lang="postcss">
-
 .t-dialog {
-  .form-item{
-  display: flex;
-}
+  .t-dialog__body {
+    height: 450px;
+    /* overflow: auto; */
+  }
+
+  .form-item {
+    display: flex;
+    margin-bottom: 8px;
+
+    label {
+      width: 100px;
+    }
+  }
+  .t-textarea {
+    width: calc(100% - 150px);
+  }
+
   .vip-label {
     font-size: 10px;
     background-color: #ff400030;
@@ -1029,5 +1322,9 @@ const dsColumns = ref([
     margin-left: 4px;
     border-radius: 2px;
   }
+  .t-tab-panel {
+    overflow: auto;
+    height: 395px;
+  }
 }
 </style>

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels