浏览代码

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

yanghao 1 天之前
父节点
当前提交
98289fc1d8
共有 33 个文件被更改,包括 2165 次插入1409 次删除
  1. 28 0
      src/api/pms/qhse/index.ts
  2. 二进制
      src/assets/imgs/leftTree-hide.png
  3. 二进制
      src/assets/imgs/leftTree-show.png
  4. 5 0
      src/components/FilePreview/index.ts
  5. 363 0
      src/components/FilePreview/src/FilePreviewDialog.vue
  6. 2 1
      src/utils/dict.ts
  7. 11 8
      src/views/pms/device/allotlog/DeviceAllot.vue
  8. 11 8
      src/views/pms/device/completeSet/DeviceCompleteSet.vue
  9. 5 9
      src/views/pms/device/index.vue
  10. 4 7
      src/views/pms/device/monitor/index.vue
  11. 11 8
      src/views/pms/device/personlog/DevicePerson.vue
  12. 11 8
      src/views/pms/device/statuslog/DeviceStatus.vue
  13. 68 31
      src/views/pms/failure/index.vue
  14. 138 131
      src/views/pms/inspect/item/index.vue
  15. 5 8
      src/views/pms/inspect/order/index.vue
  16. 115 90
      src/views/pms/inspect/plan/index.vue
  17. 135 132
      src/views/pms/inspect/route/index.vue
  18. 11 9
      src/views/pms/iotmainworkorder/IotDeviceMainAlarm.vue
  19. 5 79
      src/views/pms/iotmainworkorder/index.vue
  20. 5 9
      src/views/pms/iotopeationfill/index.vue
  21. 117 91
      src/views/pms/iotopeationfill/plan/index.vue
  22. 5 8
      src/views/pms/maintain/index.vue
  23. 11 9
      src/views/pms/maintenance/index.vue
  24. 21 14
      src/views/pms/monitor/index.vue
  25. 13 10
      src/views/pms/qhse/certificate.vue
  26. 137 126
      src/views/pms/qhse/faultReport/index.vue
  27. 15 9
      src/views/pms/qhse/index.vue
  28. 145 98
      src/views/pms/qhse/iotmeasuredetect/index.vue
  29. 455 481
      src/views/pms/qhse/safety/index.vue
  30. 4 8
      src/views/report-statistics/device_book/index2.vue
  31. 5 9
      src/views/report-statistics/fault_report/index.vue
  32. 4 8
      src/views/report-statistics/inspection_order/index.vue
  33. 300 0
      src/views/system/user/DeptTree2.vue

+ 28 - 0
src/api/pms/qhse/index.ts

@@ -259,3 +259,31 @@ export const IotApprovalApi = {
     return await request.get({ url: `/rq/iot-accident-report/approval`, params })
   }
 }
+
+// 隐患排查
+export const IotHiddenApi = {
+  // 获得隐患排查分页
+  getHiddenList: async (params) => {
+    return await request.get({ url: `/rq/iot-hazard/page`, params })
+  },
+  // 删除隐患排查
+  deleteHidden: async (id) => {
+    return await request.delete({ url: `/rq/iot-hazard/delete?id=` + id })
+  },
+  // 添加隐患排查
+  createHidden: async (data) => {
+    return await request.post({ url: `/rq/iot-hazard/create`, data })
+  },
+  // 修改隐患排查
+  updateHidden: async (data) => {
+    return await request.put({ url: `/rq/iot-hazard/update`, data })
+  },
+  // 导出隐患排查 Excel
+  exportHidden: async (params) => {
+    return await request.download({ url: `/rq/iot-hazard/export-excel`, params })
+  },
+  // 整改
+  rectifyHidden: async (data) => {
+    return await request.put({ url: `/rq/iot-hazard/rectify`, data })
+  }
+}

二进制
src/assets/imgs/leftTree-hide.png


二进制
src/assets/imgs/leftTree-show.png


+ 5 - 0
src/components/FilePreview/index.ts

@@ -0,0 +1,5 @@
+import FilePreviewDialog from './src/FilePreviewDialog.vue'
+
+export { FilePreviewDialog }
+export default FilePreviewDialog
+

+ 363 - 0
src/components/FilePreview/src/FilePreviewDialog.vue

@@ -0,0 +1,363 @@
+<template>
+  <el-dialog
+    v-model="visible"
+    :title="title"
+    :width="width"
+    append-to-body
+    destroy-on-close
+    class="file-preview-dialog"
+  >
+    <div class="toolbar">
+      <div class="left">
+        <el-select
+          v-if="normalizedUrls.length > 1"
+          v-model="activeUrl"
+          filterable
+          class="file-select"
+          placeholder="选择文件"
+        >
+          <el-option
+            v-for="(u, idx) in normalizedUrls"
+            :key="u + idx"
+            :label="guessNameFromUrl(u)"
+            :value="u"
+          />
+        </el-select>
+        <div v-else class="file-name" :title="activeName">
+          {{ activeName }}
+        </div>
+        <el-tag size="small" type="info" class="file-type">{{ activeExt || 'file' }}</el-tag>
+      </div>
+
+      <div class="right">
+        <el-button :disabled="!activeUrl" type="primary" plain @click="download">
+          <Icon icon="ep:download" class="mr-5px" />
+          下载
+        </el-button>
+      </div>
+    </div>
+
+    <div class="content" :style="{ height }">
+      <el-empty v-if="!activeUrl" description="暂无可预览文件" />
+
+      <template v-else>
+        <el-alert
+          v-if="previewHint"
+          :title="previewHint"
+          type="warning"
+          show-icon
+          :closable="false"
+          class="hint"
+        />
+
+        <!-- 图片 -->
+        <div v-if="activeKind === 'image'" class="stage">
+          <el-image
+            :src="activeUrl"
+            fit="contain"
+            :preview-src-list="[activeUrl]"
+            preview-teleported
+            class="img"
+          />
+        </div>
+
+        <!-- PDF -->
+        <div v-else-if="activeKind === 'pdf'" class="stage">
+          <div v-if="pdfLoading" class="loading-state">
+            <el-icon class="is-loading" style="font-size: 24px; color: var(--el-color-primary)">
+              <Loading />
+            </el-icon>
+            <p style="margin-top: 10px; color: var(--el-text-color-secondary)">正在加载 PDF...</p>
+          </div>
+          <el-empty v-else-if="pdfError" :description="pdfError" />
+          <iframe
+            v-else
+            class="frame"
+            :src="pdfBlobUrl || pdfIframeSrc"
+            title="PDF Preview"
+            frameborder="0"
+          ></iframe>
+        </div>
+
+        <!-- Word -->
+        <div v-else-if="activeKind === 'word'" class="stage">
+          <iframe
+            v-if="officeIframeSrc"
+            class="frame"
+            :src="officeIframeSrc"
+            title="Office Preview"
+            frameborder="0"
+          ></iframe>
+          <el-empty v-else description="该文件无法直接预览" />
+        </div>
+
+        <!-- 其它 -->
+        <div v-else class="stage">
+          <el-empty description="暂不支持该格式预览" />
+        </div>
+      </template>
+    </div>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { computed, ref, watch } from 'vue'
+
+defineOptions({ name: 'FilePreviewDialog' })
+
+type OfficeViewer = 'microsoft'
+
+const props = withDefaults(
+  defineProps<{
+    modelValue: boolean
+    title?: string
+    width?: string | number
+    height?: string
+    url?: string
+    urls?: string[]
+    name?: string
+    useOfficeViewer?: boolean
+    officeViewer?: OfficeViewer
+  }>(),
+  {
+    title: '文件预览',
+    width: '1100px',
+    height: '72vh',
+    url: '',
+    urls: () => [],
+    name: '',
+    useOfficeViewer: true,
+    officeViewer: 'microsoft'
+  }
+)
+
+const emit = defineEmits<{
+  (e: 'update:modelValue', v: boolean): void
+}>()
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (v: boolean) => emit('update:modelValue', v)
+})
+
+const normalizedUrls = computed(() => {
+  const list = [...(props.urls || []), props.url].filter(Boolean) as string[]
+  const uniq: string[] = []
+  for (const u of list) {
+    if (!uniq.includes(u)) uniq.push(u)
+  }
+  return uniq
+})
+
+const activeUrl = ref<string>('')
+
+watch(
+  () => normalizedUrls.value,
+  (list) => {
+    activeUrl.value = list[0] || ''
+  },
+  { immediate: true }
+)
+
+const activeName = computed(() => {
+  if (!activeUrl.value) return ''
+  if (props.name) return props.name
+  return guessNameFromUrl(activeUrl.value)
+})
+
+const activeExt = computed(() => getFileExt(activeName.value || activeUrl.value))
+
+type Kind = 'image' | 'pdf' | 'word' | 'other'
+const activeKind = computed<Kind>(() => {
+  const ext = activeExt.value
+  if (['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp', 'svg'].includes(ext)) return 'image'
+  if (ext === 'pdf') return 'pdf'
+  if (ext === 'doc' || ext === 'docx') return 'word'
+  return 'other'
+})
+
+const isHttpUrl = (u: string) => /^https?:\/\//i.test(u)
+
+const officeIframeSrc = computed(() => {
+  if (!props.useOfficeViewer) return ''
+  if (!activeUrl.value) return ''
+  if (!isHttpUrl(activeUrl.value)) return ''
+  if (props.officeViewer !== 'microsoft') return ''
+  return `https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(activeUrl.value)}`
+})
+
+const pdfIframeSrc = computed(() => {
+  if (!activeUrl.value) return ''
+  // Chrome 内置 PDF 预览支持 #toolbar=0
+  return `${activeUrl.value}#toolbar=0&navpanes=0&scrollbar=0`
+})
+
+const pdfBlobUrl = ref<string>('')
+const pdfLoading = ref(false)
+const pdfError = ref<string>('')
+
+watch(
+  () => activeUrl.value,
+  async (url) => {
+    if (!url || activeKind.value !== 'pdf') {
+      pdfBlobUrl.value = ''
+      return
+    }
+
+    pdfLoading.value = true
+    pdfError.value = ''
+
+    try {
+      const response = await fetch(url, {
+        method: 'GET',
+        mode: 'cors'
+      })
+
+      if (!response.ok) {
+        throw new Error(`HTTP ${response.status}: ${response.statusText}`)
+      }
+
+      const contentType = response.headers.get('content-type') || ''
+      if (!contentType.includes('application/pdf')) {
+        throw new Error('非 PDF 文件类型')
+      }
+
+      const blob = await response.blob()
+      const blobUrl = URL.createObjectURL(blob)
+
+      if (pdfBlobUrl.value) {
+        URL.revokeObjectURL(pdfBlobUrl.value)
+      }
+
+      pdfBlobUrl.value = blobUrl
+    } catch (error) {
+      console.error('PDF 加载失败:', error)
+      pdfError.value = error instanceof Error ? error.message : 'PDF 加载失败'
+      pdfBlobUrl.value = url
+    } finally {
+      pdfLoading.value = false
+    }
+  },
+  { immediate: true }
+)
+
+const previewHint = computed(() => {
+  if (!activeUrl.value) return ''
+  if (activeKind.value === 'word' && !officeIframeSrc.value) {
+    return 'Word 预览需要可被 Office 在线服务访问的完整 URL(通常要求公网/无鉴权),否则建议后端转 PDF 再预览。'
+  }
+  return ''
+})
+
+const download = () => {
+  if (!activeUrl.value) return
+  const a = document.createElement('a')
+  a.href = activeUrl.value
+  a.download = activeName.value || ''
+  a.target = '_blank'
+  a.rel = 'noopener noreferrer'
+  document.body.appendChild(a)
+  a.click()
+  a.remove()
+}
+
+function getFileExt(input: string) {
+  const clean = input.split('?')[0].split('#')[0]
+  const dot = clean.lastIndexOf('.')
+  if (dot < 0) return ''
+  return clean.substring(dot + 1).toLowerCase()
+}
+
+function guessNameFromUrl(u: string) {
+  try {
+    const url = new URL(u, window.location.origin)
+    const name = url.pathname.split('/').filter(Boolean).pop() || ''
+    return decodeURIComponent(name) || u
+  } catch {
+    const clean = u.split('?')[0].split('#')[0]
+    const name = clean.split('/').filter(Boolean).pop() || clean
+    try {
+      return decodeURIComponent(name)
+    } catch {
+      return name
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.file-preview-dialog {
+  :deep(.el-dialog__body) {
+    padding-top: 10px;
+  }
+}
+
+.toolbar {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px;
+  padding: 0 0 10px;
+  border-bottom: 1px solid var(--el-border-color-lighter);
+}
+
+.left {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  min-width: 0;
+}
+
+.file-select {
+  width: min(520px, 48vw);
+}
+
+.file-name {
+  max-width: min(520px, 48vw);
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+  color: var(--el-text-color-primary);
+  font-weight: 600;
+}
+
+.file-type {
+  flex: none;
+}
+
+.right {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  flex: none;
+}
+
+.content {
+  margin-top: 12px;
+  position: relative;
+}
+
+.hint {
+  margin-bottom: 10px;
+}
+
+.stage {
+  height: 100%;
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 10px;
+  overflow: hidden;
+  background: linear-gradient(180deg, rgba(0, 0, 0, 0.02), rgba(0, 0, 0, 0.01));
+}
+
+.img {
+  width: 100%;
+  height: 100%;
+}
+
+.frame {
+  width: 100%;
+  height: 100%;
+  border: 0;
+  background: #fff;
+}
+</style>

+ 2 - 1
src/utils/dict.ts

@@ -321,7 +321,8 @@ export enum DICT_TYPE {
   PERSON_CERT = 'person_cert',
   ORG_CERT = 'org_cert',
   DANGER_GRADE = 'danger_grade',
-  ACCIDENT_REPORT_STATUS = 'accident_report_status'
+  ACCIDENT_REPORT_STATUS = 'accident_report_status',
+  QHSE_HAZARD_STATUS = 'qhse_hazard_status'
 }
 
 export function realValue(type: any, value: string) {

+ 11 - 8
src/views/pms/device/allotlog/DeviceAllot.vue

@@ -1,12 +1,8 @@
 <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">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -150,7 +146,13 @@
 
       <!-- 列表 -->
       <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+        <el-table
+          v-loading="loading"
+          :data="list"
+          height="calc(85vh - 173px)"
+          :stripe="true"
+          :show-overflow-tooltip="true"
+        >
           <el-table-column :label="t('monitor.serial')" width="70" align="center">
             <template #default="scope">
               {{ scope.$index + 1 }}
@@ -213,7 +215,7 @@
 import download from '@/utils/download'
 import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import DeviceAllotLogDrawer from '@/views/pms/device/allotlog/DeviceAllotLogDrawer.vue'
 import { rangeShortcuts } from '@/utils/formatTime'
@@ -230,6 +232,7 @@ const ifShow = ref(false)
 const isDetail = ref(false) // 是否查看详情
 const list = ref<IotDeviceVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
+let isLeftContentCollapsed = ref(false)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,

+ 11 - 8
src/views/pms/device/completeSet/DeviceCompleteSet.vue

@@ -1,12 +1,8 @@
 <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">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -50,7 +46,13 @@
 
       <!-- 列表 -->
       <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+        <el-table
+          v-loading="loading"
+          height="calc(85vh - 130px)"
+          :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 }}
@@ -226,7 +228,7 @@
 
 <script setup lang="ts">
 import { IotDeviceApi } from '@/api/pms/device'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { defaultProps, handleTree } from '@/utils/tree'
 import * as DeptApi from '@/api/system/dept'
 import { ElMessageBox } from 'element-plus'
@@ -238,6 +240,7 @@ defineOptions({ name: 'IotDeviceComplete' })
 const loading = ref(true) // 列表的加载中
 
 const { t } = useI18n()
+let isLeftContentCollapsed = ref(false)
 
 const list = ref([]) // 列表的数据
 const total = ref(0) // 列表的总页数

+ 5 - 9
src/views/pms/device/index.vue

@@ -1,13 +1,8 @@
 <template>
   <el-row :gutter="20">
     <!-- 左侧部门树 -->
-    <el-col :span="4" :xs="24">
-      <!--      <div><Icon icon="ep:edit" @click="shou(treeShow)"/> </div>-->
-      <ContentWrap class="h-1/1" v-if="treeShow">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="contentSpan" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -171,7 +166,7 @@
           :data="list"
           :stripe="true"
           :show-overflow-tooltip="true"
-          height="65vh"
+          height="calc(85vh - 215px)"
           @sort-change="handleSortChange"
         >
           <el-table-column :label="t('iotDevice.serial')" width="70" align="center" fixed="left">
@@ -696,7 +691,7 @@
 import download from '@/utils/download'
 import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 
 import { buildSortingField } from '@/utils'
 import { defaultProps, handleTree } from '@/utils/tree'
@@ -720,6 +715,7 @@ const isDetail = ref(false) // 是否查看详情
 const list = ref<IotDeviceVO[]>([]) // 列表的数据
 const productClassifyList = ref<Tree[]>([]) // 树形结构
 const total = ref(0) // 列表的总页数
+let isLeftContentCollapsed = ref(false)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,

+ 4 - 7
src/views/pms/device/monitor/index.vue

@@ -1,12 +1,8 @@
 <template>
   <el-row :gutter="20">
     <!-- 左侧部门树 -->
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="20" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap v-loading="loading">
         <ContentWrap>
           <!-- 搜索工作栏 -->
@@ -295,7 +291,7 @@ import { DICT_TYPE, getDictLabel, getStrDictOptions } from '@/utils/dict'
 import { dateFormatter } from '@/utils/formatTime'
 import { DeviceApi, DeviceVO } from '@/api/iot/device/device'
 import download from '@/utils/download'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { IotDeviceApi } from '@/api/pms/device'
 /** IoT 设备列表 */
 defineOptions({ name: 'IoTDeviceMonitor' })
@@ -306,6 +302,7 @@ const { t } = useI18n() // 国际化
 const loading = ref(true) // 列表加载中
 const list = ref<DeviceVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
+let isLeftContentCollapsed = ref(false)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 12,

+ 11 - 8
src/views/pms/device/personlog/DevicePerson.vue

@@ -1,12 +1,8 @@
 <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">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -105,7 +101,13 @@
 
       <!-- 列表 -->
       <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+        <el-table
+          v-loading="loading"
+          :data="list"
+          :stripe="true"
+          height="calc(85vh - 175px)"
+          :show-overflow-tooltip="true"
+        >
           <el-table-column :label="t('monitor.serial')" width="70" align="center">
             <template #default="scope">
               {{ scope.$index + 1 }}
@@ -155,7 +157,7 @@
 <script setup lang="ts">
 import download from '@/utils/download'
 import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import DevicePersonLogDrawer from '@/views/pms/device/personlog/DevicePersonLogDrawer.vue'
 import { rangeShortcuts } from '@/utils/formatTime'
@@ -171,6 +173,7 @@ const loading = ref(true) // 列表的加载中
 const ifShow = ref(false)
 const list = ref<IotDeviceVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
+let isLeftContentCollapsed = ref(false)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,

+ 11 - 8
src/views/pms/device/statuslog/DeviceStatus.vue

@@ -1,12 +1,8 @@
 <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">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -145,7 +141,13 @@
 
       <!-- 列表 -->
       <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+        <el-table
+          height="calc(85vh - 175px)"
+          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 }}
@@ -208,7 +210,7 @@
 import download from '@/utils/download'
 import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { CACHE_KEY, useCache } from '@/hooks/web/useCache'
 import DeviceStatusLogDrawer from '@/views/pms/device/statuslog/DeviceStatusLogDrawer.vue'
 import { rangeShortcuts } from '@/utils/formatTime'
