|
|
|
|
|
<template>
|
|
|
|
|
|
<div class="project-budget-container">
|
|
|
|
|
|
<!-- 审批按钮组件 -->
|
|
|
|
|
|
<approvalButton
|
|
|
|
|
|
@submitForm="handleSave"
|
|
|
|
|
|
@approvalVerifyOpen="approvalVerifyOpen"
|
|
|
|
|
|
@handleApprovalRecord="handleApprovalRecord"
|
|
|
|
|
|
:buttonLoading="buttonLoading"
|
|
|
|
|
|
:id="searchForm.budgetId"
|
|
|
|
|
|
:status="searchForm.flowStatus"
|
|
|
|
|
|
:pageType="routeParams.type"
|
|
|
|
|
|
:mode="false"
|
|
|
|
|
|
/>
|
|
|
|
|
|
<!-- 顶部表单区域 -->
|
|
|
|
|
|
<el-card class="mb-6">
|
|
|
|
|
|
<el-form :model="searchForm" label-width="80px">
|
|
|
|
|
|
<el-row :gutter="20">
|
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
|
<el-form-item label="项目名称">
|
|
|
|
|
|
<el-input v-model="searchForm.projectName" placeholder="请选择项目名称" readonly @click="showProjectSelectDialog">
|
|
|
|
|
|
<template #append>
|
|
|
|
|
|
<el-icon @click="showProjectSelectDialog">
|
|
|
|
|
|
<el-icon :size="16">
|
|
|
|
|
|
<Search />
|
|
|
|
|
|
</el-icon>
|
|
|
|
|
|
</el-icon>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="6">
|
|
|
|
|
|
<el-form-item label="项目号">
|
|
|
|
|
|
<el-input v-model="searchForm.projectCode" placeholder="项目号" readonly />
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
</el-col>
|
|
|
|
|
|
<!-- <el-col :span="12" class="text-right">-->
|
|
|
|
|
|
<!-- <el-button type="primary" @click="exportBudget" size="default">-->
|
|
|
|
|
|
<!-- <el-icon><Download /></el-icon>-->
|
|
|
|
|
|
<!-- 导出-->
|
|
|
|
|
|
<!-- </el-button>-->
|
|
|
|
|
|
<!-- </el-col>-->
|
|
|
|
|
|
</el-row>
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 中间标签页区域 -->
|
|
|
|
|
|
<!-- 研发项目预算 -->
|
|
|
|
|
|
<el-card class="mb-6" v-if="searchForm.projectCategory === PROJECT_CATEGORY.RD">
|
|
|
|
|
|
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
|
|
|
|
|
<el-tab-pane label="预算表" name="budgetTable">
|
|
|
|
|
|
<RdBudgetTable ref="rdBudgetTableRef" :projectId="searchForm.projectId" :costData="costData" :projectInfo="projectInfo" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="设备费" name="equipmentCost">
|
|
|
|
|
|
<RdEquipmentCost ref="rdEquipmentCostRef" :projectId="searchForm.projectId" @update="handleEquipmentCostUpdate" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="材料费" name="materialCost">
|
|
|
|
|
|
<RdMaterialCost ref="rdMaterialCostRef" :projectId="searchForm.projectId" @update="handleMaterialCostUpdate" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="会议差旅交流" name="travelMeetingExchange">
|
|
|
|
|
|
<RdTravelMeetingExchange ref="rdTravelMeetingExchangeRef" :projectId="searchForm.projectId" @update="handleTravelMeetingExchangeUpdate" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="人工劳务咨询" name="laborService">
|
|
|
|
|
|
<RdLaborService ref="rdLaborServiceRef" :projectId="searchForm.projectId" @update="handleLaborServiceUpdate" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="资料文献费" name="literatureCost">
|
|
|
|
|
|
<RdLiteratureCost ref="rdLiteratureCostRef" :projectId="searchForm.projectId" @update="handleLiteratureCostUpdate" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="测试化验费" name="testingCost">
|
|
|
|
|
|
<RdTestingCost ref="rdTestingCostRef" :projectId="searchForm.projectId" @update="handleTestingCostUpdate" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="其他" name="otherCost">
|
|
|
|
|
|
<RdOtherCost ref="rdOtherCostRef" :projectId="searchForm.projectId" @update="handleOtherCostUpdate" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
</el-tabs>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 市场项目预算 -->
|
|
|
|
|
|
<el-card class="tab-card" shadow="never" v-if="searchForm.projectCategory === PROJECT_CATEGORY.MARKET">
|
|
|
|
|
|
<el-tabs v-model="activeTab" @tab-click="handleTabClick">
|
|
|
|
|
|
<el-tab-pane label="预算表" name="budget">
|
|
|
|
|
|
<BudgetTable ref="budgetTableRef" :projectInfo="projectInfo" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="材料费" name="material">
|
|
|
|
|
|
<MaterialCost ref="materialCostRef" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="人工费" name="labor">
|
|
|
|
|
|
<LaborCost ref="laborCostRef" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="安装费" name="installation">
|
|
|
|
|
|
<InstallationCost ref="installationCostRef" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="差旅费" name="travel">
|
|
|
|
|
|
<TravelCost ref="travelCostRef" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
<el-tab-pane label="其他" name="other">
|
|
|
|
|
|
<OtherCost ref="otherCostRef" />
|
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
</el-tabs>
|
|
|
|
|
|
</el-card>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 项目选择弹窗 -->
|
|
|
|
|
|
<el-dialog v-model="projectSelectDialogVisible" title="选择项目" width="800px">
|
|
|
|
|
|
<div class="mb-4">
|
|
|
|
|
|
<el-input v-model="projectSearchForm.keyword" placeholder="请输入项目名称或项目号" clearable @input="handleProjectSearch">
|
|
|
|
|
|
<template #append>
|
|
|
|
|
|
<el-button @click="handleProjectSearch">搜索</el-button>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-input>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<el-table :data="projectList" border @row-click="selectProject" style="width: 100%" v-loading="projectLoading" highlight-current-row>
|
|
|
|
|
|
<el-table-column prop="projectCode" label="项目号" width="150" />
|
|
|
|
|
|
<el-table-column prop="projectName" label="项目名称" min-width="180" />
|
|
|
|
|
|
<el-table-column prop="businessDirection" label="业务方向" width="120">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<dict-tag :options="business_direction" :value="scope.row.businessDirection" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="projectCategory" label="项目类别" width="160">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<dict-tag :options="project_category" :value="scope.row.projectCategory" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
<el-table-column prop="projectStatus" label="项目状态" width="100">
|
|
|
|
|
|
<template #default="scope">
|
|
|
|
|
|
<dict-tag :options="project_status" :value="scope.row.projectStatus" />
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
|
|
|
|
<div class="mt-4 flex justify-between items-center">
|
|
|
|
|
|
<pagination
|
|
|
|
|
|
v-show="projectTotal > 0"
|
|
|
|
|
|
:total="projectTotal"
|
|
|
|
|
|
v-model:page="projectSearchForm.pageNum"
|
|
|
|
|
|
v-model:limit="projectSearchForm.pageSize"
|
|
|
|
|
|
@pagination="getProjectList"
|
|
|
|
|
|
/>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
<span class="dialog-footer">
|
|
|
|
|
|
<el-button @click="projectSelectDialogVisible = false">取消</el-button>
|
|
|
|
|
|
<el-button type="primary" @click="confirmProjectSelection">确定</el-button>
|
|
|
|
|
|
</span>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 提交审批组件 -->
|
|
|
|
|
|
<submitVerify ref="submitVerifyRef" :task-variables="taskVariables" @submit-callback="submitCallback" />
|
|
|
|
|
|
<!-- 审批记录 -->
|
|
|
|
|
|
<approvalRecord ref="approvalRecordRef" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import { ref, reactive, onMounted } 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';
|
|
|
|
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
|
const { business_direction, project_status, project_category } = toRefs<any>(
|
|
|
|
|
|
proxy?.useDict('business_direction', 'project_status', 'project_category')
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
//研发项目tab
|
|
|
|
|
|
import RdBudgetTable from './rd/BudgetTable.vue';
|
|
|
|
|
|
import RdEquipmentCost from './rd/EquipmentCost.vue';
|
|
|
|
|
|
import RdMaterialCost from './rd/MaterialCost.vue';
|
|
|
|
|
|
import RdTravelMeetingExchange from './rd/TravelMeetingExchange.vue';
|
|
|
|
|
|
import RdLaborService from './rd/LaborService.vue';
|
|
|
|
|
|
import RdLiteratureCost from './rd/LiteratureCost.vue';
|
|
|
|
|
|
import RdTestingCost from './rd/TestingCost.vue';
|
|
|
|
|
|
import RdOtherCost from './rd/OtherCost.vue';
|
|
|
|
|
|
|
|
|
|
|
|
//市场项目tab
|
|
|
|
|
|
import BudgetTable from './market/BudgetTable.vue';
|
|
|
|
|
|
import MaterialCost from './market/MaterialCost.vue';
|
|
|
|
|
|
import LaborCost from './market/LaborCost.vue';
|
|
|
|
|
|
import InstallationCost from './market/InstallationCost.vue';
|
|
|
|
|
|
import TravelCost from './market/TravelCost.vue';
|
|
|
|
|
|
import OtherCost from './market/OtherCost.vue';
|
|
|
|
|
|
|
|
|
|
|
|
// 定义成本数据类型
|
|
|
|
|
|
interface CostData {
|
|
|
|
|
|
equipmentCost: number;
|
|
|
|
|
|
materialCost: number;
|
|
|
|
|
|
travelCost: number;
|
|
|
|
|
|
meetingCost: number;
|
|
|
|
|
|
internationalExchangeCost: number;
|
|
|
|
|
|
consultingDevelopmentCost: number;
|
|
|
|
|
|
laborCost: number;
|
|
|
|
|
|
serviceCost: number;
|
|
|
|
|
|
literatureCost: number;
|
|
|
|
|
|
testingCost: number;
|
|
|
|
|
|
otherCost: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 研发项目预算tab组件引用
|
|
|
|
|
|
const rdBudgetTableRef = ref();
|
|
|
|
|
|
const rdEquipmentCostRef = ref();
|
|
|
|
|
|
const rdMaterialCostRef = ref();
|
|
|
|
|
|
const rdTravelMeetingExchangeRef = ref();
|
|
|
|
|
|
const rdLaborServiceRef = ref();
|
|
|
|
|
|
const rdLiteratureCostRef = ref();
|
|
|
|
|
|
const rdTestingCostRef = ref();
|
|
|
|
|
|
const rdOtherCostRef = ref();
|
|
|
|
|
|
|
|
|
|
|
|
// 市场项目预算tab组件引用
|
|
|
|
|
|
const budgetTableRef = ref();
|
|
|
|
|
|
const materialCostRef = ref();
|
|
|
|
|
|
const laborCostRef = ref();
|
|
|
|
|
|
const installationCostRef = ref();
|
|
|
|
|
|
const travelCostRef = ref();
|
|
|
|
|
|
const otherCostRef = ref();
|
|
|
|
|
|
|
|
|
|
|
|
const PROJECT_CATEGORY = reactive({
|
|
|
|
|
|
MARKET: '1', //销售市场项目
|
|
|
|
|
|
RD: '2' //研发项目
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const BUSINESS_STATUS = reactive({
|
|
|
|
|
|
DRAFT: '1', //暂存
|
|
|
|
|
|
WAITING: '2', //审批中
|
|
|
|
|
|
AVAILABLE: '3' //可用
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 响应式数据
|
|
|
|
|
|
// 将activeTab从computed改为ref,使其可变
|
|
|
|
|
|
const activeTab = ref('budget');
|
|
|
|
|
|
const buttonLoading = ref(false);
|
|
|
|
|
|
|
|
|
|
|
|
const searchForm = reactive({
|
|
|
|
|
|
budgetId: undefined,
|
|
|
|
|
|
projectId: '',
|
|
|
|
|
|
projectName: '',
|
|
|
|
|
|
projectCode: '',
|
|
|
|
|
|
projectCategory: '1',
|
|
|
|
|
|
flowStatus: ''
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 监听项目类别变化,更新默认标签页
|
|
|
|
|
|
watch(
|
|
|
|
|
|
() => searchForm.projectCategory,
|
|
|
|
|
|
(newCategory) => {
|
|
|
|
|
|
if (newCategory === PROJECT_CATEGORY.RD) {
|
|
|
|
|
|
activeTab.value = 'budgetTable';
|
|
|
|
|
|
} else if (newCategory === PROJECT_CATEGORY.MARKET) {
|
|
|
|
|
|
activeTab.value = 'budget';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// 项目信息
|
|
|
|
|
|
const projectInfo = reactive<ProjectInfoVO>({
|
|
|
|
|
|
projectName: '',
|
|
|
|
|
|
projectCode: '',
|
|
|
|
|
|
projectCategory: ''
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 市场预算成本数据
|
|
|
|
|
|
const marketCostData = reactive<CostData>({
|
|
|
|
|
|
materialCost: 0,
|
|
|
|
|
|
laborCost: 0,
|
|
|
|
|
|
installCost: 0,
|
|
|
|
|
|
travelCost: 0,
|
|
|
|
|
|
serviceCost: 0,
|
|
|
|
|
|
literatureCost: 0,
|
|
|
|
|
|
testingCost: 0,
|
|
|
|
|
|
otherCost: 0
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 研发预算成本数据
|
|
|
|
|
|
const costData = reactive<CostData>({
|
|
|
|
|
|
equipmentCost: 0,
|
|
|
|
|
|
materialCost: 0,
|
|
|
|
|
|
travelCost: 0,
|
|
|
|
|
|
meetingCost: 0,
|
|
|
|
|
|
internationalExchangeCost: 0,
|
|
|
|
|
|
consultingDevelopmentCost: 0,
|
|
|
|
|
|
laborCost: 0,
|
|
|
|
|
|
serviceCost: 0,
|
|
|
|
|
|
literatureCost: 0,
|
|
|
|
|
|
testingCost: 0,
|
|
|
|
|
|
otherCost: 0
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 项目选择弹窗相关
|
|
|
|
|
|
const projectSelectDialogVisible = ref(false);
|
|
|
|
|
|
const selectedProject = ref<ProjectInfoVO | null>(null);
|
|
|
|
|
|
const projectList = ref<ProjectInfoVO[]>([]);
|
|
|
|
|
|
const projectTotal = ref(0);
|
|
|
|
|
|
const projectLoading = ref(true);
|
|
|
|
|
|
const projectSearchForm = reactive({
|
|
|
|
|
|
keyword: '',
|
|
|
|
|
|
pageNum: 1,
|
|
|
|
|
|
pageSize: 10
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
/** 查询项目信息列表 */
|
|
|
|
|
|
const getProjectList = async () => {
|
|
|
|
|
|
projectLoading.value = true;
|
|
|
|
|
|
const res = await listProjectInfo(projectSearchForm);
|
|
|
|
|
|
console.log(res);
|
|
|
|
|
|
projectList.value = res.rows;
|
|
|
|
|
|
projectTotal.value = res.total;
|
|
|
|
|
|
projectLoading.value = false;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 标签页切换处理
|
|
|
|
|
|
const handleTabClick = (tab: any) => {
|
|
|
|
|
|
activeTab.value = tab.paneName;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 显示项目选择弹窗
|
|
|
|
|
|
const showProjectSelectDialog = () => {
|
|
|
|
|
|
projectSelectDialogVisible.value = true;
|
|
|
|
|
|
selectedProject.value = undefined;
|
|
|
|
|
|
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;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 确认项目选择
|
|
|
|
|
|
const confirmProjectSelection = () => {
|
|
|
|
|
|
if (!selectedProject.value) {
|
|
|
|
|
|
ElMessage.warning('请选择一个项目');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新项目信息
|
|
|
|
|
|
searchForm.projectId = selectedProject.value.projectId;
|
|
|
|
|
|
searchForm.projectName = selectedProject.value.projectName;
|
|
|
|
|
|
searchForm.projectCode = selectedProject.value.projectCode;
|
|
|
|
|
|
searchForm.projectCategory = selectedProject.value.projectCategory;
|
|
|
|
|
|
|
|
|
|
|
|
projectInfo.projectId = selectedProject.value.projectId;
|
|
|
|
|
|
projectInfo.projectName = selectedProject.value.projectName;
|
|
|
|
|
|
projectInfo.projectCode = selectedProject.value.projectCode;
|
|
|
|
|
|
projectInfo.projectCategory = selectedProject.value.projectCategory;
|
|
|
|
|
|
|
|
|
|
|
|
projectSelectDialogVisible.value = false;
|
|
|
|
|
|
// ElMessage.success('项目选择成功');
|
|
|
|
|
|
|
|
|
|
|
|
// 重置所有成本数据
|
|
|
|
|
|
Object.keys(costData).forEach((key) => {
|
|
|
|
|
|
costData[key as keyof CostData] = 0;
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 设备费更新处理
|
|
|
|
|
|
const handleEquipmentCostUpdate = (data: { equipmentCost: number }) => {
|
|
|
|
|
|
costData.equipmentCost = data.equipmentCost;
|
|
|
|
|
|
if (rdBudgetTableRef.value) {
|
|
|
|
|
|
rdBudgetTableRef.value.updateCostData(costData);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 材料费更新处理
|
|
|
|
|
|
const handleMaterialCostUpdate = (data: { mainMaterialCost: number; otherMaterialCost: number; totalAmount: number }) => {
|
|
|
|
|
|
costData.materialCost = data.materialCost;
|
|
|
|
|
|
if (rdBudgetTableRef.value) {
|
|
|
|
|
|
rdBudgetTableRef.value.updateCostData(costData);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 会议差旅交流更新处理
|
|
|
|
|
|
const handleTravelMeetingExchangeUpdate = (data: { travelCost: number; meetingCost: number; internationalExchangeCost: number }) => {
|
|
|
|
|
|
costData.travelCost = data.travelCost;
|
|
|
|
|
|
costData.meetingCost = data.meetingCost;
|
|
|
|
|
|
costData.internationalExchangeCost = data.internationalExchangeCost;
|
|
|
|
|
|
if (rdBudgetTableRef.value) {
|
|
|
|
|
|
rdBudgetTableRef.value.updateCostData(costData);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 人工劳务咨询更新处理
|
|
|
|
|
|
const handleLaborServiceUpdate = (data: { techConsultTotal: number; laborTotal: number; serviceTotal: number }) => {
|
|
|
|
|
|
costData.consultingDevelopmentCost = data.techConsultTotal;
|
|
|
|
|
|
costData.laborCost = data.laborTotal;
|
|
|
|
|
|
costData.serviceCost = data.serviceTotal;
|
|
|
|
|
|
if (rdBudgetTableRef.value) {
|
|
|
|
|
|
rdBudgetTableRef.value.updateCostData(costData);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 资料文献费更新处理
|
|
|
|
|
|
const handleLiteratureCostUpdate = (data: { literatureCost: number }) => {
|
|
|
|
|
|
costData.literatureCost = data.literatureCost;
|
|
|
|
|
|
if (rdBudgetTableRef.value) {
|
|
|
|
|
|
rdBudgetTableRef.value.updateCostData(costData);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 测试化验费更新处理
|
|
|
|
|
|
const handleTestingCostUpdate = (data: { totalAmount: number }) => {
|
|
|
|
|
|
costData.testingCost = data.totalAmount;
|
|
|
|
|
|
if (rdBudgetTableRef.value) {
|
|
|
|
|
|
rdBudgetTableRef.value.updateCostData(costData);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 其他费用更新处理
|
|
|
|
|
|
const handleOtherCostUpdate = (data: { totalAmount: number }) => {
|
|
|
|
|
|
costData.otherCost = data.totalAmount;
|
|
|
|
|
|
if (rdBudgetTableRef.value) {
|
|
|
|
|
|
rdBudgetTableRef.value.updateCostData(costData);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 更新市场项目预算预算表数据
|
|
|
|
|
|
const updateBudgetTable = () => {
|
|
|
|
|
|
if (!budgetTableRef.value) return;
|
|
|
|
|
|
|
|
|
|
|
|
// 获取各tab页的合计数据
|
|
|
|
|
|
const materialTotal = materialCostRef.value?.getTotalAmount() || { budgetAmount: 0, reducedAmount: 0 };
|
|
|
|
|
|
const laborTotal = laborCostRef.value?.getTotalAmount() || { budgetAmount: 0, reducedAmount: 0 };
|
|
|
|
|
|
const installationTotal = installationCostRef.value?.getTotalAmount() || { budgetAmount: 0, reducedAmount: 0 };
|
|
|
|
|
|
const travelTotal = travelCostRef.value?.getTotalAmount() || { budgetAmount: 0, reducedAmount: 0 };
|
|
|
|
|
|
const otherTotal = otherCostRef.value?.getTotalAmount() || { budgetAmount: 0, reducedAmount: 0 };
|
|
|
|
|
|
|
|
|
|
|
|
// 更新预算表
|
|
|
|
|
|
budgetTableRef.value.updateBudgetDetailData('材料费', materialTotal.budgetAmount, materialTotal.reducedAmount);
|
|
|
|
|
|
budgetTableRef.value.updateBudgetDetailData('人工费', laborTotal.budgetAmount, laborTotal.reducedAmount);
|
|
|
|
|
|
budgetTableRef.value.updateBudgetDetailData('安装费', installationTotal.budgetAmount, installationTotal.reducedAmount);
|
|
|
|
|
|
budgetTableRef.value.updateBudgetDetailData('差旅费', travelTotal.budgetAmount, travelTotal.reducedAmount);
|
|
|
|
|
|
budgetTableRef.value.updateBudgetDetailData('其他费用', otherTotal.budgetAmount, otherTotal.reducedAmount);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 新增一个方法用于在tab切换时更新预算数据
|
|
|
|
|
|
const updateBudgetDataOnTabChange = () => {
|
|
|
|
|
|
nextTick(() => {
|
|
|
|
|
|
if (searchForm.projectCategory === PROJECT_CATEGORY.RD) {
|
|
|
|
|
|
// 确保调用更新方法时传递正确的参数
|
|
|
|
|
|
if (rdEquipmentCostRef.value?.getTotalAmount) {
|
|
|
|
|
|
handleEquipmentCostUpdate({ equipmentCost: rdEquipmentCostRef.value.getTotalAmount() });
|
|
|
|
|
|
}
|
|
|
|
|
|
if (rdMaterialCostRef.value?.getTotalAmount) {
|
|
|
|
|
|
const materialTotal = rdMaterialCostRef.value.getTotalAmount();
|
|
|
|
|
|
handleMaterialCostUpdate({
|
|
|
|
|
|
mainMaterialCost: materialTotal.mainMaterialCost || 0,
|
|
|
|
|
|
otherMaterialCost: materialTotal.otherMaterialCost || 0,
|
|
|
|
|
|
materialCost: (materialTotal.mainMaterialCost || 0) + (materialTotal.otherMaterialCost || 0)
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
// 其他方法类似处理...
|
|
|
|
|
|
} else if (searchForm.projectCategory === PROJECT_CATEGORY.MARKET) {
|
|
|
|
|
|
updateBudgetTable();
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 监听tab切换,更新预算表数据
|
|
|
|
|
|
watch(activeTab, () => {
|
|
|
|
|
|
updateBudgetDataOnTabChange();
|
|
|
|
|
|
});
|
|
|
|
|
|
// 导出预算
|
|
|
|
|
|
const exportBudget = () => {
|
|
|
|
|
|
if (!searchForm.projectId) {
|
|
|
|
|
|
ElMessage.warning('请先选择项目');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ElMessage.success('导出预算成功');
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 保存预算
|
|
|
|
|
|
const handleSave = async (status: string, mode: boolean) => {
|
|
|
|
|
|
if (!searchForm.projectId) {
|
|
|
|
|
|
ElMessage.warning('请先选择项目');
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (searchForm.projectCategory === PROJECT_CATEGORY.RD) {
|
|
|
|
|
|
handleEquipmentCostUpdate();
|
|
|
|
|
|
handleMaterialCostUpdate();
|
|
|
|
|
|
handleTravelMeetingExchangeUpdate();
|
|
|
|
|
|
handleLaborServiceUpdate();
|
|
|
|
|
|
handleLiteratureCostUpdate();
|
|
|
|
|
|
handleTestingCostUpdate();
|
|
|
|
|
|
handleOtherCostUpdate();
|
|
|
|
|
|
} else if (searchForm.projectCategory === PROJECT_CATEGORY.MARKET) {
|
|
|
|
|
|
updateBudgetTable();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
// 收集所有标签页的数据
|
|
|
|
|
|
if (searchForm.projectCategory === PROJECT_CATEGORY.RD) {
|
|
|
|
|
|
const allData = {
|
|
|
|
|
|
projectId: searchForm.projectId,
|
|
|
|
|
|
costData: { ...costData },
|
|
|
|
|
|
equipmentCostData: rdEquipmentCostRef.value?.equipmentList || [],
|
|
|
|
|
|
materialCostData: rdMaterialCostRef.value?.materialList || [],
|
|
|
|
|
|
travelMeetingExchangeData: {
|
|
|
|
|
|
travelList: rdTravelMeetingExchangeRef.value?.travelList || [],
|
|
|
|
|
|
meetingList: rdTravelMeetingExchangeRef.value?.meetingList || [],
|
|
|
|
|
|
internationalExchangeList: rdTravelMeetingExchangeRef.value?.internationalExchangeList || []
|
|
|
|
|
|
},
|
|
|
|
|
|
laborServiceData: {
|
|
|
|
|
|
techConsultList: rdLaborServiceRef.value?.techConsultList || [],
|
|
|
|
|
|
expertMeetingList: rdLaborServiceRef.value?.expertMeetingList || [],
|
|
|
|
|
|
expertCommList: rdLaborServiceRef.value?.expertCommList || [],
|
|
|
|
|
|
laborList: rdLaborServiceRef.value?.laborList || [],
|
|
|
|
|
|
serviceList: rdLaborServiceRef.value?.serviceList || []
|
|
|
|
|
|
},
|
|
|
|
|
|
literatureCostData: {
|
|
|
|
|
|
materialsList: rdLiteratureCostRef.value?.materialsList || [],
|
|
|
|
|
|
literatureRetrieval: rdLiteratureCostRef.value?.literatureRetrieval || {},
|
|
|
|
|
|
softwareList: rdLiteratureCostRef.value?.softwareList || []
|
|
|
|
|
|
},
|
|
|
|
|
|
testingCostData: rdTestingCostRef.value?.testingList || [],
|
|
|
|
|
|
otherCostData: rdOtherCostRef.value?.otherCostList || []
|
|
|
|
|
|
};
|
|
|
|
|
|
} else if (searchForm.projectCategory === PROJECT_CATEGORY.MARKET) {
|
|
|
|
|
|
const budgetDetailData = budgetTableRef.value?.budgetDetailData;
|
|
|
|
|
|
let budgetForm = budgetTableRef.value?.budgetForm;
|
|
|
|
|
|
budgetForm.budgetStatus = status === 'draft' ? BUSINESS_STATUS.DRAFT : BUSINESS_STATUS.WAITING;
|
|
|
|
|
|
budgetForm.flowStatus = status === 'draft' ? 'draft' : 'waiting';
|
|
|
|
|
|
budgetForm.erpBudgetDetailList = budgetDetailData;
|
|
|
|
|
|
budgetForm.erpBudgetMaterialCostList = materialCostRef.value?.budgetMaterialCostList;
|
|
|
|
|
|
|
|
|
|
|
|
console.log(budgetForm);
|
|
|
|
|
|
|
|
|
|
|
|
buttonLoading.value = true;
|
|
|
|
|
|
if (budgetForm.budgetId) {
|
|
|
|
|
|
await updateErpBudgetInfo(budgetForm).finally(() => (buttonLoading.value = false));
|
|
|
|
|
|
} else {
|
|
|
|
|
|
await addErpBudgetInfo(budgetForm).finally(() => (buttonLoading.value = false));
|
|
|
|
|
|
}
|
|
|
|
|
|
proxy?.$modal.msgSuccess('操作成功');
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
ElMessage.error('保存失败');
|
|
|
|
|
|
console.error('保存失败:', error);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭页面
|
|
|
|
|
|
const handleClose = () => {
|
|
|
|
|
|
// 可以添加确认对话框
|
|
|
|
|
|
// 这里简单实现返回上一页
|
|
|
|
|
|
window.history.back();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const route = useRoute();
|
|
|
|
|
|
// 初始化
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
|
// 可以在这里加载初始数据
|
|
|
|
|
|
routeParams.value.type = 'add';
|
|
|
|
|
|
|
|
|
|
|
|
nextTick(async () => {
|
|
|
|
|
|
// 获取路由参数
|
|
|
|
|
|
routeParams.value = route.query;
|
|
|
|
|
|
const id = routeParams.value.id as string | number;
|
|
|
|
|
|
if (id && (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval')) {
|
|
|
|
|
|
proxy?.$modal.loading('正在加载数据,请稍后...');
|
|
|
|
|
|
const res = await getErpBudgetInfo(id);
|
|
|
|
|
|
Object.assign(searchForm, res.data);
|
|
|
|
|
|
Object.assign(budgetTableRef.value?.budgetForm,res.data)
|
|
|
|
|
|
|
|
|
|
|
|
proxy?.$modal.closeLoading();
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 新增模式:如果已有合同编号,禁用生成按钮
|
|
|
|
|
|
// if (form.value.contractCode) {
|
|
|
|
|
|
// isCodeGenerated.value = true;
|
|
|
|
|
|
// } else if (form.value.contractFlag === '1') {
|
|
|
|
|
|
// // 如果有合同但没有编号,允许生成
|
|
|
|
|
|
// isCodeGenerated.value = false;
|
|
|
|
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
import SubmitVerify from '@/components/Process/submitVerify.vue';
|
|
|
|
|
|
import ApprovalRecord from '@/components/Process/approvalRecord.vue';
|
|
|
|
|
|
import ApprovalButton from '@/components/Process/approvalButton.vue';
|
|
|
|
|
|
import { getContractInfo } from '@/api/oa/erp/contractInfo';
|
|
|
|
|
|
// 路由参数
|
|
|
|
|
|
const routeParams = ref<Record<string, any>>({});
|
|
|
|
|
|
// 审批相关组件引用
|
|
|
|
|
|
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
|
|
|
|
|
|
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
|
|
|
|
|
|
// 审批
|
|
|
|
|
|
const approvalVerifyOpen = async () => {
|
|
|
|
|
|
await submitVerifyRef.value.openDialog(routeParams.value.taskId);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 审批记录
|
|
|
|
|
|
const handleApprovalRecord = () => {
|
|
|
|
|
|
approvalRecordRef.value.init(searchForm.budgetId);
|
|
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
|
.project-budget-container {
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.dialog-footer {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
gap: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.justify-end {
|
|
|
|
|
|
justify-content: flex-end;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.gap-2 {
|
|
|
|
|
|
gap: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.gap-4 {
|
|
|
|
|
|
gap: 16px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.mb-6 {
|
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|