Преглед изворни кода

✨ feat: 设备动态添加创建时间查询,设备调拨责任人为必填

Zimo пре 5 часа
родитељ
комит
463272d680

+ 81 - 70
src/views/pms/device/allotlog/ConfigDeviceAllot.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="container" >
+  <div class="container">
     <el-row :gutter="20" class="equal-height-row">
       <!-- 左侧设备列表 -->
       <el-col :span="12" class="col-height">
@@ -32,13 +32,10 @@
 
           <el-scrollbar height="500px">
             <el-checkbox-group v-model="selectedDevices">
-              <div
-                v-for="device in filteredDevices"
-                :key="device.id"
-                class="checkbox-item"
-              >
+              <div v-for="device in filteredDevices" :key="device.id" class="checkbox-item">
                 <el-checkbox :label="device.id">
-                  {{ device.deviceCode }} ({{ device.deviceName }}) - {{ device.deptName }} —— {{ device.devicePersons }}
+                  {{ device.deviceCode }} ({{ device.deviceName }}) - {{ device.deptName }} ——
+                  {{ device.devicePersons }}
                 </el-checkbox>
               </div>
             </el-checkbox-group>
@@ -51,7 +48,12 @@
         <div class="card right-card">
           <h3>{{ t('configDevice.deptList') }}</h3>
           <ContentWrap class="dept-tree-container" height="400px">
-            <DeptTree2 ref="deptTreeRef" v-model="selectedDeptId" height="100%" @update:modelValue="handleDeptChange"/>
+            <DeptTree2
+              ref="deptTreeRef"
+              v-model="selectedDeptId"
+              height="100%"
+              @update:model-value="handleDeptChange"
+            />
           </ContentWrap>
         </div>
       </el-col>
@@ -83,7 +85,10 @@
           </div>
 
           <div class="control-group">
-            <label class="control-title">{{ t('configDevice.reasonForAdjustment') }}<span class="required-star">*</span></label>
+            <label class="control-title"
+              >{{ t('configDevice.reasonForAdjustment')
+              }}<span class="required-star">*</span></label
+            >
             <div class="reason-input-wrapper">
               <el-input
                 v-model="formData.reason"
@@ -104,15 +109,11 @@
         <el-table :data="tempRelations" style="width: 100%">
           <el-table-column prop="deviceNames" label="设备" width="200" />
           <el-table-column prop="deptName" label="部门" />
-          <el-table-column prop="deptId" label="部门id" v-if="false"/>
+          <el-table-column prop="deptId" label="部门id" v-if="false" />
           <el-table-column prop="reason" label="调拨原因" />
           <el-table-column label="操作" width="120">
             <template #default="{ row }">
-              <el-button
-                type="danger"
-                size="small"
-                @click="removeTempRelation(row.deviceId)"
-              >
+              <el-button type="danger" size="small" @click="removeTempRelation(row.deviceId)">
                 删除
               </el-button>
             </template>
@@ -123,13 +124,12 @@
       <el-button
         type="primary"
         size="large"
-        style="min-width: 180px;"
+        style="min-width: 180px"
         @click="submitRelations"
         :disabled="tempRelations.length === 0 || !formData.reason.trim()"
       >
         {{ t('iotMaintain.save') }}
       </el-button>
-
     </div>
   </div>
 </template>
@@ -137,14 +137,14 @@
 <script setup lang="ts">
 import { ref, computed, onMounted } from 'vue'
 import { ElMessage } from 'element-plus'
-import {defaultProps, handleTree} from "@/utils/tree";
-import * as DeptApi from "@/api/system/dept";
-import * as UserApi from "@/api/system/user";
-import {IotDeviceApi, IotDeviceVO} from "@/api/pms/device";
-import DeptTree2 from "@/views/pms/device/DeptTree2.vue";
+import { defaultProps, handleTree } from '@/utils/tree'
+import * as DeptApi from '@/api/system/dept'
+import * as UserApi from '@/api/system/user'
+import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
+import DeptTree2 from '@/views/pms/device/DeptTree2.vue'
 import { useRouter, useRoute } from 'vue-router'