@@ -225,6 +227,7 @@ const ifShow = ref(false)
 const list = ref<IotDeviceVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
 const currentDeviceId = ref() // 设备id
+let isLeftContentCollapsed = ref(false)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,

+ 68 - 31
src/views/pms/failure/index.vue

@@ -1,11 +1,7 @@
 <template>
   <el-row :gutter="20">
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="20" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -15,15 +11,15 @@
           :inline="true"
           label-width="68px"
         >
-<!--          <el-form-item label="故障编码" prop="failureCode">-->
-<!--            <el-input-->
-<!--              v-model="queryParams.failureCode"-->
-<!--              placeholder="请输入故障编码"-->
-<!--              clearable-->
-<!--              @keyup.enter="handleQuery"-->
-<!--              class="!w-200px"-->
-<!--            />-->
-<!--          </el-form-item>-->
+          <!--          <el-form-item label="故障编码" prop="failureCode">-->
+          <!--            <el-input-->
+          <!--              v-model="queryParams.failureCode"-->
+          <!--              placeholder="请输入故障编码"-->
+          <!--              clearable-->
+          <!--              @keyup.enter="handleQuery"-->
+          <!--              class="!w-200px"-->
+          <!--            />-->
+          <!--          </el-form-item>-->
           <el-form-item :label="t('fault.faultTitle')" label-width="70px" prop="failureName">
             <el-input
               v-model="queryParams.failureName"
@@ -34,7 +30,12 @@
             />
           </el-form-item>
           <el-form-item :label="t('fault.status')" label-width="40px" prop="status">
-            <el-select v-model="queryParams.status" :placeholder="t('fault.status')" clearable class="!w-200px">
+            <el-select
+              v-model="queryParams.status"
+              :placeholder="t('fault.status')"
+              clearable
+              class="!w-200px"
+            >
               <el-option
                 v-for="dict in getStrDictOptions(DICT_TYPE.PMS_FAILURE_STATUS)"
                 :key="dict.value"
@@ -112,8 +113,7 @@
           </el-form-item>
           <el-form-item>
             <el-button v-if="!ifShow" @click="moreQuery(true)" type="warning"
-              ><Icon icon="ep:search" class="mr-5px" />
-              {{ t('fault.moreSearch') }}</el-button
+              ><Icon icon="ep:search" class="mr-5px" /> {{ t('fault.moreSearch') }}</el-button
             >
             <el-button v-if="ifShow" @click="moreQuery(false)" type="danger"
               ><Icon icon="ep:search" class="mr-5px" /> {{ t('fault.closeSearch') }}</el-button
@@ -142,27 +142,58 @@
 
       <!-- 列表 -->
       <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+        <el-table
+          height="calc(85vh - 130px)"
+          v-loading="loading"
+          :data="list"
+          :stripe="true"
+          :show-overflow-tooltip="true"
+        >
           <el-table-column :label="t('fault.serial')" width="70" align="center">
             <template #default="scope">
               {{ scope.$index + 1 }}
             </template>
           </el-table-column>
           <!--        <el-table-column label="故障编码" align="center" prop="failureCode" />-->
-          <el-table-column :label="t('fault.faultTitle')" align="left" prop="failureName" min-width="350"/>
-          <el-table-column :label="t('iotMaintain.deviceCode')" align="center" prop="deviceCode" min-width="220" />
-          <el-table-column :label="t('fault.deviceName')" align="center" prop="deviceName" min-width="240"/>
+          <el-table-column
+            :label="t('fault.faultTitle')"
+            align="left"
+            prop="failureName"
+            min-width="350"
+          />
+          <el-table-column
+            :label="t('iotMaintain.deviceCode')"
+            align="center"
+            prop="deviceCode"
+            min-width="220"
+          />
+          <el-table-column
+            :label="t('fault.deviceName')"
+            align="center"
+            prop="deviceName"
+            min-width="240"
+          />
           <el-table-column :label="t('fault.status')" align="center" prop="status" min-width="110">
             <template #default="scope">
               <dict-tag :type="DICT_TYPE.PMS_FAILURE_STATUS" :value="scope.row.status" />
             </template>
           </el-table-column>
-          <el-table-column :label="t('faultForm.failureType')" align="center" prop="failureType" min-width="110">
+          <el-table-column
+            :label="t('faultForm.failureType')"
+            align="center"
+            prop="failureType"
+            min-width="110"
+          >
             <template #default="scope">
               <dict-tag :type="DICT_TYPE.FAILURE_TYPE" :value="scope.row.failureType" />
             </template>
           </el-table-column>
-          <el-table-column :label="t('fault.approvalStatus')"  align="center" prop="auditStatus" min-width="130">
+          <el-table-column
+            :label="t('fault.approvalStatus')"
+            align="center"
+            prop="auditStatus"
+            min-width="130"
+          >
             <template #default="scope">
               <dict-tag :type="DICT_TYPE.CRM_AUDIT_STATUS" :value="scope.row.auditStatus" />
             </template>
@@ -179,9 +210,9 @@
             :formatter="dateFormatter"
             min-width="180px"
           />
-          <el-table-column :label="t('fault.solveTime')" align="center" prop="dealHour" >
+          <el-table-column :label="t('fault.solveTime')" align="center" prop="dealHour">
             <template #default="scope">
-              {{ scope.row.dealHour&&scope.row.dealHour>0?scope.row.dealHour+'H':"" }}
+              {{ scope.row.dealHour && scope.row.dealHour > 0 ? scope.row.dealHour + 'H' : '' }}
             </template>
           </el-table-column>
           <el-table-column
@@ -191,7 +222,12 @@
             :formatter="dateFormatter"
             min-width="180px"
           />
-          <el-table-column :label="t('fault.operation')" align="center" min-width="120px" fixed="right">
+          <el-table-column
+            :label="t('fault.operation')"
+            align="center"
+            min-width="120px"
+            fixed="right"
+          >
             <template #default="scope">
               <el-button
                 link
@@ -199,7 +235,7 @@
                 @click="openForm('detail', scope.row.id, true)"
                 v-hasPermi="['rq:iot-failure-report:query']"
               >
-                {{t('fault.view')}}
+                {{ t('fault.view') }}
               </el-button>
               <el-button
                 link
@@ -208,7 +244,7 @@
                 @click="openForm('update', scope.row.id, false)"
                 v-hasPermi="['rq:iot-failure-report:update']"
               >
-                {{t('fault.edit')}}
+                {{ t('fault.edit') }}
               </el-button>
               <el-button
                 link
@@ -217,7 +253,7 @@
                 @click="handleDelete(scope.row.id)"
                 v-hasPermi="['rq:iot-failure-report:delete']"
               >
-                {{t('fault.del')}}
+                {{ t('fault.del') }}
               </el-button>
             </template>
           </el-table-column>
@@ -242,7 +278,7 @@ import download from '@/utils/download'
 import { IotFailureReportApi, IotFailureReportVO } from '@/api/pms/failure'
 import IotFailureReportForm from './IotFailureReportForm.vue'
 import { DICT_TYPE, getBoolDictOptions, getStrDictOptions } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 
 /** 故障上报 列表 */
 defineOptions({ name: 'IotFailureReport' })
@@ -253,6 +289,7 @@ const ifShow = ref(false)
 const loading = ref(true) // 列表的加载中
 const list = ref<IotFailureReportVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
+let isLeftContentCollapsed = ref(false)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,

+ 138 - 131
src/views/pms/inspect/item/index.vue

