project 任务的父节点“完成百分比”不更新问题

部署运行你感兴趣的模型镜像

project的父节点下是所有子节点的“完成百分比”都是100%了,父节点的“完成百分比”却是0或者其他非100%数值。

解决方法:

“工具”->"选项"->"计算方式":选择“自动”,并点击“立即计算”

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

<template> <div class="app-container"> <el-form :model="queryParams" ref="queryRef" :inline="true" v-show="showSearch" label-width="68px"> <el-form-item label="公司简称" prop="companyId"> <el-select v-model="queryParams.companyId" placeholder="请选择公司简称" clearable filterable @change="handleCompanyChange" @focus="getCompanyList" :disabled="isBaseInfoDisabled"> <el-option v-for="item in companyOptions" :key="item.companyId" :label="item.companyName" :value="item.companyId" /> </el-select> </el-form-item> <el-form-item label="项目名称" prop="projectId"> <el-select v-model="queryParams.projectId" placeholder="请选择项目名称" clearable filterable :disabled="!queryParams.companyId || isBaseInfoDisabled" :loading="companyLoading" loading-text="加载中..."> <el-option v-for="item in projectOptions" :key="item.projectId" :label="item.projectName" :value="item.projectId" /> <template #empty> <div v-if="!companyLoading">暂无数据</div> </template> </el-select> </el-form-item> <el-form-item label="计划名称" prop="planName"> <el-input v-model="queryParams.planName" placeholder="请输入计划名称" clearable @keyup.enter="handleQuery" :disabled="isViewModel" /> </el-form-item> <el-form-item label="版本" prop="version"> <el-input v-model="queryParams.version" placeholder="" clearable @keyup.enter="handleQuery" disabled /> </el-form-item> <el-form-item label="备注" prop="remark"> <el-input v-model="queryParams.remark" placeholder="" clearable @keyup.enter="handleQuery" :disabled="isViewModel" /> </el-form-item> </el-form> <el-row :gutter="10" class="mb8" v-if="!isViewModel"> <el-col :span="1.5"> <el-upload action="/dev-api/schedule/total/importProject" :before-upload="beforeUpload" :show-file-list="false" :on-success="handleUploadSuccess" :on-error="handleUploadError" :headers="headerObj" :loading="uploadLoading"> <el-button type="primary" plain icon="Plus" v-hasPermi="['system:total:add']" :loading="uploadLoading">导入project文件</el-button> </el-upload> </el-col> <el-col :span="1.5"> <el-button type="primary" plain icon="Plus" @click="handleAddRow" v-hasPermi="['system:total:add']">新增行</el-button> </el-col> <el-col :span="1.5"> <el-button type="success" plain icon="Edit" :disabled="single" @click="handleAddSibling" v-hasPermi="['system:total:edit']">增加同级节点</el-button> </el-col> <el-col :span="1.5"> <el-button type="warning" plain icon="Download" @click="handleAddChild" :disabled="single" v-hasPermi="['system:total:export']">增加子节点</el-button> </el-col> <el-col :span="1.5"> <el-button type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete" v-hasPermi="['system:total:remove']">删除节点</el-button> </el-col> <right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar> </el-row> <el-table v-loading="loading" :data="visibleData" row-key="scheduleDetailId" ref="tableRef" height="400" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" @selection-change="handleSelectionChange" :check-strictly="true" virtual-scroll :row-height="50" :estimated-row-height="50"> <el-table-column type="selection" width="55" align="center" v-if="!isViewModel" /> <el-table-column label="主键" align="center" prop="scheduleDetailId" v-if="false" /> <el-table-column label="总计划id" align="center" prop="scheduleId" v-if="false" /> <el-table-column label="序号" align="center" prop="seqNum" /> <el-table-column label="等级" align="center"> <template #default="scope"> <span>{{ computeLevel(scope.row.seqNum) }}</span> </template> </el-table-column> <el-table-column label="任务名称" align="center"> <template #default="scope"> <el-input v-model="scope.row.taskName" placeholder="请输入任务名称" :disabled="isViewModel" /> </template> </el-table-column> <el-table-column label="计划开始日期" align="center" width="180"> <template #default="scope"> <el-date-picker v-model="scope.row.planStartDate" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width: 130px" :disabled="isViewModel" /> </template> </el-table-column> <el-table-column label="计划结束日期" align="center" width="180"> <template #default="scope"> <el-date-picker v-model="scope.row.planEndDate" type="date" value-format="YYYY-MM-DD" placeholder="选择日期" style="width: 130px" :disabled="isViewModel" /> </template> </el-table-column> <el-table-column label="计划天数" align="center"> <template #default="scope"> <el-input v-model="scope.row.planDays" placeholder="请输入计划天数" :disabled="isViewModel" /> </template> </el-table-column> <el-table-column label="里程碑节点" align="center"> <template #default="scope"> <el-select v-model="scope.row.milestoneFlag" clearable placeholder="请选择" :disabled="isViewModel"> <el-option label="否" :value="0"></el-option> <el-option label="是" :value="1"></el-option> </el-select> </template> </el-table-column> <el-table-column label="前置节点" align="center"> <template #default="scope"> <el-input v-model="scope.row.predecessorNodes" :disabled="isViewModel" /> </template> </el-table-column> <el-table-column label="备注" align="center" prop="remark"> <template #default="scope"> <el-input v-model="scope.row.remark" :disabled="isViewModel" /> </template> </el-table-column> </el-table> <!-- 新增底部按钮区域 --> <el-row :gutter="10" class="mt20" justify="center" v-if="!isViewModel"> <el-col :span="4"> <el-button type="info" plain icon="Back" @click="handleCancel"> 返回 </el-button> </el-col> <el-col :span="4"> <el-button type="primary" plain icon="Upload" :loading="saveLoading" @click="handleSave"> 保存数据 </el-button> </el-col> </el-row> </div> </template> <script setup name="Total"> import { listTotal, getTotal, delTotal, addTotal, updateTotal } from "@/api/schedule/total" import { queryCompanyInfo } from "@/api/project/info" import { listCompany, listProject } from "@/api/base/base.js"; import { defineProps, defineEmits } from 'vue' import { getToken } from '@/utils/auth' import { computed } from 'vue' // 新增虚拟滚动相关变量 const visibleData = ref([]) const startIndex = ref(0) const visibleCount = ref(20) // 可视区域显示行数 // 新增变量控制父子节点选中状态是否关联 const checkStrictly = ref(true); const scheduleId = ref(null) const selectedRows = ref([]); // 存储选中的行对象 const tableRef = ref(null); // 表格引用 let lastFetchTime = 0 let isFetching = false; const { proxy } = getCurrentInstance() const totalList = ref([]) const open = ref(false) const loading = ref(false) const showSearch = ref(true) const ids = ref([]) const single = ref(true) const multiple = ref(true) const total = ref(0) const title = ref("") const uploadLoading = ref(false) // 定义props const props = defineProps({ scheduleId: { type: [Number, String], default: null }, type: { type: String, default: 'add' }, viewModel: { type: Boolean, default: false } }) const state = ref() //计算属性,判断是否为查看模式 const isViewModel = computed(() => { return props.type === 'view' || props.viewModel }) // 新增计算属性,判断基础信息是否可编辑 const isBaseInfoDisabled = computed(() => { return props.type !== 'add'; // 只有新增模式可编辑 }); // 定义事件 const emit = defineEmits(['save-success', 'cancel']) const data = reactive({ form: {}, queryParams: { pageNum: 1, pageSize: 10, companyId: null, projectId: null, companyAbbr: null, projectName: null, planName: null, version: null, fileUrl: null, documentStatus: null, approvalStatus: null, effectiveBy: null, createdBy: null, createdTime: null, updatedBy: null, updatedTime: null }, rules: { } }) onMounted(() => { // 初始化时解除父子节点选中关联 checkStrictly.value = false; // 如果是新增模式,设置版本号为1 if (props.type === 'add') { queryParams.value.version = 1; } if (props.scheduleId) { scheduleId.value = props.scheduleId loadDetailData() } }) // 获取token const headerObj = ref({ Authorization: 'Bearer ' + getToken() }) // 新增数据加载方法 const loadDetailData = async () => { try { loading.value = true; const response = await getTotal(scheduleId.value); const detailData = response.data; // 新增字段映射转换 const mappedData = detailData.details.map(item => ({ ...item, scheduleDetailId: item.id, // 映射主键 scheduleId: detailData.scheduleId, // 补充计划ID planStartDate: item.planStartDate ? item.planStartDate.split('T')[0] : '', // 转换日期 planEndDate: item.planEndDate ? item.planEndDate.split('T')[0] : '' })); totalList.value = mappedData; // 填充基础信息 queryParams.value = { companyId: detailData.companyId, projectId: detailData.projectId, planName: detailData.planName, version: detailData.version, remark: detailData.remark }; companyOptions.value = [{ companyId: detailData.companyId, companyName: detailData.companyName // 使用接口返回的公司名称 }]; projectOptions.value = [{ projectId: detailData.projectId, projectName: detailData.projectName // 使用接口返回的项目名称 }]; // 填充计划明细 totalList.value = detailData.details || []; // 关键修改:主动加载项目列表 if (detailData.companyId) { // 先设置 companyId queryParams.value.companyId = detailData.companyId; // 再触发项目列表加载 await getProjectList(detailData.companyId); // 最后设置 projectId(确保项目列表已加载) queryParams.value.projectId = detailData.projectId; } } catch (error) { proxy.$modal.msgError("加载详情数据失败"); } finally { loading.value = false; } }; const { queryParams, form, rules } = toRefs(data) // 新增公司列表相关状态 const companyOptions = ref([]) const companyLoading = ref(false) const projectOptions = ref([]) //上传project文件 const beforeUpload = (file) => { const isMPP = file.name.endsWith('.mpp'); debugger; if (!isMPP) { proxy.$modal.msgError('只能上传MPP格式文件!'); return false; } uploadLoading.value = true; proxy.$modal.msgSuccess("请稍等,上传中...", { duration: 3000 }) return true; }; const handleUploadSuccess = (response) => { uploadLoading.value = false; if (response.code === 200) { queryParams.value.fileUrl = response.data.fileUrl; // 正确更新ref值 totalList.value = processDataMapping(response.data.details); // 强制触发视图更新 nextTick(() => { tableRef.value?.clearSort(); }); proxy.$modal.msgSuccess("上传成功") } }; const formatDate = (dateString) => { if (!dateString) return null; // 处理ISO格式和短日期格式 return dateString.includes('T') ? dateString.split('T')[0] : dateString; }; // 处理数据映射 const processDataMapping = (data, parentSeq = "0", level = 1) => { return data.map((item, index) => { // 生成当前节点序号(基于父节点序号和当前索引) const seqNum = parentSeq === "0" ? (index + 1).toString() : `${parentSeq}.${index + 1}`; const newItem = { ...item, scheduleDetailId: item.scheduleDetailId, planStartDate: formatDate(item.planStartDate), planEndDate: formatDate(item.planEndDate), // 添加序号和等级计算 seqNum, priorityLevel: level, // 直接使用层级作为等级 // 添加计算等级字段(用于前端显示) computedLevel: level }; // 递归处理子节点 if (item.children?.length) { newItem.children = processDataMapping( item.children, seqNum, level + 1 ); } return newItem; }); }; const handleUploadError = (err, file, fileList) => { uploadLoading.value = false; proxy.$modal.msgError('上传失败: ' + err.message); }; // 获取公司列表 async function getCompanyList() { try { companyLoading.value = true; const response = await listCompany(); // 调用接口获取全部公司 companyOptions.value = response.data || []; // 确保当前选中的公司存在选项中 if (queryParams.value.companyId && !companyOptions.value.some(c => c.companyId === queryParams.value.companyId)) { companyOptions.value.push({ companyId: queryParams.value.companyId, companyName: queryParams.value.companyName || '未知公司' }); } } catch (error) { proxy.$modal.msgError("获取公司列表失败"); } finally { companyLoading.value = false; } } function handleCompanyChange(companyId) { queryParams.value.projectId = null queryParams.value.projectName = null projectOptions.value = [] if (companyId) { getProjectList(companyId) } } // 获取公司下项目名称列表 async function getProjectList(companyId) { if (Date.now() - lastFetchTime < 1000) return if (isFetching) return try { isFetching = true companyLoading.value = true lastFetchTime = Date.now() const response = await listProject({ companyId: companyId || queryParams.value.companyId }) projectOptions.value = response.data || [] } catch (error) { proxy.$modal.msgError("获取项目列表失败") projectOptions.value = [] } finally { isFetching = false companyLoading.value = false } } // 新增项目列表相关 const companyOption = ref([]) /** 查询总工期计划列表 */ function getList() { loading.value = true listTotal(queryParams.value).then(response => { totalList.value = response.rows total.value = response.total loading.value = false }) } // 取消按钮 function cancel() { open.value = false reset() } // 表单重置 function reset() { form.value = { scheduleId: null, companyId: null, companyAbbr: null, projectId: null, planName: null, version: null, documentStatus: null, approvalStatus: null, effectiveBy: null, remark: null, createdBy: null, createdTime: null, updatedBy: null, updatedTime: null } proxy.resetForm("totalRef") } /** 搜索按钮操作 */ function handleQuery() { queryParams.value.pageNum = 1 getList() } /** 重置按钮操作 */ function resetQuery() { proxy.resetForm("queryRef") handleQuery() } // 处理选择事件 const handleSelect = (selection, row) => { // 关键修改:确保只选中当前行,选中子节点 if (selection.includes(row)) { // 取消所有子节点的选中状态 if (row.children && row.children.length > 0) { const toggleChildrenSelection = (children) => { children.forEach(child => { tableRef.value.toggleRowSelection(child, false); if (child.children && child.children.length > 0) { toggleChildrenSelection(child.children); } }); }; toggleChildrenSelection(row.children); } } }; // 多选框选中数据 function handleSelectionChange(selection) { ids.value = selection.map(item => item.scheduleId) selectedRows.value = selection; single.value = selection.length != 1 multiple.value = !selection.length } /** 新增按钮操作 */ function handleAdd() { reset() open.value = true title.value = "添加总工期计划" } /** 修改按钮操作 */ function handleUpdate(row) { reset() const _scheduleId = row.scheduleId || ids.value getTotal(_scheduleId).then(response => { form.value = response.data open.value = true title.value = "修改总工期计划" }) } /** 提交按钮 */ function submitForm() { proxy.$refs["totalRef"].validate(valid => { if (valid) { if (form.value.scheduleId != null) { updateTotal(form.value).then(response => { proxy.$modal.msgSuccess("修改成功") open.value = false getList() }) } else { addTotal(form.value).then(response => { proxy.$modal.msgSuccess("新增成功") open.value = false getList() }) } } }) } /** 删除按钮操作 */ function handleDelete(row) { if (!selectedRows.value.length) { proxy.$modal.msgError("请选择要删除的节点"); return; } proxy.$modal.confirm('确认删除选中节点及其子节点?').then(() => { // 创建新数组避免直接修改原数组 const newList = removeNodes(totalList.value, selectedRows.value); // 更新列表数据 totalList.value = newList; // 清空选中状态 selectedRows.value = []; single.value = true; multiple.value = true; }).then(() => { proxy.$modal.msgSuccess("删除成功"); // getList(); // 刷新数据保持前后端一致 }).catch(() => { }); } /** 导出按钮操作 */ function handleExport() { proxy.download('system/total/export', { ...queryParams.value }, `total_${new Date().getTime()}.xlsx`) } // 添加计算等级的方法 const computeLevel = (seqNum) => { if (!seqNum) return 1; return (seqNum.split('.').length) } // 添加根节点 const handleAddRow = () => { if (!queryParams.value.companyId) { proxy.$modal.msgError("请先选择公司") return } const newRow = { scheduleDetailId: Date.now(), scheduleId: queryParams.value.companyId, seqNum: String(totalList.value.length + 1), priorityLevel: '', taskName: '', planStartDate: '', planEndDate: '', planDays: '', milestoneFlag: '', predecessorNodes: '', remark: '', children: [] } totalList.value.push(newRow) totalList.value = [...totalList.value] // 触发响应式更新 } // 添加子节点处理函数 const handleAddChild = () => { if (!selectedRows.value.length) { proxy.$modal.msgError("请选择一行父节点"); return; } const parent = selectedRows.value[0]; if (typeof parent.seqNum === 'undefined') { proxy.$modal.msgError("父节点缺少序号信息"); return; } const newChild = { scheduleDetailId: Date.now(), scheduleId: parent.scheduleId, seqNum: generateChildSeqNum(parent.seqNum, parent), priorityLevel: '', taskName: '', planStartDate: '', planEndDate: '', planDays: '', milestoneFlag: '', predecessorNodes: '', remark: '', children: [] }; // 添加子节点到父节点 if (!parent.children) { parent.children = []; } parent.children.push(newChild); // 触发视图更新 totalList.value = [...totalList.value]; // 展开父节点 nextTick(() => { if (tableRef.value) { tableRef.value.toggleRowExpansion(parent, true); } else { console.error('表格组件引用未初始化'); // 备用方案:强制刷新表格 totalList.value = JSON.parse(JSON.stringify(totalList.value)); } }); }; // 生成子节点序号 function generateChildSeqNum(parentSeq, parentNode) { const seqStr = (parentSeq || '0').toString(); const parts = seqStr.split('.').map(Number); // 获取父节点现有的子节点数量 const childCount = parentNode.children ? parentNode.children.length : 0; // 在父级序号基础上追加新层级,序号基于现有子节点数量+1 return [...parts, childCount + 1].join('.'); } /** 添加同级节点 */ const handleAddSibling = () => { if (!selectedRows.value.length) { proxy.$modal.msgError("请选择一行节点"); return; } const currentNode = selectedRows.value[0]; if (typeof currentNode.seqNum === 'undefined') { proxy.$modal.msgError("节点缺少序号信息"); return; } // 查找父节点 const parentNode = findParentNode(currentNode, totalList.value); // 生成同级序号 const newSeqNum = generateSiblingSeqNum(currentNode.seqNum); // 创建新节点 const newNode = { scheduleDetailId: Date.now(), scheduleId: currentNode.scheduleId, seqNum: newSeqNum, priorityLevel: '', taskName: '', planStartDate: '', planEndDate: '', planDays: '', milestoneFlag: '', predecessorNodes: '', remark: '', children: [] }; // 添加到正确位置 if (parentNode) { parentNode.children.push(newNode); } else { totalList.value.push(newNode); } // 触发视图更新 totalList.value = [...totalList.value]; // 展开父节点(如果有) nextTick(() => { if (parentNode && tableRef.value) { tableRef.value.toggleRowExpansion(parentNode, true); } }); }; /** 查找父节点 */ function findParentNode(targetNode, nodes, parent = null) { for (const node of nodes) { if (node === targetNode) return parent; if (node.children?.length) { const found = findParentNode(targetNode, node.children, node); if (found) return found; } } return null; } function generateSiblingSeqNum(currentSeq) { const seqStr = String(currentSeq || '0'); // 处理 undefined/null const currentParts = seqStr.split('.').map(Number); const parentParts = currentParts.slice(0, -1); const level = currentParts.length; let maxNum = 0; // 递归遍历查找同级节点 const traverse = (nodes) => { for (const node of nodes) { const nodeParts = node.seqNum.split('.').map(Number); const isSameParent = parentParts.every((v, i) => nodeParts[i] === v); const isSameLevel = nodeParts.length === level; if (isSameParent && isSameLevel) { maxNum = Math.max(maxNum, nodeParts[level - 1]); } if (node.children?.length) { traverse(node.children); } } }; traverse(totalList.value); return [...parentParts, maxNum + 1].join('.'); } /** * 递归删除节点及其子节点 * @param {Array} nodes 当前节点列表 * @param {Array} targets 要删除的节点列表 * @returns 新节点列表 */ function removeNodes(nodes, targets) { return nodes.filter(node => { // 如果当前节点在删除列表中,跳过(即删除) if (isNodeInTargets(node, targets)) { return false; } // 如果有子节点,递归处理 if (node.children?.length) { node.children = removeNodes(node.children, targets); } return true; }); } /** * 检查节点是否在目标列表中 */ function isNodeInTargets(node, targets) { return targets.some(target => target.scheduleDetailId === node.scheduleDetailId ); } // 新增保存加载状态 const saveLoading = ref(false) /** 保存按钮操作 */ const handleSave = async () => { try { saveLoading.value = true; // 递归展开树形数据 const flattenTree = (nodes) => { let result = []; nodes.forEach(node => { result.push({ scheduleDetailId: node.scheduleDetailId, seqNum: node.seqNum, priorityLevel: computeLevel(node.seqNum), taskName: node.taskName, planStartDate: node.planStartDate, planEndDate: node.planEndDate, planDays: node.planDays, milestoneFlag: node.milestoneFlag, predecessorNodes: node.predecessorNodes, remark: node.remark }); if (node.children?.length) { result = result.concat(flattenTree(node.children)); } }); return result; }; // 构造提交数据 const submitData = { scheduleId: Array.isArray(scheduleId.value) ? scheduleId.value[0] : scheduleId.value, companyId: queryParams.value.companyId, projectId: queryParams.value.projectId, planName: queryParams.value.planName, version: queryParams.value.version, remark: queryParams.value.remark, fileUrl: queryParams.value.fileUrl, details: flattenTree(totalList.value) // 递归处理所有节点 }; if (props.type === 'add') { await addTotal(submitData); proxy.$modal.msgSuccess("保存成功"); } else if (props.type === 'edit') { await updateTotal(submitData); proxy.$modal.msgSuccess("修改成功"); } emit('save-success') } catch (error) { console.error('保存失败:', error); } finally { saveLoading.value = false; } }; // 计算当前可视数据 const updateVisibleData = () => { // 扁平化树形数据用于虚拟滚动 const flatData = flattenTree(totalList.value) visibleData.value = flatData.slice( startIndex.value, startIndex.value + visibleCount.value ) } // 树形数据扁平化 const flattenTree = (nodes) => { const result = [] const stack = [...nodes] while (stack.length) { const node = stack.shift() result.push(node) if (node.children && node.children.length) { stack.unshift(...node.children) // 保持子节点顺序 } } return result } // 监听滚动事件更新数据 const handleScroll = ({ scrollTop }) => { const rowHeight = 50 startIndex.value = Math.floor(scrollTop / rowHeight) updateVisibleData() } /** 返回列表 */ const handleCancel = () => { emit('cancel') } </script> <style scoped> .footer-buttons { margin-top: 40px; position: sticky; bottom: 20px; background: white; padding: 10px 0; box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.05); z-index: 100; width: 100%; } </style>为什么一行数据都显示出来了
09-17
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值