-import { useTagsViewStore } from "@/store/modules/tagsView";
-import {UserVO} from "@/api/system/user";
+import { useTagsViewStore } from '@/store/modules/tagsView'
+import { UserVO } from '@/api/system/user'
 
 const router = useRouter()
 const route = useRoute() // 获取路由参数
@@ -168,33 +168,36 @@ const formData = ref({
   deviceStatus: undefined,
   reason: '',
   assetProperty: undefined,
-  picUrl: undefined,
+  picUrl: undefined
 })
 
 const queryParams = reactive({
-  deptId: formData.value.deptId,
+  deptId: formData.value.deptId
 })
 
 const deptTreeRef = ref<InstanceType<typeof DeptTree2>>()
 
 const emit = defineEmits(['success', 'node-click']) // 定义 success 树点击 事件,用于操作成功后的回调
 
-const simpleUsers = ref<UserVO[]>([])     // 人员下拉列表选项
+const simpleUsers = ref<UserVO[]>([]) // 人员下拉列表选项
 const selectedPersons = ref<number[]>([]) // 存储选中的人员ID
 
 // 响应式数据
-const tempRelationsMap = ref(new Map<number, {
-  deviceId: number
-  deviceNames: string
-  deptId: number
-  deptName: string
-  reason: string
-}>())
+const tempRelationsMap = ref(
+  new Map<
+    number,
+    {
+      deviceId: number
+      deviceNames: string
+      deptId: number
+      deptName: string
+      reason: string
+    }
+  >()
+)
 
 // 计算属性转换 Map 为数组
