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.

333 lines
9.0 KiB
Vue

<template>
<div class="budget-table">
<!-- 标题 -->
<div class="title">项目经费预算表</div>
<!-- 表单 -->
<el-form :model="budgetForm" label-width="150px" class="budget-form">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目名称">
<el-input v-model="budgetForm.projectName" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目号">
<el-input v-model="budgetForm.projectCode" readonly />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目经理">
<el-input v-model="budgetForm.managerId" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="产品经理">
<el-input v-model="budgetForm.productManagerId" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="合同额">
<el-input v-model="budgetForm.contractAmount" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="合同净额">
<el-input v-model="budgetForm.netContractAmount" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="预算成本">
<el-input v-model="budgetForm.budgetCost" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目毛利率">
<el-input v-model="budgetForm.budgetRate" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="降成本后预算成本">
<el-input v-model="budgetForm.reduceBudgetCost" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="降成本后预算毛利率">
<el-input v-model="budgetForm.reduceBudgetRate" />
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目预算期间">
<el-input v-model="budgetForm.duringOperation" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="单位">
<el-input v-model="budgetForm.unitId" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<!-- 表格 -->
<el-table
:data="budgetDetailData"
border
style="width: 100%"
:summary-method="getSummaries"
show-summary
>
<el-table-column prop="sortOrder" label="序号" width="80" align="center" />
<el-table-column prop="budgetItem" label="预算科目名称" min-width="150" />
<el-table-column prop="budgetCost" label="预算成本" width="160" align="right">
<template #default="scope">
<el-input
v-if="scope.row.budgetItem === '运输费' || scope.row.budgetItem === '售后服务费' || scope.row.budgetItem === '其他成本'"
v-model="scope.row.budgetCost"
@input="updateBudgetCost(scope.row)"
/>
<span v-else>{{ formatNumber(scope.row.budgetCost) }}</span>
</template>
</el-table-column>
<el-table-column prop="reduceBudgetCost" label="降成本后预算成本" width="160" align="right">
<template #default="scope">
<el-input
v-if="scope.row.budgetItem === '运输费' || scope.row.budgetItem === '售后服务费' || scope.row.budgetItem === '其他成本'"
v-model="scope.row.reduceBudgetCost"
@input="updateReduceBudgetCost(scope.row)"
/>
<span v-else>{{ formatNumber(scope.row.reduceBudgetCost) }}</span>
</template>
</el-table-column>
<el-table-column prop="referenceProjectName" label="参考项目" width="280">
<template #default="scope">
<el-input v-model="scope.row.referenceProjectName" />
</template>
</el-table-column>
</el-table>
<!-- 备注 -->
<div class="note">以上成本均为不含税价格</div>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, computed, watch } from 'vue'
// 表单数据
const budgetForm = 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: undefined,
exportFlag: undefined,
budgetStatus: undefined,
flowStatus: undefined,
contractId: undefined,
remark: undefined,
})
// 预算数据
const budgetDetailData = ref([
{
sortOrder: 1,
budgetItem: '材料费',
budgetCost: 0,
reduceBudgetCost: 0,
referenceProjectId: undefined,
referenceProjectName:''
},
{
sortOrder: 2,
budgetItem: '人工费',
budgetCost: 0,
reduceBudgetCost: 0,
referenceProjectId: undefined,
referenceProjectName:''
},
{
sortOrder: 3,
budgetItem: '安装费',
budgetCost: 0,
reduceBudgetCost: 0,
referenceProjectId: undefined,
referenceProjectName:''
},
{
sortOrder: 4,
budgetItem: '差旅费',
budgetCost: 0,
reduceBudgetCost: 0,
referenceProjectId: undefined,
referenceProjectName:''
},
{
sortOrder: 5,
budgetItem: '运输费',
budgetCost: 0,
reduceBudgetCost: 0,
referenceProjectId: undefined,
referenceProjectName:''
},
{
sortOrder: 6,
budgetItem: '售后服务费',
budgetCost: 0,
reduceBudgetCost: 0,
referenceProjectId: undefined,
referenceProjectName:''
},
{
sortOrder: 7,
budgetItem: '其他费用',
budgetCost: 0,
reduceBudgetCost: 0,
referenceProjectId: undefined,
referenceProjectName:''
},
{
sortOrder: 8,
budgetItem: '其他成本',
budgetCost: 0,
reduceBudgetCost: 0,
referenceProjectId: undefined,
referenceProjectName:''
}
])
// 格式化数字
const formatNumber = (value: number) => {
if (!value) return '0.00'
return value.toLocaleString('zh-CN', { minimumFractionDigits: 2, maximumFractionDigits: 2 })
}
// 合计方法
const getSummaries = (param: any) => {
const { columns, data } = param
const sums: any[] = []
columns.forEach((column: any, index: number) => {
if (index === 1) {
sums[index] = '合计'
return
}
if (index === 0 || index === 4) {
sums[index] = ''
return
}
const values = data.map((item: any) => Number(item[column.property]))
if (!values.every((value: number) => Number.isNaN(value))) {
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])
} else {
sums[index] = ''
}
})
return sums
}
// 更新预算数据的方法(供父组件调用)
const updateBudgetDetailData = (type: string, budgetCost: number, reduceBudgetCost: number) => {
const item = budgetDetailData.value.find(item => item.budgetItem === type)
if (item) {
item.budgetCost = budgetCost
item.reduceBudgetCost = reduceBudgetCost
}
}
// 更新预算成本
const updateBudgetCost = (row: any) => {
row.budgetCost = parseFloat(row.budgetCost) || 0
}
// 更新降成本后预算成本
const updateReduceBudgetCost = (row: any) => {
row.reduceBudgetCost = parseFloat(row.reduceBudgetCost) || 0
}
// 暴露方法给父组件
defineExpose({
updateBudgetDetailData,
budgetForm,
budgetDetailData
})
// 接收项目信息
const props = defineProps({
projectInfo: {
type: Object,
default: () => ({})
}
})
// 监听项目信息变化
watch(() => props.projectInfo, (newProjectInfo) => {
if (newProjectInfo) {
budgetForm.projectId = newProjectInfo.projectId || ''
budgetForm.projectName = newProjectInfo.projectName || ''
budgetForm.projectCode = newProjectInfo.projectCode || ''
budgetForm.projectCategory = newProjectInfo.projectCategory || ''
}
}, { deep: true, immediate: true })
</script>
<style scoped>
.budget-table {
padding: 20px;
}
.title {
text-align: center;
font-size: 18px;
font-weight: bold;
margin-bottom: 20px;
}
.budget-form {
margin-bottom: 20px;
}
.note {
margin-top: 10px;
font-size: 12px;
color: #666;
text-align: center;
}
</style>