From 80cca114a9211b1510c9777f5a597d429efe90da Mon Sep 17 00:00:00 2001 From: dhr <2216804034@qq.com> Date: Tue, 23 Sep 2025 20:36:47 +0800 Subject: [PATCH 1/5] 0922 --- .env.development | 2 +- src/api/zhinengxunjian/baoxiou/index.ts | 8 + src/api/zhinengxunjian/gongdan/index.ts | 57 + src/api/zhinengxunjian/jiedian/index.ts | 57 + src/api/zhinengxunjian/qiangxiu/index.ts | 57 + src/utils/request.ts | 8 +- .../zhinengxunjian/InspectionManagement.vue | 4 +- src/views/zhinengxunjian/baoxiuguanli.vue | 1034 ++++++++--- src/views/zhinengxunjian/baoxiujilu.vue | 1172 ++++++++++-- src/views/zhinengxunjian/gongdanliebiao.vue | 1131 +++++++++--- src/views/zhinengxunjian/index.vue | 2 +- src/views/zhinengxunjian/paidanjilu.vue | 74 +- src/views/zhinengxunjian/qiangxiuguanli.vue | 1629 ++++++++++++++--- src/views/zhinengxunjian/qiangxiujilu.vue | 1049 +++++++++-- src/views/zhinengxunjian/shiyanguanli.vue | 4 +- src/views/zhinengxunjian/shiyanjilu.vue | 2 +- src/views/zhinengxunjian/shiyanrenwu.vue | 4 +- src/views/zhinengxunjian/xunjianjihua.vue | 4 +- src/views/zhinengxunjian/xunjianrenwu.vue | 4 +- 19 files changed, 5107 insertions(+), 1195 deletions(-) create mode 100644 src/api/zhinengxunjian/gongdan/index.ts create mode 100644 src/api/zhinengxunjian/jiedian/index.ts create mode 100644 src/api/zhinengxunjian/qiangxiu/index.ts diff --git a/.env.development b/.env.development index f7ff9da..f9304df 100644 --- a/.env.development +++ b/.env.development @@ -5,7 +5,7 @@ VITE_APP_TITLE = 新能源场站智慧运维平台 VITE_APP_ENV = 'development' # 开发环境 -VITE_APP_BASE_API = 'http://192.168.110.149:18899' +VITE_APP_BASE_API = 'http://192.168.110.210:18899' # 应用访问路径 例如使用前缀 /admin/ VITE_APP_CONTEXT_PATH = '/' diff --git a/src/api/zhinengxunjian/baoxiou/index.ts b/src/api/zhinengxunjian/baoxiou/index.ts index 4c0c107..0a680e3 100644 --- a/src/api/zhinengxunjian/baoxiou/index.ts +++ b/src/api/zhinengxunjian/baoxiou/index.ts @@ -47,3 +47,11 @@ export const uploadbaoxiu = (data) => { data: data }); }; + +export const baoxiuRecord = (data) => { + return request({ + url: '/ops/report/record', + method: 'get', + params: data + }); +}; diff --git a/src/api/zhinengxunjian/gongdan/index.ts b/src/api/zhinengxunjian/gongdan/index.ts new file mode 100644 index 0000000..781d8d2 --- /dev/null +++ b/src/api/zhinengxunjian/gongdan/index.ts @@ -0,0 +1,57 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +//查询列表 +export const gongdanlist = (query) => { + return request({ + url: '/ops/order/list', + method: 'get', + params: query + }); +}; +//新增待办事项 +export const addgongdan = (data) => { + return request({ + url: '/ops/order', + method: 'post', + data: data + }); +}; +//修改待办事项 +export const updategongdan = (data) => { + return request({ + url: '/ops/order', + method: 'put', + data: data + }); +}; +//删除待办事项 + +export function delgongdan(ids) { + return request({ + url: `/ops/order/${ids}`, // 拼接ids作为路径参数 + method: 'delete' + }); +} + +export const gongdanDetail = (id) => { + return request({ + url: `/ops/order/${id}`, + method: 'get' + }); +}; + +export const uploadgongdan = (data) => { + return request({ + url: '/resource/oss/upload', + method: 'post', + data: data + }); +}; + +export const gongdanRecord = (data) => { + return request({ + url: '/ops/order/record', + method: 'get', + params: data + }); +}; diff --git a/src/api/zhinengxunjian/jiedian/index.ts b/src/api/zhinengxunjian/jiedian/index.ts new file mode 100644 index 0000000..c38b3ec --- /dev/null +++ b/src/api/zhinengxunjian/jiedian/index.ts @@ -0,0 +1,57 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +//查询列表 +export const jiedianlist = (query) => { + return request({ + url: '/ops/node/list', + method: 'get', + params: query + }); +}; +//新增待办事项 +export const addjiedian = (data) => { + return request({ + url: '/ops/node', + method: 'post', + data: data + }); +}; +//修改待办事项 +export const updatejiedian = (data) => { + return request({ + url: '/ops/node', + method: 'put', + data: data + }); +}; +//删除待办事项 + +export function deljiedian(ids) { + return request({ + url: `/ops/node/${ids}`, // 拼接ids作为路径参数 + method: 'delete' + }); +} + +export const jiedianDetail = (id) => { + return request({ + url: `/ops/node/${id}`, + method: 'get' + }); +}; + +export const uploadjiedian = (data) => { + return request({ + url: '/resource/oss/upload', + method: 'post', + data: data + }); +}; + +export const jiedianRecord = (data) => { + return request({ + url: '/ops/node/record', + method: 'get', + params: data + }); +}; diff --git a/src/api/zhinengxunjian/qiangxiu/index.ts b/src/api/zhinengxunjian/qiangxiu/index.ts new file mode 100644 index 0000000..bb717c0 --- /dev/null +++ b/src/api/zhinengxunjian/qiangxiu/index.ts @@ -0,0 +1,57 @@ +import request from '@/utils/request'; +import { AxiosPromise } from 'axios'; +//查询列表 +export const qiangxiulist = (query) => { + return request({ + url: '/ops/repair/list', + method: 'get', + params: query + }); +}; +//新增待办事项 +export const addqiangxiu = (data) => { + return request({ + url: '/ops/repair', + method: 'post', + data: data + }); +}; +//修改待办事项 +export const updateqiangxiu = (data) => { + return request({ + url: '/ops/repair', + method: 'put', + data: data + }); +}; +//删除待办事项 + +export function delqiangxiu(ids) { + return request({ + url: `/ops/repair/${ids}`, // 拼接ids作为路径参数 + method: 'delete' + }); +} + +export const qiangxiuDetail = (id) => { + return request({ + url: `/ops/repair/${id}`, + method: 'get' + }); +}; + +export const uploadqiangxiu = (data) => { + return request({ + url: '/resource/oss/upload', + method: 'post', + data: data + }); +}; + +export const qiangxiuRecord = (data) => { + return request({ + url: '/ops/repair/record', + method: 'get', + params: data + }); +}; diff --git a/src/utils/request.ts b/src/utils/request.ts index f3b06ad..ca0e341 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -23,12 +23,18 @@ export const globalHeaders = () => { }; }; +// 设置默认请求头 axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'; +axios.defaults.headers['Accept'] = 'application/json, text/plain, */*'; axios.defaults.headers['clientid'] = import.meta.env.VITE_APP_CLIENT_ID; // 创建 axios 实例 const service = axios.create({ baseURL: import.meta.env.VITE_APP_BASE_API, - timeout: 50000 + timeout: 50000, + headers: { + 'Content-Type': 'application/json;charset=utf-8', + 'Accept': 'application/json, text/plain, */*' + } }); // 请求拦截器 diff --git a/src/views/zhinengxunjian/InspectionManagement.vue b/src/views/zhinengxunjian/InspectionManagement.vue index 8ab639d..aaab44b 100644 --- a/src/views/zhinengxunjian/InspectionManagement.vue +++ b/src/views/zhinengxunjian/InspectionManagement.vue @@ -48,8 +48,8 @@
- 搜索 - 手动创建计划 + 搜索 + 手动创建计划
diff --git a/src/views/zhinengxunjian/baoxiuguanli.vue b/src/views/zhinengxunjian/baoxiuguanli.vue index 58c6ad7..08cb703 100644 --- a/src/views/zhinengxunjian/baoxiuguanli.vue +++ b/src/views/zhinengxunjian/baoxiuguanli.vue @@ -34,24 +34,21 @@
- -
-
- 搜索 - 手动创建任务 + 搜索 + 手动创建任务
@@ -72,7 +69,7 @@
报修时间 - {{ task.reportTime }} + {{ task.createTime }}
报修人 @@ -82,11 +79,7 @@ 维修人 {{ task.maintainer }}
- -
- 预计完成 - {{ task.expectedCompleteTime }} -
+
状态 {{ task.statusText }} @@ -159,12 +152,6 @@ - - - - - - @@ -173,29 +160,9 @@ - - -
-
- -
点击或拖拽图片至此处上传
-
支持JPG、PNG格式,最多3张
-
-
-
-
已选择{{ createTaskForm.fileList.length }}张图片,将在提交时上传
@@ -222,7 +189,196 @@ + + +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+

