|
@@ -1,5 +1,507 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div> ssssssssss </div>
|
|
|
|
|
|
|
+ <ContentWrap>
|
|
|
|
|
+ <el-form ref="queryFormRef" :model="queryParams" inline class="project-filter">
|
|
|
|
|
+ <el-form-item label="项目名称" prop="projectName">
|
|
|
|
|
+ <el-input
|
|
|
|
|
+ v-model="queryParams.projectName"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ placeholder="请输入项目名称"
|
|
|
|
|
+ class="!w-220px"
|
|
|
|
|
+ @keyup.enter="handleQuery"
|
|
|
|
|
+ />
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item label="项目类型" prop="projectType">
|
|
|
|
|
+ <el-select
|
|
|
|
|
+ v-model="queryParams.projectType"
|
|
|
|
|
+ clearable
|
|
|
|
|
+ placeholder="请选择项目类型"
|
|
|
|
|
+ class="!w-220px"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-option v-for="item in projectTypes" :key="item" :label="item" :value="item" />
|
|
|
|
|
+ </el-select>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ <el-form-item>
|
|
|
|
|
+ <el-button type="primary" @click="handleQuery"
|
|
|
|
|
+ ><Icon icon="ep:search" class="mr-5px" />搜索</el-button
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" />重置</el-button>
|
|
|
|
|
+ </el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ </ContentWrap>
|
|
|
|
|
+
|
|
|
|
|
+ <ContentWrap>
|
|
|
|
|
+ <div class="project-table-heading">
|
|
|
|
|
+ <div><h2>科研项目</h2><span>维护项目申报信息、预算及阶段性计划</span></div>
|
|
|
|
|
+ <el-tag type="info" effect="plain">共 {{ filteredList.length }} 个项目</el-tag>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <zm-table :loading="loading" :data="filteredList" row-key="id">
|
|
|
|
|
+ <zm-table-column label="项目名称" prop="projectName" min-width="250" show-overflow-tooltip />
|
|
|
|
|
+ <zm-table-column label="项目级别" prop="projectLevel" min-width="180" />
|
|
|
|
|
+ <zm-table-column
|
|
|
|
|
+ label="执行单位"
|
|
|
|
|
+ prop="executingUnit"
|
|
|
|
|
+ min-width="210"
|
|
|
|
|
+ show-overflow-tooltip
|
|
|
|
|
+ />
|
|
|
|
|
+ <zm-table-column label="项目负责人" prop="projectLeader" width="130" />
|
|
|
|
|
+ <zm-table-column label="项目类型" prop="projectType" min-width="180" />
|
|
|
|
|
+ <zm-table-column label="状态" width="110" align="center"
|
|
|
|
|
+ ><template #default="{ row }"
|
|
|
|
|
+ ><el-tag :type="row.status === '进行中' ? 'success' : 'info'">{{
|
|
|
|
|
+ row.status
|
|
|
|
|
+ }}</el-tag></template
|
|
|
|
|
+ ></zm-table-column
|
|
|
|
|
+ >
|
|
|
|
|
+ <zm-table-column label="操作" width="120" fixed="right" align="center"
|
|
|
|
|
+ ><template #default="{ row }">
|
|
|
|
|
+ <el-button link type="primary" @click="openForm(row)">填报</el-button></template
|
|
|
|
|
+ ></zm-table-column
|
|
|
|
|
+ >
|
|
|
|
|
+ </zm-table>
|
|
|
|
|
+ <Pagination
|
|
|
|
|
+ v-model:page="queryParams.pageNo"
|
|
|
|
|
+ v-model:limit="queryParams.pageSize"
|
|
|
|
|
+ :total="filteredList.length"
|
|
|
|
|
+ />
|
|
|
|
|
+ </ContentWrap>
|
|
|
|
|
+
|
|
|
|
|
+ <ResearchProjectDialog
|
|
|
|
|
+ v-model="dialogVisible"
|
|
|
|
|
+ :title="dialogTitle"
|
|
|
|
|
+ width="100%"
|
|
|
|
|
+ :fullscreen="true"
|
|
|
|
|
+ destroy-on-close
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-form
|
|
|
|
|
+ ref="formRef"
|
|
|
|
|
+ v-loading="formLoading"
|
|
|
|
|
+ :model="formData"
|
|
|
|
|
+ :rules="formRules"
|
|
|
|
|
+ label-width="112px"
|
|
|
|
|
+ class="project-form"
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-divider content-position="left">项目基本信息</el-divider>
|
|
|
|
|
+ <el-row :gutter="24">
|
|
|
|
|
+ <el-col :span="12"
|
|
|
|
|
+ ><el-form-item label="项目级别" prop="projectLevel"
|
|
|
|
|
+ ><el-select
|
|
|
|
|
+ disabled
|
|
|
|
|
+ v-model="formData.projectLevel"
|
|
|
|
|
+ class="w-full"
|
|
|
|
|
+ placeholder="请选择项目级别"
|
|
|
|
|
+ ><el-option
|
|
|
|
|
+ v-for="item in projectLevels"
|
|
|
|
|
+ :key="item"
|
|
|
|
|
+ :label="item"
|
|
|
|
|
+ :value="item" /></el-select></el-form-item
|
|
|
|
|
+ ></el-col>
|
|
|
|
|
+ <el-col :span="12"
|
|
|
|
|
+ ><el-form-item label="执行单位" prop="executingUnit"
|
|
|
|
|
+ ><el-input
|
|
|
|
|
+ disabled
|
|
|
|
|
+ v-model="formData.executingUnit"
|
|
|
|
|
+ placeholder="请输入执行单位" /></el-form-item
|
|
|
|
|
+ ></el-col>
|
|
|
|
|
+ <el-col :span="12"
|
|
|
|
|
+ ><el-form-item label="项目名称" prop="projectName"
|
|
|
|
|
+ ><el-input
|
|
|
|
|
+ disabled
|
|
|
|
|
+ v-model="formData.projectName"
|
|
|
|
|
+ placeholder="请输入项目名称" /></el-form-item
|
|
|
|
|
+ ></el-col>
|
|
|
|
|
+ <el-col :span="12"
|
|
|
|
|
+ ><el-form-item label="项目负责人" prop="projectLeader"
|
|
|
|
|
+ ><el-input
|
|
|
|
|
+ disabled
|
|
|
|
|
+ v-model="formData.projectLeader"
|
|
|
|
|
+ placeholder="请输入项目负责人" /></el-form-item
|
|
|
|
|
+ ></el-col>
|
|
|
|
|
+ <el-col :span="12"
|
|
|
|
|
+ ><el-form-item label="项目组成员"
|
|
|
|
|
+ ><el-input
|
|
|
|
|
+ disabled
|
|
|
|
|
+ v-model="formData.projectMembers"
|
|
|
|
|
+ placeholder="请输入成员姓名,多个成员用逗号分隔" /></el-form-item
|
|
|
|
|
+ ></el-col>
|
|
|
|
|
+ <el-col :span="12"
|
|
|
|
|
+ ><el-form-item label="项目类型" prop="projectType"
|
|
|
|
|
+ ><el-select
|
|
|
|
|
+ disabled
|
|
|
|
|
+ v-model="formData.projectType"
|
|
|
|
|
+ class="w-full"
|
|
|
|
|
+ placeholder="请选择项目类型"
|
|
|
|
|
+ ><el-option
|
|
|
|
|
+ v-for="item in projectTypes"
|
|
|
|
|
+ :key="item"
|
|
|
|
|
+ :label="item"
|
|
|
|
|
+ :value="item" /></el-select></el-form-item
|
|
|
|
|
+ ></el-col>
|
|
|
|
|
+ </el-row>
|
|
|
|
|
+ <el-divider content-position="left">项目内容</el-divider>
|
|
|
|
|
+ <el-form-item label="目标和意义" prop="purpose"
|
|
|
|
|
+ ><el-input
|
|
|
|
|
+ disabled
|
|
|
|
|
+ v-model="formData.purpose"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :rows="3"
|
|
|
|
|
+ maxlength="500"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ placeholder="请输入项目目标和意义"
|
|
|
|
|
+ /></el-form-item>
|
|
|
|
|
+ <el-form-item label="主要研究内容" prop="researchContent"
|
|
|
|
|
+ ><el-input
|
|
|
|
|
+ disabled
|
|
|
|
|
+ v-model="formData.researchContent"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :rows="4"
|
|
|
|
|
+ maxlength="2000"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ placeholder="请输入主要研究内容"
|
|
|
|
|
+ /></el-form-item>
|
|
|
|
|
+ <el-form-item label="创新点" prop="innovation"
|
|
|
|
|
+ ><el-input
|
|
|
|
|
+ disabled
|
|
|
|
|
+ v-model="formData.innovation"
|
|
|
|
|
+ type="textarea"
|
|
|
|
|
+ :rows="3"
|
|
|
|
|
+ maxlength="500"
|
|
|
|
|
+ show-word-limit
|
|
|
|
|
+ placeholder="请输入项目创新点"
|
|
|
|
|
+ /></el-form-item>
|
|
|
|
|
+ <el-form-item label="进度安排" prop="schedule">
|
|
|
|
|
+ <div class="schedule-editor">
|
|
|
|
|
+ <div class="schedule-toolbar"><span>按阶段填写工作计划与截止时间</span></div>
|
|
|
|
|
+ <el-table :data="formData.schedule" border class="schedule-table">
|
|
|
|
|
+ <el-table-column type="index" label="#" width="56" align="center" />
|
|
|
|
|
+ <el-table-column label="工作计划" min-width="300"
|
|
|
|
|
+ ><template #default="{ row }"
|
|
|
|
|
+ ><el-input disabled v-model="row.workPlan" placeholder="请输入工作计划" /></template
|
|
|
|
|
+ ></el-table-column>
|
|
|
|
|
+ <el-table-column label="截止时间" width="190"
|
|
|
|
|
+ ><template #default="{ row }"
|
|
|
|
|
+ ><el-date-picker
|
|
|
|
|
+ disabled
|
|
|
|
|
+ v-model="row.deadline"
|
|
|
|
|
+ type="date"
|
|
|
|
|
+ value-format="YYYY-MM-DD"
|
|
|
|
|
+ placeholder="选择日期"
|
|
|
|
|
+ class="w-full" /></template
|
|
|
|
|
+ ></el-table-column>
|
|
|
|
|
+ <el-table-column label="实际完成情况" min-width="300"
|
|
|
|
|
+ ><template #default="{ row }"
|
|
|
|
|
+ ><el-input v-model="row.result" placeholder="请输入实际完成情况" /></template
|
|
|
|
|
+ ></el-table-column>
|
|
|
|
|
+ <!-- <el-table-column label="操作" width="90" align="center"
|
|
|
|
|
+ ><template #default="{ $index }"
|
|
|
|
|
+ ><el-button link type="danger" @click="removeSchedule($index)"
|
|
|
|
|
+ ><Icon icon="ep:delete" /></el-button></template
|
|
|
|
|
+ ></el-table-column> -->
|
|
|
|
|
+ </el-table>
|
|
|
|
|
+ </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]"
|
|
|
|
|
+ :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
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-divider content-position="left">附件上传</el-divider>
|
|
|
|
|
+ <el-form-item label="立项申请表" required
|
|
|
|
|
+ ><UploadFile
|
|
|
|
|
+ v-model="formData.attachments.application"
|
|
|
|
|
+ :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"
|
|
|
|
|
+ :auto-upload="false"
|
|
|
|
|
+ :limit="1"
|
|
|
|
|
+ :file-type="['doc', 'docx', 'pdf']"
|
|
|
|
|
+ /></el-form-item>
|
|
|
|
|
+ <el-form-item label="其他文件"
|
|
|
|
|
+ ><UploadFile
|
|
|
|
|
+ v-model="formData.attachments.others"
|
|
|
|
|
+ :return-object="true"
|
|
|
|
|
+ :auto-upload="false"
|
|
|
|
|
+ :limit="3"
|
|
|
|
|
+ :file-type="['doc', 'docx', 'pdf', 'xls', 'xlsx', 'jpg', 'png']"
|
|
|
|
|
+ /></el-form-item>
|
|
|
|
|
+ </el-form>
|
|
|
|
|
+ <template #footer>
|
|
|
|
|
+ <div
|
|
|
|
|
+ style="
|
|
|
|
|
+ position: fixed;
|
|
|
|
|
+ bottom: 0;
|
|
|
|
|
+ left: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ background: #fff;
|
|
|
|
|
+ padding: 10px;
|
|
|
|
|
+ z-index: 100;
|
|
|
|
|
+ /* border-top: 1px solid #ccc; */
|
|
|
|
|
+ box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.1);
|
|
|
|
|
+ "
|
|
|
|
|
+ >
|
|
|
|
|
+ <el-button @click="dialogVisible = false">取消</el-button
|
|
|
|
|
+ ><el-button type="primary" :loading="submitLoading" @click="submitForm">保存项目</el-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </ResearchProjectDialog>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
-<script lang="ts" setup></script>
|
|
|
|
|
|
|
+<script setup lang="ts">
|
|
|
|
|
+import { computed, reactive, ref } from 'vue'
|
|
|
|
|
+import { ElMessage, ElMessageBox, type FormRules } from 'element-plus'
|
|
|
|
|
+
|
|
|
|
|
+defineOptions({ name: 'ResearchProjectList' })
|
|
|
|
|
+type ScheduleItem = { workPlan: string; deadline: string }
|
|
|
|
|
+type Project = {
|
|
|
|
|
+ id: number
|
|
|
|
|
+ projectLevel: string
|
|
|
|
|
+ executingUnit: string
|
|
|
|
|
+ projectName: string
|
|
|
|
|
+ projectLeader: string
|
|
|
|
|
+ projectMembers: string
|
|
|
|
|
+ projectType: string
|
|
|
|
|
+ purpose: string
|
|
|
|
|
+ researchContent: string
|
|
|
|
|
+ innovation: string
|
|
|
|
|
+ schedule: ScheduleItem[]
|
|
|
|
|
+ budget: Record<string, number>
|
|
|
|
|
+ attachments: { application: any[]; report: any[]; others: any[] }
|
|
|
|
|
+ status: string
|
|
|
|
|
+}
|
|
|
|
|
+const projectLevels = ['专业公司级/石油技术公司级', '集团公司级', '国家级']
|
|
|
|
|
+const projectTypes = [
|
|
|
|
|
+ '基础研究类',
|
|
|
|
|
+ '原理方法类',
|
|
|
|
|
+ '产品、工具和设备研发类',
|
|
|
|
|
+ '成熟工艺技术配套及推广类',
|
|
|
|
|
+ '情报调研类'
|
|
|
|
|
+]
|
|
|
|
|
+const budgetItems = [
|
|
|
|
|
+ { key: 'labor', label: '人工' },
|
|
|
|
|
+ { key: 'materials', label: '直接材料投入' },
|
|
|
|
|
+ { key: 'supplies', label: '劳保用品' },
|
|
|
|
|
+ { key: 'fuel', label: '燃料' },
|
|
|
|
|
+ { key: 'waterElectricity', label: '水电费' },
|
|
|
|
|
+ { key: 'inspection', label: '检验费' },
|
|
|
|
|
+ { key: 'transport', label: '搬运运输费' },
|
|
|
|
|
+ { key: 'depreciation', label: '折旧' },
|
|
|
|
|
+ { key: 'rental', label: '租赁' },
|
|
|
|
|
+ { key: 'design', label: '设计费' },
|
|
|
|
|
+ { key: 'recruitment', label: '招待费' },
|
|
|
|
|
+ { key: 'travel', label: '差旅费' },
|
|
|
|
|
+ { key: 'technical', label: '技术服务' },
|
|
|
|
|
+ { key: 'other', label: '其他' }
|
|
|
|
|
+]
|
|
|
|
|
+const createSchedule = (): ScheduleItem => ({ workPlan: '', deadline: '' })
|
|
|
|
|
+const createBudget = () =>
|
|
|
|
|
+ Object.fromEntries(budgetItems.map(({ key }) => [key, 0])) as Record<string, number>
|
|
|
|
|
+const createForm = () => ({
|
|
|
|
|
+ projectLevel: '',
|
|
|
|
|
+ executingUnit: '',
|
|
|
|
|
+ projectName: '',
|
|
|
|
|
+ projectLeader: '',
|
|
|
|
|
+ projectMembers: '',
|
|
|
|
|
+ projectType: '',
|
|
|
|
|
+ purpose: '',
|
|
|
|
|
+ researchContent: '',
|
|
|
|
|
+ innovation: '',
|
|
|
|
|
+ schedule: [createSchedule(), createSchedule(), createSchedule()],
|
|
|
|
|
+ budget: createBudget(),
|
|
|
|
|
+ attachments: { application: [], report: [], others: [] }
|
|
|
|
|
+})
|
|
|
|
|
+const list = ref<Project[]>([
|
|
|
|
|
+ {
|
|
|
|
|
+ id: 1,
|
|
|
|
|
+ projectLevel: '专业公司级/石油技术公司级',
|
|
|
|
|
+ executingUnit: '瑞恒兴仪 / 四川瑞都',
|
|
|
|
|
+ projectName: '高温高压井下工具可靠性研究',
|
|
|
|
|
+ projectLeader: '张明远',
|
|
|
|
|
+ projectMembers: '李强,王璐',
|
|
|
|
|
+ projectType: '产品、工具和设备研发类',
|
|
|
|
|
+ purpose: '提升复杂工况下工具运行可靠性。',
|
|
|
|
|
+ researchContent: '开展材料、结构及工艺适应性研究。',
|
|
|
|
|
+ innovation: '建立高温高压工况下的评价方法。',
|
|
|
|
|
+ schedule: [
|
|
|
|
|
+ { workPlan: '完成方案设计', deadline: '2026-06-30' },
|
|
|
|
|
+ { workPlan: '完成样机试制', deadline: '2026-10-31' }
|
|
|
|
|
+ ],
|
|
|
|
|
+ budget: createBudget(),
|
|
|
|
|
+ attachments: { application: [], report: [], others: [] },
|
|
|
|
|
+ status: '进行中'
|
|
|
|
|
+ }
|
|
|
|
|
+])
|
|
|
|
|
+const loading = ref(false)
|
|
|
|
|
+const queryFormRef = ref()
|
|
|
|
|
+const queryParams = reactive({ pageNo: 1, pageSize: 10, projectName: '', projectType: '' })
|
|
|
|
|
+const filteredList = computed(() =>
|
|
|
|
|
+ list.value.filter(
|
|
|
|
|
+ (item) =>
|
|
|
|
|
+ (!queryParams.projectName || item.projectName.includes(queryParams.projectName)) &&
|
|
|
|
|
+ (!queryParams.projectType || item.projectType === queryParams.projectType)
|
|
|
|
|
+ )
|
|
|
|
|
+)
|
|
|
|
|
+const dialogVisible = ref(false)
|
|
|
|
|
+const dialogTitle = ref('新增科研项目')
|
|
|
|
|
+const formLoading = ref(false)
|
|
|
|
|
+const submitLoading = ref(false)
|
|
|
|
|
+const formRef = ref()
|
|
|
|
|
+const editingId = ref<number>()
|
|
|
|
|
+const formData = ref(createForm())
|
|
|
|
|
+const formRules = reactive<FormRules>({
|
|
|
|
|
+ projectLevel: [{ required: true, message: '请选择项目级别', trigger: 'change' }],
|
|
|
|
|
+ executingUnit: [{ required: true, message: '请输入执行单位', trigger: 'blur' }],
|
|
|
|
|
+ projectName: [{ required: true, message: '请输入项目名称', trigger: 'blur' }],
|
|
|
|
|
+ projectLeader: [{ required: true, message: '请输入项目负责人', trigger: 'blur' }],
|
|
|
|
|
+ projectType: [{ required: true, message: '请选择项目类型', trigger: 'change' }],
|
|
|
|
|
+ purpose: [{ required: true, message: '请输入目标和意义', trigger: 'blur' }],
|
|
|
|
|
+ researchContent: [{ required: true, message: '请输入主要研究内容', trigger: 'blur' }],
|
|
|
|
|
+ innovation: [{ required: true, message: '请输入创新点', trigger: 'blur' }]
|
|
|
|
|
+})
|
|
|
|
|
+const budgetTotal = computed(() =>
|
|
|
|
|
+ Object.values(formData.value.budget).reduce((sum, value) => sum + Number(value || 0), 0)
|
|
|
|
|
+)
|
|
|
|
|
+const handleQuery = () => {
|
|
|
|
|
+ queryParams.pageNo = 1
|
|
|
|
|
+}
|
|
|
|
|
+const resetQuery = () => {
|
|
|
|
|
+ queryFormRef.value?.resetFields()
|
|
|
|
|
+ handleQuery()
|
|
|
|
|
+}
|
|
|
|
|
+const cloneAttachments = (attachments: Project['attachments']) => ({
|
|
|
|
|
+ application:
|
|
|
|
|
+ attachments?.application?.map((file) =>
|
|
|
|
|
+ file && typeof file === 'object' ? { ...file } : file
|
|
|
|
|
+ ) || [],
|
|
|
|
|
+ report:
|
|
|
|
|
+ attachments?.report?.map((file) => (file && typeof file === 'object' ? { ...file } : file)) ||
|
|
|
|
|
+ [],
|
|
|
|
|
+ others:
|
|
|
|
|
+ attachments?.others?.map((file) => (file && typeof file === 'object' ? { ...file } : file)) ||
|
|
|
|
|
+ []
|
|
|
|
|
+})
|
|
|
|
|
+const cloneProjectForm = (project: Project) => ({
|
|
|
|
|
+ projectLevel: project.projectLevel,
|
|
|
|
|
+ executingUnit: project.executingUnit,
|
|
|
|
|
+ projectName: project.projectName,
|
|
|
|
|
+ projectLeader: project.projectLeader,
|
|
|
|
|
+ projectMembers: project.projectMembers,
|
|
|
|
|
+ projectType: project.projectType,
|
|
|
|
|
+ purpose: project.purpose,
|
|
|
|
|
+ researchContent: project.researchContent,
|
|
|
|
|
+ innovation: project.innovation,
|
|
|
|
|
+ schedule: project.schedule.map((item) => ({ ...item })),
|
|
|
|
|
+ budget: { ...project.budget },
|
|
|
|
|
+ attachments: cloneAttachments(project.attachments)
|
|
|
|
|
+})
|
|
|
|
|
+const openForm = (project?: Project) => {
|
|
|
|
|
+ editingId.value = project?.id
|
|
|
|
|
+ dialogTitle.value = project ? '编辑科研项目' : '新增科研项目'
|
|
|
|
|
+ formData.value = project ? cloneProjectForm(project) : createForm()
|
|
|
|
|
+ dialogVisible.value = true
|
|
|
|
|
+}
|
|
|
|
|
+const addSchedule = () => formData.value.schedule.push(createSchedule())
|
|
|
|
|
+const removeSchedule = (index: number) => formData.value.schedule.splice(index, 1)
|
|
|
|
|
+const submitForm = async () => {
|
|
|
|
|
+ const valid = await formRef.value?.validate().catch(() => false)
|
|
|
|
|
+ if (!valid) return
|
|
|
|
|
+ submitLoading.value = true
|
|
|
|
|
+ const project = {
|
|
|
|
|
+ ...formData.value,
|
|
|
|
|
+ id: editingId.value || Date.now(),
|
|
|
|
|
+ status: '进行中'
|
|
|
|
|
+ } as Project
|
|
|
|
|
+ const index = list.value.findIndex((item) => item.id === project.id)
|
|
|
|
|
+ if (index === -1) list.value.unshift(project)
|
|
|
|
|
+ else list.value[index] = project
|
|
|
|
|
+ submitLoading.value = false
|
|
|
|
|
+ dialogVisible.value = false
|
|
|
|
|
+ ElMessage.success('项目已保存')
|
|
|
|
|
+}
|
|
|
|
|
+const removeProject = async (id: number) => {
|
|
|
|
|
+ const confirmed = await ElMessageBox.confirm('确定删除这个科研项目吗?', '提示', {
|
|
|
|
|
+ type: 'warning',
|
|
|
|
|
+ confirmButtonText: '确定',
|
|
|
|
|
+ cancelButtonText: '取消'
|
|
|
|
|
+ })
|
|
|
|
|
+ .then(() => true)
|
|
|
|
|
+ .catch(() => false)
|
|
|
|
|
+ if (confirmed) {
|
|
|
|
|
+ list.value = list.value.filter((item) => item.id !== id)
|
|
|
|
|
+ ElMessage.success('删除成功')
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</script>
|
|
|
|
|
+
|
|
|
|
|
+<style scoped lang="scss">
|
|
|
|
|
+.project-filter {
|
|
|
|
|
+ margin-bottom: -15px;
|
|
|
|
|
+}
|
|
|
|
|
+.project-table-heading {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ margin-bottom: 16px;
|
|
|
|
|
+}
|
|
|
|
|
+.project-table-heading h2 {
|
|
|
|
|
+ margin: 0 0 4px;
|
|
|
|
|
+ color: var(--el-text-color-primary);
|
|
|
|
|
+ font-size: 18px;
|
|
|
|
|
+}
|
|
|
|
|
+.project-table-heading span {
|
|
|
|
|
+ color: var(--el-text-color-secondary);
|
|
|
|
|
+ font-size: 13px;
|
|
|
|
|
+}
|
|
|
|
|
+.project-form {
|
|
|
|
|
+ padding: 0 8px;
|
|
|
|
|
+}
|
|
|
|
|
+.project-form :deep(.el-divider__text) {
|
|
|
|
|
+ color: var(--el-color-primary);
|
|
|
|
|
+ font-weight: 600;
|
|
|
|
|
+}
|
|
|
|
|
+.schedule-editor {
|
|
|
|
|
+ width: 100%;
|
|
|
|
|
+}
|
|
|
|
|
+.schedule-toolbar {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: space-between;
|
|
|
|
|
+ margin-bottom: 10px;
|
|
|
|
|
+ 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-total strong {
|
|
|
|
|
+ color: var(--el-color-danger);
|
|
|
|
|
+ font-size: 20px;
|
|
|
|
|
+ font-variant-numeric: tabular-nums;
|
|
|
|
|
+}
|
|
|
|
|
+@media (max-width: 900px) {
|
|
|
|
|
+ .budget-grid :deep(.el-col) {
|
|
|
|
|
+ width: 50%;
|
|
|
|
|
+ flex: 0 0 50%;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+</style>
|