This commit is contained in:
dhr
2025-09-25 20:03:08 +08:00
parent 9913a7854c
commit 6b9bfb66b1
15 changed files with 4715 additions and 1591 deletions

View File

@ -133,7 +133,7 @@
</div>
<!-- 添加新任务弹窗 -->
<el-dialog v-model="createTaskDialogVisible" title="添加新任务" width="500px" :before-close="handleCancelCreateTask">
<el-dialog v-model="createTaskDialogVisible" title="添加新任务" width="700px" :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="输入任务名称" />
@ -211,6 +211,27 @@
<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>
@ -304,7 +325,7 @@
</div>
<div class="info-item">
<span class="info-label">联系电话</span>
<span class="info-value">{{ detailData.person?.phone || '-' }}</span>
<span class="info-value">{{ detailData.person?.phonenumber || '-' }}</span>
</div>
</div>
<div class="info-row">
@ -312,10 +333,6 @@
<span class="info-label">性别</span>
<span class="info-value">{{ detailData.person?.sex === '1' ? '男' : detailData.person?.sex === '2' ? '女' : '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">民族</span>
<span class="info-value">{{ detailData.person?.nation || '-' }}</span>
</div>
</div>
</div>
</div>
@ -356,6 +373,26 @@
</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)">
{{ getStepStatusText(node.status) }}
</div>
</div>
</div>
</div>
<!-- 执行结果信息卡片 -->
<div v-if="detailData.taskType === '2' || detailData.taskType === 2" class="detail-card">
<h3 class="card-title">延期信息</h3>
@ -388,37 +425,10 @@
import { ref, computed, onMounted } from 'vue';
import router from '@/router';
import { xjrenwuDetail, xjrenwuExport, xjrenwulist, addxjrenwu, updatexjrenwu, delxjrenwu } from '@/api/zhinengxunjian/xunjian/renwu';
import { xjrenwuDetail, xjrenwulist, addxjrenwu, updatexjrenwu } from '@/api/zhinengxunjian/xunjian/renwu';
import { xunjianUserlist, xunjianlist } from '@/api/zhinengxunjian/xunjian/index';
import { ElMessage, ElLoading } from 'element-plus';
// 根据任务类型获取对应的文本1待执行2已延期3执行中4已完成
const getTaskTypeText = (type) => {
const typeMap = {
'1': '待执行',
'2': '已延期',
'3': '执行中',
'4': '已完成'
};
// 处理可能的数字输入
return typeMap[type.toString()] || '未知类型';
};
// 根据问题类型获取对应的文本1磁盘使用率2内存使用率3服务状态4响应时间5设备运行状态
const getProblemTypeText = (type) => {
const problemTypeMap = {
'1': '磁盘使用率',
'2': '内存使用率',
'3': '服务状态',
'4': '响应时间',
'5': '设备运行状态'
};
// 处理可能的数字输入
return problemTypeMap[type.toString()] || '未知问题';
};
// 激活的选项卡
const activeTab = ref('task');
import { addjiedian } from '@/api/zhinengxunjian/jiedian/index';
import { ElMessage, ElLoading, ElForm } from 'element-plus';
// 筛选条件
const taskStatus = ref('');
@ -428,7 +438,7 @@ const executor = ref('');
// 任务数据 - 初始为空数组通过API获取
const tasks = ref([]);
// 详情弹窗相关变量
// 任务详情弹窗相关变量
const detailDialogVisible = ref(false);
const detailData = ref(null);
const isDetailLoading = ref(false);
@ -459,6 +469,34 @@ const getStatusClass = (status) => {
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: {
@ -505,11 +543,7 @@ const getTaskList = async () => {
const params = {
pageSize: pageSize.value,
pageNum: currentPage.value,
personId: executor.value !== '' ? executor.value : undefined,
// 根据任务状态映射到后端需要的taskType
taskType: taskStatus.value ? mapTaskStatusToType(taskStatus.value) : undefined,
// 添加计划类型筛选
planType: planType.value || undefined
personId: 1
};
const response = await xjrenwulist(params);
@ -583,44 +617,6 @@ const getTaskList = async () => {
}
};
// 辅助函数将前端状态映射为后端需要的taskType
const mapTaskStatusToType = (status) => {
const statusMap = {
'pending': '1',
'delayed': '2',
'executing': '3',
'completed': '4'
};
return statusMap[status] || '';
};
// 根据person对象获取执行人姓名
const getExecutorName = (person) => {
if (person && typeof person === 'object' && person.userName) {
return person.userName;
}
const executorMap = {
'zhangming': '张明',
'lihua': '李华',
'wangqiang': '王强',
'zhaowei': '赵伟'
};
return executorMap[person] || '未知用户';
};
// 根据plan对象获取计划名称
const getPlanName = (plan) => {
if (plan && typeof plan === 'object' && plan.planName) {
return plan.planName;
}
const planMap = {
'daily': '每日巡检计划',
'weekly': '每周巡检计划',
'monthly': '每月巡检计划'
};
return planMap[plan] || '未知计划';
};
// 页面加载时获取数据
onMounted(() => {
getTaskList();
@ -675,10 +671,11 @@ const createTaskForm = ref({
timeRange: [],
workTimeRange1: null,
workTimeRange2: null,
relatedPlan: 'all',
relatedPlan: '',
executor: '',
taskType: '1', // 默认待执行
problemType: ''
problemType: '',
steps: [{ name: '', intendedPurpose: '', intendedTime: '' }] // 任务步骤数组
});
const createTaskRules = {
@ -694,6 +691,17 @@ 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 = [];
@ -719,6 +727,21 @@ const getTaskTimeInfoString = () => {
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 () => {
// 表单验证
@ -727,6 +750,13 @@ const handleSaveTask = async () => {
return;
}
// 验证所有步骤
const hasEmptyStep = createTaskForm.value.steps.some((step) => !step.name.trim() || !step.intendedPurpose.trim());
if (hasEmptyStep) {
ElMessage.warning('请填写完整所有步骤信息');
return;
}
try {
// 获取timeInfo字符串
const taskTimeInfo = getTaskTimeInfoString();
@ -736,12 +766,45 @@ const handleSaveTask = async () => {
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 = {
createDept: 0,
createBy: 0,
projectId: 1,
createTime: new Date().toISOString(),
updateBy: 0,
updateTime: new Date().toISOString(),
params: {
property1: 'string',
@ -757,7 +820,8 @@ const handleSaveTask = async () => {
personId: createTaskForm.value.executor !== 'all' ? createTaskForm.value.executor : 0,
taskProgress: 0,
taskType: createTaskForm.value.taskType,
problemType: createTaskForm.value.problemType
problemType: createTaskForm.value.problemType,
nodeIds: nodeIds // 添加步骤ID字符串与工单列表页面保持一致
};
// 调用新增任务接口
@ -774,10 +838,11 @@ const handleSaveTask = async () => {
timeRange: [],
workTimeRange1: null,
workTimeRange2: null,
relatedPlan: 'all',
relatedPlan: '',
executor: '',
taskType: '1',
problemType: ''
problemType: '',
steps: [{ name: '', intendedPurpose: '', intendedTime: '' }]
};
// 重新获取任务列表
getTaskList();
@ -846,11 +911,8 @@ const getPlansList = async () => {
label: item.planName || `计划${item.id}`,
value: item.id.toString()
}));
planList.value.unshift({ label: '全部计划', value: 'all' });
} catch (error) {
console.error('获取计划列表失败:', error);
planList.value = [{ label: '全部计划', value: 'all' }];
}
};
@ -868,8 +930,13 @@ const handleCancelCreateTask = () => {
taskName: '',
inspectionTarget: '',
timeRange: [],
relatedPlan: 'all',
executor: 'all'
workTimeRange1: null,
workTimeRange2: null,
relatedPlan: '',
executor: '',
taskType: '1',
problemType: '',
steps: [{ name: '', intendedPurpose: '', intendedTime: '' }]
};
};
@ -974,6 +1041,7 @@ const handleAction = async (task) => {
const updateData = {
...originalTask.rawData,
id: task.id,
startTime: new Date().toISOString().slice(0, 19).replace('T', ' '),
taskType: '3', // 3表示执行中
status: 'executing',
taskProgress: 0
@ -1039,6 +1107,9 @@ const handleAction = async (task) => {
</script>
<style scoped>
@import url('./css/step-bars.css');
@import url('./css/detail-dialog.css');
.inspection-tasks {
padding: 20px;
background-color: #f5f7fa;
@ -1394,6 +1465,96 @@ const handleAction = async (task) => {
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;
@ -1573,4 +1734,11 @@ const handleAction = async (task) => {
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>