فهرست منبع

feat(故障知识库): 优化列表页及新增编辑弹窗样式

- 重构故障知识库列表页查询区和表格布局
- 查询条件及操作按钮支持响应式自动换行
- 使用 el-auto-resizer 实现 ZmTable 高度自适应
- 优化分页、按钮尺寸、字段间距和移动端布局
- 重构新增编辑弹窗标题及表单信息分区
- 基础信息调整为响应式双列布局
- 优化描述、备注文本域和底部操作按钮
- 保留原有权限、校验、导出及增删改逻辑
Zimo 1 هفته پیش
والد
کامیت
c91e0d1800
2فایلهای تغییر یافته به همراه409 افزوده شده و 158 حذف شده
  1. 189 45
      src/views/pms/information/IotInformationDbForm.vue
  2. 220 113
      src/views/pms/information/index.vue

+ 189 - 45
src/views/pms/information/IotInformationDbForm.vue

@@ -1,63 +1,100 @@
 <template>
-  <Dialog :title="dialogTitle" v-model="dialogVisible">
+  <Dialog
+    v-model="dialogVisible"
+    :title="dialogTitle"
+    width="760px"
+    :fullscreen="false"
+    class="information-form-dialog">
+    <template #title>
+      <div class="dialog-title mb-4">
+        <div class="dialog-title__icon">
+          <Icon icon="ep:notebook" />
+        </div>
+        <div>
+          <div class="dialog-title__text">{{ dialogTitle }}</div>
+          <div class="dialog-title__subtitle">维护设备故障现象、影响及解决方案</div>
+        </div>
+      </div>
+    </template>
+
     <el-form
       ref="formRef"
       :model="formData"
       :rules="formRules"
-      label-width="100px"
-      v-loading="formLoading"
-    >
-      <el-row>
-        <el-col :span="12">
-          <el-form-item :label="t('info.deviceClass')" prop="deviceType" >
-            <el-tree-select
-              v-model="formData.deviceType"
-              :data="productClassifyList"
-              :props="defaultProps"
-              check-strictly
-              node-key="id"
-              :placeholder="t('deviceForm.category')"
-            />
-          </el-form-item>
-        </el-col>
-        <el-col :span="12">
-        <el-form-item :label="t('faultForm.faultImpact')" prop="failureInfluence">
-          <el-input v-model="formData.failureInfluence" :placeholder="t('faultForm.faultImpactHolder')" />
-        </el-form-item>
+      size="default"
+      label-width="92px"
+      class="information-form"
+      v-loading="formLoading">
+      <section class="form-section">
+        <div class="section-heading"><span></span>基础信息</div>
+        <el-row :gutter="20">
+          <el-col :xs="24" :sm="12">
+            <el-form-item :label="t('info.deviceClass')" prop="deviceType">
+              <el-tree-select
+                v-model="formData.deviceType"
+                :data="productClassifyList"
+                :props="defaultProps"
+                check-strictly
+                node-key="id"
+                :placeholder="t('deviceForm.category')"
+                filterable
+                clearable />
+            </el-form-item>
           </el-col>
-        <el-col :span="12">
-        <el-form-item :label="t('faultForm.faultySystem')" prop="failureSystem">
-          <el-input v-model="formData.failureSystem" :placeholder="t('faultForm.faultySystemHolder')" />
-        </el-form-item>
+          <el-col :xs="24" :sm="12">
+            <el-form-item :label="t('faultForm.faultImpact')" prop="failureInfluence">
+              <el-input
+                v-model="formData.failureInfluence"
+                :placeholder="t('faultForm.faultImpactHolder')" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12">
+            <el-form-item :label="t('faultForm.faultySystem')" prop="failureSystem">
+              <el-input
+                v-model="formData.failureSystem"
+                :placeholder="t('faultForm.faultySystemHolder')" />
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :sm="12">
+            <el-form-item :label="t('iotMaintain.solution')" prop="solutions">
+              <el-input v-model="formData.solutions" :placeholder="t('iotMaintain.soHolder')" />
+            </el-form-item>
           </el-col>
