Sfoglia il codice sorgente

feat(维修管理): 优化维修工单列表、表单、详情及记录页面

- 统一维修工单列表与设备状态页面的布局风格
- 优化查询条件换行、部门树、分页及自适应表格
- 重构维修工单新增编辑页的信息分区和响应式表单排布
- 将维修项目和物料明细表格替换为 ZmTable
- 增加新增编辑页吸底保存操作栏
- 统一维修详情页字段排布、关联物料及吸底返回栏
- 优化物料详情抽屉的标题、表格和响应式宽度
- 重构维修记录页的工单概览、筛选区和记录表格
- 修复 ZmTable 空数据状态误显示横向滚动条的问题
- 修复表格 loading 缺失及物料抽屉加载状态警告
- 统一相关页面按钮尺寸、边框、间距和返回操作
Zimo 1 settimana fa
parent
commit
f960c7e70d

+ 2 - 0
src/components/ZmTable/index.vue

@@ -725,6 +725,8 @@ defineExpose({
   }
 
   .el-table__empty-block {
+    width: 100% !important;
+    min-width: 100%;
     min-height: var(--zm-table-empty-min-height);
     background: var(--zm-table-empty-bg);
   }

+ 250 - 115
src/views/pms/iotmainworkorder/SelectedMaterialDrawer.vue

@@ -1,156 +1,291 @@
 <template>
   <el-drawer
-    :title="t('bomList.materialDetail')"
     :append-to-body="true"
     :model-value="modelValue"
-    @update:model-value="$emit('update:modelValue', $event)"
     :show-close="false"
     direction="rtl"
     :size="computedSize"
-    :before-close="handleClose"
-  >
-    <template v-if="nodeId">
-      <div v-loading="loading" style="height: 100%">
-        <el-table :data="filteredMaterials" style="width: 100%">
-          <el-table-column prop="bomNodeId" :label="t('bomList.bomNode')" width="180" v-if="false"/>
-          <el-table-column prop="factory" :label="t('workOrderMaterial.factory')" width="180" v-if="!hideExtraColumns"/>
-          <el-table-column prop="costCenter" :label="t('workOrderMaterial.costCenter')" width="180" v-if="!hideExtraColumns"/>
-          <el-table-column prop="projectDepartment" :label="t('workOrderMaterial.storageLocation')"  width="180" v-if="!hideExtraColumns"/>
-          <el-table-column prop="materialName" :label="t('workOrderMaterial.materialName')" width="180" />
-          <el-table-column prop="materialCode" :label="t('workOrderMaterial.materialCode')" width="180" />
-          <el-table-column prop="unit" :label="t('workOrderMaterial.unit')" width="180" />
-          <el-table-column prop="unitPrice" :label="t('workOrderMaterial.unitPrice')" width="180" />
-          <el-table-column prop="quantity" :label="t('workOrderMaterial.ConsumptionQuantity')" width="180" />
-          <el-table-column prop="materialSource" :label="t('bomList.type')" width="180" />
-          <el-table-column :label="t('workplace.operation')" align="right" v-if="!props.detail">
-            <template #default="scope">
-              <el-button
-                size="small"
-                type="danger"
-                @click="handleDelete(scope.row)"
-              >{{ t('form.delete') }}</el-button>
-            </template>
-          </el-table-column>
-        </el-table>
-        <!-- 分页
-        <Pagination
-          :total="total"
-          v-model:page="queryParams.pageNo"
-          v-model:limit="queryParams.pageSize"
-          @pagination="loadMaterials(props.nodeId)"
-        />
-        -->
+    class="selected-material-drawer"
+    @update:model-value="emit('update:modelValue', $event)">
+    <template #header>
+      <div class="drawer-header">
+        <div class="drawer-header__main">
+          <div class="drawer-header__icon">
+            <Icon icon="ep:goods" :size="22" />
+          </div>
+          <div>
+            <div class="drawer-title">{{ t('bomList.materialDetail') }}</div>
+            <div class="drawer-subtitle">查看当前维修项目关联的物料明细</div>
+          </div>
+        </div>
+        <el-button circle @click="handleClose">
+          <Icon icon="ep:close" />
+        </el-button>
       </div>
     </template>
 
+    <div class="drawer-body">
+      <section class="material-section">
+        <div class="section-header">
+          <div class="section-title">
+            <span class="section-marker"></span>
+            <span>关联物料</span>
+          </div>
+          <span class="material-total">共 {{ filteredMaterials.length }} 条</span>
+        </div>
+
+        <div class="table-container">
+          <ZmTable
+            :data="filteredMaterials"
+            :loading="loading"
+            :show-border="true"
+            settings-cache-key="pms-maintain-selected-material">
+            <ZmTableColumn
+              v-if="!hideExtraColumns"
+              prop="factory"
+              :label="t('workOrderMaterial.factory')"
+              min-width="140" />
+            <ZmTableColumn
+              v-if="!hideExtraColumns"
+              prop="costCenter"
+              :label="t('workOrderMaterial.costCenter')"
+              min-width="150" />
+            <ZmTableColumn
+              v-if="!hideExtraColumns"
+              prop="projectDepartment"
+              :label="t('workOrderMaterial.storageLocation')"
+              min-width="150" />
+            <ZmTableColumn
+              prop="materialName"
+              :label="t('workOrderMaterial.materialName')"
+              min-width="180" />
+            <ZmTableColumn
+              prop="materialCode"
+              :label="t('workOrderMaterial.materialCode')"
+              min-width="150" />
+            <ZmTableColumn prop="unit" :label="t('workOrderMaterial.unit')" width="100" />
+            <ZmTableColumn
+              prop="unitPrice"
+              :label="t('workOrderMaterial.unitPrice')"
+              min-width="120" />
+            <ZmTableColumn
+              prop="quantity"
+              :label="t('workOrderMaterial.ConsumptionQuantity')"
+              min-width="130" />
+            <ZmTableColumn prop="materialSource" :label="t('bomList.type')" min-width="120" />
+            <ZmTableColumn
+              v-if="!detail"
+              :label="t('workplace.operation')"
+              width="100"
+              fixed="right"
+              action>
+              <template #default="{ row }">
+                <el-button link type="danger" @click="handleDelete(row)">
+                  <Icon icon="ep:delete" class="mr-4px" />{{ t('form.delete') }}
+                </el-button>
+              </template>
+            </ZmTableColumn>
+          </ZmTable>
+        </div>
+      </section>
+    </div>
+
+    <template #footer>
+      <div class="drawer-footer">
+        <el-button @click="handleClose">关闭</el-button>
+      </div>
+    </template>
   </el-drawer>
 </template>
+
 <script setup lang="ts">
+import { useTableComponents } from '@/components/ZmTable/useTableComponents'
 
-import { ref, watch, defineOptions, defineEmits } from 'vue'
-import { ElMessage } from 'element-plus'
-import * as PmsMaterialApi from '@/api/pms/material'
-const drawerVisible = ref<boolean>(false)
-const emit = defineEmits(['update:modelValue', 'add', 'delete'])
-const { t } = useI18n() // 国际化
-const message = useMessage() // 消息弹窗
+interface MaterialRow {
+  id?: number
+  bomNodeId?: number | string
+  factory?: string
+  costCenter?: string
+  projectDepartment?: string
+  materialName?: string
+  materialCode?: string
+  unit?: string
+  unitPrice?: number
+  quantity?: number
+  materialSource?: string
+}
 
-defineOptions({
-  name: 'SelectedMaterialDrawer'
-})
+defineOptions({ name: 'SelectedMaterialDrawer' })
 
-const queryParams = reactive({
-  pageNo: 1,
-  pageSize: 10,
-  createTime: [],
-  bomId: '',
-  name: '',
-  code: ''
-})
+const props = withDefaults(
+  defineProps<{
+    modelValue: boolean
+    nodeId?: number | string
+    detail?: boolean
+    materials?: MaterialRow[]
+    hideExtraColumns?: boolean
+  }>(),
+  {
+    detail: false,
+    materials: () => [],
+    hideExtraColumns: false
+  }
+)
 
-const windowWidth = ref(window.innerWidth)
-// 动态计算百分比
-const computedSize = computed(() => {
-  return windowWidth.value > 1200 ? '60%' : '80%'
-})
+const emit = defineEmits<{
+  (e: 'update:modelValue', value: boolean): void
+  (e: 'add', row: MaterialRow): void
+  (e: 'delete', row: MaterialRow): void
+}>()
 
+const { t } = useI18n()
+const message = useMessage()
+const { ZmTable, ZmTableColumn } = useTableComponents<MaterialRow>()
 const loading = ref(false)
-const total = ref(0) // 列表的总页数
-// const materials = ref([])
-
-const props = defineProps({
-  modelValue: Boolean,
-  nodeId: Number,
-  detail: Boolean,
-  materials: Array, // 接收父组件传递的完整物料列表
-  hideExtraColumns: {
-    type: Boolean,
-    default: false
-  }
-})
+const windowWidth = ref(window.innerWidth)
 
-// 监听bom树节点ID变化
-watch(() => props.nodeId, (newVal) => {
-  console.log('传递过来的nodeId', newVal)
-  if (newVal) {
-    // await loadMaterials(newVal)
-  }
+const computedSize = computed(() => {
+  if (windowWidth.value <= 768) return '100%'
+  return windowWidth.value > 1440 ? '68%' : '82%'
 })
 
-// 计算属性过滤当前节点物料
 const filteredMaterials = computed(() => {
-  return props.materials?.filter(item =>
-    item.bomNodeId === props.nodeId
-  ) || []
+  if (!props.nodeId) return []
+  return props.materials.filter((item) => item.bomNodeId === props.nodeId)
 })
 
-// 加载指定bom节点下的物料数据
-const loadMaterials = async (nodeId) => {
-  queryParams.bomId = nodeId
-  try {
-    loading.value = true
-    // API调用
-    const data = await PmsMaterialApi.listByBomId(queryParams)
-    materials.value = data.list
-    total.value = data.total
-  } catch (error) {
-    ElMessage.error('数据加载失败')
-  } finally {
-    loading.value = false
-  }
-}
-
-/** 删除物料前的确认 */
-const handleDelete = async (row: any) => {
+const handleDelete = async (row: MaterialRow) => {
   try {
     await message.delConfirm()
-    // 用户确认后触发删除事件
     emit('delete', row)
-    // message.success(t('common.delSuccess'))
-  } catch (error) {
-    // 用户取消操作
-    console.log('删除操作已取消')
-  }
+  } catch {}
+}
+
+const handleClose = () => {
+  emit('update:modelValue', false)
 }
 
-// 打开抽屉
 const openDrawer = () => {
-  drawerVisible.value = true
+  emit('update:modelValue', true)
 }
 
-// 关闭抽屉
 const closeDrawer = () => {
-  drawerVisible.value = false
+  handleClose()
 }
 
-// 关闭抽屉
-const handleClose = () => {
-  emit('update:modelValue', false)
-  // materials.value = []
+const updateWindowWidth = () => {
+  windowWidth.value = window.innerWidth
 }
 
-defineExpose({ openDrawer, closeDrawer }) // 暴露方法给父组件
+onMounted(() => window.addEventListener('resize', updateWindowWidth))
+onUnmounted(() => window.removeEventListener('resize', updateWindowWidth))
 
+defineExpose({ openDrawer, closeDrawer })
 </script>
 
-<style lang="scss" scoped></style>
+<style scoped>
+.drawer-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 16px;
+  width: 100%;
+}
+
+.drawer-header__main {
+  display: flex;
+  align-items: center;
+  min-width: 0;
+}
+
+.drawer-header__icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 44px;
+  height: 44px;
+  margin-right: 12px;
+  color: var(--el-color-primary);
+  background: var(--el-color-primary-light-9);
+  border: 1px solid var(--el-color-primary-light-7);
+  border-radius: 8px;
+}
+
+.drawer-title {
+  font-size: 18px;
+  font-weight: 600;
+  color: var(--el-text-color-primary);
+}
+
+.drawer-subtitle {
+  margin-top: 4px;
+  font-size: 13px;
+  color: var(--el-text-color-secondary);
+}
+
+.drawer-body {
+  height: 100%;
+  padding: 4px;
+}
+
+.material-section {
+  height: 100%;
+  overflow: hidden;
+  background: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+}
+
+.section-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  min-height: 54px;
+  padding: 0 18px;
+  background: var(--el-fill-color-extra-light);
+  border-bottom: 1px solid var(--el-border-color-lighter);
+}
+
+.section-title {
+  display: flex;
+  align-items: center;
+  font-size: 16px;
+  font-weight: 600;
+  color: var(--el-text-color-primary);
+}
+
+.section-marker {
+  width: 4px;
+  height: 18px;
+  margin-right: 10px;
+  background: var(--el-color-primary);
+  border-radius: 2px;
+}
+
+.material-total {
+  padding: 4px 10px;
+  font-size: 12px;
+  color: var(--el-color-primary);
+  background: var(--el-color-primary-light-9);
+  border-radius: 4px;
+}
+
+.table-container {
+  padding: 16px;
+}
+
+.drawer-footer {
+  display: flex;
+  justify-content: flex-end;
+}
+
+@media (width <= 768px) {
+  .drawer-subtitle {
+    display: none;
+  }
+
+  .table-container {
+    padding: 12px;
+  }
+}
+</style>

File diff suppressed because it is too large
+ 512 - 440
src/views/pms/maintain/IotMaintainAddEdit.vue


+ 417 - 222
src/views/pms/maintain/IotMaintainDetail.vue

@@ -1,308 +1,347 @@
 <template>
-  <ContentWrap v-loading="formLoading">
+  <div v-loading="formLoading" class="maintain-detail-page">
+    <div class="detail-header">
+      <div class="detail-header__main">
+        <div class="detail-header__icon">
+          <Icon icon="ep:tools" />
+        </div>
+        <div>
+          <h2 class="detail-title">维修工单详情</h2>
+          <p class="detail-subtitle">查看设备故障、维修处理与关联物料信息</p>
+        </div>
+      </div>
+      <el-button size="default" @click="close">
+        <Icon icon="ep:arrow-left" class="mr-5px" />返回
+      </el-button>
+    </div>
+
     <el-form
       ref="formRef"
       :model="formData"
       :rules="formData.type === 'in' ? formRules : outFormRules"
-      v-loading="formLoading"
-      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">
+      size="default"
+      label-width="112px"
+      class="detail-form">
+      <section class="detail-section">
+        <div class="section-heading"><span></span>基础信息</div>
+        <el-row :gutter="24">
+          <el-col :xs="24" :sm="12" :xl="8">
             <el-form-item :label="t('maintain.deviceName')" prop="deviceName">
-              <el-select disabled v-model="formData.deviceName" />
+              <el-input v-model="formData.deviceName" disabled />
             </el-form-item>
           </el-col>
-          <el-col :span="8">
+          <el-col :xs="24" :sm="12" :xl="8">
             <el-form-item :label="t('maintain.status')" prop="status">
-              <el-select v-model="formData.status" clearable disabled>
+              <el-select v-model="formData.status" disabled>
                 <el-option
                   v-for="dict in getStrDictOptions(DICT_TYPE.PMS_MAIN_STATUS)"
                   :key="dict.value"
                   :label="dict.label"
-                  :value="dict.value"
-                />
+                  :value="dict.value" />
               </el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="8">
+          <el-col :xs="24" :sm="12" :xl="8">
             <el-form-item :label="t('iotMaintain.repairType')" prop="type">
-              <el-select disabled v-model="formData.type" clearable>
+              <el-select v-model="formData.type" disabled>
                 <el-option
                   v-for="dict in getStrDictOptions(DICT_TYPE.PMS_MAIN_TYPE)"
                   :key="dict.value"
                   :label="dict.label"
-                  :value="dict.value"
-                />
+                  :value="dict.value" />
               </el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="8">
+          <el-col :xs="24" :sm="12" :xl="8">
             <el-form-item :label="t('iotMaintain.shutDown')" prop="ifStop">
-              <el-select v-model="formData.ifStop" clearable disabled>
+              <el-select v-model="formData.ifStop" disabled>
                 <el-option
                   v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
                   :key="dict.value"
                   :label="dict.label"
-                  :value="dict.value"
-                />
+                  :value="dict.value" />
               </el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="8">
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item :label="t('iotMaintain.failureTime')" prop="failureTime">
+              <el-date-picker
+                v-model="formData.failureTime"
+                type="datetime"
+                value-format="x"
+                disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item :label="t('iotMaintain.faultySystem')" prop="failureSystem">
+              <el-input v-model="formData.failureSystem" disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item :label="t('iotMaintain.failureImpact')" prop="failureInfluence">
+              <el-input v-model="formData.failureInfluence" disabled />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </section>
+
+      <section class="detail-section">
+        <div class="section-heading"><span></span>维修处理</div>
+        <el-row :gutter="24">
+          <el-col :xs="24" :sm="12" :xl="8">
             <el-form-item
               :label="t('iotMaintain.startTime')"
               prop="maintainStartTime"
-              :rules="formData.type === 'in' ? startRule : []"
-            >
+              :rules="formData.type === 'in' ? startRule : []">
               <el-date-picker
-                disabled
-                style="width: 150%"
                 v-model="formData.maintainStartTime"
                 type="datetime"
                 value-format="x"
-              />
+                disabled />
             </el-form-item>
           </el-col>
-          <el-col :span="8">
+          <el-col :xs="24" :sm="12" :xl="8">
             <el-form-item
               :label="t('iotMaintain.endTime')"
               prop="maintainEndTime"
-              :rules="formData.type === 'in' ? endRule : []"
-            >
+              :rules="formData.type === 'in' ? endRule : []">
               <el-date-picker
-                disabled
-                style="width: 150%"
                 v-model="formData.maintainEndTime"
                 type="datetime"
                 value-format="x"
-              />
+                disabled />
             </el-form-item>
           </el-col>
-          <el-col :span="8">
+          <el-col :xs="24" :sm="12" :xl="8">
             <el-form-item :label="t('iotMaintain.PersonInCharge')" prop="maintainPerson">
-              <el-select
-                v-model="formData.maintainPerson"
-                filterable
-                clearable
-                style="width: 100%"
-                disabled
-              >
-                <el-option
-                  v-for="item in deptUsers"
-                  :key="item.id"
-                  :label="item.nickname"
-                  :value="item.id"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item :label="t('iotMaintain.failureTime')" prop="failureTime">
-              <el-date-picker
-                disabled
-                style="width: 150%"
-                v-model="formData.failureTime"
-                type="datetime"
-                value-format="x"
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item :label="t('iotMaintain.failureImpact')" prop="failureInfluence">
-              <el-input v-model="formData.failureInfluence" disabled />
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item :label="t('iotMaintain.faultySystem')" prop="failureSystem">
-              <el-input v-model="formData.failureSystem" disabled />
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item :label="t('iotMaintain.picture')" prop="pics">
-              <UploadImgs v-model="formData.pics" :disabled="true" height="55px" />
+              <el-input v-model="formData.maintainPerson" disabled />
             </el-form-item>
           </el-col>
-          <el-col :span="8">
+          <el-col :xs="24" :sm="12" :xl="8">
             <el-form-item :label="t('maintain.maintainWay')" prop="maintainMethod">
-              <el-select v-model="formData.maintainMethod" clearable disabled>
+              <el-select v-model="formData.maintainMethod" disabled>
                 <el-option
                   v-for="dict in getStrDictOptions(DICT_TYPE.PMS_MAINTAIN_METHOD)"
                   :key="dict.value"
                   :label="dict.label"
-                  :value="dict.value"
-                />
+                  :value="dict.value" />
               </el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="8">
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item
+              :label="t('iotMaintain.repairCosts')"
+              prop="maintainFee"
+              :rules="formData.type === 'out' ? feeRules : []">
+              <el-input-number
+                v-model="formData.maintainFee"
+                :min="0"
+                :precision="2"
+                :step="0.1"
+                controls-position="right"
+                disabled />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24">
             <el-form-item :label="t('maintain.methodDescription')" prop="methodDescription">
-              <el-input v-model="formData.methodDescription" type="textarea"  disabled />
+              <el-input
+                v-model="formData.methodDescription"
+                type="textarea"
+                :rows="2"
+                resize="none"
+                disabled />
             </el-form-item>
           </el-col>
-          <el-col :span="8" v-if="formData.type === 'out'">
-            <el-form-item :label="t('iotMaintain.applyPerson')" prop="applyPersonId" :rules="formData.type==='out'?classifyRules:[]">
-              <el-select v-model="formData.applyPersonId" :placeholder="t('iotMaintain.applyPersonHolder')" disabled clearable>
+        </el-row>
+      </section>
+
+      <section v-if="formData.type === 'out'" class="detail-section">
+        <div class="section-heading"><span></span>外委信息</div>
+        <el-row :gutter="24">
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item
+              :label="t('iotMaintain.applyPerson')"
+              prop="applyPersonId"
+              :rules="classifyRules">
+              <el-select v-model="formData.applyPersonId" disabled>
                 <el-option
                   v-for="person in applyPeoPle"
                   :key="person.oaId"
                   :label="person.lastname"
-                  :value="person.oaId"
-                />
+                  :value="person.oaId" />
               </el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="8" v-if="formData.type === 'out'">
-            <el-form-item :label="t('iotMaintain.maintainClassify')" prop="maintainClassify" :rules="formData.type==='out'?classifyRules:[]">
-              <el-select v-model="formData.maintainClassify" :placeholder="t('iotMaintain.maintainClassifyHolder')" disabled clearable>
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item
+              :label="t('iotMaintain.maintainClassify')"
+              prop="maintainClassify"
+              :rules="classifyRules">
+              <el-select v-model="formData.maintainClassify" disabled>
                 <el-option
                   v-for="dict in getStrDictOptions(DICT_TYPE.PMS_MAIN_CLASSIFY)"
                   :key="dict.value"
                   :label="dict.label"
-                  :value="dict.value"
-                />
+                  :value="dict.value" />
               </el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="8" v-if="formData.type === 'out'">
-            <el-form-item :label="t('iotMaintain.kmHour')" prop="kmHour">
-              <el-input v-model="formData.kmHour" :placeholder="t('iotMaintain.kmHourHolder')" disabled/>
-            </el-form-item>
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item :label="t('iotMaintain.kmHour')" prop="kmHour"
+              ><el-input v-model="formData.kmHour" disabled
+            /></el-form-item>
           </el-col>
-          <el-col :span="8" v-if="formData.type === 'out'">
-            <el-form-item :label="t('deviceForm.model')" prop="model" :rules="formData.type==='out'?modelRules:[]">
-              <el-input v-model="formData.model" :placeholder="t('deviceForm.modelHolder')" disabled/>
-            </el-form-item>
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item :label="t('deviceForm.model')" prop="model" :rules="modelRules"
+              ><el-input v-model="formData.model" disabled
+            /></el-form-item>
           </el-col>
-          <el-col :span="8" v-if="formData.type === 'out'">
-            <el-form-item :label="t('deviceForm.enable')" prop="enableDate" :rules="formData.type==='out'?modelRules:[]">
-              <el-date-picker
-                disabled
-                style="width: 150%"
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item :label="t('deviceForm.enable')" prop="enableDate" :rules="modelRules"
+              ><el-date-picker
                 v-model="formData.enableDate"
                 type="date"
                 value-format="YYYY-MM-DD"
-                :placeholder="t('deviceForm.enableHolder')"
-              />
-            </el-form-item>
-          </el-col>
-          <el-col :span="8" v-if="formData.type === 'out'">
-            <div style="display: flex;flex-direction: row">
-              <el-form-item :label="t('iotMaintain.supplier')" prop="supplier" style="width: 86%" :rules="formData.type==='out'?supplierRules:[]">
-                <el-input
-                  disabled
-                  clearable
-                  v-model="formData.supplier"
-                  :placeholder="t('iotMaintain.suppHolder')"
-                />
-              </el-form-item>
-            </div>
-          </el-col>
-          <el-col :span="8" >
-            <el-form-item :label="t('iotMaintain.repairCosts')" prop="maintainFee" :rules="formData.type==='out'?feeRules:[]">
-              <el-input-number
                 disabled
-                v-model="formData.maintainFee"
-                :min="0"
-                :precision="2"
-                :step="0.1"
-                style="width: 100%"
-              />
-            </el-form-item>
+            /></el-form-item>
           </el-col>
-          <el-col :span="8" v-if="formData.type === 'out'">
-            <el-form-item :label="t('iotMaintain.projectManager')" prop="projectManager" :rules="formData.type==='out'?projectRules:[]">
-              <el-select v-model="formData.projectManager" :placeholder="t('iotMaintain.projectManagerHolder')" disabled clearable filterable>
-                <el-option
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item :label="t('iotMaintain.supplier')" prop="supplier" :rules="supplierRules"
+              ><el-input v-model="formData.supplier" disabled
+            /></el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item
+              :label="t('iotMaintain.projectManager')"
+              prop="projectManager"
+              :rules="projectRules">
+              <el-select v-model="formData.projectManager" disabled
+                ><el-option
                   v-for="person in projectManager"
                   :key="person.oaId"
                   :label="person.lastname"
                   :value="person.oaId"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8"  v-if="formData.type === 'out'">
-            <el-form-item :label="t('iotMaintain.address')" prop="address">
-              <el-input  v-model="formData.address" disabled/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8" v-if="formData.type === 'out'">
-            <el-form-item :label="t('iotMaintain.attachment')" prop="outFiles">
-              <UploadFile v-model="formData.outFiles" :is-show-tip="false" class="min-w-80px" multiple :limit="2" disabled />
+              /></el-select>
             </el-form-item>
           </el-col>
-          <el-col :span="16"  v-if="formData.type === 'out'" >
-            <el-form-item :label="t('iotMaintain.maintainItem')" prop="maintainItem" :rules="formData.type==='out'?itemRules:[]">
-              <el-input type="textarea" v-model="formData.maintainItem" disabled/>
-            </el-form-item>
-          </el-col>
-          <el-col :span="8">
-            <el-form-item :label="t('iotMaintain.faultDescription')" prop="description">
-              <el-input type="textarea" v-model="formData.description" disabled />
-            </el-form-item>
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item :label="t('iotMaintain.address')" prop="address"
+              ><el-input v-model="formData.address" disabled
+            /></el-form-item>
           </el-col>
-          <el-col :span="8">
-            <el-form-item :label="t('iotMaintain.repairDescription')" prop="maintainDescription">
-              <el-input type="textarea" v-model="formData.maintainDescription" disabled />
-            </el-form-item>
+          <el-col :xs="24" :sm="12" :xl="8">
+            <el-form-item :label="t('iotMaintain.attachment')" prop="outFiles"
+              ><UploadFile
+                v-model="formData.outFiles"
+                :is-show-tip="false"
+                multiple
+                :limit="2"
+                disabled
+            /></el-form-item>
           </el-col>
-          <el-col :span="8">
-            <el-form-item :label="t('iotMaintain.solution')" prop="solution">
-              <el-input type="textarea" v-model="formData.solution" disabled />
-            </el-form-item>
+          <el-col :span="24">
+            <el-form-item
+              :label="t('iotMaintain.maintainItem')"
+              prop="maintainItem"
+              :rules="itemRules"
+              ><el-input
+                v-model="formData.maintainItem"
+                type="textarea"
+                :rows="2"
+                resize="none"
+                disabled
+            /></el-form-item>
           </el-col>
-          <el-col :span="12">
-            <el-form-item :label="t('iotMaintain.remark')" prop="remark">
-              <el-input v-model="formData.remark" type="textarea" disabled />
+        </el-row>
+      </section>
+
+      <section class="detail-section">
+        <div class="section-heading"><span></span>故障描述</div>
+        <el-row :gutter="24">
+          <el-col :xs="24" :xl="8">
+            <el-form-item :label="t('iotMaintain.picture')" prop="pics">
+              <UploadImgs v-model="formData.pics" :disabled="true" height="72px" />
             </el-form-item>
           </el-col>
+          <el-col :xs="24" :xl="16"
+            ><el-form-item :label="t('iotMaintain.faultDescription')" prop="description"
+              ><el-input
+                v-model="formData.description"
+                type="textarea"
+                :rows="3"
+                resize="none"
+                disabled /></el-form-item
+          ></el-col>
+          <el-col :xs="24" :xl="12"
+            ><el-form-item :label="t('iotMaintain.repairDescription')" prop="maintainDescription"
+              ><el-input
+                v-model="formData.maintainDescription"
+                type="textarea"
+                :rows="3"
+                resize="none"
+                disabled /></el-form-item
+          ></el-col>
+          <el-col :xs="24" :xl="12"
+            ><el-form-item :label="t('iotMaintain.solution')" prop="solution"
+              ><el-input
+                v-model="formData.solution"
+                type="textarea"
+                :rows="3"
+                resize="none"
+                disabled /></el-form-item
+          ></el-col>
+          <el-col :span="24"
+            ><el-form-item :label="t('iotMaintain.remark')" prop="remark"
+              ><el-input
+                v-model="formData.remark"
+                type="textarea"
+                :rows="2"
+                resize="none"
+                disabled /></el-form-item
+          ></el-col>
         </el-row>
-      </div>
+      </section>
     </el-form>
-  </ContentWrap>
-  <ContentWrap>
-    <!-- 列表 -->
-    <ContentWrap>
-      <el-table v-loading="loading" :data="list" :stripe="true" :show-overflow-tooltip="true">
-        <el-table-column :label="t('iotMaintain.deviceCode')" align="center" prop="deviceCode" />
-        <el-table-column :label="t('iotMaintain.deviceName')" align="center" prop="deviceName" />
-        <el-table-column :label="t('iotMaintain.repairItems')" align="center" prop="name" />
-
-        <el-table-column :label="t('iotMaintain.operation')" align="center" min-width="120px">
+
+    <section class="detail-section material-section">
+      <div class="section-heading"><span></span>关联物料</div>
+      <ZmTable
+        :loading="formLoading"
+        :data="list"
+        :show-border="true"
+        settings-cache-key="pms-maintain-detail-material">
+        <ZmTableColumn
+          :label="t('iotMaintain.deviceCode')"
+          align="center"
+          prop="deviceCode"
+          fixed="left" />
+        <ZmTableColumn :label="t('iotMaintain.deviceName')" align="center" prop="deviceName" />
+        <ZmTableColumn :label="t('iotMaintain.repairItems')" align="center" prop="name" />
+        <ZmTableColumn :label="t('iotMaintain.operation')" align="center" width="120" fixed="right">
           <template #default="scope">
-            <div style="display: flex; justify-content: center; align-items: center; width: 100%">
-              <div style="margin-left: 12px">
-                <el-button
-                  link
-                  type="primary"
-                  @click="handleViewNew(scope.row.id, scope.row.bomNodeId)"
-                >
-                  物料详情
-                </el-button>
-              </div>
-            </div>
+            <el-button
+              size="default"
+              link
+              type="primary"
+              @click="handleViewNew(scope.row.id, scope.row.bomNodeId)"
+              >物料详情</el-button
+            >
           </template>
-        </el-table-column>
-      </el-table>
-    </ContentWrap>
-  </ContentWrap>
-  <ContentWrap>
-    <el-form>
-      <el-form-item style="float: right">
-        <el-button type="info" @click="close">取 消</el-button>
-      </el-form-item>
-    </el-form>
-  </ContentWrap>
+        </ZmTableColumn>
+      </ZmTable>
+    </section>
+
+    <div class="sticky-action-bar">
+      <el-button size="default" @click="close">返回</el-button>
+    </div>
+  </div>
+
   <MaterialListDrawer
     :model-value="drawerVisible"
     @update:model-value="(val) => (drawerVisible = val)"
     :node-id="currentBomNodeId"
     :materials="materialList"
-    :detail="true"
-  />
+    :detail="true" />
 </template>
 <script setup lang="ts">
 import { DICT_TYPE, getBoolDictOptions, getStrDictOptions } from '@/utils/dict'
@@ -313,12 +352,13 @@ import { useTagsViewStore } from '@/store/modules/tagsView'
 import { IotMaintainApi } from '@/api/pms/maintain'
 import MaterialListDrawer from '@/views/pms/iotmainworkorder/SelectedMaterialDrawer.vue'
 import { IotMaintainMaterialsApi } from '@/api/pms/maintain/materials'
-import {CACHE_KEY, useCache} from "@/hooks/web/useCache";
+import { useTableComponents } from '@/components/ZmTable/useTableComponents'
 
 /** 维修工单 表单 */
 defineOptions({ name: 'IotMaintainDetail' })
 
 const { t } = useI18n() // 国际化
+const { ZmTable, ZmTableColumn } = useTableComponents<IotMaintainMaterialVO>()
 
 const projectRules = [{ required: true, message: '项目经理不能为空', trigger: 'blur' }]
 const itemRules = [{ required: true, message: '维修项目不能为空', trigger: 'blur' }]
@@ -331,32 +371,26 @@ const applyPeoPle = ref([])
 const projectManager = ref([])
 
 const props = defineProps<{ id?: number }>()
-const message = useMessage() // 消息弹窗
 const { delView } = useTagsViewStore() // 视图操作
 const { currentRoute, push } = useRouter()
-const deptUsers = ref<UserApi.UserVO[]>([]) // 用户列表
-const dialogTitle = ref('') // 弹窗的标题
 const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
-const deviceLabel = ref('') // 表单的类型:create - 新增;update - 修改
 const drawerVisible = ref<boolean>(false)
-const showDrawer = ref()
 const list = ref<IotMaintainMaterialVO[]>([]) // 列表的数据
-const { params, name } = useRoute() // 查询参数
+const { params } = useRoute() // 查询参数
 const id = params.id || props.id
 const materialList = ref<any[]>([]) // 保养工单bom关联物料列表
-const { wsCache } = useCache()
 const currentBomNodeId = ref<string>()
 const formData = ref({
   methodDescription: undefined,
   maintainMethod: undefined,
-  kmHour:undefined,
-  maintainItem:undefined,
+  kmHour: undefined,
+  maintainItem: undefined,
   enableDate: undefined,
-  projectManager:undefined,
-  applyPersonId:undefined,
+  projectManager: undefined,
+  applyPersonId: undefined,
   maintainClassify: undefined,
-  address:undefined,
-  model:undefined,
+  address: undefined,
+  model: undefined,
   id: undefined,
   failureCode: undefined,
   failureName: undefined,
@@ -369,8 +403,8 @@ const formData = ref({
   failureSystem: undefined,
   description: undefined,
   pic: undefined,
-  pics:undefined,
-  outFiles:undefined,
+  pics: undefined,
+  outFiles: undefined,
   solution: undefined,
   maintainStartTime: undefined,
   maintainEndTime: undefined,
@@ -384,7 +418,7 @@ const formData = ref({
   supplier: undefined,
   maintainFee: undefined,
   outFile: undefined,
-  supplierName: undefined,
+  supplierName: undefined
 })
 const formRules = reactive({
   // deviceName: [{ required: true, message: '设备不能为空', trigger: 'blur' }],
@@ -437,13 +471,10 @@ const handleViewNew = (nodeId, bomId) => {
     materialList.value = res.list
   })
 }
-const userId = ref('')
 /** 提交表单 */
 onMounted(async () => {
-  const userInfo = wsCache.get(CACHE_KEY.USER)
-  userId.value = userInfo.user.nickname;
+  formLoading.value = true
   await IotMaintainApi.getIotMaintain(id).then((res) => {
-    debugger
     formData.value = res
     if (res.outFile) {
       formData.value.outFile = res.outFile.split(',')
@@ -462,10 +493,174 @@ onMounted(async () => {
         applyPeoPle.value = resss
       })
       IotMaintainApi.getProjectUsers(res.creator).then((ressss) => {
-        debugger
         projectManager.value = ressss
       })
     }
   })
+  formLoading.value = false
 })
 </script>
+
+<style scoped>
+.maintain-detail-page {
+  display: flex;
+  flex-direction: column;
+  min-height: calc(
+    100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
+  );
+  padding: 16px;
+  background: #f5f7fa;
+}
+
+.detail-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 16px 20px;
+  margin-bottom: 16px;
+  background: var(--el-bg-color);
+  border-radius: 8px;
+  box-shadow: 0 1px 4px rgb(0 0 0 / 8%);
+}
+
+.detail-header__main {
+  display: flex;
+  align-items: center;
+  min-width: 0;
+}
+
+.detail-header__icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 42px;
+  height: 42px;
+  margin-right: 12px;
+  font-size: 20px;
+  color: var(--el-color-primary);
+  background: var(--el-color-primary-light-9);
+  border: 1px solid var(--el-color-primary-light-7);
+  border-radius: 8px;
+}
+
+.detail-title {
+  margin: 0;
+  font-size: 18px;
+  font-weight: 600;
+  line-height: 26px;
+  color: var(--el-text-color-primary);
+}
+
+.detail-subtitle {
+  margin: 2px 0 0;
+  font-size: 13px;
+  line-height: 20px;
+  color: var(--el-text-color-secondary);
+}
+
+.detail-form,
+.detail-section {
+  min-width: 0;
+}
+
+.detail-section {
+  padding: 0 20px 4px;
+  margin-bottom: 16px;
+  overflow: hidden;
+  background: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+  box-shadow: 0 1px 4px rgb(0 0 0 / 5%);
+}
+
+.section-heading {
+  display: flex;
+  height: 46px;
+  padding: 0 20px;
+  margin: 0 -20px 16px;
+  font-size: 16px;
+  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);
+  align-items: center;
+}
+
+.section-heading span {
+  width: 4px;
+  height: 18px;
+  margin-right: 10px;
+  background: var(--el-color-primary);
+  border-radius: 2px;
+}
+
+.detail-form :deep(.el-select),
+.detail-form :deep(.el-date-editor),
+.detail-form :deep(.el-input-number) {
+  width: 100%;
+}
+
+.detail-form :deep(.el-form-item) {
+  margin-bottom: 16px;
+}
+
+.material-section {
+  padding-bottom: 20px;
+}
+
+.sticky-action-bar {
+  position: sticky;
+  bottom: 0;
+  z-index: 20;
+  display: flex;
+  align-items: center;
+  justify-content: flex-end;
+  gap: 10px;
+  min-height: 58px;
+  padding: 10px 20px;
+  margin-top: auto;
+  background: var(--el-bg-color);
+  border-top: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+  box-shadow: 0 -4px 12px rgb(0 0 0 / 8%);
+}
+
+.sticky-action-bar :deep(.el-button) {
+  margin-left: 0;
+}
+
+@media (width <= 768px) {
+  .maintain-detail-page {
+    padding: 12px;
+  }
+
+  .sticky-action-bar {
+    padding-right: 12px;
+    padding-left: 12px;
+    margin-right: -12px;
+    margin-bottom: -12px;
+    margin-left: -12px;
+  }
+
+  .detail-header,
+  .detail-section {
+    padding-right: 12px;
+    padding-left: 12px;
+  }
+
+  .detail-subtitle {
+    display: none;
+  }
+
+  .section-heading {
+    padding-right: 12px;
+    padding-left: 12px;
+    margin-right: -12px;
+    margin-left: -12px;
+  }
+
+  .detail-form {
+    --el-form-label-width: 96px;
+  }
+}
+</style>

+ 417 - 188
src/views/pms/maintain/IotMaintainRecord.vue

@@ -1,158 +1,150 @@
 <template>
-  <ContentWrap v-loading="formLoading">
-    <!-- 第一部分:日报标题 -->
-    <div class="daily-report-title">
-      <h2>{{ pageTitle }}</h2>
-    </div>
-
-    <!-- 第二部分:项目/任务信息 -->
-    <ContentWrap>
-      <div class="info-table" style="margin-top: 0.5em">
-        <!-- 表格行 -->
-        <div class="table-row">
-          <div class="table-cell">
-            <div class="cell-content">
-              <span class="cell-label">油服编码:</span>
-              <!-- 甲方字段:添加 single-line-ellipsis 类 + title 绑定完整内容 -->
-              <span
-                class="cell-value single-line-ellipsis"
-                :title="repairInfo.yfDeviceCode || repairInfo.deviceCode || '-'"
-              >
-                {{ repairInfo.yfDeviceCode || repairInfo.deviceCode || '-' }}
-              </span>
-            </div>
-          </div>
-          <div class="table-cell">
-            <div class="cell-content">
-              <span class="cell-label">设备名称:</span>
-              <span class="cell-value">{{ repairInfo.deviceName || '-' }}</span>
-            </div>
-          </div>
-          <div class="table-cell">
-            <div class="cell-content">
-              <span class="cell-label">维修类型:</span>
-              <span class="cell-value">{{
-                getDictLabel(DICT_TYPE.PMS_MAIN_TYPE, repairInfo.type)
-              }}</span>
-            </div>
-          </div>
+  <div v-loading="formLoading" class="maintain-record-page">
+    <div class="page-header">
+      <div class="page-header__main">
+        <div class="page-header__icon"><Icon icon="ep:document" /></div>
+        <div>
+          <h2 class="page-title">{{ pageTitle }}</h2>
+          <p class="page-subtitle">查看设备维修信息并维护维修过程记录</p>
         </div>
+      </div>
+      <el-button size="default" @click="close">
+        <Icon icon="ep:arrow-left" class="mr-5px" />返回
+      </el-button>
+    </div>
 
-        <div class="table-row">
-          <div class="table-cell">
-            <div class="cell-content">
-              <span class="cell-label">维修开始时间:</span>
-              <span class="cell-value">{{ formatDate(repairInfo.maintainStartTime) || '-' }}</span>
-            </div>
-          </div>
-          <div class="table-cell">
-            <div class="cell-content">
-              <span class="cell-label">维修结束日期:</span>
-              <span class="cell-value">{{ formatDate(repairInfo.maintainEndTime) || '-' }}</span>
-            </div>
-          </div>
-          <div class="table-cell">
-            <div class="cell-content">
-              <span class="cell-label">故障时间:</span>
-              <span class="cell-value">{{ formatDate(repairInfo.failureTime) || '-' }}</span>
-            </div>
-          </div>
-        </div>
-        <div class="table-row">
-          <div class="table-cell">
-            <div class="cell-content">
-              <span class="cell-label">维修人员:</span>
-              <span class="cell-value">{{ repairInfo.maintainPersonName || '-' }}</span>
-            </div>
-          </div>
-          <div class="table-cell">
-            <div class="cell-content">
-              <span class="cell-label">故障系统:</span>
-              <span class="cell-value">{{ repairInfo.failureSystem || '-' }}</span>
-            </div>
-          </div>
-          <div class="table-cell">
-            <div class="cell-content">
-              <!--              <span class="cell-label">故障时间:</span>-->
-              <!--              <span class="cell-value">{{ formatDate(repairInfo.failureTime) || '-' }}</span>-->
-            </div>
-          </div>
+    <section class="page-section info-section">
+      <div class="section-header">
+        <div class="section-title"><span></span>工单概览</div>
+      </div>
+      <div class="info-grid">
+        <div class="info-item">
+          <span class="info-label">油服编码</span>
+          <span class="info-value" :title="repairInfo.yfDeviceCode || repairInfo.deviceCode || '-'">
+            {{ repairInfo.yfDeviceCode || repairInfo.deviceCode || '-' }}
+          </span>
         </div>
+        <div class="info-item"
+          ><span class="info-label">设备名称</span
+          ><span class="info-value">{{ repairInfo.deviceName || '-' }}</span></div
+        >
+        <div class="info-item"
+          ><span class="info-label">维修类型</span
+          ><span class="info-value">{{
+            getDictLabel(DICT_TYPE.PMS_MAIN_TYPE, repairInfo.type) || '-'
+          }}</span></div
+        >
+        <div class="info-item"
+          ><span class="info-label">维修开始时间</span
+          ><span class="info-value">{{
+            formatDate(repairInfo.maintainStartTime) || '-'
+          }}</span></div
+        >
+        <div class="info-item"
+          ><span class="info-label">维修结束日期</span
+          ><span class="info-value">{{ formatDate(repairInfo.maintainEndTime) || '-' }}</span></div
+        >
+        <div class="info-item"
+          ><span class="info-label">故障时间</span
+          ><span class="info-value">{{ formatDate(repairInfo.failureTime) || '-' }}</span></div
+        >
+        <div class="info-item"
+          ><span class="info-label">维修人员</span
+          ><span class="info-value">{{ repairInfo.maintainPersonName || '-' }}</span></div
+        >
+        <div class="info-item"
+          ><span class="info-label">故障系统</span
+          ><span class="info-value">{{ repairInfo.failureSystem || '-' }}</span></div
+        >
+        <div class="info-item info-item--empty" aria-hidden="true"></div>
       </div>
-    </ContentWrap>
-    <ContentWrap>
-      <el-form :inline="true" ref="queryFormRef" :model="queryParams">
-        <el-form-item>
-          <el-form-item label="维修时间" prop="createTime">
-            <el-date-picker
-              v-model="queryParams.repairTime"
-              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-220px"
-            />
-          </el-form-item>
-        </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" @click="openForm('create')">
-            <Icon icon="ep:plus" /> 新增
+    </section>
+
+    <section class="page-section records-section">
+      <div class="section-header">
+        <div class="section-title"><span></span>维修记录</div>
+        <div class="section-actions">
+          <el-button size="default" type="primary" @click="openForm('create')">
+            <Icon icon="ep:plus" class="mr-5px" />新增
           </el-button>
-          <el-button type="warning" @click="viewTimeline">
-            <Icon icon="ep:timer" /> 时间线
+          <el-button size="default" type="warning" @click="viewTimeline">
+            <Icon icon="ep:timer" class="mr-5px" />时间线
           </el-button>
+        </div>
+      </div>
+
+      <el-form ref="queryFormRef" :model="queryParams" size="default" class="query-form">
+        <el-form-item label="维修时间" prop="repairTime">
+          <el-date-picker
+            v-model="queryParams.repairTime"
+            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="query-date" />
+        </el-form-item>
+        <el-form-item class="query-actions">
+          <el-button size="default" type="primary" @click="handleQuery"
+            ><Icon icon="ep:search" class="mr-5px" />搜索</el-button
+          >
+          <el-button size="default" @click="resetQuery"
+            ><Icon icon="ep:refresh" 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="repairTime"
-          :formatter="dateFormatter"
-          width="150px"
-        />
-        <el-table-column label="维修项" align="center" min-width="90" prop="repairItem" />
-        <el-table-column label="维修内容" align="center" min-width="150" prop="repairContent" />
-        <el-table-column label="解决办法" align="center" prop="solution" min-width="150" />
-        <el-table-column label="图片" align="center"  prop="pic" />
-        <el-table-column label="创建人" align="center" prop="createName" />
-        <el-table-column label="配合维修人员" align="center" prop="repairPersonName" />
-        <el-table-column label="备注" align="center" prop="remark" />
-        <el-table-column
-          label="创建时间"
-          align="center"
-          prop="createTime"
-          :formatter="dateFormatter"
-          width="180px"
-        />
-        <el-table-column label="操作" align="center" min-width="90px">
-          <template #default="scope">
-            <el-button link type="primary" v-if="userid.toString()===scope.row.creator" @click="openForm('update', scope.row.id)">
-              编辑
-            </el-button>
-          </template>
-        </el-table-column>
-      </el-table>
-      <!-- 分页 -->
+
+      <div class="table-wrap">
+        <ZmTable
+          :loading="loading"
+          :data="list"
+          :show-border="true"
+          settings-cache-key="pms-maintain-record-list">
+          <ZmTableColumn
+            label="维修时间"
+            prop="repairTime"
+            :formatter="dateFormatter"
+            min-width="150" />
+          <ZmTableColumn label="维修项" prop="repairItem" />
+          <ZmTableColumn label="维修内容" prop="repairContent" min-width="150" />
+          <ZmTableColumn label="解决办法" prop="solution" min-width="150" />
+          <ZmTableColumn label="图片" prop="pic" />
+          <ZmTableColumn label="创建人" prop="createName" />
+          <ZmTableColumn label="配合维修人员" prop="repairPersonName" />
+          <ZmTableColumn label="备注" prop="remark" />
+          <ZmTableColumn
+            label="创建时间"
+            prop="createTime"
+            :formatter="dateFormatter"
+            min-width="180" />
+          <ZmTableColumn label="操作" width="90" fixed="right">
+            <template #default="scope">
+              <el-button
+                v-if="userid.toString() === scope.row.creator"
+                size="default"
+                link
+                type="primary"
+                @click="openForm('update', scope.row.id)">
+                编辑
+              </el-button>
+            </template>
+          </ZmTableColumn>
+        </ZmTable>
+      </div>
+
       <Pagination
         :total="total"
         v-model:page="queryParams.pageNo"
         v-model:limit="queryParams.pageSize"
-        @pagination="getList"
-      />
-    </ContentWrap>
+        @pagination="getList" />
+    </section>
+
     <IotJdRecordForm ref="formRef" @success="getList" />
     <MaintainRecordDrawer
       ref="showDrawer"
       :model-value="drawerVisible"
-      @update:model-value="(val) => (drawerVisible = val)"
-    />
-  </ContentWrap>
+      @update:model-value="(val) => (drawerVisible = val)" />
+  </div>
 </template>
 
 <script setup lang="ts">
@@ -169,6 +161,7 @@ import { dateFormatter } from '@/utils/formatTime'
 import { IotJdRecordApi, IotJdRecordVO } from '@/api/pms/maintain/record'
 import IotJdRecordForm from '@/views/pms/maintain/record/IotJdRecordForm.vue'
 import MaintainRecordDrawer from '@/views/pms/maintain/MaintainRecordDrawer.vue'
+import { useTableComponents } from '@/components/ZmTable/useTableComponents'
 
 const { t } = useI18n()
 const message = useMessage()
@@ -176,8 +169,9 @@ const { delView } = useTagsViewStore()
 const { push, currentRoute } = useRouter()
 const { params } = useRoute()
 const userStore = useUserStore()
+const { ZmTable, ZmTableColumn } = useTableComponents<IotJdRecordVO>()
 const total = ref(0) // 列表的总页数
-const list = ref<IotJdRecordVO>([])
+const list = ref<IotJdRecordVO[]>([])
 const loading = ref(true) // 列表的加载中
 const drawerVisible = ref<boolean>(false)
 const showDrawer = ref()
@@ -243,7 +237,7 @@ const pageTitle = computed(() => {
 
 const close = () => {
   delView(unref(currentRoute))
-  push({ name: 'FillDailyReport', params: {} })
+  push({ name: 'IotMaintain', params: {} })
 }
 
 const formatDate = (timestamp: number) => {
@@ -271,10 +265,81 @@ onMounted(async () => {
 </script>
 
 <style scoped>
+/* 响应式设计 */
+@media (width <= 768px) {
+  .table-row {
+    flex-direction: column;
+  }
+
+  .table-cell {
+    border-right: none;
+    border-bottom: 1px solid #e0e0e0;
+  }
+
+  .table-cell:last-child {
+    border-bottom: none;
+  }
+}
+
+@media (width <= 1200px) {
+  .info-grid {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+  }
+
+  .info-item--empty {
+    display: none;
+  }
+}
+
+@media (width <= 768px) {
+  .maintain-record-page {
+    padding: 12px;
+  }
+
+  .page-header,
+  .section-header {
+    padding-right: 12px;
+    padding-left: 12px;
+  }
+
+  .page-subtitle {
+    display: none;
+  }
+
+  .section-header {
+    align-items: flex-start;
+    flex-direction: column;
+    padding-top: 12px;
+    padding-bottom: 12px;
+  }
+
+  .section-actions {
+    justify-content: flex-start;
+    width: 100%;
+  }
+
+  .info-grid {
+    grid-template-columns: minmax(0, 1fr);
+    margin: 12px;
+  }
+
+  .query-form,
+  .table-wrap {
+    padding-right: 12px;
+    padding-left: 12px;
+  }
+
+  .query-form,
+  .query-form :deep(.el-form-item),
+  .query-date {
+    width: 100%;
+  }
+}
+
 .info-table {
+  overflow: hidden;
   border: 1px solid #e0e0e0;
   border-radius: 4px;
-  overflow: hidden;
 }
 
 .table-row {
@@ -287,11 +352,11 @@ onMounted(async () => {
 }
 
 .table-cell {
-  flex: 1;
-  border-right: 1px solid #e0e0e0;
-  padding: 12px 8px;
-  min-height: 44px;
   display: flex;
+  min-height: 44px;
+  padding: 12px 8px;
+  border-right: 1px solid #e0e0e0;
+  flex: 1;
   align-items: center;
 }
 
@@ -311,89 +376,81 @@ onMounted(async () => {
 }
 
 .cell-label {
-  font-weight: 500;
+  min-width: 80px;
+  margin-right: 8px;
+
   /* 统一字体大小为 14px(Element 表单默认) */
   font-size: 14px;
+  font-weight: 500;
   color: #606266;
-  min-width: 80px;
-  margin-right: 8px;
   flex-shrink: 0;
 }
 
 .cell-value {
   /* 统一字体大小为 14px(Element 输入框默认) */
   font-size: 14px;
-  color: #303133;
+
   /* 统一行高为 1.5(Element 组件默认行高) */
   line-height: 1.5;
-  flex: 1;
+  color: #303133;
   word-break: break-all;
-}
-
-/* 响应式设计 */
-@media (max-width: 768px) {
-  .table-row {
-    flex-direction: column;
-  }
-
-  .table-cell {
-    border-right: none;
-    border-bottom: 1px solid #e0e0e0;
-  }
-
-  .table-cell:last-child {
-    border-bottom: none;
-  }
+  flex: 1;
 }
 
 .daily-report-title {
-  text-align: center;
-  margin: 5px 0;
   padding: 5px;
+  margin: 5px 0;
+  text-align: center;
   border-bottom: 2px solid #409eff;
 }
 
 .daily-report-title h2 {
   margin: 0;
-  color: #303133;
   font-size: 16px;
   font-weight: bold;
+  color: #303133;
 }
 
 /* 为第二、三部分增加左右留白 */
 .section-padding {
-  padding-left: 0px;
   padding-right: 40px;
+  padding-left: 0;
 }
+
 :deep(.el-textarea .el-textarea__inner) {
   min-height: 80px;
 }
 
 /* 确保表单label不换行 */
 :deep(.el-form-item__label) {
-  white-space: nowrap;
-  text-overflow: ellipsis;
   overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
 }
 
 /* 甲方字段:单行显示+超出省略 */
 .single-line-ellipsis {
-  /* 强制文本单行显示 */
-  white-space: nowrap;
   /* 超出容器部分隐藏 */
   overflow: hidden;
+
   /* 超出部分显示省略号 */
   text-overflow: ellipsis;
+
   /* 避免文本被截断(可选,根据需求调整) */
   word-break: normal;
+
+  /* 强制文本单行显示 */
+  white-space: nowrap;
 }
 
 /* 设备配置字段:换行缩进(与首行对齐) */
 .indent-multiline {
   /* 首行及换行后缩进 2em(与 label 宽度匹配,可根据需求调整) */
-  text-indent: 0em;
+  text-indent: 0;
+
   /* 允许长文本换行(覆盖原有 cell-value 的 break-all,确保中文换行正常) */
   word-break: break-word;
+
   /* 保证换行后文本正常显示(可选,清除可能的 nowrap 影响) */
   white-space: normal;
 }
@@ -401,24 +458,24 @@ onMounted(async () => {
 /* 审批模式下表单字段的只读样式 */
 :deep(.el-form-item.is-disabled .el-input__inner),
 :deep(.el-form-item.is-disabled .el-textarea__inner) {
-  background-color: #f5f7fa;
-  border-color: #e4e7ed;
   color: #c0c4cc;
   cursor: not-allowed;
+  background-color: #f5f7fa;
+  border-color: #e4e7ed;
 }
 
 :deep(.el-form-item.is-disabled .el-select .el-input__inner) {
-  background-color: #f5f7fa;
-  border-color: #e4e7ed;
   color: #c0c4cc;
   cursor: not-allowed;
+  background-color: #f5f7fa;
+  border-color: #e4e7ed;
 }
 
 :deep(.el-form-item.is-disabled .el-date-editor .el-input__inner) {
-  background-color: #f5f7fa;
-  border-color: #e4e7ed;
   color: #c0c4cc;
   cursor: not-allowed;
+  background-color: #f5f7fa;
+  border-color: #e4e7ed;
 }
 
 /* 只读模式下表单字段的样式 */
@@ -426,16 +483,16 @@ onMounted(async () => {
 :deep(.el-form-item.is-disabled .el-textarea__inner),
 :deep(.el-form-item.is-disabled .el-select .el-input__inner),
 :deep(.el-form-item.is-disabled .el-date-editor .el-input__inner) {
-  background-color: #f5f7fa;
-  border-color: #e4e7ed;
   color: #606266; /* 保持文字可读性 */
   cursor: not-allowed;
+  background-color: #f5f7fa;
+  border-color: #e4e7ed;
 }
 
 /* 详情模式下的特殊样式 */
 .detail-mode .cell-value {
-  color: #303133;
   font-weight: normal;
+  color: #303133;
 }
 
 /* 审批意见文本域样式 */
@@ -456,14 +513,14 @@ onMounted(async () => {
 
 :deep(.el-transfer-panel__item) {
   display: flex !important;
-  align-items: center !important;
   height: 32px !important;
-  line-height: 32px !important;
   padding: 0 8px !important;
   margin: 0 !important;
-  white-space: nowrap;
   overflow: hidden;
+  line-height: 32px !important;
   text-overflow: ellipsis;
+  white-space: nowrap;
+  align-items: center !important;
 }
 
 :deep(.el-transfer-panel__list) {
@@ -472,8 +529,180 @@ onMounted(async () => {
 
 /* 只读模式下的设备显示样式 */
 :deep(.is-disabled) .device-display-container {
-  background-color: #f5f7fa;
   color: #606266;
   cursor: not-allowed;
+  background-color: #f5f7fa;
+}
+
+.maintain-record-page {
+  min-height: calc(
+    100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
+  );
+  padding: 16px;
+  background: #f5f7fa;
+}
+
+.page-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  padding: 16px 20px;
+  margin-bottom: 16px;
+  background: var(--el-bg-color);
+  border-radius: 8px;
+  box-shadow: 0 1px 4px rgb(0 0 0 / 8%);
+}
+
+.page-header__main {
+  display: flex;
+  align-items: center;
+  min-width: 0;
+}
+
+.page-header__icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 42px;
+  height: 42px;
+  margin-right: 12px;
+  font-size: 20px;
+  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-title {
+  max-width: min(720px, 65vw);
+  margin: 0;
+  overflow: hidden;
+  font-size: 18px;
+  font-weight: 600;
+  line-height: 26px;
+  color: var(--el-text-color-primary);
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.page-subtitle {
+  margin: 2px 0 0;
+  font-size: 13px;
+  line-height: 20px;
+  color: var(--el-text-color-secondary);
+}
+
+.page-section {
+  margin-bottom: 16px;
+  overflow: hidden;
+  background: var(--el-bg-color);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+  box-shadow: 0 1px 4px rgb(0 0 0 / 5%);
+}
+
+.section-header {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  gap: 16px;
+  min-height: 54px;
+  padding: 0 20px;
+  background: var(--el-fill-color-extra-light);
+  border-bottom: 1px solid var(--el-border-color-lighter);
+}
+
+.section-title {
+  display: flex;
+  align-items: center;
+  flex: 0 0 auto;
+  font-size: 16px;
+  font-weight: 600;
+  color: var(--el-text-color-primary);
+}
+
+.section-title span {
+  width: 4px;
+  height: 18px;
+  margin-right: 10px;
+  background: var(--el-color-primary);
+  border-radius: 2px;
+}
+
+.section-actions {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+  justify-content: flex-end;
+}
+
+.section-actions :deep(.el-button),
+.query-actions :deep(.el-button) {
+  margin-left: 0;
+}
+
+.info-grid {
+  display: grid;
+  grid-template-columns: repeat(3, minmax(0, 1fr));
+  gap: 1px;
+  margin: 16px 20px;
+  overflow: hidden;
+  background: var(--el-border-color-lighter);
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 4px;
+}
+
+.info-item {
+  display: flex;
+  align-items: center;
+  min-width: 0;
+  min-height: 48px;
+  padding: 10px 14px;
+  background: var(--el-bg-color);
+}
+
+.info-label {
+  flex: 0 0 104px;
+  color: var(--el-text-color-secondary);
+  white-space: nowrap;
+}
+
+.info-value {
+  min-width: 0;
+  overflow: hidden;
+  font-weight: 500;
+  color: var(--el-text-color-primary);
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+
+.query-form {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 12px 20px;
+  padding: 16px 20px;
+  border-bottom: 1px solid var(--el-border-color-lighter);
+}
+
+.query-form :deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+.query-date {
+  width: 260px;
+}
+
+.query-actions :deep(.el-form-item__content) {
+  display: flex;
+  gap: 8px;
+}
+
+.table-wrap {
+  padding: 16px 20px;
+}
+
+.records-section :deep(.pagination-container) {
+  padding: 12px 20px;
 }
 </style>

+ 430 - 328
src/views/pms/maintain/index.vue

@@ -1,339 +1,322 @@
 <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"
-      >
-        <!-- 搜索工作栏 -->
-        <el-form
-          class="-mb-15px"
-          :model="queryParams"
-          ref="queryFormRef"
-          :inline="true"
-          label-width="68px"
-        >
-          <el-form-item
-            :label="t('maintain.faultCode')"
-            prop="failureCode"
-            style="margin-left: 25px"
-          >
-            <el-input
-              v-model="queryParams.failureCode"
-              :placeholder="t('maintain.codeHolder')"
-              clearable
-              @keyup.enter="handleQuery"
-              class="!w-200px"
-            />
-          </el-form-item>
-          <el-form-item :label="t('maintain.faultName')" label-width="70px" prop="failureName">
-            <el-input
-              v-model="queryParams.failureName"
-              :placeholder="t('maintain.nameHolder')"
-              clearable
-              @keyup.enter="handleQuery"
-              class="!w-200px"
-            />
-          </el-form-item>
-          <el-form-item label="执行时间" label-width="70px" prop="executeDate">
-            <el-date-picker
-              size="small"
-              v-model="queryParams.executeDate"
-              value-format="YYYY-MM-DD HH:mm:ss"
-              type="daterange"
-              start-placeholder="开始日期"
-              end-placeholder="结束日期"
-              class="!w-220px"
-              :clearable="true"
-              :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-            />
-          </el-form-item>
-          <el-form-item label="创建时间" label-width="70px" prop="createTime">
-            <el-date-picker
-              size="small"
-              v-model="queryParams.createTime"
-              value-format="YYYY-MM-DD HH:mm:ss"
-              type="daterange"
-              start-placeholder="开始日期"
-              end-placeholder="结束日期"
-              class="!w-220px"
-              :clearable="true"
-              :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-            />
-          </el-form-item>
-          <el-form-item :label="t('maintain.status')" label-width="40px" prop="status">
-            <el-select
-              v-model="queryParams.status"
-              :placeholder="t('maintain.status')"
-              clearable
-              class="!w-200px"
-            >
-              <el-option
-                v-for="dict in getStrDictOptions(DICT_TYPE.PMS_MAIN_STATUS)"
-                :key="dict.value"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item :label="t('maintain.shutDown')" v-show="ifShow" prop="ifStop">
-            <el-select
-              v-model="queryParams.ifStop"
-              :placeholder="t('maintain.shutDown')"
-              clearable
-              class="!w-200px"
-            >
-              <el-option
-                v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
-                :key="dict.value"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item
-            :label="t('maintain.failureTime')"
-            v-show="ifShow"
-            prop="failureTime"
-            style="margin-left: 25px"
-          >
-            <el-date-picker
-              v-model="queryParams.failureTime"
-              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-220px"
-            />
-          </el-form-item>
-          <el-form-item :label="t('maintain.solve')" v-show="ifShow" prop="ifDeal">
-            <el-select
-              v-model="queryParams.ifDeal"
-              :placeholder="t('maintain.solve')"
-              clearable
-              class="!w-200px"
-            >
-              <el-option
-                v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
-                :key="dict.value"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item :label="t('maintain.assist')" v-show="ifShow" prop="needHelp">
-            <el-select
-              v-model="queryParams.needHelp"
-              :placeholder="t('maintain.assist')"
-              clearable
-              class="!w-200px"
-            >
-              <el-option
-                v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
-                :key="dict.value"
-                :label="dict.label"
-                :value="dict.value"
-              />
-            </el-select>
-          </el-form-item>
-          <el-form-item>
-            <el-button v-if="!ifShow" @click="moreQuery(true)" type="warning"
-              ><Icon icon="ep:search" class="mr-5px" /> {{ t('maintain.moreSearch') }}</el-button
-            >
-            <el-button v-if="ifShow" @click="moreQuery(false)" type="danger"
-              ><Icon icon="ep:search" class="mr-5px" /> {{ t('maintain.closeSearch') }}</el-button
-            >
-            <el-button @click="handleQuery"
-              ><Icon icon="ep:search" class="mr-5px" /> {{ t('maintain.search') }}</el-button
-            >
-            <el-button @click="resetQuery"
-              ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('maintain.reset') }}</el-button
-            >
-            <el-button
-              type="primary"
-              plain
-              @click="openForm('create')"
-              v-hasPermi="['rq:iot-maintain:create']"
-            >
-              <Icon icon="ep:plus" class="mr-5px" /> {{ t('maintain.added') }}
-            </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>
+  <div
+    class="maintain-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
+      v-model="queryParams.deptId"
+      :top-id="rootDeptId"
+      :dept-id="deptId"
+      :init-select="false"
+      :show-title="false"
+      request-api="getSimpleDeptList"
+      class="maintain-tree row-span-2"
+      @node-click="handleDeptNodeClick" />
+
+    <el-form
+      ref="queryFormRef"
+      :model="queryParams"
+      size="default"
+      label-width="68px"
+      class="maintain-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('maintain.faultCode')" prop="failureCode">
+          <el-input
+            v-model="queryParams.failureCode"
+            :placeholder="t('maintain.codeHolder')"
+            clearable
+            @keyup.enter="handleQuery"
+            class="query-control" />
+        </el-form-item>
+        <el-form-item :label="t('maintain.faultName')" label-width="70px" prop="failureName">
+          <el-input
+            v-model="queryParams.failureName"
+            :placeholder="t('maintain.nameHolder')"
+            clearable
+            @keyup.enter="handleQuery"
+            class="query-control" />
+        </el-form-item>
+        <el-form-item label="执行时间" label-width="70px" prop="executeDate">
+          <el-date-picker
+            v-model="queryParams.executeDate"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            type="daterange"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            class="query-control query-control--date"
+            :clearable="true"
+            :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" />
+        </el-form-item>
+        <el-form-item label="创建时间" label-width="70px" prop="createTime">
+          <el-date-picker
+            v-model="queryParams.createTime"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            type="daterange"
+            start-placeholder="开始日期"
+            end-placeholder="结束日期"
+            class="query-control query-control--date"
+            :clearable="true"
+            :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]" />
+        </el-form-item>
+        <el-form-item :label="t('maintain.status')" prop="status">
+          <el-select
+            v-model="queryParams.status"
+            :placeholder="t('maintain.status')"
+            clearable
+            class="query-control query-control--small">
+            <el-option
+              v-for="dict in getStrDictOptions(DICT_TYPE.PMS_MAIN_STATUS)"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item :label="t('maintain.shutDown')" v-show="ifShow" prop="ifStop">
+          <el-select
+            v-model="queryParams.ifStop"
+            :placeholder="t('maintain.shutDown')"
+            clearable
+            class="query-control query-control--small">
+            <el-option
+              v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item :label="t('maintain.failureTime')" v-show="ifShow" prop="failureTime">
+          <el-date-picker
+            v-model="queryParams.failureTime"
+            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="query-control query-control--date" />
+        </el-form-item>
+        <el-form-item :label="t('maintain.solve')" v-show="ifShow" prop="ifDeal">
+          <el-select
+            v-model="queryParams.ifDeal"
+            :placeholder="t('maintain.solve')"
+            clearable
+            class="query-control query-control--small">
+            <el-option
+              v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value" />
+          </el-select>
+        </el-form-item>
+        <el-form-item :label="t('maintain.assist')" v-show="ifShow" prop="needHelp">
+          <el-select
+            v-model="queryParams.needHelp"
+            :placeholder="t('maintain.assist')"
+            clearable
+            class="query-control query-control--small">
+            <el-option
+              v-for="dict in getBoolDictOptions(DICT_TYPE.INFRA_BOOLEAN_STRING)"
+              :key="dict.value"
+              :label="dict.label"
+              :value="dict.value" />
+          </el-select>
+        </el-form-item>
       </div>
 
-      <!-- 列表 -->
-      <ContentWrap style="border: none; margin-top: 20px">
-        <zm-table :loading="loading" :data="list" height="calc(85vh - 180px)">
-          <zm-table-column :label="t('maintain.serial')" min-width="70" align="center">
-            <template #default="scope">
-              {{ scope.$index + 1 }}
-            </template>
-          </zm-table-column>
-          <zm-table-column :label="t('iotDevice.code')" align="center" prop="deviceCode" />
-          <zm-table-column :label="t('maintain.deviceName')" align="center" prop="deviceName" />
-          <zm-table-column
-            :label="t('maintain.status')"
-            align="center"
-            prop="status"
-            min-width="95"
-          >
-            <template #default="scope">
-              <dict-tag :type="DICT_TYPE.PMS_MAIN_STATUS" :value="scope.row.status" />
-            </template>
-          </zm-table-column>
-          <zm-table-column
-            :label="t('maintain.source')"
-            align="center"
-            prop="maintainType"
-            min-width="135"
-          />
-          <zm-table-column
-            :label="t('iotMaintain.repairType')"
-            align="center"
-            prop="type"
-            min-width="135"
-          >
-            <template #default="scope">
-              <dict-tag :type="DICT_TYPE.PMS_MAIN_TYPE" :value="scope.row.type" />
-            </template>
-          </zm-table-column>
-          <zm-table-column :label="t('maintain.shutDown')" align="center" prop="ifStop">
-            <template #default="scope">
-              <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.ifStop" />
-            </template>
-          </zm-table-column>
-          <zm-table-column
-            label="执行时间"
-            align="center"
-            prop="executeDate"
-            :formatter="dateFormatter"
-            min-width="180px"
-          />
-          <zm-table-column :label="t('iotMaintain.oaFlowNo')" align="center" prop="oaFlowNo" />
-          <zm-table-column
-            :label="t('maintain.dept')"
-            align="center"
-            prop="deptName"
-            min-width="135"
-          />
-          <zm-table-column
-            :label="t('inspect.createName')"
-            align="center"
-            prop="createName"
-            min-width="130"
-          />
-          <zm-table-column
-            label="创建时间"
-            align="center"
-            prop="createTime"
-            :formatter="dateFormatter"
-            width="180px"
-          />
-          <zm-table-column
-            :label="t('maintain.operation')"
-            align="center"
-            min-width="170"
-            fixed="right"
-            action
-          >
-            <template #default="scope">
-              <el-button link type="primary" @click="detail(scope.row.id)">
-                {{ t('maintain.view') }}
-              </el-button>
-              <el-button
-                link
-                v-if="
-                  scope.row.status === 'tx' &&
-                  (scope.row.auditStatus === 20 || scope.row.auditStatus === null)
-                "
-                type="danger"
-                @click="
-                  openForm('update', scope.row.id, scope.row.maintainPerson, scope.row.company)
-                "
-              >
-                {{ t('maintain.fill') }}
-              </el-button>
-              <el-button
-                link
-                v-if="scope.row.status === 'personnel' && userid === scope.row.maintainPerson"
-                type="danger"
-                @click="openDialog(scope.row.id)"
-              >
-                {{ t('maintain.maintainWay') }}
-              </el-button>
-              <el-button
-                v-if="scope.row.status !== 'personnel'"
-                link
-                type="warning"
-                @click="openRecord(scope.row.id)"
-              >
-                {{ t('maintain.record') }}
-              </el-button>
-            </template>
-          </zm-table-column>
-        </zm-table>
-        <el-dialog
-          v-model="dialogVisible"
-          title="维修方式"
-          :width="600"
-          :before-close="handleClose"
-          append-to-body
-          :close-on-click-modal="false"
-        >
-          <el-form ref="methodFormRef" :model="form" :rules="rules" label-width="70px">
-            <el-form-item label="维修方式" prop="maintainMethod">
-              <el-select v-model="form.maintainMethod" placeholder="请选择维修方式">
-                <el-option
-                  v-for="dict in getStrDictOptions(DICT_TYPE.PMS_MAINTAIN_METHOD)"
-                  :key="dict.label"
-                  :label="dict.label"
-                  :value="dict.value"
-                />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="备注说明" prop="methodDescription">
-              <el-input
-                type="textarea"
-                v-model="form.methodDescription"
-                placeholder="请输入备注说明"
-                :rows="4"
-                resize="none"
-              />
-            </el-form-item>
-          </el-form>
-          <template #footer>
-            <el-button @click="handleCancel">取消</el-button>
-            <el-button type="primary" @click="handleConfirm">确定</el-button>
+      <el-form-item class="query-actions">
+        <el-button v-if="!ifShow" type="warning" @click="moreQuery(true)">
+          <Icon icon="ep:search" class="mr-5px" /> {{ t('maintain.moreSearch') }}
+        </el-button>
+        <el-button v-else type="danger" @click="moreQuery(false)">
+          <Icon icon="ep:search" class="mr-5px" /> {{ t('maintain.closeSearch') }}
+        </el-button>
+        <el-button type="primary" @click="handleQuery">
+          <Icon icon="ep:search" class="mr-5px" /> {{ t('maintain.search') }}
+        </el-button>
+        <el-button @click="resetQuery">
+          <Icon icon="ep:refresh" class="mr-5px" /> {{ t('maintain.reset') }}
+        </el-button>
+        <el-button
+          type="primary"
+          plain
+          @click="openForm('create')"
+          v-hasPermi="['rq:iot-maintain:create']">
+          <Icon icon="ep:plus" class="mr-5px" /> {{ t('maintain.added') }}
+        </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>
+
+    <div class="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>
+              <ZmTableColumn :label="t('maintain.serial')" width="70" align="center" fixed="left">
+                <template #default="scope">
+                  {{ scope.$index + 1 }}
+                </template>
+              </ZmTableColumn>
+              <ZmTableColumn :label="t('iotDevice.code')" align="center" prop="deviceCode" />
+              <ZmTableColumn :label="t('maintain.deviceName')" align="center" prop="deviceName" />
+              <ZmTableColumn
+                :label="t('maintain.status')"
+                align="center"
+                prop="status"
+                min-width="95">
+                <template #default="scope">
+                  <dict-tag :type="DICT_TYPE.PMS_MAIN_STATUS" :value="scope.row.status" />
+                </template>
+              </ZmTableColumn>
+              <ZmTableColumn
+                :label="t('maintain.source')"
+                align="center"
+                prop="maintainType"
+                min-width="135" />
+              <ZmTableColumn
+                :label="t('iotMaintain.repairType')"
+                align="center"
+                prop="type"
+                min-width="135">
+                <template #default="scope">
+                  <dict-tag :type="DICT_TYPE.PMS_MAIN_TYPE" :value="scope.row.type" />
+                </template>
+              </ZmTableColumn>
+              <ZmTableColumn :label="t('maintain.shutDown')" align="center" prop="ifStop">
+                <template #default="scope">
+                  <dict-tag :type="DICT_TYPE.INFRA_BOOLEAN_STRING" :value="scope.row.ifStop" />
+                </template>
+              </ZmTableColumn>
+              <ZmTableColumn
+                label="执行时间"
+                align="center"
+                prop="executeDate"
+                :formatter="dateFormatter"
+                min-width="180px" />
+              <ZmTableColumn :label="t('iotMaintain.oaFlowNo')" align="center" prop="oaFlowNo" />
+              <ZmTableColumn
+                :label="t('maintain.dept')"
+                align="center"
+                prop="deptName"
+                min-width="135" />
+              <ZmTableColumn
+                :label="t('inspect.createName')"
+                align="center"
+                prop="createName"
+                min-width="130" />
+              <ZmTableColumn
+                label="创建时间"
+                align="center"
+                prop="createTime"
+                :formatter="dateFormatter"
+                width="180px" />
+              <ZmTableColumn
+                :label="t('maintain.operation')"
+                align="center"
+                min-width="170"
+                fixed="right"
+                action>
+                <template #default="scope">
+                  <el-button link type="primary" @click="detail(scope.row.id)">
+                    {{ t('maintain.view') }}
+                  </el-button>
+                  <el-button
+                    link
+                    v-if="
+                      scope.row.status === 'tx' &&
+                      (scope.row.auditStatus === 20 || scope.row.auditStatus === null)
+                    "
+                    type="danger"
+                    @click="
+                      openForm('update', scope.row.id, scope.row.maintainPerson, scope.row.company)
+                    ">
+                    {{ t('maintain.fill') }}
+                  </el-button>
+                  <el-button
+                    link
+                    v-if="scope.row.status === 'personnel' && userid === scope.row.maintainPerson"
+                    type="danger"
+                    @click="openDialog(scope.row.id)">
+                    {{ t('maintain.maintainWay') }}
+                  </el-button>
+                  <el-button
+                    v-if="scope.row.status !== 'personnel'"
+                    link
+                    type="warning"
+                    @click="openRecord(scope.row.id)">
+                    {{ t('maintain.record') }}
+                  </el-button>
+                </template>
+              </ZmTableColumn>
+            </ZmTable>
           </template>
-        </el-dialog>
-        <!-- 分页 -->
-        <Pagination
+        </el-auto-resizer>
+      </div>
+
+      <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>
+
+  <el-dialog
+    v-model="dialogVisible"
+    title="维修方式"
+    :width="600"
+    :before-close="handleClose"
+    append-to-body
+    :close-on-click-modal="false">
+    <el-form ref="methodFormRef" :model="form" :rules="rules" label-width="70px">
+      <el-form-item label="维修方式" prop="maintainMethod">
+        <el-select v-model="form.maintainMethod" placeholder="请选择维修方式">
+          <el-option
+            v-for="dict in getStrDictOptions(DICT_TYPE.PMS_MAINTAIN_METHOD)"
+            :key="dict.label"
+            :label="dict.label"
+            :value="dict.value" />
+        </el-select>
+      </el-form-item>
+      <el-form-item label="备注说明" prop="methodDescription">
+        <el-input
+          type="textarea"
+          v-model="form.methodDescription"
+          placeholder="请输入备注说明"
+          :rows="4"
+          resize="none" />
+      </el-form-item>
+    </el-form>
+    <template #footer>
+      <el-button @click="handleCancel">取消</el-button>
+      <el-button type="primary" @click="handleConfirm">确定</el-button>
+    </template>
+  </el-dialog>
 </template>
 
 <script setup lang="ts">
 import { dateFormatter } from '@/utils/formatTime'
 import download from '@/utils/download'
 import { IotMaintainApi, IotMaintainVO } from '@/api/pms/maintain'
-import DeptTree from '@/views/system/user/DeptTree2.vue'
+import DeptTreeSelect from '@/components/DeptTreeSelect/index.vue'
 import { DICT_TYPE, getBoolDictOptions, getStrDictOptions } from '@/utils/dict'
 import { useUserStore } from '@/store/modules/user'
 
 import { useTableComponents } from '@/components/ZmTable/useTableComponents'
-const { ZmTable, ZmTableColumn } = useTableComponents()
+const { ZmTable, ZmTableColumn } = useTableComponents<IotMaintainVO>()
 
 /** 维修工单 列表 */
 defineOptions({ name: 'IotMaintain' })
@@ -346,7 +329,8 @@ const loading = ref(true) // 列表的加载中
 const list = ref<IotMaintainVO[]>([]) // 列表的数据
 const total = ref(0) // 列表的总页数
 const dialogVisible = ref(false)
-let isLeftContentCollapsed = ref(false)
+const rootDeptId = 156
+const deptId = useUserStore().getUser.deptId || rootDeptId
 const queryParams = reactive({
   pageNo: 1,
   pageSize: 10,
@@ -460,6 +444,7 @@ const getList = async () => {
 /** 处理部门被点击 */
 const handleDeptNodeClick = async (row) => {
   queryParams.deptId = row.id
+  queryParams.pageNo = 1
   await getList()
 }
 /** 搜索按钮操作 */
@@ -470,10 +455,20 @@ const handleQuery = () => {
 
 /** 重置按钮操作 */
 const resetQuery = () => {
-  queryFormRef.value.resetFields()
+  queryFormRef.value?.resetFields()
   handleQuery()
 }
 
+const handleSizeChange = (val: number) => {
+  queryParams.pageSize = val
+  handleQuery()
+}
+
+const handleCurrentChange = (val: number) => {
+  queryParams.pageNo = val
+  getList()
+}
+
 /** 删除按钮操作 */
 const handleDelete = async (id: number) => {
   try {
@@ -510,11 +505,118 @@ onMounted(() => {
 })
 </script>
 
-<style lang="scss" scoped>
-::v-deep .el-table__header-wrapper {
-  position: sticky !important;
+<style scoped>
+.maintain-query {
+  display: block;
+}
+
+.query-row {
+  display: flex;
+  flex: 1 1 auto;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 12px 24px;
+  min-width: 0;
+}
+
+.query-row :deep(.el-form-item__label) {
+  white-space: nowrap;
+}
+
+.query-actions {
   width: 100%;
-  top: 0px;
-  z-index: 2000;
+  margin-top: 12px;
+}
+
+.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: 180px;
+}
+
+.query-control--small {
+  width: 140px;
+}
+
+.query-control--date {
+  width: 220px;
+}
+
+:deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+@media (width <= 1500px) {
+  .query-row {
+    gap: 12px 18px;
+  }
+
+  .query-control {
+    width: 168px;
+  }
+
+  .query-control--small {
+    width: 132px;
+  }
+
+  .query-control--date {
+    width: 210px;
+  }
+}
+
+@media (width <= 1200px) {
+  .maintain-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(.maintain-tree) {
+    grid-row: auto !important;
+    width: 100% !important;
+    height: 320px !important;
+    min-width: 0 !important;
+  }
+}
+
+@media (width <= 768px) {
+  .maintain-query {
+    padding: 12px;
+  }
+
+  .query-row,
+  .query-row :deep(.el-form-item),
+  .query-actions {
+    width: 100%;
+  }
+
+  .query-control,
+  .query-control--small,
+  .query-control--date {
+    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%;
+    margin-left: 0;
+  }
 }
 </style>

+ 43 - 22
src/views/pms/maintain/material/MaintainMaterialDrawer.vue

@@ -7,33 +7,42 @@
     :show-close="false"
     direction="rtl"
     :size="computedSize"
-    :before-close="handleClose"
-  >
+    :before-close="handleClose">
     <el-form
       ref="drawerRef"
       :model="drawerFormData"
       :rules="formRules"
       label-width="100px"
-      v-loading="formLoading"
-    >
+      v-loading="formLoading">
       <el-form-item :label="t('stock.MaterialMaster')" prop="code1" style="margin-bottom: 30px">
         <el-select
           v-model="drawerFormData.code1"
           :model-value="materialLabel"
           :placeholder="t('stock.MaterialMaster')"
-          @click="openForm"
-        />
+          @click="openForm" />
       </el-form-item>
-      <el-form-item :label="t('workOrderMaterial.materialCode')" prop="code1" style="margin-bottom: 30px">
+      <el-form-item
+        :label="t('workOrderMaterial.materialCode')"
+        prop="code1"
+        style="margin-bottom: 30px">
         <el-input v-model="drawerFormData.code1" disabled />
       </el-form-item>
-      <el-form-item :label="t('workOrderMaterial.materialName')" prop="name1" style="margin-bottom: 30px">
+      <el-form-item
+        :label="t('workOrderMaterial.materialName')"
+        prop="name1"
+        style="margin-bottom: 30px">
         <el-input v-model="drawerFormData.name1" disabled />
       </el-form-item>
       <el-form-item :label="t('workOrderMaterial.unit')" prop="unit1" style="margin-bottom: 30px">
-        <el-input v-model="drawerFormData.unit1" disabled :placeholder="t('workOrderMaterial.unit')" />
+        <el-input
+          v-model="drawerFormData.unit1"
+          disabled
+          :placeholder="t('workOrderMaterial.unit')" />
       </el-form-item>
-      <el-form-item :label="t('workOrderMaterial.ConsumptionQuantity')" prop="depleteCount1" style="margin-bottom: 30px">
+      <el-form-item
+        :label="t('workOrderMaterial.ConsumptionQuantity')"
+        prop="depleteCount1"
+        style="margin-bottom: 30px">
         <el-input-number
           v-model="drawerFormData.depleteCount1"
           :min="0"
@@ -41,25 +50,31 @@
           :step="0.1"
           style="width: 50%"
           class="w-100%"
-          controls-position="right"
-        />
+          controls-position="right" />
       </el-form-item>
-      <el-form-item :label="t('workOrderMaterial.unitPrice')" prop="price1" style="margin-bottom: 30px">
+      <el-form-item
+        :label="t('workOrderMaterial.unitPrice')"
+        prop="price1"
+        style="margin-bottom: 30px">
         <el-input-number
           v-model="drawerFormData.price1"
           :min="0"
           :precision="2"
           :step="0.1"
           style="width: 50%"
-          controls-position="right"
-        />
+          controls-position="right" />
       </el-form-item>
       <el-form-item :label="t('deviceForm.remark')" prop="remark1" style="margin-bottom: 30px">
-        <el-input v-model="drawerFormData.remark1" type="textarea" :placeholder="t('deviceForm.remarkHolder')" />
+        <el-input
+          v-model="drawerFormData.remark1"
+          type="textarea"
+          :placeholder="t('deviceForm.remarkHolder')" />
       </el-form-item>
     </el-form>
     <template #footer>
-      <el-button @click="submitForm" type="primary" :disabled="formLoading">{{ t('common.ok') }}</el-button>
+      <el-button @click="submitForm" type="primary" :disabled="formLoading">{{
+        t('common.ok')
+      }}</el-button>
       <el-button @click="handleClose">{{ t('common.cancel') }}</el-button>
     </template>
   </el-drawer>
@@ -73,6 +88,7 @@ import { CommonStatusEnum } from '@/utils/constants'
 import MaterialSelect from './MaterialSelect.vue'
 const { t } = useI18n() // 国际化
 const drawerVisible = ref<boolean>(false)
+const formLoading = ref(false)
 const emit = defineEmits(['update:modelValue', 'add'])
 const materialLabel = ref('') // 表单的类型:create - 新增;update - 修改
 defineOptions({
@@ -125,7 +141,7 @@ const formRules = reactive({
   code1: [{ required: true, message: '物料编码不能为空', trigger: 'blur' }],
   name1: [{ required: true, message: '物料名称不能为空', trigger: 'blur' }],
   depleteCount1: [{ required: true, message: '消耗数量不能为空', trigger: 'blur' }],
-  price1:[{ required: true, message: '单价不能为空', trigger: 'blur' }],
+  price1: [{ required: true, message: '单价不能为空', trigger: 'blur' }]
 })
 const materialFormRef = ref()
 const openForm = () => {
@@ -163,10 +179,15 @@ const handleClose = () => {
 }
 const drawerRef = ref()
 const submitForm = async () => {
-  await drawerRef.value.validate()
-  drawerFormData.value.sourceType = '手动添加'
-  emit('add', drawerFormData.value)
-  emit('update:modelValue', false)
+  formLoading.value = true
+  try {
+    await drawerRef.value.validate()
+    drawerFormData.value.sourceType = '手动添加'
+    emit('add', drawerFormData.value)
+    emit('update:modelValue', false)
+  } finally {
+    formLoading.value = false
+  }
 }
 
 defineExpose({ openDrawer, closeDrawer }) // 暴露方法给父组件

Some files were not shown because too many files changed in this diff