瀏覽代碼

feat(views): 更新创建设备模块

wangshun 3 月之前
父節點
當前提交
985d0648e3

+ 99 - 0
src/views/create-device/BasicInformation.vue

@@ -0,0 +1,99 @@
+<script setup lang="ts">
+import { onMounted, ref } from 'vue';
+
+import { useRequest } from '@/hooks/request';
+import { getPageList, groupList } from '@/api';
+
+import type { DeviceGroupItem, EquipmentInformationForm, EquipmentTypeItem, UseGuideStepItemProps } from '@/types';
+
+defineProps<UseGuideStepItemProps<EquipmentInformationForm>>();
+
+const { handleRequest } = useRequest();
+
+const deviceGroup = ref<DeviceGroupItem[]>([]);
+const equipmentType = ref<EquipmentTypeItem[]>([]);
+
+onMounted(() => {
+  handleRequest(async () => {
+    deviceGroup.value = await getPageList();
+
+    equipmentType.value = await groupList({
+      dataType: 1,
+    });
+  });
+});
+</script>
+
+<template>
+  <div>
+    <div class="use-guide-title">基础信息</div>
+    <div class="use-guide-description">在设备铭牌上可以找到SN码和设备密码</div>
+    <ARow>
+      <ACol :span="7" style="margin-top: 40px">
+        <AFormItem label="设备组" name="deviceData">
+          <ACascader
+            class="equipment-group"
+            size="large"
+            v-model:value="form.deviceData"
+            :field-names="{ label: 'groupName', value: 'id', children: 'deviceGroupChilds' }"
+            :options="deviceGroup"
+            placeholder="请选择"
+          />
+        </AFormItem>
+        <AFormItem label="设备类型" name="deviceType">
+          <ASelect
+            class="equipment-type"
+            size="large"
+            v-model:value="form.deviceType"
+            :options="equipmentType"
+            :field-names="{ label: 'dataName', value: 'id' }"
+          />
+          <ATooltip placement="top">
+            <template #title>
+              <span>建议按「设备名」「序号」的方式命名,比如:电表1</span>
+            </template>
+            <img class="information-prompt" referrerpolicy="no-referrer" src="@/assets/img/device-tip.png" />
+          </ATooltip>
+        </AFormItem>
+        <AFormItem label="设备名" name="deviceName">
+          <AInput class="equipment-name" v-model:value="form.deviceName" />
+        </AFormItem>
+      </ACol>
+      <ACol :span="17">
+        <img class="information-img" referrerpolicy="no-referrer" src="@/assets/img/device-img.png" />
+      </ACol>
+    </ARow>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.information-prompt {
+  width: 14px;
+  height: 14px;
+  margin-left: 13px;
+  cursor: pointer;
+}
+
+.information-img {
+  width: 123px;
+  height: 123px;
+  margin-top: 30px;
+  margin-left: 50px;
+}
+
+.equipment-group {
+  width: 224px;
+  margin-left: 18px;
+}
+
+.equipment-type {
+  width: 224px;
+  margin-left: 5px;
+}
+
+.equipment-name {
+  width: 224px;
+  height: 40px;
+  margin-left: 18px;
+}
+</style>

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

