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
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>
|