-const tempRelations = computed(() =>
-  Array.from(tempRelationsMap.value.values())
-)
+const tempRelations = computed(() => Array.from(tempRelationsMap.value.values()))
 
 const updateTempRelations = () => {
   if (!selectedDeptId.value) {
@@ -218,8 +221,8 @@ const updateTempRelations = () => {
   })
 
   // 添加/更新当前选中设备的关联
-  selectedDevices.value.forEach(deviceId => {
-    const device = simpleDevices.value.find(d => d.id === deviceId)
+  selectedDevices.value.forEach((deviceId) => {
+    const device = simpleDevices.value.find((d) => d.id === deviceId)
     if (device) {
       tempRelationsMap.value.set(deviceId, {
         deviceId,
@@ -233,9 +236,13 @@ const updateTempRelations = () => {
 }
 
 // 添加设备选择监听
-watch([selectedDevices, selectedDeptId], () => {
-  updateTempRelations();
-}, {deep: true, immediate: true, debounce: 100})
+watch(
+  [selectedDevices, selectedDeptId],
+  () => {
+    updateTempRelations()
+  },
+  { deep: true, immediate: true, debounce: 100 }
+)
 
 // 监听部门变化
 watch(selectedDeptId, (newVal) => {
@@ -302,7 +309,7 @@ const loadDeptPersons = async (deptId: number) => {
     // 调用API获取部门人员
     const params = { deptId: deptId, pageNo: 1, pageSize: 10 }
     const data = await UserApi.simpleUserList(params)
-    simpleUsers.value = data.map(user => ({
+    simpleUsers.value = data.map((user) => ({
       id: user.id,
       nickname: user.nickname || user.username
     }))
@@ -313,12 +320,11 @@ const loadDeptPersons = async (deptId: number) => {
 }
 
 // 计算选中的设备原部门集合
-const originDeptIds = computed(() =>
-  selectedDevices.value
-    .map(deviceId =>
-      simpleDevices.value.find(d => d.id === deviceId)?.deptId
-    )
-    .filter(Boolean) as number[]
+const originDeptIds = computed(
+  () =>
+    selectedDevices.value
+      .map((deviceId) => simpleDevices.value.find((d) => d.id === deviceId)?.deptId)
+      .filter(Boolean) as number[]
 )
 
 // 部门选择校验方法
@@ -338,7 +344,7 @@ const filteredDevices = computed(() => {
   const searchText = deviceFilterText.value.toLowerCase().trim()
   if (!searchText) return simpleDevices.value
 
-  return simpleDevices.value.filter(device => {
+  return simpleDevices.value.filter((device) => {
     return (
       (device.deviceCode || '').toLowerCase().includes(searchText) ||
       (device.deviceName || '').toLowerCase().includes(searchText)
@@ -348,29 +354,35 @@ const filteredDevices = computed(() => {
 
 // 在计算属性区域添加 selectedPersonNames
 const selectedPersonNames = computed(() => {
-  return selectedPersons.value.map(id => {
-    const user = simpleUsers.value.find(u => u.id === id)
-    return user?.nickname || ''
-  }).filter(Boolean) // 过滤掉空值
+  return selectedPersons.value
+    .map((id) => {
+      const user = simpleUsers.value.find((u) => u.id === id)
+      return user?.nickname || ''
+    })
+    .filter(Boolean) // 过滤掉空值
 })
 
 const removeTempRelation = (deviceId: number) => {
   tempRelationsMap.value.delete(deviceId)
-  selectedDevices.value = selectedDevices.value.filter(id => id !== deviceId)
-};
+  selectedDevices.value = selectedDevices.value.filter((id) => id !== deviceId)
+}
 
 const submitRelations = async () => {
   try {
     // 提交前二次校验
-    const invalidDept = tempRelations.value.some(r =>
-      originDeptIds.value.includes(r.deptId)
-    )
+    const invalidDept = tempRelations.value.some((r) => originDeptIds.value.includes(r.deptId))
     if (invalidDept) {
       ElMessage.error('存在调拨部门与设备原属部门相同的情况,请检查')
       return
     }
+
+    if (!selectedPersons.value.length) {
+      ElMessage.error('请选择责任人')
+      return
+    }
+
     // 转换为后端需要的格式
-    const submitData = tempRelations.value.map(r => ({
+    const submitData = tempRelations.value.map((r) => ({
       deviceId: r.deviceId,
       deptId: r.deptId,
       reason: r.reason,
@@ -408,14 +420,14 @@ onMounted(async () => {
       // 否则使用第一个节点
       targetDeptId = deptList.value[0].id
     } else {
-      console.warn("部门树数据为空,无法设置默认值")
+      console.warn('部门树数据为空,无法设置默认值')
       return
     }
 
     // 设置默认选中的部门(转换后树的第一个节点)
     // if (deptList.value.length > 0) {
-      // 获取转换后树的第一个节点
-      // const firstRootNode = deptList.value[0]
+    // 获取转换后树的第一个节点
+    // const firstRootNode = deptList.value[0]
 
     // 设置设备部门的默认值
     formData.value.deptId = targetDeptId
@@ -426,11 +438,11 @@ onMounted(async () => {
       await handleDeptDeviceTreeNodeClick(targetDeptNode)
     }
     // } else {
-      // console.warn("部门树数据为空,无法设置默认值")
+    // console.warn("部门树数据为空,无法设置默认值")
     // }
   } catch (error) {
-    console.error("初始化部门树失败:", error)
-    ElMessage.error("加载部门数据失败")
+    console.error('初始化部门树失败:', error)
+    ElMessage.error('加载部门数据失败')
   }
 
   nextTick(() => {
@@ -452,7 +464,6 @@ const findDeptNode = (nodes: Tree[], deptId: number): Tree | null => {
   }
   return null
 }
-
 </script>
 
 <style scoped>
@@ -469,12 +480,12 @@ const findDeptNode = (nodes: Tree[], deptId: number): Tree | null => {
   display: flex;
   flex-direction: column;
   height: 100%; /* 根据实际需要调整整体高度 */
-  box-shadow: 0 2px 12px 0 rgba(0,0,0,0.1);
+  box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
   transition: box-shadow 0.2s;
 }
 
 .card:hover {
-  box-shadow: 0 4px 16px rgba(0,0,0,0.15);
+  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
 }
 
 .list-item {
@@ -559,7 +570,7 @@ h3 {
 .col-height {
   display: flex;
   flex-direction: column;
-  min-height: 400px;/* 统一最小高度 */
+  min-height: 400px; /* 统一最小高度 */
 }
 
 .filter-input {

+ 16 - 2
src/views/pms/device/allotlog/DeviceAllot.vue

@@ -38,7 +38,7 @@
               class="!w-200px"
             />
           </el-form-item>
-          <el-form-item :label="t('deviceStatus.transfer')" prop="setFlag" label-width="140px">
+          <el-form-item :label="t('deviceStatus.transfer')" prop="setFlag">
             <el-select
               v-model="queryParams.setFlag"
               :placeholder="t('deviceStatus.choose')"
@@ -53,6 +53,18 @@
               />
             </el-select>
           </el-form-item>
+          <el-form-item label="创建时间">
+            <el-date-picker
+              v-model="queryParams.createTime"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :shortcuts="rangeShortcuts"
+              :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+              class="!w-220px"
+            />
+          </el-form-item>
           <el-form-item
             v-show="ifShow"
             :label="t('devicePerson.status')"
@@ -204,6 +216,7 @@ import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 import DeptTree from '@/views/system/user/DeptTree.vue'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import DeviceAllotLogDrawer from '@/views/pms/device/allotlog/DeviceAllotLogDrawer.vue'
+import { rangeShortcuts } from '@/utils/formatTime'
 
 /** 设备调拨 列表 */
 defineOptions({ name: 'IotDeviceAllot' })
@@ -234,7 +247,8 @@ const queryParams = reactive({
   nameplate: undefined,
   expires: undefined,
   creator: undefined,
-  setFlag: ''
+  setFlag: '',
+  createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中

+ 15 - 1
src/views/pms/device/personlog/DevicePerson.vue

@@ -67,6 +67,18 @@
               class="!w-200px"
             />
           </el-form-item>
+          <el-form-item label="创建时间">
+            <el-date-picker
+              v-model="queryParams.createTime"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :shortcuts="rangeShortcuts"
+              :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+              class="!w-220px"
+            />
+          </el-form-item>
 
           <el-form-item>
             <el-button @click="handleQuery"
@@ -146,6 +158,7 @@ import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
 import DeptTree from '@/views/system/user/DeptTree.vue'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import DevicePersonLogDrawer from '@/views/pms/device/personlog/DevicePersonLogDrawer.vue'
+import { rangeShortcuts } from '@/utils/formatTime'
 
 /** 设备台账 列表 */
 defineOptions({ name: 'IotDevicePerson' })
@@ -190,7 +203,8 @@ const queryParams = reactive({
   templateJson: undefined,
   creator: undefined,
   setFlag: '',
-  nickname: ''
+  nickname: '',
+  createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中

+ 16 - 2
src/views/pms/device/statuslog/DeviceStatus.vue

@@ -38,7 +38,7 @@
               class="!w-200px"
             />
           </el-form-item>
-          <el-form-item :label="t('deviceStatus.statusAdjust')" prop="setFlag" label-width="140px">
+          <el-form-item :label="t('deviceStatus.statusAdjust')" prop="setFlag" label-width="90px">
             <el-select
               v-model="queryParams.setFlag"
               :label="t('deviceStatus.statusAdjust')"
@@ -89,6 +89,18 @@
               />
             </el-select>
           </el-form-item>
+          <el-form-item label="创建时间">
+            <el-date-picker
+              v-model="queryParams.createTime"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :shortcuts="rangeShortcuts"
+              :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+              class="!w-220px"
+            />
+          </el-form-item>
 
           <el-form-item v-show="ifShow" :label="t('devicePerson.brand')" prop="brand">
             <el-input
@@ -199,6 +211,7 @@ import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 import DeptTree from '@/views/system/user/DeptTree.vue'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import DeviceStatusLogDrawer from '@/views/pms/device/statuslog/DeviceStatusLogDrawer.vue'
+import { rangeShortcuts } from '@/utils/formatTime'
 
 /** 设备台账 列表 */
 defineOptions({ name: 'IotDeviceStatus' })
@@ -243,7 +256,8 @@ const queryParams = reactive({
   infoUrl: undefined,
   templateJson: undefined,
   creator: undefined,
-  setFlag: ''
+  setFlag: '',
+  createTime: []
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中