任务基本信息

+
+
+
+ 任务ID: + {{ detailData.id || '-' }} +
+
+ 任务名称: + {{ detailData.name || '未命名' }} +
+
+
+
+ 任务状态: + {{ getStatusText(detailData.status) }} +
+
+ 任务等级: + {{ getPriorityText(detailData.level) }} +
+
+
+
+ 任务类型: + {{ detailData.type === '1' ? '硬件故障' : detailData.type === '2' ? '软件故障' : '-' }} +
+
+ 报修时间: + {{ formatDate(detailData.createTime) }} +
+
+
+
+ + +
+

报修人信息

+
+
+
+ 报修人: + {{ detailData.reportName || '-' }} +
+
+ 联系人: + {{ detailData.reportName || '-' }} +
+
+
+
+ 联系电话: + {{ detailData.reportPhone || '-' }} +
+
+ 维修人: + {{ detailData.sendPersonVo?.userName || '-' }} +
+
+
+
+ + +
+

报修详情

+
+
+
+ 故障位置: + {{ detailData.position || '-' }} +
+
+
+
+ 详细描述: + {{ detailData.reportInfo || '-' }} +
+
+ +
+
+ 完成时间: + {{ formatDate(detailData.completeTime) }} +
+
+
+
+ + +
+

故障图片