@@ -1,134 +1,141 @@
 <template>
   <el-row :gutter="20">
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-  <el-col :span="20" :xs="24">
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item :label="t('deviceForm.category')" prop="deviceClassify" style="width: 15vw" >
-        <el-tree-select
-          v-model="queryParams.deviceClassify"
-          :data="productClassifyList"
-          :props="defaultProps"
-          check-strictly
-          node-key="id"
-          :placeholder="t('deviceForm.categoryHolder')"
-          filterable
-        />
-      </el-form-item>
-      <el-form-item :label="t('inspect.InspectionItems')" prop="item" style="margin-left: 30px">
-        <el-input
-          v-model="queryParams.item"
-          :placeholder="t('inspect.itemHolder')"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item :label="t('common.createTime')" prop="createTime" label-width="100px">
-        <el-date-picker
-          v-model="queryParams.createTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          :start-placeholder="t('operationFill.start')"
-          :end-placeholder="t('operationFill.end')"
-          :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"><Icon icon="ep:search" class="mr-5px" />
-          {{ t('operationFill.search') }}</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> {{ t('operationFill.reset') }}</el-button>
-        <el-button
-          type="primary"
-          plain
-          @click="openForm('create')"
-          v-hasPermi="['rq:iot-inspect-item:create']"
-        >
-          <Icon icon="ep:plus" class="mr-5px" /> {{ t('operationFill.add') }}
-        </el-button>
-        <el-button
-          type="success"
-          plain
-          @click="handleExport"
-          :loading="exportLoading"
-          v-hasPermi="['rq:iot-inspect-item:export']"
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
+      <ContentWrap>
+        <!-- 搜索工作栏 -->
+        <el-form
+          class="-mb-15px"
+          :model="queryParams"
+          ref="queryFormRef"
+          :inline="true"
+          label-width="68px"
         >
-          <Icon icon="ep:download" class="mr-5px" /> 导出
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
+          <el-form-item :label="t('deviceForm.category')" prop="deviceClassify" style="width: 15vw">
+            <el-tree-select
+              v-model="queryParams.deviceClassify"
+              :data="productClassifyList"
+              :props="defaultProps"
+              check-strictly
+              node-key="id"
+              :placeholder="t('deviceForm.categoryHolder')"
+              filterable
+            />
+          </el-form-item>
+          <el-form-item :label="t('inspect.InspectionItems')" prop="item" style="margin-left: 30px">
+            <el-input
+              v-model="queryParams.item"
+              :placeholder="t('inspect.itemHolder')"
+              clearable
+              @keyup.enter="handleQuery"
+              class="!w-240px"
+            />
+          </el-form-item>
+          <el-form-item :label="t('common.createTime')" prop="createTime" label-width="100px">
+            <el-date-picker
+              v-model="queryParams.createTime"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              :start-placeholder="t('operationFill.start')"
+              :end-placeholder="t('operationFill.end')"
+              :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"
+              ><Icon icon="ep:search" class="mr-5px" /> {{ t('operationFill.search') }}</el-button
+            >
+            <el-button @click="resetQuery"
+              ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('operationFill.reset') }}</el-button
+            >
+            <el-button
+              type="primary"
+              plain
+              @click="openForm('create')"
+              v-hasPermi="['rq:iot-inspect-item:create']"
+            >
+              <Icon icon="ep:plus" class="mr-5px" /> {{ t('operationFill.add') }}
+            </el-button>
+            <el-button
+              type="success"
+              plain
+              @click="handleExport"
+              :loading="exportLoading"
+              v-hasPermi="['rq:iot-inspect-item:export']"
+            >
+              <Icon icon="ep:download" class="mr-5px" /> 导出
+            </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="主键id" align="center" prop="id" />-->
-      <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
-        <template #default="scope">
-          {{ scope.$index + 1 }}
-        </template>
-      </el-table-column>
-      <el-table-column :label="t('monitor.category')" align="center" prop="deviceClassifyName" />
-      <el-table-column :label="t('iotDevice.brand')" align="center" prop="brandName" />
-      <el-table-column :label="t('iotDevice.code')" align="center" prop="deviceCode" />
-      <el-table-column :label="t('monitor.deviceName')" align="center" prop="deviceName" />
-      <el-table-column :label="t('inspect.InspectionItems')" align="center" prop="item" />
-      <el-table-column :label="t('inspect.InspectionStandards')" align="center" prop="standard" />
-      <el-table-column :label="t('inspect.createName')" align="center" prop="createName" />
-<!--      <el-table-column :label="t('common.sort')" align="center" prop="sort" />-->
-<!--      <el-table-column :label="t('form.remark')" align="center" prop="remark" />-->
+      <!-- 列表 -->
+      <ContentWrap>
+        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+          <!--      <el-table-column label="主键id" align="center" prop="id" />-->
+          <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
+            <template #default="scope">
+              {{ scope.$index + 1 }}
+            </template>
+          </el-table-column>
+          <el-table-column
+            :label="t('monitor.category')"
+            align="center"
+            prop="deviceClassifyName"
+          />
+          <el-table-column :label="t('iotDevice.brand')" align="center" prop="brandName" />
+          <el-table-column :label="t('iotDevice.code')" align="center" prop="deviceCode" />
+          <el-table-column :label="t('monitor.deviceName')" align="center" prop="deviceName" />
+          <el-table-column :label="t('inspect.InspectionItems')" align="center" prop="item" />
+          <el-table-column
+            :label="t('inspect.InspectionStandards')"
+            align="center"
+            prop="standard"
+          />
+          <el-table-column :label="t('inspect.createName')" align="center" prop="createName" />
+          <!--      <el-table-column :label="t('common.sort')" align="center" prop="sort" />-->
+          <!--      <el-table-column :label="t('form.remark')" align="center" prop="remark" />-->
 
-      <el-table-column
-        :label="t('common.createTime')"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-<!--      <el-table-column label="部门id" align="center" prop="deptId" />-->
-      <el-table-column :label="t('operationFill.operation')" align="center" min-width="120px">
-        <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id)"
-            v-hasPermi="['rq:iot-inspect-item:update']"
-          >
-            {{ t('fault.edit') }}
-          </el-button>
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['rq:iot-inspect-item:delete']"
-          >
-            {{ t('fault.del') }}
-          </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-table-column
+            :label="t('common.createTime')"
+            align="center"
+            prop="createTime"
+            :formatter="dateFormatter"
+            width="180px"
+          />
+          <!--      <el-table-column label="部门id" align="center" prop="deptId" />-->
+          <el-table-column :label="t('operationFill.operation')" align="center" min-width="120px">
+            <template #default="scope">
+              <el-button
+                link
+                type="primary"
+                @click="openForm('update', scope.row.id)"
+                v-hasPermi="['rq:iot-inspect-item:update']"
+              >
+                {{ t('fault.edit') }}
+              </el-button>
+              <el-button
+                link
+                type="danger"
+                @click="handleDelete(scope.row.id)"
+                v-hasPermi="['rq:iot-inspect-item:delete']"
+              >
+                {{ t('fault.del') }}
+              </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>
 
   <!-- 表单弹窗:添加/修改 -->
   <IotInspectItemForm ref="formRef" @success="getList" />
@@ -139,16 +146,16 @@ import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotInspectItemApi, IotInspectItemVO } from '@/api/pms/inspect/item'
 import IotInspectItemForm from './IotInspectItemForm.vue'
-import {defaultProps, handleTree} from "@/utils/tree";
-import * as ProductClassifyApi from "@/api/pms/productclassify";
-import DeptTree from "@/views/system/user/DeptTree.vue";
+import { defaultProps, handleTree } from '@/utils/tree'
+import * as ProductClassifyApi from '@/api/pms/productclassify'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 
 /** 巡检项 列表 */
 defineOptions({ name: 'IotInspectItem' })
 
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
-
+let isLeftContentCollapsed = ref(false)
 const loading = ref(true) // 列表的加载中
 const list = ref<IotInspectItemVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
@@ -161,7 +168,7 @@ const queryParams = reactive({
   sort: undefined,
   remark: undefined,
   createTime: [],
-  deptId: undefined,
+  deptId: undefined
 })
 const productClassifyList = ref<Tree[]>([]) // 树形结构
 const queryFormRef = ref() // 搜索的表单

+ 5 - 8
src/views/pms/inspect/order/index.vue

@@ -1,11 +1,7 @@
 <template>
   <el-row :gutter="20">
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="20" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -93,7 +89,7 @@
           v-loading="loading"
           :data="list"
           :stripe="true"
-          height="65vh"
+          height="calc(85vh - 175px)"
           :show-overflow-tooltip="true"
         >
           <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
@@ -251,7 +247,7 @@ import download from '@/utils/download'
 import { IotInspectOrderApi, IotInspectOrderVO } from '@/api/pms/inspect/order'
 import IotInspectOrderForm from './IotInspectOrderForm.vue'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 
 const { push } = useRouter()
 const { params } = useRoute()
@@ -266,6 +262,7 @@ const status = params.status
 const deptId = params.deptId
 const createTime = params.createTime
 const total = ref(0) // 列表的总页数
+let isLeftContentCollapsed = ref(false)
 const dialogVisible = ref(false)
 const queryParams = reactive({
   pageNo: 1,

+ 115 - 90
src/views/pms/inspect/plan/index.vue

@@ -1,22 +1,8 @@
 <template>
   <el-row :gutter="20">
-    <el-col :class="{'leftcontent': true, 'collapsed': isLeftContentCollapsed}" :span="isLeftContentCollapsed ? 0 : 4" :xs="24">
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
 
-    <el-col class="rightcontent" :span="isLeftContentCollapsed ? 24 : 20" :xs="24" style="position: relative;height: 100vh;">
-        <div
-            class="toggle-button"
-            :style="{ left: isLeftContentCollapsed ? '0px' : '-13px' }"
-            @click="toggleLeftContent"
-            @mouseover="handleMouseOver"
-            @mouseout="handleMouseOut"
-            :title="hoverText"
-          >
-            <span style="font-size: 5px;" :class="{'triangle': true, 'rotated': isLeftContentCollapsed}"></span>
-        </div>
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -56,8 +42,12 @@
             />
           </el-form-item>
           <el-form-item>
-            <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> {{ t('operationFill.search') }}</el-button>
-            <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />  {{ t('operationFill.reset') }}</el-button>
+            <el-button @click="handleQuery"
+              ><Icon icon="ep:search" class="mr-5px" /> {{ t('operationFill.search') }}</el-button
+            >
+            <el-button @click="resetQuery"
+              ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('operationFill.reset') }}</el-button
+            >
             <el-button
               type="primary"
               plain
@@ -87,10 +77,30 @@
               {{ scope.$index + 1 }}
             </template>
           </el-table-column>
-          <el-table-column :label="t('route.planTitle')" align="center" prop="planTitle" min-width="240" />
-          <el-table-column :label="t('route.planNumber')" align="center" prop="planCode" min-width="200" />
-          <el-table-column :label="t('route.cycle')" align="center" prop="planCycle"  min-width="80"/>
-          <el-table-column :label="t('workOrderMaterial.unit')" align="center" prop="planUnit" min-width="80">
+          <el-table-column
+            :label="t('route.planTitle')"
+            align="center"
+            prop="planTitle"
+            min-width="240"
+          />
+          <el-table-column
+            :label="t('route.planNumber')"
+            align="center"
+            prop="planCode"
+            min-width="200"
+          />
+          <el-table-column
+            :label="t('route.cycle')"
+            align="center"
+            prop="planCycle"
+            min-width="80"
+          />
+          <el-table-column
+            :label="t('workOrderMaterial.unit')"
+            align="center"
+            prop="planUnit"
+            min-width="80"
+          >
             <template #default="scope">
               <dict-tag :type="DICT_TYPE.PMS_INSPECT_UNIT" :value="scope.row.planUnit" />
             </template>
@@ -119,9 +129,24 @@
             :formatter="dateFormatter"
             min-width="180px"
           />
-          <el-table-column :label="t('iotDevice.dept')" align="center" prop="deptName" min-width="130"/>
-          <el-table-column :label="t('inspect.createName')" align="center" prop="createName" min-width="130"/>
-          <el-table-column :label="t('operationFill.operation')" align="center" min-width="180" fixed="right">
+          <el-table-column
+            :label="t('iotDevice.dept')"
+            align="center"
+            prop="deptName"
+            min-width="130"
+          />
+          <el-table-column
+            :label="t('inspect.createName')"
+            align="center"
+            prop="createName"
+            min-width="130"
+          />
+          <el-table-column
+            :label="t('operationFill.operation')"
+            align="center"
+            min-width="180"
+            fixed="right"
+          >
             <template #default="scope">
               <el-button
                 link
@@ -157,26 +182,26 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted } from 'vue';
-import { dateFormatter } from '@/utils/formatTime';
-import download from '@/utils/download';
-import { IotInspectPlanApi, IotInspectPlanVO } from '@/api/pms/inspect/plan';
-import IotInspectPlanForm from './IotInspectPlanForm.vue';
-import { CommonStatusEnum } from '@/utils/constants';
-import { DICT_TYPE } from "@/utils/dict";
-import DeptTree from "@/views/system/user/DeptTree.vue";
-import { useRouter } from 'vue-router';
-import { useI18n } from 'vue-i18n';
+import { ref, reactive, onMounted } from 'vue'
+import { dateFormatter } from '@/utils/formatTime'
+import download from '@/utils/download'
+import { IotInspectPlanApi, IotInspectPlanVO } from '@/api/pms/inspect/plan'
+import IotInspectPlanForm from './IotInspectPlanForm.vue'
+import { CommonStatusEnum } from '@/utils/constants'
+import { DICT_TYPE } from '@/utils/dict'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
+import { useRouter } from 'vue-router'
+import { useI18n } from 'vue-i18n'
 
 defineOptions({ name: 'IotInspectPlan' })
 const message = useMessage() // 消息弹窗
 
