1749 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
			
		
		
	
	
			1749 lines
		
	
	
		
			49 KiB
		
	
	
	
		
			Vue
		
	
	
	
	
	
| <template>
 | ||
|   <div>
 | ||
|     <div class="inspection-tasks">
 | ||
|       <!-- <div class="navigation-tabs">
 | ||
|         <div class="nav-tab" @click="handleInspection1">待办事项</div>
 | ||
|         <div class="nav-tab active" @click="handleInspection2">巡检管理</div>
 | ||
|         <div class="nav-tab" @click="handleInspection3">试验管理</div>
 | ||
|         <div class="nav-tab" @click="handleInspection4">报修管理</div>
 | ||
|         <div class="nav-tab" @click="handleInspection5">抢修管理</div>
 | ||
|         <div class="nav-tab" @click="handleInspection6">工单管理</div>
 | ||
|         <div class="nav-tab" @click="handleInspection7">运维组织</div>
 | ||
|       </div> -->
 | ||
| 
 | ||
|       <!-- 选项卡 -->
 | ||
|       <div class="tabs-wrapper">
 | ||
|         <div style="display: flex; align-items: center; gap: 10px">
 | ||
|           <el-button type="primary" @click="handleInspectionManagement1">巡检计划</el-button>
 | ||
|           <el-button type="primary" @click="handleInspectionManagement2">巡检任务</el-button>
 | ||
|           <el-button type="primary" @click="handleInspectionManagement3">巡检记录</el-button>
 | ||
|         </div>
 | ||
|       </div>
 | ||
| 
 | ||
|       <!-- 筛选栏 -->
 | ||
|       <div class="filter-bar">
 | ||
|         <div class="filter-container">
 | ||
|           <div class="filter-item">
 | ||
|             <el-select v-model="taskStatus" placeholder="任务状态">
 | ||
|               <el-option label="待执行" value="pending"></el-option>
 | ||
|               <el-option label="执行中" value="executing"></el-option>
 | ||
|               <el-option label="已延期" value="delayed"></el-option>
 | ||
|               <el-option label="已完成" value="completed"></el-option>
 | ||
|             </el-select>
 | ||
|           </div>
 | ||
|           <div class="filter-item">
 | ||
|             <el-select v-model="planType" placeholder="全部计划">
 | ||
|               <el-option label="每日巡检计划" value="daily"></el-option>
 | ||
|               <el-option label="每周巡检计划" value="weekly"></el-option>
 | ||
|               <el-option label="每月巡检计划" value="monthly"></el-option>
 | ||
|             </el-select>
 | ||
|           </div>
 | ||
|           <div class="filter-item">
 | ||
|             <el-input v-model="executor" placeholder="执行人"></el-input>
 | ||
|           </div>
 | ||
|           <div class="filter-actions">
 | ||
|             <el-button type="primary" icon="Search" class="search-btn" @click="handleSearch"> 搜索 </el-button>
 | ||
|             <el-button type="primary" icon="Plus" class="create-btn" @click="handleCreateTask"> 手动创建任务 </el-button>
 | ||
|           </div>
 | ||
|         </div>
 | ||
|       </div>
 | ||
| 
 | ||
|       <!-- 任务卡片列表 -->
 | ||
|       <div class="task-cards">
 | ||
|         <div class="task-card" v-for="(task, index) in pagedTasks" :key="index" :class="task.statusClass">
 | ||
|           <div class="task-header">
 | ||
|             <div class="task-title">
 | ||
|               {{ task.title }}
 | ||
|             </div>
 | ||
|             <div class="task-status" :class="task.tagClass">
 | ||
|               {{ task.statusText }}
 | ||
|             </div>
 | ||
|           </div>
 | ||
| 
 | ||
|           <div class="task-details">
 | ||
|             <div class="detail-item">
 | ||
|               <span class="detail-label">计划时间</span>
 | ||
|               <span class="detail-value">{{ task.planTime }}</span>
 | ||
|             </div>
 | ||
|             <div class="detail-item">
 | ||
|               <span class="detail-label">巡检对象</span>
 | ||
|               <span class="detail-value">{{ task.target }}</span>
 | ||
|             </div>
 | ||
|             <div class="detail-item">
 | ||
|               <span class="detail-label">执行人</span>
 | ||
|               <span class="detail-value">{{ task.executor }}</span>
 | ||
|             </div>
 | ||
|             <div class="detail-item">
 | ||
|               <span class="detail-label">关联计划</span>
 | ||
|               <span class="detail-value">{{ task.relatedPlan }}</span>
 | ||
|             </div>
 | ||
| 
 | ||
|             <!-- 特定状态的额外信息 -->
 | ||
|             <div v-if="task.status === 'delayed'" class="delay-reason">
 | ||
|               <span class="detail-label">延期原因</span>
 | ||
|               <span class="detail-value">{{ task.delayReason }}</span>
 | ||
|             </div>
 | ||
| 
 | ||
|             <div v-if="task.status === 'executing'" class="progress-container">
 | ||
|               <span class="detail-label">完成进度</span>
 | ||
|               <div class="progress-bar" style="flex: 1; padding-left: 0; margin-top: 0">
 | ||
|                 <el-progress :percentage="task.progress" stroke-width="6" :stroke-color="task.progressColor"></el-progress>
 | ||
|               </div>
 | ||
|             </div>
 | ||
| 
 | ||
|             <div v-if="task.status === 'completed'" class="task-result">
 | ||
|               <span class="detail-label">结果</span>
 | ||
|               <span class="detail-value" :class="task.resultClass">{{ task.result }}</span>
 | ||
|             </div>
 | ||
|           </div>
 | ||
| 
 | ||
|           <div class="task-actions">
 | ||
|             <el-button type="text" class="action-btn view-btn" @click="handleView(task)"> 详情 </el-button>
 | ||
|             <el-button type="primary" :class="task.actionClass" @click="handleAction(task)">
 | ||
|               {{ task.actionText }}
 | ||
|             </el-button>
 | ||
|           </div>
 | ||
|         </div>
 | ||
| 
 | ||
|         <!-- 无搜索结果提示 -->
 | ||
|         <div v-if="pagedTasks.length === 0 && tasks.length === 0" class="no-results">
 | ||
|           <div class="no-results-icon">
 | ||
|             <i class="el-icon-search"></i>
 | ||
|           </div>
 | ||
|           <div class="no-results-text">未找到符合条件的任务</div>
 | ||
|           <el-button type="text" @click="resetFilters">清除筛选条件</el-button>
 | ||
|         </div>
 | ||
|       </div>
 | ||
| 
 | ||
|       <!-- 分页区域 -->
 | ||
|       <div class="pagination-section" v-if="tasks.length > 0">
 | ||
|         <div class="pagination-controls">
 | ||
|           <el-pagination
 | ||
|             @size-change="handleSizeChange"
 | ||
|             @current-change="handleCurrentChange"
 | ||
|             :current-page="currentPage"
 | ||
|             :page-sizes="[8, 12, 16, 20]"
 | ||
|             :page-size="pageSize"
 | ||
|             layout="prev, pager, next, jumper, total"
 | ||
|             :total="total"
 | ||
|             background
 | ||
|           >
 | ||
|           </el-pagination>
 | ||
|         </div>
 | ||
|       </div>
 | ||
| 
 | ||
|       <!-- 添加新任务弹窗 -->
 | ||
|       <el-dialog v-model="createTaskDialogVisible" title="添加新任务" width="750px" :before-close="handleCancelCreateTask">
 | ||
|         <el-form ref="createTaskFormRef" :model="createTaskForm" :rules="createTaskRules" label-width="80px">
 | ||
|           <el-form-item label="任务名称" prop="taskName">
 | ||
|             <el-input v-model="createTaskForm.taskName" placeholder="输入任务名称" />
 | ||
|           </el-form-item>
 | ||
| 
 | ||
|           <el-form-item label="巡检对象" prop="inspectionTarget">
 | ||
|             <el-input v-model="createTaskForm.inspectionTarget" placeholder="输入巡检内容" />
 | ||
|           </el-form-item>
 | ||
| 
 | ||
|           <el-form-item label="时间" prop="timeRange">
 | ||
|             <el-date-picker
 | ||
|               v-model="createTaskForm.timeRange"
 | ||
|               type="datetimerange"
 | ||
|               start-placeholder="开始时间"
 | ||
|               end-placeholder="结束时间"
 | ||
|               format="YYYY-MM-DD HH:mm"
 | ||
|               value-format="YYYY-MM-DD HH:mm"
 | ||
|               placeholder="选择时间范围"
 | ||
|             />
 | ||
|           </el-form-item>
 | ||
| 
 | ||
|           <el-form-item label="关联计划">
 | ||
|             <el-select v-model="createTaskForm.relatedPlan" placeholder="选择关联计划">
 | ||
|               <el-option v-for="plan in planList" :key="plan.value" :label="plan.label" :value="plan.value" />
 | ||
|             </el-select>
 | ||
|           </el-form-item>
 | ||
| 
 | ||
|           <el-form-item label="执行人" prop="executor">
 | ||
