Browse Source

perf(views): "基本信息"步骤设备分组支持新增,编辑,删除功能

wangshun 1 tháng trước cách đây
mục cha
commit
fa34f33d07

+ 9 - 1
src/i18n/locales/zh.json

@@ -207,6 +207,7 @@
     "dataMonitoring": "数据监控",
     "detailedInformation": "详细信息",
     "deviceName": "设备名",
+    "editGroup": "编辑分组",
     "equipmentGroup": "设备组",
     "equipmentNumber": "设备编号",
     "equipmentParameterCoding": "设备参数编码",
@@ -232,6 +233,7 @@
     "matchParameters": "基于设备类型对应的设备能力及序号,匹配到对应的参数。",
     "maximumWorkingPressure": "最大工作压力(bar)",
     "modelNumber": "型号",
+    "newlyAddedGroup": "新增分组",
     "nominalVolumetric": "公称容积流量(m³/min)",
     "parameterEncodingEmpty": "设备参数编码不能为空!",
     "parameterEncodingRepeat": "设备参数编码存在重复项!",
@@ -240,21 +242,27 @@
     "physicalInterfaceEmpty": "物理接口不能为空!",
     "pleaseDeviceName": "请输入设备名称",
     "pleaseDeviceType": "请选择设备类型",
+    "pleaseEnterGroupName": "请输入分组名称!",
+    "pleaseEnterNameSecondaryGroup": "请输入二级分组名称!",
     "pleaseEnterSearchName": "清输入网关参数名称搜索",
-    "pleaseEquipmentGroup": "请选择设备组",
+    "pleaseEquipmentGroup": "请选择设备组",
     "pleasePhysicalInterface": "请选择物理接口",
+    "pleaseSelectEquipmentGroup": "请选择设备组",
+    "pleaseSelectSuperiorGroup": "请选择上级分组!",
     "quickMatching": "快速匹配",
     "ratedPower": "额定功率(KW)",
     "ratedWorkingPressure": "额定工作压力(bar)",
     "remoteControl": "远程控制",
     "remoteControlSelection": "远程控制:已选",
     "responsiblePerson": "责任人",
+    "secondaryGrouping": "二级分组",
     "selectGatewayParameters": "选择网关参数",
     "selectGatewayProtocolParameters": "选择网关协议参数",
     "selectParameters": "选择参数",
     "selectionMonitoringParameters": "数据监控:已选",
     "serviceLife": "使用年限  (年)",
     "suggestion": "建议按「设备名」「序号」的方式命名,比如:电表1",
+    "superiorGroup": "上级分组",
     "uninstallPower": "卸载功率(KW)",
     "usingDepartment": "使用部门",
     "verifyData": "验证数据",

+ 6 - 0
src/types/index.ts

@@ -942,6 +942,12 @@ export interface DeviceGroup {
   orgId: number;
 }
 