-const { push } = useRouter();
-const { t } = useI18n();
+const { push } = useRouter()
+const { t } = useI18n()
 
-const loading = ref(true);
-const list = ref<IotInspectPlanVO[]>([]);
-const total = ref(0);
+const loading = ref(true)
+const list = ref<IotInspectPlanVO[]>([])
+const total = ref(0)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,
@@ -189,57 +214,58 @@ const queryParams = reactive({
   remark: undefined,
   createTime: [],
   deptId: undefined
-});
-const queryFormRef = ref();
-const exportLoading = ref(false);
+})
+const queryFormRef = ref()
+const exportLoading = ref(false)
 
-const isLeftContentCollapsed = ref(false);
-const hoverText = ref('');
+const isLeftContentCollapsed = ref(false)
+const hoverText = ref('')
 const selectedDept = ref<{ id: number; name: string }>()
 
 const handleDeptNodeClick = async (row) => {
   selectedDept.value = { id: row.id, name: row.name }
-  queryParams.deptId = row.id;
-  await getList();
-};
+  queryParams.deptId = row.id
+  await getList()
+}
 
 const handleStatusChange = async (row: IotInspectPlanVO) => {
   try {
-    const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用';
-    await message.confirm('确认要"' + text + '""' + row.planTitle + '"巡检计划吗?');
-    await IotInspectPlanApi.updateIotInspectStatus(row.id, row.status);
-    await getList();
+    const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用'
+    await message.confirm('确认要"' + text + '""' + row.planTitle + '"巡检计划吗?')
+    await IotInspectPlanApi.updateIotInspectStatus(row.id, row.status)
+    await getList()
   } catch {
-    row.status = row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE;
+    row.status =
+      row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE
   }
-};
+}
 
 const getList = async () => {
-  loading.value = true;
+  loading.value = true
   try {
-    const data = await IotInspectPlanApi.getIotInspectPlanPage(queryParams);
-    list.value = data.list;
-    total.value = data.total;
+    const data = await IotInspectPlanApi.getIotInspectPlanPage(queryParams)
+    list.value = data.list
+    total.value = data.total
   } finally {
-    loading.value = false;
+    loading.value = false
   }
-};
+}
 
 const handleQuery = () => {
-  queryParams.pageNo = 1;
-  getList();
-};
+  queryParams.pageNo = 1
+  getList()
+}
 
 const resetQuery = () => {
-  queryFormRef.value.resetFields();
+  queryFormRef.value.resetFields()
   selectedDept.value = undefined
-  handleQuery();
-};
+  handleQuery()
+}
 
 const openForm = (type: string, id?: number) => {
   if (typeof id === 'number') {
-    push({ name: 'InspectPlanEdit', params: { id } });
-    return;
+    push({ name: 'InspectPlanEdit', params: { id } })
+    return
   }
   push({
     name: 'InspectPlanAdd',
@@ -248,44 +274,44 @@ const openForm = (type: string, id?: number) => {
       deptName: selectedDept.value?.name
     }
   })
-};
+}
 
 const handleDelete = async (id: number) => {
   try {
-    await message.delConfirm();
-    await IotInspectPlanApi.deleteIotInspectPlan(id);
-    message.success(t('common.delSuccess'));
-    await getList();
+    await message.delConfirm()
+    await IotInspectPlanApi.deleteIotInspectPlan(id)
+    message.success(t('common.delSuccess'))
+    await getList()
   } catch {}
-};
+}
 
 const handleExport = async () => {
   try {
-    await message.exportConfirm();
-    exportLoading.value = true;
-    const data = await IotInspectPlanApi.exportIotInspectPlan(queryParams);
-    download.excel(data, '巡检计划.xls');
+    await message.exportConfirm()
+    exportLoading.value = true
+    const data = await IotInspectPlanApi.exportIotInspectPlan(queryParams)
+    download.excel(data, '巡检计划.xls')
   } catch {
   } finally {
-    exportLoading.value = false;
+    exportLoading.value = false
   }
-};
+}
 
 const toggleLeftContent = () => {
-  isLeftContentCollapsed.value = !isLeftContentCollapsed.value;
-};
+  isLeftContentCollapsed.value = !isLeftContentCollapsed.value
+}
 
 const handleMouseOver = () => {
-  hoverText.value = isLeftContentCollapsed.value ? '展开' : '收起';
-};
+  hoverText.value = isLeftContentCollapsed.value ? '展开' : '收起'
+}
 
 const handleMouseOut = () => {
-  hoverText.value = '';
-};
+  hoverText.value = ''
+}
 
 onMounted(() => {
-  getList();
-});
+  getList()
+})
 </script>
 
 <style scoped>
