2025-09-17 15:53:38 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div class="inspection-tasks">
|
2025-09-26 20:32:14 +08:00
|
|
|
|
<!-- <div class="navigation-tabs">
|
2025-09-17 15:53:38 +08:00
|
|
|
|
<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>
|
2025-09-26 20:32:14 +08:00
|
|
|
|
</div> -->
|
2025-09-17 20:03:00 +08:00
|
|
|
|
|
2025-09-17 15:53:38 +08:00
|
|
|
|
<!-- 选项卡 -->
|
|
|
|
|
|
<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">
|
2025-09-22 15:42:13 +08:00
|
|
|
|
<el-input v-model="executor" placeholder="执行人"></el-input>
|
2025-09-17 15:53:38 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
<div class="filter-actions">
|
2025-09-23 20:36:47 +08:00
|
|
|
|
<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>
|
2025-09-17 15:53:38 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 任务卡片列表 -->
|
|
|
|
|
|
<div class="task-cards">
|
2025-09-17 20:03:00 +08:00
|
|
|
|
<div class="task-card" v-for="(task, index) in pagedTasks" :key="index" :class="task.statusClass">
|
2025-09-17 15:53:38 +08:00
|
|
|
|
<div class="task-header">
|
|
|
|
|
|
<div class="task-title">
|
|
|
|
|
|
{{ task.title }}
|
|
|
|
|
|
</div>
|
2025-09-17 20:03:00 +08:00
|
|
|
|
<div class="task-status" :class="task.tagClass">
|
2025-09-17 15:53:38 +08:00
|
|
|
|
{{ 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>
|
2025-09-22 15:42:13 +08:00
|
|
|
|
<div class="progress-bar" style="flex: 1; padding-left: 0; margin-top: 0">
|
2025-09-17 15:53:38 +08:00
|
|
|
|
<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>
|
2025-09-22 15:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 无搜索结果提示 -->
|
|
|
|
|
|
<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>
|
2025-09-17 15:53:38 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 分页区域 -->
|
2025-09-22 15:42:13 +08:00
|
|
|
|
<div class="pagination-section" v-if="tasks.length > 0">
|
2025-09-17 15:53:38 +08:00
|
|
|
|
<div class="pagination-controls">
|
|
|
|
|
|
<el-pagination
|
|
|
|
|
|
@size-change="handleSizeChange"
|
|
|
|
|
|
@current-change="handleCurrentChange"
|
|
|
|
|
|
:current-page="currentPage"
|
|
|
|
|
|
:page-sizes="[8, 12, 16, 20]"
|
|
|
|
|
|
:page-size="pageSize"
|
2025-09-22 15:42:13 +08:00
|
|
|
|
layout="prev, pager, next, jumper, total"
|
2025-09-17 15:53:38 +08:00
|
|
|
|
:total="total"
|
|
|
|
|
|
background
|
|
|
|
|
|
>
|
|
|
|
|
|
</el-pagination>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 添加新任务弹窗 -->
|
2025-09-26 20:32:14 +08:00
|
|
|
|
<el-dialog v-model="createTaskDialogVisible" title="添加新任务" width="750px" :before-close="handleCancelCreateTask">
|
2025-09-17 15:53:38 +08:00
|
|
|
|
<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="选择关联计划">
|
2025-09-22 15:42:13 +08:00
|
|
|
|
<el-option v-for="plan in planList" :key="plan.value" :label="plan.label" :value="plan.value" />
|
2025-09-17 15:53:38 +08:00
|
|
|
|
</el-select>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
<el-form-item label="执行人" prop="executor">
|
2025-09-17 15:53:38 +08:00
|
|
|
|
<el-select v-model="createTaskForm.executor" placeholder="选择执行人">
|
2025-09-22 15:42:13 +08:00
|
|
|
|
<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>
|
|
|
|
|
|
|
2025-09-26 20:32:14 +08:00
|
|
|
|
<!-- <el-form-item label="问题类型" prop="problemType">
|
2025-09-22 15:42:13 +08:00
|
|
|
|
<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" />
|
2025-09-17 15:53:38 +08:00
|
|
|
|
</el-select>
|
2025-09-26 20:32:14 +08:00
|
|
|
|
</el-form-item> -->
|
2025-09-25 20:03:08 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 步骤条 -->
|
|
|
|
|
|
<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>
|
2025-09-17 15:53:38 +08:00
|
|
|
|
</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>
|
2025-09-22 15:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
<!-- 任务详情弹窗 -->
|
|
|
|
|
|
<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>
|
2025-09-25 20:03:08 +08:00
|
|
|
|
<span class="info-value">{{ detailData.person?.phonenumber || '-' }}</span>
|
2025-09-22 15:42:13 +08:00
|
|
|
|
</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>
|
|
|
|
|
|
|
2025-09-25 20:03:08 +08:00
|
|
|
|
<!-- 执行步骤信息卡片 -->
|
|
|
|
|
|
<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)">
|
2025-09-26 20:32:14 +08:00
|
|
|
|
{{ node.status === '2' ? '未完成' : '已完成' }}
|
2025-09-25 20:03:08 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
<!-- 执行结果信息卡片 -->
|
|
|
|
|
|
<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>
|
2025-09-17 15:53:38 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
2025-09-22 15:42:13 +08:00
|
|
|
|
import { ref, computed, onMounted } from 'vue';
|
2025-09-17 15:53:38 +08:00
|
|
|
|
import router from '@/router';
|
2025-09-22 15:42:13 +08:00
|
|
|
|
|
2025-09-25 20:03:08 +08:00
|
|
|
|
import { xjrenwuDetail, xjrenwulist, addxjrenwu, updatexjrenwu } from '@/api/zhinengxunjian/xunjian/renwu';
|
2025-09-22 15:42:13 +08:00
|
|
|
|
import { xunjianUserlist, xunjianlist } from '@/api/zhinengxunjian/xunjian/index';
|
2025-09-25 20:03:08 +08:00
|
|
|
|
import { addjiedian } from '@/api/zhinengxunjian/jiedian/index';
|
|
|
|
|
|
import { ElMessage, ElLoading, ElForm } from 'element-plus';
|
2025-09-17 15:53:38 +08:00
|
|
|
|
|
|
|
|
|
|
// 筛选条件
|
|
|
|
|
|
const taskStatus = ref('');
|
|
|
|
|
|
const planType = ref('');
|
|
|
|
|
|
const executor = ref('');
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
// 任务数据 - 初始为空数组,通过API获取
|
|
|
|
|
|
const tasks = ref([]);
|
|
|
|
|
|
|
2025-09-25 20:03:08 +08:00
|
|
|
|
// 任务详情弹窗相关变量
|
2025-09-22 15:42:13 +08:00
|
|
|
|
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';
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-25 20:03:08 +08:00
|
|
|
|
// 格式化日期时间
|
|
|
|
|
|
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] || '未知状态';
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
// 状态映射配置
|
|
|
|
|
|
const statusConfig = {
|
|
|
|
|
|
pending: {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
statusText: '待执行',
|
2025-09-17 20:03:00 +08:00
|
|
|
|
statusClass: 'card-pending',
|
|
|
|
|
|
tagClass: 'tag-pending',
|
2025-09-17 15:53:38 +08:00
|
|
|
|
actionText: '开始执行',
|
|
|
|
|
|
actionClass: 'start-btn'
|
|
|
|
|
|
},
|
2025-09-22 15:42:13 +08:00
|
|
|
|
delayed: {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
statusText: '已延期',
|
2025-09-17 20:03:00 +08:00
|
|
|
|
statusClass: 'card-delayed',
|
|
|
|
|
|
tagClass: 'tag-delayed',
|
2025-09-17 15:53:38 +08:00
|
|
|
|
actionText: '重新安排',
|
|
|
|
|
|
actionClass: 'reschedule-btn'
|
|
|
|
|
|
},
|
2025-09-22 15:42:13 +08:00
|
|
|
|
executing: {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
statusText: '执行中',
|
2025-09-17 20:03:00 +08:00
|
|
|
|
statusClass: 'card-executing',
|
|
|
|
|
|
tagClass: 'tag-executing',
|
2025-09-17 15:53:38 +08:00
|
|
|
|
actionText: '完成',
|
|
|
|
|
|
actionClass: 'complete-btn'
|
|
|
|
|
|
},
|
2025-09-22 15:42:13 +08:00
|
|
|
|
completed: {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
statusText: '已完成',
|
2025-09-17 20:03:00 +08:00
|
|
|
|
statusClass: 'card-completed',
|
|
|
|
|
|
tagClass: 'tag-completed',
|
2025-09-17 15:53:38 +08:00
|
|
|
|
actionText: '查看报告',
|
|
|
|
|
|
actionClass: 'report-btn'
|
|
|
|
|
|
}
|
2025-09-22 15:42:13 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 获取巡检任务数据
|
|
|
|
|
|
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,
|
2025-09-26 20:32:14 +08:00
|
|
|
|
personId: 1,
|
|
|
|
|
|
taskType: taskStatus.value || undefined, // 任务状态
|
|
|
|
|
|
planType: planType.value || undefined, // 计划类型
|
|
|
|
|
|
personName: executor.value || undefined // 执行人
|
2025-09-22 15:42:13 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
|
});
|
2025-09-17 15:53:38 +08:00
|
|
|
|
|
|
|
|
|
|
// 分页相关
|
|
|
|
|
|
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; // 重置到第一页
|
2025-09-22 15:42:13 +08:00
|
|
|
|
getTaskList(); // 重新获取数据
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 重置筛选条件
|
|
|
|
|
|
const resetFilters = () => {
|
|
|
|
|
|
taskStatus.value = '';
|
|
|
|
|
|
planType.value = '';
|
|
|
|
|
|
executor.value = '';
|
|
|
|
|
|
currentPage.value = 1;
|
|
|
|
|
|
getTaskList();
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 创建任务弹窗相关
|
|
|
|
|
|
const createTaskDialogVisible = ref(false);
|
|
|
|
|
|
const createTaskForm = ref({
|
|
|
|
|
|
taskName: '',
|
|
|
|
|
|
inspectionTarget: '',
|
|
|
|
|
|
timeRange: [],
|
2025-09-22 15:42:13 +08:00
|
|
|
|
workTimeRange1: null,
|
|
|
|
|
|
workTimeRange2: null,
|
2025-09-25 20:03:08 +08:00
|
|
|
|
relatedPlan: '',
|
2025-09-22 15:42:13 +08:00
|
|
|
|
executor: '',
|
|
|
|
|
|
taskType: '1', // 默认待执行
|
2025-09-26 20:32:14 +08:00
|
|
|
|
// problemType: '',
|
2025-09-25 20:03:08 +08:00
|
|
|
|
steps: [{ name: '', intendedPurpose: '', intendedTime: '' }] // 任务步骤数组
|
2025-09-17 15:53:38 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const createTaskRules = {
|
|
|
|
|
|
taskName: [{ required: true, message: '请输入任务名称', trigger: 'blur' }],
|
|
|
|
|
|
inspectionTarget: [{ required: true, message: '请输入巡检对象', trigger: 'blur' }],
|
2025-09-22 15:42:13 +08:00
|
|
|
|
timeRange: [{ required: true, message: '请选择时间范围', trigger: 'change' }],
|
|
|
|
|
|
workTimeRange1: [{ required: true, message: '请选择开始时间', trigger: 'change' }],
|
|
|
|
|
|
taskType: [{ required: true, message: '请选择任务类型', trigger: 'change' }]
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 创建任务
|
|
|
|
|
|
const handleCreateTask = () => {
|
2025-09-22 15:42:13 +08:00
|
|
|
|
openCreateTaskDialog();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-25 20:03:08 +08:00
|
|
|
|
// 重置步骤表单
|
|
|
|
|
|
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();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
// 构建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(',');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-25 20:03:08 +08:00
|
|
|
|
// 添加步骤
|
|
|
|
|
|
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);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-09-17 15:53:38 +08:00
|
|
|
|
// 保存任务
|
2025-09-22 15:42:13 +08:00
|
|
|
|
const handleSaveTask = async () => {
|
|
|
|
|
|
// 表单验证
|
|
|
|
|
|
if (!createTaskForm.value.taskName || !createTaskForm.value.inspectionTarget || !createTaskForm.value.timeRange) {
|
|
|
|
|
|
ElMessage.warning('请填写必要的任务信息');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 20:03:08 +08:00
|
|
|
|
// 验证所有步骤
|
|
|
|
|
|
const hasEmptyStep = createTaskForm.value.steps.some((step) => !step.name.trim() || !step.intendedPurpose.trim());
|
|
|
|
|
|
if (hasEmptyStep) {
|
|
|
|
|
|
ElMessage.warning('请填写完整所有步骤信息');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
try {
|
|
|
|
|
|
// 获取timeInfo字符串
|
|
|
|
|
|
const taskTimeInfo = getTaskTimeInfoString();
|
|
|
|
|
|
|
|
|
|
|
|
if (!taskTimeInfo) {
|
|
|
|
|
|
ElMessage.warning('请至少填写一个工作时间段');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 20:03:08 +08:00
|
|
|
|
// 准备步骤数据,与工单列表页面保持一致的格式
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
// 构建接口所需的数据结构
|
|
|
|
|
|
const apiData = {
|
2025-09-25 20:03:08 +08:00
|
|
|
|
projectId: 1,
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
createTime: new Date().toISOString(),
|
|
|
|
|
|
updateTime: new Date().toISOString(),
|
2025-09-26 20:32:14 +08:00
|
|
|
|
startTime: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
2025-09-22 15:42:13 +08:00
|
|
|
|
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,
|
2025-09-26 20:32:14 +08:00
|
|
|
|
// problemType: createTaskForm.value.problemType,
|
2025-09-25 20:03:08 +08:00
|
|
|
|
nodeIds: nodeIds // 添加步骤ID字符串,与工单列表页面保持一致
|
2025-09-22 15:42:13 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 调用新增任务接口
|
|
|
|
|
|
const response = await addxjrenwu(apiData);
|
|
|
|
|
|
|
|
|
|
|
|
if (response.code === 200) {
|
|
|
|
|
|
ElMessage.success('任务创建成功');
|
|
|
|
|
|
// 关闭弹窗
|
|
|
|
|
|
createTaskDialogVisible.value = false;
|
|
|
|
|
|
// 重置表单
|
|
|
|
|
|
createTaskForm.value = {
|
|
|
|
|
|
taskName: '',
|
|
|
|
|
|
inspectionTarget: '',
|
|
|
|
|
|
timeRange: [],
|
|
|
|
|
|
workTimeRange1: null,
|
|
|
|
|
|
workTimeRange2: null,
|
2025-09-25 20:03:08 +08:00
|
|
|
|
relatedPlan: '',
|
2025-09-22 15:42:13 +08:00
|
|
|
|
executor: '',
|
|
|
|
|
|
taskType: '1',
|
2025-09-26 20:32:14 +08:00
|
|
|
|
// problemType: '',
|
2025-09-25 20:03:08 +08:00
|
|
|
|
steps: [{ name: '', intendedPurpose: '', intendedTime: '' }]
|
2025-09-22 15:42:13 +08:00
|
|
|
|
};
|
|
|
|
|
|
// 重新获取任务列表
|
|
|
|
|
|
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();
|
2025-09-24 16:37:09 +08:00
|
|
|
|
// 适配新接口格式:检查code为200且rows为数组
|
|
|
|
|
|
const userRows = response.code === 200 && response.rows && Array.isArray(response.rows) ? response.rows : [];
|
2025-09-22 15:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
userList.value = userRows
|
|
|
|
|
|
.filter((item) => item && typeof item === 'object')
|
2025-09-24 16:37:09 +08:00
|
|
|
|
.map((item) => ({
|
|
|
|
|
|
label: item.userName || '未知用户',
|
|
|
|
|
|
value: String(item.userId || '') // 使用userId作为唯一标识
|
2025-09-22 15:42:13 +08:00
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 取消创建任务
|
|
|
|
|
|
const handleCancelCreateTask = () => {
|
|
|
|
|
|
createTaskDialogVisible.value = false;
|
|
|
|
|
|
// 重置表单
|
|
|
|
|
|
createTaskForm.value = {
|
|
|
|
|
|
taskName: '',
|
|
|
|
|
|
inspectionTarget: '',
|
|
|
|
|
|
timeRange: [],
|
2025-09-25 20:03:08 +08:00
|
|
|
|
workTimeRange1: null,
|
|
|
|
|
|
workTimeRange2: null,
|
|
|
|
|
|
relatedPlan: '',
|
|
|
|
|
|
executor: '',
|
|
|
|
|
|
taskType: '1',
|
2025-09-26 20:32:14 +08:00
|
|
|
|
// problemType: '',
|
2025-09-25 20:03:08 +08:00
|
|
|
|
steps: [{ name: '', intendedPurpose: '', intendedTime: '' }]
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 分页事件
|
|
|
|
|
|
const handleSizeChange = (val) => {
|
|
|
|
|
|
pageSize.value = val;
|
|
|
|
|
|
currentPage.value = 1;
|
2025-09-22 15:42:13 +08:00
|
|
|
|
getTaskList(); // 分页大小改变时重新获取数据
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleCurrentChange = (val) => {
|
|
|
|
|
|
currentPage.value = val;
|
2025-09-22 15:42:13 +08:00
|
|
|
|
getTaskList(); // 页码改变时重新获取数据
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 查看任务详情
|
2025-09-22 15:42:13 +08:00
|
|
|
|
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;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleInspectionManagement1 = () => {
|
2025-09-28 18:54:52 +08:00
|
|
|
|
router.push('/znxj/xjgl/InspectionManagement');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleInspectionManagement2 = () => {
|
2025-09-28 18:54:52 +08:00
|
|
|
|
router.push('/znxj/xjgl/xunjianrenwu');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
const handleInspectionManagement3 = () => {
|
2025-09-28 18:54:52 +08:00
|
|
|
|
router.push('/znxj/xjgl/xunjianjihua');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
const handleInspection1 = () => {
|
2025-09-28 18:54:52 +08:00
|
|
|
|
router.push('/znxj/rili');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
const handleInspection2 = () => {
|
2025-09-28 18:54:52 +08:00
|
|
|
|
router.push('/znxj/xjgl/InspectionManagement');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
const handleInspection3 = () => {
|
2025-09-28 18:54:52 +08:00
|
|
|
|
router.push('/znxj/sygl/shiyanguanli');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
const handleInspection4 = () => {
|
2025-09-28 18:54:52 +08:00
|
|
|
|
router.push('/znxj/bxgl/baoxiuguanli');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
const handleInspection5 = () => {
|
2025-09-28 18:54:52 +08:00
|
|
|
|
router.push('/znxj/qxgl/qiangxiuguanli');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
const handleInspection6 = () => {
|
2025-09-28 18:54:52 +08:00
|
|
|
|
router.push('/znxj/gdgl/gongdanliebiao');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
|
|
|
|
|
const handleInspection7 = () => {
|
2025-09-28 18:54:52 +08:00
|
|
|
|
router.push('/znxj/ywzz/renyuanzhuangtai');
|
2025-09-17 15:53:38 +08:00
|
|
|
|
};
|
2025-09-22 15:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
// 处理任务操作按钮点击事件
|
|
|
|
|
|
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,
|
2025-09-25 20:03:08 +08:00
|
|
|
|
startTime: new Date().toISOString().slice(0, 19).replace('T', ' '),
|
2025-09-22 15:42:13 +08:00
|
|
|
|
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('操作失败,请重试');
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
2025-09-17 15:53:38 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
2025-09-25 20:03:08 +08:00
|
|
|
|
@import url('./css/step-bars.css');
|
|
|
|
|
|
@import url('./css/detail-dialog.css');
|
|
|
|
|
|
|
2025-09-17 15:53:38 +08:00
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
.search-btn,
|
2025-09-17 15:53:38 +08:00
|
|
|
|
.create-btn {
|
2025-09-22 15:42:13 +08:00
|
|
|
|
height: 36px;
|
|
|
|
|
|
border-radius: 4px;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* 任务卡片样式 */
|
|
|
|
|
|
.task-cards {
|
|
|
|
|
|
display: grid;
|
2025-09-17 20:03:00 +08:00
|
|
|
|
grid-template-columns: repeat(auto-fill, minmax(310px, 1fr));
|
2025-09-17 15:53:38 +08:00
|
|
|
|
gap: 20px;
|
|
|
|
|
|
margin-bottom: 30px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.task-card {
|
2025-09-22 15:42:13 +08:00
|
|
|
|
background-color: #fff;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
|
2025-09-22 15:42:13 +08:00
|
|
|
|
padding: 16px 16px 60px 24px;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
position: relative;
|
|
|
|
|
|
overflow: hidden;
|
2025-09-17 20:03:00 +08:00
|
|
|
|
min-height: 280px;
|
|
|
|
|
|
transition: all 0.3s ease;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-17 20:03:00 +08:00
|
|
|
|
/* 卡片左侧状态竖线 */
|
2025-09-17 15:53:38 +08:00
|
|
|
|
.task-card::before {
|
|
|
|
|
|
content: '';
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
bottom: 0;
|
2025-09-22 15:42:13 +08:00
|
|
|
|
width: 6px;
|
2025-09-17 20:03:00 +08:00
|
|
|
|
box-shadow: 0 0 8px rgba(0, 0, 0, 0.05);
|
2025-09-17 15:53:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
/* 卡片状态类 */
|
2025-09-17 20:03:00 +08:00
|
|
|
|
.card-pending::before {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
background-color: #1677ff;
|
|
|
|
|
|
}
|
2025-09-17 20:03:00 +08:00
|
|
|
|
.card-delayed::before {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
background-color: #ff4d4f;
|
|
|
|
|
|
}
|
2025-09-17 20:03:00 +08:00
|
|
|
|
.card-executing::before {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
background-color: #fa8c16;
|
|
|
|
|
|
}
|
2025-09-17 20:03:00 +08:00
|
|
|
|
.card-completed::before {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
background-color: #52c41a;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-17 20:03:00 +08:00
|
|
|
|
/* 卡片阴影效果 */
|
|
|
|
|
|
.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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-17 15:53:38 +08:00
|
|
|
|
.task-card:hover {
|
|
|
|
|
|
transform: translateY(-3px);
|
2025-09-17 20:03:00 +08:00
|
|
|
|
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
2025-09-17 15:53:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
/* 状态标签样式 */
|
2025-09-17 15:53:38 +08:00
|
|
|
|
.task-status {
|
|
|
|
|
|
padding: 4px 10px;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
/* 标签状态类 */
|
2025-09-17 20:03:00 +08:00
|
|
|
|
.tag-pending {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
background-color: #e6f7ff;
|
|
|
|
|
|
color: #1677ff;
|
2025-09-17 20:03:00 +08:00
|
|
|
|
border: 1px solid #91d5ff;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-17 20:03:00 +08:00
|
|
|
|
.tag-delayed {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
background-color: #fff2f0;
|
|
|
|
|
|
color: #ff4d4f;
|
2025-09-17 20:03:00 +08:00
|
|
|
|
border: 1px solid #ffccc7;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-17 20:03:00 +08:00
|
|
|
|
.tag-executing {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
background-color: #fffbe6;
|
|
|
|
|
|
color: #fa8c16;
|
2025-09-17 20:03:00 +08:00
|
|
|
|
border: 1px solid #ffe58f;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-17 20:03:00 +08:00
|
|
|
|
.tag-completed {
|
2025-09-17 15:53:38 +08:00
|
|
|
|
background-color: #f6ffed;
|
|
|
|
|
|
color: #52c41a;
|
2025-09-17 20:03:00 +08:00
|
|
|
|
border: 1px solid #b7eb8f;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
2025-09-22 15:42:13 +08:00
|
|
|
|
align-items: center;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
margin: 10px 0;
|
|
|
|
|
|
padding-top: 8px;
|
|
|
|
|
|
border-top: 1px dashed #f0f2f5;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.progress-bar {
|
|
|
|
|
|
flex: 1;
|
2025-09-22 15:42:13 +08:00
|
|
|
|
padding-left: 10px;
|
|
|
|
|
|
margin-top: 0;
|
2025-09-17 15:53:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
2025-09-22 15:42:13 +08:00
|
|
|
|
.report-btn,
|
2025-09-17 15:53:38 +08:00
|
|
|
|
.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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
/* 无搜索结果样式 */
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-17 20:03:00 +08:00
|
|
|
|
/* 响应式设计 */
|
|
|
|
|
|
@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;
|
|
|
|
|
|
}
|
2025-09-17 15:53:38 +08:00
|
|
|
|
}
|
2025-09-22 15:42:13 +08:00
|
|
|
|
|
|
|
|
|
|
/* 任务详情弹窗样式 */
|
|
|
|
|
|
.task-detail-container {
|
|
|
|
|
|
max-height: 600px;
|
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-25 20:03:08 +08:00
|
|
|
|
/* 步骤条展示样式 */
|
|
|
|
|
|
.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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-22 15:42:13 +08:00
|
|
|
|
.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);
|
|
|
|
|
|
}
|
2025-09-25 20:03:08 +08:00
|
|
|
|
|
|
|
|
|
|
.step-content {
|
|
|
|
|
|
padding: 30px 20px;
|
|
|
|
|
|
background-color: #fafafa;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
margin-top: 20px;
|
|
|
|
|
|
}
|
2025-09-17 15:53:38 +08:00
|
|
|
|
</style>
|