@@ -1,3 +1,93 @@
+<script setup lang="ts">
+import { computed, reactive } from 'vue';
+
+import UseGuidance from '@/layout/UseGuidance.vue';
+import BasicInformation from '@/views/create-device/BasicInformation.vue';
+import DetailedInformation from '@/views/create-device/DetailedInformation.vue';
+import GatewayParameters from '@/views/create-device/GatewayParameters.vue';
+import VerifyParameters from '@/views/create-device/VerifyParameters.vue';
+import { t } from '@/i18n';
+
+import type { EquipmentInformationForm, FormRules, RegisterGatewayForm, UseGuideStepItem } from '@/types';
+
+const equipmentInformationForm = reactive<EquipmentInformationForm>({
+  groupId: 0,
+  deviceData: [],
+  deviceType: '',
+  deviceName: '',
+  brand: 0,
+  model: 0,
+  modelType: 0,
+  controlType: 0,
+  compressionLevel: 0,
+  voltageLevel: 0,
+  powerRating: '',
+  powerUnload: '',
+  maximumFlow: '',
+  pressure: '',
+  pressureMax: '',
+  productionDate: '',
+  productionNum: '',
+  deviceNum: '',
+  mainTechData: '',
+  mountedPosition: '',
+  department: '',
+  respPerson: '',
+  phone: '',
+  serviceLife: 0,
+  status: '',
+  remarks: '',
+  devId: 0,
+});
+
+const rules = computed<FormRules<RegisterGatewayForm>>(() => {
+  return {
+    deviceData: [{ required: true, message: '请选择设备组', trigger: 'change' }],
+    deviceType: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
+    deviceName: [{ required: true, message: '请输入设备名称', trigger: 'change' }],
+    brand: [{ required: true, message: '请选择', trigger: 'change' }],
+    model: [{ required: true, message: '请选择', trigger: 'change' }],
+    modelType: [{ required: true, message: '请选择', trigger: 'change' }],
+    controlType: [{ required: true, message: '请选择', trigger: 'change' }],
+    compressionLevel: [{ required: true, message: '请选择', trigger: 'change' }],
+    voltageLevel: [{ required: true, message: '请选择', trigger: 'change' }],
+    powerRating: [{ required: true, message: '请输入', trigger: 'change' }],
+    powerUnload: [{ required: true, message: '请输入', trigger: 'change' }],
+    maximumFlow: [{ required: true, message: '请输入', trigger: 'change' }],
+    pressure: [{ required: true, message: '请输入', trigger: 'change' }],
+    pressureMax: [{ required: true, message: '请输入', trigger: 'change' }],
+  };
+});
+
+const steps: UseGuideStepItem[] = [
+  {
+    title: t('setupProtocol.selectProtocolType'),
+    component: BasicInformation,
+  },
+  {
+    title: t('setupProtocol.selectConfigMethod'),
+    component: DetailedInformation,
+    contentOffset: 40,
+    formLayout: 'vertical',
+  },
+  {
+    title: t('setupProtocol.uploadFile'),
+    component: GatewayParameters,
+    contentOffset: 40,
+  },
+  {
+    title: t('setupProtocol.waitingForRecognition'),
+    component: VerifyParameters,
+    contentOffset: 40,
+  },
+];
+</script>
+
 <template>
-  <div>创建设备</div>
+  <UseGuidance
+    :title="$t('createDevice.createDevice')"
+    :steps="steps"
+    :form="equipmentInformationForm"
+    :rules="rules"
+  />
 </template>

+ 210 - 0
src/views/create-device/DetailedInformation.vue

