0925
This commit is contained in:
@ -423,7 +423,16 @@
|
||||
<div class="steps-container">
|
||||
<div class="step-item" v-for="(step, index) in formData.steps" :key="index">
|
||||
<div class="step-number">{{ index + 1 }}</div>
|
||||
<el-input v-model="step.content" placeholder="输入试验步骤" />
|
||||
<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="formData.steps.length > 1"
|
||||
type="text"
|
||||
@ -524,9 +533,29 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 实验步骤 -->
|
||||
<div v-if="detailData.testStep" class="detail-section">
|
||||
<h3 class="section-title">实验步骤</h3>
|
||||
<!-- 执行步骤 -->
|
||||
<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>
|
||||
|
||||
<!-- 兼容旧格式:如果没有nodes数据但有testStep字符串 -->
|
||||
<div v-else-if="detailData.testStep" class="detail-section">
|
||||
<h3 class="section-title">执行步骤</h3>
|
||||
<div class="steps-container">
|
||||
<div v-for="(step, index) in detailData.testStep.split(',')" :key="index" class="step-item">
|
||||
<div class="step-number">{{ index + 1 }}</div>
|
||||
@ -589,7 +618,7 @@ import router from '@/router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { shiyanDetail, shiyanlist, addshiyan, updateshiyan } from '@/api/zhinengxunjian/shiyan/index';
|
||||
import { xunjianUserlist } from '@/api/zhinengxunjian/xunjian/index';
|
||||
import { addjiedian } from '@/api/zhinengxunjian/jiedian/index';
|
||||
import { addjiedian, updatejiedian } from '@/api/zhinengxunjian/jiedian/index';
|
||||
// 1. 选项卡状态管理
|
||||
const activeTab = ref('plan'); // 默认为"巡检计划"
|
||||
const timeRange = ref('month'); // 统计时间范围:月/周/日
|
||||
@ -822,7 +851,11 @@ const formData = ref({
|
||||
envRequirements: '',
|
||||
manager: '',
|
||||
participants: [], // 改为数组存储多选的用户ID
|
||||
steps: [{ content: '' }, { content: '' }, { content: '' }],
|
||||
steps: [
|
||||
{ name: '', intendedPurpose: '', intendedTime: '' },
|
||||
{ name: '', intendedPurpose: '', intendedTime: '' },
|
||||
{ name: '', intendedPurpose: '', intendedTime: '' }
|
||||
],
|
||||
equipments: [
|
||||
{ name: '服务器(型号:XYZ-9000)', selected: false },
|
||||
{ name: '网络测试仪(型号:NT-5000)', selected: false },
|
||||
@ -920,8 +953,8 @@ const handleSave = async () => {
|
||||
inspectionItems: '',
|
||||
testSolutions: formData.value.riskMitigation,
|
||||
testStep: formData.value.steps
|
||||
.filter((step) => step.content.trim())
|
||||
.map((step) => step.content)
|
||||
.filter((step) => step.name.trim() || step.intendedPurpose.trim())
|
||||
.map((step) => `${step.name || ''}: ${step.intendedPurpose || ''}`)
|
||||
.join(','),
|
||||
testDevice: formData.value.equipments
|
||||
.filter((equip) => equip.selected)
|
||||
@ -932,26 +965,64 @@ const handleSave = async () => {
|
||||
id: editRecordId.value // 若后端用planId等,需改为对应字段名
|
||||
};
|
||||
|
||||
// 4. 调用接口
|
||||
// 4. 处理步骤数据并调用接口
|
||||
let response;
|
||||
|
||||
// 处理步骤数据格式
|
||||
const stepsData = formData.value.steps
|
||||
.filter((step) => step.name.trim() || step.intendedPurpose.trim())
|
||||
.map((step, index) => ({
|
||||
createTime: new Date().toISOString(),
|
||||
updateTime: new Date().toISOString(),
|
||||
params: {},
|
||||
module: 3,
|
||||
code: index + 1,
|
||||
name: step.name,
|
||||
intendedPurpose: step.intendedPurpose,
|
||||
intendedTime: step.intendedTime ? new Date(step.intendedTime).toISOString() : new Date().toISOString(),
|
||||
finishTime: '',
|
||||
remark: '',
|
||||
status: 2
|
||||
}));
|
||||
|
||||
// 获取nodeIds
|
||||
let nodeIds = '';
|
||||
if (editRecordId.value) {
|
||||
// 编辑模式:获取试验详情,以获取原始步骤的id
|
||||
const detailResponse = await shiyanDetail(editRecordId.value);
|
||||
if (detailResponse.code !== 200) {
|
||||
ElMessage.error('获取试验详情失败');
|
||||
return;
|
||||
}
|
||||
|
||||
const experimentDetail = detailResponse.data.rows?.[0] || detailResponse.data;
|
||||
// 兼容两种数据结构:可能在rows数组中,也可能直接在data中
|
||||
|
||||
if (experimentDetail.nodes && Array.isArray(experimentDetail.nodes)) {
|
||||
// 按code排序原始nodes数组
|
||||
const sortedNodes = [...experimentDetail.nodes].sort((a, b) => (a.code || 0) - (b.code || 0));
|
||||
|
||||
// 为新的步骤数据添加id
|
||||
const updatedSteps = stepsData.map((step, index) => ({
|
||||
...step,
|
||||
id: sortedNodes[index]?.id || 0 // 使用原始步骤的id,如果不存在则使用0
|
||||
}));
|
||||
|
||||
// 调用updatejiedian接口更新步骤,直接传递数组
|
||||
const updateResponse = await updatejiedian(updatedSteps);
|
||||
if (updateResponse.code !== 200) {
|
||||
ElMessage.error('更新步骤失败');
|
||||
return;
|
||||
}
|
||||
|
||||
// 使用原始的nodeIds,避免重新创建步骤
|
||||
nodeIds = experimentDetail.nodeIds;
|
||||
}
|
||||
|
||||
// 编辑模式:调用更新接口
|
||||
response = await updateshiyan(requestData);
|
||||
} else {
|
||||
// 处理步骤数据格式
|
||||
const stepsData = formData.value.steps
|
||||
.filter((step) => step.content.trim())
|
||||
.map((step, index) => ({
|
||||
createTime: new Date().toISOString(),
|
||||
updateTime: new Date().toISOString(),
|
||||
remark: step.content.trim(),
|
||||
status: '1', // 使用数字代码
|
||||
// module值为2(与工单列表的1不同)
|
||||
module: 2,
|
||||
sort: index + 1
|
||||
}));
|
||||
|
||||
// 首先调用addjiedian接口
|
||||
// 新增模式:调用addjiedian接口创建步骤
|
||||
const jiedianResponse = await addjiedian(stepsData);
|
||||
|
||||
if (jiedianResponse.code !== 200) {
|
||||
@ -960,7 +1031,6 @@ const handleSave = async () => {
|
||||
}
|
||||
|
||||
// 获取返回的ids,实际返回格式中msg字段包含ids字符串
|
||||
let nodeIds = '';
|
||||
if (jiedianResponse.code === 200 && jiedianResponse.msg) {
|
||||
nodeIds = jiedianResponse.msg;
|
||||
} else {
|
||||
@ -1002,7 +1072,11 @@ const resetForm = () => {
|
||||
envRequirements: '', // 环境要求为空
|
||||
manager: '', // 负责人为空
|
||||
participants: [], // 参与人员为空数组
|
||||
steps: [{ content: '' }, { content: '' }, { content: '' }], // 步骤内容为空
|
||||
steps: [
|
||||
{ name: '', intendedPurpose: '', intendedTime: '' },
|
||||
{ name: '', intendedPurpose: '', intendedTime: '' },
|
||||
{ name: '', intendedPurpose: '', intendedTime: '' }
|
||||
], // 步骤内容为空
|
||||
equipments: [
|
||||
{ name: '服务器(型号:XYZ-9000)', selected: false },
|
||||
{ name: '网络测试仪(型号:NT-5000)', selected: false },
|
||||
@ -1086,8 +1160,12 @@ const handleEditRecord = async (row) => {
|
||||
const sortedNodes = [...recordDetail.nodes].sort((a, b) => (a.code || 0) - (b.code || 0));
|
||||
// 转换为所需的格式
|
||||
sortedNodes.forEach((node) => {
|
||||
if (node.intendedPurpose && node.intendedPurpose.trim()) {
|
||||
steps.push({ content: node.intendedPurpose.trim() });
|
||||
if ((node.name && node.name.trim()) || (node.intendedPurpose && node.intendedPurpose.trim())) {
|
||||
steps.push({
|
||||
name: node.name || '',
|
||||
intendedPurpose: node.intendedPurpose || '',
|
||||
intendedTime: node.intendedTime || ''
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -1099,13 +1177,18 @@ const handleEditRecord = async (row) => {
|
||||
// 移除序号前缀(如"1. "),只保留内容
|
||||
const content = stepText.replace(/^\d+\.\s*/, '').trim();
|
||||
if (content) {
|
||||
steps.push({ content });
|
||||
// 对于旧格式数据,我们将内容放入intendedPurpose字段
|
||||
steps.push({
|
||||
name: `步骤${steps.length + 1}`,
|
||||
intendedPurpose: content,
|
||||
intendedTime: ''
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
// 确保至少有3个步骤(如果解析后为空)
|
||||
while (steps.length < 3) {
|
||||
steps.push({ content: '' });
|
||||
steps.push({ name: '', intendedPurpose: '', intendedTime: '' });
|
||||
}
|
||||
|
||||
// 4. 处理testDevice:将逗号分隔的字符串转换为设备数组
|
||||
@ -1183,7 +1266,7 @@ const handleEditRecord = async (row) => {
|
||||
};
|
||||
// 添加新步骤
|
||||
const addStep = () => {
|
||||
formData.value.steps.push({ content: '' });
|
||||
formData.value.steps.push({ name: '', intendedPurpose: '', intendedTime: '' });
|
||||
};
|
||||
|
||||
// 删除步骤
|
||||
@ -1291,10 +1374,54 @@ const formatDate = (dateString) => {
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
};
|
||||
|
||||
// 根据步骤状态获取对应的样式类
|
||||
const getStatusClass = (status) => {
|
||||
// 处理可能的数字输入
|
||||
const statusStr = status?.toString() || '';
|
||||
const statusClassMap = {
|
||||
'1': 'status-pending',
|
||||
'2': 'status-running',
|
||||
'3': 'status-completed',
|
||||
'4': 'status-delayed',
|
||||
'5': 'status-failed'
|
||||
};
|
||||
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': '已延期',
|
||||
'5': '失败'
|
||||
};
|
||||
return statusMap[statusStr] || '未知状态';
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
/* 1. 基础容器样式(继承试验系统) */
|
||||
@import url('./css/step-bars.css');
|
||||
@import url('./css/detail-dialog.css');
|
||||
.operation-inspection {
|
||||
padding: 20px;
|
||||
background-color: #f9fbfd;
|
||||
@ -1336,7 +1463,7 @@ const formatDate = (dateString) => {
|
||||
box-shadow: 0 2px 4px rgba(64, 158, 255, 0.3);
|
||||
}
|
||||
|
||||
/* 3. 页面标题(与试验系统一致) */
|
||||
/* 3. 页面标题 */
|
||||
.page-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
@ -2094,221 +2221,4 @@ const formatDate = (dateString) => {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
/* 详情弹窗样式 */
|
||||
.custom-experiment-dialog .el-dialog__body {
|
||||
padding: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.detail-content {
|
||||
max-height: 600px;
|
||||
overflow-y: auto;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
/* 详情区块 */
|
||||
.detail-section {
|
||||
margin-bottom: 24px;
|
||||
padding: 16px;
|
||||
border: 1px solid #e4e7ed;
|
||||
border-radius: 8px;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1890ff;
|
||||
margin-bottom: 16px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #e8f4ff;
|
||||
}
|
||||
|
||||
/* 基础信息网格 */
|
||||
.detail-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.detail-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.detail-label {
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.detail-value {
|
||||
font-size: 14px;
|
||||
color: #2c3e50;
|
||||
padding: 4px 0;
|
||||
}
|
||||
|
||||
/* 文本区域 */
|
||||
.detail-textarea {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.detail-text {
|
||||
font-size: 14px;
|
||||
color: #495057;
|
||||
line-height: 1.6;
|
||||
padding: 8px 0;
|
||||
min-height: 60px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
/* 设备列表样式 */
|
||||
.device-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.device-tag {
|
||||
display: inline-block;
|
||||
padding: 6px 12px;
|
||||
background-color: #f0f9ff;
|
||||
color: #1890ff;
|
||||
border: 1px solid #bae7ff;
|
||||
border-radius: 16px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
/* 步骤条样式 */
|
||||
.steps-container {
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.step-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.step-item:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.step-item:not(:last-child)::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
left: 17px;
|
||||
top: 36px;
|
||||
bottom: -16px;
|
||||
width: 2px;
|
||||
background-color: #e4e7ed;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.step-number {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 50%;
|
||||
background-color: #1890ff;
|
||||
color: white;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin-right: 16px;
|
||||
flex-shrink: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.step-content {
|
||||
flex: 1;
|
||||
padding: 8px 16px;
|
||||
background-color: #fafafa;
|
||||
border-radius: 6px;
|
||||
font-size: 14px;
|
||||
color: #2c3e50;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* 列表样式 */
|
||||
.participant-list,
|
||||
.inspection-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.participant-item,
|
||||
.inspection-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
padding: 12px 16px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.participant-name,
|
||||
.inspection-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #2c3e50;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.participant-team,
|
||||
.participant-role,
|
||||
.inspection-type {
|
||||
font-size: 13px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.participant-item,
|
||||
.inspection-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
padding: 12px 16px;
|
||||
background-color: #f8f9fa;
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
.participant-name,
|
||||
.inspection-name {
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #2c3e50;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
.participant-team,
|
||||
.participant-role,
|
||||
.inspection-type {
|
||||
font-size: 13px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
/* 详情弹窗响应式设计 */
|
||||
@media (max-width: 768px) {
|
||||
.detail-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.participant-item,
|
||||
.inspection-item {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.participant-name,
|
||||
.inspection-name {
|
||||
min-width: auto;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user