-        <el-col :span="12">
-          <el-form-item :label="t('iotMaintain.solution')" prop="solutions">
-            <el-input v-model="formData.solutions" :placeholder="t('iotMaintain.soHolder')" />
-          </el-form-item>
-        </el-col>
-        <el-col :span="24">
+        </el-row>
+      </section>
+
+      <section class="form-section">
+        <div class="section-heading"><span></span>补充说明</div>
         <el-form-item :label="t('info.description')" prop="description">
-          <el-input v-model="formData.description" type="textarea" height="150px" />
+          <el-input v-model="formData.description" type="textarea" :rows="3" resize="none" />
         </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" :placeholder="t('iotMaintain.remarkHolder')" />
+          <el-input
+            v-model="formData.remark"
+            type="textarea"
+            :rows="2"
+            resize="none"
+            :placeholder="t('iotMaintain.remarkHolder')" />
         </el-form-item>
-          </el-col>
-      </el-row>
+      </section>
     </el-form>
+
     <template #footer>
-      <el-button @click="submitForm" type="primary" :disabled="formLoading">{{ t('iotMaintain.ok') }}</el-button>
-      <el-button @click="dialogVisible = false">{{ t('iotMaintain.cancel') }}</el-button>
+      <div class="dialog-footer">
+        <el-button size="default" @click="dialogVisible = false">
+          {{ t('iotMaintain.cancel') }}
+        </el-button>
+        <el-button size="default" type="primary" :loading="formLoading" @click="submitForm">
+          {{ t('iotMaintain.ok') }}
+        </el-button>
+      </div>
     </template>
   </Dialog>
 </template>
 <script setup lang="ts">
 import { IotInformationDbApi, IotInformationDbVO } from '@/api/pms/information'
-import {defaultProps, handleTree} from "@/utils/tree";
-import * as ProductClassifyApi from "@/api/pms/productclassify";
-import {DeviceAttrModelApi} from "@/api/pms/deviceattrmodel";
+import { defaultProps, handleTree } from '@/utils/tree'
+import * as ProductClassifyApi from '@/api/pms/productclassify'
 
 /** 故障知识库 表单 */
 defineOptions({ name: 'IotInformationDbForm' })
@@ -79,12 +116,12 @@ const formData = ref({
   solutions: undefined,
   remark: undefined,
   processInstanceId: undefined,
-  auditStatus: undefined,
+  auditStatus: undefined
 })
 const formRules = reactive({
   deviceType: [{ required: true, message: t('info.classHolder1'), trigger: 'change' }],
   failureInfluence: [{ required: true, message: t('info.faultyImpact'), trigger: 'change' }],
-  solutions: [{ required: true, message: t('info.solution'), trigger: 'change' }],
+  solutions: [{ required: true, message: t('info.solution'), trigger: 'change' }]
 })
 const formRef = ref() // 表单 Ref
 
@@ -144,8 +181,115 @@ const resetForm = () => {
     solutions: undefined,
     remark: undefined,
     processInstanceId: undefined,
-    auditStatus: undefined,
+    auditStatus: undefined
   }
   formRef.value?.resetFields()
 }
 </script>