@@ -0,0 +1,210 @@
+<script setup lang="ts">
+import { onMounted, ref } from 'vue';
+
+import { useRequest } from '@/hooks/request';
+import { deviceAdd, groupList } from '@/api';
+
+import type {
+  EquipmentInformationForm,
+  EquipmentTypeItem,
+  UseGuideStepItemExpose,
+  UseGuideStepItemProps,
+} from '@/types';
+
+const props = defineProps<UseGuideStepItemProps<EquipmentInformationForm>>();
+
+const { handleRequest } = useRequest();
+
+const brandList = ref<EquipmentTypeItem[]>([]);
+const modelList = ref<EquipmentTypeItem[]>([]);
+const modelTypeList = ref<EquipmentTypeItem[]>([]);
+const controlTypeList = ref<EquipmentTypeItem[]>([]);
+const compressionLevelList = ref<EquipmentTypeItem[]>([]);
+const voltageLevelList = ref<EquipmentTypeItem[]>([]);
+
+const finish = () => {
+  handleRequest(async () => {
+    props.form.groupId = props.form.deviceData[1] ? props.form.deviceData[1] : props.form.deviceData[0];
+    props.form.devId = await deviceAdd(props.form);
+  });
+};
+
+defineExpose<UseGuideStepItemExpose>({
+  finish,
+});
+
+onMounted(() => {
+  handleRequest(async () => {
+    brandList.value = await groupList({
+      dataType: 2,
+    });
+
+    props.form.brand = brandList.value[0].id;
+
+    modelList.value = await groupList({
+      dataType: 3,
+    });
+
+    props.form.model = modelList.value[0].id;
+
+    modelTypeList.value = await groupList({
+      dataType: 4,
+    });
+
+    props.form.modelType = modelTypeList.value[0].id;
+
+    controlTypeList.value = await groupList({
+      dataType: 5,
+    });
+
+    props.form.controlType = controlTypeList.value[0].id;
+
+    compressionLevelList.value = await groupList({
+      dataType: 6,
+    });
+
+    props.form.compressionLevel = compressionLevelList.value[0].id;
+
+    voltageLevelList.value = await groupList({
+      dataType: 7,
+    });
+
+    props.form.voltageLevel = voltageLevelList.value[0].id;
+  });
+});
+</script>
+
+<template>
+  <div style="width: 1092px">
+    <div>
+      <div class="use-guide-title">详细信息</div>
+      <div class="use-guide-description" style="margin-bottom: 40px">在设备铭牌上可以找到SN码和设备密码</div>
+      <ADivider />
+    </div>
+
+    <AFlex class="equipment-information" wrap="wrap" :gap="33">
+      <AFormItem label="品牌" name="brand">
+        <ASelect v-model:value="form.brand" :options="brandList" :field-names="{ label: 'dataName', value: 'id' }" />
+      </AFormItem>
+
+      <AFormItem label="型号" name="model">
+        <ASelect v-model:value="form.model" :options="modelList" :field-names="{ label: 'dataName', value: 'id' }" />
+      </AFormItem>
+
+      <AFormItem label="控制器型号" name="modelTypeList">
+        <ASelect
+          v-model:value="form.modelType"
+          :options="modelTypeList"
+          :field-names="{ label: 'dataName', value: 'id' }"
+        />
+      </AFormItem>
+
+      <AFormItem label="调节方式" name="controlType">
+        <ASelect
+          v-model:value="form.controlType"
+          :options="controlTypeList"
+          :field-names="{ label: 'dataName', value: 'id' }"
+        />
+      </AFormItem>
+
+      <AFormItem label="压缩级数" name="compressionLevelList">
+        <ASelect
+          v-model:value="form.compressionLevel"
+          :options="compressionLevelList"
+          :field-names="{ label: 'dataName', value: 'id' }"
+        />
+      </AFormItem>
+
+      <AFormItem label="电压等级" name="voltageLevel">
+        <ASelect
+          v-model:value="form.voltageLevel"
+          :options="voltageLevelList"
+          :field-names="{ label: 'dataName', value: 'id' }"
+        />
+      </AFormItem>
+
+      <AFormItem label="额定功率(KW)" name="powerRating">
+        <AInput v-model:value="form.powerRating" />
+      </AFormItem>
+
+      <AFormItem label="卸载功率(KW)" name="powerUnload">
+        <AInput v-model:value="form.powerUnload" />
+      </AFormItem>
+
+      <AFormItem label="公称容积流量(m³/min)" name="maximumFlow">
+        <AInput v-model:value="form.maximumFlow" />
+      </AFormItem>
+
+      <AFormItem label="额定工作压力(bar)" name="pressure">
+        <AInput v-model:value="form.pressure" />
+      </AFormItem>
+
+      <AFormItem label="最大工作压力(bar)" name="pressureMax">
+        <AInput v-model:value="form.pressureMax" />
+      </AFormItem>
+
+      <AFormItem label="出厂日期" name="productionDate">
+        <ADatePicker v-model:value="form.productionDate">
+          <template #suffixIcon> </template>
+        </ADatePicker>
+      </AFormItem>
+
+      <AFormItem label="出厂编号" name="productionNum">
+        <AInput v-model:value="form.productionNum" />
+      </AFormItem>
+
+      <AFormItem label="设备编号" name="deviceNum">
+        <AInput v-model:value="form.deviceNum" />
+      </AFormItem>
+
+      <AFormItem label="主要技术参数" name="mainTechData">
+        <AInput v-model:value="form.mainTechData" />
+      </AFormItem>
+
+      <AFormItem label="安装位置" name="mountedPosition">
+        <AInput v-model:value="form.mountedPosition" />
+      </AFormItem>
+
+      <AFormItem label="使用部门" name="department">
+        <AInput v-model:value="form.department" />
+      </AFormItem>
+
+      <AFormItem label="责任人" name="respPerson">
+        <AInput v-model:value="form.respPerson" />
+      </AFormItem>
+
+      <AFormItem label="联系电话" name="phone">
+        <AInput v-model:value="form.phone" />
+      </AFormItem>
+
+      <AFormItem label="使用年限(年)" name="serviceLife">
+        <AInput v-model:value="form.serviceLife" />
+      </AFormItem>
+
+      <AFormItem label="状态" name="status">
+        <AInput v-model:value="form.status" />
+      </AFormItem>
+    </AFlex>
+    <AFormItem label="备注" name="remarks">
+      <ATextarea v-model:value="form.remarks" placeholder="请输入" allow-clear :rows="6" />
+    </AFormItem>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.information-prompt {
+  width: 14px;
+  height: 14px;
+  margin-left: 13px;
+  cursor: pointer;
+}
+
+.equipment-information {
+  row-gap: 0 !important;
+  margin-top: 40px;
+}
+
+.equipment-information div {
+  width: 192px;
+}
+</style>

