|
|
<template>
|
|
|
<div class="budget-table-container">
|
|
|
<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-date-picker v-model="rdBudgetInfoForm.preparationDate" format="YYYY-MM-DD" size="large" style="width:100%"/>
|
|
|
</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-col :span="12">
|
|
|
<el-form-item label="项目经理">
|
|
|
<el-select v-model="rdBudgetInfoForm.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-form-item>
|
|
|
</el-col>
|
|
|
|
|
|
<el-col :span="12">
|
|
|
<el-form-item label="部门负责人">
|
|
|
<el-select v-model="rdBudgetInfoForm.productManagerId" 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-col :span="12">
|
|
|
<el-form-item label="财务负责人">
|
|
|
<el-select v-model="rdBudgetInfoForm.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>
|
|
|
|
|
|
<el-table
|
|
|
:data="budgetDetailData"
|
|
|
style="width: 100%"
|
|
|
border
|
|
|
show-summary
|
|
|
:summary-method="
|
|
|
() => {
|
|
|
const cols = ['', '合计(万元)', '', '', ''];
|
|
|
cols[cols.length - 3] = formatNumber(totalAmount);
|
|
|
cols[cols.length - 2] = formatNumber(totalProportion) + '%';
|
|
|
return cols;
|
|
|
}
|
|
|
"
|
|
|
>
|
|
|
<el-table-column prop="sortOrder" label="序号" width="80" />
|
|
|
<el-table-column prop="budgetItem" label="预算科目名称" min-width="180" />
|
|
|
<el-table-column prop="budgetCost" label="项目经费" width="200">
|
|
|
<template #default="scope">
|
|
|
<span>{{ formatNumber(scope.row.budgetCost) }}</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column prop="budgetCostProportion" label="项目经费占比" width="200">
|
|
|
<template #default="scope">
|
|
|
<span>{{ computeBudgetCostProportion(scope.row) }}%</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column prop="detailSource" label="数据来源" width="360">
|
|
|
<template #default="scope">
|
|
|
<el-input v-model="scope.row.detailSource" />
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
|
|
|
<div class="mt-4 bg-gray-50 p-3 rounded text-left font-semibold">
|
|
|
<p>注: 序号1-7由表二到表七的数据自动链接生成,不需手动填写。 </p>
|
|
|
</div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script setup lang="ts">
|
|
|
import { ref, reactive, onMounted, computed, watch } from 'vue';
|
|
|
import { budgetInfoVO } from '@/api/oa/erp/budgetInfo/types';
|
|
|
import type { ProjectInfoVO } from '@/api/oa/erp/projectInfo/types';
|
|
|
|
|
|
// 响应式数据
|
|
|
// 表单数据
|
|
|
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 formatNumber = (value: number) => {
|
|
|
if (!value) return '0.00';
|
|
|
return parseFloat(value).toFixed(2);
|
|
|
};
|
|
|
|
|
|
const computeBudgetCostProportion = (row: budgetInfoVO) => {
|
|
|
const budgetCost = Number(row.budgetCost) || 0;
|
|
|
const totalBudgetCost = Number(totalAmount.value) || 0;
|
|
|
// 计算百分比
|
|
|
let percentage = 0;
|
|
|
if (totalBudgetCost > 0) {
|
|
|
percentage = (budgetCost / totalBudgetCost) * 100;
|
|
|
}
|
|
|
|
|
|
return percentage.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 }
|
|
|
]);
|
|
|
|
|
|
// 计算合计
|
|
|
const totalAmount = computed(() => {
|
|
|
return budgetDetailData.value.reduce((sum, item) => {
|
|
|
const budgetCost = Number(item.budgetCost) || 0;
|
|
|
const formattedAmount = parseFloat(budgetCost.toFixed(2));
|
|
|
return sum + formattedAmount;
|
|
|
}, 0);
|
|
|
});
|
|
|
|
|
|
const totalProportion = computed(() => {
|
|
|
return budgetDetailData.value.reduce((sum, item) => {
|
|
|
const budgetCost = Number(item.budgetCost) || 0;
|
|
|
const formattedAmount = parseFloat(budgetCost.toFixed(2));
|
|
|
let percentage = 0;
|
|
|
if (totalAmount.value > 0) {
|
|
|
percentage = (budgetCost / totalAmount.value) * 100;
|
|
|
}
|
|
|
return sum + percentage;
|
|
|
}, 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) {
|
|
|
const pi = newProjectInfo as Partial<ProjectInfoVO>;
|
|
|
rdBudgetInfoForm.projectId = pi.projectId || '';
|
|
|
rdBudgetInfoForm.projectName = pi.projectName || '';
|
|
|
rdBudgetInfoForm.projectCode = pi.projectCode || '';
|
|
|
rdBudgetInfoForm.unitName = '万元';
|
|
|
rdBudgetInfoForm.projectCategory = pi.projectCategory || '';
|
|
|
const mid = pi.managerId;
|
|
|
rdBudgetInfoForm.managerId =
|
|
|
mid !== undefined && mid !== null && String(mid).trim() !== '' ? mid : undefined;
|
|
|
|
|
|
const amt = pi.amount;
|
|
|
if (amt !== undefined && amt !== null && String(amt).trim() !== '' && !Number.isNaN(Number(amt))) {
|
|
|
rdBudgetInfoForm.contractAmount = Number(amt);
|
|
|
const ca = rdBudgetInfoForm.contractAmount;
|
|
|
rdBudgetInfoForm.netContractAmount =
|
|
|
ca !== undefined && ca !== null && ca !== '' && !Number.isNaN(Number(ca))
|
|
|
? (Number(ca) / 1.13).toFixed(2)
|
|
|
: undefined;
|
|
|
} else {
|
|
|
rdBudgetInfoForm.contractAmount = undefined;
|
|
|
rdBudgetInfoForm.netContractAmount = undefined;
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
{ deep: true, immediate: true }
|
|
|
);
|
|
|
|
|
|
// 表格合计方法
|
|
|
const getSummaries = ({ columns, data }: any) => {
|
|
|
const sums: any[] = [];
|
|
|
columns.forEach((column: any, index: number) => {
|
|
|
if (index === 1) {
|
|
|
sums[index] = '合计(万元)';
|
|
|
return;
|
|
|
}
|
|
|
if (column.property === 'budgetCost' || column.property === 'budgetCostProportion') {
|
|
|
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);
|
|
|
if (column.property === 'budgetCost') {
|
|
|
sums[index] = formatNumber(sums[index]);
|
|
|
rdBudgetInfoForm.budgetCost = sums[index];
|
|
|
} else {
|
|
|
sums[index] = formatNumber(sums[index]) + '%';
|
|
|
}
|
|
|
} else {
|
|
|
sums[index] = '';
|
|
|
}
|
|
|
});
|
|
|
return sums;
|
|
|
};
|
|
|
|
|
|
// 暴露方法给父组件
|
|
|
defineExpose({
|
|
|
updateRdBudgetDetailData,
|
|
|
budgetDetailData,
|
|
|
rdBudgetInfoForm,
|
|
|
totalAmount
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
|
.budget-table-container {
|
|
|
padding: 20px;
|
|
|
}
|
|
|
|
|
|
.title-center {
|
|
|
text-align: center;
|
|
|
margin-bottom: 30px;
|
|
|
font-size: 20px;
|
|
|
font-weight: bold;
|
|
|
}
|
|
|
</style>
|