Prechádzať zdrojové kódy

feat(pms): 优化巡检路线列表及路线配置页面

- 统一巡检路线列表的部门树、查询面板和表格布局
- 使用 DeptTreeSelect 统一部门筛选组件
- 使用 el-auto-resizer 动态计算 ZmTable 可用宽高
- 统一表格列组件并移除业务列固定宽度
- 增加固定分页栏和窄屏响应式布局
- 重构巡检路线新增编辑页面的标题及表单结构
- 优化路线信息字段排布和路线描述展示
- 增加巡检项空状态、拖拽序号及标准信息展示
- 新增吸底保存操作栏并统一按钮尺寸
- 优化选择巡检项弹窗的标题、查询区和已选标签
- 将巡检项选择表格替换为 ZmTable
- 修复复选框点击后提前关闭弹窗的问题
- 支持整行切换多选并统一确认回传
- 保留原有新增、编辑、删除、导出和拖拽排序逻辑
Zimo 1 týždeň pred
rodič
commit
1369e367a2

+ 306 - 169
src/views/pms/inspect/route/InspectItemList.vue

@@ -1,141 +1,141 @@
 <template>
-  <Dialog v-model="dialogVisible" :title="t('route.selectInspectionItems')" style="width: 1300px; min-height: 50%">
-    <ContentWrap>
+  <Dialog
+    v-model="dialogVisible"
+    :title="t('route.selectInspectionItems')"
+    width="min(1200px, 92vw)"
+    class="inspect-item-select-dialog"
+    @close="handleClose">
+    <template #title>
+      <div class="dialog-title mb-4">
+        <div class="dialog-title__icon"><Icon icon="ep:finished" /></div>
+        <div>
+          <div class="dialog-title__text">{{ t('route.selectInspectionItems') }}</div>
+          <div class="dialog-title__subtitle">筛选并选择需要加入路线的巡检项目</div>
+        </div>
+      </div>
+    </template>
+
+    <div class="dialog-content">
       <el-form
-        class="-mb-15px"
-        :model="queryParams"
         ref="queryFormRef"
-        :inline="true"
-        label-width="68px"
-      >
-        <el-form-item :label="t('inspect.InspectionItems')" prop="item" style="margin-left: 45px">
-          <el-input
-            v-model="queryParams.item"
-            :placeholder="t('inspect.itemHolder')"
-            clearable
-            @keyup.enter="handleQuery"
-            class="!w-200px"
-          />
-        </el-form-item>
-<!--        <el-form-item :label="t('iotDevice.brand')" prop="brandName">-->
-<!--          <el-input-->
-<!--            v-model="queryParams.brandName"-->
-<!--            :placeholder="t('iotDevice.brandHolder')"-->
-<!--            clearable-->
-<!--            @keyup.enter="handleQuery"-->
-<!--            class="!w-200px"-->
-<!--          />-->
-<!--        </el-form-item>-->
-        <el-form-item :label="t('common.createTime')" prop="createTime">
-          <el-date-picker
-            v-model="queryParams.createTime"
-            value-format="YYYY-MM-DD HH:mm:ss"
-            type="daterange"
-            :start-placeholder="t('info.start')"
-            :end-placeholder="t('info.end')"
-            :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-            class="!w-240px"
-          />
-        </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="handleConfirm" type="danger"
-            ><Icon icon="ep:check" class="mr-5px" /> {{ t('workOrderMaterial.confirm') }}</el-button
-          >
-<!--          <el-button @click="toggleAll" type="primary"-->
-<!--            ><Icon icon="ep:refresh" class="mr-5px" />{{ t('chooseMaintain.selectAll') }}</el-button-->
-<!--          >-->
-<!--          <el-button @click="invertSelection" type="info"-->
-<!--            ><Icon icon="ep:refresh" class="mr-5px" />{{ t('chooseMaintain.cancelAllSelection') }}</el-button-->
-<!--          >-->
+        :model="queryParams"
+        size="default"
+        label-width="88px"
+        class="query-panel">
+        <div class="query-row">
+          <el-form-item :label="t('inspect.InspectionItems')" prop="item">
+            <el-input
+              v-model="queryParams.item"
+              :placeholder="t('inspect.itemHolder')"
+              clearable
+              class="query-control"
+              @keyup.enter="handleQuery" />
+          </el-form-item>
+          <el-form-item :label="t('common.createTime')" prop="createTime">
+            <el-date-picker
+              v-model="queryParams.createTime"
+              value-format="YYYY-MM-DD HH:mm:ss"
+              type="daterange"
+              :start-placeholder="t('info.start')"
+              :end-placeholder="t('info.end')"
+              :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+              class="query-control query-control--date" />
+          </el-form-item>
+        </div>
+        <el-form-item class="query-actions">
+          <el-button type="primary" @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-form-item>
       </el-form>