+ 532 - 0
src/views/create-device/GatewayParameters.vue

@@ -0,0 +1,532 @@
+<script setup lang="ts">
+import { onMounted, ref } from 'vue';
+import { message } from 'ant-design-vue';
+
+import { useRequest } from '@/hooks/request';
+import {
+  deviceGatewayUpdate,
+  gatewayLinkGetList,
+  gatewayLinkProtocolGatewayList,
+  orgGatewaySerialNumber,
+  postProtocolPageList,
+} from '@/api';
+
+import type { DefaultOptionType, SelectValue } from 'ant-design-vue/es/select';
+import type { Key } from 'ant-design-vue/es/table/interface';
+import type {
+  EquipmentInformationForm,
+  EquipmentUpdateForm,
+  InterfaceData,
+  ListEquipmentParametersItem,
+  PostProtocolPageItem,
+  SerialNumberItem,
+  UseGuideStepItemExpose,
+  UseGuideStepItemProps,
+} from '@/types';
+
+const columns = [
+  {
+    title: '关联网关参数',
+    dataIndex: 'index',
+    key: 'index',
+  },
+  {
+    title: '网关序列号',
+    dataIndex: 'snCode',
+    key: 'snCode',
+  },
+  {
+    title: '网关型号',
+    dataIndex: 'modelName',
+    key: 'modelName',
+  },
+  {
+    title: '物理接口',
+    dataIndex: 'linkName',
+    key: 'linkName',
+  },
+  {
+    title: '组号',
+    dataIndex: 'number',
+    key: 'interface',
+  },
+  {
+    title: '参数',
+    key: 'tags',
+  },
+  {
+    title: '操作',
+    key: 'action',
+  },
+];
+
+const devColumns = [
+  {
+    title: '序列号',
+    dataIndex: 'index',
+    key: 'index',
+  },
+  {
+    title: '设备参数编码',
+    dataIndex: 'deviceNum',
+  },
+  {
+    title: '设备参数名称',
+    dataIndex: 'deviceName',
+  },
+  {
+    title: '网关参数序号',
+    dataIndex: 'name',
+  },
+  {
+    title: '网关参数编码',
+    dataIndex: 'gatewayParamCode',
+  },
+  {
+    title: '单位',
+    dataIndex: 'unit',
+  },
+  {
+    title: '分组名称',
+    dataIndex: 'module',
+  },
+  {
+    title: '操作',
+    key: 'action',
+  },
+];
+
+const customizationColumns = [
+  {
+    title: '设备参数序号',
+    dataIndex: 'index',
+    key: 'index',
+  },
+  {
+    title: '设备参数编码',
+    dataIndex: 'name1',
+    key: 'name1',
+  },
+  {
+    title: '设备参数名称',
+    dataIndex: 'name',
+    key: 'name',
+  },
+  {
+    title: '自定义公式',
+    dataIndex: 'name',
+    key: 'name',
+  },
+  {
+    title: '保留小数位',
+    dataIndex: 'name',
+    key: 'name',
+  },
+  {
+    title: '分组名称',
+    dataIndex: 'name',
+    key: 'name',
+  },
+  {
+    title: '组别排序',
+    dataIndex: 'name',
+    key: 'name',
+  },
+  {
+    title: '组内排序',
+    dataIndex: 'name',
+    key: 'name',
+  },
+  {
+    title: '是否过程数据',
+    dataIndex: 'switch',
+    key: 'switch',
+  },
+  {
+    title: '操作',
+    dataIndex: 'action',
+    key: 'action',
+  },
+];
+
+const protocolEquipmentColumns = [
+  {
+    title: '序列号',
+    dataIndex: 'index',
+    key: 'index',
+  },
+  {
+    title: '网关参数编码',
+    dataIndex: 'gatewayParamCode',
+    key: 'gatewayParamCode',
+  },
+  {
+    title: '网关参数名称',
+    dataIndex: 'paramName',
+    key: 'paramName',
+  },
+];
+
+interface GatewayList {
+  snCode: string;
+  modelName: string;
+  linkName: string;
+  divId: number;
+}
+const props = defineProps<UseGuideStepItemProps<EquipmentInformationForm>>();
+const gatewayList = ref<GatewayList[]>([]);
+const gatewayData = ref<PostProtocolPageItem[]>([]);
+const interfaceData = ref<InterfaceData[]>([]);
+
+const customizationData = ref([
+  {
+    index: 1,
+    switch: true,
+    name1: '123456',
+  },
+  {
+    index: 1,
+    switch: false,
+    name1: '789456',
+  },
+]);
+
+const { handleRequest } = useRequest();
+
+const listEquipmentParameters = ref<ListEquipmentParametersItem[]>([]);
+const chooselistEquipment = ref<ListEquipmentParametersItem[]>([]);
+const serialNumberItem = ref<SerialNumberItem[]>([]);
+const equipmentLsit = ref<Key[]>([]);
+const open = ref<boolean>(false);
+
+let gatewayId: number;
+const dataList: number[] = [];
+const equipmentList: EquipmentUpdateForm[] = [];
+
+const selectParameters = (value: string) => {
+  if (value) {
+    chooselistEquipment.value = [];
+    equipmentLsit.value = [];
+    handleRequest(async () => {
+      const { records } = await gatewayLinkProtocolGatewayList({
+        pageIndex: 1,
+        pageSize: 10,
+        gatewayId,
+      });
+      listEquipmentParameters.value = records;
+      open.value = true;
+    });
+  } else {
+    message.warning('请选择物理接口');
+  }
+};
+
+const gatewayDelete = (index: number) => {
+  gatewayList.value.splice(index, 1);
+};
+
+const gatewayDevDelete = (index: number) => {
+  gatewayData.value.splice(index, 1);
+};
+
+const handleOk = () => {
+  handleRequest(async () => {
+    chooselistEquipment.value.forEach((item) => {
+      equipmentList.push({
+        deviceId: props.form.devId,
+        gatewayId,
+        gatewayProtocolId: item.id,
+      });
+    });
+
+    await deviceGatewayUpdate(equipmentList);
+
+    protocolPageData();
+  });
+
+  open.value = false;
+};
+
+const addEquipment = (index: number) => {
+  chooselistEquipment.value.splice(index, 1);
+  equipmentLsit.value.splice(index, 1);
+};
+
+const addGateway = () => {
+  handleRequest(async () => {
+    interfaceData.value = await gatewayLinkGetList(serialNumberItem.value[0].id);
+    gatewayList.value.push({
+      snCode: serialNumberItem.value[0].snCode,
+      modelName: serialNumberItem.value[0].modelName,
+      linkName: interfaceData.value.length ? interfaceData.value[0].linkName : '',
+      divId: serialNumberItem.value[0].id,
+    });
+  });
+};
+
+const serialNumberAdd = (value: SelectValue, option: DefaultOptionType, index: number) => {
+  handleRequest(async () => {
+    gatewayId = option.id;
+    interfaceData.value = await gatewayLinkGetList(option.id);
+    gatewayList.value[index].modelName = option.modelName;
+    gatewayList.value[index].divId = option.id;
+    if (interfaceData.value.length) {
+      gatewayList.value[index].linkName = interfaceData.value.length ? interfaceData.value[0].linkName : '';
+    } else {
+      gatewayList.value[index].linkName = '';
+    }
+  });
+};
+
+const rowSelectedChange = (selectedRowKeys: Key[], selectedRows: ListEquipmentParametersItem[]) => {
+  equipmentLsit.value = selectedRowKeys;
+  chooselistEquipment.value = selectedRows;
+};
+
+const protocolPageData = () => {
+  handleRequest(async () => {
+    gatewayList.value.forEach((item) => {
+      dataList.push(item.divId);
+    });
+    const { records } = await postProtocolPageList({
+      pageIndex: 1,
+      pageSize: 10,
+      deviceIds: [props.form.devId],
+      gatewayIds: dataList,
+    });
+    if (records.length) {
+      if (gatewayData.value.length === 0) {
+        gatewayData.value = records;
+      } else {
+        // 数组合并去重
+        gatewayData.value = mergeAndDeduplicate([gatewayData.value, records]);
+      }
+    }
+  });
+};
+
+const mergeAndDeduplicate = (arrays: [PostProtocolPageItem[], PostProtocolPageItem[]]) => {
+  return arrays.reduce((acc, curr) => {
+    curr.forEach((item) => {
+      // 使用 find 方法检查累加器数组中是否已存在具有相同 id 的对象
+      const existingItem = acc.find((existing) => existing.id === item.id);
+      // 如果不存在,则将该对象添加到累加器数组中
+      if (!existingItem) {
+        acc.push(item);
+      }
+    });
+    return acc;
+  }, []);
+};
+
+const finish = () => {
+  protocolPageData();
+};
+
+defineExpose<UseGuideStepItemExpose>({
+  finish,
+});
+
+onMounted(() => {
+  handleRequest(async () => {
+    const { records } = await orgGatewaySerialNumber({
+      pageIndex: 1,
+      pageSize: 10,
+    });
+    serialNumberItem.value = records;
+  });
+});
+</script>
+
+<template>
+  <div style="width: 1092px">
+    <div>
+      <div class="use-guide-title">选择网关参数</div>
+      <div class="use-guide-description" style="margin-bottom: 40px">在设备铭牌上可以找到SN码和设备密码</div>
+      <ADivider />
+    </div>
+    <AFlex justify="space-between" style="margin-bottom: 15px">
+      <span class="header-text">关联网关参数</span>
+      <AButton ghost style="color: #32bac0; border-color: #32bac0" @click="addGateway">新增</AButton>
+    </AFlex>
+
+    <div>
+      <ATable :columns="columns" :data-source="gatewayList" :pagination="false">
+        <template #bodyCell="{ column, record, index }">
+          <template v-if="column.key === 'index'">
+            {{ index + 1 }}
+          </template>
+          <template v-if="column.key === 'snCode'">
+            <ASelect
+              size="large"
+              v-model:value="record.snCode"
+              style="width: 200px"
+              :options="serialNumberItem"
+              :field-names="{ label: 'snCode', value: 'id' }"
+              @change="(value, option) => serialNumberAdd(value, option, index)"
+            />
+          </template>
+          <template v-if="column.key === 'linkName'">
+            <ASelect
+              size="large"
+              v-model:value="record.linkName"
+              style="width: 200px"
+              :options="interfaceData"
+              :field-names="{ label: 'linkName', value: 'id' }"
+            />
+          </template>
+
+          <template v-if="column.key === 'tags'">
+            <span class="select-parameters" @click="selectParameters(record.linkName)">选择参数</span>
+          </template>
+          <template v-if="column.key === 'action'">
+            <AButton @click="gatewayDelete(index)">删除</AButton>
+          </template>
+        </template>
+      </ATable>
+    </div>
+
+    <AFlex justify="space-between" style="margin-top: 40px; margin-bottom: 15px">
+      <span class="header-text">关联网关参数</span>
+      <AButton ghost style="color: #32bac0; border-color: #32bac0">分组设置</AButton>
+    </AFlex>
+    <ATable :columns="devColumns" :data-source="gatewayData" :pagination="false">
+      <template #bodyCell="{ column, index }">
+        <template v-if="column.key === 'index'">
+          {{ index + 1 }}
+        </template>
+        <template v-else-if="column.key === 'action'">
+          <AButton @click="gatewayDevDelete(index)">删除</AButton>
+        </template>
+      </template>
+    </ATable>
+
+    <AFlex justify="space-between" style="margin-top: 40px; margin-bottom: 15px">
+      <span class="header-text">自定义监控参数</span>
+      <div>
+        <AButton ghost style="margin-right: 20px; color: #32bac0; border-color: #32bac0">新增</AButton>
+        <AButton ghost style="color: #32bac0; border-color: #32bac0">快速匹配</AButton>
+      </div>
+    </AFlex>
+
+    <ATable :columns="customizationColumns" :data-source="customizationData" :pagination="false">
+      <template #bodyCell="{ column, record, index }">
+        <template v-if="column.key === 'index'">
+          {{ index + 1 }}
+        </template>
+        <template v-else-if="column.key === 'switch'">
+          <ASwitch v-model:checked="record.switch" />
+        </template>
+        <template v-else-if="column.key === 'action'">
+          <AButton>删除</AButton>
+        </template>
+      </template>
+    </ATable>
+
+    <AModal
+      v-model:open="open"
+      title="选择网关协议参数"
+      @ok="handleOk"
+      width="920px"
+      :mask-closable="false"
+      :keyboard="false"
+    >
+      <div class="gateway-parameters">
+        <AFlex>
+          <div class="gateway-parameters-left">
+            <ATable
+              :row-selection="{
+                type: 'checkbox',
+                selectedRowKeys: equipmentLsit,
+                onChange: rowSelectedChange,
+              }"
+              row-key="id"
+              :columns="protocolEquipmentColumns"
+              :data-source="listEquipmentParameters"
+              :scroll="{ y: 380 }"
+              :pagination="false"
+            >
+              <template #bodyCell="{ column, index }">
+                <template v-if="column.key === 'index'">
+                  {{ index + 1 }}
+                </template>
+              </template>
+            </ATable>
+          </div>
+
+          <div class="gateway-parameters-right">
+            <AFlex justify="space-between" style="width: 100%; padding-bottom: 24px">
+              <div>已选 ({{ equipmentLsit.length }}/80)</div>
+              <div style="margin-right: 10px">删除</div>
+            </AFlex>
+
+            <AFlex :vertical="true" class="main-container" style="height: 390px">
+              <AFlex
+                justify="space-between"
+                v-for="(item, index) in chooselistEquipment"
+                :key="item.id"
+                class="gateway-parameters-right-dev"
+              >
+                <span>{{ item.paramName }}</span>
+                <span style="margin-right: 2px; cursor: pointer" @click="addEquipment(index)">删除</span>
+              </AFlex>
+            </AFlex>
+          </div>
+        </AFlex>
+      </div>
+    </AModal>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.header-text {
+  margin-top: 5px;
+  font-size: 16px;
+  font-style: normal;
+  font-weight: 600;
+  line-height: 24px;
+  color: #333;
+  text-align: left;
+}
+
+.select-parameters {
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 400;
+  line-height: 24px;
+  color: #32bac0;
+  text-align: left;
+  cursor: pointer;
+}
+
+.gateway-parameters {
+  width: 872px;
+  height: 473px;
+  margin-top: 24px;
+  background: #fff;
+  border: 1px solid #e4e7ed;
+  border-radius: 8px;
+}
+
+.gateway-parameters-left {
+  width: 572px;
+  padding: 24px;
+}
+
+.gateway-parameters-right {
+  width: 298px;
+  padding: 24px 20px 24px 24px;
+}
+
+.gateway-parameters-right-dev {
+  width: 100%;
+  height: 24px;
+  margin-bottom: 24px;
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 400;
+  line-height: 24px;
+  color: #666;
+  text-align: left;
+}
+</style>