+
+<style scoped>
+.dialog-title {
+  display: flex;
+  align-items: center;
+  min-width: 0;
+}
+
+.dialog-title__icon {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  width: 38px;
+  height: 38px;
+  margin-right: 12px;
+  font-size: 18px;
+  color: var(--el-color-primary);
+  background: var(--el-color-primary-light-9);
+  border: 1px solid var(--el-color-primary-light-7);
+  border-radius: 8px;
+}
+
+.dialog-title__text {
+  font-size: 17px;
+  font-weight: 600;
+  line-height: 24px;
+  color: var(--el-text-color-primary);
+}
+
+.dialog-title__subtitle {
+  margin-top: 1px;
+  font-size: 12px;
+  line-height: 18px;
+  color: var(--el-text-color-secondary);
+}
+
+.information-form {
+  display: flex;
+  flex-direction: column;
+  gap: 14px;
+}
+
+.form-section {
+  padding: 0 16px 2px;
+  overflow: hidden;
+  border: 1px solid var(--el-border-color-lighter);
+  border-radius: 8px;
+}
+
+.section-heading {
+  display: flex;
+  align-items: center;
+  height: 44px;
+  padding: 0 16px;
+  margin: 0 -16px 16px;
+  font-size: 15px;
+  font-weight: 600;
+  color: var(--el-text-color-primary);
+  background: var(--el-fill-color-extra-light);
+  border-bottom: 1px solid var(--el-border-color-lighter);
+}
+
+.section-heading span {
+  width: 4px;
+  height: 17px;
+  margin-right: 9px;
+  background: var(--el-color-primary);
+  border-radius: 2px;
+}
+
+.information-form :deep(.el-select),
+.information-form :deep(.el-tree-select) {
+  width: 100%;
+}
+
+.information-form :deep(.el-form-item) {
+  margin-bottom: 14px;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 8px;
+}
+
+.dialog-footer :deep(.el-button) {
+  margin-left: 0;
+}
+
+@media (width <= 768px) {
+  .dialog-title__subtitle {
+    display: none;
+  }
+
+  .form-section {
+    padding-right: 12px;
+    padding-left: 12px;
+  }
+
+  .section-heading {
+    padding-right: 12px;
+    padding-left: 12px;
+    margin-right: -12px;
+    margin-left: -12px;
+  }
+}
+</style>

+ 220 - 113
src/views/pms/information/index.vue

@@ -1,133 +1,135 @@
 <template>
   <div
-    style="border: none; background: #fff; display: flex; align-items: center"
-    class="py-2 rounded-sm px-2"
-  >
-    <!-- 搜索工作栏 -->
+    class="information-page grid grid-rows-[auto_1fr] gap-4 h-[calc(100vh-20px-var(--top-tool-height)-var(--tags-view-height)-var(--app-footer-height))]">
     <el-form
-      class="-mb-15px"
-      :model="queryParams"
       ref="queryFormRef"
-      :inline="true"
+      :model="queryParams"
+      size="default"
       label-width="68px"
-    >
-      <el-form-item :label="t('deviceForm.category')" prop="deviceType" style="width: 15vw">
-        <el-tree-select
-          v-model="queryParams.deviceType"
-          :data="productClassifyList"
-          :props="defaultProps"
-          check-strictly
-          node-key="id"
-          :placeholder="t('deviceForm.categoryHolder')"
-          filterable
-        />
-      </el-form-item>
-      <el-form-item :label="t('info.faultySystem')" prop="failureSystem">
-        <el-input
-          v-model="queryParams.failureSystem"
-          :placeholder="t('info.faultySystemHolder')"
-          clearable
-          @keyup.enter="handleQuery"
-          class="!w-240px"
-        />
-      </el-form-item>
-      <el-form-item :label="t('info.createTime')" prop="createTime">
-        <el-date-picker
-          v-model="queryParams.createTime"
-          value-format="YYYY-MM-DD HH:mm:ss"
-          type="daterange"
-          :start-placeholder="t('info.start')"
-          :end-placeholder="t('info.end')"
-          :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
-          class="!w-220px"
-        />
-      </el-form-item>
-      <el-form-item>
-        <el-button @click="handleQuery"
-          ><Icon icon="ep:search" class="mr-5px" /> {{ t('info.search') }}</el-button
-        >
-        <el-button @click="resetQuery"
-          ><Icon icon="ep:refresh" class="mr-5px" /> {{ t('info.reset') }}</el-button
-        >
+      class="information-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('deviceForm.category')" prop="deviceType">
+          <el-tree-select
+            v-model="queryParams.deviceType"
+            :data="productClassifyList"
+            :props="defaultProps"
+            check-strictly
+            node-key="id"
+            :placeholder="t('deviceForm.categoryHolder')"
+            filterable
+            clearable
+            class="query-control query-control--category" />
+        </el-form-item>
+        <el-form-item :label="t('info.faultySystem')" prop="failureSystem">
+          <el-input
+            v-model="queryParams.failureSystem"
+            :placeholder="t('info.faultySystemHolder')"
+            clearable
+            class="query-control"
+            @keyup.enter="handleQuery" />
+        </el-form-item>
+        <el-form-item :label="t('info.createTime')" prop="createTime">
+          <el-date-picker
+            v-model="queryParams.createTime"
+            value-format="YYYY-MM-DD HH:mm:ss"
+            type="daterange"
+            :start-placeholder="t('info.start')"
+            :end-placeholder="t('info.end')"
+            :default-time="[new Date('1 00:00:00'), new Date('1 23:59:59')]"
+            class="query-control query-control--date" />
+        </el-form-item>
+      </div>
+
+      <el-form-item class="query-actions">
+        <el-button size="default" type="primary" @click="handleQuery">
+          <Icon icon="ep:search" class="mr-5px" />{{ t('info.search') }}
+        </el-button>
+        <el-button size="default" @click="resetQuery">
+          <Icon icon="ep:refresh" class="mr-5px" />{{ t('info.reset') }}
+        </el-button>
         <el-button
