Files
maintenance_system/src/views/zhinengxunjian/InspectionManagement.vue
2025-09-26 20:32:14 +08:00

1310 lines
41 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div>
<div class="operation-inspection">
<!-- 导航标签 -->
<!-- <div class="navigation-tabs">
<div class="nav-tab" @click="handleInspection1">待办事项</div>
<div class="nav-tab active" @click="handleInspection2">巡检管理</div>
<div class="nav-tab" @click="handleInspection3">试验管理</div>
<div class="nav-tab" @click="handleInspection4">报修管理</div>
<div class="nav-tab" @click="handleInspection5">抢修管理</div>
<div class="nav-tab" @click="handleInspection6">工单管理</div>
<div class="nav-tab" @click="handleInspection7">运维组织</div>
</div> -->
<!-- 子选项卡 -->
<div class="tabs-wrapper">
<div style="display: flex; align-items: center; gap: 10px">
<el-button type="primary" @click="handleInspectionManagement1">巡检计划</el-button>
<el-button type="primary" @click="handleInspectionManagement2">巡检任务</el-button>
<el-button type="primary" @click="handleInspectionManagement3">巡检记录</el-button>
</div>
</div>
<!-- 筛选栏 -->
<div class="filter-bar">
<div class="filter-item">
<el-select v-model="planType" placeholder="计划类型" clearable>
<el-option label="全部类型" value="all"></el-option>
<el-option label="每日巡检" value="1"></el-option>
<el-option label="每周巡检" value="2"></el-option>
<el-option label="每月巡检" value="3"></el-option>
</el-select>
</div>
<div class="filter-item">
<el-select v-model="status" placeholder="全部状态" clearable>
<el-option label="全部状态" value="all"></el-option>
<el-option label="启用中" value="enabled"></el-option>
<el-option label="已停用" value="disabled"></el-option>
</el-select>
</div>
<div class="filter-item">
<el-select v-model="inspectionObject" placeholder="巡检对象" clearable>
<el-option label="全部对象" value="all"></el-option>
<el-option label="服务器" value="1"></el-option>
<el-option label="网络设备" value="2"></el-option>
<el-option label="应用系统" value="3"></el-option>
<el-option label="基础设施" value="4"></el-option>
</el-select>
</div>
<div class="filter-actions">
<el-button type="primary" icon="Search" class="search-btn" @click="handleSearch"> 搜索 </el-button>
<el-button type="primary" icon="Plus" class="create-btn" @click="handleCreate">手动创建计划</el-button>
</div>
</div>
<!-- 表格 -->
<div class="table-wrapper">
<el-table :data="pagedTableData" stripe style="width: 100%" highlight-current-row class="custom-table">
<el-table-column align="center" prop="name" label="计划名称" style="width: 14.2%"></el-table-column>
<el-table-column align="center" prop="type" label="类型" style="width: 14.2%"></el-table-column>
<el-table-column align="center" prop="object" label="巡检对象" style="width: 14.2%"></el-table-column>
<el-table-column align="center" prop="frequency" label="频率" style="width: 14.2%"></el-table-column>
<el-table-column align="center" prop="status" label="状态" style="width: 14.2%">
<template #default="scope">
<el-tag :type="scope.row.status === 'enabled' ? 'success' : 'info'" class="status-tag">
{{ scope.row.status === 'enabled' ? '启用中' : '已停用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column align="center" prop="responsible" label="负责人" style="width: 14.2%"></el-table-column>
<el-table-column align="center" label="操作" style="width: 14.2%" fixed="right">
<template #default="scope">
<el-button type="text" @click="handleEdit(scope.row)" class="action-btn">编辑</el-button>
<el-button type="text" @click="handleDetail(scope.row)" class="action-btn">详情</el-button>
<el-button
type="text"
@click="handleEnable(scope.row)"
class="action-btn"
:class="{ 'text-success': scope.row.status === 'disabled', 'text-danger': scope.row.status === 'enabled' }"
>
{{ scope.row.status === 'enabled' ? '停用' : '启用' }}
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<!-- 分页 -->
<div class="pagination-section">
<div class="pagination-info">
显示第{{ (currentPage - 1) * pageSize + 1 }}{{ Math.min(currentPage * pageSize, total) }}共有{{ total }}条记录
</div>
<div class="pagination-controls">
<el-pagination
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
:current-page="currentPage"
:page-sizes="[10, 20, 30, 40]"
:page-size="pageSize"
layout="total, sizes, prev, pager, next, jumper"
:total="total"
background
>
</el-pagination>
</div>
</div>
</div>
<!-- 创建/编辑计划弹窗 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="800px"
class="create-plan-dialog"
center
:show-close="true"
@close="cancelCreatePlan"
>
<el-form :model="formData" :rules="createFormRules" ref="formRef" label-width="150px" class="custom-form">
<!-- 计划名称 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="计划名称*" prop="planName">
<el-input v-model="formData.planName" placeholder="请输入计划名称" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<!-- 计划类型巡检对象类型 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="计划类型*" prop="planType">
<el-select v-model="formData.planType" placeholder="请选择计划类型" style="width: 100%">
<el-option label="每日巡检" value="1" />
<el-option label="每周巡检" value="2" />
<el-option label="每月巡检" value="3" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="巡检对象类型*" prop="objectType">
<el-select v-model="formData.objectType" placeholder="请选择对象类型" style="width: 100%">
<el-option label="服务器" value="1" />
<el-option label="网络设备" value="2" />
<el-option label="应用系统" value="3" />
<el-option label="基础设施" value="4" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 开始日期结束日期 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="开始日期*" prop="startDate">
<el-date-picker
v-model="formData.startDate"
type="date"
placeholder="选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="结束日期*" prop="endDate">
<el-date-picker
v-model="formData.endDate"
type="date"
placeholder="选择日期"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
style="width: 100%"
/>
</el-form-item>
</el-col>
</el-row>
<!-- 巡检频率 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="巡检频率*" prop="frequencyType" class="frequency-item">
<el-radio-group v-model="formData.frequencyType" @change="handleFrequencyChange">
<el-radio :label="'daily'" class="frequency-radio">每天</el-radio>
<el-radio :label="'weekly'" class="frequency-radio">每周</el-radio>
<el-radio :label="'monthly'" class="frequency-radio">每月</el-radio>
</el-radio-group>
<!-- 每周-选择周几 -->
<el-select
v-model="formData.weekDay"
placeholder="选择周几"
style="margin-left: 10px; width: 120px"
v-if="formData.frequencyType === 'weekly'"
>
<el-option label="周一" value="1"></el-option>
<el-option label="周二" value="2"></el-option>
<el-option label="周三" value="3"></el-option>
<el-option label="周四" value="4"></el-option>
<el-option label="周五" value="5"></el-option>
<el-option label="周六" value="6"></el-option>
<el-option label="周日" value="7"></el-option>
</el-select>
<!-- 每月-选择几号 -->
<el-select
v-model="formData.monthDay"
placeholder="选择几号"
style="margin-left: 10px; width: 120px"
v-if="formData.frequencyType === 'monthly'"
>
<el-option v-for="day in 31" :key="day" :label="`${day}号`" :value="day"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 计划开始时间持续时间 -->
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="计划开始时间*" prop="planBeginTime">
<el-time-picker v-model="formData.planBeginTime" placeholder="选择时间" format="HH:mm" value-format="HH:mm" style="width: 100%" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="持续时间(分钟)*" prop="duration">
<el-input v-model.number="formData.duration" placeholder="请输入持续时间" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
<!-- 负责人 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="负责人*" prop="person">
<el-select v-model="formData.person" placeholder="请选择负责人" style="width: 100%">
<el-option v-for="user in userList" :key="user.value" :label="user.label" :value="user.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 巡检项 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="巡检项*" prop="inspectionItemId">
<div class="inspection-item-wrapper">
<el-checkbox-group v-model="formData.inspectionItemId" class="inspection-item-group">
<el-checkbox
v-for="item in inspectionItemList"
:key="item.value"
:label="item.value"
style="margin-right: 20px; margin-bottom: 10px"
>
{{ item.label }}
</el-checkbox>
</el-checkbox-group>
<el-button type="text" @click="handleAddInspectionItem">添加自定义检查项</el-button>
</div>
</el-form-item>
</el-col>
</el-row>
<!-- 状态 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="状态*" prop="status">
<el-select v-model="formData.status" placeholder="请选择状态" style="width: 100%">
<el-option label="启用" value="1" />
<el-option label="停用" value="2" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<!-- 备注说明 -->
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="备注说明" prop="remark">
<el-input v-model="formData.remark" type="textarea" placeholder="请输入备注" rows="3" style="width: 100%" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="cancelCreatePlan">取消</el-button>
<el-button type="primary" @click="submitForm">保存任务</el-button>
</span>
</template>
</el-dialog>
<!-- 新增巡检项弹窗 -->
<el-dialog :title="itemDialogTitle" v-model="itemDialogVisible" width="650px" center append-to-body>
<el-form :model="itemForm" :rules="itemFormRules" ref="itemFormRef" label-width="135px">
<el-form-item label="自定义巡检项名称" prop="name">
<el-input v-model="itemForm.name" placeholder="请输入自定义巡检项名称" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancelAddItem">取消</el-button>
<el-button type="primary" @click="submitAddItem">确定</el-button>
</div>
</template>
</el-dialog>
<!-- 详情弹窗 -->
<el-dialog
v-model="detailDialogVisible"
title="巡检计划详情"
width="800px"
:before-close="handleCloseDetailDialog"
class="custom-experiment-dialog"
>
<div class="task-detail-container">
<!-- 基础信息区 -->
<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.planName || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">状态</span>
<span class="info-value task-status">
<el-tag :type="detailData.status === '1' ? 'success' : 'info'">
{{ detailData.status === '1' ? '启用' : detailData.status === '2' ? '停用' : '-' }}
</el-tag>
</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="info-label">计划类型</span>
<span class="info-value">{{ getPlanTypeText(detailData.planType) || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">巡检对象</span>
<span class="info-value">{{ getObjectTypeText(detailData.objectType) || '-' }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="info-label">巡检频率</span>
<span class="info-value">{{ detailData.inspectionFrequency || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">负责人</span>
<span class="info-value">{{ detailData.nickName || '-' }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="info-label">开始日期</span>
<span class="info-value">{{ formatDate(detailData.beginTime) || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">结束日期</span>
<span class="info-value">{{ formatDate(detailData.endTime) || '-' }}</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="info-label">计划开始时间</span>
<span class="info-value">{{ detailData.planBeginTime || '-' }}</span>
</div>
<div class="info-item">
<span class="info-label">持续时间</span>
<span class="info-value">{{ detailData.duration || '-' }}分钟</span>
</div>
</div>
<div class="info-row">
<div class="info-item">
<span class="info-label">巡检项</span>
<div class="info-value">
<span v-for="(item, index) in detailData.itemVoList" :key="item.id" class="inspection-item-tag">
{{ item.name }}
<span v-if="index < detailData.itemVoList.length - 1" class="item-separator"></span>
</span>
<span v-if="!detailData.itemVoList || detailData.itemVoList.length === 0">-</span>
</div>
</div>
<div class="info-item">
<span class="info-label">电站ID</span>
<span class="info-value">{{ detailData.projectId || '-' }}</span>
</div>
</div>
</div>
</div>
<!-- 备注信息 -->
<div v-if="detailData.remark" class="detail-card">
<h3 class="card-title">备注信息</h3>
<div class="card-content">
<div class="description-content">
{{ detailData.remark }}
</div>
</div>
</div>
</div>
<template #footer>
<span class="dialog-footer">
<el-button @click="closeDetailDialog">关闭</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup lang="js">
import { ref, computed, reactive, onMounted, nextTick } from 'vue';
import router from '@/router';
import { addxunjian, xunjianlist, xunjianDetail, updatexunjian, xunjianUserlist, xunjianItemlist } from '@/api/zhinengxunjian/xunjian';
import { addItem } from '@/api/zhinengxunjian/inspection/item/index';
import { ElMessage, ElMessageBox } from 'element-plus';
// 表单引用
const formRef = ref(null);
const itemFormRef = ref(null);
// 搜索筛选条件
const planType = ref('all');
const status = ref('all');
const inspectionObject = ref('all');
// 表格数据
const tableData = ref([]);
const currentPage = ref(1);
const pageSize = ref(10);
const total = ref(0);
// 下拉列表数据
const userList = ref([]);
const inspectionItemList = ref([]);
// 弹窗状态
const dialogVisible = ref(false);
const dialogTitle = ref('新建巡检计划');
const isEditing = ref(false); // 新增/编辑状态标识
const currentEditId = ref('');
const initFormData = {
planName: '',
planType: '',
objectType: '',
startDate: '',
endDate: '',
frequencyType: '',
weekDay: '',
monthDay: '',
planBeginTime: '',
duration: null,
person: '',
inspectionItemId: [],
status: '',
remark: ''
};
const initData = {
formData: { ...initFormData },
queryParams: {
pageNum: 1,
pageSize: 10,
userName: '',
phonenumber: '',
status: '',
deptId: '',
roleId: ''
},
rules: {
planName: [{ required: true, message: '请输入计划名称', trigger: 'blur' }],
planType: [{ required: true, message: '请选择计划类型', trigger: 'change' }],
objectType: [{ required: true, message: '请选择对象类型', trigger: 'change' }],
startDate: [{ required: true, message: '请选择开始日期', trigger: 'change' }],
endDate: [{ required: true, message: '请选择结束日期', trigger: 'change' }],
frequencyType: [{ required: true, message: '请选择巡检频率', trigger: 'change' }],
weekDay: [
{
required: true,
message: '请选择周几',
trigger: 'change'
}
],
monthDay: [
{
required: true,
message: '请选择几号',
trigger: 'change'
}
],
planBeginTime: [{ required: true, message: '请选择计划开始时间', trigger: 'change' }],
duration: [
{ required: true, message: '请输入持续时间', trigger: 'blur' },
{ type: 'number', min: 1, message: '持续时间必须大于0', trigger: 'blur' }
],
person: [{ required: true, message: '请选择负责人', trigger: 'change' }],
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
}
};
const data = reactive(initData);
const { formData, createFormRules } = toRefs(data);
// 表单数据 - 使用独立对象存储,避免引用污染
// const formData = ref({
// planName: '',
// planType: '',
// objectType: '',
// startDate: '',
// endDate: '',
// frequencyType: '',
// weekDay: '',
// monthDay: '',
// planBeginTime: '',
// duration: null,
// person: '',
// inspectionItemId: [],
// status: '',
// remark: ''
// });
// 表单验证规则
// const createFormRules = computed(() => ({
// planName: [{ required: true, message: '请输入计划名称', trigger: 'blur' }],
// planType: [{ required: true, message: '请选择计划类型', trigger: 'change' }],
// objectType: [{ required: true, message: '请选择对象类型', trigger: 'change' }],
// startDate: [{ required: true, message: '请选择开始日期', trigger: 'change' }],
// endDate: [{ required: true, message: '请选择结束日期', trigger: 'change' }],
// frequencyType: [{ required: true, message: '请选择巡检频率', trigger: 'change' }],
// weekDay: [
// {
// required: true,
// message: '请选择周几',
// trigger: 'change'
// }
// ],
// monthDay: [
// {
// required: true,
// message: '请选择几号',
// trigger: 'change'
// }
// ],
// planBeginTime: [{ required: true, message: '请选择计划开始时间', trigger: 'change' }],
// duration: [
// { required: true, message: '请输入持续时间', trigger: 'blur' },
// { type: 'number', min: 1, message: '持续时间必须大于0', trigger: 'blur' }
// ],
// person: [{ required: true, message: '请选择负责人', trigger: 'change' }],
// status: [{ required: true, message: '请选择状态', trigger: 'change' }]
// }));
// 巡检项弹窗数据
const itemDialogVisible = ref(false);
const itemDialogTitle = ref('新增巡检项');
const itemForm = reactive({
name: '',
type: '1',
projectId: 1
});
const itemFormRules = {
name: [{ required: true, message: '自定义巡检项名称不能为空', trigger: 'blur' }]
};
// 详情弹窗数据
const detailDialogVisible = ref(false);
const detailData = reactive({});
// 初始化页面数据
onMounted(() => {
getInspectionList();
});
// 获取巡检计划列表
const getInspectionList = async () => {
try {
const response = await xunjianlist({
pageNum: currentPage.value,
pageSize: pageSize.value,
planType: planType.value === 'all' ? '' : planType.value,
status: status.value === 'all' ? '' : status.value === 'enabled' ? '1' : '2',
objectType: inspectionObject.value === 'all' ? '' : inspectionObject.value
});
if (response.code === 200) {
const rows = Array.isArray(response.rows) ? response.rows : response.data?.rows || [];
const totalCount = Number(response.total) || Number(response.data?.total) || 0;
tableData.value = rows.map((item) => ({
id: item.id || '',
name: String(item.planName || '未知名称'),
type: getPlanTypeText(item.planType),
object: getObjectTypeText(item.objectType),
frequency: item.inspectionFrequency || '未知频率',
status: item.status === '1' ? 'enabled' : 'disabled',
responsible: item.nickName || '未知负责人',
originalData: { ...item }
}));
total.value = totalCount;
} else {
ElMessage.warning(`获取数据失败: ${response.msg || '未知错误'}`);
}
} catch (error) {
console.error('获取巡检计划列表失败:', error);
ElMessage.error('获取巡检计划列表失败');
}
};
// 分页相关方法
const pagedTableData = computed(() => {
const startIndex = (currentPage.value - 1) * pageSize.value;
const endIndex = startIndex + pageSize.value;
return tableData.value.slice(startIndex, endIndex);
});
const handleSizeChange = (val) => {
pageSize.value = val;
currentPage.value = 1;
getInspectionList();
};
const handleCurrentChange = (val) => {
currentPage.value = val;
getInspectionList();
};
// 搜索方法
const handleSearch = () => {
currentPage.value = 1;
getInspectionList();
};
// 类型转换方法
const getPlanTypeText = (type) => {
const typeMap = { '1': '每日巡检', '2': '每周巡检', '3': '每月巡检' };
return typeMap[type] || '未知类型';
};
const getObjectTypeText = (type) => {
const typeMap = { '1': '服务器', '2': '网络设备', '3': '应用系统', '4': '基础设施' };
return typeMap[type] || '未知对象';
};
// 格式化日期
const formatDate = (dateString) => {
if (!dateString) return '';
try {
const date = new Date(dateString);
return date.toLocaleDateString('zh-CN');
} catch (e) {
return dateString.split('T')[0] || dateString;
}
};
// 获取下拉列表数据
const getUsersList = async () => {
try {
const response = await xunjianUserlist();
// 适配新接口格式检查code为200且rows为数组
const userRows = response.code === 200 && response.rows && Array.isArray(response.rows) ? response.rows : [];
userList.value = userRows
.filter((item) => item && typeof item === 'object')
.map((item) => ({
label: item.userName || '未知用户',
value: String(item.userId || '') // 使用userId作为唯一标识
}));
if (userList.value.length === 0) {
userList.value = [{ label: '默认用户', value: 'default' }];
}
} catch (error) {
console.error('获取用户失败:', error);
userList.value = [{ label: '默认用户', value: 'default' }];
}
};
const getInspectionItemsList = async () => {
try {
const response = await xunjianItemlist();
if (response && response.code === 200) {
const rows = response.rows || (response.data && response.data.rows) || [];
inspectionItemList.value = rows.map((item) => ({
label: item.name || `未命名项(${item.id})`,
value: item.id
}));
} else {
inspectionItemList.value = [];
}
} catch (error) {
console.error('获取巡检项列表失败:', error);
inspectionItemList.value = [];
}
};
// 【核心修复1】完全重置表单数据
const resetFormData = () => {
formData.value = { ...initFormData };
if (formRef.value) formRef.value.resetFields();
isEditing.value = false;
currentEditId.value = '';
};
// 【核心修复2】编辑功能 - 使用深拷贝避免影响原始数据
const handleEdit = async (row) => {
console.log(formData.value);
try {
// 1. 先重置表单到初始状态
resetFormData();
Object.assign(formData.value, {
planName: '',
planType: '',
objectType: '',
startDate: '',
endDate: '',
frequencyType: 'daily',
weekDay: '',
monthDay: '',
planBeginTime: '',
duration: null,
person: '',
inspectionItemId: [],
status: '1',
remark: ''
});
// 2. 加载必要数据
await Promise.all([getUsersList(), getInspectionItemsList()]);
// 3. 深拷贝原始数据,避免引用关联
const originalData = JSON.parse(JSON.stringify(row.originalData));
// 4. 填充表单数据
console.log(formData.value);
formData.value.planName = originalData.planName || '';
formData.value.planType = originalData.planType || '';
formData.value.objectType = originalData.objectType || '';
formData.value.startDate = originalData.beginTime ? originalData.beginTime.substring(0, 10) : '';
formData.value.endDate = originalData.endTime ? originalData.endTime.substring(0, 10) : '';
formData.value.planBeginTime = originalData.planBeginTime || '';
formData.value.duration = originalData.duration || null;
formData.value.person = originalData.person || '';
formData.value.status = originalData.status || '';
formData.value.remark = originalData.remark || '';
// 处理巡检项(数组类型)
if (originalData.inspectionItemId) {
formData.value.inspectionItemId = Array.isArray(originalData.inspectionItemId)
? [...originalData.inspectionItemId] // 数组则创建新数组
: typeof originalData.inspectionItemId === 'string'
? originalData.inspectionItemId.split(',').filter((id) => id.trim())
: [];
} else {
formData.value.inspectionItemId = [];
}
// 处理频率
const frequencyDesc = originalData.inspectionFrequency || '';
if (frequencyDesc.includes('每天')) {
formData.value.frequencyType = 'daily';
} else if (frequencyDesc.includes('每周')) {
formData.value.frequencyType = 'weekly';
const weekdays = ['一', '二', '三', '四', '五', '六', '日'];
const weekDayMatch = frequencyDesc.match(/每周(.)/);
if (weekDayMatch) {
const weekDayIndex = weekdays.indexOf(weekDayMatch[1]);
if (weekDayIndex !== -1) {
formData.value.weekDay = String(weekDayIndex + 1);
}
}
} else if (frequencyDesc.includes('每月')) {
formData.value.frequencyType = 'monthly';
const monthDayMatch = frequencyDesc.match(/每月(\d+)号/);
if (monthDayMatch) {
formData.value.monthDay = monthDayMatch[1];
}
}
// 标记为编辑状态
isEditing.value = true;
currentEditId.value = row.id;
// 打开弹窗
dialogVisible.value = true;
dialogTitle.value = '编辑巡检计划';
} catch (error) {
console.error('编辑数据加载失败:', error);
ElMessage.error('加载编辑数据失败');
}
};
// 【核心修复3】新增功能 - 确保打开时表单完全干净
const handleCreate = async () => {
dialogVisible.value = true;
try {
// 1. 强制重置表单(关键步骤)
resetFormData();
Object.assign(formData, {});
// 2. 加载下拉数据
await Promise.all([getUsersList(), getInspectionItemsList()]);
// 3. 确保是新增状态
isEditing.value = false;
currentEditId.value = '';
// 4. 打开弹窗
dialogTitle.value = '新建巡检计划';
} catch (error) {
console.error('打开新增弹窗失败:', error);
ElMessage.error('打开新增窗口失败');
}
};
// 频率变更处理
const handleFrequencyChange = () => {
if (formData.value.frequencyType !== 'weekly') {
formData.value.weekDay = '';
}
if (formData.value.frequencyType !== 'monthly') {
formData.value.monthDay = '';
}
};
// 提交表单
const submitForm = async () => {
try {
if (!formRef.value) return;
const valid = await formRef.value.validate();
if (!valid) return;
// 构建频率描述
let frequencyDesc = '';
if (formData.value.frequencyType === 'daily') {
frequencyDesc = '每天';
} else if (formData.value.frequencyType === 'weekly') {
const weekdays = ['一', '二', '三', '四', '五', '六', '日'];
frequencyDesc = `每周${weekdays[formData.value.weekDay - 1]}`;
} else if (formData.value.frequencyType === 'monthly') {
frequencyDesc = `每月${formData.value.monthDay}`;
}
// 构建提交数据
const submitData = {
projectId: 1,
planName: formData.value.planName,
planType: formData.value.planType,
objectType: formData.value.objectType,
beginTime: formData.value.startDate + 'T00:00:00.000Z',
endTime: formData.value.endDate + 'T23:59:59.999Z',
inspectionFrequency: frequencyDesc,
planBeginTime: formData.value.planBeginTime,
duration: formData.value.duration,
person: formData.value.person,
inspectionItemId: formData.value.inspectionItemId.join(','),
status: formData.value.status,
remark: formData.value.remark || ''
};
// 编辑模式添加ID
if (isEditing.value && currentEditId.value) {
submitData.id = currentEditId.value;
}
// 提交请求
const response = isEditing.value ? await updatexunjian(submitData) : await addxunjian(submitData);
if (response.code === 200) {
ElMessage.success(isEditing.value ? '更新成功' : '创建成功');
dialogVisible.value = false;
resetFormData();
getInspectionList();
} else {
ElMessage.error(response.msg || (isEditing.value ? '更新失败' : '创建失败'));
}
} catch (error) {
console.error(isEditing.value ? '更新失败:' : '创建失败:', error);
ElMessage.error(isEditing.value ? '更新失败,请重试' : '创建失败,请重试');
}
};
// 取消表单操作
const cancelCreatePlan = () => {
dialogVisible.value = false;
// 延迟重置,避免关闭动画中出现数据闪烁
nextTick(() => {
resetFormData();
});
};
// 巡检项相关方法
const handleAddInspectionItem = () => {
itemForm.name = '';
itemFormRef.value?.resetFields();
itemDialogVisible.value = true;
};
const cancelAddItem = () => {
itemDialogVisible.value = false;
};
const submitAddItem = async () => {
try {
if (itemFormRef.value) {
const valid = await itemFormRef.value.validate();
if (valid) {
const response = await addItem(itemForm);
if (response.code === 200) {
ElMessage.success('新增巡检项成功');
itemDialogVisible.value = false;
await getInspectionItemsList();
} else {
ElMessage.error(response.msg || '新增巡检项失败');
}
}
}
} catch (error) {
console.error('新增巡检项失败:', error);
ElMessage.error('新增巡检项失败,请重试');
}
};
// 详情相关方法
const handleDetail = async (row) => {
try {
const response = await xunjianDetail(row.id);
if (response.code === 200 && response.data) {
Object.assign(detailData, response.data);
detailDialogVisible.value = true;
} else {
ElMessage.error(response.msg || '获取详情失败');
}
} catch (error) {
console.error('获取详情失败:', error);
ElMessage.error('获取详情失败,请重试');
}
};
const closeDetailDialog = () => {
detailDialogVisible.value = false;
Object.keys(detailData).forEach((key) => delete detailData[key]);
};
// 启用/停用
const handleEnable = async (row) => {
try {
// 获取当前行的原始数据
const originalData = row.originalData || {};
const currentStatus = originalData.status || '1';
const targetStatus = currentStatus === '1' ? '2' : '1';
const statusText = targetStatus === '1' ? '启用' : '停用';
// 显示确认对话框
await ElMessageBox.confirm(`确定要${statusText}该巡检计划吗?`, '操作确认', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
});
// 构建完整的更新数据对象,包含所有必要字段
const updateData = {
id: row.id,
projectId: originalData.projectId || 1,
planName: originalData.planName || '',
planType: originalData.planType || '',
objectType: originalData.objectType || '',
beginTime: originalData.beginTime || '',
endTime: originalData.endTime || '',
inspectionFrequency: originalData.inspectionFrequency || '',
planBeginTime: originalData.planBeginTime || '',
duration: originalData.duration || 0,
person: originalData.person || '',
inspectionItemId: originalData.inspectionItemId || '',
status: targetStatus, // 只修改状态字段
remark: originalData.remark || ''
};
// 调用更新接口,传入完整数据
const response = await updatexunjian(updateData);
if (response.code === 200) {
ElMessage.success(`已成功${statusText}该巡检计划`);
// 更新本地数据以反映状态变化
originalData.status = targetStatus;
row.status = targetStatus === '1' ? 'enabled' : 'disabled';
} else {
ElMessage.error(`${statusText}失败:${response.msg || '服务器异常'}`);
}
} catch (error) {
// 忽略用户取消操作的错误
if (error !== 'cancel') {
console.error('启用/停用失败:', error);
ElMessage.error('操作失败,请重试');
}
}
};
// 导航相关方法
const handleInspection1 = () => {
router.push('/rili/rili');
};
const handleInspection2 = () => {
router.push('/rili/InspectionManagement');
};
const handleInspection3 = () => {
router.push('/rili/shiyanguanli');
};
const handleInspection4 = () => {
router.push('/rili/baoxiuguanli');
};
const handleInspection5 = () => {
router.push('/rili/qiangxiuguanli');
};
const handleInspection6 = () => {
router.push('/rili/gongdanliebiao');
};
const handleInspection7 = () => {
router.push('/rili/renyuanzhuangtai');
};
const handleInspectionManagement1 = () => {
router.push('/rili/InspectionManagement');
};
const handleInspectionManagement2 = () => {
router.push('/rili/xunjianrenwu');
};
const handleInspectionManagement3 = () => {
router.push('/rili/xunjianjihua');
};
</script>
<style scoped>
@import url('./css/detail-dialog.css');
@import url('./css/step-bars.css');
.operation-inspection {
padding: 20px;
background-color: #f5f7fa;
min-height: 100vh;
}
.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);
}
.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;
padding: 20px;
border-radius: 8px;
margin-bottom: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 16px;
}
.filter-item {
flex-shrink: 0;
}
.filter-bar .el-select {
width: 150px;
height: 36px;
}
.filter-bar {
background-color: #fff;
border-radius: 8px;
margin-bottom: 24px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
padding: 16px 24px;
}
.filter-container {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: 16px;
width: 100%;
}
.filter-item {
flex-shrink: 0;
}
.filter-bar .el-select {
width: 180px;
height: 36px;
}
.filter-actions {
margin-left: auto;
display: flex;
gap: 10px;
}
.search-btn,
.create-btn {
height: 36px;
border-radius: 4px;
}
.table-wrapper {
background-color: #fff;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.pagination-section {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 0;
background-color: #fff;
border-radius: 8px;
padding: 16px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05);
}
.status-tag {
padding: 2px 8px;
font-size: 12px;
}
.action-btn {
padding: 0 8px;
color: #409eff;
}
.action-btn.text-success {
color: #67c23a;
}
.action-btn.text-danger {
color: #f56c6c;
}
/* 弹窗样式 */
.create-plan-dialog .el-dialog__body {
padding: 24px;
}
/* 详情弹窗样式 - 与工单列表页面保持一致 */
.custom-experiment-dialog .el-dialog__body {
max-height: 60vh;
overflow-y: auto;
padding: 24px;
}
.task-detail-container {
padding: 10px 0;
}
/* 详情卡片样式 */
.detail-card {
background-color: #fff;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.05);
border: 1px solid #f0f2f5;
}
.card-title {
font-size: 16px;
font-weight: 600;
color: #1d2129;
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 2px solid #409eff;
}
.card-content {
padding: 0 4px;
}
/* 信息行和信息项样式 */
.info-row {
display: flex;
margin-bottom: 16px;
flex-wrap: wrap;
}
.info-item {
flex: 0 0 50%;
margin-bottom: 12px;
display: flex;
align-items: flex-start;
}
.info-item.full-width {
flex: 0 0 100%;
}
.info-label {
font-weight: 500;
color: #86909c;
margin-right: 8px;
min-width: 80px;
flex-shrink: 0;
}
.info-value {
color: #4e5969;
flex: 1;
word-break: break-all;
font-size: 14px;
}
/* 骨架屏样式 */
.skeleton-loading {
display: flex;
flex-direction: column;
gap: 16px;
}
.skeleton-card {
background-color: #f5f5f5;
border-radius: 8px;
padding: 16px;
}
.skeleton-header {
height: 20px;
width: 30%;
background-color: #e0e0e0;
border-radius: 4px;
margin-bottom: 12px;
}
.skeleton-content {
display: flex;
flex-direction: column;
gap: 8px;
}
.skeleton-row {
height: 16px;
width: 100%;
background-color: #e0e0e0;
border-radius: 4px;
}
/* 优先级标签样式 */
.task-status {
padding: 4px 10px;
border-radius: 6px;
font-size: 12px;
font-weight: 500;
border: 1px solid transparent;
}
.description-content {
padding: 12px;
background-color: #f9f9f9;
border-radius: 4px;
line-height: 1.6;
color: #4e5969;
font-size: 13px;
}
</style>