|             <el-select v-model="createTaskForm.executor" placeholder="选择执行人">
 | ||
|               <el-option v-for="user in userList" :key="user.value" :label="user.label" :value="user.value" />
 | ||
|             </el-select>
 | ||
|           </el-form-item>
 | ||
| 
 | ||
|           <el-form-item label="开始时间" prop="workTimeRange1">
 | ||
|             <el-time-picker
 | ||
|               v-model="createTaskForm.workTimeRange1"
 | ||
|               type="timerange"
 | ||
|               start-placeholder="开始时间"
 | ||
|               end-placeholder="结束时间"
 | ||
|               format="HH:mm"
 | ||
|               value-format="HH:mm"
 | ||
|               class="form-input"
 | ||
|               style="width: 100%"
 | ||
|             />
 | ||
|           </el-form-item>
 | ||
| 
 | ||
|           <el-form-item label="结束时间" prop="workTimeRange2">
 | ||
|             <el-time-picker
 | ||
|               v-model="createTaskForm.workTimeRange2"
 | ||
|               type="timerange"
 | ||
|               start-placeholder="开始时间"
 | ||
|               end-placeholder="结束时间"
 | ||
|               format="HH:mm"
 | ||
|               value-format="HH:mm"
 | ||
|               class="form-input"
 | ||
|               style="width: 100%"
 | ||
|             />
 | ||
|           </el-form-item>
 | ||
| 
 | ||
|           <el-form-item label="任务类型" prop="taskType" style="display: none">
 | ||
|             <el-select v-model="createTaskForm.taskType" placeholder="选择任务类型">
 | ||
|               <el-option label="待执行" value="1" />
 | ||
|               <el-option label="已延期" value="2" />
 | ||
|               <el-option label="执行中" value="3" />
 | ||
|               <el-option label="已完成" value="4" />
 | ||
|             </el-select>
 | ||
|           </el-form-item>
 | ||
| 
 | ||
|           <!-- <el-form-item label="问题类型" prop="problemType">
 | ||
|             <el-select v-model="createTaskForm.problemType" placeholder="选择问题类型">
 | ||
|               <el-option label="磁盘使用率" value="1" />
 | ||
|               <el-option label="内存使用率" value="2" />
 | ||
|               <el-option label="服务状态" value="3" />
 | ||
|               <el-option label="响应时间" value="4" />
 | ||
|               <el-option label="设备运行状态" value="5" />
 | ||
|             </el-select>
 | ||
|           </el-form-item> -->
 | ||
| 
 | ||
|           <!-- 步骤条 -->
 | ||
|           <el-form-item label="执行步骤" class="form-item" style="width: 100%">
 | ||
|             <div class="steps-container">
 | ||
|               <div class="step-item" v-for="(step, index) in createTaskForm.steps" :key="index">
 | ||
|                 <div class="step-number">{{ index + 1 }}</div>
 | ||
|                 <el-input v-model="step.name" placeholder="输入步骤名称" style="flex: 1; margin-right: 10px" />
 | ||
|                 <el-input v-model="step.intendedPurpose" placeholder="输入预期目的" style="flex: 1; margin-right: 10px" />
 | ||
|                 <el-date-picker
 | ||
|                   v-model="step.intendedTime"
 | ||
|                   type="datetime"
 | ||
|                   placeholder="选择计划时间"
 | ||
|                   format="YYYY-MM-DD HH:mm"
 | ||
|                   value-format="YYYY-MM-DD HH:mm"
 | ||
|                   style="width: 180px; margin-right: 10px"
 | ||
|                 />
 | ||
|                 <el-button v-if="createTaskForm.steps.length > 1" type="text" @click="removeStep(index)" style="color: #f56c6c"> 删除 </el-button>
 | ||
|               </div>
 | ||
|               <el-button type="text" class="add-step-btn" @click="addStep">添加步骤</el-button>
 | ||
|             </div>
 | ||
|           </el-form-item>
 | ||
|         </el-form>
 | ||
| 
 | ||
|         <template #footer>
 | ||
|           <span class="dialog-footer">
 | ||
|             <el-button @click="handleCancelCreateTask">取消</el-button>
 | ||
|             <el-button type="primary" @click="handleSaveTask">保存任务</el-button>
 | ||
|           </span>
 | ||
|         </template>
 | ||
|       </el-dialog>
 | ||
| 
 | ||
|       <!-- 任务详情弹窗 -->
 | ||
|       <el-dialog v-model="detailDialogVisible" title="任务详情" width="800px" :before-close="handleCloseDetailDialog">
 | ||
|         <div v-if="detailData" class="task-detail-container">
 | ||
|           <!-- 加载状态骨架屏 -->
 | ||
|           <div v-if="isDetailLoading" class="skeleton-loading">
 | ||
|             <div class="skeleton-card">
 | ||
|               <div class="skeleton-header"></div>
 | ||
|               <div class="skeleton-content">
 | ||
|                 <div class="skeleton-row"></div>
 | ||
|                 <div class="skeleton-row"></div>
 | ||
|                 <div class="skeleton-row"></div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
|             <div class="skeleton-card">
 | ||
|               <div class="skeleton-header"></div>
 | ||
|               <div class="skeleton-content">
 | ||
|                 <div class="skeleton-row"></div>
 | ||
|                 <div class="skeleton-row"></div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
|             <div class="skeleton-card">
 | ||
|               <div class="skeleton-header"></div>
 | ||
|               <div class="skeleton-content">
 | ||
|                 <div class="skeleton-row"></div>
 | ||
|                 <div class="skeleton-row"></div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
|           </div>
 | ||
| 
 | ||
|           <!-- 任务基本信息卡片 -->
 | ||
|           <div class="detail-card">
 | ||
|             <h3 class="card-title">任务基本信息</h3>
 | ||
|             <div class="card-content">
 | ||
|               <div class="info-row">
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">任务名称:</span>
 | ||
|                   <span class="info-value">{{ detailData.taskName || '未命名' }}</span>
 | ||
|                 </div>
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">任务状态:</span>
 | ||
|                   <span class="info-value" :class="getStatusClass(detailData.taskType)">{{ getStatusText(detailData.taskType) }}</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|               <div class="info-row">
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">巡检对象:</span>
 | ||
|                   <span class="info-value">{{ detailData.inspectionObject || '-' }}</span>
 | ||
|                 </div>
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">完成进度:</span>
 | ||
|                   <span class="info-value">{{ detailData.taskProgress || 0 }}%</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|               <div class="info-row">
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">开始时间:</span>
 | ||
|                   <span class="info-value">{{ detailData.beginTime || '-' }}</span>
 | ||
|                 </div>
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">结束时间:</span>
 | ||
|                   <span class="info-value">{{ detailData.endTime || '-' }}</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|               <div class="info-row">
 | ||
|                 <div class="info-item full-width">
 | ||
|                   <span class="info-label">工作时间段:</span>
 | ||
|                   <span class="info-value">{{ detailData.timeInfo ? detailData.timeInfo.replace(/,/g, '—') : '-' }}</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
|           </div>
 | ||
| 
 | ||
|           <!-- 执行人信息卡片 -->
 | ||
|           <div class="detail-card">
 | ||
|             <h3 class="card-title">执行人信息</h3>
 | ||
|             <div class="card-content">
 | ||
|               <div class="info-row">
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">姓名:</span>
 | ||
|                   <span class="info-value">{{ detailData.person?.userName || '-' }}</span>
 | ||
|                 </div>
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">联系电话:</span>
 | ||
|                   <span class="info-value">{{ detailData.person?.phonenumber || '-' }}</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|               <div class="info-row">
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">性别:</span>
 | ||
|                   <span class="info-value">{{ detailData.person?.sex === '1' ? '男' : detailData.person?.sex === '2' ? '女' : '-' }}</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
|           </div>
 | ||
| 
 | ||
|           <!-- 关联计划信息卡片 -->
 | ||
|           <div class="detail-card">
 | ||
|             <h3 class="card-title">关联计划信息</h3>
 | ||
|             <div class="card-content">
 | ||
|               <div class="info-row">
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">计划名称:</span>
 | ||
|                   <span class="info-value">{{ detailData.plan?.planName || '-' }}</span>
 | ||
|                 </div>
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">计划编号:</span>
 | ||
|                   <span class="info-value">{{ detailData.plan?.id || '-' }}</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
| 
 | ||
|               <div class="info-row">
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">巡检频率:</span>
 | ||
|                   <span class="info-value">{{ detailData.plan?.inspectionFrequency || '-' }}</span>
 | ||
|                 </div>
 | ||
|                 <div class="info-item">
 | ||
|                   <span class="info-label">计划时长:</span>
 | ||
|                   <span class="info-value">{{ detailData.plan?.duration || '-' }}分钟</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|               <div class="info-row">
 | ||
|                 <div class="info-item full-width">
 | ||
|                   <span class="info-label">计划时间:</span>
 | ||
|                   <span class="info-value">{{
 | ||
|                     detailData.plan?.beginTime && detailData.plan?.endTime ? `${detailData.plan.beginTime} — ${detailData.plan.endTime}` : '-'
 | ||
|                   }}</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
|           </div>
 | ||
