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.

281 lines
8.7 KiB
Vue

<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';
// 响应式数据
// 表单数据
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) {
rdBudgetInfoForm.projectId = newProjectInfo.projectId || '';
rdBudgetInfoForm.projectName = newProjectInfo.projectName || '';
rdBudgetInfoForm.projectCode = newProjectInfo.projectCode || '';
rdBudgetInfoForm.unitName = '万元';
rdBudgetInfoForm.projectCategory = newProjectInfo.projectCategory || '';
}
},
{ 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>