-    </ContentWrap>
-    <!-- 新增:已选物料标签区域 -->
-    <ContentWrap v-if="selectedRows.length > 0"
-                 style="margin: -10px 0px 10px; padding: 10px 15px; background: #f8fafc; border-radius: 4px; border: 1px solid #ebeef5;">
-      <div style="display: flex; align-items: center; flex-wrap: wrap; gap: 8px;">
-        <div style="font-weight: bold; color: #606266; margin-right: 10px;">已选物料:</div>
-        <el-tag
-          v-for="item in selectedRows"
-          :key="item.id"
-          @close="removeTag(item)"
-          style="margin-bottom: 5px; position: relative; padding-right: 25px;"
-        >
-          {{ item.item }}
-          <!-- 自定义关闭按钮 -->
-          <span class="close-icon" @click.stop="removeTag(item)">
-            <Icon icon="ep:close" style="font-size: 12px; position: absolute; right: 8px; top: 50%; transform: translateY(-50%);"/>
-          </span>
-        </el-tag>
+
+      <div v-if="selectedRows.length > 0" class="selected-panel">
+        <div class="selected-panel__label">已选 {{ selectedRows.length }} 项</div>
+        <div class="selected-panel__tags">
+          <el-tag
+            v-for="item in selectedRows"
+            :key="`${item.id}-${item.deviceCode || ''}`"
+            closable
+            @close="removeTag(item)">
+            {{ item.item }}
+          </el-tag>
+        </div>
+      </div>
+
+      <section class="table-section">
+        <ZmTable
+          :loading="loading"
+          :data="list"
+          :max-height="360"
+          show-border
+          settings-cache-key="pms-inspect-route-item-select"
+          @row-click="handleRowClick">
+          <ZmTableColumn :label="t('workOrderMaterial.select')" :width="64" hide-in-column-settings>
+            <template #default="{ row }">
+              <el-checkbox
+                :model-value="isSelected(row)"
+                class="no-label-checkbox"
+                @click.stop="toggleRow(row)" />
+            </template>
+          </ZmTableColumn>
+          <ZmTableColumn :label="t('inspect.InspectionItems')" prop="item" />
+          <ZmTableColumn :label="t('inspect.InspectionStandards')" prop="standard" />
+          <ZmTableColumn :label="t('iotDevice.brand')" prop="brandName" />
+          <ZmTableColumn :label="t('fileInfo.appendix')" prop="urls">
+            <template #default="scope">
+              <el-button
+                v-if="scope.row.urls"
+                link
+                type="primary"
+                @click.stop="openWeb(scope.row.urls)">
+                <Icon size="18" icon="ep:view" />
+              </el-button>
+              <span v-else>-</span>
+            </template>
+          </ZmTableColumn>
+          <ZmTableColumn :label="t('common.sort')" prop="sort" />
+          <ZmTableColumn
+            :label="t('common.createTime')"
+            prop="createTime"
+            :formatter="dateFormatter" />
+        </ZmTable>
+        <Pagination
+          :total="total"
+          v-model:page="queryParams.pageNo"
+          v-model:limit="queryParams.pageSize"
+          @pagination="getList" />
+      </section>
+    </div>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <span class="selection-summary">已选择 {{ selectedRows.length }} 项</span>
+        <div>
+          <el-button size="default" @click="dialogVisible = false">取消</el-button>
+          <el-button size="default" type="primary" @click="handleConfirm">
+            <Icon icon="ep:check" class="mr-5px" />{{ t('workOrderMaterial.confirm') }}
+          </el-button>
+        </div>
       </div>
-    </ContentWrap>
-    <ContentWrap>
-      <el-table
-        ref="tableRef"
-        v-loading="loading"
-        :data="list"
-        :stripe="true"
-        :show-overflow-tooltip="true"
-        @row-click="handleRowClick"
-        row-key="id"
-        @selection-change="handleSelectionChange"
-      >
-<!--        <el-table-column-->
-<!--          type="selection"-->
-<!--          :reserve-selection="true"-->
-<!--          :selectable="(row) => !row.disabled"-->
-<!--        />-->
-        <el-table-column width="80" :label="t('workOrderMaterial.select')">
-          <template #default="{ row }">
-            <el-checkbox
-              :model-value="selectedRows.some(item => item.id === row.id&&item.deviceCode===row.deviceCode)"
-              @click.stop="selectRow(row)"
-              class="no-label-radio"
-            />
-          </template>
-        </el-table-column>
-        <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('iotDevice.brand')" align="center" prop="brandName" />
-        <el-table-column :label="t('fileInfo.appendix')" align="center" prop="urls">
-          <template #default="scope">
-            <el-button v-if="scope.row.urls" link type="primary" @click="openWeb(scope.row.urls)">
-              <Icon size="19" icon="ep:view"
-            /></el-button>
-          </template>
-        </el-table-column>
-        <el-table-column :label="t('common.sort')" align="center" prop="sort" />
-        <el-table-column
-          :label="t('common.createTime')"
-          align="center"
-          prop="createTime"
-          :formatter="dateFormatter"
-        />
-      </el-table>
-      <!-- 分页 -->
-      <Pagination
-        :total="total"
-        v-model:page="queryParams.pageNo"
-        v-model:limit="queryParams.pageSize"
-        @pagination="getList"
-      />
-    </ContentWrap>
+    </template>
   </Dialog>
 </template>
 
 <script setup lang="ts">
-import { IotDeviceVO } from '@/api/pms/device'
 import { dateFormatter } from '@/utils/formatTime'
 import { useCache } from '@/hooks/web/useCache'
 import { IotInspectItemApi, IotInspectItemVO } from '@/api/pms/inspect/item'
+import { useTableComponents } from '@/components/ZmTable/useTableComponents'
+const { ZmTable, ZmTableColumn } = useTableComponents<IotInspectItemVO>()
 const { t } = useI18n() // 国际化
 const emit = defineEmits(['choose']) // 定义 success 事件,用于操作成功后的回调
 const dialogVisible = ref(false) // 弹窗的是否展示
 const loading = ref(true) // 列表的加载中
 const queryFormRef = ref() // 搜索的表单