+          size="default"
           type="primary"
           plain
           @click="openForm('create')"
-          v-hasPermi="['rq:iot-information-db:create']"
-        >
-          <Icon icon="ep:plus" class="mr-5px" />
-          {{ t('info.add') }}
+          v-hasPermi="['rq:iot-information-db:create']">
+          <Icon icon="ep:plus" class="mr-5px" />{{ t('info.add') }}
         </el-button>
         <el-button
+          size="default"
           type="success"
           plain
           @click="handleExport"
           :loading="exportLoading"
-          v-hasPermi="['rq:iot-information-db:export']"
-        >
-          <Icon icon="ep:download" class="mr-5px" /> 导出
+          v-hasPermi="['rq:iot-information-db:export']">
+          <Icon icon="ep:download" class="mr-5px" />导出
         </el-button>
       </el-form-item>
     </el-form>
-  </div>
 
-  <!-- 列表 -->
-  <ContentWrap style="border: none; margin-top: 20px">
-    <zm-table :loading="loading" :data="list" height="70vh">
-      <zm-table-column :label="t('iotDevice.serial')" width="70" align="center">
-        <template #default="scope">
-          {{ scope.$index + 1 }}
-        </template>
-      </zm-table-column>
-      <zm-table-column :label="t('info.deviceClass')" align="center" prop="className" />
-      <zm-table-column :label="t('faultForm.faultImpact')" align="center" prop="failureInfluence" />
-      <zm-table-column :label="t('iotMaintain.faultySystem')" align="center" prop="failureSystem" />
-      <zm-table-column :label="t('info.description')" align="center" prop="description" />
-      <zm-table-column :label="t('iotMaintain.solution')" align="center" prop="solutions" />
-      <zm-table-column :label="t('iotMaintain.remark')" align="center" prop="remark" />
-      <zm-table-column
-        :label="t('info.createTime')"
-        align="center"
-        prop="createTime"
-        :formatter="dateFormatter"
-        width="180px"
-      />
-      <!--      <zm-table-column label="审核状态" align="center" prop="auditStatus" />-->
-      <zm-table-column
-        :label="t('iotMaintain.operation')"
-        align="center"
-        min-width="120px"
-        fixed="right"
-        action
-      >
-        <template #default="scope">
-          <el-button
-            link
-            type="primary"
-            @click="openForm('update', scope.row.id)"
-            v-hasPermi="['rq:iot-information-db:update']"
-          >
-            {{ t('info.edit') }}
-          </el-button>
-          <el-button
-            link
-            type="danger"
-            @click="handleDelete(scope.row.id)"
-            v-hasPermi="['rq:iot-information-db:delete']"
-          >
-            {{ t('info.delete') }}
-          </el-button>
-        </template>
-      </zm-table-column>
-    </zm-table>
-    <!-- 分页 -->
-    <Pagination
-      :total="total"
-      v-model:page="queryParams.pageNo"
-      v-model:limit="queryParams.pageSize"
-      @pagination="getList"
-    />
-  </ContentWrap>
+    <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 type="index" :label="t('iotDevice.serial')" width="70" fixed="left" />
+              <ZmTableColumn :label="t('info.deviceClass')" prop="className" fixed="left" />
+              <ZmTableColumn :label="t('faultForm.faultImpact')" prop="failureInfluence" />
+              <ZmTableColumn :label="t('iotMaintain.faultySystem')" prop="failureSystem" />
+              <ZmTableColumn :label="t('info.description')" prop="description" />
+              <ZmTableColumn :label="t('iotMaintain.solution')" prop="solutions" />
+              <ZmTableColumn :label="t('iotMaintain.remark')" prop="remark" />
+              <ZmTableColumn
+                :label="t('info.createTime')"
+                prop="createTime"
+                :formatter="dateFormatter"
+                width="180" />
+              <ZmTableColumn :label="t('iotMaintain.operation')" width="120" fixed="right" action>
+                <template #default="scope">
+                  <el-button
+                    size="default"
+                    link
+                    type="primary"
+                    @click="openForm('update', scope.row.id)"
+                    v-hasPermi="['rq:iot-information-db:update']">
+                    {{ t('info.edit') }}
+                  </el-button>
+                  <el-button
+                    size="default"
+                    link
+                    type="danger"
+                    @click="handleDelete(scope.row.id)"
+                    v-hasPermi="['rq:iot-information-db:delete']">
+                    {{ t('info.delete') }}
+                  </el-button>
+                </template>
+              </ZmTableColumn>
+            </ZmTable>
+          </template>
+        </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"
+          layout="total, sizes, prev, pager, next, jumper"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange" />
+      </div>
+    </div>
+  </div>
 
   <!-- 表单弹窗:添加/修改 -->
   <IotInformationDbForm ref="formRef" style="width: 60vw" @success="getList" />
