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.

251 lines
7.6 KiB
Vue

<template>
<div class="budget-table-container">
<h2 class="title-center">项目经费预算表</h2>
<el-form :model="formData" label-width="120px" class="mb-4">
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="项目名称">
<el-input v-model="formData.projectName" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目号">
<el-input v-model="formData.projectCode" readonly />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="项目预算期间">
<el-input v-model="formData.durationOperation" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="金额单位">
<el-input v-model="formData.unit" readonly value="万元" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<el-table :data="budgetData" style="width: 100%" border>
<el-table-column prop="index" label="序号" width="80" />
<el-table-column prop="subject" label="预算科目名称" />
<el-table-column prop="amount" label="项目经费" width="260" align="right">
<template #default="scope">
{{ scope.row.amount.toFixed(2) }}
</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.creator"
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.reviewer"
placeholder="请选择评审组长"
filterable
clearable
:filter-method="(query) => reviewerSearchQuery = query"
:remote="false"
>
<el-option
v-for="user in filteredReviewerList"
: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-row>
</el-form>
</div>
</template>
<script setup lang="ts">
import { ref, reactive, onMounted, computed, watch } from 'vue';
// 响应式数据
const formData = reactive({
projectName: '',
projectCode: '',
budgetPeriod: null,
unit: '万元'
});
const footerForm = reactive({
creator: '',
reviewer: ''
});
// 用户下拉列表数据
const userList = ref([
{ id: '1', name: '张三', department: '研发部' },
{ id: '2', name: '李四', department: '市场部' },
{ id: '3', name: '王五', department: '财务部' },
{ id: '4', name: '赵六', department: '人力资源部' },
{ id: '5', name: '钱七', department: '研发部' }
]);
// 搜索关键词
const creatorSearchQuery = ref('');
const reviewerSearchQuery = ref('');
// 过滤后的用户列表
const filteredCreatorList = computed(() => {
return userList.value.filter(user =>
user.name.includes(creatorSearchQuery.value)
);
});
const filteredReviewerList = computed(() => {
return userList.value.filter(user =>
user.name.includes(reviewerSearchQuery.value)
);
});
// 格式化显示文本
const formatUserDisplay = (user) => {
return `${user.department} - ${user.name}`;
};
// 预算数据
const budgetData = ref([
{ index: 1, subject: '设备费', amount: 0 },
{ index: 2, subject: '材料费', amount: 0 },
{ index: 3, subject: '差旅费', amount: 0 },
{ index: 4, subject: '会议费', amount: 0 },
{ index: 5, subject: '国际合作与交流费', amount: 0 },
{ index: 6, subject: '咨询开发费', amount: 0 },
{ index: 7, subject: '人工费', amount: 0 },
{ index: 8, subject: '劳务费', amount: 0 },
{ index: 9, subject: '资料/文献费', amount: 0 },
{ index: 10, subject: '测试化验费', amount: 0 },
{ index: 11, subject: '其他费用', amount: 0 },
{ index: '', subject: '合计', amount: 0 }
]);
// 计算合计
const totalAmount = computed(() => {
return budgetData.value.slice(0, -1).reduce((sum, item) => sum + item.amount, 0);
});
// 更新费用项
const updateCostItem = (subject, amount) => {
const index = budgetData.value.findIndex(item => item.subject === subject)
if (index !== -1) {
// 使用展开运算符创建新对象,确保响应式更新
const updatedItem = { ...budgetData.value[index], amount: amount }
budgetData.value.splice(index, 1, updatedItem)
// 确保合计行也被正确更新
const totalIndex = budgetData.value.length - 1
const totalRow = { ...budgetData.value[totalIndex], amount: totalAmount.value }
budgetData.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 props = defineProps({
projectInfo: {
type: Object,
default: () => ({})
}
})
// 监听项目信息变化
watch(() => props.projectInfo, (newProjectInfo) => {
if (newProjectInfo) {
formData.projectName = newProjectInfo.projectName || ''
formData.projectCode = newProjectInfo.projectCode || ''
formData.budgetPeriod = newProjectInfo.budgetPeriod || null
formData.unit = newProjectInfo.amountUnit || '万元'
}
}, { deep: true, immediate: true })
// 获取预算数据
const getBudgetData = () => {
return {
formData: { ...formData },
footerForm: { ...footerForm },
budgetData: budgetData.value.map(item => ({ ...item }))
};
};
// 暴露方法给父组件
defineExpose({
updateCostItem,
updateCostData,
getBudgetData
});
</script>
<style scoped>
.budget-table-container {
padding: 20px;
}
.title-center {
text-align: center;
margin-bottom: 30px;
font-size: 20px;
font-weight: bold;
}
</style>