-const list = ref<IotDeviceVO[]>([]) // 列表的数据
+const list = ref<IotInspectItemVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
 const queryParams = reactive({
   pageNo: 1,
@@ -143,7 +143,7 @@ const queryParams = reactive({
   item: undefined,
   deptId: undefined,
   deviceClassify: undefined,
-  brandName:undefined,
+  brandName: undefined,
   deviceId: undefined,
   createTime: undefined
 })
@@ -153,51 +153,28 @@ const openWeb = (url) => {
   )
 }
 const removeTag = (route) => {
-  // 从已选列表中移除
-  const index = selectedRows.value.findIndex(item => item.id === route.id);
-  debugger
+  const index = selectedRows.value.findIndex(
+    (item) => item.id === route.id && item.deviceCode === route.deviceCode
+  )
   if (index !== -1) {
-    selectedRows.value.splice(index, 1);
-  }
-
-  // 如果当前行在当前页,取消其选中状态
-  const rowInTable = list.value.find(item => item.id === route.id);
-  if (rowInTable) {
-    const rowIndex = list.value.indexOf(rowInTable);
-    // 这里可以添加逻辑强制更新行状态(如果需要)
+    selectedRows.value.splice(index, 1)
   }
-};
-const selectRow = (row) => {
-  selectedRow.value = selectedRow.value?.id === row.id ? null : row
-  emit('choose', row)
-  dialogVisible.value = false
-}
-const toggleAll = () => {
-  list.value.forEach((row) => {
-    tableRef.value.toggleRowSelection(row, true)
-  })
-}
-const handleSelectionChange = (val) => {
-  selectedRows.value = val
-}
-const invertSelection = () => {
-  selectedRows.value.forEach((row) => {
-    tableRef.value.toggleRowSelection(row, false)
-    selectedRows.value.splice(row)
-  })
 }
 const selectedRows = ref<IotInspectItemVO[]>([]) // 多选数据(存储所有选中行的数组)
-const tableRef = ref()
+
+const isSelected = (row: IotInspectItemVO) =>
+  selectedRows.value.some((item) => item.id === row.id && item.deviceCode === row.deviceCode)
 
 // 多选 切换行选中状态
-const toggleRow = (row) => {
-  const includes = selectedRows.value.includes(row)
-  if (includes) {
-    selectedRows.value.splice(row)
+const toggleRow = (row: IotInspectItemVO) => {
+  const index = selectedRows.value.findIndex(
+    (item) => item.id === row.id && item.deviceCode === row.deviceCode
+  )
+  if (index !== -1) {
+    selectedRows.value.splice(index, 1)
   } else {
-    selectedRows.value.push(row) // 选中
+    selectedRows.value.push(row)
   }
-  tableRef.value.toggleRowSelection(row, !includes)
 }
 
 // 点击整行选中
@@ -261,12 +238,10 @@ const handleConfirm = () => {
     }))
   )
   dialogVisible.value = false
-  handleClose()
 }
 
 // 关闭时清空选择
 const handleClose = () => {
-  tableRef.value?.clearSelection()
   selectedRows.value = []
   emit('close')
 }
@@ -275,21 +250,183 @@ const handleQuery = () => {
   queryParams.pageNo = 1
   getList()
 }
-const choose = (row: DictDataVO) => {
-  emit('choose', row)
-  dialogVisible.value = false
-}
 /** 重置按钮操作 */
 const resetQuery = () => {
-  queryFormRef.value.resetFields()
+  queryFormRef.value?.resetFields()
   handleQuery()
 }
 </script>
-<style lang="scss">
-.no-label-radio .el-radio__label {
+<style lang="scss" scoped>
+.dialog-title {
+  display: flex;
+  align-items: center;
+  min-width: 0;
+}
+
+.dialog-title__icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 38px;
+  height: 38px;
+  margin-right: 12px;
+  font-size: 18px;
+  color: var(--el-color-primary);
+  background: var(--el-color-primary-light-9);
+  border: 1px solid var(--el-color-primary-light-7);
+  border-radius: 8px;
+}
+
+.dialog-title__text {
+  font-size: 17px;
+  font-weight: 600;
+  line-height: 24px;
+  color: var(--el-text-color-primary);
+}
+
+.dialog-title__subtitle {
+  margin-top: 1px;
+  font-size: 12px;
+  line-height: 18px;
+  color: var(--el-text-color-secondary);
+}
+
+.dialog-content {
+  display: flex;
+  flex-direction: column;
+  gap: 12px;
+  padding-top: 8px;
+}
+
+.query-panel {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  justify-content: space-between;
+  padding: 12px 16px;
+  background: var(--el-fill-color-extra-light);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+  gap: 12px 20px;
+}
+
+.query-row {
+  display: flex;
+  flex: 1 1 auto;
+  flex-wrap: wrap;
+  align-items: center;
+  min-width: 0;
+  gap: 12px 20px;
+}
+
+.query-actions {
+  flex: 0 0 auto;
+}
+
+.query-actions :deep(.el-form-item__content) {
+  display: flex;
+  gap: 8px;
+}
+
+.query-actions :deep(.el-button) {
+  margin-left: 0;
+}
+
+.query-control {
+  width: 190px;
+}
+
+.query-control--date {
+  width: 240px;
+}
+
+.query-panel :deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+.query-panel :deep(.el-form-item__label) {
+  white-space: nowrap;
+}
+
+.selected-panel {
+  display: flex;
+  align-items: center;
+  padding: 10px 14px;
+  background: var(--el-color-primary-light-9);
+  border: 1px solid var(--el-color-primary-light-8);
+  border-radius: 8px;
+  gap: 12px;
+}
+
+.selected-panel__label {
+  flex: 0 0 auto;
+  font-weight: 600;
+  color: var(--el-color-primary);
+  white-space: nowrap;
+}
+
+.selected-panel__tags {
+  display: flex;
+  flex-wrap: wrap;
+  min-width: 0;
+  gap: 6px;
+  transform: translateY(1.5px);
+}
+
+.table-section {
+  padding: 12px 12px 0;
+  overflow: hidden;
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+}
+
+.table-section :deep(.pagination-container) {
+  padding: 12px 8px;
+}
+
+.no-label-checkbox :deep(.el-checkbox__label) {
   display: none;
 }