| 
 | ||
|           <!-- 执行步骤信息卡片 -->
 | ||
|           <div v-if="detailData.nodes && detailData.nodes.length > 0" class="detail-card">
 | ||
|             <h3 class="card-title">执行步骤</h3>
 | ||
|             <div class="steps-container">
 | ||
|               <div v-for="(node, index) in detailData.nodes" :key="node.id || index" class="step-item">
 | ||
|                 <div class="step-number">{{ node.code || index + 1 }}</div>
 | ||
|                 <div class="step-info">
 | ||
|                   <div class="step-name">{{ node.name || '未命名步骤' }}</div>
 | ||
|                   <div class="step-purpose">{{ node.intendedPurpose || '无说明' }}</div>
 | ||
|                   <div class="step-time">计划时间:{{ formatDateTime(node.intendedTime) }}</div>
 | ||
|                   <div v-if="node.finishTime" class="step-finish-time">完成时间:{{ formatDateTime(node.finishTime) }}</div>
 | ||
|                   <div v-if="node.remark" class="step-remark">备注:{{ node.remark }}</div>
 | ||
|                 </div>
 | ||
|                 <div class="step-status" :class="getStatusClass(node.status)">
 | ||
|                   {{ node.status === '2' ? '未完成' : '已完成' }}
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
|           </div>
 | ||
| 
 | ||
|           <!-- 执行结果信息卡片 -->
 | ||
|           <div v-if="detailData.taskType === '2' || detailData.taskType === 2" class="detail-card">
 | ||
|             <h3 class="card-title">延期信息</h3>
 | ||
|             <div class="card-content">
 | ||
|               <div class="info-row">
 | ||
|                 <div class="info-item full-width">
 | ||
|                   <span class="info-label">延期原因:</span>
 | ||
|                   <span class="info-value fail-reason">{{ detailData.delayReason || '未说明原因' }}</span>
 | ||
|                 </div>
 | ||
|               </div>
 | ||
|             </div>
 | ||
|           </div>
 | ||
|         </div>
 | ||
|         <div v-else class="loading-state">
 | ||
|           <i class="el-icon-loading el-icon--loading"></i>
 | ||
|           <span>加载中...</span>
 | ||
|         </div>
 | ||
| 
 | ||
|         <template #footer>
 | ||
|           <span class="dialog-footer">
 | ||
|             <el-button @click="handleCloseDetailDialog">关闭</el-button>
 | ||
|           </span>
 | ||
|         </template>
 | ||
|       </el-dialog>
 | ||
|     </div>
 | ||
|   </div>
 | ||
| </template>
 | ||
| 
 | ||
| <script setup>
 | ||
| import { ref, computed, onMounted } from 'vue';
 | ||
| import router from '@/router';
 | ||
| 
 | ||
| import { xjrenwuDetail, xjrenwulist, addxjrenwu, updatexjrenwu } from '@/api/zhinengxunjian/xunjian/renwu';
 | ||
| import { xunjianUserlist, xunjianlist } from '@/api/zhinengxunjian/xunjian/index';
 | ||
| import { addjiedian } from '@/api/zhinengxunjian/jiedian/index';
 | ||
| import { ElMessage, ElLoading, ElForm } from 'element-plus';
 | ||
| 
 | ||
| // 筛选条件
 | ||
| const taskStatus = ref('');
 | ||
| const planType = ref('');
 | ||
| const executor = ref('');
 | ||
| 
 | ||
| // 任务数据 - 初始为空数组,通过API获取
 | ||
| const tasks = ref([]);
 | ||
| 
 | ||
| // 任务详情弹窗相关变量
 | ||
| const detailDialogVisible = ref(false);
 | ||
| const detailData = ref(null);
 | ||
| const isDetailLoading = ref(false);
 | ||
| 
 | ||
| // 根据任务状态获取对应的文本
 | ||
| const getStatusText = (status) => {
 | ||
|   // 处理可能的数字输入
 | ||
|   const statusStr = status?.toString() || '';
 | ||
|   const statusMap = {
 | ||
|     '1': '待执行',
 | ||
|     '2': '已延期',
 | ||
|     '3': '执行中',
 | ||
|     '4': '已完成'
 | ||
|   };
 | ||
|   return statusMap[statusStr] || '未知状态';
 | ||
| };
 | ||
| 
 | ||
| // 根据任务状态获取对应的样式类
 | ||
| const getStatusClass = (status) => {
 | ||
|   // 处理可能的数字输入
 | ||
|   const statusStr = status?.toString() || '';
 | ||
|   const statusClassMap = {
 | ||
|     '1': 'status-pending',
 | ||
|     '2': 'status-delayed',
 | ||
|     '3': 'status-executing',
 | ||
|     '4': 'status-completed'
 | ||
|   };
 | ||
|   return statusClassMap[statusStr] || 'status-unknown';
 | ||
| };
 | ||
| 
 | ||
| // 格式化日期时间
 | ||
