Эх сурвалжийг харах

Merge branch 'eq_completset' of shuzhihua/pms-iot-vue into master

yanghao 1 өдөр өмнө
parent
commit
64bffa0c95

+ 2 - 2
.env.local

@@ -3,8 +3,8 @@ NODE_ENV=development
 
 VITE_DEV=true
 
-# 请求路径
-VITE_BASE_URL='https://iot.deepoil.cc'
+# 请求路径  http://192.168.188.149:48080  https://iot.deepoil.cc
+VITE_BASE_URL='http://192.168.188.150:8080'
 
 # 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
 VITE_UPLOAD_TYPE=server

+ 1 - 4
index.html

@@ -3,10 +3,7 @@
   <head>
     <script src="https://g.alicdn.com/code/npm/@ali/dingtalk-h5-remote-debug/0.1.3/index.js"></script>
     <script src="https://g.alicdn.com/dingding/dinglogin/0.0.5/ddLogin.js"></script>
-    <script
-      type="text/javascript"
-      src="https://cdn.jsdelivr.net/npm/js-base64@3.6.0/base64.min.js"
-    ></script>
+    <script async type="text/javascript" src="/js/base64.min.js"></script>
     <script src="https://api.map.baidu.com/api?v=3.0&ak=c0crhdxQ5H7WcqbcazGr7mnHrLa4GmO0"></script>
     <meta charset="UTF-8" />
     <link rel="icon" href="/favicon.ico" />

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 6 - 0
public/js/base64.min.js


+ 34 - 0
src/api/pms/device/index.ts

@@ -155,5 +155,39 @@ export const IotDeviceApi = {
   },
   getIotDeviceTdPage: async (params: any) => {
     return await request.get({ url: `/rq/iot-device/td/page`, params })
+  },
+
+  // 设备成套接口
+
+  // 查看详情时获取成套设备列表
+  getIotDeviceSets: async (params: any) => {
+    return await request.get({ url: `/rq/iot-device-group-detail/page`, params })
+  },
+
+  // 新增时根据部门id获取设备列表
+  getIotDeviceSetOptions: async (id: any) => {
+    return await request.get({ url: `/rq/iot-device/dept/${id}` })
+  },
+
+  // 获取成套列表
+  getIotDeviceSetList: async (params: any) => {
+    return await request.get({ url: `/rq/iot-device-group/page`, params })
+  },
+  // 添加成套
+  createIotDeviceSet: async (data: any) => {
+    return await request.post({ url: `/rq/iot-device-group/create`, data })
+  },
+  // 编辑成套
+  updateIotDeviceSet: async (data: any) => {
+    return await request.put({ url: `/rq/iot-device-group/update`, data })
+  },
+  // 删除成套
+  deleteIotDeviceSet: async (id: number) => {
+    return await request.delete({ url: `/rq/iot-device-group/delete?id=` + id })
+  },
+
+  // 获取关联设备
+  getIotDeviceSetRelation: async (params) => {
+    return await request.get({ url: `/rq/iot-device-group/device/group`, params })
   }
 }

Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 575 - 575
src/locales/en.ts


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 478 - 478
src/locales/ru.ts


Файлын зөрүү хэтэрхий том тул дарагдсан байна
+ 577 - 577
src/locales/zh-CN.ts


+ 47 - 11
src/views/pms/device/DeviceInfo.vue

@@ -1,7 +1,6 @@
 <template>
   <ContentWrap>
     <div style="display: flex; flex-direction: row; height: 12em; margin-top: 2px">
-
       <div style="flex: 1; height: 12em; margin-left: 20px">
         <el-image
           :key="index"
@@ -147,20 +146,46 @@
         </el-form>
       </el-tab-pane>
       <el-tab-pane :label="t('deviceInfo.fileLibrary')" name="file">
