You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

287 lines
9.3 KiB
Vue

<template>
<div class="budget-table-container">
<h2 class="title-center">项目经费预算表</h2>
<el-form :model="rdBudgetInfoForm" label-width="120px" class="mb-4">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目名称">
<el-input v-model="rdBudgetInfoForm.projectName" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目号">
<el-input v-model="rdBudgetInfoForm.projectCode" disabled />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目预算期间">
<el-input v-model="rdBudgetInfoForm.duringOperation" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="金额单位">
<el-input v-model="rdBudgetInfoForm.unitName" disabled value="万元" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-table :data="budgetDetailData" style="width: 100%" border show-summary :summary-method="getSummaries">
<el-table-column prop="sortOrder" label="序号" width="80" />
<el-table-column prop="budgetItem" label="预算科目名称" />
<el-table-column prop="budgetCost" label="项目经费" width="260" align="right">
<template #default="scope">
<span>{{ formatNumber(scope.row.budgetCost) }}</span>
</template>
</el-table-column>
</el-table>
<el-form :model="footerForm" label-width="160px" class="mt-4">
<el-row>
<el-col :span="12">
<el-form-item label="编制(项目经理)">
<el-select v-model="footerForm.managerId" filterable placeholder="请选择项目经理" clearable>
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId"></el-option>
</el-select>
<!-- <el-select-->
<!-- v-model="footerForm.managerId"-->
<!-- placeholder="请选择项目经理"-->
<!-- filterable-->
<!-- clearable-->
<!-- :filter-method="(query) => (creatorSearchQuery = query)"-->
<!-- :remote="false"-->
<!-- >-->
<!-- <el-option v-for="user in filteredCreatorList" :key="user.id" :label="formatUserDisplay(user)" :value="user.name">-->
<!-- <div class="flex items-center">-->
<!-- <span class="mr-2">{{ user.department }}</span>-->
<!-- <span>{{ user.name }}</span>-->
<!-- </div>-->
<!-- </el-option>-->
<!-- </el-select>-->
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="审核(评审组长)">
<el-select v-model="footerForm.approveUserId" filterable placeholder="请选择评审组长" clearable>
<el-option v-for="item in userList" :key="item.userId" :label="item.nickName" :value="item.userId"></el-option>
</el-select>
</el-form-item>
</el-col>
</el-row>
</el-form>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed, watch } from 'vue';
//
//
const rdBudgetInfoForm = reactive({
budgetId: undefined,
projectId: undefined,
approvedFlag: undefined,
budgetVersion: undefined,
projectCategory: undefined,
projectCode: undefined,
projectName: undefined,
managerId: undefined,
managerName: undefined,
productManagerId: undefined,
productManagerName: undefined,
approveUserId: undefined,
approveUserName: undefined,
contractAmount: undefined,
netContractAmount: undefined,
budgetCost: undefined,
budgetRate: undefined,
reduceBudgetCost: undefined,
reduceBudgetRate: undefined,
duringOperation: undefined,
unitId: undefined,
unitName: '',
exportFlag: undefined,
budgetStatus: undefined,
flowStatus: undefined,
contractId: undefined,
remark: undefined
});
const footerForm = reactive({
managerId: '',
approveUserId: ''
});
// ,
const formatNumber = (value: number) => {
if (!value) return '0.00';
return parseFloat(value).toFixed(2);
};
// 格式化数字,元转换为万元
const format2TenThousandNumber = (value: number) => {
if (!value) return '0.00';
return (parseFloat(value) / 10000).toFixed(2);
};
// 格式化显示文本
const formatUserDisplay = (user) => {
return `${user.department} - ${user.name}`;
};
// 预算数据
const budgetDetailData = ref([
{ sortOrder: 1, budgetItem: '设备费', budgetCost: 0 },
{ sortOrder: 2, budgetItem: '材料费', budgetCost: 0 },
{ sortOrder: 3, budgetItem: '差旅费', budgetCost: 0 },
{ sortOrder: 4, budgetItem: '会议费', budgetCost: 0 },
{ sortOrder: 5, budgetItem: '国际合作与交流费', budgetCost: 0 },
{ sortOrder: 6, budgetItem: '咨询开发费', budgetCost: 0 },
{ sortOrder: 7, budgetItem: '人工费', budgetCost: 0 },
{ sortOrder: 8, budgetItem: '劳务费', budgetCost: 0 },
{ sortOrder: 9, budgetItem: '资料/文献费', budgetCost: 0 },
{ sortOrder: 10, budgetItem: '测试化验费', budgetCost: 0 },
{ sortOrder: 11, budgetItem: '其他费用', budgetCost: 0 }
]);
// 计算合计
const totalAmount = computed(() => {
return budgetDetailData.value.slice(0, -1).reduce((sum, item) => sum + Number(item.budgetCost), 0);
});
// 更新费用项
const updateCostItem = (subject, amount) => {
const index = budgetDetailData.value.findIndex((item) => item.budgetItem === subject);
if (index !== -1) {
// 使用展开运算符创建新对象,确保响应式更新
const updatedItem = { ...budgetDetailData.value[index], amount: amount };
budgetDetailData.value.splice(index, 1, updatedItem);
// 确保合计行也被正确更新
const totalIndex = budgetDetailData.value.length - 1;
const totalRow = { ...budgetDetailData.value[totalIndex], amount: totalAmount.value };
budgetDetailData.value.splice(totalIndex, 1, totalRow);
}
};
// 更新所有费用数据
const updateCostData = (costData) => {
// 设备费
updateCostItem('设备费', costData.equipmentCost || 0);
// 材料费
updateCostItem('材料费', costData.materialCost || 0);
// 差旅费
updateCostItem('差旅费', costData.travelCost || 0);
// 会议费
updateCostItem('会议费', costData.meetingCost || 0);
// 国际合作与交流费
updateCostItem('国际合作与交流费', costData.internationalExchangeCost || 0);
// 咨询开发费
updateCostItem('咨询开发费', costData.consultingDevelopmentCost || 0);
// 人工费
updateCostItem('人工费', costData.laborCost || 0);
// 劳务费
updateCostItem('劳务费', costData.serviceCost || 0);
// 资料/文献费
updateCostItem('资料/文献费', costData.literatureCost || 0);
// 测试化验费
updateCostItem('测试化验费', costData.testingCost || 0);
// 其他费用
updateCostItem('其他费用', costData.otherCost || 0);
};
// 更新预算数据的方法(供父组件调用)
const updateRdBudgetDetailData = (type: string, budgetCost: number) => {
const item = budgetDetailData.value.find((item) => item.budgetItem === type);
if (item) {
item.budgetCost = budgetCost;
}
};
// 接收项目信息
const props = defineProps({
projectInfo: {
type: Object,
default: () => ({})
},
userList: {
type: Object,
default: () => ({})
}
});
// 监听项目信息变化
watch(
() => props.projectInfo,
(newProjectInfo) => {
if (newProjectInfo) {
rdBudgetInfoForm.projectId = newProjectInfo.projectId || '';
rdBudgetInfoForm.projectName = newProjectInfo.projectName || '';
rdBudgetInfoForm.projectCode = newProjectInfo.projectCode || '';
rdBudgetInfoForm.unitName = '万元';
rdBudgetInfoForm.projectCategory = newProjectInfo.projectCategory || '';
}
},
{ deep: true, immediate: true }
);
// 获取预算数据
const getBudgetData = () => {
return {
rdBudgetInfoForm: { ...rdBudgetInfoForm },
footerForm: { ...footerForm },
budgetData: budgetDetailData.value.map((item) => ({ ...item }))
};
};
// 表格合计方法
const getSummaries = ({ columns, data }: any) => {
const sums: any[] = [];
columns.forEach((column: any, index: number) => {
if (index === 1) {
sums[index] = '合计(万元)';
return;
}
if (column.property === 'budgetCost') {
const values = data.map((item: any) => Number(item[column.property]) || 0);
sums[index] = values.reduce((prev: number, curr: number) => {
const value = Number(curr);
if (!Number.isNaN(value)) {
return prev + curr;
} else {
return prev;
}
}, 0);
sums[index] = formatNumber(sums[index]);
footerForm.budgetCost = sums[index];
} else {
sums[index] = '';
}
});
return sums;
};
// 暴露方法给父组件
defineExpose({
updateCostItem,
updateCostData,
updateRdBudgetDetailData,
budgetDetailData,
rdBudgetInfoForm,
footerForm
});
</script>
<style scoped>
.budget-table-container {
padding: 20px;
}
.title-center {
text-align: center;
margin-bottom: 30px;
font-size: 20px;
font-weight: bold;
}
</style>