Browse Source

安全隐患和作业上报页面修改

starrymoonlight 2 tuần trước cách đây
mục cha
commit
6806af5eb6

+ 13 - 12
src/api/riskctrl/hazard/index.ts

@@ -1,11 +1,11 @@
 import request from '@/utils/request';
-import { AxiosPromise } from 'axios';
-import { HazardForm, HazardQuery, HazardVO, HazardStats, HazardTrend, HazardDistribution } from './types';
+import { HazardForm, HazardQuery, HazardVO, HazardTrend, HazardDistribution, HazardStatsVo } from './types';
+import { TableDataInfo } from '@/api/riskctrl/types/global';
 
 /**
  * 查询安全隐患列表
  */
-export const listHazard = (query: HazardQuery): AxiosPromise<HazardVO[]> => {
+export const listHazard = (query: HazardQuery): Promise<TableDataInfo<HazardVO>> => {
   return request({
     url: '/riskctrl/hazard/list',
     method: 'get',
@@ -16,7 +16,7 @@ export const listHazard = (query: HazardQuery): AxiosPromise<HazardVO[]> => {
 /**
  * 获取安全隐患详情
  */
-export const getHazard = (hazardId: string | number): AxiosPromise<HazardVO> => {
+export const getHazard = (hazardId: string | number): Promise<HazardVO> => {
   return request({
     url: '/riskctrl/hazard/' + hazardId,
     method: 'get'
@@ -30,7 +30,7 @@ export const addHazard = (data: HazardForm) => {
   return request({
     url: '/riskctrl/hazard',
     method: 'post',
-    data: data
+    data
   });
 };
 
@@ -41,7 +41,7 @@ export const updateHazard = (data: HazardForm) => {
   return request({
     url: '/riskctrl/hazard',
     method: 'put',
-    data: data
+    data
   });
 };
 
@@ -52,7 +52,7 @@ export const processHazard = (data: HazardForm) => {
   return request({
     url: '/riskctrl/hazard/process',
     method: 'put',
-    data: data
+    data
   });
 };
 
@@ -69,17 +69,18 @@ export const delHazard = (hazardId: Array<string | number> | string | number) =>
 /**
  * 隐患统计
  */
-export const getHazardStats = (): AxiosPromise<HazardStats> => {
+export const getHazardStats = (query?: HazardQuery): Promise<HazardStatsVo> => {
   return request({
     url: '/riskctrl/hazard/stats',
-    method: 'get'
+    method: 'get',
+    params: query
   });
 };
 
 /**
  * 隐患趋势
  */
-export const getHazardTrend = (months: number = 6): AxiosPromise<HazardTrend[]> => {
+export const getHazardTrend = (months: number = 6): Promise<HazardTrend[]> => {
   return request({
     url: '/riskctrl/hazard/trend',
     method: 'get',
@@ -90,7 +91,7 @@ export const getHazardTrend = (months: number = 6): AxiosPromise<HazardTrend[]>
 /**
  * 隐患分布
  */
-export const getHazardDistribution = (): AxiosPromise<HazardDistribution[]> => {
+export const getHazardDistribution = (): Promise<HazardDistribution[]> => {
   return request({
     url: '/riskctrl/hazard/distribution',
     method: 'get'
@@ -100,7 +101,7 @@ export const getHazardDistribution = (): AxiosPromise<HazardDistribution[]> => {
 /**
  * 最新隐患
  */
-export const getHazardLatest = (limit: number = 10): AxiosPromise<HazardVO[]> => {
+export const getHazardLatest = (limit: number = 10): Promise<HazardVO[]> => {
   return request({
     url: '/riskctrl/hazard/latest',
     method: 'get',

+ 10 - 0
src/api/riskctrl/hazard/types.ts

@@ -8,6 +8,7 @@ export interface HazardQuery extends PageQuery {
   riskLevel?: string;
   processStatus?: string;
   status?: string;
+  keyword?: string;
 }
 
 /**
@@ -31,6 +32,15 @@ export interface HazardVO extends BaseEntity {
   remark: string;
 }
 
+/**
+ * 隐患统计
+ */
+export interface HazardStatsVo {
+  totalCount: number;
+  unprocessedCount: number;
+  processedCount: number;
+}
+
 /**
  * 安全隐患表单类型
  */

+ 6 - 0
src/api/riskctrl/types/global.d.ts

@@ -0,0 +1,6 @@
+export interface TableDataInfo<T> {
+  code: number;
+  msg: string;
+  rows: T[];
+  total: number;
+}

+ 709 - 219
src/views/riskctrl/hazard/index.vue

@@ -1,142 +1,142 @@
 <template>
-  <div class="p-2">
-    <!-- 顶部统计卡片 -->
-    <el-row :gutter="16" class="mb-[10px]">
-      <el-col :span="6">
-        <el-card shadow="hover">
-          <div class="flex items-center justify-between">
+  <div class="hazard-page">
+    <div class="page-header">
+      <div class="page-title">安全隐患管理</div>
+      <div class="page-desc">管理各类型安全隐患信息,及时进行风险排查与处理</div>
+    </div>
+
+    <!-- 顶部看板 -->
+    <el-row :gutter="16" class="stat-row">
+      <el-col :span="8">
+        <el-card shadow="hover" class="stat-card stat-card-total">
+          <div class="stat-inner">
             <div>
-              <p class="text-gray-500 text-sm">隐患总数</p>
-              <p class="text-2xl font-bold text-blue-500">{{ statsData.totalCount }}</p>
+              <div class="stat-label">总隐患数量</div>
+              <div class="stat-value">{{ statsData.totalCount }}</div>
             </div>
-            <el-icon :size="40" color="#409EFF"><WarningFilled /></el-icon>
-          </div>
-        </el-card>
-      </el-col>
-      <el-col :span="6">
-        <el-card shadow="hover">
-          <div class="flex items-center justify-between">
-            <div>
-              <p class="text-gray-500 text-sm">待处理</p>
-              <p class="text-2xl font-bold text-orange-500">{{ statsData.pendingCount }}</p>
+            <div class="stat-icon stat-icon-blue">
+              <el-icon><WarningFilled /></el-icon>
             </div>
-            <el-icon :size="40" color="#E6A23C"><Clock /></el-icon>
           </div>
         </el-card>
       </el-col>
-      <el-col :span="6">
-        <el-card shadow="hover">
-          <div class="flex items-center justify-between">
+
+      <el-col :span="8">
+        <el-card shadow="hover" class="stat-card stat-card-pending">
+          <div class="stat-inner">
             <div>
-              <p class="text-gray-500 text-sm">处理中</p>
-              <p class="text-2xl font-bold text-yellow-500">{{ statsData.processingCount }}</p>
+              <div class="stat-label">未处理隐患</div>
+              <div class="stat-value stat-value-red">{{ statsData.unprocessedCount }}</div>
+            </div>
+            <div class="stat-icon stat-icon-red">
+              <el-icon><CircleCloseFilled /></el-icon>
             </div>
-            <el-icon :size="40" color="#F2C811"><Loading /></el-icon>
           </div>
         </el-card>
       </el-col>
-      <el-col :span="6">
-        <el-card shadow="hover">
-          <div class="flex items-center justify-between">
+
+      <el-col :span="8">
+        <el-card shadow="hover" class="stat-card stat-card-done">
+          <div class="stat-inner">
             <div>
-              <p class="text-gray-500 text-sm">已完成</p>
-              <p class="text-2xl font-bold text-green-500">{{ statsData.completedCount }}</p>
+              <div class="stat-label">已处理隐患</div>
+              <div class="stat-value stat-value-green">{{ statsData.processedCount }}</div>
+            </div>
+            <div class="stat-icon stat-icon-green">
+              <el-icon><CircleCheckFilled /></el-icon>
             </div>
-            <el-icon :size="40" color="#67C23A"><CircleCheckFilled /></el-icon>
           </div>
         </el-card>
       </el-col>
     </el-row>
 
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div v-show="showSearch" class="mb-[10px]">
-        <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
-            <el-form-item label="编号" prop="hazardNo">
-              <el-input v-model="queryParams.hazardNo" placeholder="请输入编号" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
-            <el-form-item label="隐患名称" prop="hazardName">
-              <el-input v-model="queryParams.hazardName" placeholder="请输入名称" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
+    <!-- 筛选区 -->
+    <el-card shadow="hover" class="search-card">
+      <el-form ref="queryFormRef" :model="queryParams" :inline="false" class="search-form">
+        <el-row :gutter="12">
+          <el-col :xs="24" :sm="12" :md="6">
             <el-form-item label="隐患类型" prop="hazardType">
-              <el-select v-model="queryParams.hazardType" placeholder="类型" clearable>
+              <el-select v-model="queryParams.hazardType" placeholder="全部类型" clearable class="w-full">
                 <el-option label="门店安全隐患" value="门店安全隐患" />
                 <el-option label="物流安全隐患" value="物流安全隐患" />
                 <el-option label="市场安全隐患" value="市场安全隐患" />
               </el-select>
             </el-form-item>
-            <el-form-item label="风险等级" prop="riskLevel">
-              <el-select v-model="queryParams.riskLevel" placeholder="等级" clearable>
-                <el-option label="高风险" value="高风险" />
-                <el-option label="中风险" value="中风险" />
-                <el-option label="低风险" value="低风险" />
-              </el-select>
-            </el-form-item>
-            <el-form-item label="状态" prop="processStatus">
-              <el-select v-model="queryParams.processStatus" placeholder="状态" clearable>
+          </el-col>
+
+          <el-col :xs="24" :sm="12" :md="6">
+            <el-form-item label="处理状态" prop="processStatus">
+              <el-select v-model="queryParams.processStatus" placeholder="全部状态" clearable class="w-full">
                 <el-option label="待处理" value="待处理" />
                 <el-option label="处理中" value="处理中" />
                 <el-option label="已处理" value="已处理" />
               </el-select>
             </el-form-item>
-            <el-form-item>
-              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
-              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
-            </el-form-item>
-          </el-form>
-        </el-card>
-      </div>
-    </transition>
-
-    <el-card shadow="hover">
-      <template #header>
-        <el-row :gutter="10">
-          <el-col :span="1.5">
-            <el-button v-has-permi="['riskctrl:hazard:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增</el-button>
           </el-col>
-          <el-col :span="1.5">
-            <el-button v-has-permi="['riskctrl:hazard:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">修改</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button v-has-permi="['riskctrl:hazard:remove']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">删除</el-button>
+
+          <el-col :xs="24" :sm="12" :md="6">
+            <el-form-item label="风险等级" prop="riskLevel">
+              <el-select v-model="queryParams.riskLevel" placeholder="全部等级" clearable class="w-full">
+                <el-option label="高风险" value="高风险" />
+                <el-option label="中风险" value="中风险" />
+                <el-option label="低风险" value="低风险" />
+              </el-select>
+            </el-form-item>
           </el-col>
-          <el-col :span="1.5">
-            <el-button v-has-permi="['riskctrl:hazard:process']" type="warning" plain :disabled="single" icon="Operation" @click="handleProcess()">处理</el-button>
+
+          <el-col :xs="24" :sm="12" :md="6">
+            <el-form-item label="关键词搜索" prop="keyword">
+              <el-input v-model="queryParams.keyword" placeholder="搜索隐患信息..." clearable :prefix-icon="Search" @keyup.enter="handleQuery" />
+            </el-form-item>
           </el-col>
-          <right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
         </el-row>
-      </template>
 
+        <div class="search-actions">
+          <el-button @click="resetQuery">重置</el-button>
+          <el-button type="primary" @click="handleQuery">搜索</el-button>
+          <el-button type="success" @click="handleAdd">
+            <el-icon style="margin-right: 4px"><Plus /></el-icon>
+            新增隐患
+          </el-button>
+        </div>
+      </el-form>
+    </el-card>
+
+    <!-- 列表 -->
+    <el-card shadow="hover" class="table-card">
       <el-table v-loading="loading" border :data="hazardList" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="50" align="center" />
-        <el-table-column v-if="columns[0].visible" key="hazardNo" label="编号" align="center" prop="hazardNo" width="140" />
-        <el-table-column v-if="columns[1].visible" key="hazardName" label="隐患名称" align="center" prop="hazardName" :show-overflow-tooltip="true" />
-        <el-table-column v-if="columns[2].visible" key="hazardType" label="类型" align="center" prop="hazardType" width="100" />
-        <el-table-column v-if="columns[3].visible" key="riskLevel" label="风险等级" align="center" prop="riskLevel" width="100">
+        <el-table-column label="隐患编号" prop="hazardNo" width="160" align="center" />
+        <el-table-column label="隐患名称" prop="hazardName" min-width="180" align="center" :show-overflow-tooltip="true" />
+        <el-table-column label="隐患类型" prop="hazardType" width="140" align="center" />
+        <el-table-column label="风险等级" prop="riskLevel" width="120" align="center">
           <template #default="scope">
-            <el-tag :type="getLevelType(scope.row.riskLevel)">{{ scope.row.riskLevel }}</el-tag>
+            <span class="tag-pill" :class="getRiskClass(scope.row.riskLevel)">
+              {{ scope.row.riskLevel }}
+            </span>
           </template>
         </el-table-column>
-        <el-table-column v-if="columns[4].visible" key="hazardLocation" label="位置" align="center" prop="hazardLocation" :show-overflow-tooltip="true" />
-        <el-table-column v-if="columns[5].visible" key="discoverTime" label="发现时间" align="center" prop="discoverTime" width="160" />
-        <el-table-column v-if="columns[6].visible" key="processStatus" label="处理状态" align="center" prop="processStatus" width="100">
+        <el-table-column label="发现时间" prop="discoverTime" width="170" align="center" />
+        <el-table-column label="处理状态" prop="processStatus" width="120" align="center">
           <template #default="scope">
-            <el-tag :type="getStatusType(scope.row.processStatus)">{{ scope.row.processStatus }}</el-tag>
+            <span class="tag-pill" :class="getStatusClass(scope.row.processStatus)">
+              {{ scope.row.processStatus || '待处理' }}
+            </span>
           </template>
         </el-table-column>
-        <el-table-column v-if="columns[7].visible" key="processUser" label="处理人" align="center" prop="processUser" width="100" />
-        <el-table-column v-if="columns[8].visible" label="创建时间" align="center" prop="createTime" width="160" />
-        <el-table-column label="操作" fixed="right" width="220" class-name="small-padding fixed-width">
+        <el-table-column label="操作" fixed="right" width="210" align="center">
           <template #default="scope">
-            <el-tooltip content="修改" placement="top">
-              <el-button v-has-permi="['riskctrl:hazard:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
-            </el-tooltip>
-            <el-tooltip content="处理" placement="top">
-              <el-button v-has-permi="['riskctrl:hazard:process']" link type="warning" icon="Operation" @click="handleProcess(scope.row)"></el-button>
-            </el-tooltip>
-            <el-tooltip content="删除" placement="top">
-              <el-button v-has-permi="['riskctrl:hazard:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
-            </el-tooltip>
+            <el-button link type="primary" @click="handleView(scope.row)">查看</el-button>
+            <el-button
+              v-if="scope.row.processStatus !== '已处理'"
+              v-has-permi="['riskctrl:hazard:process']"
+              link
+              type="success"
+              @click="handleProcess(scope.row)"
+            >
+              处理
+            </el-button>
+            <el-button v-has-permi="['riskctrl:hazard:remove']" link type="danger" @click="handleDelete(scope.row)">删除</el-button>
           </template>
         </el-table-column>
       </el-table>
@@ -144,69 +144,136 @@
       <pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
     </el-card>
 
-    <!-- 新增/修改弹窗 -->
-    <el-dialog v-model="open" :title="title" width="750px" append-to-body>
-      <el-form ref="hazardFormRef" :model="form" :rules="rules" label-width="100px">
-        <el-row :gutter="20">
+    <!-- 新增/查看/修改弹窗 -->
+    <el-dialog v-model="open" :title="dialogTitle" width="760px" append-to-body destroy-on-close class="hazard-dialog">
+      <el-form ref="hazardFormRef" :model="form" :rules="rules" label-width="92px" class="hazard-form">
+        <el-row :gutter="18">
           <el-col :span="12">
             <el-form-item label="隐患名称" prop="hazardName">
-              <el-input v-model="form.hazardName" placeholder="请输入隐患名称" />
+              <template v-if="isViewMode">
+                <div class="view-text">{{ form.hazardName || '-' }}</div>
+              </template>
+              <el-input v-else v-model="form.hazardName" placeholder="请输入隐患名称" />
             </el-form-item>
           </el-col>
+
           <el-col :span="12">
-            <el-form-item label="隐患类型" prop="hazardType">
-              <el-select v-model="form.hazardType" placeholder="请选择类型" class="w-full">
-                <el-option label="门店安全隐患" value="门店安全隐患" />
-                <el-option label="物流安全隐患" value="物流安全隐患" />
-                <el-option label="市场安全隐患" value="市场安全隐患" />
-              </el-select>
+            <el-form-item label="隐患编号" prop="hazardNo">
+              <template v-if="isViewMode">
+                <div class="view-text">{{ form.hazardNo || '-' }}</div>
+              </template>
+              <el-input v-else v-model="form.hazardNo" placeholder="系统自动生成" disabled />
             </el-form-item>
           </el-col>
         </el-row>
-        <el-row :gutter="20">
+
+        <el-form-item label="隐患类型" prop="hazardType">
+          <template v-if="isViewMode">
+            <div class="view-text">{{ form.hazardType || '-' }}</div>
+          </template>
+          <el-select v-else v-model="form.hazardType" placeholder="请选择隐患类型" class="w-full">
+            <el-option label="门店安全隐患" value="门店安全隐患" />
+            <el-option label="物流安全隐患" value="物流安全隐患" />
+            <el-option label="市场安全隐患" value="市场安全隐患" />
+          </el-select>
+        </el-form-item>
+
+        <el-row :gutter="18">
           <el-col :span="12">
             <el-form-item label="风险等级" prop="riskLevel">
-              <el-select v-model="form.riskLevel" placeholder="请选择风险等级" class="w-full">
+              <template v-if="isViewMode">
+                <span class="tag-pill" :class="getRiskClass(form.riskLevel)">
+                  {{ form.riskLevel || '-' }}
+                </span>
+              </template>
+              <el-select v-else v-model="form.riskLevel" placeholder="请选择风险等级" class="w-full">
                 <el-option label="高风险" value="高风险" />
                 <el-option label="中风险" value="中风险" />
                 <el-option label="低风险" value="低风险" />
               </el-select>
             </el-form-item>
           </el-col>
+
           <el-col :span="12">
             <el-form-item label="发现时间" prop="discoverTime">
-              <el-date-picker v-model="form.discoverTime" type="datetime" placeholder="选择时间" value-format="YYYY-MM-DD HH:mm:ss" class="w-full" />
+              <template v-if="isViewMode">
+                <div class="view-text">{{ form.discoverTime || '-' }}</div>
+              </template>
+              <el-date-picker
+                v-else
+                v-model="form.discoverTime"
+                type="datetime"
+                placeholder="年/月/日"
+                value-format="YYYY-MM-DD HH:mm:ss"
+                :disabled-date="disabledDiscoverDate"
+                class="w-full"
+              />
             </el-form-item>
           </el-col>
         </el-row>
-        <el-form-item label="位置" prop="hazardLocation">
-          <el-input v-model="form.hazardLocation" placeholder="请输入隐患位置" />
+
+        <el-form-item label="隐患位置" prop="hazardLocation">
+          <template v-if="isViewMode">
+            <div class="view-text">{{ form.hazardLocation || '-' }}</div>
+          </template>
+          <el-input v-else v-model="form.hazardLocation" placeholder="请输入隐患具体位置" />
         </el-form-item>
+
         <el-form-item label="隐患描述" prop="hazardDesc">
-          <el-input v-model="form.hazardDesc" type="textarea" :rows="3" placeholder="请输入隐患描述" />
-        </el-form-item>
-        <el-form-item label="状态" prop="status">
-          <el-radio-group v-model="form.status">
-            <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">{{ dict.label }}</el-radio>
-          </el-radio-group>
+          <template v-if="isViewMode">
+            <div class="view-text view-text-multiline">{{ form.hazardDesc || '-' }}</div>
+          </template>
+          <el-input v-else v-model="form.hazardDesc" type="textarea" :rows="4" placeholder="请详细描述隐患情况..." />
         </el-form-item>
-        <el-form-item label="备注" prop="remark">
-          <el-input v-model="form.remark" type="textarea" placeholder="请输入备注" />
+
+        <el-form-item label="上传照片" prop="photoList">
+          <template v-if="isViewMode">
+            <div v-if="photoViewList.length" class="photo-view-list">
+              <div v-for="(item, index) in photoViewList" :key="index" class="photo-view-item">
+                <el-image :src="item" fit="cover" class="photo-thumb" :preview-src-list="photoViewList" preview-teleported />
+<!--                <div class="photo-path">{{ item }}</div>-->
+              </div>
+            </div>
+            <div v-else class="view-text">-</div>
+          </template>
+
+          <el-upload
+            v-else
+            v-model:file-list="photoFileList"
+            :action="uploadUrl"
+            :headers="uploadHeaders"
+            list-type="picture-card"
+            :auto-upload="true"
+            accept=".jpg,.jpeg,.png"
+            :limit="5"
+            :on-success="handlePhotoSuccess"
+            :on-remove="handlePhotoRemove"
+            :on-exceed="handlePhotoExceed"
+            :before-upload="beforePhotoUpload"
+            class="upload-wrap"
+          >
+            <el-icon><Plus /></el-icon>
+            <template #tip>
+              <div class="el-upload__tip">仅支持 jpg/png/jpeg,最多 5 张</div>
+            </template>
+          </el-upload>
         </el-form-item>
       </el-form>
+
       <template #footer>
         <div class="dialog-footer">
-          <el-button type="primary" @click="submitForm">确 定</el-button>
-          <el-button @click="cancel">取 消</el-button>
+          <el-button v-if="!isViewMode" @click="cancel">取消</el-button>
+          <el-button v-if="isViewMode" type="primary" @click="open = false">关闭</el-button>
+          <el-button v-else type="primary" @click="submitForm">提交保存</el-button>
         </div>
       </template>
     </el-dialog>
 
     <!-- 处理弹窗 -->
-    <el-dialog v-model="processOpen" title="处理隐患" width="600px" append-to-body>
+    <el-dialog v-model="processOpen" title="处理隐患" width="620px" append-to-body>
       <el-form ref="processFormRef" :model="processForm" :rules="processRules" label-width="100px">
         <el-form-item label="处理状态" prop="processStatus">
-          <el-select v-model="processForm.processStatus" placeholder="请选择" class="w-full">
+          <el-select v-model="processForm.processStatus" placeholder="请选择处理状态" class="w-full">
             <el-option label="处理中" value="处理中" />
             <el-option label="已处理" value="已处理" />
           </el-select>
@@ -220,8 +287,8 @@
       </el-form>
       <template #footer>
         <div class="dialog-footer">
-          <el-button type="primary" @click="submitProcess">确 定</el-button>
           <el-button @click="processOpen = false">取 消</el-button>
+          <el-button type="primary" @click="submitProcess">确 定</el-button>
         </div>
       </template>
     </el-dialog>
@@ -230,7 +297,9 @@
 
 <script setup name="SafetyHazard" lang="ts">
 import { listHazard, getHazard, delHazard, addHazard, updateHazard, processHazard, getHazardStats } from '@/api/riskctrl/hazard';
-import { HazardForm, HazardQuery, HazardVO, HazardStats } from '@/api/riskctrl/hazard/types';
+import { HazardForm, HazardQuery, HazardVO, HazardStats, HazardStatsVo } from '@/api/riskctrl/hazard/types';
+import { Plus, Search } from '@element-plus/icons-vue';
+import { getToken } from '@/utils/auth';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { sys_normal_disable } = proxy.useDict('sys_normal_disable');
@@ -242,19 +311,97 @@ const ids = ref<Array<string | number>>([]);
 const single = ref(true);
 const multiple = ref(true);
 const total = ref(0);
-const title = ref('');
 const open = ref(false);
 const processOpen = ref(false);
 
+const dialogMode = ref<'add' | 'edit' | 'view'>('add');
+const dialogTitle = computed(() => {
+  if (dialogMode.value === 'add') return '新增安全隐患';
+  if (dialogMode.value === 'edit') return '修改安全隐患';
+  return '查看安全隐患';
+});
+const isViewMode = computed(() => dialogMode.value === 'view');
+
 const queryFormRef = ref<ElFormInstance>();
 const hazardFormRef = ref<ElFormInstance>();
 const processFormRef = ref<ElFormInstance>();
 
-const statsData = reactive<HazardStats>({
+const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload');
+const uploadHeaders = ref({
+  Authorization: 'Bearer ' + getToken(),
+  clientid: import.meta.env.VITE_APP_CLIENT_ID
+});
+
+const photoFileList = ref<any[]>([]);
+
+const photoViewList = computed(() => {
+  const urls = form.photoUrls;
+  if (!urls) return [];
+  return String(urls)
+    .split(',')
+    .map((item) => item.trim())
+    .filter(Boolean);
+});
+
+const syncPhotoUrls = () => {
+  form.photoUrls = photoFileList.value
+    .map((f: any) => f.response?.data?.url || f.url)
+    .filter(Boolean)
+    .join(',');
+};
+
+const initPhotoFileList = (urls?: string) => {
+  if (!urls) {
+    photoFileList.value = [];
+    return;
+  }
+
+  photoFileList.value = String(urls)
+    .split(',')
+    .map((url, idx) => ({
+      name: `photo_${idx + 1}`,
+      url,
+      response: { data: { url } }
+    }))
+    .filter((item) => item.url);
+};
+
+const handlePhotoSuccess = (response: any) => {
+  if (response?.code !== 200) {
+    proxy?.$modal.msgError(response?.msg || '上传失败');
+    return;
+  }
+  syncPhotoUrls();
+};
+
+const handlePhotoRemove = () => {
+  syncPhotoUrls();
+};
+
+const handlePhotoExceed = () => {
+  proxy?.$modal.msgWarning('最多只能上传 5 张图片');
+};
+
+const beforePhotoUpload = (file: File) => {
+  const isImage = ['image/jpeg', 'image/png', 'image/jpg'].includes(file.type);
+  if (!isImage) {
+    proxy?.$modal.msgError('只能上传 jpg/png/jpeg 图片');
+    return false;
+  }
+  const isLt5M = file.size / 1024 / 1024 < 5;
+  if (!isLt5M) {
+    proxy?.$modal.msgError('单张图片不能超过 5MB');
+    return false;
+  }
+  return true;
+};
+
+const unwrap = <T,>(res: any): T => (res?.data ?? res) as T;
+
+const statsData = reactive<HazardStatsVo>({
   totalCount: 0,
-  pendingCount: 0,
-  processingCount: 0,
-  completedCount: 0
+  unprocessedCount: 0,
+  processedCount: 0
 });
 
 const queryParams = reactive<HazardQuery>({
@@ -264,17 +411,21 @@ const queryParams = reactive<HazardQuery>({
   hazardName: undefined,
   hazardType: undefined,
   riskLevel: undefined,
-  processStatus: undefined
+  processStatus: undefined,
+  keyword: undefined
 });
 
-const form = reactive<HazardForm>({
+const form = reactive<any>({
   hazardId: undefined,
+  hazardNo: '',
   hazardName: '',
   hazardType: '',
-  riskLevel: '中风险',
+  riskLevel: '',
   discoverTime: '',
   hazardLocation: '',
   hazardDesc: '',
+  photoUrls: '',
+  processStatus: '待处理',
   status: '0',
   remark: ''
 });
@@ -289,8 +440,10 @@ const processForm = reactive({
 
 const rules = {
   hazardName: [{ required: true, message: '隐患名称不能为空', trigger: 'blur' }],
-  hazardType: [{ required: true, message: '类型不能为空', trigger: 'change' }],
-  riskLevel: [{ required: true, message: '风险等级不能为空', trigger: 'change' }]
+  hazardType: [{ required: true, message: '隐患类型不能为空', trigger: 'change' }],
+  riskLevel: [{ required: true, message: '风险等级不能为空', trigger: 'change' }],
+  discoverTime: [{ required: true, message: '发现时间不能为空', trigger: 'change' }],
+  hazardLocation: [{ required: true, message: '隐患位置不能为空', trigger: 'blur' }]
 };
 
 const processRules = {
@@ -298,155 +451,492 @@ const processRules = {
   processResult: [{ required: true, message: '处理结果不能为空', trigger: 'blur' }]
 };
 
-const columns = ref([
-  { key: 0, label: '编号', visible: true },
-  { key: 1, label: '隐患名称', visible: true },
-  { key: 2, label: '类型', visible: true },
-  { key: 3, label: '风险等级', visible: true },
-  { key: 4, label: '位置', visible: true },
-  { key: 5, label: '发现时间', visible: true },
-  { key: 6, label: '处理状态', visible: true },
-  { key: 7, label: '处理人', visible: true },
-  { key: 8, label: '创建时间', visible: true }
-]);
-
-type TagType = 'success' | 'warning' | 'danger' | 'info' | 'primary';
-
-const getLevelType = (level: string): TagType => {
-  const map: Record<string, TagType> = {
-    '高风险': 'danger',
-    '中风险': 'warning',
-    '低风险': 'info'
+const getLevelType = (level: string) => {
+  const map: Record<string, 'danger' | 'warning' | 'info' | 'success'> = {
+    高风险: 'danger',
+    中风险: 'warning',
+    低风险: 'success'
   };
   return map[level] || 'info';
 };
 
-const getStatusType = (status: string): TagType => {
-  const map: Record<string, TagType> = {
-    '待处理': 'danger',
-    '处理中': 'warning',
-    '已处理': 'success'
+const getStatusType = (status: string) => {
+  const map: Record<string, 'danger' | 'warning' | 'success' | 'info'> = {
+    待处理: 'danger',
+    处理中: 'warning',
+    已处理: 'success'
   };
   return map[status] || 'info';
 };
 
+const getRiskClass = (level: string) => {
+  const map: Record<string, string> = {
+    高风险: 'risk-high',
+    中风险: 'risk-medium',
+    低风险: 'risk-low'
+  };
+  return map[level] || 'risk-low';
+};
+
+const getStatusClass = (status: string) => {
+  const map: Record<string, string> = {
+    待处理: 'status-wait',
+    处理中: 'status-processing',
+    已处理: 'status-done'
+  };
+  return map[status] || 'status-wait';
+};
+
+const disabledDiscoverDate = (time: Date) => {
+  return time.getTime() > Date.now();
+};
+
+const handleExceed = () => {
+  proxy?.$modal.msgWarning('最多只能上传 5 张图片');
+};
+
 const loadStats = async () => {
   try {
-    const res = await getHazardStats();
-    Object.assign(statsData, res.data);
-  } catch { /* ignore */ }
+    const res = await getHazardStats(queryParams as HazardQuery);
+    const data = unwrap<any>(res);
+
+    statsData.totalCount = data.totalCount ?? data.total ?? 0;
+    statsData.unprocessedCount = data.unprocessedCount ?? data.unprocessed ?? 0;
+    statsData.processedCount = data.processedCount ?? data.processed ?? 0;
+  } catch (error) {
+    console.error('获取隐患统计失败:', error);
+    statsData.totalCount = 0;
+    statsData.unprocessedCount = 0;
+    statsData.processedCount = 0;
+  }
 };
 
 const getList = async () => {
   loading.value = true;
-  const res = await listHazard(queryParams);
-  hazardList.value = res.rows;
-  total.value = res.total;
-  loading.value = false;
+  try {
+    const res = await listHazard(queryParams);
+    const data = unwrap<{ rows?: HazardVO[]; total?: number }>(res);
+
+    hazardList.value = data.rows || [];
+    total.value = data.total || 0;
+  } catch (error) {
+    console.error('获取隐患列表失败:', error);
+    hazardList.value = [];
+    total.value = 0;
+  } finally {
+    loading.value = false;
+  }
 };
 
-const cancel = () => {
-  open.value = false;
-  reset();
+const handleQuery = async () => {
+  queryParams.pageNum = 1;
+  await loadStats();
+  await getList();
+};
+
+const resetQuery = async () => {
+  queryFormRef.value?.resetFields();
+  queryParams.hazardNo = undefined;
+  queryParams.hazardName = undefined;
+  queryParams.hazardType = undefined;
+  queryParams.riskLevel = undefined;
+  queryParams.processStatus = undefined;
+  queryParams.keyword = undefined;
+  queryParams.pageNum = 1;
+  await loadStats();
+  await getList();
+};
+
+const handleSelectionChange = (selection: HazardVO[]) => {
+  ids.value = selection.map((item) => item.hazardId);
+  single.value = selection.length !== 1;
+  multiple.value = !selection.length;
 };
 
-const reset = () => {
+const resetForm = () => {
   form.hazardId = undefined;
+  form.hazardNo = '';
   form.hazardName = '';
   form.hazardType = '';
-  form.riskLevel = '中风险';
+  form.riskLevel = '';
   form.discoverTime = '';
   form.hazardLocation = '';
   form.hazardDesc = '';
+  form.processStatus = '待处理';
   form.status = '0';
   form.remark = '';
+  photoFileList.value = [];
   nextTick(() => {
-    hazardFormRef.value?.resetFields();
+    hazardFormRef.value?.clearValidate();
   });
 };
 
-const handleQuery = () => {
-  queryParams.pageNum = 1;
-  getList();
+const handleAdd = () => {
+  dialogMode.value = 'add';
+  resetForm();
+  form.processStatus = '待处理';
+  open.value = true;
 };
 
-const resetQuery = () => {
-  queryFormRef.value?.resetFields();
-  handleQuery();
-};
+const handleView = async (row: HazardVO) => {
+  dialogMode.value = 'view';
+  resetForm();
 
-const handleSelectionChange = (selection: HazardVO[]) => {
-  ids.value = selection.map((item) => item.hazardId);
-  single.value = selection.length !== 1;
-  multiple.value = !selection.length;
-};
+  const res = await getHazard(row.hazardId);
+  const data = unwrap<HazardVO>(res);
 
-const handleAdd = () => {
-  reset();
+  Object.assign(form, data);
+  initPhotoFileList(data.photoUrls);
   open.value = true;
-  title.value = '新增安全隐患';
 };
 
 const handleUpdate = async (row?: HazardVO) => {
-  reset();
+  dialogMode.value = 'edit';
+  resetForm();
+
   const hazardId = row?.hazardId || ids.value[0];
   const res = await getHazard(hazardId);
-  Object.assign(form, res.data);
+  const data = unwrap<HazardVO>(res);
+
+  Object.assign(form, data);
+  initPhotoFileList(data.photoUrls);
   open.value = true;
-  title.value = '修改安全隐患';
+};
+
+const cancel = () => {
+  open.value = false;
+  resetForm();
 };
 
 const submitForm = () => {
   hazardFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      if (form.hazardId !== undefined) {
-        await updateHazard(form);
-        proxy?.$modal.msgSuccess('修改成功');
-      } else {
-        await addHazard(form);
-        proxy?.$modal.msgSuccess('新增成功');
-      }
-      open.value = false;
-      await getList();
-      await loadStats();
+    if (!valid) return;
+
+    syncPhotoUrls();
+
+    if (form.hazardId !== undefined) {
+      await updateHazard(form);
+      proxy?.$modal.msgSuccess('修改成功');
+    } else {
+      form.processStatus = '待处理';
+      await addHazard(form);
+      proxy?.$modal.msgSuccess('新增成功');
     }
+
+    open.value = false;
+    await loadStats();
+    await getList();
   });
 };
 
 const handleProcess = async (row?: HazardVO) => {
   const hazardId = row?.hazardId || ids.value[0];
   const res = await getHazard(hazardId);
-  processForm.hazardId = res.data.hazardId;
-  processForm.processStatus = res.data.processStatus || '处理中';
-  processForm.processResult = res.data.processResult || '';
-  processForm.processUser = res.data.processUser || '';
+  const data = unwrap<HazardVO>(res);
+
+  processForm.hazardId = data.hazardId;
+  processForm.processStatus = data.processStatus || '处理中';
+  processForm.processResult = data.processResult || '';
+  processForm.processUser = data.processUser || '';
   processOpen.value = true;
 };
 
 const submitProcess = () => {
   processFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      processForm.processTime = new Date().toISOString();
-      await processHazard(processForm as any);
-      proxy?.$modal.msgSuccess('处理成功');
-      processOpen.value = false;
-      await getList();
-      await loadStats();
-    }
+    if (!valid) return;
+
+    processForm.processTime = new Date().toISOString();
+    await processHazard(processForm as any);
+    proxy?.$modal.msgSuccess('处理成功');
+    processOpen.value = false;
+    await loadStats();
+    await getList();
   });
 };
 
 const handleDelete = async (row?: HazardVO) => {
   const hazardIds = row?.hazardId || ids.value;
-  await proxy?.$modal.confirm('是否确认删除编号为"' + hazardIds + '"的数据项?');
+  await proxy?.$modal.confirm(`是否确认删除该隐患数据?`);
   await delHazard(hazardIds);
-  await getList();
-  await loadStats();
   proxy?.$modal.msgSuccess('删除成功');
+  await loadStats();
+  await getList();
 };
 
-onMounted(() => {
-  loadStats();
-  getList();
+onMounted(async () => {
+  await loadStats();
+  await getList();
 });
 </script>
+
+<style lang="scss" scoped>
+.hazard-page {
+  padding: 16px;
+  background: #f5f7fb;
+  min-height: calc(100vh - 50px);
+}
+
+.hazard-dialog {
+  :deep(.el-dialog__header) {
+    margin-right: 0;
+    padding: 16px 20px 12px;
+    border-bottom: 1px solid #e5e7eb;
+  }
+
+  :deep(.el-dialog__body) {
+    padding: 18px 20px 10px;
+  }
+
+  :deep(.el-dialog__footer) {
+    padding: 12px 20px 18px;
+    border-top: 1px solid #f1f5f9;
+  }
+}
+
+.hazard-form {
+  :deep(.el-form-item) {
+    margin-bottom: 16px;
+  }
+
+  :deep(.el-form-item__label) {
+    font-weight: 600;
+    color: #374151;
+  }
+
+  :deep(.el-input__wrapper),
+  :deep(.el-textarea__inner),
+  :deep(.el-select__wrapper) {
+    border-radius: 6px;
+  }
+
+  :deep(.el-input.is-disabled .el-input__wrapper) {
+    background: #bfbfbf;
+    box-shadow: none;
+    color: #fff;
+  }
+
+  :deep(.el-input.is-disabled .el-input__inner) {
+    color: #fff;
+    -webkit-text-fill-color: #fff;
+  }
+}
+
+.upload-wrap {
+  width: 100%;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+
+.page-header {
+  margin-bottom: 16px;
+}
+
+.view-text {
+  width: 100%;
+  min-height: 32px;
+  line-height: 32px;
+  padding: 0 12px;
+  border-radius: 6px;
+  background: #f8fafc;
+  color: #334155;
+  border: 1px solid #e5e7eb;
+  word-break: break-all;
+}
+
+.view-text-multiline {
+  min-height: 88px;
+  padding-top: 6px;
+  padding-bottom: 6px;
+  line-height: 1.6;
+}
+
+.photo-view-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 12px;
+}
+
+.photo-view-item {
+  width: 140px;
+}
+
+.photo-thumb {
+  width: 140px;
+  height: 100px;
+  border-radius: 8px;
+  margin-bottom: 6px;
+  border: 1px solid #e5e7eb;
+}
+
+.photo-path {
+  font-size: 12px;
+  color: #64748b;
+  word-break: break-all;
+}
+
+.page-title {
+  font-size: 22px;
+  font-weight: 700;
+  color: #1f2937;
+  margin-bottom: 6px;
+}
+
+.page-desc {
+  font-size: 14px;
+  color: #6b7280;
+}
+
+.stat-row {
+  margin-bottom: 16px;
+}
+
+.stat-card {
+  border-radius: 14px;
+  border: none;
+  overflow: hidden;
+}
+
+.stat-inner {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  min-height: 88px;
+}
+
+.stat-label {
+  font-size: 14px;
+  color: #6b7280;
+  margin-bottom: 8px;
+}
+
+.stat-value {
+  font-size: 34px;
+  font-weight: 700;
+  color: #111827;
+  line-height: 1;
+}
+
+.stat-value-red {
+  color: #ef4444;
+}
+
+.stat-value-green {
+  color: #22c55e;
+}
+
+.stat-icon {
+  width: 44px;
+  height: 44px;
+  border-radius: 50%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 22px;
+}
+
+.stat-icon-blue {
+  background: #e7efff;
+  color: #2563eb;
+}
+
+.stat-icon-red {
+  background: #ffe7e7;
+  color: #ef4444;
+}
+
+.stat-icon-green {
+  background: #e8f8ef;
+  color: #22c55e;
+}
+
+.tag-pill {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  min-width: 72px;
+  height: 28px;
+  padding: 0 12px;
+  border-radius: 999px;
+  font-size: 12px;
+  font-weight: 600;
+  line-height: 1;
+}
+
+.risk-high {
+  background: #fee2e2;
+  color: #dc2626;
+  border: 1px solid #fecaca;
+}
+
+.risk-medium {
+  background: #ffedd5;
+  color: #ea580c;
+  border: 1px solid #fed7aa;
+}
+
+.risk-low {
+  background: #fef3c7;
+  color: #ca8a04;
+  border: 1px solid #fde68a;
+}
+
+.status-wait {
+  background: #f1f5f9;
+  color: #64748b;
+  border: 1px solid #e2e8f0;
+}
+
+.status-processing {
+  background: #fef3c7;
+  color: #ca8a04;
+  border: 1px solid #fde68a;
+}
+
+.status-done {
+  background: #dcfce7;
+  color: #16a34a;
+  border: 1px solid #bbf7d0;
+}
+
+.search-card,
+.table-card {
+  border-radius: 14px;
+  border: none;
+  margin-bottom: 16px;
+}
+
+.search-form {
+  width: 100%;
+}
+
+.search-actions {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+  margin-top: 6px;
+}
+
+.upload-tip {
+  margin-left: 12px;
+  color: #94a3b8;
+  font-size: 12px;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+
+:deep(.el-card__body) {
+  padding: 18px 18px 16px;
+}
+
+:deep(.el-form-item) {
+  margin-bottom: 14px;
+}
+</style>

+ 457 - 267
src/views/riskctrl/workReport/index.vue

@@ -1,92 +1,83 @@
 <template>
-  <div class="p-2">
+  <div class="page-container work-report-page">
+    <div class="page-header flex items-center justify-between">
+      <div>
+        <div class="page-title">作业上报</div>
+        <div class="page-desc">管理施工项目作业上报信息</div>
+      </div>
+
+      <el-button v-has-permi="['riskctrl:workReport:add']" type="primary" icon="Plus" @click="handleAdd()"> 新增上报 </el-button>
+    </div>
+
     <!-- 搜索区域 -->
-    <transition :enter-active-class="proxy?.animate.searchAnimate.enter" :leave-active-class="proxy?.animate.searchAnimate.leave">
-      <div v-show="showSearch" class="mb-[10px]">
-        <el-card shadow="hover">
-          <el-form ref="queryFormRef" :model="queryParams" :inline="true">
-            <el-form-item label="上报编号" prop="reportNo">
-              <el-input v-model="queryParams.reportNo" placeholder="请输入上报编号" clearable @keyup.enter="handleQuery" />
-            </el-form-item>
+    <el-card shadow="hover" class="search-card mb-[12px]">
+      <el-form ref="queryFormRef" :model="queryParams" :inline="false" class="search-form">
+        <el-row :gutter="12">
+          <el-col :xs="24" :sm="12" :md="8">
             <el-form-item label="作业类型" prop="workType">
-              <el-select v-model="queryParams.workType" placeholder="请选择作业类型" clearable>
+              <el-select v-model="queryParams.workType" placeholder="全部类型" clearable class="w-full">
                 <el-option label="临时施工上报" value="临时施工上报" />
                 <el-option label="改造工程上报" value="改造工程上报" />
               </el-select>
             </el-form-item>
+          </el-col>
+
+          <el-col :xs="24" :sm="12" :md="8">
             <el-form-item label="项目名称" prop="projectName">
               <el-input v-model="queryParams.projectName" placeholder="请输入项目名称" clearable @keyup.enter="handleQuery" />
             </el-form-item>
-            <el-form-item label="审核状态" prop="auditStatus">
-              <el-select v-model="queryParams.auditStatus" placeholder="请选择审核状态" clearable>
+          </el-col>
+
+          <el-col :xs="24" :sm="12" :md="8">
+            <el-form-item label="状态" prop="auditStatus">
+              <el-select v-model="queryParams.auditStatus" placeholder="全部状态" clearable class="w-full">
+                <el-option label="审核通过" value="0" />
                 <el-option label="待审核" value="1" />
-                <el-option label="已通过" value="0" />
-                <el-option label="已驳回" value="2" />
+                <el-option label="审核不通过" value="2" />
               </el-select>
             </el-form-item>
-            <el-form-item>
-              <el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
-              <el-button icon="Refresh" @click="resetQuery">重置</el-button>
-            </el-form-item>
-          </el-form>
-        </el-card>
-      </div>
-    </transition>
-
-    <!-- 列表区域 -->
-    <el-card shadow="hover">
-      <template #header>
-        <el-row :gutter="10">
-          <el-col :span="1.5">
-            <el-button v-has-permi="['riskctrl:workReport:add']" type="primary" plain icon="Plus" @click="handleAdd()">新增作业上报</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button v-has-permi="['riskctrl:workReport:edit']" type="success" plain :disabled="single" icon="Edit" @click="handleUpdate()">修改</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button type="info" plain :disabled="single" icon="View" @click="handleDetail()">详情</el-button>
           </el-col>
-          <el-col :span="1.5">
-            <el-button v-has-permi="['riskctrl:workReport:remove']" type="danger" plain :disabled="multiple" icon="Delete" @click="handleDelete()">删除</el-button>
-          </el-col>
-          <el-col :span="1.5">
-            <el-button v-has-permi="['riskctrl:workReport:audit']" type="warning" plain :disabled="single" icon="Select" @click="handleAudit()">审核</el-button>
-          </el-col>
-          <right-toolbar v-model:show-search="showSearch" :columns="columns" :search="true" @query-table="getList"></right-toolbar>
         </el-row>
-      </template>
 
-      <el-table v-loading="loading" border :data="reportList" @selection-change="handleSelectionChange">
-        <el-table-column type="selection" width="50" align="center" />
-        <el-table-column v-if="columns[0].visible" key="reportNo" label="上报编号" align="center" prop="reportNo" width="160" />
-        <el-table-column v-if="columns[1].visible" key="workType" label="作业类型" align="center" prop="workType" width="120" />
-        <el-table-column v-if="columns[2].visible" key="projectName" label="项目名称" align="center" prop="projectName" :show-overflow-tooltip="true" />
-        <el-table-column v-if="columns[3].visible" key="startTime" label="施工开始时间" align="center" prop="startTime" width="160" />
-        <el-table-column v-if="columns[4].visible" key="endTime" label="施工结束时间" align="center" prop="endTime" width="160" />
-        <el-table-column v-if="columns[5].visible" key="safetyOfficer" label="现场安全员" align="center" prop="safetyOfficer" width="120" />
-        <el-table-column v-if="columns[6].visible" key="contactPhone" label="联系电话" align="center" prop="contactPhone" width="130" />
-        <el-table-column v-if="columns[7].visible" key="auditStatus" label="审核状态" align="center" prop="auditStatus" width="100">
+        <div class="search-actions">
+          <el-button @click="resetQuery">重置</el-button>
+          <el-button type="primary" @click="handleQuery">搜索</el-button>
+        </div>
+      </el-form>
+    </el-card>
+
+    <!-- 列表区域 -->
+    <el-card shadow="hover" class="table-card">
+      <el-table v-loading="loading" border :data="reportList">
+        <el-table-column type="index" label="序号" width="70" align="center" />
+        <el-table-column label="作业类型" align="center" prop="workType" width="130">
           <template #default="scope">
-            <el-tag v-if="scope.row.auditStatus === '0'" type="success">已通过</el-tag>
-            <el-tag v-else-if="scope.row.auditStatus === '1'" type="warning">待审核</el-tag>
-            <el-tag v-else-if="scope.row.auditStatus === '2'" type="danger">已驳回</el-tag>
+            <span class="tag-pill work-type" :class="scope.row.workType === '临时施工上报' ? 'work-temp' : 'work-reform'">
+              {{ scope.row.workType }}
+            </span>
           </template>
         </el-table-column>
-        <el-table-column v-if="columns[8].visible" label="创建时间" align="center" prop="createTime" width="160" />
-        <el-table-column label="操作" fixed="right" width="260" class-name="small-padding fixed-width">
+
+        <el-table-column label="项目名称" align="center" prop="projectName" :show-overflow-tooltip="true" />
+        <el-table-column label="施工开始时间" align="center" prop="startTime" width="160" />
+        <el-table-column label="施工结束时间" align="center" prop="endTime" width="160" />
+        <el-table-column label="现场安全员" align="center" prop="safetyOfficer" width="120" />
+
+        <el-table-column label="状态" align="center" prop="auditStatus" width="110">
           <template #default="scope">
-            <el-tooltip content="详情" placement="top">
-              <el-button link type="info" icon="View" @click="handleDetail(scope.row)"></el-button>
-            </el-tooltip>
-            <el-tooltip content="修改" placement="top">
-              <el-button v-has-permi="['riskctrl:workReport:edit']" link type="primary" icon="Edit" @click="handleUpdate(scope.row)"></el-button>
-            </el-tooltip>
-            <el-tooltip content="审核" placement="top">
-              <el-button v-has-permi="['riskctrl:workReport:audit']" link type="warning" icon="Select" @click="handleAudit(scope.row)"></el-button>
-            </el-tooltip>
-            <el-tooltip content="删除" placement="top">
-              <el-button v-has-permi="['riskctrl:workReport:remove']" link type="primary" icon="Delete" @click="handleDelete(scope.row)"></el-button>
-            </el-tooltip>
+            <span class="tag-pill" :class="getAuditClass(scope.row.auditStatus)">
+              {{ getAuditText(scope.row.auditStatus) }}
+            </span>
+          </template>
+        </el-table-column>
+
+        <el-table-column label="操作" fixed="right" width="240" align="center" header-align="center" class-name="operation-col">
+          <template #default="scope">
+            <div class="operation-actions">
+              <el-button link type="primary" @click="handleDetail(scope.row)">查看详情</el-button>
+              <el-button v-if="scope.row.auditStatus === '1'" link type="warning" @click="handleAudit(scope.row)">审核</el-button>
+              <el-button link type="danger" @click="handleDelete(scope.row)">删除</el-button>
+            </div>
           </template>
         </el-table-column>
       </el-table>
@@ -112,6 +103,7 @@
             </el-form-item>
           </el-col>
         </el-row>
+
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="施工开始时间" prop="startTime">
@@ -138,6 +130,7 @@
             </el-form-item>
           </el-col>
         </el-row>
+
         <el-row :gutter="20">
           <el-col :span="12">
             <el-form-item label="现场安全员" prop="safetyOfficer">
@@ -150,13 +143,14 @@
             </el-form-item>
           </el-col>
         </el-row>
+
         <el-form-item label="现场施工图片" prop="workImages">
           <el-upload
             v-model:file-list="workImageList"
             :action="uploadUrl"
             :headers="uploadHeaders"
             list-type="picture-card"
-            :accept="'image/jpeg,image/png'"
+            accept="image/jpeg,image/png"
             :limit="5"
             :on-success="handleWorkImageSuccess"
             :on-remove="handleWorkImageRemove"
@@ -169,13 +163,14 @@
             </template>
           </el-upload>
         </el-form-item>
+
         <el-form-item label="施工证件" prop="permitFiles">
           <el-upload
             ref="permitUploadRef"
             v-model:file-list="permitFileList"
             :action="uploadUrl"
             :headers="uploadHeaders"
-            :accept="'.pdf,.jpg,.jpeg,.png'"
+            accept=".pdf,.jpg,.jpeg,.png"
             :limit="1"
             :on-success="handlePermitFileSuccess"
             :on-remove="handlePermitFileRemove"
@@ -187,8 +182,8 @@
             </template>
             <template #file="{ file }">
               <div class="permit-file-item">
-                <el-icon style="margin-right: 8px;"><Document /></el-icon>
-                <el-link type="primary" :href="file.url || file.response?.data?.url" target="_blank" :underline="false">
+                <el-icon style="margin-right: 8px"><Document /></el-icon>
+                <el-link type="primary" :href="getFileUrl(file)" target="_blank" :underline="false">
                   {{ file.name }}
                 </el-link>
                 <span class="permit-file-remove" @click="handlePermitFileRemove(file)">
@@ -198,6 +193,7 @@
             </template>
           </el-upload>
         </el-form-item>
+
         <el-form-item label="施工说明" prop="workDesc">
           <el-input
             v-model="form.workDesc"
@@ -208,10 +204,12 @@
             show-word-limit
           />
         </el-form-item>
+
         <el-form-item label="备注" prop="remark">
           <el-input v-model="form.remark" type="textarea" :rows="2" placeholder="请输入备注" maxlength="500" show-word-limit />
         </el-form-item>
       </el-form>
+
       <template #footer>
         <div class="dialog-footer">
           <el-button type="primary" :loading="buttonLoading" @click="submitForm">确 定</el-button>
@@ -229,10 +227,12 @@
             <el-radio value="2">审核不通过</el-radio>
           </el-radio-group>
         </el-form-item>
-        <el-form-item label="审核意见" prop="auditComment">
-          <el-input v-model="auditForm.auditComment" type="textarea" :rows="4" placeholder="请输入审核意见" maxlength="500" show-word-limit />
+
+        <el-form-item v-if="auditForm.auditStatus === '2'" label="不通过原因" prop="auditComment">
+          <el-input v-model="auditForm.auditComment" type="textarea" :rows="4" placeholder="请输入审核不通过原因" maxlength="500" show-word-limit />
         </el-form-item>
       </el-form>
+
       <template #footer>
         <div class="dialog-footer">
           <el-button type="primary" @click="submitAudit">确 定</el-button>
@@ -242,27 +242,31 @@
     </el-dialog>
 
     <!-- 详情弹窗 -->
-    <el-dialog v-model="detailOpen" title="作业上报详情" width="800px" append-to-body :close-on-click-modal="false" destroy-on-close>
-      <el-descriptions :column="2" border size="default">
-        <el-descriptions-item label="上报编号">{{ detailData.reportNo }}</el-descriptions-item>
-        <el-descriptions-item label="作业类型">{{ detailData.workType }}</el-descriptions-item>
-        <el-descriptions-item label="项目名称">{{ detailData.projectName }}</el-descriptions-item>
+    <el-dialog v-model="detailOpen" title="作业上报详情" width="780px" append-to-body :close-on-click-modal="false" destroy-on-close>
+      <el-descriptions :column="2" border class="detail-desc">
+        <el-descriptions-item label="作业类型">{{ detailData.workType || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="项目名称">{{ detailData.projectName || '-' }}</el-descriptions-item>
+
+        <el-descriptions-item label="施工开始时间">{{ detailData.startTime || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="施工结束时间">{{ detailData.endTime || '-' }}</el-descriptions-item>
+
+        <el-descriptions-item label="现场安全员">{{ detailData.safetyOfficer || '-' }}</el-descriptions-item>
+        <el-descriptions-item label="联系电话">{{ detailData.contactPhone || '-' }}</el-descriptions-item>
+
+        <el-descriptions-item label="申报时间">{{ detailData.createTime || '-' }}</el-descriptions-item>
         <el-descriptions-item label="审核状态">
-          <el-tag v-if="detailData.auditStatus === '0'" type="success">已通过</el-tag>
-          <el-tag v-else-if="detailData.auditStatus === '1'" type="warning">待审核</el-tag>
-          <el-tag v-else-if="detailData.auditStatus === '2'" type="danger">已驳回</el-tag>
+          <span class="tag-pill" :class="getAuditClass(detailData.auditStatus || '')">
+            {{ getAuditText(detailData.auditStatus || '') }}
+          </span>
+        </el-descriptions-item>
+
+        <el-descriptions-item label="施工说明" :span="2">
+          {{ detailData.workDesc || '-' }}
         </el-descriptions-item>
-        <el-descriptions-item label="施工开始时间">{{ detailData.startTime }}</el-descriptions-item>
-        <el-descriptions-item label="施工结束时间">{{ detailData.endTime }}</el-descriptions-item>
-        <el-descriptions-item label="现场安全员">{{ detailData.safetyOfficer }}</el-descriptions-item>
-        <el-descriptions-item label="联系电话">{{ detailData.contactPhone }}</el-descriptions-item>
-        <el-descriptions-item v-if="detailData.auditStatus !== '1'" label="审核意见" :span="2">{{ detailData.auditComment || '-' }}</el-descriptions-item>
-        <el-descriptions-item label="施工说明" :span="2">{{ detailData.workDesc || '-' }}</el-descriptions-item>
-        <el-descriptions-item label="备注" :span="2">{{ detailData.remark || '-' }}</el-descriptions-item>
       </el-descriptions>
 
       <el-divider content-position="left">现场施工图片</el-divider>
-      <div v-if="detailImageUrls.length > 0" style="display: flex; flex-wrap: wrap; gap: 10px;">
+      <div v-if="detailImageUrls.length > 0" class="detail-image-list">
         <el-image
           v-for="(url, idx) in detailImageUrls"
           :key="idx"
@@ -270,27 +274,25 @@
           :preview-src-list="detailImageUrls"
           :initial-index="idx"
           fit="cover"
-          style="width: 120px; height: 120px; border-radius: 6px; cursor: pointer;"
+          class="detail-image-item"
         />
       </div>
       <el-empty v-else description="暂无施工图片" :image-size="60" />
 
       <el-divider content-position="left">施工证件</el-divider>
-      <div v-if="detailPermitUrls.length > 0">
-        <div v-for="(url, idx) in detailPermitUrls" :key="idx" style="margin-bottom: 8px;">
-          <el-link type="primary" :href="url" target="_blank">
-            <el-icon><Document /></el-icon>
-            {{ getFileNameFromUrl(url) }}
-          </el-link>
-          <el-tag v-if="isPdfUrl(url)" size="small" type="info" style="margin-left: 8px;">PDF</el-tag>
-          <el-tag v-else size="small" type="success" style="margin-left: 8px;">图片</el-tag>
-        </div>
+      <div v-if="detailPermitUrls.length > 0" class="permit-card-list">
+        <a v-for="(url, idx) in detailPermitUrls" :key="idx" class="permit-card" :href="url" target="_blank" rel="noreferrer">
+          <el-icon class="permit-card-icon">
+            <Document />
+          </el-icon>
+          <span class="permit-card-name">{{ getFileNameFromUrl(url) }}</span>
+        </a>
       </div>
       <el-empty v-else description="暂无施工证件" :image-size="60" />
 
       <template #footer>
         <div class="dialog-footer">
-          <el-button @click="detailOpen = false">关 闭</el-button>
+          <el-button type="primary" @click="detailOpen = false">关闭</el-button>
         </div>
       </template>
     </el-dialog>
@@ -298,15 +300,11 @@
 </template>
 
 <script setup name="WorkReport" lang="ts">
-import { ref, reactive, onMounted, nextTick } from 'vue';
+import { ref, reactive, onMounted, nextTick, computed, getCurrentInstance } from 'vue';
 import type { ComponentInternalInstance } from 'vue';
-import type { ElFormInstance } from 'element-plus';
+import type { FormInstance } from 'element-plus';
 import { Plus, Document, Close } from '@element-plus/icons-vue';
-import { getCurrentInstance } from 'vue';
-import {
-  listWorkReport, getWorkReport, delWorkReport,
-  addWorkReport, updateWorkReport, auditWorkReport
-} from '@/api/riskctrl/workReport';
+import { listWorkReport, getWorkReport, delWorkReport, addWorkReport, updateWorkReport, auditWorkReport } from '@/api/riskctrl/workReport';
 import type { WorkReportVO } from '@/api/riskctrl/workReport/types';
 import { getToken } from '@/utils/auth';
 
@@ -315,21 +313,21 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const reportList = ref<WorkReportVO[]>([]);
 const loading = ref(true);
 const buttonLoading = ref(false);
-const showSearch = ref(true);
-const ids = ref<Array<string | number>>([]);
-const single = ref(true);
-const multiple = ref(true);
 const total = ref(0);
 const title = ref('');
 const open = ref(false);
 const auditOpen = ref(false);
+const detailOpen = ref(false);
 
-const queryFormRef = ref<ElFormInstance>();
-const reportFormRef = ref<ElFormInstance>();
-const auditFormRef = ref<ElFormInstance>();
+const queryFormRef = ref<FormInstance>();
+const reportFormRef = ref<FormInstance>();
+const auditFormRef = ref<FormInstance>();
 
 const uploadUrl = ref(import.meta.env.VITE_APP_BASE_API + '/resource/oss/upload');
-const uploadHeaders = ref({ Authorization: 'Bearer ' + getToken(), clientid: import.meta.env.VITE_APP_CLIENT_ID });
+const uploadHeaders = ref({
+  Authorization: 'Bearer ' + getToken(),
+  clientid: import.meta.env.VITE_APP_CLIENT_ID
+});
 
 const workImageList = ref<any[]>([]);
 const permitFileList = ref<any[]>([]);
@@ -394,13 +392,20 @@ const auditForm = reactive<AuditFormType>({
   auditComment: ''
 });
 
-// 详情弹窗
-const detailOpen = ref(false);
-const detailData = ref<WorkReportVO>({});
+type OssUploadResponse = {
+  data?: {
+    url?: string;
+  };
+};
+
+const getFileUrl = (file: any) => {
+  return file?.url || (file?.response as OssUploadResponse | undefined)?.data?.url || '';
+};
+
+const detailData = ref<Partial<WorkReportVO>>({});
 const detailImageUrls = ref<string[]>([]);
 const detailPermitUrls = ref<string[]>([]);
 
-/** 手机号/固话验证 */
 const validatePhone = (_rule: any, value: string, callback: any) => {
   if (!value) {
     callback();
@@ -433,28 +438,52 @@ const rules = {
     }
   ],
   safetyOfficer: [{ required: true, message: '请输入现场安全员姓名', trigger: 'blur' }],
-  contactPhone: [{ validator: validatePhone, trigger: 'blur' }]
+  contactPhone: [{ validator: validatePhone, trigger: 'blur' }],
+  workDesc: [
+    {
+      validator: (_rule: any, value: string, callback: any) => {
+        if ((value || '').length > 500) {
+          callback(new Error('施工说明不能超过 500 字'));
+        } else {
+          callback();
+        }
+      },
+      trigger: 'blur'
+    }
+  ]
+};
+
+const auditRules = computed(() => ({
+  auditStatus: [{ required: true, message: '请选择审核结果', trigger: 'change' }],
+  auditComment: auditForm.auditStatus === '2' ? [{ required: true, message: '请输入审核不通过原因', trigger: 'blur' }] : []
+}));
+
+/** 作业类型标签 */
+const getWorkTypeClass = (workType: string) => {
+  return workType === '临时施工上报' ? 'work-temp' : 'work-reform';
 };
 
-const auditRules = {
-  auditStatus: [{ required: true, message: '请选择审核结果', trigger: 'change' }]
+/** 审核状态标签 */
+const getAuditText = (status: string) => {
+  const map: Record<string, string> = {
+    '0': '审核通过',
+    '1': '待审核',
+    '2': '审核不通过'
+  };
+  return map[status] || '-';
 };
 
-const columns = ref([
-  { key: 0, label: '上报编号', visible: true },
-  { key: 1, label: '作业类型', visible: true },
-  { key: 2, label: '项目名称', visible: true },
-  { key: 3, label: '施工开始时间', visible: true },
-  { key: 4, label: '施工结束时间', visible: true },
-  { key: 5, label: '现场安全员', visible: true },
-  { key: 6, label: '联系电话', visible: true },
-  { key: 7, label: '审核状态', visible: true },
-  { key: 8, label: '创建时间', visible: true }
-]);
-
-/** 施工开始时间禁用未来日期(可选:不晚于当前时间) */
+const getAuditClass = (status: string) => {
+  const map: Record<string, string> = {
+    '0': 'audit-success',
+    '1': 'audit-pending',
+    '2': 'audit-reject'
+  };
+  return map[status] || 'audit-pending';
+};
+
+/** 施工开始时间禁用未来日期 */
 const startDisabledDate = (date: Date) => {
-  // return date.getTime() > Date.now(); // 开启则禁止选择未来时间
   return false;
 };
 
@@ -466,7 +495,6 @@ const endDisabledDate = (date: Date) => {
   return false;
 };
 
-/** 获取列表 */
 const getList = async () => {
   loading.value = true;
   try {
@@ -475,6 +503,8 @@ const getList = async () => {
     total.value = res.total || 0;
   } catch (e) {
     console.error('加载作业上报列表失败', e);
+    reportList.value = [];
+    total.value = 0;
   } finally {
     loading.value = false;
   }
@@ -501,7 +531,7 @@ const reset = () => {
   workImageList.value = [];
   permitFileList.value = [];
   nextTick(() => {
-    reportFormRef.value?.resetFields();
+    reportFormRef.value?.clearValidate();
   });
 };
 
@@ -512,135 +542,168 @@ const handleQuery = () => {
 
 const resetQuery = () => {
   queryFormRef.value?.resetFields();
-  handleQuery();
-};
-
-const handleSelectionChange = (selection: WorkReportVO[]) => {
-  ids.value = selection.map((item) => item.reportId);
-  single.value = selection.length !== 1;
-  multiple.value = !selection.length;
+  queryParams.pageNum = 1;
+  queryParams.reportNo = undefined;
+  queryParams.projectName = undefined;
+  queryParams.workType = undefined;
+  queryParams.auditStatus = undefined;
+  getList();
 };
 
-/** 新增 */
 const handleAdd = () => {
   reset();
   open.value = true;
   title.value = '新增作业上报表单';
 };
 
-/** 修改 */
+const handleDetail = async (row?: WorkReportVO) => {
+  const reportId = row?.reportId;
+  if (!reportId) return;
+
+  try {
+    const res: any = await getWorkReport(reportId);
+    const data = res.data || res;
+    detailData.value = data;
+    detailImageUrls.value = data.workImages ? data.workImages.split(',').filter(Boolean) : [];
+    detailPermitUrls.value = data.permitFiles ? data.permitFiles.split(',').filter(Boolean) : [];
+    detailOpen.value = true;
+  } catch (e) {
+    console.error('获取详情失败', e);
+  }
+};
+
 const handleUpdate = async (row?: WorkReportVO) => {
   reset();
-  const reportId = row?.reportId || ids.value[0];
+  const reportId = row?.reportId;
+  if (!reportId) return;
+
   const res: any = await getWorkReport(reportId);
-  const data = res.data;
+  const data = res.data || res;
   Object.assign(form, data);
-  // 回显图片列表
+
   if (data.workImages) {
     const urls = data.workImages.split(',').filter(Boolean);
     workImageList.value = urls.map((url: string, idx: number) => ({
       name: 'image_' + idx,
-      url: url,
-      response: { data: { url: url } }
+      url,
+      response: { data: { url } }
     }));
   }
-  // 回显证件列表
+
   if (data.permitFiles) {
     const urls = data.permitFiles.split(',').filter(Boolean);
     permitFileList.value = urls.map((url: string, idx: number) => ({
       name: 'permit_' + idx,
-      url: url,
-      response: { data: { url: url } }
+      url,
+      response: { data: { url } }
     }));
   }
+
   open.value = true;
   title.value = '修改作业上报';
 };
 
-/** 详情查看 */
-const handleDetail = async (row?: WorkReportVO) => {
-  const reportId = row?.reportId || ids.value[0];
+const handleAudit = async (row?: WorkReportVO) => {
+  const reportId = row?.reportId;
+  if (!reportId) return;
+
+  const res: any = await getWorkReport(reportId);
+  const data = res.data || res;
+  auditForm.reportId = data.reportId;
+  auditForm.auditStatus = '';
+  auditForm.auditComment = '';
+  auditOpen.value = true;
+};
+
+const submitForm = () => {
+  reportFormRef.value?.validate(async (valid: boolean) => {
+    if (!valid) return;
+
+    buttonLoading.value = true;
+    try {
+      form.workImages = workImageList.value
+        .filter((f: any) => f.response?.data?.url || f.url)
+        .map((f: any) => f.response?.data?.url || f.url)
+        .join(',');
+
+      form.permitFiles = permitFileList.value
+        .filter((f: any) => f.response?.data?.url || f.url)
+        .map((f: any) => f.response?.data?.url || f.url)
+        .join(',');
+
+      if (form.reportId) {
+        await updateWorkReport(form as any);
+        proxy?.$modal.msgSuccess('修改成功');
+      } else {
+        await addWorkReport(form as any);
+        proxy?.$modal.msgSuccess('新增成功');
+      }
+
+      open.value = false;
+      await getList();
+    } catch (e) {
+      console.error('提交失败', e);
+    } finally {
+      buttonLoading.value = false;
+    }
+  });
+};
+
+const submitAudit = () => {
+  auditFormRef.value?.validate(async (valid: boolean) => {
+    if (!valid) return;
+
+    try {
+      await auditWorkReport({
+        reportId: auditForm.reportId,
+        auditStatus: auditForm.auditStatus,
+        auditComment: auditForm.auditStatus === '2' ? auditForm.auditComment : ''
+      } as any);
+      proxy?.$modal.msgSuccess('审核完成');
+      auditOpen.value = false;
+      await getList();
+    } catch (e) {
+      console.error('审核失败', e);
+    }
+  });
+};
+
+const handleDelete = async (row?: WorkReportVO) => {
+  const reportId = row?.reportId;
+  if (!reportId) return;
+
+  await proxy?.$modal.confirm('是否确认删除该作业上报数据?');
   try {
-    const res: any = await getWorkReport(reportId);
-    detailData.value = res.data;
-    // 解析图片和证件URL
-    detailImageUrls.value = res.data.workImages
-      ? res.data.workImages.split(',').filter(Boolean)
-      : [];
-    detailPermitUrls.value = res.data.permitFiles
-      ? res.data.permitFiles.split(',').filter(Boolean)
-      : [];
-    detailOpen.value = true;
+    await delWorkReport(reportId);
+    proxy?.$modal.msgSuccess('删除成功');
+    await getList();
   } catch (e) {
-    console.error('获取详情失败', e);
+    console.error('删除失败', e);
   }
 };
 
-/** 从URL提取文件名 */
 const getFileNameFromUrl = (url: string) => {
   if (!url) return '未知文件';
   const parts = url.split('/');
   return parts[parts.length - 1] || '未知文件';
 };
 
-/** 判断是否为PDF文件 */
 const isPdfUrl = (url: string) => {
   return url?.toLowerCase().endsWith('.pdf');
 };
 
-/** 提交表单 */
-const submitForm = () => {
-  reportFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      buttonLoading.value = true;
-      try {
-        // 拼接图片路径
-        form.workImages = workImageList.value
-          .filter((f: any) => f.response?.data?.url || f.url)
-          .map((f: any) => f.response?.data?.url || f.url)
-          .join(',');
-        // 拼接证件路径
-        form.permitFiles = permitFileList.value
-          .filter((f: any) => f.response?.data?.url || f.url)
-          .map((f: any) => f.response?.data?.url || f.url)
-          .join(',');
-
-        if (form.reportId) {
-          await updateWorkReport(form as any);
-          proxy?.$modal.msgSuccess('修改成功');
-        } else {
-          await addWorkReport(form as any);
-          proxy?.$modal.msgSuccess('新增成功');
-        }
-        open.value = false;
-        await getList();
-      } catch (e) {
-        console.error('提交失败', e);
-      } finally {
-        buttonLoading.value = false;
-      }
-    }
-  });
-};
-
-/** 图片上传成功 */
-const handleWorkImageSuccess = (response: any, _file: any) => {
+const handleWorkImageSuccess = (response: any) => {
   if (response.code !== 200) {
     proxy?.$modal.msgError(response.msg || '上传失败');
   }
 };
 
-/** 图片移除 */
-const handleWorkImageRemove = (_file: any) => {
-  // 从列表中移除
-};
+const handleWorkImageRemove = (_file?: any) => {};
 
-/** 超出图片数量限制 */
 const handleImageExceed = () => {
   proxy?.$modal.msgError('最多只能上传 5 张图片');
 };
 
-/** 图片上传前校验 */
 const beforeWorkImageUpload = (file: any) => {
   const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png';
   if (!isJpgOrPng) {
@@ -655,17 +718,14 @@ const beforeWorkImageUpload = (file: any) => {
   return true;
 };
 
-/** 证件上传成功 */
-const handlePermitFileSuccess = (response: any, _file: any) => {
+const handlePermitFileSuccess = (response: any) => {
   if (response.code !== 200) {
     proxy?.$modal.msgError(response.msg || '上传失败');
   }
 };
 
-/** 证件移除 */
-const handlePermitFileRemove = (_file: any) => {};
+const handlePermitFileRemove = (_file?: any) => {};
 
-/** 证件上传前校验 */
 const beforePermitFileUpload = (file: any) => {
   const allowedTypes = ['application/pdf', 'image/jpeg', 'image/png'];
   if (!allowedTypes.includes(file.type)) {
@@ -680,56 +740,182 @@ const beforePermitFileUpload = (file: any) => {
   return true;
 };
 
-/** 审核 */
-const handleAudit = async (row?: WorkReportVO) => {
-  const reportId = row?.reportId || ids.value[0];
-  const res: any = await getWorkReport(reportId);
-  auditForm.reportId = res.data.reportId;
-  auditForm.auditStatus = '';
-  auditForm.auditComment = '';
-  auditOpen.value = true;
-};
-
-/** 提交审核 */
-const submitAudit = () => {
-  auditFormRef.value?.validate(async (valid: boolean) => {
-    if (valid) {
-      try {
-        await auditWorkReport({
-          reportId: auditForm.reportId,
-          auditStatus: auditForm.auditStatus,
-          auditComment: auditForm.auditComment
-        } as any);
-        proxy?.$modal.msgSuccess('审核完成');
-        auditOpen.value = false;
-        await getList();
-      } catch (e) {
-        console.error('审核失败', e);
-      }
-    }
-  });
-};
-
-/** 删除 */
-const handleDelete = async (row?: WorkReportVO) => {
-  const reportIds = row?.reportId || ids.value;
-  await proxy?.$modal.confirm('是否确认删除选中的作业上报数据?');
-  try {
-    await delWorkReport(reportIds);
-    proxy?.$modal.msgSuccess('删除成功');
-    await getList();
-  } catch (e) {
-    console.error('删除失败', e);
-  }
-};
-
 onMounted(() => {
   getList();
 });
 </script>
 
-<style scoped>
-.permit-file-item {
+<style scoped lang="scss">
+.work-report-page {
+  padding: 16px;
+  background: #f5f7fb;
+  min-height: calc(100vh - 50px);
+}
+
+.page-header {
+  margin-bottom: 16px;
+}
+
+.page-title {
+  font-size: 22px;
+  font-weight: 700;
+  color: #1f2937;
+  margin-bottom: 6px;
+}
+
+.page-desc {
+  font-size: 14px;
+  color: #6b7280;
+}
+
+.search-card,
+.table-card {
+  border-radius: 14px;
+  border: none;
+  margin-bottom: 16px;
+}
+
+.search-form {
+  width: 100%;
+}
+
+.search-actions {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+  margin-top: 6px;
+}
+
+.dialog-footer {
+  display: flex;
+  justify-content: flex-end;
+  gap: 10px;
+}
+
+.tag-pill {
+  display: inline-flex;
+  align-items: center;
+  justify-content: center;
+  min-width: 72px;
+  height: 28px;
+  padding: 0 12px;
+  border-radius: 999px;
+  font-size: 12px;
+  font-weight: 600;
+  line-height: 1;
+}
+
+.work-type {
+  min-width: 92px;
+}
+
+.work-temp {
+  background: #e0ecff;
+  color: #2563eb;
+  border: 1px solid #bfdbfe;
+}
+
+.work-reform {
+  background: #fff1e0;
+  color: #ea580c;
+  border: 1px solid #fed7aa;
+}
+
+.audit-success {
+  background: #dcfce7;
+  color: #16a34a;
+  border: 1px solid #bbf7d0;
+}
+
+.audit-pending {
+  background: #fef3c7;
+  color: #ca8a04;
+  border: 1px solid #fde68a;
+}
+
+.audit-reject {
+  background: #fee2e2;
+  color: #dc2626;
+  border: 1px solid #fecaca;
+}
+
+.operation-actions {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  gap: 14px;
+}
+
+.detail-image-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 10px;
+}
+
+.detail-image-item {
+  width: 120px;
+  height: 120px;
+  border-radius: 6px;
+  cursor: pointer;
+  border: 1px solid #e5e7eb;
+}
+
+.detail-desc {
+  margin-bottom: 12px;
+}
+
+.detail-image-list {
+  display: grid;
+  grid-template-columns: repeat(3, minmax(0, 1fr));
+  gap: 12px;
+}
+
+.detail-image-item {
+  width: 100%;
+  height: 132px;
+  border-radius: 8px;
+  cursor: pointer;
+  border: 1px solid #e5e7eb;
+}
+
+.permit-card-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 12px;
+}
+
+.permit-card {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  min-width: 180px;
+  padding: 12px 14px;
+  border: 1px solid #e5e7eb;
+  border-radius: 10px;
+  background: #f8fafc;
+  color: #1f2937;
+  text-decoration: none;
+  transition: all 0.2s ease;
+}
+
+.permit-card:hover {
+  border-color: #c7d2fe;
+  background: #eff6ff;
+}
+
+.permit-card-icon {
+  font-size: 18px;
+  color: #2563eb;
+  flex-shrink: 0;
+}
+
+.permit-card-name {
+  font-size: 14px;
+  word-break: break-all;
+}
+
+.permit-file-item,
+.permit-view-item {
   display: flex;
   align-items: center;
   padding: 6px 10px;
@@ -738,17 +924,21 @@ onMounted(() => {
   margin-top: 6px;
   background: var(--el-fill-color-light);
 }
-.permit-file-item .el-link {
+
+.permit-file-item .el-link,
+.permit-view-item .el-link {
   flex: 1;
   overflow: hidden;
   text-overflow: ellipsis;
   white-space: nowrap;
 }
+
 .permit-file-remove {
   cursor: pointer;
   color: var(--el-text-color-secondary);
   margin-left: 8px;
 }
+
 .permit-file-remove:hover {
   color: var(--el-color-danger);
 }