-<!--        <DeviceUpload ref="fileRef" v-if="loadedTabs.includes('1')" />-->
-        <DeviceFile ref="fileRef" :deviceId="id" :deviceName="formData.deviceName"  v-if="loadedTabs.includes('1')" />
+        <!--        <DeviceUpload ref="fileRef" v-if="loadedTabs.includes('1')" />-->
+        <DeviceFile
+          ref="fileRef"
+          :deviceId="id"
+          :deviceName="formData.deviceName"
+          v-if="loadedTabs.includes('1')"
+        />
       </el-tab-pane>
       <el-tab-pane :label="t('deviceInfo.deviceBOM')" name="bom">
-        <BomList ref="bomRef" v-model:activeName="activeName" :deviceId="id" :deviceCategoryName="formData.assetClassName" v-if="loadedTabs.includes('2')" />
+        <BomList
+          ref="bomRef"
+          v-model:activeName="activeName"
+          :deviceId="id"
+          :deviceCategoryName="formData.assetClassName"
+          v-if="loadedTabs.includes('2')"
+        />
       </el-tab-pane>
       <el-tab-pane :label="t('deviceInfo.operationRecords')" name="record">
-        <RecordList ref="recordRef" v-model:activeName="activeName" :deviceId="id" v-if="loadedTabs.includes('3')" />
+        <RecordList
+          ref="recordRef"
+          v-model:activeName="activeName"
+          :deviceId="id"
+          v-if="loadedTabs.includes('3')"
+        />
       </el-tab-pane>
       <el-tab-pane :label="t('deviceInfo.faultRecords')" name="failure">
-        <FailureList ref="failureRef" v-model:activeName="activeName" :deviceId="id" v-if="loadedTabs.includes('4')" />
+        <FailureList
+          ref="failureRef"
+          v-model:activeName="activeName"
+          :deviceId="id"
+          v-if="loadedTabs.includes('4')"
+        />
       </el-tab-pane>
       <el-tab-pane :label="t('deviceInfo.repairRecords')" name="maintain">
-        <MaintainList ref="maintainRef" v-model:activeName="activeName" :deviceId="id" v-if="loadedTabs.includes('5')" />
+        <MaintainList
+          ref="maintainRef"
+          v-model:activeName="activeName"
+          :deviceId="id"
+          v-if="loadedTabs.includes('5')"
+        />
       </el-tab-pane>
       <el-tab-pane :label="t('deviceInfo.maintenanceRecords')" name="maintenance">
         <MaintenanceList
@@ -202,6 +227,16 @@
           v-if="loadedTabs.includes('10')"
         />
       </el-tab-pane>
+
+      <!-- 关联设备 -->
+      <el-tab-pane :label="t('deviceInfo.associationDevice')" name="association">
+        <AssociationDevices
+          ref="personRef"
+          v-model:activeName="activeName"
+          :deviceId="id"
+          v-if="loadedTabs.includes('11')"
+        />
+      </el-tab-pane>
     </el-tabs>
   </ContentWrap>
 </template>
@@ -218,11 +253,12 @@ import MaintainList from '@/views/pms/device/MaintainList.vue'
 import InspectList from '@/views/pms/device/InspectList.vue'
 import MaintenanceList from '@/views/pms/device/maintenance/MaintenanceList.vue'
 import AllotLogList from '@/views/pms/device/allotlog/AllotLogList.vue'
-import DeviceStatusLogList from "@/views/pms/device/statuslog/DeviceStatusLogList.vue";
+import DeviceStatusLogList from '@/views/pms/device/statuslog/DeviceStatusLogList.vue'
 import PersonList from '@/views/pms/device/personlog/PersonList.vue'
 import RecordList from '@/views/pms/device/record/RecordList.vue'
+import AssociationDevices from '@/views/pms/device/completeSet/AssociationDevices.vue'
 import { createImageViewer } from '@/components/ImageViewer'