@@ -306,7 +332,7 @@ onMounted(() => {
 .toggle-button {
   position: absolute;
   top: 44%;
-  transform: translate(-65%,-50%);
+  transform: translate(-65%, -50%);
   width: 12px;
   height: 40px;
   background-color: #f0f0f0;
@@ -324,7 +350,6 @@ onMounted(() => {
   background-color: #afafaf;
 }
 
-
 .triangle {
   width: 0;
   height: 0;

+ 135 - 132
src/views/pms/inspect/route/index.vue

@@ -1,133 +1,136 @@
 <template>
   <el-row :gutter="20">
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="20" :xs="24">
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form
-      class="-mb-15px"
-      :model="queryParams"
-      ref="queryFormRef"
-      :inline="true"
-      label-width="68px"
-    >
-      <el-form-item :label="t('route.RouteName')" prop="routeName" style="margin-left: 25px">
-        <el-input
-          v-model="queryParams.routeName"
-          :placeholder="t('route.nHolder')"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-<!--      <el-form-item :label="t('monitor.category')" prop="deviceClassify">-->
-<!--        <el-input-->
-<!--          v-model="queryParams.deviceClassify"-->
-<!--          :placeholder="t('deviceForm.categoryHolder')"-->
-<!--          clearable-->
-<!--          @keyup.enter="handleQuery"-->
-<!--          class="!w-240px"-->
-<!--        />-->
-<!--      </el-form-item>-->
-      <el-form-item :label="t('monitor.category')" prop="deviceClassify" style="width: 15vw" >
-        <el-tree-select
-          v-model="queryParams.deviceClassify"
-          :data="productClassifyList"
-          :props="defaultProps"
-          check-strictly
-          node-key="id"
-          :placeholder="t('deviceForm.categoryHolder')"
-          filterable
-        />
-      </el-form-item>
-      <el-form-item :label="t('monitor.deviceName')"  prop="deviceName">
-        <el-input
-          v-model="queryParams.deviceName"
-          :placeholder="t('iotDevice.nameHolder')"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" />
-          {{t('file.search')}}</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> {{t('file.reset')}}</el-button>
-        <el-button
-          type="primary"
-          plain
-          @click="openForm('create')"
-          v-hasPermi="['rq:iot-inspect-route:create']"
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
+      <ContentWrap>
+        <!-- 搜索工作栏 -->
+        <el-form
+          class="-mb-15px"
+          :model="queryParams"
+          ref="queryFormRef"
+          :inline="true"
+          label-width="68px"
         >
-          <Icon icon="ep:plus" class="mr-5px" /> {{t('fault.added')}}
-        </el-button>
-        <el-button
-          type="success"
-          plain
-          @click="handleExport"
-          :loading="exportLoading"
-          v-hasPermi="['rq:iot-inspect-route:export']"
-        >
-          <Icon icon="ep:download" class="mr-5px" /> 导出
-        </el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
+          <el-form-item :label="t('route.RouteName')" prop="routeName" style="margin-left: 25px">
+            <el-input
+              v-model="queryParams.routeName"
+              :placeholder="t('route.nHolder')"
+              clearable
+              @keyup.enter="handleQuery"
+              class="!w-240px"
+            />
+          </el-form-item>
+          <!--      <el-form-item :label="t('monitor.category')" prop="deviceClassify">-->
+          <!--        <el-input-->
+          <!--          v-model="queryParams.deviceClassify"-->
+          <!--          :placeholder="t('deviceForm.categoryHolder')"-->
+          <!--          clearable-->
+          <!--          @keyup.enter="handleQuery"-->
+          <!--          class="!w-240px"-->
+          <!--        />-->
+          <!--      </el-form-item>-->
+          <el-form-item :label="t('monitor.category')" prop="deviceClassify" style="width: 15vw">
+            <el-tree-select
+              v-model="queryParams.deviceClassify"
+              :data="productClassifyList"
+              :props="defaultProps"
+              check-strictly
+              node-key="id"
+              :placeholder="t('deviceForm.categoryHolder')"
+              filterable
+            />
+          </el-form-item>
+          <el-form-item :label="t('monitor.deviceName')" prop="deviceName">
+            <el-input
+              v-model="queryParams.deviceName"
+              :placeholder="t('iotDevice.nameHolder')"
+              clearable
+              @keyup.enter="handleQuery"
+              class="!w-240px"
+            />
+          </el-form-item>
+          <el-form-item>
+            <el-button @click="handleQuery"
+              ><Icon icon="ep:search" class="mr-5px" /> {{ t('file.search') }}</el-button
+            >
+            <el-button @click="resetQuery"
+              ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('file.reset') }}</el-button
+            >
+            <el-button
+              type="primary"
+              plain
+              @click="openForm('create')"
+              v-hasPermi="['rq:iot-inspect-route:create']"
+            >
+              <Icon icon="ep:plus" class="mr-5px" /> {{ t('fault.added') }}
+            </el-button>
+            <el-button
+              type="success"
+              plain
+              @click="handleExport"
+              :loading="exportLoading"
+              v-hasPermi="['rq:iot-inspect-route:export']"
+            >
+              <Icon icon="ep:download" class="mr-5px" /> 导出
+            </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('iotDevice.serial')" width="70" align="center">
-        <template #default="scope">
-          {{ scope.$index + 1 }}
-        </template>
-      </el-table-column>
-      <el-table-column :label="t('route.RouteName')" align="center" prop="routeName" />
-      <el-table-column :label="t('monitor.category')" align="center" prop="deviceClassifyName" />
-      <el-table-column :label="t('monitor.deviceName')" align="center" prop="deviceName" />
-<!--      <el-table-column label="巡检项" align="center" prop="itemJson" />-->
-      <el-table-column :label="t('iotDevice.brand')" align="center" prop="brandName" />
-      <el-table-column
-        :label="t('common.createTime')"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-<!--      <el-table-column label="部门id" align="center" prop="deptId" />-->
-      <el-table-column :label="t('operationFill.operation')" align="center" min-width="120px">
-        <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id)"
-            v-hasPermi="['rq:iot-inspect-route:update']"
-          >
-            {{ t('fault.edit') }}
-          </el-button>
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['rq:iot-inspect-route:delete']"
-          >
-            {{ t('fault.del') }}
-          </el-button>
-        </template>
-      </el-table-column>
-    </el-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
+      <!-- 列表 -->
+      <ContentWrap>
+        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+          <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
+            <template #default="scope">
+              {{ scope.$index + 1 }}
+            </template>
+          </el-table-column>
+          <el-table-column :label="t('route.RouteName')" align="center" prop="routeName" />
+          <el-table-column
+            :label="t('monitor.category')"
+            align="center"
+            prop="deviceClassifyName"
+          />
+          <el-table-column :label="t('monitor.deviceName')" align="center" prop="deviceName" />
+          <!--      <el-table-column label="巡检项" align="center" prop="itemJson" />-->
+          <el-table-column :label="t('iotDevice.brand')" align="center" prop="brandName" />
+          <el-table-column
+            :label="t('common.createTime')"
+            align="center"
+            prop="createTime"
+            :formatter="dateFormatter"
+            width="180px"
+          />
+          <!--      <el-table-column label="部门id" align="center" prop="deptId" />-->
+          <el-table-column :label="t('operationFill.operation')" align="center" min-width="120px">
+            <template #default="scope">
+              <el-button
+                link
+                type="primary"
+                @click="openForm('update', scope.row.id)"
+                v-hasPermi="['rq:iot-inspect-route:update']"
+              >
+                {{ t('fault.edit') }}
+              </el-button>
+              <el-button
+                link
+                type="danger"
+                @click="handleDelete(scope.row.id)"
+                v-hasPermi="['rq:iot-inspect-route:delete']"
+              >
+                {{ t('fault.del') }}
+              </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>
 
@@ -140,15 +143,15 @@ import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotInspectRouteApi, IotInspectRouteVO } from '@/api/pms/inspect/route'
 import IotInspectRouteForm from './IotInspectRouteForm.vue'
-import DeptTree from "@/views/system/user/DeptTree.vue";
-import {defaultProps} from "@/utils/tree";
+import DeptTree from '@/views/system/user/DeptTree2.vue'
+import { defaultProps } from '@/utils/tree'
 const { push } = useRouter() // 路由跳转
 /** 巡检路线 列表 */
 defineOptions({ name: 'IotInspectRoute' })
 
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
-
+let isLeftContentCollapsed = ref(false)
 const loading = ref(true) // 列表的加载中
 const list = ref<IotInspectRouteVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
@@ -164,7 +167,7 @@ const queryParams = reactive({
   itemJson: undefined,
   remark: undefined,
   createTime: [],
-  deptId: undefined,
+  deptId: undefined
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
@@ -201,11 +204,11 @@ const resetQuery = () => {
 const openForm = (type: string, id?: number) => {
   //修改
   if (typeof id === 'number') {
-    push({ name: 'RouteEdit', params: {id } })
+    push({ name: 'RouteEdit', params: { id } })
     return
   }
   // 新增
-  push({ name: 'RouteAdd', params:{} })
+  push({ name: 'RouteAdd', params: {} })
 }
 
 /** 删除按钮操作 */

+ 11 - 9
src/views/pms/iotmainworkorder/IotDeviceMainAlarm.vue

@@ -1,12 +1,8 @@
 <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">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -57,7 +53,13 @@
 
       <!-- 列表 -->
       <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+        <el-table
+          height="calc(85vh - 135px)"
+          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 }}
@@ -154,10 +156,10 @@ import download from '@/utils/download'
 import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
 import { IotMainWorkOrderApi } from '@/api/pms/iotmainworkorder'
 import { DICT_TYPE } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { useCache } from '@/hooks/web/useCache'
 import DeviceAlarmBomList from '@/views/pms/iotmainworkorder/DeviceAlarmBomList.vue'
-
+let isLeftContentCollapsed = ref(false)
 const showRunTime = computed(() => {
   const all = list.value?.map((item) => item.totalRunTime)
   return all.some((item) => Boolean(item))

+ 5 - 79
src/views/pms/iotmainworkorder/index.vue

@@ -1,33 +1,7 @@
 <template>
   <el-row :gutter="20">
-    <el-col
-      :class="{ leftcontent: true, collapsed: isLeftContentCollapsed }"
-      :span="isLeftContentCollapsed ? 0 : 4"
-      :xs="24"
-    >
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col
-      class="rightcontent"
-      :span="isLeftContentCollapsed ? 24 : 20"
-      :xs="24"
-      style="position: relative; height: 100vh"
-    >
-      <div
-        class="toggle-button"
-        :style="{ left: isLeftContentCollapsed ? '0px' : '-13px' }"
-        @click="toggleLeftContent"
-        @mouseover="handleMouseOver"
-        @mouseout="handleMouseOut"
-        :title="hoverText"
-      >
-        <span
-          style="font-size: 5px"
-          :class="{ triangle: true, rotated: isLeftContentCollapsed }"
-        ></span>
-      </div>
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -108,6 +82,7 @@
           :stripe="true"
           style="width: 100%"
           ref="tableRef"
+          height="calc(85vh - 175px)"
         >
           <el-table-column
             :label="t('iotDevice.serial')"
@@ -302,7 +277,7 @@ import download from '@/utils/download'
 import { IotMainWorkOrderApi, IotMainWorkOrderVO } from '@/api/pms/iotmainworkorder'
 import IotMainWorkOrderForm from './IotMainWorkOrderForm.vue'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { useUserStore } from '@/store/modules/user'
 const { push } = useRouter() // 路由跳转
 
@@ -350,7 +325,6 @@ const queryParams = reactive({
 })
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
-const hoverText = ref('')
 
 // 定义表单验证规则
 const delayReasonRules = {
@@ -674,18 +648,6 @@ const getDistanceClass = (distance: number | string | null) => {
   return ''
 }
 
-const toggleLeftContent = () => {
-  isLeftContentCollapsed.value = !isLeftContentCollapsed.value
-}
-
-const handleMouseOver = () => {
-  hoverText.value = isLeftContentCollapsed.value ? '展开' : '收起'
-}
-
-const handleMouseOut = () => {
-  hoverText.value = ''
-}
-
 /** 添加/修改操作 */
 const formRef = ref()
 const openForm = (type: string, id?: number) => {
@@ -796,8 +758,7 @@ watch(isLeftContentCollapsed, () => {
 }
 
 .leftcontent.collapsed {
-  width: 0;
-  overflow: hidden;
+  overflow: visible;
 }
 
 /* 正数样式 - 淡绿色 */
@@ -860,41 +821,6 @@ watch(isLeftContentCollapsed, () => {
   width: 100% !important;
 }
 
-.toggle-button {
-  position: absolute;
-  top: 44%;
-  transform: translate(-65%, -50%);
-  width: 12px;
-  height: 40px;
-  background-color: #f0f0f0;
-  cursor: pointer;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  z-index: 1;
-  clip-path: polygon(0 0, 100% 18%, 100% 85%, 0 100%);
-  border-radius: 8px;
-}
-
-/* 添加鼠标悬停样式 */
-.toggle-button:hover {
-  background-color: #afafaf;
-}
-
-.triangle {
-  width: 0;
-  height: 0;
-  border-top: 4px solid transparent;
-  border-bottom: 4px solid transparent;
-  transition: transform 0.4s ease;
-  border-right: 5px solid gray; /* 修改为右边框显示颜色 */
-  border-left: none; /* 移除左边框 */
-}
-
-.triangle.rotated {
-  transform: rotate(180deg);
-}
-
 /* 延时原因 必填星号样式 */
 :deep(.required-item .el-form-item__label:before) {
   content: '*';

+ 5 - 9
src/views/pms/iotopeationfill/index.vue

@@ -1,11 +1,7 @@
 <template>
   <el-row :gutter="20">
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="20" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -84,7 +80,7 @@
 
       <!-- 列表 -->
       <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true">
+        <el-table v-loading="loading" :data="list" :stripe="true" height="calc(85vh - 175px)">
           <el-table-column :label="t('common.index')" min-width="60" align="center">
             <template #default="scope">
               {{ scope.$index + 1 }}
@@ -347,7 +343,7 @@ import download from '@/utils/download'
 import { IotInspectOrderApi, IotInspectOrderVO } from '@/api/pms/inspect/order'
 //import IotInspectOrderForm from './IotInspectOrderForm.vue'
 import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { onMounted, ref } from 'vue'
 import { IotOpeationFillApi, IotOpeationFillVO } from '@/api/pms/iotopeationfill'
 import { useUserStore } from '@/store/modules/user'
@@ -361,7 +357,7 @@ defineOptions({ name: 'IotOpeationFill1' })
 const dialogVisible = ref(false)
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
-
+let isLeftContentCollapsed = ref(false)
 const loading = ref(true) // 列表的加载中
 const list = ref<IotOpeationFillVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数

+ 117 - 91
src/views/pms/iotopeationfill/plan/index.vue

@@ -1,22 +1,8 @@
 <template>
   <el-row :gutter="20">
-    <el-col :class="{'leftcontent': true, 'collapsed': isLeftContentCollapsed}" :span="isLeftContentCollapsed ? 0 : 4" :xs="24">
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
 
-    <el-col class="rightcontent" :span="isLeftContentCollapsed ? 24 : 20" :xs="24" style="position: relative;height: 100vh;">
-        <div
-            class="toggle-button"
-            :style="{ left: isLeftContentCollapsed ? '0px' : '-13px' }"
-            @click="toggleLeftContent"
-            @mouseover="handleMouseOver"
-            @mouseout="handleMouseOut"
-            :title="hoverText"
-          >
-            <span style="font-size: 5px;" :class="{'triangle': true, 'rotated': isLeftContentCollapsed}"></span>
-        </div>
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -56,8 +42,12 @@
             />
           </el-form-item>
           <el-form-item>
-            <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> {{ t('operationFill.search') }}</el-button>
-            <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />  {{ t('operationFill.reset') }}</el-button>
+            <el-button @click="handleQuery"
+              ><Icon icon="ep:search" class="mr-5px" /> {{ t('operationFill.search') }}</el-button
+            >
+            <el-button @click="resetQuery"
+              ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('operationFill.reset') }}</el-button
+            >
             <el-button
               type="primary"
               plain
@@ -81,16 +71,37 @@
 
       <!-- 列表 -->
       <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+        <el-table
+          v-loading="loading"
+          :data="list"
+          :stripe="true"
+          :show-overflow-tooltip="true"
+          height="calc(85vh - 175px)"
+        >
           <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
             <template #default="scope">
               {{ scope.$index + 1 }}
             </template>
           </el-table-column>
-          <el-table-column :label="t('route.planTitle')" align="center" prop="planTitle" min-width="240" />
-<!--          <el-table-column :label="t('route.planNumber')" align="center" prop="planCode" min-width="200" />-->
-          <el-table-column :label="t('route.cycle')" align="center" prop="planCycle"  min-width="80"/>
-          <el-table-column :label="t('workOrderMaterial.unit')" align="center" prop="planUnit" min-width="80">
+          <el-table-column
+            :label="t('route.planTitle')"
+            align="center"
+            prop="planTitle"
+            min-width="240"
+          />
+          <!--          <el-table-column :label="t('route.planNumber')" align="center" prop="planCode" min-width="200" />-->
+          <el-table-column
+            :label="t('route.cycle')"
+            align="center"
+            prop="planCycle"
+            min-width="80"
+          />
+          <el-table-column
+            :label="t('workOrderMaterial.unit')"
+            align="center"
+            prop="planUnit"
+            min-width="80"
+          >
             <template #default="scope">
               <dict-tag :type="DICT_TYPE.PMS_INSPECT_UNIT" :value="scope.row.planUnit" />
             </template>
@@ -119,9 +130,24 @@
             :formatter="dateFormatter"
             min-width="180px"
           />
-          <el-table-column :label="t('iotDevice.dept')" align="center" prop="deptName" min-width="130"/>
-          <el-table-column :label="t('inspect.createName')" align="center" prop="createName" min-width="130"/>
-          <el-table-column :label="t('operationFill.operation')" align="center" min-width="180" fixed="right">
+          <el-table-column
+            :label="t('iotDevice.dept')"
+            align="center"
+            prop="deptName"
+            min-width="130"
+          />
+          <el-table-column
+            :label="t('inspect.createName')"
+            align="center"
+            prop="createName"
+            min-width="130"
+          />
+          <el-table-column
+            :label="t('operationFill.operation')"
+            align="center"
+            min-width="180"
+            fixed="right"
+          >
             <template #default="scope">
               <el-button
                 link
@@ -157,26 +183,26 @@
 </template>
 
 <script setup lang="ts">
-import { ref, reactive, onMounted } from 'vue';
-import { dateFormatter } from '@/utils/formatTime';
-import download from '@/utils/download';
-import { IotOperationPlanApi, IotOperationPlanVO } from '@/api/pms/iotopeationfill/plan';
-import IotOperationPlanForm from './IotOperationPlanForm.vue';
-import { CommonStatusEnum } from '@/utils/constants';
-import { DICT_TYPE } from "@/utils/dict";
-import DeptTree from "@/views/system/user/DeptTree.vue";
-import { useRouter } from 'vue-router';
-import { useI18n } from 'vue-i18n';
+import { ref, reactive, onMounted } from 'vue'
+import { dateFormatter } from '@/utils/formatTime'
+import download from '@/utils/download'
+import { IotOperationPlanApi, IotOperationPlanVO } from '@/api/pms/iotopeationfill/plan'
+import IotOperationPlanForm from './IotOperationPlanForm.vue'
+import { CommonStatusEnum } from '@/utils/constants'
+import { DICT_TYPE } from '@/utils/dict'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
+import { useRouter } from 'vue-router'
+import { useI18n } from 'vue-i18n'
 
 defineOptions({ name: 'iotOperationPlan' })
 const message = useMessage() // 消息弹窗
 
-const { push } = useRouter();
-const { t } = useI18n();
+const { push } = useRouter()
+const { t } = useI18n()
 
-const loading = ref(true);
-const list = ref<IotOperationPlanVO[]>([]);
-const total = ref(0);
+const loading = ref(true)
+const list = ref<IotOperationPlanVO[]>([])
+const total = ref(0)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,
@@ -189,57 +215,58 @@ const queryParams = reactive({
   remark: undefined,
   createTime: [],
   deptId: undefined
-});
-const queryFormRef = ref();
-const exportLoading = ref(false);
+})
+const queryFormRef = ref()
+const exportLoading = ref(false)
 
-const isLeftContentCollapsed = ref(false);
-const hoverText = ref('');
+const isLeftContentCollapsed = ref(false)
+const hoverText = ref('')
 const selectedDept = ref<{ id: number; name: string }>()
 
 const handleDeptNodeClick = async (row) => {
   selectedDept.value = { id: row.id, name: row.name }
-  queryParams.deptId = row.id;
-  await getList();
-};
+  queryParams.deptId = row.id
+  await getList()
+}
 
 const handleStatusChange = async (row: IotOperationPlanVO) => {
   try {
-    const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用';
-    await message.confirm('确认要"' + text + '""' + row.planTitle + '"巡检计划吗?');
-    await IotOperationPlanApi.updateIotOperationStatus(row.id, row.status);
-    await getList();
+    const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用'
+    await message.confirm('确认要"' + text + '""' + row.planTitle + '"巡检计划吗?')
+    await IotOperationPlanApi.updateIotOperationStatus(row.id, row.status)
+    await getList()
   } catch {
-    row.status = row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE;
+    row.status =
+      row.status === CommonStatusEnum.ENABLE ? CommonStatusEnum.DISABLE : CommonStatusEnum.ENABLE
   }
-};
+}
 
 const getList = async () => {
-  loading.value = true;
+  loading.value = true
   try {
-    const data = await IotOperationPlanApi.getIotOperationPlanPage(queryParams);
-    list.value = data.list;
-    total.value = data.total;
+    const data = await IotOperationPlanApi.getIotOperationPlanPage(queryParams)
+    list.value = data.list
+    total.value = data.total
   } finally {
-    loading.value = false;
+    loading.value = false
   }
-};
+}
 
 const handleQuery = () => {
-  queryParams.pageNo = 1;
-  getList();
-};
+  queryParams.pageNo = 1
+  getList()
+}
 
 const resetQuery = () => {
-  queryFormRef.value.resetFields();
+  queryFormRef.value.resetFields()
   selectedDept.value = undefined
-  handleQuery();
-};
+  handleQuery()
+}
 
 const openForm = (type: string, id?: number) => {
   if (typeof id === 'number') {
-    push({ name: 'OperationPlanEdit', params: { id } });
-    return;
+    push({ name: 'OperationPlanEdit', params: { id } })
+    return
   }
   push({
     name: 'OperationPlanAdd',
@@ -248,44 +275,44 @@ const openForm = (type: string, id?: number) => {
       deptName: selectedDept.value?.name
     }
   })
-};
+}
 
 const handleDelete = async (id: number) => {
   try {
-    await message.delConfirm();
-    await IotOperationPlanApi.deleteIotOperationPlan(id);
-    message.success(t('common.delSuccess'));
-    await getList();
+    await message.delConfirm()
+    await IotOperationPlanApi.deleteIotOperationPlan(id)
+    message.success(t('common.delSuccess'))
+    await getList()
   } catch {}
-};
+}
 
 const handleExport = async () => {
   try {
-    await message.exportConfirm();
-    exportLoading.value = true;
-    const data = await IotOperationPlanApi.exportIotOperationPlan(queryParams);
-    download.excel(data, '巡检计划.xls');
+    await message.exportConfirm()
+    exportLoading.value = true
+    const data = await IotOperationPlanApi.exportIotOperationPlan(queryParams)
+    download.excel(data, '巡检计划.xls')
   } catch {
   } finally {
-    exportLoading.value = false;
+    exportLoading.value = false
   }
-};
+}
 
 const toggleLeftContent = () => {
-  isLeftContentCollapsed.value = !isLeftContentCollapsed.value;
-};
+  isLeftContentCollapsed.value = !isLeftContentCollapsed.value
+}
 
 const handleMouseOver = () => {
-  hoverText.value = isLeftContentCollapsed.value ? '展开' : '收起';
-};
+  hoverText.value = isLeftContentCollapsed.value ? '展开' : '收起'
+}
 
 const handleMouseOut = () => {
-  hoverText.value = '';
-};
+  hoverText.value = ''
+}
 
 onMounted(() => {
-  getList();
-});
+  getList()
+})
 </script>
 
 <style scoped>
