From 31103f3c9997aa9e950effb15c77e450f04a2c12 Mon Sep 17 00:00:00 2001 From: xs Date: Wed, 3 Dec 2025 13:40:17 +0800 Subject: [PATCH] =?UTF-8?q?1.0.45.1=EF=BC=9A=20=E5=89=8D=E7=AB=AF=EF=BC=9A?= =?UTF-8?q?=20=20=20=20feat(budget):=E5=AE=8C=E6=88=90=E7=A0=94=E5=8F=91?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E9=A2=84=E7=AE=97=E4=BF=9D=E5=AD=98=E5=92=8C?= =?UTF-8?q?=E6=9B=B4=E6=96=B0=E5=8A=9F=E8=83=BD;=20=20=20=20fix(budget):?= =?UTF-8?q?=E9=A2=84=E7=AE=97=E6=9B=B4=E6=96=B0=E6=97=B6=E5=88=A4=E6=96=AD?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BF=AE=E5=A4=8D=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/utils/objHandle.ts | 117 ++++ src/views/oa/erp/budgetInfo/edit.vue | 542 +++++++++++------- src/views/oa/erp/budgetInfo/index.vue | 186 ++++-- .../oa/erp/budgetInfo/market/BudgetTable.vue | 39 +- .../budgetInfo/market/InstallationCost.vue | 2 - .../oa/erp/budgetInfo/market/LaborCost.vue | 2 - .../oa/erp/budgetInfo/market/MaterialCost.vue | 155 +++-- .../oa/erp/budgetInfo/market/OtherCost.vue | 6 +- .../oa/erp/budgetInfo/market/TravelCost.vue | 6 +- .../oa/erp/budgetInfo/rd/BudgetTable.vue | 42 +- .../oa/erp/budgetInfo/rd/LaborService.vue | 8 +- .../oa/erp/budgetInfo/rd/TestingCost.vue | 4 +- 12 files changed, 720 insertions(+), 389 deletions(-) diff --git a/src/utils/objHandle.ts b/src/utils/objHandle.ts index 0861790..7bbf8ea 100644 --- a/src/utils/objHandle.ts +++ b/src/utils/objHandle.ts @@ -1,3 +1,4 @@ + /** * 深度比较两个对象或数组是否相同 * @param {any} obj1 @@ -13,6 +14,13 @@ export function isDeepEqual(obj1: any, obj2: any) { return obj1 === obj2; } + + if (isNumeric(obj1) && isNumeric(obj2)) { + console.log("-----111111") + // 将两者都转为数字进行比较,处理 "4.00" == 4 的情况 + return Number(obj1) === Number(obj2); + } + // 处理数组 if (Array.isArray(obj1) && Array.isArray(obj2)) { if (obj1.length !== obj2.length) return false; @@ -45,3 +53,112 @@ export function isDeepEqual(obj1: any, obj2: any) { return true; } + +/** + * 深度比较两个数组中的对象是否相等,支持数字和数字字符串的等价比较 + * @param {Array} arr1 - 第一个数组 + * @param {Array} arr2 - 第二个数组 + * @returns {boolean} - 如果两个数组的所有元素深度相等返回 true + */ +export function deepEqualArrays(arr1, arr2) { + // 1. 快速检查:相同引用或都是 null/undefined + if (arr1 === arr2) return true; + + // 2. 检查是否为数组 + if (!Array.isArray(arr1) || !Array.isArray(arr2)) { + return false; + } + + // 3. 检查数组长度 + if (arr1.length !== arr2.length) return false; + + // 4. 深度比较每个元素 + for (let i = 0; i < arr1.length; i++) { + if (!deepEqualObjects(arr1[i], arr2[i])) { + return false; + } + } + + return true; +} + +/** + * 深度比较两个对象是否相等,支持数字和数字字符串的等价比较 + * @param {any} obj1 - 第一个值 + * @param {any} obj2 - 第二个值 + * @returns {boolean} - 如果相等返回 true + */ +function deepEqualObjects(obj1, obj2) { + // 1. 快速检查 + if (obj1 === obj2) return true; + + // 2. 处理 null 和 undefined + if (obj1 == null || obj2 == null) { + return obj1 === obj2; + } + + // 3. 处理数字和数字字符串 + if (isNumeric(obj1) && isNumeric(obj2)) { + const num1 = Number(obj1); + const num2 = Number(obj2); + // 处理浮点数精度问题 + return Math.abs(num1 - num2) < 0.000001; + } + + // 4. 处理数组 + if (Array.isArray(obj1) && Array.isArray(obj2)) { + return deepEqualArrays(obj1, obj2); + } + + // 5. 处理普通对象 + if (typeof obj1 === 'object' && typeof obj2 === 'object') { + const keys1 = Object.keys(obj1); + const keys2 = Object.keys(obj2); + + if (keys1.length !== keys2.length) return false; + + // 检查所有键是否相同 + const keySet = new Set([...keys1, ...keys2]); + if (keySet.size !== keys1.length) return false; + + // 递归比较每个属性 + for (const key of keys1) { + if (!deepEqualObjects(obj1[key], obj2[key])) { + return false; + } + } + + return true; + } + + // 6. 其他情况使用严格相等 + return obj1 === obj2; +} + +/** + * 判断一个值是否可以安全转为数字 + * @param {any} value - 要检查的值 + * @returns {boolean} - 如果可以安全转为数字返回 true + */ +function isNumeric(value) { + if (value === null || value === undefined || value === '') { + return false; + } + + // 如果是数字类型 + if (typeof value === 'number') { + return !isNaN(value) && isFinite(value); + } + + // 如果是字符串 + if (typeof value === 'string') { + const trimmed = value.trim(); + if (trimmed === '') return false; + + // 检查是否为有效的数字字符串 + const num = Number(trimmed); + return !isNaN(num) && isFinite(num); + } + + return false; +} diff --git a/src/views/oa/erp/budgetInfo/edit.vue b/src/views/oa/erp/budgetInfo/edit.vue index d948e24..1d4da2e 100644 --- a/src/views/oa/erp/budgetInfo/edit.vue +++ b/src/views/oa/erp/budgetInfo/edit.vue @@ -16,21 +16,14 @@ - - - + + - - + + @@ -48,7 +41,13 @@ - + @@ -82,7 +81,7 @@ > - + @@ -103,14 +102,30 @@ - -
- - - -
+ + +
+ + + + + + + + + + + + + + + 搜索 + 重置 + + + +
+
@@ -136,8 +151,8 @@ @@ -162,7 +177,9 @@ import { ref, reactive } from 'vue'; import { ElMessage } from 'element-plus'; import { listProjectInfo, addErpBudgetInfo, updateErpBudgetInfo, getErpBudgetInfo } from '@/api/oa/erp/budgetInfo'; import { ProjectInfoVO, ProjectInfoQuery } from '@/api/oa/erp/projectInfo/types'; -import { isDeepEqual } from '@/utils/objHandle'; +import { getUserList } from '@/api/system/user'; + +import { deepEqualArrays } from '@/utils/objHandle'; const router = useRouter(); @@ -311,18 +328,6 @@ const projectInfo = reactive({ projectCategory: '' }); -// 市场预算成本数据 -const marketCostData = reactive({ - materialCost: 0, - laborCost: 0, - installCost: 0, - travelCost: 0, - serviceCost: 0, - literatureCost: 0, - testingCost: 0, - otherCost: 0 -}); - // 研发预算成本数据 const costData = reactive({ equipmentCost: 0, @@ -338,22 +343,66 @@ const costData = reactive({ otherCost: 0 }); +const userList = ref([]); +/** 查询用户列表 */ +const getUsers = async () => { + const res = await getUserList({}); + userList.value = res.data; +}; + // 项目选择弹窗相关 const projectSelectDialogVisible = ref(false); const selectedProject = ref(null); const projectList = ref([]); const projectTotal = ref(0); const projectLoading = ref(true); -const projectSearchForm = reactive({ - projectCategory: '', - pageNum: 1, - pageSize: 10 +const data = reactive<{ queryParams: ProjectInfoQuery }>({ + queryParams: { + pageNum: 1, + pageSize: 10, + contractFlag: undefined, + projectCode: undefined, + projectName: undefined, + businessDirection: undefined, + projectCategory: undefined, + spareFlag: undefined, + projectTypeId: undefined, + paymentMethod: undefined, + deptId: undefined, + managerId: undefined, + chargeId: undefined, + deputyId: undefined, + peopleId: undefined, + amount: undefined, + projectStatus: undefined, + flowStatus: undefined, + sortOrder: undefined, + contractId: undefined, + activeFlag: undefined, + params: {} + } }); +const { queryParams } = toRefs(data); + +const queryFormRef = ref(); + +/** 搜索按钮操作 */ +const handleQuery = () => { + queryParams.value.pageNum = 1; + getProjectList(); +}; + +/** 重置按钮操作 */ +const resetQuery = () => { + queryFormRef.value?.resetFields(); + handleQuery(); +}; + /** 查询项目信息列表 */ const getProjectList = async () => { projectLoading.value = true; - const res = await listProjectInfo(projectSearchForm); + const res = await listProjectInfo(queryParams.value); console.log(searchForm); projectList.value = res.rows; projectTotal.value = res.total; @@ -379,21 +428,6 @@ const showProjectSelectDialog = () => { getProjectList(); }; -// 项目搜索 -const handleProjectSearch = () => { - projectSearchForm.pageNum = 1; -}; - -// 项目分页大小变化 -const handleProjectPageSizeChange = (size: number) => { - projectSearchForm.pageSize = size; -}; - -// 项目当前页变化 -const handleProjectCurrentPageChange = (current: number) => { - projectSearchForm.pageNum = current; -}; - // 选择项目 const selectProject = (row: ProjectInfoVO) => { selectedProject.value = row; @@ -487,7 +521,7 @@ const handleOtherCostUpdate = (data: { totalAmount: number }) => { }; // 更新市场项目预算预算表数据 -const updateBudgetTable = () => { +const updateBudgetTable = async () => { if (!budgetTableRef.value) return; // 获取各tab页的合计数据 @@ -506,7 +540,7 @@ const updateBudgetTable = () => { }; // 更新研发项目预算预算表数据 -const updateRdBudgetTable = () => { +const updateRdBudgetTable = async () => { // 获取各tab页的合计数据 const equipmentAmount = rdEquipmentCostRef.value?.getTotalAmount(); const equipmentTotal = equipmentAmount.equipmentTotal || 0; @@ -577,7 +611,17 @@ const exportBudget = () => { ElMessage.success('导出预算成功'); }; -// 保存预算 +// 提取用户查找逻辑 +const findUserName = (userList: any[], userId: number): string => { + const user = userList?.find((item) => item.userId === userId); + return user?.nickName || ''; +}; + +// 提取数据比较和赋值逻辑 +const assignIfChanged = (targetArray: any[], sourceArray: any[], originalArray: any[]): any[] => { + return !deepEqualArrays(originalArray, sourceArray) ? sourceArray || [] : []; +}; + const handleSave = async (status: string, mode: boolean) => { if (!searchForm.projectId) { ElMessage.warning('请先选择项目'); @@ -586,157 +630,250 @@ const handleSave = async (status: string, mode: boolean) => { try { buttonLoading.value = true; - const budgetForm = reactive({}); - if (searchForm.projectCategory === PROJECT_CATEGORY.RD || searchForm.projectCategory === PROJECT_CATEGORY.PRE_PRODUCTION) { - updateRdBudgetTable(); - const budgetDetailData = rdBudgetTableRef.value?.budgetDetailData; + const isRdProject = [PROJECT_CATEGORY.RD, PROJECT_CATEGORY.PRE_PRODUCTION].includes(searchForm.projectCategory); + const isMarketProject = [PROJECT_CATEGORY.MARKET, PROJECT_CATEGORY.MARKET_PART].includes(searchForm.projectCategory); - Object.assign(budgetForm, - rdBudgetTableRef.value?.rdBudgetInfoForm || {}, - rdBudgetTableRef.value?.footerForm || {} - ); + let budgetForm: budgetInfoForm = {}; - budgetForm.budgetStatus = status === 'draft' ? BUSINESS_STATUS.DRAFT : BUSINESS_STATUS.WAITING; - budgetForm.flowStatus = status === 'draft' ? 'draft' : 'waiting'; - - budgetForm.erpRdBudgetEquipmentCostList = []; - budgetForm.erpRdBudgetMaterialCostList = []; - budgetForm.erpRdBudgetTravelCostList = []; - budgetForm.erpRdBudgetMeetingCostList = []; - budgetForm.erpRdBudgetExchangeCostList = []; - budgetForm.erpRdBudgetTechCostList = []; - budgetForm.erpRdBudgetLaborCostList = []; - budgetForm.erpRdBudgetLiteratureCostList = []; - budgetForm.erpRdBudgetTestingCostList = []; - budgetForm.erpRdBudgetOtherCostList = []; - - // console.log(budgetForm); - budgetForm.erpBudgetDetailList = budgetDetailData; - if (!isDeepEqual(oriRdBudgetEquipmentCostList.value, rdEquipmentCostRef.value?.equipmentData)) { - budgetForm.erpRdBudgetEquipmentCostList = rdEquipmentCostRef.value?.equipmentData; - } - if (!isDeepEqual(oriRdBudgetMaterialCostList.value, rdMaterialCostRef.value?.allMaterialData)) { - budgetForm.erpRdBudgetMaterialCostList = rdMaterialCostRef.value?.allMaterialData; - } - if (!isDeepEqual(oriRdBudgetTravelCostList.value, rdTravelMeetingExchangeRef.value?.travelList)) { - budgetForm.erpRdBudgetTravelCostList = rdTravelMeetingExchangeRef.value?.travelList; - } - if (!isDeepEqual(oriRdBudgetMeetingCostList.value, rdTravelMeetingExchangeRef.value?.meetingList)) { - budgetForm.erpRdBudgetMeetingCostList = rdTravelMeetingExchangeRef.value?.meetingList; - } - if (!isDeepEqual(oriRdBudgetExchangeCostList.value, rdTravelMeetingExchangeRef.value?.exchangeList)) { - budgetForm.erpRdBudgetExchangeCostList = rdTravelMeetingExchangeRef.value?.exchangeList; - } - - budgetForm.erpRdBudgetTechCostList = [ - ...(!isDeepEqual(oriRdBudgetTechConsultCostList.value, rdLaborServiceRef.value?.techConsultList) - ? rdLaborServiceRef.value?.techConsultList || [] - : []), - ...(!isDeepEqual(oriRdBudgetExpertMeetingCostList.value, rdLaborServiceRef.value?.expertMeetingList) - ? rdLaborServiceRef.value?.expertMeetingList || [] - : []), - ...(!isDeepEqual(oriRdBudgetExpertCommCostList.value, rdLaborServiceRef.value?.expertCommList) - ? rdLaborServiceRef.value?.expertCommList || [] - : []) - ]; - console.log('---'); - console.log(budgetForm.erpRdBudgetTechCostList); - - budgetForm.erpRdBudgetLaborCostList = [ - ...(!isDeepEqual(oriRdBudgetLaborCostList.value, rdLaborServiceRef.value?.laborList) ? rdLaborServiceRef.value?.laborList || [] : []), - ...(!isDeepEqual(oriRdBudgetServiceCostList.value, rdLaborServiceRef.value?.serviceList) ? rdLaborServiceRef.value?.serviceList || [] : []) - ]; - - budgetForm.erpRdBudgetLiteratureCostList = [ - ...(!isDeepEqual(oriRdBudgetLiteratureMaterialCostList.value, rdLiteratureCostRef.value?.materialsList) - ? rdLiteratureCostRef.value?.materialsList || [] - : []), - ...(!isDeepEqual(oriRdBudgetLiteratureSofwareCostList.value, rdLiteratureCostRef.value?.softwareList) - ? rdLiteratureCostRef.value?.softwareList || [] - : []) - ]; - - budgetForm.erpRdBudgetLiteratureCostList.push(rdLiteratureCostRef.value?.literatureRetrieval); - - if (!isDeepEqual(oriRdBudgetTestingCostList.value, rdTestingCostRef.value?.testData)) { - budgetForm.erpRdBudgetTestingCostList = rdTestingCostRef.value?.testData; - } - if (!isDeepEqual(oriRdBudgetOtherCostList.value, rdOtherCostRef.value?.otherCostList)) { - budgetForm.erpRdBudgetOtherCostList = rdOtherCostRef.value?.otherCostList; - } - - if (budgetForm.budgetId) { - budgetForm.toDeletedRdEquipmentCostIdList = rdEquipmentCostRef.value?.toDeletedEquipmentCostIdList; - budgetForm.toDeletedRdMaterialCostIdList = rdMaterialCostRef.value?.toDeletedMaterialCostIdList; - budgetForm.toDeletedRdTravelCostIdList = rdTravelMeetingExchangeRef.value?.toDeletedTravelCostIdList; - budgetForm.toDeletedRdMeetingCostIdList = rdTravelMeetingExchangeRef.value?.toDeletedMeetingCostIdList; - budgetForm.toDeletedRdExchangeCostIdList = rdTravelMeetingExchangeRef.value?.toDeletedExchangeCostIdList; - budgetForm.toDeletedRdTechCostIdList = rdLaborServiceRef.value?.toDeletedTechCostIdList; - budgetForm.toDeletedRdLaborCostIdList = rdLaborServiceRef.value?.toDeletedLaborCostIdList; - budgetForm.toDeletedRdLiteratureCostIdList = rdLiteratureCostRef.value?.toDeletedLiteratureCostIdList; - budgetForm.toDeletedRdTestingCostIdList = rdTestingCostRef.value?.toDeletedTestCostIdList; - budgetForm.toDeletedRdOtherCostIdList = rdOtherCostRef.value?.toDeletedOtherCostIdList; - } - } else if (searchForm.projectCategory === PROJECT_CATEGORY.MARKET || searchForm.projectCategory === PROJECT_CATEGORY.MARKET_PART) { - updateBudgetTable(); - - const budgetDetailData = budgetTableRef.value?.budgetDetailData; - Object.assign(budgetForm, budgetTableRef.value?.budgetForm); - budgetForm.budgetStatus = status === 'draft' ? BUSINESS_STATUS.DRAFT : BUSINESS_STATUS.WAITING; - budgetForm.flowStatus = status === 'draft' ? 'draft' : 'waiting'; - - budgetForm.erpBudgetMaterialCostList = []; - budgetForm.erpBudgetLaborCostList = []; - budgetForm.erpBudgetInstallCostList = []; - budgetForm.erpBudgetTravelCostList = []; - budgetForm.erpBudgetOtherCostList = []; - console.log(budgetForm); - budgetForm.erpBudgetDetailList = budgetDetailData; - if (!isDeepEqual(oriBudgetMaterialCostList.value, materialCostRef.value?.budgetMaterialCostList)) { - budgetForm.erpBudgetMaterialCostList = materialCostRef.value?.budgetMaterialCostList; - } - if (!isDeepEqual(oriBudgetLaborCostList.value, laborCostRef.value?.budgetLaborCostList)) { - budgetForm.erpBudgetLaborCostList = laborCostRef.value?.budgetLaborCostList; - } - if (!isDeepEqual(oriBudgetInstallCostList.value, installationCostRef.value?.installCostList)) { - budgetForm.erpBudgetInstallCostList = installationCostRef.value?.installCostList; - } - if (!isDeepEqual(oriBudgetTravelCostList.value, travelCostRef.value?.travelCostList)) { - budgetForm.erpBudgetTravelCostList = travelCostRef.value?.travelCostList; - } - if (!isDeepEqual(oriBudgetOtherCostList.value, otherCostRef.value?.otherCostList)) { - budgetForm.erpBudgetOtherCostList = otherCostRef.value?.otherCostList; - } - - if (budgetForm.budgetId) { - budgetForm.toDeletedMaterialCostIdList = materialCostRef.value?.toDeletedMaterialCostIdList; - budgetForm.toDeletedLaborCostIdList = laborCostRef.value?.toDeletedLaborCostIdList; - budgetForm.toDeletedInstallCostIdList = installationCostRef.value?.toDeletedInstallCostIdList; - budgetForm.toDeletedTravelCostIdList = travelCostRef.value?.toDeletedTravelCostIdList; - budgetForm.toDeletedOtherCostIdList = otherCostRef.value?.toDeletedOtherCostIdList; - } - } - - if (budgetForm.budgetId) { - await updateErpBudgetInfo(budgetForm).finally(() => (buttonLoading.value = false)); + if (isRdProject) { + budgetForm = await processRdBudgetForm(status); + } else if (isMarketProject) { + budgetForm = await processMarketBudgetForm(status); } else { - await addErpBudgetInfo(budgetForm).finally(() => (buttonLoading.value = false)); + ElMessage.warning('不支持的预算类型'); + return; } - proxy?.$modal.msgSuccess('操作成功'); - // 保存成功后关闭之前记录的tab - const obj = { path: '/budget/erp/budgetInfo', query: { t: Date.now(), pageNum: routeParams.value.pageNum } }; - proxy?.$tab.closeOpenPage(obj); - // router.go(-1); - // handleClose(); + + // 统一保存逻辑 + const saveOperation = budgetForm.budgetId ? () => updateErpBudgetInfo(budgetForm) : () => addErpBudgetInfo(budgetForm); + + await saveOperation(); + + ElMessage.success('操作成功'); + handleSaveSuccess(); } catch (error) { - ElMessage.error('保存失败'); console.error('保存失败:', error); + ElMessage.error('保存失败'); } finally { buttonLoading.value = false; } }; +// 处理研发预算表单 +const processRdBudgetForm = async (status: string): Promise => { + await updateRdBudgetTable(); + + const budgetDetailData = rdBudgetTableRef.value?.budgetDetailData || []; + const rdBudgetInfoForm = rdBudgetTableRef.value?.rdBudgetInfoForm || {}; + const footerForm = rdBudgetTableRef.value?.footerForm || {}; + + // 合并表单数据 + const budgetForm = { + ...rdBudgetInfoForm, + // 设置状态 + budgetStatus: getBudgetStatus(status), + flowStatus: getFlowStatus(status), + // 设置负责人信息 + managerId: footerForm.managerId, + managerName: findUserName(userList.value, footerForm.managerId), + approveUserId: footerForm.approveUserId, + approveUserName: findUserName(userList.value, footerForm.approveUserId), + // 预算详情 + erpBudgetDetailList: budgetDetailData, + // 初始化所有成本列表 + ...initializeRdCostLists() + }; + + console.log(budgetForm); + + // 处理各项成本数据 + processRdCostData(budgetForm); + + // 如果有预算ID,设置要删除的列表 + if (budgetForm.budgetId) { + setRdToDeletedLists(budgetForm); + } + + return budgetForm; +}; + +// 处理市场预算表单 +const processMarketBudgetForm = async (status: string): Promise => { + await updateBudgetTable(); + + const budgetDetailData = budgetTableRef.value?.budgetDetailData || []; + const budgetFormData = budgetTableRef.value?.budgetForm || {}; + + const budgetForm = { + ...budgetFormData, + budgetStatus: getBudgetStatus(status), + flowStatus: getFlowStatus(status), + managerName: findUserName(userList.value, budgetFormData.managerId), + productManagerName: findUserName(userList.value, budgetFormData.productManagerId), + erpBudgetDetailList: budgetDetailData, + // 初始化市场预算成本列表 + ...initializeMarketCostLists() + }; + + // 处理市场预算成本数据 + processMarketCostData(budgetForm); + + // 如果有预算ID,设置要删除的列表 + if (budgetForm.budgetId) { + setMarketToDeletedLists(budgetForm); + } + + return budgetForm; +}; + +// 辅助函数 +const getBudgetStatus = (status: string): string => { + return status === 'draft' ? BUSINESS_STATUS.DRAFT : BUSINESS_STATUS.WAITING; +}; + +const getFlowStatus = (status: string): string => { + return status === 'draft' ? 'draft' : 'waiting'; +}; + +const initializeRdCostLists = () => ({ + erpRdBudgetEquipmentCostList: [], + erpRdBudgetMaterialCostList: [], + erpRdBudgetTravelCostList: [], + erpRdBudgetMeetingCostList: [], + erpRdBudgetExchangeCostList: [], + erpRdBudgetTechCostList: [], + erpRdBudgetLaborCostList: [], + erpRdBudgetLiteratureCostList: [], + erpRdBudgetTestingCostList: [], + erpRdBudgetOtherCostList: [] +}); + +const initializeMarketCostLists = () => ({ + erpBudgetMaterialCostList: [], + erpBudgetLaborCostList: [], + erpBudgetInstallCostList: [], + erpBudgetTravelCostList: [], + erpBudgetOtherCostList: [] +}); + +// 处理研发成本数据 +const processRdCostData = (budgetForm: budgetInfoForm) => { + // 设备成本 + if (assignIfChanged([], rdEquipmentCostRef.value?.equipmentData, oriRdBudgetEquipmentCostList.value).length) { + budgetForm.erpRdBudgetEquipmentCostList = rdEquipmentCostRef.value?.equipmentData || []; + } + + // 材料成本 + if (assignIfChanged([], rdMaterialCostRef.value?.allMaterialData, oriRdBudgetMaterialCostList.value).length) { + budgetForm.erpRdBudgetMaterialCostList = rdMaterialCostRef.value?.allMaterialData || []; + } + + // 差旅、会议、交流成本 + const travelMeetingExchangeRef = rdTravelMeetingExchangeRef.value; + if (assignIfChanged([], travelMeetingExchangeRef?.travelList, oriRdBudgetTravelCostList.value).length) { + budgetForm.erpRdBudgetTravelCostList = travelMeetingExchangeRef?.travelList || []; + } + if (assignIfChanged([], travelMeetingExchangeRef?.meetingList, oriRdBudgetMeetingCostList.value).length) { + budgetForm.erpRdBudgetMeetingCostList = travelMeetingExchangeRef?.meetingList || []; + } + if (assignIfChanged([], travelMeetingExchangeRef?.exchangeList, oriRdBudgetExchangeCostList.value).length) { + budgetForm.erpRdBudgetExchangeCostList = travelMeetingExchangeRef?.exchangeList || []; + } + + // 技术成本 + const laborServiceRef = rdLaborServiceRef.value; + budgetForm.erpRdBudgetTechCostList = [ + ...assignIfChanged([], laborServiceRef?.techConsultList, oriRdBudgetTechConsultCostList.value), + ...assignIfChanged([], laborServiceRef?.expertMeetingList, oriRdBudgetExpertMeetingCostList.value), + ...assignIfChanged([], laborServiceRef?.expertCommList, oriRdBudgetExpertCommCostList.value) + ]; + + // 劳务成本 + budgetForm.erpRdBudgetLaborCostList = [ + ...assignIfChanged([], laborServiceRef?.laborList, oriRdBudgetLaborCostList.value), + ...assignIfChanged([], laborServiceRef?.serviceList, oriRdBudgetServiceCostList.value) + ]; + + // 文献成本 + const literatureCostRef = rdLiteratureCostRef.value; + budgetForm.erpRdBudgetLiteratureCostList = [ + ...assignIfChanged([], literatureCostRef?.materialsList, oriRdBudgetLiteratureMaterialCostList.value), + ...assignIfChanged([], literatureCostRef?.softwareList, oriRdBudgetLiteratureSofwareCostList.value), + literatureCostRef?.literatureRetrieval || {} + ].filter((item) => Object.keys(item).length > 0); + + // 测试成本 + if (assignIfChanged([], rdTestingCostRef.value?.testData, oriRdBudgetTestingCostList.value).length) { + budgetForm.erpRdBudgetTestingCostList = rdTestingCostRef.value?.testData || []; + } + + // 其他成本 + if (assignIfChanged([], rdOtherCostRef.value?.otherCostList, oriRdBudgetOtherCostList.value).length) { + budgetForm.erpRdBudgetOtherCostList = rdOtherCostRef.value?.otherCostList || []; + } +}; + +// 设置研发删除列表 +const setRdToDeletedLists = (budgetForm: budgetInfoForm) => { + const refs = { + equipment: rdEquipmentCostRef.value, + material: rdMaterialCostRef.value, + travelMeetingExchange: rdTravelMeetingExchangeRef.value, + laborService: rdLaborServiceRef.value, + literature: rdLiteratureCostRef.value, + testing: rdTestingCostRef.value, + other: rdOtherCostRef.value + }; + + budgetForm.toDeletedRdEquipmentCostIdList = refs.equipment?.toDeletedEquipmentCostIdList; + budgetForm.toDeletedRdMaterialCostIdList = refs.material?.toDeletedMaterialCostIdList; + budgetForm.toDeletedRdTravelCostIdList = refs.travelMeetingExchange?.toDeletedTravelCostIdList; + budgetForm.toDeletedRdMeetingCostIdList = refs.travelMeetingExchange?.toDeletedMeetingCostIdList; + budgetForm.toDeletedRdExchangeCostIdList = refs.travelMeetingExchange?.toDeletedExchangeCostIdList; + budgetForm.toDeletedRdTechCostIdList = refs.laborService?.toDeletedTechCostIdList; + budgetForm.toDeletedRdLaborCostIdList = refs.laborService?.toDeletedLaborCostIdList; + budgetForm.toDeletedRdLiteratureCostIdList = refs.literature?.toDeletedLiteratureCostIdList; + budgetForm.toDeletedRdTestingCostIdList = refs.testing?.toDeletedTestCostIdList; + budgetForm.toDeletedRdOtherCostIdList = refs.other?.toDeletedOtherCostIdList; +}; + +// 处理市场成本数据 +const processMarketCostData = (budgetForm: budgetInfoForm) => { + const assignCostData = (targetKey: keyof budgetInfoForm, sourceData: any[], originalData: any[]) => { + if (!deepEqualArrays(originalData, sourceData)) { + budgetForm[targetKey] = sourceData || []; + } + }; + + assignCostData('erpBudgetMaterialCostList', materialCostRef.value?.budgetMaterialCostList, oriBudgetMaterialCostList.value); + assignCostData('erpBudgetLaborCostList', laborCostRef.value?.budgetLaborCostList, oriBudgetLaborCostList.value); + assignCostData('erpBudgetInstallCostList', installationCostRef.value?.installCostList, oriBudgetInstallCostList.value); + assignCostData('erpBudgetTravelCostList', travelCostRef.value?.travelCostList, oriBudgetTravelCostList.value); + assignCostData('erpBudgetOtherCostList', otherCostRef.value?.otherCostList, oriBudgetOtherCostList.value); +}; + +// 设置市场删除列表 +const setMarketToDeletedLists = (budgetForm: budgetInfoForm) => { + budgetForm.toDeletedMaterialCostIdList = materialCostRef.value?.toDeletedMaterialCostIdList; + budgetForm.toDeletedLaborCostIdList = laborCostRef.value?.toDeletedLaborCostIdList; + budgetForm.toDeletedInstallCostIdList = installationCostRef.value?.toDeletedInstallCostIdList; + budgetForm.toDeletedTravelCostIdList = travelCostRef.value?.toDeletedTravelCostIdList; + budgetForm.toDeletedOtherCostIdList = otherCostRef.value?.toDeletedOtherCostIdList; +}; + +// 保存成功处理 +const handleSaveSuccess = () => { + const obj = { + path: '/budget/erp/budgetInfo', + query: { + t: Date.now(), + pageNum: routeParams.value.pageNum + } + }; + proxy?.$tab.closeOpenPage(obj); +}; + // 关闭页面 const handleClose = () => { // 可以添加确认对话框 @@ -748,9 +885,6 @@ const route = useRoute(); const budgetId = ref(); // 初始化 onMounted(() => { - // 可以在这里加载初始数据 - routeParams.value.type = 'add'; - nextTick(async () => { // 获取路由参数 routeParams.value = route.query; @@ -760,7 +894,8 @@ onMounted(() => { const res = await getErpBudgetInfo(budgetId.value); Object.assign(searchForm, res.data); -console.log(res.data) + console.log(res.data); + await getUsers(); if (searchForm.projectCategory === PROJECT_CATEGORY.RD || searchForm.projectCategory === PROJECT_CATEGORY.PRE_PRODUCTION) { oriRdBudgetEquipmentCostList.value = JSON.parse(JSON.stringify(res.data.erpRdBudgetEquipmentCostList)); @@ -891,7 +1026,7 @@ console.log(res.data) // isCodeGenerated.value = false; // } } - projectSearchForm.projectCategory = searchForm.projectCategory; + queryParams.value.projectCategory = searchForm.projectCategory; }); }); @@ -915,6 +1050,7 @@ import { rdBudgetLaborCostVO } from '@/api/oa/erp/budgetInfo/rd/rdBudgetLaborCos import { rdBudgetLiteratureCostVO } from '@/api/oa/erp/budgetInfo/rd/rdBudgetLiteratureCost/types'; import { rdBudgetTestingCostVO } from '@/api/oa/erp/budgetInfo/rd/rdBudgetTestingCost/types'; import { rdBudgetOtherCostVO } from '@/api/oa/erp/budgetInfo/rd/rdBudgetOtherCost/types'; +import { UserVO } from '@/api/system/user/types'; // 路由参数 const routeParams = ref>({}); // 审批相关组件引用 diff --git a/src/views/oa/erp/budgetInfo/index.vue b/src/views/oa/erp/budgetInfo/index.vue index 5efa6e4..c48a620 100644 --- a/src/views/oa/erp/budgetInfo/index.vue +++ b/src/views/oa/erp/budgetInfo/index.vue @@ -20,10 +20,14 @@ - + + + + + + - 搜索 重置 @@ -42,19 +46,19 @@ 新增研发项目预算 - - 修改 - - - - 删除 - - - - 导出 - + + + + + + + + + + + + +
@@ -62,31 +66,83 @@ - + - - - - - - - - + + + + + + + + + + + + - - + + + + + + @@ -107,7 +163,9 @@ import { reactive } from 'vue'; const { proxy } = getCurrentInstance() as ComponentInternalInstance; -const { project_category } = toRefs(proxy?.useDict('project_category')); +const { project_category, budget_status, wf_business_status } = toRefs( + proxy?.useDict('project_category', 'budget_status', 'wf_business_status') +); const budgetInfoList = ref([]); const userList = ref([]); @@ -139,7 +197,7 @@ const columns = ref([ { key: 7, label: `项目名称`, visible: true }, { key: 8, label: `项目经理`, visible: true }, { key: 9, label: `项目经理姓名`, visible: true }, - { key: 10, label: `产品经理,关联sys_user`, visible: true }, + { key: 10, label: `产品经理,关联sys_user`, visible: false }, { key: 11, label: `产品经理姓名`, visible: true }, { key: 12, label: `审核(评审组长)ID,关联sys_user`, visible: true }, { key: 13, label: `审核(评审组长)姓名`, visible: true }, @@ -245,6 +303,18 @@ const PROJECT_CATEGORY = reactive({ PRE_PRODUCTION: '4' //预投 }); +const BUDGET_STATUS = reactive({ + DRAFT: '1', //暂存,草稿 + AUDITING: '2', //审批中 + AVAILABLE: '3' //可用 +}); + +// 格式化百分比 +const formatRate = (value: number) => { + if (!value) return ''; + return value + '%'; +}; + /** 查询用户列表 */ const getUsers = async () => { const res = await getUserList({}); @@ -320,40 +390,50 @@ const handleAddRd = () => { }); }; -/** 修改按钮操作 */ -const handleUpdate = async (row?: budgetInfoVO) => { - const params = { pageNum: queryParams.value.pageNum, type: 'update', id: row.budgetId }; - proxy.$tab.openPage('/oa/erp/budgetInfo/edit', '项目预算申请', params); -}; - -/** 提交按钮 */ -const submitForm = () => { - budgetInfoFormRef.value?.validate(async (valid: boolean) => { - if (valid) { - buttonLoading.value = true; - if (form.value.budgetId) { - await updateErpBudgetInfo(form.value).finally(() => (buttonLoading.value = false)); - } else { - await addErpBudgetInfo(form.value).finally(() => (buttonLoading.value = false)); - } - proxy?.$modal.msgSuccess('操作成功'); - dialog.visible = false; - await getList(); +/** 查询、修改和变更按钮操作 */ +const handleUpdate = async (row?: budgetInfoVO, type?: string) => { + router.push({ + path: '/budget/budget-add/index', + query: { + type: type, + id: row.budgetId, + pageNum: queryParams.value.pageNum, + projectCategory: row.projectCategory } }); + + // const params = { pageNum: queryParams.value.pageNum, type: 'update', id: row.budgetId };//用此方法,在打开此菜单后,在每激活一次都要重新加载一次,需要在router中设置不需要缓存 + // proxy.$tab.openPage('/oa/erp/budgetInfo/edit', '项目预算申请', params); +}; + +/** 修改按钮操作 */ +const handleView = async (row?: budgetInfoVO) => { + router.push({ + path: '/budget/budget-add/index', + query: { + type: 'view', + id: row.budgetId, + pageNum: queryParams.value.pageNum, + projectCategory: row.projectCategory + } + }); + + // const params = { pageNum: queryParams.value.pageNum, type: 'update', id: row.budgetId };//用此方法,在打开此菜单后,在每激活一次都要重新加载一次,需要在router中设置不需要缓存 + // proxy.$tab.openPage('/oa/erp/budgetInfo/edit', '项目预算申请', params); }; /** 删除按钮操作 */ const handleDelete = async (row?: budgetInfoVO) => { const _budgetIds = row?.budgetId || ids.value; - await proxy?.$modal.confirm('是否确认删除项目预算编号为"' + _budgetIds + '"的数据项?').finally(() => (loading.value = false)); + const projectName = row.projectName; + await proxy?.$modal.confirm('是否确认删除项目名称为"' + projectName + '"的数据项?').finally(() => (loading.value = false)); await delErpBudgetInfo(_budgetIds); proxy?.$modal.msgSuccess('删除成功'); await getList(); }; /** 导出按钮操作 */ -const handleExport = () => { +const handleExport = (row?: budgetInfoVO) => { proxy?.download( 'oa/erp/budgetInfo/export', { @@ -367,4 +447,8 @@ onMounted(() => { getList(); getUsers(); }); + +onActivated(() => { + getList(); +}); diff --git a/src/views/oa/erp/budgetInfo/market/BudgetTable.vue b/src/views/oa/erp/budgetInfo/market/BudgetTable.vue index c537415..ddd3528 100644 --- a/src/views/oa/erp/budgetInfo/market/BudgetTable.vue +++ b/src/views/oa/erp/budgetInfo/market/BudgetTable.vue @@ -8,12 +8,12 @@ - + - + @@ -36,7 +36,7 @@ - + @@ -289,28 +289,17 @@ const updateBudgetDetailData = (type: string, budgetCost: number, reduceBudgetCo } }; -// 更新预算成本 -const updateBudgetCost = (row: any) => { - row.budgetCost = parseFloat(row.budgetCost) || 0; -}; -// 更新降成本后预算成本 -const updateReduceBudgetCost = (row: any) => { - row.reduceBudgetCost = parseFloat(row.reduceBudgetCost) || 0; -}; - -// 暴露方法给父组件 -defineExpose({ - updateBudgetDetailData, - budgetForm, - budgetDetailData -}); // 接收项目信息 const props = defineProps({ projectInfo: { type: Object, default: () => ({}) + }, + userList: { + type: Object, + default: () => ({}) } }); @@ -328,13 +317,6 @@ watch( { deep: true, immediate: true } ); -const userList = ref([]); -/** 查询用户列表 */ -const getUsers = async () => { - const res = await getUserList({}); - userList.value = res.data; -}; - const computedAmountAndRate = async () => { if (budgetForm.contractAmount) { budgetForm.netContractAmount = (budgetForm.contractAmount / 1.13).toFixed(2); @@ -347,8 +329,11 @@ const computedAmountAndRate = async () => { } }; -onMounted(() => { - getUsers(); +// 暴露方法给父组件 +defineExpose({ + updateBudgetDetailData, + budgetForm, + budgetDetailData }); // budgetForm.netContractAmount = computed(() => { diff --git a/src/views/oa/erp/budgetInfo/market/InstallationCost.vue b/src/views/oa/erp/budgetInfo/market/InstallationCost.vue index 8946c01..a1b7039 100644 --- a/src/views/oa/erp/budgetInfo/market/InstallationCost.vue +++ b/src/views/oa/erp/budgetInfo/market/InstallationCost.vue @@ -224,8 +224,6 @@ const handleAddRows = () => { reduceProposal: '' }) } - showAddDialog.value = false - addRowCount.value = 1 } // 删除单行 diff --git a/src/views/oa/erp/budgetInfo/market/LaborCost.vue b/src/views/oa/erp/budgetInfo/market/LaborCost.vue index 012f791..5206d2a 100644 --- a/src/views/oa/erp/budgetInfo/market/LaborCost.vue +++ b/src/views/oa/erp/budgetInfo/market/LaborCost.vue @@ -276,8 +276,6 @@ const handleAddRows = () => { reduceProposal: '' }); } - showAddDialog.value = false; - addRowCount.value = 1; }; // 删除单行 diff --git a/src/views/oa/erp/budgetInfo/market/MaterialCost.vue b/src/views/oa/erp/budgetInfo/market/MaterialCost.vue index e396572..f9fc132 100644 --- a/src/views/oa/erp/budgetInfo/market/MaterialCost.vue +++ b/src/views/oa/erp/budgetInfo/market/MaterialCost.vue @@ -115,16 +115,35 @@
- - - - - - - - - - + +
+ + + + + + + + + + + + + + + + + + + + + 搜索 + 重置 + + + +
+
- - - - + + + +