-import { ref, onMounted } from 'vue';
+import { ref, onMounted } from 'vue'
 
 const defaultPicUrl = ref(
   import.meta.env.VITE_BASE_URL + '/admin-api/infra/file/29/get/IntegratedSolution.png'
@@ -261,7 +297,7 @@ const formData = ref({
 })
 const pics = ref([])
 const imgSrc = ref('')
-const loadedTabs = ref(['info']); // 记录已加载的标签
+const loadedTabs = ref(['info']) // 记录已加载的标签
 
 /** 获得详情 */
 const getDetail = async () => {
@@ -294,7 +330,7 @@ const imagePreview = (imgUrl: string) => {
 const handleTabClick = (tab) => {
   if (!loadedTabs.value.includes(tab.index)) {
     // 这里可以添加每个标签对应的加载逻辑,如果有的话
-    loadedTabs.value.push(tab.index);
+    loadedTabs.value.push(tab.index)
   }
 }
 

+ 114 - 0
src/views/pms/device/completeSet/AssociationDevices.vue

@@ -0,0 +1,114 @@
+<template>
+  <div v-loading="loading" style="height: 100%">
+    <ContentWrap>
+      <!-- 搜索工作栏 -->
+      <el-form
+        class="-mb-15px"
+        :model="queryParams"
+        ref="queryFormRef"
+        :inline="true"
+        label-width="68px"
+      >
+        <el-form-item label="设备编码" prop="deviceCode">
+          <el-input
+            v-model="queryParams.deviceCode"
+            placeholder="请输入设备编码"
+            clearable
+            @keyup.enter="handleQuery"
+            class="!w-200px"
+          />
+        </el-form-item>
+        <el-form-item>
+          <el-button @click="handleQuery"
+            ><Icon icon="ep:search" class="mr-5px" /> {{ t('devicePerson.search') }}</el-button
+          >
+          <el-button @click="resetQuery"
+            ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('devicePerson.reset') }}</el-button
+          >
+        </el-form-item>
+      </el-form>
+    </ContentWrap>
+    <el-table :data="deviceList" style="width: 100%">
+      <!-- 序号 -->
+      <el-table-column :label="t('monitor.serial')" width="70" align="center">
+        <template #default="scope">
+          {{ scope.$index + 1 }}
+        </template>
+      </el-table-column>
+      <el-table-column prop="groupName" label="成套名称" align="center" />
+      <el-table-column prop="deviceName" :label="t('iotDevice.name')" align="center" />
+      <el-table-column prop="deviceCode" label="设备编码" align="center" />
+      <el-table-column prop="ifMaster" label="是否主设备" align="center">
+        <template #default="scope">
+          <el-tag v-if="scope.row.ifMaster" class="text-[#62a66a]">是</el-tag>
+
+          <el-tag v-else class="text-[#9093a6]" type="info">否</el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="ifMaster" label="创建时间" align="center">
+        <template #default="scope">
+          {{ formatToDate(scope.row.createTime) }}
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页 -->
+    <Pagination
+      :total="total"
+      v-model:page="queryParams.pageNo"
+      v-model:limit="queryParams.pageSize"
+      @pagination="getDeviceList(props.deviceId)"
+    />
+  </div>
+</template>
+<script setup lang="ts">
+import { ref } from 'vue'
+import { formatToDate } from '@/utils/dateUtil'
+import { IotDeviceApi } from '@/api/pms/device'
+const { t } = useI18n() // 国际化
+defineOptions({
+  name: 'AssociationDevices'
+})
+
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  deviceId: '',
+  deviceCode: ''
+})
+const loading = ref(false)
+const total = ref(0) // 列表的总页数
+const deviceList = ref([])
+
+const props = defineProps({
+  deviceId: Number
+})
+
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getDeviceList(props.deviceId)
+}
+
+const resetQuery = () => {
+  queryParams.deviceCode = ''
+  handleQuery()
+}
+
+const getDeviceList = async (id) => {
+  loading.value = true
+  try {
+    queryParams.deviceId = id
+    const res = await IotDeviceApi.getIotDeviceSetRelation(queryParams)
+    deviceList.value = res.list
+    total.value = res.total
+  } catch (error) {
+  } finally {
+    loading.value = false
+  }
+}
+
+onMounted(() => {
+  getDeviceList(props.deviceId)
+})
+</script>
+
+<style lang="scss" scoped></style>