@@ -306,7 +333,7 @@ onMounted(() => {
 .toggle-button {
   position: absolute;
   top: 44%;
-  transform: translate(-65%,-50%);
+  transform: translate(-65%, -50%);
   width: 12px;
   height: 40px;
   background-color: #f0f0f0;
@@ -324,7 +351,6 @@ onMounted(() => {
   background-color: #afafaf;
 }
 
-
 .triangle {
   width: 0;
   height: 0;

+ 5 - 8
src/views/pms/maintain/index.vue

@@ -1,11 +1,7 @@
 <template>
   <el-row :gutter="20">
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="20" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -172,9 +168,9 @@
         <el-table
           v-loading="loading"
           :data="list"
-          height="65vh"
           :stripe="true"
           :show-overflow-tooltip="true"
+          height="calc(85vh - 175px)"
         >
           <el-table-column :label="t('maintain.serial')" min-width="70" align="center">
             <template #default="scope">
@@ -354,7 +350,7 @@
 import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotMaintainApi, IotMaintainVO } from '@/api/pms/maintain'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { DICT_TYPE, getBoolDictOptions, getStrDictOptions } from '@/utils/dict'
 import { useUserStore } from '@/store/modules/user'
 
@@ -369,6 +365,7 @@ const loading = ref(true) // 列表的加载中
 const list = ref<IotMaintainVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
 const dialogVisible = ref(false)
+let isLeftContentCollapsed = ref(false)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,

+ 11 - 9
src/views/pms/maintenance/index.vue

@@ -1,11 +1,7 @@
 <template>
   <el-row :gutter="20">
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="20" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form
@@ -74,7 +70,13 @@
 
       <!-- 列表 -->
       <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+        <el-table
+          height="calc(85vh - 175px)"
+          v-loading="loading"
+          :data="list"
+          :stripe="true"
+          :show-overflow-tooltip="true"
+        >
           <el-table-column :label="t('iotDevice.serial')" width="70" align="center">
             <template #default="scope">
               {{ scope.$index + 1 }}
@@ -141,7 +143,7 @@
 import { dateFormatter2 } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotMaintenancePlanApi, IotMaintenancePlanVO } from '@/api/pms/maintenance'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { CommonStatusEnum } from '@/utils/constants'
 const { push } = useRouter() // 路由跳转
 
@@ -150,7 +152,7 @@ defineOptions({ name: 'IotMaintenancePlan' })
 
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
-
+let isLeftContentCollapsed = ref(false)
 const loading = ref(true) // 列表的加载中
 const list = ref<IotMaintenancePlanVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数

+ 21 - 14
src/views/pms/monitor/index.vue

@@ -1,12 +1,8 @@
 <template>
   <el-row :gutter="20">
     <!-- 左侧部门树 -->
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1" style="border: 0" v-if="treeShow">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="contentSpan" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap style="border: 0">
         <!-- 搜索工作栏 -->
         <el-form
@@ -37,8 +33,8 @@
       </ContentWrap>
 
       <!-- 列表(卡片) -->
-      <ContentWrap style="border: 0">
-        <div v-loading="loading">
+      <ContentWrap style="border: 0" class="h-[78vh] overflow-y-auto">
+        <div v-loading="loading" class="h-[75vh] overflow-x-hidden">
           <el-row :gutter="20">
             <el-col
               v-for="(item, index) in list"
@@ -73,7 +69,7 @@
                       <el-image
                         v-if="item.type === '1'"
                         :src="img1"
-                        style="width: 40px; height: 40px; margin-left: 40px"
+                        style="width: 30px; height: 30px; margin-left: 40px"
                       />
                       <el-image
                         v-else
@@ -102,7 +98,6 @@
                       {{ item.ifOnline ? '在线' : '离线' }}
                     </el-tag>
                   </div>
-                  <!-- <div class="card-row"><span class="muted">描述:</span> {{ item.remark }}</div> -->
                 </div>
                 <template #footer>
                   <el-button type="primary" link @click="handleEdit(item)"
@@ -129,10 +124,10 @@
 
 <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 DeptTree from '@/views/system/user/DeptTree2.vue'
+import { handleTree } from '@/utils/tree'
 import * as DeptApi from '@/api/system/dept'
-import { DICT_TYPE, getDictLabel, getStrDictOptions } from '@/utils/dict'
+import { DICT_TYPE } from '@/utils/dict'
 
 import img1 from '../../../assets/imgs/300.png'
 import img2 from '../../../assets/imgs/other.png'
@@ -148,6 +143,7 @@ const { t } = useI18n()
 
 const list = ref([]) // 列表的数据
 const total = ref(0) // 列表的总页数
+let isLeftContentCollapsed = ref(false)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,
@@ -519,7 +515,7 @@ onMounted(async () => {
 .device-card .card-row {
   display: flex;
   align-items: center;
-  padding: 10px 0;
+  padding: 2px 0;
 }
 
 .device-card .card-row:last-child {
@@ -561,4 +557,15 @@ onMounted(async () => {
   font-size: 12px;
   font-size: 12px;
 }
+
+::-webkit-scrollbar {
+  width: 5px; /* 设置滚动条宽度 */
+}
+::-webkit-scrollbar-thumb {
+  background-color: darkgrey; /* 设置滚动条颜色 */
+  border-radius: 10px; /* 设置圆角 */
+}
+::-webkit-scrollbar-track {
+  background: transparent; /* 设置轨道颜色 */
+}
 </style>

+ 13 - 10
src/views/pms/qhse/certificate.vue

@@ -2,12 +2,8 @@
 <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">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :span="isLeftContentCollapsed ? 24 : 20" :xs="24">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true">
@@ -24,7 +20,7 @@
               v-model="queryParams.classify"
               placeholder="证书类别"
               clearable
-              class="!w-240px"
+              class="!w-180px"
             >
               <el-option
                 v-for="dict in getStrDictOptions(DICT_TYPE.PERSON_CERT).concat(
@@ -55,8 +51,14 @@
       </ContentWrap>
 
       <!-- 列表 -->
-      <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+      <ContentWrap class="flex-1 overflow-hidden mt-15px">
+        <el-table
+          v-loading="loading"
+          :data="list"
+          :stripe="true"
+          height="calc(85vh - 130px)"
+          :show-overflow-tooltip="true"
+        >
           <el-table-column :label="t('monitor.serial')" width="70" align="center">
             <template #default="scope">
               {{ scope.$index + 1 }}
@@ -274,7 +276,7 @@
 
 <script setup lang="ts">
 import { IotMeasureCertApi } from '@/api/pms/qhse/index'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { handleTree } from '@/utils/tree'
 import * as DeptApi from '@/api/system/dept'
 import { ElMessageBox, ElMessage } from 'element-plus'
@@ -292,6 +294,7 @@ const loading = ref(true) // 列表的加载中
 const formLoading = ref(false) // 表单加载中
 const submitLoading = ref(false) // 提交按钮加载中
 const exportLoading = ref(false) // 导出按钮加载中
+const isLeftContentCollapsed = ref(false)
 
 const { t } = useI18n()
 

+ 137 - 126
src/views/pms/qhse/faultReport/index.vue

@@ -1,131 +1,135 @@
 <template>
-  <div class="app-container">
-    <ContentWrap style="border: 0">
-      <el-form
-        class="-mb-15px"
-        :model="queryParams"
-        ref="queryForm"
-        :inline="true"
-        label-width="100px"
-      >
-        <el-row :gutter="20">
-          <el-col :span="24" :xs="24" :sm="12" :md="6">
-            <el-form-item label="事件级别" prop="accidentGrade">
-              <el-input
-                v-model="queryParams.accidentGrade"
-                placeholder="请输入事件级别"
-                clearable
-                @keyup.enter="handleQuery"
-                style="width: 200px"
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="24" :xs="24" :sm="12" :md="6">
-            <el-form-item label="事件类型" prop="accidentType">
-              <el-input
-                v-model="queryParams.accidentType"
-                placeholder="请选择事件类型"
-                clearable
-                style="width: 200px"
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="24" :xs="24" :sm="24" :md="12">
-            <el-form-item style="display: block">
-              <el-button type="primary" @click="handleQuery" :icon="Search">搜索</el-button>
-              <el-button @click="resetQuery" :icon="Refresh">重置</el-button>
+  <el-row :gutter="20">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
+      <ContentWrap style="border: 0">
+        <el-form
+          class="-mb-15px"
+          :model="queryParams"
+          ref="queryForm"
+          :inline="true"
+          label-width="100px"
+        >
+          <el-row :gutter="20">
+            <el-col :span="24" :xs="24" :sm="12" :md="6">
+              <el-form-item label="事件级别" prop="accidentGrade">
+                <el-input
+                  v-model="queryParams.accidentGrade"
+                  placeholder="请输入事件级别"
+                  clearable
+                  @keyup.enter="handleQuery"
+                  style="width: 200px"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="24" :xs="24" :sm="12" :md="6">
+              <el-form-item label="事件类型" prop="accidentType">
+                <el-input
+                  v-model="queryParams.accidentType"
+                  placeholder="请选择事件类型"
+                  clearable
+                  style="width: 200px"
+                />
+              </el-form-item>
+            </el-col>
+            <el-col :span="24" :xs="24" :sm="24" :md="12">
+              <el-form-item style="display: block; padding-left: 50px">
+                <el-button type="primary" @click="handleQuery" :icon="Search">搜索</el-button>
+                <el-button @click="resetQuery" :icon="Refresh">重置</el-button>
+                <el-button
+                  type="primary"
+                  @click="openForm('create', undefined, false)"
+                  color="#626aef"
+                  v-hasPermi="['rq:iot-accident-report:create']"
+                >
+                  <Icon icon="ep:plus" class="mr-5px" /> 新增
+                </el-button>
+                <!-- <el-button type="success" plain @click="handleExport" :loading="exportLoading">
+                <Icon icon="ep:download" class="mr-5px" /> 导出
+              </el-button> -->
+              </el-form-item>
+            </el-col>
+          </el-row>
+        </el-form>
+      </ContentWrap>
+
+      <ContentWrap class="flex-1 overflow-hidden mt-15px">
+        <el-table
+          v-loading="loading"
+          :data="list"
+          row-key="id"
+          border
+          style="width: 100%"
+          :header-cell-style="{ background: '#f5f7fa', color: '#333', height: '50px' }"
+          :cell-style="{ padding: '12px 8px' }"
+          height="calc(85vh - 115px)"
+          :max-height="tableHeight"
+        >
+          <el-table-column prop="actualTime" label="事件时间" align="center" min-width="150">
+            <template #default="{ row }">
+              {{ formatDate(row.actualTime) }}
+            </template>
+          </el-table-column>
+          <el-table-column prop="accidentGrade" label="事件级别" align="center" />
+          <el-table-column prop="accidentType" label="事件类型" align="center" />
+          <el-table-column prop="accidentType" label="事件状态" align="center" width="100">
+            <template #default="scope">
+              <dict-tag :type="DICT_TYPE.ACCIDENT_REPORT_STATUS" :value="scope.row.status" />
+            </template>
+          </el-table-column>
+          <el-table-column
+            prop="lossSituation"
+            label="事件损失情况"
+            align="center"
+            show-overflow-tooltip
+            min-width="150"
+          />
+
+          <el-table-column
+            prop="accidentAddress"
+            label="事件地址"
+            align="center"
+            min-width="150"
+            show-overflow-tooltip
+          />
+          <el-table-column prop="deptName" label="部门名称" align="center" />
+          <el-table-column prop="dutyPerson" label="现场负责人" align="center" width="100" />
+          <el-table-column prop="actualTime" label="创建时间" align="center" min-width="150">
+            <template #default="{ row }">
+              {{ formatDate(row.createTime) }}
+            </template>
+          </el-table-column>
+
+          <el-table-column label="操作" align="center" width="150" fixed="right">
+            <template #default="{ row }">
               <el-button
+                link
                 type="primary"
-                @click="openForm('create', undefined, false)"
-                color="#626aef"
-                v-hasPermi="['rq:iot-accident-report:create']"
+                @click="openForm('detail', row.id, true)"
+                :icon="View"
+                v-hasPermi="['rq:iot-accident-report:query']"
+                >详情</el-button
               >
-                <Icon icon="ep:plus" class="mr-5px" /> 新增
-              </el-button>
-              <!-- <el-button type="success" plain @click="handleExport" :loading="exportLoading">
-                <Icon icon="ep:download" class="mr-5px" /> 导出
-              </el-button> -->
-            </el-form-item>
-          </el-col>
-        </el-row>
-      </el-form>
-    </ContentWrap>
-
-    <ContentWrap style="border: 0">
-      <el-table
-        v-loading="loading"
-        :data="list"
-        row-key="id"
-        border
-        style="width: 100%"
-        :header-cell-style="{ background: '#f5f7fa', color: '#333', height: '50px' }"
-        :cell-style="{ padding: '12px 8px' }"
-        height="70vh"
-        :max-height="tableHeight"
-      >
-        <el-table-column prop="actualTime" label="事件时间" align="center" min-width="150">
-          <template #default="{ row }">
-            {{ formatDate(row.actualTime) }}
-          </template>
-        </el-table-column>
-        <el-table-column prop="accidentGrade" label="事件级别" align="center" />
-        <el-table-column prop="accidentType" label="事件类型" align="center" />
-        <el-table-column prop="accidentType" label="事件状态" align="center" width="100">
-          <template #default="scope">
-            <dict-tag :type="DICT_TYPE.ACCIDENT_REPORT_STATUS" :value="scope.row.status" />
-          </template>
-        </el-table-column>
-        <el-table-column
-          prop="lossSituation"
-          label="事件损失情况"
-          align="center"
-          show-overflow-tooltip
-          min-width="150"
-        />
-
-        <el-table-column
-          prop="accidentAddress"
-          label="事件地址"
-          align="center"
-          min-width="150"
-          show-overflow-tooltip
-        />
-        <el-table-column prop="deptName" label="部门名称" align="center" />
-        <el-table-column prop="dutyPerson" label="现场负责人" align="center" width="100" />
-        <el-table-column prop="actualTime" label="创建时间" align="center" min-width="150">
-          <template #default="{ row }">
-            {{ formatDate(row.createTime) }}
-          </template>
-        </el-table-column>
-
-        <el-table-column label="操作" align="center" width="150" fixed="right">
-          <template #default="{ row }">
-            <el-button
-              link
-              type="primary"
-              @click="openForm('detail', row.id, true)"
-              :icon="View"
-              v-hasPermi="['rq:iot-accident-report:query']"
-              >详情</el-button
-            >
-            <el-button link type="primary" @click="openApprovalDialog(row)"> 流转信息 </el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-
-      <!-- 分页 -->
-      <div class="mt-2 mb-2 float-right">
-        <el-pagination
-          v-model:current-page="queryParams.pageNo"
-          v-model:page-size="queryParams.pageSize"
-          :total="total"
-          layout="total, sizes, prev, pager, next"
-          @size-change="handleSizeChange"
-          @current-change="handleCurrentChange"
-          background
-        />
-      </div>
-    </ContentWrap>
+              <el-button link type="primary" @click="openApprovalDialog(row)"> 流转信息 </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+
+        <!-- 分页 -->
+        <div class="mt-2 mb-2 float-right">
+          <el-pagination
+            v-model:current-page="queryParams.pageNo"
+            v-model:page-size="queryParams.pageSize"
+            :total="total"
+            layout="total, sizes, prev, pager, next"
+            @size-change="handleSizeChange"
+            @current-change="handleCurrentChange"
+            background
+          />
+        </div>
+      </ContentWrap>
+    </el-col>
 
     <!-- 表单弹窗 -->
     <!-- 表单弹窗:添加/修改 -->
@@ -212,7 +216,7 @@
         </div>
       </div>
     </el-drawer>
-  </div>
+  </el-row>
 </template>
 
 <script setup>
@@ -229,8 +233,10 @@ import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
 import { IotApprovalApi } from '@/api/pms/qhse/index'
 import QhseFaultReportForm from './QhseFaultReportForm.vue'
 import { useRouter } from 'vue-router'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 
 const router = useRouter()
+const isLeftContentCollapsed = ref(false)
 
 // Data
 const loading = ref(false)
@@ -291,9 +297,14 @@ const tableHeight = computed(() => {
   if (isMobile.value) {
     return window.innerHeight - 300 // 为移动端减少高度,考虑其他元素占用空间
   }
-  return '70vh'
+  return '90vh'
 })
 
+const handleDeptNodeClick = async (row) => {
+  queryParams.value.deptId = row.id
+  await getList()
+}
+
 // Methods
 const getList = async () => {
   loading.value = true

+ 15 - 9
src/views/pms/qhse/index.vue

@@ -1,12 +1,10 @@
 <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">
+
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <ContentWrap>
         <!-- 搜索工作栏 -->
         <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true">
@@ -37,8 +35,14 @@
       </ContentWrap>
 
       <!-- 列表 -->
-      <ContentWrap>
-        <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
+      <ContentWrap class="flex-1 overflow-hidden mt-15px">
+        <el-table
+          v-loading="loading"
+          :data="list"
+          :stripe="true"
+          height="calc(85vh - 130px)"
+          :show-overflow-tooltip="true"
+        >
           <el-table-column :label="t('monitor.serial')" width="70" align="center" fixed="left">
             <template #default="scope">
               {{ scope.$index + 1 }}
@@ -253,7 +257,7 @@
 
 <script setup lang="ts">
 import { IotInstrumentApi } from '@/api/pms/qhse/index'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { handleTree } from '@/utils/tree'
 import { defaultProps } from '@/utils/tree'
 import * as DeptApi from '@/api/system/dept'
@@ -270,6 +274,8 @@ const formLoading = ref(false) // 表单加载中
 const submitLoading = ref(false) // 提交按钮加载中
 let exportLoading = ref(false)
 
+const isLeftContentCollapsed = ref(false)
+
 const { t } = useI18n()
 
 const list = ref([]) // 列表的数据

+ 145 - 98
src/views/pms/qhse/iotmeasuredetect/index.vue

@@ -1,102 +1,128 @@
 <template>
-  <ContentWrap>
-    <!-- 搜索工作栏 -->
-    <el-form class="-mb-15px" :model="queryParams" ref="queryFormRef" :inline="true">
-      <el-form-item label="检测/校准日期" prop="detectDate">
-        <el-date-picker
-          v-model="queryParams.detectDate"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          start-placeholder="开始日期"
-          end-placeholder="结束日期"
-          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-          class="!w-200px"
-        />
-      </el-form-item>
-      <el-form-item label="检测/校准机构" prop="detectOrg">
-        <el-input
-          v-model="queryParams.detectOrg"
-          placeholder="请输入检测/校准机构"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-150px"
-        />
-      </el-form-item>
-      <el-form-item label="检测/校准有效期" prop="validityPeriod">
-        <el-date-picker
-          v-model="queryParams.validityPeriod"
-          value-format="YYYY-MM-DD"
-          type="date"
-          placeholder="选择检测/校准有效期"
-          clearable
-          class="!w-150px"
-        />
-      </el-form-item>
-      <el-form-item label="校准金额" prop="detectAmount">
-        <el-input
-          v-model="queryParams.detectAmount"
-          placeholder="请输入校准金额"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-150px"
-        />
-      </el-form-item>
-
-      <el-form-item>
-        <el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
-        <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
-        <el-button type="primary" plain @click="openForm('create')">
-          <Icon icon="ep:plus" class="mr-5px" /> 新增
-        </el-button>
-        <el-button type="success" plain @click="handleExport" :loading="exportLoading">
-          <Icon icon="ep:download" class="mr-5px" /> 导出
-        </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="计量器具名称" align="center" prop="measureName" />
-      <el-table-column label="证书编码" align="center" prop="measureCertNo" />
-      <el-table-column label="检测/校准日期" align="center" prop="detectDate">
-        <template #default="scope">
-          <span>{{ formatDateCorrectly(scope.row.detectDate) }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="检测/校准机构" align="center" prop="detectOrg" />
-      <el-table-column label="检测/校准标准" align="center" prop="detectStandard" />
-      <el-table-column label="检测/校准内容" align="center" prop="detectContent">
-        <template #default="scope">
-          <div v-html="scope.row.detectContent"></div>
-        </template>
-      </el-table-column>
-      <el-table-column label="检测/校准有效期" align="center" prop="validityPeriod" width="180px">
-        <template #default="scope">
-          <span>{{ formatDateCorrectly(scope.row.validityPeriod) }}</span>
-        </template>
-      </el-table-column>
-      <el-table-column label="校准金额" align="center" prop="detectAmount" />
-
-      <el-table-column label="部门名称" align="center" prop="deptName" />
-      <el-table-column label="操作" align="center" min-width="120px">
-        <template #default="scope">
-          <el-button link type="primary" @click="openForm('update', scope.row.id)">
-            编辑
-          </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-row :gutter="20">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+
+    <el-col :span="isLeftContentCollapsed ? 24 : 20" :xs="24">
+      <ContentWrap>
+        <!-- 搜索工作栏 -->
+        <el-form :model="queryParams" ref="queryFormRef" :inline="true">
+          <el-form-item label="检测/校准日期" prop="detectDate">
+            <el-date-picker
+              v-model="queryParams.detectDate"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              start-placeholder="开始日期"
+              end-placeholder="结束日期"
+              :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+              class="!w-200px"
+            />
+          </el-form-item>
+          <el-form-item label="检测/校准机构" prop="detectOrg">
+            <el-input
+              v-model="queryParams.detectOrg"
+              placeholder="请输入检测/校准机构"
+              clearable
+              @keyup.enter="handleQuery"
+              class="!w-150px"
+            />
+          </el-form-item>
+          <el-form-item label="检测/校准有效期" prop="validityPeriod">
+            <el-date-picker
+              v-model="queryParams.validityPeriod"
+              value-format="YYYY-MM-DD"
+              type="date"
+              placeholder="选择检测/校准有效期"
+              clearable
+              class="!w-150px"
+            />
+          </el-form-item>
+          <el-form-item label="校准金额" prop="detectAmount">
+            <el-input
+              v-model="queryParams.detectAmount"
+              placeholder="请输入校准金额"
+              clearable
+              @keyup.enter="handleQuery"
+              class="!w-150px"
+            />
+          </el-form-item>
+
+          <el-form-item>
+            <el-button @click="handleQuery">
+              <Icon icon="ep:search" class="mr-5px" /> 搜索
+            </el-button>
+            <el-button @click="resetQuery">
+              <Icon icon="ep:refresh" class="mr-5px" /> 重置
+            </el-button>
+            <el-button type="primary" plain @click="openForm('create')">
+              <Icon icon="ep:plus" class="mr-5px" /> 新增
+            </el-button>
+            <el-button type="success" plain @click="handleExport" :loading="exportLoading">
+              <Icon icon="ep:download" class="mr-5px" /> 导出
+            </el-button>
+          </el-form-item>
+        </el-form>
+      </ContentWrap>
+
+      <!-- 列表 -->
+      <ContentWrap class="flex-1 overflow-hidden mt-15px">
+        <el-table
+          v-loading="loading"
+          :data="list"
+          :stripe="true"
+          height="calc(85vh - 187px)"
+          :show-overflow-tooltip="true"
+        >
+          <el-table-column label="计量器具名称" align="center" prop="measureName" min-width="160" />
+          <el-table-column label="证书编码" align="center" prop="measureCertNo" min-width="160" />
+          <el-table-column label="检测/校准日期" align="center" prop="detectDate" width="140">
+            <template #default="scope">
+              <span class="iot-md-date">{{ formatDateCorrectly(scope.row.detectDate) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="检测/校准机构" align="center" prop="detectOrg" min-width="160" />
+          <el-table-column
+            label="检测/校准标准"
+            align="center"
+            prop="detectStandard"
+            min-width="160"
+          />
+          <el-table-column
+            label="检测/校准内容"
+            align="center"
+            prop="detectContent"
+            min-width="220"
+          >
+            <template #default="scope">
+              <div class="detect-content" v-html="scope.row.detectContent"></div>
+            </template>
+          </el-table-column>
+          <el-table-column label="检测/校准有效期" align="center" prop="validityPeriod" width="140">
+            <template #default="scope">
+              <span class="iot-md-date">{{ formatDateCorrectly(scope.row.validityPeriod) }}</span>
+            </template>
+          </el-table-column>
+          <el-table-column label="校准金额" align="center" prop="detectAmount" width="120" />
+          <el-table-column label="部门名称" align="center" prop="deptName" min-width="140" />
+          <el-table-column label="操作" align="center" width="140" fixed="right">
+            <template #default="scope">
+              <el-button link type="primary" @click="openForm('update', scope.row.id)">
+                编辑
+              </el-button>
+              <el-button link type="danger" @click="handleDelete(scope.row.id)"> 删除 </el-button>
+            </template>
+          </el-table-column>
+        </el-table>
+        <div class="iot-md-pagination">
+          <Pagination
+            :total="total"
+            v-model:page="queryParams.pageNo"
+            v-model:limit="queryParams.pageSize"
+            @pagination="getList"
+          />
+        </div>
+      </ContentWrap>
+    </el-col>
+  </el-row>
 
   <!-- 表单弹窗:添加/修改 -->
   <IotMeasureDetectForm ref="formRef" @success="getList" />
@@ -107,11 +133,14 @@ import download from '@/utils/download'
 import { IotMeasureDetectApi, IotMeasureDetectVO } from '@/api/pms/qhse/index'
 import IotMeasureDetectForm from './IotMeasureDetectForm.vue'
 import { formatDate } from '@/utils/formatTime'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
+
 /** 计量器具-检测校准明细 列表 */
 defineOptions({ name: 'IotMeasureDetect' })
 
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
+const isLeftContentCollapsed = ref(false)
 
 const loading = ref(true) // 列表的加载中
 const list = ref<IotMeasureDetectVO[]>([]) // 列表的数据
@@ -148,6 +177,15 @@ const handleQuery = () => {
   queryParams.pageNo = 1
   getList()
 }
+
+const selectedDept = ref<{ id: number; name: string }>()
+
+const handleDeptNodeClick = async (row) => {
+  selectedDept.value = { id: row.id, name: row.name }
+  queryParams.deptId = row.id
+  await getList()
+}
+
 const formatDateCorrectly = (timestamp) => {
   if (!timestamp) return ''
 
@@ -219,3 +257,12 @@ onMounted(() => {
   getList()
 })
 </script>
+
+<style scoped>
+::deep(.el-tree--highlight-current) {
+  height: 200px !important;
+}
+::deep(.el-transfer-panel__body) {
+  height: 700px !important;
+}
+</style>

文件差异内容过多而无法显示
+ 455 - 481
src/views/pms/qhse/safety/index.vue


+ 4 - 8
src/views/report-statistics/device_book/index2.vue

@@ -1,12 +1,8 @@
 <template style="background-color: #edf4ff">
   <el-row :gutter="20">
     <!-- 左侧部门树 -->
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1" v-if="treeShow" style="border: 0; height: 87.5vh">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="contentSpan" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <!-- 统计卡片 -->
       <el-row :gutter="21" class="mb-4">
         <el-col :span="9">
@@ -289,7 +285,7 @@
 <script setup lang="ts">
 import { IotDeviceApi, IotDeviceVO } from '@/api/pms/device'
 import { DICT_TYPE } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { buildSortingField } from '@/utils'
 import { useRefreshStore } from '@/store/modules/pms/refreshStore'
 import Echart from '@/components/Echart/src/Echart.vue'
@@ -298,7 +294,7 @@ import { formatDate } from '@/utils/formatTime'
 import { nextTick, watch } from 'vue'
 import download from '@/utils/download'
 const message = useMessage() // 消息弹窗
-
+let isLeftContentCollapsed = ref(false)
 /** 设备台账 列表 */
 defineOptions({ name: 'IotDevicePms' })
 

+ 5 - 9
src/views/report-statistics/fault_report/index.vue

@@ -1,11 +1,7 @@
 <template>
   <el-row :gutter="20">
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1" style="border: 0">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="20" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <!-- 统计卡片 -->
       <el-row :gutter="18" class="mb-4">
         <el-col :span="4">
@@ -177,7 +173,7 @@
         <el-table
           v-loading="loading"
           :data="list"
-          height="48vh"
+          height="45.6vh"
           :stripe="true"
           :show-overflow-tooltip="true"
         >
@@ -277,7 +273,7 @@ import { IotInspectOrderApi, IotInspectOrderVO } from '@/api/pms/inspect/order'
 import { watch } from 'vue'
 
 import { DICT_TYPE } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { dateFormatter } from '@/utils/formatTime'
 
 const message = useMessage() // 消息弹窗
@@ -293,7 +289,7 @@ const list = ref<IotInspectOrderVO[]>([]) // 列表的数据
 const deptId = params.deptId
 const createTime = params.createTime
 const total = ref(0) // 列表的总页数
-
+let isLeftContentCollapsed = ref(false)
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,

+ 4 - 8
src/views/report-statistics/inspection_order/index.vue

@@ -1,11 +1,7 @@
 <template>
   <el-row :gutter="20">
-    <el-col :span="4" :xs="24">
-      <ContentWrap class="h-1/1" style="border: 0">
-        <DeptTree @node-click="handleDeptNodeClick" />
-      </ContentWrap>
-    </el-col>
-    <el-col :span="20" :xs="24">
+    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
+    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
       <!-- 统计卡片 -->
       <el-row :gutter="18" class="mb-4">
         <el-col :span="4">
@@ -331,12 +327,12 @@
 <script setup lang="ts">
 import { IotInspectOrderApi, IotInspectOrderVO } from '@/api/pms/inspect/order'
 import { DICT_TYPE } from '@/utils/dict'
-import DeptTree from '@/views/system/user/DeptTree.vue'
+import DeptTree from '@/views/system/user/DeptTree2.vue'
 import { IotInspectItemVO, IotInspectOrderDetailApi } from '@/api/pms/inspect/order/detail'
 const message = useMessage() // 消息弹窗
 import { watch } from 'vue'
 import download from '@/utils/download'
-
+let isLeftContentCollapsed = ref(false)
 const { push } = useRouter()
 const { params } = useRoute()
 /** 巡检工单 列表 */

+ 300 - 0
src/views/system/user/DeptTree2.vue

@@ -0,0 +1,300 @@
+<template>
+  <el-col
+    :class="{ leftcontent: true, collapsed: isCollapsed }"
+    :span="isCollapsed ? 0 : 4"
+    :xs="24"
+  >
+    <ContentWrap class="h-[85vh]">
+      <div
+        class="dept-tree"
+        :class="{ 'is-collapsed': isCollapsed }"
+        style="overflow-y: auto; overflow-x: auto"
+      >
+        <div class="head-container" style="display: flex; flex-direction: row">
+          <el-input
+            v-model="deptName"
+            class="mb-18px"
+            style="height: 35px"
+            clearable
+            placeholder="请输入部门名称"
+          >
+            <template #prefix>
+              <Icon icon="ep:search" />
+            </template>
+          </el-input>
+        </div>
+        <div class="tree-container">
+          <el-tree
+            v-show="!isCollapsed"
+            ref="treeRef"
+            :data="deptList"
+            :expand-on-click-node="false"
+            :filter-node-method="filterNode"
+            :props="defaultProps"
+            :default-expanded-keys="firstLevelKeys"
+            highlight-current
+            node-key="id"
+            @node-click="handleNodeClick"
+            @node-contextmenu="handleRightClick"
+          />
+        </div>
+      </div>
+      <div
+        v-show="menuVisible"
+        class="custom-menu"
+        :style="{ left: menuX + 'px', top: menuY + 'px' }"
+      >
+        <ul>
+          <li @click="handleMenuClick('add')">新增子节点</li>
+          <li @click="handleMenuClick('edit')">重命名</li>
+          <li @click="handleMenuClick('delete')">删除</li>
+        </ul>
+      </div>
+    </ContentWrap>
+  </el-col>
+
+  <!-- 切换按钮移到外部,始终可见 -->
+  <button
+    :class="isCollapsed ? 'tree-toggle--outside' : 'tree-toggle--outside2'"
+    type="button"
+    :aria-label="isCollapsed ? '展开组织树' : '收起组织树'"
+    @click="toggleCollapsed"
+    :title="isCollapsed ? '展开' : '收起'"
+  >
+    <img class="tree-toggle__img" :src="isCollapsed ? hideimage : showimage" alt="" />
+  </button>
+</template>
+
+<script lang="ts" setup>
+import { ElTree } from 'element-plus'
+import * as DeptApi from '@/api/system/dept'
+import { defaultProps, handleTree } from '@/utils/tree'
+import { useTreeStore } from '@/store/modules/usersTreeStore'
+import hideimage from '@/assets/imgs/leftTree-hide.png'
+import showimage from '@/assets/imgs/leftTree-show.png'
+
+defineOptions({ name: 'SystemUserDeptTree' })
+
+type Props = {
+  collapsed?: boolean
+  collapsible?: boolean
+}
+
+const props = defineProps<Props>()
+const emits = defineEmits<{
+  (e: 'node-click', row: any): void
+  (e: 'update:collapsed', value: boolean): void
+  (e: 'toggle', value: boolean): void
+}>()
+
+const collapsible = computed(() => props.collapsible !== false)
+const collapsedLocal = ref(false)
+const isCollapsed = computed(() => (props.collapsed ?? collapsedLocal.value) === true)
+
+const deptName = ref('')
+const deptList = ref<Tree[]>([]) // 树形结构
+const treeRef = ref<InstanceType<typeof ElTree>>()
+const menuVisible = ref(false)
+const menuX = ref(0)
+const menuY = ref(0)
+const firstLevelKeys = ref([])
+let selectedNode = null
+const treeStore = useTreeStore()
+
+watch(
+  () => props.collapsed,
+  (val) => {
+    if (typeof val === 'boolean') collapsedLocal.value = val
+  },
+  { immediate: true }
+)
+
+const toggleCollapsed = () => {
+  const next = !isCollapsed.value
+  collapsedLocal.value = next
+  emits('update:collapsed', next)
+  emits('toggle', next)
+}
+
+const handleRightClick = (event, { node, data }) => {
+  event.preventDefault()
+  menuX.value = event.clientX
+  menuY.value = event.clientY
+  selectedNode = data // 存储当前操作的节点数据 ‌:ml-citation{ref="7" data="citationList"}
+  //menuVisible.value = true;
+}
+const treeContainer = ref(null)
+const setHeight = () => {
+  if (!treeContainer.value) return
+  const windowHeight = window.innerHeight
+
+  treeContainer.value.style.height = `${windowHeight * 0.78}px` // 60px 底部预留
+}
+const handleMenuClick = (action) => {
+  switch (action) {
+    case 'add':
+      // 调用新增节点逻辑 ‌:ml-citation{ref="4" data="citationList"}
+      break
+    case 'edit':
+      // 调用编辑节点逻辑 ‌:ml-citation{ref="7" data="citationList"}
+      break
+    case 'delete':
+      // 调用删除节点逻辑 ‌:ml-citation{ref="4" data="citationList"}
+      break
+  }
+  menuVisible.value = false
+}
+/** 获得部门树 */
+const getTree = async () => {
+  const res = await DeptApi.getSimpleDeptList()
+  deptList.value = []
+  deptList.value.push(...handleTree(res))
+  firstLevelKeys.value = deptList.value.map((node) => node.id)
+}
+
+/** 基于名字过滤 */
+const filterNode = (name: string, data: Tree) => {
+  if (!name) return true
+  return data.name.includes(name)
+}
+
+/** 处理部门被点击 */
+const handleNodeClick = async (row: { [key: string]: any }) => {
+  emits('node-click', row)
+  treeStore.setSelectedId(row.id)
+}
+
+/** 监听deptName */
+watch(deptName, (val) => {
+  treeRef.value!.filter(val)
+})
+
+/** 初始化 */
+onMounted(async () => {
+  await getTree()
+  // setHeight()
+  // window.addEventListener('resize', setHeight)
+})
+onUnmounted(() => {
+  window.removeEventListener('resize', setHeight)
+})
+</script>
+<style lang="scss" scoped>
+.dept-tree {
+  height: 85vh;
+  overflow-y: auto;
+
+  &.is-collapsed {
+    overflow-y: hidden;
+  }
+}
+.custom-menu {
+  position: fixed;
+  background: white;
+  border: 1px solid #ccc;
+  box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
+  z-index: 1000;
+}
+.custom-menu ul {
+  list-style: none;
+  padding: 0;
+  margin: 0;
+}
+.custom-menu li {
+  padding: 8px 20px;
+  cursor: pointer;
+}
+.custom-menu li:hover {
+  background: #f5f5f5;
+}
+.tree-container {
+  overflow-y: auto;
+  max-height: calc(85vh - 100px);
+  min-width: 100%;
+  // border: 1px solid #e4e7ed;
+  border-radius: 4px;
+  position: relative;
+  overflow-x: visible;
+}
+
+// 外部按钮样式 - 定位到左侧边缘
+.tree-toggle--outside {
+  position: absolute;
+  top: 30vh;
+  left: 0;
+  transform: translate(-50%, 0%);
+  z-index: 10;
+  background: transparent;
+  border: none;
+  cursor: pointer;
+  padding: 0;
+  line-height: 0;
+  transition: color 0.3s ease;
+}
+
+.tree-toggle--outside2 {
+  position: absolute;
+  top: 38%;
+  left: 16.5%;
+  transform: translate(-50%, 0%);
+  z-index: 10;
+  background: transparent;
+  border: none;
+  cursor: pointer;
+  padding: 0;
+  line-height: 0;
+  transition: color 0.3s ease;
+}
+
+.tree-toggle--outside:hover {
+  filter: brightness(0.9);
+  transition: color 0.3s ease;
+}
+
+.tree-toggle--outside2:hover {
+  filter: brightness(0.9);
+  transition: color 0.3s ease;
+}
+
+.tree-toggle__img {
+  display: block;
+  width: auto;
+  height: auto;
+  max-width: 100%;
+  max-height: 100%;
+  user-select: none;
+}
+
+.dept-tree.is-collapsed .tree-container {
+  overflow-y: auto;
+}
+
+.leftcontent {
+  transition: width 0.3s ease;
+  position: relative;
+}
+
+.leftcontent.collapsed {
+  width: 0 !important;
+  overflow: hidden;
+}
+
+// 小屏幕下隐藏按钮
+@media (max-width: 768px) {
+  .tree-toggle--outside,
+  .tree-toggle--outside2 {
+    display: none;
+  }
+}
+
+::-webkit-scrollbar {
+  width: 5px; /* 设置滚动条宽度 */
+}
+::-webkit-scrollbar-thumb {
+  background-color: darkgrey; /* 设置滚动条颜色 */
+  border-radius: 10px; /* 设置圆角 */
+}
+::-webkit-scrollbar-track {
+  background: transparent; /* 设置轨道颜色 */
+}
+</style>

部分文件因为文件数量过多而无法显示