@@ -142,7 +144,7 @@ import { defaultProps, handleTree } from '@/utils/tree'
 import * as ProductClassifyApi from '@/api/pms/productclassify'
 
 import { useTableComponents } from '@/components/ZmTable/useTableComponents'
-const { ZmTable, ZmTableColumn } = useTableComponents()
+const { ZmTable, ZmTableColumn } = useTableComponents<IotInformationDbVO>()
 
 /** 故障知识库 列表 */
 defineOptions({ name: 'IotInformationDb' })
@@ -190,10 +192,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 formRef = ref()
 const openForm = (type: string, id?: number) => {
@@ -236,3 +248,98 @@ onMounted(async () => {
   await getList()
 })
 </script>
+
+<style scoped>
+.information-query {
+  display: flex;
+  flex-wrap: wrap;
+  align-items: center;
+  justify-content: space-between;
+  gap: 12px 24px;
+}
+
+.query-row {
+  display: flex;
+  flex: 1 1 auto;
+  flex-wrap: wrap;
+  align-items: center;
+  gap: 12px 24px;
+  min-width: 0;
+}
+
+.query-row :deep(.el-form-item__label) {
+  white-space: nowrap;
+}
+
+.query-actions {
+  flex: 0 0 auto;
+}
+
+.query-actions :deep(.el-form-item__content) {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 8px;
+}
+
+.query-actions :deep(.el-button) {
+  margin-left: 0;
+}
+
+.query-control {
+  width: 200px;
+}
+
+.query-control--category {
+  width: 220px;
+}
+
+.query-control--date {
+  width: 240px;
+}
+
+:deep(.el-form-item) {
+  margin-bottom: 0;
+}
+
+@media (width <= 1200px) {
+  .information-page {
+    grid-template-rows: auto minmax(480px, 1fr);
+    height: auto;
+    min-height: calc(
+      100vh - 20px - var(--top-tool-height) - var(--tags-view-height) - var(--app-footer-height)
+    );
+  }
+
+  .query-actions {
+    width: 100%;
+  }
+}
+
+@media (width <= 768px) {
+  .information-query {
+    padding: 12px;
+  }
+
+  .query-row,
+  .query-row :deep(.el-form-item),
+  .query-actions {
+    width: 100%;
+  }
+
+  .query-control,
+  .query-control--category,
+  .query-control--date {
+    width: 100%;
+  }
+
+  .query-actions :deep(.el-form-item__content) {
+    display: grid;
+    grid-template-columns: repeat(2, minmax(0, 1fr));
+    width: 100%;
+  }
+
+  .query-actions :deep(.el-button) {
+    width: 100%;
+  }
+}
+</style>