+ 528 - 0
src/views/pms/device/completeSet/DeviceCompleteSet.vue

@@ -0,0 +1,528 @@
+<template>
+  <el-row :gutter="20">
+    <!-- 左侧部门树 -->
+    <el-col :span="4" :xs="24">
+      <ContentWrap class="h-1/1" v-if="treeShow">
+        <DeptTree @node-click="handleDeptNodeClick" />
+      </ContentWrap>
+    </el-col>
+    <el-col :span="contentSpan" :xs="24">
+      <ContentWrap>
+        <!-- 搜索工作栏 -->
+        <el-form
+          class="-mb-15px"
+          :model="queryParams"
+          ref="queryFormRef"
+          :inline="true"
+          label-width="68px"
+        >
+          <!-- <el-form-item label="部门名称" prop="deptName" style="margin-left: 20px">
+            <el-input
+              v-model="queryParams.deptName"
+              placeholder="请输入部门名称"
+              clearable
+              @keyup.enter="handleQuery"
+              class="!w-200px"
+            />
+          </el-form-item> -->
+          <el-form-item label="成套名称" prop="name">
+            <el-input
+              v-model="queryParams.name"
+              placeholder="请输入成套名称"
+              clearable
+              @keyup.enter="handleQuery"
+              class="!w-200px"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button @click="handleAdd" type="primary"
+              ><Icon icon="ep:plus" class="mr-5px" />新增成套</el-button
+            >
+            <el-button @click="handleQuery"
+              ><Icon icon="ep:search" class="mr-5px" /> {{ t('devicePerson.search') }}</el-button
+            >
+            <el-button @click="resetQuery"
+              ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('devicePerson.reset') }}</el-button
+            >
+          </el-form-item>
+        </el-form>
+      </ContentWrap>
+
+      <!-- 列表 -->
+      <ContentWrap>
+        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+          <el-table-column :label="t('monitor.serial')" width="70" align="center">
+            <template #default="scope">
+              {{ scope.$index + 1 }}
+            </template>
+          </el-table-column>
+          <el-table-column label="部门名称" align="center" prop="deptName" />
+          <el-table-column label="成套名称" align="center" prop="name" />
+
+          <el-table-column label="描述" align="center" prop="remark" />
+          <el-table-column label="设备数量" align="center" prop="deviceCount">
+            <template #default="scope">
+              {{ (scope.row.details && scope.row.details.length) || 0 }}
+            </template>
+          </el-table-column>
+          <el-table-column label="主设备" align="center" prop="mainDeviceName">
+            <template #default="scope">
+              {{
+                scope.row.details.filter((item) => item.ifMaster)[0]?.deviceName +
+                  ' ' +
+                  scope.row.details.filter((item) => item.ifMaster)[0]?.deviceCode || '无'
+              }}
+            </template>
+          </el-table-column>
+
+          <el-table-column :label="t('devicePerson.operation')" align="center" min-width="120px">
+            <template #default="scope">
+              <el-button link type="primary" @click="handleEdit(scope.row)"> 编辑 </el-button>
+              <el-button link type="danger" @click="handleDelete(scope.row.id)"> 删除 </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <!-- 分页 -->
+        <Pagination
+          :total="total"
+          v-model:page="queryParams.pageNo"
+          v-model:limit="queryParams.pageSize"
+          @pagination="getList"
+        />
+      </ContentWrap>
+    </el-col>
+  </el-row>
+
+  <!-- 新增/编辑成套设备对话框 -->
+  <el-dialog :title="dialogTitle" v-model="dialogVisible" width="800px" @close="cancel">
+    <template #header>
+      <div class="my-header" style="padding-bottom: 20px">
+        <span>{{ dialogTitle }}</span>
+      </div>
+    </template>
+    <el-form ref="formRef" :model="formData" :inline="true" :rules="formRules" label-width="80px">
+      <el-form-item label="成套名称" prop="name">
+        <el-input v-model="formData.name" placeholder="请输入成套名称" />
+      </el-form-item>
+
+      <el-form-item :label="t('iotDevice.dept')" prop="deptId">
+        <el-tree-select
+          v-model="formData.deptId"
+          :data="deptList"
+          :props="defaultProps"
+          check-strictly
+          node-key="id"
+          filterable
+          placeholder="请选择所在部门"
+          @change="handleDeptChange"
+          style="width: 200px"
+        />
+      </el-form-item>
+
+      <el-form-item label="描述" prop="remark">
+        <el-input
+          v-model="formData.remark"
+          style="width: 470px"
+          type="textarea"
+          placeholder="请输入描述"
+          :rows="2"
+        />
+      </el-form-item>
+
+      <el-form-item label="选择设备" prop="devices">
+        <div class="transfer-container">
+          <el-transfer
+            v-model="selectedDeviceIds"
+            :data="deviceOptions"
+            :titles="['设备列表', '已选择设备']"
+            :button-texts="['移除', '添加']"
+            filterable
+            :filter-method="filterDeviceMethod"
+            filter-placeholder="请输入设备名称"
+            @change="rightDeviceChange"
+          >
+            <template #left-empty>
+              <el-empty :image-size="60" :description="isEdit ? '加载中...' : '请选择设备'" />
+            </template>
+            <template #right-empty>
+              <el-empty :image-size="60" :description="isEdit ? '加载中...' : '请选择设备'" />
+            </template>
+          </el-transfer>
+        </div>
+      </el-form-item>
+
+      <div v-if="selectedDevices.length > 0" class="mt-4">
+        <el-form-item label="设置主设备" prop="mainDevice" label-width="100">
+          <el-select
+            v-model="mainDeviceId"
+            placeholder="请选择主设备"
+            clearable
+            filterable
+            style="width: 200px"
+            @change="setMainDevice"
+          >
+            <el-option
+              v-for="device in selectedDevices"
+              :key="device.id"
+              :label="device.label"
+              :value="device.id"
+            />
+          </el-select>
+        </el-form-item>
+      </div>
+    </el-form>
+
+    <template #footer>
+      <el-button @click="cancel">取 消</el-button>
+      <el-button type="primary" @click="submit">确 定</el-button>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { IotDeviceApi } from '@/api/pms/device'
+import DeptTree from '@/views/system/user/DeptTree.vue'
+import { defaultProps, handleTree } from '@/utils/tree'
+import * as DeptApi from '@/api/system/dept'
+import { ElMessageBox } from 'element-plus'
+const deptList = ref<Tree[]>([]) // 树形结构
+
+defineOptions({ name: 'IotDeviceComplete' })
+
+const loading = ref(true) // 列表的加载中
+
+const { t } = useI18n()
+
+const list = ref([]) // 列表的数据
+const total = ref(0) // 列表的总页数
+const queryParams = reactive({
+  pageNo: 1,
+  pageSize: 10,
+  deptName: undefined,
+  name: undefined,
+  deptId: undefined
+})
+const queryFormRef = ref(null) // 搜索的表单
+
+const contentSpan = ref(20)
+const treeShow = ref(true)
+
+// 对话框相关
+const dialogVisible = ref(false)
+const dialogTitle = ref('')
+const isEdit = ref(false)
+
+// 表单相关
+const formRef = ref()
+const formData = ref({
+  name: '',
+  details: [],
+  deptId: '',
+  remark: ''
+})
+
+// 表单验证规则
+const formRules = {
+  name: [{ required: true, message: '成套名称不能为空', trigger: 'blur' }],
+  code: [{ required: true, message: '成套编码不能为空', trigger: 'blur' }],
+  deptId: [{ required: true, message: '请选择部门', trigger: 'change' }],
+  devices: [
+    {
+      required: true,
+      validator: (rule: any, value: any, callback: any) => {
+        if (selectedDeviceIds.value.length === 0) {
+          callback(new Error('请至少选择一个设备'))
+        } else {
+          callback()
+        }
+      },
+      trigger: 'change'
+    }
+  ],
+  mainDevice: [
+    {
+      required: true,
+      validator: (rule: any, value: any, callback: any) => {
+        if (!mainDeviceId.value) {
+          callback(new Error('请选择主设备'))
+        } else {
+          callback()
+        }
+      },
+      trigger: 'change'
+    }
+  ]
+}
+
+// 部门树数据
+const selectedDeptId = ref<number | string>('')
+
+// 新增时部门改变时获取设备列表
+const handleDeptChange = async (deptId) => {
+  if (deptId) {
+    selectedDeptId.value = deptId
+    getDeviceList(deptId)
+  }
+
+  // 清空主设备she 设备列表
+  selectedDevices.value = []
+  mainDeviceId.value = ''
+  selectedDeviceIds.value = []
+}
+// 获取设备列表
+const getDeviceList = async (deptId) => {
+  try {
+    const res = await IotDeviceApi.getIotDeviceSetOptions(deptId)
+    deviceOptions.value = res.map((item) => ({
+      key: item.id, // 始终使用id作为key
+      label: `${item.deviceName} (${item.deviceCode})`,
+      ...item
+    }))
+  } catch (err) {
+    console.error(err)
+  }
+}
+
+// 设备选择相关
+const deviceOptions = ref<any[]>([])
+const selectedDeviceIds = ref([])
+const selectedDevices = ref([])
+const mainDeviceId = ref('')
+
+/** 查询列表 */
+const getList = async () => {
+  loading.value = true
+  try {
+    const data = await IotDeviceApi.getIotDeviceSetList(queryParams)
+    list.value = data.list
+    total.value = data.total
+  } finally {
+    loading.value = false
+  }
+}
+
+/** 首页处理部门被点击 */
+const handleDeptNodeClick = async (row) => {
+  queryParams.deptId = row.id
+  await getList()
+}
+
+/** 搜索按钮操作 */
+const handleQuery = () => {
+  queryParams.pageNo = 1
+  getList()
+}
+
+/** 重置按钮操作 */
+const resetQuery = () => {
+  queryFormRef.value.resetFields()
+  handleQuery()
+}
+
+// 设备过滤方法
+const filterDeviceMethod = (query, item) => {
+  return item.label.toLowerCase().includes(query.toLowerCase())
+}
+
+// 更新已选择的设备列表
+const updateSelectedDevices = () => {
+  // 根据 selectedDeviceIds 从 deviceOptions 中找到对应的设备
+  selectedDevices.value = deviceOptions.value.filter((item) =>
+    selectedDeviceIds.value.includes(item.key)
+  )
+
+  console.log('selectedDevices>>>>>>>>>>>>>>>>', selectedDevices.value)
+
+  // 构建 details 数据
+  formData.value.details = selectedDevices.value.map((item) => ({
+    deviceId: item.id,
+    deviceName: item.deviceName,
+    deviceCode: item.deviceCode,
+    deptId: item.deptId,
+    ifMaster: item.id === mainDeviceId.value ? true : false
+  }))
+
+  // 如果主设备不在当前选择中,则清空主设备
+  if (
+    mainDeviceId.value &&
+    !selectedDevices.value.some((device) => device.id === mainDeviceId.value)
+  ) {
+    mainDeviceId.value = ''
+  }
+}
+
+const rightDeviceChange = (val) => {
+  selectedDeviceIds.value = val
+  updateSelectedDevices()
+
+  // 手动触发验证
+  if (formRef.value) {
+    formRef.value.validateField('devices')
+    if (val.length > 0) {
+      formRef.value.validateField('mainDevice')
+    }
+  }
+}
+
+// 设置主设备
+const setMainDevice = (val) => {
+  mainDeviceId.value = val
+  // 更新 details 中的 ifMaster 字段
+  console.log('selectedDevices.value>>>>>>>>>>>>>>>>', selectedDevices.value)
+
+  formData.value.details = selectedDevices.value.map((item) => ({
+    deviceId: item.id,
+    deviceName: item.deviceName,
+    deviceCode: item.deviceCode,
+    deptId: item.deptId,
+    ifMaster: item.id === mainDeviceId.value ? true : false
+  }))
+
+  console.log('formData.value.details>>>>>>>>>>>>>>>>', formData.value.details)
+
+  // 手动触发验证
+  if (formRef.value) {
+    formRef.value.validateField('mainDevice')
+  }
+}
+
+// 显示新增对话框
+const handleAdd = () => {
+  isEdit.value = false
+  dialogTitle.value = '新增成套设备'
+  resetForm()
+  dialogVisible.value = true
+}
+
+// 显示编辑对话框
+const handleEdit = (row) => {
+  isEdit.value = true
+  dialogTitle.value = '编辑成套设备'
+
+  formData.value = {
+    ...row
+  }
+
+  // 先清空之前的选项
+  selectedDeviceIds.value = []
+  selectedDevices.value = []
+  deviceOptions.value = []
+
+  // 获取设备列表后再设置已选择的设备
+  getDeviceList(row.deptId).then(() => {
+    // 设置已选择的设备IDs(使用deviceOptions中的key值)
+    selectedDeviceIds.value = row.details.map((item) => {
+      // 在编辑模式下,使用deviceOptions中对应项的key作为标识
+      const option = deviceOptions.value.find((opt) => opt.deviceId === item.deviceId)
+      return option ? option.key : item.deviceId
+    })
+
+    mainDeviceId.value = row.details.find((item) => item.ifMaster)?.deviceId || ''
+
+    // 更新selectedDevices数组
+    nextTick(() => {
+      updateSelectedDevices()
+    })
+  })
+
+  dialogVisible.value = true
+}
+//删除成套
+const handleDelete = async (id: number) => {
+  ElMessageBox.confirm('确定要删除该成套设备吗?', '提示', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  })
+    .then(async () => {
+      try {
+        await IotDeviceApi.deleteIotDeviceSet(id)
+        ElMessage.success('删除成功')
+        getList()
+      } catch (error) {
+        console.error(error)
+      }
+    })
+    .catch(() => {
+      // 取消操作
+    })
+}
+
+// 重置表单
+const resetForm = () => {
+  formData.value = {
+    name: '',
+    details: [],
+    deptId: '',
+    remark: ''
+  }
+  selectedDeviceIds.value = []
+  selectedDevices.value = []
+  deviceOptions.value = []
+  mainDeviceId.value = ''
+}
+
+// 取消操作
+const cancel = () => {
+  dialogVisible.value = false
+  resetForm()
+}
+
+// 提交表单
+const submit = async () => {
+  if (!formRef.value) return
+
+  await formRef.value.validate(async (valid) => {
+    if (!valid) return
+    try {
+      const data = {
+        ...formData.value
+      }
+
+      console.log('提交数据:', data)
+
+      if (isEdit.value) {
+        await IotDeviceApi.updateIotDeviceSet(data)
+        ElMessage.success('编辑成功')
+      } else {
+        await IotDeviceApi.createIotDeviceSet(data)
+        ElMessage.success('新增成功')
+      }
+
+      dialogVisible.value = false
+      getList()
+    } catch (error) {
+      console.error(error)
+    }
+  })
+}
+
+onMounted(async () => {
+  getList()
+
+  deptList.value = handleTree(await DeptApi.getSimpleDeptList())
+})
+</script>
+
+<style scoped>
+.transfer-container {
+  display: flex;
+  flex-direction: column;
+}
+
+.transfer-footer {
+  margin-top: 8px;
+  text-align: right;
+}
+
+::deep(.el-transfer-panel) {
+  width: 300px;
+}
+
+::deep(.el-tree--highlight-current) {
+  height: 200px !important;
+}
+::deep(.el-transfer-panel__body) {
+  height: 700px !important;
+}
+</style>

Энэ ялгаанд хэт олон файл өөрчлөгдсөн тул зарим файлыг харуулаагүй болно