Răsfoiți Sursa

perf(views): 优化“网关列表” 操作逻辑

wangshun 2 luni în urmă
părinte
comite
ef21686d7f
2 a modificat fișierele cu 273 adăugiri și 53 ștergeri
  1. 10 3
      src/i18n/locales/zh.json
  2. 263 50
      src/views/gateway-list/GatewayList.vue

+ 10 - 3
src/i18n/locales/zh.json

@@ -4,6 +4,7 @@
     "addNew": "新增",
     "advancedSettings": "高级设置",
     "aiCtrl": "AI智控",
+    "all": "所有",
     "basicInfo": "基础信息",
     "binding": "绑定",
     "cancel": "取消",
@@ -32,12 +33,14 @@
     "query": "查询",
     "reLogin": "你已被登出,请重新登录",
     "requestTimedOut": "请求超时",
+    "reset": "重置",
     "return": "返回",
     "revise": "修改",
     "save": "保存",
     "search": "搜索",
     "selected": "已选",
-    "serialNumber": "序列号",
+    "sequenceNumber": "序列号",
+    "serialNumber": "序号",
     "settings": "设置",
     "skip": "跳过",
     "status": "状态",
@@ -159,6 +162,7 @@
     "addInterface": "添加接口",
     "addStation": "添加从站",
     "advancedSettingsAgreement": "协议高级设置",
+    "agreement": "协议",
     "agreementNumber": "协议号",
     "associatedEquipment": "关联设备",
     "bindingAgreement": "绑定协议",
@@ -167,6 +171,7 @@
     "deleteInterface": "是否删除接口?",
     "devicePassword": "设备密码",
     "electricMeter": "电表",
+    "fromStationNumber": "从站号",
     "gatewayConfiguration": "网关配置",
     "gatewayLog": "网关日志",
     "highDataFrequencyReportingFrequency": " 高频数据上报频率",
@@ -176,6 +181,7 @@
     "monitoringResults": "监测结果",
     "nextDelayExtraction": "下一条数据读取延时",
     "nextRoundDelayExtraction": "下一轮数据读取延时",
+    "onlineStatus": "在线状态",
     "platformAgreement": "平台协议",
     "pleasePassword": "请输入密码",
     "pleaseProtocolName": "请输入协议名称",
@@ -195,10 +201,11 @@
     "selectProtocolParameters": "选择协议参数",
     "snCode": "SN码",
     "station": "从站",
-    "stationNumber": "站号",
+    "stationNumber": "站号",
     "tip": "在设备铭牌上可以找到SN码和设备密码",
     "verificationAgreement": "协议验证",