+
+
+ +
+ +
+
+
+
+
+
+ + 加载中... +
+ + +
+ + + +
+ + + + + +
+ +
+ + + + + + + + + + + +
@@ -230,9 +386,9 @@ import { ref, computed, onMounted } from 'vue'; import router from '@/router'; import TitleComponent from './TitleComponent.vue'; -import { baoxiulist, baoxiuDetail, delbaoxiu, updatebaoxiu, addbaoxiu, uploadbaoxiu } from '@/api/zhinengxunjian/baoxiou/index'; +import { baoxiulist, baoxiuDetail, updatebaoxiu, addbaoxiu } from '@/api/zhinengxunjian/baoxiou/index'; import { xunjianUserlist } from '@/api/zhinengxunjian/xunjian'; -import { ElMessage } from 'element-plus'; +import { ElMessage, ElLoading } from 'element-plus'; // 激活的选项卡 const activeTab = ref('task'); @@ -241,6 +397,17 @@ const taskStatus = ref(''); const planType = ref(''); const executor = ref(''); +// 详情弹窗相关 +const detailDialogVisible = ref(false); +const detailData = ref(null); +const isDetailLoading = ref(false); + +// 关闭详情弹窗 +const handleCloseDetailDialog = () => { + detailDialogVisible.value = false; + detailData.value = null; +}; + // 任务数据 - 初始为空数组 const tasks = ref([]); @@ -250,6 +417,14 @@ const pageSize = ref(8); const total = ref(0); const loading = ref(false); +// 分配任务弹窗相关 +const assignDialogVisible = ref(false); +const assignTaskForm = ref({ taskId: '', sendPerson: '' }); +const assignTaskRules = { + sendPerson: [{ required: true, message: '请选择维修人', trigger: 'change' }] +}; +const assignTaskFormRef = ref(null); + // 获取报修任务列表 defineExpose({ getTaskList }); async function getTaskList() { @@ -274,16 +449,24 @@ async function getTaskList() { leftLineClass: getLeftLineClass(item.status, item.level), priorityClass: getPriorityClass(item.level), priority: getPriorityText(item.level), - reportTime: formatDate(item.reportTime), + // 修复报修时间字段名,使用与模板一致的createTime + createTime: formatDate(item.createTime || item.reportTime), + // 修复报修人字段,使用reportName reporter: item.reportName || '未知报修人', - maintainer: item.sendPerson ? `维修人ID: ${item.sendPerson}` : '未分配', - expectedCompleteTime: getExpectedCompleteTime(item.status), + // 修复维修人字段,从sendPersonVo对象中获取用户名 + maintainer: item.sendPersonVo?.userName || '未分配', + completeTime: item.completeTime ? formatDate(item.completeTime) : '', actionText: getActionText(item.status), actionClass: getActionClass(item.status), reportInfo: item.reportInfo, position: item.position, - fileUrl: item.fileUrl + fileUrl: item.fileUrl, + + reportPhone: item.reportPhone || '', + type: item.type || '', + // 保留原始数据用于详情查看 + sendPersonVo: item.sendPersonVo })); } else { tasks.value = []; @@ -371,20 +554,6 @@ function getActionClass(status) { return actionMap[status] || 'view-btn'; } -// 获取预计完成时间(根据状态和优先级估算) -function getExpectedCompleteTime(status) { - if (status === '3') return ''; // 已完成任务不需要显示预计时间 - - const now = new Date(); - // 简单估算:待处理任务默认当天18:00前完成,处理中任务默认2小时内完成 - if (status === '1') { - now.setHours(18, 0, 0, 0); - } else { - now.setHours(now.getHours() + 2); - } - return formatDate(now); -} - // 格式化日期时间 function formatDate(date) { if (!date) return ''; @@ -438,7 +607,7 @@ const createTaskForm = ref({ faultLocation: '', contactPerson: '', contactPhone: '', - sendPerson: '', // 指派维修人 + sendPerson: '未分配维修人', // 指派维修人 file: '' // 上传的文件列表 }); @@ -449,8 +618,7 @@ const createTaskRules = { detailedDescription: [{ required: true, message: '请输入详细描述', trigger: 'blur' }], faultLocation: [{ required: true, message: '请输入故障位置', trigger: 'blur' }], contactPerson: [{ required: true, message: '请输入联系人', trigger: 'blur' }], - contactPhone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }], - sendPerson: [{ required: true, message: '请选择维修人', trigger: 'change' }] + contactPhone: [{ required: true, message: '请输入联系电话', trigger: 'blur' }] }; // 用户列表(维修人) @@ -464,9 +632,9 @@ const getUsersList = async () => { const res = await xunjianUserlist(); // 根据接口返回格式,成功码是200,用户数据在rows数组中 if (res.code === 200 && res.rows && Array.isArray(res.rows)) { - // 映射用户数据,使用id作为value,userName作为显示名称 + // 映射用户数据,将id转换为字符串以避免大整数精度问题 usersList.value = res.rows.map((user) => ({ - id: user.id, + id: String(user.id), name: user.userName })); } else { @@ -481,86 +649,7 @@ const getUsersList = async () => { } }; const isSubmitting = ref(false); // 防止重复提交的状态标记 -const isUploading = ref(false); // 防止重复上传的状态标记 -// 上传文件方法 -const uploadFiles = async (files) => { - // 防止重复上传 - if (isUploading.value) { - ElMessage.warning('数据正在处理中,请稍候...'); - return []; - } - try { - isUploading.value = true; - const formData = new FormData(); - - // 关键修复:将字段名从'files'改为'file',匹配后端要求的'file'字段 - files.forEach((file) => { - formData.append('file', file.raw); // 这里将'files'改为'file' - }); - - const res = await uploadbaoxiu(formData); - - if (res.code === 200 && res.data && Array.isArray(res.data)) { - return res.data.map((item) => ({ - fileId: item.ossId, - fileUrl: item.url - })); - } else { - console.error('文件上传失败:', res.msg); - throw new Error(res.msg || '文件上传失败'); - } - } catch (error) { - console.error('文件上传异常:', error); - if (error.message.includes('重复提交')) { - throw new Error('操作过于频繁,请稍后再试'); - } - throw error; - } finally { - isUploading.value = false; - } -}; - -const handleFileChange = (file, fileList) => { - // 只处理刚添加的文件 - if (file.status === 'ready') { - // 验证文件格式和大小 - const isJPG = file.raw.type === 'image/jpeg' || file.raw.type === 'image/jpg'; - const isPNG = file.raw.type === 'image/png'; - const isLt2M = file.size / 1024 / 1024 < 2; - - if (!isJPG && !isPNG) { - ElMessage.error('上传图片只能是 JPG/JPEG 或 PNG 格式!'); - // 从文件列表中移除不符合要求的文件 - createTaskForm.value.fileList = fileList.filter((f) => f.uid !== file.uid); - return; - } - - if (!isLt2M) { - ElMessage.error('上传图片大小不能超过 2MB!'); - // 从文件列表中移除不符合要求的文件 - createTaskForm.value.fileList = fileList.filter((f) => f.uid !== file.uid); - return; - } - - // 保存验证通过的文件到表单 - createTaskForm.value.fileList = fileList; - } -}; -const handleExceed = (files, fileList) => { - ElMessage.warning(`当前限制选择 3 张图片,本次选择了 ${files.length} 张,共选择了 ${files.length + fileList.length} 张`); -}; - -// 文件移除前的钩子 -const beforeRemove = (file, fileList) => { - return window.confirm(`确定移除 ${file.name}?`); -}; - -// 文件移除时的钩子 -const handleRemove = (file, fileList) => { - createTaskForm.value.fileList = fileList; - ElMessage.info(`已移除 ${file.name}`); -}; // 创建任务 const handleCreateTask = async () => { createTaskDialogVisible.value = true; @@ -580,21 +669,6 @@ const handleSaveTask = async () => { // 先验证表单 await createTaskFormRef.value.validate(); - // 上传选中的文件 - let fileIds = []; - let fileUrls = []; - - if (createTaskForm.value.fileList && createTaskForm.value.fileList.length > 0) { - ElMessage.info(`开始上传 ${createTaskForm.value.fileList.length} 张图片...`); - const uploadResults = await uploadFiles(createTaskForm.value.fileList); - - // 提取上传结果 - fileIds = uploadResults.map((result) => result.fileId); - fileUrls = uploadResults.map((result) => result.fileUrl); - - ElMessage.success(`图片上传成功,共 ${uploadResults.length} 张`); - } - // 准备请求数据 const requestData = { projectId: 1, @@ -602,11 +676,10 @@ const handleSaveTask = async () => { type: mapRepairType(createTaskForm.value.repairType), status: '1', // 默认为待处理状态 level: mapPriorityLevel(createTaskForm.value.priority), - sendPerson: parseInt(createTaskForm.value.sendPerson), + reportInfo: createTaskForm.value.detailedDescription, position: createTaskForm.value.faultLocation, - fileId: fileIds.join(','), // 用逗号分隔多个文件ID - fileUrl: fileUrls.join(','), // 用逗号分隔多个文件URL + fileId: createTaskForm.value.file, reportName: createTaskForm.value.contactPerson, reportPhone: createTaskForm.value.contactPhone }; @@ -683,52 +756,6 @@ function mapPriorityLevel(priority) { return levelMap[priority] || '2'; } -// 文件上传前的校验 -async function beforeUpload(file) { - const isJPG = file.type === 'image/jpeg' || file.type === 'image/jpg'; - const isPNG = file.type === 'image/png'; - const isLt2M = file.size / 1024 / 1024 < 2; - - if (!isJPG && !isPNG) { - ElMessage.error('上传图片只能是 JPG/JPEG 或 PNG 格式!'); - return false; - } - if (!isLt2M) { - ElMessage.error('上传图片大小不能超过 2MB!'); - return false; - } - - try { - // 直接上传文件 - const uploadResult = await uploadFile(file); - // 存储文件信息到表单 - createTaskForm.value.fileList = [ - { - ...file, - fileId: uploadResult.fileId, - fileUrl: uploadResult.fileUrl - } - ]; - ElMessage.success('图片上传成功'); - } catch (error) { - ElMessage.error('图片上传失败,请重试'); - } - - // 阻止自动上传 - return false; -} - -// 文件上传成功处理 -function handleUploadSuccess(response, file, fileList) { - // 这个函数实际上不会被调用,因为auto-upload=false - // 真正的上传逻辑在beforeUpload函数中处理 -} - -// 文件上传失败处理 -function handleUploadError(err, file, fileList) { - ElMessage.error('上传失败,请稍后重试'); -} - // 取消创建报修任务 const handleCancelCreateTask = () => { createTaskDialogVisible.value = false; @@ -749,15 +776,275 @@ const handleCurrentChange = (val) => { }; // 查看任务详情 -const handleView = (task) => { - console.log('查看任务详情:', task); +const handleView = async (task) => { + try { + if (!task || !task.id) { + ElMessage.warning('任务ID不存在,请刷新页面重试'); + console.error('任务缺少有效id,任务数据:', task); + return; + } + + isDetailLoading.value = true; + detailData.value = null; + + const loading = ElLoading.service({ + lock: true, + text: '加载详情中...', + background: 'rgba(255, 255, 255, 0.7)' + }); + + try { + const response = await baoxiuDetail(task.id); + + // 注意:这里使用code === 200判断成功,与项目中其他详情接口保持一致 + if (response.code === 200 && response.data) { + detailData.value = response.data; + detailDialogVisible.value = true; + } else { + const errorMsg = response.msg || '未知错误'; + console.error(`获取任务详情失败: ${errorMsg}`); + ElMessage.error(`获取任务详情失败:${errorMsg}`); + } + } finally { + loading.close(); + isDetailLoading.value = false; + } + } catch (error) { + console.error('获取任务详情出错:', error); + ElMessage.error('获取任务详情出错,请重试'); + } +}; +// 拆分逗号分隔的图片URL为数组 +const splitImageUrls = (urlString) => { + if (!urlString) return []; + // 拆分并过滤空字符串(处理可能的首尾逗号或连续逗号) + return urlString.split(',').filter((url) => url.trim().length > 0); }; -// 处理选项卡点击 -const handleTabClick = () => { - currentPage.value = 1; +// 处理图片加载失败 +const handleImageError = (event, index) => { + // 可以设置一个默认图片占位符 + event.target.src = ''; + console.error(`图片加载失败: 索引 ${index}`); }; +// 处理任务操作按钮点击事件 +const handleAction = async (task) => { + try { + if (!task || !task.id) { + ElMessage.warning('任务ID不存在,请刷新页面重试'); + console.error('任务缺少有效id,任务数据:', task); + return; + } + + // 点击分配按钮:打开分配弹窗 + if (task.actionText === '分配') { + // 重置表单并设置任务ID + assignTaskForm.value = { taskId: task.id, sendPerson: '' }; + assignDialogVisible.value = true; + + // 打开弹窗时获取用户列表 + if (usersList.value.length === 0) { + await getUsersList(); + } + if (usersList.value.length === 0) { + ElMessage.error('无可用维修人员,请先配置维修人员信息'); + assignDialogVisible.value = false; + } + } + + // 点击跟进按钮:原有逻辑 + else if (task.actionText === '跟进') { + resultDialogVisible.value = true; + currentTaskId.value = task.id; + } + + // 其他操作(如评价) + else { + console.log('其他操作:', task.actionText); + } + } catch (error) { + console.error('任务操作失败:', error, '当前任务数据:', task); + ElMessage.error('操作失败,请联系技术支持'); + } +}; + +// 提交分配任务 +const handleSubmitAssign = async () => { + if (!assignTaskFormRef.value) return; + + try { + await assignTaskFormRef.value.validate(); + + // 找到原始任务数据 + const originalTask = tasks.value.find((t) => t.id === assignTaskForm.value.taskId); + if (!originalTask) { + ElMessage.warning('未找到任务完整数据,请刷新重试'); + return; + } + + // 找到选中的维修人员 + const selectedUser = usersList.value.find((u) => u.id === assignTaskForm.value.sendPerson); + if (!selectedUser) { + ElMessage.error('未找到选中的维修人员'); + return; + } + + // 构造更新参数 + const updateData = { + id: assignTaskForm.value.taskId, + status: '2', // 处理中状态 + sendPerson: selectedUser.id, + sendPersonName: selectedUser.name, + sendPersonVo: { + id: selectedUser.id, + userName: selectedUser.name + }, + // 包含后端所需的其他必要字段 + name: originalTask.title, + type: mapRepairType(originalTask.type || 'all'), + level: mapPriorityLevel(originalTask.priority), + reportName: originalTask.reporter, + reportPhone: originalTask.reportPhone || '', + position: originalTask.position || '', + reportInfo: originalTask.reportInfo || '', + projectId: 1 + }; + + // 调用接口更新 + const response = await updatebaoxiu(updateData); + if (response.code === 200) { + ElMessage.success(`任务已分配给【${selectedUser.name}】,状态更新为处理中`); + assignDialogVisible.value = false; + getTaskList(); // 刷新列表 + } else { + const errorMsg = response.msg || '未知错误'; + console.error(`任务分配失败:${errorMsg},请求参数:`, updateData); + ElMessage.error(`分配失败:${errorMsg}`); + } + } catch (error) { + if (error instanceof Error) { + ElMessage.error(`操作失败:${error.message}`); + } else if (error !== false) { + ElMessage.error('操作失败:未知错误'); + } + } +}; + +// 关闭分配弹窗 +const handleCloseAssign = () => { + assignDialogVisible.value = false; + if (assignTaskFormRef.value) { + assignTaskFormRef.value.clearValidate(); + } +}; + +// 结果输入弹窗相关状态 +const resultDialogVisible = ref(false); +const currentTaskId = ref(''); +const reportFinal = ref(''); + +// 处理结果表单验证规则 +const resultFormRules = { + reportFinal: [ + { + required: true, + message: '请输入处理结果', + trigger: 'blur' + }, + { + min: 5, + max: 500, + message: '处理结果长度在 5 到 500 个字符之间', + trigger: 'blur' + } + ] +}; + +// 表单引用 +const resultFormRef = ref(null); + +// 保存结果 +const handleSaveResult = async () => { + try { + if (!currentTaskId.value) { + ElMessage.warning('任务ID不存在'); + return; + } + + if (!reportFinal.value.trim()) { + ElMessage.warning('请输入处理结果'); + return; + } + + // 1. 查找当前任务的完整数据 + const originalTask = tasks.value.find((t) => t.id === currentTaskId.value); + if (!originalTask) { + ElMessage.warning('未找到任务完整数据,请刷新重试'); + console.error('未找到任务完整数据,任务ID:', currentTaskId.value); + return; + } + + // 2. 生成当前时间作为完成时间 + 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 reportFinishTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + + // 3. 构造完整的更新参数(与任务分配时使用相同的参数集) + const updateData = { + // ① 任务基础标识(必传) + id: currentTaskId.value, // 任务ID(核心主键) + + // ② 状态流转参数(必传) + status: '3', // 状态:3=已完成 + statusText: '已完成', // 状态文本 + reportFinal: reportFinal.value, // 处理结果 + reportFinishTime: reportFinishTime, // 完成时间 + completeTime: reportFinishTime, // 完成时间(向后兼容字段) + + // ③ 复用原始任务数据中的所有必要字段 + name: originalTask.title, // 任务名称(原始任务标题) + type: mapRepairType(originalTask.type || 'all'), // 任务类型(映射为后端需要的格式) + level: mapPriorityLevel(originalTask.priority), // 优先级(映射为后端需要的格式) + reportName: originalTask.reporter, // 报修人姓名 + reportPhone: originalTask.reportPhone || '', // 报修人电话 + position: originalTask.position || '', // 故障位置 + reportInfo: originalTask.reportInfo || '', // 报修详情 + + // ④ 维修人员信息(保留原有分配的维修人员) + sendPerson: originalTask.sendPersonVo?.id || '', // 维修人员ID + sendPersonName: originalTask.sendPersonVo?.userName || '', // 维修人员姓名 + sendPersonVo: originalTask.sendPersonVo || {}, // 维修人员完整信息 + + // ⑤ 其他必要参数 + pageNum: currentPage.value, // 分页参数 + pageSize: pageSize.value, + projectId: 1 // 项目ID + }; + + const response = await updatebaoxiu(updateData); + + if (response.code === 200) { + ElMessage.success('处理结果已保存'); + resultDialogVisible.value = false; + reportFinal.value = ''; + currentTaskId.value = ''; + getTaskList(); + } else { + const errorMsg = response.msg || '未知错误'; + console.error(`保存处理结果失败: ${errorMsg}`); + ElMessage.error(`保存处理结果失败:${errorMsg}`); + } + } catch (error) { + console.error('保存处理结果失败:', error); + ElMessage.error('保存失败,请重试'); + } +}; const handleInspectionManagement1 = () => { router.push('/rili/baoxiuguanli'); }; @@ -801,73 +1088,6 @@ onMounted(async () => { min-height: 100vh; } -/* 上传图片区域样式 */ -.upload-container { - width: 100%; -} - -.upload-box { - border: 2px dashed #dcdfe6; - border-radius: 8px; - padding: 40px 20px; - text-align: center; - cursor: pointer; - background-color: #f8f9fa; - transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - position: relative; - overflow: hidden; -} - -.upload-box::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(64, 158, 255, 0.1), transparent); - transition: all 0.6s ease; -} - -.upload-box:hover { - border-color: #409eff; - background-color: #ecf5ff; - transform: translateY(-2px); - box-shadow: 0 4px 12px rgba(64, 158, 255, 0.15); -} - -.upload-box:hover::before { - left: 100%; -} - -.avatar-uploader-icon { - font-size: 40px; - color: #c0c4cc; - margin-bottom: 12px; - transition: color 0.3s; -} - -.upload-box:hover .avatar-uploader-icon { - color: #409eff; -} - -.upload-text { - font-size: 14px; - color: #606266; - font-weight: 500; - margin-bottom: 6px; - transition: color 0.3s; -} - -.upload-box:hover .upload-text { - color: #409eff; -} - -.upload-hint { - font-size: 12px; - color: #909399; -} - /* 美化弹窗样式 */ .beautiful-dialog { border-radius: 12px; @@ -917,6 +1137,124 @@ onMounted(async () => { border-top: 1px solid #f0f2f5; text-align: center; } +.custom-experiment-dialog .el-dialog__body { + max-height: 60vh; + overflow-y: auto; + padding: 24px; +} + +/* 详情卡片样式 */ +.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; +} + +/* 故障原因样式 */ +.fail-reason { + color: #ff4d4f; + font-weight: 500; +} + +/* 加载状态 */ +.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; + background-color: #e0e0e0; + border-radius: 4px; +} + +/* 无数据提示 */ +.no-info { + text-align: center; + color: #909399; + padding: 60px 20px; +} + +/* 响应式设计 */ +@media (max-width: 768px) { + .custom-experiment-dialog { + width: 95% !important; + } + + .info-item { + flex: 0 0 100%; + } +} /* 美化表单样式 */ .elegant-form .el-form-item { @@ -1048,10 +1386,6 @@ onMounted(async () => { .elegant-form .el-form-item { margin-bottom: 20px; } - - .upload-box { - padding: 30px 15px; - } } /* 选项卡样式 */ @@ -1369,24 +1703,6 @@ onMounted(async () => { color: #fff; } -.avatar-uploader .el-upload-list { - margin-top: 12px; -} - -.avatar-uploader .el-upload-list__item { - border-radius: 6px; - margin-bottom: 8px; - transition: all 0.2s; -} - -.avatar-uploader .el-upload-list__item:hover { - transform: translateX(4px); -} - -.upload-tip { - margin-top: 10px; - font-size: 12px; -} /* 响应式设计 */ @media (max-width: 1200px) { .task-cards { @@ -1458,4 +1774,120 @@ onMounted(async () => { cursor: pointer; user-select: none; } + +/* 详情弹窗样式 */ +.custom-experiment-dialog { + .detail-content { + max-height: 60vh; + overflow-y: auto; + padding-right: 10px; + } + + .detail-section { + margin-bottom: 24px; + } + + .section-title { + font-size: 16px; + font-weight: 500; + color: #303133; + margin-bottom: 16px; + padding-bottom: 8px; + border-bottom: 1px solid #f0f2f5; + } + + .detail-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 16px; + } + + .detail-item { + display: flex; + flex-direction: column; + } + + .detail-label { + font-size: 14px; + color: #909399; + margin-bottom: 4px; + } + + .detail-value { + font-size: 14px; + color: #303133; + } + + .detail-textarea { + margin-bottom: 16px; + } + + .detail-text { + font-size: 14px; + color: #303133; + line-height: 1.6; + word-wrap: break-word; + white-space: pre-wrap; + } + + /* 多图片展示容器样式 */ + .images-container { + display: flex; + flex-wrap: wrap; + gap: 16px; + margin-top: 12px; + padding: 10px; + background-color: #f9f9f9; + border-radius: 8px; + } + + /* 单个图片项样式 */ + .image-item { + flex: 0 0 auto; + width: 200px; /* 固定宽度 */ + height: 160px; /* 固定高度 */ + border-radius: 6px; + overflow: hidden; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08); + transition: transform 0.3s ease; + } + + .image-item:hover { + transform: scale(1.03); + } + + /* 图片样式 */ + .detail-image { + width: 100%; + height: 100%; + object-fit: cover; /* 保持比例填充容器 */ + display: block; + } + + /* 图片加载失败样式 */ + .detail-image[src=''] { + background-color: #f0f0f0; + display: flex; + align-items: center; + justify-content: center; + color: #999; + font-size: 12px; + } + + .no-info { + text-align: center; + color: #909399; + padding: 40px 0; + } +} + +@media (max-width: 768px) { + .custom-experiment-dialog { + width: 90% !important; + + .detail-grid { + grid-template-columns: 1fr; + } + } +} diff --git a/src/views/zhinengxunjian/baoxiujilu.vue b/src/views/zhinengxunjian/baoxiujilu.vue index bd22772..8ff61e1 100644 --- a/src/views/zhinengxunjian/baoxiujilu.vue +++ b/src/views/zhinengxunjian/baoxiujilu.vue @@ -28,16 +28,16 @@
- - - + + +
- - - + + +
@@ -58,7 +58,7 @@ />
- 搜索 + 搜索
@@ -68,8 +68,8 @@

