yanghao 4 giorni fa
parent
commit
c0a487b7bb

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

@@ -234,6 +234,11 @@ export const IotFailureApi = {
   // 修改故障上报
   updateFailure: async (data) => {
     return await request.put({ url: `/rq/iot-accident-report/update`, data })
+  },
+
+  // 获取详情
+  getFailure: async (id) => {
+    return await request.get({ url: `/rq/iot-accident-report/get?id=` + id })
   }
 }
 

+ 971 - 0
src/views/pms/qhse/faultReport/QhseFaultReportForm.vue

@@ -0,0 +1,971 @@
+<template>
+  <div class="app-container">
+    <ContentWrap style="border: 0">
+      <el-form
+        class="-mb-15px"
+        :model="queryParams"
+        ref="queryForm"
+        :inline="true"
+        label-width="100px"
+      >
+        <el-row :gutter="20">
+          <el-col :span="24" :xs="24" :sm="12" :md="6">
+            <el-form-item label="事件级别" prop="accidentGrade">
+              <el-input
+                v-model="queryParams.accidentGrade"
+                placeholder="请输入事件级别"
+                clearable
+                @keyup.enter="handleQuery"
+                style="width: 200px"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24" :xs="24" :sm="12" :md="6">
+            <el-form-item label="事件类型" prop="accidentType">
+              <el-input
+                v-model="queryParams.accidentType"
+                placeholder="请选择事件类型"
+                clearable
+                style="width: 200px"
+              />
+            </el-form-item>
+          </el-col>
+          <el-col :span="24" :xs="24" :sm="24" :md="12">
+            <el-form-item style="display: block">
+              <el-button type="primary" @click="handleQuery" :icon="Search">搜索</el-button>
+              <el-button @click="resetQuery" :icon="Refresh">重置</el-button>
+              <el-button
+                type="primary"
+                @click="openForm('create')"
+                color="#626aef"
+                v-hasPermi="['rq:iot-accident-report:create']"
+              >
+                <Icon icon="ep:plus" class="mr-5px" /> 新增
+              </el-button>
+              <!-- <el-button type="success" plain @click="handleExport" :loading="exportLoading">
+                <Icon icon="ep:download" class="mr-5px" /> 导出
+              </el-button> -->
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+    </ContentWrap>
+
+    <ContentWrap style="border: 0">
+      <el-table
+        v-loading="loading"
+        :data="list"
+        row-key="id"
+        border
+        style="width: 100%"
+        :header-cell-style="{ background: '#f5f7fa', color: '#333', height: '50px' }"
+        :cell-style="{ padding: '12px 8px' }"
+        height="70vh"
+        :max-height="tableHeight"
+      >
+        <el-table-column prop="actualTime" label="事件时间" align="center" min-width="150">
+          <template #default="{ row }">
+            {{ formatDate(row.actualTime) }}
+          </template>
+        </el-table-column>
+        <el-table-column prop="accidentGrade" label="事件级别" align="center" />
+        <el-table-column prop="accidentType" label="事件类型" align="center" />
+        <el-table-column prop="accidentType" label="事件状态" align="center" width="100">
+          <template #default="scope">
+            <dict-tag :type="DICT_TYPE.ACCIDENT_REPORT_STATUS" :value="scope.row.status" />
+          </template>
+        </el-table-column>
+        <el-table-column
+          prop="lossSituation"
+          label="事件损失情况"
+          align="center"
+          show-overflow-tooltip
+          min-width="150"
+        />
+
+        <el-table-column
+          prop="accidentAddress"
+          label="事件地址"
+          align="center"
+          min-width="150"
+          show-overflow-tooltip
+        />
+        <el-table-column prop="deptName" label="部门名称" align="center" />
+        <el-table-column prop="dutyPerson" label="现场负责人" align="center" width="100" />
+        <el-table-column prop="actualTime" label="创建时间" align="center" min-width="150">
+          <template #default="{ row }">
+            {{ formatDate(row.createTime) }}
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作" align="center" width="150" fixed="right">
+          <template #default="{ row }">
+            <el-button
+              link
+              type="primary"
+              @click="openForm('detail', row)"
+              :icon="View"
+              v-hasPermi="['rq:iot-accident-report:query']"
+              >详情</el-button
+            >
+            <el-button link type="primary" @click="openApprovalDialog(row)"> 流转信息 </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
+
+      <!-- 分页 -->
+      <div class="mt-2 mb-2 float-right">
+        <el-pagination
+          v-model:current-page="queryParams.pageNo"
+          v-model:page-size="queryParams.pageSize"
+          :total="total"
+          layout="total, sizes, prev, pager, next"
+          @size-change="handleSizeChange"
+          @current-change="handleCurrentChange"
+          background
+        />
+      </div>
+    </ContentWrap>
+
+    <!-- 表单弹窗 -->
+    <el-dialog
+      :title="title"
+      v-model="open"
+      width="800px"
+      :fullscreen="isMobile"
+      append-to-body
+      :close-on-click-modal="false"
+      destroy-on-close
+      custom-class="self-dialog"
+    >
+      <template #title>
+        <div
+          style="
+            display: flex;
+            justify-content: space-between;
+            align-items: center;
+            border-bottom: 1px solid #ebeef5;
+            padding-bottom: 20px;
+            padding-left: 20px;
+            width: 108%;
+            margin-left: -15px;
+          "
+        >
+          <span>{{ title }}</span>
+        </div>
+      </template>
+      <el-form
+        ref="formRef"
+        :model="formData"
+        :rules="formRules"
+        label-width="120px"
+        :disabled="mode === 'detail'"
+      >
+        <el-row :gutter="20">
+          <el-col :span="24" :xs="24" :sm="24" :md="12">
+            <el-form-item label="事件时间" prop="actualTime">
+              <el-date-picker
+                v-model="formData.actualTime"
+                type="datetime"
+                format="YYYY-MM-DD HH:mm:ss"
+                value-format="x"
+                placeholder="选择事件发生时间"
+                style="width: 100%"
+                :disabled="mode === 'detail'"
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="24" :xs="24" :sm="24" :md="12">
+            <el-form-item label="事件级别" prop="accidentGrade">
+              <el-input
+                v-model="formData.accidentGrade"
+                placeholder="请输入事件级别"
+                clearable
+                :disabled="mode === 'detail'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24" :xs="24" :sm="24" :md="12">
+            <el-form-item label="事件类型" prop="accidentType">
+              <el-input
+                v-model="formData.accidentType"
+                placeholder="请选择事件类型"
+                clearable
+                style="width: 100%"
+                :disabled="mode === 'detail'"
+              />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="24" :xs="24" :sm="24" :md="12">
+            <el-form-item label="事件地址" prop="accidentAddress">
+              <el-input
+                v-model="formData.accidentAddress"
+                placeholder="请输入事件地址"
+                clearable
+                :disabled="mode === 'detail'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24" :xs="24" :sm="24" :md="12">
+            <el-form-item label="现场负责人" prop="dutyPerson">
+              <el-input
+                v-model="formData.dutyPerson"
+                placeholder="请输入现场负责人姓名"
+                clearable
+                :disabled="mode === 'detail'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="事件损失情况" prop="lossSituation">
+              <el-input
+                v-model="formData.lossSituation"
+                placeholder="请输入事件损失情况"
+                clearable
+                type="textarea"
+                :rows="2"
+                :disabled="mode === 'detail'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="现场采取措施" prop="emergencyMeasure">
+              <el-input
+                v-model="formData.emergencyMeasure"
+                type="textarea"
+                :rows="2"
+                placeholder="请输入现场采取的应急措施"
+                maxlength="500"
+                show-word-limit
+                :disabled="mode === 'detail'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <el-form-item label="事件简要经过" prop="description">
+              <el-input
+                v-model="formData.description"
+                type="textarea"
+                :rows="2"
+                placeholder="请输入事件详细经过"
+                maxlength="1000"
+                show-word-limit
+                :disabled="mode === 'detail'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <el-form-item label="附件/现场图片" prop="pic">
+              <UploadImage v-model="formData.pic" width="260px" :disabled="mode === 'detail'" />
+            </el-form-item>
+          </el-col>
+
+          <el-col :span="12">
+            <el-form-item label="备注" prop="remark">
+              <el-input
+                v-model="formData.remark"
+                type="textarea"
+                :rows="3"
+                placeholder="请输入备注信息"
+                maxlength="500"
+                show-word-limit
+                :disabled="mode === 'detail'"
+              />
+            </el-form-item>
+          </el-col>
+        </el-row>
+      </el-form>
+
+      <template #footer>
+        <div class="dialog-footer" v-if="mode !== 'detail'">
+          <el-button @click="cancel">取 消</el-button>
+          <el-button type="primary" @click="submitForm" :loading="submitLoading" color="#626aef">
+            确 定
+          </el-button>
+        </div>
+        <div class="dialog-footer" v-else>
+          <el-button @click="cancel">关 闭</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
+    <el-drawer
+      :title="approvalDialogTitle"
+      v-model="approvalDialogVisible"
+      direction="rtl"
+      size="650px"
+      :with-header="true"
+      :close-on-click-modal="false"
+      destroy-on-close
+    >
+      <template #header>
+        <div class="drawer-header">
+          <span>{{ approvalDialogTitle }}</span>
+        </div>
+      </template>
+
+      <!-- 专业化的审批流程时间线 -->
+      <div class="approval-process-container">
+        <el-timeline v-if="approvalProcessList.length > 0">
+          <el-timeline-item
+            v-for="(item, index) in approvalProcessList"
+            :key="index"
+            placement="top"
+            :color="getNodeStatusColor(item.status)"
+            :icon="Check"
+          >
+            <el-card
+              shadow="never"
+              class="approval-card"
+              :class="{ 'status-completed': item.status === '已完成' || item.status === '已批准' }"
+            >
+              <div class="card-header">
+                <div class="node-info">
+                  <el-tag :type="getTagTypeByStatus(item.status)" size="small" class="status-tag">
+                    {{ item.status }}
+                  </el-tag>
+                  <span class="node-name">{{ item.nodeName }}</span>
+                </div>
+                <div class="operator-time">
+                  <span class="time">{{ formatDate(item.createTime) }}</span>
+                </div>
+              </div>
+
+              <div class="card-content">
+                <div class="opinion-section">
+                  <span class="label">操作人:</span>
+                  <span class="opinion">{{ item.operator }}</span>
+                </div>
+
+                <div class="description-section mt-1" v-if="item.description">
+                  <span class="label">审批意见:</span>
+                  <span class="description">{{ item.description }}</span>
+                </div>
+                <div
+                  class="attachment-section"
+                  v-if="item.attachments && item.attachments.length > 0"
+                >
+                  <span class="label">附件:</span>
+                  <div class="attachments">
+                    <el-link
+                      v-for="(attachment, idx) in item.attachments"
+                      :key="idx"
+                      type="primary"
+                      :href="attachment.url"
+                      target="_blank"
+                      class="attachment-link"
+                    >
+                      {{ attachment.name }}
+                    </el-link>
+                  </div>
+                </div>
+              </div>
+            </el-card>
+          </el-timeline-item>
+        </el-timeline>
+
+        <!-- 无数据提示 -->
+        <div v-else class="no-data">
+          <el-empty description="暂无审批流程信息" :image-size="100" />
+        </div>
+      </div>
+    </el-drawer>
+  </div>
+</template>
+
+<script setup>
+import { ref, reactive, onMounted, computed } from 'vue'
+import { ElMessage, ElMessageBox } from 'element-plus'
+import { Search, Refresh, Plus, Edit, Delete, TopRight, View, Check } from '@element-plus/icons-vue'
+import { defaultProps } from '@/utils/tree'
+import { handleTree } from '@/utils/tree'
+import * as DeptApi from '@/api/system/dept'
+import { IotFailureApi } from '@/api/pms/qhse/index'
+import UploadImage from '@/components/UploadFile/src/UploadImg.vue'
+import { formatDate } from '@/utils/formatTime'
+import { DICT_TYPE, getStrDictOptions } from '@/utils/dict'
+import { IotApprovalApi } from '@/api/pms/qhse/index'
+
+// Data
+const loading = ref(false)
+const submitLoading = ref(false)
+const list = ref([])
+const total = ref(0)
+const open = ref(false)
+const title = ref('')
+const formRef = ref()
+const mode = ref('') // 'create' or 'update'
+const deptList2 = ref([]) // 树形结构
+
+// 表单数据
+const formData = ref({
+  actualTime: null, // 使用时间戳格式
+  accidentGrade: '',
+  accidentType: '',
+  accidentAddress: '',
+
+  dutyPerson: '',
+  lossSituation: '',
+  emergencyMeasure: '',
+  description: '',
+  pic: '',
+  remark: ''
+})
+
+// 查询参数
+const queryParams = ref({
+  pageNo: 1,
+  pageSize: 10,
+  actualTime: [],
+  accidentGrade: undefined,
+  accidentType: undefined
+})
+
+// 表单验证规则
+const formRules = reactive({
+  actualTime: [{ required: true, message: '请选择事件时间', trigger: 'change' }],
+  accidentGrade: [{ required: true, message: '请输入事件级别', trigger: 'blur' }],
+  accidentType: [{ required: true, message: '请选择事件类型', trigger: 'change' }],
+  accidentAddress: [{ required: true, message: '请输入事件地址', trigger: 'blur' }],
+  deptId: [{ required: true, message: '请选择所属部门', trigger: 'change' }],
+  deptName: [{ required: true, message: '请输入队伍名称', trigger: 'blur' }],
+  dutyPerson: [{ required: true, message: '请输入现场负责人', trigger: 'blur' }]
+  // description: [
+  //   { required: true, message: '请输入事件简要经过', trigger: 'blur' },
+  //   { min: 1, max: 1000, message: '事件简要经过长度应在1-1000之间', trigger: 'blur' }
+  // ]
+})
+
+// 计算属性
+const isMobile = computed(() => {
+  return window.innerWidth < 768
+})
+
+const tableHeight = computed(() => {
+  if (isMobile.value) {
+    return window.innerHeight - 300 // 为移动端减少高度,考虑其他元素占用空间
+  }
+  return '70vh'
+})
+
+// Methods
+const getList = async () => {
+  loading.value = true
+  try {
+    const response = await IotFailureApi.getFailureList(queryParams.value)
+    list.value = response.list
+    total.value = response.total
+    loading.value = false
+  } catch (error) {
+    loading.value = false
+    ElMessage.error('获取列表失败')
+  }
+}
+
+const handleSizeChange = (val) => {
+  queryParams.value.pageSize = val
+  queryParams.value.pageNo = 1 // 重置为第一页
+  getList()
+}
+
+const handleCurrentChange = (val) => {
+  queryParams.value.pageNo = val
+  getList()
+}
+
+const resetQuery = () => {
+  // 重置查询参数
+  queryParams.value = {
+    pageNo: 1,
+    pageSize: 10,
+    actualTime: [],
+    accidentGrade: undefined,
+    accidentType: undefined
+  }
+  handleQuery()
+}
+
+const handleQuery = () => {
+  queryParams.value.pageNo = 1
+  getList()
+}
+
+// 打开表单 - 支持新增、编辑和详情
+const openForm = async (modeVal, row = {}) => {
+  reset()
+  mode.value = modeVal
+
+  if (modeVal === 'create') {
+    title.value = '添加事故事件上报'
+    open.value = true
+  } else if (modeVal === 'update') {
+    title.value = '修改事故事件上报'
+
+    // 直接使用列表中的数据,不需要调用详情接口
+    formData.value = { ...row }
+
+    // 如果是字符串或日期格式的时间,转换为时间戳
+    if (typeof row.actualTime === 'string' && row.actualTime) {
+      formData.value.actualTime = new Date(row.actualTime).getTime()
+    } else if (row.actualTime instanceof Date) {
+      formData.value.actualTime = row.actualTime.getTime()
+    } else if (typeof row.actualTime === 'number') {
+      // 如果已经是时间戳格式,直接使用
+      formData.value.actualTime = row.actualTime
+    }
+
+    open.value = true
+  } else if (modeVal === 'detail') {
+    title.value = '事故事件上报详情'
+
+    // 直接使用列表中的数据
+    formData.value = { ...row }
+
+    // 如果是字符串或日期格式的时间,转换为时间戳
+    if (typeof row.actualTime === 'string' && row.actualTime) {
+      formData.value.actualTime = new Date(row.actualTime).getTime()
+    } else if (row.actualTime instanceof Date) {
+      formData.value.actualTime = row.actualTime.getTime()
+    } else if (typeof row.actualTime === 'number') {
+      // 如果已经是时间戳格式,直接使用
+      formData.value.actualTime = row.actualTime
+    }
+
+    open.value = true
+  }
+}
+
+const handleDelete = async (row) => {
+  const ids = [row.id]
+  await ElMessageBox.confirm('是否确认删除选中的故障报告数据项?', '警告', {
+    confirmButtonText: '确定',
+    cancelButtonText: '取消',
+    type: 'warning'
+  })
+
+  try {
+    // 删除API调用
+    await IotFailureApi.deleteFailure(ids)
+    ElMessage.success('删除成功')
+    getList()
+  } catch (error) {
+    ElMessage.error('删除失败')
+  }
+}
+
+const reset = () => {
+  formData.value = {
+    id: undefined,
+    actualTime: null,
+    accidentGrade: '',
+    accidentType: '',
+    accidentAddress: '',
+    // deptId: null,
+    // deptName: '',
+    dutyPerson: '',
+    lossSituation: '',
+    emergencyMeasure: '',
+    description: '',
+    pic: '',
+    remark: ''
+  }
+  if (formRef.value) {
+    formRef.value.resetFields()
+  }
+}
+
+const submitForm = async () => {
+  if (!formRef.value) return
+
+  await formRef.value.validate(async (valid) => {
+    if (valid) {
+      submitLoading.value = true
+      try {
+        // 在提交前确保时间是时间戳格式
+        const submitData = { ...formData.value }
+
+        if (mode.value === 'update' && formData.value.id) {
+          // 更新API调用
+          submitData.id = formData.value.id
+          await IotFailureApi.updateFailure(submitData)
+          ElMessage.success('更新成功')
+        } else {
+          // 创建API调用
+          await IotFailureApi.createFailure(submitData)
+          ElMessage.success('新增成功')
+        }
+        open.value = false
+        getList()
+      } catch (error) {
+        ElMessage.error(error.message || '操作失败')
+      } finally {
+        submitLoading.value = false
+      }
+    } else {
+      ElMessage.error('请填写必填项')
+    }
+  })
+}
+
+const downloadFile = (response) => {
+  // 创建 blob 对象
+  const blob = new Blob([response], {
+    type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet;charset=utf-8'
+  })
+
+  // 获取文件名
+  let fileName = '事故事件上报.xlsx'
+  const disposition = response.headers ? response.headers['content-disposition'] : ''
+  if (disposition) {
+    const filenameRegex = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/
+    const matches = filenameRegex.exec(disposition)
+    if (matches != null && matches[1]) {
+      fileName = matches[1].replace(/['"]/g, '')
+    }
+  }
+
+  // 创建下载链接
+  const url = window.URL.createObjectURL(blob)
+  const link = document.createElement('a')
+  link.href = url
+  link.setAttribute('download', fileName)
+
+  // 触发下载
+  document.body.appendChild(link)
+  link.click()
+
+  // 清理
+  document.body.removeChild(link)
+  window.URL.revokeObjectURL(url)
+}
+
+let exportLoading = ref(false)
+const handleExport = async () => {
+  try {
+    exportLoading.value = true
+    // 调用导出接口
+    const response = await IotFailureApi.exportFailure(queryParams.value)
+
+    // 下载文件
+    downloadFile(response)
+    exportLoading.value = false
+  } catch (error) {
+    ElMessage.error('导出失败,请重试')
+    console.error('导出错误:', error)
+  } finally {
+    exportLoading.value = false
+  }
+}
+
+const cancel = () => {
+  open.value = false
+  reset()
+}
+
+const approvalDialogVisible = ref(false)
+const approvalDialogTitle = ref('审批流程')
+const approvalProcessList = ref([]) // 存储审批流程信息
+const approvalSuggestion = ref('')
+// 根据状态返回标签类型
+const getTagTypeByStatus = (status) => {
+  const tagTypeMap = {
+    待处理: 'warning',
+    处理中: 'info',
+    已完成: 'success',
+    已驳回: 'danger',
+    已撤销: 'info',
+    已批准: 'success',
+    审批中: 'primary',
+    上报: 'success',
+    提交上报: 'primary'
+  }
+  return tagTypeMap[status] || 'info'
+}
+const getNodeStatusColor = (status) => {
+  const colorMap = {
+    待处理: '#E6A23C',
+    处理中: '#409EFF',
+    已完成: '#67C23A',
+    已驳回: '#F56C6C',
+    已撤销: '#909399',
+    已批准: '#67C23A',
+    审批中: '#409EFF',
+    上报: '#229242',
+    提交上报: '#409EFF'
+  }
+  return colorMap[status] || '#90939'
+}
+
+const openApprovalDialog = async (row) => {
+  approvalDialogVisible.value = true
+  approvalDialogTitle.value = `审批流程详情`
+  approvalSuggestion.value = '' // 清空审批建议
+
+  try {
+    const response = await IotApprovalApi.getApprovalProcess(row.id)
+    const processList = response.list || []
+
+    // 按 createTime 升序排序(时间正序)
+    approvalProcessList.value = processList.sort((a, b) => a.createTime - b.createTime)
+
+    // 如果没有数据,添加一条默认的“发起人”记录
+    if (approvalProcessList.value.length === 0) {
+      approvalProcessList.value.push({
+        createTime: Date.now(),
+        operator: '系统管理员',
+        status: '提交上报',
+        description: '发起事故事件上报申请',
+        nodeName: '上报发起',
+        opinion: ''
+      })
+    }
+  } catch (error) {
+    ElMessage.error('获取审批流程失败')
+    // 即使出错也显示一个基本记录
+    approvalProcessList.value = [
+      {
+        createTime: Date.now(),
+        operator: '系统',
+        status: '错误',
+        description: '无法获取审批流程信息',
+        nodeName: '系统通知',
+        opinion: ''
+      }
+    ]
+  }
+}
+
+// 监听窗口大小变化
+const handleResize = () => {
+  // 这里可以添加响应式逻辑
+}
+
+// Lifecycle hooks
+onMounted(async () => {
+  window.addEventListener('resize', handleResize)
+  getList()
+  deptList2.value = handleTree(await DeptApi.getSimpleDeptList())
+})
+
+onUnmounted(() => {
+  window.removeEventListener('resize', handleResize)
+})
+</script>
+
+<style scoped lang="scss">
+.app-container {
+  padding: 15px 10px;
+}
+
+/* 移动端适配 */
+@media (max-width: 768px) {
+  .app-container {
+    padding: 10px 5px;
+  }
+
+  .el-form-item__label {
+    display: block;
+    text-align: left;
+    padding-bottom: 5px;
+  }
+
+  .el-form-item__content {
+    margin-left: 0 !important;
+  }
+
+  .el-button + .el-button {
+    margin-left: 5px;
+  }
+
+  /* 调整表格字体大小 */
+  :deep(.el-table .el-table__cell) {
+    padding: 8px 0;
+  }
+
+  :deep(.el-table th.el-table__cell) {
+    padding: 10px 0;
+  }
+}
+
+/* 通用样式 */
+.dialog-footer {
+  text-align: right;
+}
+
+:deep(.el-textarea__inner) {
+  min-height: 80px !important;
+}
+
+/* 移动端分页样式 */
+.mt-2.flex.justify-right {
+  display: flex;
+  justify-content: center;
+  margin-top: 10px;
+}
+
+/* 移动端按钮样式 */
+@media (max-width: 768px) {
+  .el-button {
+    margin-bottom: 5px;
+  }
+}
+
+::v-deep .el-button {
+  border-radius: 3px;
+}
+
+.drawer-header {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  border-bottom: 1px solid #ebeef5;
+  padding-bottom: 20px;
+  padding-left: 20px;
+  width: 108%;
+  margin-left: -15px;
+}
+
+.approval-process-container {
+  padding: 10px 15px;
+
+  .no-data {
+    text-align: center;
+    padding: 40px 0;
+  }
+
+  .approval-card {
+    border: 1px solid #ebeef5;
+    border-radius: 6px;
+    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
+    transition: all 0.3s ease;
+
+    &.status-completed {
+      border-left: 4px solid #67c23a;
+    }
+
+    &:hover {
+      box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
+    }
+
+    .card-header {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      padding-bottom: 12px;
+      border-bottom: 1px dashed #ebeef5;
+
+      .node-info {
+        display: flex;
+        align-items: center;
+        gap: 10px;
+
+        .status-tag {
+          font-weight: bold;
+        }
+
+        .node-name {
+          font-weight: 600;
+          color: #303133;
+        }
+      }
+
+      .operator-time {
+        .time {
+          color: #909399;
+          font-size: 13px;
+        }
+      }
+    }
+
+    .card-content {
+      padding: 12px 0;
+
+      .label {
+        font-weight: 600;
+        color: #606266;
+        display: inline-block;
+        width: 70px;
+        vertical-align: top;
+      }
+
+      .opinion,
+      .description {
+        color: #303133;
+        line-height: 1.6;
+      }
+
+      .attachment-section {
+        margin-top: 8px;
+
+        .attachments {
+          display: flex;
+          flex-direction: column;
+          gap: 5px;
+          margin-top: 5px;
+
+          .attachment-link {
+            display: inline-flex;
+            align-items: center;
+            padding-left: 70px;
+            text-decoration: none;
+
+            &:hover {
+              text-decoration: underline;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+@media (max-width: 768px) {
+  .approval-process-container {
+    padding: 5px;
+
+    .approval-card {
+      .card-header {
+        flex-direction: column;
+        gap: 8px;
+        align-items: flex-start;
+
+        .node-info {
+          flex-wrap: wrap;
+        }
+      }
+
+      .card-content {
+        .label {
+          width: auto;
+          display: block;
+          margin-bottom: 4px;
+        }
+
+        .attachment-section .attachment-link {
+          padding-left: 0;
+        }
+      }
+    }
+  }
+}
+</style>

+ 178 - 0
src/views/pms/qhse/faultReport/QhseFaultReportInfo.vue

@@ -0,0 +1,178 @@
+<template>
+  <el-form ref="formRef" :model="formData" label-width="120px">
+    <el-row :gutter="20">
+      <el-col :span="24" :xs="24" :sm="24" :md="12">
+        <el-form-item label="事件时间" prop="actualTime">
+          <el-date-picker
+            v-model="formData.actualTime"
+            type="datetime"
+            format="YYYY-MM-DD HH:mm:ss"
+            value-format="x"
+            placeholder="选择事件发生时间"
+            style="width: 100%"
+            :disabled="true"
+          />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="24" :xs="24" :sm="24" :md="12">
+        <el-form-item label="事件级别" prop="accidentGrade">
+          <el-input
+            v-model="formData.accidentGrade"
+            placeholder="请输入事件级别"
+            clearable
+            :disabled="true"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="24" :xs="24" :sm="24" :md="12">
+        <el-form-item label="事件类型" prop="accidentType">
+          <el-input
+            v-model="formData.accidentType"
+            placeholder="请选择事件类型"
+            clearable
+            style="width: 100%"
+            :disabled="true"
+          />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="24" :xs="24" :sm="24" :md="12">
+        <el-form-item label="事件地址" prop="accidentAddress">
+          <el-input
+            v-model="formData.accidentAddress"
+            placeholder="请输入事件地址"
+            clearable
+            :disabled="true"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="24" :xs="24" :sm="24" :md="12">
+        <el-form-item label="现场负责人" prop="dutyPerson">
+          <el-input
+            v-model="formData.dutyPerson"
+            placeholder="请输入现场负责人姓名"
+            clearable
+            :disabled="true"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="24">
+        <el-form-item label="事件损失情况" prop="lossSituation">
+          <el-input
+            v-model="formData.lossSituation"
+            placeholder="请输入事件损失情况"
+            clearable
+            type="textarea"
+            :rows="2"
+            :disabled="true"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="24">
+        <el-form-item label="现场采取措施" prop="emergencyMeasure">
+          <el-input
+            v-model="formData.emergencyMeasure"
+            type="textarea"
+            :rows="2"
+            placeholder="请输入现场采取的应急措施"
+            maxlength="500"
+            show-word-limit
+            :disabled="true"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="24">
+        <el-form-item label="事件简要经过" prop="description">
+          <el-input
+            v-model="formData.description"
+            type="textarea"
+            :rows="2"
+            placeholder="请输入事件详细经过"
+            maxlength="1000"
+            show-word-limit
+            :disabled="true"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+
+    <el-row :gutter="20">
+      <el-col :span="12">
+        <el-form-item label="附件/现场图片" prop="pic">
+          <UploadImage v-model="formData.pic" width="260px" :disabled="true" />
+        </el-form-item>
+      </el-col>
+
+      <el-col :span="12">
+        <el-form-item label="备注" prop="remark">
+          <el-input
+            v-model="formData.remark"
+            type="textarea"
+            :rows="3"
+            placeholder="请输入备注信息"
+            maxlength="500"
+            show-word-limit
+            :disabled="true"
+          />
+        </el-form-item>
+      </el-col>
+    </el-row>
+  </el-form>
+</template>
+
+<script setup lang="ts">
+import { IotFailureApi } from '@/api/pms/qhse/index'
+/** 故障上报 表单 */
+defineOptions({ name: 'QhseFaultReportInfo' })
+
+const message = useMessage() // 消息弹窗
+const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
+
+const { params } = useRoute() // 查询参数
+const props = defineProps<{ id?: number }>()
+const formData = ref({
+  actualTime: null, // 使用时间戳格式
+  accidentGrade: '',
+  accidentType: '',
+  accidentAddress: '',
+  dutyPerson: '',
+  lossSituation: '',
+  emergencyMeasure: '',
+  description: '',
+  pic: '',
+  remark: ''
+})
+onMounted(async () => {
+  const id = props.id || params.id
+  if (!id) {
+    message.warning('参数错误,事故事件上报不能为空!')
+    close()
+    return
+  }
+  await getFailureData(id)
+})
+const getFailureData = async (id) => {
+  formLoading.value = true
+  try {
+    formData.value = await IotFailureApi.getFailure(id)
+  } finally {
+    formLoading.value = false
+  }
+}
+</script>

+ 0 - 24
src/views/pms/qhse/faultReport/index.vue

@@ -100,14 +100,6 @@
 
         <el-table-column label="操作" align="center" width="150" fixed="right">
           <template #default="{ row }">
-            <!-- <el-button
-              link
-              type="primary"
-              @click="openForm('update', row)"
-              :icon="TopRight"
-              v-hasPermi="['rq:iot-accident-report:approval']"
-              >上报审批</el-button
-            > -->
             <el-button
               link
               type="primary"
@@ -117,22 +109,6 @@
               >详情</el-button
             >
             <el-button link type="primary" @click="openApprovalDialog(row)"> 流转信息 </el-button>
-            <!-- <el-button
-              link
-              type="primary"
-              @click="openForm('update', row)"
-              :icon="Edit"
-              v-hasPermi="['rq:iot-accident-report:update']"
-              >编辑</el-button
-            > -->
-            <!-- <el-button
-              link
-              type="danger"
-              @click="handleDelete(row)"
-              :icon="Delete"
-              v-hasPermi="['rq:iot-accident-report:delete']"
-              >删除</el-button
-            > -->
           </template>
         </el-table-column>
       </el-table>