-.no-label-radio .el-radio__inner {
-  margin-right: 0;
+
+.dialog-footer {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  width: 100%;
+}
+
+.selection-summary {
+  font-size: 13px;
+  color: var(--el-text-color-secondary);
+}
+
+@media (width <= 768px) {
+  .dialog-title__subtitle {
+    display: none;
+  }
+
+  .query-panel,
+  .query-row,
+  .query-row :deep(.el-form-item),
+  .query-actions,
+  .query-control,
+  .query-control--date {
+    width: 100%;
+  }
+
+  .query-actions :deep(.el-form-item__content) {
+    display: grid;
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+    width: 100%;
+  }
+
+  .query-actions :deep(.el-button) {
+    width: 100%;
+  }
+
+  .selected-panel {
+    flex-direction: column;
+    gap: 8px;
+  }
 }
 </style>

+ 410 - 139
src/views/pms/inspect/route/IotInspectRoute.vue

@@ -1,90 +1,78 @@
 <template>
-  <ContentWrap v-loading="formLoading">
-    <ContentWrap>
-      <el-form
-        ref="formRef"
-        :model="formData"
-        :rules="formRules"
-        style="margin-right: 4em; margin-left: 0.5em; margin-top: 1em"
-        label-width="130px"
-      >
-        <div class="base-expandable-content">
-          <el-row>
-            <el-col :span="8">
-              <el-form-item :label="t('route.RouteName')" prop="routeName">
-                <el-input v-model="formData.routeName" :placeholder="t('route.RouteName')" />
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item :label="t('monitor.category')" prop="deviceClassify">
-                <el-tree-select
-                  filterable
-                  clearable
-                  v-model="formData.deviceClassify"
-                  :data="productClassifyList"
-                  :props="defaultProps"
-                  check-strictly
-                  node-key="id"
-                  :placeholder="t('deviceForm.categoryHolder')"
-                />
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item :label="t('iotDevice.brand')" prop="brandName">
-                <el-select
-                  clearable
-                  v-model="formData.brandName"
-                  @clear="brandClear"
-                  :placeholder="t('iotDevice.brandHolder')"
-                  @click="openBrand"
-                />
-              </el-form-item>
-            </el-col>
-            <el-col :span="8">
-              <el-form-item :label="t('faultForm.device')" prop="deviceName">
-                <el-input
-                  v-model="formData.deviceName"
-                  @clear="handleClear"
-                  :placeholder="t('iotMaintain.deviceHolder')"
-                  @click="openDevice"
-                  clearable
-                />
-              </el-form-item>
-            </el-col>
-            <el-col :span="16">
-              <el-form-item :label="t('inspect.routeDesc')" prop="routeDesc">
-                <el-input
-                  type="textarea"
-                  v-model="formData.routeDesc"
-                  :placeholder="t('inspect.routeDescHolder')"
-                  clearable
-                />
-              </el-form-item>
-            </el-col>
-          </el-row>
+  <div class="route-form-page" v-loading="formLoading">
+    <header class="page-header">
+      <div class="page-header__main">
+        <div class="page-header__icon"><Icon icon="ep:guide" /></div>
+        <div>
+          <h1>{{ pageTitle }}</h1>
+          <p>配置巡检设备、路线说明及巡检项执行顺序</p>
         </div>
-      </el-form>
-    </ContentWrap>
-    <ContentWrap>
-      <!-- 列表 -->
-      <ContentWrap>
-        <ContentWrap>
-          <el-form
-            class="-mb-15px"
-            :model="queryParams"
-            ref="queryFormRef"
-            :inline="true"
-            label-width="68px"
-          >
-            <el-form-item>
-              <el-button @click="openForm" type="primary"
-                ><Icon icon="ep:plus" class="mr-5px" />
-                {{ t('route.selectInspectionItems') }}</el-button
-              >
-            </el-form-item>
-          </el-form>
-        </ContentWrap>
+      </div>
+      <el-button @click="close"> <Icon icon="ep:arrow-left" class="mr-5px" />返回 </el-button>
+    </header>
+
+    <el-form
+      ref="formRef"
+      :model="formData"
+      :rules="formRules"
+      size="default"
+      label-width="100px"
+      class="route-form">
+      <section class="form-section">
+        <header class="section-heading"><span></span>路线信息</header>
+        <div class="form-grid">
+          <el-form-item :label="t('route.RouteName')" prop="routeName">
+            <el-input v-model="formData.routeName" :placeholder="t('route.RouteName')" />
+          </el-form-item>
+          <el-form-item :label="t('monitor.category')" prop="deviceClassify">
+            <el-tree-select
+              v-model="formData.deviceClassify"
+              :data="productClassifyList"
+              :props="defaultProps"
+              check-strictly
+              node-key="id"
+              :placeholder="t('deviceForm.categoryHolder')"
+              filterable
+              clearable />
+          </el-form-item>
+          <el-form-item :label="t('iotDevice.brand')" prop="brandName">
+            <el-select
+              v-model="formData.brandName"
+              :placeholder="t('iotDevice.brandHolder')"
+              clearable
+              @clear="brandClear"
+              @click="openBrand" />
+          </el-form-item>
+          <el-form-item :label="t('faultForm.device')" prop="deviceName">
+            <el-input
+              v-model="formData.deviceName"
+              :placeholder="t('iotMaintain.deviceHolder')"
+              clearable
+              @clear="handleClear"
+              @click="openDevice" />
+          </el-form-item>
+          <el-form-item :label="t('inspect.routeDesc')" prop="routeDesc" class="form-grid__wide">
+            <el-input
+              v-model="formData.routeDesc"
+              type="textarea"
+              :rows="3"
+              :placeholder="t('inspect.routeDescHolder')"
+              clearable />
+          </el-form-item>
+        </div>
+      </section>
+
+      <section class="form-section inspection-section">
+        <header class="section-heading section-heading--actions">
+          <div class="section-heading__title"><span></span>巡检项顺序</div>
+          <el-button type="primary" @click="openForm">
+            <Icon icon="ep:plus" class="mr-5px" />{{ t('route.selectInspectionItems') }}
+          </el-button>
+        </header>
+
+        <el-empty v-if="items.length === 0" description="暂未选择巡检项" :image-size="72" />
         <draggable
+          v-else
           v-model="items"
           item-key="id"
           tag="div"
@@ -92,47 +80,41 @@
           handle=".sortable-item"
           :animation="150"
           @start="dragStart"
-          @end="dragEnd"
-        >
+          @end="dragEnd">
           <template #item="{ element, index }">
             <div class="sortable-item">
-              <!-- 序号显示 -->
               <div class="order-number">{{ index + 1 }}</div>
-
-              <!-- 拖动手柄 -->
-              <span class="drag-handle">≡</span>
-
-              <!-- 组件内容 -->
+              <span class="drag-handle"><Icon icon="ep:rank" /></span>
               <div class="component-content">
-                <span style="font-weight: bold">{{ t('inspect.InspectionItems') }}:</span
-                ><span style="font-size: 14px">{{ element.item }}</span
-                >&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
-                <span style="font-weight: bold">{{ t('inspect.InspectionStandards') }}:</span
-                ><span style="font-size: 14px">{{ element.standard }}</span>
-              </div>
-              <div>
-                <el-button type="warning" @click="deleteDraggable(index)">{{ t('form.delete') }}</el-button>
+                <div class="item-field">
+                  <span class="item-field__label">{{ t('inspect.InspectionItems') }}</span>
+                  <span class="item-field__value">{{ element.item || '-' }}</span>
+                </div>
+                <div class="item-field item-field--standard">
+                  <span class="item-field__label">{{ t('inspect.InspectionStandards') }}</span>
+                  <span class="item-field__value">{{ element.standard || '-' }}</span>
+                </div>
               </div>
+              <el-button link type="danger" @click="deleteDraggable(index)">
+                <Icon icon="ep:delete" class="mr-5px" />{{ t('form.delete') }}
+              </el-button>
             </div>
           </template>
         </draggable>
-      </ContentWrap>
-    </ContentWrap>
-    <ContentWrap>
-      <el-form>
-        <el-form-item style="float: right">
-          <el-button @click="submitForm" type="primary" :disabled="formLoading">{{ t('iotMaintain.save') }}</el-button>
-          <el-button @click="close">{{ t('iotMaintain.cancel') }}</el-button>
-        </el-form-item>
-      </el-form>
-    </ContentWrap>
-    <InspectItemList
-      ref="inspectItemFormRef"
-      @choose="inspectItemChoose"
-    />
+      </section>
+    </el-form>
+
+    <footer class="action-bar">
+      <el-button size="default" @click="close">{{ t('iotMaintain.cancel') }}</el-button>
+      <el-button size="default" type="primary" :loading="formLoading" @click="submitForm">
+        <Icon icon="ep:check" class="mr-5px" />{{ t('iotMaintain.save') }}
+      </el-button>
+    </footer>
+
+    <InspectItemList ref="inspectItemFormRef" @choose="inspectItemChoose" />
     <DeviceList ref="deviceFormRef" @choose="deviceChoose" />
     <BrandList ref="brandFormRef" @choose="brandChoose" />
-  </ContentWrap>
+  </div>
 </template>
 <script setup lang="ts">
 import * as UserApi from '@/api/system/user'
@@ -146,7 +128,7 @@ import draggable from 'vuedraggable'
 import InspectItemList from '@/views/pms/inspect/route/InspectItemList.vue'
 import DeviceList from '@/views/pms/failure/DeviceList.vue'
 import { IotInspectRouteApi, IotInspectRouteVO } from '@/api/pms/inspect/route'
-import BrandList from "@/views/pms/device/BrandList.vue";
+import BrandList from '@/views/pms/device/BrandList.vue'
 
 /** 维修工单 表单 */
 defineOptions({ name: 'RouteAdd' })
@@ -159,6 +141,7 @@ const deptUsers = ref<UserApi.UserVO[]>([]) // 用户列表
 const dialogTitle = ref('') // 弹窗的标题
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
 const formType = ref('') // 表单的类型:create - 新增;update - 修改
+const pageTitle = computed(() => (formType.value === 'update' ? '编辑巡检路线' : '新增巡检路线'))
 const deviceLabel = ref('') // 表单的类型:create - 新增;update - 修改
 const drawerVisible = ref<boolean>(false)
 const showDrawer = ref()
@@ -188,7 +171,7 @@ const brandChoose = (row) => {
   // brandLabel.value = row.value
   formData.value.brandName = row.label
 }
-const brandClear = () =>{
+const brandClear = () => {
   formData.value.brand = undefined
   formData.value.brandName = undefined
 }
@@ -246,7 +229,11 @@ const openForm = () => {
     message.error('请选择设备类别')
     return
   }
-  inspectItemFormRef.value.open(formData.value.deviceClassify, formData.value.deviceId,formData.value.brandName)
+  inspectItemFormRef.value.open(
+    formData.value.deviceClassify,
+    formData.value.deviceId,
+    formData.value.brandName
+  )
 }
 const deviceFormRef = ref()
 const openDevice = () => {
@@ -263,7 +250,7 @@ const close = () => {
   // })
 
   delView(unref(currentRoute))
-  push({ name: 'IotInspectRoute', params:{}})
+  push({ name: 'IotInspectRoute', params: {} })
 }
 const itemsWithIndex = computed(() => {
   return items.value.map((item, index) => ({
@@ -275,7 +262,7 @@ const itemsWithIndex = computed(() => {
 })
 const itemsWithIndexUpdate = computed(() => {
   return items.value.map((item, index) => ({
-    itemId: item.itemId||item.id,
+    itemId: item.itemId || item.id,
     item: item.item,
     standard: item.standard,
     index: index + 1 // 序号从1开始
@@ -345,10 +332,57 @@ const handleDelete = async (id: number) => {
 }
 </script>
 <style scoped>
+@media (width <= 1400px) {
+  .route-form-page .form-grid {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+}
+
+@media (width <= 768px) {
+  .route-form-page .page-header {
+    align-items: flex-start;
+  }
+
+  .route-form-page .page-header p {
+    display: none;
+  }
+
+  .route-form-page .form-grid {
+    grid-template-columns: minmax(0, 1fr);
+    padding: 14px 14px 0;
+  }
+
+  .route-form-page .form-grid__wide {
+    grid-column: auto;
+  }
+
+  .route-form-page .section-heading--actions {
+    height: auto;
+    min-height: 44px;
+    padding-top: 6px;
+    padding-bottom: 6px;
+  }
+
+  .route-form-page .component-content {
+    grid-template-columns: minmax(0, 1fr);
+    gap: 6px;
+  }
+
+  .route-form-page .sortable-item {
+    align-items: flex-start;
+  }
+
+  .route-form-page .drag-handle {
+    padding-right: 8px;
+    padding-left: 8px;
+  }
+}
+
 .base-expandable-content {
   overflow: hidden; /* 隐藏溢出的内容 */
   transition: max-height 0.3s ease; /* 平滑过渡效果 */
 }
+
 /* 横向布局容器 */
 .horizontal-list {
   display: flex;
@@ -360,28 +394,29 @@ const handleDelete = async (id: number) => {
 
 /* 拖拽项样式 */
 .horizontal-item {
-  flex: 0 0 auto;
+  display: flex;
   min-width: 150px;
   padding: 20px;
+  cursor: move;
   background: #d3a137;
   border: 2px solid #e0e0e0;
   border-radius: 8px;
-  cursor: move;
-  user-select: none; /* 防止文字选中:ml-citation{ref="7" data="citationList"} */
   transition:
     transform 0.3s,
     box-shadow 0.3s;
-  display: flex;
+  user-select: none; /* 防止文字选中:ml-citation{ref="7" data="citationList"} */
+  flex: 0 0 auto;
   align-items: center;
   gap: 10px;
 }
 
 /* 拖拽手柄样式 */
 .drag-handle {
-  opacity: 0.5;
   cursor: move;
+  opacity: 0.5;
   transition: opacity 0.3s;
 }
+
 .drag-handle:hover {
   opacity: 1;
 }
@@ -389,31 +424,31 @@ const handleDelete = async (id: number) => {
 /* 拖拽时的悬停效果 */
 .horizontal-item:hover {
   transform: translateY(-2px);
-  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+  box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
 }
 
 /* 滚动条样式 */
 .horizontal-list::-webkit-scrollbar {
   height: 8px;
 }
+
 .horizontal-list::-webkit-scrollbar-thumb {
   background: #888;
   border-radius: 4px;
 }
 
 .sortable-container {
-  cursor: move;
   display: flex;
+  padding: 7px;
+  overflow-y: auto;
+  cursor: move;
   flex-direction: column;
   gap: 9px;
-  //max-height: 80vh;
-  overflow-y: auto;
-  padding: 7px;
 }
 
 .sortable-item {
   display: flex;
-  align-items: center;
+  height: 50%;
   padding: 8px;
   background: #fff;
   border: 1px solid #ebeef5;
@@ -422,24 +457,24 @@ const handleDelete = async (id: number) => {
     transform 0.3s,
     box-shadow 0.3s;
   user-select: none;
-  height: 50%;
+  align-items: center;
 }
 
 .sortable-item:hover {
-  box-shadow: 0 2px 12px rgba(0, 0, 0, 0.1);
+  box-shadow: 0 2px 12px rgb(0 0 0 / 10%);
 }
 
 .order-number {
+  display: flex;
   width: 24px;
   height: 24px;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  background: #409eff;
-  color: white;
-  border-radius: 50%;
   margin-right: 7px;
   font-weight: bold;
+  color: white;
+  background: #409eff;
+  border-radius: 50%;
+  align-items: center;
+  justify-content: center;
 }
 
 .drag-handle {
@@ -461,8 +496,244 @@ const handleDelete = async (id: number) => {
 .sortable-container::-webkit-scrollbar {
   width: 8px;
 }
+
 .sortable-container::-webkit-scrollbar-thumb {
   background: #c0c4cc;
   border-radius: 4px;
 }
+
+.route-form-page {
+  display: flex;
+  min-height: calc(
+    100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
+  );
+  flex-direction: column;
+  gap: 12px;
+}
+
+.page-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  min-height: 66px;
+  padding: 12px 16px;
+  background: var(--el-bg-color);
+  border-radius: 8px;
+  box-shadow: var(--el-box-shadow-light);
+}
+
+.page-header__main {
+  display: flex;
+  align-items: center;
+  min-width: 0;
+}
+
+.page-header__icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 38px;
+  height: 38px;
+  margin-right: 12px;
+  font-size: 18px;
+  color: var(--el-color-primary);
+  background: var(--el-color-primary-light-9);
+  border: 1px solid var(--el-color-primary-light-7);
+  border-radius: 8px;
+}
+
+.page-header h1 {
+  margin: 0;
+  font-size: 17px;
+  font-weight: 600;
+  line-height: 24px;
+  color: var(--el-text-color-primary);
+}
+
+.page-header p {
+  margin: 1px 0 0;
+  font-size: 12px;
+  line-height: 18px;
+  color: var(--el-text-color-secondary);
+}
+
+.route-form {
+  display: flex;
+  flex: 1;
+  min-height: 0;
+  flex-direction: column;
+  gap: 12px;
+}
+
+.form-section {
+  overflow: hidden;
+  background: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+  box-shadow: var(--el-box-shadow-lighter);
+}
+
+.section-heading {
+  display: flex;
+  align-items: center;
+  height: 44px;
+  padding: 0 16px;
+  font-size: 15px;
+  font-weight: 600;
+  color: var(--el-text-color-primary);
+  background: var(--el-fill-color-extra-light);
+  border-bottom: 1px solid var(--el-border-color-lighter);
+}
+
+.section-heading > span,
+.section-heading__title > span {
+  width: 4px;
+  height: 17px;
+  margin-right: 9px;
+  background: var(--el-color-primary);
+  border-radius: 2px;
+}
+
+.section-heading--actions {
+  justify-content: space-between;
+}
+
+.section-heading__title {
+  display: flex;
+  align-items: center;
+}
+
+.form-grid {
+  display: grid;
+  grid-template-columns: repeat(4, minmax(0, 1fr));
+  column-gap: 24px;
+  padding: 18px 18px 2px;
+}
+
+.form-grid__wide {
+  grid-column: 1 / -1;
+}
+
+.route-form :deep(.el-form-item) {
+  margin-bottom: 16px;
+}
+
+.route-form :deep(.el-input),
+.route-form :deep(.el-select),
+.route-form :deep(.el-tree-select) {
+  width: 100%;
+}
+
+.inspection-section {
+  display: flex;
+  min-height: 280px;
+  flex: 1;
+  flex-direction: column;
+}
+
+.inspection-section :deep(.el-empty) {
+  flex: 1;
+}
+
+.sortable-container {
+  display: flex;
+  min-height: 0;
+  padding: 14px;
+  overflow-y: auto;
+  cursor: default;
+  flex: 1;
+  flex-direction: column;
+  gap: 10px;
+}
+
+.sortable-item {
+  display: flex;
+  height: auto;
+  min-height: 58px;
+  padding: 10px 12px;
+  cursor: grab;
+  background: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 6px;
+  align-items: center;
+  transition:
+    border-color 0.2s ease,
+    box-shadow 0.2s ease;
+  user-select: none;
+}
+
+.sortable-item:hover {
+  border-color: var(--el-color-primary-light-7);
+  box-shadow: 0 3px 10px rgb(0 0 0 / 6%);
+}
+
+.sortable-item:active {
+  cursor: grabbing;
+}
+
+.order-number {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 26px;
+  height: 26px;
+  margin-right: 2px;
+  font-size: 13px;
+  font-weight: 600;
+  color: #fff;
+  background: var(--el-color-primary);
+  border-radius: 50%;
+  flex: 0 0 auto;
+}
+
+.drag-handle {
+  display: flex;
+  padding: 8px 12px;
+  font-size: 18px;
+  color: var(--el-text-color-placeholder);
+  cursor: grab;
+  opacity: 1;
+}
+
+.component-content {
+  display: grid;
+  grid-template-columns: minmax(180px, 1fr) minmax(280px, 2fr);
+  min-width: 0;
+  flex: 1;
+  gap: 16px;
+}
+
+.item-field {
+  display: flex;
+  min-width: 0;
+  align-items: flex-start;
+}
+
+.item-field__label {
+  margin-right: 8px;
+  font-weight: 600;
+  color: var(--el-text-color-regular);
+  white-space: nowrap;
+}
+
+.item-field__value {
+  min-width: 0;
+  color: var(--el-text-color-secondary);
+  overflow-wrap: anywhere;
+}
+
+.action-bar {
+  position: sticky;
+  bottom: 0;
+  z-index: 5;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  min-height: 56px;
+  padding: 10px 16px;
+  margin-top: auto;
+  background: var(--el-bg-color);
+  border-top: 1px solid var(--el-border-color-lighter);
+  box-shadow: 0 -4px 12px rgb(0 0 0 / 5%);
+}
 </style>

+ 267 - 144
src/views/pms/inspect/route/index.vue

@@ -1,146 +1,147 @@
 <template>
-  <el-row :gutter="20">
-    <DeptTree @node-click="handleDeptNodeClick" v-model:collapsed="isLeftContentCollapsed" />
-    <el-col :xs="24" :span="isLeftContentCollapsed ? 24 : 20">
-      <div
-        style="border: none; background: #fff; display: flex; align-items: center"
-        class="py-2 rounded-sm px-2 mb-2"
-      >
-        <!-- 搜索工作栏 -->
-        <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']"
-            >
-              <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>
+  <div
+    class="inspect-route-page grid grid-cols-[auto_1fr] grid-rows-[auto_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]">
+    <DeptTreeSelect
+      :top-id="rootDeptId"
+      :deptId="deptId"
+      v-model="queryParams.deptId"
+      :init-select="false"
+      :show-title="false"
+      request-api="getSimpleDeptList"
+      class="inspect-route-tree row-span-2"
+      @node-click="handleDeptNodeClick" />
+
+    <el-form
+      ref="queryFormRef"
+      :model="queryParams"
+      size="default"
+      label-width="88px"
+      class="inspect-route-query bg-white dark:bg-[#1d1e1f] rounded-lg shadow px-6 py-3 min-w-0">
+      <div class="query-row">
+        <el-form-item :label="t('route.RouteName')" prop="routeName">
+          <el-input
+            v-model="queryParams.routeName"
+            :placeholder="t('route.nHolder')"
+            clearable
+            class="query-control"
+            @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item :label="t('monitor.category')" prop="deviceClassify">
+          <el-tree-select
+            v-model="queryParams.deviceClassify"
+            :data="productClassifyList"
+            :props="defaultProps"
+            check-strictly
+            node-key="id"
+            :placeholder="t('deviceForm.categoryHolder')"
+            filterable
+            clearable
+            class="query-control" />
+        </el-form-item>
+        <el-form-item :label="t('monitor.deviceName')" prop="deviceName">
+          <el-input
+            v-model="queryParams.deviceName"
+            :placeholder="t('iotDevice.nameHolder')"
+            clearable
+            class="query-control"
+            @keyup.enter="handleQuery" />
+        </el-form-item>
+      </div>
+
+      <el-form-item class="query-actions">
+        <el-button type="primary" @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
+          :loading="exportLoading"
+          @click="handleExport"
+          v-hasPermi="['rq:iot-inspect-route:export']">
+          <Icon icon="ep:download" class="mr-5px" />导出
+        </el-button>
+      </el-form-item>
+    </el-form>
+
+    <div
+      class="table-panel bg-white dark:bg-[#1d1e1f] shadow rounded-lg flex flex-col p-4 min-w-0 min-h-0">
+      <div class="flex-1 relative min-h-0">
+        <el-auto-resizer class="absolute">
+          <template #default="{ width, height }">
+            <ZmTable
+              :loading="loading"
+              :data="list"
+              :width="width"
+              :height="height"
+              :max-height="height"
+              show-border
+              settings-cache-key="pms-inspect-route-list">
+              <ZmTableColumn
+                type="index"
+                :label="t('iotDevice.serial')"
+                :width="70"
+                fixed="left"
+                hide-in-column-settings />
+              <ZmTableColumn :label="t('route.RouteName')" prop="routeName" />
+              <ZmTableColumn :label="t('monitor.category')" prop="deviceClassifyName" />
+              <ZmTableColumn :label="t('monitor.deviceName')" prop="deviceName" />
+              <ZmTableColumn :label="t('iotDevice.brand')" prop="brandName" />
+              <ZmTableColumn
+                :label="t('common.createTime')"
+                prop="createTime"
+                :formatter="dateFormatter" />
+              <ZmTableColumn
+                :label="t('operationFill.operation')"
+                :width="120"
+                action
+                fixed="right">
+                <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>
+              </ZmTableColumn>
+            </ZmTable>
+          </template>
+        </el-auto-resizer>
       </div>
 
-      <!-- 列表 -->
-      <ContentWrap style="border: none; margin-top: 10px">
-        <zm-table :loading="loading" :data="list" height="64vh">
-          <zm-table-column :label="t('iotDevice.serial')" width="70" align="center">
-            <template #default="scope">
-              {{ scope.$index + 1 }}
-            </template>
-          </zm-table-column>
-          <el-table-column :label="t('route.RouteName')" align="center" prop="routeName" />
-          <zm-table-column
-            :label="t('monitor.category')"
-            align="center"
-            prop="deviceClassifyName"
-          />
-          <zm-table-column :label="t('monitor.deviceName')" align="center" prop="deviceName" />
-          <!--      <el-table-column label="巡检项" align="center" prop="itemJson" />-->
-          <zm-table-column :label="t('iotDevice.brand')" align="center" prop="brandName" />
-          <zm-table-column
-            :label="t('common.createTime')"
-            align="center"
-            prop="createTime"
-            :formatter="dateFormatter"
-            width="180px"
-          />
-          <!--      <el-table-column label="部门id" align="center" prop="deptId" />-->
-          <zm-table-column
-            :label="t('operationFill.operation')"
-            align="center"
-            min-width="120px"
-            action
-          >
-            <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>
-          </zm-table-column>
-        </zm-table>
-        <!-- 分页 -->
-        <Pagination
+      <div class="h-8 mt-2 flex items-center justify-end">
+        <el-pagination
+          v-show="total > 0"
+          size="default"
+          :current-page="queryParams.pageNo"
+          :page-size="queryParams.pageSize"
+          :background="true"
+          :page-sizes="[10, 20, 30, 50, 100]"
           :total="total"
-          v-model:page="queryParams.pageNo"
-          v-model:limit="queryParams.pageSize"
-          @pagination="getList"
-        />
-      </ContentWrap>
-    </el-col>
-  </el-row>
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange" />
+      </div>
+    </div>
+  </div>
 
   <!-- 表单弹窗:添加/修改 -->
   <IotInspectRouteForm ref="formRef" @success="getList" />
@@ -151,18 +152,19 @@ 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/DeptTree2.vue'
 import { defaultProps } from '@/utils/tree'
+import { useUserStore } from '@/store/modules/user'
 const { push } = useRouter() // 路由跳转
 /** 巡检路线 列表 */
 defineOptions({ name: 'IotInspectRoute' })
 
 import { useTableComponents } from '@/components/ZmTable/useTableComponents'
-const { ZmTable, ZmTableColumn } = useTableComponents()
+const { ZmTable, ZmTableColumn } = useTableComponents<IotInspectRouteVO>()
 
 const message = useMessage() // 消息弹窗
 const { t } = useI18n() // 国际化
-let isLeftContentCollapsed = ref(false)
+const rootDeptId = 156
+const deptId = useUserStore().getUser.deptId || rootDeptId
 const loading = ref(true) // 列表的加载中
 const list = ref<IotInspectRouteVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
@@ -183,8 +185,9 @@ const queryParams = reactive({
 const queryFormRef = ref() // 搜索的表单
 const exportLoading = ref(false) // 导出的加载中
 
-const handleDeptNodeClick = async (row) => {
+const handleDeptNodeClick = async (row: Tree) => {
   queryParams.deptId = row.id
+  queryParams.pageNo = 1
   await getList()
 }
 /** 查询列表 */
@@ -207,10 +210,20 @@ const handleQuery = () => {
 
 /** 重置按钮操作 */
 const resetQuery = () => {
-  queryFormRef.value.resetFields()
+  queryFormRef.value?.resetFields()
   handleQuery()
 }
 
+const handleSizeChange = (pageSize: number) => {
+  queryParams.pageSize = pageSize
+  handleQuery()
+}
+
+const handleCurrentChange = (pageNo: number) => {
+  queryParams.pageNo = pageNo
+  getList()
+}
+
 /** 添加/修改操作 */
 const openForm = (type: string, id?: number) => {
   //修改
@@ -255,3 +268,113 @@ onMounted(() => {
   getList()
 })
 </script>
+
+<style scoped>
+.inspect-route-query {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px 24px;
+}
+
+.query-row {
+  display: flex;
+  flex: 1 1 auto;
+  flex-wrap: wrap;
+  align-items: center;
+  min-width: 0;
+  gap: 12px 24px;
+}
+
+.query-actions {
+  flex: 0 0 auto;
+}
+
+.query-actions :deep(.el-form-item__content) {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px 10px;
+}
+
+.query-actions :deep(.el-button) {
+  margin-left: 0;
+}
+
+.query-control {
+  width: 190px;
+}
+
+:deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+:deep(.el-form-item__label) {
+  white-space: nowrap;
+}
+
+@media (width >= 1800px) {
+  .inspect-route-query,
+  .query-row {
+    flex-wrap: nowrap;
+  }
+}
+
+@media (width <= 1500px) {
+  .inspect-route-query,
+  .query-row {
+    gap: 12px 18px;
+  }
+
+  .query-control {
+    width: 175px;
+  }
+}
+
+@media (width <= 1200px) {
+  .inspect-route-page {
+    grid-template-columns: minmax(0, 1fr);
+    grid-template-rows: auto auto minmax(480px, 1fr);
+    height: auto;
+    min-height: calc(
+      100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
+    );
+  }
+
+  :deep(.inspect-route-tree) {
+    grid-row: auto !important;
+    width: 100% !important;
+    height: 320px !important;
+    min-width: 0 !important;
+  }
+
+  .query-actions {
+    width: 100%;
+  }
+}
+
+@media (width <= 768px) {
+  .inspect-route-query,
+  .table-panel {
+    padding: 12px;
+  }
+
+  .query-row,
+  .query-row :deep(.el-form-item),
+  .query-actions,
+  .query-control {
+    width: 100%;
+  }
+
+  .query-actions :deep(.el-form-item__content) {
+    display: grid;
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+    gap: 8px;
+    width: 100%;
+  }
+
+  .query-actions :deep(.el-button) {
+    width: 100%;
+  }
+}
+</style>