本月报修数

-

24

-

较上月 +4.2%

+

{{ statsLoading ? '加载中...' : statsData.byzbxs }}

+

较上月:{{ statsData.bxsjszzzl }}

本月报修数 @@ -79,8 +79,8 @@

平均处理时长

-

3.5小时

-

较上月 -0.6小时

+

{{ statsLoading ? '加载中...' : statsData.pjclsc }}

+

较上月:{{ statsData.clscjszzzl }}

平均处理时长 @@ -90,7 +90,7 @@

待处理报修

-

15

+

{{ statsLoading ? '加载中...' : statsData.dclbx }}

需及时处理

@@ -101,8 +101,8 @@

完成率

-

92%

-

较上月 +2.1%

+

{{ statsLoading ? '加载中...' : statsData.wcl }}%

+

{{ statsData.wcljszzzl }}%

完成率 @@ -112,24 +112,44 @@
- - - - - - - + + + + + + + + + + + + + + + + + + + + + + + - - @@ -153,13 +173,210 @@
+ + + +
+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + +
+

任务基本信息

+
+
+
+ 任务ID: + {{ detailData.id || '-' }} +
+
+ 任务名称: + {{ detailData.name || '未命名' }} +
+
+
+
+ 任务状态: + {{ getStatusText(detailData.status) }} +
+
+ 任务等级: + {{ getPriorityText(detailData.level) }} +
+
+
+
+ 任务类型: + {{ detailData.type === '1' ? '硬件故障' : detailData.type === '2' ? '软件故障' : '-' }} +
+
+ 报修时间: + {{ formatDate(detailData.createTime) }} +
+
+
+
+ + +
+

