|
|
@@ -200,23 +200,59 @@
|
|
|
</div>
|
|
|
</el-form-item>
|
|
|
<el-divider content-position="left">费用预算(元)</el-divider>
|
|
|
- <el-row :gutter="18" class="budget-grid"
|
|
|
- ><el-col v-for="item in budgetItems" :key="item.key" :span="6"
|
|
|
- ><el-form-item :label="item.label"
|
|
|
- ><el-input-number
|
|
|
- v-model="formData.budget[item.key]"
|
|
|
+ <el-table
|
|
|
+ :data="budgetItems"
|
|
|
+ border
|
|
|
+ class="budget-table"
|
|
|
+ :header-cell-style="{ background: '#f3f6fd', color: '#555' }"
|
|
|
+ >
|
|
|
+ <el-table-column
|
|
|
+ prop="label"
|
|
|
+ label="费用预算"
|
|
|
+ min-width="420"
|
|
|
+ align="center"
|
|
|
+ header-align="center"
|
|
|
+ />
|
|
|
+ <el-table-column label="预算" min-width="220" align="center" header-align="center">
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input-number
|
|
|
+ disabled
|
|
|
+ v-model="formData.budget[row.key]"
|
|
|
:min="0"
|
|
|
:precision="2"
|
|
|
controls-position="right"
|
|
|
- class="w-full" /></el-form-item></el-col
|
|
|
- ></el-row>
|
|
|
- <div class="budget-total"
|
|
|
- ><span>预算合计</span><strong>{{ budgetTotal.toFixed(2) }}</strong
|
|
|
- ><span>元</span></div
|
|
|
- >
|
|
|
+ class="budget-input"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column
|
|
|
+ label="实际支出"
|
|
|
+ min-width="220"
|
|
|
+ align="center"
|
|
|
+ header-align="center"
|
|
|
+ class-name="actual-spend-column"
|
|
|
+ >
|
|
|
+ <template #default="{ row }">
|
|
|
+ <el-input-number
|
|
|
+ v-model="formData.actualSpend[row.key]"
|
|
|
+ :min="0"
|
|
|
+ :precision="2"
|
|
|
+ controls-position="right"
|
|
|
+ class="budget-input"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ <div class="budget-total budget-total-row">
|
|
|
+ <strong>总计</strong>
|
|
|
+ <strong>{{ budgetTotal.toFixed(2) }}</strong>
|
|
|
+ <strong>{{ actualSpendTotal.toFixed(2) }}</strong>
|
|
|
+ <span>元</span>
|
|
|
+ </div>
|
|
|
<el-divider content-position="left">附件上传</el-divider>
|
|
|
<el-form-item label="立项申请表" required
|
|
|
><UploadFile
|
|
|
+ :disabled="true"
|
|
|
v-model="formData.attachments.application"
|
|
|
:return-object="true"
|
|
|
:auto-upload="false"
|
|
|
@@ -224,6 +260,16 @@
|
|
|
:file-type="['doc', 'docx', 'pdf']"
|
|
|
/></el-form-item>
|
|
|
<el-form-item label="开题报告" required
|
|
|
+ ><UploadFile
|
|
|
+ :disabled="true"
|
|
|
+ v-model="formData.attachments.report"
|
|
|
+ :return-object="true"
|
|
|
+ :auto-upload="false"
|
|
|
+ :limit="1"
|
|
|
+ :file-type="['doc', 'docx', 'pdf']"
|
|
|
+ /></el-form-item>
|
|
|
+
|
|
|
+ <el-form-item label="进度报告" required
|
|
|
><UploadFile
|
|
|
v-model="formData.attachments.report"
|
|
|
:return-object="true"
|
|
|
@@ -280,6 +326,7 @@ type Project = {
|
|
|
innovation: string
|
|
|
schedule: ScheduleItem[]
|
|
|
budget: Record<string, number>
|
|
|
+ actualSpend: Record<string, number>
|
|
|
attachments: { application: any[]; report: any[]; others: any[] }
|
|
|
status: string
|
|
|
}
|
|
|
@@ -292,7 +339,7 @@ const projectTypes = [
|
|
|
'情报调研类'
|
|
|
]
|
|
|
const budgetItems = [
|
|
|
- { key: 'labor', label: '人工' },
|
|
|
+ { key: 'labor', label: '人工(包含工资、福利、社保、奖金等)' },
|
|
|
{ key: 'materials', label: '直接材料投入' },
|
|
|
{ key: 'supplies', label: '劳保用品' },
|
|
|
{ key: 'fuel', label: '燃料' },
|
|
|
@@ -322,6 +369,7 @@ const createForm = () => ({
|
|
|
innovation: '',
|
|
|
schedule: [createSchedule(), createSchedule(), createSchedule()],
|
|
|
budget: createBudget(),
|
|
|
+ actualSpend: createBudget(),
|
|
|
attachments: { application: [], report: [], others: [] }
|
|
|
})
|
|
|
const list = ref<Project[]>([
|
|
|
@@ -341,6 +389,7 @@ const list = ref<Project[]>([
|
|
|
{ workPlan: '完成样机试制', deadline: '2026-10-31' }
|
|
|
],
|
|
|
budget: createBudget(),
|
|
|
+ actualSpend: createBudget(),
|
|
|
attachments: { application: [], report: [], others: [] },
|
|
|
status: '进行中'
|
|
|
}
|
|
|
@@ -375,6 +424,9 @@ const formRules = reactive<FormRules>({
|
|
|
const budgetTotal = computed(() =>
|
|
|
Object.values(formData.value.budget).reduce((sum, value) => sum + Number(value || 0), 0)
|
|
|
)
|
|
|
+const actualSpendTotal = computed(() =>
|
|
|
+ Object.values(formData.value.actualSpend).reduce((sum, value) => sum + Number(value || 0), 0)
|
|
|
+)
|
|
|
const handleQuery = () => {
|
|
|
queryParams.pageNo = 1
|
|
|
}
|
|
|
@@ -406,6 +458,7 @@ const cloneProjectForm = (project: Project) => ({
|
|
|
innovation: project.innovation,
|
|
|
schedule: project.schedule.map((item) => ({ ...item })),
|
|
|
budget: { ...project.budget },
|
|
|
+ actualSpend: { ...project.actualSpend },
|
|
|
attachments: cloneAttachments(project.attachments)
|
|
|
})
|
|
|
const openForm = (project?: Project) => {
|
|
|
@@ -484,24 +537,40 @@ const removeProject = async (id: number) => {
|
|
|
color: var(--el-text-color-secondary);
|
|
|
font-size: 13px;
|
|
|
}
|
|
|
-.budget-total {
|
|
|
- display: flex;
|
|
|
- align-items: baseline;
|
|
|
- justify-content: flex-end;
|
|
|
- gap: 8px;
|
|
|
- padding: 12px 8px;
|
|
|
- color: var(--el-text-color-secondary);
|
|
|
- border-top: 1px solid var(--el-border-color-lighter);
|
|
|
+.budget-table {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+.budget-table :deep(.el-input-number) {
|
|
|
+ width: 100%;
|
|
|
+}
|
|
|
+.budget-table :deep(.actual-spend-column) {
|
|
|
+ background-color: #fff900;
|
|
|
}
|
|
|
-.budget-total strong {
|
|
|
- color: var(--el-color-danger);
|
|
|
- font-size: 20px;
|
|
|
+.budget-total-row {
|
|
|
+ display: grid;
|
|
|
+ grid-template-columns: minmax(420px, 1fr) minmax(220px, 1fr) minmax(220px, 1fr) auto;
|
|
|
+ align-items: center;
|
|
|
+ gap: 0;
|
|
|
+ min-height: 48px;
|
|
|
+ border: 1px solid var(--el-border-color);
|
|
|
+ border-top: 0;
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+.budget-total-row strong,
|
|
|
+.budget-total-row span {
|
|
|
+ padding: 12px;
|
|
|
font-variant-numeric: tabular-nums;
|
|
|
}
|
|
|
+.budget-total-row strong:first-child {
|
|
|
+ text-align: left;
|
|
|
+}
|
|
|
+.budget-total-row strong:not(:first-child) {
|
|
|
+ border-left: 1px solid var(--el-border-color);
|
|
|
+}
|
|
|
@media (max-width: 900px) {
|
|
|
- .budget-grid :deep(.el-col) {
|
|
|
- width: 50%;
|
|
|
- flex: 0 0 50%;
|
|
|
+ .budget-total-row {
|
|
|
+ grid-template-columns: minmax(220px, 1fr) minmax(150px, 1fr) minmax(150px, 1fr) auto;
|
|
|
}
|
|
|
}
|
|
|
</style>
|