-    "verificationEquipment": "验证设备"
+    "verificationEquipment": "验证设备",
+    "verificationSuccessful": "验证成功"
   },
   "setupProtocol": {
     "addCustomParams": "新增自定义参数",

+ 263 - 50
src/views/gateway-list/GatewayList.vue

@@ -1,58 +1,95 @@
 <script setup lang="ts">
-import { onMounted, ref } from 'vue';
+import { onMounted, ref, useTemplateRef } from 'vue';
 
+import ConfirmModal from '@/components/ConfirmModal.vue';
+import SvgIcon from '@/components/SvgIcon.vue';
 import { useRequest } from '@/hooks/request';
-import { gatewayLinkGetList, gatewayLinkList, gatewayList } from '@/api';
-
-import type { GatewayListItem, GatewayQuery, InterfaceData, VerificationAgreement } from '@/types';
+import { t } from '@/i18n';
+import {
+  addGatewayLinkBatchUpdate,
+  gatewayLinkGetList,
+  gatewayLinkList,
+  gatewayList,
+  obtainListInterfaces,
+  obtainListPhysicalInterfaces,
+  orgGatewayUnregister,
+} from '@/api';
+
+import type { DefaultOptionType, SelectValue } from 'ant-design-vue/es/select';
+import type {
+  BatchUpdate,
+  GatewayListItem,
+  GatewayQuery,
+  InterfaceData,
+  LinkProtocolType,
+  ListInterfaces,
+  ListPhysicalInterfaces,
+  VerificationAgreement,
+} from '@/types';
 
 const gatewayColumns = [
   {
-    title: '序号',
+    title: t('common.serialNumber'),
     dataIndex: 'index',
     key: 'index',
   },
   {
-    title: '序列号',
+    title: t('common.sequenceNumber'),
     dataIndex: 'snCode',
     key: 'snCode',
     ellipsis: true,
   },
   {
-    title: '型号',
+    title: t('createDevice.modelNumber'),
     dataIndex: 'modelName',
     key: 'modelName',
     ellipsis: true,
   },
   {
-    title: '在线状态',
+    title: t('registerGateway.onlineStatus'),
     dataIndex: 'state',
     key: 'state',
     ellipsis: true,
   },
+  {
+    title: t('common.operation'),
+    dataIndex: 'action',
+    key: 'action',
+    width: 80,
+  },
 ];
 
 const agreementColumns = [
   {
-    title: '站号',
+    title: t('registerGateway.stationNumber'),
     dataIndex: 'station',
     key: 'station',
+    ellipsis: true,
   },
   {
-    title: '协议',
+    title: t('registerGateway.agreement'),
     dataIndex: 'protocolName',
     key: 'protocolName',
+    ellipsis: true,
   },
   {
-    title: '关联设备',
-    dataIndex: 'dev',
-    key: 'dev',
+    title: t('registerGateway.associatedEquipment'),
+    dataIndex: 'deviceName',
+    key: 'deviceName',
+    ellipsis: true,
   },
 ];
 
 const gatewayData = ref<GatewayListItem[]>([]);
 const interfaceList = ref<InterfaceData[]>([]);
 const agreementList = ref<VerificationAgreement[]>([]);
+const gatewayId = ref<number>();
+const gatewayEditor = ref<boolean>(false);
+const interfaceSelectList = ref<ListInterfaces[]>([]);
+const listPhysicalInterfaces = ref<ListPhysicalInterfaces[]>([]);
+const gatewayLinks = ref<BatchUpdate[]>([]);
+const data: LinkProtocolType[] = [];
+
 const activeKey = ref();
 const gatewayQuery = ref<GatewayQuery>({
   pageIndex: 1,
@@ -62,40 +99,136 @@ const gatewayQuery = ref<GatewayQuery>({
   total: 0,
 });
 
+const modalComponentRef = useTemplateRef('modalComponent');
+
 const { handleRequest } = useRequest();
-const addGatewayList = () => {
+
+const getGatewayList = () => {
   handleRequest(async () => {
     const { records, total } = await gatewayList(gatewayQuery.value);
+    gatewayData.value = records;
+    gatewayQuery.value.total = total;
     if (records.length) {
-      gatewayData.value = records;
-      gatewayQuery.value.total = total;
+      getObtainListInterfaces(gatewayData.value[0].modelId);
       postLinkGetList(gatewayData.value[0].id);
     }
   });
 };
 
+const addGatewayList = () => {
+  gatewayQuery.value.pageIndex = 1;
+  getGatewayList();
+};
+
+const addReset = () => {
+  gatewayQuery.value.searchContent = '';
+  gatewayQuery.value.state = -1;
+};
+
 const rowClick = (record: GatewayListItem) => {
   return {
     onClick: () => {
-      console.log(record);
-      console.log(123);
+      gatewayId.value = record.id;
+      getObtainListInterfaces(record.modelId);
       postLinkGetList(record.id);
     },
   };
 };
 
+const getObtainListInterfaces = (id: number) => {
+  handleRequest(async () => {
+    interfaceSelectList.value = await obtainListInterfaces(id);
+  });
+};
+
+const getObtainListPhysicalInterfaces = (id: number, value?: InterfaceData) => {
+  handleRequest(async () => {
+    console.log(id, value);
+    listPhysicalInterfaces.value = await obtainListPhysicalInterfaces(id);
+    if (value) {
+      if (listPhysicalInterfaces.value.length) {
+        value.protocolType = listPhysicalInterfaces.value[0].protocolName;
+      } else {
+        value.protocolType = '';
+      }
+    }
+  });
+};
+
+const choosePhysicalInterface = (
+  selectValue: SelectValue,
+  option: DefaultOptionType,
+  id: number,
+  value: InterfaceData,
+  index: number,
+) => {
+  interfaceList.value[index].interfaceType = option.interfaceType;
+  interfaceList.value[index].linkName = option.name;
+
+  getObtainListPhysicalInterfaces(id, value);
+};
+
 const postLinkGetList = (id: number) => {
   handleRequest(async () => {
     interfaceList.value = await gatewayLinkGetList(id);
     if (interfaceList.value.length) {
       activeKey.value = interfaceList.value[0].id;
       agreementList.value = await gatewayLinkList(interfaceList.value[0].id);
+      getObtainListPhysicalInterfaces(interfaceList.value[0].interfaceId);
     }
   });
 };
 
 const switchPages = () => {
-  addGatewayList();
+  getGatewayList();
+};
+
+const refreshData = () => {
+  gatewayQuery.value.pageIndex = 1;
+  getGatewayList();
+};
+
+const confirm = () => {
+  handleRequest(async () => {
+    if (gatewayId.value) {
+      await orgGatewayUnregister(gatewayId.value);
+      modalComponentRef.value?.hideView();
+      getGatewayList();
+    }
+  });
+};
+
+const addGatewayDelete = (id: number) => {
+  gatewayId.value = id;
+  modalComponentRef.value?.showView();
+};
+
+const addSave = () => {
+  handleRequest(async () => {
+    interfaceList.value.forEach((item) => {
+      agreementList.value.forEach((item) => {
+        const { protocolId, station, isStandard, dataSendInterval, highFreqSendInterval, id, linkId } = item;
+        data.push({
+          id,
+          linkId,
+          protocolId,
+          station,
+          isStandard: String(isStandard),
+          dataSendInterval,
+          highFreqSendInterval,
+        });
+      });
+
+      gatewayLinks.value.push({
+        link: item,
+        protocols: data,
+      });
+    });
+    console.log(gatewayLinks.value);
+    await addGatewayLinkBatchUpdate(gatewayLinks.value);
+
+    gatewayEditor.value = false;
+  });
 };
 
 onMounted(() => {
@@ -106,19 +239,12 @@ onMounted(() => {
 <template>
   <div>
     <AFlex justify="space-between">
-      <div class="text-top">设备管理</div>
+      <div class="text-top">{{ $t('navigation.deviceManage') }}</div>
       <div>
-        <AButton class="deletion-button icon-button default-button">
-          <AFlex align="center">
-            <SvgIcon name="delete" />
-
-            <span> 删除 </span>
-          </AFlex>
-        </AButton>
         <AButton type="primary" class="icon-button">
           <AFlex align="center">
             <SvgIcon name="plus" />
-            <span> 添加 </span>
+            <span> {{ $t('common.add') }} </span>
           </AFlex>
         </AButton>
       </div>
@@ -128,29 +254,33 @@ onMounted(() => {
       <ARow>
         <ACol :span="12" class="gateway-left">
           <AFlex justify="space-between" align="center" class="gateway-left-top">
-            <div class="gateway-left-top-text">网关列表</div>
-            <AButton type="text" class="icon-button gateway-left-top-icon">
+            <div class="gateway-left-top-text">{{ $t('navigation.gatewayList') }}</div>
+            <AButton type="text" @click="refreshData" class="icon-button gateway-left-top-icon">
               <AFlex align="center">
                 <SvgIcon name="refresh-o" />
-                <span> 刷新数据 </span>
+                <span> {{ $t('registerGateway.refreshData') }} </span>
               </AFlex>
             </AButton>
           </AFlex>
 
           <AFlex justify="space-between" align="center" class="input-bottom">
             <div>
-              <span class="gateway-left-text">搜索</span>
+              <span class="gateway-left-text">{{ $t('common.search') }}</span>
               <AInput v-model:value="gatewayQuery.searchContent" placeholder="请输入序列号、型号" class="input-width" />
             </div>
             <div>
-              <span class="gateway-left-text">状态</span>
-              <ASelect placeholder="请选择" class="select-width" />
+              <span class="gateway-left-text">{{ $t('common.status') }}</span>
+              <ASelect class="select-width" v-model:value="gatewayQuery.state" placeholder="请选择" :allow-clear="true">
+                <ASelectOption :value="-1">{{ $t('common.all') }}</ASelectOption>
+                <ASelectOption :value="1">{{ $t('common.online') }}</ASelectOption>
+                <ASelectOption :value="0">{{ $t('common.offline') }}</ASelectOption>
+              </ASelect>
             </div>
           </AFlex>
 
           <AFlex justify="flex-end">
-            <AButton type="primary" class="query-button" @click="addGatewayList">查询</AButton>
-            <AButton class="default-button">重置</AButton>
+            <AButton type="primary" class="query-button" @click="addGatewayList">{{ $t('common.query') }}</AButton>
+            <AButton @click="addReset" class="default-button">{{ $t('common.reset') }}</AButton>
           </AFlex>
 
           <ATable
@@ -165,7 +295,10 @@ onMounted(() => {
                 {{ index + 1 }}
               </template>
               <template v-else-if="column.key === 'state'">
-                {{ record.state === 0 ? '离线' : '在线' }}
+                {{ record.state === 0 ? $t('common.offline') : $t('common.online') }}
+              </template>
+              <template v-else-if="column.key === 'action'">
+                <SvgIcon @click="addGatewayDelete(record.id)" class="icon-delete" name="delete" />
               </template>
             </template>
           </ATable>
@@ -178,42 +311,122 @@ onMounted(() => {
               :show-size-changer="true"
               @change="switchPages"
               show-quick-jumper
-              :show-total="(total) => `共有${total}条`"
+              :show-total="(total) => $t('common.pageTotal', { total })"
             />
           </AFlex>
         </ACol>
         <ACol :span="12" class="gateway-right">
           <AFlex justify="space-between" align="center" class="gateway-right-top">
             <div class="gateway-left-top-text">网关配置</div>
-            <AButton type="text" class="icon-button gateway-left-top-icon">
-              <AFlex align="center">
-                <SvgIcon name="edit-o" />
-                <span> 编辑 </span>
-              </AFlex>
-            </AButton>
+            <div v-if="gatewayEditor">
+              <AButton
+                v-if="gatewayEditor"
+                type="text"
+                class="icon-button gateway-left-top-icon"
+                @click="gatewayEditor = false"
+              >
+                <AFlex align="center">
+                  <SvgIcon name="close" />
+                  <span> 取消 </span>
+                </AFlex>
+              </AButton>
+              <AButton v-if="gatewayEditor" type="text" class="icon-button gateway-left-top-icon" @click="addSave">
+                <AFlex align="center">
+                  <SvgIcon name="edit-o" />
+                  <span> 保存 </span>
+                </AFlex>
+              </AButton>
+            </div>
+            <div v-else>
+              <AButton
+                v-if="!gatewayEditor"
+                type="text"
+                class="icon-button gateway-left-top-icon"
+                @click="gatewayEditor = true"
+              >
+                <AFlex align="center">
+                  <SvgIcon name="edit-o" />
+                  <span> 编辑 </span>
+                </AFlex>
+              </AButton>
+            </div>
           </AFlex>
-          <ACollapse v-model:active-key="activeKey" accordion v-if="interfaceList.length">
-            <ACollapsePanel v-for="item in interfaceList" :key="item.id">
+          <ACollapse v-model:active-key="activeKey" accordion v-if="interfaceList.length" collapsible="icon">
+            <ACollapsePanel v-for="(item, index) in interfaceList" :key="item.id">
               <template #header>
-                <span>物理接口 {{ item.linkName }}</span>
+                <span class="interface-text-right">物理接口: </span>
+                <span v-if="!gatewayEditor">{{ item.linkName }}</span>
+                <ASelect
+                  v-else
+                  v-model:value="item.interfaceId"
+                  class="interface-select"
+                  :options="interfaceSelectList"
+                  :field-names="{ label: 'name', value: 'id' }"
+                  @change="(value, option) => choosePhysicalInterface(value, option, item.interfaceId, item, index)"
+                />
               </template>
-              <div class="gateway-right-text">协议类型: {{ item.protocolType }}</div>
-              <ATable :columns="agreementColumns" :data-source="agreementList" :pagination="false" />
+              <AFlex align="center" class="gateway-type-bottom">
+                <div class="gateway-right-text">协议类型:</div>
+                <div>
+                  <span v-if="!gatewayEditor">{{ item.protocolType }}</span>
+                  <ASelect
+                    v-else
+                    v-model:value="item.protocolType"
+                    class="interface-select"
+                    :options="listPhysicalInterfaces"
+                    :field-names="{ label: 'protocolName', value: 'protocolName' }"
+                  />
+                </div>
+              </AFlex>
+
+              <ATable :columns="agreementColumns" :data-source="agreementList" :pagination="false">
+                <template #bodyCell="{ column, record }">
+                  <template v-if="column.key === 'station'">
+                    <AInputNumber v-if="gatewayEditor" v-model:value="record.station" :min="1" />
+                  </template>
+                </template>
+              </ATable>
             </ACollapsePanel>
           </ACollapse>
         </ACol>
       </ARow>
     </div>
+    <ConfirmModal
+      ref="modalComponent"
+      :title="'删除确定'"
+      :description-text="'是否确认删除?'"
+      :icon="{ name: 'delete' }"
+      :icon-bg-color="'#F56C6C'"
+      @confirm="confirm"
+    />
   </div>
 </template>
 
 <style lang="scss" scoped>
+.gateway-type-bottom {
+  margin-bottom: 12px;
+}
+
+.interface-text-right {
+  margin-right: 10px;
+}
+
+.interface-select {
+  width: 192px;
+}
+
+.icon-delete {
+  font-size: 21px;
+  color: var(--antd-color-primary-hover);
+  cursor: pointer;
+}
+
 .gateway-left-footer {
   margin-top: 24px;
 }
 
 .gateway-right-text {
-  margin-bottom: 16px;
+  margin-right: 10px;
   font-size: 14px;
   font-style: normal;
   font-weight: 500;