+ 97 - 0
src/views/create-device/VerifyParameters.vue

@@ -0,0 +1,97 @@
+<script setup lang="ts">
+import { onMounted, ref } from 'vue';
+
+import { useRequest } from '@/hooks/request';
+import { postParameterVerification } from '@/api';
+
+import type { EquipmentInformationForm, ParameterVerification, UseGuideStepItemProps } from '@/types';
+
+const props = defineProps<UseGuideStepItemProps<EquipmentInformationForm>>();
+
+const parameterVerificationList = ref<ParameterVerification[]>([]);
+
+const { handleRequest } = useRequest();
+
+const colorShow = (value: number | string) => {
+  if (value) {
+    return 'color:#666666';
+  } else {
+    return 'color:#F56C6C';
+  }
+};
+
+onMounted(() => {
+  handleRequest(async () => {
+    parameterVerificationList.value = await postParameterVerification(props.form.devId);
+  });
+});
+</script>
+
+<template>
+  <div style="width: 1092px">
+    <div class="use-guide-title">验证数据</div>
+    <div class="use-guide-description" style="margin-bottom: 40px">在设备铭牌上可以找到SN码和设备密码</div>
+
+    <div v-for="item in parameterVerificationList" :key="item.id">
+      <AFlex align="center" class="host-parameters-header">
+        <div class="host-parameters-header-left"></div>
+        <div class="host-parameters-header-right">{{ item.deviceParamGroupName }}</div>
+      </AFlex>
+      <AFlex class="host-parameters" wrap="wrap">
+        <div v-for="ite in item.valueVos" :key="ite.valueId" class="host-parameters-div">
+          <span :style="colorShow(ite.value)">{{ ite.paramName }}:</span>
+          <span style="margin-left: 10px">{{ ite.value }}</span>
+          <span v-if="ite.value">{{ ite.unit }}</span>
+        </div>
+      </AFlex>
+    </div>
+  </div>
+</template>
+
+<style lang="scss" scoped>
+.host-parameters-header {
+  width: 1092px;
+  height: 56px;
+  background: #f5f6f7;
+  border: 1px solid #e4e7ed;
+  border-radius: 8px 8px 0 0;
+}
+
+.host-parameters {
+  width: 1092px;
+  padding: 24px;
+  padding-bottom: 8px;
+  margin-bottom: 24px;
+  border: 1px solid #e4e7ed;
+  border-top: none;
+  border-radius: 0 0 8px 8px;
+}
+
+.host-parameters-header-left {
+  width: 2px;
+  height: 16px;
+  margin-right: 10px;
+  margin-left: 24px;
+  background: #32bac0;
+}
+
+.host-parameters-header-right {
+  font-size: 16px;
+  font-style: normal;
+  font-weight: 500;
+  line-height: 24px;
+  color: #000;
+  text-align: left;
+}
+
+.host-parameters-div {
+  width: 347px;
+  margin-bottom: 16px;
+  font-size: 14px;
+  font-style: normal;
+  font-weight: 400;
+  line-height: 24px;
+  color: #333;
+  text-align: left;
+}
+</style>