报修人信息

+
+
+
+ 报修人: + {{ detailData.reportName || '-' }} +
+
+ 联系人: + {{ detailData.reportName || '-' }} +
+
+
+
+ 联系电话: + {{ detailData.reportPhone || '-' }} +
+
+ 维修人: + {{ detailData.sendPersonVo?.userName || '-' }} +
+
+
+
+ + +
+

报修详情

+
+
+
+ 故障位置: + {{ detailData.position || '-' }} +
+
+
+
+ 详细描述: + {{ detailData.reportInfo || '-' }} +
+
+ +
+
+ 完成时间: + {{ formatDate(detailData.completeTime) }} +
+
+
+
+ + +
+

故障图片

+
+
+ +
+ +
+
+
+
+
+
+ + 加载中... +
+ + +
+ + + +
+
+ + +
+
+ +
+ + + +
+
+ + + + +
+
加载中...
+
+ +
- diff --git a/src/views/zhinengxunjian/shiyanguanli.vue b/src/views/zhinengxunjian/shiyanguanli.vue index c5d072b..10ca273 100644 --- a/src/views/zhinengxunjian/shiyanguanli.vue +++ b/src/views/zhinengxunjian/shiyanguanli.vue @@ -49,8 +49,8 @@ >
- 搜索 - 新增实验记录 + 搜索 + 新增实验记录
diff --git a/src/views/zhinengxunjian/shiyanjilu.vue b/src/views/zhinengxunjian/shiyanjilu.vue index 584bdfa..8903a9a 100644 --- a/src/views/zhinengxunjian/shiyanjilu.vue +++ b/src/views/zhinengxunjian/shiyanjilu.vue @@ -54,7 +54,7 @@ class="date-picker" > - 搜索 + 搜索
diff --git a/src/views/zhinengxunjian/shiyanrenwu.vue b/src/views/zhinengxunjian/shiyanrenwu.vue index b4fcced..684be72 100644 --- a/src/views/zhinengxunjian/shiyanrenwu.vue +++ b/src/views/zhinengxunjian/shiyanrenwu.vue @@ -49,8 +49,8 @@
- 搜索 - 手动创建任务 + 搜索 + 手动创建任务
diff --git a/src/views/zhinengxunjian/xunjianjihua.vue b/src/views/zhinengxunjian/xunjianjihua.vue index b7df85b..c011be8 100644 --- a/src/views/zhinengxunjian/xunjianjihua.vue +++ b/src/views/zhinengxunjian/xunjianjihua.vue @@ -12,7 +12,7 @@
筛选 - 导出数据 + 导出数据
@@ -54,7 +54,7 @@ >
- 搜索 + 搜索
diff --git a/src/views/zhinengxunjian/xunjianrenwu.vue b/src/views/zhinengxunjian/xunjianrenwu.vue index b982e51..3360946 100644 --- a/src/views/zhinengxunjian/xunjianrenwu.vue +++ b/src/views/zhinengxunjian/xunjianrenwu.vue @@ -42,8 +42,8 @@
- 搜索 - 手动创建任务 + 搜索 + 手动创建任务
From 9913a7854cb08d626600bf590ac5294351d217e6 Mon Sep 17 00:00:00 2001 From: dhr <2216804034@qq.com> Date: Wed, 24 Sep 2025 16:37:09 +0800 Subject: [PATCH 2/5] 0924 --- src/api/zhinengxunjian/xunjian/index.ts | 2 +- .../zhinengxunjian/InspectionManagement.vue | 9 +- src/views/zhinengxunjian/banzhuzhuangtai.vue | 4 - src/views/zhinengxunjian/baoxiuguanli.vue | 10 +- src/views/zhinengxunjian/baoxiujilu.vue | 10 +- .../zhinengxunjian/cheliangzhuangtai.vue | 4 +- src/views/zhinengxunjian/gongdanliebiao.vue | 384 +++- src/views/zhinengxunjian/paidanjilu.vue | 1753 ++++++++++++++--- src/views/zhinengxunjian/qiangxiuguanli.vue | 2 +- src/views/zhinengxunjian/qiangxiujilu.vue | 51 +- src/views/zhinengxunjian/renyuanzhuangtai.vue | 4 - src/views/zhinengxunjian/shiyanguanli.vue | 88 +- src/views/zhinengxunjian/shiyanrenwu.vue | 35 +- src/views/zhinengxunjian/xunjianrenwu.vue | 17 +- src/views/zhinengxunjian/zhixingjilu.vue | 5 +- 15 files changed, 1943 insertions(+), 435 deletions(-) diff --git a/src/api/zhinengxunjian/xunjian/index.ts b/src/api/zhinengxunjian/xunjian/index.ts index 353b0bb..e60b9c7 100644 --- a/src/api/zhinengxunjian/xunjian/index.ts +++ b/src/api/zhinengxunjian/xunjian/index.ts @@ -34,7 +34,7 @@ export const delxunjian = (ids) => { //查询人员 export const xunjianUserlist = (query) => { return request({ - url: '/ops/constructionUser/list', + url: '/system/user/list', method: 'get', params: query }); diff --git a/src/views/zhinengxunjian/InspectionManagement.vue b/src/views/zhinengxunjian/InspectionManagement.vue index aaab44b..adcb6d7 100644 --- a/src/views/zhinengxunjian/InspectionManagement.vue +++ b/src/views/zhinengxunjian/InspectionManagement.vue @@ -605,13 +605,14 @@ const formatDate = (dateString) => { const getUsersList = async () => { try { const response = await xunjianUserlist(); - const userRows = response?.data?.rows || response?.rows || []; + // 适配新接口格式:检查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, index) => ({ - label: item.userName || `用户${index + 1}`, - value: item.id || `id_${index}` + .map((item) => ({ + label: item.userName || '未知用户', + value: String(item.userId || '') // 使用userId作为唯一标识 })); if (userList.value.length === 0) { diff --git a/src/views/zhinengxunjian/banzhuzhuangtai.vue b/src/views/zhinengxunjian/banzhuzhuangtai.vue index c86cef4..84b7d10 100644 --- a/src/views/zhinengxunjian/banzhuzhuangtai.vue +++ b/src/views/zhinengxunjian/banzhuzhuangtai.vue @@ -12,9 +12,6 @@ - - -
@@ -182,7 +179,6 @@ diff --git a/src/views/zhinengxunjian/baoxiuguanli.vue b/src/views/zhinengxunjian/baoxiuguanli.vue index b103a91..cba778b 100644 --- a/src/views/zhinengxunjian/baoxiuguanli.vue +++ b/src/views/zhinengxunjian/baoxiuguanli.vue @@ -1080,6 +1080,8 @@ onMounted(async () => { diff --git a/src/views/zhinengxunjian/shiyanjilu.vue b/src/views/zhinengxunjian/shiyanjilu.vue index 8903a9a..370174d 100644 --- a/src/views/zhinengxunjian/shiyanjilu.vue +++ b/src/views/zhinengxunjian/shiyanjilu.vue @@ -1,6 +1,7 @@ @@ -385,7 +442,12 @@ import { shiyanlist } from '@/api/zhinengxunjian/shiyan'; import { xunjianUserlist } from '@/api/zhinengxunjian/xunjian/index'; import { addjiedian } from '@/api/zhinengxunjian/jiedian/index'; // 引入Element Plus组件(提示/空状态/骨架屏/弹窗) -import { ElMessage, ElEmpty, ElSkeleton, ElForm, ElMessageBox } from 'element-plus'; +import { ElMessage, ElEmpty, ElSkeleton, ElForm, ElMessageBox, ElDialog } from 'element-plus'; + +// 日志弹窗相关变量 +const logsDialogVisible = ref(false); +const logsData = ref([]); +const logsLoading = ref(false); /** * 根据任务ID获取完整的任务详情数据 @@ -696,9 +758,9 @@ const mapApiToView = (apiData) => { }, '3': { statusText: '失败', - cardClass: 'card-delayed', - tagClass: 'tag-delayed', - actionText: '重试', + cardClass: 'card-failed', + tagClass: 'tag-failed', + actionText: '重新执行', actionClass: 'reschedule-btn', result: '失败', resultClass: 'result-abnormal' @@ -755,6 +817,46 @@ const mapApiToView = (apiData) => { executorName = getUserById(apiData.person); } + // 格式化失败时间 + const formatFailTime = (timeStr) => { + if (timeStr) { + const date = new Date(timeStr); + return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')} ${String( + date.getHours() + ).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`; + } + return '未记录'; + }; + + // 生成试验阶段信息 + const getTestStage = () => { + try { + // 优先查找nodes数组中status为2的第一条数据 + if (apiData && apiData.nodes && Array.isArray(apiData.nodes)) { + const firstStatusTwoNode = apiData.nodes.find((node) => { + // 确保node存在且有status属性 + if (!node || node.status === undefined) return false; + // 处理status可能是字符串或数字的情况 + return node.status === '2' || node.status === 2; + }); + if (firstStatusTwoNode && firstStatusTwoNode.name) { + return firstStatusTwoNode.name; + } + } + // 如果没有找到符合条件的nodes数据,检查是否有明确的试验阶段信息 + if (apiData && apiData.testStage) { + return apiData.testStage; + } + // 如果没有明确的阶段信息,尝试从关联计划中获取 + if (apiData && apiData.testPlan && apiData.testPlan.stage) { + return apiData.testPlan.stage; + } + } catch (error) { + console.error('获取试验阶段信息失败:', error); + } + return '未记录'; + }; + return { id: apiData.id, // 任务ID(v-for的key,唯一标识) title: apiData.taskName || '未命名任务', // 任务名称 @@ -774,7 +876,10 @@ const mapApiToView = (apiData) => { actionText: statusConfig.actionText, actionClass: statusConfig.actionClass, testFinal: apiData.testFinal, // 结果(用于详情页) - originalData: apiData // 保存原始数据,用于后续操作 + originalData: apiData, // 保存原始数据,用于后续操作 + // 失败卡片特有字段 + failTime: formatFailTime(apiData.failTime), + testStage: getTestStage() }; }; @@ -880,11 +985,44 @@ const handleAction = async (task) => { resultType = 'normal'; // 现在在外部作用域中定义 } catch (error) { if (error === 'cancel') { - // 用户点击取消(异常) - updateParams.status = '5'; - updateParams.progress = 100; - updateParams.testFinal = '异常'; - resultType = 'abnormal'; // 现在在外部作用域中定义 + // 用户点击取消(异常),弹出失败原因输入框 + try { + const failReasonResult = await ElMessageBox.prompt('请输入失败原因', '试验异常', { + confirmButtonText: '确定', + cancelButtonText: '取消', + type: 'warning', + inputPlaceholder: '请详细描述失败原因...', + inputValidator: (value) => { + if (!value || value.trim() === '') { + return '失败原因不能为空'; + } + return true; + } + }); + + // 用户输入了失败原因并确认 + updateParams.status = '3'; + updateParams.progress = ''; + updateParams.testFinal = '异常'; + updateParams.failReason = failReasonResult.value; // 绑定失败原因参数 + updateParams.failTime = formatLocalDateTime(new Date()); // 记录失败时间 + resultType = 'abnormal'; + + // 将第一条未完成的步骤状态改为3(失败) + if (taskDetails.nodes && Array.isArray(taskDetails.nodes)) { + const firstUnfinishedNode = taskDetails.nodes.find((node) => { + return node.status === '2' || node.status === 2; + }); + if (firstUnfinishedNode) { + firstUnfinishedNode.status = '3'; + // 确保更新到updateParams中 + updateParams.nodes = taskDetails.nodes; + } + } + } catch (innerError) { + // 用户取消了失败原因输入 + return; + } } else { // 关闭弹窗,不执行操作 return; @@ -895,50 +1033,51 @@ const handleAction = async (task) => { switch (task.status) { case '1': // 待执行 → 开始执行(状态改为4) updateParams.status = '4'; - updateParams.progress = 10; // 初始进度10% - // 设置开始时间为当前时间 - updateParams.planBeginTime = new Date().toISOString().slice(0, 16).replace('T', ' '); + updateParams.progress = 0; // 初始进度10% + // 设置开始时间为当前时间(使用本地时间而非UTC时间) + updateParams.planBeginTime = formatLocalDateTime(new Date()); break; case '2': // 已延期 → 重新安排(状态改为1,重置时间) updateParams.status = '1'; - updateParams.beginTime = new Date().toISOString().slice(0, 16).replace('T', ' '); + updateParams.beginTime = formatLocalDateTime(new Date()); break; case '3': // 失败 → 重试(状态改为1) updateParams.status = '1'; + // 清空失败相关字段,使用适合各字段数据类型的默认值 + updateParams.failReason = ''; + updateParams.failTime = ''; // 时间类型字段使用null + updateParams.failPhase = ''; // 整数类型字段使用0 + + // 将失败的步骤状态改回2(未完成) + if (taskDetails.nodes && Array.isArray(taskDetails.nodes)) { + taskDetails.nodes.forEach((node) => { + if (node.status === '3' || node.status === 3) { + node.status = '2'; + } + }); + // 确保更新到updateParams中 + updateParams.nodes = taskDetails.nodes; + } break; default: return; } } - // 调用更新接口 + // 对于执行中状态('4')的任务,预先设置好时间字段 + if (task.status === '4') { + // 根据结果类型设置相应的时间(使用本地时间而非UTC时间) + if (resultType === 'normal') { + updateParams.planFinishTime = formatLocalDateTime(new Date()); + } else if (resultType === 'abnormal') { + updateParams.failTime = formatLocalDateTime(new Date()); + } + } + + // 调用更新接口(只调用一次) const response = await updatesyrenwu(updateParams); if (response.code === 200) { ElMessage.success(`任务${task.actionText}成功`); - - // 只有在接口调用成功后才设置时间 - if (task.status === '4') { - // 获取最新的任务详情,确保包含所有字段 - const latestTaskDetails = await getTaskDetails(task.id); - if (latestTaskDetails) { - // 创建包含所有字段的新参数对象 - const timeUpdateParams = { - ...latestTaskDetails, - id: task.id - }; - - // 根据结果类型设置相应的时间(现在resultType已在作用域内) - if (resultType === 'normal') { - timeUpdateParams.planFinishTime = new Date().toISOString().slice(0, 16).replace('T', ' '); - } else if (resultType === 'abnormal') { - timeUpdateParams.failTime = new Date().toISOString().slice(0, 16).replace('T', ' '); - } - - // 再次调用接口更新时间 - await updatesyrenwu(timeUpdateParams); - } - } - getTaskList(); // 刷新任务列表 } else { ElMessage.error(`任务${task.actionText}失败:` + response.msg); @@ -948,6 +1087,20 @@ const handleAction = async (task) => { } }; +/** + * 格式化本地日期时间为 'YYYY-MM-DD HH:mm' 格式 + * @param {Date} date - 日期对象 + * @returns {string} 格式化后的日期时间字符串 + */ +const formatLocalDateTime = (date) => { + 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}`; +}; + /** * 打开创建任务弹窗 */ @@ -1049,7 +1202,7 @@ const handleSaveTask = async () => { progress: 0, // 初始进度0% failReason: '', failTime: '', // 失败时间(新增时为空) - failPhase: 0, + failPhase: '', faileAnalyze: '', faileTips: '', testLongTime: 0, @@ -1267,6 +1420,10 @@ const getTaskStatusClass = (status) => { box-shadow: 0 4px 16px rgba(82, 196, 26, 0.15); } +.card-failed { + box-shadow: 0 4px 16px rgba(255, 77, 79, 0.15); +} + /* 左侧状态线颜色 */ .card-pending::before { background-color: #1677ff; @@ -1280,6 +1437,9 @@ const getTaskStatusClass = (status) => { .card-completed::before { background-color: #52c41a; } +.card-failed::before { + background-color: #ff4d4f; +} /* 卡片悬停效果 */ .task-card:hover { @@ -1336,6 +1496,12 @@ const getTaskStatusClass = (status) => { border-color: #b7eb8f; } +.tag-failed { + background-color: #fff2f0; + color: #ff4d4f; + border-color: #ffccc7; +} + .task-details { margin-bottom: 16px; } @@ -1419,6 +1585,28 @@ const getTaskStatusClass = (status) => { color: #165dff; } +/* 失败卡片特殊样式 */ +.failed-task-details { + margin-bottom: 16px; +} + +.failed-reason-item { + padding-top: 8px; + border-top: 1px dashed #f0f2f5; +} + +.failed-reason { + color: #f53f3f; + font-weight: 500; +} + +.failed-task-actions { + display: flex; + justify-content: flex-end; + align-items: center; + gap: 10px; +} + /* 分页区域样式 */ .pagination-section { display: flex; @@ -1472,6 +1660,46 @@ const getTaskStatusClass = (status) => { margin-bottom: 30px; } +/* 日志弹窗样式 */ +.logs-container { + max-height: 400px; + overflow-y: auto; +} + +.logs-list { + padding: 10px 0; +} + +.log-item { + padding: 12px 0; + border-bottom: 1px solid #f0f2f5; +} + +.log-item:last-child { + border-bottom: none; +} + +.log-time { + font-size: 12px; + color: #86909c; + margin-bottom: 4px; +} + +.log-content { + font-size: 14px; + color: #1d2129; + line-height: 1.6; +} + +.no-logs { + text-align: center; + padding: 60px 0; +} + +.log-skeleton { + margin: 12px 0; +} + /* 任务详情弹窗样式 */ .task-detail-container { max-height: 600px;