| const formatDateTime = (dateTime) => {
 | ||
|   if (!dateTime) return '-';
 | ||
|   try {
 | ||
|     const date = new Date(dateTime);
 | ||
|     const year = date.getFullYear();
 | ||
|     const month = String(date.getMonth() + 1).padStart(2, '0');
 | ||
|     const day = String(date.getDate()).padStart(2, '0');
 | ||
|     const hours = String(date.getHours()).padStart(2, '0');
 | ||
|     const minutes = String(date.getMinutes()).padStart(2, '0');
 | ||
|     return `${year}-${month}-${day} ${hours}:${minutes}`;
 | ||
|   } catch (error) {
 | ||
|     return dateTime;
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| // 获取步骤状态文本
 | ||
| const getStepStatusText = (status) => {
 | ||
|   const statusStr = status?.toString() || '';
 | ||
|   const statusMap = {
 | ||
|     '1': '待执行',
 | ||
|     '2': '执行中',
 | ||
|     '3': '已完成',
 | ||
|     '4': '已延期'
 | ||
|   };
 | ||
|   return statusMap[statusStr] || '未知状态';
 | ||
| };
 | ||
| 
 | ||
| // 状态映射配置
 | ||
| const statusConfig = {
 | ||
|   pending: {
 | ||
|     statusText: '待执行',
 | ||
|     statusClass: 'card-pending',
 | ||
|     tagClass: 'tag-pending',
 | ||
|     actionText: '开始执行',
 | ||
|     actionClass: 'start-btn'
 | ||
|   },
 | ||
|   delayed: {
 | ||
|     statusText: '已延期',
 | ||
|     statusClass: 'card-delayed',
 | ||
|     tagClass: 'tag-delayed',
 | ||
|     actionText: '重新安排',
 | ||
|     actionClass: 'reschedule-btn'
 | ||
|   },
 | ||
|   executing: {
 | ||
|     statusText: '执行中',
 | ||
|     statusClass: 'card-executing',
 | ||
|     tagClass: 'tag-executing',
 | ||
|     actionText: '完成',
 | ||
|     actionClass: 'complete-btn'
 | ||
|   },
 | ||
|   completed: {
 | ||
|     statusText: '已完成',
 | ||
|     statusClass: 'card-completed',
 | ||
|     tagClass: 'tag-completed',
 | ||
|     actionText: '查看报告',
 | ||
|     actionClass: 'report-btn'
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| // 获取巡检任务数据
 | ||
| const getTaskList = async () => {
 | ||
|   // 显示加载状态
 | ||
|   const loading = ElLoading.service({
 | ||
|     lock: true,
 | ||
|     text: '加载任务中...',
 | ||
|     background: 'rgba(255, 255, 255, 0.7)'
 | ||
|   });
 | ||
| 
 | ||
|   try {
 | ||
|     // 构建搜索参数,包含所有筛选条件
 | ||
|     const params = {
 | ||
|       pageSize: pageSize.value,
 | ||
|       pageNum: currentPage.value,
 | ||
|       personId: 1,
 | ||
|       taskType: taskStatus.value || undefined, // 任务状态
 | ||
|       planType: planType.value || undefined, // 计划类型
 | ||
|       personName: executor.value || undefined // 执行人
 | ||
|     };
 | ||
| 
 | ||
|     const response = await xjrenwulist(params);
 | ||
| 
 | ||
|     if (response.code === 200 && response.rows) {
 | ||
|       tasks.value = response.rows.map((item) => {
 | ||
|         // 获取原始数据中的id
 | ||
|         const taskId = item.id || '';
 | ||
|         if (!taskId) {
 | ||
|           console.warn('当前任务缺少id,数据:', item);
 | ||
|         }
 | ||
| 
 | ||
|         // 根据taskType获取对应的status
 | ||
|         let taskStatus = 'pending';
 | ||
|         if (item.taskType === '1' || item.taskType === 1) {
 | ||
|           taskStatus = 'pending';
 | ||
|         } else if (item.taskType === '2' || item.taskType === 2) {
 | ||
|           taskStatus = 'delayed';
 | ||
|         } else if (item.taskType === '3' || item.taskType === 3) {
 | ||
|           taskStatus = 'executing';
 | ||
|         } else if (item.taskType === '4' || item.taskType === 4) {
 | ||
|           taskStatus = 'completed';
 | ||
|         }
 | ||
| 
 | ||
|         const config = statusConfig[taskStatus] || statusConfig.pending;
 | ||
| 
 | ||
|         // 构建任务对象
 | ||
|         const task = {
 | ||
|           id: taskId,
 | ||
|           title: item.taskName || '未命名任务',
 | ||
|           status: taskStatus,
 | ||
|           statusText: config.statusText,
 | ||
|           statusClass: config.statusClass,
 | ||
|           tagClass: config.tagClass,
 | ||
|           planTime: item.beginTime || '',
 | ||
|           target: item.inspectionObject || '',
 | ||
|           executor: item.person?.userName || '未知用户',
 | ||
|           relatedPlan: item.plan?.planName || '未知计划',
 | ||
|           actionText: config.actionText,
 | ||
|           actionClass: config.actionClass,
 | ||
|           rawData: item
 | ||
|         };
 | ||
| 
 | ||
|         // 补充其他状态字段
 | ||
|         if (task.status === 'delayed') {
 | ||
|           task.delayReason = item.delayReason || '未说明原因';
 | ||
|         } else if (task.status === 'executing') {
 | ||
|           task.progress = item.taskProgress || 0;
 | ||
|           task.progressColor = '#FF7D00';
 | ||
|         } else if (task.status === 'completed') {
 | ||
|           task.result = item.result || '正常';
 | ||
|           task.resultClass = item.result === '异常' ? 'result-abnormal' : 'result-normal';
 | ||
|         }
 | ||
| 
 | ||
|         return task;
 | ||
|       });
 | ||
| 
 | ||
|       total.value = response.total || tasks.value.length;
 | ||
| 
 | ||
|       // 搜索后如果没有结果,显示提示信息
 | ||
|       if (tasks.value.length === 0) {
 | ||
|         ElMessage.info('未找到符合条件的任务');
 | ||
|       }
 | ||
|     }
 | ||
|   } catch (error) {
 | ||
|     console.error('获取巡检任务数据失败:', error);
 | ||
|     ElMessage.error('获取任务列表失败');
 | ||
|   } finally {
 | ||
|     // 关闭加载状态
 | ||
|     loading.close();
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| // 页面加载时获取数据
 | ||
| onMounted(() => {
 | ||
|   getTaskList();
 | ||
| });
 | ||
| 
 | ||
| // 分页相关
 | ||
| const currentPage = ref(1);
 | ||
| const pageSize = ref(8);
 | ||
| const total = ref(tasks.value.length);
 | ||
| 
 | ||
| // 状态排序映射
 | ||
| const statusOrder = {
 | ||
|   pending: 0, // 待完成
 | ||
|   delayed: 1, // 已延期
 | ||
|   executing: 2, // 执行中
 | ||
|   completed: 3 // 已完成
 | ||
| };
 | ||
| 
 | ||
| // 分页处理后的数据(含排序)
 | ||
| const pagedTasks = computed(() => {
 | ||
|   // 先按状态排序
 | ||
|   const sortedTasks = [...tasks.value].sort((a, b) => {
 | ||
|     return statusOrder[a.status] - statusOrder[b.status];
 | ||
|   });
 | ||
| 
 | ||
|   // 再进行分页
 | ||
|   const startIndex = (currentPage.value - 1) * pageSize.value;
 | ||
|   const endIndex = startIndex + pageSize.value;
 | ||
|   return sortedTasks.slice(startIndex, endIndex);
 | ||
| });
 | ||
| 
 | ||
| // 搜索处理
 | ||
| const handleSearch = () => {
 | ||
|   currentPage.value = 1; // 重置到第一页
 | ||
|   getTaskList(); // 重新获取数据
 | ||
| };
 | ||
| 
 | ||
| // 重置筛选条件
 | ||
| const resetFilters = () => {
 | ||
|   taskStatus.value = '';
 | ||
|   planType.value = '';
 | ||
|   executor.value = '';
 | ||
|   currentPage.value = 1;
 | ||
|   getTaskList();
 | ||
| };
 | ||
| 
 | ||
| // 创建任务弹窗相关
 | ||
| const createTaskDialogVisible = ref(false);
 | ||
| const createTaskForm = ref({
 | ||
|   taskName: '',
 | ||
|   inspectionTarget: '',
 | ||
|   timeRange: [],
 | ||
|   workTimeRange1: null,
 | ||
|   workTimeRange2: null,
 | ||
|   relatedPlan: '',
 | ||
|   executor: '',
 | ||
|   taskType: '1', // 默认待执行
 | ||
|   // problemType: '',
 | ||
|   steps: [{ name: '', intendedPurpose: '', intendedTime: '' }] // 任务步骤数组
 | ||
| });
 | ||
| 
 | ||
| const createTaskRules = {
 | ||
|   taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
 | ||
|   inspectionTarget: [{ required: true, message: '请输入巡检对象', trigger: 'blur' }],
 | ||
|   timeRange: [{ required: true, message: '请选择时间范围', trigger: 'change' }],
 | ||
|   workTimeRange1: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
 | ||
|   taskType: [{ required: true, message: '请选择任务类型', trigger: 'change' }]
 | ||
| };
 | ||
| 
 | ||
| // 创建任务
 | ||
| const handleCreateTask = () => {
 | ||
|   openCreateTaskDialog();
 | ||
| };
 | ||
| 
 | ||
| // 重置步骤表单
 | ||
| const resetStepForm = () => {
 | ||
|   Object.keys(stepForm).forEach((key) => {
 | ||
|     stepForm[key] = '';
 | ||
|   });
 | ||
|   currentStep.value = 0;
 | ||
|   if (stepFormRef.value) stepFormRef.value.resetFields();
 | ||
|   if (deviceFormRef.value) deviceFormRef.value.resetFields();
 | ||
|   if (faultFormRef.value) faultFormRef.value.resetFields();
 | ||
| };
 | ||
| 
 | ||
| // 构建timeInfo字符串
 | ||
| const getTaskTimeInfoString = () => {
 | ||
|   const timeInfoArray = [];
 | ||
| 
 | ||
|   // 处理工作时间段1
 | ||
|   if (createTaskForm.value.workTimeRange1) {
 | ||
|     if (Array.isArray(createTaskForm.value.workTimeRange1)) {
 | ||
|       timeInfoArray.push(createTaskForm.value.workTimeRange1.join('-'));
 | ||
|     } else if (typeof createTaskForm.value.workTimeRange1 === 'string') {
 | ||
|       timeInfoArray.push(createTaskForm.value.workTimeRange1);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // 处理工作时间段2
 | ||
|   if (createTaskForm.value.workTimeRange2) {
 | ||
|     if (Array.isArray(createTaskForm.value.workTimeRange2)) {
 | ||
|       timeInfoArray.push(createTaskForm.value.workTimeRange2.join('-'));
 | ||
|     } else if (typeof createTaskForm.value.workTimeRange2 === 'string') {
 | ||
|       timeInfoArray.push(createTaskForm.value.workTimeRange2);
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   return timeInfoArray.join(',');
 | ||
| };
 | ||
| 
 | ||
| // 添加步骤
 | ||
| const addStep = () => {
 | ||
|   createTaskForm.value.steps.push({ name: '', intendedPurpose: '', intendedTime: '' });
 | ||
| };
 | ||
| 
 | ||
| // 删除步骤
 | ||
| const removeStep = (index) => {
 | ||
|   // 确保至少保留一个步骤
 | ||
|   if (createTaskForm.value.steps.length <= 1) {
 | ||
|     ElMessage.warning('至少需要保留一个步骤');
 | ||
|     return;
 | ||
|   }
 | ||
|   createTaskForm.value.steps.splice(index, 1);
 | ||
| };
 | ||
| 
 | ||
| // 保存任务
 | ||
| const handleSaveTask = async () => {
 | ||
|   // 表单验证
 | ||
|   if (!createTaskForm.value.taskName || !createTaskForm.value.inspectionTarget || !createTaskForm.value.timeRange) {
 | ||
|     ElMessage.warning('请填写必要的任务信息');
 | ||
|     return;
 | ||
|   }
 | ||
| 
 | ||
|   // 验证所有步骤
 | ||
|   const hasEmptyStep = createTaskForm.value.steps.some((step) => !step.name.trim() || !step.intendedPurpose.trim());
 | ||
|   if (hasEmptyStep) {
 | ||
|     ElMessage.warning('请填写完整所有步骤信息');
 | ||
|     return;
 | ||
|   }
 | ||
| 
 | ||
|   try {
 | ||
|     // 获取timeInfo字符串
 | ||
|     const taskTimeInfo = getTaskTimeInfoString();
 | ||
| 
 | ||
|     if (!taskTimeInfo) {
 | ||
|       ElMessage.warning('请至少填写一个工作时间段');
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     // 准备步骤数据,与工单列表页面保持一致的格式
 | ||
|     const stepsData = createTaskForm.value.steps
 | ||
|       .filter((step) => step.name.trim() && step.intendedPurpose.trim())
 | ||
|       .map((step, index) => ({
 | ||
|         createTime: new Date().toISOString(),
 | ||
|         updateTime: new Date().toISOString(),
 | ||
|         params: {},
 | ||
|         module: 2,
 | ||
|         code: index + 1,
 | ||
|         name: step.name,
 | ||
|         intendedPurpose: step.intendedPurpose,
 | ||
|         intendedTime: step.intendedTime ? new Date(step.intendedTime).toISOString() : new Date().toISOString(),
 | ||
|         finishTime: '',
 | ||
|         remark: '',
 | ||
|         status: 2
 | ||
|       }));
 | ||
| 
 | ||
|     // 调用添加节点接口,直接传递步骤数组
 | ||
|     const jiedianResponse = await addjiedian(stepsData);
 | ||
| 
 | ||
|     if (jiedianResponse.code !== 200) {
 | ||
|       ElMessage.error('创建步骤失败');
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     // 获取返回的ids,实际返回格式中msg字段包含ids字符串,data为null
 | ||
|     let nodeIds = '';
 | ||
|     if (jiedianResponse.code === 200 && jiedianResponse.msg) {
 | ||
|       nodeIds = jiedianResponse.msg;
 | ||
|     } else {
 | ||
|       ElMessage.warning('未获取到有效的步骤ID');
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     // 构建接口所需的数据结构
 | ||
|     const apiData = {
 | ||
|       projectId: 1,
 | ||
| 
 | ||
|       createTime: new Date().toISOString(),
 | ||
|       updateTime: new Date().toISOString(),
 | ||
|       startTime: new Date().toISOString().slice(0, 19).replace('T', ' '),
 | ||
|       params: {
 | ||
|         property1: 'string',
 | ||
|         property2: 'string'
 | ||
|       },
 | ||
| 
 | ||
|       taskName: createTaskForm.value.taskName,
 | ||
|       inspectionObject: createTaskForm.value.inspectionTarget,
 | ||
|       beginTime: createTaskForm.value.timeRange[0] ? new Date(createTaskForm.value.timeRange[0]).toISOString() : '',
 | ||
|       endTime: createTaskForm.value.timeRange[1] ? new Date(createTaskForm.value.timeRange[1]).toISOString() : '',
 | ||
|       timeInfo: taskTimeInfo,
 | ||
|       planId: createTaskForm.value.relatedPlan !== 'all' ? getPlanId(createTaskForm.value.relatedPlan) : 0,
 | ||
|       personId: createTaskForm.value.executor !== 'all' ? createTaskForm.value.executor : 0,
 | ||
|       taskProgress: 0,
 | ||
|       taskType: createTaskForm.value.taskType,
 | ||
|       // problemType: createTaskForm.value.problemType,
 | ||
|       nodeIds: nodeIds // 添加步骤ID字符串,与工单列表页面保持一致
 | ||
|     };
 | ||
| 
 | ||
|     // 调用新增任务接口
 | ||
|     const response = await addxjrenwu(apiData);
 | ||
| 
 | ||
|     if (response.code === 200) {
 | ||
|       ElMessage.success('任务创建成功');
 | ||
|       // 关闭弹窗
 | ||
|       createTaskDialogVisible.value = false;
 | ||
|       // 重置表单
 | ||
|       createTaskForm.value = {
 | ||
|         taskName: '',
 | ||
|         inspectionTarget: '',
 | ||
|         timeRange: [],
 | ||
|         workTimeRange1: null,
 | ||
|         workTimeRange2: null,
 | ||
|         relatedPlan: '',
 | ||
|         executor: '',
 | ||
|         taskType: '1',
 | ||
|         // problemType: '',
 | ||
|         steps: [{ name: '', intendedPurpose: '', intendedTime: '' }]
 | ||
|       };
 | ||
|       // 重新获取任务列表
 | ||
|       getTaskList();
 | ||
|     } else {
 | ||
|       ElMessage.error(`创建失败: ${response.msg || '未知错误'}`);
 | ||
|     }
 | ||
|   } catch (error) {
 | ||
|     console.error('保存任务失败:', error);
 | ||
|     ElMessage.error('创建任务失败,请重试');
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| // 辅助函数:获取计划ID
 | ||
| const getPlanId = (planValue) => {
 | ||
|   if (planValue === 'all') {
 | ||
|     return 0;
 | ||
|   }
 | ||
|   return planValue;
 | ||
| };
 | ||
| 
 | ||
| // 负责人列表
 | ||
| const userList = ref([]);
 | ||
| // 计划列表
 | ||
| const planList = ref([]);
 | ||
| 
 | ||
| // 获取负责人列表
 | ||
| const getUsersList = async () => {
 | ||
|   try {
 | ||
|     const response = await xunjianUserlist();
 | ||
|     // 适配新接口格式:检查code为200且rows为数组
 | ||
|     const userRows = response.code === 200 && response.rows && Array.isArray(response.rows) ? response.rows : [];
 | ||
| 
 | ||
|     userList.value = userRows
 | ||
|       .filter((item) => item && typeof item === 'object')
 | ||
|       .map((item) => ({
 | ||
|         label: item.userName || '未知用户',
 | ||
|         value: String(item.userId || '') // 使用userId作为唯一标识
 | ||
|       }));
 | ||
| 
 | ||
|     if (userList.value.length === 0) {
 | ||
|       userList.value = [{ label: '默认用户', value: 'default' }];
 | ||
|     }
 | ||
|   } catch (error) {
 | ||
|     console.error('获取负责人列表失败:', error);
 | ||
|     userList.value = [{ label: '默认用户', value: 'default' }];
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| // 获取计划列表
 | ||
| const getPlansList = async () => {
 | ||
|   try {
 | ||
|     const response = await xunjianlist();
 | ||
| 
 | ||
|     const planRows =
 | ||
|       response?.data?.rows && Array.isArray(response.data.rows)
 | ||
|         ? response.data.rows
 | ||
|         : response?.rows && Array.isArray(response.rows)
 | ||
|         ? response.rows
 | ||
|         : Array.isArray(response)
 | ||
|         ? response
 | ||
|         : [];
 | ||
| 
 | ||
|     planList.value = planRows
 | ||
|       .filter((item) => item && typeof item === 'object')
 | ||
|       .map((item) => ({
 | ||
|         label: item.planName || `计划${item.id}`,
 | ||
|         value: item.id.toString()
 | ||
|       }));
 | ||
|   } catch (error) {
 | ||
|     console.error('获取计划列表失败:', error);
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| // 打开创建任务弹窗
 | ||
| const openCreateTaskDialog = async () => {
 | ||
|   await Promise.all([getUsersList(), getPlansList()]);
 | ||
|   createTaskDialogVisible.value = true;
 | ||
| };
 | ||
| 
 | ||
| // 取消创建任务
 | ||
| const handleCancelCreateTask = () => {
 | ||
|   createTaskDialogVisible.value = false;
 | ||
|   // 重置表单
 | ||
|   createTaskForm.value = {
 | ||
|     taskName: '',
 | ||
|     inspectionTarget: '',
 | ||
|     timeRange: [],
 | ||
|     workTimeRange1: null,
 | ||
|     workTimeRange2: null,
 | ||
|     relatedPlan: '',
 | ||
|     executor: '',
 | ||
|     taskType: '1',
 | ||
|     // problemType: '',
 | ||
|     steps: [{ name: '', intendedPurpose: '', intendedTime: '' }]
 | ||
|   };
 | ||
| };
 | ||
| 
 | ||
| // 分页事件
 | ||
| const handleSizeChange = (val) => {
 | ||
|   pageSize.value = val;
 | ||
|   currentPage.value = 1;
 | ||
|   getTaskList(); // 分页大小改变时重新获取数据
 | ||
| };
 | ||
| 
 | ||
| const handleCurrentChange = (val) => {
 | ||
|   currentPage.value = val;
 | ||
|   getTaskList(); // 页码改变时重新获取数据
 | ||
| };
 | ||
| 
 | ||
| // 查看任务详情
 | ||
| const handleView = async (task) => {
 | ||
|   try {
 | ||
|     if (!task || !task.id) {
 | ||
|       ElMessage.warning('任务ID不存在,请刷新页面重试');
 | ||
|       console.error('任务缺少有效id,任务数据:', task);
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     isDetailLoading.value = true;
 | ||
|     detailData.value = null;
 | ||
|     const response = await xjrenwuDetail(task.id);
 | ||
| 
 | ||
|     if (response.code === 200 && response.data) {
 | ||
|       detailData.value = response.data;
 | ||
|       detailDialogVisible.value = true;
 | ||
|     } else {
 | ||
|       const errorMsg = response.msg || '未知错误';
 | ||
|       console.error(`获取任务详情失败: ${errorMsg}`);
 | ||
|       ElMessage.error(`获取任务详情失败:${errorMsg}`);
 | ||
|     }
 | ||
|   } catch (error) {
 | ||
|     console.error('获取任务详情出错:', error);
 | ||
|     ElMessage.error('获取任务详情出错,请重试');
 | ||
|   } finally {
 | ||
|     isDetailLoading.value = false;
 | ||
|   }
 | ||
| };
 | ||
| 
 | ||
| // 关闭详情弹窗
 | ||
| const handleCloseDetailDialog = () => {
 | ||
|   detailDialogVisible.value = false;
 | ||
|   detailData.value = null;
 | ||
| };
 | ||
| 
 | ||
| const handleInspectionManagement1 = () => {
 | ||
|   router.push('/rili/InspectionManagement');
 | ||
| };
 | ||
| 
 | ||
| const handleInspectionManagement2 = () => {
 | ||
|   router.push('/rili/xunjianrenwu');
 | ||
| };
 | ||
| const handleInspectionManagement3 = () => {
 | ||
|   router.push('/rili/xunjianjihua');
 | ||
| };
 | ||
| const handleInspection1 = () => {
 | ||
|   router.push('/rili/rili');
 | ||
| };
 | ||
| const handleInspection2 = () => {
 | ||
|   router.push('/rili/InspectionManagement');
 | ||
| };
 | ||
| const handleInspection3 = () => {
 | ||
|   router.push('/rili/shiyanguanli');
 | ||
| };
 | ||
| const handleInspection4 = () => {
 | ||
|   router.push('/rili/baoxiuguanli');
 | ||
| };
 | ||
| const handleInspection5 = () => {
 | ||
|   router.push('/rili/qiangxiuguanli');
 | ||
| };
 | ||
| const handleInspection6 = () => {
 | ||
|   router.push('/rili/gongdanliebiao');
 | ||
| };
 | ||
| const handleInspection7 = () => {
 | ||
|   router.push('/rili/renyuanzhuangtai');
 | ||
| };
 | ||
| 
 | ||
| // 处理任务操作按钮点击事件
 | ||
| const handleAction = async (task) => {
 | ||
|   try {
 | ||
|     if (!task || !task.id) {
 | ||
|       ElMessage.warning('任务ID不存在,请刷新页面重试');
 | ||
|       console.error('任务缺少有效id,任务数据:', task);
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     // 如果是待执行状态,点击开始执行按钮
 | ||
|     if (task.status === 'pending' && task.actionText === '开始执行') {
 | ||
|       const taskIndex = tasks.value.findIndex((t) => t.id === task.id);
 | ||
|       if (taskIndex === -1) {
 | ||
|         ElMessage.warning('未找到该任务数据');
 | ||
|         return;
 | ||
|       }
 | ||
| 
 | ||
|       const originalTask = tasks.value[taskIndex];
 | ||
| 
 | ||
|       const updateData = {
 | ||
|         ...originalTask.rawData,
 | ||
|         id: task.id,
 | ||
|         startTime: new Date().toISOString().slice(0, 19).replace('T', ' '),
 | ||
|         taskType: '3', // 3表示执行中
 | ||
|         status: 'executing',
 | ||
|         taskProgress: 0
 | ||
|       };
 | ||
| 
 | ||
|       const response = await updatexjrenwu(updateData);
 | ||
| 
 | ||
|       if (response.code === 200) {
 | ||
|         ElMessage.success('任务已开始执行');
 | ||
|         getTaskList();
 | ||
|       } else {
 | ||
|         const errorMsg = response.msg || '未知错误';
 | ||
|         console.error(`更新任务状态失败: ${errorMsg}`);
 | ||
|         ElMessage.error(`更新任务状态失败:${errorMsg}`);
 | ||
|       }
 | ||
|     }
 | ||
|     // 如果是执行中状态,点击完成按钮
 | ||
|     else if (task.status === 'executing' && task.actionText === '完成') {
 | ||
|       const taskIndex = tasks.value.findIndex((t) => t.id === task.id);
 | ||
|       if (taskIndex === -1) {
 | ||
|         ElMessage.warning('未找到该任务数据');
 | ||
|         return;
 | ||
|       }
 | ||
| 
 | ||
|       const originalTask = tasks.value[taskIndex];
 | ||
| 
 | ||
|       const now = new Date();
 | ||
|       const year = now.getFullYear();
 | ||
|       const month = String(now.getMonth() + 1).padStart(2, '0');
 | ||
|       const day = String(now.getDate()).padStart(2, '0');
 | ||
|       const hours = String(now.getHours()).padStart(2, '0');
 | ||
|       const minutes = String(now.getMinutes()).padStart(2, '0');
 | ||
|       const seconds = String(now.getSeconds()).padStart(2, '0');
 | ||
|       const finishTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
 | ||
| 
 | ||
|       const updateData = {
 | ||
|         ...originalTask.rawData,
 | ||
|         id: task.id,
 | ||
|         taskType: '4', // 4表示已完成
 | ||
|         status: 'completed',
 | ||
|         taskProgress: 100,
 | ||
|         finishTime: finishTime
 | ||
|       };
 | ||
| 
 | ||
|       const response = await updatexjrenwu(updateData);
 | ||
| 
 | ||
|       if (response.code === 200) {
 | ||
|         ElMessage.success('任务已完成');
 | ||
|         getTaskList();
 | ||
|       } else {
 | ||
|         const errorMsg = response.msg || '未知错误';
 | ||
|         console.error(`更新任务状态失败: ${errorMsg}`);
 | ||
|         ElMessage.error(`更新任务状态失败:${errorMsg}`);
 | ||
|       }
 | ||
|     } else {
 | ||
|       console.log('其他操作:', task.actionText);
 | ||
|     }
 | ||
|   } catch (error) {
 | ||
|     console.error('处理任务操作失败:', error);
 | ||
|     ElMessage.error('操作失败,请重试');
 | ||
|   }
 | ||
| };
 | ||
| </script>
 | ||
| 
 | ||
| <style scoped>
 | ||
| @import url('./css/step-bars.css');
 | ||
| @import url('./css/detail-dialog.css');
 | ||
| 
 | ||
| .inspection-tasks {
 | ||
|   padding: 20px;
 | ||
|   background-color: #f5f7fa;
 | ||
|   min-height: 100vh;
 | ||
| }
 | ||
| 
 | ||
| /* 选项卡样式 */
 | ||
| .tabs-wrapper {
 | ||
|   background-color: #fff;
 | ||
|   padding: 20px;
 | ||
|   border-radius: 8px;
 | ||
|   margin-bottom: 16px;
 | ||
|   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
 | ||
| }
 | ||
| 
 | ||
| /* 筛选栏样式 */
 | ||
| .filter-bar {
 | ||
|   background-color: #fff;
 | ||
|   border-radius: 8px;
 | ||
|   margin-bottom: 24px;
 | ||
|   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
 | ||
|   padding: 16px 24px;
 | ||
| }
 | ||
| 
 | ||
| .filter-container {
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
|   flex-wrap: wrap;
 | ||
|   gap: 16px;
 | ||
|   width: 100%;
 | ||
| }
 | ||
| 
 | ||
| .filter-item {
 | ||
|   flex-shrink: 0;
 | ||
| }
 | ||
| 
 | ||
| .filter-bar .el-select {
 | ||
|   width: 180px;
 | ||
|   height: 36px;
 | ||
| }
 | ||
| 
 | ||
| .filter-actions {
 | ||
|   margin-left: auto;
 | ||
|   display: flex;
 | ||
|   gap: 10px;
 | ||
| }
 | ||
| 
 | ||
| .search-btn,
 | ||
| .create-btn {
 | ||
|   height: 36px;
 | ||
|   border-radius: 4px;
 | ||
| }
 | ||
| 
 | ||
| /* 任务卡片样式 */
 | ||
| .task-cards {
 | ||
|   display: grid;
 | ||
|   grid-template-columns: repeat(auto-fill, minmax(310px, 1fr));
 | ||
|   gap: 20px;
 | ||
|   margin-bottom: 30px;
 | ||
| }
 | ||
| 
 | ||
| .task-card {
 | ||
|   background-color: #fff;
 | ||
|   border-radius: 8px;
 | ||
|   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
 | ||
|   padding: 16px 16px 60px 24px;
 | ||
|   position: relative;
 | ||
|   overflow: hidden;
 | ||
|   min-height: 280px;
 | ||
|   transition: all 0.3s ease;
 | ||
| }
 | ||
| 
 | ||
| /* 卡片左侧状态竖线 */
 | ||
| .task-card::before {
 | ||
|   content: '';
 | ||
|   position: absolute;
 | ||
|   left: 0;
 | ||
|   top: 0;
 | ||
|   bottom: 0;
 | ||
|   width: 6px;
 | ||
|   box-shadow: 0 0 8px rgba(0, 0, 0, 0.05);
 | ||
| }
 | ||
| 
 | ||
| /* 卡片状态类 */
 | ||
| .card-pending::before {
 | ||
|   background-color: #1677ff;
 | ||
| }
 | ||
| .card-delayed::before {
 | ||
|   background-color: #ff4d4f;
 | ||
| }
 | ||
| .card-executing::before {
 | ||
|   background-color: #fa8c16;
 | ||
| }
 | ||
| .card-completed::before {
 | ||
|   background-color: #52c41a;
 | ||
| }
 | ||
| 
 | ||
| /* 卡片阴影效果 */
 | ||
| .card-pending {
 | ||
|   box-shadow: 0 4px 16px rgba(22, 119, 255, 0.15);
 | ||
| }
 | ||
| .card-delayed {
 | ||
|   box-shadow: 0 4px 16px rgba(255, 77, 79, 0.15);
 | ||
| }
 | ||
| .card-executing {
 | ||
|   box-shadow: 0 4px 16px rgba(250, 140, 22, 0.15);
 | ||
| }
 | ||
| .card-completed {
 | ||
|   box-shadow: 0 4px 16px rgba(82, 196, 26, 0.15);
 | ||
| }
 | ||
| 
 | ||
| .task-card:hover {
 | ||
|   transform: translateY(-3px);
 | ||
|   box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
 | ||
| }
 | ||
| 
 | ||
| .task-header {
 | ||
|   display: flex;
 | ||
|   justify-content: space-between;
 | ||
|   align-items: flex-start;
 | ||
|   margin-bottom: 16px;
 | ||
|   padding-bottom: 12px;
 | ||
|   border-bottom: 1px solid #f0f2f5;
 | ||
| }
 | ||
| 
 | ||
| .task-title {
 | ||
|   font-size: 16px;
 | ||
|   font-weight: 500;
 | ||
|   color: #1d2129;
 | ||
| }
 | ||
| 
 | ||
| /* 状态标签样式 */
 | ||
| .task-status {
 | ||
|   padding: 4px 10px;
 | ||
|   border-radius: 6px;
 | ||
|   font-size: 12px;
 | ||
|   font-weight: 500;
 | ||
| }
 | ||
| 
 | ||
| /* 标签状态类 */
 | ||
| .tag-pending {
 | ||
|   background-color: #e6f7ff;
 | ||
|   color: #1677ff;
 | ||
|   border: 1px solid #91d5ff;
 | ||
| }
 | ||
| 
 | ||
| .tag-delayed {
 | ||
|   background-color: #fff2f0;
 | ||
|   color: #ff4d4f;
 | ||
|   border: 1px solid #ffccc7;
 | ||
| }
 | ||
| 
 | ||
| .tag-executing {
 | ||
|   background-color: #fffbe6;
 | ||
|   color: #fa8c16;
 | ||
|   border: 1px solid #ffe58f;
 | ||
| }
 | ||
| 
 | ||
| .tag-completed {
 | ||
|   background-color: #f6ffed;
 | ||
|   color: #52c41a;
 | ||
|   border: 1px solid #b7eb8f;
 | ||
| }
 | ||
| 
 | ||
| .task-details {
 | ||
|   margin-bottom: 16px;
 | ||
| }
 | ||
| 
 | ||
| .detail-item {
 | ||
|   display: flex;
 | ||
|   margin-bottom: 10px;
 | ||
|   font-size: 13px;
 | ||
| }
 | ||
| 
 | ||
| .detail-label {
 | ||
|   flex: 0 0 80px;
 | ||
|   color: #86909c;
 | ||
| }
 | ||
| 
 | ||
| .detail-value {
 | ||
|   flex: 1;
 | ||
|   color: #4e5969;
 | ||
|   word-break: break-all;
 | ||
| }
 | ||
| 
 | ||
| .delay-reason {
 | ||
|   display: flex;
 | ||
|   margin: 10px 0;
 | ||
|   font-size: 13px;
 | ||
|   padding-top: 8px;
 | ||
|   border-top: 1px dashed #f0f2f5;
 | ||
| }
 | ||
| 
 | ||
| .progress-container {
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
|   margin: 10px 0;
 | ||
|   padding-top: 8px;
 | ||
|   border-top: 1px dashed #f0f2f5;
 | ||
| }
 | ||
| 
 | ||
| .progress-bar {
 | ||
|   flex: 1;
 | ||
|   padding-left: 10px;
 | ||
|   margin-top: 0;
 | ||
| }
 | ||
| 
 | ||
| .task-result {
 | ||
|   display: flex;
 | ||
|   margin: 10px 0;
 | ||
|   font-size: 13px;
 | ||
|   padding-top: 8px;
 | ||
|   border-top: 1px dashed #f0f2f5;
 | ||
| }
 | ||
| 
 | ||
| .result-normal {
 | ||
|   color: #00b42a;
 | ||
| }
 | ||
| 
 | ||
| .result-abnormal {
 | ||
|   color: #f53f3f;
 | ||
| }
 | ||
| 
 | ||
| .task-actions {
 | ||
|   display: flex;
 | ||
|   justify-content: flex-end;
 | ||
|   align-items: center;
 | ||
|   padding-top: 12px;
 | ||
|   border-top: 1px solid #f0f2f5;
 | ||
|   position: absolute;
 | ||
|   bottom: 16px;
 | ||
|   right: 16px;
 | ||
|   left: 16px;
 | ||
|   background-color: #fff;
 | ||
|   z-index: 10;
 | ||
| }
 | ||
| 
 | ||
| .action-btn {
 | ||
|   font-size: 13px;
 | ||
|   padding: 4px 10px;
 | ||
| }
 | ||
| 
 | ||
| .view-btn {
 | ||
|   color: #165dff;
 | ||
| }
 | ||
| .report-btn,
 | ||
| .start-btn {
 | ||
|   background-color: #165dff;
 | ||
|   border-color: #165dff;
 | ||
| }
 | ||
| 
 | ||
| .reschedule-btn {
 | ||
|   background-color: #ff7d00;
 | ||
|   border-color: #ff7d00;
 | ||
| }
 | ||
| 
 | ||
| .complete-btn {
 | ||
|   background-color: #00b42a;
 | ||
|   border-color: #00b42a;
 | ||
| }
 | ||
| 
 | ||
| /* 分页区域样式 */
 | ||
| .pagination-section {
 | ||
|   display: flex;
 | ||
|   justify-content: center;
 | ||
|   margin-top: 20px;
 | ||
| }
 | ||
| 
 | ||
| /* 导航栏样式 */
 | ||
| .navigation-tabs {
 | ||
|   display: flex;
 | ||
|   margin-bottom: 20px;
 | ||
|   background-color: #fff;
 | ||
|   border-radius: 4px;
 | ||
|   box-shadow: 0 1px 4px rgba(0, 0, 0, 0.08);
 | ||
|   padding: 2px;
 | ||
| }
 | ||
| 
 | ||
| .nav-tab {
 | ||
|   padding: 12px 24px;
 | ||
|   cursor: pointer;
 | ||
|   transition: all 0.3s ease;
 | ||
|   border-radius: 4px;
 | ||
|   font-size: 14px;
 | ||
|   color: #606266;
 | ||
|   border-right: 1px solid #f0f0f0;
 | ||
|   flex: 1;
 | ||
|   text-align: center;
 | ||
| }
 | ||
| 
 | ||
| .nav-tab:last-child {
 | ||
|   border-right: none;
 | ||
| }
 | ||
| 
 | ||
| .nav-tab:hover {
 | ||
|   color: #409eff;
 | ||
|   background-color: #ecf5ff;
 | ||
| }
 | ||
| 
 | ||
| .nav-tab.active {
 | ||
|   background-color: #409eff;
 | ||
|   color: #fff;
 | ||
|   box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
 | ||
| }
 | ||
| 
 | ||
| /* 无搜索结果样式 */
 | ||
| .no-results {
 | ||
|   grid-column: 1 / -1;
 | ||
|   text-align: center;
 | ||
|   padding: 60px 20px;
 | ||
|   background-color: #fff;
 | ||
|   border-radius: 8px;
 | ||
|   box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
 | ||
| }
 | ||
| 
 | ||
| .no-results-icon {
 | ||
|   font-size: 48px;
 | ||
|   color: #c9cdd4;
 | ||
|   margin-bottom: 20px;
 | ||
| }
 | ||
| 
 | ||
| .no-results-text {
 | ||
|   font-size: 16px;
 | ||
|   color: #86909c;
 | ||
|   margin-bottom: 20px;
 | ||
| }
 | ||
| 
 | ||
| /* 响应式设计 */
 | ||
| @media (max-width: 1200px) {
 | ||
|   .task-cards {
 | ||
|     grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| @media (max-width: 768px) {
 | ||
|   .inspection-tasks {
 | ||
|     padding: 16px;
 | ||
|   }
 | ||
| 
 | ||
|   .filter-container {
 | ||
|     flex-direction: column;
 | ||
|     align-items: stretch;
 | ||
|   }
 | ||
| 
 | ||
|   .task-cards {
 | ||
|     grid-template-columns: 1fr;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| /* 任务详情弹窗样式 */
 | ||
| .task-detail-container {
 | ||
|   max-height: 600px;
 | ||
|   overflow-y: auto;
 | ||
| }
 | ||
| 
 | ||
| /* 步骤条展示样式 */
 | ||
| .step-item {
 | ||
|   display: flex;
 | ||
|   align-items: flex-start;
 | ||
|   margin-bottom: 12px;
 | ||
|   padding: 12px;
 | ||
|   background-color: #fafafa;
 | ||
|   border-radius: 6px;
 | ||
|   transition: all 0.3s ease;
 | ||
| }
 | ||
| 
 | ||
| .step-item:hover {
 | ||
|   background-color: #f5f7fa;
 | ||
|   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
 | ||
| }
 | ||
| 
 | ||
| .step-number {
 | ||
|   width: 28px;
 | ||
|   height: 28px;
 | ||
|   background-color: #409eff;
 | ||
|   color: white;
 | ||
|   border-radius: 50%;
 | ||
|   display: flex;
 | ||
|   align-items: center;
 | ||
|   justify-content: center;
 | ||
|   margin-right: 12px;
 | ||
|   font-size: 14px;
 | ||
|   font-weight: bold;
 | ||
|   flex-shrink: 0;
 | ||
| }
 | ||
| 
 | ||
| .step-info {
 | ||
|   flex: 1;
 | ||
| }
 | ||
| 
 | ||
| .step-name {
 | ||
|   font-weight: 500;
 | ||
|   color: #1d2129;
 | ||
|   margin-bottom: 4px;
 | ||
|   font-size: 14px;
 | ||
| }
 | ||
| 
 | ||
| .step-purpose {
 | ||
|   color: #606266;
 | ||
|   margin-bottom: 4px;
 | ||
|   font-size: 13px;
 | ||
| }
 | ||
| 
 | ||
| .step-time,
 | ||
| .step-finish-time,
 | ||
| .step-remark {
 | ||
|   color: #909399;
 | ||
|   font-size: 12px;
 | ||
|   margin-bottom: 2px;
 | ||
| }
 | ||
| 
 | ||
| .step-status {
 | ||
|   padding: 4px 12px;
 | ||
|   border-radius: 4px;
 | ||
|   font-size: 12px;
 | ||
|   font-weight: 500;
 | ||
|   flex-shrink: 0;
 | ||
|   margin-top: 4px;
 | ||
| }
 | ||
| 
 | ||
| /* 步骤状态样式 */
 | ||
| .step-status.status-pending {
 | ||
|   background-color: #e6f7ff;
 | ||
|   color: #1677ff;
 | ||
|   border: 1px solid #91d5ff;
 | ||
| }
 | ||
| 
 | ||
| .step-status.status-executing {
 | ||
|   background-color: #fffbe6;
 | ||
|   color: #fa8c16;
 | ||
|   border: 1px solid #ffe58f;
 | ||
| }
 | ||
| 
 | ||
| .step-status.status-completed {
 | ||
|   background-color: #f6ffed;
 | ||
|   color: #52c41a;
 | ||
|   border: 1px solid #b7eb8f;
 | ||
| }
 | ||
| 
 | ||
| .step-status.status-delayed {
 | ||
|   background-color: #fff2f0;
 | ||
|   color: #ff4d4f;
 | ||
|   border: 1px solid #ffccc7;
 | ||
| }
 | ||
| 
 | ||
| .detail-card {
 | ||
|   margin-bottom: 20px;
 | ||
|   padding: 20px;
 | ||
|   background-color: #fafafa;
 | ||
|   border-radius: 8px;
 | ||
|   box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
 | ||
| }
 | ||
| 
 | ||
| .card-title {
 | ||
|   margin: 0 0 16px 0;
 | ||
|   padding-bottom: 12px;
 | ||
|   border-bottom: 2px solid #409eff;
 | ||
|   font-size: 16px;
 | ||
|   font-weight: 600;
 | ||
|   color: #303133;
 | ||
| }
 | ||
| 
 | ||
| .card-content {
 | ||
|   display: flex;
 | ||
|   flex-direction: column;
 | ||
|   gap: 12px;
 | ||
| }
 | ||
| 
 | ||
| .info-row {
 | ||
|   display: flex;
 | ||
|   flex-wrap: wrap;
 | ||
|   gap: 20px;
 | ||
| }
 | ||
| 
 | ||
| .info-item {
 | ||
|   flex: 1;
 | ||
|   min-width: 280px;
 | ||
| }
 | ||
| 
 | ||
| .info-item.full-width {
 | ||
|   min-width: 100%;
 | ||
| }
 | ||
| 
 | ||
| .info-label {
 | ||
|   display: inline-block;
 | ||
|   width: 100px;
 | ||
|   color: #606266;
 | ||
|   font-weight: 500;
 | ||
| }
 | ||
| 
 | ||
| .info-value {
 | ||
|   color: #303133;
 | ||
|   word-break: break-word;
 | ||
| }
 | ||
| 
 | ||
| .fail-reason {
 | ||
|   color: #f56c6c;
 | ||
| }
 | ||
| 
 | ||
| .no-info {
 | ||
|   color: #909399;
 | ||
|   font-style: italic;
 | ||
|   padding: 10px 0;
 | ||
| }
 | ||
| 
 | ||
| .loading-details {
 | ||
|   padding: 20px 0;
 | ||
| }
 | ||
| 
 | ||
| /* 状态颜色样式 */
 | ||
| .status-pending {
 | ||
|   color: #e6a23c;
 | ||
| }
 | ||
| 
 | ||
| .status-executing {
 | ||
|   color: #409eff;
 | ||
| }
 | ||
| 
 | ||
| .status-completed {
 | ||
|   color: #67c23a;
 | ||
| }
 | ||
| 
 | ||
| .status-delayed {
 | ||
|   color: #f56c6c;
 | ||
| }
 | ||
| 
 | ||
| .status-unknown {
 | ||
|   color: #909399;
 | ||
| }
 | ||
| 
 | ||
| /* 加载状态样式 */
 | ||
| .loading-state {
 | ||
|   text-align: center;
 | ||
|   padding: 80px 20px;
 | ||
|   color: #6c757d;
 | ||
|   font-size: 14px;
 | ||
| }
 | ||
| 
 | ||
| .loading-state i {
 | ||
|   display: block;
 | ||
|   font-size: 48px;
 | ||
|   margin-bottom: 16px;
 | ||
|   color: #1677ff;
 | ||
| }
 | ||
| 
 | ||
| /* 骨架屏加载 */
 | ||
| .skeleton-loading {
 | ||
|   display: flex;
 | ||
|   flex-direction: column;
 | ||
|   gap: 20px;
 | ||
| }
 | ||
| 
 | ||
| .skeleton-card {
 | ||
|   background-color: #f2f2f2;
 | ||
|   border-radius: 8px;
 | ||
|   padding: 20px;
 | ||
|   animation: skeleton-loading 1.5s infinite;
 | ||
| }
 | ||
| 
 | ||
| .skeleton-header {
 | ||
|   height: 24px;
 | ||
|   width: 140px;
 | ||
|   background-color: #e0e0e0;
 | ||
|   border-radius: 4px;
 | ||
|   margin-bottom: 16px;
 | ||
| }
 | ||
| 
 | ||
| .skeleton-content {
 | ||
|   display: flex;
 | ||
|   flex-direction: column;
 | ||
|   gap: 12px;
 | ||
| }
 | ||
| 
 | ||
| .skeleton-row {
 | ||
|   height: 20px;
 | ||
|   background-color: #e0e0e0;
 | ||
|   border-radius: 4px;
 | ||
| }
 | ||
| 
 | ||
| .skeleton-row:nth-child(1) {
 | ||
|   width: 70%;
 | ||
| }
 | ||
| .skeleton-row:nth-child(2) {
 | ||
|   width: 90%;
 | ||
| }
 | ||
| .skeleton-row:nth-child(3) {
 | ||
|   width: 60%;
 | ||
| }
 | ||
| 
 | ||
| @keyframes skeleton-loading {
 | ||
|   0% {
 | ||
|     opacity: 0.6;
 | ||
|   }
 | ||
|   50% {
 | ||
|     opacity: 0.3;
 | ||
|   }
 | ||
|   100% {
 | ||
|     opacity: 0.6;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| /* 响应式设计 */
 | ||
| @media (max-width: 768px) {
 | ||
|   .info-item {
 | ||
|     min-width: 100%;
 | ||
|   }
 | ||
|   .info-row {
 | ||
|     gap: 12px;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| /* 弹窗按钮样式 */
 | ||
| .dialog-footer .el-button {
 | ||
|   padding: 10px 24px;
 | ||
|   border-radius: 6px;
 | ||
|   font-size: 14px;
 | ||
|   font-weight: 500;
 | ||
|   transition: all 0.3s ease;
 | ||
| }
 | ||
| 
 | ||
| .dialog-footer .el-button:hover {
 | ||
|   transform: translateY(-1px);
 | ||
|   box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
 | ||
| }
 | ||
| 
 | ||
| .step-content {
 | ||
|   padding: 30px 20px;
 | ||
|   background-color: #fafafa;
 | ||
|   border-radius: 8px;
 | ||
|   margin-top: 20px;
 | ||
| }
 | ||
| </style>
 |