+export interface SubmitDeviceGroup {
+  id?: number;
+  groupName: string;
+  parentId?: number;
+}
+
 export interface DeviceParamGroup {
   id: number;
   deviceParamGroupName: string;

+ 213 - 12
src/views/create-device/BasicInformation.vue

@@ -1,19 +1,44 @@
 <script setup lang="ts">
-import { computed, onMounted, ref } from 'vue';
+import { computed, onMounted, ref, useTemplateRef } from 'vue';
+import { message } from 'ant-design-vue';
 
+import ConfirmModal from '@/components/ConfirmModal.vue';
 import SvgIcon from '@/components/SvgIcon.vue';
 import { useRequest } from '@/hooks/request';
-import { getPageList, groupList } from '@/api';
+import { t } from '@/i18n';
+import {
+  addDeviceGroup,
+  addDeviceGroupUpdate,
+  deleteDeviceGroup,
+  getDeviceGroupList,
+  getPageList,
+  groupList,
+} from '@/api';
 
 import type { DefaultOptionType, SelectValue } from 'ant-design-vue/es/select';
-import type { DeviceGroupItem, EquipmentInformationForm, EquipmentTypeItem, UseGuideStepItemProps } from '@/types';
+import type { ValueType } from 'ant-design-vue/es/vc-cascader/Cascader';
+import type {
+  DeviceGroup,
+  DeviceGroupItem,
+  EquipmentInformationForm,
+  EquipmentTypeItem,
+  UseGuideStepItemProps,
+} from '@/types';
 
 const props = defineProps<UseGuideStepItemProps<EquipmentInformationForm>>();
 
 const { handleRequest } = useRequest();
 
+const modalComponentRef = useTemplateRef('modalComponent');
 const deviceGroup = ref<DeviceGroupItem[]>([]);
 const equipmentType = ref<EquipmentTypeItem[]>([]);
+const addGroupingOpen = ref<boolean>(false);
+const editorGroupName = ref<string>('');
+const titleGrouping = ref<boolean>(true);
+const groupName = ref<string>('');
+const parentId = ref<number | undefined>(undefined);
+const editorParentId = ref<number[]>([]);
+const oneDeviceGroup = ref<DeviceGroup[]>([]);
 
 const selectTypeRquipment = (value: SelectValue, option: DefaultOptionType) => {
   props.form.imgUrl = option.imgUrl;
@@ -23,13 +48,91 @@ const imgUrl = computed(() => {
   return import.meta.env.VITE_IMG_API + '/' + props.form.imgUrl;
 });
 
-onMounted(() => {
+const editorGroupingName = () => {
+  if (!props.form.deviceData || props.form.deviceData.length === 0) {
+    return message.warning(t('createDevice.pleaseEquipmentGroup'));
+  }
+  addGroupingOpen.value = true;
+  titleGrouping.value = false;
+  groupName.value = editorGroupName.value;
+};
+
+const addGroupingName = () => {
+  addGroupingOpen.value = true;
+  titleGrouping.value = true;
+  groupName.value = '';
+};
+
+const groupingNameOk = () => {
+  if (titleGrouping.value) {
+    if (!parentId.value) {
+      return message.warning(t('createDevice.pleaseSelectSuperiorGroup'));
+    }
+    if (!groupName.value) {
+      return message.warning(t('createDevice.pleaseEnterGroupName'));
+    }
+    handleRequest(async () => {
+      await addDeviceGroup({
+        groupName: groupName.value,
+        parentId: parentId.value,
+      });
+      getDeviceGroup();
+      addGroupingOpen.value = false;
+    });
+  } else {
+    if (!groupName.value) {
+      return message.warning(t('createDevice.pleaseEnterNameSecondaryGroup'));
+    }
+    handleRequest(async () => {
+      await addDeviceGroupUpdate({
+        id: editorParentId.value[1],
+        groupName: groupName.value,
+        parentId: editorParentId.value[0],
+      });
+      getDeviceGroup();
+      addGroupingOpen.value = false;
+    });
+  }
+};
+
+const confirm = () => {
+  handleRequest(async () => {
+    if (editorParentId.value.length === 2) {
+      await deleteDeviceGroup(editorParentId.value[1]);
+      modalComponentRef.value?.hideView();
+      addGroupingOpen.value = false;
+      props.form.deviceData = [];
+      getDeviceGroup();
+    }
+  });
+};
+
+const getDeviceGroup = () => {
   handleRequest(async () => {
     deviceGroup.value = await getPageList();
+  });
+};
+
+const groupingNameDelete = () => {
+  modalComponentRef.value?.showView();
+};
+
+const chooseGroup = (value: ValueType, selectOptions: DefaultOptionType[] | DefaultOptionType[][]) => {
+  if (selectOptions && selectOptions.length === 2) {
+    editorGroupName.value = (selectOptions as DeviceGroupItem[])[1].groupName;
+    editorParentId.value = value as number[];
+  }
+};
 
+onMounted(() => {
+  getDeviceGroup();
+  handleRequest(async () => {
     equipmentType.value = await groupList({
       dataType: 1,
     });
+    oneDeviceGroup.value = await getDeviceGroupList({
+      parentId: -1,
+    });
   });
 });
 </script>
@@ -37,15 +140,28 @@ onMounted(() => {
 <template>
   <div>
     <AFlex>
-      <div>
+      <div class="grouping-width">
         <AFormItem :label="$t('createDevice.equipmentGroup')" name="deviceData">
-          <ACascader
-            class="equipment-type"
-            v-model:value="form.deviceData"
-            :field-names="{ label: 'groupName', value: 'id', children: 'deviceGroupChilds' }"
-            :options="deviceGroup"
-            :placeholder="$t('common.plzSelect')"
-          />
+          <AFlex align="center">
+            <ACascader
+              class="equipment-type"
+              v-model:value="form.deviceData"
+              :field-names="{ label: 'groupName', value: 'id', children: 'deviceGroupChilds' }"
+              :options="deviceGroup"
+              :placeholder="$t('common.plzSelect')"
+              @change="chooseGroup"
+            />
+            <div @click="editorGroupingName">
+              <AFlex justify="center" align="center" class="button-flex">
+                <SvgIcon name="edit-o" />
+              </AFlex>
+            </div>
+            <div @click="addGroupingName">
+              <AFlex justify="center" align="center" class="button-flex button-plus">
+                <SvgIcon name="plus" />
+              </AFlex>
+            </div>
+          </AFlex>
         </AFormItem>
         <AFormItem :label="$t('setupProtocol.deviceType')" name="deviceType">
           <ASelect
@@ -75,10 +191,94 @@ onMounted(() => {
         <img class="information-img" referrerpolicy="no-referrer" :src="imgUrl" />
       </div>
     </AFlex>
+
+    <AModal
+      v-model:open="addGroupingOpen"
+      :title="titleGrouping ? t('createDevice.newlyAddedGroup') : t('createDevice.editGroup')"
+      :footer="null"
+      width="460px"
+      :mask-closable="false"
+      :keyboard="false"
+    >
+      <AFlex v-if="titleGrouping" align="center" class="grouping-name">
+        <div class="grouping-text">{{ $t('createDevice.superiorGroup') }}</div>
+
+        <ASelect
+          class="equipment-type"
+          v-model:value="parentId"
+          :options="oneDeviceGroup"
+          :field-names="{ label: 'groupName', value: 'id' }"
+          :placeholder="$t('common.plzSelect')"
+        />
+      </AFlex>
+      <AFlex align="center" class="grouping-name">
+        <div class="grouping-text">
+          {{ titleGrouping ? t('createDevice.groupName') : t('createDevice.secondaryGrouping') }}
+        </div>
+        <AInput class="equipment-type" v-model:value="groupName" :placeholder="$t('common.pleaseEnter')" />
+      </AFlex>
+
+      <AFlex justify="flex-end" class="footer">
+        <AButton v-if="!titleGrouping" class="default-button" @click="groupingNameDelete">{{
+          $t('common.delete')
+        }}</AButton>
+        <AButton class="default-button cancel-button button-width" @click="addGroupingOpen = false">{{
+          $t('common.cancel')
+        }}</AButton>
+        <AButton class="button-width" type="primary" @click="groupingNameOk">{{ $t('common.confirm') }}</AButton>
+      </AFlex>
+    </AModal>
+    <ConfirmModal
+      ref="modalComponent"
+      :title="$t('common.deleteConfirmation')"
+      :description-text="$t('common.confirmDeletion')"
+      :icon="{ name: 'delete' }"
+      :icon-bg-color="'#F56C6C'"
+      @confirm="confirm"
+    />
   </div>
 </template>
 
 <style lang="scss" scoped>
+.button-width {
+  width: 76px;
+}
+
+.cancel-button {
+  margin: 0 16px;
+}
+
+.footer {
+  margin-top: 24px;
+}
+
+.grouping-name {
+  margin-top: 24px;
+}
+
+.grouping-text {
+  width: 72px;
+}
+
+.grouping-width {
+  width: 440px;
+}
+
+.button-flex {
+  width: 32px;
+  height: 32px;
+  margin-left: 16px;
+  cursor: pointer;
+  background: #fff;
+  border: 1px solid #d9d9d9;
+  border-radius: 4px;
+}
+
+.button-plus {
+  margin-left: 12px !important;
+  font-size: 16px;
+}
+
 .flex-width {
   width: 350px;
 }
@@ -94,6 +294,7 @@ onMounted(() => {
 .information-img {
   width: 180px;
   height: 208px;
+  margin-left: 20px;
 }
 
 .equipment-type {

+ 1 - 1
src/views/create-device/CreateDevice.vue

@@ -67,7 +67,7 @@ const steps = ref<UseGuideStepItem[]>([
     component: shallowRef(BasicInformation),
     stepDescription: '描述文本',
     labelAlign: 'left',
-    labelCol: { span: 6 },
+    labelCol: { span: 5 },
   },
   {
     title: t('createDevice.detailedInformation'),