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.

299 lines
9.5 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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